Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
/**
2
 * Prado client-side javascript validation fascade.
3
 *
4
 * <p>There are 4 basic classes: {@link Prado.Validation},
5
 * {@link Prado.ValidationManager}, {@link Prado.WebUI.TValidationSummary}
6
 * and {@link Prado.WebUI.TBaseValidator},
7
 * that interact together to perform validation.
8
 * The {@link Prado.Validation} class co-ordinates together the
9
 * validation scheme and is responsible for maintaining references
10
 * to ValidationManagers.</p>
11
 *
12
 * <p>The {@link Prado.ValidationManager} class is responsible for
13
 * maintaining refereneces
14
 * to individual validators, validation summaries and their associated
15
 * groupings.</p>
16
 *
17
 * <p>The {@link Prado.WebUI.TValidationSummary} takes care of displaying
18
 * the validator error messages
19
 * as html output or an alert output.</p>
20
 *
21
 * <p>The {@link Prado.WebUI.TBaseValidator} is the base class for all
22
 * validators and contains
23
 * methods to interact with the actual inputs, data type conversion.</p>
24
 *
25
 * <p>An instance of {@link Prado.ValidationManager} must be instantiated first for a
26
 * particular form before instantiating validators and summaries.</p>
27
 *
28
 * <p>Usage example: adding a required field to a text box input with
29
 * ID "input1" in a form with ID "form1".</p>
30
 * <pre>
31
 * &lt;script type="text/javascript" src="../prado.js"&gt;&lt;/script&gt;
32
 * &lt;script type="text/javascript" src="../validator.js"&gt;&lt;/script&gt;
33
 * &lt;form id="form1" action="..."&gt;
34
 * &lt;div&gt;
35
 * 	&lt;input type="text" id="input1" /&gt;
36
 *  &lt;span id="validator1" style="display:none; color:red"&gt;*&lt;/span&gt;
37
 *  &lt;input type="submit text="submit" /&gt;
38
 * &lt;script type="text/javascript"&gt;
39
 * new Prado.ValidationManager({FormID : 'form1'});
40
 * var options =
41
 * {
42
 *		ID :				'validator1',
43
 *		FormID :			'form1',
44
 *		ErrorMessage :		'*',
45
 *		ControlToValidate : 'input1'
46
 * }
47
 * new Prado.WebUI.TRequiredFieldValidator(options);
48
 * new Prado.WebUI.TValidationSummary({ID:'summary1',FormID:'form1'});
49
 *
50
 * //watch the form onsubmit event, check validators, stop if not valid.
51
 * Event.observe("form1", "submit" function(ev)
52
 * {
53
 * 	 if(Prado.WebUI.Validation.isValid("form1") == false)
54
 * 		Event.stop(ev);
55
 * });
56
 * &lt;/script&gt;
57
 * &lt;/div&gt;
58
 * &lt;/form&gt;
59
 * </pre>
60
 *
61
 * @module validation
62
 */
63
 
64
Prado.Validation =  Class.create();
65
 
66
/**
67
 * Global Validation Object.
68
 *
69
 * <p>To validate the inputs of a particular form, call
70
 * <code>{@link Prado.Validation.validate}(formID, groupID)</code>
71
 * where <tt>formID</tt> is the HTML form ID, and the optional
72
 * <tt>groupID</tt> if present will only validate the validators
73
 * in a particular group.</p>
74
 * <p>Use <code>{@link Prado.Validation.validateControl}(controlClientID)</code>
75
 * to trigger validation for a single control.</p>
76
 *
77
 * @object {static} Prado.Validation
78
 */
79
Object.extend(Prado.Validation,
80
{
81
	/**
82
	 * Hash of registered validation managers
83
	 * @var managers
84
	 */
85
	managers : {},
86
 
87
	/**
88
	 * Validate the validators (those that <strong>DO NOT</strong>
89
	 * belong to a particular group) in the form specified by the
90
	 * <tt>formID</tt> parameter. If <tt>groupID</tt> is specified
91
	 * only validators belonging to that group will be validated.
92
	 * @function {boolean} ?
93
	 * @param {string} formID - ID of the form to validate
94
	 * @param {string} groupID - ID of the ValidationGroup to validate.
95
	 * @param {element} invoker - DOM element that calls for validation
96
	 * @returns true if validation succeeded
97
	 */
98
	validate : function(formID, groupID, invoker)
99
	{
100
		formID = formID || this.getForm();
101
		if(this.managers[formID])
102
		{
103
			return this.managers[formID].validate(groupID, invoker);
104
		}
105
		else
106
		{
107
			throw new Error("Form '"+formID+"' is not registered with Prado.Validation");
108
		}
109
	},
110
 
111
	/**
112
	 * Validate all validators of a specific control.
113
	 * @function {boolean} ?
114
	 * @param {string} id - ID of DOM element to validate
115
	 * @return true if all validators are valid or no validators present, false otherwise.
116
	 */
117
    validateControl : function(id)
118
    {
119
        var formId=this.getForm();
120
 
121
		if (this.managers[formId])
122
        {
123
            return this.managers[formId].validateControl(id);
124
        } else {
125
			throw new Error("A validation manager needs to be created first.");
126
        }
127
    },
128
 
129
	/**
130
	 * Return first registered form
131
	 * @function {string} ?
132
	 * @returns ID of first form.
133
	 */
134
	getForm : function()
135
	{
136
		var keys = $H(this.managers).keys();
137
		return keys[0];
138
	},
139
 
140
	/**
141
	 * Check if the validators are valid for a particular form (and group).
142
	 * The validators states will not be changed.
143
	 * The <tt>validate</tt> function should be called first.
144
	 * @function {boolean} ?
145
	 * @param {string} formID - ID of the form to validate
146
	 * @param {string} groupID - ID of the ValiationGroup to validate.
147
	 * @return true if form is valid
148
	 */
149
	isValid : function(formID, groupID)
150
	{
151
		formID = formID || this.getForm();
152
		if(this.managers[formID])
153
			return this.managers[formID].isValid(groupID);
154
		return true;
155
	},
156
 
157
	/**
158
	 * Reset the validators for a given group.
159
	 * The group is searched in the first registered form.
160
	 * @function ?
161
	 * @param {string} groupID - ID of the ValidationGroup to reset.
162
	 */
163
	reset : function(groupID)
164
	{
165
		var formID = this.getForm();
166
		if(this.managers[formID])
167
			this.managers[formID].reset(groupID);
168
	},
169
 
170
	/**
171
	 * Add a new validator to a particular form.
172
	 * @function {ValidationManager} ?
173
	 * @param {string} formID - ID of the form that the validator belongs to.
174
	 * @param {TBaseValidator} validator - Validator object
175
	 * @return ValidationManager for the form
176
	 */
177
	addValidator : function(formID, validator)
178
	{
179
		if(this.managers[formID])
180
			this.managers[formID].addValidator(validator);
181
		else
182
			throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
183
		return this.managers[formID];
184
	},
185
 
186
	/**
187
	 * Add a new validation summary.
188
	 * @function {ValidationManager} ?
189
	 * @param {string} formID - ID of the form that the validation summary belongs to.
190
	 * @param {TValidationSummary} validator - TValidationSummary object
191
	 * @return ValidationManager for the form
192
	 */
193
	addSummary : function(formID, validator)
194
	{
195
		if(this.managers[formID])
196
			this.managers[formID].addSummary(validator);
197
		else
198
			throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
199
		return this.managers[formID];
200
	},
201
 
202
	setErrorMessage : function(validatorID, message)
203
	{
204
		$H(Prado.Validation.managers).each(function(manager)
205
		{
206
			manager[1].validators.each(function(validator)
207
			{
208
				if(validator.options.ID == validatorID)
209
				{
210
					validator.options.ErrorMessage = message;
211
					$(validatorID).innerHTML = message;
212
				}
213
			});
214
		});
215
	}
216
});
217
 
218
/**
219
 * Manages validators for a particular HTML form.
220
 *
221
 * <p>The manager contains references to all the validators
222
 * summaries, and their groupings for a particular form.
223
 * Generally, {@link Prado.Validation} methods should be called rather
224
 * than calling directly the ValidationManager.</p>
225
 *
226
 * @class Prado.ValidationManager
227
 */
