Largely rewrite again
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 20 Feb 2011 15:11:49 -0700
changeset 4 fba0a3d0e323
parent 3 d65c324ac6ae
child 5 256b8ba2af6a
Largely rewrite again

But I'm making progress. Now I have unstyling defined too. Also, I
started using an extra preprocessor because I got really sick of typing
incredibly long data-anolis-spec= stuff all the time.

There are still known bugs here. I'm just committing the day's work,
since things are still way too preliminary to try having each commit do
a single logical thing.
.gitignore
Makefile
editcommands.html
preprocess
source.html
test/bold.html
test/editcommands.html
xrefs.json
--- a/.gitignore	Thu Feb 17 15:06:13 2011 -0700
+++ b/.gitignore	Sun Feb 20 15:11:49 2011 -0700
@@ -1,2 +1,3 @@
 anolis/
 data/
+intermediate.html
--- a/Makefile	Thu Feb 17 15:06:13 2011 -0700
+++ b/Makefile	Sun Feb 20 15:11:49 2011 -0700
@@ -2,9 +2,12 @@
 
 all: editcommands.html xrefs.json
 
-editcommands.html: source.html data Makefile
+intermediate.html: source.html Makefile
+	./preprocess
+
+editcommands.html: intermediate.html data Makefile
 	$(ANOLIS) --output-encoding=ascii --omit-optional-tags --enable=xspecxref \
 	--enable=refs --use-strict $< $@
 
-xrefs.json: source.html Makefile
+xrefs.json: intermediate.html Makefile
 	$(ANOLIS) --dump-xrefs $< /tmp/spec
--- a/editcommands.html	Thu Feb 17 15:06:13 2011 -0700
+++ b/editcommands.html	Sun Feb 20 15:11:49 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-17-february-2011>Work in Progress &mdash; Last Update 17 February 2011</h2>
+<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>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg+spec@aryeh.name&gt;
@@ -38,8 +38,11 @@
  <li><a href=#introduction><span class=secno>1 </span>Introduction</a></li>
  <li><a href=#issues><span class=secno>2 </span>Issues</a></li>
  <li><a href=#definitions><span class=secno>3 </span>Definitions</a></li>
- <li><a href=#styling-a-range><span class=secno>4 </span>Styling a Range</a></li>
- <li><a href=#commands><span class=secno>5 </span>Commands</a></li>
+ <li><a href=#decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a Range into Nodes</a></li>
+ <li><a href=#unstyling-an-element><span class=secno>5 </span>Unstyling an element</a></li>
+ <li><a href=#styling-a-range><span class=secno>6 </span>Styling a Range</a></li>
+ <li><a href=#unstyling-a-range><span class=secno>7 </span>Unstyling a Range</a></li>
+ <li><a href=#commands><span class=secno>8 </span>Commands</a></li>
  <li><a class=no-num href=#references>References</a></ol>
 <!--end-toc-->
 
@@ -63,6 +66,9 @@
   <li><p>Need to make CSS terminology more precise, about setting/unsetting CSS
   properties.  The intent is to modify the style attribute, CSSOM-style.
 
+  <li><p>Also not sure about computed style.  There are differences between
+  "computed" and "used" and things like that, what do we actually want here?
+
   <li><p>Some of the DOM stuff might benefit from more precision.  E.g., is
   there a precise algorithm for what it means to append a node someplace, if
   that node is already somewhere in the DOM?  What does that do if it's
@@ -72,138 +78,181 @@
 
 <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-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 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>.
 
-<h2 id=styling-a-range><span class=secno>4 </span>Styling a Range</h2>
-<p>When a user agent is to <dfn id=style-a-range>style a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must
-run the following steps.  There are three inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, and a
-nonempty list of strings <var title="">tag list</var>.
+<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
+node</a>, if <em>that</em> 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 else null.
 
