You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
764 lines
26 KiB
764 lines
26 KiB
define([ |
|
"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, |
|
_setStoreAttr: function(val){ |
|
if(this._created){ // don't repeat work that will happen in postCreate() |
|
this._deprecatedSetStore(val); |
|
} |
|
}, |
|
|
|
// query: object |
|
// A query to use when fetching items from our store |
|
query: null, |
|
_setQueryAttr: function(query){ |
|
if(this._created){ // don't repeat work that will happen in postCreate() |
|
this._deprecatedSetStore(this.store, this.selectedValue, {query: query}); |
|
} |
|
}, |
|
|
|
// queryOptions: object |
|
// Query options to use when fetching from the store |
|
queryOptions: null, |
|
_setQueryOptionsAttr: function(queryOptions){ |
|
if(this._created){ // don't repeat work that will happen in postCreate() |
|
this._deprecatedSetStore(this.store, this.selectedValue, {queryOptions: queryOptions}); |
|
} |
|
}, |
|
|
|
// 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.isArrayLike(valueOrIdx)){ |
|
return array.map(valueOrIdx, function(item){ |
|
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.isArrayLike(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.isArrayLike(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.isArrayLike(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){ |
|
kernel.deprecated(this.declaredClass+"::setStore(store, selectedValue, fetchArgs) is deprecated. Use set('query', fetchArgs.query), set('queryOptions', fetchArgs.queryOptions), set('store', store), or set('value', selectedValue) instead.", "", "2.0"); |
|
this._deprecatedSetStore(store, selectedValue, fetchArgs); |
|
}, |
|
|
|
_deprecatedSetStore: 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 (dojo.data) store |
|
if(this._queryRes && this._queryRes.close){ |
|
this._queryRes.close(); |
|
} |
|
|
|
// Cancel listener for updates to new (dojo.store) store |
|
if(this._observeHandle && this._observeHandle.remove){ |
|
this._observeHandle.remove(); |
|
this._observeHandle = null; |
|
} |
|
|
|
// 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); |
|
} |
|
if(fetchArgs.queryOptions){ |
|
this._set("queryOptions", fetchArgs.queryOptions); |
|
} |
|
|
|
// Add our new options |
|
if(store && store.query){ |
|
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){ |
|
// observe returns yet another handle that needs its own explicit gc |
|
this._observeHandle = 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(); |
|
}), lang.hitch(this, function(err){ |
|
console.error('dijit.form.Select: ' + err.toString()); |
|
this.onLoadDeferred.reject(err); |
|
})); |
|
} |
|
return oStore; // dojo/data/api/Identity |
|
}, |
|
|
|
_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.isArrayLike(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")); |
|
|
|
// 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._deprecatedSetStore(store, this._oValue, {query: this.query, queryOptions: this.queryOptions}); |
|
} |
|
|
|
this._storeInitialized = true; |
|
}, |
|
|
|
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(); |
|
} |
|
|
|
// Cancel listener for updates to new (dojo.store) store |
|
if(this._observeHandle && this._observeHandle.remove){ |
|
this._observeHandle.remove(); |
|
this._observeHandle = null; |
|
} |
|
|
|
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; |
|
});
|
|
|