Commit indiscriminately at end of day
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Tue, 22 Feb 2011 15:15:44 -0700
changeset 7 c07184a3d893
parent 6 2f1ebe466678
child 8 641c267313c3
Commit indiscriminately at end of day
editcommands.html
implementation.html
preprocess
source.html
xrefs.json
--- a/editcommands.html	Mon Feb 21 14:54:12 2011 -0700
+++ b/editcommands.html	Tue Feb 22 15:15:44 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-21-february-2011>Work in Progress &mdash; Last Update 21 February 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-22-february-2011>Work in Progress &mdash; Last Update 22 February 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg+spec@aryeh.name&gt;
@@ -110,6 +110,29 @@
 whose parent is a document, or the child of a document fragment, or whatever.
 I'm ignoring those cases for now.)
 
+<p>The <dfn id=active-range>active range</dfn> of a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#document><code class=external data-anolis-spec=domcore>Document</code></a> is the value returned by the
+following algorithm:
+
+<ol>
+  <li><p>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 href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#document><code class=external data-anolis-spec=domcore>Document</code></a>.
+
+  <li><p>If there are no <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>s associated with <var title="">selection</var>,
+  return null.
+
+  <li><p>Let <var title="">start</var> be the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point title=concept-boundary-point>boundary point</a> with the earliest
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-bp-position title=concept-bp-position>position</a> among all of <var title="">selection</var>'s <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>s'
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>starts</a>.
+
+  <li><p>Return the last <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> in <var title="">selection</var> whose <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
+  is <var title="">start</var>.
+  <!-- This is what Firefox seems to do, no reason to change it . . . -->
+
+  <p class=note>In user agents that support only one <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> per
+  <a href=http://html5.org/specs/dom-range.html#selection><code class=external data-anolis-spec=domrange>Selection</code></a>, <var title="">range</var> will simply be the only <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> associated
+  to <var title="">selection</var>.
+</ol>
+
 
 <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.
@@ -296,8 +319,9 @@
 
 <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>.
+run the following steps.  There are four inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, a
+nonempty list of strings <var title="">tag list</var>, and a string
+<var title="">commandId</var>.
 
 <div class=XXX>
 <p>This description tries to keep the algorithm as simple as possible at the
@@ -334,6 +358,15 @@
       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>If the <a href=#state>state</a> of <var title="">commandId</var> on
+      <var title="">node</var> is false, set the CSS property <var title="">property name</var>
+      of <var title="">node</var> to <var title="">property value</var>.
+      <!-- This fixes cases where the element won't actually create the desired
+      style.  E.g., <span style=font-weight:lighter><b>Foo</b></span> will not
+      actually bold "Foo" (because the default style for <b> is font-weight:
+      bolder), so we do <b style=font-weight:bold> instead.  Likewise when we
+      do similarly a bit later on in this algorithm. -->
+
       <li><p>Let <var title="">element children</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> children
       of <var title="">node</var>.
 
@@ -357,11 +390,19 @@
       other text nodes. -->
       <li><p>If the previous sibling of <var title="">node</var> is an <a href=#html-element>HTML
       element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> in <var title="">tag list</var> with no
-      attributes, let <var title="">new parent</var> equal the previous sibling of
-      <var title="">node</var>.
+      attributes, and the <a href=#state>state</a> of <var title="">commandId</var> on
+      <var title="">node</var> is true, let <var title="">new parent</var> equal the previous
+      sibling of <var title="">node</var>.
 
       <li><p>Otherwise, let <var title="">new parent</var> be a new <a href=#html-element>HTML
