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