Lots of refinement and bug-fixes
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Mon, 21 Feb 2011 14:25:51 -0700
changeset 5 256b8ba2af6a
parent 4 fba0a3d0e323
child 6 2f1ebe466678
Lots of refinement and bug-fixes

The styling algorithm now appears to be basically usable.
editcommands.html
preprocess
source.html
test/bold.html
xrefs.json
--- a/editcommands.html	Sun Feb 20 15:11:49 2011 -0700
+++ b/editcommands.html	Mon Feb 21 14:25:51 2011 -0700
@@ -19,7 +19,7 @@
 <body class=draft>
 <div class=head id=head>
 <h1>HTML Editing Commands</h1>
-<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-20-february-2011>Work in Progress &mdash; Last Update 20 February 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-21-february-2011>Work in Progress &mdash; Last Update 21 February 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg[email protected]&gt;
@@ -78,18 +78,29 @@
 
 <h2 id=definitions><span class=secno>3 </span>Definitions</h2>
 
-<p class=XXX>This definition is just an ugly workaround for the annoyance of
-xrefs.  Fix it sometime.
+<p>A <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> is an <dfn id=html-element>HTML element</dfn> if it is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> whose
+<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-namespace title=concept-element-namespace>namespace</a> is the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#html-namespace>HTML namespace</a>.
 
-<p>A <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> is an <dfn id=html-element-with-name>HTML element
-with name</dfn> <var title="">name</var> if it is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>, its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-namespace title=concept-element-namespace>namespace</a> is the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#html-namespace>HTML namespace</a>, and 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> is
-<var title="">name</var>.
+<p>The <dfn id=first-node>first node</dfn> of a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> returned by the
+following algorithm:
 
-<p>The <dfn id=first-node>first node</dfn> of a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>,
-if that is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a>, <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a>, or <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#processinginstruction><code class=external data-anolis-spec=domcore>ProcessingInstruction</code></a> node; or else
-the child of its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to its
-<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a>, if that exists; or else its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
-<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>.
+<ol>
+  <li><p>Let <var title="">range</var> be the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> under discussion.
+
+  <li><p>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is equal to
+  the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, return the first
+  <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> that is after the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> and all its
+  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendants</a> (if any) in <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>.  If there is no such <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>,
+  return the last <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in the document.
+
+  <li><p>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a>,
+  <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a>, or <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#processinginstruction><code class=external data-anolis-spec=domcore>ProcessingInstruction</code></a> node, return that.
+
+  <li><p>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> has children,
+  return the child with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a>.
+
+  <li><p>Return <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>.
+</ol>
 
 <p>The <dfn id=beginning-element>beginning element</dfn> of a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is its <a href=#first-node>first
 node</a> if that is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>; or the parent of its <a href=#first-node>first
@@ -119,27 +130,42 @@
 
   <p class=XXX>Figure out something sensible here.
 
+  <li><p>If <var title="">start node</var> and <var title="">end node</var> are both
+  <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> nodes, and <var title="">start node</var> is the same as <var title="">end
+  node</var>, and neither <var title="">start offset</var> nor <var title="">end
+  offset</var> is equal to 0 or the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">start
+  node</var>:
+
+  <ol>
+    <li><p>Run <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-text-splittext><code class=external data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var title="">start offset</var>)</code></a> on
+    <var title="">start node</var> and set <var title="">start node</var> to the
+    result.
+
+    <li><p>Run <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-text-splittext><code class=external data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var title="">end offset</var> &minus; <var title="">start offset</var>)</code></a> on <var title="">start node</var> and set
+    <var title="">start node</var> to the previous sibling of the result.
+
+    <li><p>Return the list consisting of the single <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> <var title="">start
+    node</var>, and abort these steps.
+  </ol>
+
   <li><p>If <var title="">start node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node and <var title="">start
-  offset</var> is neither 0 nor the <a href=http://html5.org/specs/dom-range.html#concept-node-length><code class=external data-anolis-spec=domrange title=concept-node-length>length</code></a> of <var title="">start
+  offset</var> is neither 0 nor the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">start
   node</var>, run <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-text-splittext><code class=external data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var title="">start offset</var>)</code></a> on
   <var title="">start node</var> and set <var title="">start node</var> to the
   returned node.  Set <var title="">start offset</var> to 0.
 
   <li><p>If <var title="">end node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node and <var title="">end
-  offset</var> is neither 0 nor the <a href=http://html5.org/specs/dom-range.html#concept-node-length><code class=external data-anolis-spec=domrange title=concept-node-length>length</code></a> of <var title="">end
+  offset</var> is neither 0 nor the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">end
   node</var>, run <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-text-splittext><code class=external data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var title="">end offset</var>)</code></a> on
   <var title="">end node</var> and set <var title="">end node</var> to the previous
   sibling of the returned node.  Set <var title="">end offset</var> to the
-  <a href=http://html5.org/specs/dom-range.html#concept-node-length><code class=external data-anolis-spec=domrange title=concept-node-length>length</code></a> of the new <var title="">end node</var>.
-
-  <p class=XXX>What if the start and end nodes are the same <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node?
-  Handle explicitly.
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of the new <var title="">end node</var>.
 
   <li><p>If <var title="">start node</var> is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> with at least one
   child, let <var title="">node</var> be the child of <var title="">start node</var>
   with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">start offset</var>.
 
-  <li><p>Otherwise, if <var title="">start node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node and <var title="">start offset</var> is its <a href=http://html5.org/specs/dom-range.html#concept-node-length><code class=external data-anolis-spec=domrange title=concept-node-length>length</code></a>, let <var title="">node</var> be
+  <li><p>Otherwise, if <var title="">start node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node and <var title="">start offset</var> is its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>, let <var title="">node</var> be
   the first <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> after <var title="">start node</var> in <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>.
 
   <li><p>Otherwise, let <var title="">node</var> be <var title="">start node</var>.
@@ -152,17 +178,30 @@
 
   <li><p>Otherwise, let <var title="">end</var> be <var title="">end node</var>.
 