228
Prado.ValidationManager = Class.create();
229
Prado.ValidationManager.prototype =
230
{
231
	/**
232
	 * Hash of registered validators by control's clientID
233
	 * @var controls
234
	 */
235
    controls: {},
236
 
237
	/**
238
	 * Initialize ValidationManager.
239
	 * @constructor {protected} ?
240
	 * @param {object} options - Options for initialization
241
	 * @... {string} FormID - ID of form of this manager
242
	 */
243
	initialize : function(options)
244
	{
245
		if(!Prado.Validation.managers[options.FormID])
246
		{
247
			/**
248
			 * List of validators
249
			 * @var {TBaseValidator[]} validators
250
			 */
251
			this.validators = [];
252
			/**
253
			 * List of validation summaries
254
			 * @var {TValidationSummary[]} summaries
255
			 */
256
			this.summaries = [];
257
			/**
258
			 * List of ValidationGroups
259
			 * @var {string[]} groups
260
			 */
261
			this.groups = [];
262
			/**
263
			 * Options of this ValidationManager
264
			 * @var {object} options
265
			 */
266
			this.options = {};
267
 
268
			this.options = options;
269
 
270
			Prado.Validation.managers[options.FormID] = this;
271
		}
272
		else
273
		{
274
			var manager = Prado.Validation.managers[options.FormID];
275
			this.validators = manager.validators;
276
			this.summaries = manager.summaries;
277
			this.groups = manager.groups;
278
			this.options = manager.options;
279
		}
280
	},
281
 
282
	/**
283
	 * Reset all validators in the given group.
284
	 * If group is null, validators without a group are used.
285
	 * @function ?
286
	 * @param {string} group - ID of ValidationGroup
287
	 */
288
	reset : function(group)
289
	{
290
		this.validatorPartition(group)[0].invoke('reset');
291
		this.updateSummary(group, true);
292
	},
293
 
294
	/**
295
	 * Validate the validators managed by this validation manager.
296
	 * If group is set, only validate validators in that group.
297
	 * @function {boolean} ?
298
	 * @param {optional string} group - ID of ValidationGroup
299
	 * @param {element} source - DOM element that calls for validation
300
	 * @return true if all validators are valid, false otherwise.
301
	 */
302
	validate : function(group, source)
303
	{
304
		var partition = this.validatorPartition(group);
305
		var valid = partition[0].invoke('validate', source).all();
306
		this.focusOnError(partition[0]);
307
		partition[1].invoke('hide');
308
		this.updateSummary(group, true);
309
		return valid;
310
	},
311
 
312
	/**
313
	 * Perform validation for all validators of a single control.
314
	 * @function {boolean} ?
315
	 * @param {string} id - ID of DOM element to validate
316
	 * @return true if all validators are valid or no validators present, false otherwise.
317
	 */
318
    validateControl : function (id)
319
    {
320
        return this.controls[id] ? this.controls[id].invoke('validate',null).all() : true;
321
    },
322
 
323
	/**
324
	 * Focus on the first validator that is invalid and options.FocusOnError is true.
325
	 * @function ?
326
	 * @param {TBaseValidator[]} validators - Array of validator objects
327
	 */
328
	focusOnError : function(validators)
329
	{
330
		for(var i = 0; i < validators.length; i++)
331
		{
332
			if(!validators[i].isValid && validators[i].options.FocusOnError)
333
				return Prado.Element.focus(validators[i].options.FocusElementID);
334
		}
335
	},
336
 
337
	/**
338
	 * Get all validators in a group and all other validators.
339
	 * Returns an array with two arrays of validators. The first
340
	 * array contains all validators in the group if group is given,
341
	 * otherwhise all validators without a group. The second array
342
	 * contains all other validators.
343
	 * @function {[ TBaseValidator[] , TBaseValidator[] ]} ?
344
	 * @param {optional string} group - ID of ValidationGroup
345
	 * @return Array with two arrays of validators.
346
	 */
347
	validatorPartition : function(group)
348
	{
349
		return group ? this.validatorsInGroup(group) : this.validatorsWithoutGroup();
350
	},
351
 
352
	/**
353
	 * Get all validators in a group.
354
	 * Returns an array with two arrays of validators. The first
355
	 * array contains all validators in the group. The second array
356
	 * contains all other validators.
357
	 * @function {[ TBaseValidator[] , TBaseValidator[] ]} ?
358
	 * @param {optional string} groupID - ID of ValidationGroup
359
	 * @return Array with two arrays of validators.
360
	 */
361
	validatorsInGroup : function(groupID)
362
	{
363
		if(this.groups.include(groupID))
364
		{
365
			return this.validators.partition(function(val)
366
			{
367
				return val.group == groupID;
368
			});
369
		}
370
		else
371
			return [[],[]];
372
	},
373
 
374
	/**
375
	 * Get all validators without a group.
376
	 * Returns an array with two arrays of validators. The first
377
	 * array contains all validators without a group. The second
378
	 * array contains all other validators.
379
	 * @function {[ TBaseValidator[] , TBaseValidator[] ]} ?
380
	 * @return Array with two arrays of validators: Array[0] has all
381
	 * validators without a group, Array[1] all other validators.
382
	 */
383
	validatorsWithoutGroup : function()
384
	{
385
		return this.validators.partition(function(val)
386
		{
387
			return !val.group;
388
		});
389
	},
390
 
391
	/**
392
	 * Get the state of validators.
393
	 * If group is set, only validators in that group are checked.
394
	 * Otherwhise only validators without a group are checked.
395
	 * @function {booelan} ?
396
	 * @param {optional string} group - ID of ValidationGroup
397
	 * @return true if all validators (in a group, if supplied) are valid.
398
	 */
399
	isValid : function(group)
400
	{
401
		return this.validatorPartition(group)[0].pluck('isValid').all();
402
	},
403
 
404
	/**
405
	 * Add a validator to this manager.
406
	 * @function ?
407
	 * @param {TBaseValidator} validator - Validator object
408
	 */
409
	addValidator : function(validator)
410
	{
411
		// Remove previously registered validator with same ID
412
        // to prevent stale validators created by AJAX updates
413
        this.removeValidator(validator);
414
 
415
		this.validators.push(validator);
416
		if(validator.group && !this.groups.include(validator.group))
417
			this.groups.push(validator.group);
418
 
419
        if (typeof this.controls[validator.control.id] === 'undefined')
420
            this.controls[validator.control.id] = Array();
421
        this.controls[validator.control.id].push(validator);
422
	},
423
 
424
	/**
425
	 * Add a validation summary.
426
	 * @function ?
427
	 * @param {TValidationSummary} summary - Validation summary.
428
	 */
429
	addSummary : function(summary)
430
	{
431
		this.summaries.push(summary);
432
	},
433
 
434
	/**
435
	 * Remove a validator from this manager
436
	 * @function ?
437
	 * @param {TBaseValidator} validator - Validator object
438
	 */
439
    removeValidator : function(validator)
440
    {
441
		this.validators = this.validators.reject(function(v)
442
		{
443
			return (v.options.ID==validator.options.ID);
444
		});
445
        if (this.controls[validator.control.id])
446
            this.controls[validator.control.id].reject( function(v)
447
            {
448
                return (v.options.ID==validator.options.ID)
449
            });
450
    },
451
 
452
	/**
453
	 * Gets validators with errors.
454
	 * If group is set, only validators in that group are returned.
455
	 * Otherwhise only validators without a group are returned.
456
	 * @function {TBaseValidator[]} ?
457
	 * @param {optional string} group - ID of ValidationGroup
458
	 * @return array list of validators with error.
459
	 */
460
	getValidatorsWithError : function(group)
461
	{
462
		return this.validatorPartition(group)[0].findAll(function(validator)
463
		{
464
			return !validator.isValid;
465
		});
466
	},
467
 
468
	/**
469
	 * Update the summary of a particular group.
470
	 * If group is set, only the summary for validators in that
471
	 * group is updated. Otherwhise only the summary for validators
472
	 * without a group is updated.
473
	 * @function ?
474
	 * @param {optional string} group - ID of ValidationGroup
475
	 * @param {boolean} refresh - Wether the summary should be refreshed
476
	 */
477
	updateSummary : function(group, refresh)
478
	{
479
		var validators = this.getValidatorsWithError(group);
480
		this.summaries.each(function(summary)
481
		{
482
			var inGroup = group && summary.group == group;
483
			var noGroup = !group && !summary.group;
484
			if(inGroup || noGroup)
485
				summary.updateSummary(validators, refresh);
486
			else
487
				summary.hideSummary(true);
488
		});
489
	}
490
};
491
 
