/*  
	project:   		  CatScript Applications
	client:    		  Chigozie 'Cat' Nduanya
	author:    		  Chigozie 'Cat' Nduanya
	date:      		  October 13, 2009
	file-name: 		  catscript.js
	version:          5.0.1
	file-type: 		  Javascript Library
*/

/* declarations: */
$cat=jQuery.noConflict();

/*
Array prototype: remove(); Usage examples are:
array.remove(1); 	 //Removes the second item from the array
array.remove(-2);    //Remove the second-to-last item from the array
array.remove(1,2);   //Remove the second and third items from the array
array.remove(-2,-1); //Remove the last and second-to-last items from the array
*/
Array.prototype.remove=function(from,to){
  var rest=this.slice((to || from) + 1 || this.length);
  this.length=from < 0 ? this.length + from : from;
  
  return this.push.apply(this,rest);
};

/*a) objects */
var Colors={
	blue: 		   '#015699',
	black:		   'black',
	brown:		   '#995F4C',
	darkMadre:	   '#503E09',
	deepBlue:	   '#010E17',
	disabledInput: '#E0E0E0',
	gray:	       '#959499',
	green:		   'green',
	hyperlink:	   '#43A3D6',
	lightMadre:	   '#DAD8A8',
	lighterBlue:   '#CDDAE9',
	limeGreen:     '#B6D226',
	oddRowColor:   '#EEF9E6',
	red:  		   'red',
	rowColor:  	   '#EAF4FB',
	cea:{
		blue:	  '#000B1F',
		brickRed: '#AA1111',
		gray:     '#333333',
		lightBlue:'#5C99D0',
		orange:   '#CC6600'
	},
	white:'#FFFFFF'
};//Colors end here...