+  <!-- We try to include a node's parent instead of that node if possible,
+  because this generally reduces the number of nodes we're handling.  So if the
+  string "oo bar" was selected in <b>Foo <i>bar</i></b>, we'd add the <i> to
+  the selection, even if the browser registered the end as the text node "bar".
+  -->
+  <li><p>While <var title="">node</var> is the first child of its parent and <var title="">end</var> is not a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendant</a> of <var title="">node</var>'s parent,
+  set <var title="">node</var> to its parent.
+
+  <li><p>While <var title="">end</var> is the last child of its parent and <var title="">node</var> is not a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendant</a> of <var title="">end</var>'s parent,
+  set <var title="">end</var> to its parent.
+
   <li><p>Let <var title="">node list</var> be an empty list of <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s.
 
   <li><p>While <var title="">node</var> is not after <var title="">end</var> in
   <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>:
 
   <ol>
-    <li>Append <var title="">node</var> to <var title="">node list</var>.
+    <li><p>Append <var title="">node</var> to <var title="">node list</var>.
 
-    <li>Set <var title="">node</var> to the first <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in
-    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a> that is after <var title="">node</var> and (if applicable) all
-    its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendants</a>.  If no such <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> exists, break out of these substeps.
+    <li><p>Set <var title="">node</var> to the first <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>
+    that is after <var title="">node</var> and (if applicable) all its
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendants</a>.  If no such <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> exists, break out of these substeps.
+
+    <li><p>While <var title="">node</var> is an <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-ancestor-node title=concept-ancestor-node>ancestor</a> of <var title="">end</var>, set <var title="">node</var> to its first child.
   </ol>
 
   <li><p>Return <var title="">node list</var>.
@@ -171,7 +210,9 @@
 
 <h2 id=unstyling-an-element><span class=secno>5 </span>Unstyling an element</h2>
 <p>When a user agent is to <dfn id=unstyle-an-element>unstyle an element</dfn>, it must run the
-following steps.
+following steps.  This algorithm might remove the element from the DOM and
+insert other elements in its place, in which case it will return an ordered
+list of the element's former children.
 
 <ol>
   <li><p>Let <var title="">element</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> to be unstyled.
@@ -179,48 +220,77 @@
   <li><p>Let <var title="">property name</var> and <var title="">tag list</var> be as
   in the invoking algorithm.
 
+  <li><p>Let <var title="">element children</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> children of
+  <var title="">element</var>.
+
+  <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> in
+  <var title="">element children</var>, in order.
+
+  <p class=note>Unstyling an element can change the number of children its
+  parent has, so the list of children to unstyle needs to be computed
+  beforehand.
+
+  <li><p>Let <var title="">children</var> be an empty list of <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s.
+
   <li><p>If either
 
   <ul>
-    <li><p><var title="">element</var> is an <a href=#html-element-with-name>HTML element with
-    name</a> either "span" or in <var title="">tag list</var>, and it has
+    <li><p><var title="">element</var> is 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> either "span" or in <var title="">tag list</var>, and it has
     only a single attribute, and that attribute is named "style", and
     that style attribute sets only the CSS property <var title="">property
     name</var>; or
 
-    <li><p><var title="">element</var> is an <a href=#html-element-with-name>HTML element with
-    name</a> in <var title="">tag list</var> and it has no attributes,
+    <li><p><var title="">element</var> is 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> in <var title="">tag list</var> and it has no attributes,
   </ul>
 
   <p>then:
 
   <ol>
-    <li><p>While <var title="">element</var> has children, insert the first child
-    of <var title="">element</var> as the previous sibling of <var title="">element</var>.
+    <li><p>While <var title="">element</var> has children:
+
+    <ol>
+      <li><p>Let <var title="">child</var> be the first child of <var title="">element</var>.
+
+      <li><p>Append <var title="">child</var> to <var title="">children</var>.
+
+      <li><p>Insert <var title="">child</var> as the previous sibling of <var title="">element</var>.
+    </ol>
 
     <li><p>Remove <var title="">element</var>.
 
-    <li><p>Abort this algorithm.
+    <li><p>Return <var title="">children</var> and abort this algorithm.
   </ol>
 
   <li><p>Unset the CSS property <var title="">property name</var> of <var title="">element</var>.
 
-  <li><p>If <var title="">element</var> is an <a href=#html-element-with-name>HTML element with
-  name</a> in <var title="">tag list</var>:
+  <li><p>If <var title="">element</var> is 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> in <var title="">tag list</var>:
 
   <ol>
-    <li><p>Let <var title="">new element</var> be a new <a href=#html-element-with-name>HTML element with
-    name</a> "span", with the same attributes and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a>
+    <li><p>Let <var title="">new element</var> be a new <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> "span", with the same attributes and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a>
     as <var title="">element</var>.
 
     <li><p>Append <var title="">new element</var> to <var title="">element</var>'s
     parent as the previous sibling of <var title="">element</var>.
 
-    <li><p>While <var title="">element</var> has children, append the first child
-    of <var title="">element</var> as the last child of <var title="">new element</var>.
+    <li><p>While <var title="">element</var> has children:
+
+    <ol>
+      <li><p>Let <var title="">child</var> be the first child of <var title="">element</var>.
+
+      <li><p>Append <var title="">child</var> to <var title="">children</var>.
+
+      <li><p>Append <var title="">child</var> as the last child of <var title="">new
+      element</var>.
+    </ol>
 
     <li><p>Remove <var title="">element</var>.
   </ol>
+
+  <li><p>Return <var title="">children</var>.
 </ol>
 
 
@@ -259,23 +329,39 @@
     <li><p>If <var title="">node</var> is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>:
 
     <ol>
-      <li><p>If <var title="">node</var> is an <a href=#html-element-with-name>HTML element with name</a>
+      <li><p>If <var title="">node</var> is 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>
       in <var title="">tag list</var>, unset the CSS property <var title="">property
       name</var> of <var title="">node</var>.  Otherwise, set the CSS property
       <var title="">property name</var> of <var title="">node</var> to <var title="">property value</var>.
 
-      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendant</a> of <var title="">node</var>.
+      <li><p>Let <var title="">element children</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> children
+      of <var title="">node</var>.
+
+      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>
+      in <var title="">element children</var>, in order.
+
+      <p class=note>Unstyling an element can change the number of children its
+      parent has, so the list of children to unstyle needs to be computed
+      beforehand.
     </ol>
 
     <li><p>Otherwise, if <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node:
 
     <ol>
