/*
 *
 *	index.js
 *	Javascript for /coder/index.php
 *	Created by Ian White
 *	Copyright 2008.
 *
 */


var doc            = null;
var paragraph      = null;
var word           = null;
var letter         = null;

var cursor         = null;
var this_word      = null;
var this_paragraph = null;
var this_text 	   = null;
var this_number    = null;

var status_div     = null;

var text 		   = new String();
var text_end       = "";
var position       = 0;
var broken         = true;



/*
 * register_keys()
 * arg: e - a keypress event
 *
 * Every key press is passed to this function which interprets what sort of action
 * needs to be taken.
 *
 */
function register_key(e) {
	keycode = e.charCode | e.keyCode;
	status_div.innerHTML = keycode
	if(keycode) {
		
		// If the command key was being held down, let the browser handle it.
		// This allows people to open new tabs and close the window using
		// keyboard commands.
		if(e.metaKey) {
			return true;
		}
		
		// Printable Character
		
		// If the ascii code is between 32 and 127, print the character
		if(e.charCode > 32 && e.charCode <= 127) {
			this_char = String.fromCharCode(e.charCode);
			if(e.charCode == 60) {
				this_char = '&#60;';
			}
			
			if(!this_word) {
				this_word = word.cloneNode(false);
				this_word.appendChild(cursor);
				this_paragraph.appendChild(this_word);
			}
			
			new_char = letter.cloneNode(true);
			if(e.charCode != 32) new_char.innerHTML = this_char;
			new_char.className = "letter";
			this_word.insertBefore(new_char, cursor);
				
		}
		
		// Space
		else if(e.charCode == 32) {
			space = letter.cloneNode(true);
			space.className = "whitespace";
			space.innerHTML = "&#8226;";
			
			this_word.insertBefore(space, cursor);
			
			old_word  = this_word;
			this_word = word.cloneNode(false);
			
			insert_after(this_word, old_word);
			
			transfer_from_node(cursor, this_word);

		}
		
		else {
			// Back arrow
			if(keycode == 63234 || keycode == 37) {
				if(prev = get_prev()) {

					cursor.className = cursor.rel;
					cursor = prev;
					cursor.rel = cursor.className;
					cursor.className += " cursor";
					
					this_word = cursor.parentNode;
					this_paragraph = this_word.parentNode.parentNode;

				}
			}
			
			// Forward arrow
			else if(keycode == 63235 || keycode == 39) {
				if(next = get_next()) {
					cursor.className = cursor.rel;
					cursor = next;
					cursor.rel = cursor.className; 
					cursor.className += " cursor";
					
					this_word = cursor.parentNode;
					this_paragraph = this_word.parentNode.parentNode;
				}
			}
			
			// Up arrow 
			else if(keycode == 63232 || keycode == 38){
				left_chars = ((cursor.offsetLeft - 1) / cursor.offsetWidth);
				if(this_paragraph.previousSibling) {
					new_cursor = this_paragraph.previousSibling.lastChild.firstChild.firstChild;
					for(i=0; i<left_chars; i++) {
						if(new_cursor.nextSibling) {
							new_cursor = new_cursor.nextSibling;
						}
						else if(new_cursor.parentNode.nextSibling) {
							new_cursor = new_cursor.parentNode.nextSibling.firstChild;
						}
						else {
							break;
						}	
					}
					
					cursor.className = cursor.rel;
					cursor = new_cursor;
					cursor.rel = cursor.className; 
					cursor.className += " cursor";
					this_paragraph = this_paragraph.previousSibling;
					this_word = cursor.parentNode;
					
				}
			}
			
			// Down arrow 
			else if (keycode == 63233 || keycode == 40) {
				left_chars = ((cursor.offsetLeft - 1) / cursor.offsetWidth);
				if(this_paragraph.nextSibling) {
					new_cursor = this_paragraph.nextSibling.lastChild.firstChild.firstChild;
					for(i=0; i<left_chars; i++) {
						if(new_cursor.nextSibling) {
							new_cursor = new_cursor.nextSibling;
						}
						else if(new_cursor.parentNode.nextSibling) {
							new_cursor = new_cursor.parentNode.nextSibling.firstChild;
						}
						else {
							break;
						}	
					}
					
					cursor.className = cursor.rel;
					cursor = new_cursor;
					cursor.rel = cursor.className; 
					cursor.className += " cursor";
					this_paragraph = this_paragraph.nextSibling;
					this_word = cursor.parentNode;
				}
			}
			
			// Backspace
			else if(keycode == 8) {
				if(prev = get_prev()) {
					
					prev.parentNode.replaceChild(cursor, prev);
					
					
					if(cursor.parentNode.parentNode.parentNode != this_paragraph) {
						transfer_node_set(this_paragraph.lastChild, cursor.parentNode.parentNode)
						doc.removeChild(this_paragraph);
						this_paragraph = cursor.parentNode.parentNode.parentNode;
					}
					
					if(cursor.parentNode != this_word) {
						transfer_node_set(this_word, cursor.parentNode);
						this_paragraph.lastChild.removeChild(this_word);
						this_word = cursor.parentNode;
					}

					if(!this_paragraph.hasChildNodes()) {
						doc.removeChild(this_paragraph);
					}
					
				}

				return false;
			}
			
			// Forward Delete
			else if(keycode == 63272 || keycode == 46) {
				if(next = get_next()) {
				
				
					if(next.parentNode.parentNode.parentNode != this_paragraph) {
						next_para = next.parentNode.parentNode.parentNode;
						transfer_node_set(next.parentNode.parentNode, this_paragraph.lastChild)
						doc.removeChild(next_para);
					}

					if(next.parentNode != this_word) {
						next_word = next.parentNode;
						transfer_node_set(next.parentNode, this_word);
						this_paragraph.lastChild.removeChild(next_word);
					}
					
					next.parentNode.replaceChild(next, cursor);
					next.rel = next.className;
					next.className += " cursor";
					cursor = next;
					
					if(!this_paragraph.hasChildNodes()) {
						doc.removeChild(this_paragraph);
					}												
				}
			}
			
			// Return
			else if(keycode == 13) {
				
				ret = letter.cloneNode(true);
				ret.className = "whitespace";
				ret.innerHTML = "&para;";
				
				this_word.insertBefore(ret, cursor);
				
				old_word  = this_word;
				old_para  = this_paragraph;
				
				this_paragraph = paragraph.cloneNode(true);
				this_word = this_paragraph.lastChild.firstChild;
				
				this_text = this_paragraph.lastChild;
				this_number = this_paragraph.firstChild;
				
				insert_after(this_word, old_word);
				insert_after(this_paragraph, old_para);
				
				transfer_from_node(cursor, this_word);
				transfer_from_node(this_word, this_text);

			}
			// Tab
			else if(keycode == 9) {
				
				tab = letter.cloneNode(true);
				tab.className = "whitespace";
				tab.innerHTML = "&not;&nbsp;&nbsp;&nbsp;";
					
				this_word.insertBefore(tab, cursor);
			
				old_word  = this_word;
				this_word = word.cloneNode(false);
				
				insert_after(this_word, old_word);
				
				transfer_from_node(cursor, this_word);

			}
			else if(keycode == 16) {
				cp.innerHTML = cursor.tagName;
			}
		}
	}
	
	return false;
}


