/*
 * Inline Form Validation Engine 1.6.2, jQuery plugin
 * 
 * Copyright(c) 2009, Cedric Dugas
 * http://www.position-relative.net
 *  
 * Form validation engine allowing custom regex rules to be added.
 * Thanks to Francois Duquette
 * Licenced under the MIT Licence
 */
 
(function($) {
  
  $.fn.validationEngine = function(settings) {

  if($.validationEngineLanguage){       // IS THERE A LANGUAGE LOCALISATION ?
    allRules = $.validationEngineLanguage.allRules;
  }else{
    $.validationEngine.debug("Validation engine rules are not loaded check your external file");
  }
  settings = jQuery.extend({
    allrules:allRules,
    validationEventTriggers:"blur",         
    inlineValidation: true, 
    returnIsValid:false,
    animateSubmit:true,
    unbindEngine:true,
    ajaxSubmit: false,
    promptPosition: "topRight", // OPENNING BOX POSITION, IMPLEMENTED: topLeft, topRight, bottomLeft, centerRight, bottomRight
    success : false,
    failure : function() {}
  }, settings); 
  $.validationEngine.settings = settings;
  $.validationEngine.ajaxValidArray = new Array();  // ARRAY FOR AJAX: VALIDATION MEMORY 
  
  if(settings.inlineValidation == true){    // Validating Inline ?
    if(!settings.returnIsValid){          // NEEDED FOR THE SETTING returnIsValid
      allowReturnIsvalid = false;
      $(this).find("[class*=validate]").not("[type=checkbox]").bind(settings.validationEventTriggers, function(caller){ _inlinEvent(this); })
      $(this).find("[class*=validate][type=checkbox]").bind("click", function(caller){ _inlinEvent(this); })

      firstvalid = false;
    }
      function _inlinEvent(caller){
        $.validationEngine.settings = settings;
        if($.validationEngine.intercept == false || !$.validationEngine.intercept){   // STOP INLINE VALIDATION THIS TIME ONLY
          $.validationEngine.onSubmitValid=false;
          $.validationEngine.loadValidation(caller); 
        }else{
          $.validationEngine.intercept = false;
        }
      }
  }
  if (settings.returnIsValid){    // Do validation and return true or false, it bypass everything;
    if ($.validationEngine.submitValidation(this,settings)){
      return false;
    }else{
      return true;
    }
  }
  $(this).bind("submit", function(caller){   // ON FORM SUBMIT, CONTROL AJAX FUNCTION IF SPECIFIED ON DOCUMENT READY
    $.validationEngine.onSubmitValid = true;
    $.validationEngine.settings = settings;
    if($.validationEngine.submitValidation(this,settings) == false){
      if($.validationEngine.submitForm(this,settings) == true) {return false;}
    }else{
      settings.failure && settings.failure(); 
      return false;
    }   
  })
};  
$.validationEngine = {
  defaultSetting : function(caller) { 
    if($.validationEngineLanguage){       // IS THERE A LANGUAGE LOCALISATION ?
      allRules = $.validationEngineLanguage.allRules;
    }else{
      $.validationEngine.debug("Validation engine rules are not loaded check your external file");
    } 
    settings = {
      allrules:allRules,
      validationEventTriggers:"blur",         
      inlineValidation: true, 
      returnIsValid:false,
      animateSubmit:true,
      unbindEngine:true,
      ajaxSubmit: false,
      promptPosition: "topRight", // OPENNING BOX POSITION, IMPLEMENTED: topLeft, topRight, bottomLeft, centerRight, bottomRight
      success : false,
      failure : function() {}
    } 
    $.validationEngine.settings = settings;
  },
  loadValidation : function(caller) {   // GET VALIDATIONS TO BE EXECUTED
    if(!$.validationEngine.settings){
      $.validationEngine.defaultSetting()
    }
    rulesParsing = $(caller).attr('class');
    rulesRegExp = /\[(.*)\]/;
    getRules = rulesRegExp.exec(rulesParsing);
    str = getRules[1];
    pattern = /\W+/;
    result= str.split(pattern); 
    
    var validateCalll = $.validationEngine.validateCall(caller,result)
    return validateCalll;
  },
  validateCall : function(caller,rules) { // EXECUTE VALIDATION REQUIRED BY THE USER FOR THIS FIELD
    var promptText =""  
    
    if(!$(caller).attr("id")) { $.validationEngine.debug("This field have no ID attribut( name & class displayed): "+$(caller).attr("name")+" "+$(caller).attr("class")) }

    caller = caller;
    ajaxValidate = false;
    var callerName = $(caller).attr("name");
    $.validationEngine.isError = false;
    $.validationEngine.showTriangle = true;
    callerType = $(caller).attr("type");

    for (i=0; i<rules.length;i++){
      switch (rules[i]){
      case "optional": 
        if(!$(caller).val()){
          $.validationEngine.closePrompt(caller);
          return $.validationEngine.isError;
        }
      break;
      case "required": 
        _required(caller,rules);
      break;
      case "custom": 
         _customRegex(caller,rules,i);
      break;
      case "ajax": 
        if(!$.validationEngine.onSubmitValid){
          _ajax(caller,rules,i);  
        };
      break;
      case "length": 
         _length(caller,rules,i);
      break;
      case "maxCheckbox": 
        _maxCheckbox(caller,rules,i);
        groupname = $(caller).attr("name");
        caller = $("input[name='"+groupname+"']");
      break;
      case "minCheckbox": 
        _minCheckbox(caller,rules,i);
        groupname = $(caller).attr("name");
        caller = $("input[name='"+groupname+"']");
      break;
      case "confirm": 
         _confirm(caller,rules,i);
      break;
      default :;
      };
    };
    radioHack();
    if ($.validationEngine.isError == true){
      linkTofield = $.validationEngine.linkTofield(caller);
      
      ($("div."+linkTofield).size() ==0) ? $.validationEngine.buildPrompt(caller,promptText,"error")  : $.validationEngine.updatePromptText(caller,promptText);
    }else{ $.validationEngine.closePrompt(caller);}     
    /* UNFORTUNATE RADIO AND CHECKBOX GROUP HACKS */
    /* As my validation is looping input with id's we need a hack for my validation to understand to group these inputs */
    function radioHack(){
        if($("input[name='"+callerName+"']").size()> 1 && (callerType == "radio" || callerType == "checkbox")) {        // Hack for radio/checkbox group button, the validation go the first radio/checkbox of the group
            caller = $("input[name='"+callerName+"'][type!=hidden]:first");     
            $.validationEngine.showTriangle = false;
        }      
      }
    /* VALIDATION FUNCTIONS */
    function _required(caller,rules){   // VALIDATE BLANK FIELD
      callerType = $(caller).attr("type");
      if (callerType == "text" || callerType == "password" || callerType == "textarea"){
                
        if(!$(caller).val()){
          $.validationEngine.isError = true;
          promptText += $.validationEngine.settings.allrules[rules[i]].alertText+"<br />";
        } 
      } 
      if (callerType == "radio" || callerType == "checkbox" ){
        callerName = $(caller).attr("name");
    
        if($("input[name='"+callerName+"']:checked").size() == 0) {
          $.validationEngine.isError = true;
          if($("input[name='"+callerName+"']").size() ==1) {
            promptText += $.validationEngine.settings.allrules[rules[i]].alertTextCheckboxe+"<br />"; 
          }else{
             promptText += $.validationEngine.settings.allrules[rules[i]].alertTextCheckboxMultiple+"<br />";
          } 
        }
      } 
      if (callerType == "select-one") { // added by paul@kinetek.net for select boxes, Thank you    
        if(!$(caller).val()) {
          $.validationEngine.isError = true;
          promptText += $.validationEngine.settings.allrules[rules[i]].alertText+"<br />";
        }
      }
      if (callerType == "select-multiple") { // added by paul@kinetek.net for select boxes, Thank you 
        if(!$(caller).find("option:selected").val()) {
          $.validationEngine.isError = true;
          promptText += $.validationEngine.settings.allrules[rules[i]].alertText+"<br />";
        }
      }
    }
    function _customRegex(caller,rules,position){    // VALIDATE REGEX RULES
      customRule = rules[position+1];
      pattern = eval($.validationEngine.settings.allrules[customRule].regex);
      
      if(!pattern.test($(caller).attr('value'))){
        $.validationEngine.isError = true;
        promptText += $.validationEngine.settings.allrules[customRule].alertText+"<br />";
      }
    }
    function _ajax(caller,rules,position){         // VALIDATE AJAX RULES
      
      customAjaxRule = rules[position+1];
      postfile = $.validationEngine.settings.allrules[customAjaxRule].file;
      fieldValue = $(caller).val();
      ajaxCaller = caller;
      fieldId = $(caller).attr("id");
      ajaxValidate = true;
      ajaxisError = $.validationEngine.isError;
      
      if(!$.validationEngine.settings.allrules[customAjaxRule].extraData){
        extraData = $.validationEngine.settings.allrules[customAjaxRule].extraData;
      }else{
        extraData = "";
      }
      /* AJAX VALIDATION HAS ITS OWN UPDATE AND BUILD UNLIKE OTHER RULES */ 
      if(!ajaxisError){
        $.ajax({
            type: "POST",
            url: postfile,
            async: true,
            data: "validateValue="+fieldValue+"&validateId="+fieldId+"&validateError="+customAjaxRule+extraData,
            beforeSend: function(){   // BUILD A LOADING PROMPT IF LOAD TEXT EXIST            
              if($.validationEngine.settings.allrules[customAjaxRule].alertTextLoad){
              
                if(!$("div."+fieldId+"formError")[0]){                  
                return $.validationEngine.buildPrompt(ajaxCaller,$.validationEngine.settings.allrules[customAjaxRule].alertTextLoad,"load");
              }else{
                $.validationEngine.updatePromptText(ajaxCaller,$.validationEngine.settings.allrules[customAjaxRule].alertTextLoad,"load");
              }
              }
            },
            error: function(data,transport){ $.validationEngine.debug("error in the ajax: "+data.status+" "+transport) },
          success: function(data){          // GET SUCCESS DATA RETURN JSON
            data = eval( "("+data+")");       // GET JSON DATA FROM PHP AND PARSE IT
            ajaxisError = data.jsonValidateReturn[2];
            customAjaxRule = data.jsonValidateReturn[1];
            ajaxCaller = $("#"+data.jsonValidateReturn[0])[0];
            fieldId = ajaxCaller;
            ajaxErrorLength = $.validationEngine.ajaxValidArray.length;
            existInarray = false;
            
             if(ajaxisError == "false"){      // DATA FALSE UPDATE PROMPT WITH ERROR;
              
              _checkInArray(false)        // Check if ajax validation alreay used on this field
              
              if(!existInarray){          // Add ajax error to stop submit        
                $.validationEngine.ajaxValidArray[ajaxErrorLength] =  new Array(2);
                $.validationEngine.ajaxValidArray[ajaxErrorLength][0] = fieldId;
                $.validationEngine.ajaxValidArray[ajaxErrorLength][1] = false;
                existInarray = false;
              }
        
              $.validationEngine.ajaxValid = false;
              promptText += $.validationEngine.settings.allrules[customAjaxRule].alertText+"<br />";
              $.validationEngine.updatePromptText(ajaxCaller,promptText,"",true);       
             }else{  
              _checkInArray(true);
              $.validationEngine.ajaxValid = true;               
              if($.validationEngine.settings.allrules[customAjaxRule].alertTextOk){ // NO OK TEXT MEAN CLOSE PROMPT       
                        $.validationEngine.updatePromptText(ajaxCaller,$.validationEngine.settings.allrules[customAjaxRule].alertTextOk,"pass",true);
              }else{
                ajaxValidate = false;     
                $.validationEngine.closePrompt(ajaxCaller);
              }   
             }
            function  _checkInArray(validate){
              for(x=0;x<ajaxErrorLength;x++){
                if($.validationEngine.ajaxValidArray[x][0] == fieldId){
                  $.validationEngine.ajaxValidArray[x][1] = validate;
                  existInarray = true;
                
                }
              }
            }
          }       
        });
      }
    }
    function _confirm(caller,rules,position){    // VALIDATE FIELD MATCH
      confirmField = rules[position+1];
      
      if($(caller).attr('value') != $("#"+confirmField).attr('value')){
        $.validationEngine.isError = true;
        promptText += $.validationEngine.settings.allrules["confirm"].alertText+"<br />";
      }
    }
    function _length(caller,rules,position){        // VALIDATE LENGTH
    
      startLength = eval(rules[position+1]);
      endLength = eval(rules[position+2]);
      feildLength = $(caller).attr('value').length;

      if(feildLength<startLength || feildLength>endLength){
        $.validationEngine.isError = true;
        promptText += $.validationEngine.settings.allrules["length"].alertText+startLength+$.validationEngine.settings.allrules["length"].alertText2+endLength+$.validationEngine.settings.allrules["length"].alertText3+"<br />"
      }
    }
    function _maxCheckbox(caller,rules,position){     // VALIDATE CHECKBOX NUMBER
    
      nbCheck = eval(rules[position+1]);
      groupname = $(caller).attr("name");
      groupSize = $("input[name='"+groupname+"']:checked").size();
      if(groupSize > nbCheck){  
        $.validationEngine.showTriangle = false;
        $.validationEngine.isError = true;
        promptText += $.validationEngine.settings.allrules["maxCheckbox"].alertText+"<br />";
      }
    }
    function _minCheckbox(caller,rules,position){     // VALIDATE CHECKBOX NUMBER
    
      nbCheck = eval(rules[position+1]);
      groupname = $(caller).attr("name");
      groupSize = $("input[name='"+groupname+"']:checked").size();
      if(groupSize < nbCheck){  
      
        $.validationEngine.isError = true;
        $.validationEngine.showTriangle = false;
        promptText += $.validationEngine.settings.allrules["minCheckbox"].alertText+" "+nbCheck+" "+$.validationEngine.settings.allrules["minCheckbox"].alertText2+"<br />";
      }
    }
    return($.validationEngine.isError) ? $.validationEngine.isError : false;
  },
  submitForm : function(caller){
    if($.validationEngine.settings.ajaxSubmit){   
      if($.validationEngine.settings.ajaxSubmitExtraData){
        extraData = $.validationEngine.settings.ajaxSubmitExtraData;
      }else{
        extraData = "";
      }
      $.ajax({
          type: "POST",
          url: $.validationEngine.settings.ajaxSubmitFile,
          async: true,
          data: $(caller).serialize()+"&"+extraData,
          error: function(data,transport){ $.validationEngine.debug("error in the ajax: "+data.status+" "+transport) },
          success: function(data){
            if(data == "true"){     // EVERYTING IS FINE, SHOW SUCCESS MESSAGE
              $(caller).css("opacity",1)
              $(caller).animate({opacity: 0, height: 0}, function(){
                $(caller).css("display","none");
                $(caller).before("<div class='ajaxSubmit'>"+$.validationEngine.settings.ajaxSubmitMessage+"</div>");
                $.validationEngine.closePrompt(".formError",true);  
                $(".ajaxSubmit").show("slow");
                if ($.validationEngine.settings.success){ // AJAX SUCCESS, STOP THE LOCATION UPDATE
                $.validationEngine.settings.success && $.validationEngine.settings.success(); 
                return false;
              }
              })
            }else{            // HOUSTON WE GOT A PROBLEM (SOMETING IS NOT VALIDATING)
              data = eval( "("+data+")"); 
              if(!data.jsonValidateReturn){
                 $.validationEngine.debug("you are not going into the success fonction and jsonValidateReturn return nothing");
              }
              errorNumber = data.jsonValidateReturn.length  
              for(index=0; index<errorNumber; index++){ 
                fieldId = data.jsonValidateReturn[index][0];
                promptError = data.jsonValidateReturn[index][1];
                type = data.jsonValidateReturn[index][2];
                $.validationEngine.buildPrompt(fieldId,promptError,type);
              }
            }
          }
      })  
      return true;
    }
    if ($.validationEngine.settings.success){ // AJAX SUCCESS, STOP THE LOCATION UPDATE
      if($.validationEngine.settings.unbindEngine){ $(caller).unbind("submit") }
      $.validationEngine.settings.success && $.validationEngine.settings.success(); 
      return true;
    }
    return false;
  },
  buildPrompt : function(caller,promptText,type,ajaxed) {     // ERROR PROMPT CREATION AND DISPLAY WHEN AN ERROR OCCUR
    if(!$.validationEngine.settings){
      $.validationEngine.defaultSetting()
    }
    var divFormError = document.createElement('div');
    var formErrorContent = document.createElement('div');
    
    linkTofield = $.validationEngine.linkTofield(caller)
    $(divFormError).addClass("formError")
    
    if(type == "pass"){ $(divFormError).addClass("greenPopup") }
    if(type == "load"){ $(divFormError).addClass("blackPopup") }
    if(ajaxed){ $(divFormError).addClass("ajaxed") }
    
    $(divFormError).addClass(linkTofield);
    $(formErrorContent).addClass("formErrorContent");
    
    $("body").append(divFormError);
    $(divFormError).append(formErrorContent);
      
    if($.validationEngine.showTriangle != false){   // NO TRIANGLE ON MAX CHECKBOX AND RADIO
      var arrow = document.createElement('div');
      $(arrow).addClass("formErrorArrow");
      $(divFormError).append(arrow);
      if($.validationEngine.settings.promptPosition == "bottomLeft" || $.validationEngine.settings.promptPosition == "bottomRight"){
      $(arrow).addClass("formErrorArrowBottom")
      $(arrow).html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
    }
      if($.validationEngine.settings.promptPosition == "topLeft" || $.validationEngine.settings.promptPosition == "topRight"){
        $(divFormError).append(arrow);
        $(arrow).html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
      }
    }
    $(formErrorContent).html(promptText)
  
    callerTopPosition = $(caller).offset().top;
    callerleftPosition = $(caller).offset().left;
    callerWidth =  $(caller).width();
    inputHeight = $(divFormError).height();
  
    /* POSITIONNING */
    if($.validationEngine.settings.promptPosition == "topRight"){callerleftPosition +=  callerWidth -30; callerTopPosition += -inputHeight -10; }
    if($.validationEngine.settings.promptPosition == "topLeft"){ callerTopPosition += -inputHeight -10; }
    
    if($.validationEngine.settings.promptPosition == "centerRight"){ callerleftPosition +=  callerWidth +13; }
    
    if($.validationEngine.settings.promptPosition == "bottomLeft"){
      callerHeight =  $(caller).height();
      callerleftPosition = callerleftPosition;
      callerTopPosition = callerTopPosition + callerHeight + 15;
    }
    if($.validationEngine.settings.promptPosition == "bottomRight"){
      callerHeight =  $(caller).height();
      callerleftPosition +=  callerWidth -30;
      callerTopPosition +=  callerHeight + 15;
    }
    $(divFormError).css({
      top:callerTopPosition,
      left:callerleftPosition,
      opacity:0
    })
    return $(divFormError).animate({"opacity":0.87},function(){return true;});  
  },
  updatePromptText : function(caller,promptText,type,ajaxed) {  // UPDATE TEXT ERROR IF AN ERROR IS ALREADY DISPLAYED
    
    linkTofield = $.validationEngine.linkTofield(caller);
    var updateThisPrompt =  "."+linkTofield;
    
    if(type == "pass") { $(updateThisPrompt).addClass("greenPopup") }else{ $(updateThisPrompt).removeClass("greenPopup")};
    if(type == "load") { $(updateThisPrompt).addClass("blackPopup") }else{ $(updateThisPrompt).removeClass("blackPopup")};
    if(ajaxed) { $(updateThisPrompt).addClass("ajaxed") }else{ $(updateThisPrompt).removeClass("ajaxed")};
  
    $(updateThisPrompt).find(".formErrorContent").html(promptText);
    callerTopPosition  = $(caller).offset().top;
    inputHeight = $(updateThisPrompt).height();
    
    if($.validationEngine.settings.promptPosition == "bottomLeft" || $.validationEngine.settings.promptPosition == "bottomRight"){
      callerHeight =  $(caller).height();
      callerTopPosition =  callerTopPosition + callerHeight + 15;
    }
    if($.validationEngine.settings.promptPosition == "centerRight"){  callerleftPosition +=  callerWidth +13;}
    if($.validationEngine.settings.promptPosition == "topLeft" || $.validationEngine.settings.promptPosition == "topRight"){
      callerTopPosition = callerTopPosition  -inputHeight -10;
    }
    $(updateThisPrompt).animate({ top:callerTopPosition });
  },
  linkTofield : function(caller){
    linkTofield = $(caller).attr("id") + "formError";
    linkTofield = linkTofield.replace(/\[/g,""); 
    linkTofield = linkTofield.replace(/\]/g,"");
    return linkTofield;
  },
  closePrompt : function(caller,outside) {            // CLOSE PROMPT WHEN ERROR CORRECTED
    if(!$.validationEngine.settings){
      $.validationEngine.defaultSetting()
    }
    if(outside){
      $(caller).fadeTo("fast",0,function(){
        $(caller).remove();
      });
      return false;
    }
    if(!ajaxValidate){
      linkTofield = $.validationEngine.linkTofield(caller);
      closingPrompt = "."+linkTofield;
      $(closingPrompt).fadeTo("fast",0,function(){
        $(closingPrompt).remove();
      });
    }
  },
  debug : function(error) {
    if(!$("#debugMode")[0]){
      $("body").append("<div id='debugMode'><div class='debugError'><strong>This is a debug mode, you got a problem with your form, it will try to help you, refresh when you think you nailed down the problem</strong></div></div>");
    }
    $(".debugError").append("<div class='debugerror'>"+error+"</div>");
  },      
  submitValidation : function(caller) {         // FORM SUBMIT VALIDATION LOOPING INLINE VALIDATION
    var stopForm = false;
    $.validationEngine.ajaxValid = true;
    $(caller).find(".formError").remove();
    var toValidateSize = $(caller).find("[class*=validate]").size();
    
    $(caller).find("[class*=validate]").each(function(){
      linkTofield = $.validationEngine.linkTofield(this);
      
      if(!$("."+linkTofield).hasClass("ajaxed")){ // DO NOT UPDATE ALREADY AJAXED FIELDS (only happen if no normal errors, don't worry)
        var validationPass = $.validationEngine.loadValidation(this);
        return(validationPass) ? stopForm = true : "";          
      };
    });
    ajaxErrorLength = $.validationEngine.ajaxValidArray.length;   // LOOK IF SOME AJAX IS NOT VALIDATE
    for(x=0;x<ajaxErrorLength;x++){
      if($.validationEngine.ajaxValidArray[x][1] == false){
        $.validationEngine.ajaxValid = false;
      }
    }
    if(stopForm || !$.validationEngine.ajaxValid){    // GET IF THERE IS AN ERROR OR NOT FROM THIS VALIDATION FUNCTIONS
      if($.validationEngine.settings.animateSubmit){
        destination = $(".formError:not('.greenPopup'):first").offset().top;
        $(".formError:not('.greenPopup')").each(function(){
          testDestination = $(this).offset().top;
          if(destination>testDestination){
            destination = $(this).offset().top;
          }
        })
        // $("html:not(:animated),body:not(:animated)").animate({ scrollTop: destination}, 1100);
      }
      return true;
    }else{
      return false;
    }
  }
}
})(jQuery);