-<p class=XXX>This is totally made up and I have no idea yet if it matches
-browsers and/or is even vaguely coherent.
+<p class=XXX>(It will be null only in weird cases, like selecting a comment
+whose parent is a document, or the child of a document fragment, or whatever.
+I'm ignoring those cases for now.)
+
+
+<h2 id=decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a Range into Nodes</h2>
+<p>When a user agent is to <dfn id=decompose-a-range>decompose a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must run the following steps.
+
+<p class=note>The algorithm returns a 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 in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> that
+are not contained in any other <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 <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.  It splits <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 if necessary, but isn't picky about <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a>s 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>s.
 
 <ol>
-  <li><p>Let <var title="">start node</var>, <var title="">start offset</var>, <var title="">end node</var>, and <var title="">end offset</var> be 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> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</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>nodes</a> and
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offsets</a> of <var title="">range</var>,
+  <li><p>Let <var title="">start node</var>, <var title="">start offset</var>, <var title="">end node</var>, and <var title="">end offset</var> be 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>
+  and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</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>nodes</a> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offsets</a> of <var title="">range</var>,
   respectively.
 
-  <li><p>Let <var title="">document</var> be the <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> of <var title="">start
-  node</var>.
-
   <li><p>If <var title="">start node</var> or <var title="">end node</var> is not 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>, <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#processinginstruction><code class=external data-anolis-spec=domcore>ProcessingInstruction</code></a>, or <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a> node, or is not 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> and has no parent, abort
-  these steps.
+  <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 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#processinginstruction><code class=external data-anolis-spec=domcore>ProcessingInstruction</code></a>, or <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a> node, or is
+  not 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> and has no parent, abort these steps.
 
   <p class=XXX>Figure out something sensible here.
 
-  <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 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 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 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>.
-
-<!-- Original declarative definition
-  <li><p>Let <var title>node list</var> be the list of <code
-  data-anolis-spec=domcore>Node</code>s <var title>node</var> in <var
-  title>document</var> satisfying all of the following conditions:
-
-  <ul>
-    <li><p>In <span data-anolis-spec=domcore>tree order</span>, <var
-    title>node</var> is after the child of <var title>start node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>start offset</var> (or after <var title>start node</var> itself, if
-    <var title>start node</var> is not an <code
-    data-anolis-spec=domcore>Element</code>).
+  <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
+  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>In <span data-anolis-spec=domcore>tree order</span>, <var
-    title>node</var> is before the child of <var title>end node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>end offset</var> (or before <var title>end node</var> itself, if <var
-    title>end node</var> is not an <code
-    data-anolis-spec=domcore>Element</code>).
-
-    <li><p><var title>node</var> is not a <span data-anolis-spec=domcore
-    title=concept-descendant-node>descendant</span> of any other <code
-    data-anolis-spec=domcore>Node</code> that satisfies the previous two
-    conditions.
-  </ul>
+  <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
+  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>.
 
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is 0, add <var title>start node</var> to <var title>node list</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.
 
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  the <span data-anolis-spec=domrange title=concept-node-length>length</span>
-  of <var title>end node</var>, add <var title>end node</var> to <var
-  title>node list</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>If there is some <code data-anolis-spec=domcore>Element</code> that is
-  not in <var title>node list</var> but which has one or more children, and its
-  children are all in <var title>node list</var>, add that <code
-  data-anolis-spec=domcore>Element</code> to <var title>node list</var> and
-  remove all its children from <var title>node list</var>.  Repeat until there
-  is no such <code data-anolis-spec=domcore>Element</code>.
-  -->
+  <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
+  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>.
+
+  <li><p>If <var title="">end 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> and <var title="">end
+  offset</var> is not 0, let <var title="">end</var> be the child of <var title="">end 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="">end offset</var> &minus; 1.
+
+  <li><p>Otherwise, if <var title="">end offset</var> is 0, let <var title="">end</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> before <var title="">end 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="">end</var> be <var title="">end node</var>.
 
   <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>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 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 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>, 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, 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 0, or 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 no children, let <var title="">node</var> be <var title="">start node</var>.
-
-  <li><p>Otherwise, 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>If <var title="">end 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="">end</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>
-  before the child of <var title="">end 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="">end
-  offset</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> (or, if
-  <var title="">end offset</var> is <var title="">end node</var>'s <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="">end</var> be the last child of <var title="">end node</var>).
-
-  <li><p>Otherwise, 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
-  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>,
-  or if <var title="">end 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 no children, let <var title="">end</var> be <var title="">end node</var>.
-
-  <li><p>Otherwise, let <var title="">end</var> be 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> before <var title="">end 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>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>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
+    <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.
   </ol>
 
-  <!-- Condense the node list for extra tidiness. -->
-  <li><p>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> in <var title="">node list</var>.
-  
-  <li><p>Repeat:
+  <li><p>Return <var title="">node list</var>.
+</ol>
+
+
+<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.
+
+<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.
+
+  <li><p>Let <var title="">property name</var> and <var title="">tag list</var> be as
+  in the invoking algorithm.
+
+  <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
+    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,
+  </ul>
+
+  <p>then:
 
   <ol>
-    <li><p>If <var title="">node</var> has a parent, and its parent 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>, and all the children of <var title="">node</var>'s parent are contained in <var title="">node list</var>, add
-    <var title="">node</var>'s parent to <var title="">node list</var> immediately
-    before <var title="">node</var>, then remove all children of <var title="">node</var>'s parent from <var title="">node list</var>.  Set <var title="">node</var> to <var title="">node</var>'s parent and continue these
-    substeps from the beginning.
+    <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>If <var title="">node</var> is 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 <var title="">node list</var>, break
-    out of these substeps.
+    <li><p>Remove <var title="">element</var>.
 
-    <li><p>Set <var title="">node</var> to 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>.
+    <li><p>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>:
+
+  <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>
+    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>Remove <var title="">element</var>.
+  </ol>
+</ol>
+
+
+<h2 id=styling-a-range><span class=secno>6 </span>Styling a Range</h2>
+<p>When a user agent is to <dfn id=style-a-range>style a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must
+run the following steps.  There are three inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, and a
+nonempty list of strings <var title="">tag list</var>.
+
+<div class=XXX>
+<p>This description tries to keep the algorithm as simple as possible at the
+expense of producing much more complicated markup in some cases than a slightly
+more sophisticated algorithm.  It's similar to what Gecko does in spirit,
+although the details differ.  The basic cause of most of the ugly markup is
+that we don't bake in any element nesting restrictions, so <code title="">&lt;em&gt;Abc&lt;/em&gt; &lt;em&gt;def&lt;/em&gt;</code> becomes <code title="">&lt;em
+style=font-weight:bold&gt;Abc&lt;/em&gt;&lt;b&gt; &lt;/b&gt;&lt;em
+style=font-weight:bold&gt;def&lt;/em&gt;</code> instead of just <code title="">&lt;b&gt;&lt;em&gt;Abc&lt;/em&gt; &lt;em&gt;def&lt;/em&gt;&lt;/b&gt;</code>.  Otherwise
+we'd have to add content model checks.  Maybe this is worth it?  IE and WebKit
+seem to do it, Gecko and Opera seem not to (they only wrap text nodes AFAICT).
+
+<p>I did add some complication by saying conflicting inline style needs to be
+removed.  This is what WebKit does.  Gecko and IE9 leave it alone, which leads
+to stuff like <code title="">&lt;b&gt;&lt;span
+style=font-weight:100&gt;Foo&lt;/span&gt;&lt;/b&gt;</code> that doesn't actually work.
+Opera avoids the problem by producing code like <code title="">&lt;span
+style=font-weight:100&gt;&lt;b&gt;Foo&lt;/b&gt;&lt;/span&gt;</code>, but that doesn't work
+with the CSS-based strategy.
+</div>
+
+<ol>
+  <li><p>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose
+  a range">decomposing</a> <var title="">range</var>.
+
   <li><p>For each <var title="">node</var> in <var title="">node list</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>
@@ -215,68 +264,24 @@
       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>For each <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> <var title="">descendant</var> of <var title="">node</var> 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>:
-
-      <ol>
-        <li><p>If either
-        
-        <ul>
-          <li><p><var title="">descendant</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
-          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="">descendant</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,
-        </ul>
-
-        <p>then append all of <var title="">descendant</var>'s children to the
-        parent of <var title="">descendant</var> in order immediately before <var title="">descendant</var>, and remove <var title="">descendant</var>.
-        Continue with the next descendant.
-
-        <li><p>Unset the CSS property <var title="">property name</var> of <var title="">descendant</var>.
-        <!-- Only WebKit appears to do this, but it makes sense.  Otherwise
-        things like <span style=font-weight:normal> won't disappear when you
-        apply bold.  (Same applies to the places below where we unset the CSS
-        property here.) -->
-
-        <li><p>If <var title="">descendant</var> is an <a href=#html-element-with-name>HTML element with
-        name</a> in <var title="">tag list</var>, let <var title="">new
-        descendant</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> as <var title="">descendant</var>.  Append <var title="">new descendant</var> to <var title="">descendant</var>'s parent as the previous sibling of <var title="">descendant</var>, then append all of <var title="">descendant</var>'s children to <var title="">new descendant</var> in
-        order, then remove <var title="">descendant</var>.
-      </ol>
+      <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>.
     </ol>
 
-    <li><p>Otherwise:
+    <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>If <var title="">node</var> is not the first child of its parent,
-      and its previous sibling is an <a href=#html-element-with-name>HTML element with name</a> equal
-      to the first string in <var title="">tag list</var>, append <var title="">node</var> to <var title="">node</var>'s previous sibling as the last
-      child.  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>.
-      <!-- Thus we merge things together a bit.  This could probably use more
-      thought. -->
-
-      <li><p>If <var title="">node</var> is not 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, or if its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data title=dom-CharacterData-data>data</a> is a
-      sequence of zero or more <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#space-character title="space
-      character">space characters</a>, 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>.
-      <!-- This avoids wrapping whitespace-only text nodes, which agrees with
-      Gecko and WebKit.  IE9 and Opera wrap the whitespace-only text nodes,
-      which seems pointless. -->
-      
       <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> set to <var title="">document</var>.
-      
+      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>.
+
       <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>
+
+    <li><p>Otherwise, do nothing.
   </ol>
 </ol>
 
@@ -285,263 +290,86 @@
 Gecko just adds a style attribute.  The latter is simpler, particularly because
 you then don't have to worry about making sure you only insert your tags in a
 valid place (which you have to so that text/html serialization is possible, if
-nothing else).  I originally specced the former approach, retained here in case
-I want to switch back:
+nothing else).  I originally specced the former approach, available in git
+history. -->
 
-<p>When a user agent is to <dfn title=wrap-range-in-tag>wrap a <code
-data-anolis-spec=domrange>Range</code> <var title>range</var> in a tag <var
-title>tag</var></dfn>, treating <var title>tag list</var> as equivalent and
-overriding CSS <var title>property</var>, it must run the following steps:
 
-<p class=XXX>This is totally made up and I have no idea yet if it matches
-browsers and/or is even vaguely coherent.
+<h2 id=unstyling-a-range><span class=secno>7 </span>Unstyling a Range</h2>
+<p>When a user agent is to <dfn id=unstyle-a-range>unstyle a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must
+run the following steps.  There are three inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, and a
+possibly empty list of strings <var title="">tag list</var>.
 
 <ol>
-  <li><p>Let <var title>start node</var>, <var title>start offset</var>, <var
-  title>end node</var>, and <var title>end offset</var> be the <span
-  data-anolis-spec=domrange title=concept-range-start>start</span> and <span
-  data-anolis-spec=domrange title=concept-range-end>end</span> <span
-  data-anolis-spec=domrange title=concept-boundary-point-node>nodes</span> and
-  <span data-anolis-spec=domrange
-  title=concept-boundary-point-offset>offsets</span> of <var title>range</var>,
-  respectively.
-
-  <li><p>Let <var title>document</var> be the <code data-anolis-spec=domcore
-  title=dom-Node-ownerDocument>ownerDocument</code> of <var title>start
-  node</var>.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>ProcessingInstruction</code>, or <code
-  data-anolis-spec=domcore>Comment</code> node, and it has no parent, abort
-  these steps.
-
-  <p class=XXX>Figure out something sensible here.  Doesn't make sense except
-  in IE, since other browsers fail if it's not contentEditable.  What does IE
-  do?  What do we expect?
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>start node</var>, 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 returned node.  Set <var title>start
-  offset</var> to 0.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is 0, set <var title>start offset</var> to the <span
-  data-anolis-spec=domrange title=concept-indexof>index of</span> <var
-  title>start node</var> in its parent, then set <var title>start node</var> to
-  its parent.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> node, set <var
-  title>start offset</var> to one plus the <span data-anolis-spec=domrange
-  title=concept-indexof>index of</span> <var title>start node</var> in its
-  parent, then set <var title>start node</var> to its parent.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>end node</var>, run
-  <code data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var
-  title>end offset</var>)</code> 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 <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of the new <var title>end node</var>.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  the <span data-anolis-spec=domrange title=concept-node-length>length</span>
-  of <var title>end node</var>, set <var title>end offset</var> to one plus the
-  <span data-anolis-spec=domrange title=concept-indexof>index of</span> <var
-  title>end node</var> in its parent, then set <var title>end node</var> to its
-  parent.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> node, set <var
-  title>end offset</var> to the <span data-anolis-spec=domrange
-  title=concept-indexof>index of</span> <var title>end node</var> in its
-  parent, then set <var title>end node</var> to its parent.
+  <li><p>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose
+  a range">decomposing</a> <var title="">range</var>.
 
-  <p class=note>The previous several steps have ensured that <var title>start
-  node</var> and <var title>end node</var> are not <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> nodes, so <var
-  title>start offset</var> and <var title>end offset</var> now represent node
-  offsets instead of character offsets.
-
-  <li><p>Let <var title>node</var> equal <var title>start node</var> and let <var
-  title>offset</var> equal <var title>start offset</var>.
-
-  <li><p>Repeat the following steps until aborted:
-  <ol>
-    <li><p><i title id=wrap-algorithm-start>Start</i>: If <var title>node</var> is
-    after <var title>end node</var> in <span data-anolis-spec=domcore>tree
-    order</span>, or if <var title>node</var> equals <var title>end node</var>
-    and <var title>offset</var> is greater than or equal to <var title>end
-    offset</var>, abort this subalgorithm.
-
-    <li><p>Let <var title>element</var> be a new <code
-    data-anolis-spec=domcore>Element</code> with no attributes, <span
-    data-anolis-spec=domcore title=concept-element-namespace>namespace</span>
-    set to the <span data-anolis-spec=domcore>HTML namespace</span>, <span
-    data-anolis-spec=domcore title=concept-element-local-name>local name</span>
-    set to <var title>tag</var>, and <code data-anolis-spec=domcore
-    title=dom-Node-ownerDocument>ownerDocument</code> set to <var
-    title>document</var>.
-
-    <li><p>While <var title>node</var> has a child with <span
-    data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>offset</var>, and that child meets one of the following conditions,
-    take the described action (if any) and increment <var title>offset</var>:
+  <li><p>For each <var title="">node</var> in <var title="">node list</var>, in
+  order:
 
