/***

    wForms 2.0
    a javascript extension to web forms.

    Build $Tue, 20 Mar 2007 15:17:28 UTC$

    THIS FILE IS AUTOMATICALLY GENERATED.  If creating patches, please
    diff against the source tree, not this file.

    Copyright (c) 2005-2007 Cedric Savarese <cedric@veerwest.com> and contributors.
    This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
    For more information, visit: http://www.formassembly.com/wForms

    Build script by Troels Knak-Nielsen <troelskn@gmail.com>

***/

// wForms - a javascript extension to web forms.
// see http://www.formassembly.com/wForms
// This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>

function wHELPERS() {};

// addEvent adapated from http://ejohn.org/projects/flexible-javascript-events/
// and  Andy Smith's (http://weblogs.asp.net/asmith/archive/2003/10/06/30744.aspx)
wHELPERS.prototype.addEvent = function(obj, type, fn) {
    if(!obj) { return; }

    if (obj.attachEvent) {
        obj['e'+type+fn] = fn;
        obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
        obj.attachEvent( 'on'+type, obj[type+fn] );
    } else if(obj.addEventListener) {
        obj.addEventListener( type,fn, false );
    } else {
        var originalHandler = obj["on" + type];
        if (originalHandler) {
          obj["on" + type] = function(e){originalHandler(e);fn(e);};
        } else {
          obj["on" + type] = fn;
        }
    }
}

wHELPERS.prototype.removeEvent = function(obj, type, fn) {
    if (obj.detachEvent) {
        if(obj[type+fn]) {
            obj.detachEvent( 'on'+type, obj[type+fn] );
            obj[type+fn] = null;
        }
    } else if(obj.removeEventListener)
        obj.removeEventListener( type, fn, false );
    else {
        obj["on" + type] = null;
    }
}

// Returns the event's source element
wHELPERS.prototype.getSourceElement = function(e) {
    if(!e) e = window.event;
    if(e.target)
        var srcE = e.target;
    else
        var srcE = e.srcElement;
    if(!srcE) return null;
    if(srcE.nodeType == 3) srcE = srcE.parentNode; // safari weirdness
    if(srcE.tagName.toUpperCase()=='LABEL' && e.type=='click') {
        // when clicking a label, firefox fires the input onclick event
        // but the label remains the source of the event. In Opera and IE
        // the source of the event is the input element. Which is the
        // expected behavior, I suppose.
        if(srcE.getAttribute('for')) {
            srcE = document.getElementById(srcE.getAttribute('for'));
        }
    }
    return srcE;
}

// Cancel the default execution of an event.
wHELPERS.prototype.preventEvent = function(e) {
    if (!e) e = window.event;
    if (e.preventDefault) e.preventDefault();
    else e.returnValue = false;
    return false;
}

// Cancel the propagation of the event
wHELPERS.prototype.stopPropagation = function(e) {
    if (!e) var e = window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
}

// Generates a random ID
wHELPERS.prototype.randomId = function () {
    var seed = (new Date()).getTime();
    seed = seed.toString().substr(6);
    for (var i=0; i<6;i++)
        seed += String.fromCharCode(48 + Math.floor((Math.random()*10)));
    return "id-" + seed;
}

// Activating an Alternate Stylesheet (thx to: http://www.howtocreate.co.uk/tutorials/index.php?tut=0&part=27)
// Use this to activate a CSS Stylesheet that shouldn't be used if javascript is turned off.
// The stylesheet rel attribute should be 'alternate stylesheet'. The title attribute MUST be set.
wHELPERS.prototype.activateStylesheet = function(sheetref) {
    if(document.getElementsByTagName) {
        var ss=document.getElementsByTagName('link');
    } else if (document.styleSheets) {
        var ss = document.styleSheets;
    }
    for(var i=0;ss[i];i++ ) {
        if(ss[i].href.indexOf(sheetref) != -1) {
            ss[i].disabled = true;
            ss[i].disabled = false;
        }
    }
}

// hasClass
wHELPERS.prototype.hasClass = function(element,className) {
    if(element && element.className) {
        if((' ' + element.className + ' ').indexOf(' ' + className +' ') != -1) {
            return true;
        }
    }
    return false;
}
wHELPERS.prototype.hasClassPrefix = function(element,className) {
    if(element && element.className) {
        if((' ' + element.className).indexOf(' ' + className) != -1) {
            return true;
        }
    }
    return false;
}
    wHELPERS.prototype.hasIdPrefix = function(element,idPrefix) {
        if(element && element.id) {
            if(element.id.indexOf(idPrefix) != -1) {
                return true;
            }
        }
        return false;
    }


