/*
Typecast 1.4 (release)
by Ara Pehlivanian (http://arapehlivanian.com)

This work is licensed under a Creative Commons Licence
http://creativecommons.org/licenses/by-nd/2.5/
*/

var Typecast = {
	InitMask : false,
	
	Init : function(){
		this.Parse(document.body.getElementsByTagName("input"));
		this.Behaviours.Mask.Init();
	},
	
	Parse : function(nodes){
		
		for(var i=0; i<nodes.length; i++){
			var node = nodes[i];

			if(node.type=="text" && node.className && node.className.indexOf("TC") != -1){
				if(!node.id) Typecast.Utils.GenerateID(node);
				
				var behaviourName = (node.className.indexOf("[") != -1) ? node.className.substring(node.className.indexOf("TC")+2, node.className.indexOf("[")) : node.className.substring(node.className.indexOf("TC")+2, node.className.length);
				
				Typecast["Init" + behaviourName] = true;
				Typecast.Behaviours[behaviourName].InitField(node);

				node.onfocus = Typecast.Behaviours[behaviourName].Run;
				node.onkeyup = Typecast.Behaviours[behaviourName].KeyHandler;
				node.onkeydown = Typecast.Behaviours[behaviourName].KeyHandler;
				node.onblur = Typecast.Behaviours[behaviourName].Stop;
				node.onmouseup = Typecast.Behaviours[behaviourName].MouseUp;
			}
		}
	},
	
	Behaviours : {
		Mask : {
			Init : function(){
			},
			
			InitField : function(field){
				var fieldData = [];
				if(!eval("Typecast.Config.Data.Mask.Masks." + field.id)){
					fieldData = field.className.substring(field.className.indexOf("[")+1, field.className.indexOf("]"))
				}else{
					fieldData = eval("Typecast.Config.Data.Mask.Masks." + field.id);
				}
				Typecast.Behaviours.Mask.ParseFieldData(field, fieldData);
				if(field.value == ''){
					field.value = field.DefaultText.join("");
				}
			},
			
			Run : function(e){
				e = (!e) ? window.event : e;
			},
			
			Stop : function(){
			},
			
			KeyHandler : function(e){
				e = (!e) ? window.event : e;
				var mask = Typecast.Behaviours.Mask;
				
				mask.CursorManager.TabbedInSetPosition(this);
				
				//Backspace
				if(e.keyCode==8 && e.type=="keydown" && this.AllowInsert){
					var preBackspaceCursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
					mask.CursorManager.Move(this, -1);
					var postBackspaceCursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
					
					if(preBackspaceCursorPosition != postBackspaceCursorPosition) mask.DataManager.RemoveCharacterByShiftLeft(this);
					mask.Render(this);
				}
				
				//Tab
				if(e.keyCode==9 && e.type=="keydown"){
					return
				}
				
				//Enter
				else if(e.keyCode==13 && e.type=="keydown"){
					return
				}
				
				//Esc
				else if(e.keyCode==27 && e.type=="keyup"){
				}
				
				//End
				else if(e.keyCode==35 && e.type=="keydown"){
					var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(this, this.DataIndex[this.DataIndex.length-1], 1);
					Typecast.Behaviours.Mask.CursorManager.SetPosition(this, startIdx);
				}

				//Home
				else if(e.keyCode==36 && e.type=="keydown"){
					Typecast.Behaviours.Mask.CursorManager.SetPosition(this, this.MaskIndex[0]);
				}
				
				//Left or Up
				else if(e.keyCode==37 && e.type=="keydown" || e.keyCode==38 && e.type=="keydown"){
					mask.CursorManager.Move(this, -1);
				}
				
				//Right or Down
				else if(e.keyCode==39 && e.type=="keydown" || e.keyCode==40 && e.type=="keydown"){
					mask.CursorManager.Move(this, 1);
				}
				
				//Insert
				else if(e.keyCode==45 && e.type=="keydown" && this.AllowInsert){
					mask.CursorManager.ToggleInsert(this);
				}
				
				//Delete
				else if(e.keyCode==46 && e.type=="keydown"){
					if(this.InsertActive){
						mask.DataManager.RemoveCharacterByShiftLeft(this);
					}else{
						mask.DataManager.RemoveCharacterByOverwrite(this);
					}
					mask.Render(this);
				}
				
				//Numeric Characters
				else if((mask.MaskManager.CurrentMaskCharacter(this) == Typecast.Config.Settings.Mask.MaskCharacters.Numeric) && (e.keyCode>=48 && e.keyCode<=57 && e.type=="keydown" || e.keyCode>=96 && e.keyCode<=105 && e.type=="keydown")){
					var keycode = parseInt(e.keyCode);
					keycode = (keycode>=96 && keycode<=105) ? keycode-48 : keycode;

					mask.DataManager.AddData(this, String.fromCharCode(keycode));
					mask.Render(this);
					mask.CursorManager.Move(this, 1);
				}
				
				//Alpha Characters 65 - 90
				else if((mask.MaskManager.CurrentMaskCharacter(this) == Typecast.Config.Settings.Mask.MaskCharacters.Alpha) && (e.keyCode>=65 && e.keyCode<=90 && e.type=="keydown")){
					mask.DataManager.AddData(this, String.fromCharCode(e.keyCode));
					mask.Render(this);
					mask.CursorManager.Move(this, 1);
				}
				
				//Refresh
				else if(e.keyCode==116 && e.type=="keydown"){
					return
				}
				
				else{
				}
				return false
			},
			
			MouseUp : function(e){
				e = (!e) ? window.event : e;
				var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
				var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(this, cursorPosition, 0);
				Typecast.Behaviours.Mask.CursorManager.SetPosition(this, startIdx);
			},
			
			ParseFieldData : function(field, fieldData){
				fieldData = fieldData.split(Typecast.Config.Settings.Mask.FieldDataSeparator);
				field.Data = [];
				field.DataIndex = [];
				field.DefaultText = (fieldData[1]) ? fieldData[1].split("") : fieldData[0].split(""); //if default text isn't provided use mask
				field.Mask = this.MaskManager.ParseMask(fieldData[0]);
				field.MaskIndex = this.MaskManager.ParseMaskIndex(field.Mask);
				field.CursorPersistance = [];
				field.InsertActive = (this.MaskManager.IsComplexMask(field)) ? false : true;
				field.HighlightChar = (this.MaskManager.IsComplexMask(field)) ? true : false;
				field.AllowInsert = (this.MaskManager.IsComplexMask(field)) ? false : true;
			},
			
			MaskManager : {
				ParseMask : function(mask){
					var arr = [];
					var maskCharacters = Typecast.Config.Settings.Mask.MaskCharacters;
					for(var i=0; i<mask.length; i++){
						for(maskCharacter in maskCharacters){
							char = eval("maskCharacters." + maskCharacter);
							if(mask.substring(i, i+1) == char){
								arr[i] = char;
							}
						}
					}
					return arr
				},
				
				ParseMaskIndex : function(mask){
					var arr = [];
					for(var i=0; i<mask.length; i++){
						if(mask[i] != null) arr[arr.length] = i;
					}
					return arr;
				},
				
				CurrentMaskCharacter : function(field){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					return field.Mask[cursorPosition];
				},
				
				FindNearestMaskCharacter : function(field, cursorPosition, dir){
					var nearestMaskCharacter = (field.DataIndex.length > 0) ? cursorPosition : field.MaskIndex[0];
					
					switch(dir){
						case -1:
							for(var i=field.DataIndex.length-1; i>-1; i--){
								if(field.DataIndex[i] < cursorPosition){
									nearestMaskCharacter = field.DataIndex[i];
									break;
								}
							}
						break;
						case 0:
							for(var i=0; i<field.DataIndex.length; i++){
								if(field.MaskIndex[i] >= cursorPosition){
									nearestMaskCharacter = field.MaskIndex[i];
									break;
								}else{
									nearestMaskCharacter = field.MaskIndex[field.DataIndex.length];
								}
							}							
						break;
						case 1:
							for(var i=0; i<field.DataIndex.length; i++){
								if(field.DataIndex[i] > cursorPosition){
									nearestMaskCharacter = field.DataIndex[i];
									break;
								}
							}
							if(cursorPosition == field.MaskIndex[field.MaskIndex.length-1]) nearestMaskCharacter = cursorPosition + 1;
							else if(cursorPosition == field.DataIndex[field.DataIndex.length-1]) nearestMaskCharacter = field.MaskIndex[field.DataIndex.length];
						break;
					}
					return nearestMaskCharacter
				},
				
				IsComplexMask : function(field){//reports if mask contains mixed mask characters... can't perform "insert" if true
					var isComplex = false;
					var previousMaskChar = "";
					for(var i=0; i<field.MaskIndex.length; i++){
						var currentMaskChar = field.Mask[field.MaskIndex[i]];
						if(currentMaskChar != previousMaskChar && previousMaskChar != ""){
							isComplex = true;
						}
						previousMaskChar = currentMaskChar;
					}
					return isComplex
				}
			},
			
			CursorManager : {
				Move : function(field, dir){
					var cursorPosition = this.GetPosition(field)[0];
					var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(field, cursorPosition, dir);
					this.SetPosition(field, startIdx);
				},
				GetPosition : function(field){
					var arr = [0,0];
					if(field.selectionStart && field.selectionEnd){
						arr[0] = field.selectionStart;
						arr[1] = field.selectionEnd;
					}
					else if(document.selection){
						var range = field.createTextRange();
						range.setEndPoint("EndToStart", document.selection.createRange());
						arr[0] = range.text.length;
						arr[1] = document.selection.createRange().text.length;
					}
					return arr
				},
				SetPosition : function(field, startIdx){
					var endIdx = startIdx + ((field.HighlightChar) ? 1 : 0);
					Typecast.Utils.PartialSelect(field, startIdx, endIdx);
				},
				TabbedInSetPosition : function(field){
					var mask = Typecast.Behaviours.Mask;
					
					if(mask.MaskManager.CurrentMaskCharacter(field) == undefined){
						var startIdx = null;
						if(field.DataIndex.length > 0 && field.DataIndex.length != field.MaskIndex.length){
							startIdx = field.MaskIndex[field.DataIndex.length];
						}
						else if(field.DataIndex.length == field.MaskIndex.length){
							startIdx = field.DataIndex[field.DataIndex.length-1] + 1;
						}
						else{
							startIdx = field.MaskIndex[0];
						}
						this.SetPosition(field, startIdx);
					}
				},
				PersistPosition : function(field){
					field.CursorPersistance = this.GetPosition(field);
				},
				RestorePosition : function(field){
					this.SetPosition(field, field.CursorPersistance[0]);
				},
				ToggleInsert : function(field){
					if(field.InsertActive){
						field.InsertActive = false;
						field.HighlightChar = true;
					}else{
						field.InsertActive = true;
						field.HighlightChar = false;
					}
					var startIdx = this.GetPosition(field)[0];
					this.SetPosition(field, startIdx);
				}
			},
			
			DataManager : {
				AddData : function(field, char){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					if(field.InsertActive){
						this.InsertCharacter(field, char);
					}else{
						this.OverwriteCharacter(field, char, cursorPosition);
					}
					this.UpdateDataIndex(field);
				},
				InsertCharacter : function(field, char){
					var lastCharacterPosition = field.MaskIndex[field.MaskIndex.length-1];
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					for(var i=lastCharacterPosition; i>=currentCharacterPosition; i--){
						field.Data[field.MaskIndex[i+1]] = field.Data[field.MaskIndex[i]];
					}
					field.Data[field.MaskIndex[currentCharacterPosition]] = char;
				},
				OverwriteCharacter : function(field, char, cursorPosition){
					field.Data[cursorPosition] = char;
				},
				RemoveCharacterByOverwrite : function(field){
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					if(currentCharacterPosition != null){
						field.Data[field.DataIndex[currentCharacterPosition]] = "";
					}
				},
				RemoveCharacterByShiftLeft : function(field){
					var lastCharacterPosition = field.DataIndex[field.DataIndex.length-1];
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					
					if(currentCharacterPosition != null && lastCharacterPosition >= cursorPosition){
						for(var i=currentCharacterPosition; i<=lastCharacterPosition; i++){
							field.Data[field.DataIndex[i]] = field.Data[field.DataIndex[i+1]];
						}
						field.Data.length = field.Data.length-1;
						this.UpdateDataIndex(field);
					}
				},
				UpdateDataIndex : function(field){
					field.DataIndex.length = 0;
					for(var i=0; i<field.Data.length; i++){
						if(field.Data[i] != undefined) field.DataIndex[field.DataIndex.length] = i;
					}
				},
				CurrentDataIndexPosition : function(field){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					var currentDataIndexPosition = null;
					for(var i=0; i<field.MaskIndex.length; i++){
						if(field.MaskIndex[i] == cursorPosition){
							currentDataIndexPosition = i;
							break;
						}
					}
					return currentDataIndexPosition
				}				
			},
			
			Render : function(field){
				this.CursorManager.PersistPosition(field);
				var composite = [];
				for(var i=0; i<field.Mask.length; i++){
					composite[i] = field.Mask[i];
					if(field.DefaultText[i]) composite[i] = field.DefaultText[i];
					if(field.Data[i]) composite[i] = field.Data[i];
				}
				field.value = composite.join("")
				
				this.CursorManager.RestorePosition(field);
			}
		},
		
		Suggest : {
			BuildHighlightedResult : function(str, fragment){
			}
		}
	},
	
	Utils : {
		FindClass : function(findClass, parentClass){
			parentClass = (!parentClass) ? 'Typecast' : parentClass;
			for(var i in eval(parentClass)){
				if(i == findClass){
					return i;
				}else{
					var found = Typecast.Utils.FindClass(findClass, parentClass + "." + i);
					// if something was found send it back up the tree
					// if the parentClass contains no dots it's the rootClass and should also be tacked on and returned
					if(found) return (parentClass.indexOf(".") == -1) ? parentClass + "." + i + "." + found : i + "." + found;
				}
			}
		},
		
		GenerateID : function(obj){
			dt = new Date();
			obj.id = "GenID" + dt.getTime();
			return obj
		},
		
		GetXY : function(obj){
			var x=0;
			var y=0;
			while(obj.offsetParent){
				x+=obj.offsetLeft;
				y+=obj.offsetTop;
				obj = obj.offsetParent;
			}
			return [x,y]
		},
		
		PartialSelect : function(field, startIdx, endIdx){
			if(field.createTextRange){
				var fld= field.createTextRange();
				fld.moveStart("character", startIdx);
				fld.moveEnd("character", endIdx - field.value.length);
				fld.select();
			}else if(field.setSelectionRange){
				field.setSelectionRange(startIdx, endIdx);
			}
		}		
	}
}

/*
Typecast 1.4 (release)
by Ara Pehlivanian (http://arapehlivanian.com)

This work is licensed under a Creative Commons Licence
http://creativecommons.org/licenses/by-nd/2.5/
*/

Typecast.Config = {
	Settings : {
		Mask : {
			FieldDataSeparator : ",",
			MaskCharacters : {
				Numeric : "#",
				Alpha : "A"
			},
			AllowInsert : true,
			DisplayMaskCharacters : false //Display mask characters when default text is not present
		}
	},
	
	Data : {
		Mask : {
			Masks : {
				fax : "(###) ###-####,(   )    -    "
			}
		}
	}
}
