]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dijit/form/_AutoCompleterMixin", [ |
2 | "dojo/data/util/filter", // patternToRegExp | |
3 | "dojo/_base/declare", // declare | |
4 | "dojo/dom-attr", // domAttr.get | |
5 | "dojo/_base/event", // event.stop | |
6 | "dojo/keys", | |
7 | "dojo/_base/lang", // lang.clone lang.hitch | |
8 | "dojo/query", // query | |
9 | "dojo/regexp", // regexp.escapeString | |
10 | "dojo/sniff", // has("ie") | |
11 | "dojo/string", // string.substitute | |
12 | "./DataList", | |
13 | "../registry", // registry.byId | |
14 | "./_TextBoxMixin", // defines _TextBoxMixin.selectInputText | |
15 | "./_SearchMixin" | |
16 | ], function(filter, declare, domAttr, event, keys, lang, query, regexp, has, string, | |
17 | DataList, registry, _TextBoxMixin, SearchMixin){ | |
18 | ||
19 | // module: | |
20 | // dijit/form/_AutoCompleterMixin | |
21 | ||
22 | return declare("dijit.form._AutoCompleterMixin", SearchMixin, { | |
23 | // summary: | |
24 | // A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect` | |
25 | // description: | |
26 | // All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`. | |
27 | // tags: | |
28 | // protected | |
29 | ||
30 | // item: Object | |
31 | // This is the item returned by the dojo/store/api/Store implementation that | |
32 | // provides the data for this ComboBox, it's the currently selected item. | |
33 | item: null, | |
34 | ||
35 | // autoComplete: Boolean | |
36 | // If user types in a partial string, and then tab out of the `<input>` box, | |
37 | // automatically copy the first entry displayed in the drop down list to | |
38 | // the `<input>` field | |
39 | autoComplete: true, | |
40 | ||
41 | // highlightMatch: String | |
42 | // One of: "first", "all" or "none". | |
43 | // | |
44 | // If the ComboBox/FilteringSelect opens with the search results and the searched | |
45 | // string can be found, it will be highlighted. If set to "all" | |
46 | // then will probably want to change `queryExpr` parameter to '*${0}*' | |
47 | // | |
48 | // Highlighting is only performed when `labelType` is "text", so as to not | |
49 | // interfere with any HTML markup an HTML label might contain. | |
50 | highlightMatch: "first", | |
51 | ||
52 | // labelAttr: String? | |
53 | // The entries in the drop down list come from this attribute in the | |
54 | // dojo.data items. | |
55 | // If not specified, the searchAttr attribute is used instead. | |
56 | labelAttr: "", | |
57 | ||
58 | // labelType: String | |
59 | // Specifies how to interpret the labelAttr in the data store items. | |
60 | // Can be "html" or "text". | |
61 | labelType: "text", | |
62 | ||
63 | // Flags to _HasDropDown to limit height of drop down to make it fit in viewport | |
64 | maxHeight: -1, | |
65 | ||
66 | // For backwards compatibility let onClick events propagate, even clicks on the down arrow button | |
67 | _stopClickEvents: false, | |
68 | ||
69 | _getCaretPos: function(/*DomNode*/ element){ | |
70 | // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22 | |
71 | var pos = 0; | |
72 | if(typeof(element.selectionStart) == "number"){ | |
73 | // FIXME: this is totally borked on Moz < 1.3. Any recourse? | |
74 | pos = element.selectionStart; | |
75 | }else if(has("ie")){ | |
76 | // in the case of a mouse click in a popup being handled, | |
77 | // then the win.doc.selection is not the textarea, but the popup | |
78 | // var r = win.doc.selection.createRange(); | |
79 | // hack to get IE 6 to play nice. What a POS browser. | |
80 | var tr = element.ownerDocument.selection.createRange().duplicate(); | |
81 | var ntr = element.createTextRange(); | |
82 | tr.move("character",0); | |
83 | ntr.move("character",0); | |
84 | try{ | |
85 | // If control doesn't have focus, you get an exception. | |
86 | // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes). | |
87 | // There appears to be no workaround for this - googled for quite a while. | |
88 | ntr.setEndPoint("EndToEnd", tr); | |
89 | pos = String(ntr.text).replace(/\r/g,"").length; | |
90 | }catch(e){ | |
91 | // If focus has shifted, 0 is fine for caret pos. | |
92 | } | |
93 | } | |
94 | return pos; | |
95 | }, | |
96 | ||
97 | _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){ | |
98 | location = parseInt(location); | |
99 | _TextBoxMixin.selectInputText(element, location, location); | |
100 | }, | |
101 | ||
102 | _setDisabledAttr: function(/*Boolean*/ value){ | |
103 | // Additional code to set disabled state of ComboBox node. | |
104 | // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr(). | |
105 | this.inherited(arguments); | |
106 | this.domNode.setAttribute("aria-disabled", value ? "true" : "false"); | |
107 | }, | |
108 | ||
109 | _onKey: function(/*Event*/ evt){ | |
110 | // summary: | |
111 | // Handles keyboard events | |
112 | ||
113 | if(evt.charCode >= 32){ return; } // alphanumeric reserved for searching | |
114 | ||
115 | var key = evt.charCode || evt.keyCode; | |
116 | ||
117 | // except for cutting/pasting case - ctrl + x/v | |
118 | if(key == keys.ALT || key == keys.CTRL || key == keys.META || key == keys.SHIFT){ | |
119 | return; // throw out spurious events | |
120 | } | |
121 | ||
122 | var pw = this.dropDown; | |
123 | var highlighted = null; | |
124 | this._abortQuery(); | |
125 | ||
126 | // _HasDropDown will do some of the work: | |
127 | // | |
128 | // 1. when drop down is not yet shown: | |
129 | // - if user presses the down arrow key, call loadDropDown() | |
130 | // 2. when drop down is already displayed: | |
131 | // - on ESC key, call closeDropDown() | |
132 | // - otherwise, call dropDown.handleKey() to process the keystroke | |
133 | this.inherited(arguments); | |
134 | ||
135 | if(evt.altKey || evt.ctrlKey || evt.metaKey){ return; } // don't process keys with modifiers - but we want shift+TAB | |
136 | ||
137 | if(this._opened){ | |
138 | highlighted = pw.getHighlightedOption(); | |
139 | } | |
140 | switch(key){ | |
141 | case keys.PAGE_DOWN: | |
142 | case keys.DOWN_ARROW: | |
143 | case keys.PAGE_UP: | |
144 | case keys.UP_ARROW: | |
145 | // Keystroke caused ComboBox_menu to move to a different item. | |
146 | // Copy new item to <input> box. | |
147 | if(this._opened){ | |
148 | this._announceOption(highlighted); | |
149 | } | |
150 | event.stop(evt); | |
151 | break; | |
152 | ||
153 | case keys.ENTER: | |
154 | // prevent submitting form if user presses enter. Also | |
155 | // prevent accepting the value if either Next or Previous | |
156 | // are selected | |
157 | if(highlighted){ | |
158 | // only stop event on prev/next | |
159 | if(highlighted == pw.nextButton){ | |
160 | this._nextSearch(1); | |
161 | event.stop(evt); // prevent submit | |
162 | break; | |
163 | }else if(highlighted == pw.previousButton){ | |
164 | this._nextSearch(-1); | |
165 | event.stop(evt); // prevent submit | |
166 | break; | |
167 | } | |
168 | event.stop(evt); // prevent submit if ENTER was to choose an item | |
169 | }else{ | |
170 | // Update 'value' (ex: KY) according to currently displayed text | |
171 | this._setBlurValue(); // set value if needed | |
172 | this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting | |
173 | } | |
174 | // fall through | |
175 | ||
176 | case keys.TAB: | |
177 | var newvalue = this.get('displayedValue'); | |
178 | // if the user had More Choices selected fall into the | |
179 | // _onBlur handler | |
180 | if(pw && ( | |
181 | newvalue == pw._messages["previousMessage"] || | |
182 | newvalue == pw._messages["nextMessage"]) | |
183 | ){ | |
184 | break; | |
185 | } | |
186 | if(highlighted){ | |
187 | this._selectOption(highlighted); | |
188 | } | |
189 | // fall through | |
190 | ||
191 | case keys.ESCAPE: | |
192 | if(this._opened){ | |
193 | this._lastQuery = null; // in case results come back later | |
194 | this.closeDropDown(); | |
195 | } | |
196 | break; | |
197 | } | |
198 | }, | |
199 | ||
200 | _autoCompleteText: function(/*String*/ text){ | |
201 | // summary: | |
202 | // Fill in the textbox with the first item from the drop down | |
203 | // list, and highlight the characters that were | |
204 | // auto-completed. For example, if user typed "CA" and the | |
205 | // drop down list appeared, the textbox would be changed to | |
206 | // "California" and "ifornia" would be highlighted. | |
207 | ||
208 | var fn = this.focusNode; | |
209 | ||
210 | // IE7: clear selection so next highlight works all the time | |
211 | _TextBoxMixin.selectInputText(fn, fn.value.length); | |
212 | // does text autoComplete the value in the textbox? | |
213 | var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr'; | |
214 | if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){ | |
215 | var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length; | |
216 | // only try to extend if we added the last character at the end of the input | |
217 | if((cpos+1) > fn.value.length){ | |
218 | // only add to input node as we would overwrite Capitalisation of chars | |
219 | // actually, that is ok | |
220 | fn.value = text;//.substr(cpos); | |
221 | // visually highlight the autocompleted characters | |
222 | _TextBoxMixin.selectInputText(fn, cpos); | |
223 | } | |
224 | }else{ | |
225 | // text does not autoComplete; replace the whole value and highlight | |
226 | fn.value = text; | |
227 | _TextBoxMixin.selectInputText(fn); | |
228 | } | |
229 | }, | |
230 | ||
231 | _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){ | |
232 | // summary: | |
233 | // Callback when a search completes. | |
234 | // description: | |
235 | // 1. generates drop-down list and calls _showResultList() to display it | |
236 | // 2. if this result list is from user pressing "more choices"/"previous choices" | |
237 | // then tell screen reader to announce new option | |
238 | var wasSelected = this.dropDown.getHighlightedOption(); | |
239 | this.dropDown.clearResultList(); | |
240 | if(!results.length && options.start == 0){ // if no results and not just the previous choices button | |
241 | this.closeDropDown(); | |
242 | return; | |
243 | } | |
244 | this._nextSearch = this.dropDown.onPage = lang.hitch(this, function(direction){ | |
245 | results.nextPage(direction !== -1); | |
246 | this.focus(); | |
247 | }); | |
248 | ||
249 | // Fill in the textbox with the first item from the drop down list, | |
250 | // and highlight the characters that were auto-completed. For | |
251 | // example, if user typed "CA" and the drop down list appeared, the | |
252 | // textbox would be changed to "California" and "ifornia" would be | |
253 | // highlighted. | |
254 | ||
255 | this.dropDown.createOptions( | |
256 | results, | |
257 | options, | |
258 | lang.hitch(this, "_getMenuLabelFromItem") | |
259 | ); | |
260 | ||
261 | // show our list (only if we have content, else nothing) | |
262 | this._showResultList(); | |
263 | ||
264 | // #4091: | |
265 | // tell the screen reader that the paging callback finished by | |
266 | // shouting the next choice | |
267 | if("direction" in options){ | |
268 | if(options.direction){ | |
269 | this.dropDown.highlightFirstOption(); | |
270 | }else if(!options.direction){ | |
271 | this.dropDown.highlightLastOption(); | |
272 | } | |
273 | if(wasSelected){ | |
274 | this._announceOption(this.dropDown.getHighlightedOption()); | |
275 | } | |
276 | }else if(this.autoComplete && !this._prev_key_backspace | |
277 | // when the user clicks the arrow button to show the full list, | |
278 | // startSearch looks for "*". | |
279 | // it does not make sense to autocomplete | |
280 | // if they are just previewing the options available. | |
281 | && !/^[*]+$/.test(query[this.searchAttr].toString())){ | |
282 | this._announceOption(this.dropDown.containerNode.firstChild.nextSibling); // 1st real item | |
283 | } | |
284 | }, | |
285 | ||
286 | _showResultList: function(){ | |
287 | // summary: | |
288 | // Display the drop down if not already displayed, or if it is displayed, then | |
289 | // reposition it if necessary (reposition may be necessary if drop down's height changed). | |
290 | this.closeDropDown(true); | |
291 | this.openDropDown(); | |
292 | this.domNode.setAttribute("aria-expanded", "true"); | |
293 | }, | |
294 | ||
295 | loadDropDown: function(/*Function*/ /*===== callback =====*/){ | |
296 | // Overrides _HasDropDown.loadDropDown(). | |
297 | // This is called when user has pressed button icon or pressed the down arrow key | |
298 | // to open the drop down. | |
299 | this._startSearchAll(); | |
300 | }, | |
301 | ||
302 | isLoaded: function(){ | |
303 | // signal to _HasDropDown that it needs to call loadDropDown() to load the | |
304 | // drop down asynchronously before displaying it | |
305 | return false; | |
306 | }, | |
307 | ||
308 | closeDropDown: function(){ | |
309 | // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open). | |
310 | // This method is the callback when the user types ESC or clicking | |
311 | // the button icon while the drop down is open. It's also called by other code. | |
312 | this._abortQuery(); | |
313 | if(this._opened){ | |
314 | this.inherited(arguments); | |
315 | this.domNode.setAttribute("aria-expanded", "false"); | |
316 | this.focusNode.removeAttribute("aria-activedescendant"); | |
317 | } | |
318 | }, | |
319 | ||
320 | _setBlurValue: function(){ | |
321 | // if the user clicks away from the textbox OR tabs away, set the | |
322 | // value to the textbox value | |
323 | // #4617: | |
324 | // if value is now more choices or previous choices, revert | |
325 | // the value | |
326 | var newvalue = this.get('displayedValue'); | |
327 | var pw = this.dropDown; | |
328 | if(pw && ( | |
329 | newvalue == pw._messages["previousMessage"] || | |
330 | newvalue == pw._messages["nextMessage"] | |
331 | ) | |
332 | ){ | |
333 | this._setValueAttr(this._lastValueReported, true); | |
334 | }else if(typeof this.item == "undefined"){ | |
335 | // Update 'value' (ex: KY) according to currently displayed text | |
336 | this.item = null; | |
337 | this.set('displayedValue', newvalue); | |
338 | }else{ | |
339 | if(this.value != this._lastValueReported){ | |
340 | this._handleOnChange(this.value, true); | |
341 | } | |
342 | this._refreshState(); | |
343 | } | |
344 | }, | |
345 | ||
346 | _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){ | |
347 | // summary: | |
348 | // Set the displayed valued in the input box, and the hidden value | |
349 | // that gets submitted, based on a dojo.data store item. | |
350 | // description: | |
351 | // Users shouldn't call this function; they should be calling | |
352 | // set('item', value) | |
353 | // tags: | |
354 | // private | |
355 | var value = ''; | |
356 | if(item){ | |
357 | if(!displayedValue){ | |
358 | displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API) | |
359 | this.store.getValue(item, this.searchAttr) : item[this.searchAttr]; | |
360 | } | |
361 | value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue; | |
362 | } | |
363 | this.set('value', value, priorityChange, displayedValue, item); | |
364 | }, | |
365 | ||
366 | _announceOption: function(/*Node*/ node){ | |
367 | // summary: | |
368 | // a11y code that puts the highlighted option in the textbox. | |
369 | // This way screen readers will know what is happening in the | |
370 | // menu. | |
371 | ||
372 | if(!node){ | |
373 | return; | |
374 | } | |
375 | // pull the text value from the item attached to the DOM node | |
376 | var newValue; | |
377 | if(node == this.dropDown.nextButton || | |
378 | node == this.dropDown.previousButton){ | |
379 | newValue = node.innerHTML; | |
380 | this.item = undefined; | |
381 | this.value = ''; | |
382 | }else{ | |
383 | var item = this.dropDown.items[node.getAttribute("item")]; | |
384 | newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API) | |
385 | this.store.getValue(item, this.searchAttr) : item[this.searchAttr]).toString(); | |
386 | this.set('item', item, false, newValue); | |
387 | } | |
388 | // get the text that the user manually entered (cut off autocompleted text) | |
389 | this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length); | |
390 | // set up ARIA activedescendant | |
391 | this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id")); | |
392 | // autocomplete the rest of the option to announce change | |
393 | this._autoCompleteText(newValue); | |
394 | }, | |
395 | ||
396 | _selectOption: function(/*DomNode*/ target){ | |
397 | // summary: | |
398 | // Menu callback function, called when an item in the menu is selected. | |
399 | this.closeDropDown(); | |
400 | if(target){ | |
401 | this._announceOption(target); | |
402 | } | |
403 | this._setCaretPos(this.focusNode, this.focusNode.value.length); | |
404 | this._handleOnChange(this.value, true); | |
405 | }, | |
406 | ||
407 | _startSearchAll: function(){ | |
408 | this._startSearch(''); | |
409 | }, | |
410 | ||
411 | _startSearchFromInput: function(){ | |
412 | this.item = undefined; // undefined means item needs to be set | |
413 | this.inherited(arguments); | |
414 | }, | |
415 | ||
416 | _startSearch: function(/*String*/ key){ | |
417 | // summary: | |
418 | // Starts a search for elements matching key (key=="" means to return all items), | |
419 | // and calls _openResultList() when the search completes, to display the results. | |
420 | if(!this.dropDown){ | |
421 | var popupId = this.id + "_popup", | |
422 | dropDownConstructor = lang.isString(this.dropDownClass) ? | |
423 | lang.getObject(this.dropDownClass, false) : this.dropDownClass; | |
424 | this.dropDown = new dropDownConstructor({ | |
425 | onChange: lang.hitch(this, this._selectOption), | |
426 | id: popupId, | |
427 | dir: this.dir, | |
428 | textDir: this.textDir | |
429 | }); | |
430 | this.focusNode.removeAttribute("aria-activedescendant"); | |
431 | this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox | |
432 | } | |
433 | this._lastInput = key; // Store exactly what was entered by the user. | |
434 | this.inherited(arguments); | |
435 | }, | |
436 | ||
437 | _getValueField: function(){ | |
438 | // summary: | |
439 | // Helper for postMixInProperties() to set this.value based on data inlined into the markup. | |
440 | // Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value. | |
441 | return this.searchAttr; | |
442 | }, | |
443 | ||
444 | //////////// INITIALIZATION METHODS /////////////////////////////////////// | |
445 | ||
446 | postMixInProperties: function(){ | |
447 | this.inherited(arguments); | |
448 | if(!this.store){ | |
449 | var srcNodeRef = this.srcNodeRef; | |
450 | // if user didn't specify store, then assume there are option tags | |
451 | this.store = new DataList({}, srcNodeRef); | |
452 | ||
453 | // if there is no value set and there is an option list, set | |
454 | // the value to the first value to be consistent with native Select | |
455 | // Firefox and Safari set value | |
456 | // IE6 and Opera set selectedIndex, which is automatically set | |
457 | // by the selected attribute of an option tag | |
458 | // IE6 does not set value, Opera sets value = selectedIndex | |
459 | if(!("value" in this.params)){ | |
460 | var item = (this.item = this.store.fetchSelectedItem()); | |
461 | if(item){ | |
462 | var valueField = this._getValueField(); | |
463 | // remove getValue() for 2.0 (old dojo.data API) | |
464 | this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField]; | |
465 | } | |
466 | } | |
467 | } | |
468 | }, | |
469 | ||
470 | postCreate: function(){ | |
471 | // summary: | |
472 | // Subclasses must call this method from their postCreate() methods | |
473 | // tags: | |
474 | // protected | |
475 | ||
476 | // find any associated label element and add to ComboBox node. | |
477 | var label=query('label[for="'+this.id+'"]'); | |
478 | if(label.length){ | |
479 | if(!label[0].id){ label[0].id = this.id + "_label"; } | |
480 | this.domNode.setAttribute("aria-labelledby", label[0].id); | |
481 | ||
482 | } | |
483 | this.inherited(arguments); | |
484 | this.connect(this, "onSearch", "_openResultList"); | |
485 | }, | |
486 | ||
487 | _getMenuLabelFromItem: function(/*Item*/ item){ | |
488 | var label = this.labelFunc(item, this.store), | |
489 | labelType = this.labelType; | |
490 | // If labelType is not "text" we don't want to screw any markup ot whatever. | |
491 | if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){ | |
492 | label = this.doHighlight(label, this._lastInput); | |
493 | labelType = "html"; | |
494 | } | |
495 | return {html: labelType == "html", label: label}; | |
496 | }, | |
497 | ||
498 | doHighlight: function(/*String*/ label, /*String*/ find){ | |
499 | // summary: | |
500 | // Highlights the string entered by the user in the menu. By default this | |
501 | // highlights the first occurrence found. Override this method | |
502 | // to implement your custom highlighting. | |
503 | // tags: | |
504 | // protected | |
505 | ||
506 | var | |
507 | // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true | |
508 | modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""), | |
509 | i = this.queryExpr.indexOf("${0}"); | |
510 | find = regexp.escapeString(find); // escape regexp special chars | |
511 | //If < appears in label, and user presses t, we don't want to highlight the t in the escaped "<" | |
512 | //first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which | |
513 | //should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the | |
514 | //HTML highlight markup. | |
515 | return this._escapeHtml(label.replace( | |
516 | new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers), | |
517 | '\uFFFF$1\uFFFF')).replace( | |
518 | /\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>' | |
519 | ); // returns String, (almost) valid HTML (entities encoded) | |
520 | }, | |
521 | ||
522 | _escapeHtml: function(/*String*/ str){ | |
523 | // TODO Should become dojo.html.entities(), when exists use instead | |
524 | // summary: | |
525 | // Adds escape sequences for special characters in XML: `&<>"'` | |
526 | str = String(str).replace(/&/gm, "&").replace(/</gm, "<") | |
527 | .replace(/>/gm, ">").replace(/"/gm, """); //balance" | |
528 | return str; // string | |
529 | }, | |
530 | ||
531 | reset: function(){ | |
532 | // Overrides the _FormWidget.reset(). | |
533 | // Additionally reset the .item (to clean up). | |
534 | this.item = null; | |
535 | this.inherited(arguments); | |
536 | }, | |
537 | ||
538 | labelFunc: function(item, store){ | |
539 | // summary: | |
540 | // Computes the label to display based on the dojo.data store item. | |
541 | // item: Object | |
542 | // The item from the store | |
543 | // store: dojo/store/api/Store | |
544 | // The store. | |
545 | // returns: | |
546 | // The label that the ComboBox should display | |
547 | // tags: | |
548 | // private | |
549 | ||
550 | // Use toString() because XMLStore returns an XMLItem whereas this | |
551 | // method is expected to return a String (#9354). | |
552 | // Remove getValue() for 2.0 (old dojo.data API) | |
553 | return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) : | |
554 | item[this.labelAttr || this.searchAttr]).toString(); // String | |
555 | }, | |
556 | ||
557 | _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){ | |
558 | // summary: | |
559 | // Hook so set('value', value) works. | |
560 | // description: | |
561 | // Sets the value of the select. | |
562 | this._set("item", item||null); // value not looked up in store | |
563 | if(value == null /* or undefined */){ value = ''; } // null translates to blank | |
564 | this.inherited(arguments); | |
565 | }, | |
566 | _setTextDirAttr: function(/*String*/ textDir){ | |
567 | // summary: | |
568 | // Setter for textDir, needed for the dropDown's textDir update. | |
569 | // description: | |
570 | // Users shouldn't call this function; they should be calling | |
571 | // set('textDir', value) | |
572 | // tags: | |
573 | // private | |
574 | this.inherited(arguments); | |
575 | // update the drop down also (_ComboBoxMenuMixin) | |
576 | if(this.dropDown){ | |
577 | this.dropDown._set("textDir", textDir); | |
578 | } | |
579 | } | |
580 | }); | |
581 | }); |