/**
 * $Id: email.js,v 1.33 2006/02/01 13:21:55 sascha Exp sascha $         
 *
 * @fileoverview email.js - Validieren von EMail-Adressen.
 * Die Syntax der EMail-Adresse wird mit regulären Ausdrücken überprüft.
 *
 * @author Sascha Hüdepohl renegat@fbe.hs-bremen.de
 * @version 0.3
 */


/**
 * Kann mit der Methode 'validate()' eine EMail-Adresse auf syntaktische
 * Korrektheit prüfen.
 *
 * @constructor
 */
function EMail() {

  /*
   * TLDs
   */
  var tlds = new Array("arpa","aero","biz","com","coop","edu","gov","info","int","jobs","mil","mobi","museum","name","net","org","pro","travel","ac","ad","ae","af","ag","ai","al","am","an","ao","aq","ar","as","at","au","aw","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bm","bn","bo","br","bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cs","cu","cv","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","eu","fi","fj","fk","fm","fo","fr","fx","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nt","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","st","su","sv","sy","sz","tc","td","tf","tg","th","tj","tk","tm","tn","to","tp","tr","tt","tv","tw","tz","ua","ug","uk","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt","yu","za","zm","zr","zw");


  /* The following pattern is used to check if the entered e-mail address
     fits the user@domain format.  It also is used to separate the username
     from the domain. */
  var emailPat = /^(.+)@(.+)$/;
  /* The following string represents the pattern for matching all special
     characters.  We don't want to allow special characters in the address.
     These characters include ( ) < > @ , ; : \ " . [ ]    */
  var specialChars = "\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";
  /* The following string represents the range of characters allowed in a
     username or domainname.  It really states which chars aren't allowed. */
  var validChars = "\[^\\s" + specialChars + "\]";
  /* The following pattern applies if the "user" is a quoted string (in
     which case, there are no rules about which characters are allowed
     and which aren't; anything goes).  E.g. "jiminy cricket"@disney.com
     is a legal e-mail address. */
  // Quotes have to be escaped: "Jiminy \"JC\" Cricket"@disney.com
  //var quotedUser = '("[^"]*")';
  var quotedUser = '("([^"]|\\\\")+")';
  /* The following pattern applies for domains that are IP addresses,
     rather than symbolic names.  E.g. joe@[123.124.233.4] is a legal
     e-mail address. NOTE: The square brackets are required. */
  var ipDomainPat = /^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
  /* The following string represents an atom (basically a series of
     non-special characters.) */
  var atom = validChars + '+';
  /* The following string represents one word in the typical username.
     For example, in john.doe@somewhere.com, john and doe are words.
     Basically, a word is either an atom or quoted string. */
  var word = "(" + atom + "|" + quotedUser + ")";
  // The following pattern describes the structure of the user
  var userPat = new RegExp("^" + word + "(\\." + word + ")*$");
  /* The following pattern describes the structure of a normal symbolic
     domain, as opposed to ipDomainPat, shown above. */
  var domainPat = new RegExp("^" + atom + "(\\." + atom +")*$");
  /* Parts of domain must not start or end in a dash
   * For this test the domain has to be extended with dots to '.domain.tld.' */
  var invalidDashPat = new RegExp("(-\\.|\\.-)");
  /* Parts of domain must not have a dash in 3rd AND 4th position.
   * except for xn-- */
  var invalidPunnyDashPat = new RegExp("\\.([^x][^n]|[^x]n|x[^n])--");
  var validPunnyDashPat = new RegExp("\\.xn--");
  // valid IDN-Form is xn--[some chars]-[some chars]
  var validIDNPat = new RegExp("\\.xn--[a-z0-9-]+-[a-z0-9]+\\.");
  //* Parts of domain must not be all Num
  var invalidNumberPat = new RegExp("\\.[0-9]+\\.");



  function validUser(user) {
    return (user.match(userPat) != null);
  }

  function validTLD(tld) {
    var i = 0;
    for (i = 0; i < tlds.length; i++) {
      if (tld == tlds[i]) {
        return true;
      }
    }
    return false;
  }


  function validDomain(domain) {
    var IPArray = domain.match(ipDomainPat);
    if (IPArray != null) { /* IP */
      for (var i=1;i<=4;i++) {
        if (IPArray[i]>255) {
          return false;
        }
      }
      return true;
    }

    var tld = domain.substring(domain.lastIndexOf('.') + 1, domain.length);
    if (!validTLD(tld)) {
      return false;
    }

    var domainArray = domain.match(domainPat);
    if (domainArray == null) {
      return false;
    }

    // add dots for easier Testing
    var dots = "." + domain + ".";
    if (dots.match(invalidDashPat)) {
      return false;
    }

    if (dots.match(invalidNumberPat)) {
      return false;
    }
    
    if (dots.match(invalidPunnyDashPat)) {
      return false;
    } else {
      var tokens = domain.split('.');
      var i = 0;
      for (i = 0; i < tokens.length; i++) {
        tokens[i] = '.' + tokens[i] + '.';
        if (tokens[i].match(validPunnyDashPat)) {
          if (!tokens[i].match(validIDNPat)) {
            return false;
          }
        }
      }
    }
    return true;
  }

  /**
   * Speichert das Ergebnis der letzten Prüfung.
   * @type boolean
   */
  this.isvalid = false;


  /**
   * @param {string} email Die zu prüfende eMail-Adresse.
   * @returns {boolean}
   */
  this.validate = function(email) {
    var re = /^(.*)@(.*)$/i;
    var e = re.exec(email.toLowerCase());
    if (e) {
      var user = RegExp.$1;
      var domain = RegExp.$2;
      this.isvalid = validUser(user) && validDomain(domain);
    } else {
      this.isvalid = false;
    }
    return this.isvalid;
  }
}




