/**
 * MoCHa - Modular Contact Handler 
 * Javascript Gear
 * 
 * "Viens-la', viens avec moi, ne pars pas sans moi"
 * 
 * @author Marco "DWJ" Solazzi
 * @projectDescription Javascript functions collection
 * @copyright Copyright (c) 2008 Marco Solazzi
 * @license Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * @requires Bicycle 1.5.x
 * @requires Base.js 1.1
 * @version 0.9a
 */
$b.extend({
	toggleClass : function (className) {
		if (this.hasClass(className)) {
			this.removeClass(className);
		} else {
			this.addClass(className);
		}
		return this;
	},	
	mAttr : function (attr,join) {
		var attrArray = [];
		this.each(function(el) {
			var elAttr = $b(el).attr(attr);
			if ( elAttr != "" || elAttr != null) {
				attrArray.checkAgainst(elAttr);
			}
		});
		return $type(join) ? attrArray.join(join) : attrArray;  
	}
});

extendObject(String.prototype,{
	format : function (meth,replace) {
				switch (meth) {
					case "uc":
						return this.toUpperCase();
					break;
					case "lc":
						return this.toLowerCase();
					break;
					case "sc":
					case "wc":
						var _meth = meth;
						var sep = _meth == "sc" ? "." : " ";
						var newString ="";
						var paragraphs = this.split(sep);
						paragraphs.map(function (str) {
							if (str.trim() != "") {
								firstChar = str.substring(0, 1).toUpperCase();
								remainChar = str.substring(1).toLowerCase();
								newString += firstChar + remainChar + sep + " ";
							}
						});
						return newString.trim();
					break;
					default:
						return this.replace(meth,replace);
					break;
				}
	},
	
	clean: function(){
		return this.replace(/\s+/g, ' ').trim();
	}
});

mocha = {
	components : {
		core : "0.9a"
	},
	release : false
};

/**
 * Here starts MoCHa class
 */