-      <li><p>Let <var title="">new parent</var> be a new <a href=#html-element-with-name>HTML element with
-      name</a> equal to the first string in <var title="">tag list</var>, with
-      no attributes, and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as <var title="">node</var>.
+      <!-- This next step is not strictly necessary from a user point of view,
+      since there's no visible difference.  We could just always insert a new
+      element.  But that can drastically complicate the DOM if there are lots
+      of text nodes, which happens easily when the algorithms here are invoked
+      a lot: you get splitText() called when decomposing ranges, and unstyling
+      elements can also dump their text node children into the DOM next to
+      other text nodes. -->
+      <li><p>If the previous sibling of <var title="">node</var> is 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> in <var title="">tag list</var> with no
+      attributes, let <var title="">new parent</var> equal the previous sibling of
+      <var title="">node</var>.
 
-      <li><p>Append <var title="">new parent</var> to <var title="">node</var>'s
-      parent as the previous sibling of <var title="">node</var>.
+      <li><p>Otherwise, let <var title="">new parent</var> be a new <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> equal to the first string in <var title="">tag list</var>, with no attributes, and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as <var title="">node</var>.  Append <var title="">new parent</var> to <var title="">node</var>'s parent as the previous sibling of <var title="">node</var>.
 
       <li><p>Append <var title="">node</var> to <var title="">new parent</var> as
       its last child.
@@ -310,19 +396,39 @@
     <li><p>If <var title="">node</var> is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>:
 
     <ol>
-      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> <var title="">node</var>.
+      <li><p>Let <var title="">children</var> be the result of <a href=#unstyle-an-element title="unstyle an element">unstyling</a> <var title="">node</var>.
+
+      <li><p>If <var title="">node</var> no longer has a parent:
+
+      <ol>
+        <li><p>Insert all the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s in <var title="">children</var> into <var title="">node list</var> immediately after <var title="">node</var>, in
+        order.
+
+        <li><p>Continue with the next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in <var title="">node list</var>,
+        if any.
+
+        <p class=note>The next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> will be the first former child of <var title="">node</var>, if <var title="">node</var> had children.
+      </ol>
 
       <li><p>If the computed value of <var title="">property name</var> for <var title="">node</var> is not <var title="">property value</var>, set the CSS
       property <var title="">property name</var> of <var title="">node</var> to <var title="">property value</var>.
 
-      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-descendant-node title=concept-descendant-node>descendant</a> of <var title="">node</var>.
+      <li><p>Let <var title="">element children</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> children
+      of <var title="">node</var>.
+
+      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>
+      in <var title="">element children</var>, in order.
+
+      <p class=note>Unstyling an element can change the number of children its
+      parent has, so the list of children to unstyle needs to be computed
+      beforehand.
     </ol>
 
     <li><p>Otherwise, if <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node and the computed value of <var title="">property name</var> for <var title="">node</var>'s parent is not <var title="">property value</var>:
 
     <ol>
-      <li><p>Let <var title="">new parent</var> be a new <a href=#html-element-with-name>HTML element with
-      name</a> "span", with no attributes, and with <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> equal to <var title="">node</var>'s.
+      <li><p>Let <var title="">new parent</var> be a new <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> "span", with no attributes, and with <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> equal to <var title="">node</var>'s.
 
       <li><p>Set the CSS property <var title="">property name</var> of <var title="">new parent</var> to <var title="">property value</var>.
 
@@ -363,13 +469,17 @@
 
 <dl>
 <dt><code title=""><dfn id=command-bold title=command-bold>bold</dfn></code>
-<dd>If the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-weight with computed value not
-equal to "bold", the user agent must <a href=#style-a-range title="style a range">style the
-<code class=external data-anolis-spec=domrange>Range</code></a> with <var title="">property
+<dd><p>If the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-weight
+with computed value not equal to "bold" (or less than 700), the user agent must
+<a href=#style-a-range title="style a range">style the <code class=external data-anolis-spec=domrange>Range</code></a> with <var title="">property
 name</var> "font-weight", <var title="">property value</var> "bold", and <var title="">tag list</var> ["b", "strong"].  Otherwise, it must <a href=#unstyle-a-range title="unstyle
 a range">unstyle it</a> with <var title="">property name</var> "font-weight",
 <var title="">property value</var> "normal", and <var title="">tag list</var> ["b",
 "strong"].
+
+<p class=XXX>b has font-weight: bolder, not font-weight: bold.  This produces
+unexpected behavior if there are font-weight: lighters or something thrown
+around.  Maybe that's not worth worrying about.
 </dl>
 
 
