dom.js

Summary

A collection of objects and methods to extend the Document Object Model.

Version: 0.8 $Id: overview-summary-dom.js.html,v 1.12 2006/08/23 23:30:17 jameso Exp $

Author: James A. Overton


Class Summary
mozile.dom.Range  
mozile.dom.Selection  

/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at http://mozile.mozdev.org/0.8/LICENSE
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is James A. Overton's code (james@overton.ca).
 *
 * The Initial Developer of the Original Code is James A. Overton.
 * Portions created by the Initial Developer are Copyright (C) 2005-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *	James A. Overton <james@overton.ca>
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * @fileoverview A collection of objects and methods to extend the Document Object Model.
 * @link http://mozile.mozdev.org 
 * @author James A. Overton <james@overton.ca>
 * @version 0.8
 * $Id: overview-summary-dom.js.html,v 1.12 2006/08/23 23:30:17 jameso Exp $
 */

mozile.provide("mozile.dom.*");
mozile.provide("mozile.dom.Range");
mozile.provide("mozile.dom.Selection");

/**
 * A collection of DOM classes and methods.
 * @type Object
 */
mozile.dom = new Object();
// JSDoc hack
mozile.dom.prototype = new mozile.Module;

/**
 * Define some shared constants.
 */
mozile.dom.ELEMENT_NODE                = 1;
mozile.dom.ATTRIBUTE_NODE              = 2;
mozile.dom.TEXT_NODE                   = 3;
mozile.dom.CDATA_SECTION_NODE          = 4;
mozile.dom.ENTITY_REFERENCE_NODE       = 5;
mozile.dom.ENTITY_NODE                 = 6;
mozile.dom.PROCESSING_INSTRUCTION_NODE = 7;
mozile.dom.COMMENT_NODE                = 8;
mozile.dom.DOCUMENT_NODE               = 9;
mozile.dom.DOCUMENT_TYPE_NODE          = 10;
mozile.dom.DOCUMENT_FRAGMENT_NODE      = 11;
mozile.dom.NOTATION_NODE               = 12;


/**
 * An array of "link" elements which have been added to this document.
 * @type Array
 */
mozile.dom.links = new Array();


/**
 * Gets the document's body element, if it exists. If none exists the document element is returned.
 * @param {Node} node Optional. A node belonging to the document to be searched. If none is given, the current document is used.
 * @type Element
 */
mozile.dom.getBody = function(node) {
	var doc = document;
	if(node) doc = node.ownerDocument;
	var elements = doc.getElementsByTagName("body");
	if(elements && elements[0]) return elements[0];
	else return doc.documentElement;
}

/**
 * Gets the document's head element, if it exists. If none exists the document element is returned.
 * @param {Node} node Optional. A node belonging to the document to be searched. If none is given, the current document is used.
 * @type Element
 */
mozile.dom.getHead = function(node) {
	var doc = document;
	if(node) doc = node.ownerDocument;
	var elements = doc.getElementsByTagName("head");
	if(elements && elements[0]) return elements[0];
	else return doc.documentElement;
}

/**
 * Gets the given node's first child element, if one exists.
 * @param {Node} parent The parent node to check.
 * @type Element
 */
mozile.dom.getFirstChildElement = function(parent) {
	for(var i=0; i < parent.childNodes.length; i++) {
		if(parent.childNodes[i].nodeType == mozile.dom.ELEMENT_NODE) 
			return parent.childNodes[i];
	}
	return null;
}

/**
 * Returns an array of all given node's child elements.
 * @param {Node} parent The parent node to check.
 * @type Array
 */
mozile.dom.getChildElements = function(parent) {
	var children = new Array();
	for(var i=0; i < parent.childNodes.length; i++) {
		if(parent.childNodes[i].nodeType == mozile.dom.ELEMENT_NODE) 
			children.push(parent.childNodes[i]);
	}
	return children;
}

/**
 * Gets the given node's next sibling element, if one exists.
 * @param {Node} node The node to start with.
 * @type Element
 */
mozile.dom.getNextSiblingElement = function(node) {
	var sibling = node.nextSibling;
	while(sibling) {
		if(sibling.nodeType == mozile.dom.ELEMENT_NODE) return sibling;
		sibling = sibling.nextSibling;
	}
	return null;
}

/**
 * Inserts the newNode after the refNode in the refNode's parent node.
 * @param {Node} newNode The node to insert.
 * @param {Node} refNode Insert the new node after this node.
 * @type Node
 */
