source: Dev/branches/rest-dojo-ui/client/dijit/form/ValidationTextBox.js @ 256

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