-      element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> equal to the first string in <var title="">tag list</var>, with no attributes, and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as <var title="">node</var>.  Append <var title="">new parent</var> to <var title="">node</var>'s parent as the previous sibling of <var title="">node</var>.
+      element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> equal to the first string in <var title="">tag
+      list</var>, with no attributes, and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as
+      <var title="">node</var>.  Append <var title="">new parent</var> to <var title="">node</var>'s
+      parent as the previous sibling of <var title="">node</var>.
+
+      <li><p>If the <a href=#state>state</a> of <var title="">commandId</var> on <var title="">new
+      parent</var> is false, set the CSS property <var title="">property name</var> of
+      <var title="">new parent</var> to <var title="">property value</var>.
 
       <li><p>Append <var title="">node</var> to <var title="">new parent</var> as
       its last child.
@@ -382,8 +423,9 @@
 
 <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>.
+run the following steps.  There are four inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, a
+possibly empty list of strings <var title="">tag list</var>, and a string
+<var title="">commandId</var>.
 
 <ol>
   <li><p>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose
@@ -456,48 +498,65 @@
 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:
+<p>When <a href=#execcommand()><code>execCommand()</code></a> is invoked, the user agent must take the
+action from the list below given by <var title="">commandId</var> on the
+<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <a href=#active-range>active range</a>.  If no action is given or if
+there is no <a href=#active-range>active range</a>, do nothing.
 