-    <dl class=switch>
-      <dt>The child is a <code data-anolis-spec=domcore>Text</code> node whose
-      <code data-anolis-spec=domcore title=dom-CharacterData-data>data</code>
-      consists of zero or more <span data-anolis-spec=html title="space
-      character">space characters</span>
-      <!- - This avoids wrapping whitespace-only text nodes, which agrees with
-      WebKit.  IE9 wraps the whitespace-only text nodes, and Gecko tries to do
-      something crazy like add CSS attributes instead of elements. - ->
-      <dt>The child is a <code data-anolis-spec=domcore>Comment</code>, <code
-      data-anolis-spec=domcore>ProcessingInstruction</code>, or <code
-      data-anolis-spec=domcore>DocumentType</code>
-      <dd>No action.
+  <ol>
+    <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>:
 
-      <dt>The child is an <code data-anolis-spec=domcore>Element</code> whose
-      <span data-anolis-spec=domcore
-      title=concept-element-namespace>namespace</span> is the <span
-      data-anolis-spec=domcore>HTML namespace</span> and whose <span
-      data-anolis-spec=domcore title=concept-element-local-name>local
-      name</span> is <var title>tag</var>
-      <dd><span>Unset the CSS property</span> <var title>property</var> on each
-      <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendant</span> of the child.
-      <!- - Only WebKit appears to do this, but it makes sense.  Otherwise
-      things like <span style=font-weight:normal> won't disappear when you
-      apply bold.  (Same applies to the places below where we unset the CSS
-      property here.) - ->
-    </dl>
+    <ol>
+      <li><p><a href=#unstyle-an-element title="unstyle an element">Unstyle</a> <var title="">node</var>.
 
-    <li><p>If <var title>offset</var> is equal to <var title>node</var>'s <span
-    data-anolis-spec=domrange title=concept-node-length>length</span>:
-    
-    <ol>
-      <li><p>If <var title>node</var> is the last <code
-      data-anolis-spec=domcore>Node</code> in <var title>document</var>, abort
-      this subalgorithm (the one that begins with <i title><a
-      href=wrap-algorithm-start>start</a></i>).
+      <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>Let <var title>node</var> equal the first <code
-      data-anolis-spec=domcore>Node</code> in <var title>document</var> that is
-      after all of <var title>node</var>'s <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendants</span> in <span
-      data-anolis-spec=domcore>tree order</span>.
-      
-      <li><p>Let <var title>offset</var> equal zero.
-      
-      <li><p>Continue from <i title><a
-      href=#wrap-algorithm-start>start</a></i>.
+      <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>.
     </ol>
 
-    <li><p>Let <var title>child</var> be the child of <var title>node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>offset</var>.
-
-    <p class=note><var title>child</var> will always be either an <code
-    data-anolis-spec=domcore>Element</code> or a <code
-    data-anolis-spec=domcore>Text</code> node.
+    <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>:
 
-    <li><p>If <var title>child</var> is an <code
-    data-anolis-spec=domcore>Element</code> which is not part of the <span
-    data-anolis-spec=html title="content models">content model</span> of <var
-    title>element</var>:
-    
     <ol>
-      <li><p>Create a new <code data-anolis-spec=domrange>Range</code> with <span
-      data-anolis-spec=domrange title=concept-range-start>start</span> (<var
-      title>child</var>, 0), <span data-anolis-spec=domrange
-      title=concept-range-end>end</span> (<var title>child</var>, <span
-      data-anolis-spec=domrange title=concept-node-length>length</span> of <Var
-      title>child</var>), and <span data-anolis-spec=domrange
-      title=concept-range-root>root</span> the same as <var title>range</var>'s
-      <span data-anolis-spec=domrange title=concept-range-root>root</span>.
-      
-      <li><p><span title=wrap-range-in-tag>Wrap that <code
-      data-anolis-spec=domrange>Range</code> in <var title>tag</var></span>,
-      treating <var title>tag list</var> as equivalent and overriding CSS <var
-      title>property</var>.
+      <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>Set the CSS property <var title="">property name</var> of <var title="">new parent</var> to <var title="">property value</var>.
+
+      <li><p>Insert <var title="">new parent</var> as <var title="">node</var>'s
+      previous sibling.
+
+      <li><p>Append <var title="">node</var> to <var title="">new parent</var> as its
+      child.
     </ol>
 
-    <li><p>Otherwise, if <var title>child</var> is an <code
-    data-anolis-spec=domcore>Element</code> whose <span
-    data-anolis-spec=domcore title=concept-element-namespace>namespace</span>
-    is the <span data-anolis-spec=domcore>HTML namespace</span> and whose <span
-    data-anolis-spec=domcore title=concept-element-local-name>local name</span>
-    is <var title>tag</var> or is in <var title>tag list</var>, change <var
-    title>child</var>'s <span data-anolis-spec=domcore
-    title=concept-element-local-name>local name</span> to <var title>tag</var>
-    and <span>unset the CSS property</span> <var title>property</var> on each
-    of its <span data-anolis-spec=domcore
-    title=concept-descendant-node>descendants</span>.
-
-    <li><p>Otherwise:
-
-    <p class=note><var title>child</var> is a <code
-    data-anolis-spec=domcore>Text</code> node or an <code
-    data-anolis-spec=domcore>Element</code> that is part of the <span
-    data-anolis-spec=html title="content models">content model</span> of <var
-    title>element</var>.
-
-    <ol>
-      <li><p>Append <var title>element</var> to <var title>node</var> as the
-      previous sibling of <var title>child</var>.
-
-      <li><p>While <var title>node</var> has a child with <span
-      data-anolis-spec=domrange title=concept-indexof>index</span> <var
-      title>offset</var> + 1, and while that child is not an <code
-      data-anolis-spec=domcore>Element</code> or is an <code
-      data-anolis-spec=domcore>Element</code> but is part of the <span
-      data-anolis-spec=html title="content models">content model</span> of <var
-      title>element</var>, append that child to <var title>element</var>.
-
-      <p class=note>Since the next sibling of <var title>element</var> is being
-      reparented on each iteration of the loop, the same <var
-      title>offset</var> will point to a new node every time.
-
-      <li><p><span>Unset the CSS property</span> <var title>property</var> on
-      each of <var title>element</var>'s descendants.
-    </ol>
-
-    <li><p>Increment <var title>offset</var>.
-
-    <p class=XXX>There appears to be no precise definition of what it means to
-    append an existing element to another one.  Is it just obvious?  Should Web
-    DOM Core be clearer?
+    <li><p>Otherwise, do nothing.
   </ol>
 </ol>
--->
 
-<h2 id=commands><span class=secno>5 </span>Commands</h2>
+
+<h2 id=commands><span class=secno>8 </span>Commands</h2>
+<p>The <dfn id=execcommand() title=execCommand()><code>execCommand(<var title="">commandId</var>,
+<var title="">showUI</var>, <var title="">value</var>)</code></dfn> method on the
+<a href=http://www.whatwg.org/html/#htmldocument><code class=external data-anolis-spec=html>HTMLDocument</code></a> interface allows scripts to
+perform actions on the current selection or at the current caret position.
+Generally, these commands would be used to implement editor UI, for example
+having a "delete" button on a toolbar.
+
+<p>There are three variants to this method, with one, two, and three arguments
+respectively. The <var title="">showUI</var> and <var title="">value</var>
+parameters, even if specified, are ignored except where otherwise stated.
+
+<p>When <a href=#execcommand()><code>execCommand()</code></a> is invoked, the user agent must run the
+following steps:
+
+<ol>
+  <li>Let <var title="">selection</var> be the result of calling <a href=http://html5.org/specs/dom-range.html#dom-document-getselection><code class=external data-anolis-spec=domrange title=dom-Document-getSelection>getSelection()</code></a> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.
+
+  <li>For each <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> associated with
+  <var title="">selection</var>, in order, take the action from the list below
+  given by <var title="">commandId</var>.
+</ol>
+
 <dl>
 <dt><code title=""><dfn id=command-bold title=command-bold>bold</dfn></code>