/*b) classes */
/*
	class: 		Cat
	parameters: -
	contract:   must provide methods for accessing the various rich classes preapred for the CatScript language.
				tested and passed.
*/
function Cat(){
	return {
		
		/*
			method: 	 Cat.button()
			parameters:  obj (container, color, text)
			description: generates a catscript button into the parametized container. 
						 no error-checking performed, I might get around to that later...
						 tested and passed.
			usage:       $cat(['add','exit']).each(function(index){
							cat.button({container:$cat('.general-list-buttons'),color:buttonColors[index],text:this});
						 });
						 OR 
						 cat.button({container:$cat('.button-cell'),color:Colors.gray,text:'edit profile'});
		*/
		button:function(obj){
			if(obj && obj.container){
				var strColor=(obj.color)?obj.color:this.color,strClass=' class="cat-button"';
				var txt=(obj.text)?obj.text:'cat button';
				var style=' style="border:1px solid '+strColor+';color:'+strColor+';padding:2px;'
					+'text-decoration:none;"', title=' title="'+txt+'"';
				var objButton={
					off:{'background':'none','color':strColor},
					on: {'background':strColor,'color':'white'}
				};
				obj.container.append('<a href="javascript:"'+style+strClass+title+'>'+txt+'</a>').find('a:last').css(objButton.on);
				obj.container.find('.cat-button:last').hover(
					function(){$cat(this).css(objButton.off);},
					function(){$cat(this).css(objButton.on);});
			}
		},//Cat.button() ends here...		
		/*
			method: 	 Cat.buttonCss()
			parameters:  button, backColor
			description: styles a button using the provided back-color; as well, hover functionality is applied using a combination
						 of the back-color and white, white on, back-color off.
						 tested and passed.
			usage:		 cat.buttonCss(button,backColor=Colors.buttons.green);
		*/
		buttonCss:function(button,backColor){
			button.css(this.border()).css({'background':backColor,'color':'white','cursor':'pointer'})
				.height('17px').css(this.pad({mode:'left-right',thickness:4}))
				.hover(function(){$cat(this).css({'background':'white','color':backColor});},
					   function(){$cat(this).css({'background':backColor,'color':'white'});});
		},//Cat.buttonCss() ends here...		
		/*
			method: 	 cat.characters()
			parameters:  mode ('alpha_numeric', 'numeric', and 'strictly_numeric')  
			description: returns a list of allowable characters based on the specified mode.
						 tested and passed.
			usage:		 var characters=cat.characters('strictly_numeric');
		*/
		characters:function(properties){
			var output='';
			switch(properties){
				case 'alpha_numeric': 
					output='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
					output+='1234567890'; 
					output+=' $/#%-;:,.()@_!';
                    output+="'"; 
				break;
				case 'non_negative_numeric': output='1234567890.'; break;
				case 'numeric': output='1234567890-.'; break;
				case 'strictly_numeric': output='1234567890';   break;
			}
			
			return output;
		},
		/*  method:      cat.clock()
			parameters:  container
			description: generates a digital clock (complete with current date) into the parametized container.
						 tested and passed.
			usage:		 cat.clock($cat('.date-panel'));
		*/
		clock:function(container){
			if(container){
				$cat('.clock').remove();
				var strFontSize='10px', date='<span>'+this.date()+'</span>';
                var time='<span class="clock-display"></span>';
				function currentClock(){
					var node=(cat.elementNode!=null)?cat.elementNode:'div';
                    with(container){
						find('.date-and-time').remove();
						html('<'+node+' class="clock">'+date+'&nbsp;&nbsp;'+time+'</'+node+'>');
						find('.clock').css('font-size',strFontSize);
					}
					tick();
				}
				
				currentClock();
			}
		},
		/*
		 	method: 	  cat.currency()
		 	parameters:   valueToBeConverted
		 	description:  converts its parameter to a currency-formatted representation and returns the currency value.
		 				  tested and passed.
		 	usage:		  $cat('currency-field').val(cat.currency($cat('currency-field').val()));
		 */
		currency:function(valueToBeConverted){
			valueToBeConverted=valueToBeConverted.toString().replace(/\$|\,/g,'');
			if(isNaN(valueToBeConverted))valueToBeConverted='0';
			var sign=(valueToBeConverted==(valueToBeConverted=Math.abs(valueToBeConverted)));
			valueToBeConverted=Math.floor(valueToBeConverted*100+0.50000000001);
			
			var cents=valueToBeConverted%100;
			valueToBeConverted=Math.floor(valueToBeConverted/100).toString();
			if(cents<10)cents='0'+cents;
			for(var i=0;i<Math.floor((valueToBeConverted.length-(1+i))/3);i++)
				valueToBeConverted=valueToBeConverted.substring(0,valueToBeConverted.length-(4*i+3))+
								   ','+valueToBeConverted.substring(valueToBeConverted.length-(4*i+3));
			
			return (((sign)?'':'-')+'$'+valueToBeConverted+'.'+cents);
		},
		/*
			method: 	 cat.date()
			parameters:  properties
			description: generates a date-display and returns it as a string. if no mode is specifed, month, day and year are returned.
						 tested and passed.
			usage:		 var date=cat.date();//full date with day included, format June 21, 2009...
						 var date=cat.date({mode:'cat'});//format->2009-01-01... returns today's date
						 var date=cat.date({mode:'cat',days:10});//date 10 days in future, cat format
						 var date=cat.date({mode:'cat',days:-10});//date 10 days ago, cat format
		*/
		date:function(properties){
			var mode=(properties && properties.mode)?properties.mode:'', output='', time=new Date();
			if(properties && properties.days){
				(properties.days<0)?time.setDate(time.getDate()+properties.days+1):
					time.setDate(time.getDate()+properties.days-1);
			}
			var month=time.getMonth()+1, 
				day=(time.getDate()<10)?'0'+time.getDate():time.getDate(), year=time.getFullYear(),
				months=['','January','February','March','April','May','June','July','August','September','October','November','December'];
						
			switch(mode){
				case 'cat':
					month=(month<10)?'0'+month:month;
					output=year+'-'+month+'-'+day;
				break;
				case 'signature': output=months[month]+ ' '+year; 		   break;
				default: 		  output=months[month]+ ' '+day+', '+year; break;
			}
			
			return output;
		},
		/*
			method: 	 Cat.dateCells()
			parameters:  currentDateCell, dateCellToRestrict, restrictOn (values are: 'minDate' OR 'maxDate' ONLY)
			description: removes any existing datecells from the current date cell and then appends a new
						 datepicker into it. onselect, the alue of this datefiield is used to restrict the 
						 date range for the dateCellToRestrict object.
						 tested and passed.
			updated:     August 27, 2009
			usage:
			var cell=$cat('.date-cell'), dateValue=cell.text();
			cat.dateCell(cell);
		*/
		dateCell:function(currentDateCell){
    			function onSelectAction(strDate){
    				cat.strDate=strDate.substr(6,strDate.length)+
                    '-'+strDate.substr(0,2)+'-'+strDate.substr(3,strDate.length-8);
    				currentDateCell.find('input:text').val(cat.strDate);
    				cat.datePickerFunctionality(currentDateCell);
    			};
    			var textfield='<input type="text" readonly="readonly" />';
    			with(currentDateCell){
    				find('.date-picker').remove();
    				append('<span class="date-picker">'+textfield+'</span>');
    				find('.date-picker input:text').width(50).datepicker({
    					onSelect:function(strDate){onSelectAction(strDate);},
    					showOn:'button', 
    					buttonImage:cat.imageDir('thumbnails')+'calendar.gif', 
    					buttonImageOnly:true
    				});
    				find('.date-picker img').width(13).height(13);
    			}
		},//Cat.dateCell() ends here...,
		/*
			method: 	 cat.days()
			parameters:  start (date format yyyy-mm-dd), finish (date format yyyy-mm-dd)
			description: calculates and returns the number of days between two dates. the date format is strict and MUST be adhered to.
						 tested and passed.
			usage:		 var startDate=$('txt_start_date').value, finishDate=$('txt_finish_date').value;
				         var days=cat.days(startDate,finishDate);
			revised:	 September 12, 2009
		*/
		days:function(start,finish){ 
			var oneDay=1000*60*60*24, x=start.split("-"), y=finish.split("-"), dateOne=new Date(x[0],(x[1]-1),x[2]), 
				dateTwo=new Date(y[0],(y[1]-1),y[2]), month1=x[1]-1, month2=y[1]-1;
			var dateOneInMilliseconds=dateOne.getTime();
		    var dateTwoInMilliseconds=dateTwo.getTime();
			var differenceInMilliseconds=Math.abs(dateOneInMilliseconds-dateTwoInMilliseconds);
		    var totalDays=Math.round(differenceInMilliseconds/oneDay);
			
			return totalDays+1;
		},
		/*
			method:      cat.defaultTextToInputs()
			parameters:  receiver
			description: locates all inputs within the receiver and then applies default-text functionality to them.
						 tested and passed.
			usage: 	     cat.defaultTextToInputs($cat('.default-titles-and-rates').find('.cell-0,.cell-2'));
		*/
		defaultTextToInputs:function(receiver){
			receiver.find('input:text').each(function(){$cat(this).addClass('default-text');});	
			$cat('.default-text').focus(function(){
				with($cat(this)){if(val()==attr('title'))removeClass('default-text-active').val('');}
			}).blur(function(){with($cat(this)){if(val()=='')addClass('default-text-active').val(attr('title'));}}).blur();
		},
		/*
			method:      cat.extractIntegersFromDateString()
			parameters:  dateToSplit
			description: converts a date to a workable integer.
						 tested and passed.
			usage: 	     cat.extractIntegersFromDateString(strDate);
		*/
		extractIntegersFromDateString:function(dateToSplit){
		      var integerArray=dateToSplit.split("-");
		      return parseInt(integerArray[0]+integerArray[1]+integerArray[2]);
		},
        getImage:function(src,width,strClass,height){
                var attrs=' src="'+cat.root+'images/thumbnails/'+src+'.jpg"';
                attrs+=' width="'+width+'" height="'+height+'" alt="..." class="'+strClass+'"';
            
                return '<img'+attrs+' />';
        },
		/*
			method:      cat.outlook
			description: central repository for default outlook for components and parts.
		*/
		outlook:{
			dropdownList:function(receiver){
				receiver.css('color',Colors.blue).addClass('font-family-tahoma font-size-11');
			}
		},
		/*
			method: 	 cat.flu() (flu stands for First Letter to Uppercase)
			parameters:  str (the string to operate on)
			description: converts the first letter of parametized string to uppercase.
						 tested and passed.
		 	usage: 		 var str=cat.flu(str);
		 */
		flu:function(str){
			return str.slice(0,1).toUpperCase() + str.slice(1);
		},//cat.flu() ends here...	
		/*
			method: 	 cat.getRadioButtons()
			parameters:  txtArray, checkedIndex
			description: returns the txtArray indexes as a radio button and accompanying text. each accompanying text is a span classed after each index; 
						 each radio button stores the value of each index of txtArray.
						 //tested and passed.
		 	usage:		 var radioButtons=cat.getRadioButtons(['outstanding','pending','paid'],0);
		*/
		getRadioButtons:function(txtArray,checkedIndex){
			var output='';
			$cat(txtArray).each(function(intIndex){
				var checked=(intIndex==checkedIndex)?' checked="checked"':'';
				output+='<input type="radio" name="radio" value="'+this+'"'+checked+' /><span class="'+this+'">'+this+'</span>';
			});
			
			return output;
		},//cat.getRadioButtons() ends here...
		hiddenTextField:function(strClassName,value){
			return '<input type="hidden" class="'+strClassName+'" value="'+value+'" />';
		},//cat.hiddenTextField() ends here...		
		/*  
			method:      cat.icon()
			parameters:  properties (object)
			description: must generate an icon-string and return to caller. an icon string comprises of 
						 an image (small-sized), and a hyperlink. 
						 this method assumes that the image is located in the standard image directory.
						 tested and passed.
			useage:		 var icon=cat.icon({directory:cat.imageDir(),source:'register.jpg'});//returns an xhtml string.
						 var icon=cat.icon({directory:cat.imageDir(),source:'add.jpg',noText:'no text'});// no text...
		*/
		icon:function(properties){
			if(properties && properties.directory){
				if(properties.source){
					var source=properties.source, directory=properties.directory, 
						imageCss='border:1px solid '+this.color+';margin-right:2px;';
					var iconText=source.substring(0,source.lastIndexOf('.'));
					var image='<img src="'+directory+source+'" width="14" height="14" alt="" style="'+imageCss+'" />';
					var iconTextTitle=iconText;
					iconText=(properties.noText)?'':'<span>'+iconText+'</span>';
					
					return '<a href="javascript:" class="icon" title="'+iconTextTitle+'">'+image+iconText+'</a>';
				}
			}
		},//cat.icon() ends here...
		inputTextField:function(strClassName,value){
			return '<input type="text" class="textbox '+strClassName+'" value="'+value+'" />';
		},//cat.inputTextField() ends here...
		/*
			method:		 cat.isoDateToString()
			parameters:  properties (object)
			description: converts an iso date (0000-00-00) to string date Month Day, Year.
						 tested and passed.										 
			usage:       cat.line(); 
						 cat.line({color:Colors.lighterBlue,mode:'top',thickness:2});
		*/
		isoDateToString:function(strDate){
			var strYear=strDate.substr(0,4);
			var strMonth=strDate.substr(5,2);
			var strDay=strDate.substr(8,2);
			function monthToString(value){
				var output='';
				switch(value){
					case '01':output='January';break;
					case '02':output='February';break;
					case '03':output='March';break;
					case '04':output='April';break;
					case '05':output='May';break;
					case '06':output='June';break;
					case '07':output='July';break;
					case '08':output='August';break;
					case '09':output='September';break;
					case '10':output='October';break;
					case '11':output='November';break;
					case '12':output='December';break;
				}
				
				return output;
			};
			
			return monthToString(strMonth)+' '+strDay+', '+strYear;
		},	
		/*
			method:		 cat.isInt()
			parameters:  value
			description: tests whether value is an integer; returns true if so, else false
						 tested and passed.										 
			usage:       creditHours=(!cat.isInt(creditHours))?0:parseFloat(creditHours);
		*/
        isInt:function(value){  
                if((parseFloat(value)==parseInt(value)) && !isNaN(value))return true;
                else return false;
        },	
		/*
			method:		 cat.line()
			parameters:  properties (object)
			description: generates a line based on the properties specified.
						 tested and passed.										 
			usage:       cat.line(); 
						 cat.line({color:Colors.lighterBlue,mode:'top',thickness:2});
		*/
		line:function(properties){
			if(properties)
				var strMode=(properties.mode)?properties.mode:'border', strColor=(properties.color)?properties.color:this.color,
					thickness=(properties.thickness)?properties.thickness:1;
			else 
				var strMode='border', strColor=this.color, thickness=1;
			var line={};
			
			switch(strMode){
				case 'bottom': 
					line={'border-bottom':thickness+'px solid '+strColor}; 
				break;
				case 'bottom-right': 
					line={
						'border-bottom':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor
					}; 
				break;
				case 'bottom-right-top': 
					line={
						'border-bottom':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor,
						'border-top':thickness+'px solid '+strColor
					}; 
				break;
				case 'bottom-top': 
					line={
						'border-bottom':thickness+'px solid '+strColor,
						'border-top':thickness+'px solid '+strColor
					}; 
				break;
				case 'left':   
					line={'border-left':thickness+'px solid '+strColor}; 
				break;
				case 'left-bottom': 
					line={
						'border-left':thickness+'px solid '+strColor,
						'border-bottom':thickness+'px solid '+strColor
					}; 
				break;
				case 'left-bottom-right': 
					line={
						'border-left':thickness+'px solid '+strColor,
						'border-bottom':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor
					}; 
				break;
				case 'left-right': 
					line={
						'border-left':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor
					}; 
				break;
				case 'left-top-right': 
					line={
						'border-left':thickness+'px solid '+strColor,
						'border-top':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor
					}; 
				break;
				case 'off':   
					line={'border':'none'}; 							  
				break;
				case 'right': 
					line={'border-right':thickness+'px solid '+strColor}; 
				break;
				case 'top':   
					line={'border-top':thickness+'px solid '+strColor};   
				break;
				case 'top-right': 
					line={
						'border-top':thickness+'px solid '+strColor,
						'border-right':thickness+'px solid '+strColor
					}; 
				break;
				case 'top-left-bottom': 
					line={
						'border-top':thickness+'px solid '+strColor,
						'border-left':thickness+'px solid '+strColor,
						'border-bottom':thickness+'px solid '+strColor
					}; 
				break;
				default: 
					line={'border':thickness+'px solid '+strColor}; 
				break;
			}
			
			return line;
		},			
		/*
			method:      cat.margin() 
			parameters:  properties (mode, thick) 
			description: applies the CSS margin outlook to calling object/element.
						 tested and passed.
			usage:		 css(cat.margin({thickness:20}));
						 css(cat.margin({mode:'top-bottom',thickness:10}));
						 css(cat.margin({mode:'top-bottom'}));
		*/
		margin:function(properties){
			if(properties)
				var strMode=(properties.mode)?properties.mode:'margin', 
					thickness=(properties.thickness)?properties.thickness+'px':'2px';
			else var strMode='margin', thickness='2px';
			var cssMargin={};		
			var marginObjects=[
				{'margin-bottom':thickness},//bottom
				{'margin-left':thickness},//left
				{'margin-right':thickness},//right
				{'margin-top':thickness},//top
				{'margin':thickness},//default
				{'margin':'0 '+thickness},//left-right
				{'margin':thickness+' 0'},//top-bottom
				{'margin':thickness+' auto'},//top-bottom-auto
				{'margin':thickness+' 0 0 '+thickness},//top-left
				{'margin':thickness+' '+thickness+' 0 0'},//top-right
				{'margin':thickness+' '+thickness+' 0 '+thickness},//top-left-right
				{'margin':'0 '+thickness+' '+thickness+' 0'}//bottom-right
			];
			
			$cat([
				'bottom',
				'left',
				'right',
				'top',
				'margin',
				'left-right',
				'top-bottom',
				'top-bottom-auto',
				'top-left',
				'top-right',
				'top-left-right',
				'bottom-right'
			]).each(function(index){if(strMode==this)cssMargin=marginObjects[index];});
			
			return cssMargin;
		},//cat.margin() ends here...
		/*
			method:      cat.pad() 
			parameters:  properties (mode, thickness) 
			description: applies the CSS padding outlook to calling object/element.
						 tested and passed.
			usage:		 css(cat.pad({thickness:20}));
						 css(cat.pad({mode:'top-bottom',thickness:10}));
						 css(cat.pad({mode:'top-bottom'}));
		*/
		pad:function(properties){
			if(properties)
				var strMode=(properties.mode)?properties.mode:'padding', 
					thickness=(properties.thickness)?properties.thickness+'px':'2px';
			else var strMode='padding', thickness='2px';
			var cssPadding={};			
			var paddingObjects=[
				{'padding-bottom':thickness},//bottom
				{'padding-left':thickness},//left
				{'padding-right':thickness},//right
				{'padding-top':thickness},//top
				{'padding':thickness},//default
				{'padding':'0 '+thickness},//left-right
				{'padding':thickness+' 0'}//top-bottom
			];			
			$cat(['bottom','left','right','top','padding','left-right','top-bottom'])
				.each(function(index){if(strMode==this)cssPadding=paddingObjects[index];});
			
			return cssPadding;
		},//cat.pad() ends here...		
		/*  
			method:      cat.panel()
			parameters:  cat (object)
			description: coverts a container into a dual-panelled structure.
						 a dual panel consists of a left and right div, each classed accordingly, as well as a clear-float div.
						 tested and passed.
			useage:		 cat.panel({container:containerToPanel,widthLeft:40}); or... 
						 cat.panel({container:containerToPanel});
		*/
		panel:function(cat){
			if(cat && cat.container){
				var widthLeft=(cat.widthLeft && cat.widthLeft>10 && cat.widthLeft<91)?
					cat.widthLeft:this.widthLeft, widthRight=100-widthLeft-1;
				var leftPanel='<div class="left-panel float-left" style="width:'+widthLeft+'%;">width:'+widthLeft+'%</div>';
				var rightPanel='<div class="right-panel float-right" style="width:'+widthRight+'%;">width:'+widthRight+'%</div>';
				$cat(cat.container).html(leftPanel+rightPanel+this.clearBoth);
			}
		},//cat.panel() ends here...
		/*
		 	method: 	 cat.roundOff()
		 	parameters:  numberToRoundOff, decimalPlaces
		 	description: rounds off the parametized number to the required decimal places.
		 				 tested and passed.
		*/
		roundOff:function(numberToRoundOff,decimalPlaces){
			var decimal=1;
			for(var x=1;x<=decimalPlaces;x++)decimal*=10;
			var output=(Math.round(numberToRoundOff*decimal)/decimal).toFixed(decimalPlaces);
			return output;
		},//cat.roundOff() ends here...			   
		/*
			 method: 	  cat.signature()
			 parameters:  strColor
			 description: generates author's signature into the current document.
			 			  if a color is specified, the signature is generated in that color. default color is gray.
						  tested and passed.
			 usage: 	  cat.signature();
		*/
		signature:function(strColor){
			$cat('.signature').remove();
			var colorStyle=(strColor)?' style="color:'+strColor+';"':'';
			$cat(document.body)
            .append("<p class='signature font-size-9 text-center'"+
            colorStyle+">programmer-analyst: <span style='color:"+Colors.gray
			+"'>chigozie (cat) nduanya</span></p>");
		},
		/*  method:      cat.slideshow()
			parameters:  container
			description: generates preset functionality as a slideshow... continous process.
						 tested and passed.
			usage:			
			pageHeaderAnimation:function(){	
				var baseText="Innovative workforce management solutions for today's world!";		
				$cat('.bar .left-panel').find('p').html(baseText)
					.addClass('page-header-animation font-weight-bold').css('color',Colors.brown);
				
				//slideshow variables
				cat.textArray=[
					baseText,
					'WHY CHOOSE CITY EMPLOYMENT AGENCY?',
					'AND HOW ARE YOU DIFFERENT FROM THE OTHERS?',
					'&bull;&nbsp;Respected Placement company',
					'&bull;&nbsp;Solid history and legacy within the staffing industry',
					'&bull;&nbsp;Innovative recruiting and retention strategies',
					'&bull;&nbsp;Broad spectrum of employment and management solutions',
					'&bull;&nbsp;Dedicated branch network that is 100% company owned',
					'&bull;&nbsp;Strong financial stability',
					'&bull;&nbsp;Exemplary reputation for integrity',
					'&bull;&nbsp;Proven quality system'
				];
				cat.colorArray=[
					Colors.brown,'#024769','#DE9D7F','#AFD775','#BD2031',
					'#EFD279','#95CBE9','#024769','#DE9D7F','#AFD775','#BD2031'
				];
				
				//slideshow parts
				cat.counter=0;
				cat.milliseconds=cat.fadeTime;
				
				//action
				cat.action=function(){	
					var textArray=cat.textArray;
					var colorArray=cat.colorArray;
					$cat('.slideshow').html(textArray[cat.counter]).css('color',colorArray[cat.counter]);
					$cat('.page-header-animation').fadeOut('slow').fadeIn('slow');
					cat.counter+=1;
					if(cat.counter==textArray.length)cat.counter=0;
				}
				
				//cat call
				cat.slideshow($cat('.page-header-animation'));
			}
		*/
		slideshow:function(container){
			$cat('.slideshow').remove();
			
			function slideshow(){
				container.html('<span class="slideshow"></span>');
				slideshowTick();
			}
			
			slideshow();
		},		
		/*
			method: 	 cat.start()
			parameters:  -
			description: applies the default body style (font-family, font-size etc)
						 tested and passed.
			usage:       cat.start();
		*/
		start:function(styling){		
			this.clearBoth='<div class="clear-both"></div>';
			this.color=Colors.lighterBlue;
			this.fadeTime=5000;
			this.widthLeft=20;	
            if(styling==null)$cat(document.body).addClass('font-family-tahoma font-size-13');
		},//cat.start() ends here...		
		/*
			method: 	 cat.strip()
			parameters:  properties (string, characters - retrieved from cat.characters()) 
			description: strips unwanted characters from the specified string. this method
						 retains the characters in the specified list of allowable characters
						 and strips off the rest.
						 tested and passed.
			usage:		 var string='.001weWE-@~|';
						 var characters=cat.characters('numeric');
						 string=cat.strip({string:string,characters:characters});
		*/
		strip:function(data){
			if(data.characters){
				for(var i=0,output='',valid=data.characters;i<data.string.length;i++)
					if(valid.indexOf(data.string.charAt(i))!=-1)
						output+=data.string.charAt(i);
				return output;
			}
		},	
		/*
			method: 	 cat.toNumericField(valueToBeConvertedToNumericField)
			parameters:  valueToBeConvertedToNumericField 
			description: converts the parametized value to a numeric field which
                         can then be used to perform calculations.
						 tested and passed.
			usage:		 trvRate=cat.toNumericField(trvRate);
		*/
		toNumericField:function(valueToBeConvertedToNumericField){
		      var value=this.strip({string:valueToBeConvertedToNumericField,
                    characters:this.characters('numeric')});
		      
              return (value=='')?0:parseFloat(value);
        },	
		/*
			method: 	 cat.updateNumericCellForNegativeValue(cell,numericValue)
			parameters:  cell - the cell to update, numericValue
			description: updates parametized numeric cell with value, then tones its
                         text according to negativity of value.
						 tested and passed.
			usage:		 var cell=$cat('.total-cell:last');
                         cat.updateNumericCellForNegativeValue(cell,0);
		*/
        updateNumericCellForNegativeValue:function(cell,numericValue){
                cell.html(cat.currency(numericValue));
                cell.removeClass('color-green');
                if(numericValue<0)cell.addClass('color-green');
        }
	};
}//Cat() ends here...

