PageRenderTime 4ms CodeModel.GetById 2ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

/js/veto.js

https://bitbucket.org/jscott1989/veto
JavaScript | 1122 lines | 750 code | 206 blank | 166 comment | 243 complexity | 6fadf23c666fffab4c7de4f6f555efef MD5 | raw file
   1var default_vtform_configuration = {
   2	 error_display_section: false
   3	,error_display_inline: true
   4	,error_display_inline_position: 'prefix'
   5	,error_display_alert: false
   6	,error_class: 'vtError'
   7	,focus: 'first_error_field' // May be 'first_error_field', 'error_section', or false for no focus
   8	,date_format: 'DD/MM/YYYY'
   9};
  10
  11function vtForm(name, configuration) {
  12    // If it's being called as a function - call the constructor
  13    // (allow for shorthand)
  14    if (this.window) return new vtForm(name, configuration);
  15    
  16    
  17    // Member variables
  18	this.sets = new Array();
  19    this.fields = new Array();
  20	this.errors = new Array();
  21    // end member variables
  22    
  23    // Constructor
  24    this.name = name;
  25	this.form_name = name;
  26	this.full_name = name;
  27    this.configuration = array_merge(default_vtform_configuration, (configuration || new Array())) ;
  28    // end constructor
  29    
  30    // Functions
  31    this.add = function(field) {
  32		if (typeof(field.required) == 'function') // Assume Required will never be removed or renamed - it is pretty basic
  33			return this.addField(field);
  34		else // Assume set
  35			return this.addSet(field);
  36    };
  37
  38	this.addField = function(field) {
  39		this.fields.push(field);
  40		return this;
  41	}
  42	
  43	this.addSet = function(set) {
  44		this.sets.push(set);
  45		set.form_name = this.name;
  46		set.full_name = this.name + '-' + set.name;
  47		set.configuration = this.configuration; // Pass it down for now - temporary! TODO Change this so it checks if it is already default
  48		return this;
  49	}
  50	
  51	this.validateSets = function() {
  52		var errors = new Object();
  53		
  54		if (this.sets) {
  55			for (set in this.sets) {
  56				this.sets[set].validate();
  57				errors = array_merge_recursive(errors, this.sets[set].errors);
  58			}
  59		}
  60		return errors;
  61	}
  62
  63    this.validate = function(fields, focus) {
  64        var errors = new Object();
  65        
  66        if (!fields) {
  67            fields = [];
  68            for (f in this.fields) {
  69                fields.push(this.fields[f]);
  70            }
  71        }
  72
  73		// Remove error class from block
  74		if (this.configuration.error_display_section) {
  75			remove_class(document.getElementById(this.full_name + '-errors'), 'vtErrors');
  76			remove_class(document.getElementById(this.full_name + '-errors'), 'vtNoErrors');
  77		}
  78
  79        for(f in fields) {
  80            var field = fields[f];
  81            // Clear the errors
  82            remove_class(document.getElementsByName(this.form_name + '[' + field.name +']')[0], this.configuration.error_class);
  83            
  84            if (document.getElementById('vt_' + this.form_name + '_' + field.name + '-error')) { 
  85                document.getElementById('vt_' + this.form_name + '_' + field.name + '-error').innerHTML = '';                                
  86                remove_class(document.getElementById('vt_' + this.form_name + '_' + field.name + '-error'), 'vtErrors');
  87                add_class(document.getElementById('vt_' + this.form_name + '_' + field.name + '-error'), 'vtNoErrors');
  88            }
  89            
  90            // Load the new errors into the array
  91            errors = array_merge_recursive(errors, field.validate(document.getElementById('vt_' + this.form_name), this.configuration));
  92        }
  93
  94        this.errors = errors;
  95		var set_errors = this.validateSets();
  96        if (is_empty(errors) && is_empty(set_errors)) {
  97			if (this.configuration.error_display_section) add_class(document.getElementById(this.full_name + '-errors'), 'vtNoErrors');
  98        	return true;
  99        }
 100        
 101        // Output the errors
 102        
 103        if (this.configuration.error_display_section) var error_display_section = '<ul>';
 104        
 105        if (this.configuration.error_display_alert) var error_display_alert = '';
 106        
 107            for(field in errors) {
 108                add_class(document.getElementsByName(this.form_name + '[' + field + ']')[0], this.configuration.error_class);
 109                
 110                if (is_array(errors[field])) {
 111                    
 112                    for(error in errors[field]) {
 113                        
 114                        if (this.configuration.error_display_section) error_display_section += '<li>' + errors[field][error] + '</li>';
 115                        if (this.configuration.error_display_alert) error_display_alert += errors[field][error] + '\n';
 116                        if (document.getElementById('vt_' + this.form_name + '_' + field + '-error')) { 
 117                            document.getElementById('vt_' + this.form_name + '_' + field + '-error').innerHTML = errors[field][error];
 118                            remove_class(document.getElementById('vt_' + this.form_name + '_' + field + '-error'), 'vtNoErrors');
 119                            add_class(document.getElementById('vt_' + this.form_name + '_' + field + '-error'), 'vtErrors');
 120                    }
 121                        
 122                        
 123                    }
 124                } else {
 125                        if (this.configuration.error_display_section) error_display_section += '<li>' + errors[field] + '</li>';
 126                        if (this.configuration.error_display_alert) error_display_alert += errors[field] + '\n';
 127                        if (document.getElementById('vt_' + this.name + '_' + field + '-error')) {
 128                            document.getElementById('vt_' + this.name + '_' + field + '-error').innerHTML = errors[field];
 129                            remove_class(document.getElementById('vt_' + this.name + '_' + field + '-error'), 'vtNoErrors');
 130                            add_class(document.getElementById('vt_' + this.name + '_' + field + '-error'), 'vtErrors');
 131                    }
 132                }
 133            }
 134            
 135            if (this.configuration.error_display_section && document.getElementById(this.full_name + '-errors')) {
 136				add_class(document.getElementById(this.full_name + '-errors'), 'vtErrors');
 137                document.getElementById(this.full_name + '-errors').innerHTML = error_display_section + '</ul>';
 138			}
 139            if (this.configuration.error_display_alert)
 140                alert(error_display_alert);
 141
 142            if (!focus) {
 143                focus = this.configuration.focus;
 144            }
 145
 146            if (focus == 'first_error_field')
 147                // Get the first key - TODO there must be a better way than this
 148                for (var field in errors) {
 149                    document.getElementsByName(this.form_name + '[' + field + ']')[0].focus();
 150                    break;
 151                }
 152            else if (focus == 'error_section')
 153                window.location = '#' + this.form_name + '-errors';
 154
 155        return false;
 156    }
 157    // end functions
 158    
 159    return this;
 160}
 161
 162vtSet = vtForm;
 163
 164function vtField(name) {
 165    // If it's being called as a function - call the constructor
 166    // (allow for shorthand)
 167    if (this.window) return new vtField(name);
 168    
 169    
 170    // Member variables
 171    this.validations = new Array();
 172    // end member variables
 173    
 174    // Constructor
 175    this.name = name;
 176    // end constructor
 177    
 178    // Functions
 179    this.add = function(validation) {
 180       this.validations.push(validation);
 181       return this;
 182    };
 183    
 184    this.validate = function(form, configuration) {
 185        var errors = new Object();
 186        for(validation in this.validations) {
 187            errors = array_merge_recursive(errors, this.validations[validation].validate(form, this.name, configuration));
 188        }
 189        return errors;
 190    }
 191    
 192    
 193        // Shorthand
 194        
 195        this.required = function(options, error) {
 196			return this.add(vtRequired(options, error));
 197		}
 198		
 199        this.matches= function(regex, options, error) {
 200			return this.add(vtMatches(regex, options, error));
 201		}
 202		
 203		this.lengthBetween = function(min, max, options, error) {
 204		    return this.add(vtLengthBetween(min, max, options, error));
 205		}
 206		
 207		this.minimumLength = function(min, options, error) {
 208		    return this.add(vtMinimumLength(min, options, error));
 209		}
 210		
 211		this.maximumLength = function(max, options, error) {
 212		    return this.add(vtMaximumLength(max, options, error));
 213		}
 214		
 215		this.number = function(options, error) {
 216		    return this.add(vtNumber(options, error));
 217		}
 218		
 219		this.between = function(min, max, options, error) {
 220		    return this.add(vtBetween(min, max, options, error));
 221		}
 222		
 223		this.minimum = function(min, options, error) {
 224		    return this.add(vtMinimum(min, options, error));
 225		}
 226		
 227		this.maximum = function(max, options, error) {
 228		    return this.add(vtMaximum(max, options, error));
 229		}
 230		
 231		this.options = function(options, _options, error) {
 232		    return this.add(vtOptions(options, _options, error));
 233		}
 234		
 235		this.notOptions = function(options, _options, error) {
 236		    return this.add(vtNotOptions(options, _options, error));
 237		}
 238		
 239		this.notEqual = function(to, options, error) {
 240		    return this.add(vtNotEqual(to, options, error))
 241		}
 242		
 243		this.email = function(options, error) {
 244		    return this.add(vtEmail(options, error));
 245		}
 246		
 247		this.date = function(options, error) {
 248		    return this.add(vtDate(options, error));
 249		}
 250		
 251		this.dateBetween = function(earliest, latest, options, error) {
 252		    return this.add(vtDateBetween(earliest, latest, options, error));
 253		}
 254		
 255		this.dateEarliest = function(earliest, options, error) {
 256		    return this.add(vtDateEarliest(earliest, options, error));
 257		}
 258		
 259		this.dateLatest = function(latest, options, error) {
 260		    return this.add(vtDateLatest(latest, options, error));
 261		}
 262		
 263		this.custom = function(javascript_function, javascript_array) {
 264		    return this.add(vtCustom(javascript_function, javascript_array));
 265		}
 266		
 267		this.sameAs = function(other_function, options, error) {
 268		    return this.add(vtSameAs(other_function, options, error));
 269		}
 270		
 271		// end shorthand
 272    // end functions
 273    
 274    return this;
 275}
 276
 277// Validations
 278
 279    function vtRequiredValidation(options, error) {
 280        // If it's being called as a function - call the constructor
 281        // (allow for shorthand)
 282        if (this.window) return new vtRequiredValidation(options, error);
 283    
 284    
 285        // Member variables
 286        // end member variables
 287    
 288        // Constructor
 289        if (is_string(options)) {
 290            error = options
 291            options = new Object()
 292        }
 293        this.options = options
 294        this.error = error || '%n is required';
 295        // end constructor
 296    
 297        // Functions
 298        this.validate = function(form, key, configuration) {
 299            if (getValueFromField(form.name + '[' + key + ']').length == 0) {
 300                var ret = new Object();
 301                ret[key] = [this.error.replace(/%n/, labelize(key))];
 302                return ret;  
 303            }
 304            return {};
 305        };
 306        // end functions
 307    
 308        return this;
 309    }
 310
 311    var vtRequired = vtRequiredValidation;
 312
 313    function vtRegularExpressionValidation(regex, options, error) {
 314        // If it's being called as a function - call the constructor
 315        // (allow for shorthand)
 316        if (this.window) return new vtRegularExpressionValidation(regex, options, error);
 317    
 318    
 319        // Member variables
 320        // end member variables
 321    
 322        // Constructor
 323        if (is_string(options)) {
 324            error = options
 325            options = new Object()
 326        }
 327        this.options = options
 328        this.error = error || '%n must match ' + regex;
 329        this.regex = new RegExp(regex);
 330        // end constructor
 331    
 332        // Functions
 333        this.validate = function(form, key, configuration) {
 334            if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 335            
 336            if (getValueFromField(form.name + '[' + key + ']').match(this.regex)) return {};
 337            else {
 338                var ret = new Object();
 339                ret[key] = [this.error.replace(/%n/, labelize(key))];
 340                return ret; 
 341            }
 342        };
 343        // end functions
 344    
 345        return this;
 346    }
 347
 348    var vtRegexValidation = vtRegularExpressionValidation;
 349    var vtMatches = vtRegexValidation;
 350    
 351    function vtEmailAddressValidation(options, error) {
 352        // If it's being called as a function - call the constructor
 353        // (allow for shorthand)
 354        if (this.window) return new vtEmailAddressValidation(options, error);
 355    
 356    
 357        // Member variables
 358        // end member variables
 359    
 360        // Constructor
 361        if (is_string(options)) {
 362            error = options
 363            options = new Object()
 364        }
 365        this.options = options
 366        this.error = error || '%n must be a valid email address'
 367        // end constructor
 368    
 369        // Functions
 370        this.validate = function(form, key, configuration) {
 371            if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 372            
 373            if (getValueFromField(form.name + '[' + key + ']').match(/^.*@.*\..*$/)) return {};
 374            else {
 375                var ret = new Object();
 376                ret[key] = [this.error.replace(/%n/, labelize(key))];
 377                return ret; 
 378            }
 379        };
 380        // end functions
 381    
 382        return this;
 383    }
 384
 385    var vtEmailValidation = vtEmailAddressValidation;
 386    var vtEmail = vtEmailValidation;
 387    
 388    function vtNumberValidation(options, error) {
 389        // If it's being called as a function - call the constructor
 390        // (allow for shorthand)
 391        if (this.window) return new vtNumberValidation(options, error);
 392    
 393        // Member variables
 394        // end member variables
 395    
 396        // Constructor
 397        if (is_string(options)) {
 398            error = options
 399            options = new Object()
 400        }
 401        this.options = options
 402        this.error = error || '%n must be a number';
 403		var number_characters = '\\d'
 404		
 405		if (this.options.separator != undefined) {
 406			if (this.options.separator === true) {
 407				// Default to comma separator
 408				number_characters = '[\\d,]';
 409			} else {
 410				number_characters = '[\\d' + RegExp.escape(this.options.separator) + ']';
 411			}
 412		}
 413		
 414		var regex_start = '^';
 415		if (this.options.negative != undefined) {
 416			if (this.options.negative === true) {
 417				// Default to - negative
 418				regex_start = '^-?';
 419			} else {
 420				regex_start = '^' + RegExp.escape(this.options.negative) + '?';
 421			}
 422		}
 423		
 424		var regex_middle = number_characters + '+';
 425		if (this.options.decimal) {
 426			if (this.options.decimal === true) {
 427				// Default to . decimal
 428				regex_middle = number_characters + '+(\\.' + number_characters + '+)?';
 429			} else {
 430				regex_middle = number_characters + '+(' + RegExp.escape(this.options.decimal) + number_characters + '+)?';
 431			}
 432		}
 433        this.regex = new RegExp(regex_start + regex_middle + '$');
 434        // end constructor
 435    
 436        // Functions
 437        this.validate = function(form, key, configuration) {
 438            if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 439            
 440            if (getValueFromField(form.name + '[' + key + ']').match(this.regex)) return {};
 441            else {
 442                var ret = new Object();
 443                ret[key] = [this.error.replace(/%n/, labelize(key))];
 444                return ret; 
 445            }
 446        };
 447        // end functions
 448    
 449        return this;
 450    }
 451
 452    var vtNumber = vtNumberValidation;
 453
 454    function vtLengthValidation(min, max, options, error) {
 455        // If it's being called as a function - call the constructor
 456        // (allow for shorthand)
 457        if (this.window) return new vtLengthValidation(min, max, options, error);
 458    
 459    
 460        // Member variables
 461        // end member variables
 462    
 463        // Constructor
 464        if (is_string(options)) {
 465            error = options
 466            options = new Object()
 467        }
 468        this.options = options
 469        if (min && max) var default_error = '%n must be between ' + min + ' and ' + max + ' characters long';
 470		else if (min) var default_error = '%n must be at least ' + min + ' characters long';
 471		else var default_error = '%n must be at most ' + max + ' characters long';
 472        this.error = error || default_error;
 473        this.min = min;
 474        this.max = max;
 475        // end constructor
 476    
 477        // Functions
 478        this.validate = function(form, key, configuration) {
 479            if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 480            
 481            var data = getValueFromField(form.name + '[' + key + ']');
 482            
 483            if (this.options.ignore != undefined) {
 484                if (is_array(this.options.ignore)) {
 485                    for (i in this.options.ignore) {
 486                        data = data.replace(new RegExp(RegExp.escape(this.options.ignore[i]), "g"), '');
 487                    }
 488                } else {
 489                    for (var i = 0; i < this.options.ignore.length; i++)
 490						data = data.replace(new RegExp(RegExp.escape(this.options.ignore.substr(i, i+1)), "g"), '');
 491                }
 492            }
 493            
 494            if (this.min && data.length < this.min || this.max && data.length > this.max)   {
 495                var ret = new Object();
 496                ret[key] = [this.error.replace(/%n/, labelize(key))];
 497                return ret; 
 498            } else return {};
 499        };
 500        // end functions
 501    
 502        return this;
 503    }
 504
 505    var vtLengthBetween = vtLengthValidation;
 506    var vtMinimumLength = function(min, error) { return vtLengthBetween(min, null, error); };
 507    var vtMaximumLength = function(max, error) { return vtLengthBetween(null, max, error); };
 508    
 509    function vtOptionsValidation(options, _options, error) {
 510        // If it's being called as a function - call the constructor
 511        // (allow for shorthand)
 512        if (this.window) return new vtOptionsValidation(options, _options, error);
 513    
 514        // Member variables
 515        // end member variables
 516    
 517        // Constructor
 518        if (is_string(_options)) {
 519            error = _options
 520            _options = new Object()
 521        }
 522        this._options = _options
 523        
 524        if (_options.should_be_in == undefined) _options.should_be_in = true;
 525        
 526        if (_options.should_be_in) var default_error = '%n must be in the options';
 527		else var default_error = '%n must not be in the options';
 528        this.error = error || default_error;
 529        this.options = options;
 530        // end constructor
 531    
 532        // Functions
 533        this.validate = function(form, key, configuration) {
 534            if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 535            
 536            var value = getValueFromField(form.name + '[' + key + ']');
 537            
 538            for (var option in this.options){
 539                if (value == this.options[option]) {
 540                    if (this._options.should_be_in) return {}
 541                    else {
 542                        var ret = new Object();
 543                        ret[key] = [this.error.replace(/%n/, labelize(key))];
 544                        return ret;
 545                    }
 546                }
 547            }
 548            if (this._options.should_be_in) {
 549                var ret = new Object();
 550                ret[key] = [this.error.replace(/%n/, labelize(key))];
 551                return ret;
 552            } else return {}
 553        };
 554        // end functions
 555    
 556        return this;
 557    }
 558    var vtOptions = vtOptionsValidation;
 559    var vtNotOptions = function(options, _options, error) { return vtOptions(options, array_merge({should_be_in: false}, _options), error); };
 560    
 561    function vtNumberRangeValidation(min, max, options, error) {
 562         // If it's being called as a function - call the constructor
 563         // (allow for shorthand)
 564         if (this.window) return new vtNumberRangeValidation(min, max, options, error);
 565
 566
 567         // Member variables
 568         // end member variables
 569
 570         // Constructor
 571         if (is_string(options)) {
 572             error = options
 573             options = new Object()
 574         }
 575         this.options = options
 576         if (min && max) var default_error = '%n must be between ' + min + ' and ' + max;
 577 		else if (min) var default_error = '%n must be at least ' + min;
 578 		else var default_error = '%n must be at most ' + max;
 579         this.error = error || default_error;
 580         this.min = min;
 581         this.max = max;
 582         // end constructor
 583
 584         // Functions
 585         this.validate = function(form, key, configuration) {
 586             if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 587
 588             if (this.min && parseInt(getValueFromField(form.name + '[' + key + ']')) < this.min || this.max && parseInt(getValueFromField(form.name + '[' + key + ']')) > this.max)   {
 589                 var ret = new Object();
 590                 ret[key] = [this.error.replace(/%n/, labelize(key))];
 591                 return ret; 
 592             } else return {};
 593         };
 594         // end functions
 595
 596         return this;
 597     }
 598
 599     var vtBetween = vtNumberRangeValidation;
 600     var vtMinimum = function(min, options, error) { return vtBetween(min, null, options, error); };
 601     var vtMaximum = function(max, options, error) { return vtBetween(null, max, options, error); };
 602
 603     function vtDateValidation(options, error) {
 604          // If it's being called as a function - call the constructor
 605          // (allow for shorthand)
 606          if (this.window) return new vtDateValidation(options, error);
 607
 608
 609          // Member variables
 610          // end member variables
 611
 612          // Constructor
 613          if (is_string(options)) {
 614              error = options
 615              options = new Object()
 616          }
 617          this.options = options
 618          this.error = error || '%n must be a date';
 619          // end constructor
 620
 621          // Functions
 622          this.validate = function(form, key, configuration) {
 623              if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 624            
 625              // First check the format
 626              date = extract_date(getValueFromField(form.name + '[' + key + ']'), configuration.date_format);
 627              if (!date) {
 628                  var ret = new Object();
 629                  ret[key] = [this.error.replace(/%n/, labelize(key))];
 630                  return ret;
 631              }
 632              
 633              if (checkdate(date.month, date.day, date.year)) return {}
 634               
 635              var ret = new Object();
 636              ret[key] = [this.error.replace(/%n/, labelize(key))];
 637              return ret;
 638          };
 639          // end functions
 640
 641          return this;
 642      }
 643
 644      var vtDate = vtDateValidation;
 645      
 646      function vtDateRangeValidation(earliest, latest, options, error) {
 647           // If it's being called as a function - call the constructor
 648           // (allow for shorthand)
 649           if (this.window) return new vtDateRangeValidation(earliest, latest, options, error);
 650
 651
 652           // Member variables
 653           // end member variables
 654
 655           // Constructor
 656           if (is_string(options)) {
 657               error = options
 658               options = new Object()
 659           }
 660           this.options = options
 661           if (earliest && latest) var default_error = '%n must be between ' + earliest + ' and ' + latest;
 662   		   else if (earliest) var default_error = '%n must be at least ' + earliest;
 663   		   else var default_error = '%n must be at most ' + latest;
 664           this.error = error || default_error;
 665           this.earliest = earliest;
 666           this.latest = latest;
 667           // end constructor
 668
 669           // Functions
 670           this.validate = function(form, key, configuration) {
 671               if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 672               date = extract_date(getValueFromField(form.name + '[' + key + ']'), configuration.date_format);
 673               // Only validate if it is a date
 674               if (!date) return {};
 675               
 676               var date_object = new Date();
 677               date_object.setFullYear(date.year, date.month, date.day);
 678               if (this.earliest) {
 679                   var earliest_date = extract_date(this.earliest, configuration.date_format);
 680                   if (!earliest_date) return {};
 681                   earliest_date_object = new Date()
 682                   earliest_date_object.setFullYear(earliest_date.year, earliest_date.month, earliest_date.day);
 683                   if (date_object.getTime() < earliest_date_object.getTime()) {
 684                       var ret = new Object();
 685                       ret[key] = [this.error.replace(/%n/, labelize(key))];
 686                       return ret;
 687                   }
 688               }
 689               if (this.latest) {
 690                   var latest_date = extract_date(this.latest, configuration.date_format);
 691                   if (!latest_date) return {};
 692                   latest_date_object = new Date()
 693                   latest_date_object.setFullYear(latest_date.year, latest_date.month, latest_date.day);
 694                   if (date_object.getTime() > latest_date_object.getTime()) {
 695                       var ret = new Object();
 696                       ret[key] = [this.error.replace(/%n/, labelize(key))];
 697                       return ret;
 698                   }
 699               }
 700               
 701               return {};
 702           };
 703           // end functions
 704
 705           return this;
 706       }
 707
 708       var vtDateBetween = vtDateRangeValidation;
 709       var vtDateEarliest = function(earliest, options, error) { return vtBetween(earliest, null, options, error); };
 710       var vtDateLatest = function(latest, options, error) { return vtBetween(null, latest, options, error); };
 711
 712       function vtCustomValidation(javascript_function, javascript_array) {
 713            // If it's being called as a function - call the constructor
 714            // (allow for shorthand)
 715            if (this.window) return new vtCustomValidation(javascript_function, javascript_array);
 716
 717
 718            // Member variables
 719            // end member variables
 720
 721            // Constructor
 722            this.javascript_function = javascript_function;
 723            this.javascript_array = javascript_array
 724            // end constructor
 725
 726            // Functions
 727            
 728            // The custom function gets given the form name, the current key, and configuration
 729            // To get the value use getValueFromField(form.name + '[' + key + ']')
 730            // Return an "associative array" like:
 731            //  var ret = new Object();
 732            //  ret[key] = "ERROR!";
 733            //  return ret;
 734            // if there is an error, otherwise return {}
 735            this.validate = function(form, key, configuration) {
 736                if (getValueFromField(form.name + '[' + key + ']').length == 0) return {};
 737                return window[this.javascript_function](form, key, configuration, this.javascript_array);
 738            };
 739            // end functions
 740
 741            return this;
 742        }
 743
 744        var vtCustom = vtCustomValidation;
 745        
 746        function vtSameAsValidation(other_field, options, error) {
 747             // If it's being called as a function - call the constructor
 748             // (allow for shorthand)
 749             if (this.window) return new vtSameAsValidation(other_field, options, error);
 750
 751
 752             // Member variables
 753             // end member variables
 754
 755             // Constructor
 756             if (is_string(options)) {
 757                 error = options
 758                 options = new Object()
 759             }
 760             this.options = options
 761             this.other_field = other_field;
 762             this.error = error || '%n must be the same as ' + labelize(other_field);
 763             // end constructor
 764
 765             // Functions
 766             this.validate = function(form, key, configuration) {
 767                 if (getValueFromField(form.name + '[' + key + ']') == document.getElementsByName(form.name + '[' + this.other_field + ']')[0].value) {
 768                    return {};
 769                 }
 770                 var ret = new Object();
 771                 ret[key] = [this.error.replace(/%n/, labelize(key))];
 772                 return ret;
 773             };
 774             // end functions
 775
 776             return this;
 777         }
 778
 779         var vtSameAs = vtSameAsValidation;
 780         
 781         
 782         function vtOptionalValidation(validation, field, requirement) {
 783             // If it's being called as a function - call the constructor
 784             // (allow for shorthand)
 785             if (this.window) return new vtOptionalValidation(validation, field, requirement);
 786
 787
 788             // Member variables
 789             // end member variables
 790
 791             // Constructor
 792             this.validation = validation;
 793             this.field = field;
 794             this.requirement = requirement;
 795             // end constructor
 796
 797             // Functions
 798             this.validate = function(form, key, configuration) {
 799                if (!is_empty(this.requirement.validate(form, this.field, configuration))) {
 800                    return this.validation.validate(form, key, configuration);
 801                }
 802                return [];
 803             };
 804             // end functions
 805
 806             return this;
 807         }
 808
 809         var vtOptional = vtOptionalValidation;
 810
 811         function vtReverseValidation(validation, error) {
 812             // If it's being called as a function - call the constructor
 813             // (allow for shorthand)
 814             if (this.window) return new vtReverseValidation(validation, error);
 815
 816
 817             // Member variables
 818             // end member variables
 819
 820             // Constructor
 821             this.validation = validation;
 822             this.error = error || "%n failed the reverse validation" // TODO: better error
 823             // end constructor
 824
 825             // Functions
 826             this.validate = function(form, key, configuration) {
 827                if (!is_empty(this.validation.validate(form, key, configuration))) {
 828                    return [];
 829                }
 830                var ret = new Object();
 831                ret[key] = [this.error.replace(/%n/, labelize(key))];
 832                return ret;
 833             };
 834             // end functions
 835
 836             return this;
 837         }
 838
 839         var vtReverse = vtReverseValidation;
 840// end validations
 841
 842
 843function labelize(string) {
 844    return ucwords(string.replace(/_/g,' '));
 845}
 846
 847function is_empty(o) {
 848    var i, v;
 849    if (typeof(o) === 'object') {
 850        for (i in o) {
 851            v = o[i];
 852            if (v !== undefined && typeof(v) !== 'function') {
 853                return false;
 854            }
 855        }
 856    }
 857    return true;
 858}
 859
 860// TODO: The following three functions may just be hiding coding problems
 861// look into them and determine if they can be improved
 862function has_class(element,value) {
 863	if (element !== undefined && element.className !== undefined)
 864		return element.className.match(new RegExp('(\\s|^)'+value+'(\\s|$)'));
 865}
 866 
 867function add_class(element, value) {
 868	if (element && element !== undefined && element.className !== undefined)
 869		if (!has_class(element,value)) element.className += " "+value;
 870}
 871 
 872function remove_class(element, value) {
 873	if (element && element != undefined && element.className !== undefined) {
 874		if (has_class(element,value)) {
 875	    	var reg = new RegExp('(\\s|^)+'+value+'(\\s|$)+');
 876			element.className=element.className.replace(reg,' ');
 877		}
 878	}
 879}
 880
 881function extract_date(date, format) {
 882    var regex = new RegExp(format.replace("DD", "(\\d+)").replace("MM", "(\\d+)").replace("YYYY", "(\\d+)"));
 883    // To get around javascripts lack of named parameters - figure out the order
 884    // todo: make this more efficient - this is a stupid way there must be something easier
 885    var dd_pos = format.indexOf('DD');
 886    var mm_pos = format.indexOf('MM');
 887    var yyyy_pos = format.indexOf('YYYY');
 888    var matches = regex.exec(date);
 889    if (!matches) return null;
 890    if (dd_pos < mm_pos) {
 891        if (mm_pos < yyyy_pos) {
 892            // DD - MM - YYYY
 893            var day = matches[1];
 894            var month = matches[2];
 895            var year = matches[3];
 896        } else {
 897            if (dd_pos < yyyy_pos) {
 898                // DD - YYYY - MM
 899                var day = matches[1];
 900                var month = matches[3];
 901                var year = matches[2];
 902            } else {
 903                // YYYY - DD - MM
 904                var day = matches[2];
 905                var month = matches[3];
 906                var year = matches[1];
 907            }
 908        }
 909    } else {
 910        if (dd_post < yyyy_pos) {
 911            // MM - DD - YYYY
 912            var day = matches[2];
 913            var month = matches[1];
 914            var year = matches[3];
 915        } else {
 916            if (mm_pos < yyyy_pos) {
 917                // MM - YYYY - DD
 918                var day = matches[3];
 919                var month = matches[1];
 920                var year = matches[2];
 921            } else {
 922                // YYYY - MM - DD
 923                var day = matches[3];
 924                var month = matches[2];
 925                var year = matches[1];
 926            }
 927        }
 928    }
 929    var ret = new Object();
 930    ret.month = month;
 931    ret.day = day;
 932    ret.year = year;
 933    return ret;
 934}
 935
 936/**
 937 * Escape Regular Expressions
 938 */
 939RegExp.escape = function(text) {
 940  if (!arguments.callee.sRE) {
 941    var specials = [
 942      '/', '.', '*', '+', '?', '|',
 943      '(', ')', '[', ']', '{', '}', '\\'
 944    ];
 945    arguments.callee.sRE = new RegExp(
 946      '(\\' + specials.join('|\\') + ')', 'g'
 947    );
 948  }
 949  return text.replace(arguments.callee.sRE, '\\$1');
 950}
 951
 952
 953/**
 954 * The following functions are taken from the MIT licensed PHP.js
 955 * The license is available in licenses/php.js.txt
 956 */
 957
 958function ucwords(string) {
 959    return string.replace(/^(.)|\s(.)/g, function (letter) { 
 960                                            return letter.toUpperCase(); 
 961                                        }
 962                         );
 963}
 964
 965function array_merge_recursive (arr1, arr2){
 966    var idx = '';
 967 
 968    if ((arr1 && (arr1 instanceof Array)) && (arr2 && (arr2 instanceof Array))) {
 969        for (idx in arr2) {
 970            arr1.push(arr2[idx]);
 971        }
 972    } else if ((arr1 && (arr1 instanceof Object)) && (arr2 && (arr2 instanceof Object))) {
 973        for (idx in arr2) {
 974            if (idx in arr1) {
 975                if (typeof arr1[idx] == 'object' && typeof arr2 == 'object') {
 976                    arr1[idx] = array_merge(arr1[idx], arr2[idx]);
 977                } else {
 978                    arr1[idx] = arr2[idx];
 979                }
 980            } else {
 981                arr1[idx] = arr2[idx];
 982            }
 983        }
 984    }
 985    
 986    return arr1;
 987}
 988
 989function array_merge() {
 990    var args = Array.prototype.slice.call(arguments);
 991    var retObj = {}, k, j = 0, i = 0;
 992    var retArr;
 993    
 994    for (i=0, retArr=true; i < args.length; i++) {
 995        if (!(args[i] instanceof Array)) {
 996            retArr=false;
 997            break;
 998        }
 999    }
1000    
1001    if (retArr) {
1002        return args;
1003    }
1004    var ct = 0;
1005    
1006    for (i=0, ct=0; i < args.length; i++) {
1007        if (args[i] instanceof Array) {
1008            for (j=0; j < args[i].length; j++) {
1009                retObj[ct++] = args[i][j];
1010            }
1011        } else {
1012            for (k in args[i]) {
1013                if (this.is_int(k)) {
1014                    retObj[ct++] = args[i][k];
1015                } else {
1016                    retObj[k] = args[i][k];
1017                }
1018            }
1019        }
1020    }
1021    
1022    return retObj;
1023}
1024
1025function is_int( mixed_var ) {
1026    if (typeof mixed_var !== 'number') {
1027        return false;
1028    }
1029 
1030    if (parseFloat(mixed_var) != parseInt(mixed_var, 10)) {
1031        return false;
1032    }
1033    
1034    return true;
1035}
1036
1037function is_string( mixed_var ) {
1038    return (typeof(mixed_var) == 'string');
1039}
1040
1041function is_array( mixed_var ) {
1042    var key = '';
1043    var getFuncName = function (fn) {
1044        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
1045        if(!name) {
1046            return '(Anonymous)';
1047        }
1048        return name[1];
1049    };
1050 
1051    if (!mixed_var) {
1052        return false;
1053    }
1054 
1055    // BEGIN REDUNDANT
1056    this.php_js = this.php_js || {};
1057    this.php_js.ini = this.php_js.ini || {};
1058    // END REDUNDANT
1059 
1060    if (typeof mixed_var === 'object') {
1061 
1062        if (this.php_js.ini['phpjs.objectsAsArrays'] &&  // Strict checking for being a JavaScript array (only check this way if call ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays)
1063            (
1064            (this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase &&
1065                    this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase() === 'off') ||
1066                parseInt(this.php_js.ini['phpjs.objectsAsArrays'].local_value, 10) === 0)
1067            ) {
1068            return mixed_var.hasOwnProperty('length') && // Not non-enumerable because of being on parent class
1069                            !mixed_var.propertyIsEnumerable('length') && // Since is own property, if not enumerable, it must be a built-in function
1070                                getFuncName(mixed_var.constructor) !== 'String'; // exclude String()
1071        }
1072 
1073        if (mixed_var.hasOwnProperty) {
1074            for (key in mixed_var) {
1075                // Checks whether the object has the specified property
1076                // if not, we figure it's not an object in the sense of a php-associative-array.
1077                if (false === mixed_var.hasOwnProperty(key)) {
1078                    return false;
1079                }
1080            }
1081        }
1082 
1083        // Read discussion at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_is_array/
1084        return true;
1085    }
1086 
1087    return false;
1088}
1089
1090function checkdate(month, day, year) {
1091    var myDate = new Date();
1092    myDate.setFullYear( year, (month - 1), day );
1093    return ((myDate.getMonth()+1) == month && day<32); 
1094}
1095
1096function getValueFromField(name) {
1097	// Get a value from a field name, regardless of the field type
1098	// This works for radio buttons and anything with a .value
1099	// TODO: make this work for selectboxes
1100	var inputs = document.getElementsByName(name);
1101	
1102	if (inputs[0].type == 'radio') {
1103		for(var i = 0; i < inputs.length; i++) {
1104			if (inputs[i].checked) return inputs[i].value;
1105		}
1106		return '';
1107	}
1108	
1109	return inputs[0].value;
1110}
1111
1112function vt_ajax_validate(form, key, configuration, validation) {
1113    if (!(jQuery)) {
1114        console.log('jQuery must be available to have ajax validation')
1115    }
1116    return jQuery.parseJSON(jQuery.ajax({
1117        type: 'GET',
1118        url: window.location,
1119        data: {"vt_action": "ajax_validation", "validation": validation, "vt_field_name": key, "vt_data": document.getElementsByName(form.name + '[' + key + ']')[0].value},
1120        async: false
1121    }).responseText);
1122}