/* ************************************************************************************* *\
 * The MIT License
 * Copyright (c) 2007 Fabio Zendhi Nagao - http://zend.lojcomm.com.br
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
\* ************************************************************************************* */


var fValidator = new Class({
	options: {
		msgContainerTag:  "div",
		msgClass:         "fValidator-msg",
    reqContainerTag:  "span",
    reqClass:         "fValidator-req",
    reqMsg:           true,
    reqText:          "*",
    reqPos:           "after",
    actionOn:         "change",
    reqMsgBtm:        'Verplicht veld',
    msgShown:         false,

		styleNeutral: {"background-color": "#ffc", "border-color": "#cc0"},
		styleInvalid: {"background-color": "#fcc", "border-color": "#c00"},
		styleValid:   {"background-color": "#cfc", "border-color": "#0c0"},

		required: {type: "required", re: /[^.*]/, msg: "This field is required."},
		alpha:    {type: "alpha", re: /^[a-z ._-]+$/i, msg: "This field accepts alphabetic characters only."},
		alphanum: {type: "alphanum", re: /^[a-z0-9 ._-]+$/i, msg: "This field accepts alphanumeric characters only."},
		integer:  {type: "integer", re: /^[-+]?\d+$/, msg: "Please enter a valid integer."},
		real:     {type: "real", re: /^[-+]?\d*(\.|\,)?\d+$/, msg: "Please enter a valid number."},
		date:     {type: "date", re: /^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$/, msg: "Please enter a valid date (mm/dd/yyyy)."},
		email:    {type: "email", re: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i, msg: "Please enter a valid email."},
		phone:    {type: "phone", re: /^[\d\s ().-]+$/, msg: "Please enter a valid phone."},
		url:      {type: "url", re: /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i, msg: "Please enter a valid url."},
		confirm:  {type: "confirm", msg: "Confirm Password does not match original Password."},

		onValid:    Class.empty,
		onInvalid:  Class.empty
	},

	initialize: function(form, options) {
		this.form = $(form);
		this.setOptions(options);

		this.fields = this.form.getElements("*[class^=fValidate]");
		this.validations = [];

		this.fields.each(function(element) {
			if(!this._isChildType(element)) element.setStyles(this.options.styleNeutral);
			element.cbErr = 0;
			var classes = element.getProperty("class").split(' ');
			classes.each(function(klass) {
				if(klass.match(/^fValidate(\[.+\])$/)) {
					var aFilters = eval(klass.match(/^fValidate(\[.+\])$/)[1]);
					for(var i = 0; i < aFilters.length; i++) {
						if(this.options[aFilters[i]]) {
              this.register(element, this.options[aFilters[i]]);
            }
            //START OWN CODE
            if((aFilters[i] == 'required' || aFilters[i] == 'req') && !this.options.msgShown) {
              this.options.msgShown = true;
              new Element('span').set({html: '<i>'+ this.options.reqText +' '+ this.options.reqMsgBtm +'</i>'}).inject(this.form,'after');
            }
            if((aFilters[i] == 'required' || aFilters[i] == 'req') && this.options.reqMsg) {
              //var fld = $$('label').filterByAttribute('for','*=',element.id)[0];
              var fld = null;
              $$('label').each(function(item,index) {
                if(item.match('[for='+ element.id +']')) {
                  fld = item;
                }
              });
              //fld = $('label[for='+ element.id +']');
              if(fld) {
                var thisEl = new Element(this.options.reqContainerTag,{'class':this.options.reqClass});
                thisEl.inject(fld,'after');
                thisEl.set({html:this.options.reqText});
                thisEl.setAttribute('title',this.options[aFilters[i]].msg);
                //new Element(this.options.reqContainerTag,{'class':this.options.reqClass}).inject(fld,this.options.reqPos).set({html:this.options.reqText}).setAttribute('title',this.options[aFilters[i]].msg);
              }
            }
            if(aFilters[i].indexOf('=') > 0) {
              var arr = aFilters[i].split('=');
              this.register(element, $merge(this.options[arr[0]],{ val: arr[1] }));
            }
            //END OWN CODE
						if(aFilters[i].charAt(0) == '=') this.register(element, $extend(this.options.confirm, {idField: aFilters[i].substr(1)}));
					}
				}
			}.bind(this));
		}.bind(this));

		this.form.addEvents({
			"submit": this._onSubmit.bind(this),
			"reset": this._onReset.bind(this)
		});
	},

	register: function(field, options) {
		field = $(field);
		this.validations.push([field, options]);
		field.addEvent(this.options.actionOn, function() {
			this._validate(field, options);
		}.bind(this));
	},

	_isChildType: function(el) {
		var elType = el.type.toLowerCase();
		if((elType == "radio") || (elType == "checkbox")) return true;
		return false;
	},

	_validate: function(field, options) {
		switch(options.type) {
			case "confirm":
				if($(options.idField).get('value') == field.get('value')) this._msgRemove(field, options);
				else this._msgInject(field, options);
				break;
			default:
				if(options.re.test(field.get('value'))) this._msgRemove(field, options);
				else this._msgInject(field, options);
		}
	},

	_validateChild: function(child, options) {
		var nlButtonGroup = this.form[child.getProperty("name")];
		var cbCheckeds = 0;
		var isValid = true;
 		for(var i = 0; i < nlButtonGroup.length; i++) {
			if(nlButtonGroup[i].checked) {
				cbCheckeds++;
				if(!options.re.test(nlButtonGroup[i].get('value'))) {
					isValid = false;
					break;
				}
			}
		}
		if(cbCheckeds == 0 && options.type == "required") isValid = false;
		if(isValid) this._msgRemove(child, options);
		else this._msgInject(child, options);
	},

	_msgInject: function(owner, options) {
		if(!$(owner.getProperty("id") + options.type +"_msg")) {
			var msgContainer = new Element(this.options.msgContainerTag, {"id": owner.getProperty("id") + options.type +"_msg", "class": this.options.msgClass})
				.set({html: options.msg})
				.setStyle("opacity", 0)
				.inject(owner,'after').fade(1);
      /*
      msgContainer
				.effect("opacity", {
					duration: 500,
					transition: Fx.Transitions.linear
				}).start(0, 1);
        */
	    owner.cbErr++;
	    this._chkStatus(owner, options);
		}
	},

	_msgRemove: function(owner, options, isReset) {
		isReset = isReset || false;
		if($(owner.getProperty("id") + options.type +"_msg")) {
			var el = $(owner.getProperty("id") + options.type +"_msg");
      el.fade(0);
			if(!isReset) {
				owner.cbErr--;
				this._chkStatus(owner, options);
			}
		}
	},

	_chkStatus: function(field, options) {
		if(field.cbErr <= 0) {
      field.cbErr = 0;
      field.morph(this.options.styleValid);
      //remove the element
      $(field.get('id') + options.type +'_msg').dispose();
			this.fireEvent("onValid", [field, options], 50);
		} else {
      field.morph(this.options.styleInvalid);
			this.fireEvent("onInvalid", [field, options], 50);
		}
	},

	_onSubmit: function(event) {
		event = new Event(event);
		var isValid = true;

		this.validations.each(function(array) {
			if(this._isChildType(array[0])) this._validateChild(array[0], array[1]);
			else this._validate(array[0], array[1]);
			if(array[0].cbErr > 0) isValid = false;
		}.bind(this));

		if(!isValid) event.stop();
		return isValid;
	},

	_onReset: function() {
		this.validations.each(function(array) {
			if(!this._isChildType(array[0])) array[0].setStyles(this.options.styleNeutral);
			array[0].cbErr = 0;
			this._msgRemove(array[0], array[1], true);
		}.bind(this));
	}
});
fValidator.implement(new Events); // Implements addEvent(type, fn), fireEvent(type, [args], delay) and removeEvent(type, fn)
fValidator.implement(new Options);// Implements setOptions(defaults, options)