(function($) {
  $.fn.validationEngineLanguage = function() {};
  $.validationEngineLanguage = {
    newLang: function() {
      $.validationEngineLanguage.allRules =   {"required":{         // Add your regex rules here, you can take telephone as an example
            "regex":"none",
            "alertText":"* This field is required",
            "alertTextCheckboxMultiple":"* Please select an option",
            "alertTextCheckboxe":"* This checkbox is required"},
          "length":{
            "regex":"none",
            "alertText":"*Between ",
            "alertText2":" and ",
            "alertText3": " characters allowed"},
          "maxCheckbox":{
            "regex":"none",
            "alertText":"* Checks allowed Exceeded"}, 
          "minCheckbox":{
            "regex":"none",
            "alertText":"* Please select ",
            "alertText2":" options"}, 
          "confirm":{
            "regex":"none",
            "alertText":"* Your field is not matching"},    
          "telephone":{
            "regex":"/^[0-9\-\(\)\ ]+$/",
            "alertText":"* Invalid phone number"},  
          "email":{
            "regex":"/^[a-zA-Z0-9_\.\-]+\@([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9]{2,4}$/",
            "alertText":"* Invalid email address"}, 
          "date":{
            "regex":"/^[0-9]{4}\-\[0-9]{1,2}\-\[0-9]{1,2}$/",
            "alertText":"* Invalid date, must be in YYYY-MM-DD format"},
          "onlyNumber":{
            "regex":"/^[0-9\ ]+$/",
            "alertText":"* Numbers only"},  
          "noSpecialCaracters":{
            "regex":"/^[0-9a-zA-Z]+$/",
            "alertText":"* No special caracters allowed"},  
          "ajaxUser":{
            "file":"validateUser.php",
            "extraData":"name=eric",
            "alertTextOk":"* This user is available", 
            "alertTextLoad":"* Loading, please wait",
            "alertText":"* This user is already taken"},  
          "ajaxName":{
            "file":"validateUser.php",
            "alertText":"* This name is already taken",
            "alertTextOk":"* This name is available", 
            "alertTextLoad":"* Loading, please wait"},    
          "onlyLetter":{
            "regex":"/^[a-zA-Z\ \']+$/",
            "alertText":"* Letters only"}
          } 
    }
  }
})(jQuery);