-<dd>The user agent must <a href=#style-a-range title="style a range">style each <code class=external data-anolis-spec=domrange>Range</code></a> of the current <a href=http://html5.org/specs/dom-range.html#selection><code class=external data-anolis-spec=domrange>Selection</code></a> with <var title="">property name</var>
-"font-weight", <var title="">property value</var> "bold", and <var title="">tag
-list</var> ["b", "strong"].
-
-<p class=XXX>Be clearer.  What's the "current Selection", and what does it mean
-to wrap "each Range"?  What if there are overlapping Ranges?
-
-<p class=XXX>Obviously this doesn't yet even attempt to include the case where
-it's already bold and the bold has to be removed.
+<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
+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"].
 </dl>
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/preprocess	Sun Feb 20 15:11:49 2011 -0700
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# This script is probably evil, but I am unspeakably sick of typing stuff like
+# <span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>.
+
+replace = {
+    "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>",
+    "bpoffsets": "<span data-anolis-spec=domrange title=concept-boundary-point-offset>offsets</span>",
+    "comment": "<code data-anolis-spec=domcore>Comment</code>",
+    "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>",
+    "index": "<span data-anolis-spec=domrange title=concept-indexof>index</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>",
+    "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>",
+    "rangestart": "<span data-anolis-spec=domrange title=concept-range-start>start</span>",
+    "text": "<code data-anolis-spec=domcore>Text</code>",
+    "treeorder": "<span data-anolis-spec=domcore>tree order</span>",
+}
+
+s = open("source.html", "r").read()
+for key in replace:
+    s = s.replace("[[" + key + "]]", replace[key])
+
+if "[[" in s:
+    raise Exception("Something mistyped?  " + s[s.find("[["):s.rfind("]]") + 2])
+
+f = open("intermediate.html", "w")
+f.write(s)
+f.close()
--- a/source.html	Thu Feb 17 15:06:13 2011 -0700
+++ b/source.html	Sun Feb 20 15:11:49 2011 -0700
@@ -54,6 +54,9 @@
   <li><p>Need to make CSS terminology more precise, about setting/unsetting CSS
   properties.  The intent is to modify the style attribute, CSSOM-style.
 
+  <li><p>Also not sure about computed style.  There are differences between
+  "computed" and "used" and things like that, what do we actually want here?
+
   <li><p>Some of the DOM stuff might benefit from more precision.  E.g., is
   there a precise algorithm for what it means to append a node someplace, if
   that node is already somewhere in the DOM?  What does that do if it's
@@ -63,6 +66,9 @@
 
 <h2>Definitions</h2>
 
+<p class=XXX>This definition is just an ugly workaround for the annoyance of
+xrefs.  Fix it sometime.
+
 <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
@@ -71,6 +77,155 @@
 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 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]].