/* ************************************************************************************* *\
  
 Extending the functionality of the fValidator Class
 
 Extra options:
 - datenl:    check date in dutch time format (dd-mm-yyyy)
\* ************************************************************************************* */
fValidatorOwn = new Class({
  Extends:    fValidator, 
  /*

  In addition to the standard options for validation, an extra option is added:
  - preformat

  if this one is set, it preformats the entered data before validating it. This makes it
  easier for the user to fill out the form.
  The following options are available:
  - datenl: formats a date (311208 and 31122008) to (31-12-2008). It also replaces (/) to (-)
  - time: formats time (0,30,030,0030) to (00:30)

  Two extra validations:
  - datenl: checks if date is good in dutch format.
  - time:   checks if entered value is a time (hh:mm)

  */
  options: {
    img:    'base/img/icon/error.png',
    datenl: {type: "datenl", re: /^(\d{2}\-\d{2}\-\d{4})?$/, msg: "Datumveld (dd-mm-jjjj)", preformat: 'datenl' },
    time:   {type: "time", re: /^(([0-1][0-9])|([2][0-3]))\:[0-5][0-9]$/, msg: "Tijd (uu:mm)", preformat: 'time' },
    zipnl:  {type: "zipnl", re: /^\d{4}\ [A-Z]{2}$/, msg: "Postcode (1234 AB)", preformat: 'zipnl' },
    minlen: {type: "minlen", re: false, msg: "Minimum lengte is {val}" },
    maxlen: {type: "maxlen", re: false, msg: "Maximum lengte is {val}" },
    minval: {type: "minval", re: false, msg: "Minimum waarde is {val}" },
    maxval: {type: "maxval", re: false, msg: "Maximum waarde is {val}" }
  },

  _validate: function(field, options) {
    //preformat the data if specified
    if(options.preformat) { this._doFormat(field,options.preformat); };
    //only do other checks if field is filled
    if(((options.type != 'required') && field.get('value').length != 0)||(options.type=='required')) {
      if(options.re === false) {
        this._validateOther(field,options);
      } else {
        this.parent(field,options);
      }
    }
	},
  
  _validateOther: function(field, options) {
    var valid = true;
    var msg   = '';
    switch(options.type) {
      case 'minlen':
        if(field.get('value').length < parseInt(options.val)) {
          valid = false;
        }
        break;
      case 'maxlen':
        if(field.get('value').length > parseInt(options.val)) {
          valid = false;
        }
        break;
      case 'minval':
        if(isNaN(field.get('value')) || parseInt(field.get('value')) < parseInt(options.val)) {
          valid = false;
        }
        break;
      case 'maxval':
        if(isNaN(field.get('value')) || parseInt(field.get('value')) > parseInt(options.val)) {
          valid = false;
        }
        break;
    }

    if(valid) {
      this._msgRemove(field, options);
    } else {
      msg   = options.msg.replace(/\{val\}/g,options.val);
      options.msg = msg;
      this._msgInject(field, options);
    }
  },

  _doFormat: function(field,type) {
    var val = field.get('value');
    switch(type) {
      case 'datenl':
        val = val.replace(/\//g,'-');
        switch(val.length) {
          case 6:
            val = val.substr(0,2) +'-'+ val.substr(2,2) +'-20'+ val.substr(4,2);
            break;
          case 8:
            if(!isNaN(val)) {
              val = val.substr(0,2) +'-'+ val.substr(2,2) +'-'+ val.substr(4,4);
            } else {
              var arr = val.split('-');
              if(arr.length == 3) {
                val = '0'+ arr[0] +'-0'+ arr[1] +'-'+ arr[2];
              } else {
                val = val.substr(0,6) +'20'+ val.substr(6,2);
              }
            }
            break;
          case 9:
            var arr = val.split('-');
            if(arr.length == 3) {
              val = ((arr[0].length==1)?'0':'') + arr[0] +'-'+ ((arr[1].length==1)?'0':'') + arr[1] +'-'+ arr[2];
            }
            break;
        }
        break;
      case 'time':
        val = val.replace(/\-/g,':');
        val = val.replace(/\//g,':');
        val = val.replace(/\\/g,':');
        val = val.replace(/ /g,':');
        switch(val.length) {
          case 1:
            val = '00:0'+ val;
            break;
          case 2:
            val = '00:'+ val;
            break;
          case 3:
            val = '0'+ val.substr(0,1) +':'+ val.substr(1,2);
            break;
          case 4:
            if(isNaN(val)) {
              val = '0'+ val;
            } else {
              val = val.substr(0,2) +':'+ val.substr(2,2);
            }
            break;
        }
        break;
      case 'zipnl':
        switch(val.length) {
          case 6:
            val = val.substr(0,4) +' '+ val.substr(4,2).toUpperCase();
            break;
          case 7:
            val = val.toUpperCase();
            break;
        }
    }
    field.value = val;
  },
  
  _msgInject: function(owner, options) {
    this.parent(owner,options);
    var fld = $(owner.getProperty("id") + options.type +"_msg");
    var msg = fld.get('text');
    fld.set({html:''});
    var el = new Element('img',{'src':this.options.img});
    el.injectInside(fld).setAttribute('title',msg);

	}
});