mozile.dom.insertAfter = function(newNode, refNode) {
	if(!newNode) throw("Error [mozile.dom.insertAfter]: No newNode. newNode: "+ newNode +" refNode: "+ refNode);
	if(!refNode) throw("Error [mozile.dom.insertAfter]: No refNode. newNode: "+ newNode +" refNode: "+ refNode);
	var parentNode = refNode.parentNode;
	if(!parentNode) return null;
	if(refNode.nextSibling) return parentNode.insertBefore(newNode, refNode.nextSibling);
	else return parentNode.appendChild(newNode);
}

/**
 * Inserts a new node as the first child of the given parent node.
 * @param {Node} newNode The node to insert.
 * @param {Node} parentNode The parent to attach this node to.
 * @type Node
 */
mozile.dom.prependChild = function(newNode, parentNode) {
	if(parentNode.firstChild) return parentNode.insertBefore(newNode, parentNode.firstChild);
	else return parentNode.appendChild(newNode);
}

/**
 * Determines whether the ancestorNode is an ancestor of the descendantNode. If the ancestorNode is the descendantNode, the method returns true.
 * @param {Node} ancestorNode 
 * @param {Node} descendantNode 
 * @param {Node} limitNode Optional. The search will stop at this node, no matter what happens. 
 * @type Boolean
 */
mozile.dom.isAncestorOf = function(ancestorNode, descendantNode, limitNode) {
	var checkNode = descendantNode;
	while(checkNode) {
		if(checkNode == ancestorNode) return true;
		else if(checkNode == limitNode) return false;
		else checkNode = checkNode.parentNode;
	}
	return false;
}

/**
 * Returns the first node which is an ancestor of both given nodes.
 * @param {Node} firstNode 
 * @param {Node} secondNode
 * @type Node
 */
mozile.dom.getCommonAncestor = function(firstNode, secondNode) {
	var ancestor = firstNode;
	while(ancestor) {
		if(mozile.dom.isAncestorOf(ancestor, secondNode)) return ancestor;
		else ancestor = ancestor.parentNode;
	}
	return null;
}

/**
 * A regular expression to check for non-whitespace characters.
 * @private
 * @type RegExp
 */
mozile.dom._matchNonWhitespace = /\S/;

/**
 * Decides whether the given node contains non-whitespace text.
 * @param {Node} node The node to be checked.
 * @type Boolean
 */
mozile.dom.isWhitespace = function(node) {
	if(!node || !node.nodeValue) return false;
	if(node.nodeValue.match(mozile.dom._matchNonWhitespace)) return false;
	return true;
}


/**
 * A test to see if the given node belongs to an X/HTML document. Fairly crude test at the moment.
 * @param {Node} node A node belonging to the document to be checked.
 * @type Boolean
 */
mozile.dom.isHTML = function(node) {
	if(!node) node = document;
	var doc = node;
	if(node.ownerDocument) doc = node.ownerDocument;
	if(!doc.documentElement) return false;
	var name = doc.documentElement.nodeName;
	if(doc.documentElement.nodeName) name = doc.documentElement.nodeName;
	if(name.toLowerCase() == "html") return true;
	else return false;
}


/**
 * A test to see if the given node should be ignored.
 * @param {Node} node The node to check.
 * @type Boolean
 */
mozile.dom.isIgnored = function(node) {
	if(node.nodeType == mozile.dom.ATTRIBUTE_NODE){
		if(node.nodeName.indexOf("xmlns") == 0) return true;
		if(mozile.browser.isOpera && node.nodeName.toLowerCase() == "shape") return true;
	}
	return false;
}

/**
 * Gets the local part of the qualified name for this node.
 * If the name is not qualified, the nodeName is returned.
 * @param {Node} node The node to check.
 * @type String
 */
mozile.dom.getLocalName = function(node) {
	if(!node) return null;
	if(node.localName) return node.localName;
	else if(node.nodeName && node.nodeName.indexOf(":") > -1) 
		return node.nodeName.substring(node.nodeName.indexOf(":") + 1);
	else return node.nodeName;
}

/**
 * Gets the prefix part of the qualified name for this node.
 * If the name is not qualified, a null value is returned.
 * @param {Node} node The node to check.
 * @type String
 */
mozile.dom.getPrefix = function(node) {
	if(!node) return null;
	if(node.prefix) return node.prefix;
	else if(node.nodeName.indexOf(":") > -1) 
		return node.nodeName.substring(0, node.nodeName.indexOf(":"));
	else return null;
}