+
+<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
+node</span>, if <em>that</em> is an [[element]]; or else null.
+
+<p class=XXX>(It will be null only in weird cases, like selecting a comment
+whose parent is a document, or the child of a document fragment, or whatever.
+I'm ignoring those cases for now.)
+
+
+<h2>Decomposing a Range into Nodes</h2>
+<p>When a user agent is to <dfn>decompose a [[range]]</dfn> <var
+title>range</var>, it must run the following steps.
+
+<p class=note>The algorithm returns a list of [[node]]s in the [[range]] that
+are not contained in any other [[node]] in the [[range]].  It splits [[text]]
+nodes if necessary, but isn't picky about [[comment]]s or
+[[processinginstruction]]s.
+
+<ol>
+  <li><p>Let <var title>start node</var>, <var title>start offset</var>, <var
+  title>end node</var>, and <var title>end offset</var> be the [[rangestart]]
+  and [[rangeend]] [[bpnodes]] and [[bpoffsets]] of <var title>range</var>,
+  respectively.
+
+  <li><p>If <var title>start node</var> or <var title>end node</var> is not an
+  [[element]], [[text]], [[processinginstruction]], or [[comment]] node, or is
+  not an [[element]] and has no parent, abort these steps.
+
+  <p class=XXX>Figure out something sensible here.
+
+  <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
+  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
+  returned node.  Set <var title>start offset</var> to 0.
+
+  <li><p>If <var title>end node</var> is a [[text]] node and <var title>end
+  offset</var> is neither 0 nor the [[nodelength]] of <var title>end
+  node</var>, run <code data-anolis-spec=domcore
+  title=dom-Text-splitText>splitText(<var title>end offset</var>)</code> 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
+  [[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>.
+
+  <li><p>Otherwise, if <var title>start node</var> is a [[text]] node and <var
+  title>start offset</var> is its [[nodelength]], let <var title>node</var> be
+  the first [[node]] after <var title>start node</var> in [[treeorder]].
+
+  <li><p>Otherwise, let <var title>node</var> be <var title>start node</var>.
+
+  <li><p>If <var title>end node</var> is an [[element]] and <var title>end
+  offset</var> is not 0, let <var title>end</var> be the child of <var
+  title>end node</var> with [[index]] <var title>end offset</var> &minus; 1.
+
+  <li><p>Otherwise, if <var title>end offset</var> is 0, let <var
+  title>end</var> be the first [[node]] before <var title>end node</var> in
+  [[treeorder]].
+
+  <li><p>Otherwise, let <var title>end</var> be <var title>end node</var>.
+
+  <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>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.
+  </ol>
+
+  <li><p>Return <var title>node list</var>.
+</ol>
+
+
+<h2>Unstyling an element</h2>
+<p>When a user agent is to <dfn>unstyle an element</dfn>, it must run the
+following steps.
+
+<ol>
+  <li><p>Let <var title>element</var> be the <code
+  data-anolis-spec=domcore>Element</code> to be unstyled.
+
+  <li><p>Let <var title>property name</var> and <var title>tag list</var> be as
+  in the invoking algorithm.
+
+  <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
+    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,
+  </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>Remove <var title>element</var>.
+
+    <li><p>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>:
+
+  <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
+    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>Remove <var title>element</var>.
+  </ol>
+</ol>
+
 
 <h2>Styling a Range</h2>
 <p>When a user agent is to <dfn>style a <code
@@ -79,174 +234,31 @@
 title>property name</var>, a new value <var title>property value</var>, and a
 nonempty list of strings <var title>tag list</var>.
 
-<p class=XXX>This is totally made up and I have no idea yet if it matches
-browsers and/or is even vaguely coherent.
+<div class=XXX>
+<p>This description tries to keep the algorithm as simple as possible at the
+expense of producing much more complicated markup in some cases than a slightly
+more sophisticated algorithm.  It's similar to what Gecko does in spirit,
+although the details differ.  The basic cause of most of the ugly markup is
+that we don't bake in any element nesting restrictions, so <code
+title>&lt;em>Abc&lt;/em> &lt;em>def&lt;/em></code> becomes <code title>&lt;em
+style=font-weight:bold>Abc&lt;/em>&lt;b> &lt;/b>&lt;em
+style=font-weight:bold>def&lt;/em></code> instead of just <code
+title>&lt;b>&lt;em>Abc&lt;/em> &lt;em>def&lt;/em>&lt;/b></code>.  Otherwise
+we'd have to add content model checks.  Maybe this is worth it?  IE and WebKit
+seem to do it, Gecko and Opera seem not to (they only wrap text nodes AFAICT).
+
+<p>I did add some complication by saying conflicting inline style needs to be
+removed.  This is what WebKit does.  Gecko and IE9 leave it alone, which leads
+to stuff like <code title>&lt;b>&lt;span
+style=font-weight:100>Foo&lt;/span>&lt;/b></code> that doesn't actually work.
+Opera avoids the problem by producing code like <code title>&lt;span
+style=font-weight:100>&lt;b>Foo&lt;/b>&lt;/span></code>, but that doesn't work
+with the CSS-based strategy.
+</div>
 
 <ol>
-  <li><p>Let <var title>start node</var>, <var title>start offset</var>, <var
-  title>end node</var>, and <var title>end offset</var> be the <span
-  data-anolis-spec=domrange title=concept-range-start>start</span> and <span
-  data-anolis-spec=domrange title=concept-range-end>end</span> <span
-  data-anolis-spec=domrange title=concept-boundary-point-node>nodes</span> and
-  <span data-anolis-spec=domrange
-  title=concept-boundary-point-offset>offsets</span> of <var title>range</var>,
-  respectively.
-
-  <li><p>Let <var title>document</var> be the <code data-anolis-spec=domcore
-  title=dom-Node-ownerDocument>ownerDocument</code> of <var title>start
-  node</var>.
-
-  <li><p>If <var title>start node</var> or <var title>end node</var> is not an
-  <code data-anolis-spec=domcore>Element</code>, <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>ProcessingInstruction</code>, or <code
-  data-anolis-spec=domcore>Comment</code> node, or is not an <code
-  data-anolis-spec=domcore>Element</code> and has no parent, abort
-  these steps.
-
-  <p class=XXX>Figure out something sensible here.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>start node</var>, 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 returned node.  Set <var title>start
-  offset</var> to 0.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>end node</var>, run
-  <code data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var
-  title>end offset</var>)</code> 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 <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of the new <var title>end node</var>.
-
-<!-- Original declarative definition
-  <li><p>Let <var title>node list</var> be the list of <code
-  data-anolis-spec=domcore>Node</code>s <var title>node</var> in <var
-  title>document</var> satisfying all of the following conditions:
-
-  <ul>
-    <li><p>In <span data-anolis-spec=domcore>tree order</span>, <var
-    title>node</var> is after the child of <var title>start node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>start offset</var> (or after <var title>start node</var> itself, if
-    <var title>start node</var> is not an <code
-    data-anolis-spec=domcore>Element</code>).
-
-    <li><p>In <span data-anolis-spec=domcore>tree order</span>, <var
-    title>node</var> is before the child of <var title>end node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>end offset</var> (or before <var title>end node</var> itself, if <var
-    title>end node</var> is not an <code
-    data-anolis-spec=domcore>Element</code>).
-
-    <li><p><var title>node</var> is not a <span data-anolis-spec=domcore
-    title=concept-descendant-node>descendant</span> of any other <code
-    data-anolis-spec=domcore>Node</code> that satisfies the previous two
-    conditions.
-  </ul>
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is 0, add <var title>start node</var> to <var title>node list</var>.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  the <span data-anolis-spec=domrange title=concept-node-length>length</span>
-  of <var title>end node</var>, add <var title>end node</var> to <var
-  title>node list</var>.
-
-  <li><p>If there is some <code data-anolis-spec=domcore>Element</code> that is
-  not in <var title>node list</var> but which has one or more children, and its
-  children are all in <var title>node list</var>, add that <code
-  data-anolis-spec=domcore>Element</code> to <var title>node list</var> and
-  remove all its children from <var title>node list</var>.  Repeat until there
-  is no such <code data-anolis-spec=domcore>Element</code>.
-  -->
-
-  <li><p>Let <var title>node list</var> be an empty list of <code
-  data-anolis-spec=domcore>Node</code>s.
-
-  <li><p>If <var title>start node</var> is an <code
-  data-anolis-spec=domcore>Element</code> with at least one child, let <var
-  title>node</var> be the first <code data-anolis-spec=domcore>Node</code>
-  after the child of <var title>start node</var> with <span
-  data-anolis-spec=domrange title=concept-indexof>index</span> <var title>start
-  offset</var>, in <span data-anolis-spec=domcore>tree order</span>.
-
-  <li><p>Otherwise, if <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is 0, or if <var title>start node</var> is an <code
-  data-anolis-spec=domcore>Element</code> with no children, let <var
-  title>node</var> be <var title>start node</var>.
-
-  <li><p>Otherwise, let <var title>node</var> be the first <code
-  data-anolis-spec=domcore>Node</code> after <var title>start node</var> in
-  <span data-anolis-spec=domcore>tree order</span>.
-
-  <li><p>If <var title>end node</var> is an <code
-  data-anolis-spec=domcore>Element</code> with at least one child, let <var
-  title>end</var> be the first <code data-anolis-spec=domcore>Node</code>
-  before the child of <var title>end node</var> with <span
-  data-anolis-spec=domrange title=concept-indexof>index</span> <var title>end
-  offset</var>, in <span data-anolis-spec=domcore>tree order</span> (or, if
-  <var title>end offset</var> is <var title>end node</var>'s <span
-  data-anolis-spec=domrange title=concept-node-length>length</span>, let <var
-  title>end</var> be the last child of <var title>end node</var>).
-
-  <li><p>Otherwise, if <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  its <span data-anolis-spec=domrange title=concept-node-length>length</span>,
-  or if <var title>end node</var> is an <code
-  data-anolis-spec=domcore>Element</code> with no children, let <var
-  title>end</var> be <var title>end node</var>.
-
-  <li><p>Otherwise, let <var title>end</var> be the last <code
-  data-anolis-spec=domcore>Node</code> before <var title>end node</var> in
-  <span data-anolis-spec=domcore>tree order</span>.
-
-  <li><p>While <var title>node</var> is not after <var title>end</var> in
-  <span data-anolis-spec=domcore>tree order</span>:
-
-  <ol>
-    <li>Append <var title>node</var> to <var title>node list</var>.
-
-    <li>Set <var title>node</var> to the first <code
-    data-anolis-spec=domcore>Node</code> in <span data-anolis-spec=domcore>tree
-    order</span> that is after <var title>node</var> and (if applicable) all
-    its <span data-anolis-spec=domcore
-    title=concept-descendant-node>descendants</span>.  If no such <code
-    data-anolis-spec=domcore>Node</code> exists, break out of these substeps.
-  </ol>
-
-  <!-- Condense the node list for extra tidiness. -->
-  <li><p>Let <var title>node</var> be the first <code
-  data-anolis-spec=domcore>Node</code> in <var title>node list</var>.
-  
-  <li><p>Repeat:
-
-  <ol>
-    <li><p>If <var title>node</var> has a parent, and its parent is an <code
-    data-anolis-spec=domcore>Element</code>, and all the children of <var
-    title>node</var>'s parent are contained in <var title>node list</var>, add
-    <var title>node</var>'s parent to <var title>node list</var> immediately
-    before <var title>node</var>, then remove all children of <var
-    title>node</var>'s parent from <var title>node list</var>.  Set <var
-    title>node</var> to <var title>node</var>'s parent and continue these
-    substeps from the beginning.
-
-    <li><p>If <var title>node</var> is the last <code
-    data-anolis-spec=domcore>Node</code> in <var title>node list</var>, break
-    out of these substeps.
-
-    <li><p>Set <var title>node</var> to the next <code
-    data-anolis-spec=domcore>Node</code> in <var title>node list</var>.
-  </ol>
+  <li><p>Let <var title>node list</var> be the result of <span title="decompose
+  a range">decomposing</span> <var title>range</var>.
 
   <li><p>For each <var title>node</var> in <var title>node list</var>, in <span
   data-anolis-spec=domcore>tree order</span>:
@@ -262,84 +274,29 @@
       <var title>property name</var> of <var title>node</var> to <var
       title>property value</var>.
 
-      <li><p>For each <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendant</span> <var
-      title>descendant</var> of <var title>node</var> that is an <code
-      data-anolis-spec=domcore>Element</code>:
-
-      <ol>
-        <li><p>If either
-        
-        <ul>
-          <li><p><var title>descendant</var> is an <span>HTML element with
-          name</span> 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>descendant</var> is an <span>HTML element with
-          name</span> in <var title>tag list</var> and it has no attributes,
-        </ul>
-
-        <p>then append all of <var title>descendant</var>'s children to the
-        parent of <var title>descendant</var> in order immediately before <var
-        title>descendant</var>, and remove <var title>descendant</var>.
-        Continue with the next descendant.
-
-        <li><p>Unset the CSS property <var title>property name</var> of <var
-        title>descendant</var>.
-        <!-- Only WebKit appears to do this, but it makes sense.  Otherwise
-        things like <span style=font-weight:normal> won't disappear when you
-        apply bold.  (Same applies to the places below where we unset the CSS
-        property here.) -->
-
-        <li><p>If <var title>descendant</var> is an <span>HTML element with
-        name</span> in <var title>tag list</var>, let <var title>new
-        descendant</var> be a new <span>HTML element with name</span> "span",
-        with the same attributes and <code data-anolis-spec=domcore
-        title=dom-Node-ownerDocument>ownerDocument</code> as <var
-        title>descendant</var>.  Append <var title>new descendant</var> to <var
-        title>descendant</var>'s parent as the previous sibling of <var
-        title>descendant</var>, then append all of <var
-        title>descendant</var>'s children to <var title>new descendant</var> in
-        order, then remove <var title>descendant</var>.
-      </ol>
+      <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>.
     </ol>
 
-    <li><p>Otherwise:
+    <li><p>Otherwise, if <var title>node</var> is a <code
+    data-anolis-spec=domcore>Text</code> node:
 
     <ol>
-      <li><p>If <var title>node</var> is not the first child of its parent,
-      and its previous sibling is an <span>HTML element with name</span> equal
-      to the first string in <var title>tag list</var>, append <var
-      title>node</var> to <var title>node</var>'s previous sibling as the last
-      child.  Continue with the next <code data-anolis-spec=domcore>Node</code>
-      in <var title>node list</var>.
-      <!-- Thus we merge things together a bit.  This could probably use more
-      thought. -->
-
-      <li><p>If <var title>node</var> is not a <code
-      data-anolis-spec=domcore>Text</code> node, or if its <span
-      data-anolis-spec=domcore title=dom-CharacterData-data>data</span> is a
-      sequence of zero or more <span data-anolis-spec=html title="space
-      character">space characters</span>, continue with the next <code
-      data-anolis-spec=domcore>Node</code> in <var title>node list</var>.
-      <!-- This avoids wrapping whitespace-only text nodes, which agrees with
-      Gecko and WebKit.  IE9 and Opera wrap the whitespace-only text nodes,
-      which seems pointless. -->
-      
       <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
-      title=dom-Node-ownerDocument>ownerDocument</code> set to <var
-      title>document</var>.
-      
+      title=dom-Node-ownerDocument>ownerDocument</code> the same as <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>
+
+    <li><p>Otherwise, do nothing.
   </ol>
 </ol>
 
@@ -348,265 +305,106 @@
 Gecko just adds a style attribute.  The latter is simpler, particularly because
 you then don't have to worry about making sure you only insert your tags in a
 valid place (which you have to so that text/html serialization is possible, if
-nothing else).  I originally specced the former approach, retained here in case
-I want to switch back:
+nothing else).  I originally specced the former approach, available in git
+history. -->
 
-<p>When a user agent is to <dfn title=wrap-range-in-tag>wrap a <code
-data-anolis-spec=domrange>Range</code> <var title>range</var> in a tag <var
-title>tag</var></dfn>, treating <var title>tag list</var> as equivalent and
-overriding CSS <var title>property</var>, it must run the following steps:
 
-<p class=XXX>This is totally made up and I have no idea yet if it matches
-browsers and/or is even vaguely coherent.
+<h2>Unstyling a Range</h2>
+<p>When a user agent is to <dfn>unstyle a <code
+data-anolis-spec=domrange>Range</code></dfn> <var title>range</var>, it must
+run the following steps.  There are three inputs: a CSS property name <var
+title>property name</var>, a new value <var title>property value</var>, and a
+possibly empty list of strings <var title>tag list</var>.
 
 <ol>
-  <li><p>Let <var title>start node</var>, <var title>start offset</var>, <var
-  title>end node</var>, and <var title>end offset</var> be the <span
-  data-anolis-spec=domrange title=concept-range-start>start</span> and <span
-  data-anolis-spec=domrange title=concept-range-end>end</span> <span
-  data-anolis-spec=domrange title=concept-boundary-point-node>nodes</span> and
-  <span data-anolis-spec=domrange
-  title=concept-boundary-point-offset>offsets</span> of <var title>range</var>,
-  respectively.
-
-  <li><p>Let <var title>document</var> be the <code data-anolis-spec=domcore
-  title=dom-Node-ownerDocument>ownerDocument</code> of <var title>start
-  node</var>.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>ProcessingInstruction</code>, or <code
-  data-anolis-spec=domcore>Comment</code> node, and it has no parent, abort
-  these steps.
-
-  <p class=XXX>Figure out something sensible here.  Doesn't make sense except
-  in IE, since other browsers fail if it's not contentEditable.  What does IE
-  do?  What do we expect?
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>start node</var>, 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 returned node.  Set <var title>start
-  offset</var> to 0.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>start offset</var>
-  is 0, set <var title>start offset</var> to the <span
-  data-anolis-spec=domrange title=concept-indexof>index of</span> <var
-  title>start node</var> in its parent, then set <var title>start node</var> to
-  its parent.
-
-  <li><p>If <var title>start node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> node, set <var
-  title>start offset</var> to one plus the <span data-anolis-spec=domrange
-  title=concept-indexof>index of</span> <var title>start node</var> in its
-  parent, then set <var title>start node</var> to its parent.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  neither 0 nor the <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of <var title>end node</var>, run
-  <code data-anolis-spec=domcore title=dom-Text-splitText>splitText(<var
-  title>end offset</var>)</code> 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 <span data-anolis-spec=domrange
-  title=concept-node-length>length</span> of the new <var title>end node</var>.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code> node and <var title>end offset</var> is
-  the <span data-anolis-spec=domrange title=concept-node-length>length</span>
-  of <var title>end node</var>, set <var title>end offset</var> to one plus the
-  <span data-anolis-spec=domrange title=concept-indexof>index of</span> <var
-  title>end node</var> in its parent, then set <var title>end node</var> to its
-  parent.
-
-  <li><p>If <var title>end node</var> is a <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> node, set <var
-  title>end offset</var> to the <span data-anolis-spec=domrange
-  title=concept-indexof>index of</span> <var title>end node</var> in its
-  parent, then set <var title>end node</var> to its parent.
+  <li><p>Let <var title>node list</var> be the result of <span title="decompose
+  a range">decomposing</span> <var title>range</var>.
 
-  <p class=note>The previous several steps have ensured that <var title>start
-  node</var> and <var title>end node</var> are not <code
-  data-anolis-spec=domcore>Text</code>, <code
-  data-anolis-spec=domcore>Comment</code>, or <code
-  data-anolis-spec=domcore>ProcessingInstruction</code> nodes, so <var
-  title>start offset</var> and <var title>end offset</var> now represent node
-  offsets instead of character offsets.
-
-  <li><p>Let <var title>node</var> equal <var title>start node</var> and let <var
-  title>offset</var> equal <var title>start offset</var>.
-
-  <li><p>Repeat the following steps until aborted:
-  <ol>
-    <li><p><i title id=wrap-algorithm-start>Start</i>: If <var title>node</var> is
-    after <var title>end node</var> in <span data-anolis-spec=domcore>tree
-    order</span>, or if <var title>node</var> equals <var title>end node</var>
-    and <var title>offset</var> is greater than or equal to <var title>end
-    offset</var>, abort this subalgorithm.
-
-    <li><p>Let <var title>element</var> be a new <code
-    data-anolis-spec=domcore>Element</code> with no attributes, <span
-    data-anolis-spec=domcore title=concept-element-namespace>namespace</span>
-    set to the <span data-anolis-spec=domcore>HTML namespace</span>, <span
-    data-anolis-spec=domcore title=concept-element-local-name>local name</span>
-    set to <var title>tag</var>, and <code data-anolis-spec=domcore
-    title=dom-Node-ownerDocument>ownerDocument</code> set to <var
-    title>document</var>.
-
-    <li><p>While <var title>node</var> has a child with <span
-    data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>offset</var>, and that child meets one of the following conditions,
-    take the described action (if any) and increment <var title>offset</var>:
+  <li><p>For each <var title>node</var> in <var title>node list</var>, in
+  order:
 
-    <dl class=switch>
-      <dt>The child is a <code data-anolis-spec=domcore>Text</code> node whose
-      <code data-anolis-spec=domcore title=dom-CharacterData-data>data</code>
-      consists of zero or more <span data-anolis-spec=html title="space
-      character">space characters</span>
-      <!- - This avoids wrapping whitespace-only text nodes, which agrees with
-      WebKit.  IE9 wraps the whitespace-only text nodes, and Gecko tries to do
-      something crazy like add CSS attributes instead of elements. - ->
-      <dt>The child is a <code data-anolis-spec=domcore>Comment</code>, <code
-      data-anolis-spec=domcore>ProcessingInstruction</code>, or <code
-      data-anolis-spec=domcore>DocumentType</code>
-      <dd>No action.
+  <ol>
+    <li><p>If <var title>node</var> is an <code
+    data-anolis-spec=domcore>Element</code>:
 
-      <dt>The child is an <code data-anolis-spec=domcore>Element</code> whose
-      <span data-anolis-spec=domcore
-      title=concept-element-namespace>namespace</span> is the <span
-      data-anolis-spec=domcore>HTML namespace</span> and whose <span
-      data-anolis-spec=domcore title=concept-element-local-name>local
-      name</span> is <var title>tag</var>
-      <dd><span>Unset the CSS property</span> <var title>property</var> on each
-      <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendant</span> of the child.
-      <!- - Only WebKit appears to do this, but it makes sense.  Otherwise
-      things like <span style=font-weight:normal> won't disappear when you
-      apply bold.  (Same applies to the places below where we unset the CSS
-      property here.) - ->
-    </dl>
+    <ol>
+      <li><p><span title="unstyle an element">Unstyle</span> <var
+      title>node</var>.
 
-    <li><p>If <var title>offset</var> is equal to <var title>node</var>'s <span
-    data-anolis-spec=domrange title=concept-node-length>length</span>:
-    
-    <ol>
-      <li><p>If <var title>node</var> is the last <code
-      data-anolis-spec=domcore>Node</code> in <var title>document</var>, abort
-      this subalgorithm (the one that begins with <i title><a
-      href=wrap-algorithm-start>start</a></i>).
+      <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>Let <var title>node</var> equal the first <code
-      data-anolis-spec=domcore>Node</code> in <var title>document</var> that is
-      after all of <var title>node</var>'s <span data-anolis-spec=domcore
-      title=concept-descendant-node>descendants</span> in <span
-      data-anolis-spec=domcore>tree order</span>.
-      
-      <li><p>Let <var title>offset</var> equal zero.
-      
-      <li><p>Continue from <i title><a
-      href=#wrap-algorithm-start>start</a></i>.
+      <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>.
     </ol>
 
-    <li><p>Let <var title>child</var> be the child of <var title>node</var> with
-    <span data-anolis-spec=domrange title=concept-indexof>index</span> <var
-    title>offset</var>.
-
-    <p class=note><var title>child</var> will always be either an <code
-    data-anolis-spec=domcore>Element</code> or a <code
-    data-anolis-spec=domcore>Text</code> node.
+    <li><p>Otherwise, if <var title>node</var> is a <code
+    data-anolis-spec=domcore>Text</code> 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>:
 
-    <li><p>If <var title>child</var> is an <code
-    data-anolis-spec=domcore>Element</code> which is not part of the <span
-    data-anolis-spec=html title="content models">content model</span> of <var
-    title>element</var>:
-    
     <ol>
-      <li><p>Create a new <code data-anolis-spec=domrange>Range</code> with <span
-      data-anolis-spec=domrange title=concept-range-start>start</span> (<var
-      title>child</var>, 0), <span data-anolis-spec=domrange
-      title=concept-range-end>end</span> (<var title>child</var>, <span
-      data-anolis-spec=domrange title=concept-node-length>length</span> of <Var
-      title>child</var>), and <span data-anolis-spec=domrange
-      title=concept-range-root>root</span> the same as <var title>range</var>'s
-      <span data-anolis-spec=domrange title=concept-range-root>root</span>.
-      
-      <li><p><span title=wrap-range-in-tag>Wrap that <code
-      data-anolis-spec=domrange>Range</code> in <var title>tag</var></span>,
-      treating <var title>tag list</var> as equivalent and overriding CSS <var
-      title>property</var>.
+      <li><p>Let <var title>new parent</var> be a new <span>HTML element with
+      name</span> "span", with no attributes, and with <code
+      data-anolis-spec=domcore
+      title=dom-Node-ownerDocument>ownerDocument</code> 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>.
+
+      <li><p>Insert <var title>new parent</var> as <var title>node</var>'s
+      previous sibling.
+
+      <li><p>Append <var title>node</var> to <var title>new parent</var> as its
+      child.
     </ol>
 
-    <li><p>Otherwise, if <var title>child</var> is an <code
-    data-anolis-spec=domcore>Element</code> whose <span
-    data-anolis-spec=domcore title=concept-element-namespace>namespace</span>
-    is the <span data-anolis-spec=domcore>HTML namespace</span> and whose <span
-    data-anolis-spec=domcore title=concept-element-local-name>local name</span>
-    is <var title>tag</var> or is in <var title>tag list</var>, change <var
-    title>child</var>'s <span data-anolis-spec=domcore
-    title=concept-element-local-name>local name</span> to <var title>tag</var>
-    and <span>unset the CSS property</span> <var title>property</var> on each
-    of its <span data-anolis-spec=domcore
-    title=concept-descendant-node>descendants</span>.
-
-    <li><p>Otherwise:
-
-    <p class=note><var title>child</var> is a <code
-    data-anolis-spec=domcore>Text</code> node or an <code
-    data-anolis-spec=domcore>Element</code> that is part of the <span
-    data-anolis-spec=html title="content models">content model</span> of <var
-    title>element</var>.
-
-    <ol>
-      <li><p>Append <var title>element</var> to <var title>node</var> as the
-      previous sibling of <var title>child</var>.
-
-      <li><p>While <var title>node</var> has a child with <span
-      data-anolis-spec=domrange title=concept-indexof>index</span> <var
-      title>offset</var> + 1, and while that child is not an <code
-      data-anolis-spec=domcore>Element</code> or is an <code
-      data-anolis-spec=domcore>Element</code> but is part of the <span
-      data-anolis-spec=html title="content models">content model</span> of <var
-      title>element</var>, append that child to <var title>element</var>.
-
-      <p class=note>Since the next sibling of <var title>element</var> is being
-      reparented on each iteration of the loop, the same <var
-      title>offset</var> will point to a new node every time.
-
-      <li><p><span>Unset the CSS property</span> <var title>property</var> on
-      each of <var title>element</var>'s descendants.
-    </ol>
-
-    <li><p>Increment <var title>offset</var>.
-
-    <p class=XXX>There appears to be no precise definition of what it means to
-    append an existing element to another one.  Is it just obvious?  Should Web
-    DOM Core be clearer?
+    <li><p>Otherwise, do nothing.
   </ol>
 </ol>
--->
+
 
 <h2>Commands</h2>
+<p>The <dfn title=execCommand()><code>execCommand(<var title>commandId</var>,
+<var title>showUI</var>, <var title>value</var>)</code></dfn> method on the
+<code data-anolis-spec=html>HTMLDocument</code> interface allows scripts to
+perform actions on the current selection or at the current caret position.
+Generally, these commands would be used to implement editor UI, for example
+having a "delete" button on a toolbar.
+
+<p>There are three variants to this method, with one, two, and three arguments
+respectively. The <var title>showUI</var> and <var title>value</var>
+parameters, even if specified, are ignored except where otherwise stated.
+
+<p>When <code>execCommand()</code> is invoked, the user agent must run the
+following steps:
+
+<ol>
+  <li>Let <var title>selection</var> be the result of calling <code
+  data-anolis-spec=domrange
+  title=dom-Document-getSelection>getSelection()</code> on the <span
+  data-anolis-spec=domrange>context object</span>.
+
+  <li>For each <code data-anolis-spec=domrange>Range</code> associated with
+  <var title>selection</var>, in order, take the action from the list below
+  given by <var title>commandId</var>.
+</ol>
+
 <dl>
 <dt><code title><dfn title=command-bold>bold</dfn></code>
-<dd>The user agent must <span title="style a range">style each <code
-data-anolis-spec=domrange>Range</code></span> of the current <code
-data-anolis-spec=domrange>Selection</code> with <var title>property name</var>
-"font-weight", <var title>property value</var> "bold", and <var title>tag
-list</var> ["b", "strong"].
-
-<p class=XXX>Be clearer.  What's the "current Selection", and what does it mean
-to wrap "each Range"?  What if there are overlapping Ranges?
-
-<p class=XXX>Obviously this doesn't yet even attempt to include the case where
-it's already bold and the bold has to be removed.
+<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
+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"].
 </dl>
 
 
--- a/test/bold.html	Thu Feb 17 15:06:13 2011 -0700
+++ b/test/bold.html	Sun Feb 20 15:11:49 2011 -0700
@@ -1,6 +1,6 @@
 <!doctype html>
 <title>execCommand("bold") tests</title>
-<div id=log></div>
+<button onclick=bold()>Bold</button>
 <div contenteditable=true id=test>
 	<br> <br>
 	Some simple text<br>
@@ -32,8 +32,6 @@
 	<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>
 </div>
-<script src=support/testharness.js></script>
-<script src=support/testharnessreport.js></script>
 <script>
 "use strict";
 
@@ -83,16 +81,50 @@
 	return "font-weight";
 }
 
