text: define("dijit/form/_FormSelectWidget", [
  	"dojo/_base/array", // array.filter array.forEach array.map array.some
  	"dojo/_base/Deferred",
  	"dojo/aspect", // aspect.after
  	"dojo/data/util/sorter", // util.sorter.createSortFunction
  	"dojo/_base/declare", // declare
  	"dojo/dom", // dom.setSelectable
  	"dojo/dom-class", // domClass.toggle
  	"dojo/_base/kernel",	// _scopeName
  	"dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
  	"dojo/query", // query
  	"dojo/when",
  	"dojo/store/util/QueryResults",
  	"./_FormValueWidget"
  ], function(array, Deferred, aspect, sorter, declare, dom, domClass, kernel, lang, query, when,
  			QueryResults, _FormValueWidget){
  
  	// module:
  	//		dijit/form/_FormSelectWidget
  
  	/*=====
  	var __SelectOption = {
  		// value: String
  		//		The value of the option.  Setting to empty (or missing) will
  		//		place a separator at that location
  		// label: String
  		//		The label for our option.  It can contain html tags.
  		// selected: Boolean
  		//		Whether or not we are a selected option
  		// disabled: Boolean
  		//		Whether or not this specific option is disabled
  	};
  	=====*/
  
  	var _FormSelectWidget = declare("dijit.form._FormSelectWidget", _FormValueWidget, {
  		// summary:
  		//		Extends _FormValueWidget in order to provide "select-specific"
  		//		values - i.e., those values that are unique to `<select>` elements.
  		//		This also provides the mechanism for reading the elements from
  		//		a store, if desired.
  
  		// multiple: [const] Boolean
  		//		Whether or not we are multi-valued
  		multiple: false,
  
  		// options: __SelectOption[]
  		//		The set of options for our select item.  Roughly corresponds to
  		//		the html `<option>` tag.
  		options: null,
  
  		// store: dojo/store/api/Store
  		//		A store to use for getting our list of options - rather than reading them
  		//		from the `<option>` html tags.   Should support getIdentity().
  		//		For back-compat store can also be a dojo/data/api/Identity.
  		store: null,
  
  		// query: object
  		//		A query to use when fetching items from our store
  		query: null,
  
  		// queryOptions: object
  		//		Query options to use when fetching from the store
  		queryOptions: null,
  
  		// labelAttr: String?
  		//		The entries in the drop down list come from this attribute in the dojo.store items.
  		//		If ``store`` is set, labelAttr must be set too, unless store is an old-style
  		//		dojo.data store rather than a new dojo/store.
  		labelAttr: "",
  
  		// onFetch: Function
  		//		A callback to do with an onFetch - but before any items are actually
  		//		iterated over (i.e. to filter even further what you want to add)
  		onFetch: null,
  
  		// sortByLabel: Boolean
  		//		Flag to sort the options returned from a store by the label of
  		//		the store.
  		sortByLabel: true,
  
  
  		// loadChildrenOnOpen: Boolean
  		//		By default loadChildren is called when the items are fetched from the
  		//		store.  This property allows delaying loadChildren (and the creation
  		//		of the options/menuitems) until the user clicks the button to open the
  		//		dropdown.
  		loadChildrenOnOpen: false,
  
  		// onLoadDeferred: [readonly] dojo.Deferred
  		//		This is the `dojo.Deferred` returned by setStore().
  		//		Calling onLoadDeferred.then() registers your
  		//		callback to be called only once, when the prior setStore completes.
  		onLoadDeferred: null,
  
  		getOptions: function(/*anything*/ valueOrIdx){
  			// summary:
  			//		Returns a given option (or options).
  			// valueOrIdx:
  			//		If passed in as a string, that string is used to look up the option
  			//		in the array of options - based on the value property.
  			//		(See dijit/form/_FormSelectWidget.__SelectOption).
  			//
  			//		If passed in a number, then the option with the given index (0-based)
  			//		within this select will be returned.
  			//
  			//		If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
  			//		returned if and only if it exists within this select.
  			//
  			//		If passed an array, then an array will be returned with each element
  			//		in the array being looked up.
  			//
  			//		If not passed a value, then all options will be returned
  			//
  			// returns:
  			//		The option corresponding with the given value or index.
  			//		null is returned if any of the following are true:
  			//
  			//		- A string value is passed in which doesn't exist
  			//		- An index is passed in which is outside the bounds of the array of options
  			//		- A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
  
  			// NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
  			//		if the value property matches - NOT if the exact option exists
  			// NOTE: if passing in an array, null elements will be placed in the returned
  			//		array when a value is not found.
  			var opts = this.options || [];
  
  			if(valueOrIdx == null){
  				return opts; // __SelectOption[]
  			}
  			if(lang.isArray(valueOrIdx)){
  				return array.map(valueOrIdx, "return this.getOptions(item);", this); // __SelectOption[]
  			}
  			if(lang.isString(valueOrIdx)){
  				valueOrIdx = { value: 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(!array.some(opts, function(option, idx){
  					for(var a in valueOrIdx){
  						if(!(a in option) || option[a] != valueOrIdx[a]){ // == and not === so that 100 matches '100'
  							return false;
  						}
  					}
  					valueOrIdx = idx;
  					return true; // stops iteration through opts
  				})){
  					valueOrIdx = -1;
  				}
  			}
  			if(valueOrIdx >= 0 && valueOrIdx < opts.length){
  				return opts[valueOrIdx]; // __SelectOption
  			}
  			return null; // null
  		},
  
  		addOption: function(/*__SelectOption|__SelectOption[]*/ option){
  			// summary:
  			//		Adds an option or options to the end of the select.  If value
  			//		of the option is empty or missing, a separator is created instead.
  			//		Passing in an array of options will yield slightly better performance
  			//		since the children are only loaded once.
  			array.forEach(lang.isArray(option) ? option : [option], function(i){
  				if(i && lang.isObject(i)){
  					this.options.push(i);
  				}
  			}, this);
  			this._loadChildren();
  		},
  
  		removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx){
  			// summary:
  			//		Removes the given option or options.  You can remove by string
  			//		(in which case the value is removed), number (in which case the
  			//		index in the options array is removed), or select option (in
  			//		which case, the select option with a matching value is removed).
  			//		You can also pass in an array of those values for a slightly
  			//		better performance since the children are only loaded once.
  			//		For numeric option values, specify {value: number} as the argument.
  			var oldOpts = this.getOptions(lang.isArray(valueOrIdx) ? valueOrIdx : [valueOrIdx]);
  			array.forEach(oldOpts, function(option){
  				// 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(option){
  					this.options = array.filter(this.options, function(node){
  						return (node.value !== option.value || node.label !== option.label);
  					});
  					this._removeOptionItem(option);
  				}
  			}, this);
  			this._loadChildren();
  		},
  
  		updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption){
  			// summary:
  			//		Updates the values of the given option.  The option to update
  			//		is matched based on the value of the entered option.  Passing
  			//		in an array of new options will yield better performance since
  			//		the children will only be loaded once.
  			array.forEach(lang.isArray(newOption) ? newOption : [newOption], function(i){
  				var oldOpt = this.getOptions({ value: i.value }), k;
  				if(oldOpt){
  					for(k in i){
  						oldOpt[k] = i[k];
  					}
  				}
  			}, this);
  			this._loadChildren();
  		},
  
  		setStore: function(store, selectedValue, fetchArgs){
  			// summary:
  			//		Sets the store you would like to use with this select widget.
  			//		The selected value is the value of the new store to set.  This
  			//		function returns the original store, in case you want to reuse
  			//		it or something.
  			// store: dojo/store/api/Store
  			//		The dojo.store you would like to use - it MUST implement getIdentity()
  			//		and MAY implement observe().
  			//		For backwards-compatibility this can also be a data.data store, in which case
  			//		it MUST implement dojo/data/api/Identity,
  			//		and MAY implement dojo/data/api/Notification.
  			// selectedValue: anything?
  			//		The value that this widget should set itself to *after* the store
  			//		has been loaded
  			// fetchArgs: Object?
  			//		Hash of parameters to set filter on store, etc.
  			//
  			//		- query: new value for Select.query,
  			//		- queryOptions: new value for Select.queryOptions,
  			//		- onFetch: callback function for each item in data (Deprecated)
  			var oStore = this.store;
  			fetchArgs = fetchArgs || {};
  
  			if(oStore !== store){
  				// Our store has changed, so cancel any listeners on old store (remove for 2.0)
  				var h;
  				while((h = this._notifyConnections.pop())){
  					h.remove();
  				}
  
  				// For backwards-compatibility, accept dojo.data store in addition to dojo.store.store.  Remove in 2.0.
  				if(!store.get){
  					lang.mixin(store, {
  						_oldAPI: true,
  						get: function(id){
  							// summary:
  							//		Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
  							//		Like dojo.store.DataStore.get() except returns native item.
  							var deferred = new Deferred();
  							this.fetchItemByIdentity({
  								identity: id,
  								onItem: function(object){
  									deferred.resolve(object);
  								},
  								onError: function(error){
  									deferred.reject(error);
  								}
  							});
  							return deferred.promise;
  						},
  						query: function(query, options){
  							// summary:
  							//		Queries the store for objects.   Like dojo/store/DataStore.query()
  							//		except returned Deferred contains array of native items.
  							var deferred = new Deferred(function(){
  								if(fetchHandle.abort){
  									fetchHandle.abort();
  								}
  							});
  							deferred.total = new Deferred();
  							var fetchHandle = this.fetch(lang.mixin({
  								query: query,
  								onBegin: function(count){
  									deferred.total.resolve(count);
  								},
  								onComplete: function(results){
  									deferred.resolve(results);
  								},
  								onError: function(error){
  									deferred.reject(error);
  								}
  							}, options));
  							return new QueryResults(deferred);
  						}
  					});
  
  					if(store.getFeatures()["dojo.data.api.Notification"]){
  						this._notifyConnections = [
  							aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
  							aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
  							aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
  						];
  					}
  				}
  				this._set("store", store);			// Our store has changed, so update our notifications
  			}
  
  			// Remove existing options (if there are any)
  			if(this.options && this.options.length){
  				this.removeOption(this.options);
  			}
  
  			// Cancel listener for updates to old store
  			if(this._queryRes && this._queryRes.close){
  				this._queryRes.close();
  			}
  
  			// If user has specified new query and query options along with this new store, then use them.
  			if(fetchArgs.query){
  				this._set("query", fetchArgs.query);
  				this._set("queryOptions", fetchArgs.queryOptions);
  			}
  
  			// Add our new options
  			if(store){
  				this._loadingStore = true;
  				this.onLoadDeferred = new Deferred();
  
  				// Run query
  				// Save result in this._queryRes so we can cancel the listeners we register below
  				this._queryRes = store.query(this.query, this.queryOptions);
  				when(this._queryRes, lang.hitch(this, function(items){
  
  					if(this.sortByLabel && !fetchArgs.sort && items.length){
  						if(store.getValue){
  							// Old dojo.data API to access items, remove for 2.0
  							items.sort(sorter.createSortFunction([
  								{
  									attribute: store.getLabelAttributes(items[0])[0]
  								}
  							], store));
  						}else{
  							// TODO: remove sortByLabel completely for 2.0?  It can be handled by queryOptions: {sort: ... }.
  							var labelAttr = this.labelAttr;
  							items.sort(function(a, b){
  								return a[labelAttr] > b[labelAttr] ? 1 : b[labelAttr] > a[labelAttr] ? -1 : 0;
  							});
  						}
  					}
  
  					if(fetchArgs.onFetch){
  						items = fetchArgs.onFetch.call(this, items, fetchArgs);
  					}
  
  					// TODO: Add these guys as a batch, instead of separately
  					array.forEach(items, function(i){
  						this._addOptionForItem(i);
  					}, this);
  
  					// Register listener for store updates
  					if(this._queryRes.observe){
  						this._queryRes.observe(lang.hitch(this, function(object, deletedFrom, insertedInto){
  							if(deletedFrom == insertedInto){
  								this._onSetItem(object);
  							}else{
  								if(deletedFrom != -1){
  									this._onDeleteItem(object);
  								}
  								if(insertedInto != -1){
  									this._onNewItem(object);
  								}
  							}
  						}), true);
  					}
  
  					// Set our value (which might be undefined), and then tweak
  					// it to send a change event with the real value
  					this._loadingStore = false;
  					this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
  					delete this._pendingValue;
  
  					if(!this.loadChildrenOnOpen){
  						this._loadChildren();
  					}else{
  						this._pseudoLoadChildren(items);
  					}
  					this.onLoadDeferred.resolve(true);
  					this.onSetStore();
  				}), function(err){
  					console.error('dijit.form.Select: ' + err.toString());
  					this.onLoadDeferred.reject(err);
  				});
  			}
  			return oStore;	// dojo/data/api/Identity
  		},
  
  		// TODO: implement set() and watch() for store and query, although not sure how to handle
  		// setting them individually rather than together (as in setStore() above)
  
  		_setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  			// summary:
  			//		set the value of the widget.
  			//		If a string is passed, then we set our value from looking it up.
  			if(!this._onChangeActive){
  				priorityChange = null;
  			}
  			if(this._loadingStore){
  				// Our store is loading - so save our value, and we'll set it when
  				// we're done
  				this._pendingValue = newValue;
  				return;
  			}
  			if(newValue == null){
  				return;
  			}
  			if(lang.isArray(newValue)){
  				newValue = array.map(newValue, function(value){
  					return lang.isObject(value) ? value : { value: value };
  				}); // __SelectOption[]
  			}else if(lang.isObject(newValue)){
  				newValue = [newValue];
  			}else{
  				newValue = [
  					{ value: newValue }
  				];
  			}
  			newValue = array.filter(this.getOptions(newValue), function(i){
  				return i && i.value;
  			});
  			var opts = this.getOptions() || [];
  			if(!this.multiple && (!newValue[0] || !newValue[0].value) && !!opts.length){
  				newValue[0] = opts[0];
  			}
  			array.forEach(opts, function(opt){
  				opt.selected = array.some(newValue, function(v){
  					return v.value === opt.value;
  				});
  			});
  			var val = array.map(newValue, function(opt){
  				return opt.value;
  			});
  
  			if(typeof val == "undefined" || typeof val[0] == "undefined"){
  				return;
  			} // not fully initialized yet or a failed value lookup
  			var disp = array.map(newValue, function(opt){
  				return opt.label;
  			});
  			this._setDisplay(this.multiple ? disp : disp[0]);
  			this.inherited(arguments, [ this.multiple ? val : val[0], priorityChange ]);
  			this._updateSelection();
  		},
  
  		_getDisplayedValueAttr: function(){
  			// summary:
  			//		returns the displayed value of the widget
  			var ret = array.map([].concat(this.get('selectedOptions')), function(v){
  				if(v && "label" in v){
  					return v.label;
  				}else if(v){
  					return v.value;
  				}
  				return null;
  			}, this);
  			return this.multiple ? ret : ret[0];
  		},
  
  		_setDisplayedValueAttr: function(label){
  			// summary:
  			//		Sets the displayed value of the widget
  			this.set('value', this.getOptions(typeof label == "string" ? { label: label } : label));
  		},
  
  		_loadChildren: function(){
  			// summary:
  			//		Loads the children represented by this widget's options.
  			//		reset the menu to make it populatable on the next click
  			if(this._loadingStore){
  				return;
  			}
  			array.forEach(this._getChildren(), function(child){
  				child.destroyRecursive();
  			});
  			// Add each menu item
  			array.forEach(this.options, this._addOptionItem, this);
  
  			// Update states
  			this._updateSelection();
  		},
  
  		_updateSelection: function(){
  			// summary:
  			//		Sets the "selected" class on the item for styling purposes
  			this.focusedChild = null;
  			this._set("value", this._getValueFromOpts());
  			var val = [].concat(this.value);
  			if(val && val[0]){
  				var self = this;
  				array.forEach(this._getChildren(), function(child){
  					var isSelected = array.some(val, function(v){
  						return child.option && (v === child.option.value);
  					});
  					if(isSelected && !self.multiple){
  						self.focusedChild = child;
  					}
  					domClass.toggle(child.domNode, this.baseClass.replace(/\s+|$/g, "SelectedOption "), isSelected);
  					child.domNode.setAttribute("aria-selected", isSelected ? "true" : "false");
  				}, this);
  			}
  		},
  
  		_getValueFromOpts: function(){
  			// summary:
  			//		Returns the value of the widget by reading the options for
  			//		the selected flag
  			var opts = this.getOptions() || [];
  			if(!this.multiple && opts.length){
  				// Mirror what a select does - choose the first one
  				var opt = array.filter(opts, function(i){
  					return i.selected;
  				})[0];
  				if(opt && opt.value){
  					return opt.value;
  				}else{
  					opts[0].selected = true;
  					return opts[0].value;
  				}
  			}else if(this.multiple){
  				// Set value to be the sum of all selected
  				return array.map(array.filter(opts, function(i){
  					return i.selected;
  				}), function(i){
  					return i.value;
  				}) || [];
  			}
  			return "";
  		},
  
  		// Internal functions to call when we have store notifications come in
  		_onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
  			if(!parentInfo || !parentInfo.parent){
  				// Only add it if we are top-level
  				this._addOptionForItem(item);
  			}
  		},
  		_onDeleteItem: function(/*item*/ item){
  			var store = this.store;
  			this.removeOption({value: store.getIdentity(item) });
  		},
  		_onSetItem: function(/*item*/ item){
  			this.updateOption(this._getOptionObjForItem(item));
  		},
  
  		_getOptionObjForItem: function(item){
  			// summary:
  			//		Returns an option object based off the given item.  The "value"
  			//		of the option item will be the identity of the item, the "label"
  			//		of the option will be the label of the item.
  
  			// remove getLabel() call for 2.0 (it's to support the old dojo.data API)
  			var store = this.store,
  				label = (this.labelAttr && this.labelAttr in item) ? item[this.labelAttr] : store.getLabel(item),
  				value = (label ? store.getIdentity(item) : null);
  			return {value: value, label: label, item: item}; // __SelectOption
  		},
  
  		_addOptionForItem: function(/*item*/ item){
  			// summary:
  			//		Creates (and adds) the option for the given item
  			var store = this.store;
  			if(store.isItemLoaded && !store.isItemLoaded(item)){
  				// We are not loaded - so let's load it and add later.
  				// Remove for 2.0 (it's the old dojo.data API)
  				store.loadItem({item: item, onItem: function(i){
  					this._addOptionForItem(i);
  				},
  					scope: this});
  				return;
  			}
  			var newOpt = this._getOptionObjForItem(item);
  			this.addOption(newOpt);
  		},
  
  		constructor: function(params /*===== , srcNodeRef =====*/){
  			// summary:
  			//		Create the widget.
  			// params: Object|null
  			//		Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
  			//		and functions, typically callbacks like onClick.
  			//		The hash can contain any of the widget's properties, excluding read-only properties.
  			// srcNodeRef: DOMNode|String?
  			//		If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
  
  			//		Saves off our value, if we have an initial one set so we
  			//		can use it if we have a store as well (see startup())
  			this._oValue = (params || {}).value || null;
  			this._notifyConnections = [];	// remove for 2.0
  		},
  
  		buildRendering: function(){
  			this.inherited(arguments);
  			dom.setSelectable(this.focusNode, false);
  		},
  
  		_fillContent: function(){
  			// summary:
  			//		Loads our options and sets up our dropdown correctly.  We
  			//		don't want any content, so we don't call any inherit chain
  			//		function.
  			if(!this.options){
  				this.options =
  					this.srcNodeRef
  						? query("> *", this.srcNodeRef).map(
  						function(node){
  							if(node.getAttribute("type") === "separator"){
  								return { value: "", label: "", selected: false, disabled: false };
  							}
  							return {
  								value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
  								label: String(node.innerHTML),
  								// FIXME: disabled and selected are not valid on complex markup children (which is why we're
  								// looking for data-dojo-value above.  perhaps we should data-dojo-props="" this whole thing?)
  								// decide before 1.6
  								selected: node.getAttribute("selected") || false,
  								disabled: node.getAttribute("disabled") || false
  							};
  						},
  						this)
  						: [];
  			}
  			if(!this.value){
  				this._set("value", this._getValueFromOpts());
  			}else if(this.multiple && typeof this.value == "string"){
  				this._set("value", this.value.split(","));
  			}
  		},
  
  		postCreate: function(){
  			// summary:
  			//		sets up our event handling that we need for functioning
  			//		as a select
  			this.inherited(arguments);
  
  			// Make our event connections for updating state
  			aspect.after(this, "onChange", lang.hitch(this, "_updateSelection"));
  
  			// moved from startup
  			//		Connects in our store, if we have one defined
  			var store = this.store;
  			if(store && (store.getIdentity || store.getFeatures()["dojo.data.api.Identity"])){
  				// Temporarily set our store to null so that it will get set
  				// and connected appropriately
  				this.store = null;
  				this.setStore(store, this._oValue);
  			}
  		},
  
  		startup: function(){
  			// summary:
  			this._loadChildren();
  			this.inherited(arguments);
  		},
  
  		destroy: function(){
  			// summary:
  			//		Clean up our connections
  
  			var h;
  			while((h = this._notifyConnections.pop())){
  				h.remove();
  			}
  
  			// Cancel listener for store updates
  			if(this._queryRes && this._queryRes.close){
  				this._queryRes.close();
  			}
  
  			this.inherited(arguments);
  		},
  
  		_addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
  			// summary:
  			//		User-overridable function which, for the given option, adds an
  			//		item to the select.  If the option doesn't have a value, then a
  			//		separator is added in that place.  Make sure to store the option
  			//		in the created option widget.
  		},
  
  		_removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
  			// summary:
  			//		User-overridable function which, for the given option, removes
  			//		its item from the select.
  		},
  
  		_setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
  			// summary:
  			//		Overridable function which will set the display for the
  			//		widget.  newDisplay is either a string (in the case of
  			//		single selects) or array of strings (in the case of multi-selects)
  		},
  
  		_getChildren: function(){
  			// summary:
  			//		Overridable function to return the children that this widget contains.
  			return [];
  		},
  
  		_getSelectedOptionsAttr: function(){
  			// summary:
  			//		hooks into this.attr to provide a mechanism for getting the
  			//		option items for the current value of the widget.
  			return this.getOptions({ selected: true });
  		},
  
  		_pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
  			// summary:
  			//		a function that will "fake" loading children, if needed, and
  			//		if we have set to not load children until the widget opens.
  			// items:
  			//		An array of items that will be loaded, when needed
  		},
  
  		onSetStore: function(){
  			// summary:
  			//		a function that can be connected to in order to receive a
  			//		notification that the store has finished loading and all options
  			//		from that store are available
  		}
  	});
  
  	/*=====
  	_FormSelectWidget.__SelectOption = __SelectOption;
  	=====*/
  
  	return _FormSelectWidget;
  });
  