// ------------------------------------------------------
// vim: set ts=2 sts=2 sw=2 et:
// Swarm Prediction Market Platform
// Copyright (c) 2007 Swarchy.com, Taiwan
// $Id: form_validation.js 1457 2008-06-06 11:04:04Z philip $
// ------------------------------------------------------

FormValidation = Class.create();

FormValidation.prototype = {
  initialize : function(form, validators, options) {
    this.options = Object.extend({
      advice_elem : null
    }, options || {});
    this.form = $(form);
    this.validators = { 'keyup':{}, 'click':{}, 'blur':{}, 'submit':{} };
    for (var i = 0; i < validators.length; i++) {
      v = validators[i];
      if (!v || !v.validator || !v.validator.field_name) {
        console.error("Invalid validators parameter");
        continue;
      }

      var when_arr = (v.when instanceof Array) ? v.when : [v.when];
      for (var n = 0; n < when_arr.length; n++) {
        var when = when_arr[n];
        switch (when) {
        case "keyup" :
        case "click" :
        case "blur" :
        case "submit" :
          var fname = v.validator.field_name;
          if (!this.validators[when][fname]) this.validators[when][fname] = [];
          this.validators[when][fname].push(v.validator);
          break;
        }
      }
    }

    // observe element events such (keyup, blur)
    var evt_names = ['keyup', 'blur', 'click'];
    var _fields = this.form.getElementsByTagName('*');
    for (var i = 0; i < _fields.length; i++) {
      var f = _fields[i];
      for (var n = 0; n < evt_names.length; n++) {
        evt_name = evt_names[n];
        if (this.validators[evt_name][f.name]) {
          Event.observe(f, evt_name, this.on_elem_evt.bind(this), false);
          if (!FormValidation.get_advice(f.name, f)) FormValidation.create_advice(f.name, f);
        }
      }
    }

    // observe submit events
    for (var n in this.validators['submit']) {
      Event.observe(this.form, 'submit', this.on_submit.bind(this), false);
      break;
    }
  },

  is_all_passed : function() {
    for (var i in this.validators) {
      var evt_validators = this.validators[i];
      for (var n in evt_validators) {
        var validators = evt_validators[n];
        for (var c = 0; c < validators.length; c++) {
          if (!validators[c].passed) return false;
        }
      }
    }
    return true;
  },

  first_failed_validators : function() {
    for (var i in this.validators) {
      var evt_validators = this.validators[i];
      for (var n in evt_validators) {
        var validators = evt_validators[n];
        for (var c = 0; c < validators.length; c++) {
          if (!validators[c].passed) return validators[c];
        }
      }
    }
    return null;
  },

  update_advice : function(validator, elem, is_valid) {
    if (is_valid != null) {
      var advice = validator.advice_elem || FormValidation.get_advice(elem.name, elem) ||
                   FormValidation.create_advice(elem.name, elem);
      advice.className = is_valid ? validator.valid_class : validator.invalid_class;
      advice.innerHTML = is_valid ? validator.valid_text : validator.invalid_text;
      advice.style.display = "";
    }
  },

  on_elem_evt :  function(ev) {
    //console.debug('on_elem_evt', ev);
    // filter out control keys
    if (ev.keyCode && ev.keyCode != 8 && ev.keyCode < 32) return;
    el = Event.element(ev);
    var validators = this.validators[ev.type][el.name];
    for(var i = 0; i < validators.length; i++) {
      var validator = validators[i];
      validator.validate(ev, el, this.update_advice);
      if (validator.passed == null) continue;
      if (!validator.passed) {
        this.update_advice(validator, el, validator.passed);
        if (validator.options.stop_on_error) break;
      } else if (i == validators.length - 1) {
        this.update_advice(validator, el, validator.passed);
      }
    }
    //console.debug('on_elem_evt completed');
  },

  on_submit :  function(ev) {
    try {
      var svalidators = this.validators['submit'];
      var _fields = this.form.getElementsByTagName('*');
      for (var i = 0; i < _fields.length; i++) {
        var fi = _fields[i];
        if (!svalidators[fi.name]) continue;
        for (var n = 0; n < svalidators[fi.name].length; n++) {
          var validator = svalidators[fi.name][n];
          if (validator) {
            validator.validate(ev, fi, this.update_advice);
            if (validator.passed == null) continue;
            if (validator.passed == false) {
              this.update_advice(validator, fi, validator.passed);
              if (validator.options.stop_on_error) {
                Field.activate(fi);
                Event.stop(ev); return false;
              }
            }
          }
        } // end of for(n)
      } // end of for(i)
    } catch(e) {
      console.debug(e);
      Event.stop(ev); return false;
    }
  }
}

// add FormValidation class methods
Object.extend(FormValidation, {
  get_elem_id : function(elem) {
    return elem.id ? elem.id : elem.name;
  },

  get_advice : function(name, elem) {
    return $('advice-' + name + '-' + FormValidation.get_elem_id(elem)) ||
      $('advice-' + FormValidation.get_elem_id(elem));
  },

  create_advice : function(name, elem) {
    eid = 'advice-' + name + '-' + FormValidation.get_elem_id(elem);
    advice = '<span class="field_valid" id="' + eid + '" style="display:none"></span>';
    new Insertion.After(elem, advice);
    return $(eid);
  }
});