-function styleRange(range, propertyName, propertyValue, tagList) {
+
+function firstNode(range) {
+	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) {
+		return range.startContainer.childNodes[range.startOffset];
+	}
+
+	return range.startContainer;
+}
+
+function beginningElement(range) {
+	var first = firstNode(range);
+	if (first.nodeType == Node.ELEMENT_NODE) {
+		return first;
+	}
+
+	if (first.parentNode.nodeType == Node.ELEMENT_NODE) {
+		return first.parentNode;
+	}
+
+	return null;
+}
+
+function decomposeRange(range) {
+	// "Let start node, start offset, end node, and end offset be the start and
+	// end nodes and offsets of range, respectively."
 	var startNode = range.startContainer;
 	var startOffset = range.startOffset;
 	var endNode = range.endContainer;
 	var endOffset = range.endOffset;
 
-	var doc = startNode.ownerDocument;
-
+	// "If start node or end node is not an Element, Text,
+	// ProcessingInstruction, or Comment node, or is not an Element and has no
+	// parent, abort these steps."
 	// Skip the sanity check about node types/detached non-elements
 
+	// "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."
 	if (startNode.nodeType == Node.TEXT_NODE
 	&& startOffset != 0
 	&& startOffset != startNode.data.length) {
@@ -100,6 +132,10 @@
 		startOffset = 0;
 	}
 
+	// "If end node is a Text node and end offset is neither 0 nor the length
+	// of end node, run splitText(end offset) on end node and set end node to
+	// the previous sibling of the returned node. Set end offset to the length
+	// of the new end node."
 	if (endNode.nodeType == Node.TEXT_NODE
 	&& endOffset != 0
 	&& endOffset != endNode.data.length) {
@@ -107,150 +143,223 @@
 		endOffset = endNode.data.length;
 	}
 
-	var nodeList = [];
-
 	var node;
+	// "If start node is an Element with at least one child, let node be the
+	// child of start node with index start offset."
 	if (startNode.nodeType == Node.ELEMENT_NODE
 	&& startNode.hasChildNodes()) {
-		node = nextNode(startNode.childNodes[startOffset]);
-	} else if (
-		(startNode.nodeType == Node.TEXT_NODE && startOffset == 0)
-		|| (startNode.nodeType == Node.ELEMENT_NODE && !startNode.hasChildNodes())
-	) {
+		node = startNode.childNodes[startOffset];
+	// "Otherwise, if start node is a Text node and start offset is its length,
+	// let node be the first Node after start node in tree order."
+	} else if (startNode.nodeType == Node.TEXT_NODE
+	&& startOffset == startNode.data.length) {
+		node = nextNode(startNode);
+	// "Otherwise, let node be start node."
+	} else {
 		node = startNode;
-	} else {
-		node = nextNode(startNode);
 	}
 
 	var end;
-	if (endNode.nodeType == Node.ELEMENT_NODE
-	&& endNode.hasChildNodes()) {
-		if (endOffset == endNode.childNodes.length) {
-			end = endNode.lastChild;
-		} else {
-			end = previousNode(endNode.childNodes[endOffset]);
-		}
-	} else if (
-		(endNode.nodeType == Node.TEXT_NODE && endOffset == endNode.data.length)
-		|| (endNode.nodeType == Node.ELEMENT_NODE && !endNode.hasChildNodes())
-	) {
+	// "If end node is an Element and end offset is not 0, let end be the child
+	// of end node with index end offset − 1."
+	if (endNode.nodeType == Node.ELEMENT_NODE && endOffset != 0) {
+		end = endNode.childNodes[endOffset - 1];
+	// "Otherwise, if end offset is 0, let end be the first Node before end
+	// node in tree order."
+	} else if (endOffset == 0) {
+		end = previousNode(endNode);
+	// "Otherwise, let end be end node."
+	} else {
 		end = endNode;
-	} else {
-		end = previousNode(endNode);
 	}
 
+	// "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)) {
+		// "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);
 	}
 
-	// FIXME what if nodeList.length == 0?
-	for (var i = 0; i < nodeList.length; i++) {
-		node = nodeList[i];
-		if (node.parentElement) {
-			var replace = true;
-			for (var j = 0; j < node.parentElement.childNodes.length; j++) {
-				if (node.parentElement.childNodes[j] != nodeList[i + j]) {
-					replace = false;
-					break;
-				}
-			}
-			if (replace) {
-				nodeList = nodeList.slice(0, i)
-					.concat(node.parentElement)
-					.concat(nodeList.slice(i + j));
-				continue;
-			}
+	return nodeList;
+}
+
+function unstyleElement(element, propertyName, tagList) {
+	// "If either
+	//
+	// * element is an HTML element with name either "span" or in tag list, and
+	//   it has only a single attribute, and that attribute is named "style",
+	//   and that style attribute sets only the CSS property property name; or
+	//
+	// * element is an HTML element with name in tag list and it has no
+	//   attributes,
+	//
+	// then:"
+	if (
+		(element.namespaceURI == htmlNamespace
+		&& (element.nodeName == "SPAN" || tagList.indexOf(element.nodeName.toLowerCase()) != -1)
+		&& element.attributes.length == 1
+		&& element.attributes[0].localName == "style"
+		&& element.style.length == 1
+		&& element.style.item(0) == convertProperty(propertyName)
+		)
+		||
+		(element.namespaceURI == htmlNamespace
+		&& 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.hasChildNodes()) {
+			element.parentNode.insertBefore(element.childNodes[0], element);
 		}
+		// "Remove element."
+		element.parentNode.removeChild(element);
+		// "Abort this algorithm."
+		return;
 	}
 
+	// "Unset the CSS property property name of element."
+	element.style[propertyName] = '';
+	if (element.getAttribute("style") == "") {
+		element.removeAttribute("style");
+	}
+
+	// "If element is an HTML element with name in tag list:"
+	if (element.namespaceURI == htmlNamespace
+	&& tagList.indexOf(element.tagName.toLowerCase()) != -1) {
+		// "Let new element be a new HTML element with name "span", with the
+		// same attributes and ownerDocument as element."
+		var newElement = element.ownerDocument.createElement("span");
+		for (var j = 0; j < element.attributes.length; j++) {
+			// FIXME: Namespaces?
+			newElement.setAttribute(element.attributes[j].localName, element.attributes[j].value);
+		}
+
+		// "Append new element to element's parent as the previous sibling of
+		// 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.hasChildNodes()) {
+			newElement.appendChild(element.childNodes[0]);
+		}
+		// "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);
+	}
+}
+
+function styleRange(range, propertyName, propertyValue, tagList) {
+	// "Let node list be the result of decomposing range."
+	var nodeList = decomposeRange(range);
+
+	// "For each node in node list, in tree order:"
 	for (var i = 0; i < nodeList.length; i++) {
-		node = nodeList[i];
+		var node = nodeList[i];
+		// "If node is an Element:"
 		if (node.nodeType == Node.ELEMENT_NODE) {
+			// "If node is an HTML element with name in tag list, unset the CSS
+			// property property name of node. Otherwise, set the CSS property
+			// property name of node to property value."
 			if (node.namespaceURI == htmlNamespace
 			&& tagList.indexOf(node.tagName.toLowerCase()) != -1) {
 				node.style[propertyName] = '';
+				if (node.getAttribute("style") == "") {
+					node.removeAttribute("style");
+				}
 			} else {
 				node.style[propertyName] = propertyValue;
 			}
 
-			var desc = nextNode(node);
-			var stop = nextNodeDescendants(node);
-			while (true) {
-				if (desc == stop) {
-					break;
-				}
-
-				if (desc.nodeType != Node.ELEMENT_NODE) {
-					desc = nextNode(desc);
-					continue;
-				}
+			// "Unstyle each Element descendant of node."
+			unstyleElementDescendants(node, 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]);
 
-				if (
-					(desc.namespaceURI == htmlNamespace
-					&& (desc.nodeName == "SPAN" || tagList.indexOf(desc.nodeName.toLowerCase()) != -1)
-					&& desc.attributes.length == 1
-					//&& desc.attributes[0].namespaceURI == htmlNamespace
-					&& desc.attributes[0].localName == "style"
-					&& desc.style.length == 1
-					&& desc.style.item(0) == convertProperty(propertyName)
-					)
-					||
-					(desc.namespaceURI == htmlNamespace
-					&& tagList.indexOf(desc.nodeName.toLowerCase()) != -1
-					&& desc.attributes.length == 0)
-				) {
-					var oldDesc = desc;
-					desc = nextNode(desc);
-					while (oldDesc.hasChildNodes()) {
-						oldDesc.parentNode.insertBefore(oldDesc.childNodes[0], oldDesc);
-					}
-					oldDesc.parentNode.removeChild(oldDesc);
-					continue;
-				}
-				desc.style[propertyName] = '';
-				if (desc.getAttribute("style") == "") {
-					desc.removeAttribute("style");
-				}
-				if (desc.namespaceURI == htmlNamespace
-				&& tagList.indexOf(desc.tagName.toLowerCase()) != -1) {
-					var newDesc = doc.createElement("span");
-					for (var j = 0; j < desc.attributes.length; j++) {
-						// Sick of playing the namespace game, assume
-						// everything is in HTML.
-						newDesc.setAttribute(desc.attributes[j].localName, desc.attributes[j].value);
-					}
-					desc.parentNode.insertBefore(newDesc, desc);
-					while (desc.hasChildNodes()) {
-						newDesc.appendChild(desc.childNodes[0]);
-					}
-					desc.parentNode.removeChild(desc);
-				}
-				desc = nextNode(desc);
-			}
-			continue;
+			// "Append new parent to node's parent as the previous sibling of
+			// node."
+			node.parentNode.insertBefore(newParent, node);
+
+			// "Append node to new parent as its last child."
+			newParent.appendChild(node);
 		}
-
-		if (node.previousSibling
-		&& node.previousSibling.nodeType == Node.ELEMENT_NODE
-		&& node.previousSibling.namespaceURI == htmlNamespace
-		&& node.previousSibling.tagName == tagList[0].toUpperCase()) {
-			node.previousSibling.appendChild(node);
-			continue;
-		}
-
-		if (node.nodeType != Node.TEXT_NODE
-		|| /^[ \t\n\f\r]*$/.test(node.data)) {
-			continue;
-		}
-
-		var newParent = doc.createElement(tagList[0]);
-		node.parentNode.insertBefore(newParent, node);
-		newParent.appendChild(node);
+		// "Otherwise, do nothing."
 	}
 }
 
