/*
 *
 *	source.js
 *	Javascript for source.php
 *	Created by Ian White
 *	Copyright 2008.
 *
 */
 
/*
 * find_line()
 * arg: e - a keypress event generated by the browser
 *
 * Called whenever a key is pressed. Checks to see if the key is
 * return or enter. If so, it checks to make sure the line number
 * in the line_number_form text input is greater than one, then
 * calls goto_line() to scroll the window to that line.
 *
 */
function find_line(e) {  
	keycode = e.charCode | e.keyCode;
	status.innerHTML = keycode;
	
	// If the key is return or enter...
	if(keycode == 13 || keycode == 3) {
	
		//Get the line number
		line_form = document.getElementById('line_number_form');
		line_num = line_form.value;
		
		// Make sure the line number is valid.
		if(!line_num || line_num < 1) {
			line_num = 1;
		}
		
		// Goto that line
		goto_line(line_num);
		
		// If the function selector exists, reset it because we are relocating.
		if(func_b = document.getElementById('function_select')) {
			func_b.value = '1';
		}
		
	}

}



/*
 * find_function()
 *
 * Called when the function_select value is changed. This
 * clears any value that may be present in the line_number_form
 * element and then uses the value of the function_select to jump
 * to the line of the function selected.
 *
 */
function find_function() {
	function_browser = document.getElementById('function_select');
	line = document.getElementById('line_number_form');
	line.value = "";
	goto_line(function_browser.value);
}


/*
 * get_previous_row()
 * arg: target - a row in the source code table.
 *
 * This function takes a row in the source code table, and returns
 * the row just prior to it, or the first row in the table if no
 * rows precede the provided row.
 *
 */
function get_previous_row(target) {
	if(target.previousSibling && (target.previousSibling.nodeType != '3' || target.previousSibling.previousSibling)) {
		// Make that the next target to check
		
		target = (target.previousSibling.nodeType != '3') ? target.previousSibling : target.previousSibling.previousSibling;
		
	}
	else {
		// Otherwise, we're at the top of the table. Stop looping
		return document.getElementById('line-1');
	}
	
	while(target.style.display == 'none') {
		// If the current row has a previous row (2 previous siblings)
		if(target.previousSibling && (target.previousSibling.nodeType != '3' || target.previousSibling.previousSibling)) {
			// Make that the next target to check
			target = (target.previousSibling.nodeType != '3') ? target.previousSibling : target.previousSibling.previousSibling;
		}
		else {
			// Otherwise, we're at the top of the table. Stop looping
			return document.getElementById('line-1');
		}
	}
	
	return target;
}


/*
 * goto_line()
 * arg: line - an integer representing the line number to jump to.
 *
 * This function takes a line number, and changes the location.href
 * so that we move down to the anchor attached to that line. In order
 * to accomplish this, it must find either the line requested or the next
 * displaying line above it if the line requested is a hidden comment. Then,
 * it must find the previous two displaying lines in order to provide buffer
 * room for the toolbar at the top of the screen. This way, the toolbar is not
 * overlayed on top of the line requested.
 *
 */
function goto_line(line) {

	target = document.getElementById('line-' + (parseInt(line) + 2));
	
	// Find 2 visible rows above the target or the first visible row above the target.
	
	// If the target is visible, then we got one, otherwise we must keep traversing up
	while(target.style.display == 'none') {
		// If the current row has a previous row (2 previous siblings)
		if(target.previousSibling && (target.previousSibling.nodeType != '3' || target.previousSibling.previousSibling)) {
			// Make that the next target to check
			target = (target.previousSibling.nodeType != '3') ? target.previousSibling : target.previousSibling.previousSibling;
		}
		else {
			// Otherwise, we're at the top of the table. Stop looping
			target = document.getElementById('line-1');
			break;
		}
	}
	
	target = get_previous_row(get_previous_row(target));

	document.location.href = '#' + target.id;
	

}


