]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/form/_FormWidget.js
remove call-by-reference to comply with php 5.4
[tt-rss.git] / lib / dijit / form / _FormWidget.js
CommitLineData
2f01fe57 1/*
81bea17a 2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
2f01fe57
AD
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5*/
6
7
81bea17a
AD
8if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9dojo._hasResource["dijit.form._FormWidget"] = true;
2f01fe57
AD
10dojo.provide("dijit.form._FormWidget");
11dojo.require("dojo.window");
12dojo.require("dijit._Widget");
13dojo.require("dijit._Templated");
14dojo.require("dijit._CssStateMixin");
81bea17a
AD
15
16
17dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
18 {
19 // summary:
20 // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
21 // which can be children of a <form> node or a `dijit.form.Form` widget.
22 //
23 // description:
24 // Represents a single HTML element.
25 // All these widgets should have these attributes just like native HTML input elements.
26 // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
27 //
28 // They also share some common methods.
29
30 // name: [const] String
31 // Name used when submitting form; same as "name" attribute or plain HTML elements
32 name: "",
33
34 // alt: String
35 // Corresponds to the native HTML <input> element's attribute.
36 alt: "",
37
38 // value: String
39 // Corresponds to the native HTML <input> element's attribute.
40 value: "",
41
42 // type: String
43 // Corresponds to the native HTML <input> element's attribute.
44 type: "text",
45
46 // tabIndex: Integer
47 // Order fields are traversed when user hits the tab key
48 tabIndex: "0",
49
50 // disabled: Boolean
51 // Should this widget respond to user input?
52 // In markup, this is specified as "disabled='disabled'", or just "disabled".
53 disabled: false,
54
55 // intermediateChanges: Boolean
56 // Fires onChange for each value change or only on demand
57 intermediateChanges: false,
58
59 // scrollOnFocus: Boolean
60 // On focus, should this widget scroll into view?
61 scrollOnFocus: true,
62
63 // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
64 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
65 value: "focusNode",
66 id: "focusNode",
67 tabIndex: "focusNode",
68 alt: "focusNode",
69 title: "focusNode"
70 }),
71
72 postMixInProperties: function(){
73 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
74 // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
75 // Regarding escaping, see heading "Attribute values" in
76 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
77 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
78 this.inherited(arguments);
79 },
80
81 postCreate: function(){
82 this.inherited(arguments);
83 this.connect(this.domNode, "onmousedown", "_onMouseDown");
84 },
85
86 _setDisabledAttr: function(/*Boolean*/ value){
87 this._set("disabled", value);
88 dojo.attr(this.focusNode, 'disabled', value);
89 if(this.valueNode){
90 dojo.attr(this.valueNode, 'disabled', value);
91 }
92 dijit.setWaiState(this.focusNode, "disabled", value);
93
94 if(value){
95 // reset these, because after the domNode is disabled, we can no longer receive
96 // mouse related events, see #4200
97 this._set("hovering", false);
98 this._set("active", false);
99
100 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
101 var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
102 dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
103 var node = this[attachPointName];
104 // complex code because tabIndex=-1 on a <div> doesn't work on FF
105 if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug
106 node.setAttribute('tabIndex', "-1");
107 }else{
108 node.removeAttribute('tabIndex');
109 }
110 }, this);
111 }else{
112 if(this.tabIndex != ""){
113 this.focusNode.setAttribute('tabIndex', this.tabIndex);
114 }
115 }
116 },
117
118 setDisabled: function(/*Boolean*/ disabled){
119 // summary:
120 // Deprecated. Use set('disabled', ...) instead.
121 dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
122 this.set('disabled', disabled);
123 },
124
125 _onFocus: function(e){
126 if(this.scrollOnFocus){
127 dojo.window.scrollIntoView(this.domNode);
128 }
129 this.inherited(arguments);
130 },
131
132 isFocusable: function(){
133 // summary:
134 // Tells if this widget is focusable or not. Used internally by dijit.
135 // tags:
136 // protected
137 return !this.disabled && this.focusNode && (dojo.style(this.domNode, "display") != "none");
138 },
139
140 focus: function(){
141 // summary:
142 // Put focus on this widget
143 if(!this.disabled){
144 dijit.focus(this.focusNode);
145 }
146 },
147
148 compare: function(/*anything*/ val1, /*anything*/ val2){
149 // summary:
150 // Compare 2 values (as returned by get('value') for this widget).
151 // tags:
152 // protected
153 if(typeof val1 == "number" && typeof val2 == "number"){
154 return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
155 }else if(val1 > val2){
156 return 1;
157 }else if(val1 < val2){
158 return -1;
159 }else{
160 return 0;
161 }
162 },
163
164 onChange: function(newValue){
165 // summary:
166 // Callback when this widget's value is changed.
167 // tags:
168 // callback
169 },
170
171 // _onChangeActive: [private] Boolean
172 // Indicates that changes to the value should call onChange() callback.
173 // This is false during widget initialization, to avoid calling onChange()
174 // when the initial value is set.
175 _onChangeActive: false,
176
177 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
178 // summary:
179 // Called when the value of the widget is set. Calls onChange() if appropriate
180 // newValue:
181 // the new value
182 // priorityChange:
183 // For a slider, for example, dragging the slider is priorityChange==false,
184 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
185 // onChange is only called form priorityChange=true events.
186 // tags:
187 // private
188 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
189 // this block executes not for a change, but during initialization,
190 // and is used to store away the original value (or for ToggleButton, the original checked state)
191 this._resetValue = this._lastValueReported = newValue;
192 }
193 this._pendingOnChange = this._pendingOnChange
194 || (typeof newValue != typeof this._lastValueReported)
195 || (this.compare(newValue, this._lastValueReported) != 0);
196 if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
197 this._lastValueReported = newValue;
198 this._pendingOnChange = false;
199 if(this._onChangeActive){
200 if(this._onChangeHandle){
201 clearTimeout(this._onChangeHandle);
202 }
203 // setTimout allows hidden value processing to run and
204 // also the onChange handler can safely adjust focus, etc
205 this._onChangeHandle = setTimeout(dojo.hitch(this,
206 function(){
207 this._onChangeHandle = null;
208 this.onChange(newValue);
209 }), 0); // try to collapse multiple onChange's fired faster than can be processed
210 }
211 }
212 },
213
214 create: function(){
215 // Overrides _Widget.create()
216 this.inherited(arguments);
217 this._onChangeActive = true;
218 },
219
220 destroy: function(){
221 if(this._onChangeHandle){ // destroy called before last onChange has fired
222 clearTimeout(this._onChangeHandle);
223 this.onChange(this._lastValueReported);
224 }
225 this.inherited(arguments);
226 },
227
228 setValue: function(/*String*/ value){
229 // summary:
230 // Deprecated. Use set('value', ...) instead.
231 dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
232 this.set('value', value);
233 },
234
235 getValue: function(){
236 // summary:
237 // Deprecated. Use get('value') instead.
238 dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
239 return this.get('value');
240 },
241
242 _onMouseDown: function(e){
243 // If user clicks on the button, even if the mouse is released outside of it,
244 // this button should get focus (to mimics native browser buttons).
245 // This is also needed on chrome because otherwise buttons won't get focus at all,
246 // which leads to bizarre focus restore on Dialog close etc.
247 if(!e.ctrlKey && dojo.mouseButtons.isLeft(e) && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
248 // Set a global event to handle mouseup, so it fires properly
249 // even if the cursor leaves this.domNode before the mouse up event.
250 var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
251 if (this.isFocusable()) {
252 this.focus();
253 }
254 this.disconnect(mouseUpConnector);
255 });
256 }
257 }
2f01fe57 258});
81bea17a
AD
259
260dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
261{
262 // summary:
263 // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
264 // description:
265 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
266 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
267 // works as expected.
268
269 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
270 // directly in the template as read by the parser in order to function. IE is known to specifically
271 // require the 'name' attribute at element creation time. See #8484, #8660.
272 // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
273 // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
274 // Seems like we really want value removed from attributeMap altogether
275 // (although there's no easy way to do that now)
276
277 // readOnly: Boolean
278 // Should this widget respond to user input?
279 // In markup, this is specified as "readOnly".
280 // Similar to disabled except readOnly form values are submitted.
281 readOnly: false,
282
283 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
284 value: "",
285 readOnly: "focusNode"
286 }),
287
288 _setReadOnlyAttr: function(/*Boolean*/ value){
289 dojo.attr(this.focusNode, 'readOnly', value);
290 dijit.setWaiState(this.focusNode, "readonly", value);
291 this._set("readOnly", value);
292 },
293
294 postCreate: function(){
295 this.inherited(arguments);
296
297 if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ // IE won't stop the event with keypress
298 this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
299 }
300 // Update our reset value if it hasn't yet been set (because this.set()
301 // is only called when there *is* a value)
302 if(this._resetValue === undefined){
303 this._lastValueReported = this._resetValue = this.value;
304 }
305 },
306
307 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
308 // summary:
309 // Hook so set('value', value) works.
310 // description:
311 // Sets the value of the widget.
312 // If the value has changed, then fire onChange event, unless priorityChange
313 // is specified as null (or false?)
314 this._handleOnChange(newValue, priorityChange);
315 },
316
317 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
318 // summary:
319 // Called when the value of the widget has changed. Saves the new value in this.value,
320 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
321 this._set("value", newValue);
322 this.inherited(arguments);
323 },
324
325 undo: function(){
326 // summary:
327 // Restore the value to the last value passed to onChange
328 this._setValueAttr(this._lastValueReported, false);
329 },
330
331 reset: function(){
332 // summary:
333 // Reset the widget's value to what it was at initialization time
334 this._hasBeenBlurred = false;
335 this._setValueAttr(this._resetValue, true);
336 },
337
338 _onKeyDown: function(e){
339 if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
340 var te;
341 if(dojo.isIE){
342 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
343 te = document.createEventObject();
344 te.keyCode = dojo.keys.ESCAPE;
345 te.shiftKey = e.shiftKey;
346 e.srcElement.fireEvent('onkeypress', te);
347 }
348 }
349 },
350
351 _layoutHackIE7: function(){
352 // summary:
353 // Work around table sizing bugs on IE7 by forcing redraw
354
355 if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
356 var domNode = this.domNode;
357 var parent = domNode.parentNode;
358 var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
359 var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
360 var _this = this;
361 while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
362 (function ping(){
363 var disconnectHandle = _this.connect(parent, "onscroll",
364 function(e){
365 _this.disconnect(disconnectHandle); // only call once
366 pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
367 setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
368 }
369 );
370 })();
371 parent = parent.parentNode;
372 }
373 }
374 }
2f01fe57 375});
81bea17a 376
2f01fe57 377}