/* declarations: */
var cat=new Cat(), intCounter=0, objTimer, timerIsOn=0;

/*
 	function:    tick()
 	parameters:  -
 	description: displays the current time in 24-hr format. please ensure that a container of class clock-display 
		  		 is already created before calling this function.
 				 tested and passed.
*/
function tick(){
	var date=new Date(), hour=date.getHours(), minute=date.getMinutes(), second=date.getSeconds();    
	minute=(minute<10)?'0'+minute:minute; 
	second=(second<10)?'0'+second:second; 
	second='<span>'+second+'</span>';
	$cat('.clock-display').html(hour+':'+minute+':'+second+' hrs');
	
	timerObject=setTimeout('tick()',500);
}//tick() ends here...

var cat=new Cat(), intCounter=0, objTimer, timerIsOn=0;

/*
 	function:    slideshowTick()
 	parameters:  -
 	description: generates a slideshow.
 				 tested and passed.
*/
function slideshowTick(){
	cat.action();	
	timerObject=setTimeout('slideshowTick()',cat.milliseconds);
}//slideshowTick() ends here...
/*
	function: Timer
	parameters:  properties (should have a processor() property)
	description: sets the property parameter as its own, then defines a
				 timeout() method to execute the properties processor after
				 a specific # of milliseconds.
				 tested and passed.
	usage:		 new Timer({
					 processor:function(){$cat('.folder-content').html(feedback);}
				 }).timeout(cat.fadeTime-4000);
*/
function Timer(properties){
	this.properties=properties;
	
	this.processor=function(){
		this.properties.processor();
	};//Timer().processor() ends here...
	
	this.timeout=function(milliseconds){
		var self=this;
		setTimeout(function(milliseconds){self.processor();},milliseconds);
	};//Timer().timeout() ends here...
}//Timer() ends here...
			
