YAHOO.namespace("cx.exp.util");

/*
This method deal wit hthe collapsable container (hidden content, links to open and close the module.
Params:
- openLink: Open link HTML element
- closeLink: Close link HTML element
- content: Content module HTML element
*/
YAHOO.cx.exp.util.toggleContainer = function(openLink, closeLink, content){
	var obj = {};
	
	var showLink = openLink;
	var hideLink = closeLink;
	var content = content;

	var subscribeEvents = function(){
		YAHOO.util.Event.addListener(showLink, "click", showLinksCallback);
		YAHOO.util.Event.addListener(hideLink, "click", hideLinksCallback);
	}
	
	var showLinksCallback = function(e){
		YAHOO.util.Dom.setStyle([hideLink,content], 'display','block');
		YAHOO.util.Dom.setStyle(showLink, 'display','none');
	}
	
	var hideLinksCallback = function(e){
		YAHOO.util.Dom.setStyle([hideLink,content], 'display','none');
		YAHOO.util.Dom.setStyle(showLink, 'display','block');
	}
	
	var init = function(){
		subscribeEvents();
	}
	init();
	
	return obj;
}

/*
This method deal with the collapsable container (hidden content, links to open and close the module.
Params:
- toggleSwitch: Switch HTML element
- openStateElements: Array of HTML element to display when toggled
- closeStateElements: Array of HTML element to hide when toggled
*/
YAHOO.cx.exp.util.toggleElements = function(toggleSwitch, openStateElements, closeStateElements){
	var obj = {};
	
	var switchEl = toggleSwitch;
	var open = false;
	var showEl = new Array();
	showEl = openStateElements;
	var hideEl = new Array();
	hideEl = closeStateElements;	
	
	var subscribeEvents = function(){
		YAHOO.util.Event.addListener(switchEl, "click", toggleCallback);
	}
	
	var toggleCallback = function(e){
		if(open){
			YAHOO.util.Dom.setStyle(showEl, 'display','none');
			YAHOO.util.Dom.setStyle(hideEl, 'display','block');
			open = false;
		}
		else{
			YAHOO.util.Dom.setStyle(showEl, 'display','block');
			YAHOO.util.Dom.setStyle(hideEl, 'display','none');
			open = true;
		}
	}
	
	var init = function(){
		subscribeEvents();
	}
	init();
	return obj;
}

YAHOO.cx.exp.util.preloadImage = function(width, height, url){
	var image = new Image(width, height);
	image.src = url;
	return image;
}

/**
 * Loads AJAX APIs by manipulating the DOM to include <script> tag
 * @param url the javascript URL to load
 * @param opts specifies all optional configuration options for the API you are loading as a JavaScript object literal
 */
YAHOO.cx.exp.util.loadScript = function (src, opts){
	try {
		if (!opts) opts = {};
		this.callback_ = opts.callback || null;	
		
		// create the script element for the google API
		var script = document.createElement("script");
		script.src = src;
		script.type = "text/javascript";
		document.getElementsByTagName("head")[0].appendChild(script);		
		
		// execute the callback as soon as the DOM is in a usable state.
		if(this.callback_ != null){
			YAHOO.util.Event.onDOMReady(this.callback_); 			
		}		
	} catch (e) {
		throw "unable to load script " + url + " with options " + opts;
	}
}

YAHOO.namespace("cx.exp.search");

/**
 * The Validator class provides form field validation functionality
 * @class Validator
 * @constructor
 * @param form {HTMLElement} DOM element reference of a form.
 * @param config {Object} (optional) Object literal of configuration params.
 */
YAHOO.cx.exp.search.Validator = function (form, config){
	this.config = config || {};
	this.form = YAHOO.util.Dom.get(form);
	this.validations = new Array();
	this.stopOnFirst = config.stopOnFirst == true ? true : false;
	this.highlight = (config.highlightClass) ? true : false;
	this.highlightClass = this.highlight ? config.highlightClass : '';
	this.notify = (config.notificationDiv) ? true : false;
	this.notificationDiv = this.notify ? YAHOO.util.Dom.get(config.notificationDiv) : '';
	this.notificationClass = config.notificationClass ? config.notificationClass : '';
	this.dateFormat = config.dateFormat ? config.dateFormat : 'ddmmyy';	
};

/**
 * Regular expressions constants for validating the date
 */
YAHOO.cx.exp.search.Validator.prototype.REGEXP = {
   MMDDYY: '^(?:0*[1-9]|1[012])[- /.](?:0*[1-9]|[12][0-9]|3[01])(?:[- /.](?:20)*\\d\\d)*$',
   DDMMYY: '^(?:0*[1-9]|[12][0-9]|3[01])[- /.](?:0*[1-9]|1[012])(?:[- /.](?:20)*\\d\\d)*$',
   YYMMDD: '^(?:(?:20)*\\d\\d)[- /.](?:0*[1-9]|1[012])[- /.](?:0*[1-9]|[12][0-9]|3[01])*$'
}