492
/**
493
 * TValidationSummary displays a summary of validation errors.
494
 *
495
 * <p>The summary is displayed inline on a Web page,
496
 * in a message box, or both. By default, a validation summary will collect
497
 * <tt>ErrorMessage</tt> of all failed validators on the page. If
498
 * <tt>ValidationGroup</tt> is not empty, only those validators who belong
499
 * to the group will show their error messages in the summary.</p>
500
 *
501
 * <p>The summary can be displayed as a list, as a bulleted list, or as a single
502
 * paragraph based on the <tt>DisplayMode</tt> option.
503
 * The messages shown can be prefixed with <tt>HeaderText</tt>.</p>
504
 *
505
 * <p>The summary can be displayed on the Web page and in a message box by setting
506
 * the <tt>ShowSummary</tt> and <tt>ShowMessageBox</tt>
507
 * options, respectively.</p>
508
 *
509
 * @class Prado.WebUI.TValidationSummary
510
 */
511
Prado.WebUI.TValidationSummary = Class.create();
512
Prado.WebUI.TValidationSummary.prototype =
513
{
514
	/**
515
	 * Initialize TValidationSummary.
516
	 * @constructor {protected} ?
517
	 * @param {object} options - Options for initialization
518
	 * @... {string} ID - ID of validation summary element
519
	 * @... {string} FormID - ID of form of this manager
520
	 * @... {optional string} ValidationGroup - ID of ValidationGroup.
521
	 * @... {optional boolean} ShowMessageBox - true to show the summary in an alert box.
522
	 * @... {optional boolean} ShowSummary - true to show the inline summary.
523
	 * @... {optional string} HeaderText - Summary header text
524
	 * @... {optional string} DisplayMode - Summary display style, 'BulletList', 'List', 'SingleParagraph'
525
	 * @... {optional boolean} Refresh - true to update the summary upon validator state change.
526
	 * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'.
527
	 * @... {optional boolean} ScrollToSummary - true to scroll to the validation summary upon refresh.
528
	 * @... {optional function} OnHideSummary - Called on hide event.
529
	 * @... {optional function} OnShowSummary - Called on show event.
530
	 */
531
	initialize : function(options)
532
	{
533
		/**
534
		 * Validator options
535
		 * @var {object} options
536
		 */
537
		this.options = options;
538
		/**
539
		 * ValidationGroup
540
		 * @var {string} group
541
		 */
542
		this.group = options.ValidationGroup;
543
		/**
544
		 * Summary DOM element
545
		 * @var {element} messages
546
		 */
547
		this.messages = $(options.ID);
548
		if(this.messages)
549
		{
550
			/**
551
			 * Current visibility state of summary
552
			 * @var {boolean} visible
553
			 */
554
			this.visible = this.messages.style.visibility != "hidden"
555
			this.visible = this.visible && this.messages.style.display != "none";
556
			Prado.Validation.addSummary(options.FormID, this);
557
		}
558
	},
559
 
560
	/**
561
	 * Update the validation summary.
562
	 * @function ?
563
	 * @param {TBaseValidator[]} validators - List of validators that failed validation.
564
	 * @param {boolean} update - true if visible summary should be updated
565
	 */
566
	updateSummary : function(validators, update)
567
	{
568
		if(validators.length <= 0)
569
		{
570
			if(update || this.options.Refresh != false)
571
			{
572
				return this.hideSummary(validators);
573
			}
574
			return;
575
		}
576
 
577
		var refresh = update || this.visible == false || this.options.Refresh != false;
578
		// Also, do not refresh summary if at least 1 validator is waiting for callback response.
579
		// This will avoid the flickering of summary if the validator passes its test
580
		refresh = refresh && validators.any(function(v) { return !v.requestDispatched; });
581
 
582
		if(this.options.ShowSummary != false && refresh)
583
		{
584
			this.updateHTMLMessages(this.getMessages(validators));
585
			this.showSummary(validators);
586
		}
587
 
588
		if(this.options.ScrollToSummary != false && refresh)
589
			window.scrollTo(this.messages.offsetLeft-20, this.messages.offsetTop-20);
590
 
591
		if(this.options.ShowMessageBox == true && refresh)
592
		{
593
			this.alertMessages(this.getMessages(validators));
594
			this.visible = true;
595
		}
596
	},
597
 
598
	/**
599
	 * Display the validator error messages as inline HTML.
600
	 * @function ?
601
	 * @param {string[]} messages - Array of error messages.
602
	 */
603
	updateHTMLMessages : function(messages)
604
	{
605
		while(this.messages.childNodes.length > 0)
606
			this.messages.removeChild(this.messages.lastChild);
607
		this.messages.insert(this.formatSummary(messages));
608
	},
609
 
610
	/**
611
	 * Display the validator error messages as an alert box.
612
	 * @function ?
613
	 * @param {string[]} messages - Array of error messages.
614
	 */
615
	alertMessages : function(messages)
616
	{
617
		var text = this.formatMessageBox(messages);
618
		setTimeout(function(){ alert(text); },20);
619
	},
620
 
621
	/**
622
	 * Get messages from validators.
623
	 * @function {string[]} ?
624
	 * @param {TBaseValidator[]} validators - Array of validators.
625
	 * @return Array of validator error messages.
626
	 */
627
	getMessages : function(validators)
628
	{
629
		var messages = [];
630
		validators.each(function(validator)
631
		{
632
			var message = validator.getErrorMessage();
633
			if(typeof(message) == 'string' && message.length > 0)
634
				messages.push(message);
635
		})
636
		return messages;
637
	},
638
 
639
	/**
640
	 * Hide the validation summary.
641
	 * @function ?
642
	 * @param {TBaseValidator[]} validators - Array of validators.
643
	 */
644
	hideSummary : function(validators)
645
	{	if(typeof(this.options.OnHideSummary) == "function")
646
		{
647
			this.messages.style.visibility="visible";
648
			this.options.OnHideSummary(this,validators)
649
		}
650
		else
651
		{
652
			this.messages.style.visibility="hidden";
653
			if(this.options.Display == "None" || this.options.Display == "Dynamic")
654
				this.messages.hide();
655
		}
656
		this.visible = false;
657
	},
658
 
659
	/**
660
	 * Shows the validation summary.
661
	 * @function ?
662
	 * @param {TBaseValidator[]} validators - Array of validators.
663
	 */
664
	showSummary : function(validators)
665
	{
666
		this.messages.style.visibility="visible";
667
		if(typeof(this.options.OnShowSummary) == "function")
668
			this.options.OnShowSummary(this,validators);
669
		else
670
			this.messages.show();
671
		this.visible = true;
672
	},
673
 
674
	/**
675
	 * Return the format parameters for the summary.
676
	 * @function {object} ?
677
	 * @param {string} type - Format type: "List", "SingleParagraph" or "BulletList" (default)
678
	 * @return Object with format parameters:
679
	 * @... {string} header - Text for header
680
	 * @... {string} first - Text to prepend before message list
681
	 * @... {string} pre - Text to prepend before each message
682
	 * @... {string} post - Text to append after each message
683
	 * @... {string} first - Text to append after message list
684
	 */
685
	formats : function(type)
686
	{
687
		switch(type)
688
		{
689
			case "SimpleList":
690
				return { header : "<br />", first : "", pre : "", post : "<br />", last : ""};
691
			case "SingleParagraph":
692
				return { header : " ", first : "", pre : "", post : " ", last : "<br />"};
693
			case "BulletList":
694
			default:
695
				return { header : "", first : "<ul>", pre : "<li>", post : "</li>", last : "</ul>"};
696
		}
697
	},
698
 
699
	/**
700
	 * Format the message summary.
701
	 * @function {string} ?
702
	 * @param {string[]} messages - Array of error messages.
703
	 * @return Formatted message
704
	 */
705
	formatSummary : function(messages)
706
	{
707
		var format = this.formats(this.options.DisplayMode);
708
		var output = this.options.HeaderText ? this.options.HeaderText + format.header : "";
709
		output += format.first;
710
		messages.each(function(message)
711
		{
712
			output += message.length > 0 ? format.pre + message + format.post : "";
713
		});
714
//		for(var i = 0; i < messages.length; i++)
715
	//		output += (messages[i].length>0) ? format.pre + messages[i] + format.post : "";
716
		output += format.last;
717
		return output;
718
	},
719
	/**
720
	 * Format the message alert box.
721
	 * @function {string} ?
722
	 * @param {string[]} messages - Array of error messages.
723
	 * @return Formatted message for alert
724
	 */
725
	formatMessageBox : function(messages)
726
	{
727
		var output = this.options.HeaderText ? this.options.HeaderText + "\n" : "";
728
		for(var i = 0; i < messages.length; i++)
729
		{
730
			switch(this.options.DisplayMode)
731
			{
732
				case "List":
733
					output += messages[i] + "\n";
734
					break;
735
				case "BulletList":
736
                default:
737
					output += "  - " + messages[i] + "\n";
738
					break;
739
				case "SingleParagraph":
740
					output += messages[i] + " ";
741
					break;
742
			}
743
		}
744
		return output;
745
	}
746
};
747
 
