]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/form/_SearchMixin.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / form / _SearchMixin.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("dijit/form/_SearchMixin", [
2 "dojo/data/util/filter", // patternToRegExp
3 "dojo/_base/declare", // declare
4 "dojo/_base/event", // event.stop
5 "dojo/keys", // keys
6 "dojo/_base/lang", // lang.clone lang.hitch
7 "dojo/query", // query
8 "dojo/sniff", // has("ie")
9 "dojo/string", // string.substitute
10 "dojo/when",
11 "../registry" // registry.byId
12], function(filter, declare, event, keys, lang, query, has, string, when, registry){
13
14 // module:
15 // dijit/form/_SearchMixin
16
17
18 return declare("dijit.form._SearchMixin", null, {
19 // summary:
20 // A mixin that implements the base functionality to search a store based upon user-entered text such as
21 // with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
22 // tags:
23 // protected
24
25 // pageSize: Integer
26 // Argument to data provider.
27 // Specifies maximum number of search results to return per query
28 pageSize: Infinity,
29
30 // store: [const] dojo/store/api/Store
31 // Reference to data provider object used by this ComboBox.
32 // The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
33 store: null,
34
35 // fetchProperties: Object
36 // Mixin to the store's fetch.
37 // For example, to set the sort order of the ComboBox menu, pass:
38 // | { sort: [{attribute:"name",descending: true}] }
39 // To override the default queryOptions so that deep=false, do:
40 // | { queryOptions: {ignoreCase: true, deep: false} }
41 fetchProperties:{},
42
43 // query: Object
44 // A query that can be passed to `store` to initially filter the items.
45 // ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
46 query: {},
47
48 // searchDelay: Integer
49 // Delay in milliseconds between when user types something and we start
50 // searching based on that value
51 searchDelay: 200,
52
53 // searchAttr: String
54 // Search for items in the data store where this attribute (in the item)
55 // matches what the user typed
56 searchAttr: "name",
57
58 // queryExpr: String
59 // This specifies what query is sent to the data store,
60 // based on what the user has typed. Changing this expression will modify
61 // whether the results are only exact matches, a "starting with" match,
62 // etc.
63 // dojo.data query expression pattern.
64 // `${0}` will be substituted for the user text.
65 // `*` is used for wildcards.
66 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
67 queryExpr: "${0}*",
68
69 // ignoreCase: Boolean
70 // Set true if the query should ignore case when matching possible items
71 ignoreCase: true,
72
73 _abortQuery: function(){
74 // stop in-progress query
75 if(this.searchTimer){
76 this.searchTimer = this.searchTimer.remove();
77 }
78 if(this._queryDeferHandle){
79 this._queryDeferHandle = this._queryDeferHandle.remove();
80 }
81 if(this._fetchHandle){
82 if(this._fetchHandle.abort){
83 this._cancelingQuery = true;
84 this._fetchHandle.abort();
85 this._cancelingQuery = false;
86 }
87 if(this._fetchHandle.cancel){
88 this._cancelingQuery = true;
89 this._fetchHandle.cancel();
90 this._cancelingQuery = false;
91 }
92 this._fetchHandle = null;
93 }
94 },
95
96 _processInput: function(/*Event*/ evt){
97 // summary:
98 // Handles input (keyboard/paste) events
99 if(this.disabled || this.readOnly){ return; }
100 var key = evt.charOrCode;
101
102 // except for cutting/pasting case - ctrl + x/v
103 if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
104 return; // throw out weird key combinations and spurious events
105 }
106
107 var doSearch = false;
108 this._prev_key_backspace = false;
109
110 switch(key){
111 case keys.DELETE:
112 case keys.BACKSPACE:
113 this._prev_key_backspace = true;
114 this._maskValidSubsetError = true;
115 doSearch = true;
116 break;
117
118 default:
119 // Non char keys (F1-F12 etc..) shouldn't start a search..
120 // Ascii characters and IME input (Chinese, Japanese etc.) should.
121 //IME input produces keycode == 229.
122 doSearch = typeof key == 'string' || key == 229;
123 }
124 if(doSearch){
125 // need to wait a tad before start search so that the event
126 // bubbles through DOM and we have value visible
127 if(!this.store){
128 this.onSearch();
129 }else{
130 this.searchTimer = this.defer("_startSearchFromInput", 1);
131 }
132 }
133 },
134
135 onSearch: function(/*===== results, query, options =====*/){
136 // summary:
137 // Callback when a search completes.
138 //
139 // results: Object
140 // An array of items from the originating _SearchMixin's store.
141 //
142 // query: Object
143 // A copy of the originating _SearchMixin's query property.
144 //
145 // options: Object
146 // The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
147 //
148 // tags:
149 // callback
150 },
151
152 _startSearchFromInput: function(){
153 this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
154 },
155
156 _startSearch: function(/*String*/ text){
157 // summary:
158 // Starts a search for elements matching text (text=="" means to return all items),
159 // and calls onSearch(...) when the search completes, to display the results.
160
161 this._abortQuery();
162 var
163 _this = this,
164 // Setup parameters to be passed to store.query().
165 // Create a new query to prevent accidentally querying for a hidden
166 // value from FilteringSelect's keyField
167 query = lang.clone(this.query), // #5970
168 options = {
169 start: 0,
170 count: this.pageSize,
171 queryOptions: { // remove for 2.0
172 ignoreCase: this.ignoreCase,
173 deep: true
174 }
175 },
176 qs = string.substitute(this.queryExpr, [text]),
177 q,
178 startQuery = function(){
179 var resPromise = _this._fetchHandle = _this.store.query(query, options);
180 if(_this.disabled || _this.readOnly || (q !== _this._lastQuery)){
181 return;
182 } // avoid getting unwanted notify
183 when(resPromise, function(res){
184 _this._fetchHandle = null;
185 if(!_this.disabled && !_this.readOnly && (q === _this._lastQuery)){ // avoid getting unwanted notify
186 when(resPromise.total, function(total){
187 res.total = total;
188 var pageSize = _this.pageSize;
189 if(isNaN(pageSize) || pageSize > res.total){ pageSize = res.total; }
190 // Setup method to fetching the next page of results
191 res.nextPage = function(direction){
192 // tell callback the direction of the paging so the screen
193 // reader knows which menu option to shout
194 options.direction = direction = direction !== false;
195 options.count = pageSize;
196 if(direction){
197 options.start += res.length;
198 if(options.start >= res.total){
199 options.count = 0;
200 }
201 }else{
202 options.start -= pageSize;
203 if(options.start < 0){
204 options.count = Math.max(pageSize + options.start, 0);
205 options.start = 0;
206 }
207 }
208 if(options.count <= 0){
209 res.length = 0;
210 _this.onSearch(res, query, options);
211 }else{
212 startQuery();
213 }
214 };
215 _this.onSearch(res, query, options);
216 });
217 }
218 }, function(err){
219 _this._fetchHandle = null;
220 if(!_this._cancelingQuery){ // don't treat canceled query as an error
221 console.error(_this.declaredClass + ' ' + err.toString());
222 }
223 });
224 };
225
226 lang.mixin(options, this.fetchProperties);
227
228 // Generate query
229 if(this.store._oldAPI){
230 // remove this branch for 2.0
231 q = qs;
232 }else{
233 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
234 // but with a toString() method to help dojo/store/JsonRest.
235 // Search string like "Co*" converted to regex like /^Co.*$/i.
236 q = filter.patternToRegExp(qs, this.ignoreCase);
237 q.toString = function(){ return qs; };
238 }
239
240 // set _lastQuery, *then* start the timeout
241 // otherwise, if the user types and the last query returns before the timeout,
242 // _lastQuery won't be set and their input gets rewritten
243 this._lastQuery = query[this.searchAttr] = q;
244 this._queryDeferHandle = this.defer(startQuery, this.searchDelay);
245 },
246
247 //////////// INITIALIZATION METHODS ///////////////////////////////////////
248
249 constructor: function(){
250 this.query={};
251 this.fetchProperties={};
252 },
253
254 postMixInProperties: function(){
255 if(!this.store){
256 var list = this.list;
257 if(list){
258 this.store = registry.byId(list);
259 }
260 }
261 this.inherited(arguments);
262 }
263 });
264});