var mochajs = Base.extend({
 
  formObj : null,
  validates : [],
  log : "",
  advcheckslog : "",
  textInput : ["text","select-one","textarea"],
  textFields : ["text","textarea"],
  lang : {},
  
  constructor: function(id,obj) {
	this.extend({
		errors : function () {
			return new Array();
		}(),
		adverrors : function () {
			return new Array();
		}(),
      options: function() {
        return $merge({
			checklist : {},
			formparser : null,
			trigger : false,
			requiredKey : "required",
			UImode : "default",
			UIcallback : false,
			error : mochaSetup.hcolor,
			lang : mochaSetup.setLang
		  },(obj || {}));
      }()
    });
	this.options.formId = id;
	this.options.method = "post";
	/**
	 * start here
	 */
	var _this = this;
	$b.ready(function (){
		
		// init the form object
		var _form = $b.getForm(_this.options.formId);
		// init the language in use
		_this.lang = mochaSetup.lang[_this.options.lang];
		// block form submission
		_form.preventSubmit();
		// Get the submit trigger: is a given element by ID or the first submit input in the form
		// then register the error check routine to the click event 
		var starter = _this.options.trigger ? "#"+_this.options.trigger : _form.buttons.submits[0]; 
		$b(starter).on("click",function () {
			if ($type(_this.options.checklist) == "object") {
				_this.errorCheck();
			} else if (_this.options.checklist == "remote") {
				var loadError = _this.lang.processError+(_this.log.length > 0 ? ":\n\n"+_this.log : "");
				alert(loadError);
				return false;
			}
		});
	});
  },
  
  setLang : function (langId) {
  	this.lang = mochaSetup.lang[langId];
  },
  
  getLangString : function (key,strings,opts) {
  	var langStr = this.lang[key];
	if ($type(opts)) {
		var temp = langStr.match(/({)/).length;
		for (i=0; i< temp; i++) {
			var langVar = langStr.match(/{.+?}/)[0];
			var langVarArray = langVar.slice(1,langVar.length-1).split("|");
			var subStr = $defined(opts[i]) ? langVarArray[opts[i]] : "";		
			langStr = langStr.replace(/{.+?}/,subStr);
		}
		langStr += " ";
	}
  	if($type(strings) == "array") {
		strings.map(function (el,i) {
			langStr = langStr.replace(new RegExp("\\%"+i.toString()),el.toString());
		});
		return langStr.trim();
	} else {	
		return langStr.replace(/\%0/g,strings.toString()).trim();
	}
  },
  
  setMethod : function (method,callback) {
  	this.options.ajaxCallback = (callback || false);
	this.options.method = method;
  },
  
  loadChecklist : function (url) {
  	var _this = this;
	var myRequest = new ajaxObject(url);
	myRequest.callback = function(responseText, responseStatus) {
		if (responseStatus==200) {
			_this.options.checklist = $defined(window.JSON) ? JSON.parse(responseText) : eval("("+responseText+")");
		} else {
			_this.log += _this.lang.noCheckListLoaded;
		}
	}
	$b.ready(function(){
		myRequest.update("","POST");
	});
  },
  
  getFormObj : function () {
  	return $b.getForm(this.options.formId);
  },
  getFields : function (name) {
  	if (name.test(",")) {
		var fields = [];
		var fieldsArray = name.split(",");
		var _this = this;
		fieldsArray.map(function (el) {
			fields.merge($b.getForm(_this.options.formId).field(el).fields);
		});
		
		return fields;
		
	} else {
		return $b.getForm(this.options.formId).field(name).fields;
	}
  },
  getFieldValue : function (name) {
  	return $b.getForm(this.options.formId).field(name).value();
  },
  getFieldLabel : function (field) {
  	var id = $b(field).attr("id");
	var searchFor = ( id != null &&  id != "" ) ? id : field.name;
	var label = $b("#"+this.options.formId).get("label",{'for':searchFor});
	if (label.length() > 0 ) {
		return label.txt().clean();
	} else {
		var newLabel = field.parentNode;
    	label = newLabel.nodeName == "LABEL" ? $b(newLabel) : false; 
    	return label ? label.txt().clean() : (searchFor.charAt(0).toUpperCase() + searchFor.substr(1, searchFor.length-1));
	}
  },
  errorCheck : function () {
  	// reset var and elements array
	this.errors = new Array();
	this.adverrors = new Array();
	this.validates = new Array();
	this.advcheckslog = "";
	this.log = "";
  	
	var _this = this;
  	var _form = this.getFormObj();
	
	/**
	 * basic required fields check
	 */
	_form.fields.map(function (el) {
		if ($b(el).hasClass(_this.options.requiredKey) && !_this.getFieldValue(el.name)) {
			if (el.type == "checkbox") {
				_this.adverrors.checkAgainst(el);
				_this.advcheckslog += _this.lang.singleCheckboxIntro+" "+_this.getFieldLabel(el)+"\n";
			} else {
				_this.errors.checkAgainst(el);
			}
		} else {
			_this.validates.checkAgainst(el);
		}
	});
	
	/**
	 * advanced checks
	 */
	for (check in this.options.checklist) {
		var field = this.getFields(check);
		// to be sure we are passing an array
		//field = isArray(field) ? field : [field];
		var rules = this.options.checklist[check];
		rules = isArray(rules) ? rules : [rules];
		/*rules.map(function (el) {
			if (el.test(/\|/)) {
				var elArgs = el.split("|");
				elArgs[1].split(",").map(function (el) {
					field.push(eval(el));	
				});
				_this[elArgs[0]].apply( _this,field );	
			} else {
				_this[el].apply(_this,field);
			}
		});*/
		field.map(function (f) {
			var pass = [f];
			rules.map(function (r) {
				if (r.test(/\[.*\]/)) {
					//eval("chars[2,'prova',[2,8],/[a-z]/]".slice("chars[2,'prova',[2,8],/[a-z]/]".split("[",1)[0].length));
					var rFunc = r.split("[",1)[0];
					var rArgs = eval(r.slice(rFunc.length));
					_this[rFunc].apply( _this,pass.merge(rArgs) );	
				} else {
					_this[r].apply(_this,pass);
				}
			});
		});
	}
	
	if ( (this.errors.length + this.adverrors.length) > 0) {
		this.errorUI(this.options.UImode);
	} else {
		
		switch (this.options.method) {
			case "post":
			default:
				_form.f.action = this.options.formparser;
				_form.f.submit();
			break;
			case "ajax":
				var callback = $type(this.options.ajaxCallback) == "function" ? this.options.ajaxCallback : function () {}; 
				_form.send(this.options.formparser,callback);
			break;
		}
	}
  },
  colorize : function () {
	if (this.options.error.charAt(0) == ".") {
		$b(this.validates).removeClass(this.options.error.substr(1));
		$b(this.errors, this.adverrors).addClass(this.options.error.substr(1));
	} else {
		$b(this.validates).css("background-color", "");
		$b(this.errors, this.adverrors).css("background-color", this.options.error);
	}
  },
  buildLog : function (mode) {
  	var mode = mode || "default";
	var log = "";
	switch (mode) {
		case "default":
			log += this.lang.defaultAlert;
				if (this.adverrors.length > 0) {
					log += "\n-----------------------------------\n";
					log += this.advcheckslog;
				}
		break;
		case "verbose":
			var _this = this;
			log += this.lang.verboseIntro+"\n";
			log += "-----------------------------------\n";
			$b(this.errors).each(function(el){
					log += _this.getFieldLabel(el)+" "+_this.lang.verboseEmpty+"\n";
				});
			log += this.advcheckslog;
		break;
	}
	return log;
  },
  errorUI : function (mode) {
  	switch (mode) {
		case "default":
		default:
			this.colorize();
			this.log = this.buildLog("default");
			alert(this.log);
		break;
		case "single":
			// get the first error
			var firstError = $b(this.errors,this.adverrors).item(0,true);
			alert(this.getLangString("singleError",this.getFieldLabel(firstError)));
			firstError.focus();
		break;
		case "verbose":
			this.colorize();
			this.log = this.buildLog("verbose");
			alert(this.log);
		break;
		case "custom":
		  	if ($type(this.options.UIcallback) == "function") {
				this.options.UIcallback.apply(this, [{
					valids: this.validates,
					errors: this.errors,
					adverrors: this.adverrors,
					logs: [this.log, this.advcheckslog]
				}]);
			}
		break;
	}
  },
  
  /**
   * Check for required fields (compatibility with php imported checklists)
   */
  required : function (field) {
  	if (!this.errors.inArray(field)) {
		if (!this.getFieldValue(field.name)) {
			this.errors.push(field);
		}
	}
  },
  
  /**
   * Extended Checks on Fields
   */
  email : function  (field) {
	var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-]{2,})+\.)+([a-zA-Z0-9]{2,})+$/;
	var xValue = this.getFieldValue(field.name);
	if (xValue && !xValue.test(filter)) {
		this.adverrors.checkAgainst (field);
		this.advcheckslog += this.getFieldLabel(field)+" "+this.lang.validEmail+"\n";
	}
  },
  checkbox : function  () {
		 var cbArray = [];
		 for (i=0;i<arguments.length-1;i++) {
		 	cbArray.push(arguments[i]);
		 }
		 var min = arguments[arguments.length-1];
		 var min = min=='all' ? min = cbArray.length : parseInt(arguments[arguments.length-1]);
         var checkErrors = 0;
         $b(cbArray).each(function (el) {
                     if (el.checked == false) {
                        checkErrors++;
                     }
         });
         if (checkErrors > cbArray.length-(min || 1)) {
			   this.adverrors.merge(cbArray);
			   var labels = [];
			   _this = this;
			   cbArray.map(function (el) {
			   		labels.push(_this.getFieldLabel(el));
			   });
			   
			   this.advcheckslog += this.getLangString("checkboxIntro",[min,labels.join(", ")])+"\n";
			   
         }
  },
  radio : function (radio) {
	    var value = $b.getForm(this.options.formId).field(radio[0].name).value();
		if (!value) {
            this.adverrors.merge(radio);
			var labels = [];
			for (i = 0; i < radio.length; i++) {
				var label = this.getFieldLabel(radio[i]);
				if (!labels.inArray(label)) {
					labels.push(label);
				}
			}
			this.advcheckslog += this.lang.radioIntro+" "+labels.join(", ")+"\n";
		}
  },
  
  match : function (pattern,target) {
  		if (this.getFieldValue(pattern.name) != this.getFieldValue(target.name)) {
			this.adverrors.merge([pattern,target]);
			var labels = [this.getFieldLabel(pattern),this.getFieldLabel(target)];
			this.advcheckslog += this.getLangString("matchError",labels)+"\n";
		}
  },
  
  chars : function (field,type,match,min,max) {
		 // find the form field value
		 
         var fieldValue = this.getFieldValue(field.name);
		 if (!fieldValue) { return; }
		 var options = {
		 	type : (type || false),
			match : ($defined(match) ? match : fieldValue.length)
		 };
		 var minC = (eval(min) || 0);
         var maxC = (max || fieldValue.length);
         /********** match min and max chars occurency *****************/

         // if match is array than create regExp limit as {value1,value2}
		 if (options.match != false) {
			if ($type(options.match) == "array") {
				var matchStr = "{" + options.match.join(",") + "}";
			}
			else {
				var matchStr = "{" + options.match.toString() + "}";
			}
         } else {
		 	var matchStr = "";
		 }
         
         
         /********** which type of check? string, number or regExp  *****************/
         var ren = "";
		 if ($type(options.type) == "string" && $defined(mochaSetup.charMatch[options.type])) {
            var exp = mochaSetup.charMatch[options.type];
			var ren = new RegExp(exp+matchStr);
         // set type as regular expression
         } else if ($type(options.type) == "regexp") {
           var ren = options.type;
         }
         /*************** make your check ***************/
         if ((fieldValue.length > maxC || fieldValue.length < minC || fieldValue.test(ren)==false) && fieldValue !="") {
            this.adverrors.checkAgainst(field);
			// build the error log
			var langStr = [this.getFieldLabel(field)];
			if ($type(options.type) == "string" && $defined(this.lang[options.type])) {
				var langKey = "charError";
				var matchOpt = [$defined(match) ? 0 : 1];
				var matchStr = $defined(match) ? match+" " : ""; 

				langStr.push(matchStr +this.lang[options.type]);
				
				var minStr = $defined(min) && min ? min.toString() : "0";
				var maxStr = $defined(max) && max ? max.toString() : this.lang.more;
				if (minStr+maxStr != "") {
					matchOpt.push(0);
					langStr.push(minStr,maxStr);
				}
				
				this.advcheckslog += this.getLangString("charError",langStr,matchOpt)+"\n";
			} else {
				this.advcheckslog += this.getLangString("charErrorRegexp",langStr)+"\n";
			}
         }
  }
	
  
  
});

