/**********************************************************
Author:
Adam Barry
eBOUND
www.ebound.dk

Date: August 9 2009

© 2009 Adam Barry, all rights reserved
-----------------------------------------------------------

Name:
keyHandler script

-----------------------------------------------------------
Description:
Functions that enables binding of keyboard-triggered
events to various HTML-elements, thus making it possible
to have various actions to key events depending on which
element is currently active.

-----------------------------------------------------------
Usage:
Identify the element which you wish to assign a function to.
Create the function that is to be executed.
Assign the function to a specific key.

<script type="text/javascript" src="keyHandler.js"></script>

-----------------------------------------------------------
Example:
<script type="text/javascript" src="keyHandler.js"></script>

Initialize:
	if (typeof addKeyAction == 'function') {
		addKeyAction(elementToHaveKeyHandlerAttached, function () { functionToLoad(); }, 13);		//Action triggered by single key-press (enter/return)
		addKeyAction(elementToHaveKeyHandlerAttached, function () { functionToLoad(); }, [13]);		//Action triggered by single key-press (enter/return)
		addKeyAction(elementToHaveKeyHandlerAttached, function () { functionToLoad(); }, [16,78]);	//Action triggered by key-combination (shift + n)
	}

	if (typeof setActiveKeyElement == 'function') {
		setActiveKeyElement(elementToHaveKeyHandlerAttached);
	}

Redefine:
	if (typeof addKeyAction == 'function') {
		addKeyAction(elementToHaveKeyHandlerAttached, function () { newFunctionToLoad(); }, [13]);
	}

Clean up:
	if(typeof removeActiveElement == 'function') {
		removeActiveElement();
	}


Assign key-actions to the webpage itself
	if (typeof addKeyAction == 'function') {
		addKeyAction(document, function () { functionToLoad(); }, [13]);
	}

	if (typeof setActiveKeyElement == 'function') {
		setActiveKeyElement(document);
	}
	

Quick key-code reference:
13: Enter/return key
27: Escape key
32: Spacebar key

-----------------------------------------------------------
Dependencies:
None

-----------------------------------------------------------
Revision history:
2010-05-21: Switch to object oriented model
			Code refactored
			Key-combinations added

**********************************************************/

/*: Keyhandler objects
----------------------------------------------------------*/
var keyActionElements = [];
function keyActionElement(element) {

	var me = this;
	this.element = element;
	this.actions = [];

	function keyAction(func, keycode) {
		this.func = func;
		this.keycode = keycode;
	}

	keyActionElement.prototype.addKeyAction = function (func, keycode) {
		keyActionObject = new keyAction(func, keycode);

		var actionPosition = me.actions.length;

		for (var i = 0; i < me.actions.length; i++) {

			if (me.actions[i].keycode.toString() == keyActionObject.keycode.toString()) {
				actionPosition = i;
				break;
			}
		}

		/* Create new action or update the existing */
		me.actions[actionPosition] = keyActionObject;
	}
}


/*: Initialize and destroy
----------------------------------------------------------*/
var currentActiveKeyElement = "";
var objectPosition = "";


function addKeyAction(element, func, keycodeArray) {
	var elementPosition = keyActionElements.length;

	/* If element exists, identify its position in keyActionElements */
	for (var i = 0; i < keyActionElements.length; i++) {
		if (keyActionElements[i].element == element) {
			elementPosition = i;
		}
	}

	/* If element doesn't exist create it */
	if (elementPosition == keyActionElements.length) {
		var keyActionObject = new keyActionElement(element);
		keyActionElements.push(keyActionObject);
	}

	/* Add (or update) a key-action to the element */
	keyActionElements[elementPosition].addKeyAction(func, keycodeArray);
}

function setActiveKeyElement(element) {
	objectPosition = keyActionElements.length-1;
	currentActiveKeyElement = keyActionElements[objectPosition];
}

function removeActiveKeyElement() {
	var currentElement = currentActiveKeyElement;

	keyActionElements.pop();
	
	/* If more elements are currently in the stack, activate the next element */
	var length = keyActionElements.length;

	if (length >= 1) {
		setActiveKeyElement(keyActionElements[length - 1].element);
	}
	else {
		currentActiveKeyElement = "";
		objectPosition = "";
	}
}


/*: Keyhandlers
----------------------------------------------------------*/
var activeKeys = []; /* Array containing the keyCodes of currently pressed keys */

function keyPressListener(event) {
	var evt = event || window.event;

	if (evt) {

		window.status = "";

		/* Add the current keyCode to the activeKeys-array if it is not already there */
		if (!activeKeys.contains(evt.keyCode)) {
			activeKeys.push(evt.keyCode);
		}

		if (keyActionElements.contains(currentActiveKeyElement)) {

			if (objectPosition <= -1) return;
			var currentObjectActions = keyActionElements[objectPosition].actions;

			for (var i = 0; i < currentObjectActions.length; i++) {

				var me = currentObjectActions[i];

				/* If multiple keycodes are assigned to the keyAction object */
				if (me.keycode.length > 1 && activeKeys.length > 1) {
					if (activeKeys.containsArray(me.keycode)) {
						activeKeys = [];
						me.func();
						return false;
					}
				}

				/* If only one key is assigned to the keyAction object */
				else {

					if (me.keycode == evt.keyCode) {
						activeKeys = [];
						me.func();
						return false;
					}

				}
			}
		}
	}
} document.onkeydown = keyPressListener;


function keyReleaseListener(event) { 
	var evt = event || window.event;

	if (evt) {

		/* Remove the current keyCode from the activeKeys-array */
		for (var i = activeKeys.length - 1; i > -1; i--) {
			if (activeKeys[i] == evt.keyCode) {
				activeKeys.splice(i, 1);
			}
		}
	}
} document.onkeyup = keyReleaseListener;
