// timeparse.js
// Modification Log
// 18 Apr 2007 TKO - Fixed parseInt usage by adding the radix parameter for each usage per bug found by John G.

var timeParse = {
	sTimeFormat:"h:mm AM/PM",
	obj: document,
	dropdownSize: 4,

	init: function(){
		var self = this;
		// Get all input elements and look for elements with a class of "timeparse"
		var els = getElementsByClassName("timeparse", "input", self.obj);
		for(var i = 0; i < els.length; i++){
			addEvent(els[i], "blur", function() {
				self.validate(this);
				this.inField = false;
				// Close the drop down when going to the next field. Only if the
				// user doesn't come back to this field. So delay, then look to
				// see if the focus has come back to this field.
				setTimeout("timeParse.closeDropdown(\"" + this.id + "\")", 250);
			});
			this.createDropdown(els[i]);
		}
	},

	closeDropdown: function(id) {
		var el = document.getElementById(id);
		if(typeof el.inField != "undefined" && typeof el.selectObj != "undefined" && el.inField == false){
			el.selectObj.style.display="none";
		}

	},

	createDropdown: function(el){
		// create span in front of element.
		// add element to span.
		// add button to span.
		// add select to span.
		
		// Turn off autocomplete.
		el.autocomplete = "off";

		var self = this;
		// Create the select dropdown.
		var select = document.createElement("select");
		select.style.display = "none";
		select.style.position = "absolute"
		select.style.width = el.offsetWidth + "px";
		// If IE use the margin to position the select under the text box.
		if(document.attachEvent && !window.opera){
			select.style.marginLeft = "-" + el.offsetWidth + "px";
			select.style.marginTop = el.offsetHeight + "px";
		} else {
			// For some reason Mozilla moves the select under the text box by itself.
			select.style.marginTop = "-1px";
		}
		select.size = self.dropdownSize;
		// Set this so that tab will go to the next field.
		select.tabIndex = -1;
		select.options.length = 0;
		select.input = el;

		// Add time interval items to the select.
		var aTimes = "12:00 AM,12:15 AM,12:30 AM,12:45 AM,01:00 AM,01:15 AM,01:30 AM,01:45 AM,02:00 AM,02:15 AM,02:30 AM,02:45 AM,03:00 AM,03:15 AM,03:30 AM,03:45 AM,04:00 AM,04:15 AM,04:30 AM,04:45 AM,05:00 AM,05:15 AM,05:30 AM,05:45 AM,06:00 AM,06:15 AM,06:30 AM,06:45 AM,07:00 AM,07:15 AM,07:30 AM,07:45 AM,08:00 AM,08:15 AM,08:30 AM,08:45 AM,09:00 AM,09:15 AM,09:30 AM,09:45 AM,10:00 AM,10:15 AM,10:30 AM,10:45 AM,11:00 AM,11:15 AM,11:30 AM,11:45 AM,12:00 PM,12:15 PM,12:30 PM,12:45 PM,01:00 PM,01:15 PM,01:30 PM,01:45 PM,02:00 PM,02:15 PM,02:30 PM,02:45 PM,03:00 PM,03:15 PM,03:30 PM,03:45 PM,04:00 PM,04:15 PM,04:30 PM,04:45 PM,05:00 PM,05:15 PM,05:30 PM,05:45 PM,06:00 PM,06:15 PM,06:30 PM,06:45 PM,07:00 PM,07:15 PM,07:30 PM,07:45 PM,08:00 PM,08:15 PM,08:30 PM,08:45 PM,09:00 PM,09:15 PM,09:30 PM,09:45 PM,10:00 PM,10:15 PM,10:30 PM,10:45 PM,11:00 PM,11:15 PM,11:30 PM,11:45 PM,12:00 AM".split(",");
		for(var i = 0; i < aTimes.length; i++)
			select.options[i] = new Option(aTimes[i]);

		// Create the button.
		var toggle = document.createElement("a");
		var toggleText = document.createTextNode("Show time dropdown");
		toggle.appendChild(toggleText);
		toggle.className = "tmsel";
		toggle.title = "Show time dropdown";
		toggle.href="#";
		// Set this so that tab will go to the next field.
		toggle.tabIndex = -1;
		// Store the select and input objects in the button.
		toggle.selectObj = select;
		toggle.input = el;
		
		// Store the select object and set custom fields in the input element.
		el.selectObj = select;
		el.inField = false;

		// Create the span.
		var span = document.createElement("span");
		span.className = "timeparse";
		span.selectObj = select;

		// Add the span.
		el.parentNode.insertBefore(span,el);

		// Move the el to the span.
		span.appendChild(el);

		// Add the button to the span.
		span.appendChild(toggle);

		// Add the select box to the span.
		span.appendChild(select);

		addEvent(el, "keyup", self.inputKeyUpHandler);
		addEvent(el, "keydown", self.inputKeyDnHandler);
		addEvent(el, "focus", function(){ this.inField = true; });
		addEvent(toggle, "click", self.toggleClickHandler);
		addEvent(select, "click", self.selectClickHandler);
		// Don't allow the select box to close.
		addEvent(select, "focus", function() { this.input.inField = true; });
		// Set the select box to close if focus doesn't return to the input field or button.
		addEvent(select, "blur", function() {
			this.input.inField = false;
			setTimeout("timeParse.closeDropdown(\"" + this.input.id + "\")", 250);
		});
	},
	
	selectClickHandler: function(e) {
		// If we click on the SELECT, set the INPUT to the text
		// value of the selected OPTION, hide the SELECT, and 
		// focus on the input box.
		this.input.value = this.options[this.selectedIndex].text;
		this.style.display = 'none';
		this.input.focus();
		return false;
	},

	toggleClickHandler: function (e) {
		e.preventDefault();
		e.stopPropagation();

		this.input.inField = true;
		if(this.selectObj.style.display == "none")
			timeParse.openDropdown(this.input);
		else
			this.selectObj.style.display = "none";

		this.input.focus();
	},

	openDropdown: function(input){
		timeParse.validate(input);
		
		// Find the input value in the select.
		var value = input.value.toUpperCase();
		var select = input.selectObj;

		// Make sure the select is displayed and if the font size has changed,
		// make sure that it is in the "correct" position.
		select.style.display = 'block';
		select.style.width = input.offsetWidth + "px";
		if(document.attachEvent && !window.opera){
			select.style.marginLeft = "-" + input.offsetWidth + "px";
			select.style.marginTop = input.offsetHeight + "px";
		}

		// No value, not selected.
		if(value == ""){
			select.selectedIndex = -1;
			return;
		}

		// Make sure that it has a leading zero.
		if(value.length < 8)
			value = "0" + value;

		// Walk through each item in the select.			
		for(var i = 0; i < select.options.length; i++){
			if(value == select.options[i].text){
				select.selectedIndex = i;
				return;
			}
		}

		// Not found, not selected.
		select.selectedIndex = -1;
	},
	
	inputKeyDnHandler: function (e) {
		var input = this;
		var select = input.selectObj;
		var keycode = e.keyCode;

		switch (keycode) {
		case 38: // up arrow
			// Display the select.
			if(select.style.display != 'block')
				timeParse.openDropdown(input);
			// Move this index up 1 but not higher than zero.
			select.selectedIndex = Math.max(0, select.selectedIndex-1);

			// Populate the INPUT with the select's value
			input.value = select.options[select.selectedIndex].text;

			e.preventDefault();
			e.stopPropagation();
			
			return false;

		case 40: // down arrow
			// Display the select.
			if(select.style.display != 'block')
				timeParse.openDropdown(input);
			// Move this index down 1 but not more than the last item.
			select.selectedIndex = Math.min(select.options.length-1, select.selectedIndex+1);
			input.value = select.options[select.selectedIndex].text;

			e.preventDefault();
			e.stopPropagation();
			
			return false;

		case 33: // page up
			// Display the select.
			if(select.style.display != 'block')
				timeParse.openDropdown(input);
			// Move this index up 1 but not higher than zero.
			select.selectedIndex = Math.max(0, select.selectedIndex-timeParse.dropdownSize);

			// Populate the INPUT with the select's value
			input.value = select.options[select.selectedIndex].text;

			e.preventDefault();
			e.stopPropagation();
			
			return false;

		case 34: // page down
			// Display the select.
			if(select.style.display != 'block')
				timeParse.openDropdown(input);

			// Move this index down 1 but not more than the last item.
			select.selectedIndex = Math.min(select.options.length-1, select.selectedIndex+timeParse.dropdownSize);
			input.value = select.options[select.selectedIndex].text;

			e.preventDefault();
			e.stopPropagation();
			
			return false;

		case 9:  // tab
		default:
			select.style.display = "none";
			break;
		}
	},

	inputKeyUpHandler: function (e) {
		var input = this;
		var select = input.selectObj;
		var keycode = e.keyCode;

		// Check for Enter or Esc
		if(keycode == 13 || keycode == 27) {
			// Tell our SELECT to hide itself
			select.style.display = "none";
			input.focus();
		}
	},

	validate: function(input, required) {
		input.value = trim(input.value);
		if(!required && input.value == '') {
			return true;
		}

		var value = input.value;
		// Test for am/pm indicator without a colon
		if (/^\d{0,6}[^:]( ?[aApP][mM]?)$/.test(value)){
			var sTime = parseInt(value,10).toString();
			if(value.substr(0,1) == "0")
				sTime = "0" + sTime;
			switch(sTime.length) {
			case 1:	// h
				value = value.substr(0,1) + ":00" + value.substr(1);
				break;
			case 2:	// hh
				value = value.substr(0,2) + ":00" + value.substr(2);
				break;
			case 3:	// hmm
				value = value.substr(0,1) + ":" + value.substr(1);
				break;
			case 4:	// hhmm
				value = value.substr(0,2) + ":" + value.substr(2);
				break;
			case 5:	// hmmss
				value = value.substr(0,1) + ":" + value.substr(1,2) + ":" + value.substr(3);
				break;
			case 6:	// hhmmss
				value = value.substr(0,2) + ":" + value.substr(2,2) + ":" + value.substr(4);
				break;
			}
		} else if(!/(\:)/.test(value)){
			// Military time.
			switch(value.length) {
			case 1:	// h
				value = value.substr(0,1) + ":00";
				break;
			case 2:	// hh
				value = value.substr(0,2) + ":00";
				break;
			case 3:	// hmm
				value = value.substr(0,1) + ":" + value.substr(1,2);
				break;
			case 4:	// hhmm
				value = value.substr(0,2) + ":" + value.substr(2,2);
				break;
			case 5:	// hmmss
				value = value.substr(0,1) + ":" + value.substr(1,2) + ":" + value.substr(3,2);
				break;
			case 6:	// hhmmss
				value = value.substr(0,2) + ":" + value.substr(2,2) + ":" + value.substr(4,2);
				break;
			}
		}			

		if(!this.isValidTime(value)){
			alert("Please enter a valid time.");
			input.focus();
			return false;
		}

		input.value = this.getStringTime(value);
		return true;
	},

	getStringTime: function(value, format) {
		// Get the date format.
		// If a format was passed, use it, otherwise the default format.
		var fmt = format?format:this.sTimeFormat;
		var re = /^(h|hh)(:mm)(:ss)?( am\/pm| AM\/PM| a\/p| A\/P)?$/;

		// Use the default format if the provided format is not valid.
		if (!re.test(fmt))
			fmt = "h:mm AM/PM";

		var hasMeridian = false;
		var meridian = "am,pm".split(",");

		if (/\b(am\/pm|AM\/PM|a\/p|A\/P)\b/.test(fmt)){
			var meridianLength = 0;
			hasMeridian = true;
			if (/\bam\/pm\b/.test(fmt)){
				meridian = "am,pm".split(",");
				meridianLength = 6;
			}else if (/\bAM\/PM\b/.test(fmt)){
				meridian = "AM,PM".split(",");
				meridianLength = 6;
			}else if (/a\/p/.test(fmt)){
				meridian = "a,p".split(",");
				meridianLength = 4;
			}else if (/\bA\/P\b/.test(fmt)){
				meridian = "A,P".split(",");
				meridianLength = 4;
			}

			fmt = fmt.substring(0, fmt.length-meridianLength)
		}

		// Initialize time variables.
		var h = "0";
		var m = "00";
		var s = "00";
		var sMeridian = "";
		var valueHasMeridian = /[aApP][mM]?$/.test(value);
		var sTemp = "";
		var values = value.split(":");
		var fmtItems = fmt.split(":");

		// Go through each item in the value field.
		for(var i = 0; i < values.length; i++){
			if(i >= fmtItems.length)
				break;

			switch (fmtItems[i]){
			case "h":
			case "hh":
				var iTemp = parseInt(values[i],10);
				// Make sure the hours are in 24 hour format.
				if(valueHasMeridian){
					if(/[pP][mM]?$/.test(value)){
						if(iTemp < 12)
							iTemp += 12;
					}else if(/[aA][mM]?$/.test(value) && iTemp == 12){
						iTemp = 0;
					}
				}

				// Does the format string expect a Meridian?
				// If so put the hours in 12 hour format.
				if(hasMeridian){
					if(iTemp > 12){			// pm
						iTemp -= 12;
						sMeridian = meridian[1];
					}else if(iTemp == 12){		// pm
						sMeridian = meridian[1];
					}else if(iTemp > 0){		// am
						sMeridian = meridian[0];
					}else if(iTemp == 0){		// am
						iTemp = 12;
						sMeridian = meridian[0];
					}
				}

				// Format the hours.
				if(fmtItems[i] == "h"){
					h = iTemp;
				}else{
					sTemp = "0" + iTemp;
					h = sTemp.substring(sTemp.length-2, sTemp.length);
				}

				break;

			case "mm":
				// Format minutes.
				sTemp = "0" + parseInt(values[i],10);
				m = sTemp.substring(sTemp.length-2, sTemp.length);
				break;

			case "ss":
				// Format seconds.
				sTemp = "0" + parseInt(values[i],10);
				s = sTemp.substring(sTemp.length-2, sTemp.length);
				break;
			}
		}

		// Format time string.
		if(fmtItems.length == 2)
			return h + ":" + m + (hasMeridian?" " + sMeridian:"");

		return h + ":" + m + ":" + s + (hasMeridian?" " + sMeridian:"");
	},

	isValidTime: function (value) {
		var hasMeridian = false;

		if (!(/^\d{1,2}[:]\d{2}([:]\d{2})?( ?[aApP][mM]?)?$/.test(value))) {
			return false; 
		}

		if (/[aApP][mM]?$/.test(value)){
			hasMeridian = true;
		}

		var values = value.split(":");
		if ( (parseFloat(values[0]) < 0) || (parseFloat(values[0]) > 23) ) {
			return false; 
		}

		if (hasMeridian) {
			if ( (parseFloat(values[0]) < 1) || (parseFloat(values[0]) > 12) ) {
				return false; 
			}
		}

		if ((parseFloat(values[1]) < 0) || (parseFloat(values[1]) > 59)) {
			 return false; 
		}

		if (values.length > 2) {
			if ((parseFloat(values[2]) < 0) || (parseFloat(values[2]) > 59)) {
				return false; 
			}
		}

		return true;
	}
}

addEvent(window,"DOMContentLoaded", function() {timeParse.init();});

