﻿(function($){
$.extend($.fn, {
  // supports only one form per call
	orchestrate: function( options ) {
    var form = $(this[0]);
		// check if a object was already created
		var orchestrator = $.data(form, 'orchestrator');
		if ( orchestrator ) {
			return orchestrator;
		}
		orchestrator = new $.orchestrator(options, form);
		form.data('orchestrator', orchestrator);
    form.jqTransform();
    orchestrator.config.formLayoutProcessor(form);
    
    // validation options - start with the default behavior below and merge in any overrides
    var validationOptions = {
        errorPlacement: function(error, element) {
              $(element).parents(".rowElem").find(".validator").append(error);
           },
       
        highlight: function(element, errorClass) {
          var target$ = $(element).parents('.jqTransformCheckboxWrapper, .jqTransformSelectWrapper, .jqTransformInputWrapper, .radio_group').next();
          if (target$.hasClass("indicator")) {
            target$.addClass("highlight");
            target$.css("visibility", "visible");
          }
        },
        
        unhighlight: function(element, errorClass) {
          var target$ = $(element).parents('.jqTransformCheckboxWrapper, .jqTransformSelectWrapper, .jqTransformInputWrapper, .radio_group').next();
          if (target$.hasClass("indicator")) {
            $(element).next().removeClass("highlight");
            target$.css("visibility", "hidden");
          }
        }    
    };
    $.extend(validationOptions, orchestrator.config.validationOptions);
		validationOptions.submitHandler = orchestrator.config.submitHandler; // always call our handler
		var validator = form.validate(validationOptions);
		//this overwrites the validator method so we can scroll to the first field in error (note, neccessary because of lack of support for radios and dropdowns
		validator.focusInvalid = function() {
		    var targ = $(".error:visible:first");
		    if (!$.inviewport(targ, {threshold : 31})) {
		      $("html,body").scrollTo($(".error:visible:first").parents(".rowElem")); 
		    }
		};
		
		
    // these force validation to occur for radios and drop downs, which is not supported by default (and are wrapped up tight by jqTransform anyway)
    if (validator.settings.onclick) {
      $(".jqTransformRadioWrapper").change(function() {
        form.validate().element($(this).children("input[type=radio]"));
      });
      $(".jqTransformSelectWrapper ul a").click(function() {
        form.validate().element($(this).parents(".jqTransformSelectWrapper").children("select"));
      });
    }
		
		
	  return orchestrator;
	}
});



// constructor for validator
$.orchestrator = function(options, form) {
  this.form = form;
  
  this.config = {
    captchaRenderer: this.defaultCaptchaRenderer,
    captchaErrorHandler: this.defaultCaptchaErrorHandler,
    submitUrl: null,
    submitHandler: this.defaultSubmitHandler,
    resultsProcessor: this.defaultResultsProcessor,
    commsErrorProcessor: this.defaultCommsErrorProcessor,
    completeProcessor: this.defaultCompleteHandler,
    beforeSendProcessor: this.defaultBeforeSendHandler,
    serverValidationRenderer: this.summaryServerValidationRenderer,
    busyIndicatorHtml$: this.defaultbusyIndicatorHtml$,
    busyIndicatorPlacement: this.defaultBusyIndicatorPlacement,
    formLayoutProcessor: this.defaultFormLayoutHandler,
    serverSuccessProcessor: null,
    serverErrorProcessor: this.defaultServerErrorProcessor,
    serverErrorUrl: null,
    serverSuccessProcessor: this.defaultSuccessProcessor,
    messages: {
      validationError: "Server validation errors:", 
      commsError: "Could not communicate with the server at this time. Please try again.",
      serverError: "Sorry, a problem occurred on the server.",
      serverSuccess: "Form was successfully submitted to the server, Thank YOU!"
    }
  };
  $.extend(this.config, options);
  
  function invoke(funct) {
    var f = funct;
    return function() {
      var args = Array.prototype.slice.call(arguments); 
      f.apply(getOrchestrator(form), args);
    };
  }

  //mimics oop by applying an instance of the orchestrator class to the this pointer for each handler
  var thisConfig = this.config;
  $.each(this.config, function(index, item) {
    if (typeof this == "function") {
      thisConfig[index] = invoke(item, [form]);
    }
  });
    
};

  function getOrchestrator(form) {
    return form.data('orchestrator');
  };

$.extend($.orchestrator, {
  prototype: {
    form: null,
    defaultSubmitHandler: function() {
        this.displayCommServerError("");
        this.form.ajaxSubmit(
	        {url: this.config.submitUrl, dataType: "json",  type: "POST", timeout: 8000, 
	            beforeSend: this.config.beforeSendProcessor,
	            success: this.config.resultsProcessor, 
	            error: this.config.commsErrorProcessor,
	            complete: this.config.completeProcessor
          }
        );
    },
    defaultBeforeSendHandler: function () {
      this.config.busyIndicatorPlacement(this.form, this.config.busyIndicatorHtml$);
      this.config.busyIndicatorHtml$.show();
    },
   defaultResultsProcessor: function(response, status, form) {
        if (response.ValidationErrors && response.ValidationErrors.length > 0) {
          var funct = this.getValidationRendererToUse();
          funct.apply(this, [response.ValidationErrors]);
          this.config.captchaRenderer();
        }
        else if (response.Error && response.Error.length > 0) {
            this.config.serverErrorProcessor();
            this.config.captchaRenderer();
        } else {
          this.config.serverSuccessProcessor();
        }
    },
    defaultSuccessProcessor: function() {
      this.form.hide();
      $("#successPanel").show();
      $("#successPanel .message").html(this.config.messages.serverSuccess);
    },
    defaultServerErrorProcessor: function () {
      var form = this.form.hide();
      $("#errorPanel").show().find("#back_button").click(function() { $("#errorPanel").hide(); form.show(); });
      $("#errorPanel .message").html(this.config.messages.serverError);
    },    
    defaultCommsErrorProcessor: function onError(XMLHttpRequest, textStatus, errorThrown) {
      this.displayCommServerError(this.config.messages.commsError);
    },
    displayCommServerError: function(message) {
      var target = $("#server_error");
      if (target.length == 0) {
        this.form.append('<div id="server_error"><div class="message"></div></div>');
      }
      $("#server_error .message").html(message);
    },
//    displayValidationErrors: function(message) {
//    
//    },
    defaultCompleteHandler: function () {
      this.config.busyIndicatorHtml$.hide();
    },
	  findByName: function( name ) {
		  // select by name and filter by form for performance over form.find("[name=...]")
		  var form = this.currentForm;
		  return $(document.getElementsByName(name)).map(function(index, element) {
			  return element.form == form && element.name == name && element  || null;
		  });
	  },
    defaultbusyIndicatorHtml$: 
        $('<img src="' + UComWebApp.getImagePath('loading.gif') + '"/>')
        .css({"vertical-align": "middle", "margin-left":"3px" }),
    
    defaultBusyIndicatorPlacement: function(form, indicatorHtml) {
         //var elem$ = $(indicatorHtml).append("body");
         var targ$ = form.find("input, button").filter(":submit");
         if (targ$.length > 0) {
           $(indicatorHtml).insertAfter(targ$[0]);
         }
    },  
    defaultCaptchaRenderer: function() {
       $("#captcha").val("");
       $("#captcha_container .refresh").click();
    },
    defaultCaptchaErrorHandler: function() {
    },
    defaultFormLayoutHandler: function(form) {
        $(".rowElem label:first-child").addClass("firstLabel");
        $(form).find(".rowElem").wrapInner('<div class="field" />').append('<div class="validator" />');
        $(".jqTransformCheckboxWrapper, .jqTransformSelectWrapper, .jqTransformInputWrapper, .radio_group").after('<div class="indicator">*</div>');
    },
    getValidationRendererToUse : function() {
      switch(typeof this.config.serverValidationRenderer) {
        case "function":
          return this.config.serverValidationRenderer;
        case "string":
          if (this.config.serverValidationRenderer.toLowerCase() == "inline") {
            return this.inlineServerValidationRenderer;
          } else {
            return this.summaryServerValidationRenderer;
          }
        default: return this.summaryServerValidationRenderer;
      }
    },
    summaryServerValidationRenderer: function(validationErrors) {
      this.displayValidationSummary(this.config.messages.validationError, validationErrors);
    },
    inlineServerValidationRenderer: function(validationErrors) {
        if (validationErrors) {
          var errors = {};
          $.each( validationErrors, function(index, item) { 
            errors[item.Key] = item.Value;
          });
          var validator = this.form.validate();
          validator.showErrors(errors);
          validator.focusInvalid();
        }
    },
    displayValidationSummary: function(message, validationErrors) {
      var target = $("#validation_summary");
      var html = '<div id="validation_summary"><div class="message"></div><ul class="validation_errors"></ul></div>';
      if (target.length == 0) {
        target = this.form.append(html);
      }
      else {
        target.replaceWith(html);
      }
      if (validationErrors && validationErrors.length > 0) {
        $("#validation_summary .message").html(message);
        captchaErrorHandler = this.config.captchaErrorHandler;
        $.each(
          validationErrors, function(index, item) { 
            if (item.Key == "captcha") {
              captchaErrorHandler(item.Key, item.Value);
            }
            $("#validation_summary .validation_errors").append("<li>" + item.Key + ": " + item.Value + "</li>"); 
          });
      }
    }
    


  } // end prototype
  
  
});
 
}) (jQuery);



  


