/**
Table-row-highlighter v0.2

Author			Michael Odden <code@michaelodden.com>
Last Updated	2009-06-03
Homepage		http://michaelodden.com/
Documentation	http://michaelodden.com/labs/js/table-row-highlighter/
Blogpost		http://michaelodden.com/webdevelopment/table-row-highlighterjs

License: Do whatever you like, but please leave a notice, and I'd love feedback/patches so I can upgrade the code.
*/

// Initiating namespace
if(typeof MIO == "undefined")  { var MIO = {}; }

// Scope-preserving-method
MIO.applyWrapper = MIO.applyWrapper ? MIO.applyWrapper : function(object, method) {
		return function() {
			return method.apply(object, arguments);
		};
	};

/* The following *Event-methods:
	written by Dean Edwards, 2005
	with input from Tino Zijdel, Matthias Miller, Diego Perini
*/
MIO.addEvent = MIO.addEvent ? MIO.addEvent : function(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = MIO.addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = MIO.handleEvent;
	}
};

MIO.removeEvent = function(element, type, handler) {
  // delete the event handler from the hash table
  if (element.events && element.events[type]) {
    delete element.events[type][handler.$$guid];
  }
};

MIO.handleEvent = function(event) {
  // grab the event object (IE uses a global event object)
  event = event || window.event;
  // get a reference to the hash table of event handlers
  var handlers = this.events[event.type];
  // execute each event handler
  for (var i in handlers) {
    this.$$handleEvent = handlers[i];
    this.$$handleEvent(event);
  }
};

MIO.addEvent.guid = 1;
/* End of Dean Edwards *Event-meethods */

// Method for listening for several keypresses
MIO.inputreader = MIO.inputreader ? MIO.inputreader : function() {
	return {
		// Callbacks
		onInput : null,
		
		// Settings
		input_lastupdate : -1,
		input_timeout : 1000, // millisecs
		
		// Debug-settings
		debug : false,
		debug_element : null,
		
		// Internals, do not edit
		cur_input : "", 
		
		toString : function() {
			return "[MIO.inputreader]";
		},
		
		// Initiates the listener 
		// Todo: modify to play nicer with the other kids
		init : function(listenat) {
			listenat = listenat ? listenat : document;
			MIO.addEvent(listenat, "keyup", MIO.applyWrapper(this, this.listener));
		},
		
		setDebugElement : function(el) {
			this.debug = true;
			this.debug_element = el;
		},
		
		// Kills the listener
		// Todo: modify to play nice with the other kids
		kill : function() {},
			
		// Called after every keypress
		register : function(keycode) {
			// Initiate an updated date-object
			var date = new Date();
			
			// On timeout - clear
			if( (date.getTime()-this.input_lastupdate) > this.input_timeout ) {
				this.clear();
			}
			
			// Update the contents
			this.input_lastupdate = date.getTime();
			
			if(keycode==-1) {
				this.cur_input = "";
			} else {
				// Todo: sanitize
				var char = String.fromCharCode(code);
				this.cur_input += char;
			}
			
			if(typeof this.onInput == "function") {
				this.onInput(this.cur_input);
			}
			
			// Output to corresponding element if debug=true
			if(this.debug==true && this.debug_element) {
				this.debug_element.value = this.cur_input;
			}
		},
		
		// Called after each timeout	
		clear : function() {
			this.cur_input = "";
		},
		
		// The listener
		listener : function(e) {
			if(!e) e = window.event;
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			
			if(code == 27) {
				code = -1;
			} else if(code < 30) {
			//	e.preventDefault();
			//	e.stopPropagation();
				return;
			}

			this.register(code);
		}
	};
};

// The part that takes care of searching, takes input from MIO.inputreader
MIO.highlighter = {
		table : null,	// Reference to the table
		check_col : 0,	// Which column to search against
		rows : null,	// The rows of the table, convenience
		inputreader : null, // Reference to an instance of the inputreader
		multi : false,	// Todo: allow multiple matches
		focusEl : null, // The element which will be inserted and gain focus
		
		toString : function() {
			return "[MIO.highlighter]";
		},
		
		init : function(table, col) {
			this.table = table ? table : document.getElementsByTagName("TABLE").item(0);
			this.check_col = col ? col : 0;
			
			if(!this._checkReqs()) {
				alert("Requirements-error");
				return false;
			}
			
			this.focusEl = document.createElement("INPUT");

			this.rows = table.getElementsByTagName("TBODY").item(0).getElementsByTagName("TR");
			this.inputreader = new MIO.inputreader();
			this.inputreader.init();
			this.inputreader.onInput = MIO.applyWrapper(this, this.highlight);
		},
		
		// Search and highlight
		highlight : function(str) {
			if(str=="") {
				this.clear();
				return;
			}
			
			var check_col = 0;
			var checkexp = new RegExp("^" + str, "i");
			this.clear();

			for(var i=0; i<this.rows.length; i++) {
				row = this.rows.item(i);
				col = row.getElementsByTagName("TD").item(check_col);

				// search...
				if(checkexp.test(col.innerHTML)) {
					// found! now let there be light!
					row.className = "highlight";
					
					// Setting focus, forcing the browser to eventually scroll
					col.appendChild(this.focusEl);
					this.focusEl.focus();
					col.removeChild(this.focusEl);
					
					if(!this.multi) {
						break;
					}
				}
			}
		},
		
		// Clear all highlighted rows
		clear : function() {
			for(var i=0; i<this.rows.length; i++) {
				row = this.rows.item(i);
				row.className = "";
			}
		},
		
		// Checks if all requirements are met
		_checkReqs : function() {
			if(typeof MIO.inputreader == "undefined") {
				return false;
			}
			
			if(typeof MIO.applyWrapper == "undefined") {
				return false;
			}
			
			if(typeof MIO.table == null) {
				return false;
			}
			
			return true;
		}
	};