midas-g_live_validator 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +12 -0
- data/Manifest.txt +22 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +83 -0
- data/Rakefile +30 -0
- data/lib/g_live_validator/active_record_extensions.rb +72 -0
- data/lib/g_live_validator/view_helpers.rb +181 -0
- data/lib/g_live_validator.rb +19 -0
- data/live_validator.gemspec +41 -0
- data/rails_generators/live_validator_assets/live_validator_assets_generator.rb +17 -0
- data/rails_generators/live_validator_assets/templates/default.css +23 -0
- data/rails_generators/live_validator_assets/templates/guilded.live_validator.js +84 -0
- data/rails_generators/live_validator_assets/templates/guilded.live_validator.min.js +18 -0
- data/rails_generators/live_validator_assets/templates/livevalidation-1.3.js +884 -0
- data/rails_generators/live_validator_assets/templates/livevalidation-1.3.min.js +4 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/live_validator_spec.rb +11 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/rspec.rake +21 -0
- metadata +118 -0
@@ -0,0 +1,884 @@
|
|
1
|
+
// LiveValidation 1.3 (standalone version)
|
2
|
+
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
|
3
|
+
// LiveValidation is licensed under the terms of the MIT License
|
4
|
+
|
5
|
+
/*********************************************** LiveValidation class ***********************************/
|
6
|
+
|
7
|
+
/**
|
8
|
+
* validates a form field in real-time based on validations you assign to it
|
9
|
+
*
|
10
|
+
* @var element {mixed} - either a dom element reference or the string id of the element to validate
|
11
|
+
* @var optionsObj {Object} - general options, see below for details
|
12
|
+
*
|
13
|
+
* optionsObj properties:
|
14
|
+
* validMessage {String} - the message to show when the field passes validation
|
15
|
+
* (DEFAULT: "Thankyou!")
|
16
|
+
* onValid {Function} - function to execute when field passes validation
|
17
|
+
* (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); } )
|
18
|
+
* onInvalid {Function} - function to execute when field fails validation
|
19
|
+
* (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); })
|
20
|
+
* insertAfterWhatNode {Int} - position to insert default message
|
21
|
+
* (DEFAULT: the field that is being validated)
|
22
|
+
* onlyOnBlur {Boolean} - whether you want it to validate as you type or only on blur
|
23
|
+
* (DEFAULT: false)
|
24
|
+
* wait {Integer} - the time you want it to pause from the last keystroke before it validates (ms)
|
25
|
+
* (DEFAULT: 0)
|
26
|
+
* onlyOnSubmit {Boolean} - whether should be validated only when the form it belongs to is submitted
|
27
|
+
* (DEFAULT: false)
|
28
|
+
*/
|
29
|
+
var LiveValidation = function(element, optionsObj){
|
30
|
+
this.initialize(element, optionsObj);
|
31
|
+
}
|
32
|
+
|
33
|
+
LiveValidation.VERSION = '1.3 standalone';
|
34
|
+
|
35
|
+
/** element types constants ****/
|
36
|
+
|
37
|
+
LiveValidation.TEXTAREA = 1;
|
38
|
+
LiveValidation.TEXT = 2;
|
39
|
+
LiveValidation.PASSWORD = 3;
|
40
|
+
LiveValidation.CHECKBOX = 4;
|
41
|
+
LiveValidation.SELECT = 5;
|
42
|
+
LiveValidation.FILE = 6;
|
43
|
+
|
44
|
+
/****** Static methods *******/
|
45
|
+
|
46
|
+
/**
|
47
|
+
* pass an array of LiveValidation objects and it will validate all of them
|
48
|
+
*
|
49
|
+
* @var validations {Array} - an array of LiveValidation objects
|
50
|
+
* @return {Bool} - true if all passed validation, false if any fail
|
51
|
+
*/
|
52
|
+
LiveValidation.massValidate = function(validations){
|
53
|
+
var returnValue = true;
|
54
|
+
for(var i = 0, len = validations.length; i < len; ++i ){
|
55
|
+
var valid = validations[i].validate();
|
56
|
+
if(returnValue) returnValue = valid;
|
57
|
+
}
|
58
|
+
return returnValue;
|
59
|
+
}
|
60
|
+
|
61
|
+
/****** prototype ******/
|
62
|
+
|
63
|
+
LiveValidation.prototype = {
|
64
|
+
|
65
|
+
validClass: 'LV_valid',
|
66
|
+
invalidClass: 'LV_invalid',
|
67
|
+
messageClass: 'LV_validation_message',
|
68
|
+
validFieldClass: 'LV_valid_field',
|
69
|
+
invalidFieldClass: 'LV_invalid_field',
|
70
|
+
|
71
|
+
/**
|
72
|
+
* initialises all of the properties and events
|
73
|
+
*
|
74
|
+
* @var - Same as constructor above
|
75
|
+
*/
|
76
|
+
initialize: function(element, optionsObj){
|
77
|
+
var self = this;
|
78
|
+
if(!element) throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");
|
79
|
+
this.element = element.nodeName ? element : document.getElementById(element);
|
80
|
+
if(!this.element) throw new Error("LiveValidation::initialize - No element with reference or id of '" + element + "' exists!");
|
81
|
+
// default properties that could not be initialised above
|
82
|
+
this.validations = [];
|
83
|
+
this.elementType = this.getElementType();
|
84
|
+
this.form = this.element.form;
|
85
|
+
// options
|
86
|
+
var options = optionsObj || {};
|
87
|
+
this.validMessage = options.validMessage || 'Thankyou!';
|
88
|
+
var node = options.insertAfterWhatNode || this.element;
|
89
|
+
this.insertAfterWhatNode = node.nodeType ? node : document.getElementById(node);
|
90
|
+
this.onValid = options.onValid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); };
|
91
|
+
this.onInvalid = options.onInvalid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); };
|
92
|
+
this.onlyOnBlur = options.onlyOnBlur || false;
|
93
|
+
this.wait = options.wait || 0;
|
94
|
+
this.onlyOnSubmit = options.onlyOnSubmit || false;
|
95
|
+
// add to form if it has been provided
|
96
|
+
if(this.form){
|
97
|
+
this.formObj = LiveValidationForm.getInstance(this.form);
|
98
|
+
this.formObj.addField(this);
|
99
|
+
}
|
100
|
+
// events
|
101
|
+
// collect old events
|
102
|
+
this.oldOnFocus = this.element.onfocus || function(){};
|
103
|
+
this.oldOnBlur = this.element.onblur || function(){};
|
104
|
+
this.oldOnClick = this.element.onclick || function(){};
|
105
|
+
this.oldOnChange = this.element.onchange || function(){};
|
106
|
+
this.oldOnKeyup = this.element.onkeyup || function(){};
|
107
|
+
this.element.onfocus = function(e){ self.doOnFocus(e); return self.oldOnFocus.call(this, e); }
|
108
|
+
if(!this.onlyOnSubmit){
|
109
|
+
switch(this.elementType){
|
110
|
+
case LiveValidation.CHECKBOX:
|
111
|
+
this.element.onclick = function(e){ self.validate(); return self.oldOnClick.call(this, e); }
|
112
|
+
// let it run into the next to add a change event too
|
113
|
+
case LiveValidation.SELECT:
|
114
|
+
case LiveValidation.FILE:
|
115
|
+
this.element.onchange = function(e){ self.validate(); return self.oldOnChange.call(this, e); }
|
116
|
+
break;
|
117
|
+
default:
|
118
|
+
if(!this.onlyOnBlur) this.element.onkeyup = function(e){ self.deferValidation(); return self.oldOnKeyup.call(this, e); }
|
119
|
+
this.element.onblur = function(e){ self.doOnBlur(e); return self.oldOnBlur.call(this, e); }
|
120
|
+
}
|
121
|
+
}
|
122
|
+
},
|
123
|
+
|
124
|
+
/**
|
125
|
+
* destroys the instance's events (restoring previous ones) and removes it from any LiveValidationForms
|
126
|
+
*/
|
127
|
+
destroy: function(){
|
128
|
+
if(this.formObj){
|
129
|
+
// remove the field from the LiveValidationForm
|
130
|
+
this.formObj.removeField(this);
|
131
|
+
// destroy the LiveValidationForm if no LiveValidation fields left in it
|
132
|
+
this.formObj.destroy();
|
133
|
+
}
|
134
|
+
// remove events - set them back to the previous events
|
135
|
+
this.element.onfocus = this.oldOnFocus;
|
136
|
+
if(!this.onlyOnSubmit){
|
137
|
+
switch(this.elementType){
|
138
|
+
case LiveValidation.CHECKBOX:
|
139
|
+
this.element.onclick = this.oldOnClick;
|
140
|
+
// let it run into the next to add a change event too
|
141
|
+
case LiveValidation.SELECT:
|
142
|
+
case LiveValidation.FILE:
|
143
|
+
this.element.onchange = this.oldOnChange;
|
144
|
+
break;
|
145
|
+
default:
|
146
|
+
if(!this.onlyOnBlur) this.element.onkeyup = this.oldOnKeyup;
|
147
|
+
this.element.onblur = this.oldOnBlur;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
this.validations = [];
|
151
|
+
this.removeMessageAndFieldClass();
|
152
|
+
},
|
153
|
+
|
154
|
+
/**
|
155
|
+
* adds a validation to perform to a LiveValidation object
|
156
|
+
*
|
157
|
+
* @var validationFunction {Function} - validation function to be used (ie Validate.Presence )
|
158
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
159
|
+
* @return {Object} - the LiveValidation object itself so that calls can be chained
|
160
|
+
*/
|
161
|
+
add: function(validationFunction, validationParamsObj){
|
162
|
+
this.validations.push( {type: validationFunction, params: validationParamsObj || {} } );
|
163
|
+
return this;
|
164
|
+
},
|
165
|
+
|
166
|
+
/**
|
167
|
+
* removes a validation from a LiveValidation object - must have exactly the same arguments as used to add it
|
168
|
+
*
|
169
|
+
* @var validationFunction {Function} - validation function to be used (ie Validate.Presence )
|
170
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
171
|
+
* @return {Object} - the LiveValidation object itself so that calls can be chained
|
172
|
+
*/
|
173
|
+
remove: function(validationFunction, validationParamsObj){
|
174
|
+
var found = false;
|
175
|
+
for( var i = 0, len = this.validations.length; i < len; i++ ){
|
176
|
+
if( this.validations[i].type == validationFunction ){
|
177
|
+
if (this.validations[i].params == validationParamsObj) {
|
178
|
+
found = true;
|
179
|
+
break;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
if(found) this.validations.splice(i,1);
|
184
|
+
return this;
|
185
|
+
},
|
186
|
+
|
187
|
+
|
188
|
+
/**
|
189
|
+
* makes the validation wait the alotted time from the last keystroke
|
190
|
+
*/
|
191
|
+
deferValidation: function(e){
|
192
|
+
if(this.wait >= 300) this.removeMessageAndFieldClass();
|
193
|
+
var self = this;
|
194
|
+
if(this.timeout) clearTimeout(self.timeout);
|
195
|
+
this.timeout = setTimeout( function(){ self.validate() }, self.wait);
|
196
|
+
},
|
197
|
+
|
198
|
+
/**
|
199
|
+
* sets the focused flag to false when field loses focus
|
200
|
+
*/
|
201
|
+
doOnBlur: function(e){
|
202
|
+
this.focused = false;
|
203
|
+
this.validate(e);
|
204
|
+
},
|
205
|
+
|
206
|
+
/**
|
207
|
+
* sets the focused flag to true when field gains focus
|
208
|
+
*/
|
209
|
+
doOnFocus: function(e){
|
210
|
+
this.focused = true;
|
211
|
+
this.removeMessageAndFieldClass();
|
212
|
+
},
|
213
|
+
|
214
|
+
/**
|
215
|
+
* gets the type of element, to check whether it is compatible
|
216
|
+
*
|
217
|
+
* @var validationFunction {Function} - validation function to be used (ie Validate.Presence )
|
218
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
219
|
+
*/
|
220
|
+
getElementType: function(){
|
221
|
+
switch(true){
|
222
|
+
case (this.element.nodeName.toUpperCase() == 'TEXTAREA'):
|
223
|
+
return LiveValidation.TEXTAREA;
|
224
|
+
case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'TEXT'):
|
225
|
+
return LiveValidation.TEXT;
|
226
|
+
case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'PASSWORD'):
|
227
|
+
return LiveValidation.PASSWORD;
|
228
|
+
case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'CHECKBOX'):
|
229
|
+
return LiveValidation.CHECKBOX;
|
230
|
+
case (this.element.nodeName.toUpperCase() == 'INPUT' && this.element.type.toUpperCase() == 'FILE'):
|
231
|
+
return LiveValidation.FILE;
|
232
|
+
case (this.element.nodeName.toUpperCase() == 'SELECT'):
|
233
|
+
return LiveValidation.SELECT;
|
234
|
+
case (this.element.nodeName.toUpperCase() == 'INPUT'):
|
235
|
+
throw new Error('LiveValidation::getElementType - Cannot use LiveValidation on an ' + this.element.type + ' input!');
|
236
|
+
default:
|
237
|
+
throw new Error('LiveValidation::getElementType - Element must be an input, select, or textarea!');
|
238
|
+
}
|
239
|
+
},
|
240
|
+
|
241
|
+
/**
|
242
|
+
* loops through all the validations added to the LiveValidation object and checks them one by one
|
243
|
+
*
|
244
|
+
* @var validationFunction {Function} - validation function to be used (ie Validate.Presence )
|
245
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
246
|
+
* @return {Boolean} - whether the all the validations passed or if one failed
|
247
|
+
*/
|
248
|
+
doValidations: function(){
|
249
|
+
this.validationFailed = false;
|
250
|
+
for(var i = 0, len = this.validations.length; i < len; ++i){
|
251
|
+
var validation = this.validations[i];
|
252
|
+
switch(validation.type){
|
253
|
+
case Validate.Presence:
|
254
|
+
case Validate.Confirmation:
|
255
|
+
case Validate.Acceptance:
|
256
|
+
this.displayMessageWhenEmpty = true;
|
257
|
+
this.validationFailed = !this.validateElement(validation.type, validation.params);
|
258
|
+
break;
|
259
|
+
default:
|
260
|
+
this.validationFailed = !this.validateElement(validation.type, validation.params);
|
261
|
+
break;
|
262
|
+
}
|
263
|
+
if(this.validationFailed) return false;
|
264
|
+
}
|
265
|
+
this.message = this.validMessage;
|
266
|
+
return true;
|
267
|
+
},
|
268
|
+
|
269
|
+
/**
|
270
|
+
* performs validation on the element and handles any error (validation or otherwise) it throws up
|
271
|
+
*
|
272
|
+
* @var validationFunction {Function} - validation function to be used (ie Validate.Presence )
|
273
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
274
|
+
* @return {Boolean} - whether the validation has passed or failed
|
275
|
+
*/
|
276
|
+
validateElement: function(validationFunction, validationParamsObj){
|
277
|
+
var value = (this.elementType == LiveValidation.SELECT) ? this.element.options[this.element.selectedIndex].value : this.element.value;
|
278
|
+
if(validationFunction == Validate.Acceptance){
|
279
|
+
if(this.elementType != LiveValidation.CHECKBOX) throw new Error('LiveValidation::validateElement - Element to validate acceptance must be a checkbox!');
|
280
|
+
value = this.element.checked;
|
281
|
+
}
|
282
|
+
var isValid = true;
|
283
|
+
try{
|
284
|
+
validationFunction(value, validationParamsObj);
|
285
|
+
} catch(error) {
|
286
|
+
if(error instanceof Validate.Error){
|
287
|
+
if( value !== '' || (value === '' && this.displayMessageWhenEmpty) ){
|
288
|
+
this.validationFailed = true;
|
289
|
+
this.message = error.message;
|
290
|
+
isValid = false;
|
291
|
+
}
|
292
|
+
}else{
|
293
|
+
throw error;
|
294
|
+
}
|
295
|
+
}finally{
|
296
|
+
return isValid;
|
297
|
+
}
|
298
|
+
},
|
299
|
+
|
300
|
+
/**
|
301
|
+
* makes it do the all the validations and fires off the onValid or onInvalid callbacks
|
302
|
+
*
|
303
|
+
* @return {Boolean} - whether the all the validations passed or if one failed
|
304
|
+
*/
|
305
|
+
validate: function(){
|
306
|
+
if(!this.element.disabled){
|
307
|
+
var isValid = this.doValidations();
|
308
|
+
if(isValid){
|
309
|
+
this.onValid();
|
310
|
+
return true;
|
311
|
+
}else {
|
312
|
+
this.onInvalid();
|
313
|
+
return false;
|
314
|
+
}
|
315
|
+
}else{
|
316
|
+
return true;
|
317
|
+
}
|
318
|
+
},
|
319
|
+
|
320
|
+
/**
|
321
|
+
* enables the field
|
322
|
+
*
|
323
|
+
* @return {LiveValidation} - the LiveValidation object for chaining
|
324
|
+
*/
|
325
|
+
enable: function(){
|
326
|
+
this.element.disabled = false;
|
327
|
+
return this;
|
328
|
+
},
|
329
|
+
|
330
|
+
/**
|
331
|
+
* disables the field and removes any message and styles associated with the field
|
332
|
+
*
|
333
|
+
* @return {LiveValidation} - the LiveValidation object for chaining
|
334
|
+
*/
|
335
|
+
disable: function(){
|
336
|
+
this.element.disabled = true;
|
337
|
+
this.removeMessageAndFieldClass();
|
338
|
+
return this;
|
339
|
+
},
|
340
|
+
|
341
|
+
/** Message insertion methods ****************************
|
342
|
+
*
|
343
|
+
* These are only used in the onValid and onInvalid callback functions and so if you overide the default callbacks,
|
344
|
+
* you must either impliment your own functions to do whatever you want, or call some of these from them if you
|
345
|
+
* want to keep some of the functionality
|
346
|
+
*/
|
347
|
+
|
348
|
+
/**
|
349
|
+
* makes a span containg the passed or failed message
|
350
|
+
*
|
351
|
+
* @return {HTMLSpanObject} - a span element with the message in it
|
352
|
+
*/
|
353
|
+
createMessageSpan: function(){
|
354
|
+
var span = document.createElement('span');
|
355
|
+
var textNode = document.createTextNode(this.message);
|
356
|
+
span.appendChild(textNode);
|
357
|
+
return span;
|
358
|
+
},
|
359
|
+
|
360
|
+
/**
|
361
|
+
* inserts the element containing the message in place of the element that already exists (if it does)
|
362
|
+
*
|
363
|
+
* @var elementToIsert {HTMLElementObject} - an element node to insert
|
364
|
+
*/
|
365
|
+
insertMessage: function(elementToInsert){
|
366
|
+
this.removeMessage();
|
367
|
+
if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value == ''))
|
368
|
+
|| this.element.value != '' ){
|
369
|
+
var className = this.validationFailed ? this.invalidClass : this.validClass;
|
370
|
+
elementToInsert.className += ' ' + this.messageClass + ' ' + className;
|
371
|
+
if(this.insertAfterWhatNode.nextSibling){
|
372
|
+
this.insertAfterWhatNode.parentNode.insertBefore(elementToInsert, this.insertAfterWhatNode.nextSibling);
|
373
|
+
}else{
|
374
|
+
this.insertAfterWhatNode.parentNode.appendChild(elementToInsert);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
},
|
378
|
+
|
379
|
+
|
380
|
+
/**
|
381
|
+
* changes the class of the field based on whether it is valid or not
|
382
|
+
*/
|
383
|
+
addFieldClass: function(){
|
384
|
+
this.removeFieldClass();
|
385
|
+
if(!this.validationFailed){
|
386
|
+
if(this.displayMessageWhenEmpty || this.element.value != ''){
|
387
|
+
if(this.element.className.indexOf(this.validFieldClass) == -1) this.element.className += ' ' + this.validFieldClass;
|
388
|
+
}
|
389
|
+
}else{
|
390
|
+
if(this.element.className.indexOf(this.invalidFieldClass) == -1) this.element.className += ' ' + this.invalidFieldClass;
|
391
|
+
}
|
392
|
+
},
|
393
|
+
|
394
|
+
/**
|
395
|
+
* removes the message element if it exists, so that the new message will replace it
|
396
|
+
*/
|
397
|
+
removeMessage: function(){
|
398
|
+
var nextEl;
|
399
|
+
var el = this.insertAfterWhatNode;
|
400
|
+
while(el.nextSibling){
|
401
|
+
if(el.nextSibling.nodeType === 1){
|
402
|
+
nextEl = el.nextSibling;
|
403
|
+
break;
|
404
|
+
}
|
405
|
+
el = el.nextSibling;
|
406
|
+
}
|
407
|
+
if(nextEl && nextEl.className.indexOf(this.messageClass) != -1) this.insertAfterWhatNode.parentNode.removeChild(nextEl);
|
408
|
+
},
|
409
|
+
|
410
|
+
/**
|
411
|
+
* removes the class that has been applied to the field to indicte if valid or not
|
412
|
+
*/
|
413
|
+
removeFieldClass: function(){
|
414
|
+
if(this.element.className.indexOf(this.invalidFieldClass) != -1) this.element.className = this.element.className.split(this.invalidFieldClass).join('');
|
415
|
+
if(this.element.className.indexOf(this.validFieldClass) != -1) this.element.className = this.element.className.split(this.validFieldClass).join(' ');
|
416
|
+
},
|
417
|
+
|
418
|
+
/**
|
419
|
+
* removes the message and the field class
|
420
|
+
*/
|
421
|
+
removeMessageAndFieldClass: function(){
|
422
|
+
this.removeMessage();
|
423
|
+
this.removeFieldClass();
|
424
|
+
}
|
425
|
+
|
426
|
+
} // end of LiveValidation class
|
427
|
+
|
428
|
+
/*************************************** LiveValidationForm class ****************************************/
|
429
|
+
/**
|
430
|
+
* This class is used internally by LiveValidation class to associate a LiveValidation field with a form it is icontained in one
|
431
|
+
*
|
432
|
+
* It will therefore not really ever be needed to be used directly by the developer, unless they want to associate a LiveValidation
|
433
|
+
* field with a form that it is not a child of
|
434
|
+
*/
|
435
|
+
|
436
|
+
/**
|
437
|
+
* handles validation of LiveValidation fields belonging to this form on its submittal
|
438
|
+
*
|
439
|
+
* @var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm
|
440
|
+
*/
|
441
|
+
var LiveValidationForm = function(element){
|
442
|
+
this.initialize(element);
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* namespace to hold instances
|
447
|
+
*/
|
448
|
+
LiveValidationForm.instances = {};
|
449
|
+
|
450
|
+
/**
|
451
|
+
* gets the instance of the LiveValidationForm if it has already been made or creates it if it doesnt exist
|
452
|
+
*
|
453
|
+
* @var element {HTMLFormElement} - a dom element reference to a form
|
454
|
+
*/
|
455
|
+
LiveValidationForm.getInstance = function(element){
|
456
|
+
var rand = Math.random() * Math.random();
|
457
|
+
if(!element.id) element.id = 'formId_' + rand.toString().replace(/\./, '') + new Date().valueOf();
|
458
|
+
if(!LiveValidationForm.instances[element.id]) LiveValidationForm.instances[element.id] = new LiveValidationForm(element);
|
459
|
+
return LiveValidationForm.instances[element.id];
|
460
|
+
}
|
461
|
+
|
462
|
+
LiveValidationForm.prototype = {
|
463
|
+
|
464
|
+
/**
|
465
|
+
* constructor for LiveValidationForm - handles validation of LiveValidation fields belonging to this form on its submittal
|
466
|
+
*
|
467
|
+
* @var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm
|
468
|
+
*/
|
469
|
+
initialize: function(element){
|
470
|
+
this.name = element.id;
|
471
|
+
this.element = element;
|
472
|
+
this.fields = [];
|
473
|
+
// preserve the old onsubmit event
|
474
|
+
this.oldOnSubmit = this.element.onsubmit || function(){};
|
475
|
+
var self = this;
|
476
|
+
this.element.onsubmit = function(e){
|
477
|
+
return (LiveValidation.massValidate(self.fields)) ? self.oldOnSubmit.call(this, e || window.event) !== false : false;
|
478
|
+
}
|
479
|
+
},
|
480
|
+
|
481
|
+
/**
|
482
|
+
* adds a LiveValidation field to the forms fields array
|
483
|
+
*
|
484
|
+
* @var element {LiveValidation} - a LiveValidation object
|
485
|
+
*/
|
486
|
+
addField: function(newField){
|
487
|
+
this.fields.push(newField);
|
488
|
+
},
|
489
|
+
|
490
|
+
/**
|
491
|
+
* removes a LiveValidation field from the forms fields array
|
492
|
+
*
|
493
|
+
* @var victim {LiveValidation} - a LiveValidation object
|
494
|
+
*/
|
495
|
+
removeField: function(victim){
|
496
|
+
var victimless = [];
|
497
|
+
for( var i = 0, len = this.fields.length; i < len; i++){
|
498
|
+
if(this.fields[i] !== victim) victimless.push(this.fields[i]);
|
499
|
+
}
|
500
|
+
this.fields = victimless;
|
501
|
+
},
|
502
|
+
|
503
|
+
/**
|
504
|
+
* destroy this instance and its events
|
505
|
+
*
|
506
|
+
* @var force {Boolean} - whether to force the detruction even if there are fields still associated
|
507
|
+
*/
|
508
|
+
destroy: function(force){
|
509
|
+
// only destroy if has no fields and not being forced
|
510
|
+
if (this.fields.length != 0 && !force) return false;
|
511
|
+
// remove events - set back to previous events
|
512
|
+
this.element.onsubmit = this.oldOnSubmit;
|
513
|
+
// remove from the instances namespace
|
514
|
+
LiveValidationForm.instances[this.name] = null;
|
515
|
+
return true;
|
516
|
+
}
|
517
|
+
|
518
|
+
}// end of LiveValidationForm prototype
|
519
|
+
|
520
|
+
/*************************************** Validate class ****************************************/
|
521
|
+
/**
|
522
|
+
* This class contains all the methods needed for doing the actual validation itself
|
523
|
+
*
|
524
|
+
* All methods are static so that they can be used outside the context of a form field
|
525
|
+
* as they could be useful for validating stuff anywhere you want really
|
526
|
+
*
|
527
|
+
* All of them will return true if the validation is successful, but will raise a ValidationError if
|
528
|
+
* they fail, so that this can be caught and the message explaining the error can be accessed ( as just
|
529
|
+
* returning false would leave you a bit in the dark as to why it failed )
|
530
|
+
*
|
531
|
+
* Can use validation methods alone and wrap in a try..catch statement yourself if you want to access the failure
|
532
|
+
* message and handle the error, or use the Validate::now method if you just want true or false
|
533
|
+
*/
|
534
|
+
|
535
|
+
var Validate = {
|
536
|
+
|
537
|
+
/**
|
538
|
+
* validates that the field has been filled in
|
539
|
+
*
|
540
|
+
* @var value {mixed} - value to be checked
|
541
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
542
|
+
*
|
543
|
+
* paramsObj properties:
|
544
|
+
* failureMessage {String} - the message to show when the field fails validation
|
545
|
+
* (DEFAULT: "Can't be empty!")
|
546
|
+
*/
|
547
|
+
Presence: function(value, paramsObj){
|
548
|
+
var paramsObj = paramsObj || {};
|
549
|
+
var message = paramsObj.failureMessage || "Can't be empty!";
|
550
|
+
if(value === '' || value === null || value === undefined){
|
551
|
+
Validate.fail(message);
|
552
|
+
}
|
553
|
+
return true;
|
554
|
+
},
|
555
|
+
|
556
|
+
/**
|
557
|
+
* validates that the value is numeric, does not fall within a given range of numbers
|
558
|
+
*
|
559
|
+
* @var value {mixed} - value to be checked
|
560
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
561
|
+
*
|
562
|
+
* paramsObj properties:
|
563
|
+
* notANumberMessage {String} - the message to show when the validation fails when value is not a number
|
564
|
+
* (DEFAULT: "Must be a number!")
|
565
|
+
* notAnIntegerMessage {String} - the message to show when the validation fails when value is not an integer
|
566
|
+
* (DEFAULT: "Must be a number!")
|
567
|
+
* wrongNumberMessage {String} - the message to show when the validation fails when is param is used
|
568
|
+
* (DEFAULT: "Must be {is}!")
|
569
|
+
* tooLowMessage {String} - the message to show when the validation fails when minimum param is used
|
570
|
+
* (DEFAULT: "Must not be less than {minimum}!")
|
571
|
+
* tooHighMessage {String} - the message to show when the validation fails when maximum param is used
|
572
|
+
* (DEFAULT: "Must not be more than {maximum}!")
|
573
|
+
* is {Int} - the length must be this long
|
574
|
+
* minimum {Int} - the minimum length allowed
|
575
|
+
* maximum {Int} - the maximum length allowed
|
576
|
+
* onlyInteger {Boolean} - if true will only allow integers to be valid
|
577
|
+
* (DEFAULT: false)
|
578
|
+
*
|
579
|
+
* NB. can be checked if it is within a range by specifying both a minimum and a maximum
|
580
|
+
* NB. will evaluate numbers represented in scientific form (ie 2e10) correctly as numbers
|
581
|
+
*/
|
582
|
+
Numericality: function(value, paramsObj){
|
583
|
+
var suppliedValue = value;
|
584
|
+
var value = Number(value);
|
585
|
+
var paramsObj = paramsObj || {};
|
586
|
+
var minimum = ((paramsObj.minimum) || (paramsObj.minimum == 0)) ? paramsObj.minimum : null;;
|
587
|
+
var maximum = ((paramsObj.maximum) || (paramsObj.maximum == 0)) ? paramsObj.maximum : null;
|
588
|
+
var is = ((paramsObj.is) || (paramsObj.is == 0)) ? paramsObj.is : null;
|
589
|
+
var notANumberMessage = paramsObj.notANumberMessage || "Must be a number!";
|
590
|
+
var notAnIntegerMessage = paramsObj.notAnIntegerMessage || "Must be an integer!";
|
591
|
+
var wrongNumberMessage = paramsObj.wrongNumberMessage || "Must be " + is + "!";
|
592
|
+
var tooLowMessage = paramsObj.tooLowMessage || "Must not be less than " + minimum + "!";
|
593
|
+
var tooHighMessage = paramsObj.tooHighMessage || "Must not be more than " + maximum + "!";
|
594
|
+
if (!isFinite(value)) Validate.fail(notANumberMessage);
|
595
|
+
if (paramsObj.onlyInteger && (/\.0+$|\.$/.test(String(suppliedValue)) || value != parseInt(value)) ) Validate.fail(notAnIntegerMessage);
|
596
|
+
switch(true){
|
597
|
+
case (is !== null):
|
598
|
+
if( value != Number(is) ) Validate.fail(wrongNumberMessage);
|
599
|
+
break;
|
600
|
+
case (minimum !== null && maximum !== null):
|
601
|
+
Validate.Numericality(value, {tooLowMessage: tooLowMessage, minimum: minimum});
|
602
|
+
Validate.Numericality(value, {tooHighMessage: tooHighMessage, maximum: maximum});
|
603
|
+
break;
|
604
|
+
case (minimum !== null):
|
605
|
+
if( value < Number(minimum) ) Validate.fail(tooLowMessage);
|
606
|
+
break;
|
607
|
+
case (maximum !== null):
|
608
|
+
if( value > Number(maximum) ) Validate.fail(tooHighMessage);
|
609
|
+
break;
|
610
|
+
}
|
611
|
+
return true;
|
612
|
+
},
|
613
|
+
|
614
|
+
/**
|
615
|
+
* validates against a RegExp pattern
|
616
|
+
*
|
617
|
+
* @var value {mixed} - value to be checked
|
618
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
619
|
+
*
|
620
|
+
* paramsObj properties:
|
621
|
+
* failureMessage {String} - the message to show when the field fails validation
|
622
|
+
* (DEFAULT: "Not valid!")
|
623
|
+
* pattern {RegExp} - the regular expression pattern
|
624
|
+
* (DEFAULT: /./)
|
625
|
+
* negate {Boolean} - if set to true, will validate true if the pattern is not matched
|
626
|
+
* (DEFAULT: false)
|
627
|
+
*
|
628
|
+
* NB. will return true for an empty string, to allow for non-required, empty fields to validate.
|
629
|
+
* If you do not want this to be the case then you must either add a LiveValidation.PRESENCE validation
|
630
|
+
* or build it into the regular expression pattern
|
631
|
+
*/
|
632
|
+
Format: function(value, paramsObj){
|
633
|
+
var value = String(value);
|
634
|
+
var paramsObj = paramsObj || {};
|
635
|
+
var message = paramsObj.failureMessage || "Not valid!";
|
636
|
+
var pattern = paramsObj.pattern || /./;
|
637
|
+
var negate = paramsObj.negate || false;
|
638
|
+
if(!negate && !pattern.test(value)) Validate.fail(message); // normal
|
639
|
+
if(negate && pattern.test(value)) Validate.fail(message); // negated
|
640
|
+
return true;
|
641
|
+
},
|
642
|
+
|
643
|
+
/**
|
644
|
+
* validates that the field contains a valid email address
|
645
|
+
*
|
646
|
+
* @var value {mixed} - value to be checked
|
647
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
648
|
+
*
|
649
|
+
* paramsObj properties:
|
650
|
+
* failureMessage {String} - the message to show when the field fails validation
|
651
|
+
* (DEFAULT: "Must be a number!" or "Must be an integer!")
|
652
|
+
*/
|
653
|
+
Email: function(value, paramsObj){
|
654
|
+
var paramsObj = paramsObj || {};
|
655
|
+
var message = paramsObj.failureMessage || "Must be a valid email address!";
|
656
|
+
Validate.Format(value, { failureMessage: message, pattern: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i } );
|
657
|
+
return true;
|
658
|
+
},
|
659
|
+
|
660
|
+
/**
|
661
|
+
* validates the length of the value
|
662
|
+
*
|
663
|
+
* @var value {mixed} - value to be checked
|
664
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
665
|
+
*
|
666
|
+
* paramsObj properties:
|
667
|
+
* wrongLengthMessage {String} - the message to show when the fails when is param is used
|
668
|
+
* (DEFAULT: "Must be {is} characters long!")
|
669
|
+
* tooShortMessage {String} - the message to show when the fails when minimum param is used
|
670
|
+
* (DEFAULT: "Must not be less than {minimum} characters long!")
|
671
|
+
* tooLongMessage {String} - the message to show when the fails when maximum param is used
|
672
|
+
* (DEFAULT: "Must not be more than {maximum} characters long!")
|
673
|
+
* is {Int} - the length must be this long
|
674
|
+
* minimum {Int} - the minimum length allowed
|
675
|
+
* maximum {Int} - the maximum length allowed
|
676
|
+
*
|
677
|
+
* NB. can be checked if it is within a range by specifying both a minimum and a maximum
|
678
|
+
*/
|
679
|
+
Length: function(value, paramsObj){
|
680
|
+
var value = String(value);
|
681
|
+
var paramsObj = paramsObj || {};
|
682
|
+
var minimum = ((paramsObj.minimum) || (paramsObj.minimum == 0)) ? paramsObj.minimum : null;
|
683
|
+
var maximum = ((paramsObj.maximum) || (paramsObj.maximum == 0)) ? paramsObj.maximum : null;
|
684
|
+
var is = ((paramsObj.is) || (paramsObj.is == 0)) ? paramsObj.is : null;
|
685
|
+
var wrongLengthMessage = paramsObj.wrongLengthMessage || "Must be " + is + " characters long!";
|
686
|
+
var tooShortMessage = paramsObj.tooShortMessage || "Must not be less than " + minimum + " characters long!";
|
687
|
+
var tooLongMessage = paramsObj.tooLongMessage || "Must not be more than " + maximum + " characters long!";
|
688
|
+
switch(true){
|
689
|
+
case (is !== null):
|
690
|
+
if( value.length != Number(is) ) Validate.fail(wrongLengthMessage);
|
691
|
+
break;
|
692
|
+
case (minimum !== null && maximum !== null):
|
693
|
+
Validate.Length(value, {tooShortMessage: tooShortMessage, minimum: minimum});
|
694
|
+
Validate.Length(value, {tooLongMessage: tooLongMessage, maximum: maximum});
|
695
|
+
break;
|
696
|
+
case (minimum !== null):
|
697
|
+
if( value.length < Number(minimum) ) Validate.fail(tooShortMessage);
|
698
|
+
break;
|
699
|
+
case (maximum !== null):
|
700
|
+
if( value.length > Number(maximum) ) Validate.fail(tooLongMessage);
|
701
|
+
break;
|
702
|
+
default:
|
703
|
+
throw new Error("Validate::Length - Length(s) to validate against must be provided!");
|
704
|
+
}
|
705
|
+
return true;
|
706
|
+
},
|
707
|
+
|
708
|
+
/**
|
709
|
+
* validates that the value falls within a given set of values
|
710
|
+
*
|
711
|
+
* @var value {mixed} - value to be checked
|
712
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
713
|
+
*
|
714
|
+
* paramsObj properties:
|
715
|
+
* failureMessage {String} - the message to show when the field fails validation
|
716
|
+
* (DEFAULT: "Must be included in the list!")
|
717
|
+
* within {Array} - an array of values that the value should fall in
|
718
|
+
* (DEFAULT: [])
|
719
|
+
* allowNull {Bool} - if true, and a null value is passed in, validates as true
|
720
|
+
* (DEFAULT: false)
|
721
|
+
* partialMatch {Bool} - if true, will not only validate against the whole value to check but also if it is a substring of the value
|
722
|
+
* (DEFAULT: false)
|
723
|
+
* caseSensitive {Bool} - if false will compare strings case insensitively
|
724
|
+
* (DEFAULT: true)
|
725
|
+
* negate {Bool} - if true, will validate that the value is not within the given set of values
|
726
|
+
* (DEFAULT: false)
|
727
|
+
*/
|
728
|
+
Inclusion: function(value, paramsObj){
|
729
|
+
var paramsObj = paramsObj || {};
|
730
|
+
var message = paramsObj.failureMessage || "Must be included in the list!";
|
731
|
+
var caseSensitive = (paramsObj.caseSensitive === false) ? false : true;
|
732
|
+
if(paramsObj.allowNull && value == null) return true;
|
733
|
+
if(!paramsObj.allowNull && value == null) Validate.fail(message);
|
734
|
+
var within = paramsObj.within || [];
|
735
|
+
//if case insensitive, make all strings in the array lowercase, and the value too
|
736
|
+
if(!caseSensitive){
|
737
|
+
var lowerWithin = [];
|
738
|
+
for(var j = 0, length = within.length; j < length; ++j){
|
739
|
+
var item = within[j];
|
740
|
+
if(typeof item == 'string') item = item.toLowerCase();
|
741
|
+
lowerWithin.push(item);
|
742
|
+
}
|
743
|
+
within = lowerWithin;
|
744
|
+
if(typeof value == 'string') value = value.toLowerCase();
|
745
|
+
}
|
746
|
+
var found = false;
|
747
|
+
for(var i = 0, length = within.length; i < length; ++i){
|
748
|
+
if(within[i] == value) found = true;
|
749
|
+
if(paramsObj.partialMatch){
|
750
|
+
if(value.indexOf(within[i]) != -1) found = true;
|
751
|
+
}
|
752
|
+
}
|
753
|
+
if( (!paramsObj.negate && !found) || (paramsObj.negate && found) ) Validate.fail(message);
|
754
|
+
return true;
|
755
|
+
},
|
756
|
+
|
757
|
+
/**
|
758
|
+
* validates that the value does not fall within a given set of values
|
759
|
+
*
|
760
|
+
* @var value {mixed} - value to be checked
|
761
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
762
|
+
*
|
763
|
+
* paramsObj properties:
|
764
|
+
* failureMessage {String} - the message to show when the field fails validation
|
765
|
+
* (DEFAULT: "Must not be included in the list!")
|
766
|
+
* within {Array} - an array of values that the value should not fall in
|
767
|
+
* (DEFAULT: [])
|
768
|
+
* allowNull {Bool} - if true, and a null value is passed in, validates as true
|
769
|
+
* (DEFAULT: false)
|
770
|
+
* partialMatch {Bool} - if true, will not only validate against the whole value to check but also if it is a substring of the value
|
771
|
+
* (DEFAULT: false)
|
772
|
+
* caseSensitive {Bool} - if false will compare strings case insensitively
|
773
|
+
* (DEFAULT: true)
|
774
|
+
*/
|
775
|
+
Exclusion: function(value, paramsObj){
|
776
|
+
var paramsObj = paramsObj || {};
|
777
|
+
paramsObj.failureMessage = paramsObj.failureMessage || "Must not be included in the list!";
|
778
|
+
paramsObj.negate = true;
|
779
|
+
Validate.Inclusion(value, paramsObj);
|
780
|
+
return true;
|
781
|
+
},
|
782
|
+
|
783
|
+
/**
|
784
|
+
* validates that the value matches that in another field
|
785
|
+
*
|
786
|
+
* @var value {mixed} - value to be checked
|
787
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
788
|
+
*
|
789
|
+
* paramsObj properties:
|
790
|
+
* failureMessage {String} - the message to show when the field fails validation
|
791
|
+
* (DEFAULT: "Does not match!")
|
792
|
+
* match {String} - id of the field that this one should match
|
793
|
+
*/
|
794
|
+
Confirmation: function(value, paramsObj){
|
795
|
+
if(!paramsObj.match) throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");
|
796
|
+
var paramsObj = paramsObj || {};
|
797
|
+
var message = paramsObj.failureMessage || "Does not match!";
|
798
|
+
var match = paramsObj.match.nodeName ? paramsObj.match : document.getElementById(paramsObj.match);
|
799
|
+
if(!match) throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '" + paramsObj.match + "'!");
|
800
|
+
if(value != match.value){
|
801
|
+
Validate.fail(message);
|
802
|
+
}
|
803
|
+
return true;
|
804
|
+
},
|
805
|
+
|
806
|
+
/**
|
807
|
+
* validates that the value is true (for use primarily in detemining if a checkbox has been checked)
|
808
|
+
*
|
809
|
+
* @var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox)
|
810
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
811
|
+
*
|
812
|
+
* paramsObj properties:
|
813
|
+
* failureMessage {String} - the message to show when the field fails validation
|
814
|
+
* (DEFAULT: "Must be accepted!")
|
815
|
+
*/
|
816
|
+
Acceptance: function(value, paramsObj){
|
817
|
+
var paramsObj = paramsObj || {};
|
818
|
+
var message = paramsObj.failureMessage || "Must be accepted!";
|
819
|
+
if(!value){
|
820
|
+
Validate.fail(message);
|
821
|
+
}
|
822
|
+
return true;
|
823
|
+
},
|
824
|
+
|
825
|
+
/**
|
826
|
+
* validates against a custom function that returns true or false (or throws a Validate.Error) when passed the value
|
827
|
+
*
|
828
|
+
* @var value {mixed} - value to be checked
|
829
|
+
* @var paramsObj {Object} - parameters for this particular validation, see below for details
|
830
|
+
*
|
831
|
+
* paramsObj properties:
|
832
|
+
* failureMessage {String} - the message to show when the field fails validation
|
833
|
+
* (DEFAULT: "Not valid!")
|
834
|
+
* against {Function} - a function that will take the value and object of arguments and return true or false
|
835
|
+
* (DEFAULT: function(){ return true; })
|
836
|
+
* args {Object} - an object of named arguments that will be passed to the custom function so are accessible through this object within it
|
837
|
+
* (DEFAULT: {})
|
838
|
+
*/
|
839
|
+
Custom: function(value, paramsObj){
|
840
|
+
var paramsObj = paramsObj || {};
|
841
|
+
var against = paramsObj.against || function(){ return true; };
|
842
|
+
var args = paramsObj.args || {};
|
843
|
+
var message = paramsObj.failureMessage || "Not valid!";
|
844
|
+
if(!against(value, args)) Validate.fail(message);
|
845
|
+
return true;
|
846
|
+
},
|
847
|
+
|
848
|
+
/**
|
849
|
+
* validates whatever it is you pass in, and handles the validation error for you so it gives a nice true or false reply
|
850
|
+
*
|
851
|
+
* @var validationFunction {Function} - validation function to be used (ie Validation.validatePresence )
|
852
|
+
* @var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox)
|
853
|
+
* @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary
|
854
|
+
*/
|
855
|
+
now: function(validationFunction, value, validationParamsObj){
|
856
|
+
if(!validationFunction) throw new Error("Validate::now - Validation function must be provided!");
|
857
|
+
var isValid = true;
|
858
|
+
try{
|
859
|
+
validationFunction(value, validationParamsObj || {});
|
860
|
+
} catch(error) {
|
861
|
+
if(error instanceof Validate.Error){
|
862
|
+
isValid = false;
|
863
|
+
}else{
|
864
|
+
throw error;
|
865
|
+
}
|
866
|
+
}finally{
|
867
|
+
return isValid
|
868
|
+
}
|
869
|
+
},
|
870
|
+
|
871
|
+
/**
|
872
|
+
* shortcut for failing throwing a validation error
|
873
|
+
*
|
874
|
+
* @var errorMessage {String} - message to display
|
875
|
+
*/
|
876
|
+
fail: function(errorMessage){
|
877
|
+
throw new Validate.Error(errorMessage);
|
878
|
+
},
|
879
|
+
|
880
|
+
Error: function(errorMessage){
|
881
|
+
this.message = errorMessage;
|
882
|
+
this.name = 'ValidationError';
|
883
|
+
}
|
884
|
+
}
|