748
/**
749
 * TBaseValidator serves as the base class for validator controls.
750
 *
751
 * <p>Validation is performed when a postback control, such as a TButton,
752
 * a TLinkButton or a TTextBox (under AutoPostBack mode) is submitting
753
 * the page and its <tt>CausesValidation</tt> option is true.
754
 * The input control to be validated is specified by <tt>ControlToValidate</tt>
755
 * option.</p>
756
 *
757
 * @class Prado.WebUI.TBaseValidator
758
 */
759
Prado.WebUI.TBaseValidator = Class.create();
760
Prado.WebUI.TBaseValidator.prototype =
761
{
762
	/**
763
	 * Initialize TBaseValidator.
764
	 * @constructor {protected} ?
765
	 * @param {object} options - Options for initialization.
766
	 * @... {string} ID - ID of validator
767
	 * @... {string} FormID - ID of form of this manager.
768
	 * @... {string} ControlToValidate - ID of control to validate.
769
	 * @... {optional string} InitialValue - Initial value of control to validate.
770
	 * @... {optional string} ErrorMessage - Validation error message.
771
	 * @... {optional string} ValidationGroup - ID of ValidationGroup.
772
	 * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'.
773
	 * @... {optional boolean} ObserveChanges - True to observer changes of ControlToValidate
774
	 * @... {optional boolean} FocusOnError - True to focus on validation error.
775
	 * @... {optional string} FocusElementID - ID of element to focus on error.
776
	 * @... {optional string} ControlCssClass - Css class to use on ControlToValidate on error
777
	 * @... {optional function} OnValidate - Called immediately after validation.
778
	 * @... {optional function} OnValidationSuccess - Called after successful validation.
779
	 * @... {optional function} OnValidationError - Called after validation error.
780
	 */
781
	initialize : function(options)
782
	{
783
	/*	options.OnValidate = options.OnValidate || Prototype.emptyFunction;
784
		options.OnSuccess = options.OnSuccess || Prototype.emptyFunction;
785
		options.OnError = options.OnError || Prototype.emptyFunction;
786
	*/
787
 
788
		/**
789
		 * Wether the validator is enabled (default true)
790
		 * @var {boolean} enabled
791
		 */
792
		this.enabled = true;
793
		/**
794
		 * Visibility state of validator(default false)
795
		 * @var {boolean} visible
796
		 */
797
		this.visible = false;
798
		/**
799
		 * State of validation (default true)
800
		 * @var {boolean} isValid
801
		 */
802
		this.isValid = true;
803
		/**
804
		 * DOM elements that are observed by this validator
805
		 * @var {private element[]} _isObserving
806
		 */
807
		this._isObserving = {};
808
		/**
809
		 * ValidationGroup
810
		 * @var {string} group
811
		 */
812
		this.group = null;
813
		/**
814
		 * Wether a request was dispatched (default false)
815
		 * @var {boolean} requestDispatched
816
		 */
817
		this.requestDispatched = false;
818
 
819
		/**
820
		 * Validator options
821
		 * @var {object} options
822
		 */
823
		this.options = options;
824
		/**
825
		 * DOM element of control to validate
826
		 * @var {element} control
827
		 */
828
		this.control = $(options.ControlToValidate);
829
		/**
830
		 * DOM element of validator
831
		 * @var {element} message
832
		 */
833
		this.message = $(options.ID);
834
		if(this.control && this.message)
835
		{
836
			this.group = options.ValidationGroup;
837
 
838
			/**
839
			 * ValidationManager of this validator
840
			 * @var {ValidationManager} manager
841
			 */
842
			this.manager = Prado.Validation.addValidator(options.FormID, this);
843
		}
844
	},
845
 
846
	/**
847
	 * Get error message.
848
	 * @function {string} ?
849
	 * @return Validation error message.
850
	 */
851
	getErrorMessage : function()
852
	{
853
		return this.options.ErrorMessage;
854
	},
855
 
856
	/**
857
	 * Update the validator.
858
	 * Updating the validator control will set the validator
859
	 * <tt>visible</tt> property to true.
860
	 * @function ?
861
	 */
862
	updateControl: function()
863
	{
864
		this.refreshControlAndMessage();
865
 
866
		//if(this.options.FocusOnError && !this.isValid )
867
		//	Prado.Element.focus(this.options.FocusElementID);
868
 
869
		this.visible = true;
870
	},
871
 
872
	/**
873
	 * Updates span and input CSS class.
874
	 * @function ?
875
	 */
876
	refreshControlAndMessage : function()
877
	{
878
		this.visible = true;
879
		if(this.message)
880
		{
881
			if(this.options.Display == "Dynamic")
882
			{
883
				var msg=this.message;
884
				this.isValid ? setTimeout(function() { msg.hide(); }, 250) : msg.show();
885
			}
886
			this.message.style.visibility = this.isValid ? "hidden" : "visible";
887
		}
888
		if(this.control)
889
			this.updateControlCssClass(this.control, this.isValid);
890
	},
891
 
892
	/**
893
	 * Update CSS class of control to validate.
894
	 * Add a css class to the input control if validator is invalid,
895
	 * removes the css class if valid.
896
	 * @function ?
897
	 * @param {element} control - DOM element of control to validate
898
	 * @param {boolean} valid - Validation state of control
899
	 */
900
	updateControlCssClass : function(control, valid)
901
	{
902
		var CssClass = this.options.ControlCssClass;
903
		if(typeof(CssClass) == "string" && CssClass.length > 0)
904
		{
905
			if(valid)
906
			{
907
				if (control.lastValidator == this.options.ID)
908
				{
909
					control.lastValidator = null;
910
					control.removeClassName(CssClass);
911
				}
912
			}
913
			else
914
			{
915
				control.lastValidator = this.options.ID;
916
				control.addClassName(CssClass);
917
			}
918
		}
919
	},
920
 
921
	/**
922
	 * Hide the validator messages and remove any validation changes.
923
	 * @function ?
924
	 */
925
	hide : function()
926
	{
927
		this.reset();
928
		this.visible = false;
929
	},
930
 
931
	/**
932
	 * Reset validator.
933
	 * Sets isValid = true and updates the validator display.
934
	 * @function ?
935
	 */
936
	reset : function()
937
	{
938
		this.isValid = true;
939
		this.updateControl();
940
	},
941
 
942
	/**
943
	 * Perform validation.
944
	 * Calls evaluateIsValid() function to set the value of isValid property.
945
	 * Triggers onValidate event and onSuccess or onError event.
946
	 * @function {boolean} ?
947
	 * @param {element} invoker - DOM element that triggered validation
948
	 * @return Valdation state of control.
949
	 */
950
	validate : function(invoker)
951
	{
952
		//try to find the control.
953
		if(!this.control)
954
			this.control = $(this.options.ControlToValidate);
955
 
956
		if(!this.control || this.control.disabled)
957
		{
958
			this.isValid = true;
959
			return this.isValid;
960
		}
961
 
962
		if(typeof(this.options.OnValidate) == "function")
963
		{
964
			if(this.requestDispatched == false)
965
				this.options.OnValidate(this, invoker);
966
		}
967
 
968
		if(this.enabled && !this.control.getAttribute('disabled'))
969
			this.isValid = this.evaluateIsValid();
970
		else
971
			this.isValid = true;
972
 
973
		this.updateValidationDisplay(invoker);
974
		this.observeChanges(this.control);
975
 
976
		return this.isValid;
977
	},
978
 
979
	/**
980
	 * Update validation display.
981
	 * Updates the validation messages and the control to validate.
982
	 * @param {element} invoker - DOM element that triggered validation
983
	 */
984
	updateValidationDisplay : function(invoker)
985
	{
986
		if(this.isValid)
987
		{
988
			if(typeof(this.options.OnValidationSuccess) == "function")
989
			{
990
				if(this.requestDispatched == false)
991
				{
992
					this.refreshControlAndMessage();
993
					this.options.OnValidationSuccess(this, invoker);
994
				}
995
			}
996
			else
997
				this.updateControl();
998
		}
999
		else
1000
		{
1001
			if(typeof(this.options.OnValidationError) == "function")
1002
			{
1003
				if(this.requestDispatched == false)
1004
				{
1005
					this.refreshControlAndMessage();
1006
					this.options.OnValidationError(this, invoker)
1007
				}
1008
			}
1009
			else
1010
				this.updateControl();
1011
		}
1012
	},
1013
 
1014
	/**
1015
	 * Add control to observe for changes.
1016
	 * Re-validates upon change. If the validator is not visible,
1017
	 * no updates are propagated.
1018
	 * @function ?
1019
	 * @param {element} control - DOM element of control to observe
1020
	 */
1021
	observeChanges : function(control)
1022
	{
1023
		if(!control) return;
1024
 
1025
		var canObserveChanges = this.options.ObserveChanges != false;
1026
		var currentlyObserving = this._isObserving[control.id+this.options.ID];
1027
 
1028
		if(canObserveChanges && !currentlyObserving)
1029
		{
1030
			var validator = this;
1031
 
1032
			Event.observe(control, 'change', function()
1033
			{
1034
				if(validator.visible)
1035
				{
1036
					validator.validate();
1037
					validator.manager.updateSummary(validator.group);
1038
				}
1039
			});
1040
			this._isObserving[control.id+this.options.ID] = true;
1041
		}
1042
	},
1043
 
1044
	/**
1045
	 * Trim a string.
1046
	 * @function {string} ?
1047
	 * @param {string} value - String that should be trimmed.
1048
	 * @return Trimmed string, empty string if value is not string.
1049
	 */
1050
	trim : function(value)
1051
	{
1052
		return typeof(value) == "string" ? value.trim() : "";
1053
	},
1054
 
1055
	/**
1056
	 * Convert the value to a specific data type.
1057
	 * @function {mixed|null} ?
1058
	 * @param {string} dataType - Data type: "Integer", "Double", "Date" or "String"
1059
	 * @param {mixed} value - Value to convert.
1060
	 * @return Converted data value.
1061
	 */
1062
	convert : function(dataType, value)
1063
	{
1064
		if(typeof(value) == "undefined")
1065
			value = this.getValidationValue();
1066
		var string = new String(value);
1067
		switch(dataType)
1068
		{
1069
			case "Integer":
1070
				return string.toInteger();
1071
			case "Double" :
1072
			case "Float" :
1073
				return string.toDouble(this.options.DecimalChar);
1074
			case "Date":
1075
				if(typeof(value) != "string")
1076
					return value;
1077
				else
1078
				{
1079
					var value = string.toDate(this.options.DateFormat);
1080
					if(value && typeof(value.getTime) == "function")
1081
						return value.getTime();
1082
					else
1083
						return null;
1084
				}
1085
			case "String":
1086
				return string.toString();
1087
		}
1088
		return value;
1089
	},
1090
 
1091
	/**
1092
	 * Get value that should be validated.
1093
	 * The ControlType property comes from TBaseValidator::getClientControlClass()
1094
	 * Be sure to update the TBaseValidator::$_clientClass if new cases are added.
1095
	 * @function {mixed} ?
1096
	 * @param {optional element} control - Control to get value from (default: this.control)
1097
	 * @return Control value to validate
1098
	 */
1099
	 getValidationValue : function(control)
1100
	 {
1101
	 	if(!control)
1102
	 		control = this.control
1103
	 	switch(this.options.ControlType)
1104
	 	{
1105
	 		case 'TDatePicker':
1106
	 			if(control.type == "text")
1107
	 			{
1108
	 				value = this.trim($F(control));
1109
 
1110
					if(this.options.DateFormat)
1111
	 				{
1112
	 					date = value.toDate(this.options.DateFormat);
1113
	 					return date == null ? value : date;
1114
	 				}
1115
	 				else
1116
		 				return value;
1117
	 			}
1118
	 			else
1119
	 			{
1120
	 				this.observeDatePickerChanges();
1121
 
1122
	 				return Prado.WebUI.TDatePicker.getDropDownDate(control);//.getTime();
1123
	 			}
1124
	 		case 'THtmlArea':
1125
	 			if(typeof tinyMCE != "undefined")
1126
					tinyMCE.triggerSave();
1127
				return this.trim($F(control));
1128
			case 'TRadioButton':
1129
				if(this.options.GroupName)
1130
					return this.getRadioButtonGroupValue();
1131
	 		default:
1132
	 			if(this.isListControlType())
1133
	 				return this.getFirstSelectedListValue();
1134
	 			else
1135
		 			return this.trim($F(control));
1136
	 	}
1137
	 },
1138
 
1139
	/**
1140
	 * Get value of radio button group
1141
	 * @function {string} ?
1142
	 * @return Value of a radio button group
1143
	 */
1144
	getRadioButtonGroupValue : function()
1145
	{
1146
		name = this.control.name;
1147
		value = "";
1148
		$A(document.getElementsByName(name)).each(function(el)
1149
		{
1150
			if(el.checked)
1151
				value =  el.value;
1152
		});
1153
		return value;
1154
	},
1155
 
1156
	 /**
1157
	  * Observe changes in the drop down list date picker, IE only.
1158
	  * @function ?
1159
	  */
1160
	 observeDatePickerChanges : function()
1161
	 {
1162
	 	if(Prado.Browser().ie)
1163
	 	{
1164
	 		var DatePicker = Prado.WebUI.TDatePicker;
1165
	 		this.observeChanges(DatePicker.getDayListControl(this.control));
1166
			this.observeChanges(DatePicker.getMonthListControl(this.control));
1167
			this.observeChanges(DatePicker.getYearListControl(this.control));
1168
	 	}
1169
	 },
1170
 
1171
	/**
1172
	 * Gets number of selections and their values.
1173
	 * @function {object} ?
1174
	 * @param {element[]} elements - Elements to get values from.
1175
	 * @param {string} initialValue - Initial value of element
1176
	 * @return Object:
1177
	 * @... {mixed[]} values - Array of selected values
1178
	 * @... {int} checks - Number of selections
1179
	 */
1180
	getSelectedValuesAndChecks : function(elements, initialValue)
1181
	{
1182
		var checked = 0;
1183
		var values = [];
1184
		var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected';
1185
		elements.each(function(element)
1186
		{
1187
			if(element[isSelected] && element.value != initialValue)
1188
			{
1189
				checked++;
1190
				values.push(element.value);
1191
			}
1192
		});
1193
		return {'checks' : checked, 'values' : values};
1194
	},
1195
 
1196
	/**
1197
	 * Get list elements of TCheckBoxList or TListBox.
1198
	 * Gets an array of the list control item input elements, for TCheckBoxList
1199
	 * checkbox input elements are returned, for TListBox HTML option elements
1200
	 * are returned.
1201
	 * @function {element[]} ?
1202
	 * @return Array of list control option DOM elements.
1203
	 */
1204
	getListElements : function()
1205
	{
1206
		switch(this.options.ControlType)
1207
		{
1208
			case 'TCheckBoxList': case 'TRadioButtonList':
1209
				var elements = [];
1210
				for(var i = 0; i < this.options.TotalItems; i++)
1211
				{
1212
					var element = $(this.options.ControlToValidate+"_c"+i);
1213
					if(this.isCheckBoxType(element))
1214
						elements.push(element);
1215
				}
1216
				return elements;
1217
			case 'TListBox':
1218
				var elements = [];
1219
				var element = $(this.options.ControlToValidate);
1220
				if(element && (type = element.type.toLowerCase()))
1221
				{
1222
					if(type == "select-one" || type == "select-multiple")
1223
						elements = $A(element.options);
1224
				}
1225
				return elements;
1226
			default:
1227
				return [];
1228
		}
1229
	},
1230
 
1231
	/**
1232
	 * Check if control is of type checkbox or radio.
1233
	 * @function {boolean} ?
1234
	 * @param {element} element - DOM element to check.
1235
	 * @return True if element is of checkbox or radio type.
1236
	 */
1237
	isCheckBoxType : function(element)
1238
	{
1239
		if(element && element.type)
1240
		{
1241
			var type = element.type.toLowerCase();
1242
			return type == "checkbox" || type == "radio";
1243
		}
1244
		return false;
1245
	},
1246
 
1247
	/**
1248
	 * Check if control to validate is a TListControl type.
1249
	 * @function {boolean} ?
1250
	 * @return True if control to validate is a TListControl type.
1251
	 */
1252
	isListControlType : function()
1253
	{
1254
		var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox'];
1255
		return list.include(this.options.ControlType);
1256
	},
1257
 
1258
	/**
1259
	 * Get first selected list value or initial value if none found.
1260
	 * @function {string} ?
1261
	 * @return First selected list value, initial value if none found.
1262
	 */
1263
	getFirstSelectedListValue : function()
1264
	{
1265
		var initial = "";
1266
		if(typeof(this.options.InitialValue) != "undefined")
1267
			initial = this.options.InitialValue;
1268
		var elements = this.getListElements();
1269
		var selection = this.getSelectedValuesAndChecks(elements, initial);
1270
		return selection.values.length > 0 ? selection.values[0] : initial;
1271
	}
1272
}
1273
 
