/*******************************************************************************
 CapsLockWarning.js        Adds a "caps-lock is on" warnings to password fields
 ------------------------------------------------------------------------------
 Adapted from                                     FormTools.addCapsLockWarnings
 Info/Docs         http://www.brothercake.com/site/resources/scripts/formtools/
 ------------------------------------------------------------------------------
*******************************************************************************/




//caps-lock warning constructor
function CapsLockWarning(passfield)
{
	//if the browser is unsupported, silently fail
	//[pre-DOM1 browsers generally, and Opera 8 specifically]
	if(typeof document.getElementById == 'undefined'
		|| typeof document.styleSheets == 'undefined') { return false; }

	//or if the passfield doesn't exist, or it already has a caps-lock warning, silently fail
	if(passfield == null || typeof passfield._capslockwarning != 'undefined') { return false; }
	
	//create a context wrapper, so that we have sole context for modifying the content
	var contextwrapper = this.createContextWrapper(passfield);

	//now create a caps-lock warning inside that, 
	//and save a reference to it as a property of the passfield
	//so that we can easily refer to it from a passfield event
	var warningnode = contextwrapper.appendChild(document.createElement('strong'));
	passfield._capslockwarning = warningnode;		
			
	//set the warning element's class, and add its default message and tooltip
	//(the visible message is styled away by default using text-indent
	// but can be made visible again for a more obvious effect;
	// but it can't contain HTML because it's be used to set the title)
	warningnode.className = 'capslock-warning';
	warningnode.appendChild(document.createTextNode('Caps-lock is ON!'));
	warningnode.setAttribute('title', warningnode.firstChild.nodeValue);
	
	//save a reference to this
	var self = this;

	//now bind a keypress listener to the password field
	//to work out when the warning message needs to be shown
	//we can't actually know when caps-lock is enabled or not, but we can infer 
	//that it's enabled when an uppercase letter is entered but shift is not pressed
	this.addListener(passfield, 'keypress', function(e)
	{
		//get the passbox reference, and a shortcut to its capslock warning element
		var passboxref = self.getTarget(e),
			warningnode = passboxref._capslockwarning;
			
		//get the character code (or keycode, which some browsers
		// incorrectly set with what should be charcode)
		var charcode = e.charCode;
		if(typeof charcode == 'undefined') { charcode = e.keyCode; }
		
		//get the character that this keycode represents
		var character = String.fromCharCode(charcode);
		
		//if the character is an upper-case letter but the shift-key is not pressed
		//then we infer from that that the caps-lock key is be pressed
		//(we only have to check A-Z because caps-lock doesn't affect any other characters)
		//so then show the warning element and its corresponding tooltip
		if(/^[A-Z]$/.test(character) && !e.shiftKey)
		{
			warningnode.style.display = 'block';
		}
		//otherwise, if it's currently displayed then hide it and remove the tooltip
		else if(warningnode.style.display == 'block')
		{
			warningnode.style.display = 'none';
		}
	});
	
	//then bind a blur handler to hide the warning message, if it's shown at the time
	this.addListener(passfield, 'blur', function(e)
	{
		//get the passbox reference, and a shortcut to its capslock warning element
		var passboxref = self.getTarget(e),
			warningnode = passboxref._capslockwarning;
		
		//if its caps-lock warning is visible, hide it now
		if(warningnode.style.display == 'block')
		{
			warningnode.style.display = 'none';
		}
	});
	
	//now bind a keydown handler for the caps lock key itself
	//we can't actually know whether it's being turned on or off
	//but if we alredy have a visible caps-lock warning when it's pressed
	//then we can know at that point that it's being disabled, so we can hide the message
	//but this is supplemental functionality, it's not crucial
	//which is good because it doens't work in opera or mac/webkit!
	this.addListener(passfield, 'keydown', function(e)
	{
		//get the passbox reference, and a shortcut to its capslock warning element
		var passboxref = self.getTarget(e),
			warningnode = passboxref._capslockwarning;
		
		//if caps-lock was just pressed, and the field's
		//caps-lock warning is visible, then hide it now
		if(e.keyCode == 20 && warningnode.style.display == 'block')
		{
			warningnode.style.display = 'none';
		}
	});

	//return true for success
	return true;
}

	
	

//associated utility methods
CapsLockWarning.prototype =
{

	//create a context wrapper element around a textbox
	createContextWrapper : function(passfield)
	{
		//create the wrapper and add its class
		//it has to be an inline element because we don't know its context
		var wrapper = document.createElement('span');
		
		//enforce relative positioning
		wrapper.style.position = 'relative';
		
		//insert the wrapper directly before the passfield
		passfield.parentNode.insertBefore(wrapper, passfield);
		
		//then move the passfield inside it
		wrapper.appendChild(passfield);
		
		//return the wrapper reference
		return wrapper;
	},
	

	//add an event listener
	//this is deliberately not called "addEvent" so that we can 
	//compress the name, which would otherwise also effect "addEventListener"
	addListener : function(eventnode, eventname, eventhandler)
	{
		if(typeof document.addEventListener != 'undefined')
		{
			return eventnode.addEventListener(eventname, eventhandler, false);
		}
		else if(typeof document.attachEvent != 'undefined')
		{
			return eventnode.attachEvent('on' + eventname, eventhandler);
		}
	},
	
	
	//get an event target by sniffing for its property name
	//(assuming here that e is already a cross-model reference
	//as it is from addListener because attachEvent in IE 
	//automatically provides a corresponding event argument)
	getTarget : function(e)
	{
		//just in case!
		if(!e) { return null; }
		
		//otherwise return the target
		return e.target ? e.target : e.srcElement;
	}
	

}



