/**
 * @projectDescription Überprüft ein HTML-Formular auf seine Gültigkeit (d.h. alle erforderlichen Felder sind ausgefüllt).
 * Zusätzlich werden Emailfelder auf eine gültige Emailadresse überprüft.  
 *
 * Bisher erfolgreich getestet auf folgenden Browsern/Systemen:
 * - Firefox 1.07+ (PC/Mac)
 * - Mozilla 1.7.13 (PC)
 * - Seamonkey 1.0.4 (PC)
 * - Netscape 8.1 (PC)
 * - Opera 9 (PC),
 * - IE 5.5+ (PC)
 *
 * @author Sascha Quasthoff (s.quasthoff@assenmacher.net)
 * @version 1.0
 *
 */


var check_o = {

  toCheck         : 'validate',                               // die CSS-Klasse der zu überprüfenden Formulare
  showMessages    : false,                                     // die Ausgabe der Meldungen kann ein- und ausgeschaltet werden
  reqMessage      : 'Notwendig',                              // die Fehlermeldung eines nicht ausgefüllten Elements
  reqMessageTitle : 'Dieses Formularfeld ist erforderlich!',  // das title-Attribut bei einem nicht ausgefüllten Elements
  invMessage      : 'Ungültig!',                              // die Fehlermeldung einer ungültigen Emailadresse
  invMessageTitle : 'Diese Emailadresse ist ungültig!',       // die title-Attribut eines nicht ausgefüllten Elements
  messageTitle    : "Dieses Feld ist für die weitere Verarbeitung erforderlich.<br />Es muss daher ausgefüllt werden, damit das Formular abgesendet werden kann.",
  messageTag      : 'ins',                                    // der Elementname für Fehlermeldungen
  messageTagID    : '_fehlt',                                 // der Zusatz der ID des Fehlermeldungstags
  cssClass        : 'fehlt',                                  // die CSS-Klasse für Elemente, die ungültig sind
  emailClass      : 'email',                                  // die CSS-Klasse fürEmail-Formularelemente 
  reqClass        : 'req',                                    // die CSS-Klasse für erforderlich Elemente


  /**
   * matchesClass: Testet das class-Attribut des Elements 'el' auf die Klasse 'classname'
   * @param (DOM-Knoten) el Das zu Ã¼berprÃ¼fende Element
   * @param (String) classname Die gesuchte CSS-Klasse
   * @return (Boolean) True oder False, je nachdem ob die CSS-Klasse gefunden wurde
   */
  matchesClass : function (me, css) {
    if (me == null || typeof(me) == 'undefined' || typeof(me.className) == 'undefined') return false; 
    var found = false;
    var CSSClasses = me.className.split(' ');
    for (var i=0; i < CSSClasses.length; i++) {
      if (CSSClasses[i] == css) {
        found = true;
        break;
      }
    }
    return found;
  },


  /**
   * checkElement: Überprüft ein Formularelement
   * @param (DOM-Knoten) n_element Das zu überprüfende Formularelement
   * @param (DOM-Knoten) n_label Das zum überprüfenden Formularelement gehörende Label-Tag
   */
  checkThisElement : function (me) {

    var that = this;
    this.is_valid = true;
    this.el = me;



    /**
     * getMyLabel: Finde zum übergebenen Formularfeld das zugehörige Label-Element.
     * FÃ¼r DOM-konforme Browser gilt der erste Teil, für den IE der zweite Teil der if-Verzweigung, da er einen Bug
     * (http://www.quirksmode.org/bugreports/archives/2005/09/getAttributefor_returns_null.html) aufweist.
     * @param (DOM-Knoten) me Ein Formularelement, dessen zugehöriges Label-Element gesucht wird
     * @return (DOM-Knoten) Das gesuchte Label-Element 
     */
    getMyLabel = function (me) {
      if (!window.navigator.systemLanguage && window.navigator.language) {
    	  labels = document.getElementsByTagName('label');
  		  for (var i=0; i < labels.length; i++) {
  		    if (labels[i].getAttribute('for') == me.id) return labels[i];
  		  }
  	  } else {
    	  labels = document.getElementsByTagName('label');
        for (var i=0; i < labels.length; i++) {
          if (document.compatMode) { // ab IE6
            if (typeof(labels[i]) != 'undefined') alert("1: "+labels[i]);
            if (typeof(labels[i].attributes) != 'undefined') alert("2: "+labels[i].attributes);
            if (typeof(labels[i].attributes('for')) != 'undefined') alert("3: "+labels[i].attributes('for'));
            if (typeof(labels[i].attributes('for').value) != 'undefined') alert("4: "+labels[i].attributes('for').value);
            if (typeof(labels[i].attributes('for').value) != 'undefined' && labels[i].attributes('for').value == me.id) return labels[i];
          }
          else { if (labels[i].attributes('for') == me.id) return labels[i]; } // bis IE 5.x
        }
      }
    }


    /**
     * Entfernt die CSS-Klasse cssClass vom Element me
     * @param {Object} me, das Element
     */
    delCSSClass = function (me) {
      if (me == null || typeof(me) == 'undefined' || typeof(me.className) == 'undefined') return; 
      var CSSClasses = me.className.split(' ');
      for (var i=0; i < CSSClasses.length; i++) {
        if (CSSClasses[i] == check_o.cssClass) CSSClasses.splice(i, 1);
      }
      return CSSClasses.join(' ');
    }


    /**
     * setMessage: Fügt einen neuen Meldungsknoten ein bzw. ersetze den vorhandenen.
     * Schritt 1: Setzt das Formularelemnt auf ungültig und fügt dem Element und dessen Vater die CSS-Klasse cssClass hinzu.
     * Schritt 2: Erstellt die Meldung, es sei denn, die Ausgabe von Meldungen ist abgeschaltet (showMessages = FALSE)
     * @param (DOM-Knoten) node Das zu überprüfende Formularelement.
     * @param (Boolean) email Gibt an, ob das Formularelement als Emailfeld behandelt wird. Bewirkt eine zusätzliche Überprüfung auf eine gültige Emailadresse.
     */
    setMessage = function (node, email) {

      /* Schritt 1: */
      that.is_valid = false;
      if (!check_o.matchesClass(node, check_o.cssClass))
        node.className += ' ' + check_o.cssClass;
      if (!check_o.matchesClass(node.parentNode, check_o.cssClass))
        node.parentNode.className += ' ' + check_o.cssClass;

      /* Schritt 2: */
      if (!check_o.showMessages) return;

	    if (nMessage = document.getElementById(node.id + check_o.messageTagID)) {
	      email ? nMessage.replaceChild(document.createTextNode(check_o.invMessage), nMessage.firstChild) : nMessage.replaceChild(document.createTextNode(check_o.reqMessage), nMessage.firstChild);
	    } else {
	      var nMessage = document.createElement(check_o.messageTag);
	      nMessage.setAttribute('id', node.id + check_o.messageTagID);
	      email ? nMessage.setAttribute('title', check_o.invMessageTitle) : nMessage.setAttribute('title', check_o.reqMessageTitle);
	      email ? nMessage.appendChild(document.createTextNode(check_o.invMessage)) : nMessage.appendChild(document.createTextNode(check_o.reqMessage));
        getMyLabel(node).appendChild(nMessage);
	    }
    }


    /**
     * unsetMessage:  
     * Entfernt die CSS-Klasse cssClass vom Element me und dessen Vaterknoten, falls vorhanden.
     * Löscht ebenfalls das Element mit der Meldung selbst.
     * @param {DOM-Knoten} me Das Formularelement, dessen Meldung gelöscht wird
     */
    unsetMessage = function (me) {
      that.is_valid = true;
      if (check_o.matchesClass(me, check_o.cssClass))
        me.className = this.delCSSClass(me);
      if (check_o.matchesClass(me.parentNode, check_o.cssClass))
        me.parentNode.className = this.delCSSClass(me.parentNode);
      if (nMessage = document.getElementById(me.id + check_o.messageTagID))
        me.nodeName == 'LEGEND' ? me.removeChild(nMessage) : getMyLabel(me).removeChild(nMessage);
    }


    /**
     * checkMail: Teste den Inhalt des Formular-Elements auf eine gültige Emailadresse
     * @param (String) text Der auf eine gültige Emailadresse zu überprüfende Text.
     * @return (Boolean) Liefert true bzw. false zurück, je nachdem ob eine gültige Emailadresse gefunden wurde, oder nicht.
     */
    checkMail = function (text) {
      reg_ex_email=/^\w[\w|\.|\-]+@[a-zA-Z][\w|\.|\-]+\.[a-zA-Z]{2,4}$/;
      return reg_ex_email.test(text.value);
    }


	  switch (me.type) {

	    // bei einem Textfeld
	    case 'text': case 'textarea': case 'password': case 'file':
        me.onchange = me.onkeyup = function () {
	        (this.value != "") ? unsetMessage(this) : setMessage(this);
	        if ((this.value != "") && check_o.matchesClass(this, check_o.emailClass))
	          checkMail(this) ? unsetMessage(this) : setMessage(this, true);
	      }
	      me.onchange();
	      break;
	
	    // bei einem Dropdownfeld mit Einfachauswahl
	    case 'select-one':
	      me.onchange = me.onkeypress = function () { this[this.selectedIndex].value != "" ? unsetMessage(this) : setMessage(this); }
	      me.onchange();
	      break;

	    // bei einem Dropdownfeld mit Mehrfachauswahl
	    case 'select-multiple':
	      me.onchange = me.onkeypress = function () { this.selectedIndex >= 0 ? unsetMessage(this) : setMessage(this); }
	      me.onchange();
	      break;

	    // bei einer Checkbox
	    case 'checkbox':
	      me.onclick = function () { this.checked ? unsetMessage(this) : setMessage(this); }
	      me.onclick();
	      break;
	
	  }

  },


  /**
   * checkThisForm: Überprüft ein Formular
   * Schritt 1: Wenn ein label-Element im Formular sowohl Kindelemente als auch ein 'for'-Attribut hat, wird nach einem 
   * Kindelement des label-Elements gesucht, dessen CSS-Klasse die REQUIRED_CLASS enthält.
   * Anschliessend wird eine neue Instanz des Objekts 'checkElement' erstellt.
   * Schritt 2: Wenn mindestens ein Formularelement nicht gültig ist, wird das Formular nicht abgesendet.
   * @param (Object) form Das zu überprüfende Formular
   */
  checkThisForm : function (me) {

    this.is_valid = true;
    var form_elements = new Array();
    var all_labels = me.getElementsByTagName('label');


    /**
     * isRequired: Testet das übergebene label-Element auf Erforderlichkeit
     * @param (DOM-Knoten) label Das zu testende label
     * @return (Boolean) True oder False, je nachdem, ob 'label' erforderlich ist oder nicht
     */
    isRequired = function (label_node) {
      var req = false;
      if (label_node.hasChildNodes && (l_childs = label_node.childNodes))
        for (var i=0; i < l_childs.length; i++) {
          if (check_o.matchesClass(l_childs[i], check_o.reqClass)) {
            req = true;
            break;
          }
        }
      return req;
    }


    /**
     * getAttributeValue: Liefert (browserübergreifend) den Wert des gesuchten Attributs 'attribute' des Elementknotens 'element'.
     * @param: element (Node): der Elementknoten, dessen Attribut gesucht wird
     * @param: attribute (String): der Attributname, dessen Wert gesucht wird
     */
    getAttributeValue = function (el, att) {
      if (!window.navigator.systemLanguage && window.navigator.language) { // DOM-Browser
        if (el.getAttributeNode(att).nodeValue) return el.getAttributeNode(att).nodeValue;
      } else { // IE
        if (document.compatMode) { // IE 6
          if (el.attributes(att).value) return el.attributes(att).value;
        } else { // IE 5.x
          if (el.attributes(att)) return el.attributes(att);
        }
      }
    }

    /* Schritt 1 */  
    for (var i=0; i < all_labels.length; i++) {
      if (isRequired(all_labels[i])) {
        form_elements[form_elements.length] = new check_o.checkThisElement(document.getElementById(getAttributeValue(all_labels[i], 'for')));
      }
    }

    /* Schritt 2 */
    for (var i=0; i < form_elements.length; i++) {
      if (typeof form_elements[i] != 'function' && !form_elements[i].is_valid) {
        this.is_valid = false;
        break;
      }
    }

    for (var i=0; i<form_elements.length; i++) {
      if (!form_elements[i].is_valid) {
        if (!!ps.showMessage && typeof ps.showMessage == 'function') {
          if (ps.language == 'de') {
            ps.showMessage ('Bitte füllen Sie die fehlenden Felder aus!', 'error');
          } else if (ps.language == 'en') {
            ps.showMessage ('Please fill in the missing fields!', 'error');
          }
        }
        break;
      }
    };

  },


  /**
   * init: Initialisiert alle Formulare der Webseite (einhängen der onsubmit-Eventhandler)
   * @return (Boolean) liefert True zurück, wenn das Formular gültig ist, sonst False
   */
  init : function () {
    all_forms = document.getElementsByTagName('form');
    for (var i=0; i < all_forms.length; i++) {

      if (check_o.matchesClass(all_forms[i], check_o.toCheck)) {
        all_forms[i].onsubmit = function (theEvent) {
          checked = (theEvent) ? new check_o.checkThisForm(theEvent.target) : new check_o.checkThisForm(window.event.srcElement);
          return checked.is_valid;
        }
      }

    }
  }

}


// Wenn die add_load_event-Funktion existiert, erstelle nach dem Laden der Seite sofort eine Instanz des form_check-Objekts
window.addEvent('domready', function() {
    check_o.init();
});

