/**
 * Playground ajax form plugin
 * @requires jQuery v1.3 or later
 * 
 * Usage: $('formelement').ajaxform(action, options);
 * 
 * @param String action   Post URL
 * @param Object options  Can have any of the following attributes:
 * 
 * okMessageHdr:      The header text that will be shown if the post is successful.
 *                      default value: 'Saved'
 * 
 * okMessageDet:      The message text that will be shown if the post is successful.
 *                      default value: 'The value have been updated'
 *                      
 * errorMessageHdr:   The header text that will be shown if the post fails.
 *                      default value: 'Error'
 *                      
 * btnText:           The text that will be shown on the submit button while the form is submitting.
 *                      default value: 'Saving...'
 *                     
 * onSuccess:         Js function that will be called on success. Should have on parameter that accepts
 *                    an JSON object.
 *                      default value: null
 *
 * onError:           Js function that will be called on fail. Should have two parameters the first one
 *                    is the main error text and the other is an associativ array with fieldnames and error messages.
 *                      default value: null
 * 
 * showStdOnSuccess:  Whether or not the standard success message is displayed or not.
 *                      default value: true
 *
 * showStdOnError:    Whether or not the standard error message is displayed or not.
 *                      default value: true
 * 
 * clearAfterSubmit:  Whether or not the form should be cleared after submit.
 *                      default value: true
 */
