var stored_hash; 																						// current hash cache used to detect hash change by old school timer method in register_state_change_callback()
var onload_tasks = new Array(); 														// array of tasks to be executed upon page load
var operational_keycodes = new Array(40,37,38,39,13,16,224,9,18,17); // keycodes that map to so-called "operational" keys such as shift, ctrl, tab, etc. Basically, ones that don't produce content

/**
 * Executes all onload tasks
 *
 */
function onload()
{
	for(var i = 0; i < onload_tasks.length; i++)
	{
		onload_tasks[i]();
	}
}

/**
 * Changes the address bar's representation of state, either by changing the URL itself (if HTML5 pushState is available) or by setting a hashbang
 *
 * Also triggers any previously set callback for state changes
 *
 * @param string state The new state
 */
function change_state(state)
{
	if(state == get_current_state()) { return; } // canary
	
	// Use pushState() to set the URL, if available
  if(typeof history.pushState == 'function')
  {
		// If no state provided, assume base state desired
		if(!state)
		{
    	state = '/';
		}
		
		history.pushState(null, null, state);
  }
  // Otherwise, default to setting a hashbang
  else 
	{
		if(state)
	  {
	    window.location.hash = '#!' + state;
	  }
		else
		{
			window.location.hash = '';
		}
	}
}

/**
 * Triggers an ajax request to obtain a component and replaces a given HTML element with the response
 *
 *
 */
function replace(element, component, parameters)
{
	if(typeof parameters  == "undefined" || !parameters) { parameters = {}; }
	
	parameters['component'] = component;
	
	var onSuccess = function(data)
	{
		$(element).replaceWith(data.html);
		
		setTimeout("three_column_layout_heights_ensure();", 500);
	}
	
	$.get(routes['component'], parameters, onSuccess);
}

/**
 * Triggers an ajax request to obtain a component and inserts it relative to a given element
 *
 *
 */
function insert(element, component, relation, parameters, callback)
{ 
	element = $(element);
	
	if(typeof parameters  == "undefined" || !parameters) { parameters = {}; }
	
	parameters['component'] = component;
	
	var onSuccess = function(data)
	{
		if(relation == 'after')
		{
			element.after(data.html);
		}
		else if(relation == 'before')
		{
			element.before(data.html);
		}
		else
		{ 
			element.append(data.html);
		}
		
		if(typeof callback == 'function')
		{
			callback();
		}
		
		setTimeout("three_column_layout_heights_ensure();", 500);
	}
	
	return $.get(routes['component'], parameters, onSuccess);
}

/**
 * Converts an array of strings into a comma-deliminated list
 *
 */
function list_array(myarray)
{
	var first_item = true;
	var result = '';
	
	var pos = 0;
	
	for(var i=0; i<myarray.length; i++)
	{
		if(pos > 0)
		{
		  if(pos != myarray.length-1)
	    {
	      result += ',';
	    }
      
      result += ' ';
	    
	    if(pos == myarray.length-1)
	    {
	      result += 'and ';
      }
		}

		result += myarray[i];
		
		pos++;
	}
	
	return result;
}

/**
 * Capitalizes the first letter of each word in string
 *
 */
function capitalize_words(val) 
{
  newVal 	= '';
  val 		= val.split(' ');

  for(var c=0; c < val.length; c++) 
  {
		newVal += val[c].substring(0,1).toUpperCase() + val[c].substring(1,val[c].length);
  }

  return newVal;
}

/**
 * Indicates whether the given string is a valid email address
 *
 * @param string email Email address string
 * @return bool Whether it's valid
 */
function is_valid_email(email) 
{
  var reg = /^\S+@\S+\.\S+$/;

  if(reg.test(email) == false) 
  {
    return false;
  }
  else
  {
    return true;
  }
}

/**
 * Trim a string of any surrounding whitespace.
 * @param {String} str Original string
 * @return Trimmed string
 */
function trim(str)
{
	if(!str) { return ''; }

 	var str = str.replace(/^\s\s*/, ''), ws = /\s/, i = str.length;

  while (ws.test(str.charAt(--i)));

  return str.slice(0, i + 1);
}

/**
 * Returns a random string
 * 
 * @param integer length Length of string
 * @return string Random string
 */
function random_string(length) 
{
	if(!length)
	{
		length = 8;
	}
	
	var chars 				= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	var randomstring 	= '';
	
	for (var i=0; i<length; i++) 
	{
		var rnum 			= Math.floor(Math.random() * chars.length);
		randomstring 	+= chars.substring(rnum,rnum+1);
	}
	
	return randomstring;
}

/**
 * Indicates whether the given variable name is set
 *
 */
function isset(varname)
{
	return(typeof(window[varname])!='undefined');
}

/**
 * Returns the number of pixels of scroll needed to hit the bottom of the page
 *
 * @return integer
 */
function scroll_bottom()
{
	var scrollPosition = window.pageYOffset;
	var windowSize     = $(window).height();
	var bodyHeight     = $(document).height();

	return Math.max(bodyHeight - (scrollPosition + windowSize), 0);
}

/**
 * Returns the current hash value stripped of the leading hash (#) and any bang (!)
 *
 */
function get_hash_value()
{
	var hash = window.location.hash.substr(1);
	
	if(hash[0] == '!')
	{
		hash = hash.substr(1);
	}
	
	return hash;
}

/**
 * Returns base URL for current page
 *
 */
function get_base_url() 
{
	var url = window.location.href;
	
	return url.substring(0, url.indexOf('/', 7)) + '/';
}
