/**
 * jQuery compareDocumentPosition plugin
 * Release #1. August 23, 2008
 * Released under the MIT License.
**/
(function() {
//return all nodes in nodeList that contain this (nodeList item is an ancestor of this)
jQuery.fn.cdpContains = function(nodeList) {
	var $this = $(this).get(0);
	return $(nodeList).filter(function() {
		var result = jQuery.fn.compareDocumentPosition($this, this);
		return !!(result & 8 && !(result & 1));
	});

};
//return all nodes in nodeList that are contained by this (nodeList item is a descendant of this)
jQuery.fn.cdpIsContained = function(nodeList) {
	var $this = $(this).get(0);
	return $(nodeList).filter(function() {
		var result = jQuery.fn.compareDocumentPosition($this, this);
		return !!(result & 16 && !(result & 1));
	});
};
//return all nodes in nodeList that precede this in the DOM (not necessarily as an ancestor)
jQuery.fn.cdpPreceding = function(nodeList) {
	var $this = $(this).get(0);
	return $(nodeList).filter(function() {
		var result = jQuery.fn.compareDocumentPosition($this, this);
		return !!(result & 2 && !(result & 1));
	});
};
//return all nodes in nodeList that follow this in the DOM (not necessarily as a descendant)
jQuery.fn.cdpFollowing = function(nodeList) {
	var $this = $(this).get(0);
	return $(nodeList).filter(function() {
		var result = jQuery.fn.compareDocumentPosition($this, this);
		return !!(result & 4 && !(result & 1));
	});
};
//return all nodes in nodeList that are disconnected from this
jQuery.fn.cdpDisconnected = function(nodeList) {
	var $this = $(this).get(0);
	return $(nodeList).filter(function() {
		return !!(jQuery.fn.compareDocumentPosition($this, this) & 1);
	});
};

jQuery.fn.compareDocumentPosition = function(node1, node2) {
	//Gecko, Opera have it built-in
	if ("compareDocumentPosition" in document.documentElement) {
		jQuery.fn.compareDocumentPosition = function(node1, node2) {
			return node1.compareDocumentPosition(node2);
			};
	}
	//Internet Explorer
	else if ("sourceIndex" in document.documentElement && "contains" in document.documentElement) {
		jQuery.fn.compareDocumentPosition = function(node1, node2) {
			if (node1 == node2) return 0;
			//if they don't have the same parent, there's a disconnect
			if (getRootParent(node1) != getRootParent(node2)) return 1;
			//use this if both nodes have a sourceIndex (text nodes don't)
			if ("sourceIndex" in node1 && "sourceIndex" in node2) {
				return comparePosition(node1, node2);
			}
			//document will definitely contain the other node
			if (node1 == document) return 20;
			else if (node2 == document) return 10;
			//get sourceIndexes to use for both nodes
			var useNode1 = getUseNode(node1), useNode2 = getUseNode(node2);
			//call this function again to get the result
			var result = comparePosition(useNode1, useNode2);
			//clean up if needed
			if (node1 != useNode1) useNode1.parentNode.removeChild(useNode1);
			if (node2 != useNode2) useNode2.parentNode.removeChild(useNode2);
			return result;

			//Compare Position - MIT Licensed, John Resig; http://ejohn.org/blog/comparing-document-position/
			//Already checked for equality and disconnect
			function comparePosition(node1, node2) {
				return (node1.contains(node2) && 16) +
					(node2.contains(node1) && 8) +
						(node1.sourceIndex >= 0 && node2.sourceIndex >= 0 ?
							(node1.sourceIndex < node2.sourceIndex && 4) +
								(node1.sourceIndex > node2.sourceIndex && 2) :
							1);
			}

			//get a node with a sourceIndex to use
			function getUseNode(node) {
				//if the node already has a sourceIndex, use that node
				if ("sourceIndex" in node) return node;
				//otherwise, insert a comment (which has a sourceIndex but minimal DOM impact) before the node and use that
				return node.parentNode.insertBefore(document.createComment(""), node);
			}
		};
	}
	else {
		//Safari and others; will work in IE
		//inspired by base2: http://code.google.com/p/base2/
		jQuery.fn.compareDocumentPosition = function(node1, node2) {
			if (node1 == node2) return 0;
			if (getRootParent(node1) != getRootParent(node2)) return 1;
			//contains() only works if both are elements
			if (node1 == document
					|| ("contains" in node1 && "contains" in node2 && node1.contains(node2))) {
				return 20;
			}
			else if (node2 == document
					 || ("contains" in node1 && "contains" in node2 && node2.contains(node1))) {
				return 10;
			}
			return compareOffsetStrings(getOffsetString(node1), getOffsetString(node2));

			//takes the sortable string from getOffset
			function compareOffsetStrings(offset1, offset2) {
				//they're siblings or at the same depth
				if (offset1.length == offset2.length) {
					return (offset1 < offset2) ? 4 : 2;
					}
				//the first one is either a parent or at a shallower depth
				else if (offset1.length < offset2.length) {
					//truncate the longer one
					var offset2start = offset2.substr(0, offset1.length);
					//if they're the same at this point, we know node1 is a parent
					if (offset1 == offset2start) return 20;
					//call itself again now that they're the same length
					return compareOffsetStrings(offset1, offset2start);
					}
				else  {
					//flip the order of the arguments...
					var result = compareOffsetStrings(offset2, offset1);
					//...then shift the bits to get the correct result
					return (result & 4) ? result >> 1 : result << 1;
				}
			}

			//make a string that's sortable to determine a sourceIndex
			function getOffsetString(node) {
				var offsets = [];
				do {
					var offset = 0, prev = node;
					//count preceding siblings
					while (prev = prev.previousSibling) {
						offset++;
					}
					//get the total number of sibling nodes (before and after)
					var padLength = node.parentNode.childNodes.length.toString().length;
					var offsetLength = offset.toString().length;
					//zero-pad the offset to make sure the string compares properly
					if (padLength > offsetLength) {
						for (; offsetLength <= padLength; offsetLength++) {
							offset = "0" + offset;
						}
					}
					offsets.unshift(offset);
				}
				while ((node = node.parentNode) && node != document);
				//reverse the array to start the string at the top of the tree
				//return the final delimited string
				return offsets.join(".");
			}
		}
	}
	//now that we've redefined the function during the first run, run it to get the actual value
	return jQuery.fn.compareDocumentPosition(node1, node2);
};

//node.ownerDocument gives the document object, which isn't the right info for a disconnect
function getRootParent(node) {
	do { var parent = node; }
	while (node = node.parentNode);
	return parent;
}
})();