$(document).ready(function() {  
  $.validationEngineLanguage.newLang()
});

/*
 * jQuery Form Plugin
 * version: 2.36 (07-NOV-2009)
 * @requires jQuery v1.2.6 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function($) {

/*
  Usage Note:
  -----------
  Do not use both ajaxSubmit and ajaxForm on the same form.  These
  functions are intended to be exclusive.  Use ajaxSubmit if you want
  to bind your own submit handler to the form.  For example,

  $(document).ready(function() {
    $('#myForm').bind('submit', function() {
      $(this).ajaxSubmit({
        target: '#output'
      });
      return false; // <-- important!
    });
  });

  Use ajaxForm when you want the plugin to manage all the event binding
  for you.  For example,

  $(document).ready(function() {
    $('#myForm').ajaxForm({
      target: '#output'
    });
  });

  When using ajaxForm, the ajaxSubmit function will be invoked for you
  at the appropriate time.
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
  // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
  if (!this.length) {
    log('ajaxSubmit: skipping submit process - no element selected');
    return this;
  }

  if (typeof options == 'function')
    options = { success: options };

  var url = $.trim(this.attr('action'));
  if (url) {
    // clean url (don't include hash vaue)
    url = (url.match(/^([^#]+)/)||[])[1];
    }
    url = url || window.location.href || '';

  options = $.extend({
    url:  url,
    type: this.attr('method') || 'GET',
    iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
  }, options || {});

  // hook for manipulating the form data before it is extracted;
  // convenient for use with rich editors like tinyMCE or FCKEditor
  var veto = {};
  this.trigger('form-pre-serialize', [this, options, veto]);
  if (veto.veto) {
    log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
    return this;
  }

  // provide opportunity to alter form data before it is serialized
  if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
    log('ajaxSubmit: submit aborted via beforeSerialize callback');
    return this;
  }

  var a = this.formToArray(options.semantic);
  if (options.data) {
    options.extraData = options.data;
    for (var n in options.data) {
      if(options.data[n] instanceof Array) {
      for (var k in options.data[n])
        a.push( { name: n, value: options.data[n][k] } );
      }
      else
       a.push( { name: n, value: options.data[n] } );
    }
  }

  // give pre-submit callback an opportunity to abort the submit
  if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
    log('ajaxSubmit: submit aborted via beforeSubmit callback');
    return this;
  }

  // fire vetoable 'validate' event
  this.trigger('form-submit-validate', [a, this, options, veto]);
  if (veto.veto) {
    log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
    return this;
  }

  var q = $.param(a);

  if (options.type.toUpperCase() == 'GET') {
    options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
    options.data = null;  // data is null for 'get'
  }
  else
    options.data = q; // data is the query string for 'post'

  var $form = this, callbacks = [];
  if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
  if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

  // perform a load on the target only if dataType is not provided
  if (!options.dataType && options.target) {
    var oldSuccess = options.success || function(){};
    callbacks.push(function(data) {
      $(options.target).html(data).each(oldSuccess, arguments);
    });
  }
  else if (options.success)
    callbacks.push(options.success);

  options.success = function(data, status) {
    for (var i=0, max=callbacks.length; i < max; i++)
      callbacks[i].apply(options, [data, status, $form]);
  };

  // are there files to upload?
  var files = $('input:file', this).fieldValue();
  var found = false;
  for (var j=0; j < files.length; j++)
    if (files[j])
      found = true;

  var multipart = false;
//  var mp = 'multipart/form-data';
//  multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

  // options.iframe allows user to force iframe mode
  // 06-NOV-09: now defaulting to iframe mode if file input is detected
   if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
     // hack to fix Safari hang (thanks to Tim Molendijk for this)
     // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
     if (options.closeKeepAlive)
       $.get(options.closeKeepAlive, fileUpload);
     else
       fileUpload();
     }
   else
     $.ajax(options);

  // fire 'notify' event
  this.trigger('form-submit-notify', [this, options]);
  return this;


  // private function for handling file uploads (hat tip to YAHOO!)
  function fileUpload() {
    var form = $form[0];

    if ($(':input[name=submit]', form).length) {
      alert('Error: Form elements must not be named "submit".');
      return;
    }

    var opts = $.extend({}, $.ajaxSettings, options);
    var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

    var id = 'jqFormIO' + (new Date().getTime());
    var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" />');
    var io = $io[0];

    $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

    var xhr = { // mock object
      aborted: 0,
      responseText: null,
      responseXML: null,
      status: 0,
      statusText: 'n/a',
      getAllResponseHeaders: function() {},
      getResponseHeader: function() {},
      setRequestHeader: function() {},
      abort: function() {
        this.aborted = 1;
        $io.attr('src', opts.iframeSrc); // abort op in progress
      }
    };

    var g = opts.global;
    // trigger ajax global events so that activity/block indicators work like normal
    if (g && ! $.active++) $.event.trigger("ajaxStart");
    if (g) $.event.trigger("ajaxSend", [xhr, opts]);

    if (s.beforeSend && s.beforeSend(xhr, s) === false) {
      s.global && $.active--;
      return;
    }
    if (xhr.aborted)
      return;

    var cbInvoked = 0;
    var timedOut = 0;

    // add submitting element to data if we know it
    var sub = form.clk;
    if (sub) {
      var n = sub.name;
      if (n && !sub.disabled) {
        options.extraData = options.extraData || {};
        options.extraData[n] = sub.value;
        if (sub.type == "image") {
          options.extraData[name+'.x'] = form.clk_x;
          options.extraData[name+'.y'] = form.clk_y;
        }
      }
    }

    // take a breath so that pending repaints get some cpu time before the upload starts
    setTimeout(function() {
      // make sure form attrs are set
      var t = $form.attr('target'), a = $form.attr('action');

      // update form attrs in IE friendly way
      form.setAttribute('target',id);
      if (form.getAttribute('method') != 'POST')
        form.setAttribute('method', 'POST');
      if (form.getAttribute('action') != opts.url)
        form.setAttribute('action', opts.url);

      // ie borks in some cases when setting encoding
      if (! options.skipEncodingOverride) {
        $form.attr({
          encoding: 'multipart/form-data',
          enctype:  'multipart/form-data'
        });
      }

      // support timout
      if (opts.timeout)
        setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

      // add "extra" data to form if provided in options
      var extraInputs = [];
      try {
        if (options.extraData)
          for (var n in options.extraData)
            extraInputs.push(
              $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                .appendTo(form)[0]);

        // add iframe to doc and submit the form
        $io.appendTo('body');
        io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
        form.submit();
      }
      finally {
        // reset attrs and remove "extra" input elements
        form.setAttribute('action',a);
        t ? form.setAttribute('target', t) : $form.removeAttr('target');
        $(extraInputs).remove();
      }
    }, 10);

    var domCheckCount = 50;

    function cb() {
      if (cbInvoked++) return;

      io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

      var ok = true;
      try {
        if (timedOut) throw 'timeout';
        // extract the server response from the iframe
        var data, doc;

        doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
        
        var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
        log('isXml='+isXml);
        if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
          if (--domCheckCount) {
            // in some browsers (Opera) the iframe DOM is not always traversable when
            // the onload callback fires, so we loop a bit to accommodate
            cbInvoked = 0;
            setTimeout(cb, 100);
            return;
          }
          log('Could not access iframe DOM after 50 tries.');
          return;
        }

        xhr.responseText = doc.body ? doc.body.innerHTML : null;
        xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
        xhr.getResponseHeader = function(header){
          var headers = {'content-type': opts.dataType};
          return headers[header];
        };

        if (opts.dataType == 'json' || opts.dataType == 'script') {
          // see if user embedded response in textarea
          var ta = doc.getElementsByTagName('textarea')[0];
          if (ta)
            xhr.responseText = ta.value;
          else {
            // account for browsers injecting pre around json response
            var pre = doc.getElementsByTagName('pre')[0];
            if (pre)
              xhr.responseText = pre.innerHTML;
          }       
        }
        else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
          xhr.responseXML = toXml(xhr.responseText);
        }
        data = $.httpData(xhr, opts.dataType);
      }
      catch(e){
        ok = false;
        $.handleError(opts, xhr, 'error', e);
      }

      // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
      if (ok) {
        opts.success(data, 'success');
        if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
      }
      if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
      if (g && ! --$.active) $.event.trigger("ajaxStop");
      if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

      // clean up
      setTimeout(function() {
        $io.remove();
        xhr.responseXML = null;
      }, 100);
    };

    function toXml(s, doc) {
      if (window.ActiveXObject) {
        doc = new ActiveXObject('Microsoft.XMLDOM');
        doc.async = 'false';
        doc.loadXML(s);
      }
      else
        doc = (new DOMParser()).parseFromString(s, 'text/xml');
      return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
    };
  };
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *  is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *  used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
  return this.ajaxFormUnbind().bind('submit.form-plugin', function() {
    $(this).ajaxSubmit(options);
    return false;
  }).bind('click.form-plugin', function(e) {
    var target = e.target;
    var $el = $(target);
    if (!($el.is(":submit,input:image"))) {
      // is this a child element of the submit el?  (ex: a span within a button)
      var t = $el.closest(':submit');
      if (t.length == 0)
        return;
      target = t[0];
    }
    var form = this;
    form.clk = target;
    if (target.type == 'image') {
      if (e.offsetX != undefined) {
        form.clk_x = e.offsetX;
        form.clk_y = e.offsetY;
      } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
        var offset = $el.offset();
        form.clk_x = e.pageX - offset.left;
        form.clk_y = e.pageY - offset.top;
      } else {
        form.clk_x = e.pageX - target.offsetLeft;
        form.clk_y = e.pageY - target.offsetTop;
      }
    }
    // clear form vars
    setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
  });
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
  return this.unbind('submit.form-plugin click.form-plugin');
};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
  var a = [];
  if (this.length == 0) return a;

  var form = this[0];
  var els = semantic ? form.getElementsByTagName('*') : form.elements;
  if (!els) return a;
  for(var i=0, max=els.length; i < max; i++) {
    var el = els[i];
    var n = el.name;
    if (!n) continue;

    if (semantic && form.clk && el.type == "image") {
      // handle image inputs on the fly when semantic == true
      if(!el.disabled && form.clk == el) {
        a.push({name: n, value: $(el).val()});
        a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
      }
      continue;
    }

    var v = $.fieldValue(el, true);
    if (v && v.constructor == Array) {
      for(var j=0, jmax=v.length; j < jmax; j++)
        a.push({name: n, value: v[j]});
    }
    else if (v !== null && typeof v != 'undefined')
      a.push({name: n, value: v});
  }

  if (!semantic && form.clk) {
    // input type=='image' are not found in elements array! handle it here
    var $input = $(form.clk), input = $input[0], n = input.name;
    if (n && !input.disabled && input.type == 'image') {
      a.push({name: n, value: $input.val()});
      a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
    }
  }
  return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
  //hand off to jQuery.param for proper encoding
  return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
  var a = [];
  this.each(function() {
    var n = this.name;
    if (!n) return;
    var v = $.fieldValue(this, successful);
    if (v && v.constructor == Array) {
      for (var i=0,max=v.length; i < max; i++)
        a.push({name: n, value: v[i]});
    }
    else if (v !== null && typeof v != 'undefined')
      a.push({name: this.name, value: v});
  });
  //hand off to jQuery.param for proper encoding
  return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *    <input name="A" type="text" />
 *    <input name="A" type="text" />
 *    <input name="B" type="checkbox" value="B1" />
 *    <input name="B" type="checkbox" value="B2"/>
 *    <input name="C" type="radio" value="C1" />
 *    <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *     array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
  for (var val=[], i=0, max=this.length; i < max; i++) {
    var el = this[i];
    var v = $.fieldValue(el, successful);
    if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
      continue;
    v.constructor == Array ? $.merge(val, v) : val.push(v);
  }
  return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
  var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
  if (typeof successful == 'undefined') successful = true;

  if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
    (t == 'checkbox' || t == 'radio') && !el.checked ||
    (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
    tag == 'select' && el.selectedIndex == -1))
      return null;

  if (tag == 'select') {
    var index = el.selectedIndex;
    if (index < 0) return null;
    var a = [], ops = el.options;
    var one = (t == 'select-one');
    var max = (one ? index+1 : ops.length);
    for(var i=(one ? index : 0); i < max; i++) {
      var op = ops[i];
      if (op.selected) {
        var v = op.value;
        if (!v) // extra pain for IE...
          v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
        if (one) return v;
        a.push(v);
      }
    }
    return a;
  }
  return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
  return this.each(function() {
    $('input,select,textarea', this).clearFields();
  });
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
  return this.each(function() {
    var t = this.type, tag = this.tagName.toLowerCase();
    if (t == 'text' || t == 'password' || tag == 'textarea')
      this.value = '';
    else if (t == 'checkbox' || t == 'radio')
      this.checked = false;
    else if (tag == 'select')
      this.selectedIndex = -1;
  });
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
  return this.each(function() {
    // guard against an input with the name of 'reset'
    // note that IE reports the reset function as an 'object'
    if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
      this.reset();
  });
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
  if (b == undefined) b = true;
  return this.each(function() {
    this.disabled = !b;
  });
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
  if (select == undefined) select = true;
  return this.each(function() {
    var t = this.type;
    if (t == 'checkbox' || t == 'radio')
      this.checked = select;
    else if (this.tagName.toLowerCase() == 'option') {
      var $sel = $(this).parent('select');
      if (select && $sel[0] && $sel[0].type == 'select-one') {
        // deselect all other options
        $sel.find('option').selected(false);
      }
      this.selected = select;
    }
  });
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
  if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
    window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
};

})(jQuery);

/**
 * AJAX Upload ( http://valums.com/ajax-upload/ ) 
 * Copyright (c) Andris Valums
 * Licensed under the MIT license ( http://valums.com/mit-license/ )
 * Thanks to Gary Haran, David Mark, Corey Burns and others for contributions 
 */
