]> git.wh0rd.org Git - tt-rss.git/blob - lib/dijit/form/FilteringSelect.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / form / FilteringSelect.js
1 /*
2         Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3         Available via Academic Free License >= 2.1 OR the modified BSD license.
4         see: http://dojotoolkit.org/license for details
5 */
6
7
8 if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo._hasResource["dijit.form.FilteringSelect"] = true;
10 dojo.provide("dijit.form.FilteringSelect");
11 dojo.require("dijit.form.ComboBox");
12
13
14 dojo.declare(
15         "dijit.form.FilteringSelect",
16         [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
17         {
18                 // summary:
19                 //              An enhanced version of the HTML SELECT tag, populated dynamically
20                 //
21                 // description:
22                 //              An enhanced version of the HTML SELECT tag, populated dynamically. It works
23                 //              very nicely with very large data sets because it can load and page data as needed.
24                 //              It also resembles ComboBox, but does not allow values outside of the provided ones.
25                 //              If OPTION tags are used as the data provider via markup, then the
26                 //              OPTION tag's child text node is used as the displayed value when selected
27                 //              while the OPTION tag's value attribute is used as the widget value on form submit.
28                 //              To set the default value when using OPTION tags, specify the selected
29                 //              attribute on 1 of the child OPTION tags.
30                 //
31                 //              Similar features:
32                 //                      - There is a drop down list of possible values.
33                 //                      - You can only enter a value from the drop down list.  (You can't
34                 //                              enter an arbitrary value.)
35                 //                      - The value submitted with the form is the hidden value (ex: CA),
36                 //                              not the displayed value a.k.a. label (ex: California)
37                 //
38                 //              Enhancements over plain HTML version:
39                 //                      - If you type in some text then it will filter down the list of
40                 //                              possible values in the drop down list.
41                 //                      - List can be specified either as a static list or via a javascript
42                 //                              function (that can get the list from a server)
43
44                 // required: Boolean
45                 //              True (default) if user is required to enter a value into this field.
46                 required: true,
47
48                 _lastDisplayedValue: "",
49
50                 _isValidSubset: function(){
51                         return this._opened;
52                 },
53
54                 isValid: function(){
55                         // Overrides ValidationTextBox.isValid()
56                         return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
57                 },
58
59                 _refreshState: function(){
60                         if(!this.searchTimer){ // state will be refreshed after results are returned
61                                 this.inherited(arguments);
62                         }
63                 },
64
65                 _callbackSetLabel: function(
66                                                 /*Array*/ result,
67                                                 /*Object*/ dataObject,
68                                                 /*Boolean?*/ priorityChange){
69                         // summary:
70                         //              Callback from dojo.data after lookup of user entered value finishes
71
72                         // setValue does a synchronous lookup,
73                         // so it calls _callbackSetLabel directly,
74                         // and so does not pass dataObject
75                         // still need to test against _lastQuery in case it came too late
76                         if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
77                                 return;
78                         }
79                         if(!result.length){
80                                 //#3268: don't modify display value on bad input
81                                 //#3285: change CSS to indicate error
82                                 this.valueNode.value = "";
83                                 dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
84                                 this._set("item", null);
85                                 this.validate(this._focused);
86                         }else{
87                                 this.set('item', result[0], priorityChange);
88                         }
89                 },
90
91                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
92                         // Callback when a data store query completes.
93                         // Overrides ComboBox._openResultList()
94
95                         // #3285: tap into search callback to see if user's query resembles a match
96                         if(dataObject.query[this.searchAttr] != this._lastQuery){
97                                 return;
98                         }
99                         dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
100
101                         if(this.item === undefined){ // item == undefined for keyboard search
102                                 // If the search returned no items that means that the user typed
103                                 // in something invalid (and they can't make it valid by typing more characters),
104                                 // so flag the FilteringSelect as being in an invalid state
105                                 this.validate(true);
106                         }
107                 },
108
109                 _getValueAttr: function(){
110                         // summary:
111                         //              Hook for get('value') to work.
112
113                         // don't get the textbox value but rather the previously set hidden value.
114                         // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
115                         return this.valueNode.value;
116                 },
117
118                 _getValueField: function(){
119                         // Overrides ComboBox._getValueField()
120                         return "value";
121                 },
122
123                 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
124                         // summary:
125                         //              Hook so set('value', value) works.
126                         // description:
127                         //              Sets the value of the select.
128                         //              Also sets the label to the corresponding value by reverse lookup.
129                         if(!this._onChangeActive){ priorityChange = null; }
130                         this._lastQuery = value;
131
132                         if(value === null || value === ''){
133                                 this._setDisplayedValueAttr('', priorityChange);
134                                 return;
135                         }
136
137                         //#3347: fetchItemByIdentity if no keyAttr specified
138                         var self = this;
139                         this.store.fetchItemByIdentity({
140                                 identity: value,
141                                 onItem: function(item){
142                                         self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
143                                 }
144                         });
145                 },
146
147                 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
148                         // summary:
149                         //              Set the displayed valued in the input box, and the hidden value
150                         //              that gets submitted, based on a dojo.data store item.
151                         // description:
152                         //              Users shouldn't call this function; they should be calling
153                         //              set('item', value)
154                         // tags:
155                         //              private
156                         this.inherited(arguments);
157                         this.valueNode.value = this.value;
158                         this._lastDisplayedValue = this.textbox.value;
159                 },
160
161                 _getDisplayQueryString: function(/*String*/ text){
162                         return text.replace(/([\\\*\?])/g, "\\$1");
163                 },
164
165                 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
166                         // summary:
167                         //              Hook so set('displayedValue', label) works.
168                         // description:
169                         //              Sets textbox to display label. Also performs reverse lookup
170                         //              to set the hidden value.  label should corresponding to item.searchAttr.
171
172                         if(label == null){ label = ''; }
173
174                         // This is called at initialization along with every custom setter.
175                         // Usually (or always?) the call can be ignored.   If it needs to be
176                         // processed then at least make sure that the XHR request doesn't trigger an onChange()
177                         // event, even if it returns after creation has finished
178                         if(!this._created){
179                                 if(!("displayedValue" in this.params)){
180                                         return;
181                                 }
182                                 priorityChange = false;
183                         }
184
185                         // Do a reverse lookup to map the specified displayedValue to the hidden value.
186                         // Note that if there's a custom labelFunc() this code
187                         if(this.store){
188                                 this.closeDropDown();
189                                 var query = dojo.clone(this.query); // #6196: populate query with user-specifics
190                                 // escape meta characters of dojo.data.util.filter.patternToRegExp().
191                                 this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
192                                 // If the label is not valid, the callback will never set it,
193                                 // so the last valid value will get the warning textbox.   Set the
194                                 // textbox value now so that the impending warning will make
195                                 // sense to the user
196                                 this.textbox.value = label;
197                                 this._lastDisplayedValue = label;
198                                 this._set("displayedValue", label);     // for watch("displayedValue") notification
199                                 var _this = this;
200                                 var fetch = {
201                                         query: query,
202                                         queryOptions: {
203                                                 ignoreCase: this.ignoreCase,
204                                                 deep: true
205                                         },
206                                         onComplete: function(result, dataObject){
207                                                 _this._fetchHandle = null;
208                                                 dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
209                                         },
210                                         onError: function(errText){
211                                                 _this._fetchHandle = null;
212                                                 console.error('dijit.form.FilteringSelect: ' + errText);
213                                                 dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
214                                         }
215                                 };
216                                 dojo.mixin(fetch, this.fetchProperties);
217                                 this._fetchHandle = this.store.fetch(fetch);
218                         }
219                 },
220
221                 undo: function(){
222                         this.set('displayedValue', this._lastDisplayedValue);
223                 }
224         }
225 );
226
227 }