mochaSetup = {
  lang : {
        EN : {
			processError : "Error processing the form",
			noCheckListLoaded : "Unable to remotely load Advanced Checklist", 
	        verboseIntro : "Followings Errors have been detected:",
			verboseEmpty : "is empty",
	        singleError : "Field %0 is empty or incorrectly filled",
	        radioIntro : "Select an option for",
	        checkboxIntro : "Select at least %0 between: %1",
	        singleCheckboxIntro : "You must check",
	        defaultAlert : "Warning: highlighted fields are empty or incorrectly filled",
			validEmail : "must be a valid email address",
			matchError : "Values of fields %0 and %1 don't match",
			charError : "Field %0 must contain {at least|only} %1 {and %2 to %3 chars}",
			charErrorRegexp : "Field %0 doesn't match content's requirements",
			l : "letters",
			n : "numbers",
			more : "more" 
        },
       IT : {
	   		processError : "Errore nell'analisi del modulo",
			noCheckListLoaded : "Impossibile caricare la lista remota di controllo avanzata",
	        verboseIntro : "Sono stati rilevati i seguenti errori:",
			verboseEmpty : "e' vuoto",
	        singleError : "Il campo %0 e' vuoto o errato",
	        radioIntro : "Selezionare un'opzione fra:",
	        checkboxIntro : "Selezionare almeno %0 fra i campi: %1",
	        singleCheckboxIntro : "Selezionare la casella",
	        defaultAlert : "Attenzione: i campi evidenziati sono vuoti o errati",
			validEmail : "dev'essere un campo email valido",
			matchError : "I campi %0 e %1 devono coincidere",
			charError : "Il campo %0 deve contenere {almeno|solo} %1 {e da %2 a %3 caratteri}",
			charErrorRegexp : "Il campo %0 non rispetta le regole di inserimento",
			l : "lettere",
			n : "numeri",
			more : "piu'"
        }
  },
  charMatch : {
	'l' : "[a-zA-Z]",
	'n' : "[0-9]"
  },
  setLang : "IT",
  hcolor : "#FF5656"
  //hcolor : ".error"
};