-var range = document.createRange();
-range.selectNodeContents(document.getElementsByTagName("div")[1]);
-styleRange(range, "fontWeight", "bold", ["b", "strong"]);
+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++) {
+		// "If node is an Element:"
+		if (node.nodeType == Node.ELEMENT_NODE) {
+			// "Unstyle node."
+			unstyleElement(node, propertyName, tagList);
+
+			// "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;
+			}
+
+			// "Unstyle each Element descendant of node."
+			unstyleElementDescendants(node, 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) {
+			// "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;
+
+			// "Insert new parent as node's previous sibling."
+			node.parentNode.insertBefore(newParent, node);
+
+			// "Append node to new parent as its child."
+			newParent.appendChild(node);
+		}
+		// "Otherwise, do nothing."
+	}
+}
+
+function bold() {
+	var selection = getSelection();
+	for (var i = 0; i < selection.rangeCount; i++) {
+		if (getComputedStyle(beginningElement(selection.getRangeAt(i))).fontWeight != "bold") {
+			styleRange(selection.getRangeAt(i), "fontWeight", "bold", ["b", "strong"]);
+		} else {
+			styleRange(selection.getRangeAt(i), "fontWeight", "normal", ["b", "strong"]);
+		}
+	}
+}
 </script>
--- a/test/editcommands.html	Thu Feb 17 15:06:13 2011 -0700
+++ b/test/editcommands.html	Sun Feb 20 15:11:49 2011 -0700
@@ -1,40 +1,10 @@
 <!doctype html>
 <title>execCommand() tests</title>