function TimedCount(){
	cat.container.html('<p class="text-center color-white">'+cat.msgLoading+' '+intCounter+'%</p>');
	intCounter=intCounter+5;
	if(parseInt(intCounter)>100){
		stopCatTimer();
		cat.container.html('<p class="text-center color-white">'+cat.msgLoaded+'</p>');
		new Timer({
			processor:function(){
				cat.timedCountFeedback();
			}
		}).timeout(cat.timedCountMilliseconds);
	}else objTimer=setTimeout("TimedCount()",100);
}//TimedCount() ends here...
function CatTimer(){
	if(!timerIsOn){
		timerIsOn=1;
		TimedCount();
	}
}
function stopCatTimer(){
	clearTimeout(objTimer);
	timerIsOn=0;
	intCounter=0;
}
/* cat extensions */
cat.addNonRequiredInputValuesToCatData=function(){			
	//task) append fields that are not on the required list; morph the canvas
	$cat('td:has(input:text)').each(function(){
		if($cat(this).is(':not(".required")')){
			cat.data+='&'+$cat(this).attr('title')+'='+$cat(this).find('input:text').val();
		}
	});					
};//cat.addNonRequiredInputValuesToCatData() ends here...
				
cat.dbTables=function(){
	$cat(document.body).html('<p>Generating database tables. Please wait...</p>');
	function serverCall(){
		cat.feedback=function(feedback){
			$cat(document.body).html('<p class="font-weight-bold">ServerCat status report</p><p>'+feedback+'</p>');
		};			
		cat.server('db tables');
	}
	
	new Timer({processor:function(){serverCall();}}).timeout(cat.fadeTime-2000);
};//cat.dbTables() ends here...