(function () {
    /* global window */
    /* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
    
    /**
     * Wrapper for FireBug's console.log
     */
    function log(){
        if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){            
            Array.prototype.unshift.call(arguments, '[Ajax Upload]');
            console.log( Array.prototype.join.call(arguments, ' '));
        }
    } 

    /**
     * Attaches event to a dom element.
     * @param {Element} el
     * @param type event name
     * @param fn callback This refers to the passed element
     */
    function addEvent(el, type, fn){
        if (el.addEventListener) {
            el.addEventListener(type, fn, false);
        } else if (el.attachEvent) {
            el.attachEvent('on' + type, function(){
                fn.call(el);
	        });
	    } else {
            throw new Error('not supported or DOM not loaded');
        }
    }   
    
    /**
     * Attaches resize event to a window, limiting
     * number of event fired. Fires only when encounteres
     * delay of 100 after series of events.
     * 
     * Some browsers fire event multiple times when resizing
     * http://www.quirksmode.org/dom/events/resize.html
     * 
     * @param fn callback This refers to the passed element
     */
    function addResizeEvent(fn){
        var timeout;
               
	    addEvent(window, 'resize', function(){
            if (timeout){
                clearTimeout(timeout);
            }
            timeout = setTimeout(fn, 100);                        
        });
    }    
    
    // Needs more testing, will be rewriten for next version        
    // getOffset function copied from jQuery lib (http://jquery.com/)
    if (document.documentElement.getBoundingClientRect){
        // Get Offset using getBoundingClientRect
        // http://ejohn.org/blog/getboundingclientrect-is-awesome/
        var getOffset = function(el){
            var box = el.getBoundingClientRect();
            var doc = el.ownerDocument;
            var body = doc.body;
            var docElem = doc.documentElement; // for ie 
            var clientTop = docElem.clientTop || body.clientTop || 0;
            var clientLeft = docElem.clientLeft || body.clientLeft || 0;
             
            // In Internet Explorer 7 getBoundingClientRect property is treated as physical,
            // while others are logical. Make all logical, like in IE8.	
            var zoom = 1;            
            if (body.getBoundingClientRect) {
                var bound = body.getBoundingClientRect();
                zoom = (bound.right - bound.left) / body.clientWidth;
            }
            
            if (zoom > 1) {
                clientTop = 0;
                clientLeft = 0;
            }
            
            var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
            
            return {
                top: top,
                left: left
            };
        };        
    } else {
        // Get offset adding all offsets 
        var getOffset = function(el){
            var top = 0, left = 0;
            do {
                top += el.offsetTop || 0;
                left += el.offsetLeft || 0;
                el = el.offsetParent;
            } while (el);
            
            return {
                left: left,
                top: top
            };
        };
    }
    
    /**
     * Returns left, top, right and bottom properties describing the border-box,
     * in pixels, with the top-left relative to the body
     * @param {Element} el
     * @return {Object} Contains left, top, right,bottom
     */
    function getBox(el){
        var left, right, top, bottom;
        var offset = getOffset(el);
        left = offset.left;
        top = offset.top;
        
        right = left + el.offsetWidth;
        bottom = top + el.offsetHeight;
        
        return {
            left: left,
            right: right,
            top: top,
            bottom: bottom
        };
    }
    
    /**
     * Helper that takes object literal
     * and add all properties to element.style
     * @param {Element} el
     * @param {Object} styles
     */
    function addStyles(el, styles){
        for (var name in styles) {
            if (styles.hasOwnProperty(name)) {
                el.style[name] = styles[name];
            }
        }
    }
        
    /**
     * Function places an absolutely positioned
     * element on top of the specified element
     * copying position and dimentions.
     * @param {Element} from
     * @param {Element} to
     */    
    function copyLayout(from, to){
	    var box = getBox(from);
        
        addStyles(to, {
	        position: 'absolute',                    
	        left : box.left + 'px',
	        top : box.top + 'px',
	        width : from.offsetWidth + 'px',
	        height : from.offsetHeight + 'px'
	    });        
    }

    /**
    * Creates and returns element from html chunk
    * Uses innerHTML to create an element
    */
    var toElement = (function(){
        var div = document.createElement('div');
        return function(html){
            div.innerHTML = html;
            var el = div.firstChild;
            return div.removeChild(el);
        };
    })();
            
    /**
     * Function generates unique id
     * @return unique id 
     */
    var getUID = (function(){
        var id = 0;
        return function(){
            return 'ValumsAjaxUpload' + id++;
        };
    })();        
 
    /**
     * Get file name from path
     * @param {String} file path to file
     * @return filename
     */  
    function fileFromPath(file){
        return file.replace(/.*(\/|\\)/, "");
    }
    
    /**
     * Get file extension lowercase
     * @param {String} file name
     * @return file extenstion
     */    
    function getExt(file){
        return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
    }

    function hasClass(el, name){        
        var re = new RegExp('\\b' + name + '\\b');        
        return re.test(el.className);
    }    
    function addClass(el, name){
        if ( ! hasClass(el, name)){   
            el.className += ' ' + name;
        }
    }    
    function removeClass(el, name){
        var re = new RegExp('\\b' + name + '\\b');                
        el.className = el.className.replace(re, '');        
    }
    
    function removeNode(el){
        el.parentNode.removeChild(el);
    }

    /**
     * Easy styling and uploading
     * @constructor
     * @param button An element you want convert to 
     * upload button. Tested dimentions up to 500x500px
     * @param {Object} options See defaults below.
     */
    window.AjaxUpload = function(button, options){
        this._settings = {
            // Location of the server-side upload script
            action: 'upload.php',
            // File upload name
            name: 'userfile',
            // Additional data to send
            data: {},
            // Submit file as soon as it's selected
            autoSubmit: true,
            // The type of data that you're expecting back from the server.
            // html and xml are detected automatically.
            // Only useful when you are using json data as a response.
            // Set to "json" in that case. 
            responseType: false,
            // Class applied to button when mouse is hovered
            hoverClass: 'hover',
            // Class applied to button when AU is disabled
            disabledClass: 'disabled',            
            // When user selects a file, useful with autoSubmit disabled
            // You can return false to cancel upload			
            onChange: function(file, extension){
            },
            // Callback to fire before file is uploaded
            // You can return false to cancel upload
            onSubmit: function(file, extension){
            },
            // Fired when file upload is completed
            // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
            onComplete: function(file, response){
            }
        };
                        
        // Merge the users options with our defaults
        for (var i in options) {
            if (options.hasOwnProperty(i)){
                this._settings[i] = options[i];
            }
        }
                
        // button isn't necessary a dom element
        if (button.jquery){
            // jQuery object was passed
            button = button[0];
        } else if (typeof button == "string") {
            if (/^#.*/.test(button)){
                // If jQuery user passes #elementId don't break it					
                button = button.slice(1);                
            }
            
            button = document.getElementById(button);
        }
        
        if ( ! button || button.nodeType !== 1){
            throw new Error("Please make sure that you're passing a valid element"); 
        }
                
        if ( button.nodeName.toUpperCase() == 'A'){
            // disable link                       
            addEvent(button, 'click', function(e){
                if (e && e.preventDefault){
                    e.preventDefault();
                } else if (window.event){
                    window.event.returnValue = false;
                }
            });
        }
                    
        // DOM element
        this._button = button;        
        // DOM element                 
        this._input = null;
        // If disabled clicking on button won't do anything
        this._disabled = false;
        
        // if the button was disabled before refresh if will remain
        // disabled in FireFox, let's fix it
        this.enable();        
        
        this._rerouteClicks();
    };
    
    // assigning methods to our class
    AjaxUpload.prototype = {
        setData: function(data){
            this._settings.data = data;
        },
        disable: function(){            
            addClass(this._button, this._settings.disabledClass);
            this._disabled = true;
            
            var nodeName = this._button.nodeName.toUpperCase();            
            if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
                this._button.setAttribute('disabled', 'disabled');
            }            
            
            // hide input
            if (this._input){
                // We use visibility instead of display to fix problem with Safari 4
                // The problem is that the value of input doesn't change if it 
                // has display none when user selects a file           
                this._input.parentNode.style.visibility = 'hidden';
            }
        },
        enable: function(){
            removeClass(this._button, this._settings.disabledClass);
            this._button.removeAttribute('disabled');
            this._disabled = false;
            
        },
        /**
         * Creates invisible file input 
         * that will hover above the button
         * <div><input type='file' /></div>
         */
        _createInput: function(){ 
            var self = this;
                        
            var input = document.createElement("input");
            input.setAttribute('type', 'file');
            input.setAttribute('name', this._settings.name);
            
            addStyles(input, {
                'position' : 'absolute',
                // in Opera only 'browse' button
                // is clickable and it is located at
                // the right side of the input
                'right' : 0,
                'margin' : 0,
                'padding' : 0,
                'fontSize' : '480px',                
                'cursor' : 'pointer'
            });            

            var div = document.createElement("div");                        
            addStyles(div, {
                'display' : 'block',
                'position' : 'absolute',
                'overflow' : 'hidden',
                'margin' : 0,
                'padding' : 0,                
                'opacity' : 0,
                // Make sure browse button is in the right side
                // in Internet Explorer
                'direction' : 'ltr',
                //Max zIndex supported by Opera 9.0-9.2
                'zIndex': 2147483583,
                'cursor' : 'pointer'
            });
            
            // Make sure that element opacity exists.
            // Otherwise use IE filter            
            if ( div.style.opacity !== "0") {
                if (typeof(div.filters) == 'undefined'){
                    throw new Error('Opacity not supported by the browser');
                }
                div.style.filter = "alpha(opacity=0)";
            }            
            
            addEvent(input, 'change', function(){
                 
                if ( ! input || input.value === ''){                
                    return;                
                }
                            
                // Get filename from input, required                
                // as some browsers have path instead of it          
                var file = fileFromPath(input.value);
                                
                if (false === self._settings.onChange.call(self, file, getExt(file))){
                    self._clearInput();                
                    return;
                }
                
                // Submit form when value is changed
                if (self._settings.autoSubmit) {
                    self.submit();
                }
            });            

            addEvent(input, 'mouseover', function(){
                addClass(self._button, self._settings.hoverClass);
            });
            
            addEvent(input, 'mouseout', function(){
                removeClass(self._button, self._settings.hoverClass);
                
                // We use visibility instead of display to fix problem with Safari 4
                // The problem is that the value of input doesn't change if it 
                // has display none when user selects a file           
                input.parentNode.style.visibility = 'hidden';

            });   
                        
	        div.appendChild(input);
            document.body.appendChild(div);
              
            this._input = input;
        },
        _clearInput : function(){
            if (!this._input){
                return;
            }            
                             
            // this._input.value = ''; Doesn't work in IE6                               
            removeNode(this._input.parentNode);
            this._input = null;                                                                   
            this._createInput();
            
            removeClass(this._button, this._settings.hoverClass);
        },
        /**
         * Function makes sure that when user clicks upload button,
         * the this._input is clicked instead
         */
        _rerouteClicks: function(){
            var self = this;
            
            // IE will later display 'access denied' error
            // if you use using self._input.click()
            // other browsers just ignore click()

            addEvent(self._button, 'mouseover', function(){
                if (self._disabled){
                    return;
                }
                                
                if ( ! self._input){
	                self._createInput();
                }
                
                var div = self._input.parentNode;                            
                copyLayout(self._button, div);
                div.style.visibility = 'visible';
                                
            });
            
            
            // commented because we now hide input on mouseleave
            /**
             * When the window is resized the elements 
             * can be misaligned if button position depends
             * on window size
             */
            //addResizeEvent(function(){
            //    if (self._input){
            //        copyLayout(self._button, self._input.parentNode);
            //    }
            //});            
                                         
        },
        /**
         * Creates iframe with unique name
         * @return {Element} iframe
         */
        _createIframe: function(){
            // We can't use getTime, because it sometimes return
            // same value in safari :(
            var id = getUID();            
             
            // We can't use following code as the name attribute
            // won't be properly registered in IE6, and new window
            // on form submit will open
            // var iframe = document.createElement('iframe');
            // iframe.setAttribute('name', id);                        
 
            var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
            // src="javascript:false; was added
            // because it possibly removes ie6 prompt 
            // "This page contains both secure and nonsecure items"
            // Anyway, it doesn't do any harm.            
            iframe.setAttribute('id', id);
            
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
            
            return iframe;
        },
        /**
         * Creates form, that will be submitted to iframe
         * @param {Element} iframe Where to submit
         * @return {Element} form
         */
        _createForm: function(iframe){
            var settings = this._settings;
                        
            // We can't use the following code in IE6
            // var form = document.createElement('form');
            // form.setAttribute('method', 'post');
            // form.setAttribute('enctype', 'multipart/form-data');
            // Because in this case file won't be attached to request                    
            var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
                        
            form.setAttribute('action', settings.action);
            form.setAttribute('target', iframe.name);                                   
            form.style.display = 'none';
            document.body.appendChild(form);
            
            // Create hidden input element for each data key
            for (var prop in settings.data) {
                if (settings.data.hasOwnProperty(prop)){
                    var el = document.createElement("input");
                    el.setAttribute('type', 'hidden');
                    el.setAttribute('name', prop);
                    el.setAttribute('value', settings.data[prop]);
                    form.appendChild(el);
                }
            }
            return form;
        },
        /**
         * Gets response from iframe and fires onComplete event when ready
         * @param iframe
         * @param file Filename to use in onComplete callback 
         */
        _getResponse : function(iframe, file){            
            // getting response
            var toDeleteFlag = false, self = this, settings = this._settings;   
               
            addEvent(iframe, 'load', function(){                
                
                if (// For Safari 
                    iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
                    // For FF, IE
                    iframe.src == "javascript:'<html></html>';"){                                                                        
                        // First time around, do not delete.
                        // We reload to blank page, so that reloading main page
                        // does not re-submit the post.
                        
                        if (toDeleteFlag) {
                            // Fix busy state in FF3
                            setTimeout(function(){
                                removeNode(iframe);
                            }, 0);
                        }
                                                
                        return;
                }
                
                var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
                
                // fixing Opera 9.26,10.00
                if (doc.readyState && doc.readyState != 'complete') {
                   // Opera fires load event multiple times
                   // Even when the DOM is not ready yet
                   // this fix should not affect other browsers
                   return;
                }
                
                // fixing Opera 9.64
                if (doc.body && doc.body.innerHTML == "false") {
                    // In Opera 9.64 event was fired second time
                    // when body.innerHTML changed from false 
                    // to server response approx. after 1 sec
                    return;
                }
                
                var response;
                
                if (doc.XMLDocument) {
                    // response is a xml document Internet Explorer property
                    response = doc.XMLDocument;
                } else if (doc.body){
                    // response is html document or plain text
                    response = doc.body.innerHTML;
                    
                    if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
                        // If the document was sent as 'application/javascript' or
                        // 'text/javascript', then the browser wraps the text in a <pre>
                        // tag and performs html encoding on the contents.  In this case,
                        // we need to pull the original text content from the text node's
                        // nodeValue property to retrieve the unmangled content.
                        // Note that IE6 only understands text/html
                        if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
                            response = doc.body.firstChild.firstChild.nodeValue;
                        }
                        
                        if (response) {
                            response = eval("(" + response + ")");
                        } else {
                            response = {};
                        }
                    }
                } else {
                    // response is a xml document
                    response = doc;
                }
                
                settings.onComplete.call(self, file, response);
                
                // Reload blank page, so that reloading main page
                // does not re-submit the post. Also, remember to
                // delete the frame
                toDeleteFlag = true;
                
                // Fix IE mixed content issue
                iframe.src = "javascript:'<html></html>';";
            });            
        },        
        /**
         * Upload file contained in this._input
         */
        submit: function(){                        
            var self = this, settings = this._settings;
            
            if ( ! this._input || this._input.value === ''){                
                return;                
            }
                                    
            var file = fileFromPath(this._input.value);
            
            // user returned false to cancel upload
            if (false === settings.onSubmit.call(this, file, getExt(file))){
                this._clearInput();                
                return;
            }
            
            // sending request    
            var iframe = this._createIframe();
            var form = this._createForm(iframe);
            
            // assuming following structure
            // div -> input type='file'
            removeNode(this._input.parentNode);            
            removeClass(self._button, self._settings.hoverClass);
                        
            form.appendChild(this._input);
                        
            form.submit();

            // request set, clean up                
            removeNode(form); form = null;                          
            removeNode(this._input); this._input = null;
            
            // Get response from iframe and fire onComplete event when ready
            this._getResponse(iframe, file);            

            // get ready for next request            
            this._createInput();
        }
    };
})(); 



