jQuery.fn.bomValidation = function (conf) {
  if (this.is('.jquery-live-validation-on')) {
      return;
  }
  else {
    this.addClass('jquery-live-validation-on');
  }
  var _self = this;
	var config = jQuery.extend({
		valid:			'Valid',				// alt for valid icon
		invalid:		'Invalid',				// alt for invalid icon
		validClass:		'valid',				// valid class
		invalidClass:	'invalid',				// invalid class
    errorClass : 'error',
		validators:			{},						// json of fields and regexps
    validateOnDisplay: false,
    minWordValidator: 2,
    eventsValidator: 'keyup focusout' //defaults events
	}, conf || {});
  
  this.validators = jQuery.extend({
    'v-carac': 			/^\s*\S+\s*.*$/m,				// name (at least one character)
    'v-word': 			/^(\b[^\s]{2,}\b)\s*$/m,				// name (at least one word 2 carac)
    'v-content': 		/^\s*((\S+)\s+){9,}(\w+|\S+)\s*$/m,				// "content" (at least 10 words)
    'v-dimensions':		/^\d+x\d+$/,			// dimensions (DIGITxDIGIT)
    'v-digit':			/^\d+$/,				// digit (at least one digit)
    'v-url': 			/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/,	// url
    'v-email': 			/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,	// email
    'v-date-dmY-fr':			/^\d{2}\/\d{2}\/(\d{2}|\d{4})$/,				// date format dd/mm/(yyyy|yy)
    'v-date-dmY-de':			/^\d{2}\.\d{2}\.(\d{2}|\d{4})$/,				// date format dd.mm.(yyyy|yy)
    'v-date-dmY-en':			/^\d{2}\.\d{2}\.(\d{2}|\d{4})$/				// date format dd.mm.(yyyy|yy)
  },config.validators);
  /*Dynamic validators*/
  /*Min words*/
  if (config.minWordValidator)
  {
    if(typeof config.minWordValidator == 'object')
    {
      jQuery.each(config.minWordValidator, function(k,v){
        _self.validators['v-minword'+v] = new RegExp('^\\s*((\\S+)\\s+){'+ (parseInt(v,10) - 1) +',}(\\w+|\\S+)\\s*$','m');
      });
    }
    else
    {
      this.validators['v-minword'+config.minWordValidator] = new RegExp('^\\s*((\\S+)\\s+){'+ (parseInt(config.minWordValidator,10) - 1) +',}(\\w+|\\S+)\\s*$','m');
    }
  }
  /*Min characters*/
  if (config.minCharsValidator){
    if(typeof config.minCharsValidator == 'object')
    {
      jQuery.each(config.minCharsValidator, function(k,v){
        _self.validators['v-minchars'+v] = new RegExp('^\\s*(\\S+\\s*){'+parseInt(v,10)+',}$','m');
      });
    }
    else
    {
      this.validators['v-minchars'+config.minCharsValidator] = new RegExp('^\\s*(\\S+\\s*){'+parseInt(config.minCharsValidator,10)+',}$','m');
    }
  }

  this.fields = {};

  /**
   *Collection of required fields
   */
  this.required = [];

  /**
   *Init function
   */
  this.init = function(){
    this.setValidators();
  };
  /**
   *Set validators
   */
  this.setValidators = function(){
    jQuery(this).find('.v-required').each(function(){
      var elm =  jQuery(this);
      var elmName =  elm.attr('name');
      jQuery.each(_self.validators,function(cls,reg){
        if(elm.hasClass(cls)){
          _self.fields[elmName] = reg;
        }
      });
    });    
  };

  /**
   *
   */
   this.validateAll = function(){
    jQuery.each(_self.required, function(){
      if(this.validate &&  typeof this.validate == 'function') this.validate();
    });
   }
  /**
   *Init Handler
   */
   this.init();

  /**
  * Main
  */

  jQuery(this).find('.v-required').each(function () {
    var t			= jQuery(this);
    var isOptional	= false;
    var fieldName	= t.attr('name');

    if (t.is('.jquery-live-validation-on')) {
      return;
    }
    else {
      t.addClass('jquery-live-validation-on');
    }
    
    // Add (in)valid icon
    var status = isOptional ? 'valid' : 'invalid';
    var validator = jQuery(t.attr('v-validator'), _self);
    validator = validator.length ? validator : jQuery('<span/>',{'class':"picto h"}).insertAfter(t);
    validator.attr('alt',status);
    var msger = jQuery(t.attr('v-msger'), _self);
    var vmsg = t.attr('msg-data');
    if(vmsg){
      msger = msger.length ? msger : jQuery('<span/>',{'class':"h v-msger "+config.errorClass}).insertAfter(validator);
      msger.html(vmsg);
    }
    var vEvents =  t.attr('v-events') ? t.attr('v-events') : config.eventsValidator;


    // This function is run now and on key up
    var validate = function () {
      var key = t.attr('name');
      var val = t.val() || '';
      var tit = t.attr('title');
      var type = t.hasClass('v-check') ? 'check' : 'val';

      // If value and title are the same it is assumed formHints is used
      // set value to empty so validation isn't done on the hint
      //val = tit == val ? '' : val;
      // Make sure the value matches or checked attribut is checked
      if('check' == type && t.is(':checked'))
      {
        if (validator.attr('alt') != config.valid) {
          validator.removeClass('v-picto-invalid').addClass('v-picto-valid').removeClass('h');
          validator.attr('alt', config.valid);
          t.removeClass(config.invalidClass).addClass(config.validClass);
          if(null != msger) msger.addClass('h');
        }
      }
      else if (('val' == type) && ((isOptional && val == '') || val.match(_self.fields[key]))) {
        // If it's not already valid
        if (validator.attr('alt') != config.valid) {
          validator.removeClass('v-picto-invalid').addClass('v-picto-valid').removeClass('h');
          validator.attr('alt', config.valid);
          t.removeClass(config.invalidClass).addClass(config.validClass);
          if(null != msger) msger.addClass('h');
        }
      }
      // It didn't validate
      else {
        // If it's not already invalid
        if (validator.attr('alt') != config.invalid) {
          validator.removeClass('v-picto-valid').addClass('v-picto-invalid').removeClass('h');
          validator.attr('alt', config.invalid);
          t.removeClass(config.validClass).addClass(config.invalidClass);
          if(null != msger) msger.removeClass('h');
        }
      }
      
      if(t.hasClass('v-matching-field') && t.val()!='') {
        var regex = new RegExp('v-matching-field-', 'g');
        var split = t.attr('class').split(regex);
        split = split[1].split(' ');
        var matchingField = $('#' + split[0]);
        if (matchingField!=null) {
          if(matchingField.val()==t.val()) {
            validator.removeClass('v-picto-invalid').addClass('v-picto-valid').removeClass('h');
            validator.attr('alt', config.valid);
            matchingField.removeClass(config.invalidClass).addClass(config.validClass);
            if(null != msger) msger.addClass('h');
          }
          else {
            validator.removeClass('v-picto-valid').addClass('v-picto-invalid').removeClass('h');
            validator.attr('alt', config.invalid);
            matchingField.removeClass(config.validClass).addClass(config.invalidClass);
            if(null != msger) msger.removeClass('h');
          }
        }
      }
      
    };

    if(config.validateOnDisplay) validate();
    t.bind(vEvents,validate);
    _self.required.push({'elm': t, 'validate': validate});
  });


  // If form contains any invalid icon on submission, return false
  jQuery(this).submit(function () {
    //Do a check on all elements
    if(!config.validateOnDisplay) _self.validateAll();
    var errors = jQuery(this).find('span[alt="' + config.invalid + '"]');
    //focus on first error
    if(errors.length){
      jQuery(this).find('.v-required.invalid').first().focus();
    }
    return !errors.length;
  });

};