--- a/preprocess	Sun Feb 20 15:11:49 2011 -0700
+++ b/preprocess	Mon Feb 21 14:25:51 2011 -0700
@@ -4,6 +4,8 @@
 # <span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>.
 
 replace = {
+    "ancestor": "<span data-anolis-spec=domcore title=concept-ancestor-node>ancestor</span>",
+    "ancestors": "<span data-anolis-spec=domcore title=concept-ancestor-node>ancestors</span>",
     "bpnode": "<span data-anolis-spec=domrange title=concept-boundary-point-node>node</span>",
     "bpnodes": "<span data-anolis-spec=domrange title=concept-boundary-point-node>nodes</span>",
     "bpoffset": "<span data-anolis-spec=domrange title=concept-boundary-point-offset>offset</span>",
@@ -12,10 +14,12 @@
     "descendant": "<span data-anolis-spec=domcore title=concept-descendant-node>descendant</span>",
     "descendants": "<span data-anolis-spec=domcore title=concept-descendant-node>descendants</span>",
     "element": "<code data-anolis-spec=domcore>Element</code>",
+    "htmlnamespace": "<span data-anolis-spec=domcore>HTML namespace</span>",
     "index": "<span data-anolis-spec=domrange title=concept-indexof>index</span>",
+    "localname": "<span data-anolis-spec=domcore title=concept-element-local-name>local name</span>",
     "namespace": "<span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>",
     "node": "<code data-anolis-spec=domcore>Node</code>",
-    "nodelength": "<code data-anolis-spec=domrange title=concept-node-length>length</code>",
+    "nodelength": "<span data-anolis-spec=domrange title=concept-node-length>length</span>",
     "processinginstruction": "<code data-anolis-spec=domcore>ProcessingInstruction</code>",
     "range": "<code data-anolis-spec=domrange>Range</code>",
     "rangeend": "<span data-anolis-spec=domrange title=concept-range-end>end</span>",
--- a/source.html	Sun Feb 20 15:11:49 2011 -0700
+++ b/source.html	Mon Feb 21 14:25:51 2011 -0700
@@ -66,22 +66,29 @@
 
 <h2>Definitions</h2>
 
-<p class=XXX>This definition is just an ugly workaround for the annoyance of
-xrefs.  Fix it sometime.
+<p>A [[node]] is an <dfn>HTML element</dfn> if it is an [[element]] whose
+[[namespace]] is the <span data-anolis-spec=domcore>HTML namespace</span>.
 
-<p>A <code data-anolis-spec=domcore>Node</code> is an <dfn>HTML element
-with name</dfn> <var title>name</var> if it is an <code
-data-anolis-spec=domcore>Element</code>, its <span data-anolis-spec=domcore
-title=concept-element-namespace>namespace</span> is the <span
-data-anolis-spec=domcore>HTML namespace</span>, and its <span
-data-anolis-spec=domcore title=concept-element-local-name>local name</span> is
-<var title>name</var>.
+<p>The <dfn>first node</dfn> of a [[range]] is the [[node]] returned by the
+following algorithm:
 
-<p>The <dfn>first node</dfn> of a [[range]] is its [[rangestart]] [[bpnode]],
-if that is a [[text]], [[comment]], or [[processinginstruction]] node; or else
-the child of its [[rangestart]] [[bpnode]] with [[index]] equal to its
-[[rangestart]] [[bpoffset]], if that exists; or else its [[rangestart]]
-[[bpnode]].
+<ol>
+  <li><p>Let <var title>range</var> be the [[range]] under discussion.
+
+  <li><p>If <var title>range</var>'s [[rangestart]] [[bpoffset]] is equal to
+  the [[nodelength]] of its [[rangestart]] [[bpnode]], return the first
+  [[node]] that is after the [[rangestart]] [[bpnode]] and all its
+  [[descendants]] (if any) in [[treeorder]].  If there is no such [[node]],
+  return the last [[node]] in the document.
+
+  <li><p>If <var title>range</var>'s [[rangestart]] [[bpnode]] is a [[text]],
+  [[comment]], or [[processinginstruction]] node, return that.
+
+  <li><p>If <var title>range</var>'s [[rangestart]] [[bpnode]] has children,
+  return the child with [[index]] equal to the [[rangestart]] [[bpoffset]].
+
+  <li><p>Return <var title>range</var>'s [[rangestart]] [[bpnode]].
+</ol>
 
 <p>The <dfn>beginning element</dfn> of a [[range]] is its <span>first
 node</span> if that is an [[element]]; or the parent of its <span>first
@@ -113,6 +120,27 @@
 
   <p class=XXX>Figure out something sensible here.
 
+  <li><p>If <var title>start node</var> and <var title>end node</var> are both
+  [[text]] nodes, and <var title>start node</var> is the same as <var title>end
+  node</var>, and neither <var title>start offset</var> nor <var title>end
+  offset</var> is equal to 0 or the [[nodelength]] of <var title>start
+  node</var>:
+
+  <ol>
+    <li><p>Run <code data-anolis-spec=domcore
+    title=dom-Text-splitText>splitText(<var title>start offset</var>)</code> on
+    <var title>start node</var> and set <var title>start node</var> to the
+    result.
+
+    <li><p>Run <code data-anolis-spec=domcore
+    title=dom-Text-splitText>splitText(<var title>end offset</var> &minus; <var
+    title>start offset</var>)</code> on <var title>start node</var> and set
+    <var title>start node</var> to the previous sibling of the result.
+
+    <li><p>Return the list consisting of the single [[node]] <var title>start
+    node</var>, and abort these steps.
+  </ol>
+
   <li><p>If <var title>start node</var> is a [[text]] node and <var title>start
   offset</var> is neither 0 nor the [[nodelength]] of <var title>start
   node</var>, run <code data-anolis-spec=domcore
@@ -128,9 +156,6 @@
   sibling of the returned node.  Set <var title>end offset</var> to the
   [[nodelength]] of the new <var title>end node</var>.
 
-  <p class=XXX>What if the start and end nodes are the same [[text]] node?
-  Handle explicitly.
-
   <li><p>If <var title>start node</var> is an [[element]] with at least one
   child, let <var title>node</var> be the child of <var title>start node</var>
   with [[index]] <var title>start offset</var>.
@@ -151,17 +176,33 @@
 
   <li><p>Otherwise, let <var title>end</var> be <var title>end node</var>.
 
+  <!-- We try to include a node's parent instead of that node if possible,
+  because this generally reduces the number of nodes we're handling.  So if the
+  string "oo bar" was selected in <b>Foo <i>bar</i></b>, we'd add the <i> to
+  the selection, even if the browser registered the end as the text node "bar".
+  -->
+  <li><p>While <var title>node</var> is the first child of its parent and <var
+  title>end</var> is not a [[descendant]] of <var title>node</var>'s parent,
+  set <var title>node</var> to its parent.
+
+  <li><p>While <var title>end</var> is the last child of its parent and <var
+  title>node</var> is not a [[descendant]] of <var title>end</var>'s parent,
+  set <var title>end</var> to its parent.
+
   <li><p>Let <var title>node list</var> be an empty list of [[node]]s.
 
   <li><p>While <var title>node</var> is not after <var title>end</var> in
   [[treeorder]]:
 
   <ol>
-    <li>Append <var title>node</var> to <var title>node list</var>.
+    <li><p>Append <var title>node</var> to <var title>node list</var>.
 
-    <li>Set <var title>node</var> to the first [[node]] in
-    [[treeorder]] that is after <var title>node</var> and (if applicable) all
-    its [[descendants]].  If no such [[node]] exists, break out of these substeps.
+    <li><p>Set <var title>node</var> to the first [[node]] in [[treeorder]]
+    that is after <var title>node</var> and (if applicable) all its
+    [[descendants]].  If no such [[node]] exists, break out of these substeps.
+
+    <li><p>While <var title>node</var> is an [[ancestor]] of <var
+    title>end</var>, set <var title>node</var> to its first child.
   </ol>
 
   <li><p>Return <var title>node list</var>.
@@ -170,7 +211,9 @@
 
 <h2>Unstyling an element</h2>
 <p>When a user agent is to <dfn>unstyle an element</dfn>, it must run the
-following steps.
+following steps.  This algorithm might remove the element from the DOM and
+insert other elements in its place, in which case it will return an ordered
+list of the element's former children.
 
 <ol>
   <li><p>Let <var title>element</var> be the <code
@@ -179,51 +222,82 @@
   <li><p>Let <var title>property name</var> and <var title>tag list</var> be as
   in the invoking algorithm.
 
+  <li><p>Let <var title>element children</var> be the [[element]] children of
+  <var title>element</var>.
+
+  <li><p><span title="unstyle an element">Unstyle</span> each [[element]] in
+  <var title>element children</var>, in order.
+
+  <p class=note>Unstyling an element can change the number of children its
+  parent has, so the list of children to unstyle needs to be computed
+  beforehand.
+
+  <li><p>Let <var title>children</var> be an empty list of [[node]]s.
+
   <li><p>If either
 
   <ul>
-    <li><p><var title>element</var> is an <span>HTML element with
-    name</span> either "span" or in <var title>tag list</var>, and it has
+    <li><p><var title>element</var> is an <span>HTML element</span> with
+    [[localname]] either "span" or in <var title>tag list</var>, and it has
     only a single attribute, and that attribute is named "style", and
     that style attribute sets only the CSS property <var title>property
     name</var>; or
 
-    <li><p><var title>element</var> is an <span>HTML element with
-    name</span> in <var title>tag list</var> and it has no attributes,
+    <li><p><var title>element</var> is an <span>HTML element</span> with
+    [[localname]] in <var title>tag list</var> and it has no attributes,
   </ul>
 
   <p>then:
 
   <ol>
-    <li><p>While <var title>element</var> has children, insert the first child
-    of <var title>element</var> as the previous sibling of <var
-    title>element</var>.
+    <li><p>While <var title>element</var> has children:
+
+    <ol>
+      <li><p>Let <var title>child</var> be the first child of <var
+      title>element</var>.
+
+      <li><p>Append <var title>child</var> to <var title>children</var>.
+
+      <li><p>Insert <var title>child</var> as the previous sibling of <var
+      title>element</var>.
+    </ol>
 
     <li><p>Remove <var title>element</var>.
 
-    <li><p>Abort this algorithm.
+    <li><p>Return <var title>children</var> and abort this algorithm.
   </ol>
 
   <li><p>Unset the CSS property <var title>property name</var> of <var
   title>element</var>.
 
-  <li><p>If <var title>element</var> is an <span>HTML element with
-  name</span> in <var title>tag list</var>:
+  <li><p>If <var title>element</var> is an <span>HTML element</span> with
+  [[localname]] in <var title>tag list</var>:
 
   <ol>
-    <li><p>Let <var title>new element</var> be a new <span>HTML element with
-    name</span> "span", with the same attributes and <code
+    <li><p>Let <var title>new element</var> be a new <span>HTML element</span> with
+    [[localname]] "span", with the same attributes and <code
     data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code>
     as <var title>element</var>.
 
     <li><p>Append <var title>new element</var> to <var title>element</var>'s
     parent as the previous sibling of <var title>element</var>.
 
-    <li><p>While <var title>element</var> has children, append the first child
-    of <var title>element</var> as the last child of <var title>new element</var>.
+    <li><p>While <var title>element</var> has children:
+
+    <ol>
+      <li><p>Let <var title>child</var> be the first child of <var
+      title>element</var>.
+
+      <li><p>Append <var title>child</var> to <var title>children</var>.
+
+      <li><p>Append <var title>child</var> as the last child of <var title>new
+      element</var>.
+    </ol>
 
     <li><p>Remove <var title>element</var>.
   </ol>
+
+  <li><p>Return <var title>children</var>.
 </ol>
 
 
@@ -268,30 +342,48 @@
     data-anolis-spec=domcore>Element</code>:
 
     <ol>
-      <li><p>If <var title>node</var> is an <span>HTML element with name</span>
+      <li><p>If <var title>node</var> is an <span>HTML element</span> with [[localname]]
       in <var title>tag list</var>, unset the CSS property <var title>property
       name</var> of <var title>node</var>.  Otherwise, set the CSS property
       <var title>property name</var> of <var title>node</var> to <var
       title>property value</var>.
 
-      <li><p><span title="unstyle an element">Unstyle</span> each <code
-      data-anolis-spec=domcore>Element</code> <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendant</span> of <var title>node</var>.
+      <li><p>Let <var title>element children</var> be the [[element]] children
+      of <var title>node</var>.
+
+      <li><p><span title="unstyle an element">Unstyle</span> each [[element]]
+      in <var title>element children</var>, in order.
+
+      <p class=note>Unstyling an element can change the number of children its
+      parent has, so the list of children to unstyle needs to be computed
+      beforehand.
     </ol>
 
     <li><p>Otherwise, if <var title>node</var> is a <code
     data-anolis-spec=domcore>Text</code> node:
 
     <ol>
-      <li><p>Let <var title>new parent</var> be a new <span>HTML element with
-      name</span> equal to the first string in <var title>tag list</var>, with
-      no attributes, and <code data-anolis-spec=domcore
+      <!-- This next step is not strictly necessary from a user point of view,
+      since there's no visible difference.  We could just always insert a new
+      element.  But that can drastically complicate the DOM if there are lots
+      of text nodes, which happens easily when the algorithms here are invoked
+      a lot: you get splitText() called when decomposing ranges, and unstyling
+      elements can also dump their text node children into the DOM next to
+      other text nodes. -->
+      <li><p>If the previous sibling of <var title>node</var> is an <span>HTML
+      element</span> with [[localname]] in <var title>tag list</var> with no
+      attributes, let <var title>new parent</var> equal the previous sibling of
+      <var title>node</var>.
+
+      <li><p>Otherwise, let <var title>new parent</var> be a new <span>HTML
+      element</span> with [[localname]] equal to the first string in <var
+      title>tag list</var>, with no attributes, and <code
+      data-anolis-spec=domcore
       title=dom-Node-ownerDocument>ownerDocument</code> the same as <var
+      title>node</var>.  Append <var title>new parent</var> to <var
+      title>node</var>'s parent as the previous sibling of <var
       title>node</var>.
 
-      <li><p>Append <var title>new parent</var> to <var title>node</var>'s
-      parent as the previous sibling of <var title>node</var>.
-
       <li><p>Append <var title>node</var> to <var title>new parent</var> as
       its last child.
     </ol>
@@ -328,17 +420,37 @@
     data-anolis-spec=domcore>Element</code>:
 
     <ol>
-      <li><p><span title="unstyle an element">Unstyle</span> <var
-      title>node</var>.
+      <li><p>Let <var title>children</var> be the result of <span
+      title="unstyle an element">unstyling</span> <var title>node</var>.
+
+      <li><p>If <var title>node</var> no longer has a parent:
+
+      <ol>
+        <li><p>Insert all the [[node]]s in <var title>children</var> into <var
+        title>node list</var> immediately after <var title>node</var>, in
+        order.
+
+        <li><p>Continue with the next [[node]] in <var title>node list</var>,
+        if any.
+
+        <p class=note>The next [[node]] will be the first former child of <var
+        title>node</var>, if <var title>node</var> had children.
+      </ol>
 
       <li><p>If the computed value of <var title>property name</var> for <var
       title>node</var> is not <var title>property value</var>, set the CSS
       property <var title>property name</var> of <var title>node</var> to <var
       title>property value</var>.
 
-      <li><p><span title="unstyle an element">Unstyle</span> each <code
-      data-anolis-spec=domcore>Element</code> <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendant</span> of <var title>node</var>.
+      <li><p>Let <var title>element children</var> be the [[element]] children
+      of <var title>node</var>.
+
+      <li><p><span title="unstyle an element">Unstyle</span> each [[element]]
+      in <var title>element children</var>, in order.
+
+      <p class=note>Unstyling an element can change the number of children its
+      parent has, so the list of children to unstyle needs to be computed
+      beforehand.
     </ol>
 
     <li><p>Otherwise, if <var title>node</var> is a <code
@@ -347,8 +459,8 @@
     title>property value</var>:
 
     <ol>
-      <li><p>Let <var title>new parent</var> be a new <span>HTML element with
-      name</span> "span", with no attributes, and with <code
+      <li><p>Let <var title>new parent</var> be a new <span>HTML element</span> with
+      [[localname]] "span", with no attributes, and with <code
       data-anolis-spec=domcore
       title=dom-Node-ownerDocument>ownerDocument</code> equal to <var
       title>node</var>'s.
@@ -396,15 +508,18 @@
 
 <dl>
 <dt><code title><dfn title=command-bold>bold</dfn></code>
-<dd>If the <span>beginning element</span> of the <code
-data-anolis-spec=domrange>Range</code> has font-weight with computed value not
-equal to "bold", the user agent must <span title="style a range">style the
-<code data-anolis-spec=domrange>Range</code></span> with <var title>property
+<dd><p>If the <span>beginning element</span> of the [[range]] has font-weight
+with computed value not equal to "bold" (or less than 700), the user agent must
+<span title="style a range">style the [[range]]</span> with <var title>property
 name</var> "font-weight", <var title>property value</var> "bold", and <var
 title>tag list</var> ["b", "strong"].  Otherwise, it must <span title="unstyle
 a range">unstyle it</span> with <var title>property name</var> "font-weight",
 <var title>property value</var> "normal", and <var title>tag list</var> ["b",
 "strong"].
+
+<p class=XXX>b has font-weight: bolder, not font-weight: bold.  This produces
+unexpected behavior if there are font-weight: lighters or something thrown
+around.  Maybe that's not worth worrying about.
 </dl>
 
 
--- a/test/bold.html	Sun Feb 20 15:11:49 2011 -0700
+++ b/test/bold.html	Mon Feb 21 14:25:51 2011 -0700
@@ -16,7 +16,7 @@
 	<span style=font-style:italic>Some more text</span><br>
 	<span style=font-style:italic;font-weight:bold>Some more text</span><br>
 	<em style=font-weight:bold>Some more text</em><br>
-	<b>Some <span style=font-weight:light>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b><br>
+	<b>Some <span style=font-weight:200>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b><br>
 	<p>Some simple text
 	<p><span>Some more text</span>
 	<p><b>Some more text</b>
@@ -30,7 +30,7 @@
 	<p><span style=font-style:italic>Some more text</span>
 	<p><span style=font-style:italic;font-weight:bold>Some more text</span>
 	<p><em style=font-weight:bold>Some more text</em>
-	<p><b>Some <span style=font-weight:light>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b>
+	<p><b>Some <span style=font-weight:200>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b>
 </div>
 <script>
 "use strict";
@@ -45,6 +45,16 @@
 	return ret;
 }
 
