/*
Script: keywatcher.js
	Version 0.2.5
	Monitor the keyboard
	
Changes:
	0.2.5 : Added Element.isChildOf() - now key events are returned from any child of the watched element

Todo:
	Add switch to decide if an EXACT element should be watched or all DOM childs
	
Dependancies:
	 <Moo.js>, <Function.js>, <Array.js>, <Element.js>
	
Author:
	Stefan Lange-Hegermann <http://www.blackmac.de/>

License:
	MIT-style license.
*/

var KEY_NONE   = 0;
var KEY_ALT    = 1;
var KEY_CTRL   = 2;
var KEY_SHIFT  = 4;
var KEY_META   = 8;


Element.implement ({
	isChildOf: function (parent) {
		var parentElement=$(parent);
		var realp=this.parentNode;
		while (realp) {
			if (realp==parentElement) return true;
			realp=realp.parentNode;
		}
		return false;
	}
})

/*
Class: Keywatcher
	Captures keyboard events for a given Element

Arguments:
	domelement - the element
	callback - function to execute when one of the listened keys is pressed (return false if you want to block the keys default actions)
*/
Keywatcher = new Class({
	initialize: function(domelement, callback) {
		
		this.domElement=$(domelement);
		this.keys=[];
		this.callback=callback;
		this.watchAll=false;
		
		this.domElement.addEvent("keydown",this.keyDown.bindAsEventListener(this));
		this.domElement.addEvent("keypress",this.keyPress.bindAsEventListener(this));
		return this;
	},
	
	/*
	Property: addKey
		Adds a key to be watched
	
	Arguments:
		key - the key as ASCII code or character
		modifiers - sum of all Modifiers
		
	Example:
		(start code)
		var nav = new Keywatcher( $E('html'), function(key,modifier) {} ).addKey("a", KEY_CTRL);
		(end)
	*/
	addKey: function(key, modifiers) {
		if (typeof(key)=="string") {
            key = key.toUpperCase().charCodeAt(0);
        }

		this.keys[key]=modifiers;
		return this;
	},
	
	/*
	Property: removeKey
		Removes a key to be watched
	
	Arguments:
		key - the key as ASCII code or character
		
	Example:
		(start code)
		nav.removeKey("a");
		(end)
	*/
	removeKey: function(key) {
		if (typeof(key)=="string") {
            key = key.toUpperCase().charCodeAt(0);
        }

		delete(this.keys[key]);
		return this;
	},
	
	/*
	Property: watchAllKeys
		Watches ALL keys and key combinations
		
	Example:
		(start code)
		nav.watchAllKeys();
		(end)
	*/
	watchAllKeys: function() {
		this.watchAll=true;
		
		return this;
	},
	
	// Private
	keyPress: function(evnt) {
		if (this.noDefault)
			evnt.preventDefault();
	},
	
	keyDown: function(evnt) {
		var targ;
		if (!evnt) var evnt = window.event;
		if (evnt.target) targ = evnt.target;
		else if (evnt.srcElement) targ = evnt.srcElement;
		if (targ.nodeType == 3) // defeat Safari bug
			targ = targ.parentNode;
		
		this.noDefault=false;
		
		if (!(targ.isChildOf(this.domElement) || targ==this.domElement))
			return;
				
		var modifiers=0;
		if (evnt.altKey) modifiers   += KEY_ALT;
		if (evnt.ctrlKey) modifiers  += KEY_CTRL;
		if (evnt.shiftKey) modifiers += KEY_SHIFT;
		if (evnt.metaKey) modifiers  += KEY_META;
		
		returnval = true;
		
		if ( this.watchAll ) {
			returnval = this.callback(evnt.keyCode, modifiers);
		}
		
		if ( typeof(this.keys[evnt.keyCode])!= "undefined" ) {
			if (this.keys[evnt.keyCode]==modifiers)
				returnval = this.callback(evnt.keyCode, modifiers);
		}

		if (returnval===false) {
			this.noDefault=true;
		}
	}
})