Make justify* well-defined for CSS3 align vals
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 10 Jul 2011 11:34:29 -0600
changeset 382 08d1d92da268
parent 381 421087165b64
child 383 09930358b11f
Make justify* well-defined for CSS3 align vals
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Sun Jul 10 10:51:09 2011 -0600
+++ b/editcommands.html	Sun Jul 10 11:34:29 2011 -0600
@@ -3061,9 +3061,6 @@
 
 <p>To <dfn id=fix-disallowed-ancestors>fix disallowed ancestors</dfn> of <var title="">node</var>:
 
-<p class=XXX>Do we want to get rid of attributes that are no longer allowed
-here?
-
 <ol>
   <li>If <var title="">node</var> is not <a href=#editable>editable</a>, abort these steps.
 
@@ -3218,6 +3215,10 @@
 Firefox ties the value closely to the state, returning true for the state if
 and only if the value matches the desired value, but this seems less useful
 than what I've specced for the state.
+
+This API is based on the four-state text-align of CSS 2.1.  We do some crude
+mapping to make it not break too badly with CSS3 values, but it's not going to
+work well given the design of the API.
 -->
 <ol>
   <li>While <var title="">node</var> is neither null nor an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code>, or it is an
@@ -3231,11 +3232,18 @@
   marginal corner case anyway.  (It will only happen if, e.g., everything up to
   and including the html and body elements have display: inline or none.) -->
 
-  <li>Return "center", "justify", "left", or "right", depending on the
-  <a href=http://www.w3.org/TR/CSS21/cascade.html#computed-value>computed value</a> of <var title="">node</var>'s "text-align" property.
-
-  <p class=XXX>What to do for "start" "auto" etc. currently undefined.  We
-  assume something CSS 2.1-ish.
+  <li>If <var title="">node</var>'s "text-align" property has <a href=http://www.w3.org/TR/CSS21/cascade.html#computed-value>computed value</a> "start",
+  return "left" if the <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#the-directionality title="the directionality">directionality</a> of <var title="">node</var> is "ltr", "right"
+  if it is "rtl".
+
+  <li>If <var title="">node</var>'s "text-align" property has <a href=http://www.w3.org/TR/CSS21/cascade.html#computed-value>computed value</a> "end", return
+  "right" if the <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#the-directionality title="the directionality">directionality</a> of <var title="">node</var> is "ltr", "left" if it
+  is "rtl".
+
+  <li>If <var title="">node</var>'s "text-align" property has <a href=http://www.w3.org/TR/CSS21/cascade.html#computed-value>computed value</a> "center",
+  "justify", "left", or "right", return that value.
+
+  <li>Return "left".
 </ol>
 
 
@@ -4971,11 +4979,8 @@
   append <var title="">node</var> to <var title="">node list</var> if the last member of
   <var title="">node list</var> (if any) is not an <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> of <var title="">node</var>;
   <var title="">node</var> is <a href=#editable>editable</a>; <var title="">node</var> is an <a href=#allowed-child>allowed
-  child</a> of "div"; and either <var title="">node</var> is an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> and the
-  CSS property "text-align" does not compute to <var title="">alignment</var> on it, or
-  it is not an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code>, but its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code>, and the CSS
-  property "text-align" does not compute to <var title="">alignment</var> on its
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+  child</a> of "div"; and <var title="">node</var>'s <a href=#alignment-value>alignment value</a> is
+  not <var title="">alignment</var>.
   <!-- Of tested browsers, only Chrome 13 dev seems to not apply the alignment
   to nodes that are already aligned.  Even then, it does apply it if the
   alignment is just inherited from the root. -->
--- a/implementation.js	Sun Jul 10 10:51:09 2011 -0600
+++ b/implementation.js	Sun Jul 10 11:34:29 2011 -0600
@@ -234,24 +234,44 @@
 	return nodes.every(callback) && nodes.length >= 1;
 }
 
