(function() {
  
  //patch inputEx.Field.setValue()
  inputEx.Field.prototype.setValue = function(value, sendUpdatedEvt) {
    if(sendUpdatedEvt !== false) {
      this.setClassFromState();
      this.fireUpdatedEvt();
    }
  };
  
  // patch inputEx.Group.disable()
  inputEx.Group.prototype.disable = function() {
    for (var i = 0 ; i < this.inputs.length ; i++) {
      this.inputs[i].disable();
    }
    
    this.disabled = true;
  };
  
  //patch inputEx.Group.validate()
  inputEx.Group.prototype.validate = function() {
    var response = true;
    
    // Validate all the sub fields
    for (var i = 0 ; i < this.inputs.length ; i++) {
      var input = this.inputs[i];

      // validate only not disabled fields
      if (input.disabled) continue;
      
      input.setClassFromState(); // update field classes (mark invalid fields...)
      var state = input.getState();
      if( state == inputEx.stateRequired || state == inputEx.stateInvalid ) {
        response = false; // but keep looping on fields to set classes
      }
    }
    
    return response;
  };
  
})();
/**
 * A DatePicker Field with extended options.
 * @class inputEx.DatePickerFieldExt
 * @extends inputEx.DatePickerField
 * @constructor
 * @param {Object} options See DatePickerField
 * <ul>
 *   <li>minDate: yui calendar configuration object</li>
 * </ul>
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom, DateMath = YAHOO.widget.DateMath;
  
  inputEx.strtr = function(str, from, to) {
    if (! lang.isString(str)) {
      return;
    }
    
    var fr = '', i = 0, lgth = 0;

    if (typeof from === 'object') {
        for (fr in from) {
            str = str.replace(fr, from[fr]);
        }
        return str;
    }
    
    lgth = to.length;
    if (from.length < to.length) {
        lgth = from.length;
    }
    for (i = 0; i < lgth; i++) {
        str = str.replace(from[i], to[i], 'g');
    }
    
    return str;
  };

  inputEx.ExtDatePickerField = function(options) {
    inputEx.ExtDatePickerField.superclass.constructor.call(this, options);
  };
  
  inputEx.ExtDatePickerField.stateInvalidMin = 'min';
  inputEx.ExtDatePickerField.stateInvalidMax = 'max';
  inputEx.ExtDatePickerField.stateInvalidStartFrom = 'startfrom';
  
  inputEx.ExtDatePickerField.byId = {};
  
  lang.extend(inputEx.ExtDatePickerField, inputEx.DatePickerField, {
    
    setOptions: function(options) {
      inputEx.ExtDatePickerField.superclass.setOptions.call(this, options);

      if (lang.isString(options.minDate)) {
        this.options.minDate = inputEx.DateField.parseWithFormat(options.minDate, this.options.dateFormat);
      } else  {
        this.options.minDate = lang.isObject(options.minDate) ? options.minDate : null;
      }
      
      if (lang.isString(options.maxDate)) {
        this.options.maxDate = inputEx.DateField.parseWithFormat(options.maxDate, this.options.dateFormat);
      } else  {
        this.options.maxDate = lang.isObject(options.maxDate) ? options.maxDate : null;
      }

      if (this.options.minDate != null) {
        this.options.calendar.mindate = this.options.minDate;
      }
      
      if (this.options.maxDate != null) {
        this.options.calendar.maxdate = this.options.maxDate;
      }
      
      // FIXME
      this.options.messages.minDate = "min date is %min_date%";
      this.options.messages.maxDate = "max date is %max_date%";
      this.options.messages.startDate = "must start from %start_from%";
      
      if (lang.isObject(options.messages)) {
        if (lang.isString(options.messages.minDate)) this.options.messages.minDate = options.messages.minDate;
        if (lang.isString(options.messages.maxDate)) this.options.messages.maxDate = options.messages.maxDate;
        if (lang.isString(options.messages.startDate)) this.options.messages.startDate = options.messages.startDate;
      }
      
      this.options.startFromField = false;
      
      inputEx.ExtDatePickerField.byId[options.id] = this;
      
      var startFromField;
      if (options.startfrom && (startFromField = inputEx.ExtDatePickerField.byId[options.startfrom])) {
        this.setStartFromField(startFromField);
      }

    },
        
    setStartFromField: function(startFromField) {
      this.options.startFromField = startFromField;
      this.options.startFromField.options.startFromRelField = this;
    },
    
    onInput: function(e) {
      inputEx.DatePickerExtField.superclass.onInput.call(this, e);
      if(this.options.startFromRelField) {
         this.options.startFromRelField.setClassFromState();
      }
    },
    
    getValue: function(forceDate) {
      var value = inputEx.ExtDatePickerField.superclass.getValue.call(this, forceDate);

      if (value === '') { return '';}
   
      if (! this.validate()) {
        value = forceDate ? null : '';
      }
      
      return value;
    },
 
    setValue: function(value, sendUpdatedEvt) {
      inputEx.ExtDatePickerField.superclass.setValue.call(this, value, sendUpdatedEvt);
      if(this.options.startFromRelField) {
         this.options.startFromRelField.setClassFromState();
      }
    },
    
    enable: function() {
      inputEx.ExtDatePickerField.superclass.enable.call(this);
      this.setClassFromState();
    },
    
    disable: function() {
      inputEx.ExtDatePickerField.superclass.disable.call(this);
      this.setClassFromState();
    },
    
    validateMinMax: function() {
      var result = 0;
      var value = this.getValue(true);
      
      if (value === '') { return true; }
      
      DateMath.clearTime(value);
      var valueTime = value.getTime();
      
      if (this.options.minDate != null && valueTime < this.options.minDate.getTime()) {
        result = -1;
      } else if (this.options.maxDate != null && valueTime > this.options.maxDate.getTime()) {
        result = 1;
      } else if (this.options.startFromField) {
        var startFromDate = this.options.startFromField.getValue(true);
        if (startFromDate !== '' && startFromDate.getTime() > valueTime) {
          result = 2;
        }
      }

      return result;
    },
    
    getState: function() { 
      if (this.isEmpty()) {
        return this.options.required ? inputEx.stateRequired : inputEx.stateEmpty;
      }
      
      var vMinMax = this.validateMinMax();

      if (vMinMax == 1) {
        return inputEx.ExtDatePickerField.stateInvalidMax;
      } else if (vMinMax == -1) {
        return inputEx.ExtDatePickerField.stateInvalidMin;
      } else if (vMinMax == 2) {
        return inputEx.ExtDatePickerField.stateInvalidStartFrom;
      }

      return this.validate() ? inputEx.stateValid : inputEx.stateInvalid;
    },
    
    getStateString: function(state) {
      var result = inputEx.ExtDatePickerField.superclass.getStateString.call(this, state);

      if (result != '') {
        return result;
      }
      
      if (this.el.disabled) {
        return '';
      }
      
      if (state == inputEx.ExtDatePickerField.stateInvalidMin) {
        var dateString = inputEx.DateField.formatDate(this.options.minDate, this.options.dateFormat);
        return inputEx.strtr(this.options.messages.minDate, { '%min_date%': dateString });
      } else if (state == inputEx.ExtDatePickerField.stateInvalidMax) {
        var dateString = inputEx.DateField.formatDate(this.options.maxDate, this.options.dateFormat);
        return inputEx.strtr(this.options.messages.maxDate, { '%max_date%': dateString });
      } else if (state == inputEx.ExtDatePickerField.stateInvalidStartFrom) {
        var dateString = inputEx.DateField.formatDate(this.options.startFromField.getValue(true), this.options.dateFormat);
        return inputEx.strtr(this.options.messages.startDate, { '%start_from%': dateString });
      }

      return '';
    }

  });
  
  // Register this class as "datepicker" type
  inputEx.registerType("datepickerext", inputEx.ExtDatePickerField);
  
})();

/**
 * A Integer Field with Increment/Decrement buttons.
 * @class inputEx.IntegerPickerField
 * @extends inputEx.IntegerField
 * @constructor
 * @param {Object} options See IntegerField
 * <ul>
 *   <li>min: min value</li>
 *   <li>max: max value</li>
 * </ul>
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.IntegerPickerField = function(options) {
    inputEx.IntegerPickerField.superclass.constructor.call(this, options);
  };
  
  lang.extend(inputEx.IntegerPickerField, inputEx.IntegerField, {
    
    setOptions: function(options) {
      inputEx.IntegerPickerField.superclass.setOptions.call(this, options);
      
      this.options.leftLabel = options.leftLabel || null;
      this.options.rightLabel = options.rightLabel || null;
    },
  
    renderComponent: function() {
      // This element wraps the input node in a float: none div
      this.wrapEl = inputEx.cn('div', {className: 'inputEx-StringField-wrapper'});

      // Attributes of the input field
      var attributes = {};
      attributes.type = 'text';
      attributes.id = this.divEl.id ? this.divEl.id + '-field' : YAHOO.util.Dom.generateId();
      if(this.options.size) { attributes.size = this.options.size; }
      if(this.options.name) { attributes.name = this.options.name; }
      if(this.options.readonly) { attributes.readonly = 'readonly'; }

      if(this.options.maxLength) { attributes.maxLength = this.options.maxLength; }
      if(!this.options.autocomplete) { attributes.autocomplete = 'off'; }

      // Create the node
      this.el = inputEx.cn('input', attributes);

      // Create the increment and decrement link-buttons
      var leftBtId = this.divEl.id ? this.divEl.id + '-dec' : YAHOO.util.Dom.generateId();
      this.leftBt = inputEx.cn('a', { className: "inputEx-Button-Dec", id: leftBtId, href: "javascript:void(0)" });
      var rightBtId = this.divEl.id ? this.divEl.id + '-inc' : YAHOO.util.Dom.generateId();
      this.rightBt = inputEx.cn('a', { className: "inputEx-Button-Inc", id: rightBtId, href: "javascript:void(0)" });
      
      // Create and append inner SPANs to the buttons
      var leftSpan = inputEx.cn('span', { className: "dec" } , null, " ");
      this.leftBt.appendChild(leftSpan);
      var rightSpan = inputEx.cn('span', { className: "inc" }, null, " ");
      this.rightBt.appendChild(rightSpan);
      
      // Create and append left label
      if (this.options.leftLabel != null) {
        var leftLabel = null;
        
        if (lang.isString(this.options.leftLabel)) {
          var leftLabel = inputEx.cn('span', { className: "leftLabel" }, null, this.options.leftLabel);
        } else if (lang.isObject(this.options.leftLabel)){
          var leftLabel = this.options.leftLabel;
        }
        
        if (leftLabel != null) {
          this.fieldContainer.appendChild(leftLabel);
        }
      }
      
      // Append all stuff to the main element
      this.wrapEl.appendChild(this.leftBt);
      this.wrapEl.appendChild(this.el);
      this.wrapEl.appendChild(this.rightBt);
      this.fieldContainer.appendChild(this.wrapEl);
      
      // Create and append right label
      if (this.options.rightLabel != null) {
        var rightLabel = null;
        
        if (lang.isString(this.options.rightLabel)) {
          var rightLabel = inputEx.cn('span', { className: "rightLabel" }, null, this.options.rightLabel);
        } else if (lang.isObject(this.options.rightLabel)){
          var rightLabel = this.options.rightLabel;
        }
        
        if (rightLabel != null) {
          this.fieldContainer.appendChild(rightLabel);
        }
      }
   },
   
   initEvents: function() {
     inputEx.IntegerPickerField.superclass.initEvents.call(this);
     
     // register click event for increment and decrement buttons 
     Event.addListener(this.leftBt, "click", this.incValue, -1, this);
     Event.addListener(this.rightBt, "click", this.incValue, 1, this);
   },
   
   incValue: function(e, incValue) {
     Event.preventDefault(e);
     
     var value = this.getValue();

     if (! lang.isNumber(value) || (! this.options.negative && value < 0)) {
       value = 0;
     }

     var newValue = value + incValue;
     if ((! this.options.negative && newValue < 0) || (newValue < this.options.min) || ((newValue > this.options.max))) {
       return;
     }
     
     this.setValue(newValue, true);
   }
  
  });
  
  inputEx.registerType("integerpicker", inputEx.IntegerPickerField);
})();
/**
 * A Group of fields with extended legend wich can be any other InputEx field/group.
 * @class inputEx.ExtGroup
 * @extends inputEx.Group
 * @constructor
 * @param {Object} options See Group
 * <ul>
 *   <li>legendFields: Legend fields object</li>
 * </ul>
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.ExtGroup = function(options) {
    inputEx.ExtGroup.superclass.constructor.call(this, options);
  };
  
  lang.extend(inputEx.ExtGroup, inputEx.Group, {
    setOptions: function(options) {
      inputEx.ExtGroup.superclass.setOptions.call(this, options);
      
      this.options.nameFormat = options.nameFormat || '%s';
      this.options.legendFields = lang.isArray(options.legendFields) ?  options.legendFields : null;
      
      this.collapsed = false;
    },
    
    renderField: function(fieldOptions) {
      if (fieldOptions.name) {
        fieldOptions.name = this.formatFieldName(fieldOptions.name);
      }
      
      return inputEx.ExtGroup.superclass.renderField.call(this, fieldOptions);
    },
    
    renderFields: function(parentEl) {
      inputEx.ExtGroup.superclass.renderFields.call(this, parentEl);

      if (this.options.legendFields != null) {
        // Iterate on legend fields
        for (var i = 0; i < this.options.legendFields.length; i++) {
          var legEl = this.options.legendFields[i];
        
          // Throw Error if input is undefined
          if(! legEl) {
            throw new Error("inputEx.Form: One of the provided legend fields is undefined ! (check trailing comma)");
          }
  
          // Render the field
          var field = this.renderField(legEl);
          this.legend.appendChild(field.getEl());
        }
      }
      
      this.legend.appendChild(inputEx.cn('div', { className: "inputEx-clear-div" }));
    },
    
    formatFieldName: function(name) {
      return this.options.nameFormat.replace('%s', name, 'g');
    },
    
    getFieldByName: function(fieldName) {
      fieldName = this.formatFieldName(fieldName);
      return inputEx.ExtGroup.superclass.getFieldByName.call(this, fieldName);
    },
    
    toggleCollapse: function() {
      if(Dom.hasClass(this.fieldset, 'inputEx-Expanded')) {
         Dom.replaceClass(this.fieldset, 'inputEx-Expanded', 'inputEx-Collapsed');
         this.collapsed = true;
      }
      else {
         Dom.replaceClass(this.fieldset, 'inputEx-Collapsed','inputEx-Expanded');
         this.collapsed = false;
      }
    },
    
    setValue: function(values, sendUpdatedEvt) {
      if (! lang.isObject(values)) {
        return;
      }

      for (k in values) {
        n = this.formatFieldName(k);

        if (this.inputsNames[n] && ! lang.isUndefined(values[k])) {
          this.inputsNames[n].setValue(values[k], sendUpdatedEvt);
        }
      }
      
      this.runFieldsInteractions();
      
      if (sendUpdatedEvt !== false) {
        this.fireUpdatedEvt();
      }
    },
    
    setErrors: function(errors) { 
      var i,k;
      if (YAHOO.lang.isArray(errors)) {
        for (i = 0 ; i < errors.length ; i++) {
          k = errors[i][0];
          n = this.formatFieldName(k);
          value = errors[i][1];
          if (this.inputsNames[n]) {
            if (this.inputsNames[n].options.showMsg) {
              this.inputsNames[n].displayMessage(value);
              Dom.replaceClass(this.inputsNames[n].divEl, "inputEx-valid", "inputEx-invalid" );
            }
          }
        }
      }
      else if (lang.isObject(errors)) {
        for (k in errors) {
          if (errors.hasOwnProperty(k)) {
            n = this.formatFieldName(k);
            if (this.inputsNames.hasOwnProperty(n)) {
              if (this.inputsNames[n].options.showMsg) {
                this.inputsNames[n].displayMessage(errors[k]);
                Dom.replaceClass(this.inputsNames[n].divEl, "inputEx-valid", "inputEx-invalid" );
              }
            }
          }
        }
      }
    }
  });
  
  inputEx.registerType("extgroup", inputEx.ExtGroup);
})();
/**
 * An extended inputEx.Form
 * @class inputEx.ExtForm
 * @extends inputEx.ExtGroup
 * @constructor
 * @param {Object} options
 */
