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"); dojo.require("dijit.form.ComboBox"); dojo.declare( "dijit.form.FilteringSelect", [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin], { // summary // An enhanced version of the HTML SELECT tag, populated dynamically // // description // An enhanced version of the HTML SELECT tag, populated dynamically. It works // very nicely with very large data sets because it can load and page data as needed. // It also resembles ComboBox, but does not allow values outside of the provided ones. // // Similar features: // - There is a drop down list of possible values. // - You can only enter a value from the drop down list. (You can't // enter an arbitrary value.) // - The value submitted with the form is the hidden value (ex: CA), // not the displayed value a.k.a. label (ex: California) // // Enhancements over plain HTML version: // - If you type in some text then it will filter down the list of // possible values in the drop down list. // - List can be specified either as a static list or via a javascript // function (that can get the list from a server) // // searchAttr: String // Searches pattern match against this field // // labelAttr: String // Optional. The text that actually appears in the drop down. // If not specified, the searchAttr text is used instead. labelAttr: "", // labelType: String // "html" or "text" labelType: "text", _isvalid:true, _lastDisplayedValue: "", isValid:function(){ return this._isvalid; }, _callbackSetLabel: function( /*Array*/ result, /*Object*/ dataObject, /*Boolean?*/ priorityChange){ // summary: // Callback function that dynamically sets the label of the // ComboBox // setValue does a synchronous lookup, // so it calls _callbackSetLabel directly, // and so does not pass dataObject // dataObject==null means do not test the lastQuery, just continue if(dataObject && dataObject.query[this.searchAttr] != this._lastQuery){ return; } if(!result.length){ //#3268: do nothing on bad input //this._setValue("", ""); //#3285: change CSS to indicate error if(!this._focused){ this.valueNode.value=""; } dijit.form.TextBox.superclass.setValue.call(this, undefined, !this._focused); this._isvalid=false; this.validate(this._focused); }else{ this._setValueFromItem(result[0], priorityChange); } }, _openResultList: function(/*Object*/ results, /*Object*/ dataObject){ // #3285: tap into search callback to see if user's query resembles a match if(dataObject.query[this.searchAttr] != this._lastQuery){ return; } this._isvalid = results.length != 0; // FIXME: should this be greater-than? this.validate(true); dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments); }, getValue:function(){ // don't get the textbox value but rather the previously set hidden value return this.valueNode.value; }, _getValueField:function(){ // used for option tag selects return "value"; }, _setValue:function( /*String*/ value, /*String*/ displayedValue, /*Boolean?*/ priorityChange){ this.valueNode.value = value; dijit.form.FilteringSelect.superclass.setValue.call(this, value, priorityChange, displayedValue); this._lastDisplayedValue = displayedValue; }, setValue: function(/*String*/ value, /*Boolean?*/ priorityChange){ // summary // Sets the value of the select. // Also sets the label to the corresponding value by reverse lookup. //#3347: fetchItemByIdentity if no keyAttr specified var self=this; var handleFetchByIdentity = function(item, priorityChange){ if(item){ if(self.store.isItemLoaded(item)){ self._callbackSetLabel([item], undefined, priorityChange); }else{ self.store.loadItem({ item: item, onItem: function(result, dataObject){ self._callbackSetLabel(result, dataObject, priorityChange); } }); } }else{ self._isvalid=false; // prevent errors from Tooltip not being created yet self.validate(false); } } this.store.fetchItemByIdentity({ identity: value, onItem: function(item){ handleFetchByIdentity(item, priorityChange); } }); }, _setValueFromItem: function(/*item*/ item, /*Boolean?*/ priorityChange){ // summary: // Set the displayed valued in the input box, based on a // selected item. // description: // Users shouldn't call this function; they should be calling // setDisplayedValue() instead this._isvalid=true; this._setValue( this.store.getIdentity(item), this.labelFunc(item, this.store), priorityChange); }, labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){ // summary: Event handler called when the label changes // return: the label that the ComboBox should display return store.getValue(item, this.searchAttr); }, _doSelect: function(/*Event*/ tgt){ // summary: // ComboBox's menu callback function // description: // FilteringSelect overrides this to set both the visible and // hidden value from the information stored in the menu this.item = tgt.item; this._setValueFromItem(tgt.item, true); }, setDisplayedValue:function(/*String*/ label, /*Boolean?*/ priorityChange){ // summary: // Set textbox to display label. Also performs reverse lookup // to set the hidden value. Used in InlineEditBox if(this.store){ var query = dojo.clone(this.query); // #6196: populate query with user-specifics this._lastQuery = query[this.searchAttr] = label; // if the label is not valid, the callback will never set it, // so the last valid value will get the warning textbox set the // textbox value now so that the impending warning will make // sense to the user this.textbox.value = label; this._lastDisplayedValue = label; var _this = this; this.store.fetch({ query: query, queryOptions: { ignoreCase: this.ignoreCase, deep: true }, onComplete: function(result, dataObject){ dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange); }, onError: function(errText){ console.error('dijit.form.FilteringSelect: ' + errText); dojo.hitch(_this, "_setValue")(undefined, label, false); } }); } }, _getMenuLabelFromItem:function(/*Item*/ item){ // internal function to help ComboBoxMenu figure out what to display if(this.labelAttr){ return { html: this.labelType=="html", label: this.store.getValue(item, this.labelAttr) }; }else{ // because this function is called by ComboBoxMenu, // this.inherited tries to find the superclass of ComboBoxMenu return dijit.form.ComboBoxMixin.prototype._getMenuLabelFromItem.apply(this, arguments); } }, postMixInProperties: function(){ // FIXME: shouldn't this just be a call to inherited? dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments); dijit.form.MappedTextBox.prototype.postMixInProperties.apply(this, arguments); }, postCreate: function(){ dijit.form.ComboBoxMixin.prototype._postCreate.apply(this, arguments); dijit.form.MappedTextBox.prototype.postCreate.apply(this, arguments); }, setAttribute: function(/*String*/ attr, /*anything*/ value){ dijit.form.MappedTextBox.prototype.setAttribute.apply(this, arguments); dijit.form.ComboBoxMixin.prototype._setAttribute.apply(this, arguments); }, undo: function(){ this.setDisplayedValue(this._lastDisplayedValue); }, _valueChanged: function(){ return this.getDisplayedValue()!=this._lastDisplayedValue; } } ); }