summaryrefslogtreecommitdiff
path: root/lib/dojo/tt-rss-layer.js.uncompressed.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dojo/tt-rss-layer.js.uncompressed.js')
-rw-r--r--lib/dojo/tt-rss-layer.js.uncompressed.js46469
1 files changed, 25451 insertions, 21018 deletions
diff --git a/lib/dojo/tt-rss-layer.js.uncompressed.js b/lib/dojo/tt-rss-layer.js.uncompressed.js
index 81d4302df..cc43d8726 100644
--- a/lib/dojo/tt-rss-layer.js.uncompressed.js
+++ b/lib/dojo/tt-rss-layer.js.uncompressed.js
@@ -1,3197 +1,5521 @@
-/*
- Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
-*/
+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/_base/sniff", // has("ie") has("mozilla")
+ "dojo/_base/window", // win.doc.selection.createRange
+ "./_FormValueWidget",
+ "./_TextBoxMixin",
+ "dojo/text!./templates/TextBox.html",
+ ".." // to export dijit._setSelectionRange, remove in 2.0
+], function(declare, domConstruct, domStyle, kernel, lang, has, win,
+ _FormValueWidget, _TextBoxMixin, template, dijit){
-/*
- This is an optimized version of Dojo, built for deployment and not for
- development. To get sources and documentation, please visit:
+/*=====
+ var _FormValueWidget = dijit.form._FormValueWidget;
+ var _TextBoxMixin = dijit.form._TextBoxMixin;
+=====*/
- http://dojotoolkit.org
-*/
+ // module:
+ // dijit/form/TextBox
+ // summary:
+ // A base class for textbox form inputs
-dojo.provide("tt-rss-layer");
-if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.date.stamp"] = true;
-dojo.provide("dojo.date.stamp");
+ var TextBox = declare(/*====="dijit.form.TextBox", =====*/ [_FormValueWidget, _TextBoxMixin], {
+ // summary:
+ // A base class for textbox form inputs
-dojo.getObject("date.stamp", true, dojo);
+ templateString: template,
+ _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
-// Methods to convert dates to or from a wire (string) format using well-known conventions
+ _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
-dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
- // summary:
- // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
- //
- // description:
- // Accepts a string formatted according to a profile of ISO8601 as defined by
- // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
- // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
- // The following combinations are valid:
- //
- // * dates only
- // | * yyyy
- // | * yyyy-MM
- // | * yyyy-MM-dd
- // * times only, with an optional time zone appended
- // | * THH:mm
- // | * THH:mm:ss
- // | * THH:mm:ss.SSS
- // * and "datetimes" which could be any combination of the above
- //
- // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
- // Assumes the local time zone if not specified. Does not validate. Improperly formatted
- // input may return null. Arguments which are out of bounds will be handled
- // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
- // Only years between 100 and 9999 are supported.
- //
- // formattedString:
- // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
- //
- // defaultTime:
- // Used for defaults for fields omitted in the formattedString.
- // Uses 1970-01-01T00:00:00.0Z by default.
+ baseClass: "dijitTextBox",
- if(!dojo.date.stamp._isoRegExp){
- dojo.date.stamp._isoRegExp =
-//TODO: could be more restrictive and check for 00-59, etc.
- /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
- }
+ 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);
+ },
- var match = dojo.date.stamp._isoRegExp.exec(formattedString),
- result = null;
+ _onInput: function(e){
+ this.inherited(arguments);
+ if(this.intermediateChanges){ // _TextBoxMixin uses onInput
+ var _this = this;
+ // the setTimeout allows the key to post to the widget input box
+ setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
+ }
+ },
- 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
+ _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',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
+ }
+ this._phspan.innerHTML="";
+ this._phspan.appendChild(document.createTextNode(v));
+ this._updatePlaceHolder();
+ },
- if(defaultTime){
- // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
- defaultTime = new Date(defaultTime);
- dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
- return defaultTime["get" + prop]();
- }), function(value, index){
- match[index] = match[index] || value;
- });
- }
- result = new Date(match[0]||1970, match[1]||0, match[2]||1, match[3]||0, match[4]||0, match[5]||0, match[6]||0); //TODO: UTC defaults
- if(match[0] < 100){
- result.setFullYear(match[0] || 1970);
- }
+ _updatePlaceHolder: function(){
+ if(this._phspan){
+ this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
+ }
+ },
- 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);
- }
- }
+ _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
+ this.inherited(arguments);
+ this._updatePlaceHolder();
+ },
- return result; // Date or null
-};
+ getDisplayedValue: function(){
+ // summary:
+ // Deprecated. Use get('displayedValue') instead.
+ // tags:
+ // deprecated
+ kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
+ return this.get('displayedValue');
+ },
-/*=====
- dojo.date.stamp.__Options = function(){
- // selector: String
- // "date" or "time" for partial formatting of the Date object.
- // Both date and time will be formatted by default.
- // zulu: Boolean
- // if true, UTC/GMT is used for a timezone
- // milliseconds: Boolean
- // if true, output milliseconds
- this.selector = selector;
- this.zulu = zulu;
- this.milliseconds = milliseconds;
- }
-=====*/
+ 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);
+ },
-dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
- // summary:
- // Format a Date object as a string according a subset of the ISO-8601 standard
- //
- // description:
- // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
- // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
- // Does not check bounds. Only years between 100 and 9999 are supported.
- //
- // dateObject:
- // A Date object
+ _onBlur: function(e){
+ if(this.disabled){ return; }
+ this.inherited(arguments);
+ this._updatePlaceHolder();
+ },
- 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);
+ _onFocus: function(/*String*/ by){
+ if(this.disabled || this.readOnly){ return; }
+ this.inherited(arguments);
+ this._updatePlaceHolder();
}
- 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);
+ });
+
+ if(has("ie")){
+ TextBox = declare(/*===== "dijit.form.TextBox.IEMixin", =====*/ TextBox, {
+ declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
+
+ _isTextSelected: function(){
+ var range = win.doc.selection.createRange();
+ var parent = range.parentElement();
+ return parent == this.textbox && range.text.length == 0;
+ },
+
+ postCreate: function(){
+ this.inherited(arguments);
+ // IE INPUT tag fontFamily has to be set directly using STYLE
+ // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
+ setTimeout(lang.hitch(this, 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.*/}
+ }), 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();
+ }
}
- formattedDate.push(time);
+ }else if(has("mozilla")){
+ TextBox = declare(/*===== "dijit.form.TextBox.MozMixin", =====*/TextBox, {
+ declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
+
+ _onBlur: function(e){
+ this.inherited(arguments);
+ if(this.selectOnClick){
+ // clear selection so that the next mouse click doesn't reselect
+ this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
+ }
+ }
+ });
+ }else{
+ TextBox.prototype.declaredClass = "dijit.form.TextBox";
}
- return formattedDate.join('T'); // String
-};
+ lang.setObject("dijit.form.TextBox", TextBox); // don't do direct assignment, it confuses API doc parser
-}
+ return TextBox;
+});
-if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.parser"] = true;
-dojo.provide("dojo.parser");
+},
+'dijit/_base/scroll':function(){
+define("dijit/_base/scroll", [
+ "dojo/window", // windowUtils.scrollIntoView
+ ".." // export symbol to dijit
+], function(windowUtils, dijit){
+ // module:
+ // dijit/_base/scroll
+ // 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);
+ };
+});
-new Date("X"); // workaround for #11279, new Date("") == NaN
+},
+'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/_base/sniff", // has("ie")
+ "dojo/_base/unload", // unload.addOnWindowUnload
+ "dojo/_base/window" // win.doc
+], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload, win) {
-dojo.parser = new function(){
- // summary:
- // The Dom/Widget parsing package
+/*=====
+ var _WidgetBase = dijit._WidgetBase;
+=====*/
- var d = dojo;
+ // module:
+ // dijit/_TemplatedMixin
+ // summary:
+ // Mixin for widgets that are instantiated from a template
- function val2type(/*Object*/ value){
+ var _TemplatedMixin = declare("dijit._TemplatedMixin", null, {
// summary:
- // Returns name of type of given value.
+ // Mixin for widgets that are instantiated from a template
- if(d.isString(value)){ return "string"; }
- if(typeof value == "number"){ return "number"; }
- if(typeof value == "boolean"){ return "boolean"; }
- if(d.isFunction(value)){ return "function"; }
- if(d.isArray(value)){ return "array"; } // typeof [] == "object"
- if(value instanceof Date) { return "date"; } // assume timestamp
- if(value instanceof d._Url){ return "url"; }
- return "object";
- }
+ // templateString: [protected] String
+ // A string that represents the widget template.
+ // Use in conjunction with dojo.cache() to load from a file.
+ templateString: null,
- function str2obj(/*String*/ value, /*String*/ type){
- // summary:
- // Convert given string value to given type
- switch(type){
- case "string":
- return value;
- case "number":
- return value.length ? Number(value) : NaN;
- case "boolean":
- // for checked/disabled value might be "" or "checked". interpret as true.
- return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
- case "function":
- if(d.isFunction(value)){
- // IE gives us a function, even when we say something like onClick="foo"
- // (in which case it gives us an invalid function "function(){ foo }").
- // Therefore, convert to string
- value=value.toString();
- value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
- }
- try{
- if(value === "" || value.search(/[^\w\.]+/i) != -1){
- // The user has specified some text for a function like "return x+5"
- return new Function(value);
- }else{
- // The user has specified the name of a function like "myOnClick"
- // or a single word function "return"
- return d.getObject(value, false) || new Function(value);
- }
- }catch(e){ return new Function(); }
- case "array":
- return value ? value.split(/\s*,\s*/) : [];
- case "date":
- switch(value){
- case "": return new Date(""); // the NaN of dates
- case "now": return new Date(); // current date
- default: return d.date.stamp.fromISOString(value);
- }
- case "url":
- return d.baseUrl + value;
- default:
- return d.fromJson(value);
- }
- }
+ // 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,
- var dummyClass = {}, instanceClasses = {
- // map from fully qualified name (like "dijit.Button") to structure like
- // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
- };
+ // 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,
- // 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).
- // TODO: remove this in 2.0, when we stop caching parameters.
- d.connect(d, "extend", function(){
- instanceClasses = {};
- });
+ // _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,
- function getProtoInfo(cls, params){
- // cls: A prototype
- // The prototype of the class to check props on
- // params: Object
- // The parameters object to mix found parameters onto.
- for(var name in cls){
- if(name.charAt(0)=="_"){ continue; } // skip internal properties
- if(name in dummyClass){ continue; } // skip "constructor" and "toString"
- params[name] = val2type(cls[name]);
- }
- return params;
- }
+/*=====
+ // _attachPoints: [private] String[]
+ // List of widget attribute names associated with data-dojo-attach-point=... in the
+ // template, ex: ["containerNode", "labelNode"]
+ _attachPoints: [],
+ =====*/
- function getClassInfo(/*String*/ className, /*Boolean*/ skipParamsLookup){
- // summary:
- // Maps a widget name string like "dijit.form.Button" to the widget constructor itself,
- // and a list of that widget's parameters and their types
- // className:
- // fully qualified name (like "dijit.form.Button")
- // returns:
- // structure like
- // {
- // cls: dijit.Button,
- // params: { label: "string", disabled: "boolean"}
- // }
-
- var c = instanceClasses[className];
- if(!c){
- // get pointer to widget class
- var cls = d.getObject(className), params = null;
- if(!cls){ return null; } // class not defined [yet]
- if(!skipParamsLookup){ // from fastpath, we don't need to lookup the attrs on the proto because they are explicit
- params = getProtoInfo(cls.prototype, {})
- }
- c = { cls: cls, params: params };
-
- }else if(!skipParamsLookup && !c.params){
- // if we're calling getClassInfo and have a cls proto, but no params info, scan that cls for params now
- // and update the pointer in instanceClasses[className]. This happens when a widget appears in another
- // widget's template which still uses dojoType, but an instance of the widget appears prior with a data-dojo-type,
- // skipping this lookup the first time.
- c.params = getProtoInfo(c.cls.prototype, {});
- }
-
- return c;
- }
+/*=====
+ // _attachEvents: [private] Handle[]
+ // List of connections associated with data-dojo-attach-event=... in the
+ // template
+ _attachEvents: [],
+ =====*/
- this._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 = "";
- var suffix = "";
- var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args"));
- if(argsStr){
- d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
- preamble += "var "+part+" = arguments["+idx+"]; ";
- });
- }
- var withStr = script.getAttribute("with");
- if(withStr && withStr.length){
- d.forEach(withStr.split(/\s*,\s*/), function(part){
- preamble += "with("+part+"){";
- suffix += "}";
- });
- }
- return new Function(preamble+script.innerHTML+suffix);
- };
+ constructor: function(){
+ this._attachPoints = [];
+ this._attachEvents = [];
+ },
- this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){
- // summary:
- // Takes array of nodes, and turns them into class instances and
- // potentially calls a startup method to allow them to connect with
- // any children.
- // nodes: Array
- // Array of nodes or objects like
- // | {
- // | type: "dijit.form.Button",
- // | node: DOMNode,
- // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
- // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
- // | }
- // mixin: Object?
- // An object that will be mixed in with each node in the array.
- // Values in the mixin will override values in the node, if they
- // exist.
- // args: Object?
- // An object used to hold kwArgs for instantiation.
- // See parse.args argument for details.
+ _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 ""; }
- var thelist = [],
- mixin = mixin||{};
- args = args||{};
+ // 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);
+ },
- // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
- var attrName = (args.scope || d._scopeName) + "Type", // typically "dojoType"
- attrData = "data-" + (args.scope || d._scopeName) + "-"; // typically "data-dojo-"
+ buildRendering: function(){
+ // summary:
+ // Construct the UI for this widget from a template, setting this.domNode.
+ // tags:
+ // protected
- d.forEach(nodes, function(obj){
- if(!obj){ return; }
+ 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);
- // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.
- var node, type, clsInfo, clazz, scripts, fastpath;
- if(obj.node){
- // new format of nodes[] array, object w/lots of properties pre-computed for me
- node = obj.node;
- type = obj.type;
- fastpath = obj.fastpath;
- clsInfo = obj.clsInfo || (type && getClassInfo(type, fastpath));
- clazz = clsInfo && clsInfo.cls;
- scripts = obj.scripts;
+ var node;
+ if(lang.isString(cached)){
+ node = domConstruct.toDom(this._stringRepl(cached));
+ if(node.nodeType != 1){
+ // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
+ throw new Error("Invalid template: " + cached);
+ }
}else{
- // old (backwards compatible) format of nodes[] array, simple array of DOMNodes. no fastpath/data-dojo-type support here.
- node = obj;
- type = attrName in mixin ? mixin[attrName] : node.getAttribute(attrName);
- clsInfo = type && getClassInfo(type);
- clazz = clsInfo && clsInfo.cls;
- scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] :
- d.query("> script[type^='dojo/']", node));
- }
- if(!clsInfo){
- throw new Error("Could not load class '" + type);
+ // if it's a node, all we have to do is clone it
+ node = cached.cloneNode(true);
}
- // 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(args.defaults){
- // settings for the document itself (or whatever subtree is being parsed)
- d._mixin(params, args.defaults);
- }
- if(obj.inherited){
- // settings from dir=rtl or lang=... on a node above this node
- d._mixin(params, obj.inherited);
- }
-
- // mix things found in data-dojo-props into the params
- if(fastpath){
- var extra = node.getAttribute(attrData + "props");
- if(extra && extra.length){
- try{
- extra = d.fromJson.call(args.propsThis, "{" + extra + "}");
- d._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 + "'");
- }
+ 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
- // For the benefit of _Templated, check if node has data-dojo-attach-point/data-dojo-attach-event
- // and mix those in as though they were parameters
- var attachPoint = node.getAttribute(attrData + "attach-point");
+ var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
+ var x = lang.isArray(rootNode) ? 0 : -1;
+ for(; x<nodes.length; x++){
+ 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){
- params.dojoAttachPoint = 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);
+ }
}
- var attachEvent = node.getAttribute(attrData + "attach-event");
+
+ // Process data-dojo-attach-event
+ var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
if(attachEvent){
- params.dojoAttachEvent = attachEvent;
- }
- dojo.mixin(params, mixin);
- }else{
- // FIXME: we need something like "deprecateOnce()" to throw dojo.deprecation for something.
- // remove this logic in 2.0
- // read parameters (ie, attributes) specified on DOMNode
-
- var attributes = node.attributes;
-
- // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
- for(var name in clsInfo.params){
- var item = name in mixin ? { value:mixin[name], specified:true } : attributes.getNamedItem(name);
- if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
- var value = item.value;
- // Deal with IE quirks for 'class' and 'style'
- switch(name){
- case "class":
- value = "className" in mixin ? mixin.className : node.className;
- break;
- case "style":
- value = "style" in mixin ? mixin.style : (node.style && node.style.cssText); // FIXME: Opera?
- }
- var _type = clsInfo.params[name];
- if(typeof value == "string"){
- params[name] = str2obj(value, _type);
- }else{
- params[name] = value;
+ // 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));
+ }
}
}
}
+ },
- // Process <script type="dojo/*"> script tags
- // <script type="dojo/method" event="foo"> tags are added to params, and passed to
- // the widget on instantiation.
- // <script type="dojo/method"> tags (with no event) are executed after instantiation
- // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
- // note: dojo/* script tags cannot exist in self closing widgets, like <input />
- var connects = [], // functions to connect after instantiation
- calls = []; // functions to call after instantiation
-
- d.forEach(scripts, function(script){
- node.removeChild(script);
- // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
- var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
- type = script.getAttribute("type"),
- nf = d.parser._functionFromScript(script, attrData);
- if(event){
- if(type == "dojo/connect"){
- connects.push({event: event, func: nf});
- }else{
- params[event] = nf;
- }
- }else{
- calls.push(nf);
+ 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){
+ // 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
+ // 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 == win.doc){
+ // 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);
+ }
- var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory;
- // create the instance
- var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
- thelist.push(instance);
+ templateString = string.trim(templateString);
- // map it to the JS namespace if that makes sense
- // FIXME: in 2.0, drop jsId support. use data-dojo-id instead
- var jsname = (node.getAttribute(attrData + "id") || node.getAttribute("jsId"));
- if(jsname){
- d.setObject(jsname, instance);
+ 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);
+ if(node.nodeType != 1){
+ throw new Error("Invalid template: " + templateString);
}
+ return (tmplts[key] = node); //Node
+ }
+ };
- // process connections and startup functions
- d.forEach(connects, function(connect){
- d.connect(instance, connect.event, null, connect.func);
- });
- d.forEach(calls, function(func){
- func.call(instance);
- });
+ 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];
+ }
});
+ }
- // Call startup on each top level instance if it makes sense (as for
- // widgets). Parent widgets will recursively call startup on their
- // (non-top level) children
- if(!mixin._started){
- // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
- // relationships in the nodes[] array so that no getParent() call is needed.
- // Note that will require a parse() call from ContentPane setting a param that the
- // ContentPane is the parent widget (so that the parse doesn't call startup() on the
- // ContentPane's children)
- d.forEach(thelist, function(instance){
- if( !args.noStart && instance &&
- dojo.isFunction(instance.startup) &&
- !instance._started &&
- (!instance.getParent || !instance.getParent())
- ){
- instance.startup();
- }
- });
+ // 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.)
+ lang.extend(_WidgetBase,{
+ dojoAttachEvent: "",
+ dojoAttachPoint: ""
+ });
+
+ return _TemplatedMixin;
+});
+
+},
+'dijit/_CssStateMixin':function(){
+define("dijit/_CssStateMixin", [
+ "dojo/touch",
+ "dojo/_base/array", // array.forEach array.map
+ "dojo/_base/declare", // declare
+ "dojo/dom-class", // domClass.toggle
+ "dojo/_base/lang", // lang.hitch
+ "dojo/_base/window" // win.body
+], function(touch, array, declare, domClass, lang, win){
+
+// module:
+// 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.
+
+return 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);
+
+ // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
+ array.forEach(["onmouseenter", "onmouseleave", touch.press], function(e){
+ this.connect(this.domNode, e, "_cssMouseEvent");
+ }, this);
+
+ // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
+ array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
+ this.watch(attr, lang.hitch(this, "_setStateClass"));
+ }, this);
+
+ // Events on sub nodes within the widget
+ for(var ap in this.cssStateNodes){
+ this._trackMouseState(this[ap], this.cssStateNodes[ap]);
}
- return thelist;
- };
+ // 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();
+ },
- this.parse = function(rootNode, args){
+ _cssMouseEvent: function(/*Event*/ event){
// summary:
- // Scan the DOM for class instances, and instantiate them.
+ // 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 "mouseenter":
+ case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
+ this._set("hovering", true);
+ this._set("active", this._mouseDown);
+ break;
+
+ case "mouseleave":
+ case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
+ this._set("hovering", false);
+ this._set("active", false);
+ break;
+
+ case "mousedown":
+ case "touchpress":
+ this._set("active", true);
+ this._mouseDown = true;
+ // Set a global event to handle mouseup, so it fires properly
+ // even if the cursor leaves this.domNode before the mouse up event.
+ // Alternately could set active=false on mouseout.
+ var mouseUpConnector = this.connect(win.body(), touch.release, function(){
+ this._mouseDown = false;
+ this._set("active", false);
+ this.disconnect(mouseUpConnector);
+ });
+ 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:
- // 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 instantitate 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 `args`
- // object can be passed in this place. If the `args` object has a
- // `rootNode` member, that is used.
- //
- // args: Object
- // a kwArgs object passed along to instantiate()
- //
- // * noStart: Boolean?
- // when set will prevent the parser from calling .startup()
- // when locating the nodes.
- // * rootNode: DomNode?
- // identical to the function's `rootNode` argument, though
- // allowed to be passed in via this `args object.
- // * 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._Templated`
- //
- // example:
- // Parse all widgets on a page:
- // | dojo.parser.parse();
- //
- // example:
- // Parse all classes within the node with id="foo"
- // | dojo.parser.parse(dojo.byId('foo'));
+ // 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".
//
- // example:
- // Parse all classes in a page, but do not call .startup() on any
- // child
- // | dojo.parser.parse({ noStart: true })
+ // 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
//
- // example:
- // Parse all classes in a node, but do not call .startup()
- // | dojo.parser.parse(someNode, { noStart:true });
- // | // or
- // | dojo.parser.parse({ noStart:true, rootNode: someNode });
+ // 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
- // determine the root node based on the passed arguments.
- var root;
- if(!args && rootNode && rootNode.rootNode){
- args = rootNode;
- root = args.rootNode;
+ // 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.disabled){
+ multiply("Disabled");
+ }else if(this.readOnly){
+ multiply("ReadOnly");
}else{
- root = rootNode;
+ if(this.active){
+ multiply("Active");
+ }else if(this.hovering){
+ multiply("Hover");
+ }
}
- root = root ? dojo.byId(root) : dojo.body();
- args = args || {};
- var attrName = (args.scope || d._scopeName) + "Type", // typically "dojoType"
- attrData = "data-" + (args.scope || d._scopeName) + "-"; // typically "data-dojo-"
-
- function scan(parent, list){
- // summary:
- // Parent is an Object representing a DOMNode, with or without a dojoType specified.
- // Scan parent's children looking for nodes with dojoType specified, storing in list[].
- // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
- // parent: Object
- // Object representing the parent node, like
- // | {
- // | node: DomNode, // scan children of this node
- // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node
- // |
- // | // attributes only set if node has dojoType specified
- // | scripts: [], // empty array, put <script type=dojo/*> in here
- // | clsInfo: { cls: dijit.form.Button, ...}
- // | }
- // list: DomNode[]
- // Output array of objects (same format as parent) representing nodes to be turned into widgets
-
- // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
- var inherited = dojo.clone(parent.inherited);
- dojo.forEach(["dir", "lang"], function(name){
- // TODO: what if this is a widget and dir/lang are declared in data-dojo-props?
- var val = parent.node.getAttribute(name);
- if(val){
- inherited[name] = val;
- }
- });
+ if(this.focused){
+ multiply("Focused");
+ }
- // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
- var scripts = parent.clsInfo && !parent.clsInfo.cls.prototype._noScript ? parent.scripts : null;
+ // 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
- // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
- var recurse = (!parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser) || (args && args.template);
+ array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
- // scan parent's children looking for dojoType and <script type=dojo/*>
- for(var child = parent.node.firstChild; child; child = child.nextSibling){
- if(child.nodeType == 1){
- // FIXME: desupport dojoType in 2.0. use data-dojo-type instead
- var type, html5 = recurse && child.getAttribute(attrData + "type");
- if(html5){
- type = html5;
- }else{
- // fallback to backward compatible mode, using dojoType. remove in 2.0
- type = recurse && child.getAttribute(attrName);
- }
-
- var fastpath = html5 == type;
-
- if(type){
- // if dojoType/data-dojo-type specified, add to output array of nodes to instantiate
- var params = {
- "type": type,
- fastpath: fastpath,
- clsInfo: getClassInfo(type, fastpath), // note: won't find classes declared via dojo.Declaration
- node: child,
- scripts: [], // <script> nodes that are parent's children
- inherited: inherited // dir & lang attributes inherited from parent
- };
- list.push(params);
-
- // Recurse, collecting <script type="dojo/..."> children, and also looking for
- // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
- scan(params, list);
- }else if(scripts && child.nodeName.toLowerCase() == "script"){
- // if <script type="dojo/...">, save in scripts[]
- type = child.getAttribute("type");
- if (type && /^dojo\/\w/i.test(type)) {
- scripts.push(child);
- }
- }else if(recurse){
- // Recurse, looking for grandchild nodes with dojoType specified
- scan({
- node: child,
- inherited: inherited
- }, list);
- }
- }
- }
+ if("_stateClasses" in this){
+ array.forEach(this._stateClasses, function(c){ delete classHash[c]; });
}
- // Ignore bogus entries in inherited hash like {dir: ""}
- var inherited = {};
- if(args && args.inherited){
- for(var key in args.inherited){
- if(args.inherited[key]){ inherited[key] = args.inherited[key]; }
- }
+ array.forEach(newStateClasses, function(c){ classHash[c] = true; });
+
+ var newClasses = [];
+ for(var c in classHash){
+ newClasses.push(c);
}
+ tn.className = newClasses.join(" ");
- // Make list of all nodes on page w/dojoType specified
- var list = [];
- scan({
- node: root,
- inherited: inherited
- }, list);
+ this._stateClasses = newStateClasses;
+ },
- // go build the object instances
- var mixin = args && args.template ? {template: true} : null;
- return this.instantiate(list, mixin, args); // Array
- };
-}();
+ _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).
-//Register the parser callback. It should be the first callback
-//after the a11y test.
+ // Current state of node (initially false)
+ // NB: setting specifically to false because domClass.toggle() needs true boolean as third arg
+ var hovering=false, active=false, focused=false;
+
+ var self = this,
+ cn = lang.hitch(this, "connect", node);
-(function(){
- var parseRunner = function(){
- if(dojo.config.parseOnLoad){
- dojo.parser.parse();
+ function setClass(){
+ var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
+ domClass.toggle(node, clazz+"Hover", hovering && !active && !disabled);
+ domClass.toggle(node, clazz+"Active", active && !disabled);
+ domClass.toggle(node, clazz+"Focused", focused && !disabled);
}
- };
- // FIXME: need to clobber cross-dependency!!
- if(dojo.getObject("dijit.wai.onload") === dojo._loaders[0]){
- dojo._loaders.splice(1, 0, parseRunner);
- }else{
- dojo._loaders.unshift(parseRunner);
- }
-})();
+ // Mouse
+ cn("onmouseenter", function(){
+ hovering = true;
+ setClass();
+ });
+ cn("onmouseleave", function(){
+ hovering = false;
+ active = false;
+ setClass();
+ });
+ cn(touch.press, function(){
+ active = true;
+ setClass();
+ });
+ cn(touch.release, function(){
+ active = false;
+ setClass();
+ });
-}
+ // Focus
+ cn("onfocus", function(){
+ focused = true;
+ setClass();
+ });
+ cn("onblur", function(){
+ focused = false;
+ setClass();
+ });
-if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.window"] = true;
-dojo.provide("dojo.window");
+ // Just in case widget is enabled/disabled while it has focus/hover/active state.
+ // Maybe this is overkill.
+ this.watch("disabled", setClass);
+ this.watch("readOnly", setClass);
+ }
+});
+});
-dojo.getObject("window", true, dojo);
+},
+'dijit/DialogUnderlay':function(){
+define("dijit/DialogUnderlay", [
+ "dojo/_base/declare", // declare
+ "dojo/dom-attr", // domAttr.set
+ "dojo/_base/window", // win.body
+ "dojo/window", // winUtils.getBox
+ "./_Widget",
+ "./_TemplatedMixin",
+ "./BackgroundIframe"
+], function(declare, domAttr, win, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
-dojo.window.getBox = function(){
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+=====*/
+
+ // module:
+ // dijit/DialogUnderlay
// summary:
- // Returns the dimensions and scroll position of the viewable area of a browser window
+ // The component that blocks the screen behind a `dijit.Dialog`
- var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement;
+ 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.
- // get scroll position
- var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work
- return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
-};
+ // 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>",
-dojo.window.get = function(doc){
- // summary:
- // Get window object associated with document doc
+ // Parameters on creation or updatable later
- // In some IE versions (at least 6.0), document.parentWindow does not return a
- // reference to the real window object (maybe a copy), so we must fix it as well
- // We use IE specific execScript to attach the real window reference to
- // document._parentWindow for later use
- if(dojo.isIE && window !== document.parentWindow){
- /*
- In IE 6, only the variable "window" can be used to connect events (others
- may be only copies).
- */
- doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
- //to prevent memory leak, unset it after use
- //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
- var win = doc._parentWindow;
- doc._parentWindow = null;
- return win; // Window
- }
+ // dialogId: String
+ // Id of the dialog.... DialogUnderlay's id is based on this id
+ dialogId: "",
- return doc.parentWindow || doc.defaultView; // Window
-};
+ // class: String
+ // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
+ "class": "",
-dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
- // summary:
- // Scroll the passed node into view, if it is not already.
-
- // don't rely on node.scrollIntoView working just because the function is there
+ _setDialogIdAttr: function(id){
+ domAttr.set(this.node, "id", id + "_underlay");
+ this._set("dialogId", id);
+ },
- try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
- node = dojo.byId(node);
- var doc = node.ownerDocument || dojo.doc,
- body = doc.body || dojo.body(),
- html = doc.documentElement || body.parentNode,
- isIE = dojo.isIE, isWK = dojo.isWebKit;
- // if an untested browser, then use the native method
- if((!(dojo.isMoz || isIE || isWK || dojo.isOpera) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
- node.scrollIntoView(false); // short-circuit to native if possible
- return;
- }
- var backCompat = doc.compatMode == 'BackCompat',
- clientAreaRoot = (isIE >= 9 && node.ownerDocument.parentWindow.frameElement)
- ? ((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 = !dojo._isBodyLtr(),
- nodePos = pos || dojo.position(node),
- el = node.parentNode,
- isFixed = function(el){
- return ((isIE <= 6 || (isIE && backCompat))? false : (dojo.style(el, 'position').toLowerCase() == "fixed"));
- };
- if(isFixed(node)){ return; } // nothing to do
+ _setClassAttr: function(clazz){
+ this.node.className = "dijitDialogUnderlay " + clazz;
+ this._set("class", clazz);
+ },
- while(el){
- if(el == body){ el = scrollRoot; }
- var elPos = dojo.position(el),
- fixedPos = isFixed(el);
-
- if(el == scrollRoot){
- elPos.w = rootWidth; elPos.h = rootHeight;
- if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
- if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
- if(elPos.y < 0 || !isIE){ elPos.y = 0; }
- }else{
- var pb = dojo._getPadBorderExtents(el);
- elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
- 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;
+ postCreate: function(){
+ // summary:
+ // Append the underlay to the body
+ win.body().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();
+ 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";
}
- }catch(error){
- console.error('scrollIntoView: ' + error);
- node.scrollIntoView(false);
- }
-};
+ });
+});
-}
+},
+'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\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\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/query", // query
+ "dojo/_base/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, query, has,
+ registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
+ Menu, MenuItem, Button, _HasDropDown){
-if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.manager"] = true;
-dojo.provide("dijit._base.manager");
+/*=====
+var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
+var Menu = dijit.Menu;
+var _HasDropDown = dijit._HasDropDown;
+var TabController = dijit.layout.TabController;
+=====*/
+
+
+// module:
+// dijit/layout/ScrollingTabController
+// summary:
+// Set of tabs with left/right arrow keys and a menu to switch between tabs not
+// all fitting on a single row.
-dojo.declare("dijit.WidgetSet", null, {
+var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
// summary:
- // A set of widgets indexed by id. A default instance of this class is
- // available as `dijit.registry`
- //
- // example:
- // Create a small list of widgets:
- // | var ws = new dijit.WidgetSet();
- // | ws.add(dijit.byId("one"));
- // | ws.add(dijit.byId("two"));
- // | // destroy both:
- // | ws.forEach(function(w){ w.destroy(); });
- //
- // example:
- // Using dijit.registry:
- // | dijit.registry.forEach(function(w){ /* do something */ });
+ // 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
- constructor: function(){
- this._hash = {};
- this.length = 0;
- },
+ baseClass: "dijitTabController dijitScrollingTabController",
- add: function(/*dijit._Widget*/ widget){
- // summary:
- // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
- //
- // widget: dijit._Widget
- // Any dijit._Widget subclass.
- if(this._hash[widget.id]){
- throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
+ 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")
}
- this._hash[widget.id] = widget;
- this.length++;
+
+ domClass.add(this.tablistWrapper, this.tabStripClass);
},
- 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--;
- }
+ 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;
},
- forEach: function(/*Function*/ func, /* Object? */thisObj){
- // summary:
- // Call specified function for each widget in this set.
- //
- // func:
- // A callback function to run for each item. Is passed the widget, the index
- // in the iteration, and the full hash, similar to `dojo.forEach`.
- //
- // thisObj:
- // An optional scope parameter
- //
- // example:
- // Using the default `dijit.registry` instance:
- // | dijit.registry.forEach(function(widget){
- // | console.log(widget.declaredClass);
- // | });
- //
- // returns:
- // Returns self, in order to allow for further chaining.
+ onAddChild: function(page, insertIndex){
+ this.inherited(arguments);
+
+ // changes to the tab button label or iconClass will have changed the width of the
+ // buttons, so do a resize
+ array.forEach(["label", "iconClass"], function(attr){
+ this.pane2watches[page.id].push(
+ this.pane2button[page.id].watch(attr, lang.hitch(this, function(){
+ if(this._postStartup && this._dim){
+ this.resize(this._dim);
+ }
+ }))
+ );
+ }, this);
+
+ // 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");
+ },
- thisObj = thisObj || dojo.global;
- var i = 0, id;
- for(id in this._hash){
- func.call(thisObj, this._hash[id], i++, this._hash);
+ 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;
}
- return this; // dijit.WidgetSet
+
+ this.inherited(arguments);
},
- filter: function(/*Function*/ filter, /* Object? */thisObj){
+ _initButtons: function(){
// summary:
- // Filter down this WidgetSet to a smaller new WidgetSet
- // Works the same as `dojo.filter` and `dojo.NodeList.filter`
- //
- // filter:
- // Callback function to test truthiness. Is passed the widget
- // reference and the pseudo-index in the object.
- //
- // thisObj: Object?
- // Option scope to use for the filter function.
- //
- // example:
- // Arbitrary: select the odd widgets in this list
- // | dijit.registry.filter(function(w, i){
- // | return i % 2 == 0;
- // | }).forEach(function(w){ /* odd ones */ });
+ // Creates the buttons used to scroll to view tabs that
+ // may not be visible if the TabContainer is too narrow.
- thisObj = thisObj || dojo.global;
- var res = new dijit.WidgetSet(), i = 0, id;
- for(id in this._hash){
- var w = this._hash[id];
- if(filter.call(thisObj, w, i++, this._hash)){
- res.add(w);
+ // 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 + domStyle.get(rightTab, "width") - leftTab.offsetLeft;
+ }else{
+ return 0;
}
- return res; // dijit.WidgetSet
},
- byId: function(/*String*/ id){
+ _enableBtn: function(width){
// summary:
- // Find a widget in this list by it's id.
- // example:
- // Test if an id is in a particular WidgetSet
- // | var ws = new dijit.WidgetSet();
- // | ws.add(dijit.byId("bar"));
- // | var t = ws.byId("bar") // returns a widget
- // | var x = ws.byId("foo"); // returns undefined
-
- return this._hash[id]; // dijit._Widget
+ // 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;
},
- byClass: function(/*String*/ cls){
+ resize: function(dim){
// summary:
- // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
- //
- // cls: String
- // The Class to scan for. Full dot-notated string.
- //
- // example:
- // Find all `dijit.TitlePane`s in a page:
- // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
+ // Hides or displays the buttons used to scroll the tab list and launch the menu
+ // that selects tabs.
- var res = new dijit.WidgetSet(), id, widget;
- for(id in this._hash){
- widget = this._hash[id];
- if(widget.declaredClass == cls){
- res.add(widget);
- }
- }
- return res; // dijit.WidgetSet
-},
+ // Save the dimensions to be used when a child is renamed.
+ this._dim = dim;
- toArray: function(){
- // summary:
- // Convert this WidgetSet into a true Array
- //
- // example:
- // Work with the widget .domNodes in a real Array
- // | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
+ // 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);
- var ar = [];
- for(var id in this._hash){
- ar.push(this._hash[id]);
+ // 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());
}
- return ar; // dijit._Widget[]
-},
- map: function(/* Function */func, /* Object? */thisObj){
+ // 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:
- // Create a new Array from this WidgetSet, following the same rules as `dojo.map`
- // example:
- // | var nodes = dijit.registry.map(function(w){ return w.domNode; });
- //
- // returns:
- // A new array of the returned values.
- return dojo.map(this.toArray(), func, thisObj); // Array
+ // 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;
},
- every: function(func, thisObj){
+ _convertToScrollLeft: function(val){
// summary:
- // A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
- //
- // func: Function
- // A callback function run for every widget in this list. Exits loop
- // when the first false return is encountered.
+ // 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.
//
- // thisObj: Object?
- // Optional scope parameter to use for the callback
+ // 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._Widget*/ 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();
- thisObj = thisObj || dojo.global;
- var x = 0, i;
- for(i in this._hash){
- if(!func.call(thisObj, this._hash[i], x++, this._hash)){
- return false; // Boolean
+ if(sl > node.offsetLeft ||
+ sl + domStyle.get(this.scrollNode, "width") <
+ node.offsetLeft + domStyle.get(node, "width")){
+ this.createSmoothScroll().play();
+ }
}
}
- return true; // Boolean
+
+ this.inherited(arguments);
},
- some: function(func, thisObj){
+ _getScrollBounds: function(){
// summary:
- // A synthetic clone of `dojo.some` acting explictly on this WidgetSet
- //
- // func: Function
- // A callback function run for every widget in this list. Exits loop
- // when the first true return is encountered.
- //
- // thisObj: Object?
- // Optional scope parameter to use for the callback
+ // 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();
- thisObj = thisObj || dojo.global;
- var x = 0, i;
- for(i in this._hash){
- if(func.call(thisObj, this._hash[i], x++, this._hash)){
- return true; // Boolean
- }
+ 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 + domStyle.get(children[children.length-1].domNode, "width")) - scrollNodeWidth :
+ maxPossibleScroll
+ };
+ }else{
+ // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
+ var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
+ return {
+ min: onlyScrollPosition,
+ max: onlyScrollPosition
+ };
}
- return false; // Boolean
- }
+ },
-});
+ _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();
-(function(){
+ // 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);
- /*=====
- dijit.registry = {
+ // 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:
- // A list of widgets on a page.
+ // Creates a dojo._Animation object that smoothly scrolls the tab list
+ // either to a fixed horizontal pixel value, or to the selected tab.
// description:
- // Is an instance of `dijit.WidgetSet`
- };
- =====*/
- dijit.registry = new dijit.WidgetSet();
+ // 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.
- var hash = dijit.registry._hash,
- attr = dojo.attr,
- hasAttr = dojo.hasAttr,
- style = dojo.style;
+ // 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();
+ }
- dijit.byId = function(/*String|dijit._Widget*/ id){
+ 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._Animation
+ },
+
+ _getBtnNode: function(/*Event*/ e){
// summary:
- // Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
- return typeof id == "string" ? hash[id] : id; // dijit._Widget
- };
+ // 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;
+ },
- var _widgetTypeCtr = {};
- dijit.getUniqueId = function(/*String*/widgetType){
+ doSlideRight: function(/*Event*/ e){
// summary:
- // Generates a unique id for a given widgetType
-
- var id;
- do{
- id = widgetType + "_" +
- (widgetType in _widgetTypeCtr ?
- ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
- }while(hash[id]);
- return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
- };
-
- dijit.findWidgets = function(/*DomNode*/ root){
+ // Scrolls the menu to the right.
+ // e:
+ // The mouse click event.
+ this.doSlide(1, this._getBtnNode(e));
+ },
+
+ doSlideLeft: function(/*Event*/ e){
// summary:
- // Search subtree under root returning widgets found.
- // Doesn't search for nested widgets (ie, widgets inside other widgets).
-
- var outAry = [];
-
- function getChildrenHelper(root){
- for(var node = root.firstChild; node; node = node.nextSibling){
- if(node.nodeType == 1){
- var widgetId = node.getAttribute("widgetId");
- if(widgetId){
- var widget = hash[widgetId];
- if(widget){ // may be null on page w/multiple dojo's loaded
- outAry.push(widget);
- }
- }else{
- getChildrenHelper(node);
- }
- }
- }
- }
-
- getChildrenHelper(root);
- return outAry;
- };
-
- dijit._destroyAll = function(){
+ // Scrolls the menu to the left.
+ // e:
+ // The mouse click event.
+ this.doSlide(-1,this._getBtnNode(e));
+ },
+
+ doSlide: function(/*Number*/ direction, /*DomNode*/ node){
// summary:
- // Code to destroy all widgets and do other cleanup on page unload
-
- // Clean up focus manager lingering references to widgets and nodes
- dijit._curFocus = null;
- dijit._prevFocus = null;
- dijit._activeStack = [];
-
- // Destroy all the widgets, top down
- dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
- // Avoid double destroy of widgets like Menu that are attached to <body>
- // even though they are logically children of other widgets.
- if(!widget._destroyed){
- if(widget.destroyRecursive){
- widget.destroyRecursive();
- }else if(widget.destroy){
- widget.destroy();
+ // 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; }
+});
+/*=====
+ScrollingTabControllerButtonMixin = dijit.layout._ScrollingTabControllerButtonMixin;
+=====*/
+
+// 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",
+ 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,
+ 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/place':function(){
+define("dijit/place", [
+ "dojo/_base/array", // array.forEach array.map array.some
+ "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
+ "dojo/dom-style", // domStyle.getComputedStyle
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/_base/window", // win.body
+ "dojo/window", // winUtils.getBox
+ "." // dijit (defining dijit.place to match API doc)
+], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
+
+ // module:
+ // dijit/place
+ // summary:
+ // Code to place a popup relative to another node
+
+
+ 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();
+
+ // 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().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)]
+ };
+
+ // 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 mb = domGeometry. getMarginBox(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 - mb.w,
+ 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (mb.w >> 1)) - mb.w) // M orientation is more flexible
+ }[corner.charAt(1)],
+ startYpos = {
+ 'T': pos.y,
+ 'B': pos.y - mb.h,
+ 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (mb.h >> 1)) - mb.h)
+ }[corner.charAt(0)],
+ startX = Math.max(view.l, startXpos),
+ startY = Math.max(view.t, startYpos),
+ endX = Math.min(view.l + view.w, startXpos + mb.w),
+ endY = Math.min(view.t + view.h, startYpos + mb.h),
+ width = endX - startX,
+ height = endY - startY;
+
+ overflow += (mb.w - width) + (mb.h - height);
+
+ if(best == null || overflow < best.overflow){
+ best = {
+ corner: corner,
+ aroundCorner: choice.aroundCorner,
+ x: startX,
+ y: startY,
+ w: width,
+ h: height,
+ overflow: overflow,
+ spaceAvailable: spaceAvailable
+ };
+ }
+
+ return !overflow;
});
- };
-
- if(dojo.isIE){
- // Only run _destroyAll() for IE because we think it's only necessary in that case,
- // and because it causes problems on FF. See bug #3531 for details.
- dojo.addOnWindowUnload(function(){
- dijit._destroyAll();
- });
+
+ // 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(),
+ 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;
}
-
- dijit.byNode = function(/*DOMNode*/ node){
- // summary:
- // Returns the widget corresponding to the given DOMNode
- return hash[node.getAttribute("widgetId")]; // dijit._Widget
+
+ /*=====
+ dijit.place.__Position = function(){
+ // x: Integer
+ // horizontal coordinate in pixels, relative to document body
+ // y: Integer
+ // vertical coordinate in pixels, relative to document body
+
+ this.x = x;
+ this.y = y;
};
-
- dijit.getEnclosingWidget = function(/*DOMNode*/ node){
- // summary:
- // Returns the widget whose DOM tree contains the specified DOMNode, or null if
- // the node is not contained within the DOM tree of any widget
- while(node){
- var id = node.getAttribute && node.getAttribute("widgetId");
- if(id){
- return hash[id];
- }
- node = node.parentNode;
- }
- return null;
+ =====*/
+
+ /*=====
+ dijit.place.__Rectangle = function(){
+ // 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" from backwards-compatibility.
+
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
};
+ =====*/
- var shown = (dijit._isElementShown = function(/*Element*/ elem){
- var s = style(elem);
- return (s.visibility != "hidden")
- && (s.visibility != "collapsed")
- && (s.display != "none")
- && (attr(elem, "type") != "hidden");
- });
-
- dijit.hasDefaultTabStop = function(/*Element*/ elem){
+ return (dijit.place = {
// summary:
- // Tests if element is tab-navigable even without an explicit tabIndex setting
-
- // No explicit tabIndex setting, need to investigate node type
- switch(elem.nodeName.toLowerCase()){
- case "a":
- // An <a> w/out a tabindex is only navigable if it has an href
- return hasAttr(elem, "href");
- case "area":
- case "button":
- case "input":
- case "object":
- case "select":
- case "textarea":
- // These are navigable by default
- return true;
- case "iframe":
- // If it's an editor <iframe> then it's tab navigable.
- 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;
+ // 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;
+
+ // Adjust anchor positioning for the case that a parent node has overflw hidden, therefore cuasing the anchor not to be completely visible
+ if(anchor.parentNode){
+ var parent = anchor.parentNode;
+ while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
+ var parentPos = domGeometry.position(parent, true);
+ var parentStyleOverflow = domStyle.getComputedStyle(parent).overflow;
+ if(parentStyleOverflow == "hidden" || parentStyleOverflow == "auto" || parentStyleOverflow == "scroll"){
+ 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;
+ }
+ 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);
}
- return 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(attr(elem, "disabled")){
- return false;
- }else if(hasAttr(elem, "tabIndex")){
- // Explicit tab index setting
- return attr(elem, "tabIndex") >= 0; // boolean
- }else{
- // No explicit tabIndex setting, so depends on node type
- return dijit.hasDefaultTabStop(elem);
+ });
+
+ var position = _place(node, choices, layoutNode, {w: width, h: height});
+ position.aroundNodePos = aroundNodePos;
+
+ return position;
}
});
+});
- dijit._getTabNavigable = function(/*DOMNode*/ root){
+},
+'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",
+ "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
+ "dojo/_base/lang", // lang.hitch lang.isFunction
+ "dojo/touch",
+ "dojo/_base/window", // win.doc
+ "dojo/window", // winUtils.getBox
+ "./registry", // registry.byNode()
+ "./focus",
+ "./popup",
+ "./_FocusMixin"
+], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, touch,
+ win, winUtils, registry, focus, popup, _FocusMixin){
+
+/*=====
+ var _FocusMixin = dijit._FocusMixin;
+=====*/
+
+ // module:
+ // dijit/_HasDropDown
+ // summary:
+ // Mixin for widgets that need drop down ability.
+
+ return declare("dijit._HasDropDown", _FocusMixin, {
// summary:
- // Finds descendants of the specified root node.
+ // 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:
//
- // 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){
- dojo.query("> *", parent).forEach(function(child){
- // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
- // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
- if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){
- return;
- }
+ // * 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"],
- if(isTabNavigable(child)){
- var tabindex = attr(child, "tabIndex");
- if(!hasAttr(child, "tabIndex") || tabindex == 0){
- if(!first){ first = child; }
- last = child;
- }else if(tabindex > 0){
- if(!lowest || tabindex < lowestTabindex){
- lowestTabindex = tabindex;
- lowest = child;
- }
- if(!highest || tabindex >= highestTabindex){
- highestTabindex = tabindex;
- highest = child;
+ // _stopClickEvents: Boolean
+ // When set to false, the click events will not be stopped, in
+ // case you want to use them in your subwidget
+ _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 propogation, so that:
+ // 1. TimeTextBox etc. can focusthe <input> on mousedown
+ // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
+ // 3. user defined onMouseDown handler fires
+ e.preventDefault();
+
+ this._docHandler = this.connect(win.doc, touch.release, "_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;
}
}
- var rn = radioName(child);
- if(dojo.attr(child, "checked") && rn) {
- radioSelected[rn] = child;
+ 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(child.nodeName.toUpperCase() != 'SELECT'){
- walkTree(child);
+ }
+ if(this._opened){
+ if(dropDown.focus && dropDown.autoFocus !== false){
+ // Focus the dropdown widget - do it on a delay so that we
+ // don't steal our own focus.
+ window.setTimeout(lang.hitch(dropDown, "focus"), 1);
}
- });
- };
- 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){
- // summary:
- // Finds the descendant of the specified root node
- // that is first in the tabbing order
- var elems = dijit._getTabNavigable(dojo.byId(root));
- return elems.lowest ? elems.lowest : elems.first; // DomNode
- };
-
- dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
- // summary:
- // Finds the descendant of the specified root node
- // that is last in the tabbing order
- var elems = dijit._getTabNavigable(dojo.byId(root));
- return elems.last ? elems.last : elems.highest; // DomNode
- };
-
- /*=====
- dojo.mixin(dijit, {
- // defaultDuration: Integer
- // The default animation speed (in ms) to use for all Dijit
- // transitional animations, unless otherwise specified
- // on a per-instance basis. Defaults to 200, overrided by
- // `djConfig.defaultDuration`
- defaultDuration: 200
- });
- =====*/
-
- dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
+ }else{
+ // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
+ // setTimeout() needed to make it work on IE (test DateTextBox)
+ setTimeout(lang.hitch(this, "focus"), 0);
+ }
-})();
+ if(has("ios")){
+ this._justGotMouseUp = true;
+ setTimeout(lang.hitch(this, function(){
+ this._justGotMouseUp = false;
+ }), 0);
+ }
+ },
-}
+ _onDropDownClick: function(/*Event*/ e){
+ if(has("ios") && !this._justGotMouseUp){
+ // This branch fires on iPhone for ComboBox, because the button node is an <input> and doesn't
+ // generate touchstart/touchend events. Pretend we just got a mouse down / mouse up.
+ // The if(has("ios") 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);
+ }
-if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.focus"] = true;
-dojo.provide("dijit._base.focus");
+ // 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");
+ },
-// summary:
-// These functions are used to query or set the focus and selection.
-//
-// Also, they trace when widgets become activated/deactivated,
-// so that the widget can fire _onFocus/_onBlur events.
-// "Active" here means something similar to "focused", but
-// "focus" isn't quite the right word because we keep track of
-// a whole stack of "active" widgets. Example: ComboButton --> Menu -->
-// MenuItem. The onBlur event for ComboButton doesn't fire due to focusing
-// on the Menu or a MenuItem, since they are considered part of the
-// ComboButton widget. It only happens when focus is shifted
-// somewhere completely different.
-
-dojo.mixin(dijit, {
- // _curFocus: DomNode
- // Currently focused item on screen
- _curFocus: null,
-
- // _prevFocus: DomNode
- // Previously focused item on screen
- _prevFocus: null,
-
- isCollapsed: function(){
- // summary:
- // Returns true if there is no text selected
- return dijit.getBookmark().isCollapsed;
- },
-
- getBookmark: function(){
- // summary:
- // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
- var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus;
-
- if(dojo.global.getSelection){
- //W3C Range API for selections.
- sel = dojo.global.getSelection();
- if(sel){
- if(sel.isCollapsed){
- tg = cf? cf.tagName : "";
- if(tg){
- //Create a fake rangelike item to restore selections.
- tg = tg.toLowerCase();
- if(tg == "textarea" ||
- (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
- sel = {
- start: cf.selectionStart,
- end: cf.selectionEnd,
- node: cf,
- pRange: true
- };
- return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
- }
- }
- bm = {isCollapsed:true};
- 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
- }
- };
+ postCreate: function(){
+ // summary:
+ // set up nodes and connect our mouse and keypress events
+
+ this.inherited(arguments);
+
+ this.connect(this._buttonNode, touch.press, "_onDropDownMouseDown");
+ this.connect(this._buttonNode, "onclick", "_onDropDownClick");
+ this.connect(this.focusNode, "onkeypress", "_onKey");
+ this.connect(this.focusNode, "onkeyup", "_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;
}
- bm = {};
+ this.inherited(arguments);
+ },
- //'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;
+ _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.charOrCode == keys.ESCAPE){
+ this.closeDropDown();
+ event.stop(e);
+ }else if(!this._opened &&
+ (e.charOrCode == keys.DOWN_ARROW ||
+ ( (e.charOrCode == keys.ENTER || e.charOrCode == " ") &&
+ //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){
+ setTimeout(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{
- bm.mark = rg.getBookmark();
+ afterLoad();
}
- }else{
- console.warn("No idea how to store the current selection for this browser!");
- }
- return bm; // Object
- },
+ return d;
+ },
- moveToBookmark: function(/*Object*/bookmark){
- // summary:
- // Moves current selection to a bookmark
- // bookmark:
- // This should be a returned object from dijit.getBookmark()
+ 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
- var _doc = dojo.doc,
- mark = bookmark.mark;
- if(mark){
- if(dojo.global.getSelection){
- //W3C Rangi API (FF, WebKit, Opera, etc)
- var sel = dojo.global.getSelection();
- if(sel && sel.removeAllRanges){
- if(mark.pRange){
- var r = mark;
- var n = r.node;
- n.selectionStart = r.start;
- n.selectionEnd = r.end;
- }else{
- sel.removeAllRanges();
- sel.addRange(mark);
+ 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(),
+ 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
+ var mb = domGeometry.getMarginSize(ddNode);
+ var overHeight = (maxHeight && mb.h > maxHeight);
+ domStyle.set(ddNode, {
+ overflowX: "hidden",
+ overflowY: overHeight ? "auto" : "hidden"
+ });
+ if(overHeight){
+ mb.h = maxHeight;
+ if("w" in mb){
+ mb.w += 16; // room for vertical scrollbar
}
}else{
- console.warn("No idea how to restore selection for this browser!");
- }
- }else if(_doc.selection && mark){
- //'IE' way.
- var rg;
- if(mark.pRange){
- rg = mark.range;
- }else if(dojo.isArray(mark)){
- rg = _doc.body.createControlRange();
- //rg.addElement does not have call/apply method, so can not call it directly
- //rg is not available in "range.addElement(item)", so can't use that either
- dojo.forEach(mark, function(n){
- rg.addElement(n);
- });
+ 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{
- rg = _doc.body.createTextRange();
- rg.moveToBookmark(mark);
+ delete mb.w;
}
- rg.select();
+
+ // 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._opened = false;
+ }
+ });
+ domAttr.set(this._popupStateNode, "popupActive", "true");
+ domClass.add(self._popupStateNode, "dijitHasDropDownOpen");
+ this._opened=true;
+
+ // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
+ 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._opened){
+ if(focus){ this.focus(); }
+ popup.close(this.dropDown);
+ this._opened = false;
}
}
- },
- getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
+ });
+});
+
+},
+'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/json", // json.stringify
+ "dojo/_base/lang" // lang.hitch
+], function(array, aspect, declare, json, lang){
+
+ // module:
+ // dijit/tree/TreeStoreModel
+ // summary:
+ // Implements dijit.Tree.model connecting to a dojo.data store with a single
+ // root item.
+
+ return declare("dijit.tree.TreeStoreModel", null, {
// summary:
- // Called as getFocus(), this returns an Object showing the current focus
- // and selected text.
- //
- // Called as getFocus(widget), where widget is a (widget representing) a button
- // that was just pressed, it returns where focus was before that button
- // was pressed. (Pressing the button may have either shifted focus to the button,
- // or removed focus altogether.) In this case the selected text is not returned,
- // since it can't be accurately determined.
- //
- // menu: dijit._Widget or {domNode: DomNode} structure
- // The button that was just pressed. If focus has disappeared or moved
- // to this button, returns the previous focus. In this case the bookmark
- // information is already lost, and null is returned.
+ // 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.Store
+ // Underlying store
+ store: null,
+
+ // childrenAttrs: String[]
+ // One or more attribute names (attributes in the dojo.data item) that specify that item's children
+ childrenAttrs: ["children"],
+
+ // newItemIdAttr: String
+ // Name of attribute in the Object passed to newItem() that specifies the id.
//
- // openedForWindow:
- // iframe in which menu was opened
+ // 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).
//
- // returns:
- // A handle to restore focus/selection, to be passed to `dijit.focus`
- var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus;
- return {
- node: node,
- bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark),
- openedForWindow: openedForWindow
- }; // Object
- },
+ // Setting this to null or "" will make every drop create a new item.
+ newItemIdAttr: "id",
- 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
+ // labelAttr: String
+ // If specified, get label for tree node from this attribute, rather
+ // than by calling store.getLabel()
+ labelAttr: "",
- if(!handle){ return; }
+ // root: [readonly] dojo.data.Item
+ // Pointer to the root item (read only, not a parameter)
+ root: null,
- 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;
+ // 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,
- // 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*/}
+ // 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: store must support dojo.data.Identity");
}
- dijit._onFocusNode(node);
- }
- // set the selection
- // do not need to restore if current selection is not empty
- // (use keyboard to select a menu item) or if previous selection was collapsed
- // as it may cause focus shift (Esp in IE).
- if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){
- if(openedForWindow){
- openedForWindow.focus();
+ // 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)
+ ]);
}
- try{
- dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]);
- }catch(e2){
- /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
+ },
+
+ 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(this.declaredClass + ": query " + json.stringify(this.query) + " returned " + items.length +
+ " items, but must return exactly one item");
+ }
+ this.root = items[0];
+ onItem(this.root);
+ }),
+ onError: onError
+ });
}
- }
- },
+ },
- // _activeStack: dijit._Widget[]
- // List of currently active widgets (focused widget and it's ancestors)
- _activeStack: [],
+ 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);
+ },
- registerIframe: function(/*DomNode*/ iframe){
- // summary:
- // Registers listeners on the specified iframe so that any click
- // or focus event on that iframe (or anything in it) is reported
- // as a focus/click event on the <iframe> itself.
- // description:
- // Currently only used by editor.
- // returns:
- // Handle to pass to unregisterIframe()
- return dijit.registerWin(iframe.contentWindow, iframe);
- },
+ 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.
- 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()
+ 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);
+ }
- dijit.unregisterWin(handle);
- },
+ // count how many items need to be loaded
+ var _waitCount = 0;
+ if(!this.deferItemLoadingUntilExpand){
+ array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
+ }
- 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()
+ 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
+ });
+ }
+ });
+ }
+ },
- // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
+ // =======================================================================
+ // Inspecting items
- var mousedownListener = function(evt){
- dijit._justMouseDowned = true;
- setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
-
- // workaround weird IE bug where the click is on an orphaned node
- // (first time clicking a Select/DropDownButton inside a TooltipDialog)
- if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){
- return;
+ 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
}
+ },
- dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
- };
- //dojo.connect(targetWindow, "onscroll", ???);
-
- // Listen for blur and focus events on targetWindow's document.
- // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
- // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
- // fire.
- // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
- // (at least for FF) the focus event doesn't fire on <html> or <body>.
- var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document;
- if(doc){
- if(dojo.isIE){
- targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
- var activateListener = function(evt){
- // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
- // Should consider those more like a mouse-click than a focus....
- if(evt.srcElement.tagName.toLowerCase() != "#document" &&
- dijit.isTabNavigable(evt.srcElement)){
- dijit._onFocusNode(effectiveNode || evt.srcElement);
+ // =======================================================================
+ // Write interface
+
+ newItem: function(/* dojo.dnd.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.
+ // 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{
- dijit._onTouchNode(effectiveNode || evt.srcElement);
+ // 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);
+ }
}
- };
- doc.attachEvent('onactivate', activateListener);
- var deactivateListener = function(evt){
- dijit._onBlurNode(effectiveNode || evt.srcElement);
- };
- doc.attachEvent('ondeactivate', deactivateListener);
+ }});
+ }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);
+ }
+ }
+ },
- return function(){
- targetWindow.document.detachEvent('onmousedown', mousedownListener);
- doc.detachEvent('onactivate', activateListener);
- doc.detachEvent('ondeactivate', deactivateListener);
- doc = null; // prevent memory leak (apparent circular reference via closure)
- };
+ 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{
- doc.body.addEventListener('mousedown', mousedownListener, true);
- var focusListener = function(evt){
- dijit._onFocusNode(effectiveNode || evt.target);
- };
- doc.addEventListener('focus', focusListener, true);
- var blurListener = function(evt){
- dijit._onBlurNode(effectiveNode || evt.target);
- };
- doc.addEventListener('blur', blurListener, true);
+ // item's label/icon/etc. changed.
+ this.onChange(item);
+ }
+ }
+ });
+});
- return function(){
- doc.body.removeEventListener('mousedown', mousedownListener, true);
- doc.removeEventListener('focus', focusListener, true);
- doc.removeEventListener('blur', blurListener, true);
- doc = null; // prevent memory leak (apparent circular reference via closure)
- };
+},
+'dijit/_MenuBase':function(){
+define("dijit/_MenuBase", [
+ "./popup",
+ "dojo/window",
+ "./_Widget",
+ "./_KeyNavContainer",
+ "./_TemplatedMixin",
+ "dojo/_base/declare", // declare
+ "dojo/dom", // dom.isDescendant domClass.replace
+ "dojo/dom-attr",
+ "dojo/dom-class", // domClass.replace
+ "dojo/_base/lang", // lang.hitch
+ "dojo/_base/array" // array.indexOf
+], function(pm, winUtils, _Widget, _KeyNavContainer, _TemplatedMixin,
+ declare, dom, domAttr, domClass, lang, array){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _KeyNavContainer = dijit._KeyNavContainer;
+=====*/
+
+// module:
+// dijit/_MenuBase
+// summary:
+// Base class for Menu and MenuBar
+
+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,
+
+ 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.focusedChild._onClick(evt);
+ }else{
+ var topMenu = this._getTopMenu();
+ if(topMenu && topMenu._isMenuBar){
+ topMenu.focusNext();
}
}
},
- unregisterWin: function(/*Handle*/ handle){
+ _onPopupHover: function(/*Event*/ /*===== evt =====*/){
// 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.
+ // This handler is called when the mouse moves over the popup.
+ // tags:
+ // private
- // Currently our handle is actually a function
- handle && handle();
+ // 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);
+ }
},
- _onBlurNode: function(/*DomNode*/ node){
+ onItemHover: function(/*MenuItem*/ item){
// summary:
- // Called when focus leaves a node.
- // Usually ignored, _unless_ it *isn't* follwed by touching another node,
- // which indicates that we tabbed off the last field on the page,
- // in which case every widget is marked inactive
- dijit._prevFocus = dijit._curFocus;
- dijit._curFocus = null;
+ // Called when cursor is over a MenuItem.
+ // tags:
+ // protected
- if(dijit._justMouseDowned){
- // the mouse down caused a new widget to be marked as active; this blur event
- // is coming late, so ignore it.
- return;
+ // 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 = setTimeout(lang.hitch(this, "_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;
+ },
- // if the blur event isn't followed by a focus event then mark all widgets as inactive.
- if(dijit._clearActiveWidgetsTimer){
- clearTimeout(dijit._clearActiveWidgetsTimer);
+ _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 = setTimeout(function(){
+ itemPopup._pendingClose_timer = null;
+ if(itemPopup.parentMenu){
+ itemPopup.parentMenu.currentPopup = null;
+ }
+ pm.close(itemPopup); // this calls onClose
+ }, this.popupDelay);
}
- dijit._clearActiveWidgetsTimer = setTimeout(function(){
- delete dijit._clearActiveWidgetsTimer;
- dijit._setStack([]);
- dijit._prevFocus = null;
- }, 100);
},
- _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
+ onItemUnhover: function(/*MenuItem*/ item){
// 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
+ // Callback fires when mouse exits a MenuItem
+ // tags:
+ // protected
- // ignore the recent blurNode event
- if(dijit._clearActiveWidgetsTimer){
- clearTimeout(dijit._clearActiveWidgetsTimer);
- delete dijit._clearActiveWidgetsTimer;
+ if(this.isActive){
+ this._stopPopupTimer();
}
+ if(this._hoveredChild == item){ this._hoveredChild = null; }
+ },
- // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
- var newStack=[];
- try{
- while(node){
- var popupParent = dojo.attr(node, "dijitPopupParent");
- if(popupParent){
- node=dijit.byId(popupParent).domNode;
- }else if(node.tagName && node.tagName.toLowerCase() == "body"){
- // is this the root of the document or just the root of an iframe?
- if(node === dojo.body()){
- // node is the root of the main document
- break;
- }
- // otherwise, find the iframe this node refers to (can't access it via parentNode,
- // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
- node=dojo.window.get(node.ownerDocument).frameElement;
- }else{
- // if this node is the root node of a widget, then add widget id to stack,
- // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
- // to support MenuItem)
- var id = node.getAttribute && node.getAttribute("widgetId"),
- widget = id && dijit.byId(id);
- if(widget && !(by == "mouse" && widget.get("disabled"))){
- newStack.unshift(id);
- }
- node=node.parentNode;
- }
- }
- }catch(e){ /* squelch */ }
+ _stopPopupTimer: function(){
+ // summary:
+ // Cancels the popup timer because the user has stop hovering
+ // on the MenuItem, etc.
+ // tags:
+ // private
+ if(this.hover_timer){
+ clearTimeout(this.hover_timer);
+ this.hover_timer = null;
+ }
+ },
+
+ _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
+ // summary:
+ // Cancels the pending-close timer because the close has been preempted
+ // tags:
+ // private
+ if(popup._pendingClose_timer){
+ clearTimeout(popup._pendingClose_timer);
+ popup._pendingClose_timer = null;
+ }
+ },
- dijit._setStack(newStack, by);
+ _stopFocusTimer: function(){
+ // summary:
+ // Cancels the pending-focus timer because the menu was closed before focus occured
+ // tags:
+ // private
+ if(this._focus_timer){
+ clearTimeout(this._focus_timer);
+ this._focus_timer = null;
+ }
+ },
+
+ _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;
},
- _onFocusNode: function(/*DomNode*/ node){
+ onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
// summary:
- // Callback when node is focused
+ // Handle clicks on an item.
+ // tags:
+ // private
- if(!node){
- return;
+ // this can't be done in _onFocus since the _onFocus events occurs asynchronously
+ if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
+ this._markActive();
}
- 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;
+ this.focusChild(item);
+
+ if(item.disabled){ return false; }
+
+ if(item.popup){
+ this._openPopup();
+ }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(evt);
}
+ },
- dijit._onTouchNode(node);
+ _openPopup: function(){
+ // summary:
+ // Open the popup to the side of/underneath the current menu item
+ // tags:
+ // protected
- if(node == dijit._curFocus){ return; }
- if(dijit._curFocus){
- dijit._prevFocus = dijit._curFocus;
+ 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){ return; }
+ 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(popup.focus){
+ // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
+ // if the cursor happens to collide with the popup, it will generate an onmouseover event
+ // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
+ // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
+ popup._focus_timer = setTimeout(lang.hitch(popup, function(){
+ this._focus_timer = null;
+ this.focus();
+ }), 0);
}
- dijit._curFocus = node;
- dojo.publish("focusNode", [node]);
},
- _setStack: function(/*String[]*/ newStack, /*String*/ by){
+ _markActive: function(){
// 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
+ // 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");
+ },
- var oldStack = dijit._activeStack;
- dijit._activeStack = newStack;
+ 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
- // 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;
+ 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;
}
- var widget;
- // for all elements that have gone out of focus, send blur event
- for(var i=oldStack.length-1; i>=nCommon; i--){
- widget = dijit.byId(oldStack[i]);
- if(widget){
- widget._focused = false;
- widget.set("focused", false);
- widget._hasBeenBlurred = true;
- if(widget._onBlur){
- widget._onBlur(by);
- }
- dojo.publish("widgetBlur", [widget, by]);
- }
+ if(this.focusedChild){ // unhighlight the focused item
+ this.focusedChild._setSelected(false);
+ this.focusedChild._onUnhover();
+ this.focusedChild = null;
}
+ },
- // for all element that have come into focus, send focus event
- for(i=nCommon; i<newStack.length; i++){
- widget = dijit.byId(newStack[i]);
- if(widget){
- widget._focused = true;
- widget.set("focused", true);
- if(widget._onFocus){
- widget._onFocus(by);
- }
- dojo.publish("widgetFocus", [widget, by]);
- }
+ _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._hoveredChild._onUnhover(); // 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();
}
}
});
-// register top window and all the iframes it contains
-dojo.addOnLoad(function(){
- var handle = dijit.registerWin(window);
- if(dojo.isIE){
- dojo.addOnWindowUnload(function(){
- dijit.unregisterWin(handle);
- handle = null;
- })
- }
});
-}
+},
+'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/_base/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
+ "." // 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
+ // summary:
+ // Returns a singleton that tracks the currently focused node, and which widgets are currently "active".
-if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.AdapterRegistry"] = true;
-dojo.provide("dojo.AdapterRegistry");
+/*=====
+ dijit.focus = {
+ // 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,
-dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
- // summary:
- // A registry to make contextual calling/searching easier.
- // description:
- // Objects of this class keep list of arrays in the form [name, check,
- // wrap, directReturn] that are used to determine what the contextual
- // result of a set of checked arguments is. All check/wrap functions
- // in this registry should be of the same arity.
- // example:
- // | // create a new registry
- // | var reg = new dojo.AdapterRegistry();
- // | reg.register("handleString",
- // | dojo.isString,
- // | function(str){
- // | // do something with the string here
- // | }
- // | );
- // | reg.register("handleArr",
- // | dojo.isArray,
- // | function(arr){
- // | // do something with the array here
- // | }
- // | );
- // |
- // | // now we can pass reg.match() *either* an array or a string and
- // | // the value we pass will get handled by the right function
- // | reg.match("someValue"); // will call the first function
- // | reg.match(["someValue"]); // will call the second
+ // activeStack: dijit._Widget[]
+ // List of currently active widgets (focused widget and it's ancestors)
+ activeStack: [],
- this.pairs = [];
- this.returnWrappers = returnWrappers || false; // Boolean
-};
+ registerIframe: function(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.
+ },
-dojo.extend(dojo.AdapterRegistry, {
- register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
- // summary:
- // register a check function to determine if the wrap function or
- // object gets selected
- // name:
- // a way to identify this matcher.
- // check:
- // a function that arguments are passed to from the adapter's
- // match() function. The check function should return true if the
- // given arguments are appropriate for the wrap function.
- // directReturn:
- // If directReturn is true, the value passed in for wrap will be
- // returned instead of being called. Alternately, the
- // AdapterRegistry can be set globally to "return not call" using
- // the returnWrappers property. Either way, this behavior allows
- // the registry to act as a "search" function instead of a
- // function interception library.
- // override:
- // If override is given and true, the check function will be given
- // highest priority. Otherwise, it will be the lowest priority
- // adapter.
- this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
- },
-
- match: function(/* ... */){
- // summary:
- // Find an adapter for the given arguments. If no suitable adapter
- // is found, throws an exception. match() accepts any number of
- // arguments, all of which are passed to all matching functions
- // from the registered pairs.
- for(var i = 0; i < this.pairs.length; i++){
- var pair = this.pairs[i];
- if(pair[1].apply(this, arguments)){
- if((pair[3])||(this.returnWrappers)){
- return pair[2];
+ registerWin: function(targetWindow, 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: Window?
+ // If specified this is the window associated with the iframe,
+ // i.e. iframe.contentWindow.
+ // effectiveNode: DOMNode?
+ // 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.
+ }
+ };
+=====*/
+
+ var FocusManager = declare([Stateful, Evented], {
+ // curNode: DomNode
+ // Currently focused item on screen
+ curNode: null,
+
+ // activeStack: dijit._Widget[]
+ // 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.
+ // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
+ // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
+ // fire.
+ // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
+ // (at least for FF) the focus event doesn't fire on <html> or <body>.
+ var doc = has("ie") ? targetWindow.document.documentElement : targetWindow.document;
+ if(doc){
+ if(has("ie")){
+ targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
+ var activateListener = 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('onactivate', activateListener);
+ var deactivateListener = function(evt){
+ _this._onBlurNode(effectiveNode || evt.srcElement);
+ };
+ doc.attachEvent('ondeactivate', deactivateListener);
+
+ return {
+ remove: function(){
+ targetWindow.document.detachEvent('onmousedown', mousedownListener);
+ doc.detachEvent('onactivate', activateListener);
+ doc.detachEvent('ondeactivate', deactivateListener);
+ doc = null; // prevent memory leak (apparent circular reference via closure)
+ }
+ };
}else{
- return pair[2].apply(this, arguments);
+ 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)
+ }
+ };
}
}
- }
- throw new Error("No match found");
- },
+ },
- unregister: function(name){
- // summary: Remove a named adapter from the registry
+ _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
+ this.set("prevNode", this.curNode);
+ this.set("curNode", null);
+
+ 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;
+ }
- // FIXME: this is kind of a dumb way to handle this. On a large
- // registry this will be slow-ish and we can use the name as a lookup
- // should we choose to trade memory for speed.
- for(var i = 0; i < this.pairs.length; i++){
- var pair = this.pairs[i];
- if(pair[0] == name){
- this.pairs.splice(i, 1);
- return true;
+ // if the blur event isn't followed by a focus event then mark all widgets as inactive.
+ if(this._clearActiveWidgetsTimer){
+ clearTimeout(this._clearActiveWidgetsTimer);
}
- }
- return false;
- }
-});
+ this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){
+ delete this._clearActiveWidgetsTimer;
+ this._setStack([]);
+ this.prevNode = null;
+ }), 100);
+ },
-}
+ _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
+ // summary:
+ // Callback when node is focused or mouse-downed
+ // node:
+ // The node that was touched.
+ // by:
+ // "mouse" if the focus/touch was caused by a mouse down event
-if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.place"] = true;
-dojo.provide("dijit._base.place");
+ // 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
-dijit.getViewport = function(){
- // summary:
- // Returns the dimensions and scroll position of the viewable area of a browser window
+ if(!node){
+ return;
+ }
- return dojo.window.getBox();
-};
+ if(node.nodeType == 9){
+ // Ignore focus events on the document itself. This is here so that
+ // (for example) clicking the up/down arrows of a spinner
+ // (which don't get focus) won't cause that widget to blur. (FF issue)
+ return;
+ }
-/*=====
-dijit.__Position = function(){
- // x: Integer
- // horizontal coordinate in pixels, relative to document body
- // y: Integer
- // vertical coordinate in pixels, relative to document body
-
- thix.x = x;
- this.y = y;
-}
-=====*/
+ this._onTouchNode(node);
+ if(node == this.curNode){ return; }
+ this.set("curNode", node);
+ },
-dijit.placeOnScreen = function(
- /* DomNode */ node,
- /* dijit.__Position */ pos,
- /* String[] */ corners,
- /* dijit.__Position? */ padding){
- // summary:
- // Positions one of the node's corners at specified position
- // such that node is fully visible in viewport.
- // description:
- // NOTE: node is assumed to be absolutely or relatively positioned.
- // pos:
- // Object like {x: 10, y: 20}
- // corners:
- // Array of Strings representing order to try corners in, like ["TR", "BL"].
- // Possible values are:
- // * "BL" - bottom left
- // * "BR" - bottom right
- // * "TL" - top left
- // * "TR" - top right
- // padding:
- // set padding to put some buffer around the element you want to position.
- // example:
- // Try to place node's top right corner at (10,20).
- // If that makes node go (partially) off screen, then try placing
- // bottom left corner at (10,20).
- // | placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
-
- var choices = dojo.map(corners, function(corner){
- var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
- if(padding){
- c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
- c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
- }
- return c;
- });
+ _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;
+ }
+ }
- return dijit._place(node, choices);
-}
+ 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);
+ }
+ }
-dijit._place = function(/*DomNode*/ node, choices, layoutNode, /*Object*/ 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 = dojo.window.getBox();
-
- // This won't work if the node is inside a <div style="position: relative">,
- // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong
- // and also it might get cutoff)
- if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
- dojo.body().appendChild(node);
- }
+ // 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);
+ }
+ }
+ },
- var best = null;
- dojo.some(choices, function(choice){
- var corner = choice.corner;
- var pos = choice.pos;
- var overflow = 0;
+ focus: function(node){
+ // summary:
+ // Focus the specified node, suppressing errors if they occur
+ if(node){
+ try{ node.focus(); }catch(e){/*quiet*/}
+ }
+ }
+ });
- // calculate amount of space available given specified position of node
- var spaceAvailable = {
- w: corner.charAt(1) == 'L' ? (view.l + view.w) - pos.x : pos.x - view.l,
- h: corner.charAt(1) == 'T' ? (view.t + view.h) - pos.y : pos.y - view.t
- };
+ var singleton = new FocusManager();
- // 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;
- style.visibility = "hidden";
- style.display = "";
- var mb = dojo.marginBox(node);
- style.display = oldDisplay;
- style.visibility = oldVis;
-
- // coordinates and size of node with specified corner placed at pos,
- // and clipped by viewport
- var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)),
- startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)),
- endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x),
- endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y),
- width = endX - startX,
- height = endY - startY;
-
- overflow += (mb.w - width) + (mb.h - height);
-
- if(best == null || overflow < best.overflow){
- best = {
- corner: corner,
- aroundCorner: choice.aroundCorner,
- x: startX,
- y: startY,
- w: width,
- h: height,
- overflow: overflow,
- spaceAvailable: spaceAvailable
- };
+ // register top window and all the iframes it contains
+ ready(function(){
+ var handle = singleton.registerWin(win.doc.parentWindow || win.doc.defaultView);
+ if(has("ie")){
+ unload.addOnWindowUnload(function(){
+ handle.remove();
+ handle = null;
+ })
}
-
- 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);
+ // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
+ // as a function to set focus.
+ 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;
+ });
- // 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 = dojo._isBodyLtr(),
- s = node.style;
- s.top = best.y + "px";
- s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
-
- return best;
-}
-
-dijit.placeOnScreenAroundNode = function(
- /* DomNode */ node,
- /* DomNode */ aroundNode,
- /* Object */ aroundCorners,
- /* Function? */ layoutNode){
+ return singleton;
+});
+},
+'dojo/i18n':function(){
+define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json"],
+ function(dojo, require, has, array, config, lang, xhr, json) {
+ // module:
+ // dojo/i18n
// summary:
- // Position node adjacent or kitty-corner to aroundNode
- // such that it's fully visible in viewport.
- //
+ // This module implements the !dojo/i18n plugin and the v1.6- i18n API
// description:
- // Place node such that corner of node touches a corner of
- // aroundNode, and that node is fully visible.
- //
- // aroundCorners:
- // Ordered list of pairs of corners to try matching up.
- // Each pair of corners is represented as a key/value in the hash,
- // where the key corresponds to the aroundNode's corner, and
- // the value corresponds to the node's corner:
- //
- // | { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...}
- //
- // The following strings are used to represent the four corners:
- // * "BL" - bottom left
- // * "BR" - bottom right
- // * "TL" - top left
- // * "TR" - top right
- //
- // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
- // For things like tooltip, they are displayed differently (and have different dimensions)
- // based on their orientation relative to the parent. This adjusts the popup based on orientation.
- //
- // example:
- // | dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
- // This will try to position node such that node's top-left corner is at the same position
- // as the bottom left corner of the aroundNode (ie, put node below
- // aroundNode, with left edges aligned). If that fails it will try to put
- // the bottom-right corner of node where the top right corner of aroundNode is
- // (ie, put node above aroundNode, with right edges aligned)
- //
+ // 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.
- // get coordinates of aroundNode
- aroundNode = dojo.byId(aroundNode);
- var aroundNodePos = dojo.position(aroundNode, true);
- // place the node around the calculated rectangle
- return dijit._placeOnScreenAroundRect(node,
- aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h, // rectangle
- aroundCorners, layoutNode);
-};
+ has.add("dojo-preload-i18n-Api",
+ // if true, define the preload localizations machinery
+ 1
+ );
-/*=====
-dijit.__Rectangle = function(){
- // x: Integer
- // horizontal offset in pixels, relative to document body
- // y: Integer
- // vertical offset in pixels, relative to document body
- // width: Integer
- // width in pixels
- // height: Integer
- // height in pixels
-
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
-}
-=====*/
+ true || has.add("dojo-v1x-i18n-Api",
+ // if true, define the v1.x i18n functions
+ 1
+ );
+ var
+ thisModule= dojo.i18n=
+ // the dojo.i18n module
+ {},
+
+ 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
+ ){
+ // 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
+ //
-dijit.placeOnScreenAroundRectangle = function(
- /* DomNode */ node,
- /* dijit.__Rectangle */ aroundRect,
- /* Object */ aroundCorners,
- /* Function */ layoutNode){
+ 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;
+ },
- // 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.
+ cache= {},
- return dijit._placeOnScreenAroundRect(node,
- aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height, // rectangle
- aroundCorners, layoutNode);
-};
+ getL10nName= dojo.getL10nName = function(moduleName, bundleName, locale){
+ locale = locale ? locale.toLowerCase() : dojo.locale;
+ moduleName = "dojo/i18n!" + moduleName.replace(/\./g, "/");
+ bundleName = bundleName.replace(/\./g, "/");
+ return (/root/i.test(locale)) ?
+ (moduleName + "/nls/" + bundleName) :
+ (moduleName + "/nls/" + locale + "/" + bundleName);
+ },
-dijit._placeOnScreenAroundRect = function(
- /* DomNode */ node,
- /* Number */ x,
- /* Number */ y,
- /* Number */ width,
- /* Number */ height,
- /* Object */ aroundCorners,
- /* Function */ layoutNode){
+ doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){
+ // 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();
+ });
+ });
+ },
- // summary:
- // Like dijit.placeOnScreenAroundNode(), except it accepts coordinates
- // of a rectangle to place node adjacent to.
+ normalize = function(id, toAbsMid){
+ // 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;
+ },
- // TODO: combine with placeOnScreenAroundRectangle()
+ getLocalesToLoad = function(targetLocale){
+ var list = config.extraLocale || [];
+ list = lang.isArray(list) ? list : [list];
+ list.push(targetLocale);
+ return list;
+ },
- // Generate list of possible positions for node
- var choices = [];
- for(var nodeCorner in aroundCorners){
- choices.push( {
- aroundCorner: nodeCorner,
- corner: aroundCorners[nodeCorner],
- pos: {
- x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width),
- y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height)
+ load = function(id, require, load){
+ //
+ // 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 absolue; 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 folllows:
+ //
+ // 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);
+ }
+ // 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 = [];
}
- return dijit._place(node, choices, layoutNode, {w: width, h: height});
-};
+ 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;
+ },
-dijit.placementRegistry= new dojo.AdapterRegistry();
-dijit.placementRegistry.register("node",
- function(n, x){
- return typeof x == "object" &&
- typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined";
- },
- dijit.placeOnScreenAroundNode);
-dijit.placementRegistry.register("rect",
- function(n, x){
- return typeof x == "object" &&
- "x" in x && "y" in x && "width" in x && "height" in x;
- },
- dijit.placeOnScreenAroundRectangle);
+ isXd = function(mid){
+ return (1 && 1) ?
+ require.isXdUrl(require.toUrl(mid + ".js")) :
+ true;
+ },
-dijit.placeOnScreenAroundElement = function(
- /* DomNode */ node,
- /* Object */ aroundElement,
- /* Object */ aroundCorners,
- /* Function */ layoutNode){
+ preloading = 0,
- // summary:
- // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
- // for the "around" argument and finds a proper processor to place a node.
+ preloadWaitQueue = [],
- return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments);
-};
+ preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean*/ guaranteedAmdFormat){
+ // summary:
+ // Load available flattened resource bundles associated with a particular module for dojo.locale and all dojo.config.extraLocale (if any)
+ //
+ // descirption:
+ // 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 lonly 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.
+ //
+
+ 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 true;
+ }
+ parts.pop();
+ }
+ return func("ROOT");
+ }
-dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
- // summary:
- // Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
- //
- // position: String[]
- // This variable controls the position of the drop down.
- // It's an array of strings with the following values:
- //
- // * before: places drop down to the left of the target node/widget, or to the right in
- // the case of RTL scripts like Hebrew and Arabic
- // * after: places drop down to the right of the target node/widget, or to the left in
- // the case of RTL scripts like Hebrew and Arabic
- // * above: drop down goes above target node
- // * below: drop down goes below target node
- //
- // The list is positions is tried, in order, until a position is found where the drop down fits
- // within the viewport.
- //
- // leftToRight: Boolean
- // Whether the popup will be displaying in leftToRight mode.
- //
- var align = {};
- dojo.forEach(position, function(pos){
- switch(pos){
- case "after":
- align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
- break;
- case "before":
- align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
- break;
- case "below-alt":
- leftToRight = !leftToRight;
- // fall through
- case "below":
- // first try to align left borders, next try to align right borders (or reverse for RTL mode)
- align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR";
- align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL";
- break;
- case "above-alt":
- leftToRight = !leftToRight;
- // fall through
- case "above":
- default:
- // first try to align left borders, next try to align right borders (or reverse for RTL mode)
- align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR";
- align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL";
- break;
- }
- });
- return align;
-};
+ function preload(locale){
+ locale = normalizeLocale(locale);
+ forEachLocale(locale, function(loc){
+ if(array.indexOf(localesGenerated, loc)>=0){
+ var mid = bundlePrefix.replace(/\./g, "/")+"_"+loc;
+ preloading++;
+ (isXd(mid) || guaranteedAmdFormat ? require : syncRequire)([mid], function(rollup){
+ for(var p in rollup){
+ cache[p + "/" + locale] = rollup[p];
+ }
+ --preloading;
+ while(!preloading && preloadWaitQueue.length){
+ load.apply(null, preloadWaitQueue.shift());
+ }
+ });
+ return true;
+ }
+ return false;
+ });
+ }
-}
+ preload();
+ array.forEach(dojo.config.extraLocale, preload);
+ },
-if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.window"] = true;
-dojo.provide("dijit._base.window");
+ waitForPreloads = function(id, require, load){
+ if(preloading){
+ preloadWaitQueue.push([id, require, load]);
+ }
+ return preloading;
+ };
+ }
+ if(1){
+ // this code path assumes the dojo loader and won't work with a standard AMD loader
+ var 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
+
+ // 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(){define.called = 1;},"
+ + " 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 1;"
+
+ + "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){
+ var results= [];
+ array.forEach(deps, function(mid){
+ var url= require.toUrl(mid + ".js");
+
+ function load(text){
+ var result = evalBundle(text, checkForLegacyModules, mid);
+ if(result===1){
+ // 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
+ require([mid], function(bundle){
+ results.push(cache[url]= bundle);
+ });
+ }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);
+ },
-dijit.getDocumentWindow = function(doc){
- return dojo.window.get(doc);
-};
+ 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= getL10nName(moduleName, bundleName, locale).substring(10);
+ load(l10nName, (!isXd(l10nName) ? syncRequire : 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}");
+ t.is({prop:1}, check); t.is(undefined, check[1]);
-if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.popup"] = true;
-dojo.provide("dijit._base.popup");
+ check = evalBundle("({prop:1})");
+ t.is({prop:1}, check); t.is(undefined, check[1]);
+
+ check = evalBundle("{'prop-x':1}");
+ t.is({'prop-x':1}, check); t.is(undefined, check[1]);
+
+ check = evalBundle("({'prop-x':1})");
+ t.is({'prop-x':1}, check); t.is(undefined, check[1]);
+
+ check = evalBundle("define({'prop-x':1})");
+ t.is(1, check);
+
+ check = evalBundle("this is total nonsense and should throw an error");
+ t.is(check instanceof Error, true);
+ });
+ });
+ }
+ }
+ return lang.mixin(thisModule, {
+ dynamic:true,
+ normalize:normalize,
+ load:load,
+ cache:cache
+ });
+});
+},
+'dijit/hccss':function(){
+define("dijit/hccss", [
+ "require", // require.toUrl
+ "dojo/_base/config", // config.blankGif
+ "dojo/dom-class", // domClass.add domConstruct.create domStyle.getComputedStyle
+ "dojo/dom-construct", // domClass.add domConstruct.create domStyle.getComputedStyle
+ "dojo/dom-style", // domClass.add domConstruct.create domStyle.getComputedStyle
+ "dojo/ready", // ready
+ "dojo/_base/sniff", // has("ie") has("mozilla")
+ "dojo/_base/window" // win.body
+], function(require, config, domClass, domConstruct, domStyle, ready, has, win){
+
+ // module:
+ // dijit/hccss
+ // summary:
+ // Test if computer is in high contrast mode, and sets dijit_a11y flag on <body> if it is.
+ if(has("ie") || has("mozilla")){ // NOTE: checking in Safari messes things up
+ // priority is 90 to run ahead of parser priority of 100
+ ready(90, function(){
+ // summary:
+ // Detects if we are in high-contrast mode or not
+
+ // create div for testing if high contrast mode is on or images are turned off
+ var div = domConstruct.create("div",{
+ id: "a11yTestNode",
+ style:{
+ cssText:'border: 1px solid;'
+ + 'border-color:red green;'
+ + 'position: absolute;'
+ + 'height: 5px;'
+ + 'top: -999px;'
+ + 'background-image: url("' + (config.blankGif || require.toUrl("dojo/resources/blank.gif")) + '");'
+ }
+ }, win.body());
+
+ // test it
+ var cs = domStyle.getComputedStyle(div);
+ if(cs){
+ var bkImg = cs.backgroundImage;
+ var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
+ if(needsA11y){
+ domClass.add(win.body(), "dijit_a11y");
+ }
+ if(has("ie")){
+ div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
+ }else{
+ win.body().removeChild(div);
+ }
+ }
+ });
+ }
+});
+},
+'dijit/tree/ForestStoreModel':function(){
+define("dijit/tree/ForestStoreModel", [
+ "dojo/_base/array", // array.indexOf array.some
+ "dojo/_base/declare", // declare
+ "dojo/_base/lang", // lang.hitch
+ "dojo/_base/window", // win.global
+ "./TreeStoreModel"
+], function(array, declare, lang, win, TreeStoreModel){
/*=====
-dijit.popup.__OpenArgs = function(){
- // popup: Widget
- // widget to display
- // parent: Widget
- // the button etc. that is displaying this popup
- // around: DomNode
- // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
- // x: Integer
- // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
- // y: Integer
- // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
- // orient: Object|String
- // When the around parameter is specified, orient should be an
- // ordered list of tuples of the form (around-node-corner, popup-node-corner).
- // dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
- // until the popup appears fully within the viewport.
- //
- // The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
- // 1. (BL, TL)
- // 2. (TL, BL)
- // where BL means "bottom left" and "TL" means "top left".
- // So by default, it first tries putting the popup below the around node, left-aligning them,
- // and then tries to put it above the around node, still left-aligning them. Note that the
- // default is horizontally reversed when in RTL mode.
- //
- // When an (x,y) position is specified rather than an around node, orient is either
- // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
- // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
- // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
- // and the top-right corner.
- // onCancel: Function
- // callback when user has canceled the popup by
- // 1. hitting ESC or
- // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
- // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
- // onClose: Function
- // callback whenever this popup is closed
- // onExecute: Function
- // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
- // padding: dijit.__Position
- // adding a buffer around the opening position. This is only useful when around is not set.
- this.popup = popup;
- this.parent = parent;
- this.around = around;
- this.x = x;
- this.y = y;
- this.orient = orient;
- this.onCancel = onCancel;
- this.onClose = onClose;
- this.onExecute = onExecute;
- this.padding = padding;
-}
+var TreeStoreModel = dijit.tree.TreeStoreModel;
=====*/
-dijit.popup = {
+// module:
+// dijit/tree/ForestStoreModel
+// 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.
+
+return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
// summary:
- // This singleton is used to show/hide widgets as popups.
+ // 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
- // _stack: dijit._Widget[]
- // Stack of currently popped up widgets.
- // (someone opened _stack[0], and then it opened _stack[1], etc.)
- _stack: [],
-
- // _beginZIndex: Number
- // Z-index of the first popup. (If first popup opens other
- // popups they get a higher z-index.)
- _beginZIndex: 1000,
-
- _idGen: 1,
-
- _createWrapper: function(/*Widget || DomNode*/ 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.declaredClass ? widget._popupWrapper : (widget.parentNode && dojo.hasClass(widget.parentNode, "dijitPopup")),
- node = widget.domNode || widget;
-
- 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 = dojo.create("div",{
- "class":"dijitPopup",
- style:{ display: "none"},
- role: "presentation"
- }, dojo.body());
- wrapper.appendChild(node);
-
- var s = node.style;
- s.display = "";
- s.visibility = "";
- s.position = "";
- s.top = "0px";
+ // Parameters to constructor
- if(widget.declaredClass){ // TODO: in 2.0 change signature to always take widget, then remove if()
- widget._popupWrapper = wrapper;
- dojo.connect(widget, "destroy", function(){
- dojo.destroy(wrapper);
- delete widget._popupWrapper;
+ // 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);
}
-
- return wrapper;
},
- moveOffScreen: function(/*Widget || DomNode*/ 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.
+ // =======================================================================
+ // Inspecting items
- // Create wrapper if not already there
- var wrapper = this._createWrapper(widget);
+ isItem: function(/* anything */ something){
+ return (something === this.root) ? true : this.inherited(arguments);
+ },
- dojo.style(wrapper, {
- visibility: "hidden",
- top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
- display: ""
- });
+ fetchItemByIdentity: function(/* object */ keywordArgs){
+ if(keywordArgs.identity == this.root.id){
+ var scope = keywordArgs.scope?keywordArgs.scope:win.global;
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, this.root);
+ }
+ }else{
+ this.inherited(arguments);
+ }
},
- hide: function(/*dijit._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.
+ getIdentity: function(/* item */ item){
+ return (item === this.root) ? this.root.id : this.inherited(arguments);
+ },
- // Create wrapper if not already there
- var wrapper = this._createWrapper(widget);
+ getLabel: function(/* item */ item){
+ return (item === this.root) ? this.root.label : this.inherited(arguments);
+ },
- dojo.style(wrapper, "display", "none");
+ // =======================================================================
+ // Write interface
+
+ newItem: function(/* dojo.dnd.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);
+ }
},
-
- getTopPopup: function(){
+
+ onNewRootItem: function(/* dojo.dnd.Item */ /*===== args =====*/){
// 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 */
+ // 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);
}
- return stack[pi];
},
- open: function(/*dijit.popup.__OpenArgs*/ args){
+ // =======================================================================
+ // Handling for top level children
+
+ onAddToRoot: function(/* item */ item){
// summary:
- // Popup the widget at the specified position
- //
+ // 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:
- // opening at the mouse position
- // | dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
- //
+ // | 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:
- // opening the widget as a dropdown
- // | dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
+ // | 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.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).
//
- // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
- // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
-
- var stack = this._stack,
- widget = args.popup,
- orient = args.orient || (
- (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ?
- {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
- {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
- ),
- around = args.around,
- id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
+ // 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);
+ },
- // 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 || !dojo.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
- dijit.popup.close(stack[stack.length-1].widget);
+ 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();
}
- // Get pointer to popup wrapper, and create wrapper if it doesn't exist
- var wrapper = this._createWrapper(widget);
+ 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
- dojo.attr(wrapper, {
- id: id,
- style: {
- zIndex: this._beginZIndex + stack.length
- },
- "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
- dijitPopupParent: args.parent ? args.parent.id : ""
- });
+ this._requeryTop();
+ this.inherited(arguments);
+ }
- if(dojo.isIE || dojo.isMoz){
- if(!widget.bgIframe){
- // setting widget.bgIframe triggers cleanup in _Widget.destroy()
- widget.bgIframe = new dijit.BackgroundIframe(wrapper);
- }
- }
+});
- // position the wrapper node and make it visible
- var best = around ?
- dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
- dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
+});
- wrapper.style.display = "";
- wrapper.style.visibility = "visible";
- widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown
+},
+'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/_base/window", // win.doc.createTextNode
+ "dojo/i18n!./nls/ComboBox"
+], function(array, declare, domAttr, i18n, win){
+
+// module:
+// dijit/form/_ComboBoxMenuMixin
+// summary:
+// Focus-less menu for internal use in `dijit.form.ComboBox`
- var handlers = [];
+return declare( "dijit.form._ComboBoxMenuMixin", null, {
+ // summary:
+ // Focus-less menu for internal use in `dijit.form.ComboBox`
+ // tags:
+ // private
- // provide default escape and tab key handling
- // (this will work for any widget, not just menu)
- handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
- if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
- dojo.stopEvent(evt);
- args.onCancel();
- }else if(evt.charOrCode === dojo.keys.TAB){
- dojo.stopEvent(evt);
- var topPopup = this.getTopPopup();
- if(topPopup && topPopup.onCancel){
- topPopup.onCancel();
- }
- }
- }));
+ // _messages: Object
+ // Holds "next" and "previous" text for paging buttons on drop down
+ _messages: null,
- // watch for cancel/execute events on the popup and notify the caller
- // (for a menu, "execute" means clicking an item)
- if(widget.onCancel){
- handlers.push(dojo.connect(widget, "onCancel", args.onCancel));
- }
+ postMixInProperties: function(){
+ this.inherited(arguments);
+ this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
+ },
- handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){
- var topPopup = this.getTopPopup();
- if(topPopup && topPopup.onExecute){
- topPopup.onExecute();
- }
- }));
+ buildRendering: function(){
+ this.inherited(arguments);
- stack.push({
- widget: widget,
- parent: args.parent,
- onExecute: args.onExecute,
- onCancel: args.onCancel,
- onClose: args.onClose,
- handlers: handlers
- });
+ // fill in template with i18n messages
+ this.previousButton.innerHTML = this._messages["previousMessage"];
+ this.nextButton.innerHTML = this._messages["nextMessage"];
+ },
- if(widget.onOpen){
- // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
- widget.onOpen(best);
- }
+ _setValueAttr: function(/*Object*/ value){
+ this.value = value;
+ this.onChange(value);
+ },
- return best;
+ 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);
+ }
},
- close: function(/*dijit._Widget?*/ popup){
+ // stubs
+ onChange: function(/*Number*/ /*===== direction =====*/){
// summary:
- // Close specified popup and any popups that it parented.
- // If no popup is specified, closes all popups.
+ // Notifies ComboBox/FilteringSelect that user selected an option.
+ // tags:
+ // callback
+ },
- var stack = this._stack;
+ onPage: function(/*Number*/ /*===== direction =====*/){
+ // summary:
+ // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
+ // tags:
+ // callback
+ },
- // 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 && dojo.some(stack, function(elem){return elem.widget == popup;})) ||
- (!popup && stack.length)){
- var top = stack.pop(),
- widget = top.widget,
- onClose = top.onClose;
+ onClose: function(){
+ // summary:
+ // Callback from dijit.popup code to this widget, notifying it that it closed
+ // tags:
+ // private
+ this._setSelectedAttr(null);
+ },
- if(widget.onClose){
- // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
- widget.onClose();
- }
- dojo.forEach(top.handlers, dojo.disconnect);
+ _createOption: function(/*Object*/ item, labelFunc){
+ // summary:
+ // Creates an option to appear on the popup menu subclassed by
+ // `dijit.form.FilteringSelect`.
- // 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();
- }
+ var menuitem = this._createMenuItem();
+ var labelObject = labelFunc(item);
+ if(labelObject.html){
+ menuitem.innerHTML = labelObject.label;
+ }else{
+ menuitem.appendChild(
+ win.doc.createTextNode(labelObject.label)
+ );
+ }
+ // #3250: in blank options, assign a normal height
+ if(menuitem.innerHTML == ""){
+ menuitem.innerHTML = "&#160;"; // &nbsp;
}
- }
-};
-// TODO: remove dijit._frames, it isn't being used much, since popups never release their
-// iframes (see [22236])
-dijit._frames = new function(){
- // summary:
- // cache of iframes
+ // update menuitem.dir if BidiSupport was required
+ this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
- var queue = [];
+ menuitem.item=item;
+ return menuitem;
+ },
- this.pop = function(){
- var iframe;
- if(queue.length){
- iframe = queue.pop();
- iframe.style.display="";
- }else{
- if(dojo.isIE < 9){
- var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
- var html="<iframe src='" + burl + "'"
- + " style='position: absolute; left: 0px; top: 0px;"
- + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
- iframe = dojo.doc.createElement(html);
- }else{
- iframe = dojo.create("iframe");
- iframe.src = 'javascript:""';
- iframe.className = "dijitBackgroundIframe";
- dojo.style(iframe, "opacity", 0.1);
+ 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
+
+ // 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);
+ 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;
}
- iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
- dijit.setWaiRole(iframe,"presentation");
+ }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;
}
- return iframe;
- };
-
- this.push = function(iframe){
- iframe.style.display="none";
- queue.push(iframe);
- }
-}();
+ this.nextButton.style.display = displayMore ? "" : "none";
+ domAttr.set(this.nextButton,"id", this.id + "_next");
+ return this.containerNode.childNodes;
+ },
-dijit.BackgroundIframe = function(/*DomNode*/ node){
- // summary:
- // For IE/FF z-index schenanigans. id attribute is required.
- //
- // description:
- // new dijit.BackgroundIframe(node)
- // Makes a background iframe as a child of node, that fills
- // area (and position) of node
-
- if(!node.id){ throw new Error("no id"); }
- if(dojo.isIE || dojo.isMoz){
- var iframe = (this.iframe = dijit._frames.pop());
- node.appendChild(iframe);
- if(dojo.isIE<7 || dojo.isQuirks){
- this.resize(node);
- this._conn = dojo.connect(node, 'onresize', this, function(){
- this.resize(node);
- });
- }else{
- dojo.style(iframe, {
- width: '100%',
- height: '100%'
- });
+ 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);
+ },
-dojo.extend(dijit.BackgroundIframe, {
- resize: function(node){
+ highlightFirstOption: function(){
// 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){
- dojo.style(this.iframe, {
- width: node.offsetWidth + 'px',
- height: node.offsetHeight + 'px'
- });
- }
+ // Highlight the first real item in the list (not Previous Choices).
+ this.selectFirstNode();
},
- destroy: function(){
+
+ highlightLastOption: function(){
// summary:
- // destroy the iframe
- if(this._conn){
- dojo.disconnect(this._conn);
- this._conn = null;
+ // 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();
}
- if(this.iframe){
- dijit._frames.push(this.iframe);
- delete this.iframe;
+ },
+
+ selectLastNode: function(){
+ this.inherited(arguments);
+ if(this.getHighlightedOption() == this.nextButton){
+ this.selectPreviousNode();
}
+ },
+
+ getHighlightedOption: function(){
+ return this._getSelectedAttr();
}
});
-}
+});
+
+},
+'dojo/parser':function(){
+define(
+ "dojo/parser", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/html", "./_base/window", "./_base/url",
+ "./_base/json", "./aspect", "./date/stamp", "./query", "./on", "./ready"],
+ function(dojo, dlang, darray, dhtml, dwindow, _Url, djson, aspect, dates, query, don){
+
+// module:
+// dojo/parser
+// summary:
+// The Dom/Widget parsing package
-if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.scroll"] = true;
-dojo.provide("dijit._base.scroll");
+new Date("X"); // workaround for #11279, new Date("") == NaN
+var features = {
+ // Feature detection for when node.attributes only lists the attributes specified in the markup
+ // rather than old IE/quirks behavior where it lists every default value too
+ "dom-attributes-explicit": document.createElement("div").attributes.length < 40
+};
+function has(feature){
+ return features[feature];
+}
-dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
+dojo.parser = new function(){
// summary:
- // Scroll the passed node into view, if it is not already.
- // Deprecated, use `dojo.window.scrollIntoView` instead.
-
- dojo.window.scrollIntoView(node, pos);
-};
+ // The Dom/Widget parsing package
-}
+ var _nameMap = {
+ // Map from widget name (ex: "dijit.form.Button") to structure mapping
+ // lowercase version of attribute names to the version in the widget ex:
+ // {
+ // label: "label",
+ // onclick: "onClick"
+ // }
+ };
+ function getNameMap(proto){
+ // summary:
+ // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
+ var map = {};
+ for(var name in proto){
+ if(name.charAt(0)=="_"){ continue; } // skip internal properties
+ map[name.toLowerCase()] = name;
+ }
+ return map;
+ }
+ // 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).
+ aspect.after(dlang, "extend", function(){
+ _nameMap = {};
+ }, true);
-if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.uacss"] = true;
-dojo.provide("dojo.uacss");
+ // Map from widget name (ex: "dijit.form.Button") to constructor
+ var _ctorMap = {};
+ this._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 = "";
+ var suffix = "";
+ var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args"));
+ if(argsStr){
+ darray.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
+ preamble += "var "+part+" = arguments["+idx+"]; ";
+ });
+ }
+ var withStr = script.getAttribute("with");
+ if(withStr && withStr.length){
+ darray.forEach(withStr.split(/\s*,\s*/), function(part){
+ preamble += "with("+part+"){";
+ suffix += "}";
+ });
+ }
+ return new Function(preamble+script.innerHTML+suffix);
+ };
-(function(){
- // summary:
- // Applies pre-set CSS classes to the top-level HTML node, based on:
- // - browser (ex: dj_ie)
- // - browser version (ex: dj_ie6)
- // - box model (ex: dj_contentBox)
- // - text direction (ex: dijitRtl)
- //
- // In addition, browser, browser version, and box model are
- // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
+ this.instantiate = /*====== dojo.parser.instantiate= ======*/function(nodes, mixin, args){
+ // summary:
+ // Takes array of nodes, and turns them into class instances and
+ // potentially calls a startup method to allow them to connect with
+ // any children.
+ // nodes: Array
+ // Array of nodes or objects like
+ // | {
+ // | type: "dijit.form.Button",
+ // | node: DOMNode,
+ // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
+ // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
+ // | }
+ // mixin: Object?
+ // An object that will be mixed in with each node in the array.
+ // Values in the mixin will override values in the node, if they
+ // exist.
+ // args: Object?
+ // An object used to hold kwArgs for instantiation.
+ // See parse.args argument for details.
- var d = dojo,
- html = d.doc.documentElement,
- ie = d.isIE,
- opera = d.isOpera,
- maj = Math.floor,
- ff = d.isFF,
- boxModel = d.boxModel.replace(/-/,''),
+ var thelist = [],
+ mixin = mixin||{};
+ args = args||{};
- classes = {
- dj_ie: ie,
- dj_ie6: maj(ie) == 6,
- dj_ie7: maj(ie) == 7,
- dj_ie8: maj(ie) == 8,
- dj_ie9: maj(ie) == 9,
- dj_quirks: d.isQuirks,
- dj_iequirks: ie && d.isQuirks,
+ // Precompute names of special attributes we are looking for
+ // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
+ var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType"
+ attrData = "data-" + (args.scope || dojo._scopeName) + "-",// typically "data-dojo-"
+ dataDojoType = attrData + "type", // typically "data-dojo-type"
+ dataDojoProps = attrData + "props", // typically "data-dojo-props"
+ dataDojoAttachPoint = attrData + "attach-point",
+ dataDojoAttachEvent = attrData + "attach-event",
+ dataDojoId = attrData + "id";
+
+ // And make hash to quickly check if a given attribute is special, and to map the name to something friendly
+ var specialAttrs = {};
+ darray.forEach([dataDojoProps, dataDojoType, dojoType, dataDojoId, "jsId", dataDojoAttachPoint,
+ dataDojoAttachEvent, "dojoAttachPoint", "dojoAttachEvent", "class", "style"], function(name){
+ specialAttrs[name.toLowerCase()] = name.replace(args.scope, "dojo");
+ });
- // NOTE: Opera not supported by dijit
- dj_opera: opera,
+ darray.forEach(nodes, function(obj){
+ if(!obj){ return; }
- dj_khtml: d.isKhtml,
+ var node = obj.node || obj,
+ type = dojoType in mixin ? mixin[dojoType] : obj.node ? obj.type : (node.getAttribute(dataDojoType) || node.getAttribute(dojoType)),
+ ctor = _ctorMap[type] || (_ctorMap[type] = dlang.getObject(type)),
+ proto = ctor && ctor.prototype;
+ if(!ctor){
+ throw new Error("Could not load class '" + type);
+ }
- dj_webkit: d.isWebKit,
- dj_safari: d.isSafari,
- dj_chrome: d.isChrome,
+ // 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 = {};
- dj_gecko: d.isMozilla,
- dj_ff3: maj(ff) == 3
- }; // no dojo unsupported browsers
+ if(args.defaults){
+ // settings for the document itself (or whatever subtree is being parsed)
+ dlang.mixin(params, args.defaults);
+ }
+ if(obj.inherited){
+ // settings from dir=rtl or lang=... on a node above this node
+ dlang.mixin(params, obj.inherited);
+ }
- classes["dj_" + boxModel] = true;
+ // 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{
+ // Special path for IE, 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]*/, "").replace(/>.*$/, "");
- // apply browser, browser version, and box model class names
- var classStr = "";
- for(var clz in classes){
- if(classes[clz]){
- classStr += clz + " ";
- }
- }
- html.className = d.trim(html.className + " " + classStr);
+ 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,
+ specified: true
+ };
+ });
+ }
- // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
- // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
- // Unshift() is to run sniff code before the parser.
- dojo._loaders.unshift(function(){
- if(!dojo._isBodyLtr()){
- var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")
- html.className = d.trim(html.className + " " + rtlClassStr);
- }
- });
-})();
+ // 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;
+ while(item = attributes[i++]){
+ if(!item || !item.specified){
+ continue;
+ }
-}
+ var name = item.name,
+ lcName = name.toLowerCase(),
+ value = item.value;
-if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.sniff"] = true;
-dojo.provide("dijit._base.sniff");
+ if(lcName in specialAttrs){
+ switch(specialAttrs[lcName]){
+ // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
+ case "data-dojo-props":
+ var extra = value;
+ break;
+ // data-dojo-id or jsId. TODO: drop jsId in 2.0
+ case "data-dojo-id":
+ case "jsId":
+ var jsname = value;
+ break;
-// summary:
-// Applies pre-set CSS classes to the top-level HTML node, see
-// `dojo.uacss` for details.
-//
-// Simply doing a require on this module will
-// establish this CSS. Modified version of Morris' CSS hack.
+ // 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;
+ }
+ }else{
+ // Normal attribute, ex: value="123"
-if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.typematic"] = true;
-dojo.provide("dijit._base.typematic");
+ // Find attribute in widget corresponding to specified name.
+ // May involve case conversion, ex: onclick --> onClick
+ if(!(name in proto)){
+ var map = (_nameMap[type] || (_nameMap[type] = getNameMap(proto)));
+ 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 function like "myOnClick"
+ // or a single word function "return"
+ params[name] = dlang.getObject(value, false) || new Function(value);
+ }
+ 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 dojo._Url) ? (dojo.baseUrl + value) :
+ djson.fromJson(value);
+ }
+ }else{
+ params[name] = value;
+ }
+ }
+ }
-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.
+ // Mix things found in data-dojo-props into the params, overriding any direct settings
+ if(extra){
+ try{
+ extra = djson.fromJson.call(args.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 + "'");
+ }
+ }
- _fireEventAndReload: function(){
- this._timer = null;
- this._callback(++this._count, this._node, this._evt);
-
- // Schedule next event, timer is at most minDelay (default 10ms) to avoid
- // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
- this._currentTimeout = Math.max(
- this._currentTimeout < 0 ? this._initialDelay :
- (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
- this._minDelay);
- this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout);
- },
+ // Any parameters specified in "mixin" override everything else.
+ dlang.mixin(params, mixin);
- trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
- // summary:
- // Start a timed, repeating callback sequence.
- // If already started, the function call is ignored.
- // This method is not normally called by the user but can be
- // when the normal listener code is insufficient.
- // evt:
- // key or mouse event object to pass to the user callback
- // _this:
- // pointer to the user's widget space.
- // node:
- // the DOM node object to pass the the callback function
- // callback:
- // function to call until the sequence is stopped called with 3 parameters:
- // count:
- // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
- // node:
- // the DOM node object passed in
- // evt:
- // key or mouse event object
- // obj:
- // user space object used to uniquely identify each typematic sequence
- // subsequentDelay (optional):
- // if > 1, the number of milliseconds until the 3->n events occur
- // or else the fractional time multiplier for the next event's delay, default=0.9
- // initialDelay (optional):
- // the number of milliseconds until the 2nd event occurs, default=500ms
- // minDelay (optional):
- // the maximum delay in milliseconds for event to fire, default=10ms
- if(obj != this._obj){
- this.stop();
- this._initialDelay = initialDelay || 500;
- this._subsequentDelay = subsequentDelay || 0.90;
- this._minDelay = minDelay || 10;
- this._obj = obj;
- this._evt = evt;
- this._node = node;
- this._currentTimeout = -1;
- this._count = -1;
- this._callback = dojo.hitch(_this, callback);
- this._fireEventAndReload();
- this._evt = dojo.mixin({faux: true}, evt);
+ var scripts = obj.node ? obj.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 connects = [], // functions to connect after instantiation
+ calls = [], // functions to call after instantiation
+ watch = [], //functions to watch after instantiation
+ on = []; //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"),
+ type = script.getAttribute("type"),
+ nf = this._functionFromScript(script, attrData);
+ if(event){
+ if(type == "dojo/connect"){
+ connects.push({event: event, func: nf});
+ }else if(type == "dojo/on"){
+ on.push({event: event, func: nf});
+ }else{
+ params[event] = nf;
+ }
+ }else if(type == "dojo/watch"){
+ watch.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);
+ thelist.push(instance);
+
+ // 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<connects.length; i++){
+ aspect.after(instance, connects[i].event, dojo.hitch(instance, connects[i].func), true);
+ }
+ for(i=0; i<calls.length; i++){
+ calls[i].call(instance);
+ }
+ for(i=0; i<watch.length; i++){
+ instance.watch(watch[i].prop, watch[i].func);
+ }
+ for(i=0; i<on.length; i++){
+ don(instance, on[i].event, on[i].func);
+ }
+ }, 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){
+ darray.forEach(thelist, function(instance){
+ if( !args.noStart && instance &&
+ dlang.isFunction(instance.startup) &&
+ !instance._started
+ ){
+ instance.startup();
+ }
+ });
}
- },
+ return thelist;
+ };
- stop: function(){
+ this.parse = /*====== dojo.parser.parse= ======*/ function(rootNode, args){
// 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;
+ // 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 `args`
+ // object can be passed in this place. If the `args` object has a
+ // `rootNode` member, that is used.
+ //
+ // args: Object
+ // a kwArgs object passed along to instantiate()
+ //
+ // * noStart: Boolean?
+ // when set will prevent the parser from calling .startup()
+ // when locating the nodes.
+ // * rootNode: DomNode?
+ // identical to the function's `rootNode` argument, though
+ // allowed to be passed in via this `args object.
+ // * 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`
+ //
+ // example:
+ // Parse all widgets on a page:
+ // | dojo.parser.parse();
+ //
+ // example:
+ // Parse all classes within the node with id="foo"
+ // | dojo.parser.parse(dojo.byId('foo'));
+ //
+ // example:
+ // Parse all classes in a page, but do not call .startup() on any
+ // child
+ // | dojo.parser.parse({ noStart: true })
+ //
+ // example:
+ // Parse all classes in a node, but do not call .startup()
+ // | dojo.parser.parse(someNode, { noStart:true });
+ // | // or
+ // | dojo.parser.parse({ noStart:true, rootNode: someNode });
+
+ // determine the root node based on the passed arguments.
+ var root;
+ if(!args && rootNode && rootNode.rootNode){
+ args = rootNode;
+ root = args.rootNode;
+ }else{
+ root = rootNode;
}
- },
+ root = root ? dhtml.byId(root) : dwindow.body();
+ args = args || {};
- addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
- // summary:
- // Start listening for a specific typematic key.
- // See also the trigger method for other parameters.
- // keyObject:
- // an object defining the key to listen for:
- // charOrCode:
- // the printable character (string) or keyCode (number) to listen for.
- // keyCode:
- // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
- // charCode:
- // (deprecated - use charOrCode) the charCode (number) to listen for.
- // ctrlKey:
- // desired ctrl key state to initiate the callback sequence:
- // - pressed (true)
- // - released (false)
- // - either (unspecified)
- // altKey:
- // same as ctrlKey but for the alt key
- // shiftKey:
- // same as ctrlKey but for the shift key
- // returns:
- // an array of dojo.connect handles
- if(keyObject.keyCode){
- keyObject.charOrCode = keyObject.keyCode;
- dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
- }else if(keyObject.charCode){
- keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
- dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
+ var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType"
+ attrData = "data-" + (args.scope || dojo._scopeName) + "-", // typically "data-dojo-"
+ dataDojoType = attrData + "type", // typically "data-dojo-type"
+ dataDojoTextDir = attrData + "textdir"; // typically "data-dojo-textdir"
+
+ // List of all nodes on page w/dojoType specified
+ var list = [];
+
+ // 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 = args && args.inherited;
+ if(!inherited){
+ function findAncestorAttr(node, attr){
+ return (node.getAttribute && node.getAttribute(attr)) ||
+ (node !== dwindow.doc && node !== dwindow.doc.documentElement && node.parentNode ? findAncestorAttr(node.parentNode, attr) : null);
+ }
+ inherited = {
+ dir: findAncestorAttr(root, "dir"),
+ lang: findAncestorAttr(root, "lang"),
+ textDir: findAncestorAttr(root, dataDojoTextDir)
+ };
+ for(var key in inherited){
+ if(!inherited[key]){ delete inherited[key]; }
+ }
}
- return [
- dojo.connect(node, "onkeypress", this, function(evt){
- if(evt.charOrCode == keyObject.charOrCode &&
- (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
- (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
- (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
- (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
- dojo.stopEvent(evt);
- dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
- }else if(dijit.typematic._obj == keyObject){
- dijit.typematic.stop();
- }
- }),
- dojo.connect(node, "onkeyup", this, function(evt){
- if(dijit.typematic._obj == keyObject){
- dijit.typematic.stop();
- }
- })
- ];
- },
+ var parent = {
+ inherited: inherited
+ };
- addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
- // summary:
- // Start listening for a typematic mouse click.
- // See the trigger method for other parameters.
- // returns:
- // an array of dojo.connect handles
- var dc = dojo.connect;
- return [
- dc(node, "mousedown", this, function(evt){
- dojo.stopEvent(evt);
- dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
- }),
- dc(node, "mouseup", this, function(evt){
- dojo.stopEvent(evt);
- dijit.typematic.stop();
- }),
- dc(node, "mouseout", this, function(evt){
- dojo.stopEvent(evt);
- dijit.typematic.stop();
- }),
- dc(node, "mousemove", this, function(evt){
- evt.preventDefault();
- }),
- dc(node, "dblclick", this, function(evt){
- dojo.stopEvent(evt);
- if(dojo.isIE){
- dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
- setTimeout(dojo.hitch(this, dijit.typematic.stop), 50);
- }
- })
- ];
- },
+ // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
+ var scripts;
- addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
- // summary:
- // Start listening for a specific typematic key and mouseclick.
- // This is a thin wrapper to addKeyListener and addMouseListener.
- // See the addMouseListener and addKeyListener methods for other parameters.
- // mouseNode:
- // the DOM node object to listen on for mouse events.
- // keyNode:
- // the DOM node object to listen on for key events.
- // returns:
- // an array of dojo.connect handles
- return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat(
- this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay));
- }
-};
+ // 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;
+ }
-if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base.wai"] = true;
-dojo.provide("dijit._base.wai");
+ // 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;
+ scripts = parent.scripts;
+ scriptsOnly = false;
+ parent = parent.parent;
+ continue;
+ }
+ if(node.nodeType != 1){
+ // Text or comment node, skip to next sibling
+ node = node.nextSibling;
+ continue;
+ }
-dijit.wai = {
- onload: function(){
- // summary:
- // Detects if we are in high-contrast mode or not
+ 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){
+ node = node.nextSibling;
+ continue;
+ }
- // This must be a named function and not an anonymous
- // function, so that the widget parsing code can make sure it
- // registers its onload function after this function.
- // DO NOT USE "this" within this function.
+ // Check for data-dojo-type attribute, fallback to backward compatible dojoType
+ var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
- // create div for testing if high contrast mode is on or images are turned off
- var div = dojo.create("div",{
- id: "a11yTestNode",
- style:{
- cssText:'border: 1px solid;'
- + 'border-color:red green;'
- + 'position: absolute;'
- + 'height: 5px;'
- + 'top: -999px;'
- + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");'
+ // 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;
}
- }, dojo.body());
- // test it
- var cs = dojo.getComputedStyle(div);
- if(cs){
- var bkImg = cs.backgroundImage;
- var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
- dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
- if(dojo.isIE){
- div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
- }else{
- dojo.body().removeChild(div);
+ // Setup data structure to save info on current node for when we return from processing descendant nodes
+ var current = {
+ node: node,
+ scripts: scripts,
+ parent: parent
+ };
+
+ // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate
+ var ctor = type && (_ctorMap[type] || (_ctorMap[type] = dlang.getObject(type))), // note: won't find classes declared via dojo.Declaration
+ childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children
+ if(type){
+ list.push({
+ "type": type,
+ node: node,
+ scripts: childScripts,
+ inherited: getEffective(current) // dir & lang settings for current node, explicit or inherited
+ });
}
+
+ // 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 && !(args && args.template);
+ parent = current;
+
}
- }
-};
-// Test if computer is in high contrast mode.
-// Make sure the a11y test runs first, before widgets are instantiated.
-if(dojo.isIE || dojo.isMoz){ // NOTE: checking in Safari messes things up
- dojo._loaders.unshift(dijit.wai.onload);
+ // go build the object instances
+ var mixin = args && args.template ? {template: true} : null;
+ return this.instantiate(list, mixin, args); // Array
+ };
+}();
+
+
+//Register the parser callback. It should be the first callback
+//after the a11y test.
+if(dojo.config.parseOnLoad){
+ dojo.ready(100, dojo.parser, "parse");
}
-dojo.mixin(dijit, {
- 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);
- },
+return dojo.parser;
+});
- 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 dojo.trim((dojo.attr(elem, "role") || "").replace("wairole:",""));
- },
+},
+'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\"\n/></span>\n",
+'dojo/dnd/Manager':function(){
+define("dojo/dnd/Manager", ["../main", "../Evented", "./common", "./autoscroll", "./Avatar"], function(dojo, Evented) {
+ // module:
+ // dojo/dnd/Manager
+ // summary:
+ // TODOC
- setWaiRole: function(/*Element*/ elem, /*String*/ role){
- // summary:
- // Sets the role on an element.
- // description:
- // Replace existing role attribute with new role.
- dojo.attr(elem, "role", role);
+var Manager = dojo.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 = [];
},
- 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.)
+ // avatar's offset from the mouse
+ OFFSET_X: 16,
+ OFFSET_Y: 16,
- var roleValue = dojo.attr(elem, "role");
- if(!roleValue){ return; }
- if(role){
- var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
- dojo.attr(elem, "role", t);
+ // 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();
+ }
+ dojo.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();
+ dojo.publish("/dnd/source/over", [null]);
+ }
}else{
- elem.removeAttribute("role");
+ dojo.publish("/dnd/source/over", [null]);
}
},
-
- hasWaiState: function(/*Element*/ elem, /*String*/ state){
+ startDrag: function(source, nodes, copy){
// 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);
+ // 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
+ this.source = source;
+ this.nodes = nodes;
+ this.copy = Boolean(copy); // normalizing to true boolean
+ this.avatar = this.makeAvatar();
+ dojo.body().appendChild(this.avatar.node);
+ dojo.publish("/dnd/start", [source, nodes, this.copy]);
+ this.events = [
+ dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
+ dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"),
+ dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"),
+ dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"),
+ // cancel text selection and text dragging
+ dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent),
+ dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
+ ];
+ var c = "dojoDnd" + (copy ? "Copy" : "Move");
+ dojo.addClass(dojo.body(), c);
},
-
- getWaiState: function(/*Element*/ elem, /*String*/ state){
+ canDrop: function(flag){
// 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) || "";
+ // 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
+ dojo.removeClass(dojo.body(), ["dojoDndCopy", "dojoDndMove"]);
+ dojo.forEach(this.events, dojo.disconnect);
+ 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 dojo.dnd.Avatar(this);
+ },
+ updateAvatar: function(){
+ // summary:
+ // updates the avatar; it is separate to be overwritten dynamically, if needed
+ this.avatar.update();
},
- setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
+ // mouse event processors
+ onMouseMove: function(e){
// summary:
- // Sets a state on an element.
- // description:
- // Sets an attribute called "aria-"+state.
+ // event processor for onmousemove
+ // e: Event
+ // mouse event
+ var a = this.avatar;
+ if(a){
+ dojo.dnd.autoScrollNodes(e);
+ //dojo.dnd.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(dojo.isCopyKey(e)));
+ if(this.copy != copy){
+ this._setCopyStatus(copy);
+ }
+ }
+ },
+ 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(dojo.isCopyKey(e))),
+ params = [this.source, this.nodes, copy, this.target, e];
+ dojo.publish("/dnd/drop/before", params);
+ dojo.publish("/dnd/drop", params);
+ }else{
+ dojo.publish("/dnd/cancel");
+ }
+ this.stopDrag();
+ }
+ },
- elem.setAttribute("aria-"+state, value);
+ // 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 dojo.keys.CTRL:
+ var copy = Boolean(this.source.copyState(true));
+ if(this.copy != copy){
+ this._setCopyStatus(copy);
+ }
+ break;
+ case dojo.keys.ESCAPE:
+ dojo.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 == dojo.keys.CTRL){
+ var copy = Boolean(this.source.copyState(false));
+ if(this.copy != copy){
+ this._setCopyStatus(copy);
+ }
+ }
},
- removeWaiState: function(/*Element*/ elem, /*String*/ state){
+ // utilities
+ _setCopyStatus: function(copy){
// summary:
- // Removes a state from an element.
- // description:
- // Sets an attribute called "aria-"+state.
+ // changes the copy status
+ // copy: Boolean
+ // the copy status
+ this.copy = copy;
+ this.source._markDndStatus(this.copy);
+ this.updateAvatar();
+ dojo.replaceClass(dojo.body(),
+ "dojoDnd" + (this.copy ? "Copy" : "Move"),
+ "dojoDnd" + (this.copy ? "Move" : "Copy"));
+ }
+});
+
+// dojo.dnd._manager:
+// The manager singleton variable. Can be overwritten if needed.
+dojo.dnd._manager = null;
- elem.removeAttribute("aria-"+state);
+Manager.manager = dojo.dnd.manager = function(){
+ // summary:
+ // Returns the current DnD manager. Creates one if it is not created yet.
+ if(!dojo.dnd._manager){
+ dojo.dnd._manager = new dojo.dnd.Manager();
}
+ return dojo.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){
-if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._base"] = true;
-dojo.provide("dijit._base");
+/*=====
+ var Button = dijit.form.Button;
+ var _ToggleButtonMixin = dijit.form._ToggleButtonMixin;
+=====*/
+ // module:
+ // dijit/form/ToggleButton
+ // summary:
+ // A templated button widget that can be in two states (checked or not).
+ 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/kernel", "../_base/lang", "../_base/array"], function(dojo, lang, array) {
+ // module:
+ // dojo/date/stamp
+ // summary:
+ // TODOC
+lang.getObject("date.stamp", true, dojo);
+// Methods to convert dates to or from a wire (string) format using well-known conventions
+dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
+ // summary:
+ // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
+ //
+ // description:
+ // Accepts a string formatted according to a profile of ISO8601 as defined by
+ // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
+ // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
+ // The following combinations are valid:
+ //
+ // * dates only
+ // | * yyyy
+ // | * yyyy-MM
+ // | * yyyy-MM-dd
+ // * times only, with an optional time zone appended
+ // | * THH:mm
+ // | * THH:mm:ss
+ // | * THH:mm:ss.SSS
+ // * and "datetimes" which could be any combination of the above
+ //
+ // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
+ // Assumes the local time zone if not specified. Does not validate. Improperly formatted
+ // input may return null. Arguments which are out of bounds will be handled
+ // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
+ // Only years between 100 and 9999 are supported.
+ //
+ // formattedString:
+ // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
+ //
+ // defaultTime:
+ // Used for defaults for fields omitted in the formattedString.
+ // Uses 1970-01-01T00:00:00.0Z by default.
+ if(!dojo.date.stamp._isoRegExp){
+ dojo.date.stamp._isoRegExp =
+//TODO: could be more restrictive and check for 00-59, etc.
+ /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
+ }
+ var match = dojo.date.stamp._isoRegExp.exec(formattedString),
+ result = null;
-}
+ if(match){
+ match.shift();
+ if(match[1]){match[1]--;} // Javascript Date months are 0-based
+ if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
+
+ if(defaultTime){
+ // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
+ defaultTime = new Date(defaultTime);
+ 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);
+ }
+ }
-if(!dojo._hasResource["dojo.Stateful"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.Stateful"] = true;
-dojo.provide("dojo.Stateful");
+ return result; // Date or null
+};
+/*=====
+ dojo.date.stamp.__Options = function(){
+ // selector: String
+ // "date" or "time" for partial formatting of the Date object.
+ // Both date and time will be formatted by default.
+ // zulu: Boolean
+ // if true, UTC/GMT is used for a timezone
+ // milliseconds: Boolean
+ // if true, output milliseconds
+ this.selector = selector;
+ this.zulu = zulu;
+ this.milliseconds = milliseconds;
+ }
+=====*/
-dojo.declare("dojo.Stateful", null, {
+dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
+ // summary:
+ // Format a Date object as a string according a subset of the ISO-8601 standard
+ //
+ // description:
+ // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
+ // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
+ // Does not check bounds. Only years between 100 and 9999 are supported.
+ //
+ // dateObject:
+ // A Date object
+
+ var _ = function(n){ return (n < 10) ? "0" + n : n; };
+ options = options || {};
+ var formattedDate = [],
+ getter = options.zulu ? "getUTC" : "get",
+ date = "";
+ if(options.selector != "time"){
+ var year = dateObject[getter+"FullYear"]();
+ date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
+ }
+ formattedDate.push(date);
+ if(options.selector != "date"){
+ var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
+ var millis = dateObject[getter+"Milliseconds"]();
+ if(options.milliseconds){
+ time += "."+ (millis < 100 ? "0" : "") + _(millis);
+ }
+ if(options.zulu){
+ time += "Z";
+ }else if(options.selector != "time"){
+ var timezoneOffset = dateObject.getTimezoneOffset();
+ var absOffset = Math.abs(timezoneOffset);
+ time += (timezoneOffset > 0 ? "-" : "+") +
+ _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
+ }
+ formattedDate.push(time);
+ }
+ return formattedDate.join('T'); // String
+};
+
+return dojo.date.stamp;
+});
+
+},
+'dojo/Stateful':function(){
+define("dojo/Stateful", ["./_base/kernel", "./_base/declare", "./_base/lang", "./_base/array"], function(dojo, declare, lang, array) {
+ // module:
+ // dojo/Stateful
+ // summary:
+ // TODOC
+
+return dojo.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
@@ -3203,15 +5527,17 @@ dojo.declare("dojo.Stateful", null, {
// | obj.set("foo","bar");
postscript: function(mixin){
if(mixin){
- dojo.mixin(this, mixin);
+ lang.mixin(this, mixin);
}
},
-
+
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
@@ -3220,8 +5546,8 @@ dojo.declare("dojo.Stateful", null, {
// | stateful = new dojo.Stateful({foo: 3});
// | stateful.get("foo") // returns 3
// | stateful.foo // returns 3
-
- return this[name];
+
+ return this[name]; //Any
},
set: function(/*String*/name, /*Object*/value){
// summary:
@@ -3230,6 +5556,8 @@ dojo.declare("dojo.Stateful", null, {
// 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.
@@ -3257,7 +5585,7 @@ dojo.declare("dojo.Stateful", null, {
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
- return this;
+ return this; //dojo.Stateful
},
watch: function(/*String?*/name, /*Function*/callback){
// summary:
@@ -3275,7 +5603,7 @@ dojo.declare("dojo.Stateful", null, {
// 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;
@@ -3312,3643 +5640,5497 @@ dojo.declare("dojo.Stateful", null, {
propertyCallbacks.push(callback);
return {
unwatch: function(){
- propertyCallbacks.splice(dojo.indexOf(propertyCallbacks, callback), 1);
+ propertyCallbacks.splice(array.indexOf(propertyCallbacks, callback), 1);
}
- };
+ }; //Object
}
-
+
});
-}
+});
-if(!dojo._hasResource["dijit._WidgetBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._WidgetBase"] = true;
-dojo.provide("dijit._WidgetBase");
+},
+'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/_base/kernel",
+ "dojo/keys", // keys
+ "dojo/_base/lang", // lang.getObject lang.hitch
+ "dojo/_base/sniff", // has("ie")
+ "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,
+ kernel, keys, lang, has, topic, focus, manager, ready,
+ _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
+/*=====
+ var _Widget = dijit._Widget;
+ var _Container = dijit._Container;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _CssStateMixin = dijit._CssStateMixin;
+ var StackContainer = dijit.layout.StackContainer;
+ var ContentPane = dijit.layout.ContentPane;
+=====*/
+ // module:
+ // dijit/layout/AccordionContainer
+ // 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.
-(function(){
+ // 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.
-dojo.declare("dijit._WidgetBase", dojo.Stateful, {
- // summary:
- // Future base class for all Dijit widgets.
- // _Widget extends this class adding support for various features needed by desktop.
- // 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: "",
+ 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
- // 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: "",
+ templateString: template,
- // 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: "",
+ // label: String
+ // Title of the pane
+ label: "",
+ _setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
- // class: String
- // HTML class attribute
- "class": "",
+ // title: String
+ // Tooltip that appears on hover
+ title: "",
+ _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
- // style: String||Object
- // HTML style attributes as cssText string or name/value hash
- style: "",
+ // iconClassAttr: String
+ // CSS class for icon to left of label
+ iconClassAttr: "",
+ _setIconClassAttr: { node: "iconNode", type: "class" },
- // 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: "",
+ baseClass: "dijitAccordionTitle",
- // 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: "",
+ getParent: function(){
+ // summary:
+ // Returns the AccordionContainer parent.
+ // tags:
+ // private
+ return this.parent;
+ },
- // baseClass: [protected] String
- // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
- // widget state.
- baseClass: "",
+ 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);
+ },
- // srcNodeRef: [readonly] DomNode
- // pointer to original DOM node
- srcNodeRef: null,
+ getTitleHeight: function(){
+ // summary:
+ // Returns the height of the title dom node.
+ return domGeometry.getMarginSize(this.domNode).h; // Integer
+ },
- // domNode: [readonly] DomNode
- // This is our visible representation of the widget! Other DOM
- // Nodes may by assigned to other properties, usually through the
- // template system's dojoAttachPoint syntax, but the domNode
- // property is the canonical "top level" node in widget UI.
- domNode: null,
+ // 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);
+ },
- // containerNode: [readonly] DomNode
- // Designates where children of the source DOM node will be placed.
- // "Children" in this case refers to both DOM nodes and widgets.
- // For example, for myWidget:
- //
- // | <div dojoType=myWidget>
- // | <b> here's a plain DOM node
- // | <span dojoType=subWidget>and a widget</span>
- // | <i> and another plain DOM node </i>
- // | </div>
- //
- // containerNode would point to:
- //
- // | <b> here's a plain DOM node
- // | <span dojoType=subWidget>and a widget</span>
- // | <i> and another plain DOM node </i>
- //
- // In templated widgets, "containerNode" is set via a
- // dojoAttachPoint assignment.
- //
- // containerNode must be defined for any widget that accepts innerHTML
- // (like ContentPane or BorderContainer or even Button), and conversely
- // is null for widgets that don't, like TextBox.
- containerNode: null,
+ _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);
+ this.focusNode.setAttribute("aria-selected", isSelected);
+ 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.
/*=====
- // _started: Boolean
- // startup() has completed.
- _started: false,
+ // 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,
=====*/
- // attributeMap: [protected] Object
- // attributeMap sets up a "binding" between attributes (aka properties)
- // of the widget and the widget's DOM.
- // Changes to widget attributes listed in attributeMap will be
- // reflected into the DOM.
- //
- // For example, calling 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: {id:"", dir:"", lang:"", "class":"", style:"", title:""},
+/*=====
+ // contentWidget: dijit._Widget
+ // Pointer to the real child widget
+ contentWidget: null,
+=====*/
- // _blankGif: [protected] String
- // Path to a blank 1x1 image.
- // Used by <img> nodes in templates that really get their image via CSS background-image.
- _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(),
+ baseClass: "dijitAccordionInnerContainer",
- //////////// INITIALIZATION METHODS ///////////////////////////////////////
+ // tell nested layout widget that we will take care of sizing
+ isLayoutContainer: true,
- postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
- // summary:
- // Kicks off widget instantiation. See create() for details.
- // tags:
- // private
- this.create(params, srcNodeRef);
- },
+ buildRendering: function(){
+ // Builds a template like:
+ // <div class=dijitAccordionInnerContainer>
+ // Button
+ // <div class=dijitAccordionChildWrapper>
+ // ContentPane
+ // </div>
+ // </div>
- create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
- // summary:
- // Kick off the life-cycle of a widget
- // params:
- // Hash of initialization parameters for widget, including
- // scalar values (like title, duration etc.) and functions,
- // typically callbacks like onClick.
- // srcNodeRef:
- // If a srcNodeRef (DOM node) is specified:
- // - use srcNodeRef.innerHTML as my contents
- // - if this is a behavioral widget then apply behavior
- // to that srcNodeRef
- // - otherwise, replace srcNodeRef with my generated DOM
- // tree
- // description:
- // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
- // etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget
- // for a discussion of the widget creation lifecycle.
- //
- // Of course, adventurous developers could override create entirely, but this should
- // only be done as a last resort.
- // tags:
- // private
+ // Create wrapper div, placed where the child is now
+ this.domNode = domConstruct.place("<div class='" + this.baseClass +
+ "' role='presentation'>", this.contentWidget.domNode, "after");
- // store pointer to original DOM tree
- this.srcNodeRef = dojo.byId(srcNodeRef);
+ // 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);
- // For garbage collection. An array of handles returned by Widget.connect()
- // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
- this._connects = [];
+ // 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);
+ },
- // For garbage collection. An array of handles returned by Widget.subscribe()
- // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe()
- this._subscribes = [];
+ postCreate: function(){
+ this.inherited(arguments);
- // mix in our passed parameters
- if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
- if(params){
- this.params = params;
- dojo._mixin(this, params);
- }
- this.postMixInProperties();
+ // 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);
+ }))
+ ];
+ },
- // generate an id for the widget if one wasn't specified
- // (be sure to do this before buildRendering() because that function might
- // expect the id to be there.)
- if(!this.id){
- this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
- }
- dijit.registry.add(this);
+ _setSelectedAttr: function(/*Boolean*/ isSelected){
+ this._set("selected", isSelected);
+ this.button.set("selected", isSelected);
+ if(isSelected){
+ var cw = this.contentWidget;
+ if(cw.onSelected){ cw.onSelected(); }
+ }
+ },
- this.buildRendering();
+ startup: function(){
+ // Called by _Container.addChild()
+ this.contentWidget.startup();
+ },
- 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();
+ destroy: function(){
+ this.button.destroyRecursive();
- // 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);
- }
- }
+ array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
- if(this.domNode){
- // 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();
+ delete this.contentWidget._buttonWidget;
+ delete this.contentWidget._wrapperWidget;
- // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
- if(this.srcNodeRef && !this.srcNodeRef.parentNode){
- delete this.srcNodeRef;
- }
+ this.inherited(arguments);
+ },
- this._created = true;
- },
+ destroyDescendants: function(/*Boolean*/ preserveDom){
+ // since getChildren isn't working for me, have to code this manually
+ this.contentWidget.destroyRecursive(preserveDom);
+ }
+ });
- _applyAttributes: function(){
+ var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
// summary:
- // Step during widget creation to copy all widget attributes to the
- // DOM as per attributeMap and _setXXXAttr functions.
- // description:
- // Skips over blank/false attribute values, unless they were explicitly specified
- // as parameters to the widget, since those are the default anyway,
- // and setting tabIndex="" is different than not setting tabIndex at all.
- //
- // It processes the attributes in the attribute map first, and then
- // it goes through and processes the attributes for the _setXXXAttr
- // functions that have been specified
- // tags:
- // private
- var condAttrApply = function(attr, scope){
- if((scope.params && attr in scope.params) || scope[attr]){
- scope.set(attr, scope[attr]);
- }
- };
+ // 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>
- // Do the attributes in attributeMap
- for(var attr in this.attributeMap){
- condAttrApply(attr, this);
- }
+ // duration: Integer
+ // Amount of time (in ms) it takes to slide panes
+ duration: manager.defaultDuration,
- // And also any attributes with custom setters
- dojo.forEach(this._getSetterAttributes(), function(a){
- if(!(a in this.attributeMap)){
- condAttrApply(a, this);
+ // 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){
+ var style = this.selectedChildWidget.containerNode.style;
+ style.display = "";
+ style.overflow = "auto";
+ this.selectedChildWidget._wrapperWidget.set("selected", true);
}
- }, this);
- },
+ },
- _getSetterAttributes: function(){
- // summary:
- // Returns list of attributes with custom setters for this widget
- var ctor = this.constructor;
- if(!ctor._setterAttrs){
- var r = (ctor._setterAttrs = []),
- attrs,
- proto = ctor.prototype;
- for(var fxName in proto){
- if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){
- r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1));
+ 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);
}
- }
- return ctor._setterAttrs; // String[]
- },
+ },
- 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
- },
+ _setupChild: function(child){
+ // Overrides _LayoutWidget._setupChild().
+ // Put wrapper widget around the child widget, showing title
- buildRendering: function(){
- // summary:
- // Construct the UI for this widget, setting this.domNode
- // description:
- // Most widgets will mixin `dijit._Templated`, which implements this
- // method.
- // tags:
- // protected
+ child._wrapperWidget = AccordionInnerContainer({
+ contentWidget: child,
+ buttonWidget: this.buttonWidget,
+ id: child.id + "_wrapper",
+ dir: child.dir,
+ lang: child.lang,
+ textDir: child.textDir,
+ parent: this
+ });
- if(!this.domNode){
- // Create root node if it wasn't created by _Templated
- this.domNode = this.srcNodeRef || dojo.create('div');
- }
+ this.inherited(arguments);
+ },
- // 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( dojo.map(classes, function(name){ return name+"Rtl"; }));
+ addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
+ 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);
}
- dojo.addClass(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
- },
+ removeChild: function(child){
+ // Overrides _LayoutWidget.removeChild().
- startup: function(){
- // summary:
- // Processing after the DOM fragment is added to the document
- // description:
- // Called after a widget and its children have been created and added to the page,
- // and all related widgets have finished their create() cycle, up through postCreate().
- // This is useful for composite widgets that need to control or layout sub-widgets.
- // Many layout widgets can use this as a wiring phase.
- this._started = true;
- },
+ // Destroy 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;
+ }
- //////////// DESTROY FUNCTIONS ////////////////////////////////
+ domClass.remove(child.domNode, "dijitHidden");
- 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.inherited(arguments);
+ },
- this._beingDestroyed = true;
- this.destroyDescendants(preserveDom);
- this.destroy(preserveDom);
- },
+ 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(/*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
+ 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);
+ },
- this._beingDestroyed = true;
- this.uninitialize();
- var d = dojo,
- dfe = d.forEach,
- dun = d.unsubscribe;
- dfe(this._connects, function(array){
- dfe(array, d.disconnect);
- });
- dfe(this._subscribes, function(handle){
- dun(handle);
- });
+ _showChild: function(child){
+ // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
+ child._wrapperWidget.containerNode.style.display="block";
+ return this.inherited(arguments);
+ },
- // destroy widgets created as part of template, etc.
- dfe(this._supportingWidgets || [], function(w){
- if(w.destroyRecursive){
- w.destroyRecursive();
- }else if(w.destroy){
- w.destroy();
+ _hideChild: function(child){
+ // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
+ child._wrapperWidget.containerNode.style.display="none";
+ this.inherited(arguments);
+ },
+
+ _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ 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;
}
- });
- this.destroyRendering(preserveDom);
- dijit.registry.remove(this.id);
- this._destroyed = true;
- },
+ 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;
+ }
- 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
+ var self = this;
- if(this.bgIframe){
- this.bgIframe.destroy(preserveDom);
- delete this.bgIframe;
- }
+ if(newWidget){
+ newWidget._wrapperWidget.set("selected", true);
- if(this.domNode){
- if(preserveDom){
- dojo.removeAttr(this.domNode, "widgetId");
- }else{
- dojo.destroy(this.domNode);
+ 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);
+ }
}
- delete this.domNode;
- }
- if(this.srcNodeRef){
- if(!preserveDom){
- dojo.destroy(this.srcNodeRef);
+ if(oldWidget){
+ oldWidget._wrapperWidget.set("selected", false);
+ if(!animate){
+ this._hideChild(oldWidget);
+ }
}
- 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.
+ if(animate){
+ var newContents = newWidget._wrapperWidget.containerNode,
+ oldContents = oldWidget._wrapperWidget.containerNode;
- // get all direct descendants and destroy them recursively
- dojo.forEach(this.getChildren(), function(widget){
- if(widget.destroyRecursive){
- widget.destroyRecursive(preserveDom);
+ // 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._Widget*/ 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(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/layout/AccordionPane"];
+ require(requires); // use indirection so modules not rolled into a build
});
- },
+ }
- uninitialize: function(){
- // summary:
- // Stub function. Override to implement custom widget tear-down
- // behavior.
- // tags:
- // protected
- return false;
- },
+ // For monkey patching
+ AccordionContainer._InnerContainer = AccordionInnerContainer;
+ AccordionContainer._Button = AccordionButton;
- ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
+ return AccordionContainer;
+});
- _setClassAttr: function(/*String*/ value){
- // summary:
- // Custom setter for the CSS "class" attribute
- // tags:
- // protected
- var mapNode = this[this.attributeMap["class"] || 'domNode'];
- dojo.replaceClass(mapNode, value, this["class"]);
- this._set("class", value);
- },
+},
+'dijit/form/_AutoCompleterMixin':function(){
+define("dijit/form/_AutoCompleterMixin", [
+ "dojo/_base/connect", // keys keys.SHIFT
+ "dojo/data/util/filter", // patternToRegExp
+ "dojo/_base/declare", // declare
+ "dojo/_base/Deferred", // Deferred.when
+ "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/_base/sniff", // has("ie")
+ "dojo/string", // string.substitute
+ "dojo/_base/window", // win.doc.selection.createRange
+ "./DataList",
+ "../registry", // registry.byId
+ "./_TextBoxMixin" // defines _TextBoxMixin.selectInputText
+], function(connect, filter, declare, Deferred, domAttr, event, keys, lang, query, regexp, has, string, win,
+ DataList, registry, _TextBoxMixin){
+
+ // module:
+ // dijit/form/_AutoCompleterMixin
+ // summary:
+ // A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
- _setStyleAttr: function(/*String||Object*/ value){
+
+ return declare("dijit.form._AutoCompleterMixin", null, {
// 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
+ // A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
// description:
- // Determines which node to set the style on based on style setting
- // in attributeMap.
+ // All widgets that mix in dijit.form._AutoCompleterMixin must extend `dijit.form._FormValueWidget`.
// tags:
// protected
- var mapNode = this[this.attributeMap.style || 'domNode'];
+ // item: Object
+ // This is the item returned by the dojo.data.store implementation that
+ // provides the data for this ComboBox, it's the currently selected item.
+ item: null,
- // Note: technically we should revert any style setting made in a previous call
- // to his method, but that's difficult to keep track of.
+ // pageSize: Integer
+ // Argument to data provider.
+ // Specifies number of search results per page (before hitting "next" button)
+ pageSize: Infinity,
- if(dojo.isObject(value)){
- dojo.style(mapNode, value);
- }else{
- if(mapNode.style.cssText){
- mapNode.style.cssText += "; " + value;
- }else{
- mapNode.style.cssText = value;
- }
- }
+ // store: [const] dojo.store.api.Store
+ // Reference to data provider object used by this ComboBox
+ store: null,
- this._set("style", value);
- },
+ // 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:{},
- _attrToDom: function(/*String*/ attr, /*String*/ value){
- // summary:
- // Reflect a widget attribute (title, tabIndex, duration etc.) to
- // the widget DOM, as specified in attributeMap.
- // Note some attributes like "type"
- // cannot be processed this way as they are not mutable.
+ // query: Object
+ // A query that can be passed to 'store' to initially filter the items,
+ // before doing further filtering based on `searchAttr` and the key.
+ // Any reference to the `searchAttr` is ignored.
+ query: {},
+
+ // 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".
//
- // tags:
- // private
+ // 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",
- var commands = this.attributeMap[attr];
- dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){
+ // searchDelay: Integer
+ // Delay in milliseconds between when user types something and we start
+ // searching based on that value
+ searchDelay: 100,
- // 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
+ // searchAttr: String
+ // Search for items in the data store where this attribute (in the item)
+ // matches what the user typed
+ searchAttr: "name",
- switch(type){
- case "attribute":
- if(dojo.isFunction(value)){ // functions execute in the context of the widget
- value = dojo.hitch(this, value);
- }
+ // 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: "",
- // 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);
+ // labelType: String
+ // Specifies how to interpret the labelAttr in the data store items.
+ // Can be "html" or "text".
+ labelType: "text",
+
+ // queryExpr: String
+ // This specifies what query ComboBox/FilteringSelect sends to the data store,
+ // based on what the user has typed. Changing this expression will modify
+ // whether the drop down shows only exact matches, a "starting with" match,
+ // etc. Use it in conjunction with highlightMatch.
+ // 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 ComboBox/FilteringSelect should ignore case when matching possible items
+ ignoreCase: true,
+
+ // 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,
- dojo.attr(mapNode, attrName, value);
+ _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 = win.doc.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);
+ },
+
+ _abortQuery: function(){
+ // stop in-progress query
+ if(this.searchTimer){
+ clearTimeout(this.searchTimer);
+ this.searchTimer = null;
+ }
+ if(this._fetchHandle){
+ if(this._fetchHandle.cancel){
+ this._cancelingQuery = true;
+ this._fetchHandle.cancel();
+ this._cancelingQuery = false;
+ }
+ this._fetchHandle = null;
+ }
+ },
+
+ _onInput: function(/*Event*/ evt){
+ // summary:
+ // Handles paste events
+ this.inherited(arguments);
+ if(evt.charOrCode == 229){ // IME or cut/paste event
+ this._onKey(evt);
+ }
+ },
+
+ _onKey: function(/*Event*/ evt){
+ // summary:
+ // Handles keyboard 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;
+ var pw = this.dropDown;
+ var highlighted = null;
+ this._prev_key_backspace = false;
+ 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(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 "innerText":
- mapNode.innerHTML = "";
- mapNode.appendChild(dojo.doc.createTextNode(value));
+
+ 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);
+ break;
+ }else if(highlighted == pw.previousButton){
+ this._nextSearch(-1);
+ event.stop(evt);
+ break;
+ }
+ }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
+ }
+ // default case:
+ // if enter pressed while drop down is open, or for FilteringSelect,
+ // if we are in the middle of a query to convert a directly typed in value to an item,
+ // prevent submit
+ if(this._opened || this._fetchHandle){
+ event.stop(evt);
+ }
+ // 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;
- case "innerHTML":
- mapNode.innerHTML = value;
+
+ case ' ':
+ if(highlighted){
+ // user is effectively clicking a choice in the drop down menu
+ event.stop(evt);
+ this._selectOption(highlighted);
+ this.closeDropDown();
+ }else{
+ // user typed a space into the input box, treat as normal character
+ doSearch = true;
+ }
break;
- case "class":
- dojo.replaceClass(mapNode, value, this[attr]);
+
+ case keys.DELETE:
+ case keys.BACKSPACE:
+ this._prev_key_backspace = true;
+ doSearch = true;
break;
+
+ default:
+ // Non char keys (F1-F12 etc..) shouldn't open list.
+ // Ascii characters and IME input (Chinese, Japanese etc.) should.
+ //IME input produces keycode == 229.
+ doSearch = typeof key == 'string' || key == 229;
}
- }, this);
- },
+ if(doSearch){
+ // need to wait a tad before start search so that the event
+ // bubbles through DOM and we have value visible
+ this.item = undefined; // undefined means item needs to be set
+ this.searchTimer = setTimeout(lang.hitch(this, "_startSearchFromInput"),1);
+ }
+ },
- get: function(name){
- // summary:
- // Get a property from a widget.
- // name:
- // The property to get.
- // description:
- // Get a named property from a widget. The property may
- // potentially be retrieved via a getter method. If no getter is defined, this
- // just retrieves the object's property.
- // For example, if the widget has a properties "foo"
- // and "bar" and a method named "_getFooAttr", calling:
- // | myWidget.get("foo");
- // would be equivalent to writing:
- // | widget._getFooAttr();
- // and:
- // | myWidget.get("bar");
- // would be equivalent to writing:
- // | widget.bar;
- var names = this._getAttrNames(name);
- return this[names.g] ? this[names.g]() : this[name];
- },
-
- set: function(name, value){
- // summary:
- // Set a property on a widget
- // name:
- // The property to set.
- // value:
- // The value to set in the property.
- // description:
- // Sets named properties on a widget which may potentially be handled by a
- // setter in the widget.
- // For example, if the widget has a properties "foo"
- // and "bar" and a method named "_setFooAttr", calling:
- // | myWidget.set("foo", "Howdy!");
- // would be equivalent to writing:
- // | widget._setFooAttr("Howdy!");
- // and:
- // | myWidget.set("bar", 3);
- // would be equivalent to writing:
- // | widget.bar = 3;
- //
- // set() may also be called with a hash of name/value pairs, ex:
- // | myWidget.set({
- // | foo: "Howdy",
- // | bar: 3
- // | })
- // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
+ _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.
- if(typeof name === "object"){
- for(var x in name){
- this.set(x, name[x]);
+ 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);
}
- return this;
- }
- var names = this._getAttrNames(name);
- if(this[names.s]){
- // use the explicit setter
- var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
- }else{
- // if param is specified as DOM node attribute, copy it
- if(name in this.attributeMap){
- this._attrToDom(name, value);
+ },
+
+ _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
+ this._fetchHandle = null;
+ if( this.disabled ||
+ this.readOnly ||
+ (query[this.searchAttr] !== this._lastQuery) // TODO: better way to avoid getting unwanted notify
+ ){
+ return;
+ }
+ 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._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.charAt(0).toUpperCase() + name.substr(1);
- return (apn[name] = {
- n: name+"Node",
- s: "_set"+uc+"Attr",
- g: "_get"+uc+"Attr"
- });
- },
+ // 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.
- _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._watchCallbacks && this._created && value !== oldValue){
- this._watchCallbacks(name, oldValue, value);
- }
- },
+ var nodes = this.dropDown.createOptions(
+ results,
+ options,
+ lang.hitch(this, "_getMenuLabelFromItem")
+ );
- 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
- },
+ // show our list (only if we have content, else nothing)
+ this._showResultList();
- 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.
+ // #4091:
+ // tell the screen reader that the paging callback finished by
+ // shouting the next choice
+ if(options.direction){
+ if(1 == options.direction){
+ this.dropDown.highlightFirstOption();
+ }else if(-1 == 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(nodes[1]); // 1st real item
+ }
+ },
- return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[]
- },
+ _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");
+ },
- getChildren: function(){
- // summary:
- // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
- // Does not return nested widgets, nor widgets that are part of this widget's template.
- return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[]
- },
+ 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.
- connect: function(
- /*Object|null*/ obj,
- /*String|Function*/ event,
- /*String|Function*/ method){
- // summary:
- // Connects specified obj/event to specified method of this object
- // and registers for disconnect() on widget destroy.
- // description:
- // Provide widget-specific analog to dojo.connect, except with the
- // implicit use of this widget as the target object.
- // 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 dijit.form.Button();
- // | // when foo.bar() is called, call the listener we're going to
- // | // provide in the scope of btn
- // | btn.connect(foo, "bar", function(){
- // | console.debug(this.toString());
- // | });
- // tags:
- // protected
+ this._startSearchAll();
+ },
- var handles = [dojo._connect(obj, event, this, method)];
- this._connects.push(handles);
- return handles; // _Widget.Handle
- },
+ isLoaded: function(){
+ // signal to _HasDropDown that it needs to call loadDropDown() to load the
+ // drop down asynchronously before displaying it
+ return false;
+ },
- disconnect: function(/* _Widget.Handle */ handles){
- // summary:
- // Disconnects handle created by `connect`.
- // Also removes handle from this widget's list of connects.
- // tags:
- // protected
- for(var i=0; i<this._connects.length; i++){
- if(this._connects[i] == handles){
- dojo.forEach(handles, dojo.disconnect);
- this._connects.splice(i, 1);
- return;
+ 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");
}
- }
- },
+ },
- subscribe: function(
- /*String*/ topic,
- /*String|Function*/ method){
- // summary:
- // Subscribes to the specified topic and calls the specified method
- // of this object and registers for unsubscribe() on widget destroy.
- // description:
- // Provide widget-specific analog to dojo.subscribe, except with the
- // implicit use of this widget as the target object.
- // example:
- // | var btn = new dijit.form.Button();
- // | // when /my/topic is published, this button changes its label to
- // | // be the parameter of the topic.
- // | btn.subscribe("/my/topic", function(v){
- // | this.set("label", v);
- // | });
- var handle = dojo.subscribe(topic, this, method);
+ _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();
+ }
+ },
- // return handles for Any widget that may need them
- this._subscribes.push(handle);
- return handle;
- },
+ _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);
+ },
- unsubscribe: function(/*Object*/ handle){
- // summary:
- // Unsubscribes handle created by this.subscribe.
- // Also removes handle from this widget's list of subscriptions
- for(var i=0; i<this._subscribes.length; i++){
- if(this._subscribes[i] == handle){
- dojo.unsubscribe(handle);
- this._subscribes.splice(i, 1);
+ _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{
+ newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
+ this.store.getValue(node.item, this.searchAttr) : node.item[this.searchAttr]).toString();
+ this.set('item', node.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._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
+ },
+
+ _getQueryString: function(/*String*/ text){
+ return string.substitute(this.queryExpr, [text]);
+ },
+
+ _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.
+
+ // Setup parameters to be passed to store.query().
+ // Create a new query to prevent accidentally querying for a hidden
+ // value from FilteringSelect's keyField
+ var query = lang.clone(this.query); // #5970
+ var options = {
+ start: 0,
+ count: this.pageSize,
+ queryOptions: { // remove for 2.0
+ ignoreCase: this.ignoreCase,
+ deep: true
+ }
+ };
+ lang.mixin(options, this.fetchProperties);
+
+ // Generate query
+ var qs = this._getQueryString(key), 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;
+
+ // Function to run the query, wait for the results, and then call _openResultList()
+ var _this = this,
+ startQuery = function(){
+ var resPromise = _this._fetchHandle = _this.store.query(query, options);
+ Deferred.when(resPromise, function(res){
+ _this._fetchHandle = null;
+ res.total = resPromise.total;
+ _this._openResultList(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());
+ _this.closeDropDown();
+ }
+ });
+ };
+
+ // #5970: 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.searchTimer = setTimeout(lang.hitch(this, function(query, _this){
+ this.searchTimer = null;
+
+ startQuery();
+
+ // Setup method to handle clicking next/previous buttons to page through results
+ this._nextSearch = this.dropDown.onPage = function(direction){
+ options.start += options.count * direction;
+ // tell callback the direction of the paging so the screen
+ // reader knows which menu option to shout
+ options.direction = direction;
+ startQuery();
+ _this.focus();
+ };
+ }, query, this), this.searchDelay);
+ },
+
+ _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 ///////////////////////////////////////
+
+ constructor: function(){
+ this.query={};
+ this.fetchProperties={};
+ },
+
+ postMixInProperties: function(){
+ if(!this.store){
+ var srcNodeRef = this.srcNodeRef;
+ var list = this.list;
+ if(list){
+ this.store = registry.byId(list);
+ }else{
+ // 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];
+ }
+ }
+ }
+
+ this.inherited(arguments);
+ },
+
+ 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){
+ label[0].id = (this.id+"_label");
+ this.domNode.setAttribute("aria-labelledby", label[0].id);
+
+ }
+ this.inherited(arguments);
+ },
+
+ _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._escapeHtml(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
+ return this._escapeHtml(label).replace(
+ // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
+ new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
+ '<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*/ item, /*dojo.store.api.Store*/ store){
+ // summary:
+ // Computes the label to display based on the dojo.data store item.
+ // 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){ 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);
+ }
}
- },
+ });
+});
- isLeftToRight: function(){
+},
+'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\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>",
+'dijit/form/MappedTextBox':function(){
+define("dijit/form/MappedTextBox", [
+ "dojo/_base/declare", // declare
+ "dojo/dom-construct", // domConstruct.place
+ "./ValidationTextBox"
+], function(declare, domConstruct, ValidationTextBox){
+
+/*=====
+ var ValidationTextBox = dijit.form.ValidationTextBox;
+=====*/
+
+ // module:
+ // dijit/form/MappedTextBox
+ // 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.
+
+ return declare("dijit.form.MappedTextBox", ValidationTextBox, {
// summary:
- // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
+ // 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
- return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean
- },
- placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
- // summary:
- // Place this widget's domNode reference somewhere in the DOM based
- // on standard dojo.place conventions, or passing a Widget reference that
- // contains and addChild member.
- //
- // description:
- // A convenience function provided in all _Widgets, providing a simple
- // shorthand mechanism to put an existing (or newly created) Widget
- // somewhere in the dom, and allow chaining.
- //
- // reference:
- // The String id of a domNode, a domNode reference, or a reference to a Widget posessing
- // an addChild method.
- //
- // position:
- // If passed a string or domNode reference, the position argument
- // accepts a string just as dojo.place does, one of: "first", "last",
- // "before", or "after".
- //
- // If passed a _Widget reference, and that widget reference has an ".addChild" method,
- // it will be called passing this widget instance into that method, supplying the optional
- // position index passed.
- //
- // returns:
- // dijit._Widget
- // Provides a useful return of the newly created dijit._Widget instance so you
- // can "chain" this function by instantiating, placing, then saving the return value
- // to a variable.
- //
- // example:
- // | // create a Button with no srcNodeRef, and place it in the body:
- // | var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body());
- // | // now, 'button' is still the widget reference to the newly created button
- // | dojo.connect(button, "onClick", function(e){ console.log('click'); });
- //
- // example:
- // | // create a button out of a node with id="src" and append it to id="wrapper":
- // | var button = new dijit.form.Button({},"src").placeAt("wrapper");
- //
- // example:
- // | // place a new button as the first element of some div
- // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
- //
- // example:
- // | // create a contentpane and add it to a TabContainer
- // | var tc = dijit.byId("myTabs");
- // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
+ postMixInProperties: function(){
+ this.inherited(arguments);
- if(reference.declaredClass && reference.addChild){
- reference.addChild(this, position);
- }else{
- dojo.place(this.domNode, reference, position);
+ // 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);
}
- return this;
- }
+ });
});
-})();
+},
+'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=\"presentation\"\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", // dojo.store.util.QueryResults
+ "./_AutoCompleterMixin",
+ "./_ComboBoxMenu",
+ "../_HasDropDown",
+ "dojo/text!./templates/DropDownBox.html"
+], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
-}
+/*=====
+ var _AutoCompleterMixin = dijit.form._AutoCompleterMixin;
+ var _ComboBoxMenu = dijit.form._ComboBoxMenu;
+ var _HasDropDown = dijit._HasDropDown;
+=====*/
-if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._Widget"] = true;
-dojo.provide("dijit._Widget");
+ // module:
+ // dijit/form/ComboBoxMixin
+ // summary:
+ // Provides main functionality of ComboBox widget
+
+ 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"
+ },
-////////////////// DEFERRED CONNECTS ///////////////////
+ _setHasDownArrowAttr: function(/*Boolean*/ val){
+ this._set("hasDownArrow", val);
+ this._buttonNode.style.display = val ? "" : "none";
+ },
-// This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets'
-// DOM nodes) until someone actually needs to monitor that event.
-dojo.connect(dojo, "_connect",
- function(/*dijit._Widget*/ widget, /*String*/ event){
- if(widget && dojo.isFunction(widget._onConnect)){
- widget._onConnect(event);
+ _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.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(); });
+ var fetchHandle = this.fetch(lang.mixin({
+ query: query,
+ onBegin: function(count){
+ deferred.total = 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.
+ if(this.params.store){
+ this._setStoreAttr(this.params.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){
+ 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._connectOnUseEventHandler = function(/*Event*/ event){};
-
-////////////////// ONDIJITCLICK SUPPORT ///////////////////
-
-// Keep track of where the last keydown event was, to help avoid generating
-// spurious ondijitclick events when:
-// 1. focus is on a <button> or <a>
-// 2. user presses then releases the ENTER key
-// 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
-// 4. onkeyup event fires, causing the ondijitclick handler to fire
-dijit._lastKeyDownNode = null;
-if(dojo.isIE){
- (function(){
- var keydownCallback = function(evt){
- dijit._lastKeyDownNode = evt.srcElement;
- };
- dojo.doc.attachEvent('onkeydown', keydownCallback);
- dojo.addOnWindowUnload(function(){
- dojo.doc.detachEvent('onkeydown', keydownCallback);
- });
- })();
-}else{
- dojo.doc.addEventListener('keydown', function(evt){
- dijit._lastKeyDownNode = evt.target;
- }, true);
-}
-
-(function(){
+},
+'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
+ ".." // for exporting dijit._setSelectionRange, dijit.selectInputText
+], function(array, declare, dom, event, keys, lang, dijit){
+
+// module:
+// dijit/form/_TextBoxMixin
+// summary:
+// A mixin for textbox form input widgets
-dojo.declare("dijit._Widget", dijit._WidgetBase, {
+var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
// summary:
- // Base class for all Dijit widgets.
- //
- // Extends _WidgetBase, adding support for:
- // - deferred connections
- // A call like dojo.connect(myWidget, "onMouseMove", func)
- // will essentially do a dojo.connect(myWidget.domNode, "onMouseMove", func)
- // - ondijitclick
- // Support new dojoAttachEvent="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 id like .dj_ie on <html> node)
- // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
-
+ // A mixin for textbox form input widgets
- ////////////////// DEFERRED CONNECTS ///////////////////
+ // trim: Boolean
+ // Removes leading and trailing whitespace if true. Default is false.
+ trim: false,
- // _deferredConnects: [protected] Object
- // attributeMap addendum for event handlers that should be connected only on first use
- _deferredConnects: {
- onClick: "",
- onDblClick: "",
- onKeyDown: "",
- onKeyPress: "",
- onKeyUp: "",
- onMouseMove: "",
- onMouseDown: "",
- onMouseOut: "",
- onMouseOver: "",
- onMouseLeave: "",
- onMouseEnter: "",
- onMouseUp: ""
- },
-
- onClick: dijit._connectOnUseEventHandler,
- /*=====
- onClick: function(event){
- // summary:
- // Connect to this function to receive notifications of mouse click events.
- // event:
- // mouse Event
- // tags:
- // callback
- },
- =====*/
- onDblClick: dijit._connectOnUseEventHandler,
- /*=====
- onDblClick: function(event){
- // summary:
- // Connect to this function to receive notifications of mouse double click events.
- // event:
- // mouse Event
- // tags:
- // callback
- },
- =====*/
- onKeyDown: dijit._connectOnUseEventHandler,
- /*=====
- onKeyDown: function(event){
- // summary:
- // Connect to this function to receive notifications of keys being pressed down.
- // event:
- // key Event
- // tags:
- // callback
- },
- =====*/
- onKeyPress: dijit._connectOnUseEventHandler,
- /*=====
- onKeyPress: function(event){
+ // 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:
- // Connect to this function to receive notifications of printable keys being typed.
- // event:
- // key Event
- // tags:
- // callback
+ // 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);
},
- =====*/
- onKeyUp: dijit._connectOnUseEventHandler,
- /*=====
- onKeyUp: function(event){
+
+ _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
- // Connect to this function to receive notifications of keys being released.
- // event:
- // key Event
- // tags:
- // callback
+ // 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 && formattedValue != 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]);
},
- =====*/
- onMouseDown: dijit._connectOnUseEventHandler,
- /*=====
- onMouseDown: function(event){
+
+ // 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:
- // Connect to this function to receive notifications of when the mouse button is pressed down.
- // event:
- // mouse Event
- // tags:
- // callback
+ // 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);
},
- =====*/
- onMouseMove: dijit._connectOnUseEventHandler,
- /*=====
- onMouseMove: function(event){
+
+ _setDisplayedValueAttr: function(/*String*/ value){
// summary:
- // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
- // event:
- // mouse Event
- // tags:
- // callback
+ // 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 || value === 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);
+ }
},
- =====*/
- onMouseOut: dijit._connectOnUseEventHandler,
- /*=====
- onMouseOut: function(event){
+
+ format: function(value /*=====, constraints =====*/){
// summary:
- // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
- // event:
- // mouse Event
+ // Replaceable function to convert a value to a properly formatted string.
+ // value: String
+ // constraints: Object
// tags:
- // callback
+ // protected extension
+ return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
},
- =====*/
- onMouseOver: dijit._connectOnUseEventHandler,
- /*=====
- onMouseOver: function(event){
+
+ parse: function(value /*=====, constraints =====*/){
// summary:
- // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
- // event:
- // mouse Event
+ // Replaceable function to convert a formatted string to a value
+ // value: String
+ // constraints: Object
// tags:
- // callback
+ // protected extension
+
+ return value; // String
},
- =====*/
- onMouseLeave: dijit._connectOnUseEventHandler,
- /*=====
- onMouseLeave: function(event){
+
+ _refreshState: function(){
// summary:
- // Connect to this function to receive notifications of when the mouse moves off of this widget.
- // event:
- // mouse Event
+ // 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:
- // callback
+ // protected
},
- =====*/
- onMouseEnter: dijit._connectOnUseEventHandler,
+
/*=====
- onMouseEnter: function(event){
+ onInput: function(event){
// summary:
- // Connect to this function to receive notifications of when the mouse moves onto this widget.
+ // 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:
- // mouse Event
+ // keydown | keypress | cut | paste | input
// tags:
// callback
},
=====*/
- onMouseUp: dijit._connectOnUseEventHandler,
- /*=====
- onMouseUp: function(event){
+ onInput: function(){},
+
+ __skipInputEvent: false,
+ _onInput: function(){
// summary:
- // Connect to this function to receive notifications of when the mouse button is released.
- // event:
- // mouse Event
- // tags:
- // callback
+ // 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._refreshState();
+
+ // In case someone is watch()'ing for changes to displayedValue
+ this._set("displayedValue", this.get("displayedValue"));
},
- =====*/
- create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
- // To avoid double-connects, remove entries from _deferredConnects
- // that have been setup manually by a subclass (ex, by dojoAttachEvent).
- // If a subclass has redefined a callback (ex: onClick) then assume it's being
- // connected to manually.
- this._deferredConnects = dojo.clone(this._deferredConnects);
- for(var attr in this.attributeMap){
- delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
- }
- for(attr in this._deferredConnects){
- if(this[attr] !== dijit._connectOnUseEventHandler){
- delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists
- }
- }
+ 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);
- if(this.domNode){
- // If the developer has specified a handler as a widget parameter
- // (ex: new Button({onClick: ...})
- // then naturally need to connect from DOM node to that handler immediately,
- for(attr in this.params){
- this._onConnect(attr);
+ // 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 charCode = e.charOrCode || e.keyCode || 229;
+ if(e.type == "keydown"){
+ switch(charCode){ // ignore "state" keys
+ case keys.SHIFT:
+ case keys.ALT:
+ case keys.CTRL:
+ case keys.META:
+ case keys.CAPS_LOCK:
+ return;
+ default:
+ if(charCode >= 65 && charCode <= 90){ return; } // keydown for A-Z can be processed with keypress
+ }
}
- }
+ if(e.type == "keypress" && typeof charCode != "string"){ return; }
+ 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 = lang.mixin({}, e, {
+ charOrCode: charCode,
+ wasConsumed: false,
+ preventDefault: function(){
+ faux.wasConsumed = true;
+ e.preventDefault();
+ },
+ stopPropagation: function(){ e.stopPropagation(); }
+ });
+ // give web page author a chance to consume the event
+ if(this.onInput(faux) === false){
+ event.stop(faux); // return false means stop
+ }
+ if(faux.wasConsumed){ return; } // if preventDefault was called
+ setTimeout(lang.hitch(this, "_onInput", faux), 0); // widget notification after key has posted
+ };
+ array.forEach([ "onkeydown", "onkeypress", "onpaste", "oncut", "oninput", "oncompositionend" ], function(event){
+ this.connect(this.textbox, event, handleEvent);
+ }, this);
},
- _onConnect: function(/*String*/ event){
+ _blankValue: '', // if the textbox is blank, what value should be reported
+ filter: function(val){
// summary:
- // Called when someone connects to one of my handlers.
- // "Turn on" that handler if it isn't active yet.
+ // 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
//
- // This is also called for every single initialization parameter
- // so need to do nothing for parameters like "id".
// tags:
- // private
- if(event in this._deferredConnects){
- var mapNode = this[this._deferredConnects[event] || 'domNode'];
- this.connect(mapNode, event.toLowerCase(), event);
- delete this._deferredConnects[event];
+ // 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;
},
- ////////////////// FOCUS RELATED ///////////////////
- // _onFocus() and _onBlur() are called by the focus manager
-
- // focused: [readonly] Boolean
- // This widget or a widget it contains has focus, or is "active" because
- // it was recently clicked.
- focused: false,
-
- isFocusable: function(){
- // summary:
- // Return true if this widget can currently be focused
- // and false if not
- return this.focus && (dojo.style(this.domNode, "display") != "none");
+ _setBlurValue: function(){
+ this._setValueAttr(this.get('value'), true);
},
- 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(e){
+ if(this.disabled){ return; }
+ this._setBlurValue();
+ this.inherited(arguments);
- 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
+ if(this._selectOnClickHandle){
+ this.disconnect(this._selectOnClickHandle);
+ }
},
- _onFocus: function(e){
- // summary:
- // This is where widgets do processing for when they are active,
- // such as changing CSS classes. See onFocus() for more details.
- // tags:
- // protected
- this.onFocus();
+ _isTextSelected: function(){
+ return this.textbox.selectionStart == this.textbox.selectionEnd;
},
- _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();
- },
+ _onFocus: function(/*String*/ by){
+ if(this.disabled || this.readOnly){ return; }
- ////////////////// DEPRECATED METHODS ///////////////////
+ // 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);
- setAttribute: function(/*String*/ attr, /*anything*/ value){
- // summary:
- // Deprecated. Use set() instead.
- // tags:
- // deprecated
- dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
- this.set(attr, value);
+ // 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);
+ }
+ });
+ }
+ // 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();
},
- attr: function(/*String|Object*/name, /*Object?*/value){
+ reset: function(){
+ // Overrides dijit._FormWidget.reset().
+ // Additionally resets the displayed textbox value to ''
+ this.textbox.value = '';
+ this.inherited(arguments);
+ },
+ _setTextDirAttr: function(/*String*/ textDir){
// 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.
+ // Setter for textDir.
// description:
- // This method is deprecated, use get() or set() directly.
+ // Users shouldn't call this function; they should be calling
+ // set('textDir', value)
+ // tags:
+ // private
- // Print deprecation warning but only once per calling function
- if(dojo.config.isDebug){
- var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
- caller = (arguments.callee.caller || "unknown caller").toString();
- if(!alreadyCalledHash[caller]){
- dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
- caller, "", "2.0");
- alreadyCalledHash[caller] = true;
- }
+ // 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);
}
+ }
+});
- var args = arguments.length;
- if(args >= 2 || typeof name === "object"){ // setter
- return this.set.apply(this, arguments);
- }else{ // getter
- return this.get(name);
- }
- },
-
- ////////////////// ONDIJITCLICK SUPPORT ///////////////////
- // nodesWithKeyClick: [private] String[]
- // List of nodes that correctly handle click events via native browser support,
- // and don't need dijit's help
- nodesWithKeyClick: ["input", "button"],
+_TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
+ if(element.setSelectionRange){
+ element.setSelectionRange(start, stop);
+ }
+};
- connect: function(
- /*Object|null*/ obj,
- /*String|Function*/ event,
- /*String|Function*/ method){
- // summary:
- // Connects specified obj/event to specified method of this object
- // and registers for disconnect() on widget destroy.
- // description:
- // Provide widget-specific analog to dojo.connect, except with the
- // implicit use of this widget as the target object.
- // This version of connect also provides a special "ondijitclick"
- // event which triggers on a click or space or enter keyup.
- // 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 dijit.form.Button();
- // | // when foo.bar() is called, call the listener we're going to
- // | // provide in the scope of btn
- // | btn.connect(foo, "bar", function(){
- // | console.debug(this.toString());
- // | });
- // tags:
- // protected
+_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).
- var d = dojo,
- dc = d._connect,
- handles = this.inherited(arguments, [obj, event == "ondijitclick" ? "onclick" : event, method]);
-
- if(event == "ondijitclick"){
- // add key based click activation for unsupported nodes.
- // do all processing onkey up to prevent spurious clicks
- // for details see comments at top of this file where _lastKeyDownNode is defined
- if(d.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button
- var m = d.hitch(this, method);
- handles.push(
- dc(obj, "onkeydown", this, function(e){
- //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
- if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
- !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
- // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
- dijit._lastKeyDownNode = e.target;
-
- // Stop event to prevent scrolling on space key in IE.
- // But don't do this for _HasDropDown because it surpresses the onkeypress
- // event needed to open the drop down when the user presses the SPACE key.
- if(!("openDropDown" in this && obj == this._buttonNode)){
- e.preventDefault();
- }
- }
- }),
- dc(obj, "onkeyup", this, function(e){
- //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
- if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
- e.target == dijit._lastKeyDownNode && // === breaks greasemonkey
- !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
- //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
- dijit._lastKeyDownNode = null;
- return m(e);
- }
- })
- );
- }
- }
+ // 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 handles; // _Widget.Handle
- },
+return _TextBoxMixin;
+});
- ////////////////// MISCELLANEOUS METHODS ///////////////////
+},
+'dijit/form/SimpleTextarea':function(){
+define("dijit/form/SimpleTextarea", [
+ "dojo/_base/declare", // declare
+ "dojo/dom-class", // domClass.add
+ "dojo/_base/sniff", // has("ie") has("opera")
+ "dojo/_base/window", // win.doc.selection win.doc.selection.createRange
+ "./TextBox"
+], function(declare, domClass, has, win, TextBox){
- _onShow: function(){
- // summary:
- // Internal method called when this widget is made visible.
- // See `onShow` for details.
- this.onShow();
- },
+/*=====
+ var TextBox = dijit.form.TextBox;
+=====*/
- 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
+// module:
+// dijit/form/SimpleTextarea
+// 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.
+
+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 dijit.form.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);
},
- 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
+ buildRendering: function(){
+ this.inherited(arguments);
+ if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
+ domClass.add(this.textbox, "dijitTextAreaCols");
+ }
},
- 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
+ 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);
+ },
- return true; // Boolean
+ _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(win.doc.selection){ //IE
+ textarea.focus();
+ var range = win.doc.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\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n",
+'dijit/_base/window':function(){
+define("dijit/_base/window", [
+ "dojo/window", // windowUtils.get
+ ".." // export symbol to dijit
+], function(windowUtils, dijit){
+ // module:
+ // dijit/_base/window
+ // summary:
+ // Back compatibility module, new code should use windowUtils directly instead of using this module.
-if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.string"] = true;
-dojo.provide("dojo.string");
+ dijit.getDocumentWindow = function(doc){
+ return windowUtils.get(doc);
+ };
+});
-dojo.getObject("string", true, dojo);
+},
+'dijit/form/RadioButton':function(){
+define("dijit/form/RadioButton", [
+ "dojo/_base/declare", // declare
+ "./CheckBox",
+ "./_RadioButtonMixin"
+], function(declare, CheckBox, _RadioButtonMixin){
/*=====
-dojo.string = {
- // summary: String utilities for Dojo
-};
+ var CheckBox = dijit.form.CheckBox;
+ var _RadioButtonMixin = dijit.form._RadioButtonMixin;
=====*/
-dojo.string.rep = function(/*String*/str, /*Integer*/num){
- // summary:
- // Efficiently replicate a string `n` times.
- // str:
- // the string to replicate
- // num:
- // number of times to replicate the string
-
- if(num <= 0 || !str){ return ""; }
-
- var buf = [];
- for(;;){
- if(num & 1){
- buf.push(str);
- }
- if(!(num >>= 1)){ break; }
- str += str;
- }
- return buf.join(""); // String
-};
+ // module:
+ // dijit/form/RadioButton
+ // summary:
+ // Radio button widget
-dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
- // summary:
- // Pad a string to guarantee that it is at least `size` length by
- // filling with the character `ch` at either the start or end of the
- // string. Pads at the start, by default.
- // text:
- // the string to pad
- // size:
- // length to provide padding
- // ch:
- // character to pad, defaults to '0'
- // end:
- // adds padding at the end if true, otherwise pads at start
- // example:
- // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
- // | dojo.string.pad("Dojo", 10, "+", true);
+ return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
+ // summary:
+ // Same as an HTML radio, but with fancy styling.
- if(!ch){
- ch = '0';
- }
- var out = String(text),
- pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
- return end ? out + pad : pad + out; // String
-};
+ baseClass: "dijitRadio"
+ });
+});
-dojo.string.substitute = function( /*String*/ template,
- /*Object|Array*/map,
- /*Function?*/ transform,
- /*Object?*/ thisObject){
- // summary:
- // Performs parameterized substitutions on a string. Throws an
- // exception if any parameter is unmatched.
- // template:
- // a string with expressions in the form `${key}` to be replaced or
- // `${key:format}` which specifies a format function. keys are case-sensitive.
- // map:
- // hash to search for substitutions
- // transform:
- // a function to process all parameters before substitution takes
- // place, e.g. mylib.encodeXML
- // thisObject:
- // where to look for optional format function; default to the global
- // namespace
- // example:
- // Substitutes two expressions in a string from an Array or Object
- // | // returns "File 'foo.html' is not found in directory '/temp'."
- // | // by providing substitution data in an Array
- // | dojo.string.substitute(
- // | "File '${0}' is not found in directory '${1}'.",
- // | ["foo.html","/temp"]
- // | );
- // |
- // | // also returns "File 'foo.html' is not found in directory '/temp'."
- // | // but provides substitution data in an Object structure. Dotted
- // | // notation may be used to traverse the structure.
- // | dojo.string.substitute(
- // | "File '${name}' is not found in directory '${info.dir}'.",
- // | { name: "foo.html", info: { dir: "/temp" } }
- // | );
- // example:
- // Use a transform function to modify the values:
- // | // returns "file 'foo.html' is not found in directory '/temp'."
- // | dojo.string.substitute(
- // | "${0} is not found in ${1}.",
- // | ["foo.html","/temp"],
- // | function(str){
- // | // try to figure out the type
- // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
- // | return prefix + " '" + str + "'";
- // | }
- // | );
- // example:
- // Use a formatter
- // | // returns "thinger -- howdy"
- // | dojo.string.substitute(
- // | "${0:postfix}", ["thinger"], null, {
- // | postfix: function(value, key){
- // | return value + " -- howdy";
- // | }
- // | }
- // | );
+},
+'dijit/main':function(){
+define("dijit/main", [
+ "dojo/_base/kernel"
+], function(dojo){
+ // module:
+ // dijit
+ // summary:
+ // The dijit package main module
- thisObject = thisObject || dojo.global;
- transform = transform ?
- dojo.hitch(thisObject, transform) : function(v){ return v; };
+ return dojo.dijit;
+});
- return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
- function(match, key, format){
- var value = dojo.getObject(key, false, map);
- if(format){
- value = dojo.getObject(format, false, thisObject).call(thisObject, value, key);
- }
- return transform(value, key).toString();
- }); // String
-};
+},
+'dijit/_OnDijitClickMixin':function(){
+define("dijit/_OnDijitClickMixin", [
+ "dojo/on",
+ "dojo/_base/array", // array.forEach
+ "dojo/keys", // keys.ENTER keys.SPACE
+ "dojo/_base/declare", // declare
+ "dojo/_base/sniff", // has("ie")
+ "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/_OnDijitClickMixin
+ // summary:
+ // Mixin so you can pass "ondijitclick" to this.connect() method,
+ // as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key)
+
+
+ // 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("ie")){
+ (function(){
+ var keydownCallback = function(evt){
+ lastKeyDownNode = evt.srcElement;
+ };
+ win.doc.attachEvent('onkeydown', keydownCallback);
+ unload.addOnWindowUnload(function(){
+ win.doc.detachEvent('onkeydown', keydownCallback);
+ });
+ })();
+ }else{
+ win.doc.addEventListener('keydown', function(evt){
+ lastKeyDownNode = evt.target;
+ }, true);
+ }
-/*=====
-dojo.string.trim = function(str){
- // summary:
- // Trims whitespace from both sides of the string
- // str: String
- // String to be trimmed
- // returns: String
- // Returns the trimmed string
- // description:
- // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
- // The short yet performant version of this function is dojo.trim(),
- // which is part of Dojo base. Uses String.prototype.trim instead, if available.
- return ""; // String
-}
-=====*/
+ // Custom a11yclick (a.k.a. ondijitclick) event
+ var a11yclick = function(node, listener){
+ 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.
+
+ function clickKey(/*Event*/ e){
+ return (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
+ !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
+ }
+ var handles = [
+ on(node, "keypress", 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)
+ // Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will
+ // suppress the onkeypress event, breaking _HasDropDown
+ e.preventDefault();
+ }
+ }),
-dojo.string.trim = String.prototype.trim ?
- dojo.trim : // aliasing to the native function
- function(str){
- str = str.replace(/^\s+/, '');
- for(var i = str.length - 1; i >= 0; i--){
- if(/\S/.test(str.charAt(i))){
- str = str.substring(0, i + 1);
- break;
- }
+ 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;
+ listener.call(this, e);
+ }
+ }),
+
+ on(node, "click", function(e){
+ // and connect for mouse clicks too (or touch-clicks on mobile)
+ listener.call(this, e);
+ })
+ ];
+
+ return {
+ remove: function(){
+ array.forEach(handles, function(h){ h.remove(); });
+ }
+ };
}
- return str;
};
-}
+ return 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 dijit.form.Button();
+ // | // when foo.bar() is called, call the listener we're going to
+ // | // provide in the scope of btn
+ // | btn.connect(foo, "bar", function(){
+ // | console.debug(this.toString());
+ // | });
+ // tags:
+ // protected
-if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.cache"] = true;
-dojo.provide("dojo.cache");
+ return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
+ }
+ });
+});
+},
+'dijit/InlineEditBox':function(){
+require({cache:{
+'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\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", [
+ "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/_base/sniff", // has("ie")
+ "./focus",
+ "./_Widget",
+ "./_TemplatedMixin",
+ "./_WidgetsInTemplateMixin",
+ "./_Container",
+ "./form/Button",
+ "./form/_TextBoxMixin",
+ "./form/TextBox",
+ "dojo/text!./templates/InlineEditBox.html",
+ "dojo/i18n!./nls/common"
+], function(array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has,
+ fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
/*=====
-dojo.cache = {
- // summary:
- // A way to cache string content that is fetchable via `dojo.moduleUrl`.
-};
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
+ var _Container = dijit._Container;
+ var Button = dijit.form.Button;
+ var TextBox = dijit.form.TextBox;
=====*/
- var cache = {};
- dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
- // summary:
- // A getter and setter for storing the string content associated with the
- // module and url arguments.
- // description:
- // module and url are used to call `dojo.moduleUrl()` to generate a module URL.
- // If value is specified, the cache value for the moduleUrl will be set to
- // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
- // in its internal cache and return that cached value for the URL. To clear
- // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
- // the URL contents, only modules on the same domain of the page can use this capability.
- // The build system can inline the cache values though, to allow for xdomain hosting.
- // module: String||Object
- // If a String, the module name to use for the base part of the URL, similar to module argument
- // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
- // generates a valid path for the cache item. For example, a dojo._Url object.
- // url: String
- // The rest of the path to append to the path derived from the module argument. If
- // module is an object, then this second argument should be the "value" argument instead.
- // value: String||Object?
- // If a String, the value to use in the cache for the module/url combination.
- // If an Object, it can have two properties: value and sanitize. The value property
- // should be the value to use in the cache, and sanitize can be set to true or false,
- // to indicate if XML declarations should be removed from the value and if the HTML
- // inside a body tag in the value should be extracted as the real value. The value argument
- // or the value property on the value argument are usually only used by the build system
- // as it inlines cache content.
- // example:
- // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
- // of call is used to avoid an issue with the build system erroneously trying to intern
- // this example. To get the build system to intern your dojo.cache calls, use the
- // "dojo.cache" style of call):
- // | //If template.html contains "<h1>Hello</h1>" that will be
- // | //the value for the text variable.
- // | var text = dojo["cache"]("my.module", "template.html");
- // example:
- // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
- // (the dojo["cache"] style of call is used to avoid an issue with the build system
- // erroneously trying to intern this example. To get the build system to intern your
- // dojo.cache calls, use the "dojo.cache" style of call):
- // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
- // | //text variable will contain just "<h1>Hello</h1>".
- // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
- // example:
- // Same example as previous, but demostrates how an object can be passed in as
- // the first argument, then the value argument can then be the second argument.
- // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
- // | //text variable will contain just "<h1>Hello</h1>".
- // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
-
- //Module could be a string, or an object that has a toString() method
- //that will return a useful path. If it is an object, then the "url" argument
- //will actually be the value argument.
- if(typeof module == "string"){
- var pathObj = dojo.moduleUrl(module, url);
- }else{
- pathObj = module;
- value = url;
- }
- var key = pathObj.toString();
+// module:
+// dijit/InlineEditBox
+// summary:
+// An element with in-line edit capabilities
- var val = value;
- if(value != undefined && !dojo.isString(value)){
- val = ("value" in value ? value.value : undefined);
- }
+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
- var sanitize = value && value.sanitize ? true : false;
+ templateString: template,
- if(typeof val == "string"){
- //We have a string, set cache value
- val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
- }else if(val === null){
- //Remove cached value
- delete cache[key];
- }else{
- //Allow cache values to be empty strings. If key property does
- //not exist, fetch it.
- if(!(key in cache)){
- val = dojo._getText(key);
- cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
- }
- val = cache[key];
- }
- return val; //String
- };
+ 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);
+ },
- dojo.cache._sanitize = function(/*String*/val){
- // summary:
- // Strips <?xml ...?> declarations so that external SVG and XML
- // documents can be added to a document without worry. Also, if the string
- // is an HTML document, only the part inside the body tag is returned.
- // description:
- // Copied from dijit._Templated._sanitizeTemplateString.
- if(val){
- val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
- var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
- if(matches){
- val = matches[1];
+ buildRendering: function(){
+ this.inherited(arguments);
+
+ // Create edit widget in place in the template
+ var cls = typeof this.editor == "string" ? lang.getObject(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"], 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{
- val = "";
+ // inline-block mode
+ editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
}
- return val; //String
- };
-
-}
-
-if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._Templated"] = true;
-dojo.provide("dijit._Templated");
+ 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);
+ }
+ }
+ },
-dojo.declare("dijit._Templated",
- null,
- {
+ _onIntermediateChange: function(/*===== val =====*/){
// summary:
- // Mixin for widgets that are instantiated from a template
-
- // templateString: [protected] String
- // A string that represents the widget template. Pre-empts the
- // templatePath. In builds that have their strings "interned", the
- // templatePath is converted to an inline templateString, thereby
- // preventing a synchronous network call.
- //
- // Use in conjunction with dojo.cache() to load from a file.
- templateString: null,
+ // 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());
+ },
- // templatePath: [protected deprecated] String
- // Path to template (HTML file) for this widget relative to dojo.baseUrl.
- // Deprecated: use templateString with dojo.cache() instead.
- templatePath: null,
+ destroy: function(){
+ this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
+ this.inherited(arguments);
+ },
- // widgetsInTemplate: [protected] Boolean
- // Should we parse the template to find widgets that might be
- // declared in markup inside it? False by default.
- widgetsInTemplate: false,
+ getValue: function(){
+ // summary:
+ // Return the [display] value of the edit widget
+ var ew = this.editWidget;
+ return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
+ },
- // skipNodeCache: [protected] Boolean
- // If using a cached widget template node poses issues for a
- // particular widget class, it can set this property to ensure
- // that its template is always re-built from a string
- _skipNodeCache: false,
+ _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
- // _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,
+ 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
+ }
-/*=====
- // _attachPoints: [private] String[]
- // List of widget attribute names associated with dojoAttachPoint=... in the
- // template, ex: ["containerNode", "labelNode"]
- _attachPoints: [],
- =====*/
+ // _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.
+ }
+ },
-/*=====
- // _attachEvents: [private] Handle[]
- // List of connections associated with dojoAttachEvent=... in the
- // template
- _attachEvents: [],
- =====*/
+ _onBlur: function(){
+ // summary:
+ // Called when focus moves outside the editor
+ // tags:
+ // private
- constructor: function(){
- this._attachPoints = [];
- this._attachEvents = [];
- },
+ 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);
+ }
+ }
+ },
- _stringRepl: function(tmpl){
- // summary:
- // Does substitution of ${foo} type properties in template string
- // tags:
- // private
- var className = this.declaredClass, _this = this;
- // Cache contains a string because we need to do property replacement
- // do the property replacement
- return dojo.string.substitute(tmpl, this, function(value, key){
- if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); }
- if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
- if(value == null){ return ""; }
+ _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
- // 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);
- },
+ if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
+ fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
+ }
+ },
- buildRendering: function(){
- // summary:
- // Construct the UI for this widget from a template, setting this.domNode.
- // tags:
- // protected
+ 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
+ );
+ },
- // Lookup cached version of template, and download to cache if it
- // isn't there already. Returns either a DomNode or a string, depending on
- // whether or not the template contains ${foo} replacement parameters.
- var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
+ focus: function(){
+ // summary:
+ // Focus the edit widget.
+ // tags:
+ // protected
- var node;
- if(dojo.isString(cached)){
- node = dojo._toDom(this._stringRepl(cached));
- if(node.nodeType != 1){
- // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
- throw new Error("Invalid template: " + cached);
- }
- }else{
- // if it's a node, all we have to do is clone it
- node = cached.cloneNode(true);
+ this.editWidget.focus();
+ setTimeout(lang.hitch(this, function(){
+ if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
+ _TextBoxMixin.selectInputText(this.editWidget.focusNode);
}
+ }), 0);
+ }
+});
- this.domNode = node;
- // Call down to _Widget.buildRendering() to get base classes assigned
- // TODO: change the baseClass assignment to attributeMap
- this.inherited(arguments);
+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
- // 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);
+ // editing: [readonly] Boolean
+ // Is the node currently in edit mode?
+ editing: false,
- if(this.widgetsInTemplate){
- // Store widgets that we need to start at a later point in time
- var cw = (this._startupWidgets = dojo.parser.parse(node, {
- noStart: !this._earlyTemplatedStartup,
- template: true,
- inherited: {dir: this.dir, lang: this.lang},
- 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
- }));
+ // autoSave: Boolean
+ // Changing the value automatically saves it; don't have to push save button
+ // (and save button isn't even displayed)
+ autoSave: true,
- this._supportingWidgets = dijit.findWidgets(node);
+ // buttonSave: String
+ // Save button label
+ buttonSave: "",
- this._attachTemplateNodes(cw, function(n,p){
- return n[p];
- });
- }
+ // buttonCancel: String
+ // Cancel button label
+ buttonCancel: "",
- this._fillContent(this.srcNodeRef);
- },
+ // 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,
- _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);
- }
- }
- },
+ // editor: String|Function
+ // Class name (or reference to the Class) for Editor widget
+ editor: TextBox,
- _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 dojoAttachPoint
- // 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
- // * dojoAttachEvent
- // * waiRole
- // * waiState
- // rootNode: DomNode|Array[Widgets]
- // the node to search for properties. All children will be searched.
- // getAttrFunc: Function?
- // a function which will be used to obtain property for a given
- // DomNode/Widget
- // tags:
- // private
+ // editorWrapper: String|Function
+ // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
+ // buttons.
+ editorWrapper: InlineEditor,
- getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
+ // editorParams: Object
+ // Set of parameters for editor, like {required: true}
+ editorParams: {},
- var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
- var x = dojo.isArray(rootNode) ? 0 : -1;
- for(; x<nodes.length; x++){
- var baseNode = (x == -1) ? rootNode : nodes[x];
- if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
- continue;
- }
- // Process dojoAttachPoint
- 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(dojo.isArray(this[point])){
- this[point].push(baseNode);
- }else{
- this[point]=baseNode;
- }
- this._attachPoints.push(point);
- }
- }
+ // disabled: Boolean
+ // If true, clicking the InlineEditBox to edit it will have no effect.
+ disabled: false,
- // Process dojoAttachEvent
- 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 = dojo.trim;
- while((event = events.shift())){
- if(event){
- var thisFunc = null;
- if(event.indexOf(":") != -1){
- // oh, if only JS had tuple assignment
- var funcNameArr = event.split(":");
- event = trim(funcNameArr[0]);
- thisFunc = trim(funcNameArr[1]);
- }else{
- event = trim(event);
- }
- if(!thisFunc){
- thisFunc = event;
- }
- this._attachEvents.push(this.connect(baseNode, event, thisFunc));
- }
- }
- }
+ onChange: function(/*===== value =====*/){
+ // summary:
+ // Set this handler to be notified of changes to value.
+ // tags:
+ // callback
+ },
- // waiRole, waiState
- // TODO: remove this in 2.0, templates are now using role=... and aria-XXX=... attributes directicly
- var role = getAttrFunc(baseNode, "waiRole");
- if(role){
- dijit.setWaiRole(baseNode, role);
- }
- var values = getAttrFunc(baseNode, "waiState");
- if(values){
- dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
- if(stateValue.indexOf('-') != -1){
- var pair = stateValue.split('-');
- dijit.setWaiState(baseNode, pair[0], pair[1]);
- }
- });
- }
- }
- },
+ onCancel: function(){
+ // summary:
+ // Set this handler to be notified when editing is cancelled.
+ // tags:
+ // callback
+ },
- startup: function(){
- dojo.forEach(this._startupWidgets, function(w){
- if(w && !w._started && w.startup){
- w.startup();
- }
- });
- this.inherited(arguments);
- },
+ // width: String
+ // Width of editor. By default it's width=100% (ie, block mode).
+ width: "100%",
- destroyRendering: function(){
- // Delete all attach points to prevent IE6 memory leaks.
- dojo.forEach(this._attachPoints, function(point){
- delete this[point];
- }, this);
- this._attachPoints = [];
+ // value: String
+ // The display value of the widget in read-only mode
+ value: "",
- // And same for event handlers
- dojo.forEach(this._attachEvents, this.disconnect, this);
- this._attachEvents = [];
-
- this.inherited(arguments);
- }
- }
-);
+ // 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;
-// key is either templatePath or templateString; object is either string or DOM tree
-dijit._Templated._templateCache = {};
+ constructor: function(){
+ // summary:
+ // Sets up private arrays etc.
+ // tags:
+ // private
+ this.editorParams = {};
+ },
-dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
- // summary:
- // Static method to get a template based on the templatePath or
- // templateString key
- // templatePath: String||dojo.uri.Uri
- // The URL to get the template from.
- // templateString: String?
- // a string to use in lieu of fetching the template from a URL. Takes precedence
- // over templatePath
- // returns: Mixed
- // Either string (if there are ${} variables that need to be replaced) or just
- // a DOM tree (if the node can be cloned directly)
-
- // is it already cached?
- var tmplts = dijit._Templated._templateCache;
- var key = templateString || templatePath;
- var cached = tmplts[key];
- if(cached){
- try{
- // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value
- if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
- // string or node of the same document
- return cached;
- }
- }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
- dojo.destroy(cached);
- }
+ postMixInProperties: function(){
+ this.inherited(arguments);
- // If necessary, load template string from template path
- if(!templateString){
- templateString = dojo.cache(templatePath, {sanitize: true});
- }
- templateString = dojo.string.trim(templateString);
+ // save pointer to original source node, since Widget nulls-out srcNodeRef
+ this.displayNode = this.srcNodeRef;
- if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
- // there are variables in the template so all we can do is cache the string
- return (tmplts[key] = templateString); //String
- }else{
- // there are no variables in the template so we can cache the DOM tree
- var node = dojo._toDom(templateString);
- if(node.nodeType != 1){
- throw new Error("Invalid template: " + templateString);
+ // 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]);
}
- return (tmplts[key] = node); //Node
- }
-};
-
-if(dojo.isIE){
- dojo.addOnWindowUnload(function(){
- var cache = dijit._Templated._templateCache;
- for(var key in cache){
- var value = cache[key];
- if(typeof value == "object"){ // value is either a string or a DOM node template
- dojo.destroy(value);
- }
- delete cache[key];
+ this.displayNode.setAttribute("role", "button");
+ if(!this.displayNode.getAttribute("tabIndex")){
+ this.displayNode.setAttribute("tabIndex", 0);
}
- });
-}
-// These arguments can be specified for widgets which are used in templates.
-// Since any widget can be specified as sub widgets in template, mix it
-// into the base widget class. (This is a hack, but it's effective.)
-dojo.extend(dijit._Widget,{
- dojoAttachEvent: "",
- dojoAttachPoint: "",
- waiRole: "",
- waiState:""
-});
+ if(!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');
+ },
-if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._Container"] = true;
-dojo.provide("dijit._Container");
+ 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);
+ if(disabled){
+ this.displayNode.removeAttribute("tabIndex");
+ }else{
+ this.displayNode.setAttribute("tabIndex", 0);
+ }
+ domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
+ this._set("disabled", disabled);
+ },
-dojo.declare("dijit._Container",
- null,
- {
+ _onMouseOver: function(){
// summary:
- // Mixin for widgets that contain a set of widget children.
- // description:
- // Use this mixin for widgets that needs to know about and
- // keep track of their widget children. Suitable for widgets like BorderContainer
- // and TabContainer which contain (only) a set of child widgets.
- //
- // It's not suitable for widgets like ContentPane
- // which contains mixed HTML (plain DOM nodes in addition to widgets),
- // and where contained widgets are not necessarily directly below
- // this.containerNode. In that case calls like addChild(node, position)
- // wouldn't make sense.
+ // Handler for onmouseover and onfocus event.
+ // tags:
+ // private
+ if(!this.disabled){
+ domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
+ }
+ },
- // isContainer: [protected] Boolean
- // Indicates that this widget acts as a "parent" to the descendant widgets.
- // When the parent is started it will call startup() on the child widgets.
- // See also `isLayoutContainer`.
- isContainer: true,
+ _onMouseOut: function(){
+ // summary:
+ // Handler for onmouseout and onblur event.
+ // tags:
+ // private
+ domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
+ },
- buildRendering: function(){
- this.inherited(arguments);
- if(!this.containerNode){
- // all widgets with descendants must set containerNode
- this.containerNode = this.domNode;
- }
- },
+ _onClick: function(/*Event*/ e){
+ // summary:
+ // Handler for onclick event.
+ // tags:
+ // private
+ if(this.disabled){ return; }
+ if(e){ event.stop(e); }
+ this._onMouseOut();
- addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
- // summary:
- // Makes the given widget a child of this widget.
- // description:
- // Inserts specified child widget's dom node as a child of this widget's
- // container node, and possibly does other processing (such as layout).
+ // Since FF gets upset if you move a node while in an event handler for that node...
+ setTimeout(lang.hitch(this, "edit"), 0);
+ },
- var refNode = this.containerNode;
- if(insertIndex && typeof insertIndex == "number"){
- var children = this.getChildren();
- if(children && children.length >= insertIndex){
- refNode = children[insertIndex-1].domNode;
- insertIndex = "after";
- }
- }
- dojo.place(widget.domNode, refNode, insertIndex);
+ edit: function(){
+ // summary:
+ // Display the editor widget in place of the original (read only) markup.
+ // tags:
+ // private
- // 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();
- }
- },
+ if(this.disabled || this.editing){ return; }
+ this._set('editing', true);
- removeChild: function(/*Widget or int*/ widget){
- // summary:
- // Removes the passed widget instance from this widget but does
- // not destroy it. You can also pass in an integer indicating
- // the index within the container to remove
+ // save some display node values that can be restored later
+ this._savedPosition = domStyle.get(this.displayNode, "position") || "static";
+ this._savedOpacity = domStyle.get(this.displayNode, "opacity") || "1";
+ this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
- if(typeof widget == "number"){
- widget = this.getChildren()[widget];
- }
+ 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");
- if(widget){
- var node = widget.domNode;
- if(node && node.parentNode){
- node.parentNode.removeChild(node); // detach but don't destroy
- }
+ // 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._started){
+ this.startup();
}
- },
+ }
+ var ww = this.wrapperWidget;
- hasChildren: function(){
- // summary:
- // Returns true if widget has children, i.e. if this.containerNode contains something.
- return this.getChildren().length > 0; // Boolean
- },
+ // 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
+ // visiblity:hidden removes focus outline
- destroyDescendants: function(/*Boolean*/ preserveDom){
- // summary:
- // Destroys all the widgets inside this.containerNode,
- // but not this widget itself
- dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); });
- },
+ domStyle.set(this.displayNode, { position: "absolute", opacity: "0" }); // makes display node invisible, display style used for focus-ability
+ domStyle.set(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
+ domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
- _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
- // summary:
- // Get the next or previous widget sibling of child
- // dir:
- // if 1, get the next sibling
- // if -1, get the previous sibling
- // tags:
- // private
- var node = child.domNode,
- which = (dir>0 ? "nextSibling" : "previousSibling");
- do{
- node = node[which];
- }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
- return node && dijit.byNode(node); // dijit._Widget
- },
+ // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
+ // focus can be shifted without incident. (browser may needs some time to render the editor.)
+ setTimeout(lang.hitch(ww, function(){
+ this.focus(); // both nodes are showing, so we can switch focus safely
+ this._resetValue = this.getValue();
+ }), 0);
+ },
- getIndexOfChild: function(/*dijit._Widget*/ child){
- // summary:
- // Gets the index of the child in this container or -1 if not found
- return dojo.indexOf(this.getChildren(), child); // int
- },
+ _onBlur: function(){
+ // summary:
+ // Called when focus moves outside the InlineEditBox.
+ // Performs garbage collection.
+ // tags:
+ // private
- startup: function(){
- // summary:
- // Called after all the widgets have been instantiated and their
- // dom nodes have been inserted somewhere under dojo.doc.body.
- //
- // Widgets should override this method to do any initialization
- // dependent on other widgets existing, and then call
- // this superclass method to finish things off.
- //
- // startup() in subclasses shouldn't do anything
- // size related because the size of the widget hasn't been set yet.
+ this.inherited(arguments);
+ if(!this.editing){
+ /* causes IE focus problems, see TooltipDialog_a11y.html...
+ setTimeout(lang.hitch(this, function(){
+ if(this.wrapperWidget){
+ this.wrapperWidget.destroy();
+ delete this.wrapperWidget;
+ }
+ }), 0);
+ */
+ }
+ },
- if(this._started){ return; }
+ destroy: function(){
+ if(this.wrapperWidget && !this.wrapperWidget._destroyed){
+ this.wrapperWidget.destroy();
+ delete this.wrapperWidget;
+ }
+ this.inherited(arguments);
+ },
- // Startup all children of this widget
- dojo.forEach(this.getChildren(), function(child){ child.startup(); });
+ _showText: function(/*Boolean*/ focus){
+ // summary:
+ // Revert to display mode, and optionally focus on display node
+ // tags:
+ // private
- this.inherited(arguments);
+ var ww = this.wrapperWidget;
+ domStyle.set(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
+ domStyle.set(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity }); // make the original text visible
+ 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(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._Contained"] = true;
-dojo.provide("dijit._Contained");
+ 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
-dojo.declare("dijit._Contained",
- null,
- {
- // summary:
- // Mixin for widgets that are children of a container widget
- //
- // example:
- // | // make a basic custom widget that knows about it's parents
- // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
+ this._showText(focus); // set focus as needed
+ },
- getParent: function(){
- // summary:
- // Returns the parent widget of this widget, assuming the parent
- // specifies isContainer
- var parent = dijit.getEnclosingWidget(this.domNode.parentNode);
- return parent && parent.isContainer ? parent : null;
- },
+ 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);
+ },
- _getSibling: function(/*String*/ which){
- // summary:
- // Returns next or previous sibling
- // which:
- // Either "next" or "previous"
- // tags:
- // private
- var node = this.domNode;
- do{
- node = node[which+"Sibling"];
- }while(node && node.nodeType != 1);
- return node && dijit.byNode(node); // dijit._Widget
- },
+ _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.
- getPreviousSibling: function(){
- // summary:
- // Returns null if this is the first child of the parent,
- // otherwise returns the next element sibling to the "left".
+ 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);
- return this._getSibling("previous"); // dijit._Widget
- },
+ if(this._started){
+ // tell the world that we have changed
+ setTimeout(lang.hitch(this, "onChange", val), 0); // setTimeout 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);
+ }
+ },
- getNextSibling: function(){
- // summary:
- // Returns null if this is the last child of the parent,
- // otherwise returns the next element sibling to the "right".
+ 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");
+ },
- return this._getSibling("next"); // dijit._Widget
- },
+ cancel: function(/*Boolean*/ focus){
+ // summary:
+ // Revert to display mode, discarding any changes made in the editor
+ // tags:
+ // private
- 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
+ if(this.disabled || !this.editing){ return; }
+ this._set('editing', false);
- var p = this.getParent();
- if(!p || !p.getIndexOfChild){
- return -1; // int
- }
- return p.getIndexOfChild(this); // int
- }
- }
- );
+ // tell the world that we have no changes
+ setTimeout(lang.hitch(this, "onCancel"), 0); // setTimeout 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
+ }
+ }
+});
-if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout._LayoutWidget"] = true;
-dojo.provide("dijit.layout._LayoutWidget");
+InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
+return InlineEditBox;
+});
+},
+'dojo/selector/acme':function(){
+define("dojo/selector/acme", ["../_base/kernel", "../has", "../dom", "../_base/sniff", "../_base/array", "../_base/lang", "../_base/window"], function(dojo, has, dom){
+ // module:
+ // dojo/selector/acme
+ // summary:
+ // This module defines the Acme selector engine
+/*
+ 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
+ ////////////////////////////////////////////////////////////////////////
-dojo.declare("dijit.layout._LayoutWidget",
- [dijit._Widget, dijit._Container, dijit._Contained],
- {
- // summary:
- // Base class for a _Container widget which is responsible for laying out its children.
- // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
+ // 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 = dojo.trim;
+ var each = dojo.forEach;
+ // d.isIE; // float
+ // d.isSafari; // float
+ // d.isOpera; // float
+ // d.isWebKit; // float
+ // d.doc ; // document element
- // 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",
+ var getDoc = function(){ return dojo.doc; };
+ // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
+ var cssCaseBug = ((dojo.isWebKit||dojo.isMozilla) && ((getDoc().compatMode) == "BackCompat"));
- // 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,
+ ////////////////////////////////////////////////////////////////////////
+ // Global utilities
+ ////////////////////////////////////////////////////////////////////////
- buildRendering: function(){
- this.inherited(arguments);
- dojo.addClass(this.domNode, "dijitContainer");
- },
- startup: function(){
- // summary:
- // Called after all the widgets have been instantiated and their
- // dom nodes have been inserted somewhere under dojo.doc.body.
- //
- // Widgets should override this method to do any initialization
- // dependent on other widgets existing, and then call
- // this superclass method to finish things off.
- //
- // startup() in subclasses shouldn't do anything
- // size related because the size of the widget hasn't been set yet.
+ var specials = ">~+";
- if(this._started){ return; }
+ // 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;
- // Need to call inherited first - so that child widgets get started
- // up correctly
- this.inherited(arguments);
+ // how high?
+ var yesman = function(){ return true; };
- // 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();
+ ////////////////////////////////////////////////////////////////////////
+ // Tokenizer
+ ////////////////////////////////////////////////////////////////////////
- // Since my parent isn't a layout container, and my style *may be* width=height=100%
- // or something similar (either set directly or via a CSS class),
- // monitor when my size changes so that I can re-layout.
- // For browsers where I can't directly monitor when my size changes,
- // monitor when the viewport changes size, which *may* indicate a size change for me.
- this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
- // Using function(){} closure to ensure no arguments to resize.
- this.resize();
- });
- }
- },
+ 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 += " ";
+ }
- resize: function(changeSize, resultSize){
- // summary:
- // Call this to resize a widget, or after its size has changed.
- // description:
- // Change size mode:
- // When changeSize is specified, changes the marginBox of this widget
- // and forces it to relayout its contents accordingly.
- // changeSize may specify height, width, or both.
- //
- // If resultSize is specified it indicates the size the widget will
- // become after changeSize has been applied.
- //
- // Notification mode:
- // When changeSize is null, indicates that the caller has already changed
- // the size of the widget, or perhaps it changed because the browser
- // window was resized. Tells widget to relayout its contents accordingly.
- //
- // If resultSize is also specified it indicates the size the widget has
- // become.
- //
- // In either mode, this method also:
- // 1. Sets this._borderBox and this._contentBox to the new size of
- // the widget. Queries the current domNode size if necessary.
- // 2. Calls layout() to resize contents (and maybe adjust child widgets).
- //
- // changeSize: Object?
- // Sets the widget to this margin-box size and position.
- // May include any/all of the following properties:
- // | {w: int, h: int, l: int, t: int}
- //
- // resultSize: Object?
- // The margin-box size of this widget after applying changeSize (if
- // changeSize is specified). If caller knows this size and
- // passes it in, we don't need to query the browser to get the size.
- // | {w: int, h: int}
+ var ts = function(/*Integer*/ s, /*Integer*/ e){
+ // trim and slice.
- var node = this.domNode;
+ // 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));
+ };
- // set margin box size, unless it wasn't specified, in which case use current size
- if(changeSize){
- dojo.marginBox(node, changeSize);
+ // 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,
+ 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;
+ }
+ };
- // set offset of the node
- if(changeSize.t){ node.style.top = changeSize.t + "px"; }
- if(changeSize.l){ node.style.left = changeSize.l + "px"; }
+ 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;
}
+ };
- // If either height or width wasn't specified by the user, then query node for it.
- // But note that setting the margin box and then immediately querying dimensions may return
- // inaccurate results, so try not to depend on it.
- var mb = resultSize || {};
- dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
- if( !("h" in mb) || !("w" in mb) ){
- mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values
+ 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;
}
+ };
- // Compute and save the size of my border box and content box
- // (w/out calling dojo.contentBox() since that may fail if size was recently set)
- var cs = dojo.getComputedStyle(node);
- var me = dojo._getMarginExtents(node, cs);
- var be = dojo._getBorderExtents(node, cs);
- var bb = (this._borderBox = {
- w: mb.w - (me.w + be.w),
- h: mb.h - (me.h + be.h)
- });
- var pe = dojo._getPadExtents(node, cs);
- this._contentBox = {
- l: dojo._toPixelValue(node, cs.paddingLeft),
- t: dojo._toPixelValue(node, cs.paddingTop),
- w: bb.w - pe.w,
- h: bb.h - pe.h
- };
+ var endAll = function(){
+ // at the end of a simple fragment, so wall off the matches
+ endId();
+ endTag();
+ endClass();
+ };
- // Callback for widget to adjust size of its children
- this.layout();
- },
+ 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;
+ };
- 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
- },
+ // 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;
+ }
+ };
- _setupChild: function(/*dijit._Widget*/child){
- // summary:
- // Common setup for initial children and children which are added after startup
- // tags:
- // protected extension
+ // 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;
+ }
+
+ 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);
+ }
+ }
+ // 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;
+ };
- var cls = this.baseClass + "-child "
- + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
- dojo.addClass(child.domNode, cls);
- },
- addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
- // Overrides _Container.addChild() to call _setupChild()
- this.inherited(arguments);
- if(this._started){
- this._setupChild(child);
- }
- },
+ ////////////////////////////////////////////////////////////////////////
+ // DOM query infrastructure
+ ////////////////////////////////////////////////////////////////////////
- removeChild: function(/*dijit._Widget*/ child){
- // Overrides _Container.removeChild() to remove class added by _setupChild()
- var cls = this.baseClass + "-child"
- + (child.baseClass ?
- " " + this.baseClass + "-" + child.baseClass : "");
- dojo.removeClass(child.domNode, cls);
-
- this.inherited(arguments);
- }
- }
-);
+ 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; }
-dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
- // summary:
- // Given the margin-box size of a node, return its content box size.
- // Functions like dojo.contentBox() but is more reliable since it doesn't have
- // to wait for the browser to compute sizes.
- var cs = dojo.getComputedStyle(node);
- var me = dojo._getMarginExtents(node, cs);
- var pb = dojo._getPadBorderExtents(node, cs);
- return {
- l: dojo._toPixelValue(node, cs.paddingLeft),
- t: dojo._toPixelValue(node, cs.paddingTop),
- w: mb.w - (me.w + pb.w),
- h: mb.h - (me.h + pb.h)
+ return function(){
+ return first.apply(window, arguments) && second.apply(window, arguments);
+ }
};
-};
-(function(){
- var capitalize = function(word){
- return word.substring(0,1).toUpperCase() + word.substring(1);
+ 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 size = function(widget, dim){
- // size the child
- var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
+ var _isElement = function(n){ return (1 == n.nodeType); };
- // record child's size
- if(newSize){
- // if the child returned it's new size then use that
- dojo.mixin(widget, newSize);
- }else{
- // otherwise, call marginBox(), but favor our own numbers when we have them.
- // the browser lies sometimes
- dojo.mixin(widget, dojo.marginBox(widget.domNode));
- dojo.mixin(widget, dim);
+ // 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;
};
- dijit.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 = dojo.mixin({}, dim);
+ 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);
+ return (ea.lastIndexOf(value)==(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);
+ }
+ }
+ };
- dojo.addClass(container, "dijitLayoutContainer");
+ // 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);
- // 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 = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
- .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
+ var _lookLeft = function(node){
+ // look left
+ while(node = node[_ps]){
+ if(_simpleNodeTest(node)){ return false; }
+ }
+ return true;
+ };
- // set positions/sizes
- dojo.forEach(children, function(child){
- var elm = child.domNode,
- pos = (child.region || child.layoutAlign);
+ var _lookRight = function(node){
+ // look right
+ while(node = node[_ns]){
+ if(_simpleNodeTest(node)){ return false; }
+ }
+ return true;
+ };
- // 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";
+ var getNodeIndex = function(node){
+ var root = node.parentNode;
+ var i = 0,
+ tret = root.children || root.childNodes,
+ ci = (node["_i"]||-1),
+ cl = (root["_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
+ root["_l"] = l;
+ ci = -1;
+ for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
+ if(_simpleNodeTest(te)){
+ 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;
+ };
- dojo.addClass(elm, "dijitAlign" + capitalize(pos));
+ var isEven = function(elem){
+ return !((getNodeIndex(elem)) % 2);
+ };
- // Size adjustments to make to this child widget
- var sizeSetting = {};
+ var isOdd = function(elem){
+ return ((getNodeIndex(elem)) % 2);
+ };
- // 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;
+ var pseudos = {
+ "checked": function(name, condition){
+ return function(elem){
+ return !!("checked" in elem ? elem.checked : elem.selected);
}
-
- // 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";
+ },
+ "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; }
}
- }else if(pos == "left" || pos == "right"){
- sizeSetting.h = dim.h;
- size(child, sizeSetting);
- dim.w -= child.w;
- if(pos == "left"){
- dim.l += child.w;
+ 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{
- elmStyle.left = dim.l + dim.w + "px";
+ condition = idx;
}
- }else if(pos == "client" || pos == "center"){
- size(child, dim);
}
- });
+ var ncount = pi(condition);
+ return function(elem){
+ return (getNodeIndex(elem) == ncount);
+ }
+ }
};
-})();
+ var defaultGetter = (dojo.isIE && (dojo.isIE < 9 || dojo.isQuirks)) ? 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||{};
-if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._CssStateMixin"] = true;
-dojo.provide("dijit._CssStateMixin");
+ var ff = null;
+ if(!("el" in ignores)){
+ ff = agree(ff, _isElement);
+ }
-dojo.declare("dijit._CssStateMixin", [], {
- // summary:
- // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
- // state changes, and also higher-level state changes such becoming disabled or selected.
- //
- // description:
- // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
- // maintain CSS classes on the widget root node (this.domNode) depending on hover,
- // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
- // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
- //
- // It also sets CSS like dijitButtonDisabled based on widget semantic state.
- //
- // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
- // within the widget).
+ if(!("tag" in ignores)){
+ if(query.tag != "*"){
+ ff = agree(ff, function(elem){
+ return (elem && (elem.tagName == query.getTag()));
+ });
+ }
+ }
- // 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: {},
+ 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;
+ });
+ }
- // 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,
+ if(!("pseudos" in ignores)){
+ each(query.pseudos, function(pseudo){
+ var pn = pseudo.name;
+ if(pseudos[pn]){
+ ff = agree(ff, pseudos[pn](pn, pseudo.value));
+ }
+ });
+ }
- _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.
+ 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);
+ }
+ });
+ }
- this.inherited(arguments);
+ if(!("id" in ignores)){
+ if(query.id){
+ ff = agree(ff, function(elem){
+ return (!!elem && (elem.id == query.id));
+ });
+ }
+ }
- // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
- dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
- this.connect(this.domNode, e, "_cssMouseEvent");
- }, this);
-
- // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
- dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
- this.watch(attr, dojo.hitch(this, "_setStateClass"));
- }, this);
+ if(!ff){
+ if(!("default" in ignores)){
+ ff = yesman;
+ }
+ }
+ return ff;
+ };
- // Events on sub nodes within the widget
- for(var ap in this.cssStateNodes){
- this._trackMouseState(this[ap], this.cssStateNodes[ap]);
+ 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;
}
- // 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:
- // Sets hovering and active properties depending on mouse state,
- // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
+ 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;
+ }
+ };
- if(!this.disabled){
- switch(event.type){
- case "mouseenter":
- case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
- this._set("hovering", true);
- this._set("active", this._mouseDown);
- break;
+ // 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;
+ };
+ };
- case "mouseleave":
- case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
- this._set("hovering", false);
- this._set("active", false);
- break;
+ /*
+ // thanks, Dean!
+ var itemIsAfterRoot = d.isIE ? function(item, root){
+ return (item.sourceIndex > root.sourceIndex);
+ } : function(item, root){
+ return (item.compareDocumentPosition(root) == 2);
+ };
+ */
- case "mousedown" :
- this._set("active", true);
- this._mouseDown = true;
- // Set a global event to handle mouseup, so it fires properly
- // even if the cursor leaves this.domNode before the mouse up event.
- // Alternately could set active=false on mouseout.
- var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
- this._mouseDown = false;
- this._set("active", false);
- this.disconnect(mouseUpConnector);
- });
- break;
+ // 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;
+ };
- _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).
+ 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:
//
- // 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".
+ // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
+ // if infixOperator == " ":
+ // if only(id):
+ // return def(root):
+ // return d.byId(id, root);
//
- // 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
+ // elif id:
+ // return def(root):
+ // return filter(d.byId(id, root));
//
- // 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
+ // 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;
+ };
- // Compute new set of classes
- var newStateClasses = this.baseClass.split(" ");
+ }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 tret = root.getElementsByTagName(query.getTag());
+ 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 tret = root.getElementsByTagName(query.getTag());
+ 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;
+ };
- function multiply(modifier){
- newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
+ 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
+ // gauranteed 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;
+ };
- if(!this.isLeftToRight()){
- // For RTL mode we need to set an addition class like dijitTextBoxRtl.
- multiply("Rtl");
- }
+ ////////////////////////////////////////////////////////////////////////
+ // the query runner
+ ////////////////////////////////////////////////////////////////////////
- if(this.checked){
- multiply("Checked");
- }
- if(this.state){
- multiply(this.state);
- }
- if(this.selected){
- multiply("Selected");
- }
+ // 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 = {};
- if(this.disabled){
- multiply("Disabled");
- }else if(this.readOnly){
- multiply("ReadOnly");
- }else{
- if(this.active){
- multiply("Active");
- }else if(this.hovering){
- multiply("Hover");
+ // this is the second level of spliting, 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;
}
}
- if(this._focused){
- multiply("Focused");
+ // otherwise, break it up and return a runner that iterates over the parts recursively
+ return function(root){
+ return filterDown(root, qparts);
}
+ };
- // 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
+ // 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 determiniation (e.g. does it support QSA, does the query in
+ // question work in the native QSA impl, etc.).
+ var nua = navigator.userAgent;
+ // some versions of Safari provided QSA, but it was buggy and crash-prone.
+ // We need te detect the right "internal" webkit version to make this work.
+ var wk = "WebKit/";
+ var is525 = (
+ dojo.isWebKit &&
+ (nua.indexOf(wk) > 0) &&
+ (parseFloat(nua.split(wk)[1]) > 528)
+ );
- dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
+ // 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 = dojo.isIE ? "commentStrip" : "nozip";
- if("_stateClasses" in this){
- dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
- }
+ var qsa = "querySelectorAll";
+ var qsaAvail = (
+ !!getDoc()[qsa] &&
+ // see #5832
+ (!dojo.isSafari || (dojo.isSafari > 3.1) || is525 )
+ );
- dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
+ //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;
+ };
- var newClasses = [];
- for(var c in classHash){
- newClasses.push(c);
+ 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(infixSpaceRe, infixSpaceFunc);
+
+ 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; }
}
- tn.className = newClasses.join(" ");
- this._stateClasses = newStateClasses;
- },
+ // 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; }
- _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).
+ // 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
+ (!dojo.isIE || (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
+ );
- // Current state of node (initially false)
- // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
- var hovering=false, active=false, focused=false;
+ // 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
- var self = this,
- cn = dojo.hitch(this, "connect", node);
- function setClass(){
- var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
- dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
- dojo.toggleClass(node, clazz+"Active", active && !disabled);
- dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
+ 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.split(/\s*,\s*/);
+ 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;
+ }
+ );
}
+ };
- // Mouse
- cn("onmouseenter", function(){
- hovering = true;
- setClass();
- });
- cn("onmouseleave", function(){
- hovering = false;
- active = false;
- setClass();
- });
- cn("onmousedown", function(){
- active = true;
- setClass();
- });
- cn("onmouseup", function(){
- active = false;
- setClass();
- });
+ var _zipIdx = 0;
- // Focus
- cn("onfocus", function(){
- focused = true;
- setClass();
- });
- cn("onblur", function(){
- focused = false;
- setClass();
- });
+ // NOTE:
+ // this function is Moo inspired, but our own impl to deal correctly
+ // with XML in IE
+ var _nodeUID = dojo.isIE ? function(node){
+ if(caseSensitive){
+ // XML docs don't have uniqueID on their nodes
+ return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
- // Just in case widget is enabled/disabled while it has focus/hover/active state.
- // Maybe this is overkill.
- this.watch("disabled", setClass);
- this.watch("readOnly", setClass);
- }
-});
+ }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 doucment 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*
+ if(dojo.isIE && caseSensitive){
+ var szidx = _zipIdx+"";
+ arr[0].setAttribute(_zipIdxName, szidx);
+ for(var x = 1, te; te = arr[x]; x++){
+ if(arr[x].getAttribute(_zipIdxName) != szidx){
+ ret.push(te);
+ }
+ te.setAttribute(_zipIdxName, szidx);
+ }
+ }else if(dojo.isIE && arr.commentStrip){
+ try{
+ for(var x = 1, te; te = arr[x]; x++){
+ if(_isElement(te)){
+ ret.push(te);
+ }
+ }
+ }catch(e){ /* squelch */ }
+ }else{
+ if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; }
+ for(var x = 1, te; te = arr[x]; x++){
+ if(arr[x][_zipIdxName] != _zipIdx){
+ ret.push(te);
+ }
+ te[_zipIdxName] = _zipIdx;
+ }
+ }
+ return ret;
+ };
-if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form._FormWidget"] = true;
-dojo.provide("dijit.form._FormWidget");
+ // 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 pseduo-selectors which don't get a lot of day-to-day use:
+ // | * `:root`, `:lang()`, `:target`, `:focus`
+ // * all visual and state selectors:
+ // | * `:root`, `:active`, `:hover`, `:visisted`, `: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();
+ var od = root.ownerDocument||root.documentElement;
+ // throw the big case sensitivity switch
+ // NOTE:
+ // Opera in XHTML mode doesn't detect case-sensitivity correctly
+ // and it's not clear that there's any way to test for it
+ caseSensitive = (root.contentType && root.contentType=="application/xml") ||
+ (dojo.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) ||
+ (!!od) &&
+ (dojo.isIE ? od.xml : (root.xmlVersion || od.xmlVersion));
+ // 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 dojo.query(filter, root).indexOf(node) != -1;
+ };
+ for(var x = 0, te; te = nodeList[x]; x++){
+ if(filterFunc(te)){ tmpNodeList.push(te); }
+ }
+ return tmpNodeList;
+ };
+ return query;
+});//end defineQuery
-dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
- {
+},
+'dojo/dnd/autoscroll':function(){
+define("dojo/dnd/autoscroll", ["../main", "../window"], function(dojo) {
+ // module:
+ // dojo/dnd/autoscroll
// summary:
- // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
- // which can be children of a <form> node or a `dijit.form.Form` widget.
- //
- // description:
- // Represents a single HTML element.
- // All these widgets should have these attributes just like native HTML input elements.
- // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
- //
- // They also share some common methods.
+ // TODOC
- // 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: "",
+dojo.getObject("dnd", true, dojo);
- // value: String
- // Corresponds to the native HTML <input> element's attribute.
- value: "",
+dojo.dnd.getViewport = dojo.window.getBox;
- // type: String
- // Corresponds to the native HTML <input> element's attribute.
- type: "text",
+dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
+dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
- // tabIndex: Integer
- // Order fields are traversed when user hits the tab key
- tabIndex: "0",
+dojo.dnd.V_AUTOSCROLL_VALUE = 16;
+dojo.dnd.H_AUTOSCROLL_VALUE = 16;
- // disabled: Boolean
- // Should this widget respond to user input?
- // In markup, this is specified as "disabled='disabled'", or just "disabled".
- disabled: false,
+dojo.dnd.autoScroll = function(e){
+ // summary:
+ // a handler for onmousemove event, which scrolls the window, if
+ // necesary
+ // e: Event
+ // onmousemove event
- // intermediateChanges: Boolean
- // Fires onChange for each value change or only on demand
- intermediateChanges: false,
+ // FIXME: needs more docs!
+ var v = dojo.window.getBox(), dx = 0, dy = 0;
+ if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
+ dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
+ }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
+ dx = dojo.dnd.H_AUTOSCROLL_VALUE;
+ }
+ if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
+ dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
+ }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
+ dy = dojo.dnd.V_AUTOSCROLL_VALUE;
+ }
+ window.scrollBy(dx, dy);
+};
- // scrollOnFocus: Boolean
- // On focus, should this widget scroll into view?
- scrollOnFocus: true,
+dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
+dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
- // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- value: "focusNode",
- id: "focusNode",
- tabIndex: "focusNode",
- alt: "focusNode",
- title: "focusNode"
- }),
+dojo.dnd.autoScrollNodes = function(e){
+ // summary:
+ // a handler for onmousemove event, which scrolls the first avaialble
+ // Dom element, it falls back to dojo.dnd.autoScroll()
+ // e: Event
+ // onmousemove event
- postMixInProperties: function(){
- // Setup name=foo string to be referenced from the template (but only if a name has been specified)
- // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
- // Regarding escaping, see heading "Attribute values" in
- // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
- this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
- this.inherited(arguments);
- },
+ // FIXME: needs more docs!
- postCreate: function(){
- this.inherited(arguments);
- this.connect(this.domNode, "onmousedown", "_onMouseDown");
- },
+ var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
- _setDisabledAttr: function(/*Boolean*/ value){
- this._set("disabled", value);
- dojo.attr(this.focusNode, 'disabled', value);
- if(this.valueNode){
- dojo.attr(this.valueNode, 'disabled', value);
+ for(var n = e.target; n;){
+ if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
+ var s = dojo.getComputedStyle(n),
+ overflow = (s.overflow.toLowerCase() in dojo.dnd._validOverflow),
+ overflowX = (s.overflowX.toLowerCase() in dojo.dnd._validOverflow),
+ overflowY = (s.overflowY.toLowerCase() in dojo.dnd._validOverflow);
+ if(overflow || overflowX || overflowY){
+ b = dojo._getContentBox(n, s);
+ t = dojo.position(n, true);
+ }
+ // overflow-x
+ if(overflow || overflowX){
+ w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2);
+ rx = e.pageX - t.x;
+ if(dojo.isWebKit || dojo.isOpera){
+ // FIXME: this code should not be here, it should be taken into account
+ // either by the event fixing code, or the dojo.position()
+ // FIXME: this code doesn't work on Opera 9.5 Beta
+ rx += dojo.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(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2);
+ ry = e.pageY - t.y;
+ if(dojo.isWebKit || dojo.isOpera){
+ // FIXME: this code should not be here, it should be taken into account
+ // either by the event fixing code, or the dojo.position()
+ // FIXME: this code doesn't work on Opera 9.5 Beta
+ ry += dojo.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; }
}
- dijit.setWaiState(this.focusNode, "disabled", value);
+ try{
+ n = n.parentNode;
+ }catch(x){
+ n = null;
+ }
+ }
+ dojo.dnd.autoScroll(e);
+};
- 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);
+ return dojo.dnd;
+});
- // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
- var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
- dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
- var node = this[attachPointName];
- // complex code because tabIndex=-1 on a <div> doesn't work on FF
- if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug
- node.setAttribute('tabIndex', "-1");
- }else{
- node.removeAttribute('tabIndex');
- }
- }, this);
- }else{
- if(this.tabIndex != ""){
- this.focusNode.setAttribute('tabIndex', this.tabIndex);
- }
+},
+'dojo/data/ItemFileWriteStore':function(){
+define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/window",
+ "./ItemFileReadStore", "../date/stamp"
+], function(lang, declare, arrayUtil, jsonUtil, window, ItemFileReadStore, dateStamp) {
+ // module:
+ // dojo/data/ItemFileWriteStore
+ // summary:
+ // TODOC
+
+/*===== var ItemFileReadStore = dojo.data.ItemFileReadStore; =====*/
+return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore, {
+ constructor: function(/* object */ keywordParameters){
+ // keywordParameters: {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. 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;
}
- },
- setDisabled: function(/*Boolean*/ disabled){
- // summary:
- // Deprecated. Use set('disabled', ...) instead.
- dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
- this.set('disabled', disabled);
+ // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
+ this._saveInProgress = false;
},
- _onFocus: function(e){
- if(this.scrollOnFocus){
- dojo.window.scrollIntoView(this.domNode);
+ 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");
}
- 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 && (dojo.style(this.domNode, "display") != "none");
+ _getIdentifierAttribute: function(){
+ // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
+ return this.getFeatures()['dojo.data.api.Identity'];
},
- focus: function(){
- // summary:
- // Put focus on this widget
- if(!this.disabled){
- dijit.focus(this.focusNode);
+
+/* 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();
}
- },
- 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;
+ 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{
- return 0;
+ 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");
+ }
}
- },
- 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,
+ // 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");
- _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;
+ 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._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){
- clearTimeout(this._onChangeHandle);
+ 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);
}
- // setTimout allows hidden value processing to run and
- // also the onChange handler can safely adjust focus, etc
- this._onChangeHandle = setTimeout(dojo.hitch(this,
- function(){
- this._onChangeHandle = null;
- this.onChange(newValue);
- }), 0); // try to collapse multiple onChange's fired faster than can be processed
+ 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);
}
- },
- create: function(){
- // Overrides _Widget.create()
- this.inherited(arguments);
- this._onChangeActive = true;
- },
+ this._pending._newItems[newIdentity] = newItem;
- destroy: function(){
- if(this._onChangeHandle){ // destroy called before last onChange has fired
- clearTimeout(this._onChangeHandle);
- this.onChange(this._lastValueReported);
+ //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.inherited(arguments);
+ this.onNew(newItem, pInfo); // dojo.data.api.Notification call
+ return newItem; // item
},
- setValue: function(/*String*/ value){
- // summary:
- // Deprecated. Use set('value', ...) instead.
- dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
- this.set('value', value);
+ _removeArrayElement: function(/* Array */ array, /* anything */ element){
+ var index = arrayUtil.indexOf(array, element);
+ if(index != -1){
+ array.splice(index, 1);
+ return true;
+ }
+ return false;
},
- getValue: function(){
- // summary:
- // Deprecated. Use get('value') instead.
- dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
- return this.get('value');
- },
-
- _onMouseDown: function(e){
- // If user clicks on the button, even if the mouse is released outside of it,
- // this button should get focus (to mimics native browser buttons).
- // This is also needed on chrome because otherwise buttons won't get focus at all,
- // which leads to bizarre focus restore on Dialog close etc.
- if(!e.ctrlKey && dojo.mouseButtons.isLeft(e) && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
- // Set a global event to handle mouseup, so it fires properly
- // even if the cursor leaves this.domNode before the mouse up event.
- var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
- if (this.isFocusable()) {
- this.focus();
- }
- this.disconnect(mouseUpConnector);
- });
- }
- }
-});
+ deleteItem: function(/* item */ item){
+ // summary: See dojo.data.api.Write.deleteItem()
+ this._assert(!this._saveInProgress);
+ this._assertIsItem(item);
-dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
-{
- // summary:
- // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
- // description:
- // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
- // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
- // works as expected.
+ // 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);
- // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
- // directly in the template as read by the parser in order to function. IE is known to specifically
- // require the 'name' attribute at element creation time. See #8484, #8660.
- // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
- // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
- // Seems like we really want value removed from attributeMap altogether
- // (although there's no easy way to do that now)
+ //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.
- // 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,
+ //Get the attributes list before we generate the backup so it
+ //doesn't pollute the attributes list.
+ var attributes = this.getAttributes(item);
- attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
- value: "",
- readOnly: "focusNode"
- }),
+ //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]);
+ }
- _setReadOnlyAttr: function(/*Boolean*/ value){
- dojo.attr(this.focusNode, 'readOnly', value);
- dijit.setWaiState(this.focusNode, "readonly", value);
- this._set("readOnly", value);
- },
+ //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);
- postCreate: function(){
- this.inherited(arguments);
+ //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);
+ }
+ }
+ }
+ }
+ }
+ }
- if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ // IE won't stop the event with keypress
- this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
+ this._arrayOfAllItems[indexInArrayOfAllItems] = null;
+
+ item[this._storeRefPropName] = null;
+ if(this._itemsByIdentity){
+ delete this._itemsByIdentity[identity];
}
- // 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;
+ 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;
},
- _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);
+ setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
+ // summary: See dojo.data.api.Write.set()
+ return this._setValueOrValues(item, attribute, value, true); // boolean
},
- _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);
+ setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
+ // summary: See dojo.data.api.Write.setValues()
+ return this._setValueOrValues(item, attribute, values, true); // boolean
},
- undo: function(){
- // summary:
- // Restore the value to the last value passed to onChange
- this._setValueAttr(this._lastValueReported, false);
+ unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
+ // summary: See dojo.data.api.Write.unsetAttribute()
+ return this._setValueOrValues(item, attribute, [], true);
},
- reset: function(){
- // summary:
- // Reset the widget's value to what it was at initialization time
- this._hasBeenBlurred = false;
- this._setValueAttr(this._resetValue, true);
- },
+ _setValueOrValues: function(/* 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.");
+ }
- _onKeyDown: function(e){
- if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
- var te;
- if(dojo.isIE){
- e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
- te = document.createEventObject();
- te.keyCode = dojo.keys.ESCAPE;
- te.shiftKey = e.shiftKey;
- e.srcElement.fireEvent('onkeypress', te);
+ // 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;
}
- },
- _layoutHackIE7: function(){
- // summary:
- // Work around table sizing bugs on IE7 by forcing redraw
+ // Okay, now we can actually change this attribute on the item
+ var success = false;
- if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
- var domNode = this.domNode;
- var parent = domNode.parentNode;
- var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
- var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
- var _this = this;
- while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
- (function ping(){
- var disconnectHandle = _this.connect(parent, "onscroll",
- function(e){
- _this.disconnect(disconnectHandle); // only call once
- pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
- setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
+ 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;
}
- );
- })();
- parent = parent.parentNode;
+ }, 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
+ },
-if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.dijit"] = true;
-dojo.provide("dijit.dijit");
+ _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.
+ 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(/* item */ refItem, /* 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(/* 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);
+ },
-/*=====
-dijit.dijit = {
- // summary:
- // A roll-up for common dijit methods
- // description:
- // A rollup file for the build system including the core and common
- // dijit files.
- //
- // example:
- // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script>
- //
-};
-=====*/
+ _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
+ },
-// All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
+ save: function(/* object */ keywordArgs){
+ // summary: See dojo.data.api.Write.save()
+ this._assert(!this._saveInProgress);
-// And some other stuff that we tend to pull in all the time anyway
+ // 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:{}
+ };
-if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.fx.Toggler"] = true;
-dojo.provide("dojo.fx.Toggler");
+ self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
+ if(keywordArgs && keywordArgs.onComplete){
+ var scope = keywordArgs.scope || window.global;
+ keywordArgs.onComplete.call(scope);
+ }
+ };
+ var saveFailedCallback = function(err){
+ self._saveInProgress = false;
+ if(keywordArgs && keywordArgs.onError){
+ var scope = keywordArgs.scope || window.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();
+ }
+ },
-dojo.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();
+ revert: function(){
+ // summary: See dojo.data.api.Write.revert()
+ this._assert(!this._saveInProgress);
- // node: DomNode
- // the node to target for the showing and hiding animations
- node: null,
+ 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];
+ }
- // showFunc: Function
- // The function that returns the `dojo.Animation` to show the node
- showFunc: dojo.fadeIn,
+ // 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];
- // hideFunc: Function
- // The function that returns the `dojo.Animation` to hide the node
- hideFunc: dojo.fadeOut,
+ //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];
+ }
+ }
- // showDuration:
- // Time in milliseconds to run the show Animation
- showDuration: 200,
+ 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];
+ }
+ }
- // hideDuration:
- // Time in milliseconds to run the hide Animation
- hideDuration: 200,
+ this._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+ return true; // boolean
+ },
- // 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
+ 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
+ }
+ },
- /*=====
- _showArgs: null,
- _showAnim: null,
+/* dojo.data.api.Notification */
- _hideArgs: null,
- _hideAnim: null,
+ onSet: function(/* item */ item,
+ /*attribute-name-string*/ attribute,
+ /*object|array*/ oldValue,
+ /*object|array*/ newValue){
+ // summary: See dojo.data.api.Notification.onSet()
- _isShowing: false,
- _isHiding: false,
- =====*/
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
- constructor: function(args){
- var _t = this;
+ onNew: function(/* item */ newItem, /*object?*/ parentInfo){
+ // summary: See dojo.data.api.Notification.onNew()
- dojo.mixin(_t, args);
- _t.node = args.node;
- _t._showArgs = dojo.mixin({}, args);
- _t._showArgs.node = _t.node;
- _t._showArgs.duration = _t.showDuration;
- _t.showAnim = _t.showFunc(_t._showArgs);
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
- _t._hideArgs = dojo.mixin({}, args);
- _t._hideArgs.node = _t.node;
- _t._hideArgs.duration = _t.hideDuration;
- _t.hideAnim = _t.hideFunc(_t._hideArgs);
+ onDelete: function(/* item */ deletedItem){
+ // summary: See dojo.data.api.Notification.onDelete()
- dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
- dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
},
- show: function(delay){
- // summary: Toggle the node to showing
- // delay: Integer?
- // Ammount of time to stall playing the show animation
- return this.showAnim.play(delay || 0);
- },
+ 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.
- hide: function(delay){
- // summary: Toggle the node to hidden
- // delay: Integer?
- // Ammount of time to stall playing the hide animation
- return this.hideAnim.play(delay || 0);
+ //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.");
+ }
+ }
}
});
-}
-
-if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.fx"] = true;
-dojo.provide("dojo.fx");
-
+});
+},
+'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
+ "dojo/_base/window", // win.doc
+ "../registry" // registry.getEnclosingWidget
+], function(array, declare, domAttr, event, lang, query, win, registry){
+
+ // module:
+ // dijit/form/_RadioButtonMixin
+ // summary:
+ // Mixin to provide widget functionality for an HTML radio button
-/*=====
-dojo.fx = {
- // summary: Effects library on top of Base animations
-};
-=====*/
-(function(){
-
- var d = dojo,
- _baseObj = {
- _fire: function(evt, args){
- if(this[evt]){
- this[evt].apply(this, args||[]);
- }
- return this;
- }
- };
+ return declare("dijit.form._RadioButtonMixin", null, {
+ // summary:
+ // Mixin to provide widget functionality for an HTML radio button
- var _chain = function(animations){
- this._index = -1;
- this._animations = animations||[];
- this._current = this._onAnimateCtx = this._onEndCtx = null;
+ // type: [private] String
+ // type attribute on <input> node.
+ // Users should not change this value.
+ type: "radio",
- this.duration = 0;
- d.forEach(this._animations, function(a){
- this.duration += a.duration;
- if(a.delay){ this.duration += a.delay; }
- }, this);
- };
- d.extend(_chain, {
- _onAnimate: function(){
- this._fire("onAnimate", arguments);
- },
- _onEnd: function(){
- d.disconnect(this._onAnimateCtx);
- d.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 = d.connect(this._current, "onAnimate", this, "_onAnimate");
- this._onEndCtx = d.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 = d.connect(this._current, "beforeBegin", this, function(){
- this._fire("beforeBegin");
- }),
- onBegin = d.connect(this._current, "onBegin", this, function(arg){
- this._fire("onBegin", arguments);
- }),
- onPlay = d.connect(this._current, "onPlay", this, function(arg){
- this._fire("onPlay", arguments);
- d.disconnect(beforeBegin);
- d.disconnect(onBegin);
- d.disconnect(onPlay);
- });
- if(this._onAnimateCtx){
- d.disconnect(this._onAnimateCtx);
- }
- this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
- if(this._onEndCtx){
- d.disconnect(this._onEndCtx);
- }
- this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
- this._current.play.apply(this._current, arguments);
- return this;
+ _getRelatedWidgets: function(){
+ // Private function needed to help iterate over all radio buttons in a group.
+ var ary = [];
+ query("input[type=radio]", this.focusNode.form || win.doc).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;
},
- pause: function(){
- if(this._current){
- var e = d.connect(this._current, "onPause", this, function(arg){
- this._fire("onPause", arguments);
- d.disconnect(e);
- });
- this._current.pause();
+
+ _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);
+ }
+ }));
}
- return this;
},
- gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
- this.pause();
- var offset = this.duration * percent;
- this._current = null;
- d.some(this._animations, function(a){
- if(a.duration <= offset){
- this._current = a;
- return true;
- }
- offset -= a.duration;
+
+ _onClick: function(/*Event*/ e){
+ if(this.checked || this.disabled){ // nothing to do
+ event.stop(e);
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 = d.connect(this._current, "onStop", this, function(arg){
- this._fire("onStop", arguments);
- d.disconnect(e);
- });
- this._current.stop();
+ 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;
- },
- status: function(){
- return this._current ? this._current.status() : "stopped";
- },
- destroy: function(){
- if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
- if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
+ return this.inherited(arguments);
}
});
- d.extend(_chain, _baseObj);
-
- dojo.fx.chain = function(/*dojo.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`
- // | dojo.fx.chain([
- // | dojo.fadeIn({ node:node }),
- // | dojo.fadeOut({ node:otherNode })
- // | ]).play();
- //
- return new _chain(animations) // dojo.Animation
- };
+});
- var _combine = function(animations){
- this._animations = animations||[];
- this._connects = [];
- this._finished = 0;
+},
+'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\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\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></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", ["../main", "./Moveable"], function(dojo) {
+ // module:
+ // dojo/dnd/TimedMoveable
+ // summary:
+ // TODOC
- this.duration = 0;
- d.forEach(animations, function(a){
- var duration = a.duration;
- if(a.delay){ duration += a.delay; }
- if(this.duration < duration){ this.duration = duration; }
- this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
- }, this);
-
- this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
- var self = this;
- d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
- function(evt){
- self._connects.push(d.connect(self._pseudoAnimation, evt,
- function(){ self._fire(evt, arguments); }
- ));
- }
- );
- };
- d.extend(_combine, {
- _doAction: function(action, args){
- d.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;
- d.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(){
- d.forEach(this._connects, dojo.disconnect);
- }
+ /*=====
+ dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
+ // timeout: Number
+ // delay move by this number of ms,
+ // accumulating position changes during the timeout
+ timeout: 0
});
- d.extend(_combine, _baseObj);
+ =====*/
- dojo.fx.combine = function(/*dojo.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
- // | dojo.fx.combine([
- // | dojo.fadeIn({ node:node }),
- // | dojo.fadeOut({ node:otherNode })
- // | ]).play();
- //
- // example:
- // When the longest animation ends, execute a function:
- // | var anim = dojo.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.Animation
- };
+ // precalculate long expressions
+ var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
- dojo.fx.wipeIn = function(/*Object*/ args){
+ dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
// 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:
- // | dojo.fx.wipeIn({
- // | node:"someId"
- // | }).play()
- var node = args.node = d.byId(args.node), s = node.style, o;
-
- var anim = d.animateProperty(d.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 = d.style(node, "height");
- return Math.max(height, 1);
- }
- },
- end: function(){
- return node.scrollHeight;
- }
- }
- }
- }, args));
+ // 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.
- d.connect(anim, "onEnd", function(){
- s.height = "auto";
- s.overflow = o;
- });
+ // object attributes (for markup)
+ timeout: 40, // in ms, 40ms corresponds to 25 fps
- return anim; // dojo.Animation
- };
+ 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: dojo.dnd.__TimedMoveableArgs
+ // object with additional parameters.
- dojo.fx.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:
- // | dojo.fx.wipeOut({ node:"someId" }).play()
-
- var node = args.node = d.byId(args.node), s = node.style, o;
-
- var anim = d.animateProperty(d.mixin({
- properties: {
- height: {
- end: 1 // 0 causes IE to display the whole panel
- }
+ // sanitize parameters
+ if(!params){ params = {}; }
+ if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
+ this.timeout = params.timeout;
}
- }, args));
-
- d.connect(anim, "beforeBegin", function(){
- o = s.overflow;
- s.overflow = "hidden";
- s.display = "";
- });
- d.connect(anim, "onEnd", function(){
- s.overflow = o;
- s.height = "auto";
- s.display = "none";
- });
-
- return anim; // dojo.Animation
- };
-
- dojo.fx.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:
- // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
-
- var node = args.node = d.byId(args.node),
- top = null, left = null;
-
- var init = (function(n){
- return function(){
- var cs = d.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 = d.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 = d.animateProperty(d.mixin({
- properties: {
- top: args.top || 0,
- left: args.left || 0
+ onMoveStop: function(/* dojo.dnd.Mover */ mover){
+ if(mover._timer){
+ // stop timer
+ clearTimeout(mover._timer);
+ // reflect the last received position
+ oldOnMove.call(this, mover, mover._leftTop)
}
- }, args));
- d.connect(anim, "beforeBegin", anim, init);
-
- return anim; // dojo.Animation
- };
-
-})();
-
-}
-
-if(!dojo._hasResource["dojo.NodeList-fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.NodeList-fx"] = true;
-dojo.provide("dojo.NodeList-fx");
+ dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
+ },
+ onMove: function(/* dojo.dnd.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);
+ }
+ }
+ });
+ return dojo.dnd.TimedMoveable;
+
+});
+},
+'dojo/NodeList-fx':function(){
+define("dojo/NodeList-fx", ["dojo/_base/NodeList", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
+ function(NodeList, lang, connectLib, baseFx, coreFx) {
+ // module:
+ // dojo/NodeList-fx
+ // summary:
+ // TODOC
/*=====
dojo["NodeList-fx"] = {
- // summary: Adds dojo.fx animation support to dojo.query()
+ // 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.
};
+
+// doc alias helpers:
+NodeList = dojo.NodeList;
=====*/
-dojo.extend(dojo.NodeList, {
+lang.extend(NodeList, {
_anim: function(obj, method, args){
args = args||{};
- var a = dojo.fx.combine(
+ var a = coreFx.combine(
this.map(function(item){
var tmpArgs = { node: item };
- dojo.mixin(tmpArgs, args);
+ lang.mixin(tmpArgs, args);
return obj[method](tmpArgs);
})
);
@@ -6956,129 +11138,133 @@ dojo.extend(dojo.NodeList, {
},
wipeIn: function(args){
- // summary:
+ // summary:
// wipe in all elements of this NodeList via `dojo.fx.wipeIn`
//
- // args: Object?
+ // args: Object?
// Additional dojo.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
- // returns: dojo.Animation|dojo.NodeList
+ // returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// Fade in all tables with class "blah":
// | dojo.query("table.blah").wipeIn().play();
//
- // example:
+ // example:
// Utilizing `auto` to get the NodeList back:
// | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
//
- return this._anim(dojo.fx, "wipeIn", args); // dojo.Animation|dojo.NodeList
+ return this._anim(coreFx, "wipeIn", args); // dojo.Animation|dojo.NodeList
},
wipeOut: function(args){
- // summary:
+ // summary:
// wipe out all elements of this NodeList via `dojo.fx.wipeOut`
//
- // args: Object?
+ // args: Object?
// Additional dojo.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
- // returns: dojo.Animation|dojo.NodeList
+ // returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// Wipe out all tables with class "blah":
// | dojo.query("table.blah").wipeOut().play();
- return this._anim(dojo.fx, "wipeOut", args); // dojo.Animation|dojo.NodeList
+ return this._anim(coreFx, "wipeOut", args); // dojo.Animation|dojo.NodeList
},
slideTo: function(args){
- // summary:
+ // summary:
// slide all elements of the node list to the specified place via `dojo.fx.slideTo`
//
- // args: Object?
+ // args: Object?
// Additional dojo.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
- // returns: dojo.Animation|dojo.NodeList
+ // returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// | Move all tables with class "blah" to 300/300:
// | dojo.query("table.blah").slideTo({
// | left: 40,
// | top: 50
// | }).play();
- return this._anim(dojo.fx, "slideTo", args); // dojo.Animation|dojo.NodeList
+ return this._anim(coreFx, "slideTo", args); // dojo.Animation|dojo.NodeList
},
fadeIn: function(args){
- // summary:
+ // summary:
// fade in all elements of this NodeList via `dojo.fadeIn`
//
- // args: Object?
+ // args: Object?
// Additional dojo.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
- // returns: dojo.Animation|dojo.NodeList
+ // returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// Fade in all tables with class "blah":
// | dojo.query("table.blah").fadeIn().play();
- return this._anim(dojo, "fadeIn", args); // dojo.Animation|dojo.NodeList
+ return this._anim(baseFx, "fadeIn", args); // dojo.Animation|dojo.NodeList
},
fadeOut: function(args){
- // summary:
+ // summary:
// fade out all elements of this NodeList via `dojo.fadeOut`
//
- // args: Object?
+ // args: Object?
// Additional dojo.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
- // returns: dojo.Animation|dojo.NodeList
+ // returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// Fade out all elements with class "zork":
// | dojo.query(".zork").fadeOut().play();
- // example:
+ // 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:
+ // example:
// Using `auto`:
// | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
//
- return this._anim(dojo, "fadeOut", args); // dojo.Animation|dojo.NodeList
+ return this._anim(baseFx, "fadeOut", args); // dojo.Animation|dojo.NodeList
},
animateProperty: function(args){
- // summary:
+ // summary:
// Animate all elements of this NodeList across the properties specified.
// syntax identical to `dojo.animateProperty`
//
+ // args: Object?
+ // Additional dojo.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
// returns: dojo.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.Animation instance is returned and must be .play()'ed
//
- // example:
+ // example:
// | dojo.query(".zork").animateProperty({
// | duration: 500,
// | properties: {
@@ -7094,7 +11280,7 @@ dojo.extend(dojo.NodeList, {
// | height:240
// | }
// | }).onclick(handler);
- return this._anim(dojo, "animateProperty", args); // dojo.Animation|dojo.NodeList
+ return this._anim(baseFx, "animateProperty", args); // dojo.Animation|dojo.NodeList
},
anim: function( /*Object*/ properties,
@@ -7102,31 +11288,31 @@ dojo.extend(dojo.NodeList, {
/*Function?*/ easing,
/*Function?*/ onEnd,
/*Integer?*/ delay){
- // summary:
+ // 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
+ // properties: Object
// the properties to animate. does NOT support the `auto` parameter like other
// NodeList-fx methods.
- // duration: Integer?
+ // duration: Integer?
// Optional. The time to run the animations for
- // easing: Function?
+ // easing: Function?
// Optional. The easing function to use.
- // onEnd: Function?
+ // onEnd: Function?
// A function to be called when the animation ends
- // delay:
+ // delay:
// how long to delay playing the returned animation
- // example:
+ // example:
// Another way to fade out:
// | dojo.query(".thinger").anim({ opacity: 0 });
- // example:
+ // 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 = dojo.fx.combine(
+ var canim = coreFx.combine(
this.map(function(item){
- return dojo.animateProperty({
+ return baseFx.animateProperty({
node: item,
properties: properties,
duration: duration||350,
@@ -7135,837 +11321,496 @@ dojo.extend(dojo.NodeList, {
})
);
if(onEnd){
- dojo.connect(canim, "onEnd", onEnd);
+ connectLib.connect(canim, "onEnd", onEnd);
}
return canim.play(delay||0); // dojo.Animation
}
});
-}
-
-if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.colors"] = true;
-dojo.provide("dojo.colors");
-
-dojo.getObject("colors", true, dojo);
+return NodeList;
+});
-//TODO: this module appears to break naming conventions
+},
+'dijit/form/_ListMouseMixin':function(){
+define("dijit/form/_ListMouseMixin", [
+ "dojo/_base/declare", // declare
+ "dojo/_base/event", // event.stop
+ "dojo/touch",
+ "./_ListBase"
+], function(declare, event, touch, _ListBase){
/*=====
-dojo.colors = {
- // summary: Color utilities
-}
+var _ListBase = dijit.form._ListBase;
=====*/
-(function(){
- // 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;
- };
-
- dojo.colorFromRgb = function(/*String*/ color, /*dojo.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 = dojo.map(c, function(x){
- return parseFloat(x) * 2.56;
- });
- if(l == 4){ a[3] = c[3]; }
- return dojo.colorFromArray(a, obj); // dojo.Color
- }
- return dojo.colorFromArray(c, obj); // dojo.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 dojo.colorFromArray(a, obj); // dojo.Color
- }
- }
- return null; // dojo.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
- };
-
- dojo.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.Color
- };
-})();
-
-
-dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
- // summary: creates a greyscale color with an optional alpha
- return dojo.colorFromArray([g, g, g, a]);
-};
-
-// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
-dojo.mixin(dojo.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],
- transparent: [0, 0, 0, 0],
- turquoise: [64,224,208],
- violet: [238,130,238],
- wheat: [245,222,179],
- whitesmoke: [245,245,245],
- yellowgreen: [154,205,50]
-});
-
-}
-
-if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.i18n"] = true;
-dojo.provide("dojo.i18n");
+// module:
+// dijit/form/_ListMouseMixin
+// summary:
+// a mixin to handle mouse or touch events for a focus-less menu
-dojo.getObject("i18n", true, dojo);
+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
-/*=====
-dojo.i18n = {
- // summary: Utility classes to enable loading of resources for internationalization (i18n)
-};
-=====*/
+ postCreate: function(){
+ this.inherited(arguments);
+ this.connect(this.domNode, touch.press, "_onMouseDown");
+ this.connect(this.domNode, touch.release, "_onMouseUp");
+ this.connect(this.domNode, "onmouseover", "_onMouseOver");
+ this.connect(this.domNode, "onmouseout", "_onMouseOut");
+ },
-// when using a real AMD loader, dojo.i18n.getLocalization is already defined by dojo/lib/backCompat
-dojo.i18n.getLocalization = dojo.i18n.getLocalization || function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
- // summary:
- // Returns an Object containing the localization for a given resource
- // bundle in a package, matching the specified locale.
- // description:
- // Returns a hash containing name/value pairs in its prototypesuch
- // that values can be easily overridden. Throws an exception if the
- // bundle is not found. Bundle must have already been loaded by
- // `dojo.requireLocalization()` or by a build optimization step. NOTE:
- // try not to call this method as part of an object property
- // definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In
- // some loading situations, the bundle may not be available in time
- // for the object definition. Instead, call this method inside a
- // function that is run after all modules load or the page loads (like
- // in `dojo.addOnLoad()`), or in a widget lifecycle method.
- // packageName:
- // package which is associated with this resource
- // bundleName:
- // the base filename of the resource bundle (without the ".js" suffix)
- // locale:
- // the variant to load (optional). By default, the locale defined by
- // the host environment: dojo.locale
-
- locale = dojo.i18n.normalizeLocale(locale);
-
- // look for nearest locale match
- var elements = locale.split('-');
- var module = [packageName,"nls",bundleName].join('.');
- var bundle = dojo._loadedModules[module];
- if(bundle){
- var localization;
- for(var i = elements.length; i > 0; i--){
- var loc = elements.slice(0, i).join('_');
- if(bundle[loc]){
- localization = bundle[loc];
- break;
- }
- }
- if(!localization){
- localization = bundle.ROOT;
+ _onMouseDown: function(/*Event*/ evt){
+ event.stop(evt);
+ if(this._hoveredNode){
+ this.onUnhover(this._hoveredNode);
+ this._hoveredNode = null;
}
+ this._isDragging = true;
+ this._setSelectedAttr(this._getTarget(evt));
+ },
- // make a singleton prototype so that the caller won't accidentally change the values globally
- if(localization){
- var clazz = function(){};
- clazz.prototype = localization;
- return new clazz(); // Object
+ _onMouseUp: function(/*Event*/ evt){
+ event.stop(evt);
+ this._isDragging = false;
+ var selectedNode = this._getSelectedAttr();
+ var target = this._getTarget(evt);
+ 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);
}
- }
-
- throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
-};
-
-dojo.i18n.normalizeLocale = function(/*String?*/locale){
- // summary:
- // Returns canonical form of locale, as used by Dojo.
- //
- // description:
- // All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
- // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by
- // the user agent's locale unless overridden by djConfig.
-
- var result = locale ? locale.toLowerCase() : dojo.locale;
- if(result == "root"){
- result = "ROOT";
- }
- return result; // String
-};
-
-dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
- // summary:
- // See dojo.requireLocalization()
- // description:
- // Called by the bootstrap, but factored out so that it is only
- // included in the build when needed.
+ },
- var targetLocale = dojo.i18n.normalizeLocale(locale);
- var bundlePackage = [moduleName, "nls", bundleName].join(".");
- // NOTE:
- // When loading these resources, the packaging does not match what is
- // on disk. This is an implementation detail, as this is just a
- // private data structure to hold the loaded resources. e.g.
- // `tests/hello/nls/en-us/salutations.js` is loaded as the object
- // `tests.hello.nls.salutations.en_us={...}` The structure on disk is
- // intended to be most convenient for developers and translators, but
- // in memory it is more logical and efficient to store in a different
- // order. Locales cannot use dashes, since the resulting path will
- // not evaluate as valid JS, so we translate them to underscores.
-
- //Find the best-match locale to load if we have available flat locales.
- var bestLocale = "";
- if(availableFlatLocales){
- var flatLocales = availableFlatLocales.split(",");
- for(var i = 0; i < flatLocales.length; i++){
- //Locale must match from start of string.
- //Using ["indexOf"] so customBase builds do not see
- //this as a dojo._base.array dependency.
- if(targetLocale["indexOf"](flatLocales[i]) == 0){
- if(flatLocales[i].length > bestLocale.length){
- bestLocale = flatLocales[i];
- }
- }
- }
- if(!bestLocale){
- bestLocale = "ROOT";
+ _onMouseOut: function(/*Event*/ /*===== evt ====*/){
+ if(this._hoveredNode){
+ this.onUnhover(this._hoveredNode);
+ if(this._getSelectedAttr() == this._hoveredNode){
+ this.onSelect(this._hoveredNode);
+ }
+ this._hoveredNode = null;
}
- }
-
- //See if the desired locale is already loaded.
- var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
- var bundle = dojo._loadedModules[bundlePackage];
- var localizedBundle = null;
- if(bundle){
- if(dojo.config.localizationComplete && bundle._built){return;}
- var jsLoc = tempLocale.replace(/-/g, '_');
- var translationPackage = bundlePackage+"."+jsLoc;
- localizedBundle = dojo._loadedModules[translationPackage];
- }
+ if(this._isDragging){
+ this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
+ }
+ },
- if(!localizedBundle){
- bundle = dojo["provide"](bundlePackage);
- var syms = dojo._getModuleSymbols(moduleName);
- var modpath = syms.concat("nls").join("/");
- var parent;
-
- dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
- var jsLoc = loc.replace(/-/g, '_');
- var translationPackage = bundlePackage + "." + jsLoc;
- var loaded = false;
- if(!dojo._loadedModules[translationPackage]){
- // Mark loaded whether it's found or not, so that further load attempts will not be made
- dojo["provide"](translationPackage);
- var module = [modpath];
- if(loc != "ROOT"){module.push(loc);}
- module.push(bundleName);
- var filespec = module.join("/") + '.js';
- loaded = dojo._loadPath(filespec, null, function(hash){
- hash = hash.root || hash;
- // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
- var clazz = function(){};
- clazz.prototype = parent;
- bundle[jsLoc] = new clazz();
- for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
- });
- }else{
- loaded = true;
+ _onMouseOver: function(/*Event*/ evt){
+ if(this._cancelDrag){
+ var time = (new Date()).getTime();
+ if(time > this._cancelDrag){
+ this._isDragging = false;
}
- if(loaded && bundle[jsLoc]){
- parent = bundle[jsLoc];
- }else{
- bundle[jsLoc] = parent;
+ this._cancelDrag = null;
+ }
+ var node = this._getTarget(evt);
+ if(!node){ return; }
+ if(this._hoveredNode != node){
+ if(this._hoveredNode){
+ this._onMouseOut({ target: this._hoveredNode });
}
-
- if(availableFlatLocales){
- //Stop the locale path searching if we know the availableFlatLocales, since
- //the first call to this function will load the only bundle that is needed.
- return true;
+ if(node && node.parentNode == this.containerNode){
+ if(this._isDragging){
+ this._setSelectedAttr(node);
+ }else{
+ this._hoveredNode = node;
+ this.onHover(node);
+ }
}
- });
+ }
}
+});
- //Save the best locale bundle as the target locale bundle when we know the
- //the available bundles.
- if(availableFlatLocales && targetLocale != bestLocale){
- bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
- }
-};
+});
-(function(){
- // If other locales are used, dojo.requireLocalization should load them as
- // well, by default.
- //
- // Override dojo.requireLocalization to do load the default bundle, then
- // iterate through the extraLocale list and load those translations as
- // well, unless a particular locale was requested.
+},
+'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\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
+ // summary:
+ // TODOC
- var extra = dojo.config.extraLocale;
- if(extra){
- if(!extra instanceof Array){
- extra = [extra];
- }
- var req = dojo.i18n._requireLocalization;
- dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
- req(m,b,locale, availableFlatLocales);
- if(locale){return;}
- for(var i=0; i<extra.length; i++){
- req(m,b,extra[i], availableFlatLocales);
- }
- };
- }
-})();
-
-dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
- // summary:
- // A helper method to assist in searching for locale-based resources.
- // Will iterate through the variants of a particular locale, either up
- // or down, executing a callback function. For example, "en-us" and
- // true will try "en-us" followed by "en" and finally "ROOT".
+/*=====
+dojo.__cookieProps = function(){
+ // 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
+ this.expires = expires;
+ this.path = path;
+ this.domain = domain;
+ this.secure = secure;
+}
+=====*/
- locale = dojo.i18n.normalizeLocale(locale);
- var elements = locale.split('-');
- var searchlist = [];
- for(var i = elements.length; i > 0; i--){
- searchlist.push(elements.slice(0, i).join('-'));
- }
- searchlist.push(false);
- if(down){searchlist.reverse();}
+dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__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:
+ // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
+ //
+ // example:
+ // de-serialize a cookie back into a JavaScript object:
+ // | var config = dojo.fromJson(dojo.cookie("configObj"));
+ //
+ // example:
+ // delete a cookie:
+ // | dojo.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(); }
- for(var j = searchlist.length - 1; j >= 0; j--){
- var loc = searchlist[j] || "ROOT";
- var stop = searchFunc(loc);
- if(stop){ break; }
+ 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.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
+dojo.cookie.isSupported = function(){
// summary:
- // Load built, flattened resource bundles, if available for all
- // locales used in the page. Only called by built layer files.
-
- function preload(locale){
- locale = dojo.i18n.normalizeLocale(locale);
- dojo.i18n._searchLocalePath(locale, true, function(loc){
- for(var i=0; i<localesGenerated.length;i++){
- if(localesGenerated[i] == loc){
- dojo["require"](bundlePrefix+"_"+loc);
- return true; // Boolean
- }
- }
- return false; // Boolean
- });
- }
- preload();
- var extra = dojo.config.extraLocale||[];
- for(var i=0; i<extra.length; i++){
- preload(extra[i]);
+ // 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;
};
-}
-
-if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._PaletteMixin"] = true;
-dojo.provide("dijit._PaletteMixin");
-
-
+return dojo.cookie;
+});
-dojo.declare("dijit._PaletteMixin",
- [dijit._CssStateMixin],
- {
+},
+'dojo/cache':function(){
+define("dojo/cache", ["./_base/kernel", "./text"], function(dojo, text){
+ // module:
+ // dojo/cache
// 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.
+ // The module defines dojo.cache by loading dojo/text.
- // 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
- // < 1.0 means that each typematic event fires at an increasing faster rate
- timeoutChangeRate: 0.90,
-
- // value: String
- // Currently selected color/emoticon/etc.
- value: null,
-
- // _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,
-=====*/
+ //dojo.cache is defined in dojo/text
+ return dojo.cache;
+});
-/*=====
- // _xDim: [protected] Integer
- // This is the number of cells horizontally across.
- _xDim: null,
-=====*/
+},
+'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=\"presentation\"\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){
/*=====
- // _yDim: [protected] Integer
- // This is the number of cells vertically down.
- _yDim: null,
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
=====*/
- // tabIndex: String
- // Widget tab index.
- tabIndex: "0",
+// module:
+// dijit/ProgressBar
+// summary:
+// A progress indication widget, showing the amount completed
+// (often the percentage completed) of a task.
- // cellClass: [protected] String
- // CSS class applied to each cell in the palette
- cellClass: "dijitPaletteCell",
- // dyeClass: [protected] String
- // Name of javascript class for Object created for each cell of the palette.
- // dyeClass should implements dijit.Dye interface
- dyeClass: '',
+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>
- _preparePalette: function(choices, titles, dyeClassObj) {
- // 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
- // dyeClassObj: Constructor?
- // If specified, use this constructor rather than this.dyeClass
+ // progress: [const] String (Percentage or Number)
+ // Number or percentage indicating amount of task completed.
+ // Deprecated. Use "value" instead.
+ progress: "0",
- this._cells = [];
- var url = this._blankGif;
-
- dyeClassObj = dyeClassObj || dojo.getObject(this.dyeClass);
+ // 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: "",
- for(var row=0; row < choices.length; row++){
- var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
- for(var col=0; col < choices[row].length; col++){
- var value = choices[row][col];
- if(value){
- var cellObject = new dyeClassObj(value, row, col);
-
- var cellNode = dojo.create("td", {
- "class": this.cellClass,
- tabIndex: "-1",
- title: titles[value]
- });
+ // maximum: [const] Float
+ // Max sample number
+ maximum: 100,
- // prepare cell inner structure
- cellObject.fillCell(cellNode, url);
+ // places: [const] Number
+ // Number of places to show in values; 0 by default
+ places: 0,
- this.connect(cellNode, "ondijitclick", "_onCellClick");
- this._trackMouseState(cellNode, this.cellClass);
+ // 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,
- dojo.place(cellNode, rowNode);
+ // label: String?
+ // Label on progress bar. Defaults to percentage for determinate progress bar and
+ // blank for indeterminate progress bar.
+ label:"",
- cellNode.index = this._cells.length;
+ // 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: '',
- // save cell info into _cells
- this._cells.push({node:cellNode, dye:cellObject});
- }
- }
- }
- this._xDim = choices[0].length;
- this._yDim = choices.length;
+ templateString: template,
- // 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.
+ // _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"),
- 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._connects.push(
- dijit.typematic.addKeyListener(
- this.domNode,
- {charOrCode:dojo.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
- )
- );
+ postMixInProperties: function(){
+ this.inherited(arguments);
+ if(!("value" in this.params)){
+ this.value = this.indeterminate ? Infinity : this.progress;
}
},
- postCreate: function(){
+ buildRendering: 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
- dijit.focus(this._currentFocus);
+ this.indeterminateHighContrastImage.setAttribute("src",
+ this._indeterminateHighContrastImagePath.toString());
+ this.update();
},
- _onCellClick: function(/*Event*/ evt){
+ update: function(/*Object?*/attributes){
// summary:
- // Handler for click, enter key & space key. Selects the cell.
- // evt:
- // The event.
+ // 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
- var target = evt.currentTarget,
- 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.
- // Use setTimeout because IE doesn't like changing focus inside of an event handler.
- this._setCurrent(target);
- setTimeout(dojo.hitch(this, function(){
- dijit.focus(target);
- this._setValueAttr(value, true);
- }));
-
- // workaround bug where hover class is not removed on popup because the popup is
- // closed and then there's no onblur event on the cell
- dojo.removeClass(target, "dijitPaletteCellHover");
+ // TODO: deprecate this method and use set() instead
- dojo.stopEvent(evt);
- },
+ lang.mixin(this, attributes || {});
+ var tip = this.internalProgress, ap = this.domNode;
+ var percent = 1;
+ if(this.indeterminate){
+ ap.removeAttribute("aria-valuenow");
+ ap.removeAttribute("aria-valuemin");
+ ap.removeAttribute("aria-valuemax");
+ }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;
+ }
- _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
- dojo.attr(this._currentFocus, "tabIndex", "-1");
+ ap.setAttribute("aria-describedby", this.labelNode.id);
+ ap.setAttribute("aria-valuenow", this.progress);
+ ap.setAttribute("aria-valuemin", 0);
+ ap.setAttribute("aria-valuemax", this.maximum);
}
+ this.labelNode.innerHTML = this.report(percent);
- // Set tabIndex of new cell
- this._currentFocus = node;
- if(node){
- dojo.attr(node, "tabIndex", this.tabIndex);
- }
+ domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
+ tip.style.width = (percent * 100) + "%";
+ this.onChange();
},
- _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:
- // Optional parameter used to tell the select whether or not to fire
- // onChange event.
-
- // clear old selected cell
- if(this._selectedCell >= 0){
- dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
- }
- 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;
- dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
- break;
- }
- }
+ _setValueAttr: function(v){
+ this._set("value", v);
+ if(v == Infinity){
+ this.update({indeterminate:true});
+ }else{
+ this.update({indeterminate:false, progress:v});
}
-
- // record new value, or null if no matching cell
- this._set("value", this._selectedCell >= 0 ? value : null);
+ },
- if(priorityChange || priorityChange === undefined){
- this.onChange(value);
- }
+ _setLabelAttr: function(label){
+ this._set("label", label);
+ this.update();
},
- onChange: function(value){
- // summary:
- // Callback when a cell is selected.
- // value: String
- // Value corresponding to cell.
+ _setIndeterminateAttr: function(indeterminate){
+ // Deprecated, use set("value", ...) instead
+ this.indeterminate = indeterminate;
+ this.update();
},
- _navigateByKey: function(increment, typeCount){
+ report: function(/*float*/percent){
// 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.
+ // Generates message to show inside progress bar (normally indicating amount of task completed).
+ // May be overridden.
// tags:
- // private
-
- // typecount == -1 means the key is released.
- if(typeCount == -1){ return; }
-
- var newFocusIndex = this._currentFocus.index + increment;
- if(newFocusIndex < this._cells.length && newFocusIndex > -1){
- var focusNode = this._cells[newFocusIndex].node;
- this._setCurrent(focusNode);
+ // extension
- // Actually focus the node, for the benefit of screen readers.
- // Use setTimeout because IE doesn't like changing focus inside of an event handler
- setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
- }
+ return this.label ? this.label :
+ (this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
},
- _getDye: function(/*DomNode*/ cell){
+ onChange: function(){
// summary:
- // Get JS object for given cell DOMNode
-
- return this._cells[cell.index].dye;
+ // Callback fired when progress updates.
+ // tags:
+ // extension
}
});
-/*=====
-dojo.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.
- },
+},
+'dijit/_base/popup':function(){
+define("dijit/_base/popup", [
+ "dojo/dom-class", // domClass.contains
+ "../popup",
+ "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
+], function(domClass, popup){
+
+// module:
+// dijit/_base/popup
+// summary:
+// 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(){}
+ };
+ }
+ return origCreateWrapper.call(this, widget);
+};
- fillCell: function(cell, blankGif){
- // summary:
- // Add cell DOMNode inner structure
- // cell: DomNode
- // The surrounding cell
- // blankGif: String
- // URL for blank cell image
+// Support old format of orient parameter
+var origOpen = popup.open;
+popup.open = function(/*dijit.popup.__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;
}
-);
-=====*/
-
-}
-
-if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.ColorPalette"] = true;
-dojo.provide("dijit.ColorPalette");
-
-
-
+ 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",
+ "./_TemplatedMixin",
+ "./_PaletteMixin",
+ "dojo/i18n", // i18n.getLocalization
+ "dojo/_base/Color", // dojo.Color dojo.Color.named
+ "dojo/_base/declare", // declare
+ "dojo/dom-class", // domClass.contains
+ "dojo/dom-construct", // domConstruct.place
+ "dojo/_base/window", // win.body
+ "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, i18n, Color,
+ declare, domClass, domConstruct, win, string){
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _PaletteMixin = dijit._PaletteMixin;
+=====*/
+// module:
+// dijit/ColorPalette
+// summary:
+// A keyboard accessible color-picking widget
-dojo.declare("dijit.ColorPalette",
- [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
- {
+var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
// summary:
// A keyboard accessible color-picking widget
// description:
@@ -7973,7 +11818,7 @@ dojo.declare("dijit.ColorPalette",
// Can be used standalone, or as a popup.
//
// example:
- // | <div dojoType="dijit.ColorPalette"></div>
+ // | <div data-dojo-type="dijit.ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
@@ -8004,29 +11849,35 @@ dojo.declare("dijit.ColorPalette",
// templateString: String
// The template of this widget.
- templateString: dojo.cache("dijit", "templates/ColorPalette.html", "<div class=\"dijitInline dijitColorPalette\">\n\t<table class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\">\n\t\t<tbody dojoAttachPoint=\"gridNode\"></tbody>\n\t</table>\n</div>\n"),
+ templateString: template,
baseClass: "dijitColorPalette",
+ _dyeFactory: function(value, row, col){
+ // Overrides _PaletteMixin._dyeFactory().
+ return new this._dyeClass(value, row, col);
+ },
+
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, {
+ hc: domClass.contains(win.body(), "dijit_a11y"),
+ palette: this.palette
+ });
+
// Creates <img> nodes in each cell of the template.
- // Pass in "customized" dijit._Color constructor for specified palette and high-contrast vs. normal mode
this._preparePalette(
this._palettes[this.palette],
- dojo.i18n.getLocalization("dojo", "colors", this.lang),
- dojo.declare(dijit._Color, {
- hc: dojo.hasClass(dojo.body(), "dijit_a11y"),
- palette: this.palette
- })
- );
+ i18n.getLocalization("dojo", "colors", this.lang));
}
});
-dojo.declare("dijit._Color", dojo.Color, {
+ColorPalette._Color = declare("dijit._Color", Color, {
// summary:
// Object associated with each cell in a ColorPalette palette.
// Implements dijit.Dye.
@@ -8049,15 +11900,15 @@ dojo.declare("dijit._Color", dojo.Color, {
// _imagePaths: [protected] Map
// This is stores the path to the palette images used for high-contrast mode display
_imagePaths: {
- "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
- "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png")
+ "7x10": require.toUrl("./themes/a11y/colors7x10.png"),
+ "3x4": require.toUrl("./themes/a11y/colors3x4.png")
},
constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
this._alias = alias;
this._row = row;
this._col = col;
- this.setColor(dojo.Color.named[alias]);
+ this.setColor(Color.named[alias]);
},
getValue: function(){
@@ -8068,12 +11919,12 @@ dojo.declare("dijit._Color", dojo.Color, {
},
fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
- var html = dojo.string.substitute(this.hc ? this.hcTemplate : this.template, {
+ var html = string.substitute(this.hc ? this.hcTemplate : this.template, {
// substitution variables for normal mode
color: this.toHex(),
blankGif: blankGif,
alt: this._alias,
-
+
// variables used for high contrast mode
image: this._imagePaths[this.palette].toString(),
left: this._col * -20 - 5,
@@ -8081,270 +11932,2043 @@ dojo.declare("dijit._Color", dojo.Color, {
size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
});
- dojo.place(html, cell);
+ domConstruct.place(html, cell);
}
});
-}
-if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.common"] = true;
-dojo.provide("dojo.dnd.common");
+return ColorPalette;
+});
-dojo.getObject("dnd", true, dojo);
+},
+'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
+ // summary:
+ // This module contains 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("/");
+ }
+ }
+ }
-dojo.dnd.getCopyKeyState = dojo.isCopyKey;
+ 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);
+ }
+ }
-dojo.dnd._uniqueId = 0;
-dojo.dnd.getUniqueId = function(){
+ 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;
+});
+
+},
+'dojo/text':function(){
+define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo, require, has, xhr){
+ // module:
+ // dojo/text
// summary:
- // returns a unique string for use with any DOM element
- var id;
- do{
- id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
- }while(dojo.byId(id));
- return id;
+ // 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.
+
+ var getText;
+ if(1){
+ getText= function(url, sync, load){
+ xhr("GET", {url:url, sync:!!sync, load:load});
+ };
+ }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 = {},
+
+ result= {
+ dynamic:
+ // the dojo/text caches it's own resources because of dojo.cache
+ 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 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]),
+ text = notFound,
+ finish = function(text){
+ load(stripFlag ? strip(text) : text);
+ };
+ if(absMid in theCache){
+ text = theCache[absMid];
+ }else if(url in require.cache){
+ text = require.cache[url];
+ }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);
+ }
+ }
+ };
+
+ dojo.cache= function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
+ // * (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 result;
+
+/*=====
+dojo.cache = function(module, url, 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 demostrates how an object can be passed in as
+ // the first argument, then the value argument can then be the second argument.
+ // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
+ // | //text variable will contain just "<h1>Hello</h1>".
+ // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
+ return val; //String
};
+=====*/
+});
-dojo.dnd._empty = {};
-dojo.dnd.isFormElement = function(/*Event*/ e){
+},
+'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\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",
+'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${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", "./_base/sniff", "./_base/window"],
+ function(geometry, lang, ready, has, baseWindow){
+ // module:
+ // dojo/uacss
// summary:
- // returns true if user clicked on a form element
- var t = e.target;
- if(t.nodeType == 3 /*TEXT_NODE*/){
- t = t.parentNode;
+ // Applies pre-set CSS classes to the top-level HTML node, based on:
+ // - browser (ex: dj_ie)
+ // - browser version (ex: dj_ie6)
+ // - box model (ex: dj_contentBox)
+ // - text direction (ex: dijitRtl)
+ //
+ // In addition, browser, browser version, and box model are
+ // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
+
+ var
+ html = baseWindow.doc.documentElement,
+ ie = has("ie"),
+ opera = has("opera"),
+ maj = Math.floor,
+ ff = has("ff"),
+ boxModel = geometry.boxModel.replace(/-/,''),
+
+ classes = {
+ "dj_ie": ie,
+ "dj_ie6": maj(ie) == 6,
+ "dj_ie7": maj(ie) == 7,
+ "dj_ie8": maj(ie) == 8,
+ "dj_ie9": maj(ie) == 9,
+ "dj_quirks": has("quirks"),
+ "dj_iequirks": ie && 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"),
+ "dj_ff3": maj(ff) == 3
+ }; // no dojo unsupported browsers
+
+ classes["dj_" + boxModel] = true;
+
+ // apply browser, browser version, and box model class names
+ var classStr = "";
+ for(var clz in classes){
+ if(classes[clz]){
+ classStr += clz + " ";
+ }
}
- return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
-};
+ 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;
+});
-if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.autoscroll"] = true;
-dojo.provide("dojo.dnd.autoscroll");
+},
+'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.getMarginBox domGeometry.position
+ "dojo/dom-style", // domStyle.set, domStyle.get
+ "dojo/_base/lang", // lang.hitch lang.isArrayLike
+ "dojo/_base/sniff", // has("ie")
+ "dojo/_base/window", // win.body
+ "./_base/manager", // manager.defaultDuration
+ "./place",
+ "./_Widget",
+ "./_TemplatedMixin",
+ "./BackgroundIframe",
+ "dojo/text!./templates/Tooltip.html",
+ "." // sets dijit.showTooltip etc. for back-compat
+], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, has, win,
+ manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
+/*=====
+ var _Widget = dijit._Widget;
+ var BackgroundIframe = dijit.BackgroundIframe;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+=====*/
-dojo.getObject("dnd", true, dojo);
+ // module:
+ // dijit/Tooltip
+ // summary:
+ // Defines dijit.Tooltip widget (to display a tooltip), showTooltip()/hideTooltip(), and _MasterTooltip
-dojo.dnd.getViewport = dojo.window.getBox;
-dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
-dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
+ 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
-dojo.dnd.V_AUTOSCROLL_VALUE = 16;
-dojo.dnd.H_AUTOSCROLL_VALUE = 16;
+ // duration: Integer
+ // Milliseconds to fade in/fade out
+ duration: manager.defaultDuration,
-dojo.dnd.autoScroll = function(e){
+ templateString: template,
+
+ postCreate: function(){
+ win.body().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.__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;
+ }
+
+ // reset width; it may have been set by orient() on a previous tooltip show()
+ this.domNode.width = "auto";
+
+ 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;
+
+ 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";
+ }
+
+ // 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
+
+ //Adjust the spaceAvailable width, without changing the spaceAvailable object
+ var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
+
+ 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];
+
+ // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
+ this.domNode.style.width = "auto";
+ var size = domGeometry.getContentBox(this.domNode);
+
+ var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
+ var widthWasReduced = width < size.w;
+
+ this.domNode.style.width = width+"px";
+
+ //Adjust width for tooltips that have a really long word or a nowrap setting
+ if(widthWasReduced){
+ this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
+ var scrollWidth = this.containerNode.scrollWidth;
+ this.containerNode.style.overflow = "visible"; //change it back
+ if(scrollWidth > width){
+ scrollWidth = scrollWidth + domStyle.get(this.domNode,"paddingLeft") + domStyle.get(this.domNode,"paddingRight");
+ this.domNode.style.width = scrollWidth + "px";
+ }
+ }
+
+ // Reposition the tooltip connector.
+ if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
+ var mb = domGeometry.getMarginBox(node);
+ var tooltipConnectorHeight = this.connectorNode.offsetHeight;
+ if(mb.h > spaceAvailable.h){
+ // The tooltip starts at the top of the page and will extend past the aroundNode
+ var aroundNodePlacement = spaceAvailable.h - ((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),
+ mb.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 - tooltipSpaceAvaliableWidth);
+ },
+
+ _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", typeof textDir != 'undefined'? 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: dijit.__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 they used to, so for back-compat convert them to after-centered, before-centered
+ 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.
+
+ // 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[]
+ // Id of domNode(s) to attach the tooltip to.
+ // When user hovers over specified dom node, the tooltip will appear.
+ connectId: [],
+
+ // position: String[]
+ // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
+ position: [],
+
+ _setConnectIdAttr: function(/*String|String[]*/ 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, lang.hitch(this, "disconnect"));
+ }, 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); });
+
+ // Make connections
+ this._connections = array.map(this._connectIds, function(id){
+ var node = dom.byId(id);
+ return [
+ this.connect(node, "onmouseenter", "_onHover"),
+ this.connect(node, "onmouseleave", "_onUnHover"),
+ this.connect(node, "onfocus", "_onHover"),
+ this.connect(node, "onblur", "_onUnHover")
+ ];
+ }, this);
+
+ this._set("connectId", newId);
+ },
+
+ addTarget: function(/*DOMNODE || 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);
+ },
+
+ _onHover: function(/*Event*/ e){
+ // 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){
+ var target = e.target;
+ this._showTimer = setTimeout(lang.hitch(this, function(){this.open(target)}), this.showDelay);
+ }
+ },
+
+ _onUnHover: function(/*Event*/ /*===== e =====*/){
+ // summary:
+ // Despite the name of this method, it actually handles both mouseleave and blur
+ // events on the target node, hiding the tooltip.
+ // tags:
+ // private
+
+ // keep a tooltip open if the associated element still has focus (even though the
+ // mouse moved away)
+ if(this._focus){ return; }
+
+ if(this._showTimer){
+ clearTimeout(this._showTimer);
+ delete this._showTimer;
+ }
+ this.close();
+ },
+
+ open: function(/*DomNode*/ target){
+ // summary:
+ // Display the tooltip; usually not called directly.
+ // tags:
+ // private
+
+ if(this._showTimer){
+ clearTimeout(this._showTimer);
+ delete this._showTimer;
+ }
+ Tooltip.show(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight(), this.textDir);
+
+ this._connectNode = target;
+ 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)
+ clearTimeout(this._showTimer);
+ 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
+ },
+
+ uninitialize: function(){
+ this.close();
+ 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
+
+ // dijit.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.
+ Tooltip.defaultPosition = ["after-centered", "before-centered"];
+
+
+ return Tooltip;
+});
+
+},
+'dojo/string':function(){
+define("dojo/string", ["./_base/kernel", "./_base/lang"], function(dojo, lang) {
+ // module:
+ // dojo/string
// summary:
- // a handler for onmousemove event, which scrolls the window, if
- // necesary
- // e: Event
- // onmousemove event
+ // TODOC
- // FIXME: needs more docs!
- var v = dojo.window.getBox(), dx = 0, dy = 0;
- if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
- dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
- }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
- dx = dojo.dnd.H_AUTOSCROLL_VALUE;
+lang.getObject("string", true, dojo);
+
+/*=====
+dojo.string = {
+ // summary: String utilities for Dojo
+};
+=====*/
+
+dojo.string.rep = function(/*String*/str, /*Integer*/num){
+ // summary:
+ // Efficiently replicate a string `n` times.
+ // str:
+ // the string to replicate
+ // num:
+ // number of times to replicate the string
+
+ if(num <= 0 || !str){ return ""; }
+
+ var buf = [];
+ for(;;){
+ if(num & 1){
+ buf.push(str);
+ }
+ if(!(num >>= 1)){ break; }
+ str += str;
}
- if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
- dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
- }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
- dy = dojo.dnd.V_AUTOSCROLL_VALUE;
+ return buf.join(""); // String
+};
+
+dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
+ // summary:
+ // Pad a string to guarantee that it is at least `size` length by
+ // filling with the character `ch` at either the start or end of the
+ // string. Pads at the start, by default.
+ // text:
+ // the string to pad
+ // size:
+ // length to provide padding
+ // ch:
+ // character to pad, defaults to '0'
+ // end:
+ // adds padding at the end if true, otherwise pads at start
+ // example:
+ // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
+ // | dojo.string.pad("Dojo", 10, "+", true);
+
+ if(!ch){
+ ch = '0';
}
- window.scrollBy(dx, dy);
+ var out = String(text),
+ pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
+ return end ? out + pad : pad + out; // String
};
-dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
-dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
+dojo.string.substitute = function( /*String*/ template,
+ /*Object|Array*/map,
+ /*Function?*/ transform,
+ /*Object?*/ thisObject){
+ // summary:
+ // Performs parameterized substitutions on a string. Throws an
+ // exception if any parameter is unmatched.
+ // template:
+ // a string with expressions in the form `${key}` to be replaced or
+ // `${key:format}` which specifies a format function. keys are case-sensitive.
+ // map:
+ // hash to search for substitutions
+ // transform:
+ // a function to process all parameters before substitution takes
+ // place, e.g. mylib.encodeXML
+ // thisObject:
+ // where to look for optional format function; default to the global
+ // namespace
+ // example:
+ // Substitutes two expressions in a string from an Array or Object
+ // | // returns "File 'foo.html' is not found in directory '/temp'."
+ // | // by providing substitution data in an Array
+ // | dojo.string.substitute(
+ // | "File '${0}' is not found in directory '${1}'.",
+ // | ["foo.html","/temp"]
+ // | );
+ // |
+ // | // also returns "File 'foo.html' is not found in directory '/temp'."
+ // | // but provides substitution data in an Object structure. Dotted
+ // | // notation may be used to traverse the structure.
+ // | dojo.string.substitute(
+ // | "File '${name}' is not found in directory '${info.dir}'.",
+ // | { name: "foo.html", info: { dir: "/temp" } }
+ // | );
+ // example:
+ // Use a transform function to modify the values:
+ // | // returns "file 'foo.html' is not found in directory '/temp'."
+ // | dojo.string.substitute(
+ // | "${0} is not found in ${1}.",
+ // | ["foo.html","/temp"],
+ // | function(str){
+ // | // try to figure out the type
+ // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
+ // | return prefix + " '" + str + "'";
+ // | }
+ // | );
+ // example:
+ // Use a formatter
+ // | // returns "thinger -- howdy"
+ // | dojo.string.substitute(
+ // | "${0:postfix}", ["thinger"], null, {
+ // | postfix: function(value, key){
+ // | return value + " -- howdy";
+ // | }
+ // | }
+ // | );
-dojo.dnd.autoScrollNodes = function(e){
+ thisObject = thisObject || dojo.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
+};
+
+/*=====
+dojo.string.trim = function(str){
// summary:
- // a handler for onmousemove event, which scrolls the first avaialble
- // Dom element, it falls back to dojo.dnd.autoScroll()
- // e: Event
- // onmousemove event
+ // 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
+}
+=====*/
- // FIXME: needs more docs!
- for(var n = e.target; n;){
- if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
- var s = dojo.getComputedStyle(n);
- if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
- var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
- //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
- var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
- h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
- rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
- if(dojo.isWebKit || dojo.isOpera){
- // FIXME: this code should not be here, it should be taken into account
- // either by the event fixing code, or the dojo.position()
- // FIXME: this code doesn't work on Opera 9.5 Beta
- rx += dojo.body().scrollLeft;
- ry += dojo.body().scrollTop;
+dojo.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;
+ };
+
+return dojo.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", [
+ ".",
+ "./_base",
+ "dojo/parser",
+ "./_Widget",
+ "./_TemplatedMixin",
+ "./_Container",
+ "./layout/_LayoutWidget",
+ "./form/_FormWidget",
+ "./form/_FormValueWidget"
+], function(dijit){
+
+ // module:
+ // dijit/dijit
+ // 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\"\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){
+
+/*=====
+ Button = dijit.form.Button;
+ _Container = dijit._Container;
+ _HasDropDown = dijit._HasDropDown;
+=====*/
+
+// module:
+// dijit/form/DropDownButton
+// summary:
+// A button with a drop down
+
+
+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 dijit.form.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/_base/sniff", // has("ie"), has("quirks")
+ "./_FormWidgetMixin"
+], function(declare, domAttr, keys, has, _FormWidgetMixin){
+
+/*=====
+ var _FormWidgetMixin = dijit.form._FormWidgetMixin;
+=====*/
+
+ // module:
+ // dijit/form/_FormValueMixin
+ // summary:
+ // Mixin for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
+
+ 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.focusNode.setAttribute("aria-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)){
+ var te;
+ if(has("ie") < 9 || (has("ie") && has("quirks"))){
+ e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
+ te = document.createEventObject();
+ te.keyCode = keys.ESCAPE;
+ te.shiftKey = e.shiftKey;
+ e.srcElement.fireEvent('onkeypress', te);
}
- if(rx > 0 && rx < b.w){
- if(rx < w){
- dx = -w;
- }else if(rx > b.w - w){
- dx = w;
- }
+ }
+ }
+ });
+});
+
+},
+'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/_base/sniff", // has("webkit")
+ "dojo/_base/window", // win.body
+ "dojo/window", // winUtils.scrollIntoView
+ "../a11y" // a11y.hasDefaultTabStop
+], function(array, declare, domAttr, domStyle, lang, mouse, has, win, winUtils, a11y){
+
+// module:
+// dijit/form/_FormWidgetMixin
+// 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.
+
+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._Widget.attr`.
+ //
+ // 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",
+
+ // tabIndex: Integer
+ // 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');
}
- //console.log("ry =", ry, "b.h =", b.h, "h =", h);
- if(ry > 0 && ry < b.h){
- if(ry < h){
- dy = -h;
- }else if(ry > b.h - h){
- dy = h;
- }
+ }, 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(win.body(), "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();
}
- var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
- n.scrollLeft = n.scrollLeft + dx;
- n.scrollTop = n.scrollTop + dy;
- if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
+ // 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
}
}
- try{
- n = n.parentNode;
- }catch(x){
- n = null;
+ },
+
+ 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);
}
- dojo.dnd.autoScroll(e);
-};
+});
-}
+});
-if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.Mover"] = true;
-dojo.provide("dojo.dnd.Mover");
+},
+'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/layout/_ContentPaneResizeMixin':function(){
+define("dijit/layout/_ContentPaneResizeMixin", [
+ "dojo/_base/array", // array.filter array.forEach
+ "dojo/_base/declare", // declare
+ "dojo/dom-attr", // domAttr.has
+ "dojo/dom-class", // domClass.contains domClass.toggle
+ "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
+ "dojo/_base/lang", // lang.mixin
+ "dojo/query", // query
+ "dojo/_base/sniff", // has("ie")
+ "dojo/_base/window", // win.global
+ "../registry", // registry.byId
+ "./utils", // marginBox2contextBox
+ "../_Contained"
+], function(array, declare, domAttr, domClass, domGeometry, lang, query, has, win,
+ registry, layoutUtils, _Contained){
+/*=====
+var _Contained = dijit._Contained;
+=====*/
+// module:
+// dijit/layout/_ContentPaneResizeMixin
+// 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.
-dojo.declare("dojo.dnd.Mover", null, {
- constructor: function(node, e, host){
+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:
- // 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.
- // 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 = dojo.byId(node);
- var pos = e.touches ? e.touches[0] : e;
- this.marginBox = {l: pos.pageX, t: pos.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 two
- // connects are disconnected
- dojo.connect(d, "onmousemove", this, "onFirstMove"),
- dojo.connect(d, "ontouchmove", this, "onFirstMove"),
+ // See `dijit.layout._LayoutWidget.startup` for description.
+ // Although ContentPane doesn't extend _LayoutWidget, it does implement
+ // the same API.
- // These are called continually during the drag
- dojo.connect(d, "onmousemove", this, "onMouseMove"),
- dojo.connect(d, "ontouchmove", this, "onMouseMove"),
+ if(this._started){ return; }
- // And these are called at the end of the drag
- dojo.connect(d, "onmouseup", this, "onMouseUp"),
- dojo.connect(d, "ontouchend", this, "onMouseUp"),
+ var parent = this.getParent();
+ this._childOfLayoutWidget = parent && parent.isLayoutContainer;
- // cancel text selection and text dragging
- dojo.connect(d, "ondragstart", dojo.stopEvent),
- dojo.connect(d.body, "onselectstart", dojo.stopEvent)
- ];
- // notify that the move has started
- if(h && h.onMoveStart){
- h.onMoveStart(this);
+ // 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){
+ // If my parent isn't a layout container, since my style *may be* width=height=100%
+ // or something similar (either set directly or via a CSS class),
+ // monitor when my size changes so that I can re-layout.
+ // For browsers where I can't directly monitor when my size changes,
+ // monitor when the viewport changes size, which *may* indicate a size change for me.
+ this.connect(has("ie") ? this.domNode : win.global, 'onresize', function(){
+ // Using function(){} closure to ensure no arguments to resize.
+ this._needLayout = !this._childOfLayoutWidget;
+ this.resize();
+ });
}
},
- // mouse event processors
- onMouseMove: function(e){
+
+ _checkIfSingleChild: function(){
// summary:
- // event processor for onmousemove/ontouchmove
- // e: Event
- // mouse/touch event
- dojo.dnd.autoScroll(e);
- var m = this.marginBox,
- pos = e.touches ? e.touches[0] : e;
- this.host.onMove(this, {l: m.l + pos.pageX, t: m.t + pos.pageY}, e);
- dojo.stopEvent(e);
+ // 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 childNodes = query("> *", this.containerNode).filter(function(node){
+ return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
+ }),
+ childWidgetNodes = childNodes.filter(function(node){
+ return domAttr.has(node, "data-dojo-type") || domAttr.has(node, "dojoType") || domAttr.has(node, "widgetId");
+ }),
+ candidateWidgets = array.filter(childWidgetNodes.map(registry.byNode), function(widget){
+ return widget && widget.domNode && widget.resize;
+ });
+
+ if(
+ // all child nodes are widgets
+ childNodes.length == childWidgetNodes.length &&
+
+ // all but one are invisible (like dojo.data)
+ candidateWidgets.length == 1
+ ){
+ this._singleChild = candidateWidgets[0];
+ }else{
+ delete this._singleChild;
+ }
+
+ // 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);
},
- onMouseUp: function(e){
- if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
- e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
- this.destroy();
+
+ resize: function(changeSize, resultSize){
+ // summary:
+ // See `dijit.layout._LayoutWidget.resize` for description.
+ // Although ContentPane doesn't extend _LayoutWidget, it does implement
+ // the same API.
+
+ // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
+ // never called, 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();
}
- dojo.stopEvent(e);
+
+ this._resizeCalled = true;
+
+ this._scheduleLayout(changeSize, resultSize);
},
- // utilities
- onFirstMove: function(e){
+
+ _scheduleLayout: function(changeSize, resultSize){
// 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 = dojo.marginBox(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 dojo.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 = dojo.doc.body;
- var bs = dojo.getComputedStyle(b);
- var bm = dojo._getMarginBox(b, bs);
- var bc = dojo._getContentBox(b, bs);
- l = m.l - (bc.l - bm.l);
- t = m.t - (bc.t - bm.t);
- break;
+ // 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;
}
- this.marginBox.l = l - this.marginBox.l;
- this.marginBox.t = t - this.marginBox.t;
- if(h && h.onFirstMove){
- h.onFirstMove(this, e);
+ },
+
+ _layout: function(changeSize, resultSize){
+ // summary:
+ // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
+ // Also, since I am a Container widget, each of my children expects me to
+ // call resize() or layout() on them.
+ //
+ // 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
+
+ // Set margin box size, unless it wasn't specified, in which case use current size.
+ if(changeSize){
+ domGeometry.setMarginBox(this.domNode, changeSize);
}
-
- // Disconnect onmousemove and ontouchmove events that call this function
- dojo.disconnect(this.events.shift());
- dojo.disconnect(this.events.shift());
+
+ // 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();
+
+ delete this._needLayout;
},
- destroy: function(){
+
+ _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:
- // stops the move, deletes all references, so the object can be garbage-collected
- dojo.forEach(this.events, dojo.disconnect);
- // undo global settings
- var h = this.host;
- if(h && h.onMoveStop){
- h.onMoveStop(this);
+ // 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');
}
- // destroy objects
- this.events = this.node = this.host = null;
+ },
+
+ _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)
+
+ 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);
+
+ // Need to keep track of whether ContentPane has been shown (which is different than
+ // whether or not it's currently visible).
+ this._wasShown = true;
}
});
-}
+});
+
+},
+'dijit/WidgetSet':function(){
+define("dijit/WidgetSet", [
+ "dojo/_base/array", // array.forEach array.map
+ "dojo/_base/declare", // declare
+ "dojo/_base/window", // win.global
+ "./registry" // to add functions to dijit.registry
+], function(array, declare, win, registry){
+
+ // module:
+ // dijit/WidgetSet
+ // summary:
+ // Legacy registry code. New modules should just use registry.
+ // Will be removed in 2.0.
+
+ var WidgetSet = declare("dijit.WidgetSet", null, {
+ // summary:
+ // A set of widgets indexed by id. A default instance of this class is
+ // available as `dijit.registry`
+ //
+ // example:
+ // Create a small list of widgets:
+ // | var ws = new dijit.WidgetSet();
+ // | ws.add(dijit.byId("one"));
+ // | ws.add(dijit.byId("two"));
+ // | // destroy both:
+ // | ws.forEach(function(w){ w.destroy(); });
+ //
+ // example:
+ // Using dijit.registry:
+ // | dijit.registry.forEach(function(w){ /* do something */ });
+
+ constructor: function(){
+ this._hash = {};
+ this.length = 0;
+ },
+
+ add: function(/*dijit._Widget*/ widget){
+ // summary:
+ // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
+ //
+ // widget: dijit._Widget
+ // Any dijit._Widget subclass.
+ if(this._hash[widget.id]){
+ throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
+ }
+ this._hash[widget.id] = widget;
+ this.length++;
+ },
+
+ remove: function(/*String*/ id){
+ // summary:
+ // Remove a widget from this WidgetSet. Does not destroy the widget; simply
+ // removes the reference.
+ if(this._hash[id]){
+ delete this._hash[id];
+ this.length--;
+ }
+ },
+
+ forEach: function(/*Function*/ func, /* Object? */thisObj){
+ // summary:
+ // Call specified function for each widget in this set.
+ //
+ // func:
+ // A callback function to run for each item. Is passed the widget, the index
+ // in the iteration, and the full hash, similar to `array.forEach`.
+ //
+ // thisObj:
+ // An optional scope parameter
+ //
+ // example:
+ // Using the default `dijit.registry` instance:
+ // | dijit.registry.forEach(function(widget){
+ // | console.log(widget.declaredClass);
+ // | });
+ //
+ // returns:
+ // Returns self, in order to allow for further chaining.
+
+ thisObj = thisObj || win.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
+ // | dijit.registry.filter(function(w, i){
+ // | return i % 2 == 0;
+ // | }).forEach(function(w){ /* odd ones */ });
+
+ thisObj = thisObj || win.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
+ // | var ws = new dijit.WidgetSet();
+ // | ws.add(dijit.byId("bar"));
+ // | var t = ws.byId("bar") // returns a widget
+ // | var x = ws.byId("foo"); // returns undefined
+
+ return this._hash[id]; // dijit._Widget
+ },
+
+ byClass: function(/*String*/ cls){
+ // summary:
+ // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
+ //
+ // cls: String
+ // The Class to scan for. Full dot-notated string.
+ //
+ // example:
+ // Find all `dijit.TitlePane`s in a page:
+ // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
+
+ var res = new 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
+ // | array.map(dijit.registry.toArray(), function(w){ return w.domNode; });
+
+ var ar = [];
+ for(var id in this._hash){
+ ar.push(this._hash[id]);
+ }
+ return ar; // dijit._Widget[]
+ },
+
+ map: function(/* Function */func, /* Object? */thisObj){
+ // summary:
+ // Create a new Array from this WidgetSet, following the same rules as `array.map`
+ // example:
+ // | var nodes = dijit.registry.map(function(w){ return w.domNode; });
+ //
+ // returns:
+ // A new array of the returned values.
+ return array.map(this.toArray(), func, thisObj); // Array
+ },
-if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.Moveable"] = true;
-dojo.provide("dojo.dnd.Moveable");
+ 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 || win.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 || win.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", ["../main", "../Evented", "../touch", "./Mover"], function(dojo, Evented, touch) {
+ // module:
+ // dojo/dnd/Moveable
+ // summary:
+ // TODOC
/*=====
@@ -8368,12 +13992,12 @@ dojo.declare("dojo.dnd.__MoveableArgs", [], {
});
=====*/
-dojo.declare("dojo.dnd.Moveable", null, {
+dojo.declare("dojo.dnd.Moveable", [Evented], {
// object attributes (for markup)
handle: "",
delay: 0,
skip: false,
-
+
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
@@ -8389,8 +14013,7 @@ dojo.declare("dojo.dnd.Moveable", null, {
this.skip = params.skip;
this.mover = params.mover ? params.mover : dojo.dnd.Mover;
this.events = [
- dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
- dojo.connect(this.handle, "ontouchstart", this, "onMouseDown"),
+ dojo.connect(this.handle, touch.press, this, "onMouseDown"),
// cancel text selection and text dragging
dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
@@ -8398,8 +14021,8 @@ dojo.declare("dojo.dnd.Moveable", null, {
},
// markup methods
- markupFactory: function(params, node){
- return new dojo.dnd.Moveable(node, params);
+ markupFactory: function(params, node, ctor){
+ return new ctor(node, params);
},
// methods
@@ -8409,7 +14032,7 @@ dojo.declare("dojo.dnd.Moveable", null, {
dojo.forEach(this.events, dojo.disconnect);
this.events = this.node = this.handle = null;
},
-
+
// mouse event processors
onMouseDown: function(e){
// summary:
@@ -8419,14 +14042,11 @@ dojo.declare("dojo.dnd.Moveable", null, {
if(this.skip && dojo.dnd.isFormElement(e)){ return; }
if(this.delay){
this.events.push(
- dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
- dojo.connect(this.handle, "ontouchmove", this, "onMouseMove"),
- dojo.connect(this.handle, "onmouseup", this, "onMouseUp"),
- dojo.connect(this.handle, "ontouchend", this, "onMouseUp")
+ dojo.connect(this.handle, touch.move, this, "onMouseMove"),
+ dojo.connect(this.handle, touch.release, this, "onMouseUp")
);
- var pos = e.touches ? e.touches[0] : e;
- this._lastX = pos.pageX;
- this._lastY = pos.pageY;
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
}else{
this.onDragDetected(e);
}
@@ -8437,8 +14057,7 @@ dojo.declare("dojo.dnd.Moveable", null, {
// event processor for onmousemove/ontouchmove, used only for delayed drags
// e: Event
// mouse/touch event
- var pos = e.touches ? e.touches[0] : e;
- if(Math.abs(pos.pageX - this._lastX) > this.delay || Math.abs(pos.pageY - this._lastY) > this.delay){
+ if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
this.onMouseUp(e);
this.onDragDetected(e);
}
@@ -8463,7 +14082,7 @@ dojo.declare("dojo.dnd.Moveable", null, {
dojo.stopEvent(e);
}
},
-
+
// local events
onDragDetected: function(/* Event */ e){
// summary:
@@ -8489,7 +14108,7 @@ dojo.declare("dojo.dnd.Moveable", null, {
// summary:
// called during the very first move notification;
// can be used to initialize coordinates, can be overwritten.
-
+
// default implementation does nothing
},
onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
@@ -8505,24 +14124,2321 @@ dojo.declare("dojo.dnd.Moveable", null, {
onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary:
// called before every incremental move; can be overwritten.
-
+
// default implementation does nothing
},
onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary:
// called after every incremental move; can be overwritten.
-
+
// default implementation does nothing
}
});
+return dojo.dnd.Moveable;
+});
+
+},
+'dojo/store/util/SimpleQueryEngine':function(){
+define("dojo/store/util/SimpleQueryEngine", ["../../_base/array"], function(arrayUtil) {
+ // module:
+ // dojo/store/util/SimpleQueryEngine
+ // summary:
+ // The module defines a simple filtering query engine for object stores.
+
+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.util.SimpleQueryEngine.__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 = dojo.store.util.SimpleQueryEngine;
+ // | // define our query method
+ // | this.query = function(query, options){
+ // | return dojo.store.util.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){
+ if(!required.test(object[key])){
+ 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
+ if(options && options.sort){
+ results.sort(function(a, b){
+ for(var sort, i=0; sort = options.sort[i]; i++){
+ var aValue = a[sort.attribute];
+ var bValue = b[sort.attribute];
+ if (aValue != bValue) {
+ return !!sort.descending == 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/_base/sniff", // has("ie")
+ "." // setting dijit.typematic global
+], function(array, connect, event, kernel, lang, on, has, dijit){
+
+// module:
+// 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.
+
+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 (optional):
+ // if > 1, the number of milliseconds until the 3->n events occur
+ // or else the fractional time multiplier for the next event's delay, default=0.9
+ // initialDelay (optional):
+ // the number of milliseconds until the 2nd event occurs, default=500ms
+ // minDelay (optional):
+ // the maximum delay in milliseconds for event to fire, default=10ms
+ if(obj != this._obj){
+ this.stop();
+ this._initialDelay = initialDelay || 500;
+ this._subsequentDelay = subsequentDelay || 0.90;
+ this._minDelay = minDelay || 10;
+ this._obj = obj;
+ this._evt = evt;
+ this._node = node;
+ this._currentTimeout = -1;
+ this._count = -1;
+ this._callback = lang.hitch(_this, callback);
+ this._fireEventAndReload();
+ this._evt = lang.mixin({faux: true}, evt);
+ }
+ },
+
+ stop: function(){
+ // summary:
+ // Stop an ongoing timed, repeating callback sequence.
+ if(this._timer){
+ clearTimeout(this._timer);
+ this._timer = null;
+ }
+ if(this._obj){
+ this._callback(-1, this._node, this._evt);
+ this._obj = null;
+ }
+ },
+
+ addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
+ // summary:
+ // Start listening for a specific typematic key.
+ // See also the trigger method for other parameters.
+ // keyObject:
+ // an object defining the key to listen for:
+ // charOrCode:
+ // the printable character (string) or keyCode (number) to listen for.
+ // keyCode:
+ // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
+ // charCode:
+ // (deprecated - use charOrCode) the charCode (number) to listen for.
+ // ctrlKey:
+ // desired ctrl key state to initiate the callback sequence:
+ // - pressed (true)
+ // - released (false)
+ // - either (unspecified)
+ // altKey:
+ // same as ctrlKey but for the alt key
+ // shiftKey:
+ // same as ctrlKey but for the shift key
+ // returns:
+ // 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){
+ event.stop(evt);
+ typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
+ })),
+ on(node, "mouseup", lang.hitch(this, function(evt){
+ if(this._obj){
+ event.stop(evt);
+ }
+ typematic.stop();
+ })),
+ on(node, "mouseout", lang.hitch(this, function(evt){
+ event.stop(evt);
+ typematic.stop();
+ })),
+ on(node, "mousemove", lang.hitch(this, function(evt){
+ evt.preventDefault();
+ })),
+ on(node, "dblclick", lang.hitch(this, function(evt){
+ event.stop(evt);
+ if(has("ie")){
+ 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\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\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/event", // event.stop
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/_base/sniff", // has("ie")
+ "./_Widget",
+ "./_TemplatedMixin",
+ "./_Contained",
+ "./_CssStateMixin",
+ "dojo/text!./templates/MenuItem.html"
+], function(declare, dom, domAttr, domClass, event, kernel, has,
+ _Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _Contained = dijit._Contained;
+ var _CssStateMixin = dijit._CssStateMixin;
+=====*/
+
+ // module:
+ // dijit/MenuItem
+ // summary:
+ // A line item in a Menu Widget
+
+
+ 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: { node: "containerNode", type: "innerHTML" },
+
+ // 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);
+ },
+
+ _onHover: function(){
+ // summary:
+ // Handler when mouse is moved onto menu item
+ // tags:
+ // protected
+ this.getParent().onItemHover(this);
+ },
+
+ _onUnhover: function(){
+ // summary:
+ // Handler when mouse is moved off of menu item,
+ // possibly to a child menu, or maybe to a sibling
+ // menuitem or somewhere else entirely.
+ // tags:
+ // protected
+
+ // if we are unhovering the currently selected item
+ // then unselect it
+ this.getParent().onItemUnhover(this);
+
+ // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
+ // FF and IE don't generate an onmouseout event for the MenuItem.
+ // So, help out _CssStateMixin in this case.
+ this._set("hovering", false);
+ },
+
+ _onClick: function(evt){
+ // summary:
+ // Internal handler for click events on MenuItem.
+ // tags:
+ // private
+ this.getParent().onItemClick(this, evt);
+ event.stop(evt);
+ },
+
+ 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);
+ }
+ });
+});
+
+},
+'dijit/layout/TabController':function(){
+require({cache:{
+'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\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",
+ "../Menu",
+ "../MenuItem",
+ "dojo/text!./templates/_TabButton.html",
+ "dojo/i18n!../nls/common"
+], function(declare, dom, domAttr, domClass, i18n, lang, StackController, Menu, MenuItem, template){
+
+/*=====
+ var StackController = dijit.layout.StackController;
+ var Menu = dijit.Menu;
+ var MenuItem = dijit.MenuItem;
+=====*/
+
+ // module:
+ // dijit/layout/TabController
+ // 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`.
+
+ 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.
+ setTimeout(function(){
+ n.className = n.className;
+ }, 1);
+ },
+
+ _setCloseButtonAttr: function(/*Boolean*/ disp){
+ // summary:
+ // Hide/show close button
+ this._set("closeButton", disp);
+ domClass.toggle(this.innerDiv, "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);
+ }
+ // add context menu onto title button
+ this._closeMenu = new Menu({
+ id: this.id+"_Menu",
+ dir: this.dir,
+ lang: this.lang,
+ textDir: this.textDir,
+ targetNodeIds: [this.domNode]
+ });
+
+ this._closeMenu.addChild(new MenuItem({
+ label: _nlsResources.itemClose,
+ dir: this.dir,
+ lang: this.lang,
+ textDir: this.textDir,
+ onClick: lang.hitch(this, "onClickCloseButton")
+ }));
+ }else{
+ if(this._closeMenu){
+ this._closeMenu.destroyRecursive();
+ delete this._closeMenu;
+ }
+ }
+ },
+ _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 || '');
+ }
+ },
+
+ destroy: function(){
+ if(this._closeMenu){
+ this._closeMenu.destroyRecursive();
+ delete this._closeMenu;
+ }
+ this.inherited(arguments);
+ }
+ });
+
+ 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,
+
+ _rectifyRtlTabList: function(){
+ // summary:
+ // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
+
+ if(0 >= this.tabPosition.indexOf('-h')){ return; }
+ if(!this.pane2button){ return; }
+
+ var maxWidth = 0;
+ for(var pane in this.pane2button){
+ var ow = this.pane2button[pane].innerDiv.scrollWidth;
+ maxWidth = Math.max(maxWidth, ow);
+ }
+ //unify the length of all the tabs
+ for(pane in this.pane2button){
+ this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
+ }
+ }
+ });
+
+ TabController.TabButton = TabButton; // for monkey patching
+
+ return TabController;
+});
+
+},
+'dijit/layout/_LayoutWidget':function(){
+define("dijit/layout/_LayoutWidget", [
+ "dojo/_base/lang", // lang.mixin
+ "../_Widget",
+ "../_Container",
+ "../_Contained",
+ "dojo/_base/declare", // declare
+ "dojo/dom-class", // domClass.add domClass.remove
+ "dojo/dom-geometry", // domGeometry.marginBox
+ "dojo/dom-style", // domStyle.getComputedStyle
+ "dojo/_base/sniff", // has("ie")
+ "dojo/_base/window" // win.global
+], function(lang, _Widget, _Container, _Contained,
+ declare, domClass, domGeometry, domStyle, has, win){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _Container = dijit._Container;
+ var _Contained = dijit._Contained;
+=====*/
+
+ // module:
+ // dijit/layout/_LayoutWidget
+ // summary:
+ // _LayoutWidget 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.
+
+
+ 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.connect(win.global, 'onresize', function(){
+ // Using function(){} closure to ensure no arguments passed to resize().
+ this.resize();
+ });
+ }
+ },
+
+ resize: function(changeSize, resultSize){
+ // summary:
+ // Call this to resize a widget, or after its size has changed.
+ // description:
+ // Change size mode:
+ // When changeSize is specified, changes the marginBox of this widget
+ // and forces it to relayout its contents accordingly.
+ // changeSize may specify height, width, or both.
+ //
+ // If resultSize is specified it indicates the size the widget will
+ // become after changeSize has been applied.
+ //
+ // Notification mode:
+ // When changeSize is null, indicates that the caller has already changed
+ // the size of the widget, or perhaps it changed because the browser
+ // window was resized. Tells widget to relayout its contents accordingly.
+ //
+ // If resultSize is also specified it indicates the size the widget has
+ // become.
+ //
+ // In either mode, this method also:
+ // 1. Sets this._borderBox and this._contentBox to the new size of
+ // the widget. Queries the current domNode size if necessary.
+ // 2. Calls layout() to resize contents (and maybe adjust child widgets).
+ //
+ // changeSize: Object?
+ // Sets the widget to this margin-box size and position.
+ // May include any/all of the following properties:
+ // | {w: int, h: int, l: int, t: int}
+ //
+ // resultSize: Object?
+ // The margin-box size of this widget after applying changeSize (if
+ // changeSize is specified). If caller knows this size and
+ // passes it in, we don't need to query the browser to get the size.
+ // | {w: int, h: int}
+
+ var node = this.domNode;
+
+ // set margin box size, unless it wasn't specified, in which case use current size
+ if(changeSize){
+ 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._Widget*/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._Widget*/ child, /*Integer?*/ insertIndex){
+ // Overrides _Container.addChild() to call _setupChild()
+ this.inherited(arguments);
+ if(this._started){
+ this._setupChild(child);
+ }
+ },
+
+ removeChild: function(/*dijit._Widget*/ child){
+ // Overrides _Container.removeChild() to remove class added by _setupChild()
+ 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/_base/sniff", // has("ie") has("mozilla")
+ "dojo/_base/window", // win.body
+ "./place",
+ "./BackgroundIframe",
+ "." // dijit (defining dijit.popup to match API doc)
+], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has, win,
+ place, BackgroundIframe, dijit){
+
+ // module:
+ // dijit/popup
+ // summary:
+ // Used to show drop downs (ex: the select list of a ComboBox)
+ // or popups (ex: right-click context menus)
+
+
+ /*=====
+ dijit.popup.__OpenArgs = function(){
+ // popup: Widget
+ // widget to display
+ // parent: Widget
+ // the button etc. that is displaying this popup
+ // around: DomNode
+ // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
+ // x: Integer
+ // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
+ // y: Integer
+ // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
+ // orient: Object|String
+ // When the around parameter is specified, orient should be 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: dijit.__Position
+ // adding a buffer around the opening position. This is only useful when around is not set.
+ this.popup = popup;
+ this.parent = parent;
+ this.around = around;
+ this.x = x;
+ this.y = y;
+ this.orient = orient;
+ this.onCancel = onCancel;
+ this.onClose = onClose;
+ this.onExecute = onExecute;
+ this.padding = padding;
+ }
+ =====*/
+
+ /*=====
+ dijit.popup = {
+ // summary:
+ // Used to show drop downs (ex: the select list of a ComboBox)
+ // or popups (ex: right-click context menus).
+ //
+ // Access via require(["dijit/popup"], function(popup){ ... }).
+
+ moveOffScreen: function(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.
+ // widget: dijit._WidgetBase
+ // The widget
+ },
+
+ hide: function(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.
+ // widget: dijit._WidgetBase
+ // The widget
+ },
+
+ open: function(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.
+ // args: dijit.popup.__OpenArgs
+ // Parameters
+ return {}; // Object specifying which position was chosen
+ },
+
+ close: function(popup){
+ // summary:
+ // Close specified popup and any popups that it parented.
+ // If no popup is specified, closes all popups.
+ // widget: dijit._WidgetBase?
+ // The widget, optional
+ }
+ };
+ =====*/
+
+ var PopupManager = declare(null, {
+ // _stack: dijit._Widget[]
+ // Stack of currently popped up widgets.
+ // (someone opened _stack[0], and then it opened _stack[1], etc.)
+ _stack: [],
+
+ // _beginZIndex: Number
+ // Z-index of the first popup. (If first popup opens other
+ // popups they get a higher z-index.)
+ _beginZIndex: 1000,
+
+ _idGen: 1,
+
+ _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"
+ }, win.body());
+ wrapper.appendChild(node);
+
+ var s = node.style;
+ s.display = "";
+ s.visibility = "";
+ s.position = "";
+ s.top = "0px";
+
+ widget._popupWrapper = wrapper;
+ aspect.after(widget, "destroy", function(){
+ domConstruct.destroy(wrapper);
+ delete widget._popupWrapper;
+ });
+ }
+
+ 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(/*dijit.popup.__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(),
+ 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
+ "../registry",
+ ".." // for setting exports to dijit namespace
+], function(array, config, registry, dijit){
+
+ // module:
+ // dijit/_base/manager
+ // summary:
+ // Shim to methods on registry, plus a few other declarations.
+ // New code should access dijit/registry directly when possible.
+
+ /*=====
+ dijit.byId = function(id){
+ // summary:
+ // Returns a widget by it's id, or if passed a widget, no-op (like dom.byId())
+ // id: String|dijit._Widget
+ return registry.byId(id); // dijit._Widget
+ };
+
+ dijit.getUniqueId = function(widgetType){
+ // summary:
+ // Generates a unique id for a given widgetType
+ // widgetType: String
+ return registry.getUniqueId(widgetType); // String
+ };
+
+ dijit.findWidgets = function(root){
+ // summary:
+ // Search subtree under root returning widgets found.
+ // Doesn't search for nested widgets (ie, widgets inside other widgets).
+ // root: DOMNode
+ return registry.findWidgets(root);
+ };
+
+ dijit._destroyAll = function(){
+ // summary:
+ // Code to destroy all widgets and do other cleanup on page unload
+
+ return registry._destroyAll();
+ };
+
+ dijit.byNode = function(node){
+ // summary:
+ // Returns the widget corresponding to the given DOMNode
+ // node: DOMNode
+ return registry.byNode(node); // dijit._Widget
+ };
+
+ dijit.getEnclosingWidget = function(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
+ // node: DOMNode
+ return registry.getEnclosingWidget(node);
+ };
+ =====*/
+ array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){
+ dijit[name] = registry[name];
+ });
+
+ /*=====
+ dojo.mixin(dijit, {
+ // 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: 200
+ });
+ =====*/
+ dijit.defaultDuration = config["defaultDuration"] || 200;
+
+ return dijit;
+});
+
+},
+'dijit/layout/StackController':function(){
+define("dijit/layout/StackController", [
+ "dojo/_base/array", // array.forEach array.indexOf array.map
+ "dojo/_base/declare", // declare
+ "dojo/_base/event", // event.stop
+ "dojo/keys", // keys
+ "dojo/_base/lang", // lang.getObject
+ "dojo/_base/sniff", // has("ie")
+ "../focus", // focus.focus()
+ "../registry", // registry.byId
+ "../_Widget",
+ "../_TemplatedMixin",
+ "../_Container",
+ "../form/ToggleButton",
+ "dojo/i18n!../nls/common"
+], function(array, declare, event, keys, lang, has,
+ focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _Container = dijit._Container;
+ var ToggleButton = dijit.form.ToggleButton;
+=====*/
+
+ // module:
+ // dijit/layout/StackController
+ // summary:
+ // Set of buttons to select a page in a `dijit.layout.StackContainer`
+
+ 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,
+
+ _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
+ this.inherited(arguments);
+ this.focusNode.removeAttribute("aria-pressed");
+ },
+
+ buildRendering: function(/*Event*/ evt){
+ this.inherited(arguments);
+ (this.focusNode || this.domNode).setAttribute("role", "tab");
+ },
+
+ onClick: function(/*Event*/ /*===== evt =====*/){
+ // summary:
+ // This is for TabContainer where the tabs are <span> rather than button,
+ // so need to set focus explicitly (on some browsers)
+ // Note that you shouldn't override this method, but you can connect to it.
+ focus.focus(this.focusNode);
+
+ // ... now let StackController catch the event and tell me what to do
+ },
+
+ onClickCloseButton: function(/*Event*/ evt){
+ // summary:
+ // StackContainer connects to this function; if your widget contains a close button
+ // then clicking it should call this function.
+ // Note that you shouldn't override this method, but you can connect to it.
+ evt.stopPropagation();
+ }
+ });
+
+
+ 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,
+
+ constructor: function(){
+ this.pane2button = {}; // mapping from pane id to buttons
+ this.pane2connects = {}; // mapping from pane id to this.connect() handles
+ this.pane2watches = {}; // mapping from pane id to watch() handles
+ },
+
+ postCreate: function(){
+ this.inherited(arguments);
+
+ // Listen to notifications from StackContainer
+ 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");
+ },
+
+ 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);
+ }
+ },
+
+ destroy: function(){
+ for(var pane in this.pane2button){
+ this.onRemoveChild(registry.byId(pane));
+ }
+ this.inherited(arguments);
+ },
+
+ onAddChild: function(/*dijit._Widget*/ 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,
+ label: page.title,
+ dir: page.dir,
+ lang: page.lang,
+ textDir: page.textDir,
+ showLabel: page.showTitle,
+ iconClass: page.iconClass,
+ closeButton: page.closable,
+ title: page.tooltip
+ });
+ button.focusNode.setAttribute("aria-selected", "false");
+
+
+ // map from page attribute to corresponding tab button attribute
+ var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
+ buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
+
+ // watch() so events like page title changes are reflected in tab button
+ this.pane2watches[page.id] = array.map(pageAttrList, function(pageAttr, idx){
+ return page.watch(pageAttr, function(name, oldVal, newVal){
+ button.set(buttonAttrList[idx], newVal);
+ });
+ });
+
+ // connections so that clicking a tab button selects the corresponding page
+ this.pane2connects[page.id] = [
+ this.connect(button, 'onClick', lang.hitch(this,"onButtonClick", page)),
+ this.connect(button, 'onClickCloseButton', lang.hitch(this,"onCloseButtonClick", 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){ // put the first child into the tab order
+ button.focusNode.setAttribute("tabIndex", "0");
+ button.focusNode.setAttribute("aria-selected", "true");
+ this._currentChild = page;
+ }
+ // make sure all tabs have the same length
+ if(!this.isLeftToRight() && has("ie") && this._rectifyRtlTabList){
+ this._rectifyRtlTabList();
+ }
+ },
+
+ onRemoveChild: function(/*dijit._Widget*/ 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; }
+
+ // disconnect/unwatch connections/watches related to page being removed
+ array.forEach(this.pane2connects[page.id], lang.hitch(this, "disconnect"));
+ delete this.pane2connects[page.id];
+ array.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
+ delete this.pane2watches[page.id];
+
+ var button = this.pane2button[page.id];
+ if(button){
+ this.removeChild(button);
+ delete this.pane2button[page.id];
+ button.destroy();
+ }
+ delete page.controlButton;
+ },
+
+ onSelectChild: function(/*dijit._Widget*/ 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("aria-selected", "false");
+ oldButton.focusNode.setAttribute("tabIndex", "-1");
+ }
+
+ var newButton=this.pane2button[page.id];
+ newButton.set('checked', true);
+ newButton.focusNode.setAttribute("aria-selected", "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._Widget*/ page){
+ // summary:
+ // Called whenever one of my child buttons is pressed in an attempt to select a page
+ // tags:
+ // private
+
+ if(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.
+ var button=this.pane2button[page.id];
+ button.set('checked', true);
+ }
+ var container = registry.byId(this.containerId);
+ container.selectChild(page);
+ },
+
+ onCloseButtonClick: function(/*dijit._Widget*/ 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 current = array.indexOf(children, this.pane2button[this._currentChild.id]);
+ // pick next button to focus on
+ var offset = forward ? 1 : children.length - 1;
+ return children[ (current + offset) % children.length ]; // dijit._Widget
+ },
+
+ 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:
+ case keys.END:
+ var children = this.getChildren();
+ if(children && children.length){
+ children[e.charOrCode == keys.HOME ? 0 : children.length-1].onClick();
+ }
+ 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.adjacent(!e.shiftKey).onClick();
+ 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.adjacent(forward).onClick();
+ 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", ["../main", "../Evented", "../touch", "./common", "./autoscroll"], function(dojo, Evented, touch) {
+ // module:
+ // dojo/dnd/Mover
+ // summary:
+ // TODOC
+
+
+dojo.declare("dojo.dnd.Mover", [Evented], {
+ constructor: function(node, e, host){
+ // 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.
+ // 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 = dojo.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 two
+ // connects are disconnected
+ dojo.connect(d, touch.move, this, "onFirstMove"),
+
+ // These are called continually during the drag
+ dojo.connect(d, touch.move, this, "onMouseMove"),
+
+ // And these are called at the end of the drag
+ dojo.connect(d, touch.release, this, "onMouseUp"),
+
+ // cancel text selection and text dragging
+ dojo.connect(d, "ondragstart", dojo.stopEvent),
+ dojo.connect(d.body, "onselectstart", dojo.stopEvent)
+ ];
+ // 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
+ dojo.dnd.autoScroll(e);
+ var m = this.marginBox;
+ this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
+ dojo.stopEvent(e);
+ },
+ onMouseUp: function(e){
+ if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
+ e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
+ this.destroy();
+ }
+ dojo.stopEvent(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 = dojo.marginBox(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 dojo.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 = dojo.doc.body;
+ var bs = dojo.getComputedStyle(b);
+ var bm = dojo._getMarginBox(b, bs);
+ var bc = dojo._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 onmousemove and ontouchmove events that call this function
+ dojo.disconnect(this.events.shift());
+ },
+ destroy: function(){
+ // summary:
+ // stops the move, deletes all references, so the object can be garbage-collected
+ dojo.forEach(this.events, dojo.disconnect);
+ // undo global settings
+ var h = this.host;
+ if(h && h.onMoveStop){
+ h.onMoveStop(this);
+ }
+ // destroy objects
+ this.events = this.node = this.host = null;
+ }
+});
+
+return dojo.dnd.Mover;
+});
+
+},
+'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){
+
+/*=====
+ var _TabContainerBase = dijit.layout._TabContainerBase;
+ var TabController = dijit.layout.TabController;
+ var ScrollingTabController = dijit.layout.ScrollingTabController;
+=====*/
+
+ // module:
+ // dijit/layout/TabContainer
+ // summary:
+ // A Container with tabs to select each child (only one of which is displayed at a time).
+
+
+ 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.
+
+ // useMenu: [const] Boolean
+ // True if a menu should be used to select tabs when they are too
+ // wide to fit the TabContainer, false otherwise.
+ useMenu: true,
+
+ // useSlider: [const] Boolean
+ // True if a slider should be used to select tabs when they are too
+ // wide to fit the TabContainer, false otherwise.
+ useSlider: true,
+
+ // controllerWidget: String
+ // An optional parameter to override the widget used to display the tab labels
+ controllerWidget: "",
+
+ _makeController: function(/*DomNode*/ srcNode){
+ // summary:
+ // Instantiate tablist controller widget and return reference to it.
+ // Callback from _TabContainerBase.postCreate().
+ // tags:
+ // protected extension
+
+ var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
+ TabController = lang.getObject(this.controllerWidget);
+
+ return new TabController({
+ id: this.id + "_tablist",
+ 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 ?
+ "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
+ }
+ }
+ });
+});
+
+},
+'dijit/BackgroundIframe':function(){
+define("dijit/BackgroundIframe", [
+ "require", // require.toUrl
+ ".", // 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/_base/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
+ // summary:
+ // new dijit.BackgroundIframe(node)
+ // Makes a background iframe as a child of node, that fills
+ // area (and position) of node
+
+ // 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}\" 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", ["../main", "./common"], function(dojo) {
+ // module:
+ // dojo/dnd/Avatar
+ // summary:
+ // TODOC
+
+
+dojo.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
+ this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
+ var a = dojo.create("table", {
+ "class": "dojoDndAvatar",
+ style: {
+ position: "absolute",
+ zIndex: "1999",
+ margin: "0px"
+ }
+ }),
+ source = this.manager.source, node,
+ b = dojo.create("tbody", null, a),
+ tr = dojo.create("tr", null, b),
+ td = dojo.create("td", null, tr),
+ icon = this.isA11y ? dojo.create("span", {
+ id : "a11yIcon",
+ innerHTML : this.manager.copy ? '+' : "<"
+ }, td) : null,
+ span = dojo.create("span", {
+ innerHTML: source.generateText ? this._generateText() : ""
+ }, td),
+ k = Math.min(5, this.manager.nodes.length), i = 0;
+ // we have to set the opacity on IE only after the node is live
+ dojo.attr(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 = dojo.create("table"),
+ tbody = dojo.create("tbody", null, table);
+ tbody.appendChild(node);
+ node = table;
+ }
+ }
+ node.id = "";
+ tr = dojo.create("tr", null, b);
+ td = dojo.create("td", null, tr);
+ td.appendChild(node);
+ dojo.attr(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
+ dojo.destroy(this.node);
+ this.node = false;
+ },
+ update: function(){
+ // summary:
+ // updates the avatar to reflect the current DnD state
+ dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
+ if (this.isA11y){
+ var icon = dojo.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
+ dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
+ function(node){
+ node.innerHTML = this._generateText();
+ }, this);
+ },
+ _generateText: function(){
+ // summary: generates a proper text to reflect copying or moving of items
+ return this.manager.nodes.length.toString();
+ }
+});
+
+return dojo.dnd.Avatar;
+});
+
+},
+'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/_base/kernel", // kernel.deprecated
+ "dojo/_base/lang", // lang.trim
+ "dojo/ready",
+ "./_FormWidget",
+ "./_ButtonMixin",
+ "dojo/text!./templates/Button.html"
+], function(require, declare, domClass, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
+
+/*=====
+ var _FormWidget = dijit.form._FormWidget;
+ var _ButtonMixin = dijit.form._ButtonMixin;
+=====*/
+
+// module:
+// dijit/form/Button
+// summary:
+// Button widget
+
+// Back compat w/1.6, remove for 2.0
+if(!kernel.isAsync){
+ 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
+ });
}
-if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.move"] = true;
-dojo.provide("dojo.dnd.move");
+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 dijit.form.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
+ // 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", ["../main", "./Mover", "./Moveable"], function(dojo) {
+ // module:
+ // dojo/dnd/move
+ // summary:
+ // TODOC
/*=====
@@ -8542,11 +16458,6 @@ dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
// object attributes (for markup)
constraints: function(){},
within: false,
-
- // markup methods
- markupFactory: function(params, node){
- return new dojo.dnd.move.constrainedMoveable(node, params);
- },
constructor: function(node, params){
// summary:
@@ -8599,11 +16510,6 @@ dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMo
// box:
// object attributes (for markup)
box: {},
-
- // markup methods
- markupFactory: function(params, node){
- return new dojo.dnd.move.boxConstrainedMoveable(node, params);
- },
constructor: function(node, params){
// summary:
@@ -8631,11 +16537,6 @@ dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constraine
// object attributes (for markup)
area: "content",
- // markup methods
- markupFactory: function(params, node){
- return new dojo.dnd.move.parentConstrainedMoveable(node, params);
- },
-
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
@@ -8674,129 +16575,2110 @@ dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
+return dojo.dnd.move;
+});
+
+},
+'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.create domConstruct.destroy domConstruct.place
+ "dojo/dom-geometry", // isBodyLtr
+ "dojo/dom-style", // domStyle.set, domStyle.get
+ "dojo/_base/kernel",
+ "dojo/_base/lang", // mixin(), isArray(), etc.
+ "dojo/on",
+ "dojo/ready",
+ "dojo/Stateful", // Stateful
+ "dojo/topic",
+ "dojo/_base/window", // win.doc.createTextNode
+ "./registry" // registry.getUniqueId(), registry.findWidgets()
+], function(require, array, aspect, config, connect, declare,
+ dom, domAttr, domClass, domConstruct, domGeometry, domStyle, kernel,
+ lang, on, ready, Stateful, topic, win, registry){
+
+/*=====
+var Stateful = dojo.Stateful;
+=====*/
+
+// module:
+// dijit/_WidgetBase
+// summary:
+// Future base class for all Dijit widgets.
+
+// For back-compat, remove in 2.0.
+if(!kernel.isAsync){
+ 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);
+ };
}
-if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
-dojo.provide("dojo.dnd.TimedMoveable");
+return declare("dijit._WidgetBase", Stateful, {
+ // 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,
/*=====
-dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
- // timeout: Number
- // delay move by this number of ms,
- // accumulating position changes during the timeout
- timeout: 0
+ // _started: 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 ///////////////////////////////////////
+
+ postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
+ // summary:
+ // Kicks off widget instantiation. See create() for details.
+ // tags:
+ // private
+ this.create(params, srcNodeRef);
+ },
+
+ create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
+ // summary:
+ // Kick off the life-cycle of a widget
+ // params:
+ // Hash of initialization parameters for widget, including
+ // scalar values (like title, duration etc.) and functions,
+ // typically callbacks like onClick.
+ // srcNodeRef:
+ // If a srcNodeRef (DOM node) is specified:
+ // - use srcNodeRef.innerHTML as my contents
+ // - if this is a behavioral widget then apply behavior
+ // to that srcNodeRef
+ // - otherwise, replace srcNodeRef with my generated DOM
+ // tree
+ // description:
+ // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
+ // etc.), some of which of you'll want to override. See http://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.
+ // tags:
+ // private
+
+ // store pointer to original DOM tree
+ this.srcNodeRef = dom.byId(srcNodeRef);
+
+ // For garbage collection. An array of listener handles returned by this.connect() / this.subscribe()
+ this._connects = [];
+
+ // For widgets internal to this widget, invisible to calling code
+ 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
+ // (be sure to do this before buildRendering() because that function might
+ // expect the id to be there.)
+ if(!this.id){
+ this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
+ }
+ registry.add(this);
+
+ this.buildRendering();
+
+ 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);
+ }
+ }
+
+ if(this.domNode){
+ // 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.
+ if(this.srcNodeRef && !this.srcNodeRef.parentNode){
+ 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.
+ 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 attribute that was either specified as parameter to constructor,
+ // or was found above and has a default non-null value. For correlated attributes like value and displayedValue, the one
+ // specified as a parameter should take precedence, so apply attributes in this.params last.
+ // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
+ // NaN and thus is not ignored like a default value of "".
+ array.forEach(list, function(attr){
+ if(this.params && attr in this.params){
+ // skip this one, do it below
+ }else if(this[attr]){
+ this.set(attr, this[attr]);
+ }
+ }, this);
+ for(var param in this.params){
+ this.set(param, this[param]);
+ }
+ },
+
+ 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 || domConstruct.create('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();
+
+ // remove this.connect() and this.subscribe() listeners
+ var c;
+ while((c = this._connects.pop())){
+ c.remove();
+ }
+
+ // destroy widgets created as part of template, etc.
+ var w;
+ while((w = this._supportingWidgets.pop())){
+ if(w.destroyRecursive){
+ w.destroyRecursive();
+ }else if(w.destroy){
+ w.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:
+ // Stub function. Override 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.
+ //
+ // 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);
+
+ domAttr.set(mapNode, attrName, value);
+ break;
+ case "innerText":
+ mapNode.innerHTML = "";
+ mapNode.appendChild(win.doc.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 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._watchCallbacks && this._created && value !== oldValue){
+ this._watchCallbacks(name, oldValue, value);
+ }
+ },
+
+ on: function(/*String*/ type, /*Function*/ func){
+ // summary:
+ // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
+ // 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))`.
+
+ return aspect.after(this, this._onMap(type), func, true);
+ },
+
+ _onMap: function(/*String*/ type){
+ // summary:
+ // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove")
+ 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[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._Widget[]
+ },
+
+ 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:
+ // Connects specified obj/event to specified method of this object
+ // and registers for disconnect() on widget destroy.
+ // description:
+ // Provide widget-specific analog to dojo.connect, except with the
+ // implicit use of this widget as the target object.
+ // 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 dijit.form.Button();
+ // | // when foo.bar() is called, call the listener we're going to
+ // | // provide in the scope of btn
+ // | btn.connect(foo, "bar", function(){
+ // | console.debug(this.toString());
+ // | });
+ // tags:
+ // protected
+
+ var handle = connect.connect(obj, event, this, method);
+ this._connects.push(handle);
+ return handle; // _Widget.Handle
+ },
+
+ disconnect: function(handle){
+ // summary:
+ // Disconnects handle created by `connect`.
+ // Also removes handle from this widget's list of connects.
+ // tags:
+ // protected
+ var i = array.indexOf(this._connects, handle);
+ if(i != -1){
+ handle.remove();
+ this._connects.splice(i, 1);
+ }
+ },
+
+ subscribe: function(t, method){
+ // summary:
+ // Subscribes to the specified topic and calls the specified method
+ // of this object and registers for unsubscribe() on widget destroy.
+ // description:
+ // Provide widget-specific analog to dojo.subscribe, except with the
+ // implicit use of this widget as the target object.
+ // t: String
+ // The topic
+ // method: Function
+ // The callback
+ // example:
+ // | var btn = new dijit.form.Button();
+ // | // when /my/topic is published, this button changes its label to
+ // | // be the parameter of the topic.
+ // | btn.subscribe("/my/topic", function(v){
+ // | this.set("label", v);
+ // | });
+ // tags:
+ // protected
+ var handle = topic.subscribe(t, lang.hitch(this, method));
+ this._connects.push(handle);
+ return handle; // _Widget.Handle
+ },
+
+ unsubscribe: function(/*Object*/ handle){
+ // summary:
+ // Unsubscribes handle created by this.subscribe.
+ // Also removes handle from this widget's list of subscriptions
+ // tags:
+ // protected
+ this.disconnect(handle);
+ },
+
+ 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(); //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's domNode reference somewhere in the DOM based
+ // on standard domConstruct.place conventions, or passing a Widget reference that
+ // contains and addChild member.
+ //
+ // description:
+ // A convenience function provided in all _Widgets, providing a simple
+ // shorthand mechanism to put an existing (or newly created) Widget
+ // somewhere in the dom, and allow chaining.
+ //
+ // reference:
+ // The String id of a domNode, a domNode reference, or a reference to a Widget possessing
+ // an addChild method.
+ //
+ // position:
+ // If passed a string or domNode reference, the position argument
+ // accepts a string just as domConstruct.place does, one of: "first", "last",
+ // "before", or "after".
+ //
+ // If passed a _Widget reference, and that widget reference has an ".addChild" method,
+ // it will be called passing this widget instance into that method, supplying the optional
+ // position index passed.
+ //
+ // returns:
+ // dijit._Widget
+ // Provides a useful return of the newly created dijit._Widget instance so you
+ // can "chain" this function by instantiating, placing, then saving the return value
+ // to a variable.
+ //
+ // example:
+ // | // create a Button with no srcNodeRef, and place it in the body:
+ // | var button = new dijit.form.Button({ label:"click" }).placeAt(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 dijit.form.Button({},"src").placeAt("wrapper");
+ //
+ // example:
+ // | // place a new button as the first element of some div
+ // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
+ //
+ // example:
+ // | // create a contentpane and add it to a TabContainer
+ // | var tc = dijit.byId("myTabs");
+ // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
+
+ if(reference.declaredClass && reference.addChild){
+ reference.addChild(this, position);
+ }else{
+ domConstruct.place(this.domNode, reference, position);
+ }
+ 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/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/_base/sniff", // has("ie")
+ "../_Widget",
+ "../_TemplatedMixin",
+ "./_FormMixin",
+ "../layout/_ContentPaneResizeMixin"
+], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _FormMixin = dijit.form._FormMixin;
+ var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
=====*/
-(function(){
- // precalculate long expressions
- var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
-
- dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
+ // module:
+ // dijit/form/Form
+ // summary:
+ // Widget corresponding to HTML form tag, for validation and serialization
+
+
+ return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
// 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){
+ // 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:
- // an object that makes a node moveable with a timer
- // node: Node||String
- // a node (or node's id) to be moved
- // params: dojo.dnd.__TimedMoveableArgs
- // object with additional parameters.
-
- // sanitize parameters
- if(!params){ params = {}; }
- if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
- this.timeout = params.timeout;
+ // 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, []);
}
},
-
- // markup methods
- markupFactory: function(params, node){
- return new dojo.dnd.TimedMoveable(node, params);
+
+ 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
},
-
- onMoveStop: function(/* dojo.dnd.Mover */ mover){
- if(mover._timer){
- // stop timer
- clearTimeout(mover._timer)
- // reflect the last received position
- oldOnMove.call(this, mover, mover._leftTop)
+
+ _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);
}
- dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
},
- onMove: function(/* dojo.dnd.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);
+
+ 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();
}
}
});
-})();
+});
-}
+},
+'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){
-if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form._FormMixin"] = true;
-dojo.provide("dijit.form._FormMixin");
+/*=====
+ var StackContainer = dijit.layout.StackContainer;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+=====*/
+
+// module:
+// dijit/layout/_TabContainerBase
+// summary:
+// Abstract base class for TabContainer. Must define _makeController() to instantiate
+// and return the widget that displays the tab labels
-dojo.declare("dijit.form._FormMixin", null, {
+return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
// 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)
+ // Abstract base class for TabContainer. Must define _makeController() to instantiate
+ // and return the widget that displays the tab labels
// 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
+ // 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._Widget*/ 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);
+ }
+});
+
+});
+
+},
+'dojo/store/Memory':function(){
+define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine"], function(declare, QueryResults, SimpleQueryEngine) {
+ // module:
+ // dojo/store/Memory
+ // summary:
+ // The module defines an in-memory object store.
+
+
+return declare("dojo.store.Memory", null, {
+ // summary:
+ // This is a basic in-memory object store. It implements dojo.store.api.Store.
+ constructor: function(/*dojo.store.Memory*/ options){
+ // summary:
+ // Creates a memory object store.
+ // options:
+ // 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 = (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 dojo.store.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
+ // summary:
+ // 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/_base/kernel",
+ "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
+ "dojo/ready",
+ "./_Widget",
+ "./_KeyNavContainer",
+ "./_TemplatedMixin"
+], function(require, declare, kernel, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _KeyNavContainer = dijit._KeyNavContainer;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+=====*/
+
+ // module:
+ // dijit/Toolbar
+ // summary:
+ // A Toolbar widget, used to hold things like `dijit.Editor` buttons
+
+
+ // Back compat w/1.6, remove for 2.0
+ if(!kernel.isAsync){
+ 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/_base/kernel", // kernel.isAsync
+ "dojo/_base/lang", // lang.extend
+ "dojo/ready",
+ "dojo/topic", // publish
+ "../registry", // registry.byId
+ "../_WidgetBase",
+ "./_LayoutWidget",
+ "dojo/i18n!../nls/common"
+], function(array, cookie, declare, domClass, kernel, lang, ready, topic,
+ registry, _WidgetBase, _LayoutWidget){
/*=====
- // value: Object
- // Name/value hash for each child widget with a name and value.
- // Child widgets without names are not part of the hash.
+var _WidgetBase = dijit._WidgetBase;
+var _LayoutWidget = dijit.layout._LayoutWidget;
+var StackController = dijit.layout.StackController;
+=====*/
+
+// module:
+// dijit/layout/StackContainer
+// summary:
+// A container that has multiple children, but shows only one child at a time.
+
+// Back compat w/1.6, remove for 2.0
+if(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/layout/StackController"];
+ require(requires); // use indirection so modules not rolled into a build
+ });
+}
+
+// These arguments can be specified for the children of a StackContainer.
+// 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.)
+lang.extend(_WidgetBase, {
+ // selected: Boolean
+ // Parameter for children of `dijit.layout.StackContainer` or subclasses.
+ // Specifies that this widget should be the initially displayed pane.
+ // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
+ selected: false,
+
+ // closable: Boolean
+ // Parameter for children of `dijit.layout.StackContainer` or subclasses.
+ // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
+ closable: false,
+
+ // iconClass: String
+ // Parameter for children of `dijit.layout.StackContainer` or subclasses.
+ // CSS Class specifying icon to use in label associated with this pane.
+ iconClass: "dijitNoIcon",
+
+ // showTitle: Boolean
+ // Parameter for children of `dijit.layout.StackContainer` or subclasses.
+ // When true, display title of this widget as tab label etc., rather than just using
+ // icon specified in iconClass
+ showTitle: true
+});
+
+return declare("dijit.layout.StackContainer", _LayoutWidget, {
+ // summary:
+ // A container that has multiple children, but shows only
+ // one child at a time
//
- // 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).
+ // description:
+ // A container for widgets (ContentPanes, for example) That displays
+ // only one Widget at a time.
//
- // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
+ // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
//
- // Example:
- // | { name: "John Smith", interests: ["sports", "movies"] }
+ // Can be base class for container, Wizard, Show, etc.
+
+ // 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,
=====*/
- // 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: "",
+ buildRendering: function(){
+ this.inherited(arguments);
+ domClass.add(this.domNode, "dijitLayoutContainer");
+ this.containerNode.setAttribute("role", "tabpanel");
+ },
- // 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}, ...])
- //
- //
+ 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(){
+ // 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._Widget*/ 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._Widget*/ 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._Widget*/ 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._Widget|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._Widget
+ // The newly selected widget.
+ // oldWidget: dijit._Widget
+ // 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.
+ var children = this.getChildren();
+ var index = array.indexOf(children, this.selectedChildWidget);
+ index += forward ? 1 : children.length - 1;
+ return children[ index % children.length ]; // dijit._Widget
+ },
+
+ 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._Widget*/ 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._Widget*/ 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._Widget*/ 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;
+ }
+});
+
+});
+
+},
+'dojo/regexp':function(){
+define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang) {
+ // module:
+ // dojo/regexp
+ // summary:
+ // TODOC
+
+lang.getObject("regexp", true, dojo);
+
+/*=====
+dojo.regexp = {
+ // summary: Regular expressions and Builder resources
+};
+=====*/
+
+dojo.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
+};
+
+dojo.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 dojo.regexp.group(b.join("|"), nonCapture); // String
+};
+
+dojo.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 dojo.regexp;
+});
+
+},
+'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/window" // winUtils.scrollIntoView
+], function(array, declare, kernel, lang, winUtils){
+
+ // module:
+ // dijit/form/_FormMixin
+ // 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)
+
+ 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(){
- dojo.forEach(this.getDescendants(), function(widget){
+ array.forEach(this._getDescendantFormWidgets(), function(widget){
if(widget.reset){
widget.reset();
}
@@ -8812,14 +18694,14 @@ dojo.declare("dijit.form._FormMixin", null, {
// 2 - it will call focus() on the first invalid
// sub-widget
var didFocus = false;
- return dojo.every(dojo.map(this.getDescendants(), function(widget){
+ 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
- dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
+ winUtils.scrollIntoView(widget.containerNode || widget.domNode);
widget.focus();
didFocus = true;
}
@@ -8828,7 +18710,7 @@ dojo.declare("dijit.form._FormMixin", null, {
},
setValues: function(val){
- dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
+ kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
return this.set('value', val);
},
_setValueAttr: function(/*Object*/ obj){
@@ -8837,7 +18719,7 @@ dojo.declare("dijit.form._FormMixin", null, {
// generate map from name --> [list of widgets with that name]
var map = { };
- dojo.forEach(this.getDescendants(), function(widget){
+ array.forEach(this._getDescendantFormWidgets(), function(widget){
if(!widget.name){ return; }
var entry = map[widget.name] || (map[widget.name] = [] );
entry.push(widget);
@@ -8848,25 +18730,25 @@ dojo.declare("dijit.form._FormMixin", null, {
continue;
}
var widgets = map[name], // array of widgets w/this name
- values = dojo.getObject(name, false, obj); // list of values for those widgets
+ values = lang.getObject(name, false, obj); // list of values for those widgets
if(values === undefined){
continue;
}
- if(!dojo.isArray(values)){
+ 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
- dojo.forEach(widgets, function(w, i){
- w.set('value', dojo.indexOf(values, w.value) != -1);
+ 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
- dojo.forEach(widgets, function(w, i){
+ array.forEach(widgets, function(w, i){
w.set('value', values[i]);
});
}
@@ -8875,7 +18757,7 @@ dojo.declare("dijit.form._FormMixin", null, {
/***
* TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
- dojo.forEach(this.containerNode.elements, function(element){
+ array.forEach(this.containerNode.elements, function(element){
if(element.name == ''){return}; // like "continue"
var namePath = element.name.split(".");
var myObj=obj;
@@ -8917,20 +18799,20 @@ dojo.declare("dijit.form._FormMixin", null, {
switch(element.type){
case "checkbox":
element.checked = (name in myObj) &&
- dojo.some(myObj[name], function(val){ return val == element.value; });
+ 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;
- dojo.forEach(element.options, function(option){
- option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
+ 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";
- dojo.forEach(element.options, function(option){
+ array.forEach(element.options, function(option){
option.selected = option.value == myObj[name];
});
break;
@@ -8943,13 +18825,13 @@ dojo.declare("dijit.form._FormMixin", null, {
}
});
*/
-
+
// Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
// which I am monitoring.
},
getValues: function(){
- dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
+ kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
return this.get('value');
},
_getValueAttr: function(){
@@ -8962,14 +18844,14 @@ dojo.declare("dijit.form._FormMixin", null, {
// 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 setTimout(..., 0) in _handleOnChange()
+ // and even if it did it would come too late due to the setTimeout(..., 0) 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 = { };
- dojo.forEach(this.getDescendants(), function(widget){
+ array.forEach(this._getDescendantFormWidgets(), function(widget){
var name = widget.name;
if(!name || widget.disabled){ return; }
@@ -8981,45 +18863,45 @@ dojo.declare("dijit.form._FormMixin", null, {
if(/Radio/.test(widget.declaredClass)){
// radio button
if(value !== false){
- dojo.setObject(name, value, obj);
+ lang.setObject(name, value, obj);
}else{
// give radio widgets a default of null
- value = dojo.getObject(name, false, obj);
+ value = lang.getObject(name, false, obj);
if(value === undefined){
- dojo.setObject(name, null, obj);
+ lang.setObject(name, null, obj);
}
}
}else{
// checkbox/toggle button
- var ary=dojo.getObject(name, false, obj);
+ var ary=lang.getObject(name, false, obj);
if(!ary){
ary=[];
- dojo.setObject(name, ary, obj);
+ lang.setObject(name, ary, obj);
}
if(value !== false){
ary.push(value);
}
}
}else{
- var prev=dojo.getObject(name, false, obj);
+ var prev=lang.getObject(name, false, obj);
if(typeof prev != "undefined"){
- if(dojo.isArray(prev)){
+ if(lang.isArray(prev)){
prev.push(value);
}else{
- dojo.setObject(name, [prev, value], obj);
+ lang.setObject(name, [prev, value], obj);
}
}else{
// unique name
- dojo.setObject(name, value, obj);
+ lang.setObject(name, value, obj);
}
}
});
/***
- * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
+ * 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 = { };
- dojo.forEach(this.containerNode.elements, function(elm){
+ array.forEach(this.containerNode.elements, function(elm){
if(!elm.name) {
return; // like "continue"
}
@@ -9038,13 +18920,13 @@ dojo.declare("dijit.form._FormMixin", null, {
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
- } else if(typeof(myObj[nameA[0]]) == "undefined"){
+ }else if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]] = { }
} // if
if(nameA.length == 1){
myObj=myObj[nameA[0]];
- } else{
+ }else{
myObj=myObj[nameA[0]][nameIndex];
} // if
} // for
@@ -9052,15 +18934,15 @@ dojo.declare("dijit.form._FormMixin", null, {
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{
+ }else{
// can not set value when there is no name
}
- } else if(elm.type == "checkbox" && elm.checked){
+ }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"){
+ }else if(elm.type == "select-multiple"){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
@@ -9084,7 +18966,7 @@ dojo.declare("dijit.form._FormMixin", null, {
return this.state == "";
},
- onValidStateChange: function(isValid){
+ 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
@@ -9096,20 +18978,20 @@ dojo.declare("dijit.form._FormMixin", null, {
_getState: function(){
// summary:
// Compute what this.state should be based on state of children
- var states = dojo.map(this._descendants, function(w){
+ var states = array.map(this._descendants, function(w){
return w.get("state") || "";
});
- return dojo.indexOf(states, "Error") >= 0 ? "Error" :
- dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
+ return array.indexOf(states, "Error") >= 0 ? "Error" :
+ array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
},
disconnectChildren: function(){
// summary:
// Remove connections to monitor changes to children's value, error state, and disabled state,
// in order to update Form.value and Form.state.
- dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect"));
- dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); });
+ array.forEach(this._childConnections || [], lang.hitch(this, "disconnect"));
+ array.forEach(this._childWatches || [], function(w){ w.unwatch(); });
},
connectChildren: function(/*Boolean*/ inStartup){
@@ -9126,10 +19008,10 @@ dojo.declare("dijit.form._FormMixin", null, {
// Remove old connections, if any
this.disconnectChildren();
- this._descendants = this.getDescendants();
+ this._descendants = this._getDescendantFormWidgets();
// (Re)set this.value and this.state. Send watch() notifications but not on startup.
- var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set");
+ var set = inStartup ? function(name, val){ _this[name] = val; } : lang.hitch(this, "_set");
set("value", this.get("value"));
set("state", this._getState());
@@ -9137,14 +19019,14 @@ dojo.declare("dijit.form._FormMixin", null, {
// Form.state
var conns = (this._childConnections = []),
watches = (this._childWatches = []);
- dojo.forEach(dojo.filter(this._descendants,
+ array.forEach(array.filter(this._descendants,
function(item){ return item.validate; }
),
function(widget){
// We are interested in whenever the widget changes validity state - or
// whenever the disabled attribute on that widget is changed.
- dojo.forEach(["state", "disabled"], function(attr){
- watches.push(widget.watch(attr, function(attr, oldVal, newVal){
+ array.forEach(["state", "disabled"], function(attr){
+ watches.push(widget.watch(attr, function(){
_this.set("state", _this._getState());
}));
});
@@ -9154,7 +19036,7 @@ dojo.declare("dijit.form._FormMixin", null, {
var onChange = function(){
// summary:
// Called when child's value or disabled state changes
-
+
// Use setTimeout() to collapse value changes in multiple children into a single
// update to my value. Multiple updates will occur on:
// 1. Form.set()
@@ -9169,8 +19051,8 @@ dojo.declare("dijit.form._FormMixin", null, {
_this._set("value", _this.get("value"));
}, 10);
};
- dojo.forEach(
- dojo.filter(this._descendants, function(item){ return item.onChange; } ),
+ array.forEach(
+ array.filter(this._descendants, function(item){ return item.onChange; } ),
function(widget){
// When a child widget's value changes,
// the efficient thing to do is to just update that one attribute in this.value,
@@ -9203,800 +19085,611 @@ dojo.declare("dijit.form._FormMixin", null, {
}
});
+});
-}
-
-if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._DialogMixin"] = true;
-dojo.provide("dijit._DialogMixin");
+},
+'dijit/DropDownMenu':function(){
+require({cache:{
+'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" 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){
+/*=====
+ var _MenuBase = dijit._MenuBase;
+ var _OnDijitClickMixin = dijit._OnDijitClickMixin;
+=====*/
+ // module:
+ // dijit/DropDownMenu
+ // summary:
+ // dijit.DropDownMenu widget
-dojo.declare("dijit._DialogMixin", null,
- {
+ return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
// summary:
- // This provides functions useful to Dialog and TooltipDialog
+ // A menu, without features for context menu (Meaning, drop down menu)
- attributeMap: dijit._Widget.prototype.attributeMap,
-
- 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
- },
+ templateString: template,
- 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
- },
+ baseClass: "dijitMenu",
- 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'));
+ postCreate: function(){
+ 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]);
},
- _getFocusItems: function(){
+ _onKeyPress: function(/*Event*/ evt){
// summary:
- // Finds focusable items in dialog,
- // and sets this._firstFocusItem and this._lastFocusItem
+ // Handle keyboard based menu navigation.
// tags:
// protected
- var elems = dijit._getTabNavigable(this.containerNode);
- this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
- this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
- }
- }
-);
-
-}
-
-if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.DialogUnderlay"] = true;
-dojo.provide("dijit.DialogUnderlay");
-
-
-
-
-
-dojo.declare(
- "dijit.DialogUnderlay",
- [dijit._Widget, dijit._Templated],
- {
- // 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' dojoAttachPoint='node'></div></div>",
+ if(evt.ctrlKey || evt.altKey){ return; }
- // Parameters on creation or updatable later
+ 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;
+ }
+ }
+ });
+});
- // dialogId: String
- // Id of the dialog.... DialogUnderlay's id is based on this id
- dialogId: "",
+},
+'dojo/data/util/simpleFetch':function(){
+define("dojo/data/util/simpleFetch", ["dojo/_base/lang", "dojo/_base/window", "./sorter"],
+ function(lang, winUtil, sorter) {
+ // module:
+ // dojo/data/util/simpleFetch
+ // summary:
+ // TODOC
- // class: String
- // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
- "class": "",
+var simpleFetch = lang.getObject("dojo.data.util.simpleFetch", true);
- attributeMap: { id: "domNode" },
+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.
+ // 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 = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ var self = this;
- _setDialogIdAttr: function(id){
- dojo.attr(this.node, "id", id + "_underlay");
- this._set("dialogId", id);
- },
+ var _errorHandler = function(errorData, requestObject){
+ if(requestObject.onError){
+ var scope = requestObject.scope || winUtil.global;
+ requestObject.onError.call(scope, errorData, requestObject);
+ }
+ };
- _setClassAttr: function(clazz){
- this.node.className = "dijitDialogUnderlay " + clazz;
- this._set("class", clazz);
- },
+ var _fetchHandler = function(items, requestObject){
+ var oldAbortFunction = requestObject.abort || null;
+ var aborted = false;
- postCreate: function(){
- // summary:
- // Append the underlay to the body
- dojo.body().appendChild(this.domNode);
- },
+ var startIndex = requestObject.start?requestObject.start:0;
+ var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
- 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
+ requestObject.abort = function(){
+ aborted = true;
+ if(oldAbortFunction){
+ oldAbortFunction.call(requestObject);
+ }
+ };
- var is = this.node.style,
- os = this.domNode.style;
+ var scope = requestObject.scope || winUtil.global;
+ if(!requestObject.store){
+ requestObject.store = self;
+ }
+ if(requestObject.onBegin){
+ requestObject.onBegin.call(scope, items.length, requestObject);
+ }
+ if(requestObject.sort){
+ items.sort(sorter.createSortFunction(requestObject.sort, self));
+ }
+ 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);
+ }
+ };
+ this._fetchItems(request, _fetchHandler, _errorHandler);
+ return request; // Object
+};
- // 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";
+return simpleFetch;
+});
- // then resize and show
- var viewport = dojo.window.getBox();
- os.top = viewport.t + "px";
- os.left = viewport.l + "px";
- is.width = viewport.w + "px";
- is.height = viewport.h + "px";
- os.display = "block";
- },
+},
+'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/_base/kernel",
+ "dojo/keys", // keys.F10
+ "dojo/_base/lang", // lang.hitch
+ "dojo/on",
+ "dojo/_base/sniff", // has("ie"), has("quirks")
+ "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames win.withGlobal
+ "dojo/window", // winUtils.get
+ "./popup",
+ "./DropDownMenu",
+ "dojo/ready"
+], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, kernel, keys, lang, on,
+ has, win, winUtils, pm, DropDownMenu, ready){
- show: function(){
- // summary:
- // Show the dialog underlay
- this.domNode.style.display = "block";
- this.layout();
- this.bgIframe = new dijit.BackgroundIframe(this.domNode);
- },
+/*=====
+ var DropDownMenu = dijit.DropDownMenu;
+=====*/
- hide: function(){
- // summary:
- // Hides the dialog underlay
- this.bgIframe.destroy();
- delete this.bgIframe;
- this.domNode.style.display = "none";
- }
- }
-);
+// module:
+// dijit/Menu
+// summary:
+// Includes dijit.Menu widget and base class dijit._MenuBase
+// Back compat w/1.6, remove for 2.0
+if(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
+ require(requires); // use indirection so modules not rolled into a build
+ });
}
-if(!dojo._hasResource["dijit.layout._ContentPaneResizeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout._ContentPaneResizeMixin"] = true;
-dojo.provide("dijit.layout._ContentPaneResizeMixin");
-
+return declare("dijit.Menu", DropDownMenu, {
+ // summary:
+ // A context menu you can assign to multiple elements
+ constructor: function(){
+ 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: [],
-dojo.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
+ // 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,
- // 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,
+ // leftClickToOpen: [const] Boolean
+ // If true, menu will open on left click instead of right click, similar to a file menu.
+ leftClickToOpen: false,
- // isContainer: [protected] Boolean
- // Indicates that this widget acts as a "parent" to the descendant widgets.
- // When the parent is started it will call startup() on the child widgets.
- // See also `isLayoutContainer`.
- isContainer: true,
+ // refocus: Boolean
+ // When this menu closes, re-focus the element which had focus before it was opened.
+ refocus: true,
- // isLayoutContainer: [protected] Boolean
- // Indicates that this widget will call resize() on it's child widgets
- // when they become visible.
- isLayoutContainer: true,
+ postCreate: function(){
+ if(this.contextMenuForWindow){
+ this.bindDomNode(win.body());
+ }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);
+ },
- _startChildren: function(){
+ // thanks burstlib!
+ _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
- // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
-
- // This starts all the widgets
- dojo.forEach(this.getChildren(), function(child){
- child.startup();
- child._started = true;
- });
+ // 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
},
- startup: function(){
+ _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
- // See `dijit.layout._LayoutWidget.startup` for description.
- // Although ContentPane doesn't extend _LayoutWidget, it does implement
- // the same API.
+ // 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
+ },
- if(this._started){ return; }
+ bindDomNode: function(/*String|DomNode*/ node){
+ // summary:
+ // Attach menu to given node
+ node = dom.byId(node);
- var parent = dijit._Contained.prototype.getParent.call(this);
- this._childOfLayoutWidget = parent && parent.isLayoutContainer;
+ var cn; // Connect node
- // 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;
+ // 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.withGlobal(window, win.body);
+ }else{
- this.inherited(arguments);
+ // 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() ? win.doc.documentElement : node);
+ }
- this._startChildren();
- if(this._isShown()){
- this._onShow();
- }
+ // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
+ var binding = {
+ node: node,
+ iframe: iframe
+ };
- if(!this._childOfLayoutWidget){
- // If my parent isn't a layout container, since my style *may be* width=height=100%
- // or something similar (either set directly or via a CSS class),
- // monitor when my size changes so that I can re-layout.
- // For browsers where I can't directly monitor when my size changes,
- // monitor when the viewport changes size, which *may* indicate a size change for me.
- this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
- // Using function(){} closure to ensure no arguments to resize.
- this._needLayout = !this._childOfLayoutWidget;
- this.resize();
- });
- }
- },
+ // 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));
- _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.
+ // 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){
+ return [
+ // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
+ // rather than shift-F10?
+ on(cn, this.leftClickToOpen ? "click" : "contextmenu", lang.hitch(this, function(evt){
+ // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
+ event.stop(evt);
+ this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
+ })),
+ on(cn, "keydown", lang.hitch(this, function(evt){
+ if(evt.shiftKey && evt.keyCode == keys.F10){
+ event.stop(evt);
+ this._scheduleOpen(evt.target, iframe); // no coords - open near target node
+ }
+ }))
+ ];
+ });
+ binding.connects = cn ? doConnects(cn) : [];
- var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
- return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
- }),
- childWidgetNodes = childNodes.filter(function(node){
- return dojo.hasAttr(node, "data-dojo-type") || dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
- }),
- candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
- return widget && widget.domNode && widget.resize;
- });
+ 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.
- if(
- // all child nodes are widgets
- childNodes.length == childWidgetNodes.length &&
+ 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
- // all but one are invisible (like dojo.data)
- candidateWidgets.length == 1
- ){
- this._singleChild = candidateWidgets[0];
- }else{
- delete this._singleChild;
+ var window = this._iframeContentWindow(iframe);
+ cn = win.withGlobal(window, win.body);
+ binding.connects = doConnects(cn);
+ });
+ if(iframe.addEventListener){
+ iframe.addEventListener("load", binding.onloadHandler, false);
+ }else{
+ iframe.attachEvent("onload", binding.onloadHandler);
+ }
}
-
- // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
- dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
- resize: function(changeSize, resultSize){
+ unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
- // See `dijit.layout._LayoutWidget.resize` for description.
- // Although ContentPane doesn't extend _LayoutWidget, it does implement
- // the same API.
+ // Detach menu from given node
- // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
- // never called, 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();
+ var node;
+ try{
+ node = dom.byId(nodeName);
+ }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;
}
- this._resizeCalled = true;
+ // 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();
+ }
- this._scheduleLayout(changeSize, resultSize);
- },
+ // 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);
+ }
+ }
- _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;
+ domAttr.remove(node, attrName);
+ delete this._bindings[bid];
}
},
- _layout: function(changeSize, resultSize){
+ _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
// summary:
- // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
- // Also, since I am a Container widget, each of my children expects me to
- // call resize() or layout() on them.
+ // Set timer to display myself. Using a timer rather than displaying immediately solves
+ // two problems:
//
- // 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
-
- // Set margin box size, unless it wasn't specified, in which case use current size.
- if(changeSize){
- dojo.marginBox(this.domNode, changeSize);
- }
+ // 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.)
- // 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 || {};
- dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
- if(!("h" in mb) || !("w" in mb)){
- mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
- }
- this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
- }else{
- this._contentBox = dojo.contentBox(cn);
+ if(!this._openTimer){
+ this._openTimer = setTimeout(lang.hitch(this, function(){
+ delete this._openTimer;
+ this._openMyself({
+ target: target,
+ iframe: iframe,
+ coords: coords
+ });
+ }), 1);
}
-
- this._layoutChildren();
-
- delete this._needLayout;
},
-
- _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 || dojo.contentBox(this.containerNode);
+ _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).
- // 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.
- dojo.forEach(this.getChildren(), function(widget){
- if(widget.resize){
- widget.resize();
- }
- });
- }
- },
+ var target = args.target,
+ iframe = args.iframe,
+ coords = args.coords;
- _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
+ // 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 = win.withGlobal(window, "_docScroll", dojo);
- 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;
+ 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;
}
- 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') && !dojo.hasClass(node, "dijitHidden") &&
- parent && parent.style && (parent.style.display != 'none');
+ coords = domGeometry.position(target, true);
+ coords.x += 10;
+ coords.y += 10;
}
- },
- _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)
+ 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;
- if(this._needLayout){
- // If a layout has been scheduled for when we become visible, do it now
- this._layout(this._changeSize, this._resultSize);
+ 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.inherited(arguments);
+ 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
+ };
+ },
- // Need to keep track of whether ContentPane has been shown (which is different than
- // whether or not it's currently visible).
- this._wasShown = true;
+ uninitialize: function(){
+ array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
+ this.inherited(arguments);
}
});
-}
-
-if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.html"] = true;
-dojo.provide("dojo.html");
-
-
-dojo.getObject("html", true, dojo);
+});
-// the parser might be needed..
-(function(){ // private scope, sort of a namespace
+},
+'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
+ // summary:
+ // Mixin to provide widget functionality corresponding to an HTML checkbox
- // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
- var idCounter = 0,
- d = dojo;
-
- dojo.html._secureForInnerHtml = function(/*String*/ cont){
+ return declare("dijit.form._CheckBoxMixin", null, {
// summary:
- // removes !DOCTYPE and title elements from the html string.
+ // Mixin to provide widget functionality corresponding to an HTML checkbox
//
- // 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
+ // 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.
//
- return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
- };
-
-/*====
- dojo.html._emptyNode = function(node){
- // summary:
- // removes all child nodes from the given node
- // node: DOMNode
- // the parent element
- };
-=====*/
- dojo.html._emptyNode = dojo.empty;
-
- dojo.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
- d.empty(node);
-
- if(cont) {
- if(typeof cont == "string") {
- cont = d._toDom(cont, node.ownerDocument);
- }
- if(!cont.nodeType && d.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) {
- d.place( cont[i], node, "last");
- }
- } else {
- // pass nodes, documentFragments and unknowns through to dojo.place
- d.place(cont, node, "last");
- }
- }
-
- // return DomNode
- return node;
- };
-
- // we wrap up the content-setting operation in a object
- dojo.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: dojo._scopeName,
- // startup: Boolean
- // Start the child widgets after parsing them. Only obeyed if parseContent is true.
- startup: true,
-
- // lifecyle 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"
- dojo.mixin(this, params || {});
-
- // give precedence to params.node vs. the node argument
- // and ensure its a node, not an id string
- node = this.node = dojo.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();
- this.onEnd();
+ // type: [private] String
+ // type attribute on <input> node.
+ // Overrides `dijit.form.Button.type`. Users should not change this value.
+ type: "checkbox",
- return this.node;
- },
- setContent: function(){
- // summary:
- // sets the content on the node
+ // 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",
- var node = this.node;
- if(!node) {
- // can't proceed
- throw new Error(this.declaredClass + ": setContent given no node");
- }
- try{
- node = dojo.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
+ // 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",
- // destroy any widgets from a previous run
- // NOTE: if you dont want this you'll need to empty
- // the parseResults array property yourself to avoid bad things happenning
- if(this.parseResults && this.parseResults.length) {
- dojo.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
- dojo.html._emptyNode(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(dojo.isString(cont)){
- if(this.cleanContent){
- cont = dojo.html._secureForInnerHtml(cont);
- }
-
- if(this.extractContent){
- var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
- if(match){ cont = match[1]; }
- }
- }
+ _setReadOnlyAttr: function(/*Boolean*/ value){
+ this._set("readOnly", value);
+ domAttr.set(this.focusNode, 'readOnly', value);
+ this.focusNode.setAttribute("aria-readonly", value);
+ },
- // 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 if you need those..
- this._parse();
- }
- return this.node; /* DomNode */
- },
-
- 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.node;
- delete this.content;
- },
-
- onContentError: function(err){
- return "Error occured setting content: " + 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 dont 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
- // Any errors resulting from parsing are passed to _onError for handling
+ // 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,
- var rootNode = this.node;
- try{
- // store the results (widgets, whatever) for potential retrieval
- var inherited = {};
- dojo.forEach(["dir", "lang", "textDir"], function(name){
- if(this[name]){
- inherited[name] = this[name];
- }
- }, this);
- this.parseResults = dojo.parser.parse({
- rootNode: rootNode,
- noStart: !this.startup,
- inherited: inherited,
- scope: this.parserScope
- });
- }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
- // overide/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
- dojo.html._setNodeContent(this.node, errText, true);
- }
+ postMixInProperties: function(){
+ if(this.value == ""){
+ this.value = "on";
}
- }); // end dojo.declare()
+ this.inherited(arguments);
+ },
- dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
+ 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:
- // 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:
- // dojo.html.set(node, "some string");
- // dojo.html.set(node, contentNode, {options});
- // dojo.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 dojo.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 dojo.html._ContentSetter(dojo.mixin(
- params,
- { content: cont, node: node }
- ));
- return op.set();
+ // 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);
}
- };
-})();
-
-}
-
-if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.ContentPane"] = true;
-dojo.provide("dijit.layout.ContentPane");
-
-
+ });
+});
+},
+'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",
+ "./_ContentPaneResizeMixin",
+ "dojo/string", // string.substitute
+ "dojo/html", // html._ContentSetter html._emptyNode
+ "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/_base/window", // win.body win.doc.createDocumentFragment
+ "dojo/_base/xhr", // xhr.get
+ "dojo/i18n" // i18n.getLocalization
+], function(kernel, lang, _Widget, _ContentPaneResizeMixin, string, html, nlsLoading,
+ array, declare, Deferred, dom, domAttr, win, xhr, i18n){
+/*=====
+ var _Widget = dijit._Widget;
+ var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
+=====*/
+// module:
+// dijit/layout/ContentPane
+// summary:
+// A widget containing an HTML fragment, specified inline
+// or by uri. Fragment may include widgets.
-dojo.declare(
- "dijit.layout.ContentPane", [dijit._Widget, dijit.layout._ContentPaneResizeMixin],
-{
+return declare("dijit.layout.ContentPane", [_Widget, _ContentPaneResizeMixin], {
// summary:
// A widget containing an HTML fragment, specified inline
// or by uri. Fragment may include widgets.
@@ -10031,13 +19724,11 @@ dojo.declare(
// 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>.
@@ -10053,7 +19744,7 @@ dojo.declare(
// 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: dojo._scopeName,
+ parserScope: kernel._scopeName,
// preventCache: Boolean
// Prevent caching of data from href's by appending a timestamp to the href.
@@ -10069,11 +19760,11 @@ dojo.declare(
// loadingMessage: String
// Message that shows while downloading
- loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
+ loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
// errorMessage: String
// Message that shows if an error occurs
- errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
+ errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
// isLoaded: [readonly] Boolean
// True if the ContentPane has data in it, either specified
@@ -10086,9 +19777,15 @@ dojo.declare(
baseClass: "dijitContentPane",
+ /*======
+ // ioMethod: dojo.xhrGet|dojo.xhrPost
+ // Function that should grab the content specified via href.
+ ioMethod: dojo.xhrGet,
+ ======*/
+
// ioArgs: Object
// Parameters to pass to xhrGet() request, for example:
- // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
+ // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
ioArgs: {},
// onLoadDeferred: [readonly] dojo.Deferred
@@ -10101,12 +19798,10 @@ dojo.declare(
// or content is loaded.
onLoadDeferred: null,
- // Override _Widget's attributeMap because we don't want the title attribute (used to specify
+ // 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.
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- title: []
- }),
+ _setTitleAttr: null,
// Flag to parser that I'll parse my contents, so it shouldn't.
stopParser: true,
@@ -10122,21 +19817,21 @@ dojo.declare(
// 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)){
- var df = dojo.doc.createDocumentFragment();
- srcNodeRef = dojo.byId(srcNodeRef)
+ var df = win.doc.createDocumentFragment();
+ srcNodeRef = dom.byId(srcNodeRef);
while(srcNodeRef.firstChild){
df.appendChild(srcNodeRef.firstChild);
}
- params = dojo.delegate(params, {content: df});
+ params = lang.delegate(params, {content: df});
}
this.inherited(arguments, [params, srcNodeRef]);
},
postMixInProperties: function(){
this.inherited(arguments);
- var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
- this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
- this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
+ var messages = i18n.getLocalization("dijit", "loading", this.lang);
+ this.loadingMessage = string.substitute(this.loadingMessage, messages);
+ this.errorMessage = string.substitute(this.errorMessage, messages);
},
buildRendering: function(){
@@ -10152,12 +19847,12 @@ dojo.declare(
// over a node (TODO: remove in 2.0, no longer needed after #11490)
this.domNode.title = "";
- if(!dojo.attr(this.domNode,"role")){
- dijit.setWaiRole(this.domNode, "group");
+ if(!domAttr.get(this.domNode,"role")){
+ this.domNode.setAttribute("role", "group");
}
},
- _startChildren: function(){
+ startup: function(){
// summary:
// Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
@@ -10166,8 +19861,8 @@ dojo.declare(
// And this catches stuff like dojo.dnd.Source
if(this._contentSetter){
- dojo.forEach(this._contentSetter.parseResults, function(obj){
- if(!obj._started && !obj._destroyed && dojo.isFunction(obj.startup)){
+ array.forEach(this._contentSetter.parseResults, function(obj){
+ if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
@@ -10178,7 +19873,7 @@ dojo.declare(
setHref: function(/*String|Uri*/ href){
// summary:
// Deprecated. Use set('href', ...) instead.
- dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
+ kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
return this.set("href", href);
},
_setHrefAttr: function(/*String|Uri*/ href){
@@ -10193,8 +19888,8 @@ dojo.declare(
// Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
this.cancel();
- this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
- this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
+ this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
+ this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
this._set("href", href);
@@ -10209,13 +19904,13 @@ dojo.declare(
this._hrefChanged = true;
}
- return this.onLoadDeferred; // dojo.Deferred
+ return this.onLoadDeferred; // Deferred
},
setContent: function(/*String|DomNode|Nodelist*/data){
// summary:
// Deprecated. Use set('content', ...) instead.
- dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
+ kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
this.set("content", data);
},
_setContentAttr: function(/*String|DomNode|Nodelist*/data){
@@ -10237,19 +19932,19 @@ dojo.declare(
// 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 dojo.Deferred(dojo.hitch(this, "cancel"));
+ 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 dojoType=ContentPane>...</div>)
+ // 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.addCallback(dojo.hitch(this, "onLoad"));
+ this.onLoadDeferred.addCallback(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; // dojo.Deferred
+ return this.onLoadDeferred; // Deferred
},
_getContentAttr: function(){
// summary:
@@ -10319,8 +20014,8 @@ dojo.declare(
// Cancel possible prior in-flight request
this.cancel();
- this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
- this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
+ this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
+ this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
this._load();
return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
},
@@ -10338,11 +20033,11 @@ dojo.declare(
url: this.href,
handleAs: "text"
};
- if(dojo.isObject(this.ioArgs)){
- dojo.mixin(getArgs, this.ioArgs);
+ if(lang.isObject(this.ioArgs)){
+ lang.mixin(getArgs, this.ioArgs);
}
- var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
+ var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs));
hand.addCallback(function(html){
try{
@@ -10391,7 +20086,7 @@ dojo.declare(
}
},
- destroyDescendants: function(){
+ destroyDescendants: function(/*Boolean*/ preserveDom){
// summary:
// Destroy all the widgets inside the ContentPane and empty containerNode
@@ -10406,24 +20101,26 @@ dojo.declare(
// For historical reasons we need to delete all widgets under this.containerNode,
// even ones that the user has created manually.
var setter = this._contentSetter;
- dojo.forEach(this.getChildren(), function(widget){
+ array.forEach(this.getChildren(), function(widget){
if(widget.destroyRecursive){
- widget.destroyRecursive();
+ widget.destroyRecursive(preserveDom);
}
});
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
- dojo.forEach(setter.parseResults, function(widget){
- if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
- widget.destroyRecursive();
+ array.forEach(setter.parseResults, function(widget){
+ if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == win.body()){
+ widget.destroyRecursive(preserveDom);
}
});
delete setter.parseResults;
}
// And then clear away all the DOM nodes
- dojo.html._emptyNode(this.containerNode);
+ if(!preserveDom){
+ html._emptyNode(this.containerNode);
+ }
// Delete any state information we have about current contents
delete this._singleChild;
@@ -10436,17 +20133,17 @@ dojo.declare(
// first get rid of child widgets
this.destroyDescendants();
- // dojo.html.set will take care of the rest of the details
+ // 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 dojo.html._ContentSetter)){
- setter = this._contentSetter = new dojo.html._ContentSetter({
+ if(! (setter && setter instanceof html._ContentSetter)){
+ setter = this._contentSetter = new html._ContentSetter({
node: this.containerNode,
- _onError: dojo.hitch(this, this._onError),
- onContentError: dojo.hitch(this, function(e){
+ _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);
@@ -10458,19 +20155,20 @@ dojo.declare(
})/*,
_onError */
});
- };
+ }
- var setterParams = dojo.mixin({
+ var setterParams = lang.mixin({
cleanContent: this.cleanContent,
extractContent: this.extractContent,
- parseContent: this.parseOnLoad,
+ parseContent: !cont.domNode && this.parseOnLoad,
parserScope: this.parserScope,
startup: false,
dir: this.dir,
- lang: this.lang
+ lang: this.lang,
+ textDir: this.textDir
}, this._contentSetterParams || {});
- setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
+ setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
// setter params must be pulled afresh from the ContentPane each time
delete this._contentSetterParams;
@@ -10482,8 +20180,9 @@ dojo.declare(
if(!isFakeContent){
if(this._started){
// Startup each top level child widget (and they will start their children, recursively)
- this._startChildren();
-
+ delete this._started;
+ this.startup();
+
// 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
@@ -10508,7 +20207,7 @@ dojo.declare(
},
// EVENT's, should be overide-able
- onLoad: function(data){
+ onLoad: function(/*===== data =====*/){
// summary:
// Event hook, is called after everything is loaded and widgetified
// tags:
@@ -10534,7 +20233,7 @@ dojo.declare(
return this.loadingMessage;
},
- onContentError: function(/*Error*/ error){
+ onContentError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called on DOM faults, require faults etc. in content.
//
@@ -10547,7 +20246,7 @@ dojo.declare(
// extension
},
- onDownloadError: function(/*Error*/ error){
+ onDownloadError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called when download error occurs.
//
@@ -10569,2979 +20268,2798 @@ dojo.declare(
}
});
-}
-
-if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.TooltipDialog"] = true;
-dojo.provide("dijit.TooltipDialog");
-
-
-
-
-
-
-dojo.declare(
- "dijit.TooltipDialog",
- [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
- {
- // summary:
- // Pops up a dialog that appears like a Tooltip
+});
- // title: String
- // Description of tooltip dialog (required for a11y)
- title: "",
+},
+'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/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
+ ".." // for exporting symbols to dijit, remove in 2.0
+], function(array, domClass, domGeometry, domStyle, lang, dijit){
+
+ // module:
+ // dijit/layout/utils
+ // summary:
+ // marginBox2contentBox() and layoutChildren()
- // doLayout: [protected] Boolean
- // Don't change this parameter from the default value.
- // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
- // is never a child of a layout container, nor can you specify the size of
- // TooltipDialog in order to control the size of an inner widget.
- doLayout: false,
+ var layout = lang.getObject("layout", true, dijit);
+ /*===== layout = dijit.layout =====*/
- // 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,
+ 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)
+ };
+ };
- // baseClass: [protected] String
- // The root className to use for the various states of this widget
- baseClass: "dijitTooltipDialog",
+ function capitalize(word){
+ return word.substring(0,1).toUpperCase() + word.substring(1);
+ }
- // _firstFocusItem: [private] [readonly] DomNode
- // The pointer to the first focusable node in the dialog.
- // Set by `dijit._DialogMixin._getFocusItems`.
- _firstFocusItem: null,
+ function size(widget, dim){
+ // size the child
+ var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
- // _lastFocusItem: [private] [readonly] DomNode
- // The pointer to which node has focus prior to our dialog.
- // Set by `dijit._DialogMixin._getFocusItems`.
- _lastFocusItem: null,
+ // 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);
+ }
+ }
- templateString: dojo.cache("dijit", "templates/TooltipDialog.html", "<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\"></div>\n</div>\n"),
+ 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.
- _setTitleAttr: function(/*String*/ title){
- this.containerNode.title = title;
- this._set("title", title)
- },
+ // copy dim because we are going to modify it
+ dim = lang.mixin({}, dim);
- postCreate: function(){
- this.inherited(arguments);
- this.connect(this.containerNode, "onkeypress", "_onKey");
- },
+ domClass.add(container, "dijitLayoutContainer");
- orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
- // summary:
- // Configure widget to be displayed in given position relative to the button.
- // This is called from the dijit.popup code, and should not be called
- // directly.
- // tags:
- // protected
- var newC = "dijitTooltipAB" + (corner.charAt(1) == 'L' ? "Left" : "Right")
- + " dijitTooltip"
- + (corner.charAt(0) == 'T' ? "Below" : "Above");
-
- dojo.replaceClass(this.domNode, newC, this._currentOrientClass || "");
- this._currentOrientClass = newC;
- },
+ // 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"; }));
- focus: function(){
- // summary:
- // Focus on first field
- this._getFocusItems(this.containerNode);
- dijit.focus(this._firstFocusItem);
- },
+ // 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)
+ }
- onOpen: function(/*Object*/ pos){
- // summary:
- // Called when dialog is displayed.
- // This is called from the dijit.popup code, and should not be called directly.
- // tags:
- // protected
+ // 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";
- this.orient(this.domNode,pos.aroundCorner, pos.corner);
- this._onShow(); // lazy load trigger
- },
+ domClass.add(elm, "dijitAlign" + capitalize(pos));
- onClose: function(){
- // summary:
- // Called when dialog is hidden.
- // This is called from the dijit.popup code, and should not be called directly.
- // tags:
- // protected
- this.onHide();
- },
+ // Size adjustments to make to this child widget
+ var sizeSetting = {};
- _onKey: function(/*Event*/ evt){
- // summary:
- // Handler for keyboard events
- // description:
- // Keep keyboard focus in dialog; close dialog on escape key
- // tags:
- // private
+ // 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;
+ }
- var node = evt.target;
- var dk = dojo.keys;
- if(evt.charOrCode === dk.TAB){
- this._getFocusItems(this.containerNode);
+ // 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";
}
- var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
- if(evt.charOrCode == dk.ESCAPE){
- // Use setTimeout to avoid crash on IE, see #10396.
- setTimeout(dojo.hitch(this, "onCancel"), 0);
- dojo.stopEvent(evt);
- }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
- if(!singleFocusItem){
- dijit.focus(this._lastFocusItem); // send focus to last item in dialog
- }
- dojo.stopEvent(evt);
- }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
- if(!singleFocusItem){
- dijit.focus(this._firstFocusItem); // send focus to first item in dialog
- }
- dojo.stopEvent(evt);
- }else if(evt.charOrCode === dk.TAB){
- // we want the browser's default tab handling to move focus
- // but we don't want the tab to propagate upwards
- evt.stopPropagation();
+ }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);
}
- }
- );
-
-}
-
-if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.Dialog"] = true;
-dojo.provide("dijit.Dialog");
-
-
-
+ });
+ };
+ 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
+ // summary:
+ // Mixin for widgets that are children of a container widget
+ 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._Widget
+ },
+ getPreviousSibling: function(){
+ // summary:
+ // Returns null if this is the first child of the parent,
+ // otherwise returns the next element sibling to the "left".
+ return this._getSibling("previous"); // dijit._Widget
+ },
+ getNextSibling: function(){
+ // summary:
+ // Returns null if this is the last child of the parent,
+ // otherwise returns the next element sibling to the "right".
+ return this._getSibling("next"); // dijit._Widget
+ },
+ getIndexInParent: function(){
+ // summary:
+ // Returns the index of this widget within its container parent.
+ // It returns -1 if the parent does not exist, or if the parent
+ // is not a dijit._Container
+ var p = this.getParent();
+ if(!p || !p.getIndexOfChild){
+ return -1; // int
+ }
+ return p.getIndexOfChild(this); // int
+ }
+ });
+});
-// dijit/TooltipDialog required for back-compat. TODO: remove in 2.0
+},
+'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){
/*=====
-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 _FocusMixin = dijit._FocusMixin;
+ var _Container = dijit._Container;
=====*/
-dojo.declare(
- "dijit._DialogBase",
- [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
- {
- // 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 dojoType="dijit.Dialog" href="test.html"></div>
- //
- // example:
- // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
- // | dojo.body().appendChild(foo.domNode);
- // | foo.startup();
- templateString: dojo.cache("dijit", "templates/Dialog.html", "<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"),
-
- baseClass: "dijitDialog",
-
- cssStateNodes: {
- closeButtonNode: "dijitDialogCloseIcon"
- },
+ // module:
+ // dijit/_KeyNavContainer
+ // summary:
+ // A _Container with keyboard navigation of its children.
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- title: [
- { node: "titleNode", type: "innerHTML" },
- { node: "titleBar", type: "attribute" }
- ],
- "aria-describedby":""
- }),
+ return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
- // 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: dijit.defaultDuration,
+ // 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.
- // 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,
+/*=====
+ // focusedChild: [protected] Widget
+ // The currently focused child widget, or null if there isn't one
+ focusedChild: null,
+=====*/
- // 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,
+ // tabIndex: Integer
+ // 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",
- // _firstFocusItem: [private readonly] DomNode
- // The pointer to the first focusable node in the dialog.
- // Set by `dijit._DialogMixin._getFocusItems`.
- _firstFocusItem: null,
+ 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
- // _lastFocusItem: [private readonly] DomNode
- // The pointer to which node has focus prior to our dialog.
- // Set by `dijit._DialogMixin._getFocusItems`.
- _lastFocusItem: null,
+ // TODO: call this automatically from my own postCreate()
- // 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,
+ 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");
+ },
- // 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,
+ startupKeyNavChildren: function(){
+ kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
+ },
- //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 dojoType="dijit.Dialog" aria-describedby="intro" .....>
- // <div id="intro">Introductory text</div>
- // <div>rest of dialog contents</div>
- // </div>
- "aria-describedby":"",
+ startup: function(){
+ this.inherited(arguments);
+ array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
+ },
- postMixInProperties: function(){
- var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
- dojo.mixin(this, _nlsResources);
+ addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
this.inherited(arguments);
+ this._startupChild(widget);
},
- postCreate: function(){
- dojo.style(this.domNode, {
- display: "none",
- position:"absolute"
- });
- dojo.body().appendChild(this.domNode);
+ focus: function(){
+ // summary:
+ // Default focus() implementation: focus the first child.
+ this.focusFirstChild();
+ },
- this.inherited(arguments);
+ focusFirstChild: function(){
+ // summary:
+ // Focus the first focusable child in the container.
+ // tags:
+ // protected
+ this.focusChild(this._getFirstFocusableChild());
+ },
- this.connect(this, "onExecute", "hide");
- this.connect(this, "onCancel", "hide");
- this._modalconnects = [];
+ focusLastChild: function(){
+ // summary:
+ // Focus the last focusable child in the container.
+ // tags:
+ // protected
+ this.focusChild(this._getLastFocusableChild());
},
- onLoad: function(){
+ focusNext: 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.
+ // Focus the next widget
// tags:
- // callback
+ // protected
+ this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
+ },
- // 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 && dijit._DialogLevelManager.isTop(this)){
- this._getFocusItems(this.domNode);
- dijit.focus(this._firstFocusItem);
- }
- this.inherited(arguments);
+ 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);
},
- _endDrag: function(e){
+ focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
// summary:
- // Called after dragging the Dialog. Saves the position of the dialog in the viewport.
+ // 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:
- // private
- if(e && e.node && e.node === this.domNode){
- this._relativePosition = dojo.position(e.node);
+ // 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);
},
- _setup: function(){
+ _startupChild: function(/*dijit._Widget*/ widget){
// summary:
- // Stuff we need to do before showing the Dialog for the first
- // time (but we defer it until right beforehand, for
- // performance reasons).
+ // 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
- var node = this.domNode;
-
- if(this.titleBar && this.draggable){
- this._moveable = (dojo.isIE == 6) ?
- new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285
- new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
- this._dndListener = dojo.subscribe("/dnd/move/stop",this,"_endDrag");
- }else{
- dojo.addClass(node,"dijitDialogFixed");
- }
+ widget.set("tabIndex", "-1");
- this.underlayAttrs = {
- dialogId: this.id,
- "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
- };
+ 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");
+ });
},
- _size: function(){
+ _onContainerFocus: function(evt){
// summary:
- // If necessary, shrink dialog contents so dialog fits in viewport
+ // 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
- this._checkIfSingleChild();
+ // 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.
- // 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 dojo.marginBox(this.domNode) call below.
- if(this._singleChild){
- if(this._singleChildOriginalStyle){
- this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
- }
- delete this._singleChildOriginalStyle;
- }else{
- dojo.style(this.containerNode, {
- width:"auto",
- height:"auto"
- });
- }
+ // 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; }
- var mb = dojo._getMarginSize(this.domNode);
- var viewport = dojo.window.getBox();
- if(mb.w >= viewport.w || mb.h >= viewport.h){
- // Reduce size of dialog contents so that dialog fits in viewport
+ this.focusFirstChild();
- var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
- h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
+ // 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");
+ },
- if(this._singleChild && this._singleChild.resize){
- this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
- this._singleChild.resize({w: w, h: h});
- }else{
- dojo.style(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();
- }
+ _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);
},
- _position: function(){
+ _onContainerKeypress: function(evt){
// 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.
+ // When a key is pressed, if it's an arrow key etc. then
+ // it's handled here.
// tags:
// private
- if(!dojo.hasClass(dojo.body(),"dojoMove")){
- var node = this.domNode,
- viewport = dojo.window.getBox(),
- p = this._relativePosition,
- bb = p ? null : dojo._getBorderBox(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))
- ;
- dojo.style(node,{
- left: l + "px",
- top: t + "px"
- });
+ if(evt.ctrlKey || evt.altKey){ return; }
+ var func = this._keyNavCodes[evt.charOrCode];
+ if(func){
+ func();
+ event.stop(evt);
}
},
- _onKey: function(/*Event*/ evt){
+ _onChildBlur: function(/*dijit._Widget*/ /*===== widget =====*/){
// summary:
- // Handles the keyboard events for accessibility reasons
+ // Called when focus leaves a child widget to go
+ // to a sibling widget.
+ // Used by MenuBase.js (TODO: move code there)
// tags:
- // private
-
- if(evt.charOrCode){
- var dk = dojo.keys;
- var node = evt.target;
- if(evt.charOrCode === dk.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 === dk.TAB){
- if(!singleFocusItem){
- dijit.focus(this._lastFocusItem); // send focus to last item in dialog
- }
- dojo.stopEvent(evt);
- }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
- if(!singleFocusItem){
- dijit.focus(this._firstFocusItem); // send focus to first item in dialog
- }
- dojo.stopEvent(evt);
- }else{
- // see if the key is for the dialog
- while(node){
- if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
- if(evt.charOrCode == dk.ESCAPE){
- this.onCancel();
- }else{
- return; // just let it go
- }
- }
- node = node.parentNode;
- }
- // this key is for the disabled document window
- if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
- dojo.stopEvent(evt);
- // opera won't tab to a div
- }else if(!dojo.isOpera){
- try{
- this._firstFocusItem.focus();
- }catch(e){ /*squelch*/ }
- }
- }
- }
+ // protected
},
- show: function(){
+ _getFirstFocusableChild: function(){
// summary:
- // Display the dialog
- // returns: dojo.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();
- }
-
- this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
- this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
- // IE gives spurious resize events and can actually get stuck
- // in an infinite loop if we don't ignore them
- var viewport = dojo.window.getBox();
- if(!this._oldViewport ||
- viewport.h != this._oldViewport.h ||
- viewport.w != this._oldViewport.w){
- this.layout();
- this._oldViewport = viewport;
- }
- }));
- this._modalconnects.push(dojo.connect(this.domNode, "onkeypress", this, "_onKey"));
-
- dojo.style(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 dojo.Deferred(dojo.hitch(this, function(){
- fadeIn.stop();
- delete this._fadeInDeferred;
- }));
-
- fadeIn = dojo.fadeIn({
- node: this.domNode,
- duration: this.duration,
- beforeBegin: dojo.hitch(this, function(){
- dijit._DialogLevelManager.show(this, this.underlayAttrs);
- }),
- onEnd: dojo.hitch(this, function(){
- if(this.autofocus && dijit._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);
- dijit.focus(this._firstFocusItem);
- }
- this._fadeInDeferred.callback(true);
- delete this._fadeInDeferred;
- })
- }).play();
-
- return this._fadeInDeferred;
+ // Returns first child that can be focused
+ return this._getNextFocusableChild(null, 1); // dijit._Widget
},
- hide: function(){
+ _getLastFocusableChild: function(){
// summary:
- // Hide the dialog
- // returns: dojo.Deferred
- // Deferred object that resolves when the hide animation is complete
+ // Returns last child that can be focused
+ return this._getNextFocusableChild(null, -1); // dijit._Widget
+ },
- // if we haven't been initialized yet then we aren't showing and we can just return
- if(!this._alreadyInitialized){
- return;
+ _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);
}
- if(this._fadeInDeferred){
- this._fadeInDeferred.cancel();
+ 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._Widget
+ }
+ child = this._getSiblingOfChild(child, dir);
}
+ // no focusable child found
+ return null; // dijit._Widget
+ }
+ });
+});
- // fade-in Animation object, setup below
- var fadeOut;
+},
+'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", // dojo.store.Memory
+ "../registry" // registry.add registry.remove
+], function(declare, dom, lang, query, MemoryStore, registry){
+
+ // module:
+ // dijit/form/DataList
+ // summary:
+ // Inefficient but small data store specialized for inlined data via OPTION tags
- this._fadeOutDeferred = new dojo.Deferred(dojo.hitch(this, function(){
- fadeOut.stop();
- delete this._fadeOutDeferred;
- }));
+ function toItem(/*DOMNode*/ option){
+ // summary:
+ // Convert <option> node to hash
+ return {
+ id: option.value,
+ value: option.value,
+ name: lang.trim(option.innerText || option.textContent || '')
+ };
+ }
- fadeOut = dojo.fadeOut({
- node: this.domNode,
- duration: this.duration,
- onEnd: dojo.hitch(this, function(){
- this.domNode.style.display = "none";
- dijit._DialogLevelManager.hide(this);
- this.onHide();
- this._fadeOutDeferred.callback(true);
- delete this._fadeOutDeferred;
- })
- }).play();
+ 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>
+ // | ...
- if(this._scrollConnected){
- this._scrollConnected = false;
- }
- dojo.forEach(this._modalconnects, dojo.disconnect);
- this._modalconnects = [];
+ constructor: function(/*Object?*/ params, /*DomNode|String*/ srcNodeRef){
+ // store pointer to original DOM tree
+ this.domNode = dom.byId(srcNodeRef);
- if(this._relativePosition){
- delete this._relativePosition;
+ lang.mixin(this, params);
+ if(this.id){
+ registry.add(this); // add to registry so it can be easily found by id
}
- this._set("open", false);
+ this.domNode.style.display = "none";
- return this._fadeOutDeferred;
- },
-
- layout: function(){
- // summary:
- // Position the Dialog and the underlay
- // tags:
- // private
- if(this.domNode.style.display != "none"){
- if(dijit._underlay){ // avoid race condition during show()
- dijit._underlay.layout();
- }
- this._position();
- }
+ this.inherited(arguments, [{
+ data: query("option", this.domNode).map(toItem)
+ }]);
},
destroy: function(){
- if(this._fadeInDeferred){
- this._fadeInDeferred.cancel();
- }
- if(this._fadeOutDeferred){
- this._fadeOutDeferred.cancel();
- }
- if(this._moveable){
- this._moveable.destroy();
- }
- if(this._dndListener){
- dojo.unsubscribe(this._dndListener);
- }
- dojo.forEach(this._modalconnects, dojo.disconnect);
-
- dijit._DialogLevelManager.hide(this);
+ registry.remove(this.id);
+ },
- this.inherited(arguments);
+ 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);
}
- }
-);
+ });
+});
-dojo.declare(
- "dijit.Dialog",
- [dijit.layout.ContentPane, dijit._DialogBase],
- {}
-);
+},
+'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<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\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}\" ${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/_base/kernel",
+ "dojo/query", // query
+ "dojo/ready",
+ "./ToggleButton",
+ "./_CheckBoxMixin",
+ "dojo/text!./templates/CheckBox.html",
+ "dojo/NodeList-dom" // NodeList.addClass/removeClass
+], function(require, declare, domAttr, kernel, query, ready, ToggleButton, _CheckBoxMixin, template){
+
+/*=====
+ var ToggleButton = dijit.form.ToggleButton;
+ var _CheckBoxMixin = dijit.form._CheckBoxMixin;
+=====*/
-dijit._DialogLevelManager = {
+ // module:
+ // dijit/form/CheckBox
// 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.
+ // Checkbox widget
- show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
+ // Back compat w/1.6, remove for 2.0
+ if(!kernel.isAsync){
+ 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:
- // 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.
+ // Same as an HTML checkbox, but with fancy styling.
//
- // New dialog will be displayed on top of all currently displayed dialogs.
+ // 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.
//
- // Caller is responsible for setting focus in new dialog after the fade-in
- // animation completes.
-
- var ds = dijit._dialogStack;
-
- // Save current focus
- ds[ds.length-1].focus = dijit.getFocus(dialog);
-
- // Display the underlay, or if already displayed then adjust for this new dialog
- var underlay = dijit._underlay;
- if(!underlay || underlay._destroyed){
- underlay = dijit._underlay = new dijit.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 : 950;
- if(ds.length == 1){ // first dialog
- underlay.show();
- }
- dojo.style(dijit._underlay.domNode, 'zIndex', zIndex - 1);
-
- // Dialog
- dojo.style(dialog.domNode, 'zIndex', zIndex);
-
- ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
- },
-
- hide: function(/*dijit._Widget*/ 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.
+ // There are two modes:
+ // 1. High contrast mode
+ // 2. Normal mode
//
- // Caller is responsible for either setting display:none on the dialog domNode,
- // or calling dijit.popup.hide(), or removing it from the page DOM.
-
- var ds = dijit._dialogStack;
-
- if(ds[ds.length-1].dialog == dialog){
- // Removing the top (or only) dialog in the stack, return focus
- // to previous dialog
+ // 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.
- ds.pop();
+ templateString: template,
- var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
+ baseClass: "dijitCheckBox",
- // Adjust underlay
- if(ds.length == 1){
- // Returning to original page.
- // Hide the underlay, unless the underlay widget has already been destroyed
- // because we are being called during page unload (when all widgets are destroyed)
- if(!dijit._underlay._destroyed){
- dijit._underlay.hide();
- }
- }else{
- // Popping back to previous dialog, adjust underlay
- dojo.style(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
- dijit._underlay.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(!focus || (pd.dialog && !dojo.isDescendant(focus.node, pd.dialog.domNode))){
- pd.dialog._getFocusItems(pd.dialog.domNode);
- focus = pd.dialog._firstFocusItem;
- }
-
- try{
- dijit.focus(focus);
- }catch(e){
- /* focus() will fail if user opened the dialog by clicking a non-focusable element */
- }
+ _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._set("value", newValue);
+ domAttr.set(this.focusNode, 'value', newValue);
+ newValue = true;
}
- }else{
- // Removing a dialog out of order (#9944, #10705).
- // Don't need to mess with underlay or z-index or anything.
- var idx = dojo.indexOf(dojo.map(ds, function(elem){return elem.dialog}), dialog);
- if(idx != -1){
- ds.splice(idx, 1);
+ if(this._created){
+ this.set('checked', newValue, priorityChange);
}
- }
- },
-
- isTop: function(/*dijit._Widget*/ dialog){
- // summary:
- // Returns true if specified Dialog is the top in the task
- var ds = dijit._dialogStack;
- 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)
-// }
-dijit._dialogStack = [
- {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
-];
-
-}
-
-if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._HasDropDown"] = true;
-dojo.provide("dijit._HasDropDown");
-
-
-
-dojo.declare("dijit._HasDropDown",
- null,
- {
- // summary:
- // Mixin for widgets that need drop down ability.
+ },
+ _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);
+ },
- // _buttonNode: [protected] DomNode
- // The button/icon/node to click to display the drop down.
- // Can be set via a dojoAttachPoint assignment.
- // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
- _buttonNode: null,
+ // Override behavior from Button, since we don't have an iconNode
+ _setIconClassAttr: 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 dojoAttachPoint assignment.
- // If missing, then _buttonNode will be used.
- _arrowWrapperNode: null,
+ postMixInProperties: function(){
+ this.inherited(arguments);
- // _popupStateNode: [protected] DomNode
- // The node to set the popupActive class on.
- // Can be set via a dojoAttachPoint assignment.
- // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
- _popupStateNode: null,
+ // 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" : "";
+ },
- // _aroundNode: [protected] DomNode
- // The node to display the popup around.
- // Can be set via a dojoAttachPoint assignment.
- // If missing, then domNode will be used.
- _aroundNode: null,
+ _fillContent: function(){
+ // Override Button::_fillContent() since it doesn't make sense for CheckBox,
+ // since CheckBox doesn't even have a container
+ },
- // dropDown: [protected] Widget
- // The widget to display as a popup. This widget *must* be
- // defined before the startup function is called.
- dropDown: null,
+ _onFocus: function(){
+ if(this.id){
+ query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
+ }
+ this.inherited(arguments);
+ },
- // 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,
+ _onBlur: function(){
+ if(this.id){
+ query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
+ }
+ this.inherited(arguments);
+ }
+ });
+});
- // forceWidth: [protected] Boolean
- // Set to true to make the drop down exactly as wide as this
- // widget. Overrides autoWidth.
- forceWidth: false,
+},
+'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/lang", // lang.hitch
+ "dojo/mouse", // mouse.isLeft
+ "dojo/on",
+ "dojo/touch",
+ "dojo/_base/window", // win.global
+ "./_dndContainer"
+], function(array, connect, declare, lang, mouse, on, touch, win, _dndContainer){
+
+ // module:
+ // dijit/tree/_dndSelector
+ // 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`.
- // 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"],
+ 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
- // _stopClickEvents: Boolean
- // When set to false, the click events will not be stopped, in
- // case you want to use them in your subwidget
- _stopClickEvents: true,
+ /*=====
+ // selection: Hash<String, DomNode>
+ // (id, DomNode) map for every TreeNode that's currently selected.
+ // The DOMNode is the TreeNode.rowNode.
+ selection: {},
+ =====*/
- _onDropDownMouseDown: function(/*Event*/ e){
+ constructor: function(){
// summary:
- // Callback when the user mousedown's on the arrow icon
-
- if(this.disabled || this.readOnly){ return; }
+ // Initialization
+ // tags:
+ // private
- dojo.stopEvent(e);
+ this.selection={};
+ this.anchor = null;
- this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
+ this.tree.domNode.setAttribute("aria-multiselect", !this.singular);
- this.toggleDropDown();
+ 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"))
+ );
},
- _onDropDownMouseUp: function(/*Event?*/ e){
- // summary:
- // Callback when the user lifts their mouse after mouse down on the arrow icon.
- // If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
- // dropDown node. 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 = dojo.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(dojo.hasClass(t, "dijitPopup")){
- overMenu = true;
- }else{
- t = t.parentNode;
- }
- }
- if(overMenu){
- t = e.target;
- if(dropDown.onItemClick){
- var menuItem;
- while(t && !(menuItem = dijit.byNode(t))){
- t = t.parentNode;
- }
- if(menuItem && menuItem.onClick && menuItem.getParent){
- menuItem.getParent().onItemClick(menuItem, e);
- }
- }
- return;
- }
- }
- }
- if(this._opened && dropDown.focus && dropDown.autoFocus !== false){
- // Focus the dropdown widget - do it on a delay so that we
- // don't steal our own focus.
- window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
- }
- },
+ // 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,
- _onDropDownClick: function(/*Event*/ e){
- // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
- if(this._stopClickEvents){
- dojo.stopEvent(e);
+ // 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;
},
- buildRendering: function(){
- this.inherited(arguments);
-
- this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
- this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
+ selectNone: function(){
+ // summary:
+ // Unselects all items
+ // tags:
+ // private
- // 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";
- dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
+ this.setSelection([]);
+ return this; // self
},
- postCreate: function(){
+ destroy: function(){
// summary:
- // set up nodes and connect our mouse and keypress events
-
+ // Prepares the object to be garbage-collected
this.inherited(arguments);
+ this.selection = this.anchor = null;
+ },
+ addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
+ // summary:
+ // add node to current selection
+ // node: Node
+ // node to add
+ // isAnchor: Boolean
+ // Whether the node should become anchor.
- this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
- this.connect(this._buttonNode, "onclick", "_onDropDownClick");
- this.connect(this.focusNode, "onkeypress", "_onKey");
- this.connect(this.focusNode, "onkeyup", "_onKeyUp");
+ this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
+ if(isAnchor){ this.anchor = node; }
+ return node;
},
+ removeTreeNode: function(/*dijit._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._TreeNode*/node){
+ // summary:
+ // return true if node is currently selected
+ // node: Node
+ // the node to check whether it's in the current selection
- 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();
+ return node.id && !!this.selection[node.id];
+ },
+ setSelection: function(/*dijit._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.dropDown;
- }
- this.inherited(arguments);
+ 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();
},
-
- _onKey: function(/*Event*/ e){
+ _setDifference: function(xs,ys){
// summary:
- // Callback when the user presses a key while focused on the button node
+ // 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.
- if(this.disabled || this.readOnly){ return; }
+ array.forEach(ys, function(y){ y.__exclude__ = true; });
+ var ret = array.filter(xs, function(x){ return !x.__exclude__; });
- 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 */
- dojo.stopEvent(e);
- return;
- }
- }
- if(d && this._opened && e.charOrCode == dojo.keys.ESCAPE){
- this.closeDropDown();
- dojo.stopEvent(e);
- }else if(!this._opened &&
- (e.charOrCode == dojo.keys.DOWN_ARROW ||
- ( (e.charOrCode == dojo.keys.ENTER || e.charOrCode == " ") &&
- //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;
- dojo.stopEvent(e);
- }
+ // 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]
- _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){
- setTimeout(dojo.hitch(d, "focus"), 1);
- }
- }
+ var selected = this.getSelectedTreeNodes();
+ var paths = [], nodes = [];
+ array.forEach(selected, function(node){
+ nodes.push(node);
+ paths.push(node.getTreePath());
+ });
+ 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);
},
-
- _onBlur: function(){
+ // mouse events
+ onMouseDown: function(e){
// summary:
- // Called magically when focus has shifted away from this widget and it's dropdown
+ // Event processor for onmousedown/ontouchstart
+ // e: Event
+ // onmousedown/ontouchstart event
+ // tags:
+ // protected
- // 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 = dijit._curFocus && this.dropDown && dojo.isDescendant(dijit._curFocus, this.dropDown.domNode);
+ // ignore click on expando node
+ if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
- this.closeDropDown(focusMe);
+ if(!mouse.isLeft(e)){ return; } // ignore right-click
- this.inherited(arguments);
+ e.preventDefault();
+
+ 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);
},
- isLoaded: function(){
+ onMouseUp: function(e){
// summary:
- // Returns whether or not the dropdown is loaded. This can
- // be overridden in order to force a call to loadDropDown().
+ // Event processor for onmouseup/ontouchend
+ // e: Event
+ // onmouseup/ontouchend event
// tags:
// protected
- return true;
+ // _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);
},
-
- loadDropDown: function(/* Function */ loadCallback){
+ onMouseMove: function(/*===== e =====*/){
// summary:
- // Loads the data for the dropdown, and at some point, calls
- // the given callback. This is basically a callback when the
- // user presses the down arrow button to open the drop down.
- // tags:
- // protected
+ // event processor for onmousemove/ontouchmove
+ // e: Event
+ // onmousemove/ontouchmove event
+ this._doDeselect = false;
+ },
- loadCallback();
+ _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");
+ }
},
- toggleDropDown: function(){
+ userSelect: function(node, multi, range){
// 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
+ // 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.disabled || this.readOnly){ return; }
- if(!this._opened){
- // If we aren't loaded, load it first so there isn't a flicker
- if(!this.isLoaded()){
- this.loadDropDown(dojo.hitch(this, "openDropDown"));
- return;
+ if(this.singular){
+ if(this.anchor == node && multi){
+ this.selectNone();
}else{
- this.openDropDown();
+ this.setSelection([node]);
+ this.anchor = node;
}
}else{
- this.closeDropDown();
+ 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;
+ }
+ }
}
},
- openDropDown: function(){
+ getItem: function(/*String*/ key){
// 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()
+ // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
+ // Called by dojo.dnd.Source.checkAcceptance().
// tags:
// protected
- var dropDown = this.dropDown,
- ddNode = dropDown.domNode,
- aroundNode = this._aroundNode || this.domNode,
- self = this;
+ var widget = this.selection[key];
+ return {
+ data: widget,
+ type: ["treeNode"]
+ }; // dojo.dnd.Item
+ },
- // Prepare our popup's height and honor maxHeight if it exists.
+ forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
+ // summary:
+ // Iterates over selected items;
+ // see `dojo.dnd.Container.forInItems()` for details
+ o = o || win.global;
+ for(var id in this.selection){
+ // console.log("selected item id: " + id);
+ f.call(o, this.getItem(id), id, this);
+ }
+ }
+ });
+});
- // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
- // ie, dependent on how much space is available (BK)
+},
+'dijit/_Container':function(){
+define("dijit/_Container", [
+ "dojo/_base/array", // array.forEach array.indexOf
+ "dojo/_base/declare", // declare
+ "dojo/dom-construct", // domConstruct.place
+ "./registry" // registry.byNode()
+], function(array, declare, domConstruct, registry){
+
+ // module:
+ // dijit/_Container
+ // summary:
+ // Mixin for widgets that contain a set of widget children.
- 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;
- }
+ return declare("dijit._Container", null, {
+ // summary:
+ // Mixin for widgets that contain a set of widget children.
+ // description:
+ // Use this mixin for widgets that needs to know about and
+ // keep track of their widget children. Suitable for widgets like BorderContainer
+ // and TabContainer which contain (only) a set of child widgets.
+ //
+ // It's not suitable for widgets like ContentPane
+ // which contains mixed HTML (plain DOM nodes in addition to widgets),
+ // and where contained widgets are not necessarily directly below
+ // this.containerNode. In that case calls like addChild(node, position)
+ // wouldn't make sense.
+
+ buildRendering: function(){
+ this.inherited(arguments);
+ if(!this.containerNode){
+ // all widgets with descendants must set containerNode
+ this.containerNode = this.domNode;
}
+ },
- // 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 = "";
- }
- dojo.style(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 = dojo.window.getBox(),
- position = dojo.position(aroundNode, false);
- maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
- }
+ addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
+ // summary:
+ // Makes the given widget a child of this widget.
+ // description:
+ // Inserts specified child widget's dom node as a child of this widget's
+ // container node, and possibly does other processing (such as layout).
- // Attach dropDown to DOM and make make visibility:hidden rather than display:none
- // so we call startup() and also get the size
- if(dropDown.startup && !dropDown._started){
- dropDown.startup();
+ 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);
- dijit.popup.moveOffScreen(dropDown);
- // Get size of drop down, and determine if vertical scroll bar needed
- var mb = dojo._getMarginSize(ddNode);
- var overHeight = (maxHeight && mb.h > maxHeight);
- dojo.style(ddNode, {
- overflowX: "hidden",
- overflowY: overHeight ? "auto" : "hidden"
- });
- if(overHeight){
- mb.h = maxHeight;
- if("w" in mb){
- mb.w += 16; // room for vertical scrollbar
- }
- }else{
- delete mb.h;
- }
+ // 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();
+ }
+ },
- // 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(dojo.isFunction(dropDown.resize)){
- dropDown.resize(mb);
- }else{
- dojo.marginBox(ddNode, mb);
- }
+ 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
+
+ if(typeof widget == "number"){
+ widget = this.getChildren()[widget];
}
- var retVal = dijit.popup.open({
- parent: this,
- popup: dropDown,
- around: aroundNode,
- orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
- onExecute: function(){
- self.closeDropDown(true);
- },
- onCancel: function(){
- self.closeDropDown(true);
- },
- onClose: function(){
- dojo.attr(self._popupStateNode, "popupActive", false);
- dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
- self._opened = false;
+ if(widget){
+ var node = widget.domNode;
+ if(node && node.parentNode){
+ node.parentNode.removeChild(node); // detach but don't destroy
}
- });
- dojo.attr(this._popupStateNode, "popupActive", "true");
- dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
- this._opened=true;
+ }
+ },
- // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
- return retVal;
+ hasChildren: function(){
+ // summary:
+ // Returns true if widget has children, i.e. if this.containerNode contains something.
+ return this.getChildren().length > 0; // Boolean
},
- closeDropDown: function(/*Boolean*/ focus){
+ _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
// summary:
- // Closes the drop down on this widget
- // focus:
- // If true, refocuses the button widget
+ // Get the next or previous widget sibling of child
+ // dir:
+ // if 1, get the next sibling
+ // if -1, get the previous sibling
// tags:
- // protected
+ // private
+ var node = child.domNode,
+ which = (dir>0 ? "nextSibling" : "previousSibling");
+ do{
+ node = node[which];
+ }while(node && (node.nodeType != 1 || !registry.byNode(node)));
+ return node && registry.byNode(node); // dijit._Widget
+ },
- if(this._opened){
- if(focus){ this.focus(); }
- dijit.popup.close(this.dropDown);
- this._opened = false;
- }
+ getIndexOfChild: function(/*dijit._Widget*/ child){
+ // summary:
+ // Gets the index of the child in this container or -1 if not found
+ return array.indexOf(this.getChildren(), child); // int
}
+ });
+});
- }
-);
-
-}
-
-if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.Button"] = true;
-dojo.provide("dijit.form.Button");
-
+},
+'dojo/data/ItemFileReadStore':function(){
+define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
+ "../Evented", "../_base/window", "./util/filter", "./util/simpleFetch", "../date/stamp"
+], function(kernel, lang, declare, array, xhr, Evented, window, filterUtil, simpleFetch, dateStamp) {
+ // module:
+ // dojo/data/ItemFileReadStore
+ // summary:
+ // TODOC
+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 'identifer' 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}
+ // keywordParameters: {data: jsonObject}
+ // keywordParameters: {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;
+ }
+ },
-dojo.declare("dijit.form.Button",
- dijit.form._FormWidget,
- {
- // 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 dojoType="dijit.form.Button" onClick="...">Hello world</button>
- //
- // example:
- // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
- // | dojo.body().appendChild(button1.domNode);
+ url: "", // use "" rather than undefined for the benefit of the parser (#3539)
- // label: HTML String
- // Text to display in button.
- // If the label is hidden (showLabel=false) then and no title has
- // been specified, then label is also set as title attribute of icon.
- label: "",
+ //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
+ //when clearOnClose and close is used.
+ _ccUrl: "",
- // 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,
+ data: null, // define this so that the parser can populate it
- // iconClass: String
- // Class to apply to DOMNode in button to make it display an icon
- iconClass: "",
+ typeMap: null, //Define so parser can populate.
- // type: String
- // Defines the type of button. "button", "submit", or "reset".
- type: "button",
+ //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,
- baseClass: "dijitButton",
+ //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,
- templateString: dojo.cache("dijit.form", "templates/Button.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"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\tdojoAttachPoint=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
+ //Parameter for specifying that it is OK for the xhrGet call to fail silently.
+ failOk: false,
- attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
- value: "valueNode"
- }),
+ //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,
- _onClick: function(/*Event*/ e){
- // summary:
- // Internal function to handle click actions
- if(this.disabled){
- return false;
+ _assertIsItem: function(/* 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("dojo.data.ItemFileReadStore: Invalid item argument.");
}
- this._clicked(); // widget click actions
- return this.onClick(e); // user click actions
},
- _onButtonClick: function(/*Event*/ e){
- // summary:
- // Handler when the user activates the button portion.
- if(this._onClick(e) === false){ // returning nothing is same as true
- e.preventDefault(); // needed for checkbox
- }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
- for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
- var widget=dijit.byNode(node);
- if(widget && typeof widget._onSubmit == "function"){
- widget._onSubmit(e);
- break;
- }
- }
- }else if(this.valueNode){
- this.valueNode.click();
- e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
+ _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("dojo.data.ItemFileReadStore: Invalid attribute argument.");
}
},
- buildRendering: function(){
- this.inherited(arguments);
- dojo.setSelectable(this.focusNode, false);
- },
-
- _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))){
- this.set('label', source.innerHTML);
- }
+ getValue: function( /* 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
},
- _setShowLabelAttr: function(val){
- if(this.containerNode){
- dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
- }
- this._set("showLabel", val);
- },
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
- 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
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ // Clone it before returning. refs: #10474
+ return (item[attribute] || []).slice(0); // Array
},
- _clicked: function(/*Event*/ e){
- // summary:
- // Internal overridable function for when the button is clicked
+ getAttributes: function(/* 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
},
- setLabel: function(/*String*/ content){
- // summary:
- // Deprecated. Use set('label', ...) instead.
- dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
- this.set("label", content);
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ return (attribute in item);
},
- _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.innerHTML = content;
- if(this.showLabel == false && !this.params.title){
- this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
+ containsValue: function(/* 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.
},
- _setIconClassAttr: function(/*String*/ val){
- // Custom method so that icon node is hidden when not in use, to avoid excess padding/margin
- // appearing around it (even if it's a 0x0 sized <img> node)
-
- var oldVal = this.iconClass || "dijitNoIcon",
- newVal = val || "dijitNoIcon";
- dojo.replaceClass(this.iconNode, newVal, oldVal);
- this._set("iconClass", val);
- }
-});
-
-
-dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
- // summary:
- // A button with a drop down
- //
- // example:
- // | <button dojoType="dijit.form.DropDownButton" label="Hello world">
- // | <div dojotype="dijit.Menu">...</div>
- // | </button>
- //
- // example:
- // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
- // | dojo.body().appendChild(button1);
- //
-
- baseClass : "dijitDropDownButton",
-
- templateString: dojo.cache("dijit.form", "templates/DropDownButton.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\" dojoAttachPoint=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"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\tdojoAttachPoint=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdojoAttachPoint=\"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\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
-
- _fillContent: function(){
- // Overrides Button._fillContent().
+ _containsValue: function( /* 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)
//
- // 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 = dojo.query("*", this.srcNodeRef);
- dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
+ // 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
+ }
+ });
+ },
- // save pointer to srcNode so we can grab the drop down widget after it's instantiated
- this.dropDownContainer = this.srcNodeRef;
+ 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
},
- 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 = dojo.query("[widgetId]", this.dropDownContainer)[0];
- this.dropDown = dijit.byNode(dropDownNode);
- delete this.dropDownContainer;
- }
- if(this.dropDown){
- dijit.popup.hide(this.dropDown);
- }
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something); //boolean
+ },
- this.inherited(arguments);
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
},
- 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));
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
},
- loadDropDown: function(){
- // Loads our dropdown
- var dropDown = this.dropDown;
- if(!dropDown){ return; }
- if(!this.isLoaded()){
- var handler = dojo.connect(dropDown, "onLoad", this, function(){
- dojo.disconnect(handler);
- this.openDropDown();
- });
- dropDown.refresh();
- }else{
- this.openDropDown();
+ getLabel: function(/* 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
},
- isFocusable: function(){
- // Overridden so that focus is handled by the _HasDropDown mixin, not by
- // the _FormWidget mixin.
- return this.inherited(arguments) && !this._mouseDown;
- }
-});
-
-dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
- // summary:
- // A combination button and drop-down button.
- // Users can click one side to "press" the button, or click an arrow
- // icon to display the drop down.
- //
- // example:
- // | <button dojoType="dijit.form.ComboButton" onClick="...">
- // | <span>Hello world</span>
- // | <div dojoType="dijit.Menu">...</div>
- // | </button>
- //
- // example:
- // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
- // | dojo.body().appendChild(button1.domNode);
- //
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this._labelAttr){
+ return [this._labelAttr]; //array
+ }
+ return null; //null
+ },
- templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" dojoAttachPoint=\"buttonNode\" dojoAttachEvent=\"ondijitclick:_onButtonClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" role=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdojoAttachPoint=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" dojoAttachPoint=\"valueNode\"\n\t\t/></td></tr></tbody\n></table>\n"),
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ var self = this,
+ filter = function(requestArgs, arrayOfItems){
+ var items = [],
+ i, key;
+ if(requestArgs.query){
+ var value,
+ ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
- attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
- id: "",
- tabIndex: ["focusNode", "titleNode"],
- title: "titleNode"
- }),
+ //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(!self._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);
+ }
+ };
- // optionsTitle: String
- // Text that describes the options menu (accessibility)
- optionsTitle: "",
+ if(this._loadFinished){
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }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("dojo.data.ItemFileReadStore: ",
+ "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;
+ }
- baseClass: "dijitComboButton",
+ //See if there was any forced reset of data.
+ if(this.data != null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
- // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
- // mouse action over specified node
- cssStateNodes: {
- "buttonNode": "dijitButtonNode",
- "titleNode": "dijitButtonContents",
- "_popupStateNode": "dijitDownArrowButton"
- },
+ 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: filter});
+ }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;
- _focusedNode: null,
+ filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
+ self._handleQueuedFetches();
+ }catch(e){
+ self._loadFinished = true;
+ self._loadInProgress = false;
+ errorCallback(e, keywordArgs);
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ errorCallback(error, keywordArgs);
+ });
- _onButtonKeyPress: function(/*Event*/ evt){
- // summary:
- // Handler for right arrow key when focus is on left part of button
- if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
- dijit.focus(this._popupStateNode);
- dojo.stopEvent(evt);
+ //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;
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ }else{
+ errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
+ }
}
},
- _onArrowKeyPress: function(/*Event*/ evt){
- // summary:
- // Handler for left arrow key when focus is on right part of button
- if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
- dijit.focus(this.titleNode);
- dojo.stopEvent(evt);
+ _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;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
+ }else{
+ this.fetchItemByIdentity(delayedQuery);
+ }
+ }
+ this._queuedFetches = [];
}
},
-
- focus: function(/*String*/ position){
- // summary:
- // Focuses this widget to according to position, if specified,
- // otherwise on arrow node
- // position:
- // "start" or "end"
- if(!this.disabled){
- dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
- }
- }
-});
-
-dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
- // summary:
- // A button that can be in two states (checked or not).
- // Can be base class for things like tabs or checkbox or radio buttons
-
- baseClass: "dijitToggleButton",
- // 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,
-
- attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
- checked:"focusNode"
- }),
-
- _clicked: function(/*Event*/ evt){
- this.set('checked', !this.checked);
+ _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;
},
- _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
- this._set("checked", value);
- dojo.attr(this.focusNode || this.domNode, "checked", value);
- dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
- this._handleOnChange(value, priorityChange);
- },
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ 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.
- setChecked: function(/*Boolean*/ checked){
- // summary:
- // Deprecated. Use set('checked', true/false) instead.
- dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
- this.set('checked', checked);
+ if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
+ (this.url == "" || this.url == null)
+ ) && this.data == null){
+ console.debug("dojo.data.ItemFileReadStore: 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 = [];
+ }
},
- 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);
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.ToggleButton"] = true;
-dojo.provide("dijit.form.ToggleButton");
-
-
-
-
-}
-
-if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.CheckBox"] = true;
-dojo.provide("dijit.form.CheckBox");
-
-
-
-dojo.declare(
- "dijit.form.CheckBox",
- dijit.form.ToggleButton,
- {
- // 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: dojo.cache("dijit.form", "templates/CheckBox.html", "<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"focusNode\"\n\t \tdojoAttachEvent=\"onclick:_onClick\"\n/></div>\n"),
-
- baseClass: "dijitCheckBox",
-
- // 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).
- //
- // However, get('value') will return either the string or false depending on
- // whether or not the checkbox is checked.
+ _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.
//
- // set('value', string) will check the checkbox and change the value to the
- // specified string
+ // dataObject:
+ // The JS data object containing the raw data to convery into item format.
//
- // set('value', boolean) will change the checked state.
- 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,
-
- // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
- // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
- attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
- readOnly: "focusNode"
- }),
+ // returns: array
+ // Array of items in store item format.
- _setReadOnlyAttr: function(/*Boolean*/ value){
- this._set("readOnly", value);
- dojo.attr(this.focusNode, 'readOnly', value);
- dijit.setWaiState(this.focusNode, "readonly", value);
- },
+ // First, we define a couple little utility functions...
+ var addingArrays = false,
+ self = this;
- _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
- // dojoType="dijit.CheckBox" value="chicken">)
- if(typeof newValue == "string"){
- this._set("value", newValue);
- dojo.attr(this.focusNode, 'value', newValue);
- newValue = true;
- }
- if(this._created){
- this.set('checked', newValue, priorityChange);
- }
- },
- _getValueAttr: function(){
+ function valueIsAnItem(/* anything */ aValue){
// 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 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,
+ // 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;
+ }
- postMixInProperties: function(){
- if(this.value == ""){
- this.value = "on";
+ function addItemAndSubItemsToArrayOfAllItems(/* 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);
+ }
+ }
+ }
}
+ }
- // Need to set initial checked state as part of template, so that form submit works.
- // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
- // to <body>, see #8666
- this.checkedAttrSetting = this.checked ? "checked" : "";
-
- this.inherited(arguments);
- },
-
- _fillContent: function(/*DomNode*/ source){
- // Override Button::_fillContent() since it doesn't make sense for CheckBox,
- // since CheckBox doesn't even have a container
- },
+ this._labelAttr = dataObject.label;
- reset: function(){
- // Override ToggleButton.reset()
+ // 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.
- this._hasBeenBlurred = false;
+ // Step 1: Walk through the object hierarchy and build a list of all items
+ var i,
+ item;
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = dataObject.items;
- this.set('checked', this.params.checked || false);
+ for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
+ item = this._arrayOfTopLevelItems[i];
+ if(lang.isArray(item)){
+ addingArrays = true;
+ }
+ addItemAndSubItemsToArrayOfAllItems(item);
+ item[this._rootItemPropName]=true;
+ }
- // Handle unlikely event that the <input type=checkbox> value attribute has changed
- this._set("value", this.params.value || "on");
- dojo.attr(this.focusNode, 'value', this.value);
- },
+ // 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;
- _onFocus: function(){
- if(this.id){
- dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
+ 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;
}
- this.inherited(arguments);
- },
+ }
- _onBlur: function(){
- if(this.id){
- dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
- }
- this.inherited(arguments);
- },
+ // 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 += "_";
+ }
- _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){
- dojo.stopEvent(e);
- return false;
+ // 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("dojo.data.ItemFileReadStore: 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("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }
+ }
}
- return this.inherited(arguments);
+ }else{
+ this._features['dojo.data.api.Identity'] = Number;
}
- }
-);
-
-dojo.declare(
- "dijit.form.RadioButton",
- dijit.form.CheckBox,
- {
- // summary:
- // Same as an HTML radio, but with fancy styling.
- type: "radio",
- baseClass: "dijitRadio",
+ // 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;
+ }
- _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){
- var _this = this;
- // search for radio buttons with the same name that need to be unchecked
- dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
- function(inputNode){
- if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
- var widget = dijit.getEnclosingWidget(inputNode);
- if(widget && widget.checked){
- widget.set('checked', false);
+ // 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);
}
}
}
- );
- }
- },
-
- _clicked: function(/*Event*/ e){
- if(!this.checked){
- this.set('checked', true);
+ }
}
}
- }
-);
-
-}
-
-if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.DropDownButton"] = true;
-dojo.provide("dijit.form.DropDownButton");
-
-
-
-
-}
-
-if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.regexp"] = true;
-dojo.provide("dojo.regexp");
-
-dojo.getObject("regexp", true, dojo);
+ },
-/*=====
-dojo.regexp = {
- // summary: Regular expressions and Builder resources
-};
-=====*/
+ _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.
-dojo.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
+ //Stub function, does nothing. Real processing is in ItemFileWriteStore.
+ },
- return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
- if(except && except.indexOf(ch) != -1){
- return ch;
+ getIdentity: function(/* 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 "\\" + ch;
- }); // String
-};
-
-dojo.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 dojo.regexp.group(b.join("|"), nonCapture); // String
-};
-
-dojo.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 null; // null
+ },
-}
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
-if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.util.sorter"] = true;
-dojo.provide("dojo.data.util.sorter");
+ // 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("dojo.data.ItemFileReadStore: ",
+ "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;
+ }
-dojo.getObject("data.util.sorter", true, dojo);
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
-dojo.data.util.sorter.basicComparator = function( /*anything*/ a,
- /*anything*/ b){
- // summary:
- // Basic comparision 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}
-};
+ if(this._jsonFileUrl){
-dojo.data.util.sorter.createSortFunction = function( /* attributes array */sortSpec,
- /*dojo.data.core.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: array
- // 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: object
- // The datastore object to look up item values from.
- //
- var sortFunctions=[];
+ 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:window.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:window.global;
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
- 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 = dojo.data.util.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();
+ }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:window.global;
+ keywordArgs.onItem.call(scope, item);
}
- 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
+ }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:window.global;
+ keywordArgs.onItem.call(scope, item);
}
}
- return 0; //int
- }; // Function
-};
-
-}
-
-if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.util.simpleFetch"] = true;
-dojo.provide("dojo.data.util.simpleFetch");
-
-
-dojo.getObject("data.util.simpleFetch", true, dojo);
-
-dojo.data.util.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.
- // 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 = request || {};
- if(!request.store){
- request.store = this;
- }
- var self = this;
-
- var _errorHandler = function(errorData, requestObject){
- if(requestObject.onError){
- var scope = requestObject.scope || dojo.global;
- requestObject.onError.call(scope, errorData, requestObject);
- }
- };
-
- var _fetchHandler = function(items, requestObject){
- var oldAbortFunction = requestObject.abort || null;
- var aborted = false;
-
- var startIndex = requestObject.start?requestObject.start:0;
- var 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 || dojo.global;
- if(!requestObject.store){
- requestObject.store = self;
- }
- if(requestObject.onBegin){
- requestObject.onBegin.call(scope, items.length, requestObject);
- }
- if(requestObject.sort){
- items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
- }
- 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);
- }
+ _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(requestObject.onComplete && !aborted){
- var subset = null;
- if(!requestObject.onItem){
- subset = items.slice(startIndex, endIndex);
- }
- requestObject.onComplete.call(scope, subset, requestObject);
+ if(item === undefined){
+ item = null;
}
- };
- this._fetchItems(request, _fetchHandler, _errorHandler);
- return request; // Object
-};
+ return item; // Object
+ },
-}
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
-if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.util.filter"] = true;
-dojo.provide("dojo.data.util.filter");
+ 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
+ }
+ },
-dojo.getObject("data.util.filter", true, dojo);
+ _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("dojo.data.ItemFileReadStore: ",
+ "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;
+ }
-dojo.data.util.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.
+ //See if there was any forced reset of data.
+ if(this.data != null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
- 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;
+ 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("dojo.data.ItemFileReadStore: 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;
}
}
- rxp += "$";
- if(ignoreCase){
- return new RegExp(rxp,"mi"); //RegExp
- }else{
- return new RegExp(rxp,"m"); //RegExp
- }
-
-};
+});
+//Mix in the simple fetch implementation to this class.
+lang.extend(ItemFileReadStore,simpleFetch);
-}
+return ItemFileReadStore;
+});
-if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.TextBox"] = true;
-dojo.provide("dijit.form.TextBox");
+},
+'dojo/html':function(){
+define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"], function(dojo, lang, darray, declare, dom, domConstruct, parser) {
+ // module:
+ // dojo/html
+ // summary:
+ // TODOC
+ lang.getObject("html", true, dojo);
+ // the parser might be needed..
-dojo.declare(
- "dijit.form.TextBox",
- dijit.form._FormValueWidget,
- {
+ // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
+ var idCounter = 0;
+
+ dojo.html._secureForInnerHtml = function(/*String*/ cont){
// summary:
- // A base class for textbox form inputs
+ // 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
+ };
- // trim: Boolean
- // Removes leading and trailing whitespace if true. Default is false.
- trim: false,
+/*====
+ dojo.html._emptyNode = function(node){
+ // summary:
+ // removes all child nodes from the given node
+ // node: DOMNode
+ // the parent element
+ };
+=====*/
+ dojo.html._emptyNode = domConstruct.empty;
- // uppercase: Boolean
- // Converts all characters to uppercase if true. Default is false.
- uppercase: false,
+ dojo.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
- // lowercase: Boolean
- // Converts all characters to lowercase if true. Default is false.
- lowercase: false,
+ // always empty
+ domConstruct.empty(node);
- // propercase: Boolean
- // Converts the first character of each word to uppercase if true.
- propercase: false,
+ 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");
+ }
+ }
- // maxLength: String
- // HTML INPUT tag maxLength declaration.
- maxLength: "",
+ // return DomNode
+ return node;
+ };
- // selectOnClick: [const] Boolean
- // If true, all text will be selected when focused with mouse
- selectOnClick: false,
+ // we wrap up the content-setting operation in a object
+ declare("dojo.html._ContentSetter", null,
+ {
+ // node: DomNode|String
+ // An node which will be the parent element that we set content into
+ node: "",
- // 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: "",
-
- templateString: dojo.cache("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\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
- _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
+ // 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: "",
- _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
+ // id: String?
+ // Usually only used internally, and auto-generated with each instance
+ id: "",
- baseClass: "dijitTextBox",
+ // 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,
- attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
- maxLength: "focusNode"
- }),
-
- postMixInProperties: function(){
- var type = this.type.toLowerCase();
- if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
- this.templateString = this._singleNodeTemplate;
- }
- this.inherited(arguments);
- },
+ // 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,
- _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 = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
- }
- this._phspan.innerHTML="";
- this._phspan.appendChild(document.createTextNode(v));
-
- this._updatePlaceHolder();
- },
-
- _updatePlaceHolder: function(){
- if(this._phspan){
- this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
- }
- },
+ // parseContent: Boolean
+ // Should the node by passed to the parser after the new content is set
+ parseContent: false,
- _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);
- },
+ // 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: dojo._scopeName,
- _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.
+ // startup: Boolean
+ // Start the child widgets after parsing them. Only obeyed if parseContent is true.
+ startup: true,
- 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 && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
- this.textbox.value = formattedValue;
- this._set("displayedValue", this.get("displayedValue"));
- }
+ // lifecyle 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..
- this._updatePlaceHolder();
+ // the original params are mixed directly into the instance "this"
+ lang.mixin(this, params || {});
- this.inherited(arguments, [filteredValue, priorityChange]);
- },
+ // 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 );
- // 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: "",
+ 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);
+ }
- getDisplayedValue: function(){
- // summary:
- // Deprecated. Use get('displayedValue') instead.
- // tags:
- // deprecated
- dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
- return this.get('displayedValue');
- },
+ this.onBegin();
+ this.setContent();
+ this.onEnd();
- _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)
+ return this.node;
+ },
+ setContent: function(){
+ // summary:
+ // sets the content on the node
- // 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);
- },
+ var node = this.node;
+ if(!node) {
+ // can't proceed
+ throw new Error(this.declaredClass + ": setContent given no node");
+ }
+ try{
+ node = dojo.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
- setDisplayedValue: function(/*String*/ value){
- // summary:
- // Deprecated. Use set('displayedValue', ...) instead.
- // tags:
- // deprecated
- dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
- this.set('displayedValue', value);
- },
+ // 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
+ },
- _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.
+ empty: function() {
+ // summary
+ // cleanly empty out existing content
- if(value === null || value === undefined){ value = '' }
- else if(typeof value != "string"){ value = String(value) }
+ // destroy any widgets from a previous run
+ // NOTE: if you dont want this you'll need to empty
+ // the parseResults array property yourself to avoid bad things happenning
+ 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
+ dojo.html._emptyNode(this.node);
+ },
- this.textbox.value = value;
+ 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;
- // 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);
+ if(lang.isString(cont)){
+ if(this.cleanContent){
+ cont = dojo.html._secureForInnerHtml(cont);
+ }
- this._set("displayedValue", this.get('displayedValue'));
- },
+ if(this.extractContent){
+ var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+ if(match){ cont = match[1]; }
+ }
+ }
- format: function(/*String*/ value, /*Object*/ constraints){
- // summary:
- // Replacable function to convert a value to a properly formatted string.
- // tags:
- // protected extension
- return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
- },
+ // clean out the node and any cruft associated with it - like widgets
+ this.empty();
- parse: function(/*String*/ value, /*Object*/ constraints){
- // summary:
- // Replacable function to convert a formatted string to a value
- // tags:
- // protected extension
+ this.content = cont;
+ return this.node; /* DomNode */
+ },
- return value; // String
- },
+ 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 if you need those..
+ this._parse();
+ }
+ return this.node; /* DomNode */
+ },
- _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
- },
+ 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.node;
+ delete this.content;
+ },
- _onInput: function(e){
- if(e && e.type && /key/i.test(e.type) && e.keyCode){
- switch(e.keyCode){
- case dojo.keys.SHIFT:
- case dojo.keys.ALT:
- case dojo.keys.CTRL:
- case dojo.keys.TAB:
- return;
- }
- }
- if(this.intermediateChanges){
- var _this = this;
- // the setTimeout allows the key to post to the widget input box
- setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
- }
- this._refreshState();
+ onContentError: function(err){
+ return "Error occured setting content: " + err;
+ },
- // In case someone is watch()'ing for changes to displayedValue
- this._set("displayedValue", this.get("displayedValue"));
- },
+ _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 dont 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
+ // Any errors resulting from parsing are passed to _onError for handling
- postCreate: function(){
- if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
- // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
- setTimeout(dojo.hitch(this, function(){
- var s = dojo.getComputedStyle(this.domNode);
- 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;
- }
+ 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);
+ this.parseResults = parser.parse({
+ rootNode: rootNode,
+ noStart: !this.startup,
+ inherited: inherited,
+ scope: this.parserScope
+ });
+ }catch(e){
+ this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
}
- }), 0);
- }
-
- // 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);
+ },
- if(dojo.isMoz || dojo.isOpera){
- this.connect(this.textbox, "oninput", "_onInput");
- }else{
- this.connect(this.textbox, "onkeydown", "_onInput");
- this.connect(this.textbox, "onkeyup", "_onInput");
- this.connect(this.textbox, "onpaste", "_onInput");
- this.connect(this.textbox, "oncut", "_onInput");
+ _onError: function(type, err, consoleText){
+ // summary:
+ // shows user the string that is returned by on[type]Error
+ // overide/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
+ dojo.html._setNodeContent(this.node, errText, true);
+ }
}
- },
+ }); // end dojo.declare()
- _blankValue: '', // if the textbox is blank, what value should be reported
- filter: function(val){
+ dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
// summary:
- // Auto-corrections (such as trimming) that are applied to textbox
- // value on blur or form submit.
+ // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
+ // may be a better choice for simple HTML insertion.
// 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 = dojo.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;
- },
+ // 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:
+ // dojo.html.set(node, "some string");
+ // dojo.html.set(node, contentNode, {options});
+ // dojo.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 dojo.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 dojo.html._ContentSetter(lang.mixin(
+ params,
+ { content: cont, node: node }
+ ));
+ return op.set();
+ }
+ };
- _setBlurValue: function(){
- this._setValueAttr(this.get('value'), true);
- },
+ return dojo.html;
+});
- _onBlur: function(e){
- if(this.disabled){ return; }
- this._setBlurValue();
- this.inherited(arguments);
+},
+'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){
- if(this._selectOnClickHandle){
- this.disconnect(this._selectOnClickHandle);
- }
- if(this.selectOnClick && dojo.isMoz){
- this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
- }
-
- this._updatePlaceHolder();
- },
+/*=====
+ var _CssStateMixin = dijit._CssStateMixin;
+=====*/
- _onFocus: function(/*String*/ by){
- if(this.disabled || this.readOnly){ return; }
+// module:
+// dijit/_PaletteMixin
+// summary:
+// A keyboard accessible palette, for picking a color/emoticon/etc.
- // 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);
-
- // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
- // and if not, then select all the text
- var textIsNotSelected;
- if(dojo.isIE){
- var range = dojo.doc.selection.createRange();
- var parent = range.parentElement();
- textIsNotSelected = parent == this.textbox && range.text.length == 0;
- }else{
- textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
- }
- if(textIsNotSelected){
- dijit.selectInputText(this.textbox);
- }
- });
- }
+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.
- this._updatePlaceHolder();
-
- // 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);
+ // defaultTimeout: Number
+ // Number of milliseconds before a held key or button becomes typematic
+ defaultTimeout: 500,
- this._refreshState();
- },
+ // timeoutChangeRate: Number
+ // Fraction of time used to change the typematic timer between events
+ // 1.0 means that each typematic event fires at defaultTimeout intervals
+ // < 1.0 means that each typematic event fires at an increasing faster rate
+ timeoutChangeRate: 0.90,
- reset: function(){
- // Overrides dijit._FormWidget.reset().
- // Additionally resets the displayed textbox value to ''
- this.textbox.value = '';
- this.inherited(arguments);
- }
- }
-);
+ // value: String
+ // Currently selected color/emoticon/etc.
+ value: "",
-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).
+ // _selectedCell: [private] Integer
+ // Index of the currently selected cell. Initially, none selected
+ _selectedCell: -1,
- // TODO: use functions in _editor/selection.js?
- var _window = dojo.global;
- var _document = dojo.doc;
- element = dojo.byId(element);
- if(isNaN(start)){ start = 0; }
- if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
- dijit.focus(element);
- if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
- 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();
- }
- }else if(_window["getSelection"]){
- if(element.setSelectionRange){
- element.setSelectionRange(start, stop);
- }
- }
-};
+/*=====
+ // _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,
+=====*/
-if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.Tooltip"] = true;
-dojo.provide("dijit.Tooltip");
+/*=====
+ // _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] String
+ // Name of javascript class for Object created for each cell of the palette.
+ // dyeClass should implements dijit.Dye interface
+ dyeClass: '',
+
+ // summary: String
+ // Localized summary for the palette table
+ summary: '',
+ _setSummaryAttr: "paletteTableNode",
-dojo.declare(
- "dijit._MasterTooltip",
- [dijit._Widget, dijit._Templated],
- {
+ _dyeFactory: function(value /*===== , row, col =====*/){
// 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
+ // Return instance of dijit.Dye for specified cell of palette
// tags:
- // protected
-
- // duration: Integer
- // Milliseconds to fade in/fade out
- duration: dijit.defaultDuration,
-
- templateString: dojo.cache("dijit", "templates/Tooltip.html", "<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" dojoAttachPoint=\"connectorNode\"></div\n></div>\n"),
-
- postCreate: function(){
- dojo.body().appendChild(this.domNode);
-
- this.bgIframe = new dijit.BackgroundIframe(this.domNode);
+ // extension
+ var dyeClassObj = lang.getObject(this.dyeClass);
+ return new dyeClassObj(value);
+ },
- // Setup fade-in and fade-out functions.
- this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
- this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
- },
+ _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
- show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
- // 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)
+ this._cells = [];
+ var url = this._blankGif;
- if(this.aroundNode && this.aroundNode === aroundNode){
- return;
- }
+ this.connect(this.gridNode, "ondijitclick", "_onCellClick");
- // reset width; it may have been set by orient() on a previous tooltip show()
- this.domNode.width = "auto";
+ 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);
- 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;
+ var cellNode = domConstruct.create("td", {
+ "class": this.cellClass,
+ tabIndex: "-1",
+ title: titles[value],
+ role: "gridcell"
+ });
- var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
+ // prepare cell inner structure
+ cellObject.fillCell(cellNode, url);
- // show it
- dojo.style(this.domNode, "opacity", 0);
- this.fadeIn.play();
- this.isShowingNow = true;
- this.aroundNode = aroundNode;
- },
+ domConstruct.place(cellNode, rowNode);
- 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
-
- //Adjust the spaceAvailable width, without changing the spaceAvailable object
- var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
+ cellNode.index = this._cells.length;
- node.className = "dijitTooltip " +
- {
- "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
- "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
- "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
- "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
- "BR-BL": "dijitTooltipRight",
- "BL-BR": "dijitTooltipLeft"
- }[aroundCorner + "-" + tooltipCorner];
-
- // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
- this.domNode.style.width = "auto";
- var size = dojo.contentBox(this.domNode);
-
- var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
- var widthWasReduced = width < size.w;
-
- this.domNode.style.width = width+"px";
-
- //Adjust width for tooltips that have a really long word or a nowrap setting
- if(widthWasReduced){
- this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
- var scrollWidth = this.containerNode.scrollWidth;
- this.containerNode.style.overflow = "visible"; //change it back
- if(scrollWidth > width){
- scrollWidth = scrollWidth + dojo.style(this.domNode,"paddingLeft") + dojo.style(this.domNode,"paddingRight");
- this.domNode.style.width = scrollWidth + "px";
- }
- }
-
- // Reposition the tooltip connector.
- if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
- var mb = dojo.marginBox(node);
- var tooltipConnectorHeight = this.connectorNode.offsetHeight;
- if(mb.h > spaceAvailable.h){
- // The tooltip starts at the top of the page and will extend past the aroundNode
- var aroundNodePlacement = spaceAvailable.h - (aroundNodeCoords.h / 2) - (tooltipConnectorHeight / 2);
- 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),
- mb.h - tooltipConnectorHeight) + "px";
- this.connectorNode.style.top = "";
+ // save cell info into _cells
+ this._cells.push({node:cellNode, dye:cellObject});
}
- }else{
- // reset the tooltip back to the defaults
- this.connectorNode.style.top = "";
- this.connectorNode.style.bottom = "";
- }
-
- return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
- },
-
- _onShow: function(){
- // summary:
- // Called at end of fade-in operation
- // tags:
- // protected
- if(dojo.isIE){
- // 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
}
- },
+ }
+ this._xDim = choices[0].length;
+ this._yDim = choices.length;
- _onHide: function(){
- // summary:
- // Called at end of fade-out operation
- // tags:
- // protected
+ // 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.
- 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;
- }
+ 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._connects.push(
+ 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
+ )
+ );
}
+ },
- }
-);
-
-dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
- // summary:
- // 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.
- if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
- return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
-};
+ postCreate: function(){
+ this.inherited(arguments);
-dijit.hideTooltip = function(aroundNode){
- // summary:
- // Hide the tooltip
- if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
- return dijit._masterTT.hide(aroundNode);
-};
+ // Set initial navigable node.
+ this._setCurrent(this._cells[0].node);
+ },
-dojo.declare(
- "dijit.Tooltip",
- dijit._Widget,
- {
+ focus: function(){
// summary:
- // Pops up a tooltip (a help message) when you hover over a node.
-
- // 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[]
- // Id of domNode(s) to attach the tooltip to.
- // When user hovers over specified dom node, the tooltip will appear.
- connectId: [],
-
- // position: String[]
- // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
- position: [],
-
- _setConnectIdAttr: function(/*String*/ newId){
- // summary:
- // Connect to node(s) (specified by id)
-
- // Remove connections to old nodes (if there are any)
- dojo.forEach(this._connections || [], function(nested){
- dojo.forEach(nested, dojo.hitch(this, "disconnect"));
- }, this);
-
- // Make connections to nodes in newIds.
- var ary = dojo.isArrayLike(newId) ? newId : (newId ? [newId] : []);
- this._connections = dojo.map(ary, function(id){
- var node = dojo.byId(id);
- return node ? [
- this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
- this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
- this.connect(node, "onfocus", "_onTargetFocus"),
- this.connect(node, "onblur", "_onTargetBlur")
- ] : [];
- }, this);
-
- this._set("connectId", newId);
+ // Focus this widget. Puts focus on the most recently focused cell.
- this._connectIds = ary; // save as array
- },
+ // The cell already has tabIndex set, just need to set CSS and focus it
+ focus.focus(this._currentFocus);
+ },
- addTarget: function(/*DOMNODE || String*/ node){
- // summary:
- // Attach tooltip to specified node if it's not already connected
+ _onCellClick: function(/*Event*/ evt){
+ // summary:
+ // Handler for click, enter key & space key. Selects the cell.
+ // evt:
+ // The event.
+ // tags:
+ // private
- // TODO: remove in 2.0 and just use set("connectId", ...) interface
+ var target = evt.target;
- var id = node.id || node;
- if(dojo.indexOf(this._connectIds, id) == -1){
- this.set("connectId", this._connectIds.concat(id));
+ // 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;
+ }
- removeTarget: function(/*DOMNODE || String*/ node){
- // summary:
- // Detach tooltip from specified node
+ var value = this._getDye(target).getValue();
- // 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 = dojo.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);
- }
- },
+ // 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);
- buildRendering: function(){
- this.inherited(arguments);
- dojo.addClass(this.domNode,"dijitTooltipData");
- },
+ event.stop(evt);
+ },
- startup: function(){
- this.inherited(arguments);
+ _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");
+ }
- // 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;
- dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
- },
+ // Set tabIndex of new cell
+ this._currentFocus = node;
+ if(node){
+ domAttr.set(node, "tabIndex", this.tabIndex);
+ }
+ },
- _onTargetMouseEnter: function(/*Event*/ e){
- // summary:
- // Handler for mouseenter event on the target node
- // tags:
- // private
- this._onHover(e);
- },
+ _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:
+ // Optional parameter used to tell the select whether or not to fire
+ // onChange event.
- _onTargetMouseLeave: function(/*Event*/ e){
- // summary:
- // Handler for mouseleave event on the target node
- // tags:
- // private
- this._onUnHover(e);
- },
+ // clear old selected cell
+ if(this._selectedCell >= 0){
+ domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
+ }
+ this._selectedCell = -1;
- _onTargetFocus: function(/*Event*/ e){
- // summary:
- // Handler for focus event on the target node
- // tags:
- // private
+ // 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;
+ }
+ }
+ }
- this._focus = true;
- this._onHover(e);
- },
+ // record new value, or null if no matching cell
+ this._set("value", this._selectedCell >= 0 ? value : null);
- _onTargetBlur: function(/*Event*/ e){
- // summary:
- // Handler for blur event on the target node
- // tags:
- // private
+ if(priorityChange || priorityChange === undefined){
+ this.onChange(value);
+ }
+ },
- this._focus = false;
- this._onUnHover(e);
- },
+ onChange: function(/*===== value =====*/){
+ // summary:
+ // Callback when a cell is selected.
+ // value: String
+ // Value corresponding to cell.
+ },
- _onHover: function(/*Event*/ e){
- // 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){
- var target = e.target;
- this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
- }
- },
+ _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
- _onUnHover: function(/*Event*/ e){
- // summary:
- // Despite the name of this method, it actually handles both mouseleave and blur
- // events on the target node, hiding the tooltip.
- // tags:
- // private
+ // typecount == -1 means the key is released.
+ if(typeCount == -1){ return; }
- // keep a tooltip open if the associated element still has focus (even though the
- // mouse moved away)
- if(this._focus){ return; }
+ var newFocusIndex = this._currentFocus.index + increment;
+ if(newFocusIndex < this._cells.length && newFocusIndex > -1){
+ var focusNode = this._cells[newFocusIndex].node;
+ this._setCurrent(focusNode);
- if(this._showTimer){
- clearTimeout(this._showTimer);
- delete this._showTimer;
- }
- this.close();
- },
+ // Actually focus the node, for the benefit of screen readers.
+ // Use setTimeout because IE doesn't like changing focus inside of an event handler
+ setTimeout(lang.hitch(dijit, "focus", focusNode), 0);
+ }
+ },
- open: function(/*DomNode*/ target){
- // summary:
- // Display the tooltip; usually not called directly.
- // tags:
- // private
+ _getDye: function(/*DomNode*/ cell){
+ // summary:
+ // Get JS object for given cell DOMNode
- if(this._showTimer){
- clearTimeout(this._showTimer);
- delete this._showTimer;
- }
- dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
+ return this._cells[cell.index].dye;
+ }
+});
- this._connectNode = target;
- this.onShow(target, this.position);
- },
+/*=====
+declare("dijit.Dye",
+ null,
+ {
+ // summary:
+ // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
- close: function(){
+ constructor: function(alias, row, col){
// summary:
- // Hide the tooltip or cancel timer for show of tooltip
- // tags:
- // private
-
- if(this._connectNode){
- // if tooltip is currently shown
- dijit.hideTooltip(this._connectNode);
- delete this._connectNode;
- this.onHide();
- }
- if(this._showTimer){
- // if tooltip is scheduled to be shown (after a brief delay)
- clearTimeout(this._showTimer);
- delete this._showTimer;
- }
+ // Initialize according to value or alias like "white"
+ // alias: String
},
- onShow: function(target, position){
+ getValue: function(){
// summary:
- // Called when the tooltip is shown
- // tags:
- // callback
+ // Return "value" of cell; meaning of "value" varies by subclass.
+ // description:
+ // For example color hex value, emoticon ascii value etc, entity hex value.
},
- onHide: function(){
+ fillCell: function(cell, blankGif){
// summary:
- // Called when the tooltip is hidden
- // tags:
- // callback
- },
-
- uninitialize: function(){
- this.close();
- this.inherited(arguments);
+ // Add cell DOMNode inner structure
+ // cell: DomNode
+ // The surrounding cell
+ // blankGif: String
+ // URL for blank cell image
}
}
);
+=====*/
-// dijit.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 following values:
-//
-// * before: places tooltip to the left of the target node/widget, or to the right in
-// the case of RTL scripts like Hebrew and Arabic
-// * after: places tooltip to the right of the target node/widget, or to the left in
-// the case of RTL scripts like Hebrew and Arabic
-// * above: tooltip goes above target node
-// * below: tooltip goes below target 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" 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.
-dijit.Tooltip.defaultPosition = ["after", "before"];
-
-}
-
-if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.ValidationTextBox"] = true;
-dojo.provide("dijit.form.ValidationTextBox");
-
+});
+},
+'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/i18n", // i18n.getLocalization
+ "./TextBox",
+ "../Tooltip",
+ "dojo/text!./templates/ValidationTextBox.html",
+ "dojo/i18n!./nls/validate"
+], function(declare, i18n, TextBox, Tooltip, template){
+/*=====
+ var Tooltip = dijit.Tooltip;
+ var TextBox = dijit.form.TextBox;
+=====*/
+ // module:
+ // dijit/form/ValidationTextBox
+ // summary:
+ // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
-/*=====
- dijit.form.ValidationTextBox.__Constraints = function(){
- // locale: String
- // locale used for validation, picks up value from this widget's lang attribute
- // _flags_: anything
- // various flags passed to regExpGen function
- this.locale = "";
- this._flags_ = "";
- }
-=====*/
+ /*=====
+ dijit.form.ValidationTextBox.__Constraints = function(){
+ // locale: String
+ // locale used for validation, picks up value from this widget's lang attribute
+ // _flags_: anything
+ // various flags passed to regExpGen function
+ this.locale = "";
+ this._flags_ = "";
+ }
+ =====*/
-dojo.declare(
- "dijit.form.ValidationTextBox",
- dijit.form.TextBox,
- {
+ return declare("dijit.form.ValidationTextBox", TextBox, {
// summary:
// Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
// tags:
// protected
- templateString: dojo.cache("dijit.form", "templates/ValidationTextBox.html", "<div class=\"dijit dijitReset dijitInlineTable 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\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
+ templateString: template,
baseClass: "dijitTextBox dijitValidationTextBox",
// required: Boolean
@@ -13584,7 +23102,7 @@ dojo.declare(
// Do not specify both regExp and regExpGen
regExp: ".*",
- regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ constraints){
+ regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ /*===== constraints =====*/){
// summary:
// Overridable function used to generate regExp when dependent on constraints.
// Do not specify both regExp and regExpGen.
@@ -13605,7 +23123,7 @@ dojo.declare(
// summary:
// Hook so set('value', ...) works.
this.inherited(arguments);
- this.validate(this._focused);
+ this.validate(this.focused);
},
validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
@@ -13625,7 +23143,7 @@ dojo.declare(
return this.textbox.value.search(this._partialre) == 0;
},
- isValid: function(/*Boolean*/ isFocused){
+ isValid: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Tests if value is valid.
// Can override with your own routine in a subclass.
@@ -13640,7 +23158,7 @@ dojo.declare(
return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
},
- getErrorMessage: function(/*Boolean*/ isFocused){
+ getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return an error message to show if appropriate
// tags:
@@ -13648,7 +23166,7 @@ dojo.declare(
return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
},
- getPromptMessage: function(/*Boolean*/ isFocused){
+ getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return a hint message to show when widget is first focused
// tags:
@@ -13670,7 +23188,7 @@ dojo.declare(
var isEmpty = this._isEmpty(this.textbox.value);
var isValidSubset = !isValid && isFocused && this._isValidSubset();
this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
- dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
+ 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
@@ -13692,15 +23210,16 @@ dojo.declare(
// By default uses a tooltip.
// tags:
// extension
- dijit.hideTooltip(this.domNode);
- if(message && this._focused){
- dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
+ if(message && this.focused){
+ Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
+ }else{
+ Tooltip.hide(this.domNode);
}
},
_refreshState: function(){
// Overrides TextBox._refreshState()
- this.validate(this._focused);
+ this.validate(this.focused);
this.inherited(arguments);
},
@@ -13725,7 +23244,7 @@ dojo.declare(
// 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 != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
- function (re){
+ function(re){
switch(re.charAt(0)){
case '{':
case '+':
@@ -13757,7 +23276,7 @@ dojo.declare(
postMixInProperties: function(){
this.inherited(arguments);
- this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
+ this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
@@ -13772,7 +23291,7 @@ dojo.declare(
_setRequiredAttr: function(/*Boolean*/ value){
this._set("required", value);
- dijit.setWaiState(this.focusNode, "required", value);
+ this.focusNode.setAttribute("aria-required", value);
this._refreshState();
},
@@ -13795,1438 +23314,1696 @@ dojo.declare(
this.inherited(arguments);
}
- }
-);
-
-dojo.declare(
- "dijit.form.MappedTextBox",
- dijit.form.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);
+},
+'dijit/_base/typematic':function(){
+define("dijit/_base/typematic", ["../typematic"], function(){
+ // for back-compat, just loads top level module
+});
- // we want the name attribute to go to the hidden <input>, not the displayed <input>,
- // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
- this.nameAttrSetting = "";
- },
+},
+'dijit/_base':function(){
+define("dijit/_base", [
+ ".",
+ "./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
+ // summary:
+ // Includes all the modules in dijit/_base
- serialize: function(/*anything*/ val, /*Object?*/ 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.
- // tags:
- // protected extension
- return val.toString ? val.toString() : ""; // String
- },
+ return dijit._base;
+});
- 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
- },
+},
+'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",
+ "dojo/_base/window", // win.body win.doc win.doc.createElement
+ "../_WidgetBase",
+ "../_Widget",
+ "../_TemplatedMixin",
+ "./_LayoutWidget",
+ "./utils" // layoutUtils.layoutChildren
+], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch, win,
+ _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
- validate: function(){
- // Overrides `dijit.form.TextBox.validate`
- this.valueNode.value = this.toString();
- return this.inherited(arguments);
- },
+/*=====
+ var _WidgetBase = dijit._WidgetBase;
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _LayoutWidget = dijit.layout._LayoutWidget;
+=====*/
- buildRendering: function(){
- // Overrides `dijit._Templated.buildRendering`
+// module:
+// dijit/layout/BorderContainer
+// summary:
+// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
- this.inherited(arguments);
+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
- // 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 dojo.create() with an attrs argument
- // to make dojo.query(input[name=...]) work on IE. (see #8660)
- this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
- },
+/*=====
+ // container: [const] dijit.layout.BorderContainer
+ // Pointer to the parent BorderContainer
+ container: null,
- reset: function(){
- // Overrides `dijit.form.ValidationTextBox.reset` to
- // reset the hidden textbox value to ''
- this.valueNode.value = '';
- this.inherited(arguments);
- }
- }
-);
+ // child: [const] dijit.layout._LayoutWidget
+ // Pointer to the pane associated with this splitter
+ child: null,
-/*=====
- dijit.form.RangeBoundTextBox.__Constraints = function(){
- // min: Number
- // Minimum signed value. Default is -Infinity
- // max: Number
- // Maximum signed value. Default is +Infinity
- this.min = min;
- this.max = max;
- }
+ // region: [const] String
+ // Region of pane associated with this splitter.
+ // "top", "bottom", "left", "right".
+ region: null,
=====*/
-dojo.declare(
- "dijit.form.RangeBoundTextBox",
- dijit.form.MappedTextBox,
- {
- // summary:
- // Base class for textbox form widgets which defines a range of valid values.
+ // 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,
- // rangeMessage: String
- // The message to display if value is out-of-range
- rangeMessage: "",
+ 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>',
- /*=====
- // constraints: dijit.form.RangeBoundTextBox.__Constraints
- constraints: {},
- ======*/
+ constructor: function(){
+ this._handlers = [];
+ },
- rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
- // summary:
- // Overridable function used to validate the range of the numeric input value.
- // tags:
- // protected
- return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
- ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
- },
+ postMixInProperties: function(){
+ this.inherited(arguments);
- isInRange: function(/*Boolean*/ isFocused){
- // summary:
- // Tests if the value is in the min/max range specified in constraints
- // tags:
- // protected
- return this.rangeCheck(this.get('value'), this.constraints);
- },
+ this.horizontal = /top|bottom/.test(this.region);
+ this._factor = /top|left/.test(this.region) ? 1 : -1;
+ this._cookieName = this.container.id + "_" + this.region;
+ },
- _isDefinitelyOutOfRange: function(){
- // summary:
- // Returns true if the value is out of range and will remain
- // out of range even if the user types more characters
- var val = this.get('value');
- var isTooLittle = false;
- var isTooMuch = false;
- if("min" in this.constraints){
- var min = this.constraints.min;
- min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
- isTooLittle = (typeof min == "number") && min < 0;
- }
- if("max" in this.constraints){
- var max = this.constraints.max;
- max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
- isTooMuch = (typeof max == "number") && max > 0;
+ 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;
}
- return isTooLittle || isTooMuch;
- },
+ }
+ },
- _isValidSubset: function(){
- // summary:
- // Overrides `dijit.form.ValidationTextBox._isValidSubset`.
- // Returns true if the input is syntactically valid, and either within
- // range or could be made in range by more typing.
- return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
- },
+ _computeMaxSize: function(){
+ // summary:
+ // Return the maximum size that my corresponding pane can be set to
- isValid: function(/*Boolean*/ isFocused){
- // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
- return this.inherited(arguments) &&
- ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
- },
+ 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
- getErrorMessage: function(/*Boolean*/ isFocused){
- // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
- var v = this.get('value');
- if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
- return this.rangeMessage; // String
- }
- return this.inherited(arguments);
- },
+ return Math.min(this.child.maxSize, childSize + spaceAvailable);
+ },
- postMixInProperties: function(){
- this.inherited(arguments);
- if(!this.rangeMessage){
- this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
- this.rangeMessage = this.messages.rangeMessage;
- }
- },
+ _startDrag: function(e){
+ if(!this.cover){
+ this.cover = win.doc.createElement('div');
+ domClass.add(this.cover, "dijitSplitterCover");
+ domConstruct.place(this.cover, this.child.domNode, "after");
+ }
+ domClass.add(this.cover, "dijitSplitterCoverActive");
- _setConstraintsAttr: function(/*Object*/ constraints){
- this.inherited(arguments);
- if(this.focusNode){ // not set when called from postMixInProperties
- if(this.constraints.min !== undefined){
- dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
- }else{
- dijit.removeWaiState(this.focusNode, "valuemin");
- }
- if(this.constraints.max !== undefined){
- dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
- }else{
- dijit.removeWaiState(this.focusNode, "valuemax");
+ // 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 = win.doc;
+
+ 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(win.body(), "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;
+ }
- _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
- // summary:
- // Hook so set('value', ...) works.
+ if(this.container.persist){
+ cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
+ }
+ },
- dijit.setWaiState(this.focusNode, "valuenow", value);
- this.inherited(arguments);
+ _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
-if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.ComboBox"] = true;
-dojo.provide("dijit.form.ComboBox");
+ 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.
+ // 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);
+ },
-dojo.declare(
- "dijit.form.ComboBoxMixin",
- dijit._HasDropDown,
- {
- // summary:
- // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
- // description:
- // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
- // tags:
- // protected
+ startup: function(){
+ if(this._started){ return; }
+ array.forEach(this.getChildren(), this._setupChild, this);
+ this.inherited(arguments);
+ },
- // item: Object
- // This is the item returned by the dojo.data.store implementation that
- // provides the data for this ComboBox, it's the currently selected item.
- item: null,
+ _setupChild: function(/*dijit._Widget*/ child){
+ // Override _LayoutWidget._setupChild().
- // pageSize: Integer
- // Argument to data provider.
- // Specifies number of search results per page (before hitting "next" button)
- pageSize: Infinity,
+ var region = child.region;
+ if(region){
+ this.inherited(arguments);
- // store: [const] Object
- // Reference to data provider object used by this ComboBox
- store: null,
+ domClass.add(child.domNode, this.baseClass+"Pane");
- // fetchProperties: Object
- // Mixin to the dojo.data 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:{},
+ var ltr = this.isLeftToRight();
+ if(region == "leading"){ region = ltr ? "left" : "right"; }
+ if(region == "trailing"){ region = ltr ? "right" : "left"; }
- // query: Object
- // A query that can be passed to 'store' to initially filter the items,
- // before doing further filtering based on `searchAttr` and the key.
- // Any reference to the `searchAttr` is ignored.
- query: {},
+ // 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;
- // 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,
+ domConstruct.place(splitter.domNode, child.domNode, "after");
- // 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",
+ // 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.
+ }
+ },
- // searchDelay: Integer
- // Delay in milliseconds between when user types something and we start
- // searching based on that value
- searchDelay: 100,
+ layout: function(){
+ // Implement _LayoutWidget.layout() virtual method.
+ this._layoutChildren();
+ },
- // searchAttr: String
- // Search for items in the data store where this attribute (in the item)
- // matches what the user typed
- searchAttr: "name",
+ addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
+ // Override _LayoutWidget.addChild().
+ this.inherited(arguments);
+ if(this._started){
+ this.layout(); //OPT
+ }
+ },
- // 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: "",
+ removeChild: function(/*dijit._Widget*/ child){
+ // Override _LayoutWidget.removeChild().
- // labelType: String
- // Specifies how to interpret the labelAttr in the data store items.
- // Can be "html" or "text".
- labelType: "text",
+ var region = child.region;
+ var splitter = child._splitterWidget;
+ if(splitter){
+ splitter.destroy();
+ delete child._splitterWidget;
+ }
+ this.inherited(arguments);
- // queryExpr: String
- // This specifies what query ComboBox/FilteringSelect sends to the data store,
- // based on what the user has typed. Changing this expression will modify
- // whether the drop down shows only exact matches, a "starting with" match,
- // etc. Use it in conjunction with highlightMatch.
- // 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}*",
+ 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");
+ },
- // ignoreCase: Boolean
- // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
- ignoreCase: true,
+ getChildren: function(){
+ // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
+ return array.filter(this.inherited(arguments), function(widget){
+ return !widget.isSplitter;
+ });
+ },
- // hasDownArrow: Boolean
- // Set this textbox to have a down arrow button, to display the drop down list.
- // Defaults to true.
- hasDownArrow: true,
+ // 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;
+ },
- templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\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\tdojoAttachPoint=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"),
+ resize: function(newSize, currentSize){
+ // Overrides _LayoutWidget.resize().
- baseClass: "dijitTextBox dijitComboBox",
+ // 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);
- // dropDownClass: [protected extension] String
- // Name of the dropdown widget class used to select a date/time.
- // Subclasses should specify this.
- dropDownClass: "dijit.form._ComboBoxMenu",
+ domStyle.set(node, "padding", "0px");
+ }
- // Set classes like dijitDownArrowButtonHover depending on
- // mouse action over button node
- cssStateNodes: {
- "_buttonNode": "dijitDownArrowButton"
- },
+ this.inherited(arguments);
+ },
- // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
- maxHeight: -1,
+ _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
- // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
- _stopClickEvents: false,
+ 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;
+ }
- _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(dojo.isIE){
- // in the case of a mouse click in a popup being handled,
- // then the dojo.doc.selection is not the textarea, but the popup
- // var r = dojo.doc.selection.createRange();
- // hack to get IE 6 to play nice. What a POS browser.
- var tr = dojo.doc.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.
+ // 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 pos;
- },
+ return 0;
+ });
- _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
- location = parseInt(location);
- dijit.selectInputText(element, location, location);
- },
+ // 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);
+ }
+ });
- _setDisabledAttr: function(/*Boolean*/ value){
- // Additional code to set disabled state of ComboBox node.
- // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
- this.inherited(arguments);
- dijit.setWaiState(this.domNode, "disabled", value);
- },
+ // 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
+ };
- _abortQuery: function(){
- // stop in-progress query
- if(this.searchTimer){
- clearTimeout(this.searchTimer);
- this.searchTimer = null;
- }
- if(this._fetchHandle){
- if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
- this._fetchHandle = null;
- }
- },
+ // Layout the children, possibly changing size due to a splitter drag
+ layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
+ changedChildId, changedChildSize);
+ },
- _onInput: function(/*Event*/ evt){
- // summary:
- // Handles paste events
- if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
- this.searchTimer = setTimeout(dojo.hitch(this, function(){
- this._onKey({charOrCode: 229}); // fake IME key to cause a search
- }), 100); // long delay that will probably be preempted by keyboard input
+ destroyRecursive: function(){
+ // Destroy splitters first, while getChildren() still works
+ array.forEach(this.getChildren(), function(child){
+ var splitter = child._splitterWidget;
+ if(splitter){
+ splitter.destroy();
}
- this.inherited(arguments);
- },
+ delete child._splitterWidget;
+ });
- _onKey: function(/*Event*/ evt){
- // summary:
- // Handles keyboard events
+ // Then destroy the real children, and myself
+ this.inherited(arguments);
+ }
+});
- var key = evt.charOrCode;
+// This argument can be specified for the children of a BorderContainer.
+// Since any widget can be specified as a LayoutContainer child, mix it
+// into the base widget class. (This is a hack, but it's effective.)
+lang.extend(_WidgetBase, {
+ // region: [const] String
+ // Parameter for children of `dijit.layout.BorderContainer`.
+ // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
+ // See the `dijit.layout.BorderContainer` description for details.
+ region: '',
- // except for cutting/pasting case - ctrl + x/v
- if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
- return; // throw out weird key combinations and spurious events
- }
-
- var doSearch = false;
- var pw = this.dropDown;
- var dk = dojo.keys;
- var highlighted = null;
- this._prev_key_backspace = false;
- this._abortQuery();
+ // layoutPriority: [const] Number
+ // Parameter for children of `dijit.layout.BorderContainer`.
+ // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
+ // between children with a lower layoutPriority.
+ layoutPriority: 0,
- // _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);
+ // splitter: [const] Boolean
+ // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
+ // If true, enables user to resize the widget by putting a draggable splitter between
+ // this widget and the region=center widget.
+ splitter: false,
- if(this._opened){
- highlighted = pw.getHighlightedOption();
- }
- switch(key){
- case dk.PAGE_DOWN:
- case dk.DOWN_ARROW:
- case dk.PAGE_UP:
- case dk.UP_ARROW:
- // Keystroke caused ComboBox_menu to move to a different item.
- // Copy new item to <input> box.
- if(this._opened){
- this._announceOption(highlighted);
- }
- dojo.stopEvent(evt);
- break;
+ // minSize: [const] Number
+ // Parameter for children of `dijit.layout.BorderContainer`.
+ // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
+ minSize: 0,
- case dk.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);
- dojo.stopEvent(evt);
- break;
- }else if(highlighted == pw.previousButton){
- this._nextSearch(-1);
- dojo.stopEvent(evt);
- break;
- }
- }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
- }
- // default case:
- // if enter pressed while drop down is open, or for FilteringSelect,
- // if we are in the middle of a query to convert a directly typed in value to an item,
- // prevent submit, but allow event to bubble
- if(this._opened || this._fetchHandle){
- evt.preventDefault();
- }
- // fall through
+ // maxSize: [const] Number
+ // Parameter for children of `dijit.layout.BorderContainer`.
+ // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
+ maxSize: Infinity
+});
- case dk.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();
- }
- if(this._opened){
- this._lastQuery = null; // in case results come back later
- this.closeDropDown();
- }
- break;
+// For monkey patching
+BorderContainer._Splitter = _Splitter;
+BorderContainer._Gutter = _Gutter;
- case ' ':
- if(highlighted){
- // user is effectively clicking a choice in the drop down menu
- dojo.stopEvent(evt);
- this._selectOption();
- this.closeDropDown();
- }else{
- // user typed a space into the input box, treat as normal character
- doSearch = true;
- }
- break;
+return BorderContainer;
+});
- case dk.DELETE:
- case dk.BACKSPACE:
- this._prev_key_backspace = true;
- doSearch = true;
- break;
+},
+'dojo/window':function(){
+define("dojo/window", ["./_base/lang", "./_base/sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
+ function(lang, has, baseWindow, dom, geom, style) {
- default:
- // Non char keys (F1-F12 etc..) shouldn't open list.
- // 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
- this.item = undefined; // undefined means item needs to be set
- this.searchTimer = setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
- }
- },
+// module:
+// dojo/window
+// summary:
+// TODOC
- _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 window = lang.getObject("dojo.window", true);
- var fn = this.focusNode;
+/*=====
+dojo.window = {
+ // summary:
+ // TODO
+};
+window = dojo.window;
+=====*/
- // IE7: clear selection so next highlight works all the time
- dijit.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._getCaretPos(fn);
- // 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
- dijit.selectInputText(fn, cpos);
- }
- }else{
- // text does not autoComplete; replace the whole value and highlight
- fn.value = text;
- dijit.selectInputText(fn);
- }
- },
+window.getBox = function(){
+ // summary:
+ // Returns the dimensions and scroll position of the viewable area of a browser window
- _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
- // 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
- this._fetchHandle = null;
- if( this.disabled ||
- this.readOnly ||
- (dataObject.query[this.searchAttr] != this._lastQuery)
- ){
- return;
- }
- var wasSelected = this.dropDown._highlighted_option && dojo.hasClass(this.dropDown._highlighted_option, "dijitMenuItemSelected");
- this.dropDown.clearResultList();
- if(!results.length && !this._maxOptions){ // if no results and not just the previous choices button
- this.closeDropDown();
- return;
- }
+ var
+ scrollRoot = (baseWindow.doc.compatMode == 'BackCompat') ? baseWindow.body() : baseWindow.doc.documentElement,
+ // get scroll position
+ scroll = geom.docScroll(), // scrollRoot.scrollTop/Left should work
+ w, h;
+
+ if(has("touch")){ // if(scrollbars not supported)
+ var uiWindow = baseWindow.doc.parentWindow || baseWindow.doc.defaultView; // use UI window, not dojo.global window. baseWindow.doc.parentWindow probably not needed since it's not defined for webkit
+ // 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
+ };
+};
- // 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.
+window.get = function(doc){
+ // summary:
+ // Get window object associated with document doc
- dataObject._maxOptions = this._maxOptions;
- var nodes = this.dropDown.createOptions(
- results,
- dataObject,
- dojo.hitch(this, "_getMenuLabelFromItem")
- );
+ // 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
+ }
- // show our list (only if we have content, else nothing)
- this._showResultList();
+ return doc.parentWindow || doc.defaultView; // Window
+};
- // #4091:
- // tell the screen reader that the paging callback finished by
- // shouting the next choice
- if(dataObject.direction){
- if(1 == dataObject.direction){
- this.dropDown.highlightFirstOption();
- }else if(-1 == dataObject.direction){
- this.dropDown.highlightLastOption();
+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,
+ body = doc.body || baseWindow.body(),
+ 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 && node.ownerDocument.parentWindow.frameElement)
+ ? ((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(),
+ 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;
}
- if(wasSelected){
- this._announceOption(this.dropDown.getHighlightedOption());
+ clientSize = el.clientHeight;
+ scrollBarSize = elPos.h - clientSize;
+ if(clientSize > 0 && scrollBarSize > 0){
+ elPos.h = clientSize;
}
- }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(dataObject.query[this.searchAttr])){
- this._announceOption(nodes[1]); // 1st real item
}
- },
+ 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);
+ }
+};
- _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).
+return window;
+});
- this.closeDropDown(true);
+},
+'dojo/number':function(){
+define("dojo/number", ["./_base/kernel", "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
+ function(dojo, lang, i18n, nlsNumber, dstring, dregexp) {
- // hide the tooltip
- this.displayMessage("");
+ // module:
+ // dojo/number
+ // summary:
+ // TODOC
- this.openDropDown();
+lang.getObject("number", true, dojo);
- dijit.setWaiState(this.domNode, "expanded", "true");
- },
+/*=====
+dojo.number = {
+ // summary: localized formatting and parsing routines for Number
+}
- 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();
- },
+dojo.number.__FormatOptions = function(){
+ // pattern: String?
+ // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
+ // with this string. Default value is based on locale. Overriding this property will defeat
+ // localization. Literal characters in patterns are not supported.
+ // type: String?
+ // choose a format type based on the locale from the following:
+ // decimal, scientific (not yet supported), percent, currency. decimal by default.
+ // places: Number?
+ // fixed number of decimal places to show. This overrides any
+ // information in the provided pattern.
+ // round: Number?
+ // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
+ // means do not round.
+ // locale: String?
+ // override the locale used to determine formatting rules
+ // fractional: Boolean?
+ // If false, show no decimal places, overriding places and pattern settings.
+ this.pattern = pattern;
+ this.type = type;
+ this.places = places;
+ this.round = round;
+ this.locale = locale;
+ this.fractional = fractional;
+}
+=====*/
- isLoaded: function(){
- // signal to _HasDropDown that it needs to call loadDropDown() to load the
- // drop down asynchronously before displaying it
- return false;
- },
+dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
+ // summary:
+ // Format a Number as a String, using locale-specific settings
+ // description:
+ // Create a string from a Number using a known localized pattern.
+ // Formatting patterns appropriate to the locale are chosen from the
+ // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
+ // delimiters.
+ // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
+ // value:
+ // the number to be formatted
- 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);
- dijit.setWaiState(this.domNode, "expanded", "false");
- dijit.removeWaiState(this.focusNode,"activedescendant");
- }
- },
+ 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 dojo.number._applyPattern(value, pattern, options); // String
+};
- _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){
- dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
- }
- this._refreshState();
- }
- },
+//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
+dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
- _onBlur: function(){
- // summary:
- // Called magically when focus has shifted away from this widget and it's drop down
- this.closeDropDown();
- this.inherited(arguments);
- },
+dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
+ // summary:
+ // Apply pattern to format value as a string using options. Gives no
+ // consideration to local customs.
+ // value:
+ // the number to be formatted.
+ // pattern:
+ // a pattern string as described by
+ // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
+ // options: dojo.number.__FormatOptions?
+ // _applyPattern is usually called via `dojo.number.format()` which
+ // populates an extra property in the options parameter, "customs".
+ // The customs object specifies group and decimal parameters if set.
- _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
- if(!displayedValue){
- displayedValue = this.store.getValue(item, this.searchAttr);
- }
- var value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
- this._set("item", item);
- dijit.form.ComboBox.superclass._setValueAttr.call(this, value, priorityChange, displayedValue);
- },
+ //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);
- _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.
+ //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");
+ }
- 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{
- newValue = this.store.getValue(node.item, this.searchAttr).toString();
- this.set('item', node.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
- dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
- // autocomplete the rest of the option to announce change
- this._autoCompleteText(newValue);
- },
+ //TODO: support @ sig figs?
+ var numberPatternRE = dojo.number._numberPatternRE;
+ var numberPattern = positivePattern.match(numberPatternRE);
+ if(!numberPattern){
+ throw new Error("unable to find a number expression in pattern: "+pattern);
+ }
+ if(options.fractional === false){ options.places = 0; }
+ return pattern.replace(numberPatternRE,
+ dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
+};
- _selectOption: function(/*Event*/ evt){
- // summary:
- // Menu callback function, called when an item in the menu is selected.
- if(evt){
- this._announceOption(evt.target);
- }
- this.closeDropDown();
- this._setCaretPos(this.focusNode, this.focusNode.value.length);
- dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
- },
+dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
+ // summary:
+ // Rounds to the nearest value with the given number of decimal places, away from zero
+ // description:
+ // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
+ // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
+ // fractional increments also, such as the nearest quarter.
+ // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
+ // value:
+ // The number to round
+ // places:
+ // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
+ // Must be non-negative.
+ // increment:
+ // Rounds next place to nearest value of increment/10. 10 by default.
+ // example:
+ // >>> dojo.number.round(-0.5)
+ // -1
+ // >>> dojo.number.round(162.295, 2)
+ // 162.29 // note floating point error. Should be 162.3
+ // >>> dojo.number.round(10.71, 0, 2.5)
+ // 10.75
+ var factor = 10 / (increment || 10);
+ return (factor * +value).toFixed(places) / factor; // Number
+};
- _startSearchAll: function(){
- this._startSearch('');
- },
+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 = dojo.number.round;
+ dojo.number.round = function(v, p, m){
+ var d = Math.pow(10, -p || 0), a = Math.abs(v);
+ if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
+ d = 0;
+ }
+ return round(v, p, m) + (v > 0 ? d : -d);
+ };
+}
- _startSearchFromInput: function(){
- this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
- },
+/*=====
+dojo.number.__FormatAbsoluteOptions = function(){
+ // decimal: String?
+ // the decimal separator
+ // group: String?
+ // the group separator
+ // places: Number?|String?
+ // number of decimal places. the range "n,m" will format to m places.
+ // round: Number?
+ // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
+ // means don't round.
+ this.decimal = decimal;
+ this.group = group;
+ this.places = places;
+ this.round = round;
+}
+=====*/
- _getQueryString: function(/*String*/ text){
- return dojo.string.substitute(this.queryExpr, [text]);
- },
+dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
+ // summary:
+ // Apply numeric pattern to absolute value using options. Gives no
+ // consideration to local customs.
+ // value:
+ // the number to be formatted, ignores sign
+ // pattern:
+ // the number portion of a pattern (e.g. `#,##0.00`)
+ options = options || {};
+ if(options.places === true){options.places=0;}
+ if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
- _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 = dojo.getObject(this.dropDownClass, false);
- this.dropDown = new dropDownConstructor({
- onChange: dojo.hitch(this, this._selectOption),
- id: popupId,
- dir: this.dir
- });
- dijit.removeWaiState(this.focusNode,"activedescendant");
- dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
- }
- // create a new query to prevent accidentally querying for a hidden
- // value from FilteringSelect's keyField
- var query = dojo.clone(this.query); // #5970
- this._lastInput = key; // Store exactly what was entered by the user.
- this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
- // #5970: 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.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
- this.searchTimer = null;
- var fetch = {
- queryOptions: {
- ignoreCase: this.ignoreCase,
- deep: true
- },
- query: query,
- onBegin: dojo.hitch(this, "_setMaxOptions"),
- onComplete: dojo.hitch(this, "_openResultList"),
- onError: function(errText){
- _this._fetchHandle = null;
- console.error('dijit.form.ComboBox: ' + errText);
- _this.closeDropDown();
- },
- start: 0,
- count: this.pageSize
- };
- dojo.mixin(fetch, _this.fetchProperties);
- this._fetchHandle = _this.store.fetch(fetch);
-
- var nextSearch = function(dataObject, direction){
- dataObject.start += dataObject.count*direction;
- // #4091:
- // tell callback the direction of the paging so the screen
- // reader knows which menu option to shout
- dataObject.direction = direction;
- this._fetchHandle = this.store.fetch(dataObject);
- this.focus();
- };
- this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
- }, query, this), this.searchDelay);
- },
+ var patternParts = pattern.split("."),
+ comma = typeof options.places == "string" && options.places.indexOf(","),
+ maxPlaces = options.places;
+ if(comma){
+ maxPlaces = options.places.substring(comma + 1);
+ }else if(!(maxPlaces >= 0)){
+ maxPlaces = (patternParts[1] || []).length;
+ }
+ if(!(options.round < 0)){
+ value = dojo.number.round(value, maxPlaces, options.round);
+ }
- _setMaxOptions: function(size, request){
- this._maxOptions = size;
- },
+ 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);
+ }
- _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;
- },
+ // Truncate fractional
+ if(maxPlaces < fractional.length){
+ valueParts[1] = fractional.substr(0, maxPlaces);
+ }
+ }else{
+ if(valueParts[1]){ valueParts.pop(); }
+ }
- //////////// INITIALIZATION METHODS ///////////////////////////////////////
+ // 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);
+ }
- constructor: function(){
- this.query={};
- this.fetchProperties={};
- },
+ // Truncate whole
+ if(patternDigits.indexOf("#") == -1){
+ valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
+ }
+ }
- postMixInProperties: function(){
- if(!this.store){
- var srcNodeRef = this.srcNodeRef;
+ // 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 || ",");
- // if user didn't specify store, then assume there are option tags
- this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
+ return valueParts.join(options.decimal || ".");
+};
- // 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
+/*=====
+dojo.number.__RegexpOptions = function(){
+ // pattern: String?
+ // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
+ // with this string. Default value is based on locale. Overriding this property will defeat
+ // localization.
+ // type: String?
+ // choose a format type based on the locale from the following:
+ // decimal, scientific (not yet supported), percent, currency. decimal by default.
+ // locale: String?
+ // override the locale used to determine formatting rules
+ // strict: Boolean?
+ // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
+ // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
+ // places: Number|String?
+ // number of decimal places to accept: Infinity, a positive number, or
+ // a range "n,m". Defined by pattern or Infinity if pattern not provided.
+ this.pattern = pattern;
+ this.type = type;
+ this.locale = locale;
+ this.strict = strict;
+ this.places = places;
+}
+=====*/
+dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
+ // summary:
+ // Builds the regular needed to parse a number
+ // description:
+ // Returns regular expression with positive and negative match, group
+ // and decimal separators
+ return dojo.number._parseInfo(options).regexp; // String
+};
- // 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();
- this.value = this.store.getValue(item, valueField);
- }
+dojo.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(dojo.number._numberPatternRE, function(format){
+ var flags = {
+ signed: false,
+ separator: options.strict ? group : [group,""],
+ fractional: options.fractional,
+ decimal: decimal,
+ exponent: false
+ },
+
+ parts = format.split('.'),
+ places = options.places;
+
+ // special condition for percent (factor != 1)
+ // allow decimal places even if not specified in pattern
+ if(parts.length == 1 && factor != 1){
+ parts[1] = "###";
+ }
+ if(parts.length == 1 || places === 0){
+ flags.fractional = false;
+ }else{
+ if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
+ if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
+ if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
+ flags.places = places;
+ }
+ var groups = parts[0].split(',');
+ if(groups.length > 1){
+ flags.groupSize = groups.pop().length;
+ if(groups.length > 1){
+ flags.groupSize2 = groups.pop().length;
}
}
+ return "("+dojo.number._realNumberRegexp(flags)+")";
+ });
+ }, true);
- this.inherited(arguments);
- },
+ 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;
+ });
+ }
- postCreate: function(){
- // summary:
- // Subclasses must call this method from their postCreate() methods
- // tags:
- // protected
+//TODO: substitute localized sign/percent/permille/etc.?
- // find any associated label element and add to ComboBox node.
- var label=dojo.query('label[for="'+this.id+'"]');
- if(label.length){
- label[0].id = (this.id+"_label");
- dijit.setWaiState(this.domNode, "labelledby", label[0].id);
+ // normalize whitespace and return
+ return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
+};
- }
- this.inherited(arguments);
- },
+/*=====
+dojo.number.__ParseOptions = function(){
+ // pattern: String?
+ // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
+ // with this string. Default value is based on locale. Overriding this property will defeat
+ // localization. Literal characters in patterns are not supported.
+ // type: String?
+ // choose a format type based on the locale from the following:
+ // decimal, scientific (not yet supported), percent, currency. decimal by default.
+ // locale: String?
+ // override the locale used to determine formatting rules
+ // strict: Boolean?
+ // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
+ // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
+ // fractional: Boolean?|Array?
+ // Whether to include the fractional portion, where the number of decimal places are implied by pattern
+ // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
+ this.pattern = pattern;
+ this.type = type;
+ this.locale = locale;
+ this.strict = strict;
+ this.fractional = fractional;
+}
+=====*/
+dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
+ // summary:
+ // Convert a properly formatted string to a primitive Number, using
+ // locale-specific settings.
+ // description:
+ // Create a Number from a string using a known localized pattern.
+ // Formatting patterns are chosen appropriate to the locale
+ // and follow the syntax described by
+ // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
+ // Note that literal characters in patterns are not supported.
+ // expression:
+ // A string representation of a Number
+ var info = dojo.number._parseInfo(options),
+ results = (new RegExp("^"+info.regexp+"$")).exec(expression);
+ if(!results){
+ return NaN; //NaN
+ }
+ var absoluteMatch = results[1]; // match for the positive expression
+ if(!results[1]){
+ if(!results[2]){
+ return NaN; //NaN
+ }
+ // matched the negative pattern
+ absoluteMatch =results[2];
+ info.factor *= -1;
+ }
- _setHasDownArrowAttr: function(val){
- this.hasDownArrow = val;
- this._buttonNode.style.display = val ? "" : "none";
- },
+ // 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
+};
- _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._escapeHtml(this._lastInput));
- labelType = "html";
- }
- return {html: labelType == "html", label: label};
- },
+/*=====
+dojo.number.__RealNumberRegexpFlags = function(){
+ // places: Number?
+ // The integer number of decimal places or a range given as "n,m". If
+ // not given, the decimal part is optional and the number of places is
+ // unlimited.
+ // decimal: String?
+ // A string for the character used as the decimal point. Default
+ // is ".".
+ // fractional: Boolean?|Array?
+ // Whether decimal places are used. Can be true, false, or [true,
+ // false]. Default is [true, false] which means optional.
+ // exponent: Boolean?|Array?
+ // Express in exponential notation. Can be true, false, or [true,
+ // false]. Default is [true, false], (i.e. will match if the
+ // exponential part is present are not).
+ // eSigned: Boolean?|Array?
+ // The leading plus-or-minus sign on the exponent. Can be true,
+ // false, or [true, false]. Default is [true, false], (i.e. will
+ // match if it is signed or unsigned). flags in regexp.integer can be
+ // applied.
+ this.places = places;
+ this.decimal = decimal;
+ this.fractional = fractional;
+ this.exponent = exponent;
+ this.eSigned = eSigned;
+}
+=====*/
- 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
+dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
+ // summary:
+ // Builds a regular expression to match a real number in exponential
+ // notation
- 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 = dojo.regexp.escapeString(find); // escape regexp special chars
- return this._escapeHtml(label).replace(
- // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
- new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
- '<span class="dijitComboBoxHighlightMatch">$1</span>'
- ); // returns String, (almost) valid HTML (entities encoded)
- },
+ // 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]; }
- _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;");
- return str; // string
+ var integerRE = dojo.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
+ );
- reset: function(){
- // Overrides the _FormWidget.reset().
- // Additionally reset the .item (to clean up).
- this.item = null;
- this.inherited(arguments);
- },
+ var exponentRE = dregexp.buildGroupRE(flags.exponent,
+ function(q){
+ if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
+ return "";
+ }
+ );
- labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
- // summary:
- // Computes the label to display based on the dojo.data store item.
- // returns:
- // The label that the ComboBox should display
- // tags:
- // private
+ var realRE = integerRE + decimalRE;
+ // allow for decimals without integers, e.g. .25
+ if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
+ return realRE + exponentRE; // String
+};
- // Use toString() because XMLStore returns an XMLItem whereas this
- // method is expected to return a String (#9354)
- return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
- }
+/*=====
+dojo.number.__IntegerRegexpFlags = function(){
+ // signed: Boolean?
+ // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
+ // Default is `[true, false]`, (i.e. will match if it is signed
+ // or unsigned).
+ // separator: String?
+ // The character used as the thousands separator. Default is no
+ // separator. For more than one symbol use an array, e.g. `[",", ""]`,
+ // makes ',' optional.
+ // groupSize: Number?
+ // group size between separators
+ // groupSize2: Number?
+ // second grouping, where separators 2..n have a different interval than the first separator (for India)
+ this.signed = signed;
+ this.separator = separator;
+ this.groupSize = groupSize;
+ this.groupSize2 = groupSize2;
+}
+=====*/
+
+dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
+ // summary:
+ // Builds a regular expression that matches an integer
+
+ // assign default values to missing parameters
+ flags = flags || {};
+ if(!("signed" in flags)){ flags.signed = [true, false]; }
+ if(!("separator" in flags)){
+ flags.separator = "";
+ }else if(!("groupSize" in flags)){
+ flags.groupSize = 3;
}
-);
-dojo.declare(
- "dijit.form._ComboBoxMenu",
- [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
- {
- // summary:
- // Focus-less menu for internal use in `dijit.form.ComboBox`
- // tags:
- // private
+ var signRE = dregexp.buildGroupRE(flags.signed,
+ function(q){ return q ? "[-+]" : ""; },
+ true
+ );
- templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
- +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' role='option'></li>"
- +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' role='option'></li>"
- +"</ul>",
+ var numberRE = dregexp.buildGroupRE(flags.separator,
+ function(sep){
+ if(!sep){
+ return "(?:\\d+)";
+ }
- // _messages: Object
- // Holds "next" and "previous" text for paging buttons on drop down
- _messages: null,
-
- baseClass: "dijitComboBoxMenu",
+ sep = dregexp.escapeString(sep);
+ if(sep == " "){ sep = "\\s"; }
+ else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
- postMixInProperties: function(){
- this.inherited(arguments);
- this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
+ 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
+ );
- buildRendering: function(){
- this.inherited(arguments);
+ return signRE + numberRE; // String
+};
- // fill in template with i18n messages
- this.previousButton.innerHTML = this._messages["previousMessage"];
- this.nextButton.innerHTML = this._messages["nextMessage"];
- },
+return dojo.number;
+});
- _setValueAttr: function(/*Object*/ value){
- this.value = value;
- this.onChange(value);
- },
+},
+'dijit/_FocusMixin':function(){
+define("dijit/_FocusMixin", [
+ "./focus",
+ "./_WidgetBase",
+ "dojo/_base/declare", // declare
+ "dojo/_base/lang" // lang.extend
+], function(focus, _WidgetBase, declare, lang){
- // stubs
- onChange: function(/*Object*/ value){
+/*=====
+ var _WidgetBase = dijit._WidgetBase;
+=====*/
+
+ // module:
+ // dijit/_FocusMixin
+ // summary:
+ // Mixin to widget to provide _onFocus() and _onBlur() methods that
+ // fire when a widget or it's descendants get/lose focus
+
+ // 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:
- // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
- // Probably should be called onSelect.
+ // Called when the widget becomes "active" because
+ // it or a widget inside of it either has focus, or has recently
+ // been clicked.
// tags:
// callback
},
- onPage: function(/*Number*/ direction){
+
+ onBlur: function(){
// summary:
- // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
+ // 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
},
- onClose: function(){
+ _onFocus: function(){
// summary:
- // Callback from dijit.popup code to this widget, notifying it that it closed
+ // This is where widgets do processing for when they are active,
+ // such as changing CSS classes. See onFocus() for more details.
// tags:
- // private
- this._blurOptionNode();
+ // protected
+ this.onFocus();
},
- _createOption: function(/*Object*/ item, labelFunc){
+ _onBlur: function(){
// summary:
- // Creates an option to appear on the popup menu subclassed by
- // `dijit.form.FilteringSelect`.
-
- var menuitem = dojo.create("li", {
- "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
- role: "option"
- });
- var labelObject = labelFunc(item);
- if(labelObject.html){
- menuitem.innerHTML = labelObject.label;
- }else{
- menuitem.appendChild(
- dojo.doc.createTextNode(labelObject.label)
- );
- }
- // #3250: in blank options, assign a normal height
- if(menuitem.innerHTML == ""){
- menuitem.innerHTML = "&nbsp;";
- }
- menuitem.item=item;
- return menuitem;
- },
-
- createOptions: function(results, dataObject, labelFunc){
- // summary:
- // Fills in the items in the drop down list
- // results:
- // Array of dojo.data items
- // dataObject:
- // dojo.data store
- // labelFunc:
- // Function to produce a label in the drop down list from a dojo.data item
-
- //this._dataObject=dataObject;
- //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
- // display "Previous . . ." button
- this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
- dojo.attr(this.previousButton, "id", this.id + "_prev");
- // create options using _createOption function defined by parent
- // ComboBox (or FilteringSelect) class
- // #2309:
- // iterate over cache nondestructively
- dojo.forEach(results, function(item, i){
- var menuitem = this._createOption(item, labelFunc);
- dojo.attr(menuitem, "id", this.id + i);
- this.domNode.insertBefore(menuitem, this.nextButton);
- }, this);
- // display "Next . . ." button
- var displayMore = false;
- //Try to determine if we should show 'more'...
- if(dataObject._maxOptions && dataObject._maxOptions != -1){
- if((dataObject.start + dataObject.count) < dataObject._maxOptions){
- displayMore = true;
- }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
- //Weird return from a datastore, 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(dataObject.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";
- dojo.attr(this.nextButton,"id", this.id + "_next");
- return this.domNode.childNodes;
- },
+ // 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();
+ }
+ });
- clearResultList: function(){
- // summary:
- // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
- while(this.domNode.childNodes.length>2){
- this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
- }
- this._blurOptionNode();
- },
+ return declare("dijit._FocusMixin", null, {
+ // summary:
+ // Mixin to widget to provide _onFocus() and _onBlur() methods that
+ // fire when a widget or it's descendants get/lose focus
- _onMouseDown: function(/*Event*/ evt){
- dojo.stopEvent(evt);
- },
+ // flag that I want _onFocus()/_onBlur() notifications from focus manager
+ _focusManager: focus
+ });
- _onMouseUp: function(/*Event*/ evt){
- if(evt.target === this.domNode || !this._highlighted_option){
- // !this._highlighted_option check to prevent immediate selection when menu appears on top
- // of <input>, see #9898. Note that _HasDropDown also has code to prevent this.
- return;
- }else if(evt.target == this.previousButton){
- this._blurOptionNode();
- this.onPage(-1);
- }else if(evt.target == this.nextButton){
- this._blurOptionNode();
- this.onPage(1);
- }else{
- var tgt = evt.target;
- // while the clicked node is inside the div
- while(!tgt.item){
- // recurse to the top
- tgt = tgt.parentNode;
- }
- this._setValueAttr({ target: tgt }, true);
- }
- },
+});
- _onMouseOver: function(/*Event*/ evt){
- if(evt.target === this.domNode){ return; }
- var tgt = evt.target;
- if(!(tgt == this.previousButton || tgt == this.nextButton)){
- // while the clicked node is inside the div
- while(!tgt.item){
- // recurse to the top
- tgt = tgt.parentNode;
- }
- }
- this._focusOptionNode(tgt);
- },
+},
+'dojo/data/util/filter':function(){
+define("dojo/data/util/filter", ["dojo/_base/lang"], function(lang) {
+ // module:
+ // dojo/data/util/filter
+ // summary:
+ // TODOC
- _onMouseOut: function(/*Event*/ evt){
- if(evt.target === this.domNode){ return; }
- this._blurOptionNode();
- },
+var filter = lang.getObject("dojo.data.util.filter", true);
- _focusOptionNode: function(/*DomNode*/ node){
- // summary:
- // Does the actual highlight.
- if(this._highlighted_option != node){
- this._blurOptionNode();
- this._highlighted_option = node;
- dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
- }
- },
+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.
- _blurOptionNode: function(){
- // summary:
- // Removes highlight on highlighted option.
- if(this._highlighted_option){
- dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
- this._highlighted_option = null;
- }
- },
+ 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
+ }
- _highlightNextOption: function(){
- // summary:
- // Highlight the item just below the current selection.
- // If nothing selected, highlight first option.
+};
- // because each press of a button clears the menu,
- // the highlighted option sometimes becomes detached from the menu!
- // test to see if the option has a parent to see if this is the case.
- if(!this.getHighlightedOption()){
- var fc = this.domNode.firstChild;
- this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
- }else{
- var ns = this._highlighted_option.nextSibling;
- if(ns && ns.style.display != "none"){
- this._focusOptionNode(ns);
- }else{
- this.highlightFirstOption();
- }
- }
- // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
- dojo.window.scrollIntoView(this._highlighted_option);
- },
+return filter;
+});
- highlightFirstOption: function(){
- // summary:
- // Highlight the first real item in the list (not Previous Choices).
- var first = this.domNode.firstChild;
- var second = first.nextSibling;
- this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
- dojo.window.scrollIntoView(this._highlighted_option);
- },
+},
+'dijit/_WidgetsInTemplateMixin':function(){
+define("dijit/_WidgetsInTemplateMixin", [
+ "dojo/_base/array", // array.forEach
+ "dojo/_base/declare", // declare
+ "dojo/parser", // parser.parse
+ "dijit/registry" // registry.findWidgets
+], function(array, declare, parser, registry){
+
+ // module:
+ // dijit/_WidgetsInTemplateMixin
+ // summary:
+ // Mixin to supplement _TemplatedMixin when template contains widgets
- highlightLastOption: function(){
- // summary:
- // Highlight the last real item in the list (not More Choices).
- this._focusOptionNode(this.domNode.lastChild.previousSibling);
- dojo.window.scrollIntoView(this._highlighted_option);
- },
+ return declare("dijit._WidgetsInTemplateMixin", null, {
+ // summary:
+ // Mixin to supplement _TemplatedMixin when template contains widgets
- _highlightPrevOption: function(){
- // summary:
- // Highlight the item just above the current selection.
- // If nothing selected, highlight last option (if
- // you select Previous and try to keep scrolling up the list).
- if(!this.getHighlightedOption()){
- var lc = this.domNode.lastChild;
- this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
- }else{
- var ps = this._highlighted_option.previousSibling;
- if(ps && ps.style.display != "none"){
- this._focusOptionNode(ps);
- }else{
- this.highlightLastOption();
- }
- }
- dojo.window.scrollIntoView(this._highlighted_option);
- },
+ // _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,
- _page: function(/*Boolean*/ up){
- // summary:
- // Handles page-up and page-down keypresses
+ // 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,
- var scrollamount = 0;
- var oldscroll = this.domNode.scrollTop;
- var height = dojo.style(this.domNode, "height");
- // if no item is highlighted, highlight the first option
- if(!this.getHighlightedOption()){
- this._highlightNextOption();
- }
- while(scrollamount<height){
- if(up){
- // stop at option 1
- if(!this.getHighlightedOption().previousSibling ||
- this._highlighted_option.previousSibling.style.display == "none"){
- break;
- }
- this._highlightPrevOption();
- }else{
- // stop at last option
- if(!this.getHighlightedOption().nextSibling ||
- this._highlighted_option.nextSibling.style.display == "none"){
- break;
- }
- this._highlightNextOption();
- }
- // going backwards
- var newscroll=this.domNode.scrollTop;
- scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
- oldscroll=newscroll;
- }
- },
+ _beforeFillContent: function(){
+ if(this.widgetsInTemplate){
+ // Before copying over content, instantiate widgets in template
+ var node = this.domNode;
- pageUp: function(){
- // summary:
- // Handles pageup keypress.
- // TODO: just call _page directly from handleKey().
- // tags:
- // private
- this._page(true);
- },
+ 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
+ }));
- pageDown: function(){
- // summary:
- // Handles pagedown keypress.
- // TODO: just call _page directly from handleKey().
- // tags:
- // private
- this._page(false);
- },
+ this._supportingWidgets = registry.findWidgets(node);
- getHighlightedOption: function(){
- // summary:
- // Returns the highlighted option.
- var ho = this._highlighted_option;
- return (ho && ho.parentNode) ? ho : null;
+ this._attachTemplateNodes(cw, function(n,p){
+ return n[p];
+ });
+ }
},
- 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.charOrCode){
- case dojo.keys.DOWN_ARROW:
- this._highlightNextOption();
- return false;
- case dojo.keys.PAGE_DOWN:
- this.pageDown();
- return false;
- case dojo.keys.UP_ARROW:
- this._highlightPrevOption();
- return false;
- case dojo.keys.PAGE_UP:
- this.pageUp();
- return false;
- default:
- return true;
- }
+ startup: function(){
+ array.forEach(this._startupWidgets, function(w){
+ if(w && !w._started && w.startup){
+ w.startup();
+ }
+ });
+ this.inherited(arguments);
}
- }
-);
-
-dojo.declare(
- "dijit.form.ComboBox",
- [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
- {
- // summary:
- // Auto-completing text box, and base class for dijit.form.FilteringSelect.
- //
- // description:
- // The drop down box's values are populated from an class called
- // a data provider, which returns a list of values based on the characters
- // that the user has typed into the input box.
- // If OPTION tags are used as the data provider via markup,
- // then the OPTION tag's child text node is used as the widget value
- // when selected. The OPTION tag's value attribute is ignored.
- // To set the default value when using OPTION tags, specify the selected
- // attribute on 1 of the child OPTION tags.
- //
- // Some of the options to the ComboBox are actually arguments to the data
- // provider.
+ });
+});
- _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
- // summary:
- // Hook so set('value', value) works.
- // description:
- // Sets the value of the select.
- this._set("item", null); // value not looked up in store
- if(!value){ value = ''; } // null translates to blank
- dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
- }
- }
-);
+},
+'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
+ // summary:
+ // TODOC
-dojo.declare("dijit.form._ComboBoxDataStore", null, {
+return declare("dojo.fx.Toggler", null, {
// summary:
- // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
+ // A simple `dojo.Animation` toggler API.
//
// description:
- // Provides a store for inlined data like:
- //
- // | <select>
- // | <option value="AL">Alabama</option>
- // | ...
- //
- // Actually. just implements the subset of dojo.data.Read/Notification
- // needed for ComboBox and FilteringSelect to work.
+ // 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`).
//
- // Note that an item is just a pointer to the <option> DomNode.
-
- constructor: function( /*DomNode*/ root){
- this.root = root;
- if(root.tagName != "SELECT" && root.firstChild){
- root = dojo.query("select", root);
- if(root.length > 0){ // SELECT is a child of srcNodeRef
- root = root[0];
- }else{ // no select, so create 1 to parent the option tags to define selectedIndex
- this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
- root = this.root.firstChild;
- }
- this.root = root;
- }
- dojo.query("> option", root).forEach(function(node){
- // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be.
- // If it is needed then can we just hide the select itself instead?
- //node.style.display="none";
- node.innerHTML = dojo.trim(node.innerHTML);
- });
+ // 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,
- getValue: function( /*item*/ item,
- /*attribute-name-string*/ attribute,
- /*value?*/ defaultValue){
- return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
- },
+ // showFunc: Function
+ // The function that returns the `dojo.Animation` to show the node
+ showFunc: baseFx.fadeIn,
- isItemLoaded: function(/*anything*/ something){
- return true;
- },
+ // hideFunc: Function
+ // The function that returns the `dojo.Animation` to hide the node
+ hideFunc: baseFx.fadeOut,
- getFeatures: function(){
- return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
- },
+ // showDuration:
+ // Time in milliseconds to run the show Animation
+ showDuration: 200,
- _fetchItems: function( /*Object*/ args,
- /*Function*/ findCallback,
- /*Function*/ errorCallback){
- // summary:
- // See dojo.data.util.simpleFetch.fetch()
- if(!args.query){ args.query = {}; }
- if(!args.query.name){ args.query.name = ""; }
- if(!args.queryOptions){ args.queryOptions = {}; }
- var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
- items = dojo.query("> option", this.root).filter(function(option){
- return (option.innerText || option.textContent || '').match(matcher);
- } );
- if(args.sort){
- items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
- }
- findCallback(items, args);
- },
+ // hideDuration:
+ // Time in milliseconds to run the hide Animation
+ hideDuration: 200,
- close: function(/*dojo.data.api.Request || args || null*/ request){
- return;
- },
+ // 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
- getLabel: function(/*item*/ item){
- return item.innerHTML;
- },
+ /*=====
+ _showArgs: null,
+ _showAnim: null,
+
+ _hideArgs: null,
+ _hideAnim: null,
- getIdentity: function(/*item*/ item){
- return dojo.attr(item, "value");
+ _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));
},
- fetchItemByIdentity: function(/*Object*/ args){
- // summary:
- // Given the identity of an item, this method returns the item that has
- // that identity through the onItem callback.
- // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
- //
- // description:
- // Given arguments like:
- //
- // | {identity: "CA", onItem: function(item){...}
- //
- // Call `onItem()` with the DOM node `<option value="CA">California</option>`
- var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
- args.onItem(item);
+ show: function(delay){
+ // summary: Toggle the node to showing
+ // delay: Integer?
+ // Ammount of time to stall playing the show animation
+ return this.showAnim.play(delay || 0);
},
- fetchSelectedItem: function(){
- // summary:
- // Get the option marked as selected, like `<option selected>`.
- // Not part of dojo.data API.
- var root = this.root,
- si = root.selectedIndex;
- return typeof si == "number"
- ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
- : null; // dojo.data.Item
+ hide: function(delay){
+ // summary: Toggle the node to hidden
+ // delay: Integer?
+ // Ammount of time to stall playing the hide animation
+ return this.hideAnim.play(delay || 0);
}
});
-//Mix in the simple fetch implementation to this class.
-dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
-}
+});
-if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.FilteringSelect"] = true;
-dojo.provide("dijit.form.FilteringSelect");
+},
+'dijit/form/FilteringSelect':function(){
+define("dijit/form/FilteringSelect", [
+ "dojo/data/util/filter", // filter.patternToRegExp
+ "dojo/_base/declare", // declare
+ "dojo/_base/Deferred", // Deferred.when
+ "dojo/_base/lang", // lang.mixin
+ "./MappedTextBox",
+ "./ComboBoxMixin"
+], function(filter, declare, Deferred, lang, MappedTextBox, ComboBoxMixin){
+
+/*=====
+ var MappedTextBox = dijit.form.MappedTextBox;
+ var ComboBoxMixin = dijit.form.ComboBoxMixin;
+=====*/
+ // module:
+ // dijit/form/FilteringSelect
+ // summary:
+ // An enhanced version of the HTML SELECT tag, populated dynamically
-dojo.declare(
- "dijit.form.FilteringSelect",
- [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
- {
+ return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
// summary:
// An enhanced version of the HTML SELECT tag, populated dynamically
//
@@ -15265,7 +25042,7 @@ dojo.declare(
isValid: function(){
// Overrides ValidationTextBox.isValid()
- return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
+ return !!this.item || (!this.required && this.get('displayedValue') == ""); // #5974
},
_refreshState: function(){
@@ -15276,39 +25053,37 @@ dojo.declare(
_callbackSetLabel: function(
/*Array*/ result,
- /*Object*/ dataObject,
+ /*Object*/ query,
+ /*Object*/ options,
/*Boolean?*/ priorityChange){
// summary:
- // Callback from dojo.data after lookup of user entered value finishes
+ // 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((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
+ 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.valueNode.value = "";
- dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
- this._set("item", null);
- this.validate(this._focused);
+ this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
}else{
this.set('item', result[0], priorityChange);
}
},
- _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
+ _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(dataObject.query[this.searchAttr] != this._lastQuery){
+ if(query[this.searchAttr] !== this._lastQuery){
return;
}
- dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
+ this.inherited(arguments);
if(this.item === undefined){ // item == undefined for keyboard search
// If the search returned no items that means that the user typed
@@ -15332,28 +25107,32 @@ dojo.declare(
return "value";
},
- _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
+ _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; }
- this._lastQuery = value;
- if(value === null || value === ''){
- this._setDisplayedValueAttr('', priorityChange);
- return;
- }
-
- //#3347: fetchItemByIdentity if no keyAttr specified
- var self = this;
- this.store.fetchItemByIdentity({
- identity: value,
- onItem: function(item){
- self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
+ if(item === undefined){
+ if(value === null || value === ''){
+ value = '';
+ if(!lang.isString(displayedValue)){
+ this._setDisplayedValueAttr(displayedValue||'', priorityChange);
+ return;
+ }
}
- });
+
+ var self = this;
+ this._lastQuery = value;
+ Deferred.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){
@@ -15366,7 +25145,6 @@ dojo.declare(
// tags:
// private
this.inherited(arguments);
- this.valueNode.value = this.value;
this._lastDisplayedValue = this.textbox.value;
},
@@ -15398,9 +25176,22 @@ dojo.declare(
// Note that if there's a custom labelFunc() this code
if(this.store){
this.closeDropDown();
- var query = dojo.clone(this.query); // #6196: populate query with user-specifics
- // escape meta characters of dojo.data.util.filter.patternToRegExp().
- this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
+ 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
@@ -15409,233 +25200,918 @@ dojo.declare(
this._lastDisplayedValue = label;
this._set("displayedValue", label); // for watch("displayedValue") notification
var _this = this;
- var fetch = {
- query: query,
- queryOptions: {
- ignoreCase: this.ignoreCase,
- deep: true
- },
- onComplete: function(result, dataObject){
- _this._fetchHandle = null;
- dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
- },
- onError: function(errText){
- _this._fetchHandle = null;
- console.error('dijit.form.FilteringSelect: ' + errText);
- dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
- }
+ var options = {
+ ignoreCase: this.ignoreCase,
+ deep: true
};
- dojo.mixin(fetch, this.fetchProperties);
- this._fetchHandle = this.store.fetch(fetch);
+ lang.mixin(options, this.fetchProperties);
+ this._fetchHandle = this.store.query(query, options);
+ Deferred.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", ["dojo/_base/lang"], function(lang) {
+ // module:
+ // dojo/data/util/sorter
+ // summary:
+ // TODOC
+
+var sorter = lang.getObject("dojo.data.util.sorter", true);
+
+sorter.basicComparator = function( /*anything*/ a,
+ /*anything*/ b){
+ // summary:
+ // Basic comparision 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 array */sortSpec, /*dojo.data.core.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: array
+ // 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: object
+ // The datastore object to look up item values from.
+ //
+ var sortFunctions=[];
-if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.Form"] = true;
-dojo.provide("dijit.form.Form");
+ 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
+// summary:
+// A mixin to add a thin standard API wrapper to a normal HTML button
+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 dijit.form.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",
-dojo.declare(
- "dijit.form.Form",
- [dijit._Widget, dijit._Templated, dijit.form._FormMixin, dijit.layout._ContentPaneResizeMixin],
- {
+ _onClick: function(/*Event*/ e){
// summary:
- // Widget corresponding to HTML form tag, for validation and serialization
- //
- // example:
- // | <form dojoType="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');
+ // 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;
+ },
- // HTML <FORM> attributes
+ postCreate: function(){
+ this.inherited(arguments);
+ dom.setSelectable(this.focusNode, false);
+ },
- // name: String?
- // Name of form for scripting.
- name: "",
+ 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
+ },
- // action: String?
- // Server-side form handler.
- action: "",
+ _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;
+ }
+});
- // 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: "",
+},
+'dojo/colors':function(){
+define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil) {
+ // module:
+ // dojo/colors
+ // summary:
+ // TODOC
- // accept-charset: String?
- // List of supported charsets.
- "accept-charset": "",
+ var ColorExt = lang.getObject("dojo.colors", true);
- // accept: String?
- // List of MIME types for file upload.
- accept: "",
+//TODO: this module appears to break naming conventions
- // target: String?
- // Target frame for the document to be opened in.
- target: "",
+/*=====
+ lang.mixin(dojo, {
+ colors: {
+ // summary: Color utilities, extending Base dojo.Color
+ }
+ });
+=====*/
- templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
+ // 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.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.Color
+ }
+ return Color.fromArray(c, obj); // dojo.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.Color
+ }
+ }
+ return null; // dojo.Color
+ };
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- action: "",
- method: "",
- encType: "",
- "accept-charset": "",
- accept: "",
- target: ""
- }),
+ 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
+ };
- postMixInProperties: function(){
- // Setup name=foo string to be referenced from the template (but only if a name has been specified)
- // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
- this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
- this.inherited(arguments);
+ 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.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.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;
+});
+
+},
+'dijit/registry':function(){
+define("dijit/registry", [
+ "dojo/_base/array", // array.forEach array.map
+ "dojo/_base/sniff", // has("ie")
+ "dojo/_base/unload", // unload.addOnWindowUnload
+ "dojo/_base/window", // win.body
+ "." // dijit._scopeName
+], function(array, has, unload, win, dijit){
+
+ // module:
+ // dijit/registry
+ // summary:
+ // Registry of existing widget on page, plus some utility methods.
+ // Must be accessed through AMD api, ex:
+ // require(["dijit/registry"], function(registry){ registry.byId("foo"); })
+
+ var _widgetTypeCtr = {}, hash = {};
+
+ var registry = {
+ // summary:
+ // A set of widgets indexed by id
+
+ length: 0,
+
+ add: function(/*dijit._Widget*/ widget){
+ // summary:
+ // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
+ //
+ // widget: dijit._Widget
+ // Any dijit._Widget 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++;
},
- execute: function(/*Object*/ formContents){
+ remove: function(/*String*/ id){
// summary:
- // Deprecated: use submit()
- // tags:
- // deprecated
+ // Remove a widget from the registry. Does not destroy the widget; simply
+ // removes the reference.
+ if(hash[id]){
+ delete hash[id];
+ this.length--;
+ }
},
- onExecute: function(){
+ byId: function(/*String|Widget*/ id){
// summary:
- // Deprecated: use onSubmit()
- // tags:
- // deprecated
+ // Find a widget by it's id.
+ // If passed a widget then just returns the widget.
+ return typeof id == "string" ? hash[id] : id; // dijit._Widget
},
- _setEncTypeAttr: function(/*String*/ value){
- this.encType = value;
- dojo.attr(this.domNode, "encType", value);
- if(dojo.isIE){ this.domNode.encoding = value; }
+ byNode: function(/*DOMNode*/ node){
+ // summary:
+ // Returns the widget corresponding to the given DOMNode
+ return hash[node.getAttribute("widgetId")]; // dijit._Widget
},
- postCreate: function(){
- // IE tries to hide encType
- // TODO: remove in 2.0, no longer necessary with data-dojo-params
- if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
- var item = this.srcNodeRef.attributes.getNamedItem('encType');
- if(item && !item.specified && (typeof item.value == "string")){
- this.set('encType', item.value);
+ toArray: function(){
+ // summary:
+ // Convert registry into a true Array
+ //
+ // example:
+ // Work with the widget .domNodes in a real Array
+ // | array.map(dijit.registry.toArray(), function(w){ return w.domNode; });
+
+ var ar = [];
+ for(var id in hash){
+ ar.push(hash[id]);
+ }
+ return ar; // dijit._Widget[]
+ },
+
+ 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(/*DomNode*/ root){
+ // summary:
+ // Search subtree under root returning widgets found.
+ // Doesn't search for nested widgets (ie, widgets inside other widgets).
+
+ var outAry = [];
+
+ function getChildrenHelper(root){
+ for(var node = root.firstChild; node; node = node.nextSibling){
+ if(node.nodeType == 1){
+ var widgetId = node.getAttribute("widgetId");
+ if(widgetId){
+ var widget = hash[widgetId];
+ if(widget){ // may be null on page w/multiple dojo's loaded
+ outAry.push(widget);
+ }
+ }else{
+ getChildrenHelper(node);
+ }
+ }
}
}
- this.inherited(arguments);
+
+ getChildrenHelper(root);
+ return outAry;
},
- reset: function(/*Event?*/ e){
+ _destroyAll: function(){
// summary:
- // restores all widget values back to their init values,
- // calls onReset() which can cancel the reset by returning false
+ // Code to destroy all widgets and do other cleanup on page unload
- // 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, []);
+ // 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.getAttribute && node.getAttribute("widgetId");
+ if(id){
+ return hash[id];
+ }
+ node = node.parentNode;
}
+ return null;
},
- onReset: function(/*Event?*/ e){
+ // In case someone needs to access hash.
+ // Actually, this is accessed from WidgetSet back-compatibility code
+ _hash: hash
+ };
+
+ if(has("ie")){
+ // Only run _destroyAll() for IE because we think it's only necessary in that case,
+ // and because it causes problems on FF. See bug #3531 for details.
+ unload.addOnWindowUnload(function(){
+ registry._destroyAll();
+ });
+ }
+
+ /*=====
+ dijit.registry = {
+ // summary:
+ // A list of widgets on a page.
+ };
+ =====*/
+ 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.getObject lang.mixin lang.hitch
+ "dojo/mouse", // mouse.enter, mouse.leave
+ "dojo/on"
+], function(aspect, declare, domClass, event, lang, mouse, on){
+
+ // module:
+ // dijit/tree/_dndContainer
+ // 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`.
+
+ 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:
- // 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
+ // A constructor of the Container
+ // tree: Node
+ // Node or node's id to build the container on
+ // params: dijit.tree.__SourceArgs
+ // A dict of parameters, which gets mixed into the object
// tags:
- // callback
- return true; // Boolean
+ // 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 = [
+ // container level events
+ on(this.node, mouse.enter, lang.hitch(this, "onOverEvent")),
+ on(this.node, mouse.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"))
+ ];
},
- _onReset: function(e){
- this.reset(e);
- dojo.stopEvent(e);
- return false;
+ 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;
},
- _onSubmit: function(e){
- var fp = dijit.form.Form.prototype;
- // TODO: remove this if statement beginning with 2.0
- if(this.execute != fp.execute || this.onExecute != fp.onExecute){
- dojo.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
- dojo.stopEvent(e);
- }
+ // mouse events
+ onMouseOver: function(widget /*===== , evt =====*/){
+ // summary:
+ // Called when mouse is moved over a TreeNode
+ // widget: TreeNode
+ // evt: Event
+ // tags:
+ // protected
+ this.current = widget;
},
- onSubmit: function(/*Event?*/ e){
+ onMouseOut: function(/*===== widget, evt =====*/){
// 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
+ // Called when mouse is moved away from a TreeNode
+ // widget: TreeNode
+ // evt: Event
// tags:
- // extension
+ // protected
+ this.current = null;
+ },
- return this.isValid(); // Boolean
+ _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;
},
- submit: function(){
+ _addItemClass: function(node, type){
// summary:
- // programmatically submit form if and only if the `onSubmit` returns true
- if(!(this.onSubmit() === false)){
- this.containerNode.submit();
- }
+ // 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\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\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
+ "..", // 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
+ // summary:
+ // Deprecated methods for setting/getting wai roles and states.
+ // New code should call setAttribute()/getAttribute() directly.
+ //
+ // Also loads hccss to apply dijit_a11y class to root node if machine is in high-contrast mode.
-if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.RadioButton"] = true;
-dojo.provide("dijit.form.RadioButton");
+ lang.mixin(dijit, {
+ 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.
-// TODO: for 2.0, move the RadioButton code into this file
+ 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.
-if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form._FormSelectWidget"] = true;
-dojo.provide("dijit.form._FormSelectWidget");
+ 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);
+ }
+ });
+
+ return dijit;
+});
+
+},
+'dijit/form/_FormSelectWidget':function(){
+define("dijit/form/_FormSelectWidget", [
+ "dojo/_base/array", // array.filter array.forEach array.map array.some
+ "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
+ "./_FormValueWidget"
+], function(array, aspect, sorter, declare, dom, domClass, kernel, lang, query, _FormValueWidget){
+
+/*=====
+ var _FormValueWidget = dijit.form._FormValueWidget;
+=====*/
+// module:
+// dijit/form/_FormSelectWidget
+// summary:
+// Extends _FormValueWidget in order to provide "select-specific"
+// values - i.e., those values that are unique to <select> elements.
/*=====
@@ -15656,7 +26132,7 @@ dijit.form.__SelectOption = function(){
}
=====*/
-dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
+return 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.
@@ -15673,7 +26149,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
options: null,
// store: dojo.data.api.Identity
- // A store which, at the very least impelements dojo.data.api.Identity
+ // A store which, at the very least implements dojo.data.api.Identity
// to use for getting our list of options - rather than reading them
// from the <option> html tags.
store: null,
@@ -15688,7 +26164,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// onFetch: Function
// A callback to do with an onFetch - but before any items are actually
- // iterated over (i.e. to filter even futher what you want to add)
+ // iterated over (i.e. to filter even further what you want to add)
onFetch: null,
// sortByLabel: Boolean
@@ -15739,13 +26215,13 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
if(lookupValue === undefined){
return opts; // dijit.form.__SelectOption[]
}
- if(dojo.isArray(lookupValue)){
- return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
+ if(lang.isArray(lookupValue)){
+ return array.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
}
- if(dojo.isObject(valueOrIdx)){
+ 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(!dojo.some(this.options, function(o, idx){
+ if(!array.some(this.options, function(o, idx){
if(o === lookupValue ||
(o.value && o.value === lookupValue.value)){
lookupValue = idx;
@@ -15765,7 +26241,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
}
}
if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
- return this.options[lookupValue] // dijit.form.__SelectOption
+ return this.options[lookupValue]; // dijit.form.__SelectOption
}
return null; // null
},
@@ -15776,9 +26252,9 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// 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(!dojo.isArray(option)){ option = [option]; }
- dojo.forEach(option, function(i){
- if(i && dojo.isObject(i)){
+ if(!lang.isArray(option)){ option = [option]; }
+ array.forEach(option, function(i){
+ if(i && lang.isObject(i)){
this.options.push(i);
}
}, this);
@@ -15793,13 +26269,13 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// 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(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
+ if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
var oldOpts = this.getOptions(valueOrIdx);
- dojo.forEach(oldOpts, function(i){
+ 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 = dojo.filter(this.options, function(node, idx){
+ this.options = array.filter(this.options, function(node){
return (node.value !== i.value || node.label !== i.label);
});
this._removeOptionItem(i);
@@ -15812,10 +26288,10 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// 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 yeild better performance since
+ // in an array of new options will yield better performance since
// the children will only be loaded once.
- if(!dojo.isArray(newOption)){ newOption = [newOption]; }
- dojo.forEach(newOption, function(i){
+ 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]; }
@@ -15833,8 +26309,8 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// function returns the original store, in case you want to reuse
// it or something.
// store: dojo.data.api.Identity
- // The store you would like to use - it MUST implement Identity,
- // and MAY implement Notification.
+ // The store you would like to use - 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
@@ -15844,13 +26320,14 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
fetchArgs = fetchArgs || {};
if(oStore !== store){
// Our store has changed, so update our notifications
- dojo.forEach(this._notifyConnections || [], dojo.disconnect);
- delete this._notifyConnections;
+ var h;
+ while(h = this._notifyConnections.pop()){ h.remove(); }
+
if(store && store.getFeatures()["dojo.data.api.Notification"]){
this._notifyConnections = [
- dojo.connect(store, "onNew", this, "_onNewItem"),
- dojo.connect(store, "onDelete", this, "_onDeleteItem"),
- dojo.connect(store, "onSet", this, "_onSetItem")
+ 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);
@@ -15867,28 +26344,28 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// Add our new options
if(store){
this._loadingStore = true;
- store.fetch(dojo.delegate(fetchArgs, {
+ store.fetch(lang.delegate(fetchArgs, {
onComplete: function(items, opts){
if(this.sortByLabel && !fetchArgs.sort && items.length){
- items.sort(dojo.data.util.sorter.createSortFunction([{
+ items.sort(sorter.createSortFunction([{
attribute: store.getLabelAttributes(items[0])[0]
}], store));
}
-
+
if(fetchArgs.onFetch){
items = fetchArgs.onFetch.call(this, items, opts);
}
// TODO: Add these guys as a batch, instead of separately
- dojo.forEach(items, function(i){
+ array.forEach(items, function(i){
this._addOptionForItem(i);
}, this);
-
+
// 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{
@@ -15922,30 +26399,30 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
return;
}
var opts = this.getOptions() || [];
- if(!dojo.isArray(newValue)){
+ if(!lang.isArray(newValue)){
newValue = [newValue];
}
- dojo.forEach(newValue, function(i, idx){
- if(!dojo.isObject(i)){
+ array.forEach(newValue, function(i, idx){
+ if(!lang.isObject(i)){
i = i + "";
}
if(typeof i === "string"){
- newValue[idx] = dojo.filter(opts, function(node){
+ newValue[idx] = array.filter(opts, function(node){
return node.value === i;
})[0] || {value: "", label: ""};
}
}, this);
// Make sure some sane default is set
- newValue = dojo.filter(newValue, function(i){ return i && i.value; });
+ newValue = array.filter(newValue, function(i){ return i && i.value; });
if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
newValue[0] = opts[0];
}
- dojo.forEach(opts, function(i){
- i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
+ array.forEach(opts, function(i){
+ i.selected = array.some(newValue, function(v){ return v.value === i.value; });
});
- var val = dojo.map(newValue, function(i){ return i.value; }),
- disp = dojo.map(newValue, function(i){ return i.label; });
+ var val = array.map(newValue, function(i){ return i.value; }),
+ disp = array.map(newValue, function(i){ return i.label; });
this._set("value", this.multiple ? val : val[0]);
this._setDisplay(this.multiple ? disp : disp[0]);
@@ -15957,10 +26434,10 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// summary:
// returns the displayed value of the widget
var val = this.get("value");
- if(!dojo.isArray(val)){
+ if(!lang.isArray(val)){
val = [val];
}
- var ret = dojo.map(this.getOptions(val), function(v){
+ var ret = array.map(this.getOptions(val), function(v){
if(v && "label" in v){
return v.label;
}else if(v){
@@ -15976,11 +26453,11 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// Loads the children represented by this widget's options.
// reset the menu to make it populatable on the next click
if(this._loadingStore){ return; }
- dojo.forEach(this._getChildren(), function(child){
+ array.forEach(this._getChildren(), function(child){
child.destroyRecursive();
});
// Add each menu item
- dojo.forEach(this.options, this._addOptionItem, this);
+ array.forEach(this.options, this._addOptionItem, this);
// Update states
this._updateSelection();
@@ -15991,16 +26468,16 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// Sets the "selected" class on the item for styling purposes
this._set("value", this._getValueFromOpts());
var val = this.value;
- if(!dojo.isArray(val)){
+ if(!lang.isArray(val)){
val = [val];
}
if(val && val[0]){
- dojo.forEach(this._getChildren(), function(child){
- var isSelected = dojo.some(val, function(v){
+ array.forEach(this._getChildren(), function(child){
+ var isSelected = array.some(val, function(v){
return child.option && (v === child.option.value);
});
- dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
- dijit.setWaiState(child.domNode, "selected", isSelected);
+ domClass.toggle(child.domNode, this.baseClass + "SelectedOption", isSelected);
+ child.domNode.setAttribute("aria-selected", isSelected);
}, this);
}
},
@@ -16012,7 +26489,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
var opts = this.getOptions() || [];
if(!this.multiple && opts.length){
// Mirror what a select does - choose the first one
- var opt = dojo.filter(opts, function(i){
+ var opt = array.filter(opts, function(i){
return i.selected;
})[0];
if(opt && opt.value){
@@ -16023,7 +26500,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
}
}else if(this.multiple){
// Set value to be the sum of all selected
- return dojo.map(dojo.filter(opts, function(i){
+ return array.map(array.filter(opts, function(i){
return i.selected;
}), function(i){
return i.value;
@@ -16064,8 +26541,8 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
var store = this.store;
if(!store.isItemLoaded(item)){
// We are not loaded - so let's load it and add later
- store.loadItem({item: item, onComplete: function(i){
- this._addOptionForItem(item);
+ store.loadItem({item: item, onItem: function(i){
+ this._addOptionForItem(i);
},
scope: this});
return;
@@ -16079,11 +26556,12 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// 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 = (keywordArgs || {}).value || null;
+ this._notifyConnections = [];
},
buildRendering: function(){
this.inherited(arguments);
- dojo.setSelectable(this.focusNode, false);
+ dom.setSelectable(this.focusNode, false);
},
_fillContent: function(){
@@ -16093,13 +26571,13 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// function.
var opts = this.options;
if(!opts){
- opts = this.options = this.srcNodeRef ? dojo.query(">",
+ opts = 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-" + dojo._scopeName + "-value") || node.getAttribute("value")),
+ 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?)
@@ -16112,7 +26590,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
if(!this.value){
this._set("value", this._getValueFromOpts());
}else if(this.multiple && typeof this.value == "string"){
- this_set("value", this.value.split(","));
+ this._set("value", this.value.split(","));
}
},
@@ -16134,7 +26612,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// Connects in our store, if we have one defined
this.inherited(arguments);
var store = this.store, fetchArgs = {};
- dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
+ array.forEach(["query", "queryOptions", "onFetch"], function(i){
if(this[i]){
fetchArgs[i] = this[i];
}
@@ -16151,11 +26629,12 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
destroy: function(){
// summary:
// Clean up our connections
- dojo.forEach(this._notifyConnections || [], dojo.disconnect);
+ var h;
+ while(h = this._notifyConnections.pop()){ h.remove(); }
this.inherited(arguments);
},
- _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
+ _addOptionItem: function(/*dijit.form.__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
@@ -16163,13 +26642,13 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
// in the created option widget.
},
- _removeOptionItem: function(/*dijit.form.__SelectOption*/ option){
+ _removeOptionItem: function(/*dijit.form.__SelectOption*/ /*===== option =====*/){
// summary:
// User-overridable function which, for the given option, removes
// its item from the select.
},
- _setDisplay: function(/*String or String[]*/ newDisplay){
+ _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
@@ -16189,7 +26668,7 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
return this.getOptions(this.get("value"));
},
- _pseudoLoadChildren: function(/*item[]*/ items){
+ _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.
@@ -16205,1327 +26684,51 @@ dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
}
});
-}
-
-if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit._KeyNavContainer"] = true;
-dojo.provide("dijit._KeyNavContainer");
-
-
+});
-dojo.declare("dijit._KeyNavContainer",
- dijit._Container,
- {
+},
+'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=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\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-construct", // domConstruct.create
+ "dojo/dom-geometry", // domGeometry.setMarginBox
+ "dojo/_base/event", // event.stop
+ "dojo/i18n", // i18n.getLocalization
+ "dojo/_base/lang", // lang.hitch
+ "./_FormSelectWidget",
+ "../_HasDropDown",
+ "../Menu",
+ "../MenuItem",
+ "../MenuSeparator",
+ "../Tooltip",
+ "dojo/text!./templates/Select.html",
+ "dojo/i18n!./nls/validate"
+], function(array, declare, domAttr, domClass, domConstruct, domGeometry, event, i18n, lang,
+ _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
- // summary:
- // A _Container with keyboard navigation of its children.
- // description:
- // To use this mixin, call connectKeyNavHandlers() in
- // postCreate() and call startupKeyNavChildren() in startup().
- // 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,
+ var _FormSelectWidget = dijit.form._FormSelectWidget;
+ var _HasDropDown = dijit._HasDropDown;
+ var _FormSelectWidget = dijit._FormSelectWidget;
+ var Menu = dijit.Menu;
+ var MenuItem = dijit.MenuItem;
+ var MenuSeparator = dijit.MenuSeparator;
+ var Tooltip = dijit.Tooltip;
=====*/
- // tabIndex: Integer
- // 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",
-
- _keyNavCodes: {},
-
- connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
- // summary:
- // Call in postCreate() to attach the keyboard handlers
- // to the container.
- // preKeyCodes: dojo.keys[]
- // Key codes for navigating to the previous child.
- // nextKeyCodes: dojo.keys[]
- // Key codes for navigating to the next child.
- // tags:
- // protected
-
- var keyCodes = (this._keyNavCodes = {});
- var prev = dojo.hitch(this, this.focusPrev);
- var next = dojo.hitch(this, this.focusNext);
- dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
- dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
- keyCodes[dojo.keys.HOME] = dojo.hitch(this, "focusFirstChild");
- keyCodes[dojo.keys.END] = dojo.hitch(this, "focusLastChild");
- this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
- this.connect(this.domNode, "onfocus", "_onContainerFocus");
- },
-
- startupKeyNavChildren: function(){
- // summary:
- // Call in startup() to set child tabindexes to -1
- // tags:
- // protected
- dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
- },
-
- addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
- // summary:
- // Add a child to our _Container
- dijit._KeyNavContainer.superclass.addChild.apply(this, 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
- var child = this._getFirstFocusableChild();
- if(child){ // edge case: Menu could be empty or hidden
- this.focusChild(child);
- }
- },
-
- focusLastChild: function(){
- // summary:
- // Focus the last focusable child in the container.
- // tags:
- // protected
- var child = this._getLastFocusableChild();
- if(child){ // edge case: Menu could be empty or hidden
- this.focusChild(child);
- }
- },
-
- focusNext: function(){
- // summary:
- // Focus the next widget
- // tags:
- // protected
- var child = this._getNextFocusableChild(this.focusedChild, 1);
- this.focusChild(child);
- },
-
- 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
- var child = this._getNextFocusableChild(this.focusedChild, -1);
- this.focusChild(child, true);
- },
-
- focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
- // summary:
- // Focus 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(this.focusedChild && widget !== this.focusedChild){
- this._onChildBlur(this.focusedChild);
- }
- 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._Widget*/ 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)
-
- // focus bubbles on Firefox,
- // so just make sure that focus has really gone to the container
- if(evt.target !== this.domNode){ 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
- dojo.attr(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){
- dojo.attr(this.domNode, "tabIndex", this.tabIndex);
- }
- 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();
- dojo.stopEvent(evt);
- }
- },
-
- _onChildBlur: function(/*dijit._Widget*/ widget){
- // summary:
- // Called when focus leaves a child widget to go
- // to a sibling widget.
- // tags:
- // protected
- },
-
- _getFirstFocusableChild: function(){
- // summary:
- // Returns first child that can be focused
- return this._getNextFocusableChild(null, 1); // dijit._Widget
- },
-
- _getLastFocusableChild: function(){
- // summary:
- // Returns last child that can be focused
- return this._getNextFocusableChild(null, -1); // dijit._Widget
- },
-
- _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._Widget
- }
- child = this._getSiblingOfChild(child, dir);
- }
- // no focusable child found
- return null; // dijit._Widget
- }
- }
-);
-
-}
-
-if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.MenuItem"] = true;
-dojo.provide("dijit.MenuItem");
-
-
-
-
-
-
-dojo.declare("dijit.MenuItem",
- [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
- {
- // summary:
- // A line item in a Menu Widget
-
- // Make 3 columns
- // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
- templateString: dojo.cache("dijit", "templates/MenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div dojoAttachPoint=\"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"),
-
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- label: { node: "containerNode", type: "innerHTML" },
- iconClass: { node: "iconNode", type: "class" }
- }),
-
- baseClass: "dijitMenuItem",
-
- // label: String
- // Menu text
- label: '',
-
- // iconClass: String
- // Class to apply to DOMNode to make it display an icon.
- iconClass: "",
-
- // 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";
- dojo.attr(this.containerNode, "id", label);
- if(this.accelKeyNode){
- dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
- label += " " + this.id + "_accel";
- }
- dijit.setWaiState(this.domNode, "labelledby", label);
- dojo.setSelectable(this.domNode, false);
- },
-
- _onHover: function(){
- // summary:
- // Handler when mouse is moved onto menu item
- // tags:
- // protected
- this.getParent().onItemHover(this);
- },
-
- _onUnhover: function(){
- // summary:
- // Handler when mouse is moved off of menu item,
- // possibly to a child menu, or maybe to a sibling
- // menuitem or somewhere else entirely.
- // tags:
- // protected
-
- // if we are unhovering the currently selected item
- // then unselect it
- this.getParent().onItemUnhover(this);
-
- // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
- // FF and IE don't generate an onmouseout event for the MenuItem.
- // So, help out _CssStateMixin in this case.
- this._set("hovering", false);
- },
-
- _onClick: function(evt){
- // summary:
- // Internal handler for click events on MenuItem.
- // tags:
- // private
- this.getParent().onItemClick(this, evt);
- dojo.stopEvent(evt);
- },
-
- onClick: function(/*Event*/ evt){
- // summary:
- // User defined function to handle clicks
- // tags:
- // callback
- },
-
- focus: function(){
- // summary:
- // Focus on this MenuItem
- try{
- if(dojo.isIE == 8){
- // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
- this.containerNode.focus();
- }
- dijit.focus(this.focusNode);
- }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()
- */
-
- dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
- },
-
- setLabel: function(/*String*/ content){
- // summary:
- // Deprecated. Use set('label', ...) instead.
- // tags:
- // deprecated
- dojo.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
- dojo.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.
-
- dijit.setWaiState(this.focusNode, '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
- dojo.attr(this.containerNode,'colSpan',value?"1":"2");
-
- this._set("accelKey", value);
- }
- });
-
-}
-
-if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.PopupMenuItem"] = true;
-dojo.provide("dijit.PopupMenuItem");
-
-
-
-dojo.declare("dijit.PopupMenuItem",
- dijit.MenuItem,
- {
- _fillContent: function(){
- // summary:
- // When Menu is declared in markup, this code gets the menu label and
- // the popup widget from the srcNodeRef.
- // description:
- // srcNodeRefinnerHTML contains both the menu item text and a popup widget
- // The first part holds the menu item text and the second part is the popup
- // example:
- // | <div dojoType="dijit.PopupMenuItem">
- // | <span>pick me</span>
- // | <popup> ... </popup>
- // | </div>
- // tags:
- // protected
-
- if(this.srcNodeRef){
- var nodes = dojo.query("*", this.srcNodeRef);
- dijit.PopupMenuItem.superclass._fillContent.call(this, 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; }
- this.inherited(arguments);
-
- // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
- // land now. move it to dojo.doc.body.
- if(!this.popup){
- var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
- this.popup = dijit.byNode(node);
- }
- dojo.body().appendChild(this.popup.domNode);
- this.popup.startup();
-
- this.popup.domNode.style.display="none";
- if(this.arrowWrapper){
- dojo.style(this.arrowWrapper, "visibility", "");
- }
- dijit.setWaiState(this.focusNode, "haspopup", "true");
- },
-
- destroyDescendants: function(){
- if(this.popup){
- // Destroy the popup, unless it's already been destroyed. This can happen because
- // the popup is a direct child of <body> even though it's logically my child.
- if(!this.popup._destroyed){
- this.popup.destroyRecursive();
- }
- delete this.popup;
- }
- this.inherited(arguments);
- }
- });
-
-}
-
-if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.CheckedMenuItem"] = true;
-dojo.provide("dijit.CheckedMenuItem");
-
-
-
-dojo.declare("dijit.CheckedMenuItem",
- dijit.MenuItem,
- {
- // summary:
- // A checkbox-like menu item for toggling on and off
-
- templateString: dojo.cache("dijit", "templates/CheckedMenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&nbsp;</td>\n</tr>\n"),
-
- // checked: Boolean
- // Our checked state
- checked: false,
- _setCheckedAttr: function(/*Boolean*/ checked){
- // summary:
- // Hook so attr('checked', bool) works.
- // Sets the class and state for the check box.
- dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
- dijit.setWaiState(this.domNode, "checked", checked);
- this._set("checked", checked);
- },
-
- onChange: function(/*Boolean*/ checked){
- // summary:
- // User defined function to handle check/uncheck events
- // tags:
- // callback
- },
-
- _onClick: function(/*Event*/ e){
- // summary:
- // Clicking this item just toggles its state
- // tags:
- // private
- if(!this.disabled){
- this.set("checked", !this.checked);
- this.onChange(this.checked);
- }
- this.inherited(arguments);
- }
- });
-
-}
-
-if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.MenuSeparator"] = true;
-dojo.provide("dijit.MenuSeparator");
-
-
-
-
-
-dojo.declare("dijit.MenuSeparator",
- [dijit._Widget, dijit._Templated, dijit._Contained],
- {
- // summary:
- // A line between two menu items
-
- templateString: dojo.cache("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>\n"),
-
- buildRendering: function(){
- this.inherited(arguments);
- dojo.setSelectable(this.domNode, false);
- },
-
- isFocusable: function(){
- // summary:
- // Override to always return false
- // tags:
- // protected
-
- return false; // Boolean
- }
- });
-
-}
-
-if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.Menu"] = true;
-dojo.provide("dijit.Menu");
-
-
-
-
-
-
-
-
-
-
-// "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
-
-dojo.declare("dijit._MenuBase",
- [dijit._Widget, dijit._Templated, dijit._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,
-
- startup: function(){
- if(this._started){ return; }
-
- dojo.forEach(this.getChildren(), function(child){ child.startup(); });
- this.startupKeyNavChildren();
-
- 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.focusedChild._onClick(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 = setTimeout(dojo.hitch(this, "_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;
- },
-
- _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 = setTimeout(function(){
- itemPopup._pendingClose_timer = null;
- if(itemPopup.parentMenu){
- itemPopup.parentMenu.currentPopup = null;
- }
- dijit.popup.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; }
- },
-
- _stopPopupTimer: function(){
- // summary:
- // Cancels the popup timer because the user has stop hovering
- // on the MenuItem, etc.
- // tags:
- // private
- if(this.hover_timer){
- clearTimeout(this.hover_timer);
- this.hover_timer = null;
- }
- },
-
- _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
- // summary:
- // Cancels the pending-close timer because the close has been preempted
- // tags:
- // private
- if(popup._pendingClose_timer){
- clearTimeout(popup._pendingClose_timer);
- popup._pendingClose_timer = null;
- }
- },
-
- _stopFocusTimer: function(){
- // summary:
- // Cancels the pending-focus timer because the menu was closed before focus occured
- // tags:
- // private
- if(this._focus_timer){
- clearTimeout(this._focus_timer);
- this._focus_timer = null;
- }
- },
-
- _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._Widget*/ 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();
- }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(evt);
- }
- },
-
- _openPopup: function(){
- // summary:
- // Open the popup to the side of/underneath the current menu 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){ return; }
- if(this.currentPopup){
- this._stopPendingCloseTimer(this.currentPopup);
- dijit.popup.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;
- dijit.popup.open({
- parent: this,
- popup: popup,
- around: from_item.domNode,
- orient: this._orient || (this.isLeftToRight() ?
- {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
- {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
- 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: dojo.hitch(this, "_cleanUp")
- });
-
- this.currentPopup = popup;
- // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
- popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
-
- if(popup.focus){
- // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
- // if the cursor happens to collide with the popup, it will generate an onmouseover event
- // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
- // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
- popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
- this._focus_timer = null;
- this.focus();
- }), 0);
- }
- },
-
- _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;
- dojo.replaceClass(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
- dojo.replaceClass(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();
-
- var fromItem = this.focusedChild && this.focusedChild.from_item;
-
- if(this.currentPopup){
- // If focus is on my child menu then move focus to me,
- // because IE doesn't like it when you display:none a node with focus
- if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
- this.focusedChild.focusNode.focus();
- }
- // Close all popups that are open and descendants of this menu
- dijit.popup.close(this.currentPopup);
- this.currentPopup = null;
- }
-
- if(this.focusedChild){ // unhighlight the focused item
- this.focusedChild._setSelected(false);
- this.focusedChild._onUnhover();
- 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._hoveredChild._onUnhover(); // 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();
- }
- }
-});
-
-dojo.declare("dijit.Menu",
- dijit._MenuBase,
- {
- // summary
- // A context menu you can assign to multiple elements
-
- // TODO: most of the code in here is just for context menu (right-click menu)
- // support. In retrospect that should have been a separate class (dijit.ContextMenu).
- // Split them for 2.0
-
- constructor: function(){
- this._bindings = [];
- },
-
- templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
-
- baseClass: "dijitMenu",
-
- // 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: [],
-
- // 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, similiar 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(dojo.body());
- }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)
- dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
- }
- var k = dojo.keys, l = this.isLeftToRight();
- this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
- this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
- this.connectKeyNavHandlers([k.UP_ARROW], [k.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);
- dojo.stopEvent(evt);
- break;
- case this._closeSubMenuKey:
- if(this.parentMenu){
- if(this.parentMenu._isMenuBar){
- this.parentMenu.focusPrev();
- }else{
- this.onCancel(false);
- }
- }else{
- dojo.stopEvent(evt);
- }
- break;
- }
- },
-
- // thanks burstlib!
- _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
- // summary:
- // Returns the window reference of the passed iframe
- // tags:
- // private
- var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
- // Moz. TODO: is this available when defaultView isn't?
- this._iframeContentDocument(iframe_el)['__parent__'] ||
- (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
- return win; // Window
- },
-
- _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
- // summary:
- // Returns a reference to the document object inside iframe_el
- // tags:
- // protected
- var doc = iframe_el.contentDocument // W3
- || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
- || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
- || null;
- return doc; // HTMLDocument
- },
-
- bindDomNode: function(/*String|DomNode*/ node){
- // summary:
- // Attach menu to given node
- node = dojo.byId(node);
-
- 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,
- win = this._iframeContentWindow(iframe);
- cn = dojo.withGlobal(win, dojo.body);
- }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 == dojo.body() ? dojo.doc.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.
- dojo.attr(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 = dojo.hitch(this, function(cn){
- return [
- // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
- // rather than shift-F10?
- dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
- // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
- dojo.stopEvent(evt);
- this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
- }),
- dojo.connect(cn, "onkeydown", this, function(evt){
- if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
- dojo.stopEvent(evt);
- this._scheduleOpen(evt.target, 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 dojo.connect(), see #9609.
-
- binding.onloadHandler = dojo.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 win = this._iframeContentWindow(iframe);
- cn = dojo.withGlobal(win, dojo.body);
- 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 = dojo.byId(nodeName);
- }catch(e){
- // On IE the dojo.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 && dojo.hasAttr(node, attrName)){
- var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
- dojo.forEach(b.connects, dojo.disconnect);
-
- // 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);
- }
- }
-
- dojo.removeAttr(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 dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the
- // oncontextmenu event.)
-
- if(!this._openTimer){
- this._openTimer = setTimeout(dojo.hitch(this, 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;
-
- // 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 od = target.ownerDocument,
- ifc = dojo.position(iframe, true),
- win = this._iframeContentWindow(iframe),
- scroll = dojo.withGlobal(win, "_docScroll", dojo);
-
- var cs = dojo.getComputedStyle(iframe),
- tp = dojo._toPixelValue,
- left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
- top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
-
- coords.x += ifc.x + left - scroll.x;
- coords.y += ifc.y + top - scroll.y;
- }
- }else{
- coords = dojo.position(target, true);
- coords.x += 10;
- coords.y += 10;
- }
-
- var self=this;
- var savedFocus = dijit.getFocus(this);
- function closeAndRestoreFocus(){
- // user has clicked on a menu or popup
- if(self.refocus){
- dijit.focus(savedFocus);
- }
- dijit.popup.close(self);
- }
- dijit.popup.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
- dijit.popup.close(this);
- // don't try to restore focus; user has clicked another part of the screen
- // and set focus there
- };
- },
-
- uninitialize: function(){
- dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
- this.inherited(arguments);
- }
-}
-);
-
-}
-
-if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.Select"] = true;
-dojo.provide("dijit.form.Select");
-
-
-
-
-
+// module:
+// dijit/form/Select
+// summary:
+// This is a "styleable" select box - it is basically a DropDownButton which
+// can take a <select> as its input.
-dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
+var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
// summary:
// An internally-used menu for dropdown that allows us a vertical scrollbar
buildRendering: function(){
@@ -17534,25 +26737,25 @@ dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
// otherwise, we won't respond correctly to heights/overflows
this.inherited(arguments);
var o = (this.menuTableNode = this.domNode);
- var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
+ var n = (this.domNode = domConstruct.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
if(o.parentNode){
o.parentNode.replaceChild(n, o);
}
- dojo.removeClass(o, "dijitMenuTable");
+ domClass.remove(o, "dijitMenuTable");
n.className = o.className + " dijitSelectMenu";
o.className = "dijitReset dijitMenuTable";
- dijit.setWaiRole(o,"listbox");
- dijit.setWaiRole(n,"presentation");
+ 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
+ // stop mousemove from selecting text on IE to be consistent with other browsers
this.inherited(arguments);
- this.connect(this.domNode, "onmousemove", dojo.stopEvent);
+ this.connect(this.domNode, "onmousemove", event.stop);
},
resize: function(/*Object*/ mb){
@@ -17565,7 +26768,7 @@ dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
// mb: Object
// The margin box to set this dropdown to.
if(mb){
- dojo.marginBox(this.domNode, 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
@@ -17576,25 +26779,21 @@ dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
}
});
-dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
+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",
- templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" dojoAttachPoint=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} dojoAttachPoint=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
-
- // attributeMap: Object
- // Add in our style to be applied to the focus node
- attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
+ templateString: template,
// required: Boolean
// Can be true or false, default is false.
required: false,
- // state: String
- // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
+ // state: [readonly] String
+ // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
state: "",
// message: String
@@ -17607,7 +26806,7 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
// emptyLabel: string
// What to display in an "empty" dropdown
- emptyLabel: "&nbsp;",
+ emptyLabel: "&#160;", // &nbsp;
// _isLoaded: Boolean
// Whether or not we have been loaded
@@ -17627,8 +26826,8 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
this.value = this.options[si >= 0 ? si : 0].value;
}
// Create the dropDown widget
- this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
- dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
+ this.dropDown = new _SelectMenu({id: this.id + "_menu"});
+ domClass.add(this.dropDown.domNode, this.baseClass + "Menu");
},
_getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
@@ -17637,17 +26836,17 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
// 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 dijit.MenuSeparator();
+ return new MenuSeparator();
}else{
// Just a regular menu option
- var click = dojo.hitch(this, "_setValueAttr", option);
- var item = new dijit.MenuItem({
+ var click = lang.hitch(this, "_setValueAttr", option);
+ var item = new MenuItem({
option: option,
label: option.label || this.emptyLabel,
onClick: click,
disabled: option.disabled || false
});
- dijit.setWaiRole(item.focusNode, "listitem");
+ item.focusNode.setAttribute("role", "listitem");
return item;
}
},
@@ -17689,8 +26888,8 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
}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)
- dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
- var item = new dijit.MenuItem({label: "&nbsp;"});
+ array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
+ var item = new MenuItem({label: "&#160;"});
this.dropDown.addChild(item);
}
}else{
@@ -17708,7 +26907,19 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
_setValueAttr: function(value){
this.inherited(arguments);
- dojo.attr(this.valueNode, "value", this.get("value"));
+ domAttr.set(this.valueNode, "value", this.get("value"));
+ this.validate(this.focused); // to update this.state
+ },
+
+ _setDisabledAttr: function(/*Boolean*/ value){
+ this.inherited(arguments);
+ this.validate(this.focused); // to update this.state
+ },
+
+ _setRequiredAttr: function(/*Boolean*/ value){
+ this._set("required", value);
+ this.focusNode.setAttribute("aria-required", value);
+ this.validate(this.focused); // to update this.state
},
_setDisplay: function(/*String*/ newDisplay){
@@ -17716,32 +26927,31 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
// sets the display for the given value (or values)
var lbl = newDisplay || this.emptyLabel;
this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
- dijit.setWaiState(this.focusNode, "valuetext", lbl);
+ this.focusNode.setAttribute("aria-valuetext", lbl);
},
validate: function(/*Boolean*/ isFocused){
// summary:
- // Called by oninit, onblur, and onkeypress.
+ // 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.isValid(isFocused);
- this._set("state", isValid ? "" : "Error");
- dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
+
+ var isValid = this.disabled || this.isValid(isFocused);
+ this._set("state", isValid ? "" : "Incomplete");
+ this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
var message = isValid ? "" : this._missingMsg;
- if(this.message !== message){
- this._set("message", message);
- dijit.hideTooltip(this.domNode);
- if(message){
- dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
- }
+ 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){
+ 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.
@@ -17752,16 +26962,15 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
// summary:
// Overridden so that the state will be cleared.
this.inherited(arguments);
- dijit.hideTooltip(this.domNode);
- this._set("state", "");
- this._set("message", "")
+ Tooltip.hide(this.domNode);
+ this.validate(this.focused); // to update this.state
},
postMixInProperties: function(){
// summary:
// set the missing message
this.inherited(arguments);
- this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
+ this._missingMsg = i18n.getLocalization("dijit.form", "validate",
this.lang).missingMessage;
},
@@ -17771,12 +26980,12 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
this.inherited(arguments);
- this.connect(this.domNode, "onmousemove", dojo.stopEvent);
+ this.connect(this.domNode, "onmousemove", event.stop);
},
_setStyleAttr: function(/*String||Object*/ value){
this.inherited(arguments);
- dojo.toggleClass(this.domNode, this.baseClass + "FixedWidth", !!this.tableNode.style.width);
+ domClass.toggle(this.domNode, this.baseClass + "FixedWidth", !!this.domNode.style.width);
},
isLoaded: function(){
@@ -17809,5780 +27018,3530 @@ dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropD
delete this.dropDown;
}
this.inherited(arguments);
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.form.SimpleTextarea"] = true;
-dojo.provide("dijit.form.SimpleTextarea");
-
-
-
-dojo.declare("dijit.form.SimpleTextarea",
- dijit.form.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 dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
- //
- // example:
- // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
-
- baseClass: "dijitTextBox dijitTextArea",
-
- attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
- rows:"textbox", cols: "textbox"
- }),
-
- // rows: Number
- // The number of rows of text.
- rows: "3",
-
- // rows: Number
- // The number of characters per line.
- cols: "20",
-
- templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='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(){
+ _onFocus: function(){
+ this.validate(true); // show tooltip if second focus of required tooltip, but no selection
this.inherited(arguments);
- if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
- dojo.addClass(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);
- },
-
- _previousValue: "",
- _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){
- if(e){ dojo.stopEvent(e); }
- var textarea = this.textbox;
- if(textarea.selectionStart){
- var pos = textarea.selectionStart;
- var cr = 0;
- if(dojo.isOpera){
- 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(dojo.doc.selection){ //IE
- textarea.focus();
- var range = dojo.doc.selection.createRange();
- // delete overflow characters
- range.moveStart("character", -overflow);
- range.text = '';
- // show cursor
- range.select();
- }
- }
- this._previousValue = this.textbox.value;
- }
+ _onBlur: function(){
+ Tooltip.hide(this.domNode);
this.inherited(arguments);
}
});
-}
-
-if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.InlineEditBox"] = true;
-dojo.provide("dijit.InlineEditBox");
-
-
-
-
+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
+ // summary:
+ // The module defines a query results wrapper
+var util = lang.getObject("dojo.store.util", true);
-dojo.declare("dijit.InlineEditBox",
- dijit._Widget,
- {
+util.QueryResults = function(results){
// summary:
- // An element with in-line edit capabilites
+ // A function that wraps the results of a store query with additional
+ // methods.
//
// 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
- // Class name (or reference to the Class) for Editor widget
- editor: "dijit.form.TextBox",
-
- // editorWrapper: String|Function
- // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
- // buttons.
- editorWrapper: "dijit._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%",
+ // 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`.
+ //
+ // returns: Object
+ // 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
+ // | });
- // value: String
- // The display value of the widget in read-only mode
- value: "",
+ 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 util.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;
+};
- // 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: dojo.isIE <= 6 ? // font-family needed on IE6 but it messes up IE8
- "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>" :
- "<span style='text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
+return util.QueryResults;
+});
- constructor: function(){
- // summary:
- // Sets up private arrays etc.
- // tags:
- // private
- this.editorParams = {};
- },
+},
+'dijit/form/_ListBase':function(){
+define("dijit/form/_ListBase", [
+ "dojo/_base/declare", // declare
+ "dojo/window" // winUtils.scrollIntoView
+], function(declare, winUtils){
+
+// module:
+// dijit/form/_ListBase
+// summary:
+// Focus-less menu to handle UI events consistently
- postMixInProperties: function(){
- this.inherited(arguments);
+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
- // save pointer to original source node, since Widget nulls-out srcNodeRef
- this.displayNode = this.srcNodeRef;
+ // selected: DOMnode
+ // currently selected node
+ selected: null,
- // 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]);
- }
- dijit.setWaiRole(this.displayNode, "button");
- if(!this.displayNode.getAttribute("tabIndex")){
- this.displayNode.setAttribute("tabIndex", 0);
+ _getTarget: function(/*Event*/ evt){
+ var tgt = evt.target;
+ var container = this.containerNode;
+ if(tgt == container || tgt == this.domNode){ return null; }
+ while(tgt && tgt.parentNode != container){
+ // recurse to the top
+ tgt = tgt.parentNode;
}
-
- if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
- this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML :
- (this.displayNode.innerText||this.displayNode.textContent||""));
- }
- if(!this.value){
- this.displayNode.innerHTML = this.noValueIndicator;
- }
-
- dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode');
+ return tgt;
},
- setDisabled: function(/*Boolean*/ disabled){
+ selectFirstNode: function(){
// summary:
- // Deprecated. Use set('disabled', ...) instead.
- // tags:
- // deprecated
- dojo.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.
- dijit.setWaiState(this.domNode, "disabled", disabled);
- if(disabled){
- this.displayNode.removeAttribute("tabIndex");
- }else{
- this.displayNode.setAttribute("tabIndex", 0);
+ // Select the first displayed item in the list.
+ var first = this.containerNode.firstChild;
+ while(first && first.style.display == "none"){
+ first = first.nextSibling;
}
- dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
- this._set("disabled", disabled);
+ this._setSelectedAttr(first);
},
- _onMouseOver: function(){
+ selectLastNode: function(){
// summary:
- // Handler for onmouseover and onfocus event.
- // tags:
- // private
- if(!this.disabled){
- dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
+ // 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);
},
- _onMouseOut: function(){
+ selectNextNode: function(){
// summary:
- // Handler for onmouseout and onblur event.
- // tags:
- // private
- dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
- },
-
- _onClick: function(/*Event*/ e){
- // summary:
- // Handler for onclick event.
- // tags:
- // private
- if(this.disabled){ return; }
- if(e){ dojo.stopEvent(e); }
- this._onMouseOut();
-
- // Since FF gets upset if you move a node while in an event handler for that node...
- setTimeout(dojo.hitch(this, "edit"), 0);
- },
-
- edit: function(){
- // summary:
- // Display the editor widget in place of the original (read only) markup.
- // tags:
- // private
-
- if(this.disabled || this.editing){ return; }
- this.editing = true;
-
- // save some display node values that can be restored later
- this._savedPosition = dojo.style(this.displayNode, "position") || "static";
- this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1";
- this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0";
-
- if(this.wrapperWidget){
- var ew = this.wrapperWidget.editWidget;
- ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
+ // Select the item just below the current selection.
+ // If nothing selected, select first node.
+ var selectedNode = this._getSelectedAttr();
+ if(!selectedNode){
+ this.selectFirstNode();
}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 = dojo.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" ? dojo.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: dojo.getComputedStyle(this.displayNode),
- save: dojo.hitch(this, "save"),
- cancel: dojo.hitch(this, "cancel")
- }, placeholder);
- if(!this._started){
- this.startup();
+ var next = selectedNode.nextSibling;
+ while(next && next.style.display == "none"){
+ next = next.nextSibling;
+ }
+ if(!next){
+ this.selectFirstNode();
+ }else{
+ this._setSelectedAttr(next);
}
}
- var ww = this.wrapperWidget;
-
- if(dojo.isIE){
- dijit.focus(dijit.getFocus()); // IE (at least 8) needs help with tab order changes
- }
- // 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
- // visiblity:hidden removes focus outline
-
- dojo.style(this.displayNode, { position: "absolute", opacity: "0", display: "none" }); // makes display node invisible, display style used for focus-ability
- dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
- dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
-
- // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
- // focus can be shifted without incident. (browser may needs some time to render the editor.)
- setTimeout(dojo.hitch(this, function(){
- ww.focus(); // both nodes are showing, so we can switch focus safely
- ww._resetValue = ww.getValue();
- }), 0);
},
- _onBlur: function(){
+ selectPreviousNode: 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...
- setTimeout(dojo.hitch(this, function(){
- if(this.wrapperWidget){
- this.wrapperWidget.destroy();
- delete this.wrapperWidget;
- }
- }), 0);
- */
- }
- },
-
- destroy: function(){
- if(this.wrapperWidget && !this.wrapperWidget._destroyed){
- this.wrapperWidget.destroy();
- delete this.wrapperWidget;
+ // 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._getSelectedAttr();
+ 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);
+ }
}
- this.inherited(arguments);
},
- _showText: function(/*Boolean*/ focus){
+ _setSelectedAttr: function(/*DomNode*/ node){
// summary:
- // Revert to display mode, and optionally focus on display node
- // tags:
- // private
-
- var ww = this.wrapperWidget;
- dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
- dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity, display: "" }); // make the original text visible
- dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex);
- if(focus){
- dijit.focus(this.displayNode);
+ // Does the actual select.
+ if(this.selected != node){
+ var selectedNode = this._getSelectedAttr();
+ if(selectedNode){
+ this.onDeselect(selectedNode);
+ this.selected = null;
+ }
+ if(node && node.parentNode == this.containerNode){
+ this.selected = node;
+ winUtils.scrollIntoView(node);
+ this.onSelect(node);
+ }
+ }else if(node){
+ this.onSelect(node);
}
},
- save: function(/*Boolean*/ focus){
+ _getSelectedAttr: function(){
// 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.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
- dojo.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 = dojo.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);
+ // Returns the selected node.
+ var v = this.selected;
+ return (v && v.parentNode == this.containerNode) ? v : (this.selected = null);
+ }
+});
- if(this._started){
- // tell the world that we have changed
- setTimeout(dojo.hitch(this, "onChange", val), 0); // setTimeout prevents browser freeze for long-running event handlers
- }
- },
+});
- getValue: function(){
- // summary:
- // Deprecated. Use get('value') instead.
- // tags:
- // deprecated
- dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
- return this.get("value");
- },
+},
+'dijit/form/_FormWidget':function(){
+define("dijit/form/_FormWidget", [
+ "dojo/_base/declare", // declare
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/ready",
+ "../_Widget",
+ "../_CssStateMixin",
+ "../_TemplatedMixin",
+ "./_FormWidgetMixin"
+], function(declare, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){
- cancel: function(/*Boolean*/ focus){
- // summary:
- // Revert to display mode, discarding any changes made in the editor
- // tags:
- // private
+/*=====
+var _Widget = dijit._Widget;
+var _TemplatedMixin = dijit._TemplatedMixin;
+var _CssStateMixin = dijit._CssStateMixin;
+var _FormWidgetMixin = dijit.form._FormWidgetMixin;
+=====*/
- if(this.disabled || !this.editing){ return; }
- this.editing = false;
+// module:
+// dijit/form/_FormWidget
+// summary:
+// FormWidget
- // tell the world that we have no changes
- setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
- this._showText(focus);
- }
-});
+// Back compat w/1.6, remove for 2.0
+if(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/form/_FormValueWidget"];
+ require(requires); // use indirection so modules not rolled into a build
+ });
+}
-dojo.declare(
- "dijit._InlineEditor",
- [dijit._Widget, dijit._Templated],
-{
+return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
// 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:
+ // 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.
//
- // style: Object
- // Set of CSS attributes of display node, to replicate in editor
+ // description:
+ // Represents a single HTML element.
+ // All these widgets should have these attributes just like native HTML input elements.
+ // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
//
- // value: String
- // Value as an HTML string or plain text string, depending on renderAsHTML flag
-
- templateString: dojo.cache("dijit", "templates/InlineEditBox.html", "<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\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"),
- widgetsInTemplate: true,
-
- postMixInProperties: function(){
- this.inherited(arguments);
- this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
- dojo.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
- var cls = typeof this.editor == "string" ? dojo.getObject(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 = dojo.getComputedStyle(this.domNode);
- dojo.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);
- dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], 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 = dojo.delegate(this.inlineEditBox.editorParams, {
- style: editStyle,
- dir: this.dir,
- lang: this.lang
- });
- 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
- dojo.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);
- }
- }
- },
+ // They also share some common methods.
- _onIntermediateChange: function(val){
+ setDisabled: function(/*Boolean*/ disabled){
// 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);
+ // Deprecated. Use set('disabled', ...) instead.
+ kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
+ this.set('disabled', disabled);
},
- getValue: function(){
+ setValue: function(/*String*/ value){
// summary:
- // Return the [display] value of the edit widget
- var ew = this.editWidget;
- return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
+ // 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);
},
- _onKeyPress: function(e){
+ getValue: function(){
// 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 == dojo.keys.ESCAPE){
- dojo.stopEvent(e);
- this.cancel(true); // sets editing=false which short-circuits _onBlur processing
- }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){
- dojo.stopEvent(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.
- }
+ // Deprecated. Use get('value') instead.
+ kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
+ return this.get('value');
},
- _onBlur: function(){
- // summary:
- // Called when focus moves outside the editor
- // tags:
- // private
-
+ 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);
- 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()){
- dojo.style(this.inlineEditBox.displayNode, { display: "" });
- dijit.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();
- setTimeout(dojo.hitch(this, function(){
- if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
- dijit.selectInputText(this.editWidget.focusNode);
- }
- }), 0);
- }
+ // 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
});
-}
-
-if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.cookie"] = true;
-dojo.provide("dojo.cookie");
-
-
+});
-/*=====
-dojo.__cookieProps = function(){
- // 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. << FIXME: 0 seems to disappear right away? FF3.
- // 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
- this.expires = expires;
- this.path = path;
- this.domain = domain;
- this.secure = secure;
-}
-=====*/
+},
+'dojo/DeferredList':function(){
+define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray) {
+ // module:
+ // dojo/DeferredList
+ // summary:
+ // TODOC
-dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__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:
- // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
- //
- // example:
- // de-serialize a cookie back into a JavaScript object:
- // | var config = dojo.fromJson(dojo.cookie("configObj"));
+dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
+ // summary:
+ // Provides event handling for a group of Deferred objects.
+ // description:
+ // DeferredList takes an array of existing deferreds and returns a new deferred of its own
+ // this new deferred will typically have its callback fired when all of the deferreds in
+ // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
+ // fireOnOneErrback, will fire before all the deferreds as appropriate
//
- // example:
- // delete a cookie:
- // | dojo.cookie("configObj", null, {expires: -1});
- var c = document.cookie;
- if(arguments.length == 1){
- var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
- return matches ? decodeURIComponent(matches[1]) : undefined; // String or 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;
+ // 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, []]);
}
-};
-
-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.
+ 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);
+ }
- if(!("cookieEnabled" in navigator)){
- this("__djCookieTest__", "CookiesAllowed");
- navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
- if(navigator.cookieEnabled){
- this("__djCookieTest__", "", {expires: -1});
}
- }
- return navigator.cookieEnabled;
+ });
};
+dojo.DeferredList.prototype = new Deferred();
-}
-
-if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.StackController"] = true;
-dojo.provide("dijit.layout.StackController");
-
-
-
-
-
-
-
-dojo.declare(
- "dijit.layout.StackController",
- [dijit._Widget, dijit._Templated, dijit._Container],
- {
- // summary:
- // Set of buttons to select a page in a page list.
- // description:
- // Monitors the specified StackContainer, and whenever a page is
- // added, deleted, or selected, updates itself accordingly.
-
- templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
-
- // containerId: [const] String
- // The id of the page container that I point to
- containerId: "",
-
- // buttonWidget: [const] String
- // The name of the button widget to create to correspond to each page
- buttonWidget: "dijit.layout._StackButton",
-
- constructor: function(){
- this.pane2button = {}; // mapping from pane id to buttons
- this.pane2connects = {}; // mapping from pane id to this.connect() handles
- this.pane2watches = {}; // mapping from pane id to watch() handles
- },
-
- buildRendering: function(){
- this.inherited(arguments);
- dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above.
- },
+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.
- postCreate: function(){
- this.inherited(arguments);
+ 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;
+};
- // Listen to notifications from StackContainer
- 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");
- },
+return dojo.DeferredList;
+});
- onStartup: function(/*Object*/ info){
- // summary:
- // Called after StackContainer has finished initializing
- // tags:
- // private
- dojo.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);
- }
- },
+},
+'dojo/dnd/common':function(){
+define("dojo/dnd/common", ["../main"], function(dojo) {
+ // module:
+ // dojo/dnd/common
+ // summary:
+ // TODOC
- destroy: function(){
- for(var pane in this.pane2button){
- this.onRemoveChild(dijit.byId(pane));
- }
- this.inherited(arguments);
- },
+dojo.getObject("dnd", true, dojo);
- onAddChild: function(/*dijit._Widget*/ 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
- var cls = dojo.getObject(this.buttonWidget);
- var button = new cls({
- id: this.id + "_" + page.id,
- label: page.title,
- dir: page.dir,
- lang: page.lang,
- showLabel: page.showTitle,
- iconClass: page.iconClass,
- closeButton: page.closable,
- title: page.tooltip
- });
- dijit.setWaiState(button.focusNode,"selected", "false");
+dojo.dnd.getCopyKeyState = dojo.isCopyKey;
+dojo.dnd._uniqueId = 0;
+dojo.dnd.getUniqueId = function(){
+ // summary:
+ // returns a unique string for use with any DOM element
+ var id;
+ do{
+ id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
+ }while(dojo.byId(id));
+ return id;
+};
- // map from page attribute to corresponding tab button attribute
- var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
- buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
+dojo.dnd._empty = {};
- // watch() so events like page title changes are reflected in tab button
- this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
- return page.watch(pageAttr, function(name, oldVal, newVal){
- button.set(buttonAttrList[idx], newVal);
- });
- });
-
- // connections so that clicking a tab button selects the corresponding page
- this.pane2connects[page.id] = [
- this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
- this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
- ];
+dojo.dnd.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
+};
- 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){ // put the first child into the tab order
- button.focusNode.setAttribute("tabIndex", "0");
- dijit.setWaiState(button.focusNode, "selected", "true");
- this._currentChild = page;
- }
- // make sure all tabs have the same length
- if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
- this._rectifyRtlTabList();
- }
- },
+return dojo.dnd;
+});
- onRemoveChild: function(/*dijit._Widget*/ 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; }
-
- // disconnect/unwatch connections/watches related to page being removed
- dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
- delete this.pane2connects[page.id];
- dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
- delete this.pane2watches[page.id];
-
- var button = this.pane2button[page.id];
- if(button){
- this.removeChild(button);
- delete this.pane2button[page.id];
- button.destroy();
- }
- delete page.controlButton;
- },
+},
+'dijit/_base/place':function(){
+define("dijit/_base/place", [
+ "dojo/_base/array", // array.forEach
+ "dojo/_base/lang", // lang.isArray
+ "dojo/window", // windowUtils.getBox
+ "../place",
+ ".." // export to dijit namespace
+], function(array, lang, windowUtils, place, dijit){
+
+ // module:
+ // dijit/_base/place
+ // summary:
+ // Back compatibility module, new code should use dijit/place directly instead of using this module.
- onSelectChild: function(/*dijit._Widget*/ page){
- // summary:
- // Called when a page has been selected in the StackContainer, either by me or by another StackController
- // tags:
- // private
+ dijit.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()
- if(!page){ return; }
+ return windowUtils.getBox();
+ };
- if(this._currentChild){
- var oldButton=this.pane2button[this._currentChild.id];
- oldButton.set('checked', false);
- dijit.setWaiState(oldButton.focusNode, "selected", "false");
- oldButton.focusNode.setAttribute("tabIndex", "-1");
- }
+ /*=====
+ dijit.placeOnScreen = function(node, pos, corners, padding){
+ // summary:
+ // Positions one of the node's corners at specified position
+ // such that node is fully visible in viewport.
+ // Deprecated, new code should use dijit.place.at() instead.
+ };
+ =====*/
+ dijit.placeOnScreen = place.at;
- var newButton=this.pane2button[page.id];
- newButton.set('checked', true);
- dijit.setWaiState(newButton.focusNode, "selected", "true");
- this._currentChild = page;
- newButton.focusNode.setAttribute("tabIndex", "0");
- var container = dijit.byId(this.containerId);
- dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
- },
+ /*=====
+ dijit.placeOnScreenAroundElement = function(node, aroundElement, 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.
+ };
+ ====*/
+ dijit.placeOnScreenAroundElement = function(node, aroundNode, aroundCorners, layoutNode){
+ // 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]});
+ }
+ }
- onButtonClick: function(/*dijit._Widget*/ page){
- // summary:
- // Called whenever one of my child buttons is pressed in an attempt to select a page
- // tags:
- // private
+ return place.around(node, aroundNode, positions, true, layoutNode);
+ };
- var container = dijit.byId(this.containerId);
- container.selectChild(page);
- },
+ /*=====
+ dijit.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.
+ };
+ =====*/
+ dijit.placeOnScreenAroundNode = dijit.placeOnScreenAroundElement;
- onCloseButtonClick: function(/*dijit._Widget*/ page){
- // summary:
- // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
- // tags:
- // private
-
- var container = dijit.byId(this.containerId);
- container.closeChild(page);
- if(this._currentChild){
- var b = this.pane2button[this._currentChild.id];
- if(b){
- dijit.focus(b.focusNode || b.domNode);
- }
- }
- },
+ /*=====
+ dijit.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.
+ };
+ =====*/
+ dijit.placeOnScreenAroundRectangle = dijit.placeOnScreenAroundElement;
- // 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
+ dijit.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.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;
+ };
- if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
- // find currently focused button in children array
- var children = this.getChildren();
- var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
- // pick next button to focus on
- var offset = forward ? 1 : children.length - 1;
- return children[ (current + offset) % children.length ]; // dijit._Widget
- },
+ return dijit;
+});
- 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){
- var k = dojo.keys;
- switch(e.charOrCode){
- case k.LEFT_ARROW:
- case k.UP_ARROW:
- if(!e._djpage){ forward = false; }
- break;
- case k.PAGE_UP:
- if(e.ctrlKey){ forward = false; }
- break;
- case k.RIGHT_ARROW:
- case k.DOWN_ARROW:
- if(!e._djpage){ forward = true; }
- break;
- case k.PAGE_DOWN:
- if(e.ctrlKey){ forward = true; }
- break;
- case k.HOME:
- case k.END:
- var children = this.getChildren();
- if(children && children.length){
- children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
- }
- dojo.stopEvent(e);
- break;
- case k.DELETE:
- if(this._currentChild.closable){
- this.onCloseButtonClick(this._currentChild);
- }
- dojo.stopEvent(e);
- break;
- default:
- if(e.ctrlKey){
- if(e.charOrCode === k.TAB){
- this.adjacent(!e.shiftKey).onClick();
- dojo.stopEvent(e);
- }else if(e.charOrCode == "w"){
- if(this._currentChild.closable){
- this.onCloseButtonClick(this._currentChild);
- }
- dojo.stopEvent(e); // avoid browser tab closing.
- }
- }
- }
- // handle next/previous page navigation (left/right arrow, etc.)
- if(forward !== null){
- this.adjacent(forward).onClick();
- dojo.stopEvent(e);
- }
- }
- },
+},
+'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){
- 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);
- }
- });
+/*=====
+ var _WidgetBase = dijit._WidgetBase;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _Contained = dijit._Contained;
+=====*/
+ // module:
+ // dijit/MenuSeparator
+ // summary:
+ // A line between two menu items
-dojo.declare("dijit.layout._StackButton",
- dijit.form.ToggleButton,
- {
+ return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
// summary:
- // Internal widget used by StackContainer.
- // description:
- // The button-like or tab-like object you click to select or delete a page
- // tags:
- // private
+ // A line between two menu items
- // Override _FormWidget.tabIndex.
- // StackContainer buttons are not in the tab order by default.
- // Probably we should be calling this.startupKeyNavChildren() instead.
- tabIndex: "-1",
+ templateString: template,
- buildRendering: function(/*Event*/ evt){
+ buildRendering: function(){
this.inherited(arguments);
- dijit.setWaiRole((this.focusNode || this.domNode), "tab");
+ dom.setSelectable(this.domNode, false);
},
- onClick: function(/*Event*/ evt){
+ isFocusable: function(){
// summary:
- // This is for TabContainer where the tabs are <span> rather than button,
- // so need to set focus explicitly (on some browsers)
- // Note that you shouldn't override this method, but you can connect to it.
- dijit.focus(this.focusNode);
-
- // ... now let StackController catch the event and tell me what to do
- },
+ // Override to always return false
+ // tags:
+ // protected
- onClickCloseButton: function(/*Event*/ evt){
- // summary:
- // StackContainer connects to this function; if your widget contains a close button
- // then clicking it should call this function.
- // Note that you shouldn't override this method, but you can connect to it.
- evt.stopPropagation();
+ return false; // Boolean
}
});
+});
-}
-
-if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.StackContainer"] = true;
-dojo.provide("dijit.layout.StackContainer");
-
-
-
-
-
-
-
-dojo.declare(
- "dijit.layout.StackContainer",
- dijit.layout._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.
-
- // 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",
+},
+'dijit/form/_ComboBoxMenu':function(){
+define("dijit/form/_ComboBoxMenu", [
+ "dojo/_base/declare", // declare
+ "dojo/dom-class", // domClass.add domClass.remove
+ "dojo/dom-construct", // domConstruct.create
+ "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, domConstruct, domStyle, keys,
+ _WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
/*=====
- // selectedChildWidget: [readonly] dijit._Widget
- // References the currently selected child widget, if any.
- // Adjust selected child with selectChild() method.
- selectedChildWidget: null,
+ var _WidgetBase = dijit._WidgetBase;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _ComboBoxMenuMixin = dijit.form._ComboBoxMenuMixin;
+ var _ListMouseMixin = dijit.form._ListMouseMixin;
=====*/
- buildRendering: function(){
- this.inherited(arguments);
- dojo.addClass(this.domNode, "dijitLayoutContainer");
- dijit.setWaiRole(this.containerNode, "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
- dojo.forEach(children, this._setupChild, this);
-
- // Figure out which child to initially display, defaulting to first one
- if(this.persist){
- this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
- }else{
- dojo.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.
- dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
+ // module:
+ // dijit/form/_ComboBoxMenu
+ // summary:
+ // Focus-less menu for internal use in `dijit.form.ComboBox`
- // 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);
- },
+ 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
- resize: function(){
- // 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.
- var selected = this.selectedChildWidget;
- if(selected && !this._hasBeenShown){
- this._hasBeenShown = true;
- this._showChild(selected);
- }
- this.inherited(arguments);
- },
+ templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;'>"
+ +"<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>",
- _setupChild: function(/*dijit._Widget*/ child){
- // Overrides _LayoutWidget._setupChild()
+ baseClass: "dijitComboBoxMenu",
- this.inherited(arguments);
+ postCreate: function(){
+ this.inherited(arguments);
+ if(!this.isLeftToRight()){
+ domClass.add(this.previousButton, "dijitMenuItemRtl");
+ domClass.add(this.nextButton, "dijitMenuItemRtl");
+ }
+ },
- dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
+ _createMenuItem: function(){
+ return domConstruct.create("div", {
+ "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
+ role: "option"
+ });
+ },
- // remove the title attribute so it doesn't show up when i hover
- // over a node
- child.domNode.title = "";
- },
+ onHover: function(/*DomNode*/ node){
+ // summary:
+ // Add hover CSS
+ domClass.add(node, "dijitMenuItemHover");
+ },
- addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
- // Overrides _Container.addChild() to do layout and publish events
+ onUnhover: function(/*DomNode*/ node){
+ // summary:
+ // Remove hover CSS
+ domClass.remove(node, "dijitMenuItemHover");
+ },
- this.inherited(arguments);
+ onSelect: function(/*DomNode*/ node){
+ // summary:
+ // Add selected CSS
+ domClass.add(node, "dijitMenuItemSelected");
+ },
- if(this._started){
- dojo.publish(this.id+"-addChild", [child, insertIndex]);
+ onDeselect: function(/*DomNode*/ node){
+ // summary:
+ // Remove selected CSS
+ domClass.remove(node, "dijitMenuItemSelected");
+ },
- // 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()
- this.layout();
+ _page: function(/*Boolean*/ up){
+ // summary:
+ // Handles page-up and page-down keypresses
- // if this is the first child, then select it
- if(!this.selectedChildWidget){
- this.selectChild(child);
+ 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();
}
- }
- },
-
- removeChild: function(/*dijit._Widget*/ 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
- dojo.publish(this.id + "-removeChild", [page]);
- }
-
- // If we 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._beingDestroyed){ 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]);
+ 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;
}
- }
-
- 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._Widget|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 = dijit.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);
- dojo.publish(this.id+"-selectChild", [page]);
-
- if(this.persist){
- dojo.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(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
- // summary:
- // Hide the old widget and display the new widget.
- // Subclasses should override this.
- // 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();
+ 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.charOrCode){
+ 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;
}
}
+ });
+});
- 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.
- var children = this.getChildren();
- var index = dojo.indexOf(children, this.selectedChildWidget);
- index += forward ? 1 : children.length - 1;
- return children[ index % children.length ]; // dijit._Widget
- },
-
- 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){
- dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
- },
-
- layout: function(){
- // Implement _LayoutWidget.layout() virtual method.
- if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
- this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
- }
- },
+},
+'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<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\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/_base/kernel", // kernel.isAsync
+ "dojo/keys",
+ "dojo/_base/lang", // lang.mixin lang.hitch
+ "dojo/on",
+ "dojo/ready",
+ "dojo/_base/sniff", // has("ie") has("opera")
+ "dojo/_base/window", // win.body
+ "dojo/window", // winUtils.getBox
+ "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",
+ ".", // 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, kernel, keys, lang, on, ready, has, win, winUtils,
+ Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
+ DialogUnderlay, ContentPane, template, dijit){
+
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _CssStateMixin = dijit._CssStateMixin;
+ var _FormMixin = dijit.form._FormMixin;
+ var _DialogMixin = dijit._DialogMixin;
+=====*/
- _showChild: function(/*dijit._Widget*/ 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);
- dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
+ // module:
+ // dijit/Dialog
+ // summary:
+ // A modal dialog Widget
- return page._onShow() || true;
- },
- _hideChild: function(/*dijit._Widget*/ page){
+ /*=====
+ dijit._underlay = function(kwArgs){
// summary:
- // Hide the specified child by changing it's CSS, and call _onHide() so
- // it's notified.
- page._set("selected", false);
- dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
-
- page.onHide();
- },
+ // 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.
+ };
+ =====*/
- closeChild: function(/*dijit._Widget*/ page){
+ var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
// 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){
- dojo.forEach(this.getChildren(), function(child){
- this.removeChild(child);
- child.destroyRecursive(preserveDom);
- }, this);
- }
-});
-
-// For back-compat, remove for 2.0
-
-
-// These arguments can be specified for the children of a StackContainer.
-// 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.)
-dojo.extend(dijit._Widget, {
- // selected: Boolean
- // Parameter for children of `dijit.layout.StackContainer` or subclasses.
- // Specifies that this widget should be the initially displayed pane.
- // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
- selected: false,
-
- // closable: Boolean
- // Parameter for children of `dijit.layout.StackContainer` or subclasses.
- // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
- closable: false,
-
- // iconClass: String
- // Parameter for children of `dijit.layout.StackContainer` or subclasses.
- // CSS Class specifying icon to use in label associated with this pane.
- iconClass: "",
-
- // showTitle: Boolean
- // Parameter for children of `dijit.layout.StackContainer` or subclasses.
- // When true, display title of this widget as tab label etc., rather than just using
- // icon specified in iconClass
- showTitle: true
-});
-
-}
-
-if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.AccordionPane"] = true;
-dojo.provide("dijit.layout.AccordionPane");
-
-
+ // 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 dijit.Dialog({ title: "test dialog", content: "test content" };
+ // | dojo.body().appendChild(foo.domNode);
+ // | foo.startup();
-dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, {
- // summary:
- // Deprecated widget. Use `dijit.layout.ContentPane` instead.
- // tags:
- // deprecated
+ templateString: template,
- constructor: function(){
- dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
- },
+ baseClass: "dijitDialog",
- onSelected: function(){
- // summary:
- // called when this pane is selected
- }
-});
+ cssStateNodes: {
+ closeButtonNode: "dijitDialogCloseIcon"
+ },
-}
+ // Map widget attributes to DOMNode attributes.
+ _setTitleAttr: [
+ { node: "titleNode", type: "innerHTML" },
+ { node: "titleBar", type: "attribute" }
+ ],
-if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.AccordionContainer"] = true;
-dojo.provide("dijit.layout.AccordionContainer");
+ // 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,
-//dojo.require("dijit.layout.AccordionPane "); // for back compat, remove for 2.0
+ //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":"",
-// 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.
+ postMixInProperties: function(){
+ var _nlsResources = i18n.getLocalization("dijit", "common");
+ lang.mixin(this, _nlsResources);
+ this.inherited(arguments);
+ },
-dojo.declare(
- "dijit.layout.AccordionContainer",
- dijit.layout.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 dojoType="dijit.layout.AccordionContainer">
- // | <div dojoType="dijit.layout.ContentPane" title="pane 1">
- // | </div>
- // | <div dojoType="dijit.layout.ContentPane" title="pane 2">
- // | <p>This is some text</p>
- // | </div>
- // | </div>
+ postCreate: function(){
+ domStyle.set(this.domNode, {
+ display: "none",
+ position:"absolute"
+ });
+ win.body().appendChild(this.domNode);
- // duration: Integer
- // Amount of time (in ms) it takes to slide panes
- duration: dijit.defaultDuration,
+ this.inherited(arguments);
- // buttonWidget: [const] String
- // The name of the widget used to display the title of each pane
- buttonWidget: "dijit.layout._AccordionButton",
+ this.connect(this, "onExecute", "hide");
+ this.connect(this, "onCancel", "hide");
+ this._modalconnects = [];
+ },
-/*=====
- // _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",
+ 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
- buildRendering: function(){
+ // 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);
- this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
- dijit.setWaiRole(this.domNode, "tablist"); // TODO: put this in template
},
- startup: function(){
- if(this._started){ return; }
- this.inherited(arguments);
- if(this.selectedChildWidget){
- var style = this.selectedChildWidget.containerNode.style;
- style.display = "";
- style.overflow = "auto";
- this.selectedChildWidget._wrapperWidget.set("selected", true);
- }
+ _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();
+ 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();
},
- 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;}
+ _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
- // space taken up by title, plus wrapper div (with border/margin) for open pane
- var wrapperDomNode = openPane._wrapperWidget.domNode,
- wrapperDomNodeMargin = dojo._getMarginExtents(wrapperDomNode),
- wrapperDomNodePadBorder = dojo._getPadBorderExtents(wrapperDomNode),
- wrapperContainerNode = openPane._wrapperWidget.containerNode,
- wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode),
- wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
- mySize = this._contentBox;
+ var node = this.domNode;
- // get cumulative height of all the unselected title bars
- var totalCollapsedHeight = 0;
- dojo.forEach(this.getChildren(), function(child){
- if(child != openPane){
- totalCollapsedHeight += dojo._getMarginSize(child._wrapperWidget.domNode).h;
- }
- });
- this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
- - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
- - openPane._buttonWidget.getTitleHeight();
+ 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");
+ }
- // Memo size to make displayed child
- this._containerContentBox = {
- h: this._verticalSpace,
- w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
- - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
+ this.underlayAttrs = {
+ dialogId: this.id,
+ "class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
};
-
- if(openPane){
- openPane.resize(this._containerContentBox);
- }
},
- _setupChild: function(child){
- // Overrides _LayoutWidget._setupChild().
- // Put wrapper widget around the child widget, showing title
-
- child._wrapperWidget = new dijit.layout._AccordionInnerContainer({
- contentWidget: child,
- buttonWidget: this.buttonWidget,
- id: child.id + "_wrapper",
- dir: child.dir,
- lang: child.lang,
- parent: this
- });
+ _size: function(){
+ // summary:
+ // If necessary, shrink dialog contents so dialog fits in viewport
+ // tags:
+ // private
- this.inherited(arguments);
- },
+ this._checkIfSingleChild();
- addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
- 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.
+ // 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(this._singleChildOriginalStyle){
+ this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
+ }
+ delete this._singleChildOriginalStyle;
+ }else{
+ domStyle.set(this.containerNode, {
+ width:"auto",
+ height:"auto"
+ });
+ }
- // First add in child as a direct child of this AccordionContainer
- dojo.place(child.domNode, this.containerNode, insertIndex);
+ var bb = domGeometry.position(this.domNode);
+ var viewport = winUtils.getBox();
+ if(bb.w >= viewport.w || bb.h >= viewport.h){
+ // Reduce size of dialog contents so that dialog fits in viewport
- if(!child._started){
- child.startup();
- }
-
- // Then stick the wrapper widget around the child widget
- this._setupChild(child);
+ var w = Math.min(bb.w, Math.floor(viewport.w * 0.75)),
+ h = Math.min(bb.h, Math.floor(viewport.h * 0.75));
- // Code below copied from StackContainer
- dojo.publish(this.id+"-addChild", [child, insertIndex]);
- this.layout();
- if(!this.selectedChildWidget){
- this.selectChild(child);
+ if(this._singleChild && this._singleChild.resize){
+ 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{
- // 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);
+ if(this._singleChild && this._singleChild.resize){
+ this._singleChild.resize();
+ }
}
},
- 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){
- dojo.place(child.domNode, child._wrapperWidget.domNode, "after");
- child._wrapperWidget.destroy();
- delete child._wrapperWidget;
+ _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(win.body(), "dojoMove")){ // don't do anything if called during auto-scroll
+ var node = this.domNode,
+ viewport = winUtils.getBox(),
+ 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"
+ });
}
-
- dojo.removeClass(child.domNode, "dijitHidden");
-
- this.inherited(arguments);
},
- getChildren: function(){
- // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
- return dojo.map(this.inherited(arguments), function(child){
- return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
- }, this);
- },
+ _onKey: function(/*Event*/ evt){
+ // summary:
+ // Handles the keyboard events for accessibility reasons
+ // tags:
+ // private
- destroy: function(){
- if(this._animation){
- this._animation.stop();
- }
- dojo.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();
+ 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{
- child.destroyRecursive();
+ // 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*/ }
+ }
}
- });
- 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);
- },
+ show: function(){
+ // summary:
+ // Display the dialog
+ // returns: dojo.Deferred
+ // Deferred object that resolves when the display animation is complete
- _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){
- // Overrides StackContainer._transition() to provide sliding of title bars etc.
+ if(this.open){ return; }
- if(dojo.isIE < 8){
- // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
- animate = false;
+ if(!this._started){
+ this.startup();
}
- 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;
+ // first time we show the dialog, there's some initialization stuff to do
+ if(!this._alreadyInitialized){
+ this._setup();
+ this._alreadyInitialized=true;
}
- 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(this._fadeOutDeferred){
+ this._fadeOutDeferred.cancel();
}
- if(oldWidget){
- oldWidget._wrapperWidget.set("selected", false);
- if(!animate){
- this._hideChild(oldWidget);
+ this._modalconnects.push(on(window, "scroll", lang.hitch(this, "layout")));
+ this._modalconnects.push(on(window, "resize", lang.hitch(this, function(){
+ // IE gives spurious resize events and can actually get stuck
+ // in an infinite loop if we don't ignore them
+ var viewport = winUtils.getBox();
+ if(!this._oldViewport ||
+ viewport.h != this._oldViewport.h ||
+ viewport.w != this._oldViewport.w){
+ this.layout();
+ this._oldViewport = viewport;
}
- }
+ })));
+ this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
- if(animate){
- var newContents = newWidget._wrapperWidget.containerNode,
- oldContents = oldWidget._wrapperWidget.containerNode;
+ domStyle.set(this.domNode, {
+ opacity:0,
+ display:""
+ });
- // 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 = dojo._getMarginExtents(wrapperContainerNode),
- wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
- animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
+ this._set("open", true);
+ this._onShow(); // lazy load trigger
- oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
+ this._size();
+ this._position();
- this._animation = new dojo.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);
+ // 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._animation.onStop = this._animation.onEnd;
- this._animation.play();
- }
+ this._fadeInDeferred.callback(true);
+ delete this._fadeInDeferred;
+ })
+ }).play();
- return d; // If child has an href, promise that fires when the widget has finished loading
+ return this._fadeInDeferred;
},
- // note: we are treating the container as controller here
- _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
+ hide: function(){
// 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)){
+ // Hide the dialog
+ // returns: dojo.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
+ if(!this._alreadyInitialized){
return;
}
- var k = dojo.keys,
- c = e.charOrCode;
- if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
- (e.ctrlKey && c == k.PAGE_UP)){
- this._adjacent(false)._buttonWidget._onTitleClick();
- dojo.stopEvent(e);
- }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
- (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
- this._adjacent(true)._buttonWidget._onTitleClick();
- dojo.stopEvent(e);
+ if(this._fadeInDeferred){
+ this._fadeInDeferred.cancel();
}
- }
- }
-);
-
-dojo.declare("dijit.layout._AccordionInnerContainer",
- [dijit._Widget, dijit._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: String
- // Name of 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._Widget
- // Pointer to the real child widget
- contentWidget: null,
-=====*/
-
- baseClass: "dijitAccordionInnerContainer",
+ // fade-in Animation object, setup below
+ var fadeOut;
- // tell nested layout widget that we will take care of sizing
- isContainer: true,
- isLayoutContainer: true,
+ 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'));
- buildRendering: function(){
- // Builds a template like:
- // <div class=dijitAccordionInnerContainer>
- // Button
- // <div class=dijitAccordionChildWrapper>
- // ContentPane
- // </div>
- // </div>
+ fadeOut = fx.fadeOut({
+ node: this.domNode,
+ duration: this.duration,
+ onEnd: lang.hitch(this, function(){
+ this.domNode.style.display = "none";
+ DialogLevelManager.hide(this);
+ this._fadeOutDeferred.callback(true);
+ delete this._fadeOutDeferred;
+ })
+ }).play();
- // Create wrapper div, placed where the child is now
- this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after");
-
- // wrapper div's first child is the button widget (ie, the title bar)
- var child = this.contentWidget,
- cls = dojo.getObject(this.buttonWidget);
- this.button = child._buttonWidget = (new cls({
- contentWidget: child,
- label: child.title,
- title: child.tooltip,
- dir: child.dir,
- lang: child.lang,
- iconClass: child.iconClass,
- id: child.id + "_button",
- parent: this.parent
- })).placeAt(this.domNode);
-
- // and then the actual content widget (changing it from prior-sibling to last-child),
- // wrapped by a <div class=dijitAccordionChildWrapper>
- this.containerNode = dojo.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
- dojo.place(this.contentWidget.domNode, this.containerNode);
- },
+ if(this._scrollConnected){
+ this._scrollConnected = false;
+ }
+ var h;
+ while(h = this._modalconnects.pop()){
+ h.remove();
+ }
- postCreate: function(){
- this.inherited(arguments);
+ if(this._relativePosition){
+ delete this._relativePosition;
+ }
+ this._set("open", false);
- // Map changes in content widget's title etc. to changes in the button
- var button = this.button;
- this._contentWidgetWatches = [
- this.contentWidget.watch('title', dojo.hitch(this, function(name, oldValue, newValue){
- button.set("label", newValue);
- })),
- this.contentWidget.watch('tooltip', dojo.hitch(this, function(name, oldValue, newValue){
- button.set("title", newValue);
- })),
- this.contentWidget.watch('iconClass', dojo.hitch(this, function(name, oldValue, newValue){
- button.set("iconClass", newValue);
- }))
- ];
+ return this._fadeOutDeferred;
},
- _setSelectedAttr: function(/*Boolean*/ isSelected){
- this._set("selected", isSelected);
- this.button.set("selected", isSelected);
- if(isSelected){
- var cw = this.contentWidget;
- if(cw.onSelected){ cw.onSelected(); }
+ layout: function(){
+ // summary:
+ // Position the Dialog and the underlay
+ // tags:
+ // private
+ if(this.domNode.style.display != "none"){
+ if(dijit._underlay){ // avoid race condition during show()
+ dijit._underlay.layout();
+ }
+ this._position();
}
},
- startup: function(){
- // Called by _Container.addChild()
- this.contentWidget.startup();
- },
-
destroy: function(){
- this.button.destroyRecursive();
-
- dojo.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
+ 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();
+ }
- delete this.contentWidget._buttonWidget;
- delete this.contentWidget._wrapperWidget;
+ DialogLevelManager.hide(this);
this.inherited(arguments);
- },
-
- destroyDescendants: function(){
- // since getChildren isn't working for me, have to code this manually
- this.contentWidget.destroyRecursive();
}
-});
-
-dojo.declare("dijit.layout._AccordionButton",
- [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
- {
- // summary:
- // The title bar to click to open up an accordion pane.
- // Internal widget used by AccordionContainer.
- // tags:
- // private
-
- templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "<div dojoAttachEvent='onclick:_onTitleClick' class='dijitAccordionTitle'>\n\t<div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='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\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"),
- attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
- label: {node: "titleTextNode", type: "innerHTML" },
- title: {node: "titleTextNode", type: "attribute", attribute: "title"},
- iconClass: { node: "iconNode", type: "class" }
- }),
-
- baseClass: "dijitAccordionTitle",
-
- getParent: function(){
- // summary:
- // Returns the AccordionContainer parent.
- // tags:
- // private
- return this.parent;
- },
-
- buildRendering: function(){
- this.inherited(arguments);
- var titleTextNodeId = this.id.replace(' ','_');
- dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
- dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
- dojo.setSelectable(this.domNode, false);
- },
+ });
- getTitleHeight: function(){
- // summary:
- // Returns the height of the title dom node.
- return dojo._getMarginSize(this.domNode).h; // Integer
- },
+ var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {});
+ Dialog._DialogBase = _DialogBase; // for monkey patching
- // TODO: maybe the parent should set these methods directly rather than forcing the code
- // into the button widget?
- _onTitleClick: function(){
+ var DialogLevelManager = Dialog._DialogLevelManager = {
// summary:
- // Callback when someone clicks my title.
- var parent = this.getParent();
- parent.selectChild(this.contentWidget, true);
- dijit.focus(this.focusNode);
- },
+ // 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.
- _onTitleKeyPress: function(/*Event*/ evt){
- return this.getParent()._onKeyPress(evt, this.contentWidget);
- },
-
- _setSelectedAttr: function(/*Boolean*/ isSelected){
- this._set("selected", isSelected);
- dijit.setWaiState(this.focusNode, "expanded", isSelected);
- dijit.setWaiState(this.focusNode, "selected", isSelected);
- this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
- }
-});
+ _beginZIndex: 950,
-}
+ show: function(/*dijit._Widget*/ 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.
-if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.BorderContainer"] = true;
-dojo.provide("dijit.layout.BorderContainer");
+ // Save current focus
+ ds[ds.length-1].focus = focus.curNode;
+ // Display the underlay, or if already displayed then adjust for this new dialog
+ var underlay = dijit._underlay;
+ if(!underlay || underlay._destroyed){
+ underlay = dijit._underlay = 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(dijit._underlay.domNode, 'zIndex', zIndex - 1);
+ // Dialog
+ domStyle.set(dialog.domNode, 'zIndex', zIndex);
+ ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
+ },
-dojo.declare(
- "dijit.layout.BorderContainer",
- dijit.layout._LayoutWidget,
-{
- // summary:
- // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
- //
- // description:
- // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
- // that contains a child widget marked region="center" and optionally children widgets marked
- // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
- // Children along the edges will be laid out according to width or height dimensions and may
- // include optional splitters (splitter="true") to make them resizable by the user. The remaining
- // space is designated for the center region.
- //
- // 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 conrol layout precedence of horizontal vs. vertical panes.
- // example:
- // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
- // | style="width: 400px; height: 300px;">
- // | <div dojoType="dijit.layout.ContentPane" region="top">header text</div>
- // | <div dojoType="dijit.layout.ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
- // | <div dojoType="dijit.layout.ContentPane" region="center">client area</div>
- // | </div>
+ hide: function(/*dijit._Widget*/ 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.
- // 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",
+ if(ds[ds.length-1].dialog == dialog){
+ // Removing the top (or only) dialog in the stack, return focus
+ // to previous dialog
- // 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,
+ ds.pop();
- // liveSplitters: [const] Boolean
- // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
- liveSplitters: true,
+ var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
- // persist: Boolean
- // Save splitter positions in a cookie.
- persist: false,
+ // Adjust underlay
+ if(ds.length == 1){
+ // Returning to original page.
+ // Hide the underlay, unless the underlay widget has already been destroyed
+ // because we are being called during page unload (when all widgets are destroyed)
+ if(!dijit._underlay._destroyed){
+ dijit._underlay.hide();
+ }
+ }else{
+ // Popping back to previous dialog, adjust underlay
+ domStyle.set(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
+ dijit._underlay.set(pd.underlayAttrs);
+ }
- baseClass: "dijitBorderContainer",
+ // 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;
+ }
- // _splitterClass: String
- // Optional hook to override the default Splitter widget used by BorderContainer
- _splitterClass: "dijit.layout._Splitter",
+ 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);
+ }
+ }
+ },
- 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";
+ isTop: function(/*dijit._Widget*/ dialog){
+ // summary:
+ // Returns true if specified Dialog is the top in the task
+ return ds[ds.length-1].dialog == dialog;
}
- this.inherited(arguments);
- },
+ };
- startup: function(){
- if(this._started){ return; }
- dojo.forEach(this.getChildren(), this._setupChild, this);
- this.inherited(arguments);
- },
+ // 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(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/TooltipDialog"];
+ require(requires); // use indirection so modules not rolled into a build
+ });
+ }
- _setupChild: function(/*dijit._Widget*/ child){
- // Override _LayoutWidget._setupChild().
+ return Dialog;
+});
- var region = child.region;
- if(region){
- this.inherited(arguments);
+},
+'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",
+ ".." // for exporting symbols to dijit
+], function(array, dom, lang, topic, win, focus, dijit){
+
+ // module:
+ // dijit/_base/focus
+ // summary:
+ // Deprecated module to monitor currently focused node and stack of currently focused widgets.
+ // New code should access dijit/focus directly.
- dojo.addClass(child.domNode, this.baseClass+"Pane");
+ lang.mixin(dijit, {
+ // _curFocus: DomNode
+ // Currently focused item on screen
+ _curFocus: null,
- var ltr = this.isLeftToRight();
- if(region == "leading"){ region = ltr ? "left" : "right"; }
- if(region == "trailing"){ region = ltr ? "right" : "left"; }
+ // _prevFocus: DomNode
+ // Previously focused item on screen
+ _prevFocus: null,
- // 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 = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
- var splitter = new _Splitter({
- id: child.id + "_splitter",
- container: this,
- child: child,
- region: region,
- live: this.liveSplitters
- });
- splitter.isSplitter = true;
- child._splitterWidget = splitter;
+ isCollapsed: function(){
+ // summary:
+ // Returns true if there is no text selected
+ return dijit.getBookmark().isCollapsed;
+ },
- dojo.place(splitter.domNode, child.domNode, "after");
+ 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 = {};
- // Splitters aren't added as Contained children, so we need to call startup explicitly
- splitter.startup();
+ //'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!");
}
- child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
- }
- },
+ return bm; // Object
+ },
- layout: function(){
- // Implement _LayoutWidget.layout() virtual method.
- this._layoutChildren();
- },
+ 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();
+ }
+ }
+ },
- addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
- // Override _LayoutWidget.addChild().
- this.inherited(arguments);
- if(this._started){
- this.layout(); //OPT
- }
- },
+ getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
+ // summary:
+ // Called as getFocus(), this returns an Object showing the current focus
+ // and selected text.
+ //
+ // Called as getFocus(widget), where widget is a (widget representing) a button
+ // that was just pressed, it returns where focus was before that button
+ // was pressed. (Pressing the button may have either shifted focus to the button,
+ // or removed focus altogether.) In this case the selected text is not returned,
+ // since it can't be accurately determined.
+ //
+ // menu: dijit._Widget or {domNode: DomNode} structure
+ // The button that was just pressed. If focus has disappeared or moved
+ // to this button, returns the previous focus. In this case the bookmark
+ // information is already lost, and null is returned.
+ //
+ // openedForWindow:
+ // iframe in which menu was opened
+ //
+ // returns:
+ // A handle to restore focus/selection, to be passed to `dijit.focus`
+ var node = !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
+ },
- removeChild: function(/*dijit._Widget*/ child){
- // Override _LayoutWidget.removeChild().
+ // _activeStack: dijit._Widget[]
+ // List of currently active widgets (focused widget and it's ancestors)
+ _activeStack: [],
- 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.
- dojo.removeClass(child.domNode, this.baseClass+"Pane");
- dojo.style(child.domNode, {
- top: "auto",
- bottom: "auto",
- left: "auto",
- right: "auto",
- position: "static"
- });
- dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
- },
+ 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);
+ },
- getChildren: function(){
- // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
- return dojo.filter(this.inherited(arguments), function(widget){
- return !widget.isSplitter;
- });
- },
+ 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()
- // TODO: remove in 2.0
- getSplitter: function(/*String*/region){
- // summary:
- // Returns the widget responsible for rendering the splitter associated with region
- // tags:
- // deprecated
- return dojo.filter(this.getChildren(), function(child){
- return child.region == region;
- })[0]._splitterWidget;
- },
+ handle && handle.remove();
+ },
- resize: function(newSize, currentSize){
- // Overrides _LayoutWidget.resize().
+ 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()
- // resetting potential padding to 0px to provide support for 100% width/height + padding
- // TODO: this hack doesn't respect the box model and is a temporary fix
- if(!this.cs || !this.pe){
- var node = this.domNode;
- this.cs = dojo.getComputedStyle(node);
- this.pe = dojo._getPadExtents(node, this.cs);
- this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
- this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
+ return focus.registerWin(targetWindow, effectiveNode);
+ },
- dojo.style(node, "padding", "0px");
- }
+ 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.
- this.inherited(arguments);
- },
+ handle && handle.remove();
+ }
+ });
- _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
+ // 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:
- // 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
+ // 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(!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;
- }
+ if(!handle){ 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 = dojo.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;
- });
+ 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;
- // Make new list, combining the externally specified children with splitters and gutters
- var childrenAndSplitters = [];
- dojo.forEach(wrappers, function(wrapper){
- var pane = wrapper.pane;
- childrenAndSplitters.push(pane);
- if(pane._splitterWidget){
- childrenAndSplitters.push(pane._splitterWidget);
+ // 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*/}
}
- });
-
- // 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
- dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters,
- changedChildId, changedChildSize);
- },
+ focus._onFocusNode(node);
+ }
- destroyRecursive: function(){
- // Destroy splitters first, while getChildren() still works
- dojo.forEach(this.getChildren(), function(child){
- var splitter = child._splitterWidget;
- if(splitter){
- splitter.destroy();
+ // 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();
}
- delete child._splitterWidget;
- });
-
- // Then destroy the real children, and myself
- this.inherited(arguments);
- }
-});
-
-// This argument can be specified for the children of a BorderContainer.
-// Since any widget can be specified as a LayoutContainer child, mix it
-// into the base widget class. (This is a hack, but it's effective.)
-dojo.extend(dijit._Widget, {
- // region: [const] String
- // Parameter for children of `dijit.layout.BorderContainer`.
- // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
- // See the `dijit.layout.BorderContainer` description for details.
- region: '',
-
- // layoutPriority: [const] Number
- // Parameter for children of `dijit.layout.BorderContainer`.
- // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
- // between children with a lower layoutPriority.
- layoutPriority: 0,
+ try{
+ win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
+ }catch(e2){
+ /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
+ }
+ }
+ };
- // splitter: [const] Boolean
- // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
- // If true, enables user to resize the widget by putting a draggable splitter between
- // this widget and the region=center widget.
- splitter: false,
+ // 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;
+ });
- // minSize: [const] Number
- // Parameter for children of `dijit.layout.BorderContainer`.
- // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
- minSize: 0,
+ 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
+ });
- // maxSize: [const] Number
- // Parameter for children of `dijit.layout.BorderContainer`.
- // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
- maxSize: Infinity
+ return dijit;
});
-dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
-{
- // summary:
- // A draggable spacer between two items in a `dijit.layout.BorderContainer`.
- // description:
- // This is instantiated by `dijit.layout.BorderContainer`. Users should not
- // create it directly.
- // tags:
- // private
+},
+'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`
/*=====
- // 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,
+dijit.tree.__SourceArgs = function(){
+ // 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
+ this.isSource = isSource;
+ this.accept = accept;
+ this.autoSync = autoSync;
+ this.copyOnly = copyOnly;
+ this.dragThreshold = dragThreshold;
+ this.betweenThreshold = betweenThreshold;
+}
=====*/
- // 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,
+return declare("dijit.tree.dndSource", _dndSelector, {
+ // summary:
+ // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
- templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
+ // isSource: [private] Boolean
+ // Can be used as a DnD source.
+ isSource: true,
- postMixInProperties: function(){
- this.inherited(arguments);
+ // accept: String[]
+ // List of accepted types (text strings) for the Tree; defaults to
+ // ["text"]
+ accept: ["text", "treeNode"],
- this.horizontal = /top|bottom/.test(this.region);
- this._factor = /top|left/.test(this.region) ? 1 : -1;
- this._cookieName = this.container.id + "_" + this.region;
- },
+ // copyOnly: [private] Boolean
+ // Copy items, if true, use a state of Ctrl key otherwise
+ copyOnly: false,
- buildRendering: function(){
- this.inherited(arguments);
+ // dragThreshold: Number
+ // The move delay in pixels before detecting a drag; 5 by default
+ dragThreshold: 5,
- dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
+ // betweenThreshold: Integer
+ // Distance from upper/lower edge of node to allow drop to reorder nodes
+ betweenThreshold: 0,
- if(this.container.persist){
- // restore old size
- var persistSize = dojo.cookie(this._cookieName);
- if(persistSize){
- this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
+ constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
+ // summary:
+ // a constructor of the Tree DnD Source
+ // tags:
+ // private
+ if(!params){ params = {}; }
+ lang.mixin(this, params);
+ this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
+ 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;
}
}
- },
-
- _computeMaxSize: function(){
- // summary:
- // Return the maximum size that my corresponding pane can be set to
-
- var dim = this.horizontal ? 'h' : 'w',
- childSize = dojo.marginBox(this.child.domNode)[dim],
- center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
- spaceAvailable = dojo.marginBox(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 = dojo.doc.createElement('div');
- dojo.addClass(this.cover, "dijitSplitterCover");
- dojo.place(this.cover, this.child.domNode, "after");
- }
- dojo.addClass(this.cover, "dijitSplitterCoverActive");
+ // 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;
- // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
- if(this.fake){ dojo.destroy(this.fake); }
- if(!(this._resize = this.live)){ //TODO: disable live for IE6?
- // create fake splitter to display at old position while we drag
- (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
- dojo.addClass(this.domNode, "dijitSplitterShadow");
- dojo.place(this.fake, this.domNode, "after");
- }
- dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
- if(this.fake){
- dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
+ // states
+ this.sourceState = "";
+ if(this.isSource){
+ domClass.add(this.node, "dojoDndSource");
}
-
- //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 = dojo.marginBox(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 = dojo.hitch(this.container, "_layoutChildren", this.child.id),
- de = dojo.doc;
-
- this._handlers = (this._handlers || []).concat([
- dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
- var delta = e[axis] - pageStart,
- childSize = factor * delta + childStart,
- boundChildSize = Math.max(Math.min(childSize, max), min);
-
- if(resize || forceResize){
- layoutFunc(boundChildSize);
- }
- // TODO: setting style directly (usually) sets content box size, need to set margin box size
- splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
- }),
- dojo.connect(de, "ondragstart", dojo.stopEvent),
- dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
- dojo.connect(de, "onmouseup", this, "_stopDrag")
- ]);
- dojo.stopEvent(e);
- },
-
- _onMouse: function(e){
- var o = (e.type == "mouseover" || e.type == "mouseenter");
- dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
- dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
- },
-
- _stopDrag: function(e){
- try{
- if(this.cover){
- dojo.removeClass(this.cover, "dijitSplitterCoverActive");
- }
- if(this.fake){ dojo.destroy(this.fake); }
- dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter"
- + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
- this._drag(e); //TODO: redundant with onmousemove?
- this._drag(e, true);
- }finally{
- this._cleanupHandlers();
- delete this._drag;
+ this.targetState = "";
+ if(this.accept){
+ domClass.add(this.node, "dojoDndTarget");
}
- if(this.container.persist){
- dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
- }
+ // 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"))
+ ];
},
- _cleanupHandlers: function(){
- dojo.forEach(this._handlers, dojo.disconnect);
- delete this._handlers;
+ // 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
},
- _onKeyPress: function(/*Event*/ e){
- // should we apply typematic to this?
- this._resize = true;
- var horizontal = this.horizontal;
- var tick = 1;
- var dk = dojo.keys;
- switch(e.charOrCode){
- case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
- tick *= -1;
-// break;
- case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
- break;
- default:
-// this.inherited(arguments);
- return;
- }
- var childSize = dojo._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));
- dojo.stopEvent(e);
+ 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(){
- this._cleanupHandlers();
- delete this.child;
- delete this.container;
- delete this.cover;
- delete this.fake;
- this.inherited(arguments);
- }
-});
-
-dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
-{
- // summary:
- // Just a spacer div to separate side pane from center pane.
- // Basically a trick to lookup the gutter/splitter width from the theme.
- // description:
- // Instantiated by `dijit.layout.BorderContainer`. Users should not
- // create directly.
- // tags:
- // private
-
- templateString: '<div class="dijitGutter" role="presentation"></div>',
-
- postMixInProperties: function(){
+ // summary:
+ // Prepares the object to be garbage-collected.
this.inherited(arguments);
- this.horizontal = /top|bottom/.test(this.region);
+ var h;
+ while(h = this.topics.pop()){ h.remove(); }
+ this.targetAnchor = null;
},
- buildRendering: function(){
- this.inherited(arguments);
- dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout._TabContainerBase"] = true;
-dojo.provide("dijit.layout._TabContainerBase");
-
-
-
-
-dojo.declare("dijit.layout._TabContainerBase",
- [dijit.layout.StackContainer, dijit._Templated],
- {
- // summary:
- // Abstract base class for TabContainer. Must define _makeController() to instantiate
- // and return the widget that displays the tab labels
- // description:
- // A TabContainer is a container that has multiple panes, but shows only
- // one pane at a time. There are a set of tabs corresponding to each pane,
- // where each tab has the name (aka title) of the pane, and optionally a close button.
-
- // tabPosition: String
- // Defines where tabs go relative to tab content.
- // "top", "bottom", "left-h", "right-h"
- tabPosition: "top",
-
- baseClass: "dijitTabContainer",
-
- // tabStrip: [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: dojo.cache("dijit.layout", "templates/TabContainer.html", "<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" dojoAttachPoint=\"tablistNode\"></div>\n\t<div dojoAttachPoint=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n"),
-
- postMixInProperties: function(){
- // set class name according to tab position, ex: dijitTabContainerTop
- this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
-
- this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
+ _onDragMouse: function(e){
+ // summary:
+ // Helper method for processing onmousemove/onmouseover events while drag is in progress.
+ // Keeps track of current drop target.
- this.inherited(arguments);
- },
+ 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)
- buildRendering: function(){
- this.inherited(arguments);
+ // 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";
+ }
+ }
- // 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(newTarget != oldTarget || newDropPosition != oldDropPosition){
+ if(oldTarget){
+ this._removeItemClass(oldTarget.rowNode, oldDropPosition);
+ }
+ if(newTarget){
+ this._addItemClass(newTarget.rowNode, newDropPosition);
+ }
- if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
+ // 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 model = this.tree.model,
+ 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);
+ }
+ }
- if(this.nested){
- /* workaround IE's lack of support for "a > b" selectors by
- * tagging each node in the template.
- */
- dojo.addClass(this.domNode, "dijitTabContainerNested");
- dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
- dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
- dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
- }else{
- dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
+ this.targetAnchor = newTarget;
+ this.dropPosition = newDropPosition;
}
},
- _setupChild: function(/*dijit._Widget*/ tab){
- // Overrides StackContainer._setupChild().
- dojo.addClass(tab.domNode, "dijitTabPane");
- this.inherited(arguments);
- },
-
- startup: function(){
- if(this._started){ return; }
-
- // wire up the tablist and its tabs
- this.tablist.startup();
-
+ 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);
- },
-
- layout: function(){
- // Overrides StackContainer.layout().
- // Configure the content pane to take up all the space except for where the tabs are
-
- if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
-
- var sc = this.selectedChildWidget;
-
- if(this.doLayout){
- // position and size the titles and the container node
- var titleAlign = this.tabPosition.replace(/-h/, "");
- this.tablist.layoutAlign = titleAlign;
- var children = [this.tablist, {
- domNode: this.tablistSpacer,
- layoutAlign: titleAlign
- }, {
- domNode: this.containerNode,
- layoutAlign: "client"
- }];
- dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
-
- // Compute size to make each of my children.
- // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
- this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
-
- if(sc && sc.resize){
- sc.resize(this._containerContentBox);
- }
+ var m = DNDManager.manager();
+ if(this.isDragging){
+ this._onDragMouse(e);
}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 = dojo.contentBox(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();
+ 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)));
+ }
}
}
},
- destroy: function(){
- if(this.tablist){
- this.tablist.destroy();
- }
+ 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);
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.TabController"] = true;
-dojo.provide("dijit.layout.TabController");
-
-
-
-
-
-
-// Menu is used for an accessible close button, would be nice to have a lighter-weight solution
-
-
-dojo.declare("dijit.layout.TabController",
- dijit.layout.StackController,
-{
- // summary:
- // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
- // Used internally by `dijit.layout.TabContainer`.
- // description:
- // Lets the user select the currently shown pane in a TabContainer or StackContainer.
- // TabController also monitors the TabContainer, and whenever a pane is
- // added or deleted updates itself accordingly.
- // tags:
- // private
-
- templateString: "<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
-
- // tabPosition: String
- // Defines where tabs go relative to the content.
- // "top", "bottom", "left-h", "right-h"
- tabPosition: "top",
-
- // buttonWidget: String
- // The name of the tab widget to create to correspond to each page
- buttonWidget: "dijit.layout._TabButton",
+ },
- _rectifyRtlTabList: function(){
+ onMouseUp: function(e){
// summary:
- // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
-
- if(0 >= this.tabPosition.indexOf('-h')){ return; }
- if(!this.pane2button){ return; }
-
- var maxWidth = 0;
- for(var pane in this.pane2button){
- var ow = this.pane2button[pane].innerDiv.scrollWidth;
- maxWidth = Math.max(maxWidth, ow);
- }
- //unify the length of all the tabs
- for(pane in this.pane2button){
- this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
+ // Event processor for onmouseup/ontouchend
+ // e: Event
+ // onmouseup/ontouchend event
+ // tags:
+ // private
+ if(this.mouseDown){
+ this.mouseDown = false;
+ this.inherited(arguments);
}
- }
-});
-
-dojo.declare("dijit.layout._TabButton",
- dijit.layout._StackButton,
- {
- // summary:
- // A tab (the thing you click to select a pane).
- // description:
- // Contains the title of the pane, and optionally a close-button to destroy the pane.
- // This is an internal widget and should not be instantiated directly.
- // tags:
- // private
-
- // baseClass: String
- // The CSS class applied to the domNode.
- baseClass: "dijitTab",
-
- // Apply dijitTabCloseButtonHover when close button is hovered
- cssStateNodes: {
- closeNode: "dijitTabCloseButton"
},
- templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div role=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n \t<div role=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" dojoAttachPoint='iconNode' />\n\t\t <span dojoAttachPoint='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" dojoAttachPoint='closeNode'\n\t\t \t\tdojoAttachEvent='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span dojoAttachPoint='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"),
-
- // Override _FormWidget.scrollOnFocus.
- // Don't scroll the whole tab container into view when the button is focused.
- scrollOnFocus: false,
-
- buildRendering: function(){
+ onMouseOut: function(){
+ // summary:
+ // Event processor for when mouse is moved away from a TreeNode
+ // tags:
+ // private
this.inherited(arguments);
-
- dojo.setSelectable(this.containerNode, false);
+ this._unmarkTargetAnchor();
},
- startup: function(){
- this.inherited(arguments);
- var n = this.domNode;
-
- // Required to give IE6 a kick, as it initially hides the
- // tabs until they are focused on.
- setTimeout(function(){
- n.className = n.className;
- }, 1);
+ 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;
},
- _setCloseButtonAttr: function(/*Boolean*/ disp){
+ // topic event processors
+ onDndSourceOver: function(source){
// summary:
- // Hide/show close button
- this._set("closeButton", disp);
- dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
- this.closeNode.style.display = disp ? "" : "none";
- if(disp){
- var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
- if(this.closeNode){
- dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
- }
- // add context menu onto title button
- var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
- this._closeMenu = new dijit.Menu({
- id: this.id+"_Menu",
- dir: this.dir,
- lang: this.lang,
- targetNodeIds: [this.domNode]
- });
-
- this._closeMenu.addChild(new dijit.MenuItem({
- label: _nlsResources.itemClose,
- dir: this.dir,
- lang: this.lang,
- onClick: dojo.hitch(this, "onClickCloseButton")
- }));
- }else{
- if(this._closeMenu){
- this._closeMenu.destroyRecursive();
- delete this._closeMenu;
- }
+ // 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);
}
},
- _setLabelAttr: function(/*String*/ content){
+ onDndStart: function(source, nodes, copy){
// 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 == false && !this.params.title){
- this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
- }
- },
+ // 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
- destroy: function(){
- if(this._closeMenu){
- this._closeMenu.destroyRecursive();
- delete this._closeMenu;
+ if(this.isSource){
+ this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
}
- this.inherited(arguments);
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
-dojo.provide("dijit.layout.ScrollingTabController");
-
-
-
-
-
-
-dojo.declare("dijit.layout.ScrollingTabController",
- dijit.layout.TabController,
- {
- // summary:
- // Set of tabs with left/right arrow keys and a menu to switch between tabs not
- // all fitting on a single row.
- // Works only for horizontal tabs (either above or below the content, not to the left
- // or right).
- // tags:
- // private
-
- templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" containerId=\"${containerId}\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdropDownPosition=\"below-alt, above-alt\"\n\t\t\tdojoAttachPoint=\"_menuBtn\" showLabel=\"false\">&#9660;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\" iconClass=\"dijitTabStripSlideLeftIcon\"\n\t\t\tdojoAttachPoint=\"_leftBtn\" dojoAttachEvent=\"onClick: doSlideLeft\" showLabel=\"false\">&#9664;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\" iconClass=\"dijitTabStripSlideRightIcon\"\n\t\t\tdojoAttachPoint=\"_rightBtn\" dojoAttachEvent=\"onClick: doSlideRight\" showLabel=\"false\">&#9654;</div>\n\t<div class='dijitTabListWrapper' dojoAttachPoint='tablistWrapper'>\n\t\t<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"),
-
- // useMenu: [const] Boolean
- // True if a menu should be used to select tabs when they are too
- // wide to fit the TabContainer, false otherwise.
- useMenu: true,
-
- // useSlider: [const] Boolean
- // True if a slider should be used to select tabs when they are too
- // wide to fit the TabContainer, false otherwise.
- useSlider: true,
-
- // tabStripClass: [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,
-
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- "class": "containerNode"
- }),
-
- buildRendering: function(){
- this.inherited(arguments);
- var n = this.domNode;
+ var accepted = this.checkAcceptance(source, nodes);
- this.scrollNode = this.tablistWrapper;
- this._initButtons();
+ this._changeState("Target", accepted ? "" : "Disabled");
- if(!this.tabStripClass){
- this.tabStripClass = "dijitTabContainer" +
- this.tabPosition.charAt(0).toUpperCase() +
- this.tabPosition.substr(1).replace(/-.*/, "") +
- "None";
- dojo.addClass(n, "tabStrip-disabled")
+ if(this == source){
+ DNDManager.manager().overSource(this);
}
- dojo.addClass(this.tablistWrapper, this.tabStripClass);
- },
-
- onStartup: function(){
- this.inherited(arguments);
-
- // Do not show the TabController until the related
- // StackController has added it's children. This gives
- // a less visually jumpy instantiation.
- dojo.style(this.domNode, "visibility", "visible");
- this._postStartup = true;
+ this.isDragging = true;
},
- onAddChild: function(page, insertIndex){
- this.inherited(arguments);
-
- // changes to the tab button label or iconClass will have changed the width of the
- // buttons, so do a resize
- dojo.forEach(["label", "iconClass"], function(attr){
- this.pane2watches[page.id].push(
- this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){
- if(this._postStartup && this._dim){
- this.resize(this._dim);
- }
- }))
- );
- }, this);
-
- // Increment the width of the wrapper when a tab is added
- // This makes sure that the buttons never wrap.
- // The value 200 is chosen as it should be bigger than most
- // Tab button widths.
- dojo.style(this.containerNode, "width",
- (dojo.style(this.containerNode, "width") + 200) + "px");
- },
+ 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: 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
- 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;
- }
+ // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
+ // make signature itemCreator(sourceItem, node, target) (or similar).
- this.inherited(arguments);
+ return array.map(nodes, function(node){
+ return {
+ "id": node.id,
+ "name": node.textContent || node.innerText || ""
+ };
+ }); // Object[]
},
- _initButtons: function(){
+ onDndDrop: function(source, nodes, copy){
// summary:
- // Creates the buttons used to scroll to view tabs that
- // may not be visible if the TabContainer is too narrow.
+ // 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;
- // Make a list of the buttons to display when the tab labels become
- // wider than the TabContainer, and hide the other buttons.
- // Also gets the total width of the displayed buttons.
- this._btnWidth = 0;
- this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
- if((this.useMenu && btn == this._menuBtn.domNode) ||
- (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
- this._btnWidth += dojo._getMarginSize(btn).w;
- return true;
+ this.isDragging = false;
+
+ // Compute the new parent item
+ var newParentItem;
+ var insertIndex;
+ 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;
+ }
}else{
- dojo.style(btn, "display", "none");
- return false;
+ newParentItem = (target && target.item) || tree.item;
}
- }, 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 + dojo.style(rightTab, "width") - leftTab.offsetLeft;
- }else{
- return 0;
- }
- },
-
- _enableBtn: function(width){
- // summary:
- // Determines if the tabs are wider than the width of the TabContainer, and
- // thus that we need to display left/right/menu navigation buttons.
- var tabsWidth = this._getTabsWidth();
- width = width || dojo.style(this.scrollNode, "width");
- return tabsWidth > 0 && width < tabsWidth;
- },
-
- resize: function(dim){
- // summary:
- // Hides or displays the buttons used to scroll the tab list and launch the menu
- // that selects tabs.
+ // If 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;
- if(this.domNode.offsetWidth == 0){
- return;
- }
+ 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);
- // Save the dimensions to be used when a child is renamed.
- this._dim = dim;
+ // 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;
+ }
- // Set my height to be my natural height (tall enough for one row of tab labels),
- // and my content-box width based on margin-box width specified in dim parameter.
- // But first reset scrollNode.height in case it was set by layoutChildren() call
- // in a previous run of this method.
- this.scrollNode.style.height = "auto";
- this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
- this._contentBox.h = this.scrollNode.offsetHeight;
- dojo.contentBox(this.domNode, this._contentBox);
+ 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.
- // 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");
+ if(typeof insertIndex == "number"){
+ if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
+ insertIndex -= 1;
+ }
+ }
+ model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
+ }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);
+ }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);
+ }
- // Position and size the navigation buttons and the tablist
- this._leftBtn.layoutAlign = "left";
- this._rightBtn.layoutAlign = "right";
- this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
- dijit.layout.layoutChildren(this.domNode, this._contentBox,
- [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
+ // Create new item in the tree, based on the drag source.
+ model.newItem(newItemsParams[idx], newParentItem, insertIndex);
+ }
+ }, this);
- // set proper scroll so that selected tab is visible
- if(this._selectedTab){
- if(this._anim && this._anim.status() == "playing"){
- this._anim.stop();
- }
- var w = this.scrollNode,
- sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
- w.scrollLeft = sl;
+ // 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);
}
-
- // 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"
- var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
- dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
- + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
- return sl;
+ this.onDndCancel();
},
- _convertToScrollLeft: function(val){
+ onDndCancel: function(){
// summary:
- // Given a scroll value where 0 means "scrolled all the way to the left"
- // and some positive number, based on # of pixels of possible scroll (ex: 1000)
- // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
- // to achieve that scroll.
- //
- // This method is to adjust for RTL funniness in various browsers and versions.
- if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
- return val;
- }else{
- var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
- return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
- }
+ // 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", "");
},
- onSelectChild: function(/*dijit._Widget*/ page){
+ // When focus moves in/out of the entire Tree
+ onOverEvent: function(){
// summary:
- // Smoothly scrolls to a tab when it is selected.
-
- var tab = this.pane2button[page.id];
- if(!tab || !page){return;}
-
- // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
- var node = tab.domNode;
- if(this._postResize && node != this._selectedTab){
- this._selectedTab = node;
-
- var sl = this._getScroll();
-
- if(sl > node.offsetLeft ||
- sl + dojo.style(this.scrollNode, "width") <
- node.offsetLeft + dojo.style(node, "width")){
- this.createSmoothScroll().play();
- }
- }
-
+ // This method is called when mouse is moved over our container (like onmouseenter)
+ // tags:
+ // private
this.inherited(arguments);
+ DNDManager.manager().overSource(this);
},
-
- _getScrollBounds: function(){
+ onOutEvent: function(){
// summary:
- // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
- // tabs (respectively)
- var children = this.getChildren(),
- scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
- containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
- maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
- tabsWidth = this._getTabsWidth();
-
- if(children.length && tabsWidth > scrollNodeWidth){
- // Scrolling should happen
- return {
- min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
- max: this.isLeftToRight() ?
- (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
- maxPossibleScroll
- };
- }else{
- // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
- var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
- return {
- min: onlyScrollPosition,
- max: onlyScrollPosition
- };
+ // 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);
}
- },
-
- _getScrollForSelectedTab: function(){
- // summary:
- // Returns the scroll value setting so that the selected tab
- // will appear in the center
- var w = this.scrollNode,
- n = this._selectedTab,
- scrollNodeWidth = dojo.style(this.scrollNode, "width"),
- scrollBounds = this._getScrollBounds();
-
- // TODO: scroll minimal amount (to either right or left) so that
- // selected tab is fully visible, and just return if it's already visible?
- var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
- pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
+ m.outSource(this);
- // 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;
+ this.inherited(arguments);
},
- createSmoothScroll: function(x){
+ _isParentChildDrop: function(source, targetRow){
// 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();
- }
+ // 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(this._anim && this._anim.status() == "playing"){
- this._anim.stop();
+ // 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 self = this,
- w = this.scrollNode,
- anim = new dojo._Animation({
- beforeBegin: function(){
- if(this.curve){ delete this.curve; }
- var oldS = w.scrollLeft,
- newS = self._convertToScrollLeft(x);
- anim.curve = new dojo._Line(oldS, newS);
- },
- onAnimate: function(val){
- w.scrollLeft = val;
- }
- });
- this._anim = anim;
- // Disable/enable left/right buttons according to new scroll position
- this._setButtonClass(x);
+ var root = source.tree.domNode;
+ var ids = source.selection;
- return anim; // dojo._Animation
- },
+ var node = targetRow.parentNode;
- _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 && !dojo.hasClass(n, "tabStripButton")){
- n = n.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 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));
+ return node.id && ids[node.id];
},
- doSlide: function(/*Number*/ direction, /*DomNode*/ node){
+ _unmarkTargetAnchor: function(){
// summary:
- // Scrolls the tab list to the left or right by 75% of the widget width.
- // direction:
- // If the direction is 1, the widget scrolls to the right, if it is
- // -1, it scrolls to the left.
-
- if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
-
- var sWidth = dojo.style(this.scrollNode, "width");
- var d = (sWidth * 0.75) * direction;
-
- var to = this._getScroll() + d;
-
- this._setButtonClass(to);
-
- this.createSmoothScroll(to).play();
+ // 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;
},
- _setButtonClass: function(/*Number*/ scroll){
+ _markDndStatus: function(copy){
// 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);
+ // Changes source's state based on "copy" status
+ this._changeState("Source", copy ? "Copied" : "Moved");
}
});
-
-dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
- baseClass: "dijitTab tabStripButton",
-
- templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t\t<span dojoAttachPoint=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>\n"),
-
- // 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; }
});
-dojo.declare("dijit.layout._ScrollingTabControllerButton",
- [dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]);
-
-dojo.declare(
- "dijit.layout._ScrollingTabControllerMenuButton",
- [dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin],
-{
- // id of the TabContainer itself
- containerId: "",
+},
+'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/_base/sniff", // has("ie")
+ "./_base/manager", // manager._isElementShown
+ "." // for exporting methods to dijit namespace
+], function(array, config, declare, dom, domAttr, domStyle, has, manager, dijit){
+
+ // module:
+ // dijit/a11y
+ // summary:
+ // Accessibility utility functions (keyboard, tab stops, etc.)
- // -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",
+ 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");
+ });
- isLoaded: function(){
- // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
- return false;
- },
+ dijit.hasDefaultTabStop = function(/*Element*/ elem){
+ // summary:
+ // Tests if element is tab-navigable even without an explicit tabIndex setting
- loadDropDown: function(callback){
- this.dropDown = new dijit.Menu({
- id: this.containerId + "_menu",
- dir: this.dir,
- lang: this.lang
- });
- var container = dijit.byId(this.containerId);
- dojo.forEach(container.getChildren(), function(page){
- var menuItem = new dijit.MenuItem({
- id: page.id + "_stcMi",
- label: page.title,
- iconClass: page.iconClass,
- dir: page.dir,
- lang: page.lang,
- onClick: function(){
- container.selectChild(page);
+ // 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;
+ }
}
- });
- this.dropDown.addChild(menuItem);
- }, this);
- callback();
- },
-
- closeDropDown: function(/*Boolean*/ focus){
- this.inherited(arguments);
- if(this.dropDown){
- this.dropDown.destroyRecursive();
- delete this.dropDown;
+ return body && (body.contentEditable == 'true' ||
+ (body.firstChild && body.firstChild.contentEditable == 'true'));
+ default:
+ return elem.contentEditable == 'true';
}
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.layout.TabContainer"] = true;
-dojo.provide("dijit.layout.TabContainer");
-
-
+ };
+ 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);
+ }
+ });
-dojo.declare("dijit.layout.TabContainer",
- dijit.layout._TabContainerBase,
- {
+ dijit._getTabNavigable = function(/*DOMNode*/ root){
// summary:
- // A Container with tabs to select each child (only one of which is displayed at a time).
+ // Finds descendants of the specified root node.
+ //
// description:
- // A TabContainer is a container that has multiple panes, but shows only
- // one pane at a time. There are a set of tabs corresponding to each pane,
- // where each tab has the name (aka title) of the pane, and optionally a close button.
-
- // useMenu: [const] Boolean
- // True if a menu should be used to select tabs when they are too
- // wide to fit the TabContainer, false otherwise.
- useMenu: true,
-
- // useSlider: [const] Boolean
- // True if a slider should be used to select tabs when they are too
- // wide to fit the TabContainer, false otherwise.
- useSlider: true,
-
- // controllerWidget: String
- // An optional parameter to override the widget used to display the tab labels
- controllerWidget: "",
-
- _makeController: function(/*DomNode*/ srcNode){
- // summary:
- // Instantiate tablist controller widget and return reference to it.
- // Callback from _TabContainerBase.postCreate().
- // tags:
- // protected extension
-
- var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
- TabController = dojo.getObject(this.controllerWidget);
+ // 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 = {};
- return new TabController({
- id: this.id + "_tablist",
- dir: this.dir,
- lang: this.lang,
- tabPosition: this.tabPosition,
- doLayout: this.doLayout,
- containerId: this.id,
- "class": cls,
- nested: this.nested,
- useMenu: this.useMenu,
- useSlider: this.useSlider,
- tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
- }, srcNode);
- },
+ 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();
+ }
- postMixInProperties: function(){
- this.inherited(arguments);
+ 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") && child.scopeName !== "HTML") || !shown(child)){
+ continue;
+ }
- // Scrolling controller only works for horizontal non-nested tabs
- if(!this.controllerWidget){
- this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
- "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
+ if(isTabNavigable(child)){
+ var tabindex = domAttr.get(child, "tabIndex");
+ 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;
}
-});
-
-}
-
-if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.number"] = true;
-dojo.provide("dojo.number");
-
-
-
-
-
-dojo.getObject("number", true, dojo);
-
-/*=====
-dojo.number = {
- // summary: localized formatting and parsing routines for Number
-}
-dojo.number.__FormatOptions = function(){
- // pattern: String?
- // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
- // with this string. Default value is based on locale. Overriding this property will defeat
- // localization. Literal characters in patterns are not supported.
- // type: String?
- // choose a format type based on the locale from the following:
- // decimal, scientific (not yet supported), percent, currency. decimal by default.
- // places: Number?
- // fixed number of decimal places to show. This overrides any
- // information in the provided pattern.
- // round: Number?
- // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
- // means do not round.
- // locale: String?
- // override the locale used to determine formatting rules
- // fractional: Boolean?
- // If false, show no decimal places, overriding places and pattern settings.
- this.pattern = pattern;
- this.type = type;
- this.places = places;
- this.round = round;
- this.locale = locale;
- this.fractional = fractional;
-}
-=====*/
+ return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
+ };
+ dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
+ // summary:
+ // Finds the descendant of the specified root node
+ // that is first in the tabbing order
+ var elems = dijit._getTabNavigable(dom.byId(root));
+ return elems.lowest ? elems.lowest : elems.first; // DomNode
+ };
-dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
- // summary:
- // Format a Number as a String, using locale-specific settings
- // description:
- // Create a string from a Number using a known localized pattern.
- // Formatting patterns appropriate to the locale are chosen from the
- // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
- // delimiters.
- // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
- // value:
- // the number to be formatted
+ dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
+ // summary:
+ // Finds the descendant of the specified root node
+ // that is last in the tabbing order
+ var elems = dijit._getTabNavigable(dom.byId(root));
+ return elems.last ? elems.last : elems.highest; // DomNode
+ };
- options = dojo.mixin({}, options || {});
- var locale = dojo.i18n.normalizeLocale(options.locale),
- bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
- options.customs = bundle;
- var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
- if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
- return dojo.number._applyPattern(value, pattern, options); // String
-};
+ return {
+ hasDefaultTabStop: dijit.hasDefaultTabStop,
+ isTabNavigable: dijit.isTabNavigable,
+ _getTabNavigable: dijit._getTabNavigable,
+ getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
+ getLastInTabbingOrder: dijit.getLastInTabbingOrder
+ };
+});
-//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
-dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
+},
+'dijit/form/_ToggleButtonMixin':function(){
+define("dijit/form/_ToggleButtonMixin", [
+ "dojo/_base/declare", // declare
+ "dojo/dom-attr" // domAttr.set
+], function(declare, domAttr){
+
+// module:
+// dijit/form/_ToggleButtonMixin
+// summary:
+// A mixin to provide functionality to allow a button that can be in two states (checked or not).
-dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
+return declare("dijit.form._ToggleButtonMixin", null, {
// summary:
- // Apply pattern to format value as a string using options. Gives no
- // consideration to local customs.
- // value:
- // the number to be formatted.
- // pattern:
- // a pattern string as described by
- // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
- // options: dojo.number.__FormatOptions?
- // _applyPattern is usually called via `dojo.number.format()` which
- // populates an extra property in the options parameter, "customs".
- // The customs object specifies group and decimal parameters if set.
+ // A mixin to provide functionality to allow a button that can be in two states (checked or not).
- //TODO: support escapes
- options = options || {};
- var group = options.customs.group,
- decimal = options.customs.decimal,
- patternList = pattern.split(';'),
- positivePattern = patternList[0];
- pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
-
- //TODO: only test against unescaped
- if(pattern.indexOf('%') != -1){
- value *= 100;
- }else if(pattern.indexOf('\u2030') != -1){
- value *= 1000; // per mille
- }else if(pattern.indexOf('\u00a4') != -1){
- group = options.customs.currencyGroup || group;//mixins instead?
- decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
- pattern = pattern.replace(/\u00a4{1,3}/, function(match){
- var prop = ["symbol", "currency", "displayName"][match.length-1];
- return options[prop] || options.currency || "";
- });
- }else if(pattern.indexOf('E') != -1){
- throw new Error("exponential notation not supported");
- }
-
- //TODO: support @ sig figs?
- var numberPatternRE = dojo.number._numberPatternRE;
- var numberPattern = positivePattern.match(numberPatternRE);
- if(!numberPattern){
- throw new Error("unable to find a number expression in pattern: "+pattern);
- }
- if(options.fractional === false){ options.places = 0; }
- return pattern.replace(numberPatternRE,
- dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
-};
-
-dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
- // summary:
- // Rounds to the nearest value with the given number of decimal places, away from zero
- // description:
- // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
- // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
- // fractional increments also, such as the nearest quarter.
- // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
- // value:
- // The number to round
- // places:
- // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
- // Must be non-negative.
- // increment:
- // Rounds next place to nearest value of increment/10. 10 by default.
- // example:
- // >>> dojo.number.round(-0.5)
- // -1
- // >>> dojo.number.round(162.295, 2)
- // 162.29 // note floating point error. Should be 162.3
- // >>> dojo.number.round(10.71, 0, 2.5)
- // 10.75
- var factor = 10 / (increment || 10);
- return (factor * +value).toFixed(places) / factor; // Number
-};
-
-if((0.9).toFixed() == 0){
- // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
- // is just after the rounding place and is >=5
- (function(){
- var round = dojo.number.round;
- dojo.number.round = function(v, p, m){
- var d = Math.pow(10, -p || 0), a = Math.abs(v);
- if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
- d = 0;
- }
- return round(v, p, m) + (v > 0 ? d : -d);
- };
- })();
-}
-
-/*=====
-dojo.number.__FormatAbsoluteOptions = function(){
- // decimal: String?
- // the decimal separator
- // group: String?
- // the group separator
- // places: Number?|String?
- // number of decimal places. the range "n,m" will format to m places.
- // round: Number?
- // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
- // means don't round.
- this.decimal = decimal;
- this.group = group;
- this.places = places;
- this.round = round;
-}
-=====*/
+ // 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,
-dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
- // summary:
- // Apply numeric pattern to absolute value using options. Gives no
- // consideration to local customs.
- // value:
- // the number to be formatted, ignores sign
- // pattern:
- // the number portion of a pattern (e.g. `#,##0.00`)
- options = options || {};
- if(options.places === true){options.places=0;}
- if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
+ // aria-pressed for toggle buttons, and aria-checked for checkboxes
+ _aria_attr: "aria-pressed",
- var patternParts = pattern.split("."),
- comma = typeof options.places == "string" && options.places.indexOf(","),
- maxPlaces = options.places;
- if(comma){
- maxPlaces = options.places.substring(comma + 1);
- }else if(!(maxPlaces >= 0)){
- maxPlaces = (patternParts[1] || []).length;
- }
- if(!(options.round < 0)){
- value = dojo.number.round(value, maxPlaces, options.round);
- }
+ _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;
+ },
- var valueParts = String(Math.abs(value)).split("."),
- fractional = valueParts[1] || "";
- if(patternParts[1] || options.places){
- if(comma){
- options.places = options.places.substring(0, comma);
- }
- // Pad fractional with trailing zeros
- var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
- if(pad > fractional.length){
- valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
- }
+ _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
+ this._set("checked", value);
+ domAttr.set(this.focusNode || this.domNode, "checked", value);
+ (this.focusNode || this.domNode).setAttribute(this._aria_attr, value ? "true" : "false"); // aria values should be strings
+ this._handleOnChange(value, priorityChange);
+ },
- // Truncate fractional
- if(maxPlaces < fractional.length){
- valueParts[1] = fractional.substr(0, maxPlaces);
- }
- }else{
- if(valueParts[1]){ valueParts.pop(); }
- }
+ reset: function(){
+ // summary:
+ // Reset the widget's value to what it was at initialization time
- // Pad whole with leading zeros
- var patternDigits = patternParts[0].replace(',', '');
- pad = patternDigits.indexOf("0");
- if(pad != -1){
- pad = patternDigits.length - pad;
- if(pad > valueParts[0].length){
- valueParts[0] = dojo.string.pad(valueParts[0], pad);
- }
+ this._hasBeenBlurred = false;
- // Truncate whole
- if(patternDigits.indexOf("#") == -1){
- valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
- }
+ // set checked state to original setting
+ this.set('checked', this.params.checked || false);
}
+});
- // 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 || ".");
-};
+},
+'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/_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, kernel, lang, query, ready,
+ registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
/*=====
-dojo.number.__RegexpOptions = function(){
- // pattern: String?
- // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
- // with this string. Default value is based on locale. Overriding this property will defeat
- // localization.
- // type: String?
- // choose a format type based on the locale from the following:
- // decimal, scientific (not yet supported), percent, currency. decimal by default.
- // locale: String?
- // override the locale used to determine formatting rules
- // strict: Boolean?
- // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
- // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
- // places: Number|String?
- // number of decimal places to accept: Infinity, a positive number, or
- // a range "n,m". Defined by pattern or Infinity if pattern not provided.
- this.pattern = pattern;
- this.type = type;
- this.locale = locale;
- this.strict = strict;
- this.places = places;
-}
+ var _WidgetBase = dijit._WidgetBase;
+ var _OnDijitClickMixin = dijit._OnDijitClickMixin;
+ var _FocusMixin = dijit._FocusMixin;
=====*/
-dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
- // summary:
- // Builds the regular needed to parse a number
- // description:
- // Returns regular expression with positive and negative match, group
- // and decimal separators
- return dojo.number._parseInfo(options).regexp; // String
-};
-
-dojo.number._parseInfo = function(/*Object?*/options){
- options = options || {};
- var locale = dojo.i18n.normalizeLocale(options.locale),
- bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
- pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
-//TODO: memoize?
- group = bundle.group,
- decimal = bundle.decimal,
- factor = 1;
-
- if(pattern.indexOf('%') != -1){
- factor /= 100;
- }else if(pattern.indexOf('\u2030') != -1){
- factor /= 1000; // per mille
- }else{
- var isCurrency = pattern.indexOf('\u00a4') != -1;
- if(isCurrency){
- group = bundle.currencyGroup || group;
- decimal = bundle.currencyDecimal || decimal;
- }
- }
-
- //TODO: handle quoted escapes
- var patternList = pattern.split(';');
- if(patternList.length == 1){
- patternList.push("-" + patternList[0]);
- }
-
- var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
- pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
- return pattern.replace(dojo.number._numberPatternRE, function(format){
- var flags = {
- signed: false,
- separator: options.strict ? group : [group,""],
- fractional: options.fractional,
- decimal: decimal,
- exponent: false
- },
-
- parts = format.split('.'),
- places = options.places;
-
- // special condition for percent (factor != 1)
- // allow decimal places even if not specified in pattern
- if(parts.length == 1 && factor != 1){
- parts[1] = "###";
- }
- if(parts.length == 1 || places === 0){
- flags.fractional = false;
- }else{
- if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
- if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
- if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
- flags.places = places;
- }
- var groups = parts[0].split(',');
- if(groups.length > 1){
- flags.groupSize = groups.pop().length;
- if(groups.length > 1){
- flags.groupSize2 = groups.pop().length;
- }
- }
- return "("+dojo.number._realNumberRegexp(flags)+")";
- });
- }, true);
- if(isCurrency){
- // substitute the currency symbol for the placeholder in the pattern
- re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
- var prop = ["symbol", "currency", "displayName"][target.length-1],
- symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
- before = before ? "[\\s\\xa0]" : "";
- after = after ? "[\\s\\xa0]" : "";
- if(!options.strict){
- if(before){before += "*";}
- if(after){after += "*";}
- return "(?:"+before+symbol+after+")?";
- }
- return before+symbol+after;
- });
- }
-//TODO: substitute localized sign/percent/permille/etc.?
+// module:
+// dijit/_Widget
+// summary:
+// Old base for widgets. New widgets should extend _WidgetBase instead
- // normalize whitespace and return
- return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
-};
-/*=====
-dojo.number.__ParseOptions = function(){
- // pattern: String?
- // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
- // with this string. Default value is based on locale. Overriding this property will defeat
- // localization. Literal characters in patterns are not supported.
- // type: String?
- // choose a format type based on the locale from the following:
- // decimal, scientific (not yet supported), percent, currency. decimal by default.
- // locale: String?
- // override the locale used to determine formatting rules
- // strict: Boolean?
- // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
- // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
- // fractional: Boolean?|Array?
- // Whether to include the fractional portion, where the number of decimal places are implied by pattern
- // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
- this.pattern = pattern;
- this.type = type;
- this.locale = locale;
- this.strict = strict;
- this.fractional = fractional;
-}
-=====*/
-dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
+function connectToDomNode(){
// summary:
- // Convert a properly formatted string to a primitive Number, using
- // locale-specific settings.
- // description:
- // Create a Number from a string using a known localized pattern.
- // Formatting patterns are chosen appropriate to the locale
- // and follow the syntax described by
- // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
- // Note that literal characters in patterns are not supported.
- // expression:
- // A string representation of a Number
- var info = dojo.number._parseInfo(options),
- results = (new RegExp("^"+info.regexp+"$")).exec(expression);
- if(!results){
- return NaN; //NaN
- }
- var absoluteMatch = results[1]; // match for the positive expression
- if(!results[1]){
- if(!results[2]){
- return NaN; //NaN
- }
- // matched the negative pattern
- absoluteMatch =results[2];
- info.factor *= -1;
- }
-
- // Transform it to something Javascript can parse as a number. Normalize
- // decimal point and strip out group separators or alternate forms of whitespace
- absoluteMatch = absoluteMatch.
- replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
- replace(info.decimal, ".");
- // Adjust for negative sign, percent, etc. as necessary
- return absoluteMatch * info.factor; //Number
-};
-
-/*=====
-dojo.number.__RealNumberRegexpFlags = function(){
- // places: Number?
- // The integer number of decimal places or a range given as "n,m". If
- // not given, the decimal part is optional and the number of places is
- // unlimited.
- // decimal: String?
- // A string for the character used as the decimal point. Default
- // is ".".
- // fractional: Boolean?|Array?
- // Whether decimal places are used. Can be true, false, or [true,
- // false]. Default is [true, false] which means optional.
- // exponent: Boolean?|Array?
- // Express in exponential notation. Can be true, false, or [true,
- // false]. Default is [true, false], (i.e. will match if the
- // exponential part is present are not).
- // eSigned: Boolean?|Array?
- // The leading plus-or-minus sign on the exponent. Can be true,
- // false, or [true, false]. Default is [true, false], (i.e. will
- // match if it is signed or unsigned). flags in regexp.integer can be
- // applied.
- this.places = places;
- this.decimal = decimal;
- this.fractional = fractional;
- this.exponent = exponent;
- this.eSigned = eSigned;
+ // If user connects to a widget method === this function, then they will
+ // instead actually be connecting the equivalent event on this.domNode
}
-=====*/
-
-dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
- // summary:
- // Builds a regular expression to match a real number in exponential
- // notation
-
- // assign default values to missing parameters
- flags = flags || {};
- //TODO: use mixin instead?
- if(!("places" in flags)){ flags.places = Infinity; }
- if(typeof flags.decimal != "string"){ flags.decimal = "."; }
- if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
- if(!("exponent" in flags)){ flags.exponent = [true, false]; }
- if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
-
- var integerRE = dojo.number._integerRegexp(flags),
- decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
- function(q){
- var re = "";
- if(q && (flags.places!==0)){
- re = "\\" + flags.decimal;
- if(flags.places == Infinity){
- re = "(?:" + re + "\\d+)?";
- }else{
- re += "\\d{" + flags.places + "}";
- }
- }
- return re;
- },
- true
- );
- var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
- function(q){
- if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
- return "";
+// 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));
}
- );
-
- var realRE = integerRE + decimalRE;
- // allow for decimals without integers, e.g. .25
- if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
- return realRE + exponentRE; // String
-};
-
-/*=====
-dojo.number.__IntegerRegexpFlags = function(){
- // signed: Boolean?
- // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
- // Default is `[true, false]`, (i.e. will match if it is signed
- // or unsigned).
- // separator: String?
- // The character used as the thousands separator. Default is no
- // separator. For more than one symbol use an array, e.g. `[",", ""]`,
- // makes ',' optional.
- // groupSize: Number?
- // group size between separators
- // groupSize2: Number?
- // second grouping, where separators 2..n have a different interval than the first separator (for India)
- this.signed = signed;
- this.separator = separator;
- this.groupSize = groupSize;
- this.groupSize2 = groupSize2;
+ return originalConnect.apply(connect, arguments);
+ };
}
-=====*/
-
-dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
- // summary:
- // Builds a regular expression that matches an integer
-
- // assign default values to missing parameters
- flags = flags || {};
- if(!("signed" in flags)){ flags.signed = [true, false]; }
- if(!("separator" in flags)){
- flags.separator = "";
- }else if(!("groupSize" in flags)){
- flags.groupSize = 3;
- }
-
- var signRE = dojo.regexp.buildGroupRE(flags.signed,
- function(q){ return q ? "[-+]" : ""; },
- true
- );
-
- var numberRE = dojo.regexp.buildGroupRE(flags.separator,
- function(sep){
- if(!sep){
- return "(?:\\d+)";
- }
-
- sep = dojo.regexp.escapeString(sep);
- if(sep == " "){ sep = "\\s"; }
- else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
-
- var grp = flags.groupSize, grp2 = flags.groupSize2;
- //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
- if(grp2){
- var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
- return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
- }
- return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
- },
- true
- );
-
- return signRE + numberRE; // String
-};
-
+aspect.around(connect, "connect", aroundAdvice);
+if(kernel.connect){
+ aspect.around(kernel, "connect", aroundAdvice);
}
-if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.ProgressBar"] = true;
-dojo.provide("dijit.ProgressBar");
-
-
-
-
-
-
-dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
+var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
// summary:
- // A progress indication widget, showing the amount completed
- // (often the percentage completed) of a task.
+ // Base class for all Dijit widgets.
//
- // example:
- // | <div dojoType="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: '',
+ // 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 id like .dj_ie on <html> node)
+ // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
- templateString: dojo.cache("dijit", "templates/ProgressBar.html", "<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div dojoAttachPoint=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&nbsp;</span\n\t></div\n\t><div dojoAttachPoint=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img dojoAttachPoint=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"),
- // _indeterminateHighContrastImagePath: [private] dojo._URL
- // URL to image to use for indeterminate progress bar when display is in high contrast mode
- _indeterminateHighContrastImagePath:
- dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
+ ////////////////// DEFERRED CONNECTS ///////////////////
- postMixInProperties: function(){
- this.inherited(arguments);
- if(!("value" in this.params)){
- this.value = this.indeterminate ? Infinity : this.progress;
- }
+ onClick: connectToDomNode,
+ /*=====
+ onClick: function(event){
+ // summary:
+ // Connect to this function to receive notifications of mouse click events.
+ // event:
+ // mouse Event
+ // tags:
+ // callback
},
-
- buildRendering: function(){
- this.inherited(arguments);
- this.indeterminateHighContrastImage.setAttribute("src",
- this._indeterminateHighContrastImagePath.toString());
- this.update();
+ =====*/
+ onDblClick: connectToDomNode,
+ /*=====
+ onDblClick: function(event){
+ // summary:
+ // Connect to this function to receive notifications of mouse double click events.
+ // event:
+ // mouse Event
+ // tags:
+ // callback
},
-
- update: function(/*Object?*/attributes){
+ =====*/
+ onKeyDown: connectToDomNode,
+ /*=====
+ onKeyDown: function(event){
// 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 ..." })
+ // Connect to this function to receive notifications of keys being pressed down.
+ // event:
+ // key Event
// tags:
- // private
-
- // TODO: deprecate this method and use set() instead
-
- dojo.mixin(this, attributes || {});
- var tip = this.internalProgress, ap = this.domNode;
- var percent = 1;
- if(this.indeterminate){
- dijit.removeWaiState(ap, "valuenow");
- dijit.removeWaiState(ap, "valuemin");
- dijit.removeWaiState(ap, "valuemax");
- }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.progress / this.maximum;
- }
-
- dijit.setWaiState(ap, "describedby", this.labelNode.id);
- dijit.setWaiState(ap, "valuenow", this.progress);
- dijit.setWaiState(ap, "valuemin", 0);
- dijit.setWaiState(ap, "valuemax", this.maximum);
- }
- this.labelNode.innerHTML = this.report(percent);
-
- dojo.toggleClass(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
- tip.style.width = (percent * 100) + "%";
- this.onChange();
+ // callback
},
-
- _setValueAttr: function(v){
- this._set("value", v);
- if(v == Infinity){
- this.update({indeterminate:true});
- }else{
- this.update({indeterminate:false, progress:v});
- }
+ =====*/
+ onKeyPress: connectToDomNode,
+ /*=====
+ onKeyPress: function(event){
+ // summary:
+ // Connect to this function to receive notifications of printable keys being typed.
+ // event:
+ // key Event
+ // tags:
+ // callback
},
-
- _setLabelAttr: function(label){
- this._set("label", label);
- this.update();
+ =====*/
+ onKeyUp: connectToDomNode,
+ /*=====
+ onKeyUp: function(event){
+ // summary:
+ // Connect to this function to receive notifications of keys being released.
+ // event:
+ // key Event
+ // tags:
+ // callback
},
-
- _setIndeterminateAttr: function(indeterminate){
- // Deprecated, use set("value", ...) instead
- this.indeterminate = indeterminate;
- this.update();
+ =====*/
+ 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
},
-
- report: function(/*float*/percent){
+ =====*/
+ onMouseMove: connectToDomNode,
+ /*=====
+ onMouseMove: function(event){
// summary:
- // Generates message to show inside progress bar (normally indicating amount of task completed).
- // May be overridden.
+ // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
+ // event:
+ // mouse Event
// tags:
- // extension
-
- return this.label ? this.label :
- (this.indeterminate ? "&nbsp;" : dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
+ // callback
},
-
- onChange: function(){
+ =====*/
+ onMouseOut: connectToDomNode,
+ /*=====
+ onMouseOut: function(event){
// summary:
- // Callback fired when progress updates.
+ // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
+ // event:
+ // mouse Event
// tags:
- // extension
- }
-});
-
-}
-
-if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.ToolbarSeparator"] = true;
-dojo.provide("dijit.ToolbarSeparator");
-
-
-
-
-dojo.declare("dijit.ToolbarSeparator",
- [ dijit._Widget, dijit._Templated ],
- {
+ // callback
+ },
+ =====*/
+ onMouseOver: connectToDomNode,
+ /*=====
+ onMouseOver: function(event){
// summary:
- // A spacer between two `dijit.Toolbar` items
- templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
- buildRendering: function(){
- this.inherited(arguments);
- dojo.setSelectable(this.domNode, false);
- },
- isFocusable: function(){
- // summary:
- // This widget isn't focusable, so pass along that fact.
- // tags:
- // protected
- return false;
- }
-
- });
-
-}
-
-if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.Toolbar"] = true;
-dojo.provide("dijit.Toolbar");
-
-
-
-
-
-
-// Note: require of ToolbarSeparator is for back-compat, remove for 2.0
-
-dojo.declare("dijit.Toolbar",
- [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
- {
- // summary:
- // A Toolbar widget, used to hold things like `dijit.Editor` buttons
-
- templateString:
- '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
- // '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
- // '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
- // '</table>' +
- '</div>',
-
- baseClass: "dijitToolbar",
-
- postCreate: function(){
- this.inherited(arguments);
-
- this.connectKeyNavHandlers(
- this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
- this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
- );
+ // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
+ // event:
+ // mouse Event
+ // tags:
+ // callback
},
-
- startup: function(){
- if(this._started){ return; }
-
- this.startupKeyNavChildren();
-
- this.inherited(arguments);
- }
-}
-);
-
-}
-
-if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.DeferredList"] = true;
-dojo.provide("dojo.DeferredList");
-
-
-dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
- // summary:
- // Provides event handling for a group of Deferred objects.
- // description:
- // DeferredList takes an array of existing deferreds and returns a new deferred of its own
- // this new deferred will typically have its callback fired when all of the deferreds in
- // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
- // fireOnOneErrback, will fire before all the deferreds as appropriate
- //
- // list:
- // The list of deferreds to be synchronizied with this DeferredList
- // fireOnOneCallback:
- // Will cause the DeferredLists callback to be fired as soon as any
- // of the deferreds in its list have been fired instead of waiting until
- // the entire list has finished
- // fireonOneErrback:
- // Will cause the errback to fire upon any of the deferreds errback
- // canceller:
- // A deferred canceller function, see dojo.Deferred
- var resultList = [];
- dojo.Deferred.call(this);
- var self = this;
- if(list.length === 0 && !fireOnOneCallback){
- this.resolve([0, []]);
- }
- var finished = 0;
- dojo.forEach(list, function(item, i){
- item.then(function(result){
- if(fireOnOneCallback){
- self.resolve([i, result]);
- }else{
- addResult(true, result);
- }
- },function(error){
- if(fireOnOneErrback){
- self.reject(error);
- }else{
- addResult(false, error);
- }
- if(consumeErrors){
- return null;
- }
- throw error;
- });
- function addResult(succeeded, result){
- resultList[i] = [succeeded, result];
- finished++;
- if(finished === list.length){
- self.resolve(resultList);
- }
-
- }
- });
-};
-dojo.DeferredList.prototype = new dojo.Deferred();
-
-dojo.DeferredList.prototype.gatherResults= function(deferredList){
- // summary:
- // Gathers the results of the deferreds for packaging
- // as the parameters to the Deferred Lists' callback
-
- var d = new dojo.DeferredList(deferredList, false, true, false);
- d.addCallback(function(results){
- var ret = [];
- dojo.forEach(results, function(result){
- ret.push(result[1]);
- });
- return ret;
- });
- return d;
-};
-
-}
-
-if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
-dojo.provide("dijit.tree.TreeStoreModel");
-
-
-dojo.declare(
- "dijit.tree.TreeStoreModel",
- null,
- {
+ =====*/
+ onMouseLeave: connectToDomNode,
+ /*=====
+ onMouseLeave: function(event){
// summary:
- // Implements dijit.Tree.model connecting to a store with a single
- // root item. Any methods passed into the constructor will override
- // the ones defined here.
-
- // store: dojo.data.Store
- // Underlying store
- store: null,
-
- // childrenAttrs: String[]
- // One or more attribute names (attributes in the dojo.data item) that specify that item's children
- childrenAttrs: ["children"],
-
- // newItemIdAttr: String
- // Name of attribute in the Object passed to newItem() that specifies the id.
- //
- // If newItemIdAttr is set then it's used when newItem() is called to see if an
- // item with the same id already exists, and if so just links to the old item
- // (so that the old item ends up with two parents).
- //
- // Setting this to null or "" will make every drop create a new item.
- newItemIdAttr: "id",
-
- // labelAttr: String
- // If specified, get label for tree node from this attribute, rather
- // than by calling store.getLabel()
- labelAttr: "",
-
- // root: [readonly] dojo.data.Item
- // Pointer to the root item (read only, not a parameter)
- root: null,
-
- // query: anything
- // Specifies datastore query to return the root item for the tree.
- // Must only return a single item. Alternately can just pass in pointer
- // to root item.
- // example:
- // | {id:'ROOT'}
- query: null,
-
- // deferItemLoadingUntilExpand: Boolean
- // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
- // until they are expanded. This allows for lazying loading where only one
- // loadItem (and generally one network call, consequently) per expansion
- // (rather than one for each child).
- // This relies on partial loading of the children items; each children item of a
- // fully loaded item should contain the label and info about having children.
- deferItemLoadingUntilExpand: false,
-
- constructor: function(/* Object */ args){
- // summary:
- // Passed the arguments listed above (store, etc)
- // tags:
- // private
-
- dojo.mixin(this, args);
-
- this.connects = [];
-
- var store = this.store;
- if(!store.getFeatures()['dojo.data.api.Identity']){
- throw new Error("dijit.Tree: store must support dojo.data.Identity");
- }
-
- // if the store supports Notification, subscribe to the notification events
- if(store.getFeatures()['dojo.data.api.Notification']){
- this.connects = this.connects.concat([
- dojo.connect(store, "onNew", this, "onNewItem"),
- dojo.connect(store, "onDelete", this, "onDeleteItem"),
- dojo.connect(store, "onSet", this, "onSetItem")
- ]);
- }
- },
-
- destroy: function(){
- dojo.forEach(this.connects, dojo.disconnect);
- // TODO: should cancel any in-progress processing of getRoot(), getChildren()
- },
-
- // =======================================================================
- // Methods for traversing hierarchy
-
- getRoot: function(onItem, onError){
- // summary:
- // Calls onItem with the root item for the tree, possibly a fabricated item.
- // Calls onError on error.
- if(this.root){
- onItem(this.root);
- }else{
- this.store.fetch({
- query: this.query,
- onComplete: dojo.hitch(this, function(items){
- if(items.length != 1){
- throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
- " items, but must return exactly one item");
- }
- this.root = items[0];
- onItem(this.root);
- }),
- onError: onError
- });
- }
- },
-
- mayHaveChildren: function(/*dojo.data.Item*/ item){
- // summary:
- // Tells if an item has or may have children. Implementing logic here
- // avoids showing +/- expando icon for nodes that we know don't have children.
- // (For efficiency reasons we may not want to check if an element actually
- // has children until user clicks the expando node)
- return dojo.some(this.childrenAttrs, function(attr){
- return this.store.hasAttribute(item, attr);
- }, this);
- },
-
- getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
- // summary:
- // Calls onComplete() with array of child items of given parent item, all loaded.
-
- var store = this.store;
- if(!store.isItemLoaded(parentItem)){
- // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
- // mode, so we will load it and just return the children (without loading each
- // child item)
- var getChildren = dojo.hitch(this, arguments.callee);
- store.loadItem({
- item: parentItem,
- onItem: function(parentItem){
- getChildren(parentItem, onComplete, onError);
- },
- onError: onError
- });
- return;
- }
- // get children of specified item
- var childItems = [];
- for(var i=0; i<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){
- dojo.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
- dojo.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(/* dojo.dnd.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.
- // 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){
- dojo.forEach(this.childrenAttrs, function(attr){
- if(store.containsValue(oldParentItem, attr, childItem)){
- if(!bCopy){
- var values = dojo.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(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
- // summary:
- // Callback to do notifications about new, updated, or deleted items.
- // tags:
- // callback
- },
-
- onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
- // 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, dojo.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 */ item,
- /* attribute-name-string */ attribute,
- /* object | array */ oldValue,
- /* object | array */ 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.
- // tags:
- // extension
-
- if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
- // item's children list changed
- this.getChildren(item, dojo.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);
- }
- }
- });
-
-}
-
-if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
-dojo.provide("dijit.tree.ForestStoreModel");
-
-
-
-dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.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){
+ // 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:
- // Sets up variables, etc.
+ // Connect to this function to receive notifications of when the mouse moves onto this widget.
+ // event:
+ // mouse Event
// tags:
- // private
-
- // Make dummy root item
- this.root = {
- store: this,
- root: true,
- id: params.rootId,
- label: params.rootLabel,
- children: params.rootChildren // optional param
- };
+ // callback
},
-
- // =======================================================================
- // Methods for traversing hierarchy
-
- mayHaveChildren: function(/*dojo.data.Item*/ item){
+ =====*/
+ onMouseUp: connectToDomNode,
+ /*=====
+ onMouseUp: function(event){
// 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)
+ // Connect to this function to receive notifications of when the mouse button is released.
+ // event:
+ // mouse Event
// tags:
- // extension
- return item === this.root || this.inherited(arguments);
+ // callback
},
+ =====*/
- 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: dojo.hitch(this, function(items){
- this.root.children = items;
- callback(items);
- }),
- onError: onError
- });
+ constructor: function(params){
+ // 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];
}
- }else{
- this.inherited(arguments);
}
},
- // =======================================================================
- // Inspecting items
-
- isItem: function(/* anything */ something){
- return (something === this.root) ? true : this.inherited(arguments);
- },
+ postCreate: function(){
+ this.inherited(arguments);
- fetchItemByIdentity: function(/* object */ keywordArgs){
- if(keywordArgs.identity == this.root.id){
- var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
- if(keywordArgs.onItem){
- keywordArgs.onItem.call(scope, this.root);
- }
- }else{
- 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;
},
- getIdentity: function(/* item */ item){
- return (item === this.root) ? this.root.id : this.inherited(arguments);
+ on: function(/*String*/ type, /*Function*/ func){
+ if(this[this._onMap(type)] === connectToDomNode){
+ // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
+ // Also, need to specify context as "this" rather than the default context of the DOMNode
+ return connect.connect(this.domNode, type.toLowerCase(), this, func);
+ }
+ return this.inherited(arguments);
},
- getLabel: function(/* item */ item){
- return (item === this.root) ? this.root.label : 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);
},
- // =======================================================================
- // Write interface
+ ////////////////// DEPRECATED METHODS ///////////////////
- newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
+ setAttribute: function(/*String*/ attr, /*anything*/ value){
// 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);
- }
+ // Deprecated. Use set() instead.
+ // tags:
+ // deprecated
+ kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
+ this.set(attr, value);
},
- onNewRootItem: function(args){
+ attr: function(/*String|Object*/name, /*Object?*/value){
// 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
- },
+ // 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.
- 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);
+ // 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;
}
}
- dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, 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);
+
+ var args = arguments.length;
+ if(args >= 2 || typeof name === "object"){ // setter
+ return this.set.apply(this, arguments);
+ }else{ // getter
+ return this.get(name);
}
},
- // =======================================================================
- // Handling for top level children
-
- onAddToRoot: function(/* item */ item){
+ getDescendants: function(){
// 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");
- },
+ // 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.
- 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");
+ kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
+ return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit._Widget[]
},
- // =======================================================================
- // 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: dojo.hitch(this, function(newChildren){
- this.root.children = newChildren;
+ ////////////////// MISCELLANEOUS METHODS ///////////////////
- // If the list of children or the order of children has changed...
- if(oldChildren.length != newChildren.length ||
- dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
- this.onChildrenChange(this.root, newChildren);
- }
- })
- });
+ _onShow: function(){
+ // summary:
+ // Internal method called when this widget is made visible.
+ // See `onShow` for details.
+ this.onShow();
},
- onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
+ onShow: function(){
// 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).
+ // Called when this widget becomes the selected pane in a
+ // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
+ // `dijit.layout.AccordionContainer`, etc.
//
- // 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.
+ // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
// tags:
- // extension
- this._requeryTop();
-
- this.inherited(arguments);
+ // callback
},
- onDeleteItem: function(/*Object*/ item){
+ onHide: function(){
// 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(dojo.indexOf(this.root.children, item) != -1){
- this._requeryTop();
- }
-
- this.inherited(arguments);
+ // 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
},
- onSetItem: function(/* item */ item,
- /* attribute-name-string */ attribute,
- /* object | array */ oldValue,
- /* object | array */ newValue){
+ onClose: function(){
// 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.
+ // 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.
//
- // 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.
+ // 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
- this._requeryTop();
- this.inherited(arguments);
+ return true; // Boolean
}
-
});
+// For back-compat, remove in 2.0.
+if(!kernel.isAsync){
+ ready(0, function(){
+ var requires = ["dijit/_base"];
+ require(requires); // use indirection so modules not rolled into a build
+ });
}
+return _Widget;
+});
-if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.Container"] = true;
-dojo.provide("dojo.dnd.Container");
-
-
-
-
-/*
- Container states:
- "" - normal state
- "Over" - mouse over a container
- Container item states:
- "" - normal state
- "Over" - mouse over a container item
-*/
+},
+'dojo/touch':function(){
+define("dojo/touch", ["./_base/kernel", "./on", "./has", "./mouse"], function(dojo, on, has, mouse){
+// module:
+// dojo/touch
/*=====
-dojo.declare("dojo.dnd.__ContainerArgs", [], {
- creator: function(){
+ dojo.touch = {
// summary:
- // a creator function, which takes a data item, and returns an object like that:
- // {node: newNode, data: usedData, type: arrayOfStrings}
- },
-
- // skipForm: Boolean
- // don't start the drag operation, if clicked on form elements
- skipForm: false,
-
- // dropParent: Node||String
- // node or node's id to use as the parent node for dropped items
- // (must be underneath the 'node' parameter in the DOM)
- dropParent: null,
+ // 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:
+ // 1. Used with dojo.connect()
+ // | dojo.connect(node, dojo.touch.press, function(e){});
+ // | dojo.connect(node, dojo.touch.move, function(e){});
+ // | dojo.connect(node, dojo.touch.release, function(e){});
+ // | dojo.connect(node, dojo.touch.cancel, function(e){});
+ //
+ // 2. 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){});
+ //
+ // 3. Used with dojo.touch.* directly
+ // | dojo.touch.press(node, function(e){});
+ // | dojo.touch.move(node, function(e){});
+ // | dojo.touch.release(node, function(e){});
+ // | dojo.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()
+ }
+ };
+=====*/
- // _skipStartup: Boolean
- // skip startup(), which collects children, for deferred initialization
- // (this is used in the markup mode)
- _skipStartup: false
+ function _handle(/*String - press | move | release | cancel*/type){
+ return function(node, listener){//called by on(), see dojo.on
+ return on(node, type, listener);
+ };
+ }
+ var touch = has("touch");
+ //device neutral events - dojo.touch.press|move|release|cancel
+ dojo.touch = {
+ press: _handle(touch ? "touchstart": "mousedown"),
+ move: _handle(touch ? "touchmove": "mousemove"),
+ release: _handle(touch ? "touchend": "mouseup"),
+ cancel: touch ? _handle("touchcancel") : mouse.leave
+ };
+ return dojo.touch;
});
-
-dojo.dnd.Item = function(){
+},
+'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=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\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
// summary:
- // Represents (one of) the source node(s) being dragged.
- // Contains (at least) the "type" and "data" attributes.
- // type: String[]
- // Type(s) of this item, by default this is ["text"]
- // data: Object
- // Logical representation of the object being dragged.
- // If the drag object's type is "text" then data is a String,
- // if it's another type then data could be a different Object,
- // perhaps a name/value hash.
-
- this.type = type;
- this.data = data;
-}
-=====*/
+ // TODOC
+
-dojo.declare("dojo.dnd.Container", null, {
- // summary:
- // a Container object, which knows when mouse hovers over it,
- // and over which element it hovers
-
- // object attributes (for markup)
- skipForm: false,
-
/*=====
- // current: DomNode
- // The DOM node the mouse is currently hovered over
- current: null,
-
- // map: Hash<String, dojo.dnd.Item>
- // Map from an item's id (which is also the DOMNode's id) to
- // the dojo.dnd.Item itself.
- map: {},
+ dojo.fx = {
+ // summary: Effects library on top of Base animations
+ };
+ var coreFx = dojo.fx;
=====*/
- constructor: function(node, params){
- // summary:
- // a constructor of the Container
- // node: Node
- // node or node's id to build the container on
- // params: dojo.dnd.__ContainerArgs
- // a dictionary of parameters
- this.node = dojo.byId(node);
- if(!params){ params = {}; }
- this.creator = params.creator || null;
- this.skipForm = params.skipForm;
- this.parent = params.dropParent && dojo.byId(params.dropParent);
-
- // class-specific variables
- this.map = {};
- this.current = null;
+// 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
+ });
+}
- // states
- this.containerState = "";
- dojo.addClass(this.node, "dojoDndContainer");
-
- // mark up children
- if(!(params && params._skipStartup)){
- this.startup();
- }
+ var coreFx = dojo.fx = {};
- // set up events
- this.events = [
- dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
- dojo.connect(this.node, "onmouseout", this, "onMouseOut"),
- // cancel text selection and text dragging
- dojo.connect(this.node, "ondragstart", this, "onSelectStart"),
- dojo.connect(this.node, "onselectstart", this, "onSelectStart")
- ];
- },
-
- // object attributes (for markup)
- creator: function(){
- // summary:
- // creator function, dummy at the moment
- },
-
- // abstract access to the map
- getItem: function(/*String*/ key){
- // summary:
- // returns a data item by its key (id)
- return this.map[key]; // dojo.dnd.Item
- },
- setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
- // summary:
- // associates a data item with its key (id)
- this.map[key] = data;
- },
- delItem: function(/*String*/ key){
- // summary:
- // removes a data item from the map by its key (id)
- delete this.map[key];
- },
- forInItems: function(/*Function*/ f, /*Object?*/ o){
- // summary:
- // iterates over a data map skipping members that
- // are present in the empty object (IE and/or 3rd-party libraries).
- o = o || dojo.global;
- var m = this.map, e = dojo.dnd._empty;
- for(var i in m){
- if(i in e){ continue; }
- f.call(o, m[i], i, this);
- }
- return o; // Object
- },
- clearItems: function(){
- // summary:
- // removes all data items from the map
- this.map = {};
- },
-
- // methods
- getAllNodes: function(){
- // summary:
- // returns a list (an array) of all valid child nodes
- return dojo.query("> .dojoDndItem", this.parent); // NodeList
- },
- sync: function(){
- // summary:
- // sync up the node list with the data map
- var map = {};
- this.getAllNodes().forEach(function(node){
- if(node.id){
- var item = this.getItem(node.id);
- if(item){
- map[node.id] = item;
- return;
+ var _baseObj = {
+ _fire: function(evt, args){
+ if(this[evt]){
+ this[evt].apply(this, args||[]);
}
- }else{
- node.id = dojo.dnd.getUniqueId();
+ return this;
}
- var type = node.getAttribute("dndType"),
- data = node.getAttribute("dndData");
- map[node.id] = {
- data: data || node.innerHTML,
- type: type ? type.split(/\s*,\s*/) : ["text"]
- };
+ };
+
+ 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);
- this.map = map;
- return this; // self
- },
- insertNodes: function(data, before, anchor){
- // summary:
- // inserts an array of new nodes before/after an anchor node
- // data: Array
- // a list of data items, which should be processed by the creator function
- // before: Boolean
- // insert before the anchor, if true, and after the anchor otherwise
- // anchor: Node
- // the anchor node to be used as a point of insertion
- if(!this.parent.firstChild){
- anchor = null;
- }else if(before){
- if(!anchor){
- anchor = this.parent.firstChild;
- }
- }else{
- if(anchor){
- anchor = anchor.nextSibling;
- }
- }
- if(anchor){
- for(var i = 0; i < data.length; ++i){
- var t = this._normalizedCreator(data[i]);
- this.setItem(t.node.id, {data: t.data, type: t.type});
- this.parent.insertBefore(t.node, anchor);
+ };
+ _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);
}
- }else{
- for(var i = 0; i < data.length; ++i){
- var t = this._normalizedCreator(data[i]);
- this.setItem(t.node.id, {data: t.data, type: t.type});
- this.parent.appendChild(t.node);
+ },
+ 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);
}
- }
- return this; // self
- },
- destroy: function(){
- // summary:
- // prepares this object to be garbage-collected
- dojo.forEach(this.events, dojo.disconnect);
- this.clearItems();
- this.node = this.parent = this.current = null;
- },
-
- // markup methods
- markupFactory: function(params, node){
- params._skipStartup = true;
- return new dojo.dnd.Container(node, params);
- },
- startup: function(){
- // summary:
- // collects valid child items and populate the map
-
- // set up the real parent node
- if(!this.parent){
- // use the standard algorithm, if not assigned
- this.parent = this.node;
- if(this.parent.tagName.toLowerCase() == "table"){
- var c = this.parent.getElementsByTagName("tbody");
- if(c && c.length){ this.parent = c[0]; }
+ this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
+ if(this._onEndCtx){
+ connect.disconnect(this._onEndCtx);
}
- }
- this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
-
- // process specially marked children
- this.sync();
- },
-
- // mouse events
- onMouseOver: function(e){
- // summary:
- // event processor for onmouseover
- // e: Event
- // mouse event
- var n = e.relatedTarget;
- while(n){
- if(n == this.node){ break; }
- try{
- n = n.parentNode;
- }catch(x){
- n = null;
+ 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();
}
- }
- if(!n){
- this._changeState("Container", "Over");
- this.onOverEvent();
- }
- n = this._getChildByEvent(e);
- if(this.current == n){ return; }
- if(this.current){ this._removeItemClass(this.current, "Over"); }
- if(n){ this._addItemClass(n, "Over"); }
- this.current = n;
- },
- onMouseOut: function(e){
- // summary:
- // event processor for onmouseout
- // e: Event
- // mouse event
- for(var n = e.relatedTarget; n;){
- if(n == this.node){ return; }
- try{
- n = n.parentNode;
- }catch(x){
- n = null;
+ 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);
}
- }
- if(this.current){
- this._removeItemClass(this.current, "Over");
- this.current = null;
- }
- this._changeState("Container", "");
- this.onOutEvent();
- },
- onSelectStart: function(e){
- // summary:
- // event processor for onselectevent and ondragevent
- // e: Event
- // mouse event
- if(!this.skipForm || !dojo.dnd.isFormElement(e)){
- dojo.stopEvent(e);
- }
- },
-
- // utilities
- onOverEvent: function(){
- // summary:
- // this function is called once, when mouse is over our container
- },
- onOutEvent: function(){
- // summary:
- // this function is called once, when mouse is out of our container
- },
- _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";
- //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
- dojo.replaceClass(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
- dojo.addClass(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
- dojo.removeClass(node, "dojoDndItem" + type);
- },
- _getChildByEvent: function(e){
- // summary:
- // gets a child, which is under the mouse at the moment, or null
- // e: Event
- // a mouse event
- var node = e.target;
- if(node){
- for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
- if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
+ 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); }
}
- return null;
- },
- _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
- // summary:
- // adds all necessary data to the output of the user-supplied creator function
- var t = (this.creator || this.defaultCreator).call(this, item, hint);
- if(!dojo.isArray(t.type)){ t.type = ["text"]; }
- if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
- dojo.addClass(t.node, "dojoDndItem");
- return t;
- }
-});
+ });
+ lang.extend(_chain, _baseObj);
-dojo.dnd._createNode = function(tag){
- // summary:
- // returns a function, which creates an element of given tag
- // (SPAN by default) and sets its innerHTML to given text
- // tag: String
- // a tag name or empty for SPAN
- if(!tag){ return dojo.dnd._createSpan; }
- return function(text){ // Function
- return dojo.create(tag, {innerHTML: text}); // Node
+ coreFx.chain = /*===== dojo.fx.chain = =====*/ function(/*dojo.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`
+ // | dojo.fx.chain([
+ // | dojo.fadeIn({ node:node }),
+ // | dojo.fadeOut({ node:otherNode })
+ // | ]).play();
+ //
+ return new _chain(animations); // dojo.Animation
};
-};
-
-dojo.dnd._createTrTd = function(text){
- // summary:
- // creates a TR/TD structure with given text as an innerHTML of TD
- // text: String
- // a text for TD
- var tr = dojo.create("tr");
- dojo.create("td", {innerHTML: text}, tr);
- return tr; // Node
-};
-dojo.dnd._createSpan = function(text){
- // summary:
- // creates a SPAN element with given text as its innerHTML
- // text: String
- // a text for SPAN
- return dojo.create("span", {innerHTML: text}); // Node
-};
+ var _combine = function(animations){
+ this._animations = animations||[];
+ this._connects = [];
+ this._finished = 0;
-// dojo.dnd._defaultCreatorNodes: Object
-// a dictionary that maps container tag names to child tag names
-dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
+ 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);
-dojo.dnd._defaultCreator = function(node){
- // summary:
- // takes a parent node, and returns an appropriate creator function
- // node: Node
- // a container node
- var tag = node.tagName.toLowerCase();
- var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
- dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
- return function(item, hint){ // Function
- var isObj = item && dojo.isObject(item), data, type, n;
- if(isObj && item.tagName && item.nodeType && item.getAttribute){
- // process a DOM node
- data = item.getAttribute("dndData") || item.innerHTML;
- type = item.getAttribute("dndType");
- type = type ? type.split(/\s*,\s*/) : ["text"];
- n = item; // this node is going to be moved rather than copied
- }else{
- // process a DnD item object or a string
- data = (isObj && item.data) ? item.data : item;
- type = (isObj && item.type) ? item.type : ["text"];
- n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
- }
- if(!n.id){
- n.id = dojo.dnd.getUniqueId();
- }
- return {node: n, data: data, type: type};
+ 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); }
+ ));
+ }
+ );
};
-};
-
-}
-
-if(!dojo._hasResource["dijit.tree._dndContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.tree._dndContainer"] = true;
-dojo.provide("dijit.tree._dndContainer");
-
-
-
-
-dojo.getObject("tree", true, dojo);
-
-dijit.tree._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");
- }
-};
-
-dojo.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: dijit.tree.__SourceArgs
- // 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
- dojo.mixin(this, params);
-
- // class-specific variables
- this.map = {};
- this.current = null; // current TreeNode's DOM node
-
- // states
- this.containerState = "";
- dojo.addClass(this.node, "dojoDndContainer");
-
- // set up events
- this.events = [
- // container level events
- dojo.connect(this.node, "onmouseenter", this, "onOverEvent"),
- dojo.connect(this.node, "onmouseleave", this, "onOutEvent"),
-
- // switching between TreeNodes
- dojo.connect(this.tree, "_onNodeMouseEnter", this, "onMouseOver"),
- dojo.connect(this.tree, "_onNodeMouseLeave", this, "onMouseOut"),
-
- // cancel text selection and text dragging
- dojo.connect(this.node, "ondragstart", dojo, "stopEvent"),
- dojo.connect(this.node, "onselectstart", dojo, "stopEvent")
- ];
- },
-
- getItem: function(/*String*/ key){
- // summary:
- // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
- // Called by dojo.dnd.Source.checkAcceptance().
- // tags:
- // protected
-
- var widget = this.selection[key],
- ret = {
- data: widget,
- type: ["treeNode"]
- };
-
- return ret; // dojo.dnd.Item
+ lang.extend(_combine, {
+ _doAction: function(action, args){
+ arrayUtil.forEach(this._animations, function(a){
+ a[action].apply(a, args);
+ });
+ return this;
},
-
- destroy: function(){
- // summary:
- // Prepares this object to be garbage-collected
-
- dojo.forEach(this.events, dojo.disconnect);
- // this.clearItems();
- this.node = this.parent = null;
+ _onEnd: function(){
+ if(++this._finished > this._animations.length){
+ this._fire("onEnd");
+ }
},
-
- // mouse events
- onMouseOver: function(/*TreeNode*/ widget, /*Event*/ evt){
- // summary:
- // Called when mouse is moved over a TreeNode
- // tags:
- // protected
- this.current = widget;
+ _call: function(action, args){
+ var t = this._pseudoAnimation;
+ t[action].apply(t, args);
},
-
- onMouseOut: function(/*TreeNode*/ widget, /*Event*/ evt){
- // summary:
- // Called when mouse is moved away from a TreeNode
- // tags:
- // protected
- this.current = null;
+ play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
+ this._finished = 0;
+ this._doAction("play", arguments);
+ this._call("play", arguments);
+ return this;
},
-
- _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";
- //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
- dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
- this[state] = newState;
+ pause: function(){
+ this._doAction("pause", arguments);
+ this._call("pause", arguments);
+ return this;
},
-
- _addItemClass: function(node, type){
- // summary:
- // Adds a class with prefix "dojoDndItem"
- // node: Node
- // A node
- // type: String
- // A variable suffix for a class name
- dojo.addClass(node, "dojoDndItem" + type);
+ 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;
},
-
- _removeItemClass: function(node, type){
- // summary:
- // Removes a class with prefix "dojoDndItem"
- // node: Node
- // A node
- // type: String
- // A variable suffix for a class name
- dojo.removeClass(node, "dojoDndItem" + type);
+ stop: function(/*boolean?*/ gotoEnd){
+ this._doAction("stop", arguments);
+ this._call("stop", arguments);
+ return this;
},
-
- onOverEvent: function(){
- // summary:
- // This function is called once, when mouse is over our container
- // tags:
- // protected
- this._changeState("Container", "Over");
+ status: function(){
+ return this._pseudoAnimation.status();
},
-
- onOutEvent: function(){
- // summary:
- // This function is called once, when mouse is out of our container
- // tags:
- // protected
- this._changeState("Container", "");
+ destroy: function(){
+ arrayUtil.forEach(this._connects, connect.disconnect);
}
-});
+ });
+ lang.extend(_combine, _baseObj);
-}
+ coreFx.combine = /*===== dojo.fx.combine = =====*/ function(/*dojo.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
+ // | dojo.fx.combine([
+ // | dojo.fadeIn({ node:node }),
+ // | dojo.fadeOut({ node:otherNode })
+ // | ]).play();
+ //
+ // example:
+ // When the longest animation ends, execute a function:
+ // | var anim = dojo.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.Animation
+ };
-if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.tree._dndSelector"] = true;
-dojo.provide("dijit.tree._dndSelector");
+ coreFx.wipeIn = /*===== dojo.fx.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:
+ // | dojo.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.Animation
+ };
-dojo.declare("dijit.tree._dndSelector",
- dijit.tree._dndContainer,
- {
+ coreFx.wipeOut = /*===== dojo.fx.wipeOut = =====*/ function(/*Object*/ args){
// 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
+ // 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:
+ // | dojo.fx.wipeOut({ node:"someId" }).play()
- /*=====
- // selection: Hash<String, DomNode>
- // (id, DomNode) map for every TreeNode that's currently selected.
- // The DOMNode is the TreeNode.rowNode.
- selection: {},
- =====*/
+ var node = args.node = dom.byId(args.node), s = node.style, o;
- constructor: function(tree, params){
- // summary:
- // Initialization
- // tags:
- // private
+ var anim = baseFx.animateProperty(lang.mixin({
+ properties: {
+ height: {
+ end: 1 // 0 causes IE to display the whole panel
+ }
+ }
+ }, args));
- this.selection={};
- this.anchor = null;
+ 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);
- dijit.setWaiState(this.tree.domNode, "multiselect", !this.singular);
+ return anim; // dojo.Animation
+ };
- this.events.push(
- dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"),
- dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"),
- dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove")
- );
- },
+ coreFx.slideTo = /*===== dojo.fx.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()
- // 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,
+ var node = args.node = dom.byId(args.node),
+ top = null, left = null;
- // 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]);
+ 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
}
- return nodes;
- },
+ }, args));
+ connect.connect(anim, "beforeBegin", anim, init);
- selectNone: function(){
- // summary:
- // Unselects all items
- // tags:
- // private
+ return anim; // dojo.Animation
+ };
- this.setSelection([]);
- return this; // self
- },
+ return coreFx;
+});
- destroy: function(){
- // summary:
- // Prepares the object to be garbage-collected
- this.inherited(arguments);
- this.selection = this.anchor = null;
- },
- addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
- // summary
- // add node to current selection
- // node: Node
- // node to add
- // isAnchor: Boolean
- // Whether the node should become anchor.
+},
+'dijit/_DialogMixin':function(){
+define("dijit/_DialogMixin", [
+ "dojo/_base/declare", // declare
+ "./a11y" // _getTabNavigable
+], function(declare, a11y){
+
+ // module:
+ // dijit/_DialogMixin
+ // summary:
+ // _DialogMixin provides functions useful to Dialog and TooltipDialog
- this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
- if(isAnchor){ this.anchor = node; }
- return node;
- },
- removeTreeNode: function(/*dijit._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._TreeNode*/node){
- // summary
- // return true if node is currently selected
- // node: Node
- // the node to check whether it's in the current selection
+ return declare("dijit._DialogMixin", null, {
+ // summary:
+ // This provides functions useful to Dialog and TooltipDialog
- return node.id && !!this.selection[node.id];
- },
- setSelection: function(/*dijit._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();
- dojo.forEach(this._setDifference(oldSelection, newSelection), dojo.hitch(this, function(node){
- node.setSelected(false);
- if(this.anchor == node){
- delete this.anchor;
- }
- delete this.selection[node.id];
- }));
- dojo.forEach(this._setDifference(newSelection, oldSelection), dojo.hitch(this, function(node){
- node.setSelected(true);
- this.selection[node.id] = node;
- }));
- this._updateSelectionProperties();
+ 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
},
- _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.
-
- dojo.forEach(ys, function(y){ y.__exclude__ = true; });
- var ret = dojo.filter(xs, function(x){ return !x.__exclude__; });
- // clean up after ourselves.
- dojo.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 = [];
- dojo.forEach(selected, function(node) {
- nodes.push(node);
- paths.push(node.getTreePath());
- });
- var items = dojo.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);
- },
- // mouse events
- onMouseDown: function(e){
+ onCancel: function(){
// summary:
- // Event processor for onmousedown
- // e: Event
- // mouse event
- // tags:
+ // 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
-
- // ignore click on expando node
- if(!this.current || this.tree.isExpandoNode( e.target, this.current)){ return; }
-
- if(e.button == dojo.mouseButtons.RIGHT){ return; } // ignore right-click
-
- dojo.stopEvent(e);
-
- var treeNode = this.current,
- copy = dojo.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){
+ onExecute: function(){
// summary:
- // Event processor for onmouseup
- // e: Event
- // mouse event
- // tags:
+ // 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
-
- // _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, dojo.isCopyKey( e ), e.shiftKey);
- },
- onMouseMove: function(e){
- // summary
- // event processor for onmousemove
- // e: Event
- // mouse event
- this._doDeselect = false;
},
- userSelect: function(node, multi, range){
+ _onSubmit: function(){
// 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:
+ // Callback when user hits submit button
+ // type:
// 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 = dijit.tree._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;
- }
- 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;
- }
- }
- }
+ this.onExecute(); // notify container that we are about to execute
+ this.execute(this.get('value'));
},
- forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
+ _getFocusItems: function(){
// summary:
- // Iterates over selected items;
- // see `dojo.dnd.Container.forInItems()` for details
- o = o || dojo.global;
- for(var id in this.selection){
- // console.log("selected item id: " + id);
- f.call(o, this.getItem(id), id, this);
- }
+ // 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;
}
+ });
});
-}
-
-if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.Tree"] = true;
-dojo.provide("dijit.Tree");
-
-
-
-
-
-
-
-
-
+},
+'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\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\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\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></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\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\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/_base/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/fx", // fxUtils.wipeIn fxUtils.wipeOut
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/keys", // arrows etc.
+ "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
+ "dojo/topic",
+ "./focus",
+ "./registry", // registry.getEnclosingWidget(), manager.defaultDuration
+ "./_base/manager", // manager.getEnclosingWidget(), 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, fxUtils, kernel, keys, lang, topic,
+ focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
+ treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
+/*=====
+ var _Widget = dijit._Widget;
+ var _TemplatedMixin = dijit._TemplatedMixin;
+ var _CssStateMixin = dijit._CssStateMixin;
+ var _Container = dijit._Container;
+ var _Contained = dijit._Contained;
+=====*/
+// module:
+// dijit/Tree
+// summary:
+// dijit.Tree widget, and internal dijit._TreeNode widget
-dojo.declare(
+var TreeNode = declare(
"dijit._TreeNode",
- [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
+ [_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
{
// summary:
// Single node within a tree. This class is used internally
@@ -23590,7 +30549,7 @@ dojo.declare(
// tags:
// private
- // item: [const] dojo.data.Item
+ // item: [const] Item
// the dojo.data entry this tree represents
item: null,
@@ -23602,6 +30561,7 @@ dojo.declare(
// 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)
@@ -23617,7 +30577,7 @@ dojo.declare(
// then after dojo.data query it becomes "LOADING" and, finally "LOADED"
state: "UNCHECKED",
- templateString: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n"),
+ templateString: treeNodeTemplate,
baseClass: "dijitTreeNode",
@@ -23627,10 +30587,8 @@ dojo.declare(
labelNode: "dijitTreeLabel"
},
- attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
- label: {node: "labelNode", type: "innerText"},
- tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
- }),
+ // 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);
@@ -23642,7 +30600,7 @@ dojo.declare(
this._updateItemClasses(this.item);
if(this.isExpandable){
- dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
+ this.labelNode.setAttribute("aria-expanded", this.isExpanded);
}
//aria-selected should be false on all selectable elements.
@@ -23659,13 +30617,13 @@ dojo.declare(
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
- dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
- dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
+ domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px");
+ domStyle.set(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
- dojo.forEach(this.getChildren(), function(child){
+ array.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
-
+
this._set("indent", indent);
},
@@ -23723,9 +30681,9 @@ dojo.declare(
var oldCls = this[clsName];
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
- dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
-
- dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
+ domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
+
+ domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
@@ -23734,11 +30692,11 @@ dojo.declare(
// tags:
// private
var parent = this.getParent();
- if(!parent || parent.rowNode.style.display == "none"){
+ 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 */
- dojo.addClass(this.domNode, "dijitTreeIsRoot");
+ domClass.add(this.domNode, "dijitTreeIsRoot");
}else{
- dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
+ domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
@@ -23754,7 +30712,7 @@ dojo.declare(
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
- dojo.replaceClass(this.expandoNode, styles[idx], styles);
+ domClass.replace(this.expandoNode, styles[idx], styles);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
@@ -23778,27 +30736,27 @@ dojo.declare(
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
- dijit.setWaiState(this.labelNode, "expanded", "true");
+ this.labelNode.setAttribute("aria-expanded", "true");
if(this.tree.showRoot || this !== this.tree.rootNode){
- dijit.setWaiRole(this.containerNode, "group");
+ this.containerNode.setAttribute("role", "group");
}
- dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
+ domClass.add(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode){
- dijit.setWaiState(this.tree.domNode, "expanded", "true");
+ this.tree.domNode.setAttribute("aria-expanded", "true");
}
var def,
- wipeIn = dojo.fx.wipeIn({
- node: this.containerNode, duration: dijit.defaultDuration,
+ wipeIn = fxUtils.wipeIn({
+ node: this.containerNode, duration: manager.defaultDuration,
onEnd: function(){
def.callback(true);
}
});
// Deferred that fires when expand is complete
- def = (this._expandDeferred = new dojo.Deferred(function(){
+ def = (this._expandDeferred = new Deferred(function(){
// Canceller
wipeIn.stop();
}));
@@ -23821,17 +30779,17 @@ dojo.declare(
}
this.isExpanded = false;
- dijit.setWaiState(this.labelNode, "expanded", "false");
+ this.labelNode.setAttribute("aria-expanded", "false");
if(this == this.tree.rootNode){
- dijit.setWaiState(this.tree.domNode, "expanded", "false");
+ this.tree.domNode.setAttribute("aria-expanded", "false");
}
- dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
+ domClass.remove(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(!this._wipeOut){
- this._wipeOut = dojo.fx.wipeOut({
- node: this.containerNode, duration: dijit.defaultDuration
+ this._wipeOut = fxUtils.wipeOut({
+ node: this.containerNode, duration: manager.defaultDuration
});
}
this._wipeOut.play();
@@ -23859,8 +30817,8 @@ dojo.declare(
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
- dojo.forEach(this.getChildren(), function(child){
- dijit._Container.prototype.removeChild.call(this, child);
+ array.forEach(this.getChildren(), function(child){
+ _Container.prototype.removeChild.call(this, child);
}, this);
this.state = "LOADED";
@@ -23871,7 +30829,7 @@ dojo.declare(
// Create _TreeNode widget for each specified tree node, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
- dojo.forEach(items, function(item){
+ array.forEach(items, function(item){
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
@@ -23893,6 +30851,7 @@ dojo.declare(
tooltip: tree.getTooltip(item),
dir: tree.dir,
lang: tree.lang,
+ textDir: tree.textDir,
indent: this.indent + 1
});
if(existingNodes){
@@ -23905,14 +30864,14 @@ dojo.declare(
// 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(item)){
+ 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
- dojo.forEach(this.getChildren(), function(child, idx){
+ array.forEach(this.getChildren(), function(child){
child._updateLayout();
});
}else{
@@ -23940,7 +30899,7 @@ dojo.declare(
}
}
- return new dojo.DeferredList(defs); // dojo.Deferred
+ return new DeferredList(defs); // dojo.Deferred
},
getTreePath: function(){
@@ -23955,7 +30914,7 @@ dojo.declare(
return path;
},
- getIdentity: function() {
+ getIdentity: function(){
return this.tree.model.getIdentity(this.item);
},
@@ -23968,7 +30927,7 @@ dojo.declare(
this.collapse();
}
- dojo.forEach(children, function(child){
+ array.forEach(children, function(child){
child._updateLayout();
});
},
@@ -23984,7 +30943,7 @@ dojo.declare(
this._setExpando(false);
},
- _onLabelFocus: function(evt){
+ _onLabelFocus: function(){
// summary:
// Called when this row is focused (possibly programatically)
// Note that we aren't using _onFocus() builtin to dijit
@@ -24001,8 +30960,8 @@ dojo.declare(
// 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).
- dijit.setWaiState(this.labelNode, "selected", selected);
- dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
+ this.labelNode.setAttribute("aria-selected", selected);
+ domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
},
setFocusable: function(/*Boolean*/ selected){
@@ -24045,13 +31004,20 @@ dojo.declare(
// tags:
// private
this.tree._onNodeMouseLeave(this, evt);
+ },
+
+ _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);
+ }
}
});
-dojo.declare(
- "dijit.Tree",
- [dijit._Widget, dijit._Templated],
-{
+var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
// summary:
// This widget displays hierarchical data from a store.
@@ -24091,7 +31057,7 @@ dojo.declare(
// Since setting the paths may be asynchronous (because ofwaiting 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: [],
@@ -24115,7 +31081,7 @@ dojo.declare(
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
- templateString: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"),
+ templateString: treeTemplate,
// persist: Boolean
// Enables/disables use of cookies for state saving.
@@ -24125,11 +31091,11 @@ dojo.declare(
// Fully expand the tree on load. Overrides `persist`.
autoExpand: false,
- // dndController: [protected] String
- // Class name to use as as the dnd controller. Specifying this class enables DnD.
- // Generally you should specify this as "dijit.tree.dndSource".
- // Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
- dndController: "dijit.tree._dndSelector",
+ // 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"],
@@ -24238,7 +31204,7 @@ dojo.declare(
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
- dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
+ topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
},
postMixInProperties: function(){
@@ -24252,11 +31218,11 @@ dojo.declare(
this._itemNodesMap={};
- if(!this.cookieName){
+ if(!this.cookieName && this.id){
this.cookieName = this.id + "SaveStateCookie";
}
- this._loadDeferred = new dojo.Deferred();
+ this._loadDeferred = new Deferred();
this.inherited(arguments);
},
@@ -24279,8 +31245,8 @@ dojo.declare(
this.inherited(arguments);
if(this.dndController){
- if(dojo.isString(this.dndController)){
- this.dndController = dojo.getObject(this.dndController);
+ if(lang.isString(this.dndController)){
+ this.dndController = lang.getObject(this.dndController);
}
var params={};
for(var i=0; i<this.dndParams.length;i++){
@@ -24296,7 +31262,7 @@ dojo.declare(
// summary:
// User specified a store&query rather than model, so create model from store/query
this._v10Compat = true;
- dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
+ kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
var modelParams = {
id: this.id + "_ForestStoreModel",
@@ -24307,15 +31273,15 @@ dojo.declare(
// Only override the model's mayHaveChildren() method if the user has specified an override
if(this.params.mayHaveChildren){
- modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
+ modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
}
if(this.params.getItemChildren){
- modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
+ modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
});
}
- this.model = new dijit.tree.ForestStoreModel(modelParams);
+ 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
@@ -24337,22 +31303,23 @@ dojo.declare(
// Initial load of the tree.
// Load root node (possibly hidden) and it's children.
this.model.getRoot(
- dojo.hitch(this, function(item){
+ 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
- dijit.setWaiRole(this.domNode, 'presentation');
-
- dijit.setWaiRole(rn.labelNode, 'presentation');
- dijit.setWaiRole(rn.containerNode, 'tree');
+ this.domNode.setAttribute("role", "presentation");
+
+ rn.labelNode.setAttribute("role", "presentation");
+ rn.containerNode.setAttribute("role", "tree");
}
this.domNode.appendChild(rn.domNode);
var identity = this.model.getIdentity(item);
@@ -24365,7 +31332,7 @@ dojo.declare(
rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
// load top level children and then fire onLoad() event
- this._expandNode(rn).addCallback(dojo.hitch(this, function(){
+ this._expandNode(rn).addCallback(lang.hitch(this, function(){
this._loadDeferred.callback(true);
this.onLoad();
}));
@@ -24376,34 +31343,34 @@ dojo.declare(
);
},
- getNodesByItem: function(/*dojo.data.Item or id*/ item){
+ 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 = dojo.isString(item) ? item : this.model.getIdentity(item);
+ 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(/*dojo.data.Item or id*/ item){
+ _setSelectedItemAttr: function(/*Item or id*/ item){
this.set('selectedItems', [item]);
},
- _setSelectedItemsAttr: function(/*dojo.data.Items or ids*/ items){
+ _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;
- this._loadDeferred.addCallback( dojo.hitch(this, function(){
- var identities = dojo.map(items, function(item){
- return (!item || dojo.isString(item)) ? item : tree.model.getIdentity(item);
+ this._loadDeferred.addCallback( lang.hitch(this, function(){
+ var identities = array.map(items, function(item){
+ return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
});
var nodes = [];
- dojo.forEach(identities, function(id){
+ array.forEach(identities, function(id){
nodes = nodes.concat(tree._itemNodesMap[id] || []);
});
this.set('selectedNodes', nodes);
@@ -24413,14 +31380,14 @@ dojo.declare(
_setPathAttr: function(/*Item[] || String[]*/ path){
// summary:
// Singular variant of _setPathsAttr
- if(path.length) {
+ if(path.length){
return this.set("paths", [path]);
- } else {
- //Empty list is interpreted as "select nothing"
+ }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.
@@ -24433,12 +31400,12 @@ dojo.declare(
// We may need to wait for some nodes to expand, so setting
// each path will involve a Deferred. We bring those deferreds
// together witha DeferredList.
- return new dojo.DeferredList(dojo.map(paths, function(path){
- var d = new dojo.Deferred();
-
+ return new DeferredList(array.map(paths, function(path){
+ var d = new Deferred();
+
// normalize path to use identity
- path = dojo.map(path, function(item){
- return dojo.isString(item) ? item : tree.model.getIdentity(item);
+ path = array.map(path, function(item){
+ return lang.isString(item) ? item : tree.model.getIdentity(item);
});
if(path.length){
@@ -24453,7 +31420,7 @@ dojo.declare(
function selectPath(path, nodes, def){
// Traverse path; the next path component should be among "nodes".
var nextPath = path.shift();
- var nextNode = dojo.filter(nodes, function(node){
+ var nextNode = array.filter(nodes, function(node){
return node.getIdentity() == nextPath;
})[0];
if(!!nextNode){
@@ -24463,16 +31430,16 @@ dojo.declare(
//Successfully reached the end of this path
def.callback(nextNode);
}
- } else {
+ }else{
def.errback("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", dojo.map(
- dojo.filter(newNodes,function(x){return x[0];}),
+ tree.set("selectedNodes", array.map(
+ array.filter(newNodes,function(x){return x[0];}),
function(x){return x[1];}));
}
},
@@ -24481,7 +31448,7 @@ dojo.declare(
this.set('selectedNodes', [node]);
},
_setSelectedNodesAttr: function(nodes){
- this._loadDeferred.addCallback( dojo.hitch(this, function(){
+ this._loadDeferred.addCallback( lang.hitch(this, function(){
this.dndController.setSelection(nodes);
}));
},
@@ -24490,7 +31457,7 @@ dojo.declare(
////////////// Data store related functions //////////////////////
// These just get passed to the model; they are here for back-compat
- mayHaveChildren: function(/*dojo.data.Item*/ item){
+ mayHaveChildren: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
@@ -24502,7 +31469,7 @@ dojo.declare(
// deprecated
},
- getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
+ getItemChildren: function(/*===== parentItem, onComplete =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
@@ -24530,48 +31497,62 @@ dojo.declare(
return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
},
- getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
+ 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(/*dojo.data.Item*/ item, /*Boolean*/ opened){
+ 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(/*dojo.data.Item*/ item, /*Boolean*/ opened){
+ getIconStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display icon
- // returns:
+ // item: dojo.data.Item
+ // opened: Boolean
+ // returns: Object
// Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
// tags:
// extension
},
- getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
+ 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(/*dojo.data.Item*/ item, /*Boolean*/ opened){
+ 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){
+ getTooltip: function(/*dojo.data.Item*/ /*===== item =====*/){
// summary:
// Overridable function to get the tooltip for a tree node (given the item)
// tags:
@@ -24585,8 +31566,7 @@ dojo.declare(
// summary:
// Translates keypress events into commands for the controller
if(e.altKey){ return; }
- var dk = dojo.keys;
- var treeNode = dijit.getEnclosingWidget(e.target);
+ var treeNode = registry.getEnclosingWidget(e.target);
if(!treeNode){ return; }
var key = e.charOrCode;
@@ -24594,7 +31574,7 @@ dojo.declare(
// Check for key navigation.
if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
- dojo.stopEvent(e);
+ event.stop(e);
}
}else{ // handle non-printables (arrow keys)
// clear record of recent printables (being saved for multi-char letter navigation),
@@ -24608,30 +31588,30 @@ dojo.declare(
if(!map){
// setup table mapping keys to events
map = {};
- map[dk.ENTER]="_onEnterKey";
+ map[keys.ENTER]="_onEnterKey";
//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.
- map[dk.SPACE]= map[" "] = "_onEnterKey";
- map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
- map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
- map[dk.UP_ARROW]="_onUpArrow";
- map[dk.DOWN_ARROW]="_onDownArrow";
- map[dk.HOME]="_onHomeKey";
- map[dk.END]="_onEndKey";
+ 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]){
this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
- dojo.stopEvent(e);
+ event.stop(e);
}
}
},
_onEnterKey: function(/*Object*/ message){
this._publish("execute", { item: message.item, node: message.node } );
- this.dndController.userSelect(message.node, dojo.isCopyKey( message.evt ), message.evt.shiftKey);
+ this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
this.onClick(message.item, message.node, message.evt);
},
@@ -24716,7 +31696,7 @@ dojo.declare(
}
},
- _onEndKey: function(/*Object*/ message){
+ _onEndKey: function(){
// summary:
// End key pressed; go to last visible node.
@@ -24787,7 +31767,7 @@ dojo.declare(
isExpandoNode: function(node, widget){
// summary:
// check whether a dom node is the expandoNode for a particular TreeNode widget
- return dojo.isDescendant(node, widget.expandoNode);
+ return dom.isDescendant(node, widget.expandoNode);
},
_onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
@@ -24806,7 +31786,7 @@ dojo.declare(
this.onClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
- dojo.stopEvent(e);
+ event.stop(e);
},
_onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
@@ -24825,7 +31805,7 @@ dojo.declare(
this.onDblClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
- dojo.stopEvent(e);
+ event.stop(e);
},
_onExpandoClick: function(/*Object*/ message){
@@ -24845,27 +31825,37 @@ dojo.declare(
}
},
- onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
+ onClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is clicked
+ // item: dojo.data.Item
+ // node: TreeNode
+ // evt: Event
// tags:
// callback
},
- onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
+ onDblClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is double-clicked
+ // item: dojo.data.Item
+ // node: TreeNode
+ // evt: Event
// tags:
// callback
},
- onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
+ onOpen: function(/*===== item, node =====*/){
// summary:
// Callback when a node is opened
+ // item: dojo.data.Item
+ // node: TreeNode
// tags:
// callback
},
- onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
+ onClose: function(/*===== item, node =====*/){
// summary:
// Callback when a node is closed
+ // item: dojo.data.Item
+ // node: TreeNode
// tags:
// callback
},
@@ -24913,10 +31903,7 @@ dojo.declare(
node.collapse();
this.onClose(node.item, node);
- if(node.item){
- this._state(node.item,false);
- this._saveState();
- }
+ this._state(node, false);
}
},
@@ -24945,7 +31932,7 @@ dojo.declare(
// 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.
- var def = (node._expandNodeDeferred = new dojo.Deferred());
+ var def = (node._expandNodeDeferred = new Deferred());
// Get the children
model.getChildren(
@@ -24982,10 +31969,7 @@ dojo.declare(
this.onOpen(node.item, node);
- if(item){
- this._state(item, true);
- this._saveState();
- }
+ this._state(node, true);
}
return def; // dojo.Deferred
@@ -25000,7 +31984,7 @@ dojo.declare(
// protected
// set focus so that the label will be voiced using screen readers
- dijit.focus(node.labelNode);
+ focus.focus(node.labelNode);
},
_onNodeFocus: function(/*dijit._Widget*/ node){
@@ -25023,13 +32007,13 @@ dojo.declare(
}
},
- _onNodeMouseEnter: function(/*dijit._Widget*/ node){
+ _onNodeMouseEnter: function(/*dijit._Widget*/ /*===== node =====*/){
// summary:
// Called when mouse is over a node (onmouseenter event),
// this is monitored by the DND code
},
- _onNodeMouseLeave: function(/*dijit._Widget*/ node){
+ _onNodeMouseLeave: function(/*dijit._Widget*/ /*===== node =====*/){
// summary:
// Called when mouse leaves a node (onmouseleave event),
// this is monitored by the DND code
@@ -25047,7 +32031,7 @@ dojo.declare(
if(nodes){
var label = this.getLabel(item),
tooltip = this.getTooltip(item);
- dojo.forEach(nodes, function(node){
+ array.forEach(nodes, function(node){
node.set({
item: item, // theoretically could be new JS Object representing same item
label: label,
@@ -25066,7 +32050,7 @@ dojo.declare(
parentNodes = this._itemNodesMap[identity];
if(parentNodes){
- dojo.forEach(parentNodes,function(parentNode){
+ array.forEach(parentNodes,function(parentNode){
parentNode.setChildItems(newChildrenList);
});
}
@@ -25080,7 +32064,7 @@ dojo.declare(
nodes = this._itemNodesMap[identity];
if(nodes){
- dojo.forEach(nodes,function(node){
+ array.forEach(nodes,function(node){
// Remove node from set of selected nodes (if it's selected)
this.dndController.removeTreeNode(node);
@@ -25100,43 +32084,39 @@ dojo.declare(
_initState: function(){
// summary:
// Load in which nodes should be opened automatically
- if(this.persist){
- var cookie = dojo.cookie(this.cookieName);
- this._openedItemIds = {};
- if(cookie){
- dojo.forEach(cookie.split(','), function(item){
- this._openedItemIds[item] = true;
+ 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(item,expanded){
+ _state: function(node, expanded){
// summary:
- // Query or set expanded state for an item,
+ // Query or set expanded state for an node
if(!this.persist){
return false;
}
- var id=this.model.getIdentity(item);
+ var path = array.map(node.getTreePath(), function(item){
+ return this.model.getIdentity(item);
+ }, this).join("/");
if(arguments.length === 1){
- return this._openedItemIds[id];
- }
- if(expanded){
- this._openedItemIds[id] = true;
+ return this._openedNodes[path];
}else{
- delete this._openedItemIds[id];
- }
- },
- _saveState: function(){
- // summary:
- // Create and save a cookie with the currently expanded nodes identifiers
- if(!this.persist){
- return;
- }
- var ary = [];
- for(var id in this._openedItemIds){
- ary.push(id);
+ if(expanded){
+ this._openedNodes[path] = true;
+ }else{
+ delete this._openedNodes[path];
+ }
+ var ary = [];
+ for(var id in this._openedNodes){
+ ary.push(id);
+ }
+ cookie(this.cookieName, ary.join(","), {expires:365});
}
- dojo.cookie(this.cookieName, ary.join(","), {expires:365});
},
destroy: function(){
@@ -25147,7 +32127,7 @@ dojo.declare(
if(this.rootNode){
this.rootNode.destroyRecursive();
}
- if(this.dndController && !dojo.isString(this.dndController)){
+ if(this.dndController && !lang.isString(this.dndController)){
this.dndController.destroy();
}
this.rootNode = null;
@@ -25162,13 +32142,13 @@ dojo.declare(
resize: function(changeSize){
if(changeSize){
- dojo.marginBox(this.domNode, changeSize);
+ domGeometry.setMarginBox(this.domNode, changeSize);
}
// The only 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)
- this._nodePixelIndent = dojo._getMarginSize(this.tree.indentDetector).w;
+ this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w;
if(this.tree.rootNode){
// If tree has already loaded, then reset indent for all the nodes
@@ -25184,2631 +32164,84 @@ dojo.declare(
// 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 dijit._TreeNode(args);
- }
-});
-
-// For back-compat. TODO: remove in 2.0
-
-}
-
-if(!dojo._hasResource["dojo.dnd.Avatar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.Avatar"] = true;
-dojo.provide("dojo.dnd.Avatar");
-
-
-
-dojo.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();
+ return new TreeNode(args);
},
- // methods
- construct: function(){
- // summary:
- // constructor function;
- // it is separate so it can be (dynamically) overwritten in case of need
- this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
- var a = dojo.create("table", {
- "class": "dojoDndAvatar",
- style: {
- position: "absolute",
- zIndex: "1999",
- margin: "0px"
- }
- }),
- source = this.manager.source, node,
- b = dojo.create("tbody", null, a),
- tr = dojo.create("tr", null, b),
- td = dojo.create("td", null, tr),
- icon = this.isA11y ? dojo.create("span", {
- id : "a11yIcon",
- innerHTML : this.manager.copy ? '+' : "<"
- }, td) : null,
- span = dojo.create("span", {
- innerHTML: source.generateText ? this._generateText() : ""
- }, td),
- k = Math.min(5, this.manager.nodes.length), i = 0;
- // we have to set the opacity on IE only after the node is live
- dojo.attr(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 = dojo.create("table"),
- tbody = dojo.create("tbody", null, table);
- tbody.appendChild(node);
- node = table;
- }
- }
- node.id = "";
- tr = dojo.create("tr", null, b);
- td = dojo.create("td", null, tr);
- td.appendChild(node);
- dojo.attr(tr, {
- "class": "dojoDndAvatarItem",
- style: {opacity: (9 - i) / 10}
- });
+ _setTextDirAttr: function(textDir){
+ if(textDir && this.textDir!= textDir){
+ this._set("textDir",textDir);
+ this.rootNode.set("textDir", textDir);
}
- this.node = a;
- },
- destroy: function(){
- // summary:
- // destructor for the avatar; called to remove all references so it can be garbage-collected
- dojo.destroy(this.node);
- this.node = false;
- },
- update: function(){
- // summary:
- // updates the avatar to reflect the current DnD state
- dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
- if (this.isA11y){
- var icon = dojo.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
- dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
- function(node){
- node.innerHTML = this._generateText();
- }, this);
- },
- _generateText: function(){
- // summary: generates a proper text to reflect copying or moving of items
- return this.manager.nodes.length.toString();
}
});
-}
-
-if(!dojo._hasResource["dojo.dnd.Manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.dnd.Manager"] = true;
-dojo.provide("dojo.dnd.Manager");
-
-
-
-
+Tree._TreeNode = TreeNode; // for monkey patching
-dojo.declare("dojo.dnd.Manager", null, {
- // 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: 16,
- OFFSET_Y: 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();
- }
- dojo.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();
- dojo.publish("/dnd/source/over", [null]);
- }
- }else{
- dojo.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
- this.source = source;
- this.nodes = nodes;
- this.copy = Boolean(copy); // normalizing to true boolean
- this.avatar = this.makeAvatar();
- dojo.body().appendChild(this.avatar.node);
- dojo.publish("/dnd/start", [source, nodes, this.copy]);
- this.events = [
- dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
- dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"),
- dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"),
- dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"),
- // cancel text selection and text dragging
- dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent),
- dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
- ];
- var c = "dojoDnd" + (copy ? "Copy" : "Move");
- dojo.addClass(dojo.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
- dojo.removeClass(dojo.body(), ["dojoDndCopy", "dojoDndMove"]);
- dojo.forEach(this.events, dojo.disconnect);
- 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 dojo.dnd.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){
- dojo.dnd.autoScrollNodes(e);
- //dojo.dnd.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(dojo.isCopyKey(e)));
- if(this.copy != copy){
- this._setCopyStatus(copy);
- }
- }
- },
- 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(dojo.isCopyKey(e))),
- params = [this.source, this.nodes, copy, this.target, e];
- dojo.publish("/dnd/drop/before", params);
- dojo.publish("/dnd/drop", params);
- }else{
- dojo.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 dojo.keys.CTRL:
- var copy = Boolean(this.source.copyState(true));
- if(this.copy != copy){
- this._setCopyStatus(copy);
- }
- break;
- case dojo.keys.ESCAPE:
- dojo.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 == dojo.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();
- dojo.replaceClass(dojo.body(),
- "dojoDnd" + (this.copy ? "Copy" : "Move"),
- "dojoDnd" + (this.copy ? "Move" : "Copy"));
- }
+return Tree;
});
-// dojo.dnd._manager:
-// The manager singleton variable. Can be overwritten if needed.
-dojo.dnd._manager = null;
-
-dojo.dnd.manager = function(){
- // summary:
- // Returns the current DnD manager. Creates one if it is not created yet.
- if(!dojo.dnd._manager){
- dojo.dnd._manager = new dojo.dnd.Manager();
- }
- return dojo.dnd._manager; // Object
-};
-
-}
-
-if(!dojo._hasResource["dijit.tree.dndSource"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dijit.tree.dndSource"] = true;
-dojo.provide("dijit.tree.dndSource");
-
-
-
+},
+'dijit/form/_FormValueWidget':function(){
+define("dijit/form/_FormValueWidget", [
+ "dojo/_base/declare", // declare
+ "dojo/_base/sniff", // has("ie")
+ "./_FormWidget",
+ "./_FormValueMixin"
+], function(declare, has, _FormWidget, _FormValueMixin){
/*=====
-dijit.tree.__SourceArgs = function(){
- // 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
- this.isSource = isSource;
- this.accept = accept;
- this.autoSync = autoSync;
- this.copyOnly = copyOnly;
- this.dragThreshold = dragThreshold;
- this.betweenThreshold = betweenThreshold;
-}
+var _FormWidget = dijit.form._FormWidget;
+var _FormValueMixin = dijit.form._FormValueMixin;
=====*/
-dojo.declare("dijit.tree.dndSource", dijit.tree._dndSelector, {
- // summary:
- // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
-
- // isSource: [private] 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,
-
- constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
- // summary:
- // a constructor of the Tree DnD Source
- // tags:
- // private
- if(!params){ params = {}; }
- dojo.mixin(this, params);
- this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
- 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){
- dojo.addClass(this.node, "dojoDndSource");
- }
- this.targetState = "";
- if(this.accept){
- dojo.addClass(this.node, "dojoDndTarget");
- }
-
- // set up events
- this.topics = [
- dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
- dojo.subscribe("/dnd/start", this, "onDndStart"),
- dojo.subscribe("/dnd/drop", this, "onDndDrop"),
- dojo.subscribe("/dnd/cancel", 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("destroy",arguments);
- dojo.forEach(this.topics, dojo.unsubscribe);
- this.targetAnchor = null;
- },
-
- _onDragMouse: function(e){
- // summary:
- // Helper method for processing onmousemove/onmouseover events while drag is in progress.
- // Keeps track of current drop target.
-
- var m = dojo.dnd.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 = dojo.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(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 if(m.source == this && (newTarget.id in this.selection)){
- // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
- 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 events over the Tree
- // e: Event
- // onmousemouse event
- // tags:
- // private
- if(this.isDragging && this.targetState == "Disabled"){ return; }
- this.inherited(arguments);
- var m = dojo.dnd.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 = dojo.map(nodes, function(n){return n.domNode});
- m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
- }
- }
- }
- },
-
- onMouseDown: function(e){
- // summary:
- // Event processor for onmousedown
- // e: Event
- // onmousedown 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
- // e: Event
- // onmouseup 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 = dojo.dnd.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){
- dojo.dnd.manager().overSource(this);
- }
-
- this.isDragging = true;
- },
-
- itemCreator: function(/*DomNode[]*/ nodes, target, /*dojo.dnd.Source*/ 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.
- // 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
-
- // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
- // make signature itemCreator(sourceItem, node, target) (or similar).
-
- return dojo.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,
- requeryRoot = false; // set to true iff top level items change
-
- this.isDragging = false;
-
- // Compute the new parent item
- var targetWidget = target;
- var newParentItem;
- var insertIndex;
- newParentItem = (targetWidget && targetWidget.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 = (targetWidget.getParent() && targetWidget.getParent().item) || tree.item;
- // Compute the insert index for reordering
- insertIndex = targetWidget.getIndexInParent();
- if(this.dropPosition == "After"){
- insertIndex = targetWidget.getIndexInParent() + 1;
- }
- }else{
- newParentItem = (targetWidget && targetWidget.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;
-
- dojo.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(dojo.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);
- }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);
- }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);
- }
- }, 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(targetWidget);
- }
- 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);
- dojo.dnd.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 = dojo.dnd.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;
+// module:
+// dijit/form/_FormValueWidget
+// summary:
+// FormValueWidget
- // 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];
- },
+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.
- _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;
- },
+ // 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.
- _markDndStatus: function(copy){
+ _layoutHackIE7: function(){
// summary:
- // Changes source's state based on "copy" status
- this._changeState("Source", copy ? "Copied" : "Moved");
- }
-});
-
-}
-
-if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
-dojo.provide("dojo.data.ItemFileReadStore");
-
-
-
-
-
-dojo.declare("dojo.data.ItemFileReadStore", null,{
- // 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 'identifer' 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}
- // keywordParameters: {data: jsonObject}
- // keywordParameters: {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 dojo.date.stamp.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.
-
- //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,
-
- //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,
-
- //Parameter for specifying that it is OK for the xhrGet call to fail silently.
- failOk: false,
-
- //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(/* 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("dojo.data.ItemFileReadStore: 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("dojo.data.ItemFileReadStore: Invalid attribute argument.");
- }
- },
-
- getValue: function( /* 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(/* 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(/* 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( /* 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(/* item */ item,
- /* attribute-name-string */ attribute,
- /* anything */ value){
- // summary:
- // See dojo.data.api.Read.containsValue()
- var regexp = undefined;
- if(typeof value === "string"){
- regexp = dojo.data.util.filter.patternToRegExp(value, false);
- }
- return this._containsValue(item, attribute, value, regexp); //boolean.
- },
-
- _containsValue: function( /* 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 dojo.some(this.getValues(item, attribute), function(possibleValue){
- if(possibleValue !== null && !dojo.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(/* 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(/* item */ item){
- // summary:
- // See dojo.data.api.Read.getLabelAttributes()
- if(this._labelAttr){
- return [this._labelAttr]; //array
- }
- return null; //null
- },
-
- _fetchItems: function( /* Object */ keywordArgs,
- /* Function */ findCallback,
- /* Function */ errorCallback){
- // summary:
- // See dojo.data.util.simpleFetch.fetch()
- var self = this,
- filter = function(requestArgs, arrayOfItems){
- 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] = dojo.data.util.filter.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(!self._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);
- }
- };
-
- if(this._loadFinished){
- filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
- }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){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "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: filter});
- }else{
- this._loadInProgress = true;
- var getArgs = {
- url: self._jsonFileUrl,
- handleAs: "json-comment-optional",
- preventCache: this.urlPreventCache,
- failOk: this.failOk
- };
- var getHandler = dojo.xhrGet(getArgs);
- getHandler.addCallback(function(data){
- try{
- self._getItemsFromLoadedData(data);
- self._loadFinished = true;
- self._loadInProgress = false;
-
- filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
- 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;
- filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
- }catch(e){
- errorCallback(e, keywordArgs);
- }
- }else{
- errorCallback(new Error("dojo.data.ItemFileReadStore: 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;
- if(delayedFilter){
- delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
- }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 || keywordArgs || null */ 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("dojo.data.ItemFileReadStore: 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});
- var isItem = (
- (aValue !== null) &&
- (typeof aValue === "object") &&
- (!dojo.isArray(aValue) || addingArrays) &&
- (!dojo.isFunction(aValue)) &&
- (aValue.constructor == Object || dojo.isArray(aValue)) &&
- (typeof aValue._reference === "undefined") &&
- (typeof aValue._type === "undefined") &&
- (typeof aValue._value === "undefined") &&
- self.hierarchical
- );
- return isItem;
- }
-
- function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
- self._arrayOfAllItems.push(anItem);
- for(var attribute in anItem){
- var valueForAttribute = anItem[attribute];
- if(valueForAttribute){
- if(dojo.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(dojo.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(!dojo.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("dojo.data.ItemFileReadStore: 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("dojo.data.ItemFileReadStore: 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(dojo.isFunction(mappingObj)){
- arrayOfValues[j] = new mappingObj(value._value);
- }else if(dojo.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(!dojo.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(/* 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){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "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){
+ // Work around table sizing bugs on IE7 by forcing redraw
- 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 = dojo.xhrGet(getArgs);
- getHandler.addCallback(function(data){
- var scope = keywordArgs.scope?keywordArgs.scope:dojo.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:dojo.global;
- keywordArgs.onError.call(scope, error);
+ 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
+ setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
}
- });
- }
-
- }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:dojo.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:dojo.global;
- keywordArgs.onItem.call(scope, item);
+ );
+ })();
+ parent = parent.parentNode;
}
}
- },
-
- _getItemByIdentity: function(/* Object */ identity){
- // summary:
- // Internal function to look an item up by its identity map.
- var item = null;
- if(this._itemsByIdentity &&
- 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(/* 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){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "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 = dojo.xhrGet(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("dojo.data.ItemFileReadStore: 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.
-dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
-
-}
-
-if(!dojo._hasResource["dojo.data.ItemFileWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.ItemFileWriteStore"] = true;
-dojo.provide("dojo.data.ItemFileWriteStore");
-
-
-
-dojo.declare("dojo.data.ItemFileWriteStore", dojo.data.ItemFileReadStore, {
- constructor: function(/* object */ keywordParameters){
- // keywordParameters: {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. 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 dojo.date.stamp.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(){
- var identifierAttribute = this.getFeatures()['dojo.data.api.Identity'];
- // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
- return identifierAttribute;
- },
-
-
-/* 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(dojo.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(!dojo.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 = dojo.indexOf(array, element);
- if(index != -1){
- array.splice(index, 1);
- return true;
- }
- return false;
- },
-
- deleteItem: function(/* 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] = dojo.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.
- dojo.forEach(attributes, function(attribute){
- dojo.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 = dojo.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(/* 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(/* 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(/* item */ item, /* attribute-name-string */ attribute){
- // summary: See dojo.data.api.Write.unsetAttribute()
- return this._setValueOrValues(item, attribute, [], true);
- },
-
- _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
- this._assert(!this._saveInProgress);
-
- // Check for valid arguments
- this._assertIsItem(item);
- this._assert(dojo.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] = dojo.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(dojo.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(!dojo.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(dojo.isArray(newValueOrValues)){
- var newValues = newValueOrValues;
- // Unfortunately, it's not safe to just do this:
- // newValueArray = newValues;
- // 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(!dojo.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 = {};
- dojo.forEach(oldValues, function(possibleItem){
- if(this.isItem(possibleItem)){
- var id = this.getIdentity(possibleItem);
- map[id.toString()] = true;
- }
- }, this);
- dojo.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(/*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.
-
- 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(/* item */ refItem, /* item */ parentItem, /*strin*/ 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: " + dojo.toJson(item[this._reverseRefMap]));
- }
- }
- },
-
- _getValueOrValues: function(/* 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)){
- var item = 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}
- var identity = this.getIdentity(item);
- var referenceObject = {_reference: identity};
- return referenceObject;
- }else{
- if(typeof value === "object"){
- for(var type in this._datatypeMap){
- var typeMap = this._datatypeMap[type];
- if(dojo.isObject(typeMap) && !dojo.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 attribute = key;
- var valueArray = this.getValues(item, attribute);
- if(valueArray.length == 1){
- serializableItem[attribute] = this._flatten(valueArray[0]);
- }else{
- var serializableArray = [];
- for(var j = 0; j < valueArray.length; ++j){
- serializableArray.push(this._flatten(valueArray[j]));
- serializableItem[attribute] = serializableArray;
- }
- }
- }
- }
- serializableStructure.items.push(serializableItem);
- }
- }
- var prettyPrint = true;
- return dojo.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(dojo.isObject(something)){
- var i;
- for(i in something){
- empty = false;
- break;
- }
- }else if(dojo.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 || dojo.global;
- keywordArgs.onComplete.call(scope);
- }
- };
- var saveFailedCallback = function(err){
- self._saveInProgress = false;
- if(keywordArgs && keywordArgs.onError){
- var scope = keywordArgs.scope || dojo.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(key in modifiedItem){
- delete modifiedItem[key];
- }
- dojo.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]){
- dojo.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
- if(!this._isEmpty(this._pending._newItems) ||
- !this._isEmpty(this._pending._modifiedItems) ||
- !this._isEmpty(this._pending._deletedItems)){
- return true;
- }
- return false; // boolean
- }
- },
-
-/* dojo.data.api.Notification */
-
- onSet: function(/* 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(/* 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(/* 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.");
- }
- }
- }
});
-}
-
-
-dojo.i18n._preloadLocalizations("dojo.nls.tt-rss-layer", ["ROOT","ar","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hu","it","it-it","ja","ja-jp","ko","ko-kr","nb","nl","nl-nl","pl","pt","pt-br","pt-pt","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]);
+},
+'*now':function(r){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de-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);