/**
 * Clear input texts with css class .inputTxt on click
 * @param {Event} e event to monitor
 * @returns void
 */
BOM.Utils.clearInputTxt = function(e)
{
  var elt = jQuery(e.target);
  if (e.type == 'focusout')
  {
    if (elt.val() == ''){
      elt.val(elt.data().saved);
    } else {
      elt.data('saved', elt.val());
    }

  } else {
    if (elt.data('saved') == null) {
      elt.val('');
    }
  };
};

//Attach click and focusout event on clearInputTxt fields
jQuery('.inputTxt, .inputText2').live('focusin blur', BOM.Utils.clearInputTxt);

// Attach footer's Newsletter form callbacks to load data from /membres/
jQuery('#ftNLForm').live('submit', function(e){
  e.preventDefault();
  e.stopPropagation();
  // get form data
  var sForm = jQuery(e.target).serialize();
  // create footer dialog container
  var msg=jQuery('#ftMsg');
  if (!msg.length){
    msg = jQuery('<div/>', {
      id : "ftMsg"
    });
    jQuery(this).append(msg);
  };
  
  // post data to subscribe to a newsletter
  jQuery.post(jQuery(this).attr('action'), jQuery(this).serialize(), function(data) {
    msg.html(data.message);
    options = {
      width: 350,
      dialogClass: 'modalDefault modalAjax',
      modal: true,
      resizable: false,
      buttons: {
        Ok: function() {
          jQuery(this).dialog('close');
        }
      }
    };
    msg.dialog(options);
  });
});