+function nodeLength(node) {
+	if (node.nodeType == Node.TEXT_NODE
+	|| node.nodeType == Node.COMMENT_NODE
+	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
+		return node.data.length;
+	}
+
+	return node.childNodes.length;
+}
+
 function nextNode(node) {
 	if (node.hasChildNodes()) {
 		return node.firstChild;
@@ -60,15 +70,16 @@
 		}
 		return node;
 	}
-	if (node.parentElement) {
-		return node.parentElement;
+	if (node.parentNode
+	&& node.parentNode.nodeType == Node.ELEMENT_NODE) {
+		return node.parentNode;
 	}
 	return null;
 }
 
 function nextNodeDescendants(node) {
 	while (node && !node.nextSibling) {
-		node = node.parentElement;
+		node = node.parentNode;
 	}
 	if (!node) {
 		return null;
@@ -83,16 +94,36 @@
 
 
 function firstNode(range) {
+	// "If range's start offset is equal to the length of its start node,
+	// return the first Node that is after the start node and all its
+	// descendants (if any) in tree order. If there is no such Node, return the
+	// last Node in the document."
+	if (range.startOffset == nodeLength(range.startContainer)) {
+		var ret = nextNodeDescendants(range.startContainer);
+		if (!ret) {
+			ret = range.startContainer;
+			while (ret.hasChildNodes()) {
+				ret = ret.childNodes[ret.childNodes.length - 1];
+			}
+		}
+		return ret;
+	}
+
+	// "If range's start node is a Text, Comment, or ProcessingInstruction
+	// node, return that."
 	if (range.startContainer.nodeType == Node.TEXT_NODE
 	|| range.startContainer.nodeType == Node.COMMENT_NODE
 	|| range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
 		return range.startContainer;
 	}
 
-	if (range.startContainer.childNodes.length > range.startOffset) {
+	// "If range's start node has children, return the child with index equal
+	// to the start offset."
+	if (range.startContainer.hasChildNodes()) {
 		return range.startContainer.childNodes[range.startOffset];
 	}
 
+	// "Return range's start node."
 	return range.startContainer;
 }
 
@@ -122,6 +153,29 @@
 	// parent, abort these steps."
 	// Skip the sanity check about node types/detached non-elements
 
+	// "If start node and end node are both Text nodes, and start node is the
+	// same as end node, and neither start offset nor end offset is equal to 0
+	// or the length of start node:"
+	if (startNode.nodeType == Node.TEXT_NODE
+	&& endNode.nodeType == Node.TEXT_NODE
+	&& startNode.isSameNode(endNode)
+	&& startOffset != 0
+	&& startOffset != startNode.data.length
+	&& endOffset != 0
+	&& endOffset != startNode.data.length) {
+		// "Run splitText(start offset) on start node and set start node to the
+		// result."
+		startNode = startNode.splitText(startOffset);
+
+		// "Run splitText(end offset − start offset) on start node and set
+		// start node to the previous sibling of the result."
+		startNode = startNode.splitText(endOffset - startOffset).previousSibling;
+
+		// "Return the list consisting of the single Node start node, and abort
+		// these steps."
+		return [startNode];
+	}
+
 	// "If start node is a Text node and start offset is neither 0 nor the
 	// length of start node, run splitText(start offset) on start node and set
 	// start node to the returned node. Set start offset to 0."
@@ -173,26 +227,62 @@
 		end = endNode;
 	}
 
+	// "While node is the first child of its parent and end is not a descendant
+	// of node's parent, set node to its parent."
+	while (node == node.parentNode.firstChild
+	&& !(end.compareDocumentPosition(node.parentNode) & Node.DOCUMENT_POSITION_CONTAINS)) {
+		node = node.parentNode;
+	}
+
+	// "While end is the last child of its parent and node is not a descendant
+	// of end's parent, set end to its parent."
+	while (end == end.parentNode.lastChild
+	&& !(node.compareDocumentPosition(end.parentNode) & Node.DOCUMENT_POSITION_CONTAINS)) {
+		end = end.parentNode;
+	}
+
 	// "Let node list be an empty list of Nodes."
 	var nodeList = [];
 
 	// "While node is not after end in tree order:"
-	while (node && !(end.compareDocumentPosition(node) & 4)) {
+	while (!(end.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING)) {
 		// "Append node to node list."
 		nodeList.push(node);
+
 		// "Set node to the first Node in tree order that is after node and (if
 		// applicable) all its descendants. If no such Node exists, break out
 		// of these substeps."
-		//
-		// If no such node exists, node will be set to null by this line and
-		// we'll break out due to the "node" part of the while condition.
 		node = nextNodeDescendants(node);
+		if (!node) {
+			break;
+		}
+
+		// "While node is an ancestor of end, set node to its first child."
+		while (end.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINS) {
+			node = node.firstChild;
+		}
 	}
 
 	return nodeList;
 }
 
 function unstyleElement(element, propertyName, tagList) {
+	// "Let element children be the Element children of element."
+	var elementChildren = [];
+	for (var j = 0; j < element.childNodes.length; j++) {
+		if (element.childNodes[j].nodeType == Node.ELEMENT_NODE) {
+			elementChildren.push(element.childNodes[j]);
+		}
+	}
+
+	// "Unstyle each Element in element children, in order."
+	for (var j = 0; j < elementChildren.length; j++) {
+		unstyleElement(elementChildren[j], propertyName, tagList);
+	}
+
+	// "Let children be an empty list of Nodes."
+	var children = [];
+
 	// "If either
 	//
 	// * element is an HTML element with name either "span" or in tag list, and
@@ -216,15 +306,23 @@
 		&& tagList.indexOf(element.nodeName.toLowerCase()) != -1
 		&& element.attributes.length == 0)
 	) {
-		// "While element has children, insert the first child of element as
-		// the previous sibling of element."
+		// "While element has children:"
 		while (element.hasChildNodes()) {
-			element.parentNode.insertBefore(element.childNodes[0], element);
+			// "Let child be the first child of element."
+			var child = element.firstChild;
+
+			// "Append child to children."
+			children.push(child);
+
+			// "Insert child as the previous sibling of element."
+			element.parentNode.insertBefore(child, element);
 		}
+
 		// "Remove element."
 		element.parentNode.removeChild(element);
-		// "Abort this algorithm."
-		return;
+
+		// "Return children and abort this algorithm."
+		return children;
 	}
 
 	// "Unset the CSS property property name of element."
@@ -248,24 +346,24 @@
 		// element."
 		element.parentNode.insertBefore(newElement, element);
 
-		// "While element has children, append the first child of element as
-		// the last child of new element."
+		// "While element has children:"
 		while (element.hasChildNodes()) {
-			newElement.appendChild(element.childNodes[0]);
+			// "Let child be the first child of element."
+			var child = element.firstChild;
+
+			// "Append child to children."
+			children.push(child);
+
+			// "Append child as the last child of new element."
+			newElement.appendChild(child);
 		}
+
 		// "Remove element."
 		element.parentNode.removeChild(element);
 	}