/*
 * toggle_comments()
 *
 * This is called whenever the 'Hide Comments' button is pressed. It checks
 * see if the switch is turned on or off and hides or shows the comments
 * in the text file accordingly
 *
 */
function toggle_comments() {
	// This will hold the style declaration for each row.
    display_str = '';
    
    // See note on line 163 for buttooooon explaination.
	comment_buttoon = document.getElementById('comment_button');
	if(comment_buttoon.style.backgroundPosition == '0px 0px') {
		comment_buttoon.style.backgroundPosition = '0px 18px';
	}
	else {
		comment_buttoon.style.backgroundPosition = '0px 0px';
		display_str = 'none';
	}
	
	// Iterate through the rows, looking for entirely commented rows.
	trs = document.getElementsByTagName('tr');
	for(i=0; i<trs.length; i++) {
		// Determines if we hide the whole line, or just the comment.
		line_switch = true;
		
		// Grab the code cell
		code = (trs[i].lastChild.nodeType == '3') ? trs[i].lastChild.previousSibling : trs[i].lastChild;
		
		// If it's not empty, we'll have a look...
		if(code.childNodes.length != 0) {
			// Iterate through the items in the line
			for(k=0; k < code.childNodes.length; k++) {
				
				// If we run into a comment, get rid of it.
				if(code.childNodes[k].nodeType !=3 && (code.childNodes[k].className == 'block-comment' || code.childNodes[k].className == 'comment')) {
					if(line_switch) {
						trs[i].style.display = display_str;
						break;
					}
					else {
						code.childNodes[k].style.display = display_str;
					}
				}
				// If we run into some space characters, ignore them
				else if(code.childNodes[k].nodeType == 3 && code.childNodes[k].nodeValue.match(/\s*/)) {
					continue;
				}
				// If we run into some text, then we can't turn off the line, just the comments.
				else if(code.childNodes[k].innerHTML.match(/\w+/)) {
					line_switch = false;
				}
				// ... if we run into... something else... then we'll keep moving along.
				else {
					break;
				}
			}
		}
			
	}
	
	// Make sure that we're still on the line the person was viewing if they had
	// specified a line or function.
	line = document.getElementById('line_number_form');
	function_browser = document.getElementById('function_select');
	if(line.value) {
		goto_line(line.value);
	}
	else if(function_browser && function_browser.value != '1') {
		goto_line(function_browser.value);
	}
	
	
	// Get rid of those pesky selections
	if (document.selection)
        document.selection.empty();
    else if (window.getSelection)
        window.getSelection().removeAllRanges();
	
	return false;
}

/*
 * source_init()
 *
 * This function is called when the browser finishes loading the page.
 * It sets up all of the event handlers required to make the user interface
 * work.
 *
 */
function source_init() {
	line_form = document.getElementById('line_number_form');
	if (line_form.addEventListener){
		line_form.addEventListener('keypress', find_line, false);
	} else if (line_form.attachEvent){
		line_form.attachEvent('onkeypress', find_line);
	}
	
	// Apparently "comment_button" is reserved by IE7, so I guess we'll use comment_buttoon.
	// MSIE sucks so hardcore.
	comment_buttoon = document.getElementById('comment_button');
	if (comment_buttoon.attachEvent){
		comment_buttoon.attachEvent('onclick', toggle_comments);
	} else if (comment_buttoon.addEventListener){
		comment_buttoon.addEventListener('click', toggle_comments, false); 
	}
	
	function_browser = document.getElementById('function_select');
	if(function_browser) {
		if (function_browser.addEventListener){
			function_browser.addEventListener('change', find_function, false); 
		} else if (function_browser.attachEvent){
			function_browser.attachEvent('onchange', find_function);
		}

	}
	

}


if (window.addEventListener){
	window.addEventListener('load', source_init, false); 
} else if (window.attachEvent){
	window.attachEvent('onload', source_init);
}
