]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/form/Select.js.uncompressed.js
Merge remote-tracking branch 'upstream/master'
[tt-rss.git] / lib / dijit / form / Select.js.uncompressed.js
1 require({cache:{
2 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t/></div\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer\"\n\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n"}});
3 define("dijit/form/Select", [
4 "dojo/_base/array", // array.forEach
5 "dojo/_base/declare", // declare
6 "dojo/dom-attr", // domAttr.set
7 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
8 "dojo/dom-geometry", // domGeometry.setMarginBox
9 "dojo/_base/event", // event.stop
10 "dojo/i18n", // i18n.getLocalization
11 "dojo/_base/lang", // lang.hitch
12 "dojo/sniff", // has("ie")
13 "./_FormSelectWidget",
14 "../_HasDropDown",
15 "../Menu",
16 "../MenuItem",
17 "../MenuSeparator",
18 "../Tooltip",
19 "dojo/text!./templates/Select.html",
20 "dojo/i18n!./nls/validate"
21 ], function(array, declare, domAttr, domClass, domGeometry, event, i18n, lang, has,
22 _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
23
24 // module:
25 // dijit/form/Select
26
27
28 var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
29 // summary:
30 // An internally-used menu for dropdown that allows us a vertical scrollbar
31
32 // Override Menu.autoFocus setting so that opening a Select highlights the current value.
33 autoFocus: true,
34
35 buildRendering: function(){
36 // summary:
37 // Stub in our own changes, so that our domNode is not a table
38 // otherwise, we won't respond correctly to heights/overflows
39 this.inherited(arguments);
40 var o = (this.menuTableNode = this.domNode);
41 var n = (this.domNode = this.ownerDocument.createElement("div"));
42 n.style.cssText = "overflow-x: hidden; overflow-y: scroll";
43 if(o.parentNode){
44 o.parentNode.replaceChild(n, o);
45 }
46 domClass.remove(o, "dijitMenuTable");
47 n.className = o.className + " dijitSelectMenu";
48 o.className = "dijitReset dijitMenuTable";
49 o.setAttribute("role", "listbox");
50 n.setAttribute("role", "presentation");
51 n.appendChild(o);
52 },
53
54 postCreate: function(){
55 // summary:
56 // stop mousemove from selecting text on IE to be consistent with other browsers
57
58 this.inherited(arguments);
59
60 this.connect(this.domNode, "onselectstart", event.stop);
61 },
62
63
64 focus: function(){
65 // summary:
66 // Overridden so that the previously selected value will be focused instead of only the first item
67 var found = false,
68 val = this.parentWidget.value;
69 if(lang.isArray(val)){
70 val = val[val.length-1];
71 }
72 if(val){ // if focus selected
73 array.forEach(this.parentWidget._getChildren(), function(child){
74 if(child.option && (val === child.option.value)){ // find menu item widget with this value
75 found = true;
76 this.focusChild(child, false); // focus previous selection
77 }
78 }, this);
79 }
80 if(!found){
81 this.inherited(arguments); // focus first item by default
82 }
83 },
84
85 resize: function(/*Object*/ mb){
86 // summary:
87 // Overridden so that we are able to handle resizing our
88 // internal widget. Note that this is not a "full" resize
89 // implementation - it only works correctly if you pass it a
90 // marginBox.
91 //
92 // mb: Object
93 // The margin box to set this dropdown to.
94 if(mb){
95 domGeometry.setMarginBox(this.domNode, mb);
96 if("w" in mb){
97 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
98 // 100% is safer than a pixel value because there may be a scroll bar with
99 // browser/OS specific width.
100 this.menuTableNode.style.width = "100%";
101 }
102 }
103 }
104 });
105
106 var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
107 // summary:
108 // This is a "styleable" select box - it is basically a DropDownButton which
109 // can take a `<select>` as its input.
110
111 baseClass: "dijitSelect dijitValidationTextBox",
112
113 templateString: template,
114
115 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
116
117 // required: Boolean
118 // Can be true or false, default is false.
119 required: false,
120
121 // state: [readonly] String
122 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
123 state: "",
124
125 // message: String
126 // Currently displayed error/prompt message
127 message: "",
128
129 // tooltipPosition: String[]
130 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
131 tooltipPosition: [],
132
133 // emptyLabel: string
134 // What to display in an "empty" dropdown
135 emptyLabel: "&#160;", // &nbsp;
136
137 // _isLoaded: Boolean
138 // Whether or not we have been loaded
139 _isLoaded: false,
140
141 // _childrenLoaded: Boolean
142 // Whether or not our children have been loaded
143 _childrenLoaded: false,
144
145 _fillContent: function(){
146 // summary:
147 // Set the value to be the first, or the selected index
148 this.inherited(arguments);
149 // set value from selected option
150 if(this.options.length && !this.value && this.srcNodeRef){
151 var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
152 this.value = this.options[si >= 0 ? si : 0].value;
153 }
154 // Create the dropDown widget
155 this.dropDown = new _SelectMenu({ id: this.id + "_menu", parentWidget: this });
156 domClass.add(this.dropDown.domNode, this.baseClass.replace(/\s+|$/g, "Menu "));
157 },
158
159 _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){
160 // summary:
161 // For the given option, return the menu item that should be
162 // used to display it. This can be overridden as needed
163 if(!option.value && !option.label){
164 // We are a separator (no label set for it)
165 return new MenuSeparator({ownerDocument: this.ownerDocument});
166 }else{
167 // Just a regular menu option
168 var click = lang.hitch(this, "_setValueAttr", option);
169 var item = new MenuItem({
170 option: option,
171 label: option.label || this.emptyLabel,
172 onClick: click,
173 ownerDocument: this.ownerDocument,
174 dir: this.dir,
175 disabled: option.disabled || false
176 });
177 item.focusNode.setAttribute("role", "option");
178 return item;
179 }
180 },
181
182 _addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option){
183 // summary:
184 // For the given option, add an option to our dropdown.
185 // If the option doesn't have a value, then a separator is added
186 // in that place.
187 if(this.dropDown){
188 this.dropDown.addChild(this._getMenuItemForOption(option));
189 }
190 },
191
192 _getChildren: function(){
193 if(!this.dropDown){
194 return [];
195 }
196 return this.dropDown.getChildren();
197 },
198
199 _loadChildren: function(/*Boolean*/ loadMenuItems){
200 // summary:
201 // Resets the menu and the length attribute of the button - and
202 // ensures that the label is appropriately set.
203 // loadMenuItems: Boolean
204 // actually loads the child menu items - we only do this when we are
205 // populating for showing the dropdown.
206
207 if(loadMenuItems === true){
208 // this.inherited destroys this.dropDown's child widgets (MenuItems).
209 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
210 // issues later in _setSelected). (see #10296)
211 if(this.dropDown){
212 delete this.dropDown.focusedChild;
213 }
214 if(this.options.length){
215 this.inherited(arguments);
216 }else{
217 // Drop down menu is blank but add one blank entry just so something appears on the screen
218 // to let users know that they are no choices (mimicing native select behavior)
219 array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
220 var item = new MenuItem({
221 ownerDocument: this.ownerDocument,
222 label: this.emptyLabel
223 });
224 this.dropDown.addChild(item);
225 }
226 }else{
227 this._updateSelection();
228 }
229
230 this._isLoaded = false;
231 this._childrenLoaded = true;
232
233 if(!this._loadingStore){
234 // Don't call this if we are loading - since we will handle it later
235 this._setValueAttr(this.value, false);
236 }
237 },
238
239 _refreshState: function(){
240 if(this._started){
241 this.validate(this.focused);
242 }
243 },
244
245 startup: function(){
246 this.inherited(arguments);
247 this._refreshState(); // after all _set* methods have run
248 },
249
250 _setValueAttr: function(value){
251 this.inherited(arguments);
252 domAttr.set(this.valueNode, "value", this.get("value"));
253 this._refreshState(); // to update this.state
254 },
255
256 _setDisabledAttr: function(/*Boolean*/ value){
257 this.inherited(arguments);
258 this._refreshState(); // to update this.state
259 },
260
261 _setRequiredAttr: function(/*Boolean*/ value){
262 this._set("required", value);
263 this.focusNode.setAttribute("aria-required", value);
264 this._refreshState(); // to update this.state
265 },
266
267 _setOptionsAttr: function(/*Array*/ options){
268 this._isLoaded = false;
269 this._set('options', options);
270 },
271
272 _setDisplay: function(/*String*/ newDisplay){
273 // summary:
274 // sets the display for the given value (or values)
275 var lbl = newDisplay || this.emptyLabel;
276 this.containerNode.innerHTML = '<span role="option" class="dijitReset dijitInline ' + this.baseClass.replace(/\s+|$/g, "Label ")+'">' + lbl + '</span>';
277 },
278
279 validate: function(/*Boolean*/ isFocused){
280 // summary:
281 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
282 // description:
283 // Show missing or invalid messages if appropriate, and highlight textbox field.
284 // Used when a select is initially set to no value and the user is required to
285 // set the value.
286
287 var isValid = this.disabled || this.isValid(isFocused);
288 this._set("state", isValid ? "" : (this._hasBeenBlurred ? "Error" : "Incomplete"));
289 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
290 var message = isValid ? "" : this._missingMsg;
291 if(message && this.focused && this._hasBeenBlurred){
292 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
293 }else{
294 Tooltip.hide(this.domNode);
295 }
296 this._set("message", message);
297 return isValid;
298 },
299
300 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
301 // summary:
302 // Whether or not this is a valid value. The only way a Select
303 // can be invalid is when it's required but nothing is selected.
304 return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
305 },
306
307 reset: function(){
308 // summary:
309 // Overridden so that the state will be cleared.
310 this.inherited(arguments);
311 Tooltip.hide(this.domNode);
312 this._refreshState(); // to update this.state
313 },
314
315 postMixInProperties: function(){
316 // summary:
317 // set the missing message
318 this.inherited(arguments);
319 this._missingMsg = i18n.getLocalization("dijit.form", "validate", this.lang).missingMessage;
320 },
321
322 postCreate: function(){
323 // summary:
324 // stop mousemove from selecting text on IE to be consistent with other browsers
325
326 this.inherited(arguments);
327
328 this.connect(this.domNode, "onselectstart", event.stop);
329 this.domNode.setAttribute("aria-expanded", "false");
330
331 if(has("ie") < 9){
332 // IE INPUT tag fontFamily has to be set directly using STYLE
333 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
334 this.defer(function(){
335 try{
336 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
337 if(s){
338 var ff = s.fontFamily;
339 if(ff){
340 var inputs = this.domNode.getElementsByTagName("INPUT");
341 if(inputs){
342 for(var i=0; i < inputs.length; i++){
343 inputs[i].style.fontFamily = ff;
344 }
345 }
346 }
347 }
348 }catch(e){/*when used in a Dialog, and this is called before the dialog is
349 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
350 });
351 }
352 },
353
354 _setStyleAttr: function(/*String||Object*/ value){
355 this.inherited(arguments);
356 domClass.toggle(this.domNode, this.baseClass.replace(/\s+|$/g, "FixedWidth "), !!this.domNode.style.width);
357 },
358
359 isLoaded: function(){
360 return this._isLoaded;
361 },
362
363 loadDropDown: function(/*Function*/ loadCallback){
364 // summary:
365 // populates the menu
366 this._loadChildren(true);
367 this._isLoaded = true;
368 loadCallback();
369 },
370
371 closeDropDown: function(){
372 // overriding _HasDropDown.closeDropDown()
373 this.inherited(arguments);
374
375 if(this.dropDown && this.dropDown.menuTableNode){
376 // Erase possible width: 100% setting from _SelectMenu.resize().
377 // Leaving it would interfere with the next openDropDown() call, which
378 // queries the natural size of the drop down.
379 this.dropDown.menuTableNode.style.width = "";
380 }
381 },
382
383 destroy: function(preserveDom){
384 if(this.dropDown && !this.dropDown._destroyed){
385 this.dropDown.destroyRecursive(preserveDom);
386 delete this.dropDown;
387 }
388 this.inherited(arguments);
389 },
390
391 _onFocus: function(){
392 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
393 this.inherited(arguments);
394 },
395
396 _onBlur: function(){
397 Tooltip.hide(this.domNode);
398 this.inherited(arguments);
399 this.validate(false);
400 }
401 });
402
403 Select._Menu = _SelectMenu; // for monkey patching
404
405 return Select;
406 });