Write collectAllEffectivelyContainedNodes() helper
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Tue, 28 Jun 2011 14:50:46 -0600
changeset 339 178bf9301a87
parent 338 8963fd7c4a1f
child 340 b2c785beb965
Write collectAllEffectivelyContainedNodes() helper
implementation.js
--- a/implementation.js	Tue Jun 28 14:33:05 2011 -0600
+++ b/implementation.js	Tue Jun 28 14:50:46 2011 -0600
@@ -207,65 +207,22 @@
 // one with (property X) and at least one with (property not-X).  Otherwise
 // false."
 function indetermHelper(callback) {
-	var range = getActiveRange();
-	// XXX: This algorithm for getting all effectively contained nodes might be
-	// wrong . . .
-	var node = range.startContainer;
-	while (node.parentNode && node.parentNode.firstChild == node) {
-		node = node.parentNode;
-	}
-	var stop = nextNodeDescendants(range.endContainer);
-
-	var hasProperty = false;
-	var lacksProperty = false;
-	for (; node && node != stop; node = nextNode(node)) {
-		if (!isEffectivelyContained(node, range)
-		|| node.nodeType != Node.TEXT_NODE
-		|| !isEditable(node)) {
-			continue;
-		}
-
-		if (callback(node)) {
-			hasProperty = true;
-		} else {
-			lacksProperty = true;
-		}
-		if (hasProperty && lacksProperty) {
-			return true;
-		}
-	}
-	return false;
+	var nodes = collectAllEffectivelyContainedNodes(getActiveRange(), function(node) {
+		return isEditable(node)
+			&& node.nodeType == Node.TEXT_NODE;
+	});
+	return nodes.some(callback) && !nodes.every(callback);
 }
 
 // For computing states of the form "True if every editable Text node that is
 // effectively contained in the active range (has property X), and there is at
 // least one such Text node.  Otherwise false."
 function stateHelper(callback) {
-	var range = getActiveRange();
-	// XXX: This algorithm for getting all effectively contained nodes might be
-	// wrong . . .
-	var node = range.startContainer;
-	while (node.parentNode && node.parentNode.firstChild == node) {
-		node = node.parentNode;
-	}
-	var stop = nextNodeDescendants(range.endContainer);
-
-	var atLeastOne = false;
-	for (; node && node != stop; node = nextNode(node)) {
-		if (!isEffectivelyContained(node, range)
-		|| node.nodeType != Node.TEXT_NODE
-		|| !isEditable(node)) {
-			continue;
-		}
-
-		atLeastOne = true;
-
-		if (!callback(node)) {
-			return false;
-		}
-	}
-
-	return atLeastOne;
+	var nodes = collectAllEffectivelyContainedNodes(getActiveRange(), function(node) {
+		return isEditable(node)
+			&& node.nodeType == Node.TEXT_NODE;
+	});
+	return nodes.every(callback) && nodes.length >= 1;
 }
 
 // Returns a standard CSS 2.1 computed value for text-align, adapting
@@ -1417,6 +1374,29 @@
 	return false;
 }
 
+// Like collectAllContainedNodes(), but for effectively contained nodes.
+function collectAllEffectivelyContainedNodes(range, condition) {
+	if (typeof condition == "undefined") {
+		condition = function() { return true };
+	}
+	var node = range.startContainer;
+	while (isEffectivelyContained(node.parentNode, range)) {
+		node = node.parentNode;
+	}
+
+	var stop = nextNodeDescendants(range.endContainer);
+
+	var nodeList = [];
+	while (isBefore(node, stop)) {
+		if (isEffectivelyContained(node, range)
+		&& condition(node)) {
+			nodeList.push(node);
+		}
+		node = nextNode(node);
+	}
+	return nodeList;
+}
+
 // "An unwrappable node is an HTML element which may not be used where only
 // phrasing content is expected (not counting unknown or obsolete elements,
 // which cannot be used at all); or any Element whose display property computes
@@ -3143,19 +3123,12 @@
 		// value "sub" and at least one with some other effective value; or if
 		// there is some editable Text node effectively contained in the active
 		// range with effective value "mixed". Otherwise false."
-		//
-		// The use of stateHelper() here is a bit of a hack, but it works.  The
-		// way the logic works out is that it returns true if every editable
-		// text node etc. does *not* have effective value "mixed" and there's
-		// at least one, so if we negate it that means either there's no
-		// editable etc. text node or else one has effective value mixed.  The
-		// second invocation of stateHelper() handles the case where there are
-		// no editable Text nodes effectively contained in the range.
-		if (indetermHelper(function(node) { return getEffectiveValue(node, "subscript") == "sub" })) {
-			return true;
-		}
-		return !stateHelper(function(node) { return getEffectiveValue(node, "subscript") != "mixed" })
-			&& stateHelper(function() { return true });
+		var nodes = collectAllEffectivelyContainedNodes(getActiveRange(), function(node) {
+			return isEditable(node) && node.nodeType == Node.TEXT_NODE;
+		});
+		return (nodes.some(function(node) { return getEffectiveValue(node, "subscript") == "sub" })
+			&& nodes.some(function(node) { return getEffectiveValue(node, "subscript") != "sub" }))
+			|| nodes.some(function(node) { return getEffectiveValue(node, "subscript") == "mixed" });
 	}, state: function() { return stateHelper(function(node) {
 		// "True if every editable Text node that is effectively contained in
 		// the active range has effective value "sub", and there is at least
@@ -3194,13 +3167,12 @@
 		// value "super" and at least one with some other effective value; or
 		// if there is some editable Text node effectively contained in the
 		// active range with effective value "mixed". Otherwise false."
-		//
-		// See subscript for implementation remark.
-		if (indetermHelper(function(node) { return getEffectiveValue(node, "superscript") == "super" })) {
-			return true;
-		}
-		return !stateHelper(function(node) { return getEffectiveValue(node, "superscript") != "mixed" })
-			&& stateHelper(function() { return true });
+		var nodes = collectAllEffectivelyContainedNodes(getActiveRange(), function(node) {
+			return isEditable(node) && node.nodeType == Node.TEXT_NODE;
+		});
+		return (nodes.some(function(node) { return getEffectiveValue(node, "superscript") == "super" })
+			&& nodes.some(function(node) { return getEffectiveValue(node, "superscript") != "super" }))
+			|| nodes.some(function(node) { return getEffectiveValue(node, "superscript") == "mixed" });
 	}, state: function() { return stateHelper(function(node) {
 		// "True if every editable Text node that is effectively contained in
 		// the active range has effective value "super", and there is at least