/*
 * get_prev()
 *
 * This function returns the previous letter in relation to where the cursor
 * is currently located regardless of whether the previous letter is in
 * another word or paragraph.
 *
 */
function get_prev() {
	if(cursor.previousSibling) {
		return cursor.previousSibling;
	}
	else if(this_word.previousSibling) {
		return this_word.previousSibling.lastChild;
	}
	else if(this_paragraph.previousSibling) {
		return this_paragraph.previousSibling.lastChild.lastChild.lastChild;
	}
	
}


/*
 * get_next()
 *
 * This function returns the next letter in relation to where the cursor
 * is currently located regardless of whether the next letter is in
 * another word or paragraph.
 *
 */
function get_next() {
	if(cursor.nextSibling) {
		return cursor.nextSibling;
	}
	else if(this_word.nextSibling) {	
		return this_word.nextSibling.firstChild;
	}
	else if(this_paragraph.nextSibling) {
		return this_paragraph.nextSibling.lastChild.firstChild.firstChild;
	}
}


/*
 * transfer_node_set()
 * args: from - an html element that contains child nodes.
 *       to   - an html element capable of holding child nodes.
 *
 * This function takes the child nodes of the from argument
 * and appends them to the to argument.
 *
 */
function transfer_node_set(from, to) {
	while(node = from.firstChild) {
		to.appendChild(node);
	}
}