-}
 
-function unstyleElementDescendants(element, propertyName, tagList) {
-	for (var i = 0; i < element.childNodes.length; i++) {
-		if (element.childNodes[i].nodeType != Node.ELEMENT_NODE) {
-			continue;
-		}
-		unstyleElement(element.childNodes[i], propertyName, tagList);
-		unstyleElementDescendants(element.childNodes[i], propertyName, tagList);
-	}
+	// "Return children."
+	return children;
 }
 
 function styleRange(range, propertyName, propertyValue, tagList) {
@@ -290,18 +388,38 @@
 				node.style[propertyName] = propertyValue;
 			}
 
-			// "Unstyle each Element descendant of node."
-			unstyleElementDescendants(node, propertyName, tagList);
+			// "Let element children be the Element children of node."
+			var elementChildren = [];
+			for (var j = 0; j < node.childNodes.length; j++) {
+				if (node.childNodes[j].nodeType == Node.ELEMENT_NODE) {
+					elementChildren.push(node.childNodes[j]);
+				}
+			}
+
+			// "Unstyle each Element in element children, in order."
+			for (var j = 0; j < elementChildren.length; j++) {
+				unstyleElement(elementChildren[j], propertyName, tagList);
+			}
 		// "Otherwise, if node is a Text node:"
 		} else if (node.nodeType == Node.TEXT_NODE) {
-			// "Let new parent be a new HTML element with name equal to the first
-			// string in tag list, with no attributes, and ownerDocument the same
-			// as node."
-			var newParent = node.ownerDocument.createElement(tagList[0]);
-
-			// "Append new parent to node's parent as the previous sibling of
-			// node."
-			node.parentNode.insertBefore(newParent, node);
+			var newParent;
+			// "If the previous sibling of node is an HTML element with local
+			// name in tag list with no attributes, let new parent equal the
+			// previous sibling of node."
+			if (node.previousSibling
+			&& node.previousSibling.nodeType == Node.ELEMENT_NODE
+			&& node.previousSibling.namespaceURI == htmlNamespace
+			&& tagList.indexOf(node.previousSibling.tagName.toLowerCase()) != -1
+			&& node.previousSibling.attributes.length == 0) {
+				newParent = node.previousSibling;
+			} else {
+				// "Otherwise, let new parent be a new HTML element with local
+				// name equal to the first string in tag list, with no
+				// attributes, and ownerDocument the same as node. Append new
+				// parent to node's parent as the previous sibling of node."
+				newParent = node.ownerDocument.createElement(tagList[0]);
+				node.parentNode.insertBefore(newParent, node);
+			}
 
 			// "Append node to new parent as its last child."
 			newParent.appendChild(node);
@@ -310,37 +428,65 @@
 	}
 }
 