/**
 * Validieren einer EMail-Adresse die in einem HTML-Formular eingegeben wird.
 *
 * <tt>errorCallBack</tt> und <tt>noerrorCallBack</tt> sind optional. Wird <tt>noerrorCallBack</tt> angegeben, muß
 * auch <tt>errorCallBack</tt> angegeben werden.
 *
 * @constructor
 * @param {string} formfieldid Die id des Formularfelds in das die EMail-Adresse eingegeben wird.
 * @param {string} objname Der Name der Instanzvariablen.
 * @param {string} errorCallBack Referenz auf eine Funktion die gerufen werden soll, wenn die EMail-Adresse nicht validiert. Optional.
 * @param {string} noerrorCallBack Referenz auf eine Funktion die gerufen werden soll, wenn die EMail-Adresse validiert. Optional.
 */
function EMailFormField(formfieldid, objname) {
  var formfield = document.getElementById(formfieldid);
  var myname = objname;
  var errorCallback = null;
  var noerrorCallback = null;
  var browser = new Browser();


  if (arguments.length > 2) {
    errorCallback = arguments[2];
  }

  if (arguments.length > 3) {
    noerrorCallback = arguments[3];
  }

  if (browser.isIE()) {
    var inputelement_html = formfield.outerHTML;
    inputelement_html = inputelement_html.substring(0, inputelement_html.length - 1);
    inputelement_html = inputelement_html + ' onBlur="' + myname + '.validate(this.value);">';
    formfield.outerHTML = inputelement_html;
  } else {
    var blur = document.createAttribute('onblur');
    blur.nodeValue = myname + ".validate(this.value);";
    formfield.setAttributeNode(blur);
  }


  /**
   * Eventhandler für die Eingabe einer EMail-Adresse.
   * Wird während der Instanziierung von <tt>EMailFormField</tt> automatisch
   * als Eventhandler '<tt>onBlur</tt>' für das durch <tt>formfieldid</tt>
   * bezeichnete Formularelement eingerichtet.  Validiert die EMail-Adresse
   * nicht, so wird der <tt>errorCallBack</tt> aufgerufen.  Anderenfalls der
   * <tt>noerrorCallBack</tt>.
   * <br><br>
   * Die CallBack-Funktionen bekommen als Parameter das Formular-Input-Element.
   * <br><br>
   * Whitespace am Anfang und Ende der Adresse wird entfernt. Ein Leerstring
   * gibt true zurück. Die CallBacks werden in dem Fall nicht aufgerufen.
   * @param {string} email Die EMail-Adresse. Der IE will es so.
   * @returns {boolean}
   */
  this.validate = function(email) {
    email = email.replace(/^\s+/, '').replace(/\s+$/, '');
    if (email.length == 0) {
      EMailFormField.prototype.isvalid = true;
      return true;
    }
    document.getElementById(formfieldid).value = email;
    if (!EMailFormField.prototype.validate(email)) {
      if (typeof(errorCallback) == 'function') {
        errorCallback(document.getElementById(formfieldid));
      }
      return false;
    } else {
      if (typeof(noerrorCallback) == 'function') {
        noerrorCallback(document.getElementById(formfieldid));
      }
      return true;
    }
  }
}

EMailFormField.prototype = new EMail;