/**
 * Constants for date formats
 */
YAHOO.cx.exp.search.Validator.prototype.DATE = {
   MMDDYY: 'mmddyy',
   DDMMYY: 'ddmmyy',
   YYMMDD: 'yymmdd'
}

/**
 * The Validation class encapsulates the rules for an element array
 * @class Validation
 * @constructor
 * @param elements {Array} DOM element reference of a fields
 * @param rule {Object} Object literal of predefined rules.
 * @param rule {String} (optional) validation exception message.
 */
YAHOO.cx.exp.search.Validation = function (elements, rule, message){
	this.elements  = elements;
	this.rule  = rule;
	this.message = message;
};

/**
 * Add the rules for the specified element array
 * @param elements {Array} DOM element reference of a fields
 * @param rule {Object} Object literal of predefined rules.
 * @param rule {String} (optional) validation exception message.
 */
YAHOO.cx.exp.search.Validator.prototype.addValidation = function (elements, rule, message){
	this.validations.push(new YAHOO.cx.exp.search.Validation(elements, rule, message));	
};

/**
 * Add the the specified validation object for the validator to operate on.
 * @param validationObject {Object} Object literal of validations
 */
YAHOO.cx.exp.search.Validator.prototype.addValidationObj = function (validation){
	this.validations.push(validation);	
};

/**
 * Validate the set of rules assosiated with the validator
 */
YAHOO.cx.exp.search.Validator.prototype.validate = function (){
	this.clear();
	var messages = {};
	var success = true;
	// iterate through all the validations
	for(var i=0; i < this.validations.length; i++) {
		var elements = this.validations[i].elements;	
		var rule = this.validations[i].rule;	
		var message = this.validations[i].message;	
		// iterate through all the elements that need to be validated
		for(var j=0; j < elements.length; j++){
			var elem = YAHOO.util.Dom.get(elements[j]);
			var ret = this.assert(elem, rule); // test the rule
			if (ret != true) {
				success = false;
				if (this.highlight){
					this._highlight(elem, true);
				}
				var msgExists = false;
				for (var k in messages) {
					if (message === messages[k]){
				      msgExists = true;
				     }
			    }
			    if(!msgExists)
				     messages[i] = message; // elements of the same rule will re-indexed
			}
		}		
		// check the stop-on-first condition 
		if ((success != true) && (this.stopOnFirst)){
			break;			
		}		
	}
	// notify the failed validation
	if((success != true) && (this.notify)) {
		this._notify(messages); 
		return false;
	}
	return true;
};

/**
 * Tests whether the elements asserts with the specified rules
 * @param elements {HTMLElement} DOM element reference of a field
 * @param rule {Object} Object literal of predefined rules.
 */
YAHOO.cx.exp.search.Validator.prototype.assert = function (elem, rule){
	var ret = true;
	for (var i in rule) {
       ret = this._check(i, rule[i], elem.value)
	   if (ret != true) { // and condition failure
          break;
       }
    }
	return ret;
};

/**
 * Checks whether the element values complies with the specified rule value
 */
YAHOO.cx.exp.search.Validator.prototype._check = function (ruleName, ruleValue, elementValue){
	var ret = true;
	switch (ruleName) {
        case 'required' :
          ret =  (!(ruleValue && YAHOO.lang.trim(elementValue) == ''));
          break;
        case 'minLength':
		  ret = (elementValue.length > ruleValue);
          break;
	    case 'maxLength':
		  ret = (elementValue.length <= ruleValue);
          break;
		case 'date':
		  if(ruleValue && YAHOO.lang.trim(elementValue) != '') {			
			ret = ( this._getDate(elementValue) != null);
		  }
          break;
		case 'mask':
		  ret = this._mask(elementValue, ruleValue);
          break;
		case 'after':	
		  if(YAHOO.lang.trim(elementValue) != '') { 
			 var returnDate = this._getDate(elementValue) ;
			 var startDate =  this._getDate(YAHOO.util.Dom.get(ruleValue).value);
		     ret = (returnDate > startDate);
		  }
          break;
	}
	return ret;
};

/**
 * Gets the date onkect for the specified string
 */
YAHOO.cx.exp.search.Validator.prototype._getDate = function (sDate){
	var formatted = false;
	switch (this.dateFormat){
	case  YAHOO.cx.exp.search.Validator.prototype.DATE.DDMMYY: 
		formatted  = this._mask(sDate, YAHOO.cx.exp.search.Validator.prototype.REGEXP.DDMMYY);
		break;
	case  YAHOO.cx.exp.search.Validator.prototype.DATE.MMDDYY:
		formatted  = this._mask(sDate, YAHOO.cx.exp.search.Validator.prototype.REGEXP.MMDDYY);
		break;
	case  YAHOO.cx.exp.search.Validator.prototype.DATE.YYMMDD:
		formatted  = this._mask(sDate, YAHOO.cx.exp.search.Validator.prototype.REGEXP.YYMMDD);
		break;	
	} 
	
	if(formatted) {
		return  this._getDateFromString(sDate, this.dateFormat);
	}else {
		return  null;
	}
};