-// Returns a standard CSS 2.1 computed value for text-align, adapting
-// nonstandard values and things like "start" and "auto".
-function getRealTextAlign(element) {
-	var computedAlign = getComputedStyle(element).textAlign
-		.replace(/^-(moz|webkit)-/, "");
-	if (computedAlign == "auto" || computedAlign == "start") {
-		// Depends on directionality.  Note: this is a serious hack.
-		do {
-			var dir = element.dir.toLowerCase();
-			element = element.parentNode;
-		} while (element && element.nodeType == Node.ELEMENT_NODE && dir != "ltr" && dir != "rtl");
-		if (dir == "rtl") {
-			computedAlign = "right";
-		} else {
-			computedAlign = "left";
-		}
-	}
-	return computedAlign;
+// "the directionality" from HTML.  I don't bother caring about non-HTML
+// elements.
+//
+// "The directionality of an element is either 'ltr' or 'rtl', and is
+// determined as per the first appropriate set of steps from the following
+// list:"
+function getDirectionality(element) {
+	// "If the element's dir attribute is in the ltr state
+	//     The directionality of the element is 'ltr'."
+	if (element.dir == "ltr") {
+		return "ltr";
+	}
+
+	// "If the element's dir attribute is in the rtl state
+	//     The directionality of the element is 'rtl'."
+	if (element.dir == "rtl") {
+		return "rtl";
+	}
+
+	// "If the element's dir attribute is in the auto state
+	// "If the element is a bdi element and the dir attribute is not in a
+	// defined state (i.e. it is not present or has an invalid value)
+	//     [lots of complicated stuff]
+	//
+	// Skip this, since no browser implements it anyway.
+
+	// "If the element is a root element and the dir attribute is not in a
+	// defined state (i.e. it is not present or has an invalid value)
+	//     The directionality of the element is 'ltr'."
+	if (!isHtmlElement(element.parentNode)) {
+		return "ltr";
+	}
+
+	// "If the element has a parent element and the dir attribute is not in a
+	// defined state (i.e. it is not present or has an invalid value)
+	//     The directionality of the element is the same as the element's
+	//     parent element's directionality."
+	return getDirectionality(element.parentNode);
 }
 
 //@}
@@ -3596,9 +3616,31 @@
 		return "left";
 	}
 
-	// "Return "center", "justify", "left", or "right", depending on the
-	// computed value of node's "text-align" property."
-	return getRealTextAlign(node);
+	var computedValue = getComputedStyle(node).textAlign
+		// Hack around browser non-standardness
+		.replace(/^-(moz|webkit)-/, "")
+		.replace(/^auto$/, "start");
+
+	// "If node's "text-align" property has computed value "start", return
+	// "left" if the directionality of node is "ltr", "right" if it is "rtl"."
+	if (computedValue == "start") {
+		return getDirectionality(node) == "ltr" ? "left" : "right";
+	}
+
+	// "If node's "text-align" property has computed value "end", return
+	// "right" if the directionality of node is "ltr", "left" if it is "rtl"."
+	if (computedValue == "end") {
+		return getDirectionality(node) == "ltr" ? "right" : "left";
+	}
+
+	// "If node's "text-align" property has computed value "center", "justify",
+	// "left", or "right", return that value."
+	if (["center", "justify", "left", "right"].indexOf(computedValue) != -1) {
+		return computedValue;
+	}
+
+	// "Return "left"."
+	return "left";
 }
 //@}
 
@@ -5113,23 +5155,12 @@
 
 	// "For each node node contained in new range, append node to node list if
 	// the last member of node list (if any) is not an ancestor of node; node
-	// is editable; node is an allowed child of div; and either node is an
-	// Element and the CSS property "text-align" does not compute to alignment
-	// on it, or it is not an Element, but its parent is an Element, and the
-	// CSS property "text-align" does not compute to alignment on its parent."
+	// is editable; node is an allowed child of "div"; and node's alignment
+	// value is not alignment."
 	nodeList = getContainedNodes(newRange, function(node) {
-		if (!isEditable(node) || !isAllowedChild(node, "div")) {
-			return false;
-		}
-		// Gecko and WebKit have lots of fun here confusing us with
-		// vendor-specific values, and in Gecko's case "start".
-		var element = node.nodeType == Node.ELEMENT_NODE
-			? node
-			: node.parentNode;
-		if (!element || element.nodeType != Node.ELEMENT_NODE) {
-			return false;
-		}
-		return getRealTextAlign(element) != alignment;
+		return isEditable(node)
+			&& isAllowedChild(node, "div")
+			&& getAlignmentValue(node) != alignment;
 	});
 
 	// "While node list is not empty:"
