Define indeterm/value for formatBlock
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 29 Jun 2011 13:14:53 -0600
changeset 347 af1f237ace1c
parent 346 5916e1f46d55
child 348 d593a7b7d217
Define indeterm/value for formatBlock

I think I've got them all now . . .
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Wed Jun 29 12:42:49 2011 -0600
+++ b/editcommands.html	Wed Jun 29 13:14:53 2011 -0600
@@ -5317,6 +5317,8 @@
   the "in the same editing host" thing handles it?
   </div>
 
+  <p class=XXX>We probably don't want to break out of tables and such here.
+
   <li>Let <var title="">node list</var> be a list of <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a>, initially empty.
 
   <li>For each <var title="">node</var> in <var title="">original node list</var>, <a href=#fix-prohibited-paragraph-descendants>fix
@@ -5425,7 +5427,102 @@
   </ol>
 </ol>
 
-<p class=XXX>Spec value/indeterm.
+<p><a href=#indeterminate>Indeterminate</a>:
+<!--
+Firefox 6.0a2 throws, Chrome 14 dev always returns false, Opera 11.11 doesn't
+support indeterm to start with, IE9 was uncooperative in testing so I'm not
+sure what it does.  I'm speccing it just because it makes sense.
+-->
+<ol>
+  <li><a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>, and let <var title="">new
+  range</var> be the result.
+
+  <li>Let <var title="">node list</var> be all <a href=#visible-node title="visible node">visible</a>
+  <a href=#editable>editable</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that are <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new
+  range</var> and have no <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.
+
+  <li>If <var title="">node list</var> is empty, return false.
+
+  <li>Let <var title="">type</var> be null.
+
+  <li>For each <var title="">node</var> in <var title="">node list</var>:
+
+  <ol>
+    <li>While <var title="">node</var>'s <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 <a href=#editable>editable</a> and
+    <a href=#in-the-same-editing-host>in the same editing host</a> as <var title="">node</var>, and
+    <var title="">node</var> is not an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>
+    "address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", set
+    <var title="">node</var> to 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>.
+
+    <p class=XXX>This doesn't really make sense if there's an intervening table
+    or something.
+
+    <li>Let <var title="">current type</var> be the empty string.
+
+    <li>If <var title="">node</var> is an <a href=#editable>editable</a> <a href=#html-element>HTML
+    element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> "address", "div", "h1", "h2", "h3", "h4",
+    "h5", "h6", "p", or "pre", set <var title="">current type</var> to <var title="">node</var>'s
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>.
+
+    <li>If <var title="">type</var> is null, set <var title="">type</var> to <var title="">current
+    type</var>.
+
+    <li>Otherwise, if <var title="">type</var> does not equal <var title="">current type</var>,
+    return true.
+  </ol>
+
+  <li>Return false.
+</ol>
+
+<p><a href=#value>Value</a>:
+<!--
+IE9 returns human-readable strings like "Normal" (p/div/etc.), "Formatted"
+(pre), "Heading 1" (h1), etc.  Firefox 6.0a2 and Chrome 14 dev both return the
+appropriate tag name in lowercase, or the empty string if there is no
+appropriate tag.  Opera 11.11 behaves the same, but with uppercase.
+
+IE9 looks like it recognizes address, h*, pre, dd, dt, ol, ul, and dir, with
+everything else registering as "Normal".  Firefox 6.0a2 recognizes only the
+arguments it accepts for formatBlock, namely address, h*, p, and pre.  Chrome
+14 dev recognizes address, div, h*, dd, dl, dt, p, pre plus lots of random
+other stuff like blockquote and section.  I'll go with everything that
+execCommand("formatblock") accepts as an argument, which at the time of this
+writing means what Firefox supports plus div.
+-->
+<ol>
+  <li><a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>, and let <var title="">new
+  range</var> be the result.
+
+  <li>Let <var title="">node</var> be the first <a href=#visible-node title="visible
+  node">visible</a> <a href=#editable>editable</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in
+  <var title="">new range</var> and has no <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.  If there is no such <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>,
+  return the empty string.
+
+  <li>While <var title="">node</var>'s <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 <a href=#editable>editable</a> and <a href=#in-the-same-editing-host>in
+  the same editing host</a> as <var title="">node</var>, and <var title="">node</var> is not
+  an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> "address", "div", "h1", "h2",
+  "h3", "h4", "h5", "h6", "p", or "pre", set <var title="">node</var> to 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>.
+  <!--
+  Opera 11.11 doesn't require it be editable, so it will return "DIV" instead of
+  "" for <div contenteditable>foo</div>.
+
+  Chrome 14 dev will report "div" for <div><ol><li>foo</ol></div> or such.
+  Opera 11.11 reports "".  IE and Firefox didn't cooperate with testing.  Opera
+  makes somewhat more sense, but formatBlock is actually broken in that case
+  right now anyway for any wrapper other than div, so I'll punt on a solution.
+  -->
+  <p class=XXX>This doesn't really make sense if there's an intervening table
+  or something.
+
+  <li>If <var title="">node</var> is an <a href=#editable>editable</a> <a href=#html-element>HTML element</a>
+  with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> "address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p",
+  or "pre", return its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>, <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#converted-to-lowercase>converted
+  to lowercase</a>.
+  <!-- Actually, we don't really need to specify "editable", since it has to be
+  editable. -->
+
+  <li>Return the empty string.
+</ol>
 
 
 <h3 id=the-forwarddelete-command><span class=secno>8.11 </span><dfn>The <code title="">forwardDelete</code> command</dfn></h3>