1274
 
1275
/**
1276
 * TRequiredFieldValidator makes the associated input control a required field.
1277
 *
1278
 * <p>The input control fails validation if its value does not change from
1279
 * the <tt>InitialValue</tt> option upon losing focus.</p>
1280
 *
1281
 * @class Prado.WebUI.TRequiredFieldValidator
1282
 * @extends Prado.WebUI.TBaseValidator
1283
 */
1284
Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
1285
{
1286
	/**
1287
	 * Evaluate validation state
1288
	 * @function {boolean} ?
1289
	 * @return True if the input value is not empty nor equal to the initial value.
1290
	 */
1291
	evaluateIsValid : function()
1292
	{
1293
    	var a = this.getValidationValue();
1294
    	var b = this.trim(this.options.InitialValue);
1295
    	return(a != b);
1296
	}
1297
});
1298
 
1299
 
1300
/**
1301
 * TCompareValidator compares the value entered by the user into an input
1302
 * control with the value entered into another input control or a constant value.
1303
 *
1304
 * <p>To compare the associated input control with another input control,
1305
 * set the <tt>ControlToCompare</tt> option to the ID path
1306
 * of the control to compare with. To compare the associated input control with
1307
 * a constant value, specify the constant value to compare with by setting the
1308
 * <tt>ValueToCompare</tt> option.</p>
1309
 *
1310
 * <p>The <tt>DataType</tt> property is used to specify the data type
1311
 * of both comparison values. Both values are automatically converted to this data
1312
 * type before the comparison operation is performed. The following value types are supported:
1313
 * - <b>Integer</b> A 32-bit signed integer data type.
1314
 * - <b>Float</b> A double-precision floating point number data type.
1315
 * - <b>Date</b> A date data type. The format can be set by the <tt>DateFormat</tt> option.
1316
 * - <b>String</b> A string data type.</p>
1317
 *
1318
 * Use the <tt>Operator</tt> property to specify the type of comparison
1319
 * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual,
1320
 * LessThan and LessThanEqual.
1321
 *
1322
 * @class Prado.WebUI.TCompareValidator
1323
 * @extends Prado.WebUI.TBaseValidator
1324
 */