/*
 * transfer_from_node()
 * args: node - the start node
 *       to   - an html element capable of holding child nodes.
 *
 * This function transfers node and any siblings after node into
 * the to element, removing them from their original parent.
 *
 */
function transfer_from_node(node, to) {
	
	while(next = node.nextSibling) {
		to.appendChild(node);
		node = next;
	}
	to.appendChild(node);
}

/*
 * insert_after()
 * args: insert_node   - the node being inserted
 *       adjacent_node - the node to insert after
 *
 * This function inserts the insert node after the adjacent_node. I implemented
 * this because the dom provides an insertBefore method, but coder consistently
 * needed an insert_after.
 *
 */
function insert_after(insert_node, adjacent_node) {
	if(adjacent_node.nextSibling) {
		adjacent_node.parentNode.insertBefore(insert_node, adjacent_node.nextSibling);
	}
	else {
		adjacent_node.parentNode.appendChild(insert_node);
	}
}

/*
 * blink()
 *
 * This function is called every 750 milliseconds by the interval
 * setup in the setup_text function. It adds and removes the blink
 * class to the cursor causing it to change between a green block
 * with black text, and a black block with green text.
 *
 */
function blink() {
	
	if(cursor.className == cursor.rel + " cursor") {
		cursor.className = cursor.rel + " cursor blink";
	}
	else if(cursor.className == "cursor") {
		cursor.className = "cursor blink";
	}
	else if(cursor.className == "cursor blink") {
		cursor.className = "cursor";
	}
	else if(cursor.className == cursor.rel + " cursor blink") {
		cursor.className = cursor.rel + " cursor";
	}
}


/*
 * setup_text()
 *
 * This function creates the prototype elements for all of the letters,
 * words, paragraphs, and the document. This cuts down on the amount of code and
 * work required later as these nodes are created. It also starts the blink
 * interval, and attaches the keypress event listener. I had to use the old style
 * event listener 'window.onkeypress' to capture the back space key properly, otherwise
 * I was unable to stop the browser from returning to the previous page when the
 * back space button was pressed.
 *
 */
function setup_text() {
	
	window.onkeypress = register_key;
	doc = document.createElement('table');
	doc.style.paddingTop = '0px';
	doc.id = 'doc';
	document.body.appendChild(doc);
	
	status_div = document.createElement('div');
	status_div.id = 'status';
	document.body.appendChild(status_div);
	
	letter           = document.createElement('span');
	letter.rel       = "letter";
	letter.className = "letter";
	letter.innerHTML = "&nbsp;";
	
	
	word             = document.createElement('span');
	word.className   = "word";
	
	paragraph = document.createElement('tr');
	paragraph.className = "paragraph_holder";
	
	line_num  = document.createElement('td');
	line_num.className = "line_number";
	
	text      = document.createElement('td');
	text.className     = "line_text";
	
	paragraph.appendChild(line_num);
	paragraph.appendChild(text);
	
	text.appendChild(word);
	
	this_paragraph   = paragraph.cloneNode(true);
	this_text		 = this_paragraph.lastChild;
	this_number		 = this_paragraph.firstChild;
	this_word        = this_text.firstChild;
	cursor		     = letter.cloneNode(true);
	cursor.className = "cursor";
	this_word.appendChild(cursor);
	
	doc.appendChild(this_paragraph);
	
	setInterval(blink,750);

	doc = document.getElementById('doc');
	status_div = document.getElementById('status');
	
}


if (window.addEventListener){
	window.addEventListener('load', setup_text, false); 
} else if (window.attachEvent){
	alert('Sorry! The Coder Javascript Demo is only available for Firefox (v1.5 and above)     \nand Safari (v2 and above). Your browser is not supported.');
	document.location.href= 'http://www.ianowhite.com';
}