--- a/source.html	Sun Jul 10 10:51:09 2011 -0600
+++ b/source.html	Sun Jul 10 11:34:29 2011 -0600
@@ -3037,9 +3037,6 @@
 <!-- @{ -->
 <p>To <dfn>fix disallowed ancestors</dfn> of <var>node</var>:
 
-<p class=XXX>Do we want to get rid of attributes that are no longer allowed
-here?
-
 <ol>
   <li>If <var>node</var> is not <span>editable</span>, abort these steps.
 
@@ -3196,6 +3193,10 @@
 Firefox ties the value closely to the state, returning true for the state if
 and only if the value matches the desired value, but this seems less useful
 than what I've specced for the state.
+
+This API is based on the four-state text-align of CSS 2.1.  We do some crude
+mapping to make it not break too badly with CSS3 values, but it's not going to
+work well given the design of the API.
 -->
 <ol>
   <li>While <var>node</var> is neither null nor an [[element]], or it is an
@@ -3209,11 +3210,18 @@
   marginal corner case anyway.  (It will only happen if, e.g., everything up to
   and including the html and body elements have display: inline or none.) -->
 
-  <li>Return "center", "justify", "left", or "right", depending on the
-  [[compval]] of <var>node</var>'s "text-align" property.
-
-  <p class=XXX>What to do for "start" "auto" etc. currently undefined.  We
-  assume something CSS 2.1-ish.
+  <li>If <var>node</var>'s "text-align" property has [[compval]] "start",
+  return "left" if the [[directionality]] of <var>node</var> is "ltr", "right"
+  if it is "rtl".
+
+  <li>If <var>node</var>'s "text-align" property has [[compval]] "end", return
+  "right" if the [[directionality]] of <var>node</var> is "ltr", "left" if it
+  is "rtl".
+
+  <li>If <var>node</var>'s "text-align" property has [[compval]] "center",
+  "justify", "left", or "right", return that value.
+
+  <li>Return "left".
 </ol>
 <!-- @} -->
 
@@ -4971,11 +4979,8 @@
   append <var>node</var> to <var>node list</var> if the last member of
   <var>node list</var> (if any) is not an [[ancestor]] of <var>node</var>;
   <var>node</var> is <span>editable</span>; <var>node</var> is an <span>allowed
-  child</span> of "div"; and either <var>node</var> is an [[element]] and the
-  CSS property "text-align" does not compute to <var>alignment</var> on it, or
-  it is not an [[element]], but its [[parent]] is an [[element]], and the CSS
-  property "text-align" does not compute to <var>alignment</var> on its
-  [[parent]].
+  child</span> of "div"; and <var>node</var>'s <span>alignment value</span> is
+  not <var>alignment</var>.
   <!-- Of tested browsers, only Chrome 13 dev seems to not apply the alignment
   to nodes that are already aligned.  Even then, it does apply it if the
   alignment is just inherited from the root. -->
--- a/tests.js	Sun Jul 10 10:51:09 2011 -0600
+++ b/tests.js	Sun Jul 10 11:34:29 2011 -0600
@@ -2417,6 +2417,11 @@
 		'<div align=left>{<div align=center>foo</div><img src=/img/lion.svg>}</div>',
 		'<div align=center>{<div align=left>foo</div><!-- bar -->}</div>',
 		'<div align=left>{<div align=center>foo</div><!-- bar -->}</div>',
+
+		'<div style=text-align:start>[foo]</div><p>extra',
+		'<div style=text-align:end>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:start>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:end>[foo]</div><p>extra',
 	],
 	//@}
 	justifyfull: [
@@ -2510,6 +2515,11 @@
 		'<div align=nonsense><p>[foo]</div><p>extra',
 		'<div style=text-align:inherit><p>[foo]</div><p>extra',
 		'<quasit align=center><p>[foo]</p></quasit><p>extra',
+
+		'<div style=text-align:start>[foo]</div><p>extra',
+		'<div style=text-align:end>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:start>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:end>[foo]</div><p>extra',
 	],
 	//@}
 	justifyleft: [
@@ -2603,6 +2613,11 @@
 		'<div align=nonsense><p>[foo]</div><p>extra',
 		'<div style=text-align:inherit><p>[foo]</div><p>extra',
 		'<quasit align=center><p>[foo]</p></quasit><p>extra',
+
+		'<div style=text-align:start>[foo]</div><p>extra',
+		'<div style=text-align:end>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:start>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:end>[foo]</div><p>extra',
 	],
 	//@}
 	justifyright: [
@@ -2696,6 +2711,11 @@
 		'<div align=nonsense><p>[foo]</div><p>extra',
 		'<div style=text-align:inherit><p>[foo]</div><p>extra',
 		'<quasit align=center><p>[foo]</p></quasit><p>extra',
+
+		'<div style=text-align:start>[foo]</div><p>extra',
+		'<div style=text-align:end>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:start>[foo]</div><p>extra',
+		'<div dir=rtl style=text-align:end>[foo]</div><p>extra',
 	],
 	//@}
 	outdent: [