--- a/implementation.js	Wed Jun 29 12:42:49 2011 -0600
+++ b/implementation.js	Wed Jun 29 13:14:53 2011 -0600
@@ -5595,6 +5595,100 @@
 					function() { return document.createElement(value) });
 			}
 		}
+	}, indeterm: function() {
+		// "Block-extend the active range, and let new range be the result."
+		var newRange = blockExtendRange(getActiveRange());
+
+		// "Let node list be all visible editable nodes that are contained in
+		// new range and have no children."
+		var nodeList = collectAllContainedNodes(newRange, function(node) {
+			return isVisibleNode(node)
+				&& isEditable(node)
+				&& !node.hasChildNodes();
+		});
+
+		// "If node list is empty, return false."
+		if (!nodeList.length) {
+			return false;
+		}
+
+		// "Let type be null."
+		var type = null;
+
+		// "For each node in node list:"
+		for (var i = 0; i < nodeList.length; i++) {
+			var node = nodeList[i];
+
+			// "While node's parent is editable and in the same editing host as
+			// node, and node is not an HTML element with local name "address",
+			// "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", set
+			// node to its parent."
+			while (isEditable(node.parentNode)
+			&& inSameEditingHost(node, node.parentNode)
+			&& !isHtmlElement(node, ["address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"])) {
+				node = node.parentNode;
+			}
+
+			// "Let current type be the empty string."
+			var currentType = "";
+
+			// "If node is an editable HTML element with local name "address",
+			// "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", set
+			// current type to node's local name."
+			if (isEditable(node)
+			&& isHtmlElement(node, ["address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"])) {
+				currentType = node.tagName;
+			}
+
+			// "If type is null, set type to current type."
+			if (type === null) {
+				type = currentType;
+
+			// "Otherwise, if type does not equal current type, return true."
+			} else if (type != currentType) {
+				return true;
+			}
+		}
+
+		// "Return false."
+		return false;
+	}, value: function() {
+		// "Block-extend the active range, and let new range be the result."
+		var newRange = blockExtendRange(getActiveRange());
+
+		// "Let node be the first visible editable node that is contained in
+		// new range and has no children. If there is no such node, return the
+		// empty string."
+		var nodes = collectAllContainedNodes(newRange, function(node) {
+			return isVisibleNode(node)
+				&& isEditable(node)
+				&& !node.hasChildNodes();
+		});
+		if (!nodes.length) {
+			return "";
+		}
+		var node = nodes[0];
+
+		// "While node's parent is editable and in the same editing host as
+		// node, and node is not an HTML element with local name "address",
+		// "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", set node
+		// to its parent."
+		while (isEditable(node.parentNode)
+		&& inSameEditingHost(node, node.parentNode)
+		&& !isHtmlElement(node, ["address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"])) {
+			node = node.parentNode;
+		}
+
+		// "If node is an editable HTML element with local name "address",
+		// "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", return its
+		// local name, converted to lowercase."
+		if (isEditable(node)
+		&& isHtmlElement(node, ["address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"])) {
+			return node.tagName.toLowerCase();
+		}
+
+		// "Return the empty string."
+		return "";
 	}
 };
 //@}
--- a/source.html	Wed Jun 29 12:42:49 2011 -0600
+++ b/source.html	Wed Jun 29 13:14:53 2011 -0600
@@ -5326,6 +5326,8 @@
   the "in the same editing host" thing handles it?
   </div>
 
+  <p class=XXX>We probably don't want to break out of tables and such here.
+
   <li>Let <var>node list</var> be a list of [[nodes]], initially empty.
 
   <li>For each <var>node</var> in <var>original node list</var>, <span>fix
@@ -5434,7 +5436,102 @@
   </ol>
 </ol>
 
