1 /**
  2  * @author Gillis Haasnoot <gillis.haasnoot@gmail.com>
  3  * @package Banana.Controls
  4  * @summary Base validator class
  5  */
  6 
  7 goog.provide('Banana.Controls.Decorators.Validator');
  8 
  9 /** @namespace Banana.Controls.Decorators.Validator  */
 10 namespace('Banana.Controls.Decorators').Validator = Banana.Controls.Decorators.Decorator.extend(
 11 /** @lends Banana.Controls.Decorators.Validator.prototype */
 12 {
 13 	/**
 14 	 * Creates base control to validate a control
 15 	 * Any Banana.Controls.DataControl can be validated. If you need to change the eventtype where
 16 	 * the validator is triggered on, use setValidateOnEventType. By default the validator
 17 	 * will be triggered when the validated control fires a dataChanged or focusout event
 18 	 * 
 19 	 * @param {Banana.Controls.DataControl} controlToValidate
 20 	 * @constructs
 21 	 * @extends Banana.Controls.Decorators.Decorator
 22 	 */
 23 	init : function(controlToValidate)
 24 	{
 25 		this._super(controlToValidate);
 26 
 27 		this.showIndicator = true; //little dot in front visible
 28 		
 29 		this.validationGroup = 'default';
 30 
 31 		var iconPlaceHolder = new Banana.Controls.Panel().addCssClass('BValidatorIconPlaceHolder').setId('validator icon placeholder');;
 32 		this.addControl(iconPlaceHolder)
 33 
 34 		controlToValidate.addCssClass('BValidatorDecorated');
 35 		this.controlToValidate = controlToValidate;
 36 		this.addControl(controlToValidate);
 37 
 38 		this.infoPlaceHolder = new Banana.Controls.Panel().addCssClass('BValidatorInfoPlaceHolder');
 39 		this.addControl(this.infoPlaceHolder)
 40 
 41 		this.star = new Banana.Controls.Panel().setId('validator star');;
 42 		this.star.addCssClass('BValidatorStar');
 43 		if (this.getInfoText())
 44 		{
 45 			this.star.setAttribute('title', this.getInfoText());
 46 		}
 47 		iconPlaceHolder.addControl(this.star);
 48 
 49 		this.arrow = new Banana.Controls.Panel().setId('validator arrow');
 50 		this.arrow.addCssClass('BValidatorArrow');
 51 		this.infoPlaceHolder.addControl(this.arrow);
 52 
 53 		this.info = new Banana.Controls.Panel().addCssClass('BValidatorInfo').setId('validator info placeholder');;
 54 		this.infoPlaceHolder.addControl(this.info);
 55 
 56 		Banana.Util.ValidationManager.addValidator(this);
 57 		
 58 		return this;
 59 	},
 60 
 61 	/**
 62 	 * @override
 63 	 */
 64 	createComponents : function()
 65 	{
 66 		this.info.setVisible(false);
 67 		this.arrow.setVisible(false);
 68 		
 69 		this.createLabelText();
 70 
 71 		this.star.setVisible(this.showIndicator);
 72 
 73 		//if user explicity gave type of event to validate on
 74 		if (this.validateOnEventType)
 75 		{
 76 			this.controls[1].bind(this.validateOnEventType,this.getProxy(this.isValid));	
 77 		}
 78 		//otherwise just use default
 79 		else
 80 		{
 81 			this.controls[1].bind('dataChanged',this.getProxy(this.isValid));
 82 			//TODO this cannot count for all type of controls
 83 			this.controls[1].bind('focusout',this.getProxy(function(){}))
 84 		}
 85 	},
 86 	
 87 	/**
 88 	 * When control is removed, we also remove it from the validation manager.
 89 	 * TODO: is this really needed here?
 90 	 * @override
 91 	 */
 92 	unload : function()
 93 	{
 94 		this._super();
 95 		Banana.Util.ValidationManager.removeValidator(this);
 96 	},
 97 	
 98 	/**
 99 	 * @ignore
100 	 */
101 	createLabelText : function()
102 	{
103 		if (!this.labelText)
104 		{
105 			this.labelText = new Banana.Controls.Label();
106 			this.labelText.addCssClass('BValidatorLabel');
107 			this.info.addControl(this.labelText);
108 		}
109 		
110 		this.labelText.setData(this.getInfoText());
111 	},
112 	
113 	/**
114 	 * shows indicator
115 	 */
116 	showIndicators : function()
117 	{
118 		this.info.setVisible(true);
119 		this.arrow.setVisible(true);		
120 	},
121 	
122 	/**
123 	 * hides indicator
124 	 */
125 	hideIndicators : function()
126 	{
127 		this.info.setVisible(false);
128 		this.arrow.setVisible(false);		
129 	},
130 	
131 	/**
132 	 * @param {boolean} value if true when show a little dot in front of the control
133 	 */
134 	setShowIndicator: function(value)
135 	{
136 		this.showIndicator = value;
137 		return this;
138 	},
139 		
140 	/**
141 	 * default = dataChanged
142 	 * all dom events are supported, depending on the control type
143 	 * @param {String} event type to validate on
144 	 * @return {this}
145 	 */
146 	setValidateOnEventType : function(event)
147 	{
148 		this.validateOnEventType = event;
149 		return this;
150 	},
151 
152 	/**
153 	 * @return {Banana.Controls.DataControl}
154 	 */
155 	getValidatedControl : function()
156 	{
157 		return this.controlToValidate;
158 	},
159 
160 	/**
161 	 * @override
162 	 * we need to know the original border color
163 	 */
164 	updateDisplay : function()
165 	{
166 		this.orgBorder = this.controls[1].getStyleProperty('border');
167 	},
168 	
169 	/**
170 	 * Manual way to invalidate the control
171 	 * @return {this}
172 	 */
173 	markInvalid : function(customText)
174 	{
175 		this.markedInvalid = true;
176 		
177 		if (customText)
178 		{
179 			this.oldInfoText = this.infoText;
180 			this.infoText = customText;
181 		}
182 		
183 		this.isValid();
184 		return this;
185 	},
186 	
187 	/**
188 	 * Removes manual mark as invalid flag
189 	 */
190 	removeMarkInvalid : function()
191 	{
192 		this.markedInvalid = undefined;
193 		
194 		if (this.oldInfoText)
195 		{
196 			this.infoText = this.oldInfoText;
197 		}
198 		
199 		this.isValid()
200 	},
201 
202 	/**
203 	 * @return bool true when validator is considered as valid
204 	 */
205 	isValid : function()
206 	{
207 		if (!this.controls)
208 		{
209 			//prevent situation that this method is called while child controls are being removed
210 			return false;
211 		}
212 		var c = this.controls[1];
213 		
214 		//disabled controls no need to validate
215 		if (!c.enabled)
216 		{
217 			return true;
218 		}
219 		
220 		var result;
221 		
222 		//when user manually marked this validator invalid we wont look
223 		//if this control is really invalid. We follow users wish
224 		if (this.markedInvalid)
225 		{
226 			result = false;
227 		}
228 		else
229 		{
230 			result = this.validateData(c.getData(true));
231 		}
232 		
233 		if (result==false)
234 		{
235 			this.createLabelText();
236 			c.addCssClass('BValidatorControlInvalidated');
237 			c.removeCssClass('BValidatorControlValidated');
238 			this.showIndicators();
239 			return false;
240 		}
241 		else if (result==true)
242 		{
243 			c.removeCssClass('BValidatorControlInvalidated');
244 			c.addCssClass('BValidatorControlValidated');
245 			this.hideIndicators();
246 			return true;
247 		}
248 	},
249 
250 	/**
251 	 * abstract override this method to implement your own validate data 
252 	 * @param mixed 
253 	 */
254 	validateData : function(data)
255 	{
256 		return true;
257 	},
258 
259 	/**
260 	 * @return {String}
261 	 */
262 	getInfoText : function()
263 	{
264 		return this.infoText || '';
265 	},
266 
267 	/**
268 	 * @param {String} text shown when control is invalid
269 	 * @return {this}
270 	 */
271 	setInfoText : function(text)
272 	{
273 		this.infoText = text;
274 		return this;
275 	},
276 
277 	/**
278 	 * Use this method to put validators in a group
279 	 * @param {String} group
280 	 * @return {this}
281 	 */
282 	setValidationGroup : function(group)
283 	{
284 		this.validationGroup = group;
285 		return this;
286 	},
287 	
288 	/**
289 	 * @return {String}
290 	 */
291 	getValidationGroup : function()
292 	{
293 		return this.validationGroup;
294 	},
295 
296 	/**
297 	 * @return {mixed}
298 	 */
299 	getData : function()
300 	{
301 		return this.controls[1].getData();
302 	},
303 
304 	/**
305 	 * @override
306 	 * @param {mixed} data
307 	 * @param {boolean} ignoreEvent
308 	 * @param {boolean} ignoreDom
309 	 * @return {this}
310 	 */
311 	setData : function(data,ignoreEvent,ignoreDom)
312 	{
313 		this.controls[1].setData(data,ignoreEvent,ignoreDom);
314 		return this;
315 	},
316 	
317 	/**
318 	 * @override
319 	 */
320 	getHtml : function()
321 	{
322 		var html = [];
323 
324 		var childs = this.getControls();
325 
326 		for (var i =0, len = childs.length; i < len; i++)
327 		{
328 			if (childs[i] instanceof Banana.Control)
329 			{
330 				html.push(childs[i].getHtml());
331 			}
332 
333 			else if (typeof(childs[i]) == 'string')
334 			{
335 				html.push(childs[i]);
336 			}
337 		}
338 
339 		return html.join('');
340 	}
341 });