(function() {
  var util = YAHOO.util, lang = YAHOO.lang, Event = util.Event, Dom = util.Dom;
  
  inputEx.ExtForm = function(options) {
    inputEx.ExtForm.superclass.constructor.call(this, options);
  };
  
  lang.extend(inputEx.ExtForm, inputEx.ExtGroup, {
    /**
     * Adds buttons and set ajax default parameters
     * @param {Object} options Options object as passed to the constructor
     */
    setOptions: function(options) {
       inputEx.ExtForm.superclass.setOptions.call(this, options);

       this.buttons = [];

       this.options.buttons = options.buttons || [];

       this.options.action = options.action;
     this.options.method = options.method;

     this.options.className =  options.className || 'inputEx-Group';
     this.options.autocomplete = (options.autocomplete === false || options.autocomplete === "off") ? false : true;
     
     this.options.enctype = options.enctype;

       if(options.ajax) {
          this.options.ajax = {};
          this.options.ajax.method = options.ajax.method || 'POST';
          this.options.ajax.uri = options.ajax.uri || 'default.php';
          this.options.ajax.callback = options.ajax.callback || {};
          this.options.ajax.callback.scope = options.ajax.callback.scope || this;
          this.options.ajax.showMask = lang.isUndefined(options.ajax.showMask) ? false : options.ajax.showMask;

       this.options.ajax.contentType = options.ajax.contentType || "application/json";
       this.options.ajax.wrapObject = options.ajax.wrapObject;
       }
       
       if (lang.isFunction(options.onSubmit)) {
          this.options.onSubmit = options.onSubmit;
       }
    },

    /**
     * Render the group
     */
    render: function() {
       // Create the div wrapper for this group
        this.divEl = inputEx.cn('div', {className: this.options.className});
      if(this.options.id) {
        this.divEl.id = this.options.id;
     }
            
        // Create the FORM element
       this.form = inputEx.cn('form', {method: this.options.method || 'POST', action: this.options.action || '', className: this.options.className || 'inputEx-Form'});
       this.divEl.appendChild(this.form);

     // set the enctype
     if(this.options.enctype) {
       this.form.setAttribute('enctype',this.options.enctype);
     }

      // Set the autocomplete attribute to off to disable firefox autocompletion
     if(!this.options.autocomplete) {
       this.form.setAttribute('autocomplete','off');
     }
     
       // Set the name of the form
       if(this.options.formName) { this.form.name = this.options.formName; }
        
        this.renderFields(this.form);

       this.renderButtons();
       
       if(this.options.disabled) {
           this.disable();
        }    
    },


    /**
     * Render the buttons
     */
    renderButtons: function() {
        
       var buttonConf, button, i, buttonsNb = this.options.buttons.length;
       
       this.buttonDiv = inputEx.cn('div', {className: 'inputEx-Form-buttonBar'});

       for(i = 0 ; i < buttonsNb ; i++ ) {
          buttonConf = this.options.buttons[i];
    
          // Throw Error if button is undefined
          if(!buttonConf) {
             throw new Error("inputEx.Form: One of the provided button is undefined ! (check trailing comma)");
          }
          
          button = new inputEx.widget.Button(buttonConf);
          button.render(this.buttonDiv);
          
          this.buttons.push(button);
          
       }
       
       // useful for link buttons re-styling (float required on <a>'s ... )
       this.buttonDiv.appendChild(inputEx.cn('div',null,{clear:'both'}));
       
       this.form.appendChild(this.buttonDiv);
    },


    /**
     * Init the events
     */
    initEvents: function() {
       
       var i, length;
       
       inputEx.ExtForm.superclass.initEvents.call(this);
       
       
       // Custom event to normalize form submits
       this.submitEvent = new util.CustomEvent("submit");
       
       
       // Two ways to trigger the form submitEvent firing
       //
       //
       // 1. catch a 'submit' event on form (say a user pressed <Enter> in a field)
       //
          Event.addListener(this.form, 'submit', function(e) {
          
             // always stop event
             Event.stopEvent(e);
          
             // replace with custom event
             this.submitEvent.fire();
          
          },this,true);
       
       
       //
       // 2. click on a 'submit' or 'submit-link' button
       //
          for(i=0, length=this.buttons.length; i<length; i++) {
          
             this.buttons[i].submitEvent.subscribe(function() { this.submitEvent.fire(); }, this, true);
          
          }
       
       
       // When form submitEvent is fired, call onSubmit
       this.submitEvent.subscribe(this.options.onSubmit || this.onSubmit, this, true);
    },

    /**
     * Intercept the 'onsubmit' event and stop it if !validate
     * If the ajax option object is set, use YUI async Request to send the form
     * @param {Event} e The original onSubmit event
     */
    onSubmit: function(e) {
      
       // do nothing if does not validate
      if ( !this.validate() ) {
        return; // no submit
      }
      
      if(this.options.ajax) {
         this.asyncRequest(); // send ajax request
         return;
      }
      
      // normal submit finally
      // (won't fire a dom "submit" event, so no risk to loop)
      this.form.submit();
    },

    /**
     * Send the form value in JSON through an ajax request
     */
    asyncRequest: function() {

       if(this.options.ajax.showMask) { this.showMask(); }
   
     var formValue = this.getValue();
   
     // options.ajax.uri and options.ajax.method can also be functions that return a the uri/method depending of the value of the form
     var uri = lang.isFunction(this.options.ajax.uri) ? this.options.ajax.uri(formValue) : this.options.ajax.uri;
     var method = lang.isFunction(this.options.ajax.method) ? this.options.ajax.method(formValue) : this.options.ajax.method;
   
     var postData = null;
     
     // Classic application/x-www-form-urlencoded (like html forms)
     if(this.options.ajax.contentType == "application/x-www-form-urlencoded" && method != "PUT") {
       var params = [];
       for(var key in formValue) {
         if(formValue.hasOwnProperty(key)) {
           var pName = (this.options.ajax.wrapObject ? this.options.ajax.wrapObject+'[' : '')+key+(this.options.ajax.wrapObject ? ']' : '');
           params.push( pName+"="+window.encodeURIComponent(formValue[key]));
         }
       }
       postData = params.join('&');
     }
     // The only other contentType available is "application/json"
     else {
       YAHOO.util.Connect.initHeader("Content-Type" , "application/json" , false);
       
       // method PUT don't send as x-www-form-urlencoded but in JSON
       if(method == "PUT") {
         var formVal = this.getValue();
         var p;
         if(this.options.ajax.wrapObject) {
           p = {};
           p[this.options.ajax.wrapObject] = formVal;
         }
         else {
           p = formVal;
         }
         postData = lang.JSON.stringify(p);
       }
       else {
         // We keep this case for backward compatibility, but should not be used
         // Used when we send in JSON in POST or GET
         postData = "value="+window.encodeURIComponent(lang.JSON.stringify(this.getValue()));
       }
     }
     
       util.Connect.asyncRequest( method, uri, {
          success: function(o) {
             if(this.options.ajax.showMask) { this.hideMask(); }
             if( lang.isFunction(this.options.ajax.callback.success) ) {
                this.options.ajax.callback.success.call(this.options.ajax.callback.scope,o);
             }
          },

          failure: function(o) {
             if(this.options.ajax.showMask) { this.hideMask(); }
             if( lang.isFunction(this.options.ajax.callback.failure) ) {
                this.options.ajax.callback.failure.call(this.options.ajax.callback.scope,o);
             }
          },
          
          timeout: this.options.ajax.callback.timeout || 60000,

          scope:this
       }, postData);
    },

    /**
     * Create a Mask over the form
     */
    renderMask: function() {
       if(this.maskRendered) return;

       // position as "relative" to position formMask inside as "absolute"
       Dom.setStyle(this.divEl, "position", "relative");

       // set zoom = 1 to fix hasLayout issue with IE6/7
       if (YAHOO.env.ua.ie) { Dom.setStyle(this.divEl, "zoom", 1); }

       // Render mask over the divEl
       this.formMask = inputEx.cn('div', {className: 'inputEx-Form-Mask'},
          {
             display: 'none',
             // Use offsetWidth instead of Dom.getStyle(this.divEl,"width") because
             // would return "auto" with IE instead of size in px
             width: this.divEl.offsetWidth+"px",
             height: this.divEl.offsetHeight+"px"
          },
          "<div class='inputEx-Form-Mask-bg'/><center><br/><div class='inputEx-Form-Mask-spinner'></div><br /><span>"+inputEx.messages.ajaxWait+"</span></div>");
       this.divEl.appendChild(this.formMask);
       this.maskRendered = true;
    },

    /**
     * Show the form mask
     */
    showMask: function() {
       this.renderMask();

       // Hide selects in IE 6
       this.toggleSelectsInIE(false);

       this.formMask.style.display = '';
    },

    /**
     * Hide the form mask
     */
    hideMask: function() {

       // Show selects back in IE 6
       this.toggleSelectsInIE(true);

       this.formMask.style.display = 'none';
    },

    /*
    * Method to hide selects in IE 6 when masking the form (else they would appear over the mask)
    */
    toggleSelectsInIE: function(show) {
       // IE 6 only
       if (!!YAHOO.env.ua.ie && YAHOO.env.ua.ie < 7) {
          var method = !!show ? YAHOO.util.Dom.removeClass : YAHOO.util.Dom.addClass;
          var that = this;
          YAHOO.util.Dom.getElementsBy(
             function() {return true;},
             "select",
             this.divEl,
             function(el) {method.call(that,el,"inputEx-hidden");}
          );
       }
    },


    /**
     * Enable all fields and buttons in the form
     */
    enable: function() {
       inputEx.ExtForm.superclass.enable.call(this);
       
       for (var i = 0 ; i < this.buttons.length ; i++) {
         this.buttons[i].enable();
       }
    },

    /**
     * Disable all fields and buttons in the form
     */
    disable: function() {
       inputEx.ExtForm.superclass.disable.call(this);
       
       for (var i = 0 ; i < this.buttons.length ; i++) {
          this.buttons[i].disable();
       }
    },
    
    
    /**
     * Purge all event listeners and remove the component from the dom
     */
    destroy: function() {
       
       var i, length, button;
       
       // Unsubscribe all listeners to submit event
       this.submitEvent.unsubscribeAll();
       
       // Recursively destroy buttons
       for (i = 0, length = this.buttons.length ; i < length ; i++) {
          button = this.buttons[i];
          button.destroy();
       }
       
       // destroy Form itself (+ inputs)
       inputEx.ExtForm.superclass.destroy.call(this);
    }
  });

})();
/**
 * An extended inputEx.CombineField
 * @class inputEx.ExtCombineField
 * @extends inputEx.ExtGroup
 * @constructor
 * @param {Object} options
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.ExtCombineField = function(options) {
    inputEx.ExtCombineField.superclass.constructor.call(this, options);
  };
  
  lang.extend(inputEx.ExtCombineField, inputEx.ExtGroup, {
    setOptions: function(options) {
      inputEx.ExtCombineField.superclass.setOptions.call(this, options);
  
      // Overwrite options
      this.options.className = options.className ? options.className : 'inputEx-CombineField';
      
      // Added options
      this.options.separators = options.separators;
    },

    render: function() {
      // Create a DIV element to wrap the editing el and the image
      this.divEl = inputEx.cn('div', {className: 'inputEx-fieldWrapper'});
      if(this.options.id) {
         this.divEl.id = this.options.id;
      }
      if(this.options.required) {
         Dom.addClass(this.divEl, "inputEx-required");
      }
  
      // Label element
      if(this.options.label) {
        this.labelDiv = inputEx.cn('div', {id: this.divEl.id+'-label', className: 'inputEx-label', 'for': this.divEl.id+'-field'});
        this.labelEl = inputEx.cn('label');
        this.labelEl.appendChild(document.createTextNode(this.options.label));
        this.labelDiv.appendChild(this.labelEl);
        this.divEl.appendChild(this.labelDiv);
      }
    
      this.fieldContainer = inputEx.cn('div', {className: this.options.className}); // for wrapping the field and description

      this.renderFields(this.fieldContainer);     

      if(this.options.description) {
        this.fieldContainer.appendChild(inputEx.cn('div', {id: this.divEl.id+'-desc', className: 'inputEx-description'}, null, this.options.description));
      }
      
      this.divEl.appendChild(this.fieldContainer);
      
      if(this.options.disabled) {
         this.disable();
      }
      
      // Insert a float breaker
      this.divEl.appendChild(inputEx.cn('div', {className: "inputEx-clear-div"}, null, " "));
    },
    
    renderField: function(fieldOptions) {
      // Subfields should inherit required property
      if (this.options.required) {
         fieldOptions.required = true;
      }
      
      return inputEx.ExtCombineField.superclass.renderField.call(this, fieldOptions);
    },
    
    renderFields: function(parentEl) {
      if (! this.options.fields) { return; }
      
      this.wrapEl = inputEx.cn('div', {className: 'inputEx-CombineField-wrapper'});
      
      this.appendSeparator(0, this.wrapEl);
      
      var i, n=this.options.fields.length, f, field, fieldEl,t;
      
      for(i = 0 ; i < n ; i++) {
         f = this.options.fields[i];
         if (this.options.required) {f.required = true;}
         field = this.renderField(f);
         fieldEl = field.getEl();
         t = f.type;
         if(t != "group" && t != "form") {
            // remove the line breaker (<div style='clear: both;'>)
            field.divEl.removeChild(fieldEl.childNodes[fieldEl.childNodes.length-1]);
          }
         // make the field float left
         Dom.setStyle(fieldEl, 'float', 'left');
         
         // add events
         Event.addBlurListener(fieldEl, this.onBlur, this, true);
     
         this.wrapEl.appendChild(fieldEl);
         
         this.appendSeparator(i+1, this.wrapEl);
      }
      
      parentEl.appendChild(this.wrapEl);
    },
    
    appendSeparator: function(i, parentEl) {
      if(this.options.separators && this.options.separators[i]) {
         var sep = inputEx.cn('div', {className: 'inputEx-CombineField-separator'}, null, this.options.separators[i]);
         parentEl.appendChild(sep);
      }
    },
    
    setValue: function(values, sendUpdatedEvt) {
      if(!values) {
        return;
      }
      
      var i, n=this.inputs.length;
      
      for (i = 0 ; i < n ; i++) {
        this.inputs[i].setValue(values[i], false);
      }
        
      this.runFieldsInteractions();
        
      if(sendUpdatedEvt !== false) {
        this.fireUpdatedEvt();
      }
    },
 
    getValue: function() {
      var values = [], i, n=this.inputs.length;
      for(i = 0 ; i < n; i++) {
        values.push(this.inputs[i].getValue());
      }
      return values;
    }

  });

  inputEx.registerType("extcombine", inputEx.ExtCombineField);
})();
/**
 * A phone number field
 * @class inputEx.PhoneSplitField
 * @extends inputEx.ExtCombineField
 * @constructor
 * @param {Object} options
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.PhoneSplitField = function(options) {
    inputEx.PhoneSplitField.superclass.constructor.call(this, options);
  };

  inputEx.PhoneSplitField.stateInvalidCountry = 'inv_c';
  inputEx.PhoneSplitField.stateInvalidArea= 'inv_a';
  inputEx.PhoneSplitField.stateInvalidNumber= 'inv_n';
  inputEx.PhoneSplitField.stateRequiredCountry = 'req_c';
  inputEx.PhoneSplitField.stateRequiredArea= 'req_a';
  inputEx.PhoneSplitField.stateRequiredNumber= 'req_n';

  lang.extend(inputEx.PhoneSplitField, inputEx.ExtCombineField, {
    setOptions: function(options) {

      options.fields = [];
      
      options.fields.push({ 
        type: 'string',
        className: 'inputEx-Phone-country',
        regexp: /^[0-9 ]+$/,
        size: 5,
        maxLength: 5,
        minLength: 1
      });
      
      options.fields.push({
        type: 'string',
        className: 'inputEx-Phone-area',
        regexp: /^[0-9 ]+$/,
        size: 5,
        maxLength: 5,
        minLength: 1
      });
      
      options.fields.push({
        type: 'string',
        className: 'inputEx-Phone-number',
        regexp: /^[0-9 ]+$/,
        size: 10,
        maxLength: 10,
        minLength: 1
      });

      options.separators = options.separators || [ "+", "-", "-", false ];
      
      inputEx.PhoneSplitField.superclass.setOptions.call(this, options);
      
      this.options.delimiter = options.delimiter || '-';
      
      if (! lang.isObject(options.messages)) {
        options.messages = {};
      }
      
      this.options.messages.countryRequired = options.messages.countryRequired ? options.messages.countryRequired : "Country code is required";
      this.options.messages.countryInvalid = options.messages.countryInvalid ? options.messages.countryInvalid : "Invalid country code";
      this.options.messages.areaRequired = options.messages.areaRequired ? options.messages.areaRequired : "Area code is required";
      this.options.messages.areaInvalid = options.messages.areaInvalid ? options.messages.areaInvalid : "Invalid area code";
      this.options.messages.numberRequired = options.messages.numberRequired ? options.messages.numberRequired : "Phone number is required";
      this.options.messages.numberInvalid = options.messages.numberInvalid ? options.messages.numberInvalid : "Invalid phone number";
      
      var r = '';
      var separators = options.separators;
      for (var i = 0; i < separators.length; i ++) {
         if (! separators[i]) continue;
         var c = separators[i].replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!<>\|\:])/g, '\\$1');
         r = r + c + '([0-9]+)';
      }
      
      this.patt = new RegExp("^" + r + "$", 'i');
    },
    
    render: function() {
      inputEx.PhoneSplitField.superclass.render.call(this);
      
      this.hiddenInputEl = inputEx.cn('input', { type: "hidden", name: this.options.name }); 
      this.wrapEl.appendChild(this.hiddenInputEl);
    },
    
    onChange: function(e, args) {
      inputEx.PhoneSplitField.superclass.onChange.call(this, e, args);
      
      lang.later(50, this, function() {
        this.hiddenInputEl.value = this.getValue();
      });
    },
    
    onBlur: function(e) {
      Dom.removeClass(this.getEl(), 'inputEx-focused');
      
      lang.later(50, this, function() {
        if (! YAHOO.util.Selector.query('div[class~="inputEx-focused"]', this.divEl, true)) {
          this.setClassFromState();
        }
      });
   },
    
    setValue: function(value, sendUpdatedEvt) {
      if (! lang.isString(value)) return;
      
      var separators = this.options.separators;
      
      var match = value.match(this.patt);
      if (match) {
        inputEx.PhoneSplitField.superclass.setValue.call(this, match.slice(1), sendUpdatedEvt);
        this.hiddenInputEl.value = value;
      }
    },
    
    getValue: function(forceArray) {
      if (this.isEmpty()) return "";
      
      var values = inputEx.PhoneSplitField.superclass.getValue.call(this);
      var separators = this.options.separators;
      
      for (var i = 0; i < values.length; i ++) {
        values[i] = values[i].replace(/ /gi, '');
      }
      
      if (forceArray) {
        return values;
      }
      
      var value = '';
      for (var i = 0; i < separators.length; i ++) {
        var a = (i == values.length) ? '' : values[i];
        value = value + (separators[i] ? separators[i] : '') + a;
      }
      
      return value;
    },
    
    isEmpty: function() {
      var values = inputEx.PhoneSplitField.superclass.getValue.call(this);
      return (values[0] == "" && values[1] == "" &&  values[2] == "");
    },
    
    getState: function() { 
      if (this.isEmpty()) {
        return this.options.required ? inputEx.stateRequired : inputEx.stateEmpty;
      }
      
      var values = this.getValue(true);
      var reg = /^[0-9]+$/;

      for (var i = 0; i < values.length; i++) {
        if (! values[i].length) {
          return inputEx.stateRequired;
        }
        if (! reg.test(values[i])) {
          return inputEx.stateInvalid;
        }
      }

      return this.validate() ? inputEx.stateValid : inputEx.stateInvalid;
    },
    
    getStateString: function(state) {
      var values = this.getValue(true);
      var reg = /^[0-9]+$/;

      if (lang.isArray(values)) {
        if (! values[0].length) {
          return this.options.messages.countryRequired;
        } else if (! reg.test(values[0])) {
          return this.options.messages.countryInvalid;
        } else if (! values[1].length) {
          return this.options.messages.areaRequired;
        } else if (! reg.test(values[1])) {
          return this.options.messages.areaInvalid;
        } else if (! values[2].length) {
          return this.options.messages.numberRequired;
        } else if (! reg.test(values[2])) {
          return this.options.messages.numberInvalid;
        }
      }
      
      return inputEx.PhoneSplitField.superclass.getStateString.call(this, state);
    }
  });
  
  inputEx.registerType("phonesplit", inputEx.PhoneSplitField);
})();
/**
 * A month and year select field
 * @class inputEx.MonthYearSelect
 * @extends inputEx.ExtCombineField
 * @constructor
 * @param {Object} options
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.MonthYearSelect = function(options) {
    inputEx.MonthYearSelect.superclass.constructor.call(this, options);
  };

  lang.extend(inputEx.MonthYearSelect, inputEx.ExtCombineField, {
    setOptions: function(options) {

      options.fields = [];
      
      options.fields.push({ 
        type: 'select',
        name: 'month',
        selectOptions:  options.month.selectOptions,
        selectValues:   options.month.selectValues
      });
      
      options.fields.push({
        type: 'select',
        name: 'month',
        selectOptions:  options.year.selectOptions,
        selectValues:   options.year.selectValues
      });

      options.separators = options.separators || [ false, "/", false ];
      
      inputEx.MonthYearSelect.superclass.setOptions.call(this, options);
      
      this.options.delimiter = options.delimiter || '/';
      
      if (! lang.isObject(options.messages)) {
        options.messages = {};
      }
    },
    
    setValue: function(value, sendUpdatedEvt) {
      if (! lang.isString(value)) return;
      
      var values = value.split(this.options.delimiter, 2);
      inputEx.MonthYearSelect.superclass.setValue.call(this, values, sendUpdatedEvt);
    },
    
    getValue: function() {
      if (this.isEmpty()) return "";
      
      var values = inputEx.MonthYearSelect.superclass.getValue.call(this);
      return values[0] + this.options.delimiter + values[1];
    },
    
    isEmpty: function() {
      var values = inputEx.MonthYearSelect.superclass.getValue.call(this);
      return (values[0] == "" && values[1] == "");
    },
    
    getState: function() {
      var state = inputEx.MonthYearSelect.superclass.getState.call(this);
      return state;
    }

  });
  
  inputEx.registerType("monthyearselect", inputEx.MonthYearSelect);
})();
/**
 * A phone number field
 * @class inputEx.PhoneSplitField
 * @extends inputEx.ExtCombineField
 * @constructor
 * @param {Object} options
 */