1325
Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator,
1326
{
1327
	/**
1328
	 * Additional constructor options.
1329
	 * @constructor initialize
1330
	 * @param {object} options - Additional constructor options:
1331
	 * @... {string} ControlToCompare - Control with compare value.
1332
	 * @... {string} ValueToCompare - Value to compare.
1333
	 * @... {string} Operator - Type of comparison: "Equal", "NotEqual", "GreaterThan",
1334
	 *   "GreaterThanEqual", "LessThan" or "LessThanEqual".
1335
	 * @... {string} Type - Type of values: "Integer", "Float", "Date" or "String".
1336
	 * @... {string} DateFormat - Valid date format.
1337
	 */
1338
 
1339
	//_observingComparee : false,
1340
 
1341
	/**
1342
	 * Evaluate validation state
1343
	 * @function {boolean} ?
1344
	 * @return True if comparision condition is met.
1345
	 */
1346
	evaluateIsValid : function()
1347
	{
1348
		var value = this.getValidationValue();
1349
	    if (value.length <= 0)
1350
	    	return true;
1351
 
1352
    	var comparee = $(this.options.ControlToCompare);
1353
 
1354
		if(comparee)
1355
			var compareTo = this.getValidationValue(comparee);
1356
		else
1357
			var compareTo = this.options.ValueToCompare || "";
1358
 
1359
	    var isValid =  this.compare(value, compareTo);
1360
 
1361
		if(comparee)
1362
		{
1363
			this.updateControlCssClass(comparee, isValid);
1364
			this.observeChanges(comparee);
1365
		}
1366
		return isValid;
1367
	},
1368
 
1369
	/**
1370
	 * Compare two operands.
1371
	 * The operand values are casted to type defined
1372
	 * by <tt>DataType</tt> option. False is returned if the first
1373
	 * operand converts to null. Returns true if the second operand
1374
	 * converts to null. The comparision is done based on the
1375
	 * <tt>Operator</tt> option.
1376
	 * @function ?
1377
	 * @param {mixed} operand1 - First operand.
1378
	 * @param {mixed} operand2 - Second operand.
1379
	 */
1380
	compare : function(operand1, operand2)
1381
	{
1382
		var op1, op2;
1383
		if((op1 = this.convert(this.options.DataType, operand1)) == null)
1384
			return false;
1385
		if ((op2 = this.convert(this.options.DataType, operand2)) == null)
1386
        	return true;
1387
    	switch (this.options.Operator)
1388
		{
1389
	        case "NotEqual":
1390
	            return (op1 != op2);
1391
	        case "GreaterThan":
1392
	            return (op1 > op2);
1393
	        case "GreaterThanEqual":
1394
	            return (op1 >= op2);
1395
	        case "LessThan":
1396
	            return (op1 < op2);
1397
	        case "LessThanEqual":
1398
	            return (op1 <= op2);
1399
	        default:
1400
	            return (op1 == op2);
1401
	    }
1402
	}
1403
});
1404
 
1405
/**
1406
 * TCustomValidator performs user-defined client-side validation on an
1407
 * input component.
1408
 *
1409
 * <p>To create a client-side validation function, add the client-side
1410
 * validation javascript function to the page template.
1411
 * The function should have the following signature:</p>
1412
 *
1413
 * <pre>
1414
 * &lt;script type="text/javascript"&gt;
1415
 * function ValidationFunctionName(sender, parameter)
1416
 * {
1417
 *    if(parameter == ...)
1418
 *       return true;
1419
 *    else
1420
 *       return false;
1421
 * }
1422
 * &lt;/script&gt;
1423
 * </pre>
1424
 *
1425
 * <p>Use the <tt>ClientValidationFunction</tt> option
1426
 * to specify the name of the client-side validation script function associated
1427
 * with the TCustomValidator.</p>
1428
 *
1429
 * @class Prado.WebUI.TCustomValidator
1430
 * @extends Prado.WebUI.TBaseValidator
1431
 */