-<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>.
+<p>The <dfn id=querycommandstate() title=queryCommandState()><code>queryCommandState(<var title="">commandId</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 must
+return the <a href=#state>state</a> of <var title="">commandId</var> on the
+<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <a href=#active-range>active range</a>.  If there is no <a href=#active-range>active
+range</a>, return false.
+<!-- Gecko throws an exception if there are no ranges in the selection, but
+other engines seem to just return false, which seems like nicer behavior
+anyway. -->
 
-  <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>
+<p>The <dfn id=state>state</dfn> of a command <var title="">commandId</var> on a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is
+given by the list below.  If <var title="">commandId</var> is not on the list, its
+<a href=#state>state</a> is false on any <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.  The <a href=#state>state</a> of a
+command on 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> <var title="">node</var> is the <a href=#state>state</a> of that command
+on the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> (<var title="">node</var>, 0), <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+(<var title="">node</var>, <var title="">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>), and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-root title=concept-range-root>root</a> equal to
+<var title="">node</var>'s <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>.
 
 <dl>
 <dt><code title=""><dfn id=command-bold title=command-bold>bold</dfn></code>
-<dd><p>If the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-weight
-with computed value not equal to "bold" (or less than 700), the user agent must
-<a href=#style-a-range title="style a range">style the <code class=external data-anolis-spec=domrange>Range</code></a> with <var title="">property
-name</var> "font-weight", <var title="">property value</var> "bold", and <var title="">tag list</var> ["b", "strong"].  Otherwise, it must <a href=#unstyle-a-range title="unstyle
-a range">unstyle it</a> with <var title="">property name</var> "font-weight",
-<var title="">property value</var> "normal", and <var title="">tag list</var> ["b",
-"strong"].
 
-<p class=XXX>b has font-weight: bolder, not font-weight: bold.  This produces
-unexpected behavior if there are font-weight: lighters or something thrown
-around.  Maybe that's not worth worrying about.
+<dd><p><strong>Action</strong>: If the state of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> for this command
+is false, 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"].
+
+<dd><p><strong>State</strong>: True 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 less than 700, otherwise false.
+
 
 <dt><code title=""><dfn id=command-italic title=command-italic>italic</dfn></code>
-<dd><p>If the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-style
-with computed value not equal to "italic" or "oblique", 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-style", <var title="">property value</var> "italic", and <var title="">tag list</var> ["i", "em"].  Otherwise, it must <a href=#unstyle-a-range title="unstyle
-a range">unstyle it</a> with <var title="">property name</var> "font-style",
-<var title="">property value</var> "normal", and <var title="">tag list</var> ["i",
+
+<dd><p><strong>Action</strong>: If the of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> for this command is
+false, 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-style", <var title="">property
+value</var> "italic", and <var title="">tag list</var> ["i", "em"].  Otherwise, it must
+<a href=#unstyle-a-range title="unstyle a range">unstyle it</a> with <var title="">property name</var>
+"font-style", <var title="">property value</var> "normal", and <var title="">tag list</var> ["i",
 "em"].
 
+<dd><p><strong>State</strong>: True 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-style with computed value "italic" or "oblique", otherwise
+false.
+
+
 <dt><code title=""><dfn id=command-underline title=command-underline>underline</dfn></code>
-<dd><p>If the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has
-text-decoration with a computed value that does not include "underline", 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> "text-decoration", <var title="">property
-value</var> "underline", and <var title="">tag list</var> ["u"].  Otherwise, it
-must <a href=#unstyle-a-range title="unstyle a range">unstyle it</a> with <var title="">property
-name</var> "text-decoration", <var title="">property value</var> "none", and <var title="">tag list</var> ["u"].
+
+<dd class=XXX><p><strong>Action</strong>: ???  This is totally unreasonable,
+because CSS text-decoration is a nightmare.  Styling is easy, unstyling is only
+possible through massive hacks.
+
+<dd class=XXX><p><strong>State</strong>: ...
 
 <p class=XXX>This is wrong, it will clear strikethrough and overline.  Also,
 computed style isn't useful, because text-decoration doesn't inherit.
--- a/implementation.html	Mon Feb 21 14:54:12 2011 -0700
+++ b/implementation.html	Tue Feb 22 15:15:44 2011 -0700
@@ -1,14 +1,18 @@
 <!doctype html>
 <title>execCommand() experimental implementation</title>
 <p>Spec:
-<button onclick=bold()><b>B</b></button>
-<button onclick=italic()><i>I</i></button>
-<button onclick=underline()><u>U</u></button>
+<button onclick="myExecCommand('bold')"><b>B</b></button>
+<button onclick="myExecCommand('italic')"><i>I</i></button>
+<button onclick="alert(myQueryCommandState('bold'))">B?</button>
+<button onclick="alert(myQueryCommandState('italic'))">I?</button>
 
 <p>Actual execCommand():
 <button onclick="execCommand('bold', false, null)"><b>B</b></button>
 <button onclick="execCommand('italic', false, null)"><i>I</i></button>
 <button onclick="execCommand('underline', false, null)"><u>U</u></button>
+<button onclick="alert(queryCommandState('bold'))">B?</button>
+<button onclick="alert(queryCommandState('italic'))">I?</button>
+<button onclick="alert(queryCommandState('underline'))">U?</button>
 <div contenteditable=true id=test>
 	<br> <br>
 	Some simple text<br>
@@ -153,6 +157,42 @@
 	return null;
 }
 
+function activeRange(doc) {
+	// "Let selection be the result of calling getSelection() on the Document."
+	var selection = doc.getSelection();
+
+	// "If there are no Ranges associated with selection, return null."
+	if (selection.rangeCount == 0) {
+		return null;
+	}
+
+	// "Let start be the boundary point with the earliest position among all of
+	// selection's Ranges' starts."
+	var startNode = null;
+	var startOffset = null;
+	for (var i = 0; i < selection.rangeCount; i++) {
+		if (startNode === null) {
+			startNode = selection.getRangeAt(i).startContainer;
+			startOffset = selection.getRangeAt(i).startOffset;
+			continue;
+		}
+		var testRange = doc.createRange();
+		testRange.setStart(startNode, startOffset);
+		if (testRange.compareBoundaryPoints(Range.START_TO_START, selection.getRangeAt(i)) < 0) {
+			startNode = selection.getRangeAt(i).startContainer;
+			startOffset = selection.getRangeAt(i).startOffset;
+		}
+	}
+
+	// "Return the last Range in selection whose start is start."
+	for (var i = selection.rangeCount - 1; i >= 0; i--) {
+		if (selection.getRangeAt(i).startContainer == startNode
+		&& selection.getRangeAt(i).startOffset == startOffset) {
+			return selection.getRangeAt(i);
+		}
+	}
+}
+
 function decomposeRange(range) {
 	// "Let start node, start offset, end node, and end offset be the start and
 	// end nodes and offsets of range, respectively."
@@ -379,7 +419,7 @@
 	return children;
 }
 
-function styleRange(range, propertyName, propertyValue, tagList) {
+function styleRange(range, propertyName, propertyValue, tagList, commandId) {
 	// "Let node list be the result of decomposing range."
 	var nodeList = decomposeRange(range);
 
@@ -401,6 +441,12 @@
 				node.style[propertyName] = propertyValue;
 			}
 
+			// "If the state of commandId on node is false, set the CSS
+			// property property name of node to property value."
+			if (!getState(commandId, node)) {
+				node.style[propertyName] = propertyValue;
+			}
+
 			// "Let element children be the Element children of node."
 			var elementChildren = [];
 			for (var j = 0; j < node.childNodes.length; j++) {
@@ -417,13 +463,15 @@
 		} else if (node.nodeType == Node.TEXT_NODE) {
 			var newParent;
 			// "If the previous sibling of node is an HTML element with local
-			// name in tag list with no attributes, let new parent equal the
-			// previous sibling of node."
+			// name in tag list with no attributes, and the state of commandId
+			// on node is true, let new parent equal the previous sibling of
+			// node."
 			if (node.previousSibling
 			&& node.previousSibling.nodeType == Node.ELEMENT_NODE
 			&& node.previousSibling.namespaceURI == htmlNamespace
 			&& tagList.indexOf(node.previousSibling.tagName.toLowerCase()) != -1
-			&& node.previousSibling.attributes.length == 0) {
+			&& node.previousSibling.attributes.length == 0
+			&& getState(commandId, node.previousSibling)) {
 				newParent = node.previousSibling;
 			} else {
 				// "Otherwise, let new parent be a new HTML element with local
@@ -434,6 +482,12 @@
 				node.parentNode.insertBefore(newParent, node);
 			}
 
+			// "If the state of commandId on new parent is false, set the CSS
+			// property property name of new parent to property value."
+			if (!getState(commandId, newParent)) {
+				newParent.style[propertyName] = propertyValue;
+			}
+
 			// "Append node to new parent as its last child."
 			newParent.appendChild(node);
 		}
@@ -511,40 +565,64 @@
 	}
 }
 
