1 include(bbq.gui.GUIWidget); 2 include(bbq.util.Log); 3 4 bbq.gui.form.FormField = new Class.create(bbq.gui.GUIWidget, /** @lends bbq.gui.form.FormField.prototype */ { 5 _behaviours: null, 6 _preTransformValidators: null, 7 _postTransformValidators: null, 8 _transformer: null, 9 10 /** 11 * Dispatches the following notifications: 12 * 13 * onError 14 * 15 * @constructs 16 * @extends bbq.gui.GUIWidget 17 * @param {Object} options 18 * @param {Object} [options.value] An initial value 19 * @param {Function} [options.onChange] A callback method invoked when the value of this field changes 20 * @param {Object} [options.valueTransformer] 21 * @param {Function} [options.valueTransformer.transform] Takes one argument and transforms it to a different value e.g. 1 to true 22 * @param {String} [options.name] The name to be set on the input field 23 */ 24 initialize: function($super, options) { 25 try { 26 $super(options); 27 28 this._behaviours = []; 29 this._preTransformValidators = []; 30 this._postTransformValidators = []; 31 32 this.setRootNode("input"); 33 this.addClass("FormField"); 34 35 if(this.options.onChange) { 36 this.registerListener("onChange", this.options.onChange); 37 } 38 39 if(this.options.valueTransformer) { 40 this.setValueTransformer(this.options.valueTransformer); 41 } 42 43 if(this.options.name) { 44 this.setAttribute("name", this.options.name); 45 } 46 47 this.registerListener("onFocus", function() { 48 FocusWatcher.setKeypressCallbackObject(this); 49 }.bind(this)); 50 51 this.registerListener("onBlur", function() { 52 FocusWatcher.setKeypressCallbackObject(null); 53 }.bind(this)); 54 } catch(e) { 55 Log.error("Error constructing FormField", e); 56 } 57 }, 58 59 /** 60 * @inheritDoc 61 */ 62 setRootNode: function($super, rootNode) { 63 $super(rootNode); 64 65 this.getRootNode().onfocus = this.notifyListeners.bind(this, "onFocus"); 66 this.getRootNode().onblur = this.notifyListeners.bind(this, "onBlur"); 67 this.getRootNode().onchange = this.notifyListeners.bind(this, "onChange"); 68 69 if(!Object.isUndefined(this.options.value)) { 70 this._setRawValue(this.options.value); 71 } 72 }, 73 74 _getRawValue: function() { 75 return this.getRootNode().value; 76 }, 77 78 _setRawValue: function(value) { 79 if(this._transformer && this._transformer.deTransform) { 80 value = this._transformer.deTransform(value); 81 } 82 83 this.getRootNode().value = value; 84 }, 85 86 /** 87 * Returns the value contained within this field. The value will be 88 * validated - if the value is found to be invalid an exception 89 * will be thrown. 90 * 91 * @throws {Error} The error has two fields {String} error for language translations and {bbq.gui.form.FormField} field which is the field which caused the error. 92 * @returns {Object} 93 */ 94 getValue: function() { 95 var value = this._getRawValue(); 96 97 return this._validateAndTransform(value); 98 }, 99 100 /** 101 * Returns the name of this field. 102 * 103 * @returns {String} 104 */ 105 getName: function() { 106 return this.options.name; 107 }, 108 109 /** 110 * Returns the current value of the field without first validating it (which could 111 * cause an exception to be thrown). 112 * 113 * @returns {Object} 114 */ 115 getUnvalidatedValue: function() { 116 var value = this._getRawValue(); 117 118 return this._transform(value); 119 }, 120 121 _validateAndTransform: function(value) { 122 this.removeClass("FormField_error"); 123 124 // run pre-transform validators 125 this._preValidate(value); 126 127 // transform the value if necessary 128 value = this._transform(value); 129 130 // run post transform validators 131 this._postValidate(value); 132 133 // return our value 134 return value; 135 }, 136 137 _transform: function(value) { 138 // if we have a value transformer, transform the value 139 if (this._transformer) { 140 value = this._transformer.transform(value); 141 } 142 143 return value; 144 }, 145 146 _preValidate: function(value) { 147 this._preTransformValidators.each(function(validator) { 148 var result = validator.validate(value); 149 150 if (result) { 151 var error = {error: result, field: this}; 152 153 this.addClass("FormField_error"); 154 this.notifyListeners("onError", error); 155 156 throw error; 157 } 158 }.bind(this)); 159 }, 160 161 _postValidate: function(value) { 162 this._postTransformValidators.each(function(validator) { 163 var result = validator.validate(value); 164 165 if (result) { 166 var error = {error: result, field: this}; 167 168 this.addClass("FormField_error"); 169 this.notifyListeners("onError", error); 170 171 throw error; 172 } 173 }.bind(this)); 174 }, 175 176 /** 177 * Sets the value of the field 178 * @param value 179 */ 180 setValue: function(value) { 181 this._setRawValue(value); 182 183 this._validateAndTransform(value); 184 }, 185 186 /** 187 * Adds a validator to ensure certain criteria are met. 188 * 189 * @param validator 190 * @see bbq.gui.form.validator.EmailValidator 191 * @see bbq.gui.form.validator.MustEqualFieldValidator 192 * @see bbq.gui.form.validator.NotNullValidatorValidator 193 * @see bbq.gui.form.validator.URLValidator 194 */ 195 addValidator: function(validator) { 196 if(!validator) { 197 Log.error("Invalid validator!"); 198 199 return; 200 } 201 202 if(!validator.validate) { 203 Log.error("Validator should implement a validate method"); 204 205 return; 206 } 207 208 if (validator.isPostTransformValidator && validator.isPostTransformValidator()) { 209 this._postTransformValidators.push(validator); 210 } else { 211 this._preTransformValidators.push(validator); 212 } 213 }, 214 215 /** 216 * Adds a behaviour modifier 217 * 218 * @param behaviour 219 * @see bbq.gui.form.behaviour.PlaceholderTextBehaviour 220 * @see bbq.gui.form.behaviour.ValidateOnBlurBehaviour 221 */ 222 addBehaviour: function(behaviour) { 223 if (!behaviour) { 224 Log.error("Invalid behaviour!"); 225 } 226 227 if (!behaviour.setField) { 228 Log.error("Behaviour should implement a setField method"); 229 230 return; 231 } 232 233 behaviour.setField(this); 234 235 this._behaviours.push(behaviour); 236 }, 237 238 /** 239 * Sets the value transformer which constrains the value of the field to certain values. 240 * 241 * @param transformer 242 * @see bbq.gui.form.transformer.BooleanValueTransformer 243 * @see bbq.gui.form.transformer.StrinkTokeniserTransformer 244 */ 245 setValueTransformer: function(transformer) { 246 if (!transformer) { 247 Log.error("Invalid transformer!"); 248 } 249 250 if (!transformer.transform) { 251 Log.error("Transformer should implement a transform method"); 252 253 return; 254 } 255 256 this._transformer = transformer; 257 }, 258 259 /** 260 * Removes the CSS class denoting that this field has focus 261 */ 262 loseFocus: function() { 263 this.removeClass("FormField_focused"); 264 }, 265 266 /** 267 * Adds the CSS class denoting that this field has focus 268 */ 269 acceptFocus: function() { 270 this.addClass("FormField_focused"); 271 } 272 }); 273