+<button onclick="document.execCommand('bold', false, null)">Bold</button>
 <div contenteditable=true>
-	<br> <br>
-	Some simple text<br>
-	<span>Some more text</span><br>
-	<b>Some more text</b><br>
-	<strong>Some more text</strong><br>
-	<span style=font-weight:bold>Some more text</span><br>
-	<span style=font-weight:bolder>Some more text</span><br>
-	<span style=font-weight:lighter>Some more text</span><br>
-	<span style=font-weight:900>Some more text</span><br>
-	<span style=font-weight:100>Some more text</span><br>
-	<i>Some more text</i><br>
-	<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>
-	<p>Some simple text
-	<p><span>Some more text</span>
-	<p><b>Some more text</b>
-	<p><strong>Some more text</strong>
-	<p><span style=font-weight:bold>Some more text</span>
-	<p><span style=font-weight:bolder>Some more text</span>
-	<p><span style=font-weight:lighter>Some more text</span>
-	<p><span style=font-weight:900>Some more text</span>
-	<p><span style=font-weight:100>Some more text</span>
-	<p><i>Some more text</i>
-	<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>Abcdef</b>
+	<p><strong>Abcdef</strong>
+	<p><span style=font-weight:bold>Abcdef</span>
+	<p><span style=font-weight:bolder>Abcdef</span>
+	<p><span style=font-weight:900>Abcdef</span>
 </div>
-<script>
-var selection = getSelection();
-selection.selectAllChildren(document.querySelector("div"));
-document.execCommand("bold", false, null);
-//document.execCommand("bold", false, null);
-selection.removeAllRanges();
-</script>
--- a/xrefs.json	Thu Feb 17 15:06:13 2011 -0700
+++ b/xrefs.json	Sun Feb 20 15:11:49 2011 -0700
@@ -1,5 +1,11 @@
 {
+  "beginning element": "beginning-element",
   "command-bold": "command-bold",
+  "decompose a range": "decompose-a-range",
+  "execcommand()": "execcommand()",
+  "first node": "first-node",
   "html element with name": "html-element-with-name",
-  "style a range": "style-a-range"
+  "style a range": "style-a-range",
+  "unstyle a range": "unstyle-a-range",
+  "unstyle an element": "unstyle-an-element"
 }