/**
 * Gets the index of this node among all its parent's child nodes.
 * @param {Node} node The node to check.
 * @type Integer
 */
mozile.dom.getIndex = function(node) {
	if(!node.parentNode) return null;
	for(var c=0; c < node.parentNode.childNodes.length; c++) {
		if(node.parentNode.childNodes[c] == node) return c;
	}
	return c;
}

/**
 * Gets the index of this node among its parent's children of the same type and name. Used for XPath positions.
 * @param {Node} node The node to check.
 * @type Integer
 */
mozile.dom.getPosition = function(node) {
	if(!node.parentNode) return null;
	var s=1;
	for(var c=0; c < node.parentNode.childNodes.length; c++) {
		if(node.parentNode.childNodes[c] == node) break;
		else if(node.nodeType == mozile.dom.ELEMENT_NODE) {
			if(node.parentNode.childNodes[c].nodeName == node.nodeName) s++;
		}
		else if(node.parentNode.childNodes[c].nodeType == node.nodeType) s++;
	}
	return s;
}

/**
 * Removes all the child nodes of the given node.
 * Does not remove attributes.
 * @param {Node} node The node to remove the child nodes from.
 * @type Void
 */
mozile.dom.removeChildNodes = function(node) {
	if(node.childNodes.length == 0) return;
	while(node.firstChild) {
		node.removeChild(node.firstChild);
	}
}

/**
 * Get the namespace of a node.
 * @param {Node} node The node to check.
 * @type String
 */
mozile.dom.getNamespaceURI = function(node) {
	if(node.namespaceURI) return node.namespaceURI;
	else if(node.nodeName.indexOf(":") > -1) {
		var prefix = node.nodeName.substring(0, node.nodeName.indexOf(":"));
		return mozile.dom.lookupNamespaceURI(node, prefix);
	}
	return mozile.dom.getDefaultNamespaceURI(node);
}

/**
 * Climb the tree looking for the first "xmlns" attribute.
 * @param {Node} node The node to check.
 * @type String
 */
mozile.dom.getDefaultNamespaceURI = function(node) {
	var namespaceURI = null;
	while(node) {
	  if(node.nodeType == mozile.dom.ELEMENT_NODE && node.getAttribute("xmlns"))
	  	return node.getAttribute("xmlns");
	  node = node.parentNode;
	}
	return namespaceURI;
}

/**
 * Tries to find and return the URI of the namespace of this node.
 * In browsers that support lookupNamespaceURI(prefix), when a prefix is given that method is used.
 * Otherwise, if the node has a qualified name with a prefix, the method looks for an "xmlns:prefix" attribute. 
 * If the node has no prefix, the method looks for an "xmlns" attribute.
 * Searches climb the DOM tree, starting with the node, and return the first match found.
 * @param {Node} node The node to check.
 * @param {String} prefix Optional. The prefix to check for. If none is given, the prefix of the node is used (if there is one).
 * @type String
 */
mozile.dom.lookupNamespaceURI = function(node, prefix) {
	if(!prefix) prefix = mozile.dom.getPrefix(node);
	var attr = "xmlns";

	// Proper method when a prefix is given.
	// Mozilla automatically climbs the DOM tree, but Opera does not.
	if(prefix && node.lookupNamespaceURI) {
		while(node) {
			var ns = node.lookupNamespaceURI(prefix);
			if(ns) return ns;
			else node = node.parentNode;
		}
		return null;
	}

	// Safari has to use getAttributeNS, while other browsers do not.
	if(prefix && mozile.browser.isSafari) {
		while(node && node.getAttributeNS) {
			//alert("Prefix: "+ node.nodeName +" "+ attr +":"+ prefix +" "+  node.getAttributeNS(attr, prefix));
			if(node.getAttributeNS(attr, prefix)) return node.getAttributeNS(attr, prefix);
			else node = node.parentNode;
		}
		return null;
	}

	// IE supported method for prefixes.
	if(prefix) attr = "xmlns:"+prefix;
	
	// General case.
	while(node) {
		//alert("No prefix: "+ node.nodeName +" "+ attr +" "+ node.getAttribute(attr));
		if(node.getAttribute(attr)) return node.getAttribute(attr);
		else node = node.parentNode;
	}
	return null;
}

/**
 * Tries to find and return the prefix for the qualified names of node with the given namespace URI.
 * Climbs the DOM tree, checking for any attributes starting with "xmlns:". It then checks those against the given namesace URI. If they match, the prefix part of the attribute name is returned.
 * @param {Node} node The node to check.
 * @param {String} namespaceURI Optional. The namespace URI to find the prefix for. If none is given, this node's namespaceURI is used.
 * @type String
 */