1432
Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
1433
{
1434
	/**
1435
	 * Additional constructor options.
1436
	 * @constructor initialize
1437
	 * @param {object} options - Additional constructor options:
1438
	 * @... {function} ClientValidationFunction - Custom validation function.
1439
	 */
1440
 
1441
	/**
1442
	 * Evaluate validation state
1443
	 * Returns true if no valid custom validation function is present.
1444
	 * @function {boolean} ?
1445
	 * @return True if custom validation returned true.
1446
	 */
1447
	evaluateIsValid : function()
1448
	{
1449
		var value = this.getValidationValue();
1450
		var clientFunction = this.options.ClientValidationFunction;
1451
		if(typeof(clientFunction) == "string" && clientFunction.length > 0)
1452
		{
1453
			validate = clientFunction.toFunction();
1454
			return validate(this, value);
1455
		}
1456
		return true;
1457
	}
1458
});
1459
 
1460
/**
1461
 * Uses callback request to perform validation.
1462
 *
1463
 * @class Prado.WebUI.TActiveCustomValidator
1464
 * @extends Prado.WebUI.TBaseValidator
1465
 */
1466
Prado.WebUI.TActiveCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
1467
{
1468
	/**
1469
	 * Value to validate
1470
	 * @var {string} validatingValue
1471
	 */
1472
	validatingValue : null,
1473
	/**
1474
	 * DOM element that triggered validation
1475
	 * @var {element} invoker
1476
	 */
1477
	invoker : null,
1478
 
1479
	/**
1480
	 * Override the parent implementation to store the invoker, in order to
1481
	 * re-validate after the callback has returned
1482
	 * Calls evaluateIsValid() function to set the value of isValid property.
1483
	 * Triggers onValidate event and onSuccess or onError event.
1484
	 * @function {boolean} ?
1485
	 * @param {element} invoker - DOM element that triggered validation
1486
	 * @return True if valid.
1487
	 */
1488
	validate : function(invoker)
1489
	{
1490
		this.invoker = invoker;
1491
 
1492
		//try to find the control.
1493
		if(!this.control)
1494
			this.control = $(this.options.ControlToValidate);
1495
 
1496
		if(!this.control || this.control.disabled)
1497
		{
1498
			this.isValid = true;
1499
			return this.isValid;
1500
		}
1501
 
1502
		if(typeof(this.options.OnValidate) == "function")
1503
		{
1504
			if(this.requestDispatched == false)
1505
				this.options.OnValidate(this, invoker);
1506
		}
1507
 
1508
		if(this.enabled && !this.control.getAttribute('disabled'))
1509
			this.isValid = this.evaluateIsValid();
1510
		else
1511
			this.isValid = true;
1512
 
1513
		// Only update the message if the callback has already return !
1514
		if (!this.requestDispatched)
1515
			this.updateValidationDisplay(invoker);
1516
 
1517
		this.observeChanges(this.control);
1518
 
1519
		return this.isValid;
1520
	},
1521
 
1522
	/**
1523
	 * Send CallBack to start serverside validation.
1524
	 * @function {boolean} ?
1525
	 * @return True if valid.
1526
	 */
1527
	evaluateIsValid : function()
1528
	{
1529
		value = this.getValidationValue();
1530
		if(!this.requestDispatched && (""+value) != (""+this.validatingValue))
1531
		{
1532
			this.validatingValue = value;
1533
			request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
1534
			if(this.options.DateFormat && value instanceof Date) //change date to string with formatting.
1535
				value = value.SimpleFormat(this.options.DateFormat);
1536
			request.setCallbackParameter(value);
1537
			request.setCausesValidation(false);
1538
			request.options.onSuccess = this.callbackOnSuccess.bind(this);
1539
			request.options.onFailure = this.callbackOnFailure.bind(this);
1540
			request.dispatch();
1541
			this.requestDispatched = true;
1542
			return false;
1543
		}
1544
		return this.isValid;
1545
	},
1546
 
1547
	/**
1548
	 * Parse CallBack response data on success.
1549
	 * @function ?
1550
	 * @param {CallbackRequest} request - CallbackRequest.
1551
	 * @param {string} data - Response data.
1552
	 */
1553
	callbackOnSuccess : function(request, data)
1554
	{
1555
		this.isValid = data;
1556
		this.requestDispatched = false;
1557
		if(typeof(this.options.onSuccess) == "function")
1558
			this.options.onSuccess(request,data);
1559
		this.updateValidationDisplay();
1560
		this.manager.updateSummary(this.group);
1561
		// Redispatch initial request if any
1562
		if (this.invoker instanceof Prado.CallbackRequest)
1563
		{
1564
			this.invoker.dispatch();
1565
		}
1566
 
1567
	},
1568
 
1569
	/**
1570
	 * Handle callback failure.
1571
	 * @function ?
1572
	 * @param {CallbackRequest} request - CallbackRequest.
1573
	 * @param {string} data - Response data.
1574
	 */
1575
	callbackOnFailure : function(request, data)
1576
	{
1577
		this.requestDispatched = false;
1578
		if(typeof(this.options.onFailure) == "function")
1579
			this.options.onFailure(request,data);
1580
	}
1581
});
1582
 
1583
/**
1584
 * TRangeValidator tests whether an input value is within a specified range.
1585
 *
1586
 * <p>TRangeValidator uses three key properties to perform its validation.</p>
1587
 *
1588
 * <p>The <tt>MinValue</tt> and <tt>MaxValue</tt> options specify the minimum
1589
 * and maximum values of the valid range.</p>
1590
 * <p>The <tt>DataType</tt> option is
1591
 * used to specify the data type of the value and the minimum and maximum range values.
1592
 * The values are converted to this data type before the validation
1593
 * operation is performed. The following value types are supported:</p>
1594
 *
1595
 * - <b>Integer</b> A 32-bit signed integer data type.<br />
1596
 * - <b>Float</b> A double-precision floating point number data type.<br />
1597
 * - <b>Date</b> A date data type. The date format can be specified by<br />
1598
 *   setting <tt>DateFormat</tt> option, which must be recognizable
1599
 *   by <tt>Date.SimpleParse</tt> javascript function.
1600
 * - <b>String</b> A string data type.
1601
 *
1602
 * @class Prado.WebUI.TRangeValidator
1603
 * @extends Prado.WebUI.TBaseValidator
1604
 */
1605
Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator,
1606
{
1607
	/**
1608
	 * Additional constructor options.
1609
	 * @constructor initialize
1610
	 * @param {object} options - Additional constructor options:
1611
	 * @... {string} MinValue - Minimum range value
1612
	 * @... {string} MaxValue - Maximum range value
1613
	 * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String"
1614
	 * @... {string} DateFormat - Date format for data type Date.
1615
	 */
1616
 
1617
	/**
1618
	 * Evaluate validation state
1619
	 * @function {boolean} ?
1620
	 * @return True if value is in range or value is empty,
1621
	 * false otherwhise and when type conversion failed.
1622
	 */
1623
	evaluateIsValid : function()
1624
	{
1625
		var value = this.getValidationValue();
1626
		if(value.length <= 0)
1627
			return true;
1628
		if(typeof(this.options.DataType) == "undefined")
1629
			this.options.DataType = "String";
1630
 
1631
		if(this.options.DataType != "StringLength")
1632
		{
1633
			var min = this.convert(this.options.DataType, this.options.MinValue || null);
1634
			var max = this.convert(this.options.DataType, this.options.MaxValue || null);
1635
			value = this.convert(this.options.DataType, value);
1636
		}
1637
		else
1638
		{
1639
			var min = this.options.MinValue || 0;
1640
			var max = this.options.MaxValue || Number.POSITIVE_INFINITY;
1641
			value = value.length;
1642
		}
1643
 
1644
		if(value == null)
1645
			return false;
1646
 
1647
		var valid = true;
1648
 
1649
		if(min != null)
1650
			valid = valid && (this.options.StrictComparison ? value > min : value >= min);
1651
		if(max != null)
1652
			valid = valid && (this.options.StrictComparison ? value < max : value <= max);
1653
		return valid;
1654
	}
1655
});
1656
 
1657
/**
1658
 * TRegularExpressionValidator validates whether the value of an associated
1659
 * input component matches the pattern specified by a regular expression.
1660
 *
1661
 * @class Prado.WebUI.TRegularExpressionValidator
1662
 * @extends Prado.WebUI.TBaseValidator
1663
 */