// getTop / getLeft
// Returns pixel coordinates from the top-left window corner.
wHELPERS.prototype.getTop = function(obj) {
    var cur = 0;
    if(obj.offsetParent) {
        while(obj.offsetParent) {
            if((new wHELPERS()).getComputedStyle(obj,'position') == 'relative' ) {
                // relatively postioned element
                return cur;
            }
            cur+=obj.offsetTop;
            obj = obj.offsetParent;
        }
    }
    return cur;
}
wHELPERS.prototype.getLeft = function(obj) {
    var cur = 0;
    if(obj.offsetParent) {
        while(obj.offsetParent) {
            if((new wHELPERS()).getComputedStyle(obj,'position') == 'relative' ) {
                // relatively postioned element
                return cur;
            }
            if((new wHELPERS()).getComputedStyle(obj,'position') == 'absolute' ) {
                // relatively postioned element
                return cur;
            }
            cur+=obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    return cur;
}

wHELPERS.prototype.getComputedStyle = function(element, styleName) {
    if(window.getComputedStyle) {
        return window.getComputedStyle(element,"").getPropertyValue(styleName);
    } else if(element.currentStyle) {
        return element.currentStyle[styleName];
    }
    return false;
}
// backward compatibility
    var wHelpers = wHELPERS;


   /*
    * MISC FUNCTIONS
   /* ------------------------------------------------------------------------------------------ */

// Push implementation for IE5/mac
if (!Array.prototype.push) {
    Array.prototype.push = function() {
        for (var i = 0; i < arguments.length; ++i) {
            this[this.length] = arguments[i];
        }
        return this.length;
    };
}


  if(wHELPERS) {
      var wFORMS = {

      debugLevel     : 0, /* 0: Inactive, 1+: Debug Level */

      helpers        : new wHELPERS(),
      behaviors      : {},
      onLoadComplete : new Array(),  /* stack of functions to call once all behaviors have been applied */
      processedForm  : null,

      onLoadHandler  : function() {
          for(var behaviorName in  wFORMS.behaviors) {
               wFORMS.debug('wForms/loaded behavior: ' + behaviorName);
          }

          for (var i=0;i<document.forms.length;i++) {
              wFORMS.debug('wForms/initialize: '+ (document.forms[i].name || document.forms[i].id) );
              wFORMS.addBehaviors(document.forms[i]);
          }
      },

      addBehaviors : function (node) {
        if(!node) return;

        if(!node.nodeType) {
            // argument is not a node. probably an id string.
            // (typeof not used for IE5/mac compatibility)
            node = document.getElementById(node);
        }
        if(!node || node.nodeType!=1) return;

        deep=(arguments.length>1)?arguments[1]:true;

        wFORMS._addBehaviors(node, deep);
      },

      _addBehaviors : function (node, deep) {
         if(node.getAttribute('rel')=='no-behavior') {
              return false;
         }
         // Process element nodes only
         if(node.nodeType == 1) {
              if(node.tagName == 'FORM') {
                  wFORMS.processedForm = node;
              }
              for(var behaviorName in wFORMS.behaviors) {
                  wFORMS.behaviors[behaviorName].evaluate(node);
              }
              if(deep) {
                  for (var i=0, cn=node.childNodes, l=cn.length; i<l; i++) {
                     if(cn[i].nodeType==1)
                        wFORMS._addBehaviors(cn[i], deep);
                  }
              }
              if(node.tagName == 'FORM') {
                  // run the init stack
                  for (var i=0;i<wFORMS.onLoadComplete.length;i++) {
                      wFORMS.onLoadComplete[i].func(wFORMS.onLoadComplete[i].form);
                  }
                  // empty the stack
                  if(wFORMS.onLoadComplete.length > 0) {
                      wFORMS.onLoadComplete = new Array();
                  }
              }
          }
      },

      hasBehavior: function(behaviorName) {
          if(wFORMS.behaviors[behaviorName]) return true;
          return false;
      },

      /*
       * DEBUG FUNCTIONS
       * ------------------------------------------------------------------------------------------ */
      debug : function(txt) {
        msgLevel = arguments[1] || 10;  // 1 = least importance, X = most important

        if(wFORMS.debugLevel > 0 && msgLevel >= wFORMS.debugLevel) {
            if(!wFORMS.debugOutput)
                wFORMS.initDebug();
            if(wFORMS.debugOutput)
                wFORMS.debugOutput.innerHTML += "<br />" + txt;
        }
      },

      initDebug : function() {
        var output = document.getElementById('debugOutput');
        if(!output) {
            output = document.createElement('div');
            output.id = 'debugOutput';
            output.style.position   = 'absolute';
            output.style.right      = '10px';
            output.style.top        = '10px';
            output.style.zIndex     = '300';
            output.style.fontSize   = 'x-small';
            output.style.fontFamily = 'courier';
            output.style.backgroundColor = '#DDD';
            output.style.padding    = '5px';
            if(document.body) // if page fully loaded
                wFORMS.debugOutput = document.body.appendChild(output);
        }
        if(wFORMS.debugOutput)
            wFORMS.debugOutput.ondblclick = function() { this.innerHTML = '' };
    }
  };

  wFORMS.NAME     = "wForms";
  wFORMS.VERSION  = "2.0";
  wFORMS.__repr__ = function () {
    return "[" + this.NAME + " " + this.VERSION + "]";
  };
  wFORMS.toString = function () {
    return this.__repr__();
  };


  // For backward compatibility
  wFORMS.utilities = wFORMS.helpers;
  var wf           = wFORMS;
  wf.utilities.getSrcElement                = wFORMS.helpers.getSourceElement;
  wf.utilities.XBrowserPreventEventDefault  = wFORMS.helpers.preventEvent;

  // Initializations:

  // Attach JS only stylesheet.
  wFORMS.helpers.activateStylesheet('wforms-jsonly.css');
  // Parse document and apply wForms behavior
  wFORMS.helpers.addEvent(window,'load',wFORMS.onLoadHandler);
  }


// ------------------------------------------------------------------------------------------
// Field Hint / Tooltip Behavior
// ------------------------------------------------------------------------------------------

if(wFORMS) {

       // Component properties
       wFORMS.idSuffix_fieldHint           = "-H";                     // a hint id is the associated field id (or name) plus this suffix
       wFORMS.className_inactiveFieldHint  = "field-hint-inactive";    // visual effect depends on CSS stylesheet
       wFORMS.className_activeFieldHint    = "field-hint";             // visual effect depends on CSS stylesheet


       wFORMS.behaviors['hint'] = {
           name: 'hint',

           // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
             evaluate: function(node) {
               if(node.id) {
                   if(node.id.indexOf(wFORMS.idSuffix_fieldHint)>0) {
                     // this looks like a field-hint. See if we have a matching field.
                     // try first with the id, then with the name attribute.
                     var id     = node.id.replace(wFORMS.idSuffix_fieldHint, '');
                     var hinted = document.getElementById(id) || wFORMS.processedForm[id];
                   }
                   if(hinted) {
                       // is hint placed on a radio group using the name attribute?
                       if(hinted.length > 0 && hinted[0].type=='radio') {
                           var hintedGroup = hinted;
                           l = hinted.length;
                       } else {
                           var hintedGroup = new Array(hinted);
                           l = 1;
                       }

                       for(var i=0;i<l;i++) {
                           hinted = hintedGroup[i];

                           wFORMS.debug('hint/evaluate: '+ (node.id || node.name));
                           switch(hinted.tagName.toUpperCase()) {
                               case 'SELECT':
                               case 'TEXTAREA':
                               case 'INPUT':
                                    wFORMS.helpers.addEvent(hinted,'focus',wFORMS.behaviors['hint'].run);
                                    wFORMS.helpers.addEvent(hinted,'blur' ,wFORMS.behaviors['hint'].remove);
                                    break;
                               default:
                                    wFORMS.helpers.addEvent(hinted,'mouseover',wFORMS.behaviors['hint'].run);
                                    wFORMS.helpers.addEvent(hinted,'mouseout' ,wFORMS.behaviors['hint'].remove);
                                    break;
                           }

                       }
                   }
               }
           },


           // run: executed when the behavior is activated
           run: function(e) {
               var element   = wFORMS.helpers.getSourceElement(e);
               var fieldHint = document.getElementById(element.id   + wFORMS.idSuffix_fieldHint);
               if(!fieldHint) // try again with the element's name attribute
                   fieldHint = document.getElementById(element.name + wFORMS.idSuffix_fieldHint);
               if(fieldHint) {
                   fieldHint.className = fieldHint.className.replace(wFORMS.className_inactiveFieldHint,
                                                                     wFORMS.className_activeFieldHint);
                   // Field Hint Absolute Positionning
                   fieldHint.style.top  =  (wFORMS.helpers.getTop(element)+ element.offsetHeight).toString() + "px";
                   if(element.tagName.toUpperCase() == 'SELECT')
                       fieldHint.style.left =  (wFORMS.helpers.getLeft(element) + (element.offsetWidth- 8)).toString() + "px";
                   else
                       fieldHint.style.left =  (wFORMS.helpers.getLeft(element)).toString() + "px"; /* + element.offsetWidth */
//                 wFORMS.debug('hint/run: ' + (element.id || element.name) , 5);
               }
           },

           // remove: executed if the behavior should not be applied anymore
           remove: function(e) {
               var element   = wFORMS.helpers.getSourceElement(e);
               var fieldHint = document.getElementById(element.id   + wFORMS.idSuffix_fieldHint);
               if(!fieldHint) // try again with the element's name attribute
                   fieldHint = document.getElementById(element.name + wFORMS.idSuffix_fieldHint);
               if(fieldHint)
                   fieldHint.className = fieldHint.className.replace(wFORMS.className_activeFieldHint,
                                                                     wFORMS.className_inactiveFieldHint);
//             wFORMS.debug('hint/remove: ' + (element.id || element.name) , 5);
           }
       }
   }
// ------------------------------------------------------------------------------------------
// Form Paging Behavior
// ------------------------------------------------------------------------------------------

   if(wFORMS) {
        // Component properties
        wFORMS.className_paging             = "wfPage";
        wFORMS.className_pagingCurrent      = "wfCurrentPage";
        wFORMS.className_pagingButtons      = "wfPageButton";
        wFORMS.className_hideSubmit         = "wfHideSubmit";
        wFORMS.idPrefix_pageIndex           = "wfPgIndex-";
        wFORMS.runValidationOnPageNext      = true;

        if(!wFORMS.arrMsg) wFORMS.arrMsg    = new Array();
        wFORMS.arrMsg[4]                    = "Next Page";      //arrMsg[4] for backward compatibility
        wFORMS.arrMsg[5]                    = "Previous Page";  //arrMsg[5] for backward compatibility


        wFORMS.behaviors['paging'] = {

            idSuffix_buttonsPlaceholder: "-buttons",
            className_pageNextButton: wFORMS.className_pagingButtons + " wfPageNextButton",
            className_pagePreviousButton: wFORMS.className_pagingButtons + " wfPagePreviousButton",
            behaviorInUse : false,
            allowNestedPages: false,
            onPageChange : null, /* Function to run when the page is changed */

            // ------------------------------------------------------------------------------------------
            // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
            // ------------------------------------------------------------------------------------------
            evaluate: function(node) {
                if (wFORMS.helpers.hasClass(node,wFORMS.className_paging)) {

                    if(!wFORMS.behaviors['paging'].allowNestedPages && wFORMS.behaviors['paging'].getPageElement(node)) {
                        // found a parent node that is also a page element.
                        // remove paging class so that the content is not hidden.
                        node.className = node.className.replace(wFORMS.className_paging,"");
                        return;
                    }

                    wFORMS.behaviors['paging'].behaviorInUse = true;

                    var currentPageIndex = wFORMS.behaviors['paging'].getPageIndex(node);
                    if(currentPageIndex > 1) {
                        // add previous page button
                        var placeholder = this.getButtonPlaceholder(node);
                        var button = placeholder.insertBefore(this.createPreviousPageButton(),placeholder.firstChild);
                        wFORMS.helpers.addEvent(button,'click',wFORMS.behaviors['paging'].pagingPrevious);
                    } else {
                        // set current page class
                        node.className += ' ' + wFORMS.className_pagingCurrent;

                        // get the corresponding form element
                        var form = wFORMS.behaviors['paging'].getFormElement(node);

                        // prevent submission of form with enter key.
                        wFORMS.helpers.addEvent(form,'submit', function(e) { var element = wFORMS.helpers.getSourceElement(e);
                                                                             if(element.type && element.type.toLowerCase()=='text')
                                                                                return wFORMS.preventEvent(e); } );
                        wFORMS.preventSubmissionOnEnter = true; // for input validation behavior

                    }
                    if(document.getElementById(wFORMS.idPrefix_pageIndex+(currentPageIndex+1).toString())) {
                        // add next page button
                        var placeholder = this.getButtonPlaceholder(node);
                        var button = placeholder.appendChild(this.createNextPageButton());
                        wFORMS.helpers.addEvent(button,'click',wFORMS.behaviors['paging'].pagingNext);
                        // hide submit button until the last page of the form is reached (do it once on the 1st page)
                        if(currentPageIndex==1) {
                            wFORMS.behaviors['paging'].hideSubmitButton(form);
                        }
                    }
                }

            },
            getButtonPlaceholder: function(page) {
                var p = document.getElementById(page.id+this.idSuffix_buttonsPlaceholder);
                if(!p) {
                    var buttonPlaceholder = document.createElement("div");
                    buttonPlaceholder = page.appendChild(buttonPlaceholder);
                    buttonPlaceholder.className = 'actions';
                    buttonPlaceholder.id = page.id+this.idSuffix_buttonsPlaceholder;
                    return buttonPlaceholder;
                }
                return p;
            },
            createNextPageButton: function() {
                var button = document.createElement("input");
                button.setAttribute('value',wFORMS.arrMsg[4]);
                button.setAttribute('type',"button");
                button.className = this.className_pageNextButton;
                return button;
            },
            createPreviousPageButton: function() {
                // add previous page button
                var button = document.createElement("input");
                button.setAttribute('value',wFORMS.arrMsg[5]);
                button.setAttribute('type',"button");
                button.className = this.className_pagePreviousButton;
                return button;
            },

            // ------------------------------------------------------------------------------------------
            // pagingNext
            // ------------------------------------------------------------------------------------------
            pagingNext: function(e) {
                var element  = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e

                var pageElement     = wFORMS.behaviors['paging'].getPageElement(element);
                var pageIndex       = wFORMS.behaviors['paging'].getPageIndex(pageElement) + 1;
                var nextPageElement = document.getElementById(wFORMS.idPrefix_pageIndex+pageIndex.toString());

                if(nextPageElement) {
                    if(!wFORMS.hasBehavior('validation') ||
                       (wFORMS.hasBehavior('validation') && !wFORMS.runValidationOnPageNext) ||
                       (wFORMS.hasBehavior('validation') &&  wFORMS.runValidationOnPageNext && wFORMS.functionName_formValidation(e, true))) {

                        pageElement.className      = pageElement.className.replace(new RegExp(wFORMS.className_pagingCurrent,"g"),"");
                        nextPageElement.className += ' ' + wFORMS.className_pagingCurrent;

                        // show submit button if the last page of the form is reached
                        if(wFORMS.behaviors['paging'].isLastPage(pageIndex)) {
                            var form = wFORMS.behaviors['paging'].getFormElement(nextPageElement);
                            wFORMS.behaviors['paging'].showSubmitButton(form);
                        }
                        // trigger onPageChange event
                        if(wFORMS.behaviors['paging'].onPageChange) {
                            wFORMS.behaviors['paging'].onPageChange(nextPageElement);
                        }
                        window.scroll(0,0);
                    }
                }
            },

            // ------------------------------------------------------------------------------------------
            // pagingPrevious
            // ------------------------------------------------------------------------------------------
            pagingPrevious: function(e) {
                var element  = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e

                var pageElement         = wFORMS.behaviors['paging'].getPageElement(element);
                var pageIndex           = wFORMS.behaviors['paging'].getPageIndex(pageElement) - 1;
                var previousPageElement = document.getElementById(wFORMS.idPrefix_pageIndex+pageIndex.toString());

                if(previousPageElement) {
                    pageElement.className          = pageElement.className.replace(new RegExp(wFORMS.className_pagingCurrent,"g"),"");
                    previousPageElement.className += ' ' + wFORMS.className_pagingCurrent;

                    // hide submit button
                    var form = wFORMS.behaviors['paging'].getFormElement(previousPageElement);
                    wFORMS.behaviors['paging'].hideSubmitButton(form);

                    // trigger onPageChange event
                    if(wFORMS.behaviors['paging'].onPageChange) {
                        wFORMS.behaviors['paging'].onPageChange(previousPageElement);
                    }
                    window.scroll(0,0);
                }
            },
            // ------------------------------------------------------------------------------------------
            // show/hide submit button
            // ------------------------------------------------------------------------------------------
            showSubmitButton: function(form) {
                var buttons = form.getElementsByTagName('input');
                for (var i=0;i<buttons.length;i++) {
                    if(buttons[i].type && buttons[i].type.toLowerCase() == 'submit') {
                        buttons[i].className = buttons[i].className.replace(wFORMS.className_hideSubmit,"");
                    }
                }
            },
            hideSubmitButton: function(form) {
                var buttons = form.getElementsByTagName('input');
                for (var i=0;i<buttons.length;i++) {
                    if(buttons[i].type && buttons[i].type.toLowerCase() == 'submit'
                       && !wFORMS.helpers.hasClass(buttons[i],wFORMS.className_hideSubmit) ) {
                        buttons[i].className += ' ' + wFORMS.className_hideSubmit;
                    }
                }
            },

            // ------------------------------------------------------------------------------------------
            // isLastPage
            // ------------------------------------------------------------------------------------------
            isLastPage: function(pageIndex) {
                if(isNaN(pageIndex)) {
                    pageIndex = parseInt(pageIndex.replace(/[\D]*/,""));
                }
                pageIndex++;
                var furtherPageElement = document.getElementById(wFORMS.idPrefix_pageIndex+pageIndex.toString());
                if(!furtherPageElement)
                    return true;
                return false;
            },
            // ------------------------------------------------------------------------------------------
            // gotoPage
            // ------------------------------------------------------------------------------------------
            gotoPage: function(pageIndex) {

                if(isNaN(pageIndex)) {
                    var pageElement = document.getElementById(pageIndex);
                } else {
                    var pageElement = document.getElementById(wFORMS.idPrefix_pageIndex+pageIndex.toString());
                }
                if(!pageElement) return false;

                // get the corresponding form element
                var form = wFORMS.behaviors['paging'].getFormElement(pageElement);

                // hide current page
                var allElements = form.getElementsByTagName("*");
                for(var i=0; i< allElements.length; i++) {
                    var n =  allElements[i];
                    if(wFORMS.helpers.hasClass(allElements[i],wFORMS.className_pagingCurrent)) {
                        n.className = n.className.replace(new RegExp(wFORMS.className_pagingCurrent,"g"),"");
                        break;
                    }
                }
                //show/hide submit button as necessary

                if(wFORMS.behaviors['paging'].isLastPage(pageIndex))
                    wFORMS.behaviors['paging'].showSubmitButton(form);
                else {
                    wFORMS.behaviors['paging'].hideSubmitButton(form);
                }
                // Show the page
                pageElement.className += ' ' + wFORMS.className_pagingCurrent;

                // trigger onPageChange event
                if(wFORMS.behaviors['paging'].onPageChange) {
                    wFORMS.behaviors['paging'].onPageChange(pageElement);
                }
            },
            // ------------------------------------------------------------------------------------------
            // getFormElement
            // ------------------------------------------------------------------------------------------
            getFormElement: function(element) {
                var form = element.parentNode;
                while(form && form.tagName != "FORM")
                    form = form.parentNode;
                return form;
            },
            // ------------------------------------------------------------------------------------------
            // getPageElement
            // ------------------------------------------------------------------------------------------
            getPageElement: function(element) {
                var n = element.parentNode;
                while(n && n.tagName != "FORM" && (!n.className || !wFORMS.helpers.hasClass(n,wFORMS.className_paging)))
                    n = n.parentNode;
                if(n && wFORMS.helpers.hasClass(n,wFORMS.className_paging))
                    return n;
                else
                    return null;
            },
            // ------------------------------------------------------------------------------------------
            // getPageIndex
            // ------------------------------------------------------------------------------------------
            getPageIndex: function(element) {
                if(element && element.id)
                    return parseInt(element.id.replace(/[\D]*/,""));
                else
                    return null;
            }
       } // End wFORMS.behaviors['paging']
   }



// ------------------------------------------------------------------------------------------
// Repeat Behavior
// ------------------------------------------------------------------------------------------

   if(wFORMS) {
        // Component properties
        wFORMS.className_repeat             = "repeat";
        wFORMS.className_delete             = "removeable";
        wFORMS.className_duplicateLink      = "duplicateLink";
        wFORMS.className_removeLink         = "removeLink";
        wFORMS.className_preserveRadioName  = "preserveRadioName";
        wFORMS.idSuffix_repeatCounter       = "-RC";
        wFORMS.idSuffix_duplicateLink       = "-wfDL";
        wFORMS.preserveRadioName            = false;                                     // if true, Repeat behavior will preserve name attributes for radio input.
        wFORMS.limitSwitchScope             = true;                                      // if true, Repeat behavior will limit the scope of nested switches.

        if(!wFORMS.arrMsg) wFORMS.arrMsg    = new Array();
        wFORMS.arrMsg[0]                    = "Add another response";                    // repeat link
        wFORMS.arrMsg[1]                    = "Will duplicate this question or section." // title attribute on the repeat link
        wFORMS.arrMsg[2]                    = "Remove";                                  // remove link
        wFORMS.arrMsg[3]                    = "Will remove this question or section."    // title attribute on the remove link

        wFORMS.behaviors['repeat'] = {

            onRepeat: null, /* Function to run after the element is repeated */
            onRemove: null,     /* Function to run after the element is removed  */
            allowRepeat: null, /* Function for fine control on repeatable section */

            // ------------------------------------------------------------------------------------------
            // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
            // ------------------------------------------------------------------------------------------
            evaluate: function(node) {

                // Repeatable element
                if(wFORMS.helpers.hasClass(node, wFORMS.className_repeat)) {
                    //wFORMS.debug('evaluate/repeat: '+ node.id,3);

                     if(!node.id)
                        node.id = wFORMS.helpers.randomId();

                    // Check if we have a 'repeat' link
                    var repeatLink = document.getElementById(node.id + wFORMS.idSuffix_duplicateLink);
                    if(!repeatLink) {
                        // create the repeat link
                        repeatLink = wFORMS.behaviors['repeat'].createRepeatLink(node.id);

                        // find where to insert the link
                        if(node.tagName.toUpperCase()=="TR") {
                            // find the last TD
                            var n = node.lastChild;
                            while(n && n.nodeType != 1)
                                n = n.previousSibling;
                            if(n && n.nodeType == 1)
                                n.appendChild(repeatLink);
                            // Else Couldn't find the TD. Table row malformed ?
                        } else
                            node.appendChild(repeatLink);
                    }
                    // Add hidden counter field if necessary
                    var counterField = document.getElementById(node.id + wFORMS.idSuffix_repeatCounter);
                    if(!counterField) {
                        // IE Specific :-(
                        if(document.all && !window.opera) {
                            // see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp
                            var counterFieldId = node.id + wFORMS.idSuffix_repeatCounter;
                            if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac
                                counterField   = document.createElement("INPUT NAME=\"" + counterFieldId + "\"");
                            else
                                counterField   = document.createElement("<INPUT NAME=\"" + counterFieldId + "\"></INPUT>");
                            counterField.type  ='hidden';
                            counterField.id    = counterFieldId;
                            counterField.value = "1";
                        }
                        else {
                            counterField = document.createElement("INPUT");
                            counterField.setAttribute('type','hidden'); // hidden
                            counterField.setAttribute('value','1');
                            counterField.setAttribute('name', node.id + wFORMS.idSuffix_repeatCounter);
                            counterField.setAttribute('id', node.id + wFORMS.idSuffix_repeatCounter);
                        }

                        // get the form element
                        var form = node.parentNode;
                        while(form && form.tagName.toUpperCase() != "FORM")
                            form = form.parentNode;

                        form.appendChild(counterField);
                    }

                    // Add event handler
                    wFORMS.helpers.addEvent(repeatLink,'click',wFORMS.behaviors['repeat'].duplicateFieldGroup);
                }
                // ------------------------------------------------------------------------------------------
                // Removeable element
                if(wFORMS.helpers.hasClass(node, wFORMS.className_delete)) {
                    var removeLink = wFORMS.behaviors['repeat'].createRemoveLink();
                    // find where to insert the link
                    if(node.tagName.toUpperCase()=="TR") {
                        // find the last TD
                        var n = node.lastChild;
                        while(n && n.nodeType != 1)
                            n = n.previousSibling;
                        if(n && n.nodeType == 1)
                            n.appendChild(removeLink);
                        // Else Couldn't find the TD. Table row malformed ?
                    } else
                        node.appendChild(removeLink);
                }
            },
            createRepeatLink: function(id) {
                var repeatLink = document.createElement("a");
                var spanNode = document.createElement("span");  // could be used for CSS image replacement
                var textNode = document.createTextNode(wFORMS.arrMsg[0]);
                repeatLink.id = id + wFORMS.idSuffix_duplicateLink;
                repeatLink.setAttribute('href',"#");
                repeatLink.className = wFORMS.className_duplicateLink;
                repeatLink.setAttribute('title', wFORMS.arrMsg[1]);
                spanNode.appendChild(textNode);
                repeatLink.appendChild(spanNode);
                return repeatLink;
            },
            createRemoveLink: function() {
                var removeLink = document.createElement("a");
                var spanNode   = document.createElement("span");  // could be used for CSS image replacement
                var textNode   = document.createTextNode(wFORMS.arrMsg[2]);
                removeLink.setAttribute('href',"#");
                removeLink.className = wFORMS.className_removeLink;
                removeLink.setAttribute('title',wFORMS.arrMsg[3]);
                spanNode.appendChild(textNode);
                removeLink.appendChild(spanNode);
                wFORMS.helpers.addEvent(removeLink,'click',wFORMS.behaviors['repeat'].removeFieldGroup);
                return removeLink;
            },
            duplicateFieldGroup: function(e) {

                var element  = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e

                // override of the wFORMS.preserveRadioName property using a class on the repeat link.
                var preserveRadioName = wFORMS.helpers.hasClass(element,wFORMS.className_preserveRadioName) ? true : wFORMS.preserveRadioName;
                //wFORMS.debug('preserveRadioName='+preserveRadioName);

                // Get Element to duplicate.
                while (element && !wFORMS.helpers.hasClass(element,wFORMS.className_duplicateLink)) {
                    element = element.parentNode;
                }
                var idOfRepeatedSection = element.id.replace(wFORMS.idSuffix_duplicateLink,"");
                var element = document.getElementById(idOfRepeatedSection);


                if (element) {
                    var wBehavior = wFORMS.behaviors['repeat']; // shortcut

                    // Check if we have a custom function that prevents the repeat
                    if(wBehavior.allowRepeat) {
                        if(!wBehavior.allowRepeat(element)) return false;
                    }

                    // Extract row counter information
                    counterField = document.getElementById(element.id + wFORMS.idSuffix_repeatCounter);
                    if(!counterField) return; // should not happen.
                    var rowCount = parseInt(counterField.value) + 1;
                    // Prepare id suffix
                    var suffix = "-" + rowCount.toString()
                    // duplicate node tree
                    var dupTree = wBehavior.replicateTree(element, null, suffix, preserveRadioName);  //  sourceNode.cloneNode(true);
                    // find insert point in DOM tree (after existing repeated element)
                    var insertNode = element.nextSibling;

                    while(insertNode &&
                         (insertNode.nodeType==3 ||       // skip text-node that can be generated server-side when populating a previously repeated group
                          wFORMS.helpers.hasClass(insertNode,wFORMS.className_delete))) {
                        insertNode = insertNode.nextSibling;
                    }
                    element.parentNode.insertBefore(dupTree,insertNode);     // Buggy rendering in IE5/Mac
                    // if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1)
                    //

                    // the copy is not duplicable, it's removeable
                    dupTree.className = element.className.replace(wFORMS.className_repeat,wFORMS.className_delete);
                    // Save new row count
                    document.getElementById(element.id + wFORMS.idSuffix_repeatCounter).value = rowCount;
                    // re-add wFORMS behaviors
                    wFORMS.addBehaviors(dupTree);

                    if(wBehavior.onRepeat)
                        wBehavior.onRepeat(element,dupTree);
                }

                return wFORMS.helpers.preventEvent(e);
            },

            removeFieldGroup: function(e) {
                var element  = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e
                // Get Element to remove.
                var element = element.parentNode;
                while (element && !wFORMS.helpers.hasClass(element,wFORMS.className_delete)) {
                    element = element.parentNode;
                }
                element.parentNode.removeChild(element);
                if(wFORMS.behaviors['repeat'].onRemove)
                        wFORMS.behaviors['repeat'].onRemove(element);
                return wFORMS.helpers.preventEvent(e);
            },

            removeRepeatCountSuffix: function(str) {
                return str.replace(/-\d+$/,'');
            },

            replicateTree: function(element,parentElement, idSuffix, preserveRadioName) {

                // Duplicating TEXT-NODE (do not copy value of textareas)
                if(element.nodeType==3) {
                    if(element.parentNode.tagName.toUpperCase() != 'TEXTAREA')
                        var newElement = document.createTextNode(element.data);
                }
                // Duplicating ELEMENT-NODE
                else if(element.nodeType==1) {

                    // Do not copy repeat/remove links
                    if(wFORMS.helpers.hasClass(element,wFORMS.className_duplicateLink) ||
                       wFORMS.helpers.hasClass(element,wFORMS.className_removeLink))
                        return null;
                    // Exclude duplicated elements of a nested repeat group
                    if(wFORMS.helpers.hasClass(element,wFORMS.className_delete))
                        return null;
                    // Adjust row suffix id if we find a nested repeat group
                    if(wFORMS.helpers.hasClass(element,wFORMS.className_repeat) && parentElement!=null)
                        idSuffix = idSuffix.replace('-','__');

                    if(!document.all || window.opera) {
                        // Common Branch
                        var newElement = document.createElement(element.tagName);
                    } else {
                        // IE Branch
                        // see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp
                        var tagHtml = element.tagName;

                        if(element.name)
                            if (element.tagName.toUpperCase() == "INPUT" &&
                                element.type.toLowerCase()    == "radio" && preserveRadioName)
                                tagHtml += " NAME='" + element.name + "' ";
                            else
                                tagHtml += " NAME='" + wFORMS.behaviors['repeat'].removeRepeatCountSuffix(element.name) + idSuffix + "' ";
                        if(element.type) {
                            tagHtml += " TYPE='" + element.type + "' ";
                        }
                        if(element.selected)
                            tagHtml += " SELECTED='SELECTED' ";
                        if(element.checked)
                            tagHtml += " CHECKED='CHECKED' ";

                        if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac
                            var newElement = document.createElement(tagHtml);
                        else
                            var newElement = document.createElement("<" + tagHtml + "></"+ element.tagName + ">");
                        try { newElement.type = element.type; } catch(e) {}; // nail it down for IE5 ?, breaks in IE6

                    }

                    // duplicate attributes
                    for(var i=0; i< element.attributes.length; i++) {
                        var attribute = element.attributes[i];

                        // Get attribute value.
                        if( attribute.specified || // in IE, the attributes array contains all attributes in the DTD
                            attribute.nodeName.toLowerCase() == 'value' ) { // attr.specified buggy in IE?
                            // Add the row suffix if necessary.
                            if( attribute.nodeName.toLowerCase() == "id" ||
                                attribute.nodeName.toLowerCase() == "name" ||
                                attribute.nodeName.toLowerCase() == "for") {

                                if(wFORMS.hasBehavior('hint') &&
                                   attribute.nodeValue.indexOf(wFORMS.idSuffix_fieldHint) != -1)  {
                                    //leave the field hint suffix at the end of the id.
                                    var value = attribute.nodeValue;
                                    value= wFORMS.behaviors['repeat'].removeRepeatCountSuffix(value.substr(0,value.indexOf(wFORMS.idSuffix_fieldHint))) + idSuffix + wFORMS.idSuffix_fieldHint;
                                }
                                else {
                                    if(element.tagName.toUpperCase()=="INPUT" &&
                                       element.getAttribute('type',false).toLowerCase()=="radio" &&
                                       attribute.nodeName.toLowerCase() == "name" &&
                                       preserveRadioName) {
                                        var value = attribute.nodeValue;
                                    }
                                    else {
                                        // var value = wFORMS.behaviors['repeat'].removeRepeatCountSuffix(attribute.nodeValue) + idSuffix;
                                        var value = attribute.nodeValue + idSuffix;
                                    }
                                }
                            } else {
                                // Do not copy the value attribute for text/password/file input
                                if(attribute.nodeName.toLowerCase() == "value" &&
                                   element.tagName.toUpperCase()=='INPUT'      &&
                                  (element.type.toLowerCase() == 'text'     ||
                                   element.type.toLowerCase() == 'password' ||
                                   element.type.toLowerCase() == 'hidden' ||
                                   element.type.toLowerCase() == 'file'))
                                    var value='';
                                // Do not copy the switch behavior's 'event handled' flag, stored in the rel attribute
                                else if(attribute.nodeName.toLowerCase() == "rel" &&
                                        attribute.nodeValue.indexOf('wfHandled') != -1) {
                                    var value = attribute.nodeValue.replace('wfHandled','');
                                } else
                                    var value = attribute.nodeValue;
                            }
                            // Create attribute and assign value
                            switch(attribute.nodeName.toLowerCase()) {
                                case "class":
                                    newElement.className = value;
                                    break;
                                case "style": // inline style attribute (fix for IE)
                                    if(element.style && element.style.cssText)
                                        newElement.style.cssText = element.style.cssText;
                                    break;
                                case "onclick": // inline event handler (fix for IE)
                                    newElement.onclick     = element.onclick;
                                    break;
                                case "onchange":
                                    newElement.onchange    = element.onchange;
                                    break;
                                case "onsubmit":
                                    newElement.onsubmit    = element.onsubmit;
                                    break;
                                case "onmouseover":
                                    newElement.onmouseover = element.onmouseover;
                                    break;
                                case "onmouseout":
                                    newElement.onmouseout  = element.onmouseout;
                                    break;
                                case "onmousedown":
                                    newElement.onmousedown = element.onmousedown;
                                    break;
                                case "onmouseup":
                                    newElement.onmouseup   = element.onmouseup;
                                    break;
                                case "ondblclick":
                                    newElement.ondblclick  = element.ondblclick;
                                    break;
                                case "onkeydown":
                                    newElement.onkeydown   = element.onkeydown;
                                    break;
                                case "onkeyup":
                                    newElement.onkeyup     = element.onkeyup;
                                    break;
                                case "onblur":
                                    newElement.onblur      = element.onblur;
                                    break;
                                case "onfocus":
                                    newElement.onfocus     = element.onfocus;
                                    break;
                                default:
                                    newElement.setAttribute(attribute.name, value, 0);
                            }
                        }
                    }
                }
                if(parentElement && newElement)
                    parentElement.appendChild(newElement);
                for(var i=0; i<element.childNodes.length;i++) {
                    wFORMS.behaviors['repeat'].replicateTree(element.childNodes[i],newElement,idSuffix, preserveRadioName);
                }
                return newElement;
            }
       } // End wFORMS.behaviors['repeat']


   }

// ------------------------------------------------------------------------------------------
// Switch Behavior
// ------------------------------------------------------------------------------------------

 if(wFORMS) {

        // Component properties
        wFORMS.classNamePrefix_switch       = "switch";
        wFORMS.className_switchIsOn         = "swtchIsOn";    // used to keep track of the switch state on buttons and links (where the checked attribute is not available)
        wFORMS.className_switchIsOff        = "swtchIsOff";
        wFORMS.classNamePrefix_offState     = "offstate";
        wFORMS.classNamePrefix_onState      = "onstate";
        wFORMS.switchScopeRootTag           = "";             // deprecated.

        wFORMS.switchTriggers               = [];             // associative multi-dimensional array (switchname->element Ids)
        wFORMS.switchTargets                = [];             // associative multi-dimensional array (switchname->element Ids)


        wFORMS.behaviors['switch'] = {

           // ------------------------------------------------------------------------------------------
           // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
           // ------------------------------------------------------------------------------------------
           evaluate: function(node) {

                // Handle Switch Triggers
                // add event handles and populate the wFORMS.switchTriggers
                // associative array (switchname->element Ids)
                // ------------------------------------------------------------------------------------------
                if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_switch)) {

                    if(!node.id) node.id = wFORMS.helpers.randomId();

                    if(!wFORMS.processedForm || !wFORMS.processedForm.id)
                        var formId = "undefined";
                    else
                        var formId = wFORMS.processedForm.id;

                    // Go through each class (one element can have more than one switch trigger).
                    var switchNames = wFORMS.behaviors['switch'].getSwitchNames(node);
                    for(var i=0; i < switchNames.length; i++) {
                        if(!wFORMS.switchTriggers[formId])
                            wFORMS.switchTriggers[formId] = new Array();
                        if(!wFORMS.switchTriggers[formId][switchNames[i]])
                            wFORMS.switchTriggers[formId][switchNames[i]] = new Array();
                        if(!wFORMS.switchTriggers[formId][switchNames[i]][node.id]) {
                            wFORMS.switchTriggers[formId][switchNames[i]].push(node.id);
                        }
                    }

                    switch(node.tagName.toUpperCase()) {

                        case "OPTION":
                            // get the SELECT element
                            var selectNode = node.parentNode;
                            while(selectNode && selectNode.tagName.toUpperCase() != 'SELECT') {
                                var selectNode = selectNode.parentNode;
                            }
                            if(!selectNode) { alert('Error: invalid markup in SELECT field ?'); return false;  } // invalid markup
                            if(!selectNode.id) selectNode.id = wFORMS.helpers.randomId();

                            // Make sure we have only one event handler for the select.
                            if(!selectNode.getAttribute('rel') || selectNode.getAttribute('rel').indexOf('wfHandled')==-1) {
                                //wFORMS.debug('switch/add event: '+ selectNode.className + ' ' + selectNode.tagName);
                                selectNode.setAttribute('rel', (selectNode.getAttribute('rel')||"") + ' wfHandled');
                                wFORMS.helpers.addEvent(selectNode, 'change', wFORMS.behaviors['switch'].run);
                            }
                            break;

                        case "INPUT":
                            if(node.type && node.type.toLowerCase() == 'radio') {
                                // Add the onclick event on radio inputs of the same group
                                var formElement = node.form;
                                for (var j=0; j<formElement[node.name].length; j++) {
                                    var radioNode = formElement[node.name][j];
                                    // prevents conflicts with elements with an id = name of radio group
                                    if(radioNode.type.toLowerCase() == 'radio') {
                                        // Make sure we have only one event handler for this radio input.
                                        if(!radioNode.getAttribute('rel') || radioNode.getAttribute('rel').indexOf('wfHandled')==-1) {
                                            wFORMS.helpers.addEvent(radioNode, 'click', wFORMS.behaviors['switch'].run);
                                            // flag the node
                                            radioNode.setAttribute('rel', (radioNode.getAttribute('rel')||"") + ' wfHandled');
                                        }
                                    }
                                }
                            } else {
                                wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['switch'].run);
                            }
                            break;

                        default:
                            wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['switch'].run);
                            break;
                    }
                }

                // Push targets in the wFORMS.switchTargets array
                // (associative array with switchname -> element ids)
                // ------------------------------------------------------------------------------------------
                if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_offState) ||
                    wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_onState)) {

                    if(!node.id) node.id = wFORMS.helpers.randomId();

                    if(!wFORMS.processedForm || !wFORMS.processedForm.id)
                        var formId = "undefined";
                    else
                        var formId = wFORMS.processedForm.id;

                    // Go through each class (one element can be the target of more than one switch).
                    var switchNames = wFORMS.behaviors['switch'].getSwitchNames(node);

                    for(var i=0; i < switchNames.length; i++) {
                        if(!wFORMS.switchTargets[formId])
                            wFORMS.switchTargets[formId] = new Array();
                        if(!wFORMS.switchTargets[formId][switchNames[i]])
                            wFORMS.switchTargets[formId][switchNames[i]] = new Array();
                        if(!wFORMS.switchTargets[formId][switchNames[i]][node.id])
                            wFORMS.switchTargets[formId][switchNames[i]].push(node.id);
                        //wFORMS.debug('switch/evaluate: [target] '+ switchNames[i],3);
                    }
                }

                if(node.tagName && node.tagName.toUpperCase()=='FORM') {
                    // function to be called when all behaviors have been applied
                    wFORMS.onLoadComplete.push({form:node, func: wFORMS.behaviors['switch'].init});
                    // clear existing triggers/targets for that form.
                    wFORMS.behaviors['switch'].clear(node.id);
                }
           },

           // ------------------------------------------------------------------------------------------
           // init: executed once evaluate has been applied to all elements
           // ------------------------------------------------------------------------------------------
           init: function(form) {

               if(!form || !form.id)
                    var formId = "undefined";
               else
                    var formId = form.id;
               // go through all switch triggers and activate those who are already ON
               for(var switchName in wFORMS.switchTriggers[formId]) {
                    // go through all triggers for the current switch
                    for(var i=0; i< wFORMS.switchTriggers[formId][switchName].length; i++) {
                        var element = document.getElementById(wFORMS.switchTriggers[formId][switchName][i]);
                        if(wFORMS.behaviors['switch'].isTriggerOn(element,switchName)) {
                            // if it's a select option, get the select element
                            if(element.tagName.toUpperCase()=='OPTION') {
                                var element = element.parentNode;
                                while(element && element.tagName.toUpperCase() != 'SELECT') {
                                    var element = element.parentNode;
                                }
                            }
                            // run the trigger
                            wFORMS.behaviors['switch'].run(element);
                        }
                   }
               }
           },

           // ------------------------------------------------------------------------------------------
           // run: executed when the behavior is activated
           // ------------------------------------------------------------------------------------------
           run: function(e) {
                var element   = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e;
                //wFORMS.debug('switch/run: ' + element.id , 5);

                if(element.form)
                    var form = element.form;
                else {
                    var form = element.parentNode;
                    while(form && form.tagName != "FORM")
                        form = form.parentNode;
                }
                if(!form || !form.id) {
                    // switch not within a form tag
                    var formId = "undefined";
                } else
                    var formId = form.id;

                var switches_ON  = new Array();
                var switches_OFF = new Array();

                // Get list of triggered switches (some ON, some OFF)
                switch(element.tagName.toUpperCase()) {
                    case 'SELECT':
                        for(var i=0;i<element.options.length;i++) {
                            if(i==element.selectedIndex) {
                                switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(element.options[i]));
                            } else {
                                switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(element.options[i]));
                            }
                        }

                        break;
                    case 'INPUT':
                        if(element.type.toLowerCase() == 'radio') {
                            // Go through the radio group.

                            for(var i=0;i <element.form[element.name].length;i++) {
                                var radioElement = element.form[element.name][i];
                                if(radioElement.checked) {
                                    switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(radioElement));
                                } else {
                                    //wFORMS.debug(wFORMS.behaviors['switch'].getSwitchNames(radioElement).length,1);
                                    switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(radioElement));
                                }
                            }
                        } else {
                            if(element.checked || wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn)) {
                                switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(element));
                            } else {
                                switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(element));
                            }
                        }
                        break;
                    default:
                        break;
                }

                // Turn off switches first
                for(var i=0; i < switches_OFF.length; i++) {
                    // Go through all targets of the switch
                    var elements = wFORMS.behaviors['switch'].getElementsBySwitchName(switches_OFF[i], formId);
                    for(var j=0;j<elements.length;j++) {

                        // only turn off a target if all its triggers are off
                        var triggers = wFORMS.switchTriggers[formId][switches_OFF[i]];
                        var doSwitch = true;

                        for (var k=0; k < triggers.length; k++) {
                            var trigger = document.getElementById(triggers[k]);
                            if(wFORMS.behaviors['switch'].isTriggerOn(trigger, switches_OFF[i])) {
                                // An element with the REPEAT behavior limits the scope of switches
                                // targets outside of the scope of the switch are not affected.
                                if(wFORMS.behaviors['switch'].isWithinSwitchScope(trigger, elements[j])) {
                                    // one of the trigger is still ON. no switch off
                                    doSwitch = false;
                                }
                            }
                        }
                        if(doSwitch) {
                            wFORMS.behaviors['switch'].switchState(elements[j], wFORMS.classNamePrefix_onState, wFORMS.classNamePrefix_offState);
                        }
                    }
                }
                // Turn on
                for(var i=0; i < switches_ON.length; i++) {
                    var elements = wFORMS.behaviors['switch'].getElementsBySwitchName(switches_ON[i], formId);
                    for(var j=0;j<elements.length;j++) {
                        // An element with the REPEAT behavior limits the scope of switches
                        // targets outside of the scope of the switch are not affected.
                        if(wFORMS.behaviors['switch'].isWithinSwitchScope(element, elements[j])) {

                            wFORMS.behaviors['switch'].switchState(elements[j], wFORMS.classNamePrefix_offState, wFORMS.classNamePrefix_onState);
                            //wFORMS.debug('switch/run: [turn on ' + switches_ON[i] + '] ' + elements[j].id , 3);
                        }
                    }
                }
           },

           // ------------------------------------------------------------------------------------------
           // clear: executed if the behavior should not be applied anymore
           // ------------------------------------------------------------------------------------------
           clear: function(form) {
                // @TODO: go through wFORMS.switchTriggers to remove events.
                if(form) {
                    wFORMS.switchTriggers[form] = [];
                    wFORMS.switchTargets[form] = [];
                } else {
                    wFORMS.switchTriggers = [];
                    wFORMS.switchTargets = [];
                }
           },


           // ------------------------------------------------------------------------------------------
           // Get the list of switches
           // Note: potential conflict if an element is both a switch and a target.
           getSwitchNames: function(element) {
                var switchNames = new Array();
                var classNames  = element.className.split(' ');
                for(var i=0; i < classNames.length; i++) {
                    // Note: Might be worth keeping a prefix on switchName to prevent collision with reserved names
                    if(classNames[i].indexOf(wFORMS.classNamePrefix_switch) == 0) {
                        switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_switch.length+1));
                    }
                    if(classNames[i].indexOf(wFORMS.classNamePrefix_onState) == 0) {
                        switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_onState.length+1));
                    }
                    else if(classNames[i].indexOf(wFORMS.classNamePrefix_offState) == 0) {
                        switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_offState.length+1));
                    }
                }
                return switchNames;
            },

           // ------------------------------------------------------------------------------------------
            switchState: function(element, oldStateClass, newStateClass) {
                if(!element || element.nodeType != 1) return;
                if(element.className) {
                    element.className = element.className.replace(oldStateClass, newStateClass);
                }
                // For  elements that don't have a native state variable (like checked, or selectedIndex)
                if(wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOff)) {
                    element.className = element.className.replace(wFORMS.className_switchIsOff, wFORMS.className_switchIsOn);
                } else if(wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn)) {
                    element.className = element.className.replace(wFORMS.className_switchIsOn, wFORMS.className_switchIsOff);
                }
            },

            // ------------------------------------------------------------------------------------------
            getElementsBySwitchName: function(switchName, formId) {
                var elements = new Array();
                if(wFORMS.switchTargets[formId][switchName]) {
                    for (var i=0; i<wFORMS.switchTargets[formId][switchName].length; i++) {
                        var element = document.getElementById(wFORMS.switchTargets[formId][switchName][i]);
                        if(element)
                            elements.push(element);
                    }
                }
                return elements;
            },

            // ------------------------------------------------------------------------------------------
            isTriggerOn: function(element, triggerName) {
                if(!element) return false;
                if(element.tagName.toUpperCase()=='OPTION') {
                    var selectElement = element.parentNode;
                    while(selectElement && selectElement.tagName.toUpperCase() != 'SELECT') {
                        var selectElement = selectElement.parentNode;
                    }
                    if(!selectElement) return false; // invalid markup
                    if(selectElement.selectedIndex==-1) return false; // nothing selected
                    // TODO: handle multiple-select
                    if(wFORMS.helpers.hasClass(selectElement.options[selectElement.selectedIndex],
                                               wFORMS.classNamePrefix_switch + '-' + triggerName)) {
                        return true;
                    }
                } else {
                    if(element.checked || wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn))
                        return true;
                }
                return false;
            },

            // isWithinSwitchScope: An element with the REPEAT behavior limits the scope of switches
            // targets outside of the scope of the switch are not affected.
            // ------------------------------------------------------------------------------------------
            isWithinSwitchScope: function(trigger, target) {

                if(wFORMS.hasBehavior('repeat') && wFORMS.limitSwitchScope == true) {
                    // check if the trigger is in a repeatable/removeable element
                    var scope = trigger;

                    while(scope && scope.tagName && scope.tagName.toUpperCase() != 'FORM' &&
                          !wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) &&
                          !wFORMS.helpers.hasClass(scope, wFORMS.className_delete) ) {
                        scope = scope.parentNode;
                    }
                    if(wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) ||
                       wFORMS.helpers.hasClass(scope, wFORMS.className_delete)) {
                        // yes, the trigger is nested in a repeat/remove element

                        // check if the target is in the same element.
                        var scope2 = target;
                        while(scope2 && scope2.tagName && scope2.tagName.toUpperCase() != 'FORM' &&
                              !wFORMS.helpers.hasClass(scope2, wFORMS.className_repeat) &&
                              !wFORMS.helpers.hasClass(scope2, wFORMS.className_delete) ) {
                            scope2 = scope2.parentNode;
                        }
                        if(scope == scope2) {
                            return true;  // target & trigger are in the same repeat/remove element
                        } else {
                            return false; // target not in the same repeat/remove element,
                        }
                    } else {
                        return true;      // trigger is not nested in a repeat/remove element, scope unaffected
                    }
                } else
                    return true;
            }
       } // END wFORMS.behaviors['switch'] object


   }