mozile.dom.lookupPrefix = function(node, namespaceURI) {
	if(!namespaceURI) namespaceURI = node.namespaceURI;
	if(!namespaceURI) return null;

	while(node && node.attributes) {
		for(var i=0; i < node.attributes.length; i++) {
			if(node.attributes[i].nodeName.indexOf("xmlns:") == 0 &&
				node.attributes[i].nodeValue == namespaceURI) {
				return node.attributes[i].nodeName.substring(6);
			}
		}
		node = node.parentNode;
	}
	return null;
}


/**
 * Converts a CSS style name (hyphenated) to a JavaScript style name (camelCase).
 * @param {String} styleName The name of a CSS rule.
 * @type String
 */
mozile.dom.convertStyleName = function(styleName) {
	if(!styleName || typeof(styleName) != "string") return null;
	return styleName.replace(/\-(\w)/g, function (strMatch, p1){
			return p1.toUpperCase();
	});
}

/**
 * Get the string value of a CSS style declaration.
 * Adapted from http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
 * @param {Node} node The node which has the style.
 * @param {String} cssRule The name of a CSS rule.
 * @type String
 */
mozile.dom.getStyle = function(node, cssRule) {
	var value = "";
	if(!node) return value;
	if(node.nodeType != mozile.dom.ELEMENT_NODE) node = node.parentNode;
	if(!node || node.nodeType != mozile.dom.ELEMENT_NODE) return value;

	if(document.defaultView && document.defaultView.getComputedStyle){
		value = document.defaultView.getComputedStyle(node, "").getPropertyValue(cssRule);
	}
	else if(node.currentStyle){
		cssRule = mozile.dom.convertStyleName(cssRule);
		value = node.currentStyle[cssRule];
	}

	return value;
}

/**
 * Sets the CSS style of an element.
 * @param {Element} element The element which has the style.
 * @param style Either a string denoting the CSS style or an object with keys
 * @type String
 */
mozile.dom.setStyle = function(element, rule, value) {
	if(!element) return;
	if(!rule || typeof(rule) != "string") return;
	
	rule = mozile.dom.convertStyleName(rule);
	if(element.style) element.style[rule] = value;
	else mozile.debug.debug("mozile.dom.setStyle", "Element does not have a 'style' attribute.");
}

/**
 * Adds a "link" element with the given href and type to the head of the document.
 * @param {String} href The target of the link.
 * @param {String} type Optional. The type of the link. Defaults to "text/css".
 * @type Void
 */
mozile.dom.addStyleSheet = function(href, type) {
	var link;
	if(mozile.defaultNS != null) {
		mozile.require("mozile.xml");
		link = mozile.dom.createElementNS(mozile.xml.ns.xhtml, "link");
	}
	else link = mozile.dom.createElement("link");
	link.setAttribute("class", "mozileLink");
	link.setAttribute("href", href);
	link.setAttribute("rel", "stylesheet");
	if(!type) type = "text/css";
	link.setAttribute("type", type);
	mozile.dom.getHead().appendChild(link);
	mozile.dom.links.push(link);
}


/**
 * Gets the position of the element using offsetLeft relative to the container (if provided) or the document element.
 * Adapted from http://www.quirksmode.org/js/findpos.html
 * @param {Node} obj The target element.
 * @param {Container} container Optional. The element relative to which the X position will be measured.
 * @type String
 */
mozile.dom.getX = function(obj, container) {
	var x = 0;
	if(obj.offsetParent) {
		while(obj.offsetParent) {
			x += obj.offsetLeft;
			obj = obj.offsetParent;
			if(obj == container) break;
		}
	}
	else if (obj.x) x += obj.x;
	return x;
}

/**
 * Gets the position of the element using offsetTop relative to the container (if provided) or the document element.
 * Adapted from http://www.quirksmode.org/js/findpos.html
 * @param {Node} obj The target element.
 * @param {Container} container Optional. The element relative to which the X position will be measured.
 * @type String
 */
mozile.dom.getY = function(obj, container) {
	var y = 0;
	if(obj.offsetParent) {
		while(obj.offsetParent) {
			y += obj.offsetTop;
			obj = obj.offsetParent;
			if(obj == container) break;
		}
	}
	else if (obj.y) y += obj.y;
	return y;
}