var Validator = Class.create();

Validator.prototype = {
  initialize : function(field_name, options){
    this.options = Object.extend({
      valid_text    : '&nbsp;',
      invalid_text  : 'Invalid',
      valid_class   : 'field_valid',
      invalid_class : 'field_invalid',
      advice_elem   : null,
      stop_on_error : true
    }, options || {});
    this.field_name = field_name || '';
    this.passed = false;
    this.valid_text = this.options.valid_text;
    this.invalid_text = this.options.invalid_text;
    this.valid_class = this.options.valid_class;
    this.invalid_class = this.options.invalid_class;
    this.advice_elem = this.options.advice_elem;
  },
  validate : function() { return false; }
}

//
// Field value not empty validator
//
var NotEmptyValidator = Class.create(Validator,{
  validate: function(ev, elem) {
    this.passed = elem.value.strip().length > 0;
  }
});

//
// Field value length validator
//
  var LengthValidator = Class.create(Validator,{
  validate: function(ev, elem) {
    this.passed = elem.value.length >= this.options.min_size;
  }
});

//
// Username field validator
//
var CheckedValidator = Class.create(Validator,{
  validate: function(ev, elem) {
    this.passed = elem.checked == true;
  }
});

function lookupElemInFormByName(frm, name)
{
  while(frm && frm.tagName != "FORM") { frm = frm.parentNode; }
  if (frm == null) return null;
  es = frm.getElementsByTagName('*');
  for (var i = 0; i < es.length; i++) {
    if (es[i].name == name) return es[i];
  }
}

//
// Same / Different field value validator
//
var SameValidator = Class.create(Validator,{
  validate: function(ev, elem){
    var f = lookupElemInFormByName(elem, this.options.same_field);
    return (this.passed = f && f.value == elem.value);
  }
});
var DiffValidator = Class.create(Validator,{
  validate: function(ev, elem){
    var f = lookupElemInFormByName(elem, this.options.same_field);
    this.passed = f && f.value != elem.value;
    return this.passed;
  }
});

//
// Regular expression validator
//

var RegexpValidator = Class.create(Validator,{
  validate: function(ev, elem){
    if (!this.options.regexp) this.options.regexp = /./
    this.passed = this.options.regexp.test(elem.value);
  }
});

var RegexpNoMatchValidator = Class.create(Validator,{
  validate: function(ev, elem){
    if (!this.options.regexp) this.options.regexp = /./
    this.passed = !this.options.regexp.test(elem.value);
  }
});

//
// Username field validator
//
var UsernameValidator = Class.create(Validator,{
  validate: function(ev, elem){
    if (!this.regexp) this.regexp = /^[a-zA-Z]+\w*$/;
    this.passed = this.regexp.test(elem.value);
  }
});

//
// Email field validator
//
var EmailValidator = Class.create(Validator,{
  validate: function(ev, elem){
    if (!this.regexp) this.regexp = /^[\w.]{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/;
    this.passed = this.regexp.test(elem.value);
  }
});

//
// Url field validator
//
var UrlValidator = Class.create(Validator,{
  validate: function(ev, elem){
    if (!this.regexp) this.regexp =
      /^(http(s?)\:\/\/)?[a-zA-Z0-9\-\.]+\.([a-zA-Z0-9]{1,6})(\:[0-9]+)?(\/($|[a-zA-Z0-9\.\,\:\;\?\'\\\+&%\$#\=~_\-]*))*$/;
    this.passed = this.regexp.test(elem.value);
  }
});

//
// Year field validator
//
var YearValidator = Class.create(Validator,{
  validate: function(ev, elem){
    this.passed = false;
    if (elem.value.length == 0) {
      this.passed = true; return true;
    }
    if (!this.regexp) this.regexp = /^[0-9]{4}$/;
    if (this.regexp.test(elem.value)) {
      var opt = this.options;
      var yr = parseInt(elem.value);
      this.passed = (!opt.min_year || opt.min_year <= yr) && (!opt.max_year || yr <= opt.max_year);
    }
  }
});

//
// Remote field value over AJAX validator
//
var RemoteValidator = Class.create(Validator,{
  validate: function(ev, elem, advice_func){
    if (this.timeout_id) window.clearTimeout(this.timeout_id);
    var _this = this;
    this.timeout_id = window.setTimeout(
      function() {_this.remote_validate(elem, advice_func)}, 500);
    this.passed = null;
  },
  remote_validate : function(elem, advice_func) {
    var _this = this;
    var url = this.options.url;
    var myAjax = new Ajax.Request(url,
      {method: 'post', parameters:{value:elem.value},
        onSuccess: function(req) {
          _this.passed = eval('('+req.responseText+')');
          _this.invalid_text = _this.options.invalid_text;
          advice_func(_this, elem, _this.passed);
        },
        onFailure: function(req) {
          _this.invalid_text = "(" + req.status + ")error";
          _this.passed = false;
          advice_func(_this, elem, _this.passed);
        }
      });
  }
});
