/**
 * Some functions to identify siblings and children while ignoring whitespace
 *
 * Throughout, whitespace is defined as one of the characters
 *  "\t" TAB \u0009
 *  "\n" LF  \u000A
 *  "\r" CR  \u000D
 *  " "  SPC \u0020
 *
 * This does not use Javascript's "\s" because that includes non-breaking
 * spaces (and also some other characters).
 *
 *	Last Update: April 23, 2007
 */


/**
 * Determine whether a node's text content is entirely whitespace.
 *
 * @param n		A node implementing the |CharacterData| interface (i.e.,
 *				a |Text|, |Comment|, or |CDATASection| node
 * @return		True if all of the text content of |n| is whitespace,
 *				otherwise false.
 */
function isAllWhitespace( n )
{
	// Use ECMA-262 Edition 3 String and RegExp features
	return !( /[^\t\n\r ]/.test(n.data) ) ;
}


/**
 * Determine if a node should be ignored by the iterator functions.
 *
 * @param n		An object implementing the DOM1 |Node| interface.
 * @return		true if the node is:
 *					1) A |Text| node that is all whitespace
 *					2) A |Comment| node
 *				and otherwise false.
 */

function isIgnorable( n )
{
	return ( n.nodeType == 8) ||	// comment node
	( 
		(n.nodeType == 3)			// text node 
		&& isAllWhitespace(n)		// all whitespace
	) ;
}

/**
 * Version of |previousSibling| that skips nodes that are entirely
 * whitespace or comments.  (Normally |previousSibling| is a property
 * of all DOM nodes that gives the sibling node, the node that is
 * a child of the same parent, that occurs immediately before the
 * reference node.)
 *
 * @param sib	The reference node.
 * @return		Either:
 *					1)	The closest previous sibling to |sib| that is not
 *						ignorable according to |isIgnorable|, or
 *					2)	null if no such node exists.
 */
function getPreviousSibling( s )
{
	while( (s = s.previousSibling) ) {
		if( !isIgnorable(s) ) return s ;
	}

	return null ;
}

/**
 * Version of |nextSibling| that skips nodes that are entirely
 * whitespace or comments.
 *
 * @param s		The reference node.
 * @return		Either:
 *					1)	The closest next sibling to |sib| that is not
 *						ignorable according to |isIgnorable|, or
 *					2)	null if no such node exists.
 */
function getNextSibling( s )
{
	while( (s = s.nextSibling) ) {
		if( !isIgnorable(s) ) return s ;
	}

	return null ;
}

/**
 * Version of |lastChild| that skips nodes that are entirely
 * whitespace or comments.  (Normally |lastChild| is a property
 * of all DOM nodes that gives the last of the nodes contained
 * directly in the reference node.)
 *
 * @param s		The reference node.
 * @return		Either:
 *					1)	The last child of |sib| that is not
 *						ignorable according to |isIgnorable|, or
 *					2)	null if no such node exists.
 */
function getLastChild( p )
{
	var lc = p.lastChild ;
	while(lc) {
		if( !isIgnorable(lc) ) return lc ;
		lc = lc.previousSibling ;
	}

	return null ;
}

/**
 * Version of |firstChild| that skips nodes that are entirely
 * whitespace and comments.
 *
 * @param s		The reference node.
 * @return		Either:
 *					1)	The first child of |sib| that is not
 *						ignorable according to |isIgnorable|, or
 *					2)	null if no such node exists.
 */
function getFirstChild( p )
{
	var fc = p.firstChild ;
	while(fc) {
		if( !isIgnorable(fc) ) { return fc ; } 
		fc = fc.nextSibling ;
	}
	
	return null;
}

/**
 * Version of |data| that doesn't include whitespace at the beginning
 * and end and normalizes all whitespace to a single space.  (Normally
 * |data| is a property of text nodes that gives the text of the node.)
 *
 * @param tn	The text node whose data should be returned
 * @return		A string giving the contents of the text node with
 *				whitespace collapsed.
 */
function getData( tn )
{
	var data = tn.data ;
	// Use ECMA-262 Edition 3 String and RegExp features
	data = data.replace(/[\t\n\r ]+/g, " ") ;
	if( data.charAt(0) == " " ) {
		data = data.substring(1, data.length) ;
	}
	
	if( data.charAt(data.length - 1) == " " ) {
		data = data.substring(0, data.length - 1) ;
	}

	return data ;
}