/**
 * Selection operations.
 * See this for some ideas: http://www.teria.com/~koseki/memo/xbselection/Selection.js
 * @type Object
 */
mozile.dom.selection = new Object();
// JSDoc hack
mozile.dom.selection.prototype = new mozile.Module;

/**
 * Get the global selection object. Initiate it if necessary.
 * @type Selection
 */
mozile.dom.selection.get = function() {
	//return new mozile.dom.Selection();
	if(!mozile.dom._selection) mozile.dom._selection = new mozile.dom.Selection();
	else if(mozile.browser.isIE) mozile.dom._selection._init();
	return mozile.dom._selection;
}

/**
 * Get a namespaced attribute's value.
 * @param {Element} element
 * @param {String} namespaceURI The URI of the namespace to check.
 * @param {String} name The local name of the attribute.
 * @type String
 */
mozile.dom.getAttributeNS = function(element, namespaceURI, name) {
	if(!element) return null;
	if(element.getAttributeNS) return element.getAttributeNS(namespaceURI, name);
	else {
		prefix = mozile.dom.lookupPrefix(element, namespaceURI);
		if(prefix) return element.getAttribute(prefix+":"+name);
	}
	return null;
}


/**
 * Creates an element with a qualified name using mozile.defaultNS if this variable is defined, otherwise an unqualified element.
* @param {String} name The name of the element.
* @type Element
*/
mozile.dom.createElement = function(name) {
	if(mozile.defaultNS) {
		return mozile.dom.createElementNS(mozile.defaultNS, name);
	} else {
		return document.createElement(name);
	}
}

/**
 * Tries to find and return the prefix for the qualified names of node with the given namespace URI.
 * Climbs the DOM tree, checking for any attributes starting with "xmlns:". It then checks those against the given namesace URI. If they match, the prefix part of the attribute name is returned.
 * @param {Node} node The node to check.
 * @param {String} namespaceURI The namespace URI to find the prefix for.
 * @type String
 */
mozile.dom.createElementNS = function(namespaceURI, name) {
	if(document.createElementNS && !mozile.browser.isSafari) return document.createElementNS(namespaceURI, name);
	else {
		// This is the only hack I could figure out that would work in IE6.
		mozile.require("mozile.xml");
		return mozile.xml.parseElement('<'+ name +' xmlns="'+ namespaceURI +'"/>');
	}
	return null;
}

/**
 *
 */
mozile.dom.Range = function(selectRange) {
  if (document.createRange) {
  	var range;
  	if(selectRange) range = selectRange.cloneRange();
  	else range = document.createRange();
  	for(var field in mozile.dom.Range.prototype) 
  		range[field] = mozile.dom.Range.prototype[field];
  	return range;
  } else {
  	if(selectRange && selectRange._range) {
  		var range = new mozile.dom.InternetExplorerRange(selectRange._range.duplicate());
  		range._init();
  		return range;
  	}
  	else return new mozile.dom.InternetExplorerRange();
  }
}


/**
 * Store the details about this range in an object which can be used to restore the range.
 * @type Object
 */
mozile.dom.Range.prototype.store = function () {
	var state = new Object();
	// TODO: Speed optmization for IE. Not stable.
	if(false && this._range) {
		state.format = "IE";
		state.bookmark = this._range.getBookmark();
	}
	else {
		mozile.require("mozile.xpath");
		state.type = "Range";
		state.format = "W3C";
		state.startContainer = mozile.xpath.getXPath(this.startContainer);
		state.startOffset = this.startOffset;
		if(this.startContainer == this.endContainer)
			state.endContainer = state.startContainer;
		else state.endContainer = mozile.xpath.getXPath(this.endContainer);
		state.endContainer = mozile.xpath.getXPath(this.endContainer);
		state.endOffset = this.endOffset;
	}
	return state;
}

/**
 * Takes a stored range object and creates a new range with the same properties.
 * @param {Object} state A state object from 
 * @type Void
 */
mozile.dom.Range.prototype.restore = function(state) {
try {
	// TODO: Speed optmization for IE. Actually slower?
	if(false && this._range) {
		this._range.moveToBookmark(state.bookmark);
		this._init();
	}
	else {
		mozile.require("mozile.xpath");
		//alert(mozile.util.dumpValues(state));
		var startContainer, endContainer;
		startContainer = mozile.xpath.getNode(state.startContainer);
		//alert(["startContainer", state.startContainer, startContainer.nodeName].join("\n"));
		this.setStart(startContainer, state.startOffset);
		if(state.endContainer == state.startContainer) 
			endContainer = startContainer;
		else endContainer = mozile.xpath.getNode(state.endContainer);
		//endContainer = mozile.xpath.getNode(state.endContainer);
		//alert(["endContainer", state.endContainer, endContainer.nodeName].join("\n"));
		this.setEnd(endContainer, state.endOffset);
	}
} catch(e) { 
	alert("Error [mozile.dom.Range.restore]:\n"+ mozile.dumpError(e))
	// +"\nUsing state oject:\n"+ mozile.util.dumpValues(state)); 
}
}