(function($){$.toJSON=function(o)
{if(typeof(JSON)=='object'&&JSON.stringify)
return JSON.stringify(o);var type=typeof(o);if(o===null)
return"null";if(type=="undefined")
return undefined;if(type=="number"||type=="boolean")
return o+"";if(type=="string")
return $.quoteString(o);if(type=='object')
{if(typeof o.toJSON=="function")
return $.toJSON(o.toJSON());if(o.constructor===Date)
{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+
hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
if(o.constructor===Array)
{var ret=[];for(var i=0;i<o.length;i++)
ret.push($.toJSON(o[i])||"null");return"["+ret.join(",")+"]";}
var pairs=[];for(var k in o){var name;var type=typeof k;if(type=="number")
name='"'+k+'"';else if(type=="string")
name=$.quoteString(k);else
continue;if(typeof o[k]=="function")
continue;var val=$.toJSON(o[k]);pairs.push(name+":"+val);}
return"{"+pairs.join(", ")+"}";}};$.evalJSON=function(src)
{if(typeof(JSON)=='object'&&JSON.parse)
return JSON.parse(src);return eval("("+src+")");};$.secureEvalJSON=function(src)
{if(typeof(JSON)=='object'&&JSON.parse)
return JSON.parse(src);var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))
return eval("("+src+")");else
throw new SyntaxError("Error parsing JSON, source is not valid.");};$.quoteString=function(string)
{if(string.match(_escapeable))
{return'"'+string.replace(_escapeable,function(a)
{var c=_meta[a];if(typeof c==='string')return c;c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
return'"'+string+'"';};var _escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var _meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};})(jQuery);

/*
 * jquery.tools 1.1.2 - The missing UI library for the Web
 * 
 * [tools.scrollable-1.1.2, tools.scrollable.circular-0.5.1]
 * 
 * Copyright (c) 2009 Tero Piirainen
 * http://flowplayer.org/tools/
 *
 * Dual licensed under MIT and GPL 2+ licenses
 * http://www.opensource.org/licenses
 * 
 * -----
 * 
 * File generated: Mon Feb 08 07:59:28 GMT 2010
 */
(function(b){b.tools=b.tools||{};b.tools.scrollable={version:"1.1.2",conf:{size:5,vertical:false,speed:400,keyboard:true,keyboardSteps:null,disabledClass:"disabled",hoverClass:null,clickable:true,activeClass:"active",easing:"swing",loop:false,items:".items",item:null,prev:".prev",next:".next",prevPage:".prevPage",nextPage:".nextPage",api:false}};var c;function a(o,m){var r=this,p=b(this),d=!m.vertical,e=o.children(),k=0,i;if(!c){c=r}b.each(m,function(s,t){if(b.isFunction(t)){p.bind(s,t)}});if(e.length>1){e=b(m.items,o)}function l(t){var s=b(t);return m.globalNav?s:o.parent().find(t)}o.data("finder",l);var f=l(m.prev),h=l(m.next),g=l(m.prevPage),n=l(m.nextPage);b.extend(r,{getIndex:function(){return k},getClickIndex:function(){var s=r.getItems();return s.index(s.filter("."+m.activeClass))},getConf:function(){return m},getSize:function(){return r.getItems().size()},getPageAmount:function(){return Math.ceil(this.getSize()/m.size)},getPageIndex:function(){return Math.ceil(k/m.size)},getNaviButtons:function(){return f.add(h).add(g).add(n)},getRoot:function(){return o},getItemWrap:function(){return e},getItems:function(){return e.children(m.item)},getVisibleItems:function(){return r.getItems().slice(k,k+m.size)},seekTo:function(s,w,t){if(s<0){s=0}if(k===s){return r}if(b.isFunction(w)){t=w}if(s>r.getSize()-m.size){return m.loop?r.begin():this.end()}var u=r.getItems().eq(s);if(!u.length){return r}var v=b.Event("onBeforeSeek");p.trigger(v,[s]);if(v.isDefaultPrevented()){return r}if(w===undefined||b.isFunction(w)){w=m.speed}function x(){if(t){t.call(r,s)}p.trigger("onSeek",[s])}if(d){e.animate({left:-u.position().left},w,m.easing,x)}else{e.animate({top:-u.position().top},w,m.easing,x)}c=r;k=s;v=b.Event("onStart");p.trigger(v,[s]);if(v.isDefaultPrevented()){return r}f.add(g).toggleClass(m.disabledClass,s===0);h.add(n).toggleClass(m.disabledClass,s>=r.getSize()-m.size);return r},move:function(u,t,s){i=u>0;return this.seekTo(k+u,t,s)},next:function(t,s){return this.move(1,t,s)},prev:function(t,s){return this.move(-1,t,s)},movePage:function(w,v,u){i=w>0;var s=m.size*w;var t=k%m.size;if(t>0){s+=(w>0?-t:m.size-t)}return this.move(s,v,u)},prevPage:function(t,s){return this.movePage(-1,t,s)},nextPage:function(t,s){return this.movePage(1,t,s)},setPage:function(t,u,s){return this.seekTo(t*m.size,u,s)},begin:function(t,s){i=false;return this.seekTo(0,t,s)},end:function(t,s){i=true;var u=this.getSize()-m.size;return u>0?this.seekTo(u,t,s):r},reload:function(){p.trigger("onReload");return r},focus:function(){c=r;return r},click:function(u){var v=r.getItems().eq(u),s=m.activeClass,t=m.size;if(u<0||u>=r.getSize()){return r}if(t==1){if(m.loop){return r.next()}if(u===0||u==r.getSize()-1){i=(i===undefined)?true:!i}return i===false?r.prev():r.next()}if(t==2){if(u==k){u--}r.getItems().removeClass(s);v.addClass(s);return r.seekTo(u,time,fn)}if(!v.hasClass(s)){r.getItems().removeClass(s);v.addClass(s);var x=Math.floor(t/2);var w=u-x;if(w>r.getSize()-t){w=r.getSize()-t}if(w!==u){return r.seekTo(w)}}return r},bind:function(s,t){p.bind(s,t);return r},unbind:function(s){p.unbind(s);return r}});b.each("onBeforeSeek,onStart,onSeek,onReload".split(","),function(s,t){r[t]=function(u){return r.bind(t,u)}});f.addClass(m.disabledClass).click(function(){r.prev()});h.click(function(){r.next()});n.click(function(){r.nextPage()});if(r.getSize()<m.size){h.add(n).addClass(m.disabledClass)}g.addClass(m.disabledClass).click(function(){r.prevPage()});var j=m.hoverClass,q="keydown."+Math.random().toString().substring(10);r.onReload(function(){if(j){r.getItems().hover(function(){b(this).addClass(j)},function(){b(this).removeClass(j)})}if(m.clickable){r.getItems().each(function(s){b(this).unbind("click.scrollable").bind("click.scrollable",function(t){if(b(t.target).is("a")){return}return r.click(s)})})}if(m.keyboard){b(document).unbind(q).bind(q,function(t){if(t.altKey||t.ctrlKey){return}if(m.keyboard!="static"&&c!=r){return}var u=m.keyboardSteps;if(d&&(t.keyCode==37||t.keyCode==39)){r.move(t.keyCode==37?-u:u);return t.preventDefault()}if(!d&&(t.keyCode==38||t.keyCode==40)){r.move(t.keyCode==38?-u:u);return t.preventDefault()}return true})}else{b(document).unbind(q)}});r.reload()}b.fn.scrollable=function(d){var e=this.eq(typeof d=="number"?d:0).data("scrollable");if(e){return e}var f=b.extend({},b.tools.scrollable.conf);d=b.extend(f,d);d.keyboardSteps=d.keyboardSteps||d.size;this.each(function(){e=new a(b(this),d);b(this).data("scrollable",e)});return d.api?e:this}})(jQuery);
(function(b){var a=b.tools.scrollable;a.plugins=a.plugins||{};a.plugins.circular={version:"0.5.1",conf:{api:false,clonedClass:"cloned"}};b.fn.circular=function(e){var d=b.extend({},a.plugins.circular.conf),c;b.extend(d,e);this.each(function(){var i=b(this).scrollable(),n=i.getItems(),k=i.getConf(),f=i.getItemWrap(),j=0;if(i){c=i}if(n.length<k.size){return false}n.slice(0,k.size).each(function(o){b(this).clone().appendTo(f).click(function(){i.click(n.length+o)}).addClass(d.clonedClass)});var l=b.makeArray(n.slice(-k.size)).reverse();b(l).each(function(o){b(this).clone().prependTo(f).click(function(){i.click(-o-1)}).addClass(d.clonedClass)});var m=f.children(k.item);var h=k.hoverClass;if(h){m.hover(function(){b(this).addClass(h)},function(){b(this).removeClass(h)})}function g(o){var p=m.eq(o);if(k.vertical){f.css({top:-p.position().top})}else{f.css({left:-p.position().left})}}g(k.size);b.extend(i,{move:function(s,r,p,q){var u=j+s+k.size;var t=u>i.getSize()-k.size;if(u<=0||t){var o=j+k.size+(t?-n.length:n.length);g(o);u=o+s}if(q){m.removeClass(k.activeClass).eq(u+Math.floor(k.size/2)).addClass(k.activeClass)}if(u===j+k.size){return self}return i.seekTo(u,r,p)},begin:function(p,o){return this.seekTo(k.size,p,o)},end:function(p,o){return this.seekTo(n.length,p,o)},click:function(p,r,q){if(!k.clickable){return self}if(k.size==1){return this.next()}var s=p-j,o=k.activeClass;s-=Math.floor(k.size/2);return this.move(s,r,q,true)},getIndex:function(){return j},setPage:function(p,q,o){return this.seekTo(p*k.size+k.size,q,o)},getPageAmount:function(){return Math.ceil(n.length/k.size)},getPageIndex:function(){if(j<0){return this.getPageAmount()-1}if(j>=n.length){return 0}var o=(j+k.size)/k.size-1;return o},getVisibleItems:function(){var o=j+k.size;return m.slice(o,o+k.size)}});i.onStart(function(p,o){j=o-k.size;return false});i.getNaviButtons().removeClass(k.disabledClass)});return d.api?c:this}})(jQuery);
