]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | require({cache:{ |
2 | 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}}); | |
3 | define("dijit/form/ValidationTextBox", [ | |
4 | "dojo/_base/declare", // declare | |
5 | "dojo/_base/kernel", // kernel.deprecated | |
6 | "dojo/i18n", // i18n.getLocalization | |
7 | "./TextBox", | |
8 | "../Tooltip", | |
9 | "dojo/text!./templates/ValidationTextBox.html", | |
10 | "dojo/i18n!./nls/validate" | |
11 | ], function(declare, kernel, i18n, TextBox, Tooltip, template){ | |
12 | ||
13 | // module: | |
14 | // dijit/form/ValidationTextBox | |
15 | ||
16 | ||
17 | /*===== | |
18 | var __Constraints = { | |
19 | // locale: String | |
20 | // locale used for validation, picks up value from this widget's lang attribute | |
21 | // _flags_: anything | |
22 | // various flags passed to pattern function | |
23 | }; | |
24 | =====*/ | |
25 | ||
26 | var ValidationTextBox; | |
27 | return ValidationTextBox = declare("dijit.form.ValidationTextBox", TextBox, { | |
28 | // summary: | |
29 | // Base class for textbox widgets with the ability to validate content of various types and provide user feedback. | |
30 | ||
31 | templateString: template, | |
32 | ||
33 | // required: Boolean | |
34 | // User is required to enter data into this field. | |
35 | required: false, | |
36 | ||
37 | // promptMessage: String | |
38 | // If defined, display this hint string immediately on focus to the textbox, if empty. | |
39 | // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input). | |
40 | // Think of this like a tooltip that tells the user what to do, not an error message | |
41 | // that tells the user what they've done wrong. | |
42 | // | |
43 | // Message disappears when user starts typing. | |
44 | promptMessage: "", | |
45 | ||
46 | // invalidMessage: String | |
47 | // The message to display if value is invalid. | |
48 | // The translated string value is read from the message file by default. | |
49 | // Set to "" to use the promptMessage instead. | |
50 | invalidMessage: "$_unset_$", | |
51 | ||
52 | // missingMessage: String | |
53 | // The message to display if value is empty and the field is required. | |
54 | // The translated string value is read from the message file by default. | |
55 | // Set to "" to use the invalidMessage instead. | |
56 | missingMessage: "$_unset_$", | |
57 | ||
58 | // message: String | |
59 | // Currently error/prompt message. | |
60 | // When using the default tooltip implementation, this will only be | |
61 | // displayed when the field is focused. | |
62 | message: "", | |
63 | ||
64 | // constraints: __Constraints | |
65 | // user-defined object needed to pass parameters to the validator functions | |
66 | constraints: {}, | |
67 | ||
68 | // pattern: [extension protected] String|Function(constraints) returning a string. | |
69 | // This defines the regular expression used to validate the input. | |
70 | // Do not add leading ^ or $ characters since the widget adds these. | |
71 | // A function may be used to generate a valid pattern when dependent on constraints or other runtime factors. | |
72 | // set('pattern', String|Function). | |
73 | pattern: ".*", | |
74 | ||
75 | // regExp: Deprecated [extension protected] String. Use "pattern" instead. | |
76 | regExp: "", | |
77 | ||
78 | regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){ | |
79 | // summary: | |
80 | // Deprecated. Use set('pattern', Function) instead. | |
81 | }, | |
82 | ||
83 | // state: [readonly] String | |
84 | // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error) | |
85 | state: "", | |
86 | ||
87 | // tooltipPosition: String[] | |
88 | // See description of `dijit/Tooltip.defaultPosition` for details on this parameter. | |
89 | tooltipPosition: [], | |
90 | ||
91 | _deprecateRegExp: function(attr, value){ | |
92 | if(value != ValidationTextBox.prototype[attr]){ | |
93 | kernel.deprecated("ValidationTextBox id="+this.id+", set('" + attr + "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0"); | |
94 | this.set('pattern', value); | |
95 | } | |
96 | }, | |
97 | _setRegExpGenAttr: function(/*Function*/ newFcn){ | |
98 | this._deprecateRegExp("regExpGen", newFcn); | |
99 | this.regExpGen = this._getPatternAttr; // backward compat with this.regExpGen(this.constraints) | |
100 | }, | |
101 | _setRegExpAttr: function(/*String*/ value){ | |
102 | this._deprecateRegExp("regExp", value); | |
103 | }, | |
104 | ||
105 | _setValueAttr: function(){ | |
106 | // summary: | |
107 | // Hook so set('value', ...) works. | |
108 | this.inherited(arguments); | |
109 | this.validate(this.focused); | |
110 | }, | |
111 | ||
112 | validator: function(/*anything*/ value, /*__Constraints*/ constraints){ | |
113 | // summary: | |
114 | // Overridable function used to validate the text input against the regular expression. | |
115 | // tags: | |
116 | // protected | |
117 | return (new RegExp("^(?:" + this._getPatternAttr(constraints) + ")"+(this.required?"":"?")+"$")).test(value) && | |
118 | (!this.required || !this._isEmpty(value)) && | |
119 | (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean | |
120 | }, | |
121 | ||
122 | _isValidSubset: function(){ | |
123 | // summary: | |
124 | // Returns true if the value is either already valid or could be made valid by appending characters. | |
125 | // This is used for validation while the user [may be] still typing. | |
126 | return this.textbox.value.search(this._partialre) == 0; | |
127 | }, | |
128 | ||
129 | isValid: function(/*Boolean*/ /*===== isFocused =====*/){ | |
130 | // summary: | |
131 | // Tests if value is valid. | |
132 | // Can override with your own routine in a subclass. | |
133 | // tags: | |
134 | // protected | |
135 | return this.validator(this.textbox.value, this.constraints); | |
136 | }, | |
137 | ||
138 | _isEmpty: function(value){ | |
139 | // summary: | |
140 | // Checks for whitespace | |
141 | return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean | |
142 | }, | |
143 | ||
144 | getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){ | |
145 | // summary: | |
146 | // Return an error message to show if appropriate | |
147 | // tags: | |
148 | // protected | |
149 | var invalid = this.invalidMessage == "$_unset_$" ? this.messages.invalidMessage : | |
150 | !this.invalidMessage ? this.promptMessage : this.invalidMessage; | |
151 | var missing = this.missingMessage == "$_unset_$" ? this.messages.missingMessage : | |
152 | !this.missingMessage ? invalid : this.missingMessage; | |
153 | return (this.required && this._isEmpty(this.textbox.value)) ? missing : invalid; // String | |
154 | }, | |
155 | ||
156 | getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){ | |
157 | // summary: | |
158 | // Return a hint message to show when widget is first focused | |
159 | // tags: | |
160 | // protected | |
161 | return this.promptMessage; // String | |
162 | }, | |
163 | ||
164 | _maskValidSubsetError: true, | |
165 | validate: function(/*Boolean*/ isFocused){ | |
166 | // summary: | |
167 | // Called by oninit, onblur, and onkeypress. | |
168 | // description: | |
169 | // Show missing or invalid messages if appropriate, and highlight textbox field. | |
170 | // tags: | |
171 | // protected | |
172 | var message = ""; | |
173 | var isValid = this.disabled || this.isValid(isFocused); | |
174 | if(isValid){ this._maskValidSubsetError = true; } | |
175 | var isEmpty = this._isEmpty(this.textbox.value); | |
176 | var isValidSubset = !isValid && isFocused && this._isValidSubset(); | |
177 | this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && (this._maskValidSubsetError || (isValidSubset && !this._hasBeenBlurred && isFocused))) ? "Incomplete" : "Error")); | |
178 | this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true"); | |
179 | ||
180 | if(this.state == "Error"){ | |
181 | this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus | |
182 | message = this.getErrorMessage(isFocused); | |
183 | }else if(this.state == "Incomplete"){ | |
184 | message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete | |
185 | this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused | |
186 | }else if(isEmpty){ | |
187 | message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text | |
188 | } | |
189 | this.set("message", message); | |
190 | ||
191 | return isValid; | |
192 | }, | |
193 | ||
194 | displayMessage: function(/*String*/ message){ | |
195 | // summary: | |
196 | // Overridable method to display validation errors/hints. | |
197 | // By default uses a tooltip. | |
198 | // tags: | |
199 | // extension | |
200 | if(message && this.focused){ | |
201 | Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight()); | |
202 | }else{ | |
203 | Tooltip.hide(this.domNode); | |
204 | } | |
205 | }, | |
206 | ||
207 | _refreshState: function(){ | |
208 | // Overrides TextBox._refreshState() | |
209 | if(this._created){ | |
210 | this.validate(this.focused); | |
211 | } | |
212 | this.inherited(arguments); | |
213 | }, | |
214 | ||
215 | //////////// INITIALIZATION METHODS /////////////////////////////////////// | |
216 | ||
217 | constructor: function(params /*===== , srcNodeRef =====*/){ | |
218 | // summary: | |
219 | // Create the widget. | |
220 | // params: Object|null | |
221 | // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) | |
222 | // and functions, typically callbacks like onClick. | |
223 | // The hash can contain any of the widget's properties, excluding read-only properties. | |
224 | // srcNodeRef: DOMNode|String? | |
225 | // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree. | |
226 | ||
227 | this.constraints = {}; | |
228 | this.baseClass += ' dijitValidationTextBox'; | |
229 | }, | |
230 | ||
231 | startup: function(){ | |
232 | this.inherited(arguments); | |
233 | this._refreshState(); // after all _set* methods have run | |
234 | }, | |
235 | ||
236 | _setConstraintsAttr: function(/*__Constraints*/ constraints){ | |
237 | if(!constraints.locale && this.lang){ | |
238 | constraints.locale = this.lang; | |
239 | } | |
240 | this._set("constraints", constraints); | |
241 | this._refreshState(); | |
242 | }, | |
243 | ||
244 | _setPatternAttr: function(/*String|Function*/ pattern){ | |
245 | this._set("pattern", pattern); // don't set on INPUT to avoid native HTML5 validation | |
246 | }, | |
247 | ||
248 | _getPatternAttr: function(/*__Constraints*/ constraints){ | |
249 | // summary: | |
250 | // Hook to get the current regExp and to compute the partial validation RE. | |
251 | var p = this.pattern; | |
252 | var type = (typeof p).toLowerCase(); | |
253 | if(type == "function"){ | |
254 | p = this.pattern(constraints || this.constraints); | |
255 | } | |
256 | if(p != this._lastRegExp){ | |
257 | var partialre = ""; | |
258 | this._lastRegExp = p; | |
259 | // parse the regexp and produce a new regexp that matches valid subsets | |
260 | // if the regexp is .* then there's no use in matching subsets since everything is valid | |
261 | if(p != ".*"){ | |
262 | p.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g, | |
263 | function(re){ | |
264 | switch(re.charAt(0)){ | |
265 | case '{': | |
266 | case '+': | |
267 | case '?': | |
268 | case '*': | |
269 | case '^': | |
270 | case '$': | |
271 | case '|': | |
272 | case '(': | |
273 | partialre += re; | |
274 | break; | |
275 | case ")": | |
276 | partialre += "|$)"; | |
277 | break; | |
278 | default: | |
279 | partialre += "(?:"+re+"|$)"; | |
280 | break; | |
281 | } | |
282 | }); | |
283 | } | |
284 | try{ // this is needed for now since the above regexp parsing needs more test verification | |
285 | "".search(partialre); | |
286 | }catch(e){ // should never be here unless the original RE is bad or the parsing is bad | |
287 | partialre = this.pattern; | |
288 | console.warn('RegExp error in ' + this.declaredClass + ': ' + this.pattern); | |
289 | } // should never be here unless the original RE is bad or the parsing is bad | |
290 | this._partialre = "^(?:" + partialre + ")$"; | |
291 | } | |
292 | return p; | |
293 | }, | |
294 | ||
295 | postMixInProperties: function(){ | |
296 | this.inherited(arguments); | |
297 | this.messages = i18n.getLocalization("dijit.form", "validate", this.lang); | |
298 | this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints | |
299 | }, | |
300 | ||
301 | _setDisabledAttr: function(/*Boolean*/ value){ | |
302 | this.inherited(arguments); // call FormValueWidget._setDisabledAttr() | |
303 | this._refreshState(); | |
304 | }, | |
305 | ||
306 | _setRequiredAttr: function(/*Boolean*/ value){ | |
307 | this._set("required", value); | |
308 | this.focusNode.setAttribute("aria-required", value); | |
309 | this._refreshState(); | |
310 | }, | |
311 | ||
312 | _setMessageAttr: function(/*String*/ message){ | |
313 | this._set("message", message); | |
314 | this.displayMessage(message); | |
315 | }, | |
316 | ||
317 | reset:function(){ | |
318 | // Overrides dijit/form/TextBox.reset() by also | |
319 | // hiding errors about partial matches | |
320 | this._maskValidSubsetError = true; | |
321 | this.inherited(arguments); | |
322 | }, | |
323 | ||
324 | _onBlur: function(){ | |
325 | // the message still exists but for back-compat, and to erase the tooltip | |
326 | // (if the message is being displayed as a tooltip), call displayMessage('') | |
327 | this.displayMessage(''); | |
328 | ||
329 | this.inherited(arguments); | |
330 | } | |
331 | }); | |
332 | }); |