+// Note: because browsers are inconsistent about what to return for computed
+// styles for bold, I'm making propertyValue an array in the implementation.
 function unstyleRange(range, propertyName, propertyValue, tagList) {
 	// "Let node list be the result of decomposing range."
 	var nodeList = decomposeRange(range);
 
 	// "For each node in node list, in order:"
 	for (var i = 0; i < nodeList.length; i++) {
+		var node = nodeList[i];
+
 		// "If node is an Element:"
 		if (node.nodeType == Node.ELEMENT_NODE) {
-			// "Unstyle node."
-			unstyleElement(node, propertyName, tagList);
+			// "Let children be the result of unstyling node."
+			var children = unstyleElement(node, propertyName, tagList);
+
+			// "If node no longer has a parent:"
+			if (!node.parentNode) {
+				// "Insert all the Nodes in children into node list immediately
+				// after node, in order."
+				//
+				// splice() would be perfect, but it requires varargs.  :(
+				nodeList = nodeList.slice(0, i + 1)
+					.concat(children)
+					.concat(nodeList.slice(i + 1));
+
+				// "Continue with the next Node in node list, if any."
+				continue;
+			}
 
 			// "If the computed value of property name for node is not property
 			// value, set the CSS property property name of node to property
 			// value."
-			if (getComputedStyle(node)[propertyName] != propertyValue) {
-				node.style[propertyName] = propertyValue;
+			if (propertyValue.indexOf(getComputedStyle(node)[propertyName]) == -1) {
+				node.style[propertyName] = propertyValue[0];
 			}
 
-			// "Unstyle each Element descendant of node."
-			unstyleElementDescendants(node, propertyName, tagList);
+			// "Let element children be the Element children of node."
+			var elementChildren = [];
+			for (var j = 0; j < node.childNodes.length; j++) {
+				if (node.childNodes[j].nodeType == Node.ELEMENT_NODE) {
+					elementChildren.push(node.childNodes[j]);
+				}
+			}
+
+			// "Unstyle each Element in element children, in order."
+			for (var j = 0; j < elementChildren.length; j++) {
+				unstyleElement(elementChildren[j], propertyName, tagList);
+			}
 		// "Otherwise, if node is a Text node and the computed value of
 		// property name for node's parent is not property value:"
 		} else if (node.nodeType == Node.TEXT_NODE
-		&& getComputedStyle(node.parentNode)[propertyName] != propertyValue) {
+		&& propertyValue.indexOf(getComputedStyle(node.parentNode)[propertyName]) == -1) {
 			// "Let new parent be a new HTML element with name "span", with no
 			// attributes, and with ownerDocument equal to node's."
 			var newParent = node.ownerDocument.createElement("span");
 
 			// "Set the CSS property property name of new parent to property
 			// value."
-			newParent.style[propertyName] = propertyValue;
+			newParent.style[propertyName] = propertyValue[0];
 
 			// "Insert new parent as node's previous sibling."
 			node.parentNode.insertBefore(newParent, node);
@@ -355,10 +501,12 @@
 function bold() {
 	var selection = getSelection();
 	for (var i = 0; i < selection.rangeCount; i++) {
-		if (getComputedStyle(beginningElement(selection.getRangeAt(i))).fontWeight != "bold") {
+		var fontWeight = getComputedStyle(beginningElement(selection.getRangeAt(i))).fontWeight;
+		if (fontWeight != "bold"
+		&& (!/^[0-9]+$/.test(fontWeight) || fontWeight < 700)) {
 			styleRange(selection.getRangeAt(i), "fontWeight", "bold", ["b", "strong"]);
 		} else {
-			styleRange(selection.getRangeAt(i), "fontWeight", "normal", ["b", "strong"]);
+			unstyleRange(selection.getRangeAt(i), "fontWeight", ["normal", "400"], ["b", "strong"]);
 		}
 	}
 }
--- a/xrefs.json	Sun Feb 20 15:11:49 2011 -0700
+++ b/xrefs.json	Mon Feb 21 14:25:51 2011 -0700
@@ -4,7 +4,7 @@
   "decompose a range": "decompose-a-range",
   "execcommand()": "execcommand()",
   "first node": "first-node",
-  "html element with name": "html-element-with-name",
+  "html element": "html-element",
   "style a range": "style-a-range",
   "unstyle a range": "unstyle-a-range",
   "unstyle an element": "unstyle-an-element"