1664
Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator,
1665
{
1666
	/**
1667
	 * Additional constructor option.
1668
	 * @constructor initialize
1669
	 * @param {object} options - Additional constructor option:
1670
	 * @... {string} ValidationExpression - Regular expression to match against.
1671
	 * @... {string} PatternModifiers - Pattern modifiers: combinations of g, i, and m
1672
	 */
1673
 
1674
	/**
1675
	 * Evaluate validation state
1676
	 * @function {boolean} ?
1677
	 * @return True if value matches regular expression or value is empty.
1678
	 */
1679
	evaluateIsValid : function()
1680
	{
1681
		var value = this.getValidationValue();
1682
		if (value.length <= 0)
1683
	    	return true;
1684
 
1685
		var rx = new RegExp(this.options.ValidationExpression,this.options.PatternModifiers);
1686
		var matches = rx.exec(value);
1687
		return (matches != null && value == matches[0]);
1688
	}
1689
});
1690
 
1691
/**
1692
 * TEmailAddressValidator validates whether the value of an associated
1693
 * input component is a valid email address.
1694
 *
1695
 * @class Prado.WebUI.TEmailAddressValidator
1696
 * @extends Prado.WebUI.TRegularExpressionValidator
1697
 */
1698
Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator;
1699
 
1700
 
1701
/**
1702
 * TListControlValidator checks the number of selection and their values
1703
 * for a TListControl that allows multiple selections.
1704
 *
1705
 * @class Prado.WebUI.TListControlValidator
1706
 * @extends Prado.WebUI.TBaseValidator
1707
 */
1708
Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator,
1709
{
1710
	/**
1711
	 * Evaluate validation state
1712
	 * @function {boolean} ?
1713
	 * @return True if number of selections and/or their values match requirements.
1714
	 */
1715
	evaluateIsValid : function()
1716
	{
1717
		var elements = this.getListElements();
1718
		if(elements && elements.length <= 0)
1719
			return true;
1720
 
1721
		this.observeListElements(elements);
1722
 
1723
		var selection = this.getSelectedValuesAndChecks(elements);
1724
		return this.isValidList(selection.checks, selection.values);
1725
	},
1726
 
1727
	/**
1728
	 * Observe list elements for of changes (only IE)
1729
	 * @function ?
1730
	 * @param {element[]} elements - Array of DOM elements to observe
1731
	 */
1732
	 observeListElements : function(elements)
1733
	 {
1734
		if(Prado.Browser().ie && this.isCheckBoxType(elements[0]))
1735
		{
1736
			var validator = this;
1737
			elements.each(function(element)
1738
			{
1739
				validator.observeChanges(element);
1740
			});
1741
		}
1742
	 },
1743
 
1744
	/**
1745
	 * Check if list is valid.
1746
	 * Determine if the number of checked values and the checked values
1747
	 * satisfy the required number of checks and/or the checked values
1748
	 * equal to the required values.
1749
	 * @function {boolean} ?
1750
	 * @param {int} checked - Number of required checked values
1751
	 * @param {string[]} values - Array of required checked values
1752
	 * @return True if checked values and number of checks are satisfied.
1753
	 */
1754
	isValidList : function(checked, values)
1755
	{
1756
		var exists = true;
1757
 
1758
		//check the required values
1759
		var required = this.getRequiredValues();
1760
		if(required.length > 0)
1761
		{
1762
			if(values.length < required.length)
1763
				return false;
1764
			required.each(function(requiredValue)
1765
			{
1766
				exists = exists && values.include(requiredValue);
1767
			});
1768
		}
1769
 
1770
		var min = typeof(this.options.Min) == "undefined" ?
1771
					Number.NEGATIVE_INFINITY : this.options.Min;
1772
		var max = typeof(this.options.Max) == "undefined" ?
1773
					Number.POSITIVE_INFINITY : this.options.Max;
1774
		return exists && checked >= min && checked <= max;
1775
	},
1776
 
1777
	/**
1778
	 * Get list of required values.
1779
	 * @function {string[]} ?
1780
	 * @return Array of required values that must be selected.
1781
	 */
1782
	getRequiredValues : function()
1783
	{
1784
		var required = [];
1785
		if(this.options.Required && this.options.Required.length > 0)
1786
			required = this.options.Required.split(/,\s*/);
1787
		return required;
1788
	}
1789
});
1790
 
1791
 
1792
/**
1793
 * TDataTypeValidator verifies if the input data is of the type specified
1794
 * by <tt>DataType</tt> option.
1795
 *
1796
 * <p>The following data types are supported:</p>
1797
 *
1798
 * - <b>Integer</b> A 32-bit signed integer data type.<br />
1799
 * - <b>Float</b> A double-precision floating point number data type.<br />
1800
 * - <b>Date</b> A date data type.<br />
1801
 * - <b>String</b> A string data type.<br />
1802
 *
1803
 * <p>For <b>Date</b> type, the option <tt>DateFormat</tt>
1804
 * will be used to determine how to parse the date string.</p>
1805
 *
1806
 * @class Prado.WebUI.TDataTypeValidator
1807
 * @extends Prado.WebUI.TBaseValidator
1808
 */
1809
Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator,
1810
{
1811
	/**
1812
	 * Additional constructor option.
1813
	 * @constructor initialize
1814
	 * @param {object} options - Additional constructor option:
1815
	 * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String"
1816
	 * @... {string} DateFormat - Date format for data type Date.
1817
	 */
1818
 
1819
	/**
1820
	 * Evaluate validation state
1821
	 * @function {boolean} ?
1822
	 * @return True if value matches required data type.
1823
	 */
1824
	evaluateIsValid : function()
1825
	{
1826
		value = this.getValidationValue();
1827
		if(value.length <= 0)
1828
			return true;
1829
		return this.convert(this.options.DataType, value) != null;
1830
	}
1831
});
1832
 
1833
/**
1834
 * TCaptchaValidator verifies if the input data is the same as
1835
 * the token shown in the associated CAPTCHA control.
1836
 *
1837
 * @class Prado.WebUI.TCaptchaValidator
1838
 * @extends Prado.WebUI.TBaseValidator
1839
 */
1840
Prado.WebUI.TCaptchaValidator = Class.extend(Prado.WebUI.TBaseValidator,
1841
{
1842
	/**
1843
	 * Evaluate validation state
1844
	 * @function {boolean} ?
1845
	 * @return True if value matches captcha text
1846
	 */
1847
	evaluateIsValid : function()
1848
	{
1849
		var a = this.getValidationValue();
1850
		var h = 0;
1851
		if (this.options.CaseSensitive==false)
1852
			a = a.toUpperCase();
1853
		for(var i = a.length-1; i >= 0; --i)
1854
			h += a.charCodeAt(i);
1855
		return h == this.options.TokenHash;
1856
	},
1857
 
1858
	crc32 : function(str)
1859
	{
1860
	    function Utf8Encode(string)
1861
		{
1862
	        string = string.replace(/\r\n/g,"\n");
1863
	        var utftext = "";
1864
 
1865
	        for (var n = 0; n < string.length; n++)
1866
			{
1867
	            var c = string.charCodeAt(n);
1868
 
1869
	            if (c < 128) {
1870
	                utftext += String.fromCharCode(c);
1871
	            }
1872
	            else if((c > 127) && (c < 2048)) {
1873
	                utftext += String.fromCharCode((c >> 6) | 192);
1874
	                utftext += String.fromCharCode((c & 63) | 128);
1875
	            }
1876
	            else {
1877
	                utftext += String.fromCharCode((c >> 12) | 224);
1878
	                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
1879
	                utftext += String.fromCharCode((c & 63) | 128);
1880
	            }
1881
	        }
1882
 
1883
	        return utftext;
1884
	    };
1885
 
1886
	    str = Utf8Encode(str);
1887
 
1888
	    var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D";
1889
		var crc = 0;
1890
	    var x = 0;
1891
	    var y = 0;
1892
 
1893
	    crc = crc ^ (-1);
1894
	    for( var i = 0, iTop = str.length; i < iTop; i++ )
1895
		{
1896
	        y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
1897
	        x = "0x" + table.substr( y * 9, 8 );
1898
	        crc = ( crc >>> 8 ) ^ x;
1899
	    }
1900
	    return crc ^ (-1);
1901
	}
1902
});