;(function($) {
  $.fn.ajaxform = function(action, options) {
    var opt = {};
    $.extend(opt, $.fn.ajaxform.defaults, options);
    $(this).each(function() {
      var formElement = $(this);
      if(!formElement.is('form'))
        throw 'ajaxform can only be used on a form element.';
      formElement.submit(function() {
        formSubmit($(this), action, opt);
        return false;
      });
    });
    return this;
  };
  
  $.fn.ajaxform.defaults = {
    okMessageHdr: 'Saved',
    okMessageDet: 'The values have been updated',
    errorMessageHdr: '<h4>Error</h4>',
    btnText: 'Saving...',
    onSuccess: null,
    onError: null,
    showStdOnSuccess: true,
    showStdOnError: true,
    clearAfterSubmit: true
  };
  
  function formSubmit(formElement, action, options) {
    flushTinyMCE();
    
    // Touch all of our fields so that they can receive error messages
    formElement.find('div.divformfield').each(function() {
      $(this).data('hasBeenRun', true);
    });
    
    clearErrors(formElement);
    clearInfo(formElement);
    
    var ajaxLoader = formElement.find('.ajaxloader:first');
    var showAjaxTimeout = setTimeout(function() {
      ajaxLoader.show();
    }, 500);
    
    var submitButton = formElement.find(':submit');
    var oldButtonText = (submitButton.size() == 0 ? '' : submitButton.val());
    if(submitButton.size() != 0)
      submitButton.val(options.btnText).attr('disabled', 'disabled');
    
    var resetForm = function() {
      clearTimeout(showAjaxTimeout);
      ajaxLoader.hide();
      if(submitButton.size() != 0)
        submitButton.val(oldButtonText).attr('disabled', '');
    };
    
    try {
      $.ajax({
        url: action,
        data: formElement.serializeArray(),
        type: 'POST',
        error: function(request, status, err) {
          resetForm();
          PG.defaultErrorCallback(request);
        },
        success: function(j, s) {
          resetForm();
          try {
            var json = PG.evalJSON(j);
            
            if(json['exception'] != undefined) {
              setBadFields(formElement, json['badfields']);
              
              if(options.showStdOnError) {
                var errorBox = formElement.find('.error:first');
                if(errorBox.length == 0) {
                  alert(json['exception']);
                } else {
                  errorBox.find('.header:first')
                          .html(options.errorMessageHdr)
                          .end()
                          .find('.detailed:first')
                          .html(json['exception']);
                  errorBox.fadeIn(500);
                }
              }
              if(options.onError)
                options.onError(json['exception'], json['badfields']);
            } else {
              if(options.clearAfterSubmit) {
                // Clear all of our input fields
                formElement.find(':input').each(function() {
                  // submit elements
                  if(this.type == 'submit' || this.type == 'button' || this.type == 'image') 
                    return;
                  // ignore hidden elements too
                  if(this.type == 'hidden') 
                    return;
                  if(this.type == 'checkbox') {
                    this.checked = false;
                    return;
                  }
                  $(this).val('');
                });
              }
              
              if(options.showStdOnSuccess) {
                var infoBox = formElement.find('.info:first');
                if(infoBox.length == 0) {
                  alert(options.okMessageHdr);
                } else {
                  infoBox.find('.header:first')
                         .html(options.okMessageHdr)
                         .end()
                         .find('.detailed:first')
                         .html(options.okMessageDet);
                  infoBox.fadeIn(500);
                  setTimeout(function() { infoBox.fadeOut(500); }, 5000);
                }                
              }
              if(options.onSuccess) {
                options.onSuccess(json);
              }
            }
          } catch(e) {
            alert(e);
          }
        }
      });
    } catch(e) {
      
    }
  };
  
  function flushTinyMCE() {
    try {
      if(tinyMCE)
        tinyMCE.triggerSave(true, true);
    } catch(e) {
      // tinyMCE was not defined
    }
  };
  
  function clearErrors(formElement) {
    formElement.find('.error:first').hide();
    formElement.find('.fielderror').each(function() { $(this).html('').hide(); });
    formElement.find('.badfield').each(function() { $(this).removeClass('badfield'); });
  };
  
  function clearInfo(formElement) {
    formElement.find('.info:first').hide();
  };
  
  function setBadFields(formElement, badfields) {
    // badfields might be undefined if we have no errors, however, we must still clear the other 
    // errors.
    if(!badfields) badfields = {};
    
    formElement.find('div.divformfield[name]').each(function() {
      var el = $(this);
      try {
        var fieldName = el.attr('name');
        var errorDiv = el.find('.fielderror:first');
        
        // If the user didn't touch the field yet, don't show him an error
        if(!el.data('hasBeenRun')) return;
        
        var val = badfields[fieldName];
        if(val == undefined)
          clearFieldError(el);
        else
          setFieldError(el, val);
      } catch(e) {
        alert('Unable to proceed with the request');
      }
    });
  };
  
  function setFieldError(formDiv, error) {
    if(formDiv.find('.fielderror:first').html(error).fadeIn().length == 0) {
      alert('Field has no error container');
    }
    
    formDiv.find(':input').each(function() {
      if(!$(this).hasClass('badfield')) {
        $(this).addClass('badfield');
      }
    });
  };
  
  function clearFieldError(formDiv) {
    var errorDiv = formDiv.find('.fielderror:first');
    errorDiv.hide();
    // Please do not remove this if-statement. If we set the content of a div to an empty string
    // in IE6, the div will get one line high even if it shouldn't (the line-height bug).
    
    if(errorDiv.html() != '')
      errorDiv.html('');
    
    formDiv.find(':input').each(function() {
      if($(this).hasClass('badfield'))
        $(this).removeClass('badfield');
    });
  };
  
  $.fn.validateField = function(action) {
    $(this).each(function() {
      $(this).parents('div.divformfield').data('hasBeenRun', true);
      var form = $(this).parents('form');
      $.ajax({
        url: action,
        data: form.serializeArray(),
        type: 'POST',
        success: function(j, status) {
          var json = PG.evalJSON(j);
          setBadFields(form, json['badfields']);
        }
      });
    });
    return this;
  };
  
  $.fn.fieldOnKeyPress = function(action) {
    flushTinyMCE();
    $(this).each(function() {
      var div = $(this).parents('div.divformfield');
      if(!div.data('hasBeenRun')) {
        div.data('hasBeenRun', true);
        if($(this).hasClass('badfield')) div.data('hasHadError', true);
      }
      if(div.data('hasHadError')) return $(this).validateField(action);
    });
  };
})(jQuery);