cat.imageDir=function(application){
	var imageDir=(application)?cat.root+'images/'+application+'/':cat.root+'images/';
	return imageDir;
};//cat.imageDir() ends here...

//parameters: cat.data='&field='+value (not mandatory), 
//cat.feedback=function(){} (not mandatory to have content within)
cat.server=function(serverKey){
	var key=(serverKey)?serverKey:'', data=(cat.data)?cat.data:'', root=cat.root;
	$cat.ajax({
		type:   'POST',
		url:    'processor.php',
		data:   'cat='+root+'&key='+key+data,
		success:function(feedback){cat.feedback(feedback);}
	});
};
function emailAddressBasicValidation(str){
	var at="@", dot=".", lat=str.indexOf(at), lstr=str.length;
    var ldot=str.indexOf(dot), msg='Sorry, invalid email address!';
	if(str.indexOf(at)==-1){
	   alert(msg);
	   return false;
	}	
	if(str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr){
	   alert(msg);
	   return false;
	}	
	if(str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr){
	    alert(msg);
	    return false;
	}
	if (str.indexOf(at,(lat+1))!=-1){
	    alert(msg);
	    return false;
	}
	if(str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot){
	    alert(msg);
	    return false;
	}
	if(str.indexOf(dot,(lat+2))==-1){
	    alert(msg);
	    return false;
	}
	if(str.indexOf(" ")!=-1){
	    alert(msg);
	    return false;
	}
	
	return true;					
};
function getCatSpaces(){return '<span class="cat-spaces">&nbsp;</span>';}