// ------------------------------------------------------------------------------------------
// Form Validation Behavior
// ------------------------------------------------------------------------------------------

   if(wFORMS) {
        // Component properties
        // wFORMS.functionName_formValidation  is defined at the bottom of this file
        // Those should be moved inside wFORMS.behaviors['validation']. Stays here for now for backward compatibility
        wFORMS.preventSubmissionOnEnter         = false;             // prevents submission when pressing the 'enter' key. Set to true if pagination behavior is used.
        wFORMS.showAlertOnError                 = true;             // sets to false to not show the alert when a validation error occurs.
        wFORMS.className_required               = "required";
        wFORMS.className_validationError_msg    = "errMsg";
        wFORMS.className_validationError_fld    = "errFld";
        wFORMS.classNamePrefix_validation       = "validate";
        wFORMS.idSuffix_fieldError              = "-E";

        wFORMS.behaviors['validation'] = {

            // Error messages. This may be overwritten in a separate js file for localization or customization purposes.
            errMsg_required     : "This field is required. ",
            errMsg_alpha        : "The text must use alphabetic characters only (a-z, A-Z). Numbers are not allowed.",
            errMsg_email        : "This does not appear to be a valid email address.",
            errMsg_integer      : "Please enter an integer.",
            errMsg_float        : "Please enter a number (ex. 1.9).",
            errMsg_password     : "Unsafe password. Your password should be between 4 and 12 characters long and use a combinaison of upper-case and lower-case letters.",
            errMsg_alphanum     : "Please use alpha-numeric characters only [a-z 0-9].",
            errMsg_date         : "This does not appear to be a valid date.",
            errMsg_notification : "%% error(s) detected. Your form has not been submitted yet.\nPlease check the information you provided.",  // %% will be replaced by the actual number of errors.
            errMsg_custom       : "Please enter a valid value.",

            // Class Names
            className_allRequired : "allrequired",

            // first page w/ error in a multi-page form
            jumpToErrorOnPage : null,
            currentPageIndex  : -1,

            // do not submit fields turned off by switch behavior
            submitSwitchedOffFields : false,
            switchedOffFields : [],

           // ------------------------------------------------------------------------------------------
           // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
           // ------------------------------------------------------------------------------------------
            evaluate: function(node) {
                if(node.tagName=="FORM") {
                    // functionName_formValidation can be a reference to a function, or a string with the name of the function.
                    // avoid using typeof
                    if(wFORMS.functionName_formValidation.toString()==wFORMS.functionName_formValidation) {
                        // this is a string, not a function
                        wFORMS.functionName_formValidation = eval(wFORMS.functionName_formValidation);
                    }
                    wFORMS.helpers.addEvent(node,'submit',wFORMS.functionName_formValidation);
                }
                // Add onkeydown handler on text inputs to prevent form submission when the enter key is pressed
                if(wFORMS.preventSubmissionOnEnter) {
                    if(node.tagName=='INPUT') {
                        if(!node.type || node.type.toLowerCase()=='text' ||
                                         node.type.toLowerCase()=='password' ||
                                         node.type.toLowerCase()=='file') {
                            wFORMS.helpers.addEvent(node,'keydown', function(e){
                                var evt = (e) ? e : window.event;
                                if(evt.keyCode==13) {
                                    return wFORMS.helpers.preventEvent(evt);
                                }
                            });
                        }
                    }
                }

           },
           // ------------------------------------------------------------------------------------------
           // init: executed once evaluate has been applied to all elements
           // ------------------------------------------------------------------------------------------
           init: function() {
           },

           // ------------------------------------------------------------------------------------------
           // run: executed when the behavior is activated
           // ------------------------------------------------------------------------------------------
           run: function(e) {
                var element  = wFORMS.helpers.getSourceElement(e);
                if(!element) element = e;
                //wFORMS.debug('validation/run: ' + element.id , 5);

                var currentPageOnly = arguments.length>1 ? arguments[1]:false;
                // arguments[1] : (wFORMS.hasBehavior('paging') && wFORMS.behaviors['paging'].behaviorInUse);

                wFORMS.behaviors['validation'].switchedOffFields = [];
                wFORMS.behaviors['validation'].jumpToErrorOnPage = null;

                // make sure we have the form element
                while (element && element.tagName != 'FORM') {
                    element = element.parentNode;
                }

                var nbErrors = wFORMS.behaviors['validation'].validateElement(element, currentPageOnly, true);

                // save the value in a property if someone else needs it.
                wFORMS.behaviors['validation'].errorCount = nbErrors;

                if (nbErrors > 0) {
                    if(wFORMS.behaviors['validation'].jumpToErrorOnPage) {
                        wFORMS.behaviors['paging'].gotoPage(wFORMS.behaviors['validation'].jumpToErrorOnPage);
                    }
                    if(wFORMS.showAlertOnError){ wFORMS.behaviors['validation'].showAlert(nbErrors); }
                    return wFORMS.helpers.preventEvent(e);
                }

                // Remove switched-off content if any
                // Note: in multi-page behavior the validation is run on "page next" without submitting the form.
                //       In this situation (currentPageOnly==true) switched-off conditionals should not be removed.
                if(!wFORMS.behaviors['validation'].submitSwitchedOffFields && !currentPageOnly) {
                    for(var i=0; i < wFORMS.behaviors['validation'].switchedOffFields.length; i++) {
                        var element = wFORMS.behaviors['validation'].switchedOffFields[i];
                        while(element.childNodes[0])
                            element.removeChild(element.childNodes[0]);
                    }
                }
                return true;
            },

            // ------------------------------------------------------------------------------------------
            // remove: executed if the behavior should not be applied anymore
            // ------------------------------------------------------------------------------------------
            remove: function() {
            },


            // ------------------------------------------------------------------------------------------
            // validation functions
            // ------------------------------------------------------------------------------------------
            validateElement: function(element /*, currentPageOnly, deep */) {

                if(!element) return;

                var deep = arguments.length>2 ? arguments[2] : true;

                // used in multi-page forms
                var currentPageOnly = arguments[1] ? arguments[1] : false;

                var wBehavior = wFORMS.behaviors['validation'];     // shortcut

                // do not validate elements that are in a OFF-Switch
                // Note: what happens if an element is the target of 2+ switches, some ON and some OFF ?
                if(wFORMS.hasBehavior('switch') && wFORMS.helpers.hasClassPrefix(element,wFORMS.classNamePrefix_offState)) {
                    if(!wBehavior.submitSwitchedOffFields) {
                        wBehavior.switchedOffFields.push(element);
                    }
                    return 0;
                }
                // do not validate elements that are not in the current page (Paging Behavior)
                if(wFORMS.hasBehavior('paging') && wFORMS.helpers.hasClass(element,wFORMS.className_paging)) {
                    if(!wFORMS.helpers.hasClass(element,wFORMS.className_pagingCurrent) && currentPageOnly)
                        return 0;
                    wBehavior.currentPageIndex = wFORMS.behaviors['paging'].getPageIndex(element);
                }

                var nbErrors = 0;

                // check if required
                if(!wBehavior.checkRequired(element)) {
                    wBehavior.showError(element, wBehavior.errMsg_required);
                    nbErrors++;
                    //wFORMS.debug('validation/error: [required]' + element.id + '('+nbErrors+')' , 5);
                } else {

                    // input format validation
                    if (wFORMS.helpers.hasClassPrefix(element,wFORMS.classNamePrefix_validation)) {

                        var arrClasses = element.className.split(" ");
                        for (j=0;j<arrClasses.length;j++) {
                            switch(arrClasses[j]) {
                                case "validate-alpha":
                                    if(!wBehavior.isAlpha(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_alpha);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-alphanum":
                                    if(!wBehavior.isAlphaNum(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_alphanum);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-date":
                                    if(!wBehavior.isDate(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_date);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-time":
                                    /* NOT IMPLEMENTED */
                                    break;
                                case "validate-email":
                                    if(!wBehavior.isEmail(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_email);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-integer":
                                    if(!wBehavior.isInteger(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_integer);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-float":
                                    if(!wBehavior.isFloat(element.value)) {
                                        wBehavior.showError(element,wBehavior.errMsg_float);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-strongpassword": // NOT IMPLEMENTED
                                    if(!wBehavior.isPassword(element.value)) {
                                        wBehavior.showError(element, wBehavior.errMsg_password);
                                        nbErrors++;
                                    }
                                    break;
                                case "validate-custom":
                                    var pattern = new RegExp("\/([^\/]*)\/([gi]*)");
                                    var matches = element.className.match(pattern);
                                    if(matches[0]) {
                                        var validationPattern = new RegExp(matches[1],matches[2]);
                                        if(!element.value.match(validationPattern)) {
                                            wBehavior.showError(element, wBehavior.errMsg_custom);
                                            nbErrors++;
                                        }
                                    }
                                    break;
                                //CUSTOM
                                default:
                                    if (typeof(wFORMS.behaviors['validation'].custom_validation)=='function') {
                                        nbErrors += wFORMS.behaviors['validation'].custom_validation(element,arrClasses[j]);
                                    }
                            } // end switch
                        } // end for
                    }
                } // end validation check

                // remove previous error flags if any.
                if(nbErrors==0) {
                    wBehavior.removeErrorMessage(element);
                } else {
                    // flag the first page with an error (if multi-page form)
                    if(wBehavior.currentPageIndex>0 && !wBehavior.jumpToErrorOnPage) {
                        wBehavior.jumpToErrorOnPage = wBehavior.currentPageIndex;
                    }
                }

                // recursive loop
                if(deep) {
                    for(var i=0; i < element.childNodes.length; i++) {
                        if(element.childNodes[i].nodeType==1) { // Element Nodes only
                            nbErrors += wBehavior.validateElement(element.childNodes[i], currentPageOnly, deep);
                        }
                    }
                }

                return nbErrors;
            },

            // ------------------------------------------------------------------------------------------
            checkRequired: function(element) {
                var wBehavior = wFORMS.behaviors['validation'];     // shortcut

                if(wFORMS.helpers.hasClass(element,wFORMS.className_required)) {
                    switch(element.tagName.toUpperCase()) {
                        case "INPUT":
                            var inputType = element.getAttribute("type");
                            if(!inputType) inputType = 'text'; // handles lame markup
                            switch(inputType.toLowerCase()) {
                                case "checkbox":
                                    return element.checked;
                                    break;
                                case "radio":
                                    return element.checked;
                                    break;
                                default:
                                    return !wBehavior.isEmpty(element.value);
                            }
                            break;
                        case "SELECT":
                            if(element.selectedIndex==-1) {
                                // multiple select with no selection
                                return false;
                            } else
                                return !wBehavior.isEmpty(element.options[element.selectedIndex].value);
                            break;
                        case "TEXTAREA":
                            return !wBehavior.isEmpty(element.value);
                            break;
                        default:
                            return wBehavior.checkOneRequired(element);
                            break;
                    }
                } else if(wFORMS.helpers.hasClass(element,wBehavior.className_allRequired)) {
                    return wBehavior.checkAllRequired(element);
                }
                return true;
            },
            checkOneRequired: function(element) {
                if(element.nodeType != 1) return false;
                var tagName = element.tagName.toUpperCase();
                var wBehavior = wFORMS.behaviors['validation'];

                if(tagName == "INPUT" || tagName == "SELECT" || tagName == "TEXTAREA" ) {
                    var value = wBehavior.getFieldValue(element);
                    if(!wBehavior.isEmpty(value)) {
                        return true;
                    }
                }
                for(var i=0; i<element.childNodes.length;i++) {
                    if(wBehavior.checkOneRequired(element.childNodes[i])) return true;
                }
                return false;
            },
            checkAllRequired: function(element) {

                if(element.nodeType != 1) return true;
                var tagName = element.tagName.toUpperCase();
                var wBehavior = wFORMS.behaviors['validation'];

                if(tagName == "INPUT" || tagName == "SELECT" || tagName == "TEXTAREA" ) {
                    var value = wBehavior.getFieldValue(element);
                    if(wBehavior.isEmpty(value)) {
                        return false;
                    }
                }
                for(var i=0; i<element.childNodes.length;i++) {
                    if(!wBehavior.checkAllRequired(element.childNodes[i])) return false;
                }
                return true;
            },
            getFieldValue: function(element) {
                var value = null;
                if(element && element.tagName) {
                    if(element.tagName.toUpperCase() == "INPUT") {
                        var inputType = element.getAttribute("type");
                        if(!inputType) inputType = 'text'; // handles lame markup

                        switch(inputType.toLowerCase()) {
                            case "checkbox":
                                value = element.checked?element.value:null;
                                break;
                            case "radio":
                                var radioGroup = element.form[element.name];
                                for (var i = 0; i< radioGroup.length; i++) {
                                    if (radioGroup[i].checked) {
                                        if(!value) value = new Array();
                                        value[value.length] = radioGroup[i].value;
                                    }
                                }
                                break;
                            default:
                                value = element.value;
                        }
                    } else if(element.tagName.toUpperCase() == "SELECT") {
                        if(element.selectedIndex!=-1)
                            value = element.options[element.selectedIndex].value
                        else
                            value = null; // multiple select with no selection
                    } else if(element.tagName.toUpperCase() == "TEXTAREA") {
                        value = element.value;
                    }
                }
                return value;
            },
            // ------------------------------------------------------------------------------------------
            isEmpty: function(s) {
                var regexpWhitespace = /^\s+$/;
                return  ((s == null) || (s.length == 0) || regexpWhitespace.test(s));
            },
            isAlpha: function(s) {
                var regexpAlphabetic = /^[a-zA-Z\s]+$/; // Add ' and - ?
                return wFORMS.behaviors['validation'].isEmpty(s) || regexpAlphabetic.test(s);
            },
            isAlphaNum: function(s) {
                var validChars = /^[\w\s]+$/;
                return wFORMS.behaviors['validation'].isEmpty(s) || validChars.test(s);
            },
            isDate: function(s) {
                var testDate = new Date(s);
                return wFORMS.behaviors['validation'].isEmpty(s) || !isNaN(testDate);
            },
            isEmail: function(s) {
                var regexpEmail = /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/;
                return wFORMS.behaviors['validation'].isEmpty(s) || regexpEmail.test(s);
            },
            isInteger: function(s) {
                var regexp = /^[+]?\d+$/;
                return wFORMS.behaviors['validation'].isEmpty(s) || regexp.test(s);
            },
            isFloat: function(s) {
                return wFORMS.behaviors['validation'].isEmpty(s) || !isNaN(parseFloat(s));
            },
            // NOT IMPLEMENTED
            isPassword: function(s) {
                // Matches strong password : at least 1 upper case letter, one lower case letter. 4 characters minimum. 12 max.
                //var regexp = /^(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{4,12}$/;  // <= breaks in IE5/Mac
                return wFORMS.behaviors['validation'].isEmpty(s);
            },

            // ------------------------------------------------------------------------------------------
            // Error Alert Functions
            // ------------------------------------------------------------------------------------------
            showError: function (element,errorMsg) {
                // remove existing error message if any.
                wFORMS.behaviors['validation'].removeErrorMessage(element);

                if (!element.id) element.id = wFORMS.helpers.randomId(); // we'll need an id here.
                // Add error flag to the field
                element.className += " " + wFORMS.className_validationError_fld;
                // Prepare error message
                var msgNode = document.createTextNode(" " + errorMsg);
                // Find error message placeholder.
                var fe = document.getElementById(element.id +  wFORMS.idSuffix_fieldError);
                if(!fe) { // create placeholder.
                    fe = document.createElement("div");
                    fe.setAttribute('id', element.id +  wFORMS.idSuffix_fieldError);
                    // attach the error message after the field label if possible
                    var fl = document.getElementById(element.id +  wFORMS.idSuffix_fieldLabel);
                    if(fl)
                        fl.parentNode.insertBefore(fe,fl.nextSibling);
                    else
                        // otherwise, attach it after the field tag.
                        element.parentNode.insertBefore(fe,element.nextSibling);
                }
                // Finish the error message.
                fe.appendChild(msgNode);
                fe.className += " " + wFORMS.className_validationError_msg;
            },

            showAlert: function (nbTotalErrors) {
               alert(wFORMS.behaviors['validation'].errMsg_notification.replace('%%',nbTotalErrors));
            },

            removeErrorMessage: function(element) {
                if(wFORMS.helpers.hasClass(element,wFORMS.className_validationError_fld)) {
                    var rErrClass     = new RegExp(wFORMS.className_validationError_fld,"gi");
                    element.className = element.className.replace(rErrClass,"");
                    var errorMessage  = document.getElementById(element.id + wFORMS.idSuffix_fieldError);
                    if(errorMessage)  {
                        errorMessage.innerHTML="";
                    }
                }
            }

       } // End wFORMS.behaviors['validation']

        wFORMS.functionName_formValidation = wFORMS.behaviors['validation'].run;


        // ----------------------------------------------------------------------
        // wForms 1.0 backward compatibility
        // ----------------------------------------------------------------------
        wFORMS.formValidation = wFORMS.behaviors['validation'].run;

        // Error messages.
        wFORMS.arrErrorMsg = new Array();
        wFORMS.arrErrorMsg[0] = wFORMS.behaviors['validation'].errMsg_required;
        wFORMS.arrErrorMsg[1] = wFORMS.behaviors['validation'].errMsg_alpha;
        wFORMS.arrErrorMsg[2] = wFORMS.behaviors['validation'].errMsg_email;
        wFORMS.arrErrorMsg[3] = wFORMS.behaviors['validation'].errMsg_integer;
        wFORMS.arrErrorMsg[4] = wFORMS.behaviors['validation'].errMsg_float;
        wFORMS.arrErrorMsg[5] = wFORMS.behaviors['validation'].errMsg_password;
        wFORMS.arrErrorMsg[6] = wFORMS.behaviors['validation'].errMsg_alphanum;
        wFORMS.arrErrorMsg[7] = wFORMS.behaviors['validation'].errMsg_date;
        wFORMS.arrErrorMsg[8] = wFORMS.behaviors['validation'].errMsg_notification;

   }

//CUSTOM
wFORMS.behaviors['validation'].custom_validation = function(element,classname) {
      var wBehavior = wFORMS.behaviors['validation'];         // shortcut

      var nbErrors = 0;

      if(wBehavior.isEmpty(element.value)) return 0;         // if it's not require already, don't check blank fields

      if (classname.indexOf('|') != -1) {                  // looking for 'validate-something|this|that' in classname
         var options = classname.split('|');
         classname = options[0];
         for (z=1; z<options.length; z++){               // check for field names in options and get values
            obj = document.getElementsByName(options[z]);
            value = false;
            for (i=0; i<obj.length; i++) {
               v = wBehavior.getFieldValue(obj[i]);
               if (v) value = (typeof(v) == 'string') ? v : v[0];
            }
            if (value) options[z] = value;
         }
      }

      switch(classname) {
         case "validate-minlen":
         case "validate-minlength":
            if (!options[1]) return;
            if (element.value.length < eval(options[1])) {
               wBehavior.showError(element, "Tamaño mínimo de "+options[1]+" caracteres.");
               nbErrors++;
            }
            break;
         case "validate-maxlen":
         case "validate-maxlength":
            if (!options[1]) return;
            if (element.value.length > eval(options[1])) {
               wBehavior.showError(element, "Tamaño máximo de "+options[1]+" caracteres.");
               nbErrors++;
            }
            break;
         case "validate-within":
            if (!options[1]) return;
            if (!options[2]) return;
            if (!(element.value >= options[1] && element.value <= options[2])) {
               wBehavior.showError(element, "Must be within "+options[1]+" and "+options[2]+".");
               nbErrors++;
            }
            break;
         case "validate-lt":
         case "validate-less":
            if (!options[1]) return;
            if (!element.value < options[1]) {
               wBehavior.showError(element, "El valor debe ser inferior a "+options[1]+".");
               nbErrors++;
            }
            break;
         case "validate-gt":
         case "validate-greater":
            if (!options[1]) return;
            if (!element.value > options[1]) {
               wBehavior.showError(element, "El valor debe ser superior a "+options[1]+".");
               nbErrors++;
            }
            break;
         case "validate-eq":
         case "validate-equal":
            if (!options[1]) return;
            if (element.value != options[1]) {
               wBehavior.showError(element, "Los valores no coinciden.");
               nbErrors++;
            }
            break;
         case "validate-ne":
         case "validate-neq":
         case "validate-notequal":
            if (!options[1]) return;
            if (element.value == options[1]) {
               wBehavior.showError(element, "Los valores no pueden ser los mismos.");
               nbErrors++;
            }
            break;
         case "validate-number":
         case "validate-numeric":
            if(!wBehavior.isInteger(element.value)) {
               wBehavior.showError(element, wBehavior.errMsg_integer);
               nbErrors++;
            }
            break;
         case "validate-mod10":
            if (!isMod10()) {
               wBehavior.showError(element, "Este valor no es un numero valido.");
               nbErrors++;
            }
            break;
         case "validate-creditcard":
            if (!isMod10()) {
               wBehavior.showError(element, "El valor no es de una targeta valida.");
               nbErrors++;
               return;
            }
            switch (options[1]) {
               case 'V'      : var ccReg = /^4\d{12}(\d{3})?$/; break;         // Visa
               case 'M'      : var ccReg = /^5[1-5]\d{14}$/; break;            // Mastercard
               case 'D'      :                                        // Discover
               case 'S'      : var ccReg = /^6011\d{12}$/; break;            // Discover (why S?)
               case 'A'      : var ccReg = /^3[4|7]\d{13}$/; break;            // American Express
            //   case 'DINERS'   : var ccReg = /^3[0|6|8]\d{12}$/; break;
            //   case 'ENROUTE'   : var ccReg = /^2[014|149]\d{11}$/; break;
            //   case 'JCB'      : var ccReg = /^3[088|096|112|158|337|528]\d{12}$/; break;
            //   case 'SWITCH'   : var ccReg = /^(49030[2-9]|49033[5-9]|49110[1-2]|4911(7[4-9]|8[1-2])|4936[0-9]{2}|564182|6333[0-4][0-9]|6759[0-9]{2})\d{10}(\d{2,3})?$/; break;
            //   case 'DELTA'   : var ccReg = /^4(1373[3-7]|462[0-9]{2}|5397[8|9]|54313|5443[2-5]|54742|567(2[5-9]|3[0-9]|4[0-5])|658[3-7][0-9]|659(0[1-9]|[1-4][0-9]|50)|844[09|10]|909[6-7][0-9]|9218[1|2]|98824)\d{10}$/; break;
            //   case 'SOLO'      : var ccReg = /^(6334[5-9][0-9]|6767[0-9]{2})\d{10}(\d{2,3})?$/; break;
            // Add additonal card types here
               default         : var ccReg = /^0\d{22}$/; // field definition should make this impossible
            }
            if (!ccReg.test(element.value)) {
               wBehavior.showError(element, "Targeta de credito (tipo/numero) invalido.");
               nbErrors++;
            }
            break;
         case "validate-zipcode":
            if (options[1]) {
               if ( (options[1]=='USA' && isUSAzip(element.value)) ||
                    (options[1]=='CAN' && isCANzip(element.value)) ) break;
            }
            if (isUSAzip(element.value) || isCANzip(element.value)) break;
            wBehavior.showError(element, "This is not a valid zip/postal code.");
            nbErrors++;
            break;
         case "validate-zipusa":
            if (!isUSAzip(element.value)) {
               wBehavior.showError(element, "This is not a valid zip code.");
               nbErrors++;
            }
            break;
         case "validate-zipcan":
            if (!isCANzip(element.value)) {
               wBehavior.showError(element, "This is not a valid postal code.");
               nbErrors++;
            }
            break;
         case "validate-phonenumber":
         case "validate-phone":
            regex   = new RegExp(/^\(?(\d{3})\)?[\s-\.]?(\d{3})[\s-\.]?(\d{4})$/);
            if (!regex.test(element.value)) {
               wBehavior.showError(element, "This is not a valid phone number.");
               nbErrors++;
            } else {
               matched = regex.exec(element.value);
               element.value = '(' + matched[1] + ") " + matched[2] + "-" + matched[3];
            }
            break;
         case "validate-name":
            regex   = new RegExp(/^[a-zA-Z\s.\-]+$/);
            if (!regex.test(element.value)) {
               wBehavior.showError(element, "This name appears to be invalid.");
               nbErrors++;
            }
            break;
         case "validate-street":
         case "validate-address":
         case "validate-city":
            regex   = new RegExp(/^[a-zA-Z0-9\s.#\(\)\-]+$/);
            if (!regex.test(element.value)) {
               wBehavior.showError(element, "This "+classname.substr(9)+" appears to be invalid.");
               nbErrors++;
            }
            break;
         case "validate-nopobox":
            regex   = new RegExp(/P\.* *O\.* *B*O*X*/i);
            if (regex.test(element.value)) {
               wBehavior.showError(element, "P.O. box not allowed.");
               nbErrors++;
            }
            break;

      } //switch
      return nbErrors;

      // validation functions

      isUSAzip = function(s) {
         var regex = new RegExp(/^(\d{5})[\-\s]?(\d{4})?$/);
         if (!regex.test(s)) return false;
         matched = regex.exec(s);
         matched.shift();
         s = matched.join(' ');            // always adds space
         s = s.replace(/^\s+|\s+$/g, '') ;   // trim trailing spaces
         s = s.replace(' ','-');            // replace inner space with '-'
         element.value = s;
         return true;
      }
      isCANzip = function(s) {
         s = s.toUpperCase();
         s = s.replace('-',' ');
         var regex = new RegExp(/^([A-Z]\d[A-Z])[\-\s]?(\d[A-Z]\d)$/);
         if (!regex.test(s)) return false;
         matched = regex.exec(s);
         element.value = matched[1] + " " + matched[2];
         return true;
      }
      isMod10 = function() {
         element.value = element.value.split("-").join("");
         // Add even digits in even length strings or odd digits in odd length strings.
         var checksum = 0;
         for (var i=(2-(element.value.length % 2)); i<=element.value.length; i+=2) {
               checksum += parseInt(element.value.charAt(i-1));
         }
         // Analyze odd digits in even length strings or even digits in odd length strings.
         for (var i=(element.value.length % 2) + 1; i<element.value.length; i+=2) {
            var digit = parseInt(element.value.charAt(i-1)) * 2;
            if (digit < 10) { checksum += digit; } else { checksum += (digit-9); }
         }
         return ((checksum % 10) == 0);
      }

   }