/**
 *
 */
mozile.dom.Selection = function() {
  if (window.getSelection) {
  	var selection = window.getSelection();
  	for(var field in mozile.dom.Selection.prototype) 
  		selection[field] = mozile.dom.Selection.prototype[field];
  	return selection;
  } else {
    return new mozile.dom.InternetExplorerSelection();
  }
}

/**
 * Store the details about this range in an object which can be used to restore the range.
 * @type Object
 */
mozile.dom.Selection.prototype.store = function(oldState, newOffset) {
	var state = new Object();
	if(oldState) {
		for(var i in oldState) state[i] = oldState[i];
		//state.startOffset = newOffset;
		//state.endOffset = newOffset;
		state.anchorOffset = newOffset;
		state.focusOffset = newOffset;
		return state;
	}
	else if(this.rangeCount > 0) {
		//var range = this.getRangeAt(0);
		//if(range) {
		//	range.store = mozile.dom.Range.prototype.store;
		//	return range.store();
		//}
		//else return null;
		// New idea.
		mozile.require("mozile.xpath");
		state.type = "Selection";
		state.format = "W3C";
		state.anchorNode = mozile.xpath.getXPath(this.anchorNode);
		state.anchorOffset = this.anchorOffset;
		if(this.focusNode == this.anchorNode) state.focusNode = state.anchorNode;
		else state.focusNode = mozile.xpath.getXPath(this.focusNode);
		state.focusOffset = this.focusOffset;
		state.isCollapsed = this.isCollapsed;
		return state;
	}
	return null;
}

/**
 * Takes a stored range object and creates a new range with the same properties.
 * @param {Object} state Optional. A state object from a store() call. If none is given, the data from mozile.dom.selection.last is used.
 * @type Void
 */
mozile.dom.Selection.prototype.restore = function(state) {
	if(state) {
		if(state.type == "Selection") {
			mozile.require("mozile.xpath");
			var anchorNode, focusNode;
			anchorNode = mozile.xpath.getNode(state.anchorNode);
			this.collapse(anchorNode, state.anchorOffset);
			//if(!state.isCollapsed) {
			if(state.focusNode != state.anchorNode ||
				state.focusOffset != state.anchorOffset ) {
				if(state.focusNode == state.anchorNode) focusNode = anchorNode;
				else focusNode = mozile.xpath.getNode(state.focusNode);
				try {
					this.extend(focusNode, state.focusOffset);
				} catch(e) {
					mozile.debug.debug("mozile.dom.Selection.restore", "Error extending selection to '"+ state.focusNode +" "+ state.focusOffset +"'.\n"+ mozile.dumpError(e) );
				}
			}
		}
		else if(state.type == "Range") {
			var range = new mozile.dom.Range();
			range.restore(state);
			this.removeAllRanges();
			this.addRange(range);
		}
	}
	else if(mozile.dom.selection.last) {		
		this.collapse(mozile.dom.selection.last.anchorNode, 
			mozile.dom.selection.last.anchorOffset);
		if(!mozile.dom.selection.last.isCollapsed) {
			this.extend(mozile.dom.selection.last.focusNode, 
				mozile.dom.selection.last.focusOffset);
		}
	}
}



	
/**
 * Old function, is now in the Selection constructor
 */
mozile.dom.Selection.getSelection = function () {
	if(window.getSelection) { //FF, Safari
		return window.getSelection();
	} else if (document.getSelection) { // Opera?
		return document.getSelection();
	} else if(document.selection) { // IE win
		return new mozile.dom.Selection();
	}
	return null;
}




if(mozile.browser.isIE) {
	mozile.require("mozile.dom.TreeWalker");
	mozile.require("mozile.dom.InternetExplorerRange");
	mozile.require("mozile.dom.InternetExplorerSelection");
}
else {
	mozile.dom.NodeFilter = NodeFilter;
	mozile.dom.TreeWalker = TreeWalker;
}





Documentation generated by JSDoc on Wed Aug 23 18:45:51 2006