-<p class=XXX>Spec value/indeterm.
+<p><span>Indeterminate</span>:
+<!--
+Firefox 6.0a2 throws, Chrome 14 dev always returns false, Opera 11.11 doesn't
+support indeterm to start with, IE9 was uncooperative in testing so I'm not
+sure what it does.  I'm speccing it just because it makes sense.
+-->
+<ol>
+  <li><span>Block-extend</span> the <span>active range</span>, and let <var>new
+  range</var> be the result.
+
+  <li>Let <var>node list</var> be all <span title="visible node">visible</span>
+  <span>editable</span> [[nodes]] that are [[contained]] in <var>new
+  range</var> and have no [[children]].
+
+  <li>If <var>node list</var> is empty, return false.
+
+  <li>Let <var>type</var> be null.
+
+  <li>For each <var>node</var> in <var>node list</var>:
+
+  <ol>
+    <li>While <var>node</var>'s [[parent]] is <span>editable</span> and
+    <span>in the same editing host</span> as <var>node</var>, and
+    <var>node</var> is not an <span>HTML element</span> with [[localname]]
+    "address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", or "pre", set
+    <var>node</var> to its [[parent]].
+
+    <p class=XXX>This doesn't really make sense if there's an intervening table
+    or something.
+
+    <li>Let <var>current type</var> be the empty string.
+
+    <li>If <var>node</var> is an <span>editable</span> <span>HTML
+    element</span> with [[localname]] "address", "div", "h1", "h2", "h3", "h4",
+    "h5", "h6", "p", or "pre", set <var>current type</var> to <var>node</var>'s
+    [[localname]].
+
+    <li>If <var>type</var> is null, set <var>type</var> to <var>current
+    type</var>.
+
+    <li>Otherwise, if <var>type</var> does not equal <var>current type</var>,
+    return true.
+  </ol>
+
+  <li>Return false.
+</ol>
+
+<p><span>Value</span>:
+<!--
+IE9 returns human-readable strings like "Normal" (p/div/etc.), "Formatted"
+(pre), "Heading 1" (h1), etc.  Firefox 6.0a2 and Chrome 14 dev both return the
+appropriate tag name in lowercase, or the empty string if there is no
+appropriate tag.  Opera 11.11 behaves the same, but with uppercase.
+
+IE9 looks like it recognizes address, h*, pre, dd, dt, ol, ul, and dir, with
+everything else registering as "Normal".  Firefox 6.0a2 recognizes only the
+arguments it accepts for formatBlock, namely address, h*, p, and pre.  Chrome
+14 dev recognizes address, div, h*, dd, dl, dt, p, pre plus lots of random
+other stuff like blockquote and section.  I'll go with everything that
+execCommand("formatblock") accepts as an argument, which at the time of this
+writing means what Firefox supports plus div.
+-->
+<ol>
+  <li><span>Block-extend</span> the <span>active range</span>, and let <var>new
+  range</var> be the result.
+
+  <li>Let <var>node</var> be the first <span title="visible
+  node">visible</span> <span>editable</span> [[node]] that is [[contained]] in
+  <var>new range</var> and has no [[children]].  If there is no such [[node]],
+  return the empty string.
+
+  <li>While <var>node</var>'s [[parent]] is <span>editable</span> and <span>in
+  the same editing host</span> as <var>node</var>, and <var>node</var> is not
+  an <span>HTML element</span> with [[localname]] "address", "div", "h1", "h2",
+  "h3", "h4", "h5", "h6", "p", or "pre", set <var>node</var> to its [[parent]].
+  <!--
+  Opera 11.11 doesn't require it be editable, so it will return "DIV" instead of
+  "" for <div contenteditable>foo</div>.
+
+  Chrome 14 dev will report "div" for <div><ol><li>foo</ol></div> or such.
+  Opera 11.11 reports "".  IE and Firefox didn't cooperate with testing.  Opera
+  makes somewhat more sense, but formatBlock is actually broken in that case
+  right now anyway for any wrapper other than div, so I'll punt on a solution.
+  -->
+  <p class=XXX>This doesn't really make sense if there's an intervening table
+  or something.
+
+  <li>If <var>node</var> is an <span>editable</span> <span>HTML element</span>
+  with [[localname]] "address", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p",
+  or "pre", return its [[localname]], <span data-anolis-spec=domcore>converted
+  to lowercase</span>.
+  <!-- Actually, we don't really need to specify "editable", since it has to be
+  editable. -->
+
+  <li>Return the empty string.
+</ol>
 <!-- @} -->
 
 <h3><dfn>The <code title>forwardDelete</code> command</dfn></h3>
--- a/tests.js	Wed Jun 29 12:42:49 2011 -0600
+++ b/tests.js	Wed Jun 29 13:14:53 2011 -0600
@@ -1015,9 +1015,17 @@
 		['<h1>', '<pre>[foo<br>bar]</pre>'],
 
 		['<h1>', '<p>[foo</p>bar]'],
+		['<h1>', '[foo<p>bar]</p>'],
 		['<p>', '<div>[foo<p>bar]</p></div>'],
 		['<p>', '<xmp>[foo]</xmp>'],
 		['<div>', '<xmp>[foo]</xmp>'],
+
+		// For queryCommandIndeterm() and queryCommandValue()
+		'<div><ol><li>[foo]</ol></div>',
+		'<div><table><tr><td>[foo]</table></div>',
+		'<p>[foo<h1>bar]</h1>',
+		'<h1>[foo</h1><h2>bar]</h2>',
+		'<div>[foo</div>bar]',
 	],
 	//@}
 	forwarddelete: [