/**
 * Validates the regular expression 
 */
YAHOO.cx.exp.search.Validator.prototype._mask = function (value, regex){
	return new RegExp(regex).test(value);
};

/**
 * Clears all the notifications 
 */
YAHOO.cx.exp.search.Validator.prototype.clear = function (){
	// iterate through all the validations
	for(var i=0; i < this.validations.length; i++) {
		var elements = this.validations[i].elements;
		// iterate through all the elements that need to be validated
		for(var j=0; j < elements.length; j++){
			//remove highlighting
			var elem = YAHOO.util.Dom.get(elements[j]);
			this._highlight(elem, false);
		}
	}
	if(this.notify)
		this.notificationDiv.innerHTML = '';
};

/**
 * Adds a notification paragraph to the error div container 
 */
YAHOO.cx.exp.search.Validator.prototype._notify = function (messages){
	for (var i in messages) {
		var para = document.createElement("p");
		para.className = this.notificationClass;
		var messagetext = document.createTextNode(messages[i]);
		para.appendChild(messagetext);
		this.notificationDiv.appendChild(para);		
	}
	YAHOO.util.Dom.setStyle(this.notificationDiv, 'display','block');	
};

/**
 * Highlights the label for the specified field
 */
YAHOO.cx.exp.search.Validator.prototype._highlight = function (elem, bool){
	if(bool)
		elem.className += " " + this.highlightClass;	
	else
		elem.className = elem.className.replace(this.highlightClass, "");

	var labels = YAHOO.util.Dom.getElementsBy( 
		function(el) {		
			return el.htmlFor == elem.id ;
		}, 'label', this.form);
	if(!YAHOO.lang.isUndefined(labels[0])) {
		var elt = labels[0];
		if(bool)
			elt.className += " " + this.highlightClass;	
		else
			elt.className = elt.className.replace(this.highlightClass, "");
	}
};

/**
* date util function
*/
YAHOO.cx.exp.search.Validator.prototype._getDateFromString = function (sDate, dateFormat){
  var arrDt = sDate.split(new RegExp("[- /.]"));
  var day = this._getDay(arrDt, dateFormat);
  var month = this._getMonth(arrDt, dateFormat);
  var year = this._getYear(arrDt, dateFormat );
  
  var leapYear = false;
  if((year % 400 == 0) || (year % 100 != 0 && year % 4 == 0)) {
	leapYear = true;
  }

  var daysInMonth = 28;
  if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
       daysInMonth = 31;
  } else if (month == 4 || month == 6 || month == 9 || month == 11){
	  daysInMonth = 30;
  }else if (month == 2 && leapYear == true){
	  daysInMonth = 29;
  }  
  
  if(day <= daysInMonth) {
	  return new Date(year, month - 1, day);
  } else {
	  return null;
  }
};

/**
 * Gets the day from the specified array in accordance to the date format
 */
YAHOO.cx.exp.search.Validator.prototype._getDay = function(arrDate, dateFormat) {
	var day = 0;
	switch (dateFormat){
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.DDMMYY: 
			day = parseInt(arrDate[0], 10);
			break;
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.MMDDYY:
			day = parseInt(arrDate[1], 10);
			break;
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.YYMMDD:
			day = parseInt(arrDate[2], 10);
			break;	
	}
	return day;	
};
  
/**
 * Gets the month from the specified array in accordance to the date format
 */
YAHOO.cx.exp.search.Validator.prototype._getMonth = function(arrDate, dateFormat) {
	var month = 0;
	switch (dateFormat){
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.DDMMYY:
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.YYMMDD:
			month = parseInt(arrDate[1], 10);
			break;
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.MMDDYY:
			month = parseInt(arrDate[0], 10);
			break;		
	}
	return month;		
};
    
/**
 * Gets the year from the specified array in accordance to the date format
 */		
YAHOO.cx.exp.search.Validator.prototype._getYear = function(arrDate, dateFormat) {
	var year = 0;
	switch (dateFormat){
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.DDMMYY: 
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.MMDDYY:
			if (arrDate.length > 2) {
				if (arrDate[2].length == 2) {
					year = parseInt("20" +arrDate[2], 10);
				}else {
					year = parseInt(arrDate[2], 10);
				}				
			}else {
				year = new Date().getFullYear();
			}
			break;		
		case  YAHOO.cx.exp.search.Validator.prototype.DATE.YYMMDD:
			year = parseInt(arrDate[0], 10);
			break;	
	}
	return year;		
};