-function bold() {
-	var selection = getSelection();
-	for (var i = 0; i < selection.rangeCount; i++) {
-		var fontWeight = getComputedStyle(beginningElement(selection.getRangeAt(i))).fontWeight;
-		if (fontWeight != "bold"
-		&& (!/^[0-9]+$/.test(fontWeight) || fontWeight < 700)) {
-			styleRange(selection.getRangeAt(i), "fontWeight", "bold", ["b", "strong"]);
+function myExecCommand(commandId, showUI, value) {
+	var range = activeRange(document);
+
+	if (!range) {
+		return;
+	}
+
+	switch (commandId) {
+		case "bold":
+		if (getState("bold", range)) {
+			unstyleRange(range, "fontWeight", ["normal", "400"], ["b", "strong"]);
 		} else {
-			unstyleRange(selection.getRangeAt(i), "fontWeight", ["normal", "400"], ["b", "strong"]);
+			styleRange(range, "fontWeight", "bold", ["b", "strong"], "bold");
 		}
+		break;
+
+		case "italic":
+		if (getState("italic", range)) {
+			unstyleRange(range, "fontStyle", ["normal"], ["i", "em"]);
+		} else {
+			styleRange(range, "fontStyle", "italic", ["i", "em"], "italic");
+		}
+		break;
+
+		default:
+		break;
 	}
 }
 
-function italic() {
-	var selection = getSelection();
-	for (var i = 0; i < selection.rangeCount; i++) {
-		var fontStyle = getComputedStyle(beginningElement(selection.getRangeAt(i))).fontStyle;
-		if (fontStyle != "italic" && fontStyle != "oblique") {
-			styleRange(selection.getRangeAt(i), "fontStyle", "italic", ["i", "em"]);
-		} else {
-			unstyleRange(selection.getRangeAt(i), "fontStyle", ["normal"], ["i", "em"]);
-		}
+function myQueryCommandState(commandId) {
+	var range = activeRange(document);
+
+	if (!range) {
+		return false;
 	}
+
+	return getState(commandId, range);
 }
 
-function underline() {
-	var selection = getSelection();
-	for (var i = 0; i < selection.rangeCount; i++) {
-		var textDecoration = getComputedStyle(beginningElement(selection.getRangeAt(i))).textDecoration;
-		if (!/ underline /.test(" " + textDecoration + " ")) {
-			styleRange(selection.getRangeAt(i), "textDecoration", "underline", ["u"]);
-		} else {
-			unstyleRange(selection.getRangeAt(i), "textDecoration", ["none"], ["u"]);
-		}
+function getState(commandId, range) {
+	if (range instanceof Node) {
+		var node = range;
+		range = node.ownerDocument.createRange();
+		range.selectNodeContents(node);
+	}
+
+	var style = getComputedStyle(beginningElement(range));
+
+	switch (commandId) {
+		case "bold":
+		return style.fontWeight == "bold"
+			|| (/^[0-9]+$/.test(style.fontWeight) && style.fontWeight >= 700);
+
+		case "italic":
+		return style.fontStyle == "italic" || style.fontStyle == "oblique";
+
+		default:
+		return false;
 	}
 }
 </script>
--- a/preprocess	Mon Feb 21 14:54:12 2011 -0700
+++ b/preprocess	Tue Feb 22 15:15:44 2011 -0700
@@ -2,28 +2,34 @@
 
 # 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>.
+# <var title> is also really pointless, although I should really get that fixed
+# in Anolis proper.
 
 replace = {
     "ancestor": "<span data-anolis-spec=domcore title=concept-ancestor-node>ancestor</span>",
-    "ancestors": "<span data-anolis-spec=domcore title=concept-ancestor-node>ancestors</span>",
+    "boundarypoint": "<span data-anolis-spec=domrange title=concept-boundary-point>boundary point</span>",
     "bpnode": "<span data-anolis-spec=domrange title=concept-boundary-point-node>node</span>",
-    "bpnodes": "<span data-anolis-spec=domrange title=concept-boundary-point-node>nodes</span>",
     "bpoffset": "<span data-anolis-spec=domrange title=concept-boundary-point-offset>offset</span>",
-    "bpoffsets": "<span data-anolis-spec=domrange title=concept-boundary-point-offset>offsets</span>",
+    "bpposition": "<span data-anolis-spec=domrange title=concept-bp-position>position</span>",
     "comment": "<code data-anolis-spec=domcore>Comment</code>",
+    "contextobject": "<span data-anolis-spec=domrange>context object</span>",
     "descendant": "<span data-anolis-spec=domcore title=concept-descendant-node>descendant</span>",
-    "descendants": "<span data-anolis-spec=domcore title=concept-descendant-node>descendants</span>",
+    "document": "<code data-anolis-spec=domcore>Document</code>",
     "element": "<code data-anolis-spec=domcore>Element</code>",
+    "getselection": "<code data-anolis-spec=domrange title=dom-Document-getSelection>getSelection()</code>",
     "htmlnamespace": "<span data-anolis-spec=domcore>HTML namespace</span>",
     "index": "<span data-anolis-spec=domrange title=concept-indexof>index</span>",
     "localname": "<span data-anolis-spec=domcore title=concept-element-local-name>local name</span>",
     "namespace": "<span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>",
     "node": "<code data-anolis-spec=domcore>Node</code>",
     "nodelength": "<span data-anolis-spec=domrange title=concept-node-length>length</span>",
+    "ownerdocument": "<code data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</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>",
+    "rangeroot": "<span data-anolis-spec=domrange title=concept-range-root>root</span>",
     "rangestart": "<span data-anolis-spec=domrange title=concept-range-start>start</span>",
+    "selection": "<code data-anolis-spec=domrange>Selection</code>",
     "text": "<code data-anolis-spec=domcore>Text</code>",
     "treeorder": "<span data-anolis-spec=domcore>tree order</span>",
 }
@@ -31,10 +37,14 @@
 s = open("source.html", "r").read()
 for key in replace:
     s = s.replace("[[" + key + "]]", replace[key])
+    # Plurals
+    s = s.replace("[[" + key + "s]]", replace[key].replace("</", "s</"))
 
 if "[[" in s:
     raise Exception("Something mistyped?  " + s[s.find("[["):s.rfind("]]") + 2])
 
+s = s.replace("<var>", "<var title>")
+
 f = open("intermediate.html", "w")
 f.write(s)
 f.close()
--- a/source.html	Mon Feb 21 14:54:12 2011 -0700
+++ b/source.html	Tue Feb 22 15:15:44 2011 -0700
@@ -98,6 +98,29 @@
 whose parent is a document, or the child of a document fragment, or whatever.
 I'm ignoring those cases for now.)
 
+<p>The <dfn>active range</dfn> of a [[document]] is the value returned by the
+following algorithm:
+
+<ol>
+  <li><p>Let <var>selection</var> be the result of calling [[getselection]] on
+  the [[document]].
+
+  <li><p>If there are no [[range]]s associated with <var>selection</var>,
+  return null.
+
+  <li><p>Let <var>start</var> be the [[boundarypoint]] with the earliest
+  [[bpposition]] among all of <var>selection</var>'s [[range]]s'
+  [[rangestarts]].
+
+  <li><p>Return the last [[range]] in <var>selection</var> whose [[rangestart]]
+  is <var>start</var>.
+  <!-- This is what Firefox seems to do, no reason to change it . . . -->
+
+  <p class=note>In user agents that support only one [[range]] per
+  [[selection]], <var>range</var> will simply be the only [[range]] associated
+  to <var>selection</var>.
+</ol>
+
 
 <h2>Decomposing a Range into Nodes</h2>
 <p>When a user agent is to <dfn>decompose a [[range]]</dfn> <var
@@ -304,9 +327,10 @@
 <h2>Styling a Range</h2>
 <p>When a user agent is to <dfn>style 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
-nonempty list of strings <var title>tag list</var>.
+run the following steps.  There are four inputs: a CSS property name <var
+title>property name</var>, a new value <var title>property value</var>, a
+nonempty list of strings <var title>tag list</var>, and a string
+<var>commandId</var>.
 
 <div class=XXX>
 <p>This description tries to keep the algorithm as simple as possible at the
@@ -348,6 +372,15 @@
       <var title>property name</var> of <var title>node</var> to <var
       title>property value</var>.
 
+      <li><p>If the <span>state</span> of <var>commandId</var> on
+      <var>node</var> is false, set the CSS property <var>property name</var>
+      of <var>node</var> to <var>property value</var>.
+      <!-- This fixes cases where the element won't actually create the desired
+      style.  E.g., <span style=font-weight:lighter><b>Foo</b></span> will not
+      actually bold "Foo" (because the default style for <b> is font-weight:
+      bolder), so we do <b style=font-weight:bold> instead.  Likewise when we
+      do similarly a bit later on in this algorithm. -->
+
       <li><p>Let <var title>element children</var> be the [[element]] children
       of <var title>node</var>.
 
@@ -370,19 +403,21 @@
       a lot: you get splitText() called when decomposing ranges, and unstyling
       elements can also dump their text node children into the DOM next to
       other text nodes. -->
-      <li><p>If the previous sibling of <var title>node</var> is an <span>HTML
-      element</span> with [[localname]] in <var title>tag list</var> with no
-      attributes, let <var title>new parent</var> equal the previous sibling of
-      <var title>node</var>.
+      <li><p>If the previous sibling of <var>node</var> is an <span>HTML
+      element</span> with [[localname]] in <var>tag list</var> with no
+      attributes, and the <span>state</span> of <var>commandId</var> on
+      <var>node</var> is true, let <var>new parent</var> equal the previous
+      sibling of <var>node</var>.
 
-      <li><p>Otherwise, let <var title>new parent</var> be a new <span>HTML
-      element</span> with [[localname]] equal to the first string in <var
-      title>tag list</var>, with no attributes, and <code
-      data-anolis-spec=domcore
-      title=dom-Node-ownerDocument>ownerDocument</code> the same as <var
-      title>node</var>.  Append <var title>new parent</var> to <var
-      title>node</var>'s parent as the previous sibling of <var
-      title>node</var>.
+      <li><p>Otherwise, let <var>new parent</var> be a new <span>HTML
+      element</span> with [[localname]] equal to the first string in <var>tag
+      list</var>, with no attributes, and [[ownerdocument]] the same as
+      <var>node</var>.  Append <var>new parent</var> to <var>node</var>'s
+      parent as the previous sibling of <var>node</var>.
+
+      <li><p>If the <span>state</span> of <var>commandId</var> on <var>new
+      parent</var> is false, set the CSS property <var>property name</var> of
+      <var>new parent</var> to <var>property value</var>.
 
       <li><p>Append <var title>node</var> to <var title>new parent</var> as
       its last child.
@@ -404,9 +439,10 @@
 <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>.
+run the following steps.  There are four inputs: a CSS property name <var
+title>property name</var>, a new value <var title>property value</var>, a
+possibly empty list of strings <var title>tag list</var>, and a string
+<var>commandId</var>.
 
 <ol>
   <li><p>Let <var title>node list</var> be the result of <span title="decompose
@@ -492,54 +528,66 @@
 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:
+<p>When <code>execCommand()</code> is invoked, the user agent must take the
+action from the list below given by <var>commandId</var> on the
+[[contextobject]]'s <span>active range</span>.  If no action is given or if
+there is no <span>active range</span>, do nothing.
 
-<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>.
+<p>The <dfn
+title=queryCommandState()><code>queryCommandState(<var>commandId</var>)</code></dfn>
+method on the <code data-anolis-spec=html>HTMLDocument</code> interface must
+return the <span>state</span> of <var>commandId</var> on the
+[[contextobject]]'s <span>active range</span>.  If there is no <span>active
+range</span>, return false.
+<!-- Gecko throws an exception if there are no ranges in the selection, but
+other engines seem to just return false, which seems like nicer behavior
+anyway. -->
 
-  <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>
+<p>The <dfn>state</dfn> of a command <var>commandId</var> on a [[range]] is
+given by the list below.  If <var>commandId</var> is not on the list, its
+<span>state</span> is false on any [[range]].  The <span>state</span> of a
+command on a [[node]] <var>node</var> is the <span>state</span> of that command
+on the [[range]] with [[rangestart]] (<var>node</var>, 0), [[rangeend]]
+(<var>node</var>, <var>node</var>'s [[nodelength]]), and [[rangeroot]] equal to
+<var>node</var>'s [[ownerdocument]].
 
 <dl>
 <dt><code title><dfn title=command-bold>bold</dfn></code>
-<dd><p>If the <span>beginning element</span> of the [[range]] has font-weight
-with computed value not equal to "bold" (or less than 700), the user agent must
-<span title="style a range">style the [[range]]</span> with <var title>property
-name</var> "font-weight", <var title>property value</var> "bold", and <var
-title>tag list</var> ["b", "strong"].  Otherwise, it must <span title="unstyle
-a range">unstyle it</span> with <var title>property name</var> "font-weight",
-<var title>property value</var> "normal", and <var title>tag list</var> ["b",
-"strong"].
 
-<p class=XXX>b has font-weight: bolder, not font-weight: bold.  This produces
-unexpected behavior if there are font-weight: lighters or something thrown
-around.  Maybe that's not worth worrying about.
+<dd><p><strong>Action</strong>: If the state of the [[range]] for this command
+is false, the user agent must <span title="style a range">style the
+[[range]]</span> with <var>property name</var> "font-weight", <var>property
+value</var> "bold", and <var>tag list</var> ["b", "strong"].  Otherwise, it
+must <span title="unstyle a range">unstyle it</span> with <var>property
+name</var> "font-weight", <var>property value</var> "normal", and <var>tag
+list</var> ["b", "strong"].
+
+<dd><p><strong>State</strong>: True if the <span>beginning element</span> of the
+[[range]] has font-weight with computed value less than 700, otherwise false.
+
 
 <dt><code title><dfn title=command-italic>italic</dfn></code>
-<dd><p>If the <span>beginning element</span> of the [[range]] has font-style
-with computed value not equal to "italic" or "oblique", the user agent must
-<span title="style a range">style the [[range]]</span> with <var title>property
-name</var> "font-style", <var title>property value</var> "italic", and <var
-title>tag list</var> ["i", "em"].  Otherwise, it must <span title="unstyle
-a range">unstyle it</span> with <var title>property name</var> "font-style",
-<var title>property value</var> "normal", and <var title>tag list</var> ["i",
+
+<dd><p><strong>Action</strong>: If the of the [[range]] for this command is
+false, the user agent must <span title="style a range">style the
+[[range]]</span> with <var>property name</var> "font-style", <var>property
+value</var> "italic", and <var>tag list</var> ["i", "em"].  Otherwise, it must
+<span title="unstyle a range">unstyle it</span> with <var>property name</var>
+"font-style", <var>property value</var> "normal", and <var>tag list</var> ["i",
 "em"].
 
+<dd><p><strong>State</strong>: True if the <span>beginning element</span> of the
+[[range]] has font-style with computed value "italic" or "oblique", otherwise
+false.
+
+
 <dt><code title><dfn title=command-underline>underline</dfn></code>
-<dd><p>If the <span>beginning element</span> of the [[range]] has
-text-decoration with a computed value that does not include "underline", the
-user agent must <span title="style a range">style the [[range]]</span> with
-<var title>property name</var> "text-decoration", <var title>property
-value</var> "underline", and <var title>tag list</var> ["u"].  Otherwise, it
-must <span title="unstyle a range">unstyle it</span> with <var title>property
-name</var> "text-decoration", <var title>property value</var> "none", and <var
-title>tag list</var> ["u"].
+
+<dd class=XXX><p><strong>Action</strong>: ???  This is totally unreasonable,
+because CSS text-decoration is a nightmare.  Styling is easy, unstyling is only
+possible through massive hacks.
+
+<dd class=XXX><p><strong>State</strong>: ...
 
 <p class=XXX>This is wrong, it will clear strikethrough and overline.  Also,
 computed style isn't useful, because text-decoration doesn't inherit.
--- a/xrefs.json	Mon Feb 21 14:54:12 2011 -0700
+++ b/xrefs.json	Tue Feb 22 15:15:44 2011 -0700
@@ -1,4 +1,5 @@
 {
+  "active range": "active-range",
   "beginning element": "beginning-element",
   "command-bold": "command-bold",
   "command-italic": "command-italic",
@@ -7,6 +8,8 @@
   "execcommand()": "execcommand()",
   "first node": "first-node",
   "html element": "html-element",
+  "querycommandstate()": "querycommandstate()",
+  "state": "state",
   "style a range": "style-a-range",
   "unstyle a range": "unstyle-a-range",
   "unstyle an element": "unstyle-an-element"