function keyPress(e){
	var keyId=(window.event)?event.keyCode:e.keyCode;
	switch(keyId){
		case 16: $cat('.key-pressed').val('Shift'); break; 
		case 17: $cat('.key-pressed').val('Ctrl');  break;
		case 18: $cat('.key-pressed').val('Alt');   break;
		case 19: $cat('.key-pressed').val('Pause'); break;
		case 37: $cat('.key-pressed').val('left');  break;
		case 38: $cat('.key-pressed').val('up');    break;
		case 39: $cat('.key-pressed').val('right'); break;
		case 40: $cat('.key-pressed').val('down');  break;
	}
	if($cat('.key-pressed').val()!=''){
		if(cat.focusedElement!=null){
			cat.keyPressedFeedback();
			cat.focusedElement=null;
		}
		$cat('.key-pressed').val('');
	}
};
function postalCodeBasicValidation(textfield){
    var regEx=/[a-zA-Z][0-9][a-zA-Z](-| |)[0-9][a-zA-Z][0-9]/;
    return (regEx.test(textfield.val()))?true:false;
};
cat.validateRequiredFields=function(){
	var requiredFieldIsEmpty=false;
	var allValidated=true;
	$cat('.required').each(function(){
		if($cat(this).find('input:text').length>0){
			if($cat(this).find('input:text').val()==''){
				alert('Sorry, '+$cat(this).attr('title')+' required!');
				$cat(this).find('input:text').focus();
				requiredFieldIsEmpty=true;
				
				return false;
			}else{
				if($cat(this).attr('title')=='Postal Code'){
					var postalCodeTextfield=$cat(this).find('input:text');
					if(!postalCodeBasicValidation(postalCodeTextfield)){
						alert('Sorry, the postal code you entered is invalid!');
						postalCodeTextfield.val('');
						postalCodeTextfield.focus();
						allValidated=false;
						
						return false;
					}
				}else if($cat(this).attr('title')=='Email'){
					var emailTextfield=$cat(this).find('input:text');
					if(emailAddressBasicValidation(emailTextfield.val())==false){
						emailTextfield.val('');
						emailTextfield.focus();
						allValidated=false;
						
						return false;
					}
				} 
				if(allValidated){
					var title=$cat(this).attr('title');
					if(title=='First Name')title='First_Name';
					else if(title=='Last Name')title='Last_Name';
					else if(title=='Postal Code')title='Postal_Code';
					else if(title=='Cell Phone')title='Cell_Phone';
					cat.data+='&'+title+'='+$cat(this).find('input:text').val();
				}
			} 
		}else{
			if($cat(this).find('select').val()==''){
				alert('Sorry, Province required!');
				$cat(this).find('select').focus();
				requiredFieldIsEmpty=true;
				
				return false;
			}else cat.data+='&Province='+$cat(this).find('select').val();
		}
	});
	if(!requiredFieldIsEmpty && allValidated){					
		//1) append fields that are not on the required list; morph the canvas
		$cat('td:has(input:text)').each(function(){
			if($cat(this).is(':not(".required")')){
				cat.data+='&'+$cat(this).attr('title')+
                '='+$cat(this).find('input:text').val();
			}
		});
		cat.currentWaitingMsg='Processing profile';					
		cat.canvasMetamorphosis();
		
		//2) server call	
		cat.server(cat.key);
	}
};
var MessageCenter={
	addToServerString:function(field,value){cat.data+='&'+field+'='+value;},
	submitMessage:function(){
		if($cat('.message-center-fields').length>0){
			cat.data='';
			var fields=$cat('.message-center-fields').val().split('^');
			var itsOkayToProceed=true;
			
			$cat(fields).each(function(){
				var row=$cat('.field[value="'+this+'"]').parent().parent();
				var caption=row.find('td:first').text();
				caption=caption.substring(0,caption.length-1);
				
				if(row.find('input:text').length>0){
					var value=row.find('input:text').val();
					if(value==''){
						alert('Sorry, '+caption+' required!');
						row.find('input:text').focus();
						itsOkayToProceed=false;
						
						return false;
					}
					else MessageCenter.addToServerString(this,value);
				}
				else if(row.find('textarea').length>0){
					var value=row.find('textarea').val();
					if(value==''){
						alert('Sorry, '+caption+' required!');
						row.find('textarea').focus();
						itsOkayToProceed=false;
						
						return false;
					}
					else MessageCenter.addToServerString(this,value);
				}
				else{
					var value=row.find('td:last').text();
					MessageCenter.addToServerString(this,value);
				} 
			});
			if(itsOkayToProceed){
				cat.extraData=cat.data+'&to='+$cat('.to-field').text();
				cat.task='Message Successfully Submitted!';
				Software.meow();
			}
		}
	}
};