(function() {
  var lang = YAHOO.lang, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom;
  
  inputEx.CaptchaField = function(options) {
    inputEx.CaptchaField.superclass.constructor.call(this, options);
  };

  lang.extend(inputEx.CaptchaField, inputEx.StringField, {
    setOptions: function(options) {
      options.autocomplete = false;
      
      inputEx.CaptchaField.superclass.setOptions.call(this, options);
      
      this.options.imgUri = options.imgUri;
      this.options.alt = options.alt || '';
    },
    
    renderComponent: function() {
      this.capWrapEl = inputEx.cn('div', {className: 'inputEx-CaptchaImg-wrapper'});
      
      var imgId = this.divEl.id ? this.divEl.id + '-captcha' : YAHOO.util.Dom.generateId();
      this.capImgEl = inputEx.cn('img', { id: imgId, src: this.getCaptchaUri(), alt: this.options.alt });
      
      this.capWrapEl.appendChild(this.capImgEl);
      this.fieldContainer.appendChild(this.capWrapEl);
      
      inputEx.CaptchaField.superclass.renderComponent.call(this);
    },
    
    initEvents: function() {
      inputEx.CaptchaField.superclass.initEvents.call(this);
      
      Event.addListener(this.capImgEl, 'click', this.reloadCaptcha, this, true);
    },
    
    reloadCaptcha: function(e) {
      this.capImgEl.src = this.getCaptchaUri();
    },
   
    getCaptchaUri: function() {
      return this.options.imgUri + Math.round(Math.random(0)*9998) + 1;
    }
  });
  
  inputEx.registerType("captcha", inputEx.CaptchaField);
})();
/**
 * An extended autocomplete field
 * @class inputEx.ExtAutoComplete
 * @extends inputEx.AutoComplete
 * @constructor
 * @param {Object} options
 */
(function() {
  var lang = YAHOO.lang;
  
  inputEx.ExtAutoComplete = function(options) {
    inputEx.ExtAutoComplete.superclass.constructor.call(this, options);
  };

  lang.extend(inputEx.ExtAutoComplete, inputEx.AutoComplete, {
    onChange: function(e) {
      // Clear the field when no value 
      if (this.hiddenEl.value != this.el.value) this.hiddenEl.value = this.el.value;
      lang.later(50, this, function() {
       if(this.el.value == "") {
          this.setValue("");
       }
      });

      this.setClassFromState();
    }
  });
  
  inputEx.registerType("extautocomplete", inputEx.ExtAutoComplete);
})();