
/* Mandinga client library */

var Mandinga = {
  // debug flag
  debug: true,
  
  // Exception 
  Exception: function(message, code){
    this.message = message;
    this.code = code;
    
    if(Mandinga.debug){
      console.log('Exception: ' + message);
      alert('Exception: ' + message);
    }
    
    return message;
  },
  
  // initialize a tooltip for an element
  initTooltip: function(elementName){
    Mandinga.Tooltips.init(elementName);
  },
  
  // initialize all tooltips for document
  initTooltips: function(){
    $H(this.Config.Tooltips).each(function(message, elementName){
      this.initTooltip(elementName);
    }, this);
  },
  
  // initialize a form validator
  initFormValidator: function(elementName){
    if($(elementName)){
      Mandinga.FormValidator.load(elementName);
    }
  },
  
  // initialize all tooltips for document
  initFormValidators: function(){
    $H(this.Config.FormValidators).each(function(validatorConfig, elementName){
      this.initFormValidator(elementName);
    }, this);
  }
  
  
}


// Mandinga website configuration
Mandinga.Config = {}


// Mandinga Form Validator library
Mandinga.FormValidator = {
  
  // load a form validation set of rules
  load: function(formId){
    // check config
    if(!$defined(this.config[formId])){
      throw Mandinga.Exception('Form validation configuration not found for element ' + formId);
    }
    
    // check form element
    if(!$defined($(formId))){
      throw Mandinga.Exception('Form element ' + formId + ' is not defined');
    }

    // make a new validator object
    var validator = {'rules': this.config[formId]}
    $extend(validator, Mandinga.FormValidator.base);
    
    // store validator at form level
    $(formId).store('mgFormValidator', validator);
    
    // set event on form submit
    if($(formId).retrieve('mgFormValidator').rules.bindValidate){
      $(formId).addEvent('submit', $(formId).retrieve('mgFormValidator').validate.bind($(formId).retrieve('mgFormValidator')));
    }
    
  },
  
  // Base functionalities for a form object
  base: {
    
    // Tells if form is exempted of errors
    validate: function(){
      return !this.hasErrors();
    },
    
    // Tells if form has errors
    hasErrors: function(){
      var errors = false;
      
      // TODO : check how to break a inclusion of functions
      
      // parse each element for rules
      $H(this.rules).each(function(elementRules, elementName){
        $H(elementRules).each(function(ruleParameters, ruleName){
          if(!errors){
            if(!this.checkRule(elementName, ruleName, ruleParameters)){
              errors = true;
              return;
            }
          }
        }, this);
      }, this);
      return errors;
    },
    
    // get a hash with every field error
    getErrors: function(){
      // TODO
      var errors;
    },
    
    // get error label for a specific field
    getError: function(fieldName){
      //TODO
      var error;
    },
    
    // display errors about form, this method is made to be overwritten for specific behaviours
    displayErrors: function(){
      // TODO
      // display all errors on form : add an error class for fields and display an error block
    },
    
    // get errors block, this method is made to be overwritten for specific behaviours
    getFormattedErrorBlock: function(){
      // TODO
      // create a html code <ul><li> for displaying a error block
    },
    
    // Tells if form is exempted of errors
    checkRule: function(elementName, rule, params){
      try {
        switch(rule){
          case 'required':
            value = $(elementName).get('value');
            return this.checkRequired(value);
            break;
          
          case 'minLength':
            value = $(elementName).get('value');
            return !value.length || this.checkMinLength(value, params);
            break;
          
          case 'maxLength':
            value = $(elementName).get('value');
            return !value.length || this.checkMaxLength(value, params);
            break;
          
          case 'lessThan':
            value = $(elementName).get('value');
            return !value.length || this.checkLessThan(value, params);
            break;
          
          case 'lessEqualThan':
            value = $(elementName).get('value');
            return !value.length || this.checkLessEqualThan(value, params);
            break;
          
          case 'moreThan':
            value = $(elementName).get('value');
            return !value.length || this.checkMoreThan(value, params);
            break;
          
          case 'moreEqualThan':
            value = $(elementName).get('value');
            return !value.length || this.checkMoreEqualThan(value, params);
            break;
          
          case 'email':
            value = $(elementName).get('value');
            return !value.length || this.checkEmail(value);
            break;
          
          case 'url':
            value = $(elementName).get('value');
            return !value.length || this.checkUrl(value);
            break;
          
          case 'regex':
            value = $(elementName).get('value');
            return !value.length || this.checkRegex(value, params);
            break;
          
          case 'fieldEquals':
            value = $(elementName).get('value');
            return this.checkFieldEquals(value, params);
            break;
          
          case 'fieldLessThan':
            value = $(elementName).get('value');
            return this.checkFieldLessThan(value, params);
            break;
          
          case 'fieldLessEqualThan':
            value = $(elementName).get('value');
            return this.checkFieldLessEqualThan(value, params);
            break;
          
          case 'fieldMoreThan':
            value = $(elementName).get('value');
            return this.checkFieldMoreThan(value, params);
            break;
          
          case 'fieldMoreEqualThan':
            value = $(elementName).get('value');
            return this.checkFieldMoreEqualThan(value, params);
            break;
          
          case 'groupRequired':
            return this.checkGroupRequired(elementName);
            break;
          
          case 'groupMinRequired':
            return this.checkGroupMinRequired(elementName, params);
            break;
          
          case 'groupMaxRequired':
            return this.checkGroupMaxRequired(elementName, params);
            break;
          
          default:
            throw Mandinga.Exception('Validation rule ' + rule + ' is not defined');
            break;
        }
      } catch(e) {
        throw Mandinga.Exception('rule ' + rule + ' meet errors while trying to be applied');
      }
      
      return false;
    },
    
    checkRequired: function(value){
      return result = !!value.trim().length;
    },
    
    checkMinLength: function(value, min){
      
      return value.trim().length >= min;
    },
    
    checkLessThan: function(value, max){
      return value < max;
    },
    
    checkLessEqualThan: function(value, max){
      return value <= max;
    },
    
    checkMoreThan: function(value, min){
      return value > min;
    },
    
    checkMoreEqualThan: function(value, min){
      return value >= min;
    },
    
    checkEmail: function(value){
      var regex = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
      return value.test(regex);
    },
    
    checkUrl: function(value){
      var regex = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
      return value.test(regex);
    },
    
    checkRegex: function(value, regexStr){
      var regex;
      var mismatch = false;
      
      // check if regex must match or mismatch
      if(regexStr[0] == '!'){
        mismatch = true;
        regexStr = regexStr.substr(1);
      }
      
      // create regex object
      try{
        eval('regex = ' + regexStr);
      } catch(e) {
        throw Mandinga.Exception('regex evaluation failed')
      }
      
      if(mismatch){
        return !value.test(regex);
      }
      return value.test(regex);
    },
    
    checkFieldEquals: function(value, fieldName){
      return value == $(fieldName).get('value');
    },
    
    checkFieldLessThan: function(value, fieldName){
      return value < $(fieldName).get('value');
    },
    
    checkFieldLessEqualThan: function(value, fieldName){
      return value <= $(fieldName).get('value');
    },
    
    checkFieldMoreThan: function(value, fieldName){
      return value > $(fieldName).get('value');
    },
    
    checkFieldMoreEqualThan: function(value, fieldName){
      return value >= $(fieldName).get('value');
    },
    
    checkGroupRequired: function(className, min){
      var validated =  false;
      $$(className).each(function(elt){
        if(!validated && (elt.get('type') == 'checkbox' && elt.get('checked')) || (elt.get('value'))){
          validated = true;
        }
      }, this);
      return validated;
    },
    
    checkGroupMinRequired: function(className, min){
      var matches = 0;
      $$(className).each(function(elt){
        if((min < matches && (elt.get('type') == 'checkbox') && elt.get('checked')) || (elt.get('value'))){
          matches++;
        }
      })
      return min <= matches;
    },
    
    
    checkGroupMaxRequired: function(className, max){
      var matches = 0;
      $$(className).each(function(elt){
        if((max < matches && (elt.get('type') == 'checkbox') && elt.get('checked')) || (elt.get('value'))){
          matches++;
        }
      })
      return max >= matches;
    }
    
    
  }

}


// Mandinga Tooltips library
Mandinga.Tooltips = {

  // init all tooltips for document
  init: function(elementName){
    if(elementName[0] == '.'){
      $$(elementName).each(function(elt){
        var tipContent = '';
        tipContent += $defined(this.config[elementName].title) ? this.config[elementName].title : '';
        tipContent += '::';
        tipContent += $defined(this.config[elementName].text) ? this.config[elementName].text : '';

        elt.set('title', tipContent);
        
        new Tips(elt, {offsets: {x: 16, y: -5}});
      }, this);
    } else if($(elementName)) {
      var tipContent = '';
      tipContent += $defined(this.config[elementName].title) ? this.config[elementName].title : '';
      tipContent += '::';
      tipContent += $defined(this.config[elementName].text) ? this.config[elementName].text : '';
      
      $(elementName).set('title', tipContent);
      
      new Tips($(elementName), {offsets: {x: 16, y: -5}});
    }
  }
  
}

