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 /** 9 @namespace 10 11 Views that include the Validatable mixin can be used with validators to 12 ensure their values are valid. 13 14 */ 15 SC.Validatable = { 16 17 /** @private */ 18 initMixin: function() { 19 this._validatable_validatorDidChange() ; 20 }, 21 22 /** 23 The validator for this field. 24 25 Set to a validator class or instance. If this points to a class, it will 26 be instantiated when the validator is first used. 27 28 @type SC.Validator 29 @default null 30 */ 31 validator: null, 32 33 /** 34 This property must return the human readable name you want used when 35 describing an error condition. For example, if set this property to 36 "Your Email", then the returned error string might be something like 37 "Your Email is not valid". 38 39 You can return a loc string here if you like. It will be localized when 40 it is placed into the error string. 41 42 @type String 43 @default null 44 */ 45 errorLabel: null, 46 47 /** 48 YES if the receiver is currently valid. 49 50 This property watches the value property by default. You can override 51 this property if you want to use some other method to calculate the 52 current valid state. 53 54 @field 55 @type Boolean 56 @default YES 57 */ 58 isValid: function() { 59 return SC.typeOf(this.get('value')) !== SC.T_ERROR; 60 }.property('value'), 61 62 /** 63 The form that the view belongs to. May be null if the view does not 64 belong to a form. This property is usually set automatically by an 65 owner form view. 66 67 @type SC.View 68 @default null 69 */ 70 ownerForm: null, 71 72 /** 73 Attempts to validate the receiver. 74 75 Runs the validator and returns SC.VALIDATE_OK, SC.VALIDATE_NO_CHANGE, 76 or an error object. If no validator is installed, this method will 77 always return SC.VALIDATE_OK. 78 79 @param {Boolean} partialChange YES if this is a partial edit. 80 @returns {String} SC.VALIDATE_OK, error, or SC.VALIDATE_NO_CHANGE 81 */ 82 performValidate: function(partialChange) { 83 var ret = SC.VALIDATE_OK ; 84 85 if (this._validator) { 86 var form = this.get('ownerForm') ; 87 if (partialChange) { 88 ret = this._validator.validatePartial(form,this) ; 89 90 // if the partial returned NO_CHANGE, then check to see if the 91 // field is valid anyway. If it is not valid, then don't update the 92 // value. This way the user can have partially constructed values 93 // without the validator trying to convert it to an object. 94 if ((ret == SC.VALIDATE_NO_CHANGE) && (this._validator.validateChange(form, this) == SC.VALIDATE_OK)) { 95 ret = SC.VALIDATE_OK; 96 } 97 } else ret = this._validator.validateChange(form, this) ; 98 } 99 return ret ; 100 }, 101 102 /** 103 Runs validateSubmit. You should use this in your implementation of 104 validateSubmit. If no validator is installed, this always returns 105 SC.VALIDATE_OK 106 107 @returns {String} 108 */ 109 performValidateSubmit: function() { 110 return this._validator ? this._validator.validateSubmit(this.get('ownerForm'), this) : SC.VALIDATE_OK; 111 }, 112 113 /** 114 Runs a keydown validation. Returns YES if the keydown should be 115 allowed, NO otherwise. If no validator is defined, always returns YES. 116 117 @param {String} charStr the key string 118 @returns {Boolean} 119 */ 120 performValidateKeyDown: function(evt) { 121 // ignore anything with ctrl or meta key press 122 var charStr = evt.getCharString(); 123 if (!charStr) return YES ; 124 return this._validator ? this._validator.validateKeyDown(this.get('ownerForm'), this, charStr) : YES; 125 }, 126 127 /** 128 Returns the validator object, if one has been created. 129 130 @field 131 @type SC.Validator 132 */ 133 validatorObject: function() { 134 return this._validator; 135 }.property(), 136 137 /** 138 Invoked by the owner form just before submission. Override with your 139 own method to commit any final changes after you perform validation. 140 141 The default implementation simply calls performValidateSubmit() and 142 returns that value. 143 144 @type Boolean 145 */ 146 validateSubmit: function() { return this.performValidateSubmit(); }, 147 148 /** 149 Convert the field value string into an object. 150 151 This method will call the validators objectForFieldValue if it exists. 152 153 @param {Object} fieldValue the raw value from the field. 154 @param {Boolean} partialChange 155 @returns {Object} 156 */ 157 objectForFieldValue: function(fieldValue, partialChange) { 158 return this._validator ? this._validator.objectForFieldValue(fieldValue, this.get('ownerForm'), this) : fieldValue ; 159 }, 160 161 /** 162 Convert the object into a field value. 163 164 This method will call the validator's fieldValueForObject if it exists. 165 166 @param object {Object} the objec to convert 167 @returns {Object} 168 */ 169 fieldValueForObject: function(object) { 170 return this._validator ? this._validator.fieldValueForObject(object, this.get('ownerForm'), this) : object ; 171 }, 172 173 /** @private */ 174 _validatable_displayObserver: function() { 175 this.displayDidChange(); 176 }.observes('isValid'), 177 178 /** @private */ 179 renderMixin: function(context) { 180 context.setClass('invalid', !this.get('isValid')); 181 }, 182 183 /** @private 184 Invoked whenever the attached validator changes. 185 */ 186 _validatable_validatorDidChange: function() { 187 var form = this.get('ownerForm') ; 188 var val = SC.Validator.findFor(form, this, this.get('validator')) ; 189 if (val != this._validator) { 190 this.propertyWillChange('validatorObject'); 191 if (this._validator) this._validator.detachFrom(form, this) ; 192 this._validator = val; 193 if (this._validator) this._validator.attachTo(form, this) ; 194 this.propertyDidChange('validatorObject'); 195 } 196 }.observes('validator', 'ownerForm') 197 198 }; 199