source: Dev/branches/rest-dojo-ui/client/dijit/form/Select.js @ 274

Last change on this file since 274 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 10.5 KB
Line 
1define([
2        "dojo/_base/array", // array.forEach
3        "dojo/_base/declare", // declare
4        "dojo/dom-attr", // domAttr.set
5        "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
6        "dojo/dom-construct", // domConstruct.create
7        "dojo/dom-geometry", // domGeometry.setMarginBox
8        "dojo/_base/event", // event.stop
9        "dojo/i18n", // i18n.getLocalization
10        "dojo/_base/lang", // lang.hitch
11        "./_FormSelectWidget",
12        "../_HasDropDown",
13        "../Menu",
14        "../MenuItem",
15        "../MenuSeparator",
16        "../Tooltip",
17        "dojo/text!./templates/Select.html",
18        "dojo/i18n!./nls/validate"
19], function(array, declare, domAttr, domClass, domConstruct, domGeometry, event, i18n, lang,
20                        _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
21
22/*=====
23        var _FormSelectWidget = dijit.form._FormSelectWidget;
24        var _HasDropDown = dijit._HasDropDown;
25        var _FormSelectWidget = dijit._FormSelectWidget;
26        var Menu = dijit.Menu;
27        var MenuItem = dijit.MenuItem;
28        var MenuSeparator = dijit.MenuSeparator;
29        var Tooltip = dijit.Tooltip;
30=====*/
31
32// module:
33//              dijit/form/Select
34// summary:
35//              This is a "styleable" select box - it is basically a DropDownButton which
36//              can take a <select> as its input.
37
38
39var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
40        // summary:
41        //              An internally-used menu for dropdown that allows us a vertical scrollbar
42        buildRendering: function(){
43                // summary:
44                //              Stub in our own changes, so that our domNode is not a table
45                //              otherwise, we won't respond correctly to heights/overflows
46                this.inherited(arguments);
47                var o = (this.menuTableNode = this.domNode);
48                var n = (this.domNode = domConstruct.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
49                if(o.parentNode){
50                        o.parentNode.replaceChild(n, o);
51                }
52                domClass.remove(o, "dijitMenuTable");
53                n.className = o.className + " dijitSelectMenu";
54                o.className = "dijitReset dijitMenuTable";
55                o.setAttribute("role", "listbox");
56                n.setAttribute("role", "presentation");
57                n.appendChild(o);
58        },
59
60        postCreate: function(){
61                // summary:
62                //              stop mousemove from selecting text on IE to be consistent with other browsers
63
64                this.inherited(arguments);
65
66                this.connect(this.domNode, "onmousemove", event.stop);
67        },
68
69        resize: function(/*Object*/ mb){
70                // summary:
71                //              Overridden so that we are able to handle resizing our
72                //              internal widget.  Note that this is not a "full" resize
73                //              implementation - it only works correctly if you pass it a
74                //              marginBox.
75                //
76                // mb: Object
77                //              The margin box to set this dropdown to.
78                if(mb){
79                        domGeometry.setMarginBox(this.domNode, mb);
80                        if("w" in mb){
81                                // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
82                                // 100% is safer than a pixel value because there may be a scroll bar with
83                                // browser/OS specific width.
84                                this.menuTableNode.style.width = "100%";
85                        }
86                }
87        }
88});
89
90var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
91        // summary:
92        //              This is a "styleable" select box - it is basically a DropDownButton which
93        //              can take a <select> as its input.
94
95        baseClass: "dijitSelect",
96
97        templateString: template,
98
99        // required: Boolean
100        //              Can be true or false, default is false.
101        required: false,
102
103        // state: [readonly] String
104        //              "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
105        state: "",
106
107        // message: String
108        //              Currently displayed error/prompt message
109        message: "",
110
111        //      tooltipPosition: String[]
112        //              See description of dijit.Tooltip.defaultPosition for details on this parameter.
113        tooltipPosition: [],
114
115        // emptyLabel: string
116        //              What to display in an "empty" dropdown
117        emptyLabel: "&#160;",   // &nbsp;
118
119        // _isLoaded: Boolean
120        //              Whether or not we have been loaded
121        _isLoaded: false,
122
123        // _childrenLoaded: Boolean
124        //              Whether or not our children have been loaded
125        _childrenLoaded: false,
126
127        _fillContent: function(){
128                // summary:
129                //              Set the value to be the first, or the selected index
130                this.inherited(arguments);
131                // set value from selected option
132                if(this.options.length && !this.value && this.srcNodeRef){
133                        var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
134                        this.value = this.options[si >= 0 ? si : 0].value;
135                }
136                // Create the dropDown widget
137                this.dropDown = new _SelectMenu({id: this.id + "_menu"});
138                domClass.add(this.dropDown.domNode, this.baseClass + "Menu");
139        },
140
141        _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
142                // summary:
143                //              For the given option, return the menu item that should be
144                //              used to display it.  This can be overridden as needed
145                if(!option.value && !option.label){
146                        // We are a separator (no label set for it)
147                        return new MenuSeparator();
148                }else{
149                        // Just a regular menu option
150                        var click = lang.hitch(this, "_setValueAttr", option);
151                        var item = new MenuItem({
152                                option: option,
153                                label: option.label || this.emptyLabel,
154                                onClick: click,
155                                disabled: option.disabled || false
156                        });
157                        item.focusNode.setAttribute("role", "listitem");
158                        return item;
159                }
160        },
161
162        _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
163                // summary:
164                //              For the given option, add an option to our dropdown.
165                //              If the option doesn't have a value, then a separator is added
166                //              in that place.
167                if(this.dropDown){
168                        this.dropDown.addChild(this._getMenuItemForOption(option));
169                }
170        },
171
172        _getChildren: function(){
173                if(!this.dropDown){
174                        return [];
175                }
176                return this.dropDown.getChildren();
177        },
178
179        _loadChildren: function(/*Boolean*/ loadMenuItems){
180                // summary:
181                //              Resets the menu and the length attribute of the button - and
182                //              ensures that the label is appropriately set.
183                //      loadMenuItems: Boolean
184                //              actually loads the child menu items - we only do this when we are
185                //              populating for showing the dropdown.
186
187                if(loadMenuItems === true){
188                        // this.inherited destroys this.dropDown's child widgets (MenuItems).
189                        // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
190                        // issues later in _setSelected). (see #10296)
191                        if(this.dropDown){
192                                delete this.dropDown.focusedChild;
193                        }
194                        if(this.options.length){
195                                this.inherited(arguments);
196                        }else{
197                                // Drop down menu is blank but add one blank entry just so something appears on the screen
198                                // to let users know that they are no choices (mimicing native select behavior)
199                                array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
200                                var item = new MenuItem({label: "&#160;"});
201                                this.dropDown.addChild(item);
202                        }
203                }else{
204                        this._updateSelection();
205                }
206
207                this._isLoaded = false;
208                this._childrenLoaded = true;
209
210                if(!this._loadingStore){
211                        // Don't call this if we are loading - since we will handle it later
212                        this._setValueAttr(this.value);
213                }
214        },
215
216        _setValueAttr: function(value){
217                this.inherited(arguments);
218                domAttr.set(this.valueNode, "value", this.get("value"));
219                this.validate(this.focused);    // to update this.state
220        },
221
222        _setDisabledAttr: function(/*Boolean*/ value){
223                this.inherited(arguments);
224                this.validate(this.focused);    // to update this.state
225        },
226
227        _setRequiredAttr: function(/*Boolean*/ value){
228                this._set("required", value);
229                this.focusNode.setAttribute("aria-required", value);
230                this.validate(this.focused);    // to update this.state
231        },
232
233        _setDisplay: function(/*String*/ newDisplay){
234                // summary:
235                //              sets the display for the given value (or values)
236                var lbl = newDisplay || this.emptyLabel;
237                this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
238                this.focusNode.setAttribute("aria-valuetext", lbl);
239        },
240
241        validate: function(/*Boolean*/ isFocused){
242                // summary:
243                //              Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
244                // description:
245                //              Show missing or invalid messages if appropriate, and highlight textbox field.
246                //              Used when a select is initially set to no value and the user is required to
247                //              set the value.
248
249                var isValid = this.disabled || this.isValid(isFocused);
250                this._set("state", isValid ? "" : "Incomplete");
251                this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
252                var message = isValid ? "" : this._missingMsg;
253                if(message && this.focused && this._hasBeenBlurred){
254                        Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
255                }else{
256                        Tooltip.hide(this.domNode);
257                }
258                this._set("message", message);
259                return isValid;
260        },
261
262        isValid: function(/*Boolean*/ /*===== isFocused =====*/){
263                // summary:
264                //              Whether or not this is a valid value.  The only way a Select
265                //              can be invalid is when it's required but nothing is selected.
266                return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
267        },
268
269        reset: function(){
270                // summary:
271                //              Overridden so that the state will be cleared.
272                this.inherited(arguments);
273                Tooltip.hide(this.domNode);
274                this.validate(this.focused);    // to update this.state
275        },
276
277        postMixInProperties: function(){
278                // summary:
279                //              set the missing message
280                this.inherited(arguments);
281                this._missingMsg = i18n.getLocalization("dijit.form", "validate",
282                                                                        this.lang).missingMessage;
283        },
284
285        postCreate: function(){
286                // summary:
287                //              stop mousemove from selecting text on IE to be consistent with other browsers
288
289                this.inherited(arguments);
290
291                this.connect(this.domNode, "onmousemove", event.stop);
292        },
293
294        _setStyleAttr: function(/*String||Object*/ value){
295                this.inherited(arguments);
296                domClass.toggle(this.domNode, this.baseClass + "FixedWidth", !!this.domNode.style.width);
297        },
298
299        isLoaded: function(){
300                return this._isLoaded;
301        },
302
303        loadDropDown: function(/*Function*/ loadCallback){
304                // summary:
305                //              populates the menu
306                this._loadChildren(true);
307                this._isLoaded = true;
308                loadCallback();
309        },
310
311        closeDropDown: function(){
312                // overriding _HasDropDown.closeDropDown()
313                this.inherited(arguments);
314
315                if(this.dropDown && this.dropDown.menuTableNode){
316                        // Erase possible width: 100% setting from _SelectMenu.resize().
317                        // Leaving it would interfere with the next openDropDown() call, which
318                        // queries the natural size of the drop down.
319                        this.dropDown.menuTableNode.style.width = "";
320                }
321        },
322
323        uninitialize: function(preserveDom){
324                if(this.dropDown && !this.dropDown._destroyed){
325                        this.dropDown.destroyRecursive(preserveDom);
326                        delete this.dropDown;
327                }
328                this.inherited(arguments);
329        },
330
331        _onFocus: function(){
332                this.validate(true);    // show tooltip if second focus of required tooltip, but no selection
333                this.inherited(arguments);
334        },
335
336        _onBlur: function(){
337                Tooltip.hide(this.domNode);
338                this.inherited(arguments);
339        }
340});
341
342Select._Menu = _SelectMenu;     // for monkey patching
343
344return Select;
345});
Note: See TracBrowser for help on using the repository browser.