1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 sc_require('system/string'); 9 10 SC.VALIDATE_OK = YES; 11 SC.VALIDATE_NO_CHANGE = NO; 12 13 /** 14 @class 15 16 Validators provide a way for you to implement simple form field validation 17 and transformation. To use a validator, simply name the validator in the 18 "validate" attribute in your text field. For example, if you want to 19 validate a field using the PhoneNumberValidator use this: 20 21 <input value="1234567890" validate="phone-number" /> 22 23 Validators get notified at three points. You can implement one or all 24 of these methods to support validation. All of the validate methods except 25 for validateKeypress behave the same way. You are passed a form, field, 26 and possibly the oldValue. You are expected to return Validator.OK or 27 an error string. Inside this method you typically do one of all of the 28 following: 29 30 1. You can simply validate the field value and return OK or an error str 31 2. You can modify the field value (for example, you could format the 32 string to match some predefined format). 33 3. If you need to roundtrip the server first to perform validation, you can 34 return Validator.OK, then save the form and field info until after the 35 roundtrip. On return, if there is a problem, first verify the field 36 value has not changed and then call form.errorFor(field,str) ; 37 38 @extends SC.Object 39 @since SproutCore 1.0 40 */ 41 SC.Validator = SC.Object.extend( 42 /** @scope SC.Validator.prototype */ { 43 44 // .......................................... 45 // OBJECT VALUE CONVERSION 46 // 47 // The following methods are used to convert the string value of a field 48 // to and from an object value. The default implementations return 49 // the string, but you can override this to provide specific behaviors. 50 // For example, you might add or remove a dollar sign or convert the 51 // value to a number. 52 53 /** 54 Returns the value to set in the field for the passed object value. 55 56 The form and view to be set MAY (but will not always) be passed also. You 57 should override this method to help convert an input object into a value 58 that can be displayed by the field. For example, you might convert a 59 date to a property formatted string or a number to a properly formatted 60 value. 61 62 @param {Object} object The object to transform 63 @param {SC.FormView} form The form this field belongs to. (optional) 64 @param {SC.View} view The view the value is required for. 65 @returns {Object} a value (usually a string) suitable for display 66 */ 67 fieldValueForObject: function(object, form, view) { return object; }, 68 69 /** 70 Returns the object value for the passed string. 71 72 The form and view MAY (but wil not always) be passed also. You should 73 override this method to convert a field value, such as string, into an 74 object value suitable for consumption by the rest of the app. For example 75 you may convert a string into a date or a number. 76 77 @param {String} value the field value. (Usually a String). 78 @param {SC.FormView} form The form this field belongs to. (optional) 79 @param {SC.View} view The view this value was pulled from. 80 @returns {Object} an object suitable for consumption by the app. 81 */ 82 objectForFieldValue: function(value, form, view) { return value; }, 83 84 // .......................................... 85 // VALIDATION PRIMITIVES 86 // 87 88 /** 89 Validate the field value. 90 91 You can implement standard behavior for your validator by using the validate() 92 and validateError() methods. validate() should return NO if the field is not 93 valid, YES otherwise. If you return NO from this method, then the validateError() 94 method will be called so you can generate an error object describing the specific problem. 95 96 @param {SC.FormView} form the form this view belongs to 97 @param {SC.View} field the field to validate. Responds to fieldValue. 98 @returns {Boolean} YES if field is valid. 99 */ 100 validate: function(form, field) { return true; }, 101 102 /** 103 Returns an error object if the field is invalid. 104 105 This is the other standard validator method that can be used to implement basic validation. 106 Return an error object explaining why the field is not valid. It will only be called if 107 validate() returned NO. 108 109 The default implementation of this method returns a generic error message with the loc 110 string "Invalid.Generate({fieldValue})". You can simply define this loc string in 111 strings.js if you prefer or you can override this method to provide a more specific error message. 112 113 @param {SC.FormView} form the form this view belongs to 114 @param {SC.View} field the field to validate. Responds to fieldValue. 115 @returns {SC.Error} an error object 116 */ 117 validateError: function(form, field) { 118 return SC.$error( 119 SC.String.loc("Invalid.General(%@)", field.get('fieldValue')), 120 field.get('fieldKey')) ; 121 }, 122 123 // .......................................... 124 // VALIDATION API 125 // 126 127 /** 128 Invoked just before the user ends editing of the field. 129 130 This is a primitive validation method. You can implement the two higher-level 131 methods (validate() and validateError()) if you prefer. 132 133 The default implementation calls your validate() method and then validateError() 134 if validate() returns NO. This method should return SC.VALIDATE_OK if validation 135 succeeded or an error object if it fails. 136 137 @param {SC.FormView} form the form for the field 138 @param {SC.View} field the field to validate 139 @param {Object} oldValue: the value of the field before the change 140 141 @returns SC.VALIDATE_OK or an error object. 142 143 */ 144 validateChange: function(form, field, oldValue) { 145 return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field); 146 }, 147 148 /** 149 Invoked just before the form is submitted. 150 151 This method gives your validators one last chance to perform validation 152 on the form as a whole. The default version does the same thing as the 153 validateChange() method. 154 155 @param {SC.FormView} form the form for the field 156 @param {SC.View} field the field to validate 157 158 @returns SC.VALIDATE_OK or an error object. 159 160 */ 161 validateSubmit: function(form, field) { 162 return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field); 163 }, 164 165 /** 166 Invoked 1ms after the user types a key (if a change is allowed). 167 168 You can use this validate the new partial string and return an error if 169 needed. The default will validate a partial only if there was already an 170 error. This allows the user to try to get it right before you bug them. 171 172 Unlike the other methods, you should return SC.VALIDATE_NO_CHANGE if you 173 did not actually validate the partial string. If you return 174 SC.VALIDATE_OK then any showing errors will be hidden. 175 176 @param {SC.FormView} form the form for the field 177 @param {SC.View} field the field to validate 178 179 @returns SC.VALIDATE_OK, SC.VALIDATE_NO_CHANGE or an error object. 180 */ 181 validatePartial: function(form, field) { 182 if (!field.get('isValid')) { 183 return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field); 184 } else return SC.VALIDATE_NO_CHANGE ; 185 }, 186 187 /** 188 Invoked when the user presses a key. 189 190 This method is used to restrict the letters and numbers the user is 191 allowed to enter. You should not use this method to perform full 192 validation on the field. Instead use validatePartial(). 193 194 @param {SC.FormView} form the form for the field 195 @param {SC.View} field the field to validate 196 @param {String} char the characters being added 197 198 @returns {Boolean} YES if allowed, NO otherwise 199 */ 200 validateKeyDown: function(form, field,charStr) { return true; }, 201 202 // ..................................... 203 // OTHER METHODS 204 205 /** 206 Called on all validators when they are attached to a field. 207 208 You can use this to do any setup that you need. The default does nothing. 209 210 @param {SC.FormView} form the form for the field 211 @param {SC.View} field the field to validate 212 */ 213 attachTo: function(form,field) { }, 214 215 /** 216 Called on a validator just before it is removed from a field. You can 217 tear down any setup you did for the attachTo() method. 218 219 @param {SC.FormView} form the form for the field 220 @param {SC.View} field the field to validate 221 */ 222 detachFrom: function(form, field) {} 223 224 }) ; 225 226 SC.Validator.mixin(/** @scope SC.Validator */ { 227 228 /** 229 Return value when validation was performed and value is OK. 230 */ 231 OK: true, 232 233 /** 234 Return value when validation was not performed. 235 */ 236 NO_CHANGE: false, 237 238 /** 239 Invoked by a field whenever a validator is attached to the field. 240 241 The passed validatorKey can be a validator instance, a validator class 242 or a string naming a validator. To make your validator 243 visible, you should name your validator under the SC.Validator base. 244 for example SC.Validator.Number would get used for the 'number' 245 validator key. 246 247 This understands validatorKey strings in the following format: 248 249 * 'key' or 'multiple_words' will find validators Key and MultipleWords 250 251 * if you want to share a single validator among multiple fields (for 252 example to validate that two passwords are the same) set a name inside 253 brackets. i.e. 'password[pwd]'. 254 255 @param {SC.FormView} form the form for the field 256 @param {SC.View} field the field to validate 257 @param {Object} validatorKey the key to validate 258 259 @returns {SC.Validator} validator instance or null 260 */ 261 findFor: function(form,field, validatorKey) { 262 263 // Convert the validator into a validator instance. 264 var validator ; 265 if (!validatorKey) return ; // nothing to do... 266 267 if (validatorKey instanceof SC.Validator) { 268 validator = validatorKey ; 269 } else if (validatorKey.isClass) { 270 validator = validatorKey.create() ; 271 272 } else if (SC.typeOf(validatorKey) === SC.T_STRING) { 273 274 // extract optional key name 275 var name = null ; 276 var m = validatorKey.match(/^(.+)\[(.*)\]/) ; 277 if (m) { 278 validatorKey = m[1] ; name = m[2]; 279 } 280 281 // convert the validatorKey name into a class. 282 validatorKey = SC.String.classify(validatorKey); 283 var validatorClass = SC.Validator[validatorKey] ; 284 if (SC.none(validatorClass)) { 285 throw new Error("validator %@ not found for %@".fmt(validatorKey, field)); 286 } else if (name) { 287 288 // if a key was also passed, then find the validator in the list of 289 // validators for the form. Otherwise, just create a new instance. 290 if (!form) { 291 throw new Error("named validator (%@) could not be found for field %@ because the field does not belong to a form".fmt(name,field)); 292 } 293 294 if (!form._validatorHash) form._validatorHash = {} ; 295 validator = (name) ? form._validatorHash[name] : null ; 296 if (!validator) validator = validatorClass.create() ; 297 if (name) form._validatorHash[name] = validator ; 298 } else validator = validatorClass.create() ; 299 } 300 301 return validator ; 302 }, 303 304 /** 305 Convenience class method to call the fieldValueForObject() instance 306 method you define in your subclass. 307 */ 308 fieldValueForObject: function(object, form, field) { 309 if (this.prototype && this.prototype.fieldValueForObject) { 310 return this.prototype.fieldValueForObject(object,form,field) ; 311 } 312 else return null ; 313 }, 314 315 /** 316 Convenience class method to call the objectForFieldValue() instance 317 method you define in your subclass. 318 */ 319 objectForFieldValue: function(value, form, field) { 320 if (this.prototype && this.prototype.objectForFieldValue) { 321 return this.prototype.objectForFieldValue(value,form,field) ; 322 } 323 else return null ; 324 } 325 326 }); 327