--- a/autoimplementation.html Thu Apr 28 12:54:21 2011 -0600
+++ b/autoimplementation.html Thu Apr 28 15:01:23 2011 -0600
@@ -513,6 +513,12 @@
'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+
+ // Spec:
+ '<blockquote style="margin: 0 40px"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
],
inserthorizontalrule: [
'foo[]bar',
@@ -605,6 +611,39 @@
'foo [bar <i>baz] qoz</i> quz sic',
'foo bar <i>baz [qoz</i> quz] sic',
],
+ outdent: [
+ // These mimic existing indentation in various browsers, to see how
+ // they cope with outdenting various things. This is Gecko non-CSS and
+ // Opera:
+ '<blockquote><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+
+ // IE:
+ '<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+
+ // Firefox CSS mode:
+ '<p style="margin-left: 40px">foo[bar]</p><p style="margin-left: 40px">baz</p><p>extra',
+ '<p style="margin-left: 40px">foo[bar</p><p style="margin-left: 40px">b]az</p><p>extra',
+ '<p style="margin-left: 40px">foo[bar]</p><p>baz</p><p>extra',
+ '<p style="margin-left: 40px">foo[bar</p><p>b]az</p><p>extra',
+
+ // WebKit:
+ '<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+
+ // Spec:
+ '<blockquote style="margin: 0 40px"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote style="margin: 0 40px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+ ],
removeformat: [
'foo[]bar',
'<span>foo</span>{}<span>bar</span>',
@@ -1241,6 +1280,10 @@
var normalizedSpecCell = tr.childNodes[1].lastChild.firstChild.textContent
.replace(/[[\]{}]/g, "")
.replace(/ style="margin: 0 40px"/g, "")
+ .replace(/ class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;"/g, '')
+ .replace(/ style="margin-right: 0px;" dir="ltr"/g, '')
+ .replace(/ style="margin-left: 0px;" dir="rtl"/g, '')
+ .replace(/ style="margin-(left|right): 40px;"/g, '')
.replace(/: /g, ":")
.replace(/;? ?"/g, '"')
.replace(/<(\/?)strong/g, '<$1b')
@@ -1249,6 +1292,7 @@
.replace(/#[0-9a-fA-F]{6}/g, function(match) { return match.toUpperCase(); });
var normalizedBrowserCell = tr.childNodes[2].lastChild.firstChild.textContent
.replace(/[[\]{}]/g, "")
+ .replace(/ style="margin: 0 40px"/g, "")
.replace(/ class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;"/g, '')
.replace(/ style="margin-right: 0px;" dir="ltr"/g, '')
.replace(/ style="margin-left: 0px;" dir="rtl"/g, '')
--- a/editcommands.html Thu Apr 28 12:54:21 2011 -0600
+++ b/editcommands.html Thu Apr 28 15:01:23 2011 -0600
@@ -59,11 +59,12 @@
<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=#decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a range into nodes</a></li>
- <li><a href="#clearing-an-element's-value"><span class=secno>5 </span>Clearing an element's value</a></li>
- <li><a href=#pushing-down-values><span class=secno>6 </span>Pushing down values</a></li>
- <li><a href=#forcing-the-value-of-a-node><span class=secno>7 </span>Forcing the value of a node</a></li>
- <li><a href=#setting-the-value-of-a-node><span class=secno>8 </span>Setting the value of a node</a></li>
- <li><a href=#commands><span class=secno>9 </span>Commands</a></li>
+ <li><a href=#block-extending-a-range><span class=secno>5 </span>Block-extending a range</a></li>
+ <li><a href="#clearing-an-element's-value"><span class=secno>6 </span>Clearing an element's value</a></li>
+ <li><a href=#pushing-down-values><span class=secno>7 </span>Pushing down values</a></li>
+ <li><a href=#forcing-the-value-of-a-node><span class=secno>8 </span>Forcing the value of a node</a></li>
+ <li><a href=#setting-the-value-of-a-node><span class=secno>9 </span>Setting the value of a node</a></li>
+ <li><a href=#commands><span class=secno>10 </span>Commands</a></li>
<li><a class=no-num href=#references>References</a></li>
<li><a class=no-num href=#acknowledgements>Acknowledgements</a></ol>
<!--end-toc-->
@@ -181,6 +182,9 @@
<p>An <dfn id=html-element>HTML element</dfn> is an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> whose <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-namespace title=concept-element-namespace>namespace</a> is the
<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#html-namespace>HTML namespace</a>.
+<p>An <dfn id=inline-node>inline node</dfn> is either a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node, or an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> whose
+"display" property computes to "inline", "inline-block", or "inline-table".
+
<p>Something is <dfn id=editable>editable</dfn> if either it is an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> with a <code class=external data-anolis-spec=html title=attr-contenteditable><a href=http://www.whatwg.org/html/#attr-contenteditable>contenteditable</a></code>
attribute set to the true state; or it is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#document>Document</a></code> whose <code class=external data-anolis-spec=html><a href=http://www.whatwg.org/html/#designmode>designMode</a></code> is enabled; or it is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> whose
<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is <a href=#editable>editable</a>, but which does not have a <code class=external data-anolis-spec=html title=attr-contenteditable><a href=http://www.whatwg.org/html/#attr-contenteditable>contenteditable</a></code>
@@ -534,7 +538,74 @@
</ol>
-<h2 id="clearing-an-element's-value"><span class=secno>5 </span>Clearing an element's value</h2>
+<h2 id=block-extending-a-range><span class=secno>5 </span>Block-extending a range</h2>
+<p>When a user agent is to <dfn id=block-extend>block-extend</dfn> a <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>
+<var title="">range</var>, it must run the following steps:
+
+<p class=XXX>Surely I can come up with a better name.
+
+<ol>
+ <li>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>Repeat the following steps:
+
+ <ol>
+ <li>If <var title="">start node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node or
+ <var title="">start offset</var> is 0, set <var title="">start offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+ of <var title="">start node</var> and then set <var title="">start node</var> to its
+ <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Otherwise, if <var title="">start offset</var> is equal to the
+ <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">start node</var>, set <var title="">start offset</var> to one
+ plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">start node</var> and then set <var title="">start
+ node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Otherwise, if the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> 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> minus one is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node, or an
+ (insert definition here), subtract one from <var title="">start offset</var>.
+
+ <p class=XXX>The definition should include all inline elements except
+ <br>. As elsewhere, we have trouble with the exact definition because
+ HTML doesn't classify non-conforming elements, but those are common in
+ editing and need to be handled correctly.
+ <!-- IE also includes <br> (at least for the purposes of the indent
+ command), but this is unlikely to match user expectations. -->
+
+ <li>Otherwise, break from this loop.
+ </ol>
+
+ <li>Repeat the following steps:
+
+ <ol>
+ <li>If <var title="">end offset</var> is 0, set <var title="">end offset</var> to the
+ <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end node</var> and then set <var title="">end node</var> to its
+ <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Otherwise, if <var title="">end node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node or
+ <var title="">end offset</var> is equal to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">end
+ node</var>, set <var title="">end offset</var> to one plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end
+ node</var> and then set <var title="">end node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Otherwise, if the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> 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> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node, or an (insert
+ definition here), add one to <var title="">end offset</var>.
+
+ <p class=XXX>Same definition as before.
+
+ <li>Otherwise, break from this loop.
+ </ol>
+
+ <li>Let <var title="">new range</var> be a new <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> 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> 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> are <var title="">start node</var>,
+ <var title="">start offset</var>, <var title="">end node</var>, and <var title="">end offset</var>.
+
+ <li>Return <var title="">new range</var>.
+</ol>
+
+
+<h2 id="clearing-an-element's-value"><span class=secno>6 </span>Clearing an element's value</h2>
<p>When a user agent is to <dfn id=clear-the-value>clear the value</dfn> of an element
<var title="">element</var>, it must run the following steps:
@@ -624,7 +695,7 @@
</ol>
-<h2 id=pushing-down-values><span class=secno>6 </span>Pushing down values</h2>
+<h2 id=pushing-down-values><span class=secno>7 </span>Pushing down values</h2>
<p>When a user agent is to <dfn id=push-down-values>push down values</dfn> to a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>
<var title="">node</var>, given a command <var title="">command</var> and a new value
<var title="">new value</var>, it must run the following steps:
@@ -741,7 +812,7 @@
</ol>
-<h2 id=forcing-the-value-of-a-node><span class=secno>7 </span>Forcing the value of a node</h2>
+<h2 id=forcing-the-value-of-a-node><span class=secno>8 </span>Forcing the value of a node</h2>
<p>When a user agent is to <dfn id=force-the-value>force the value</dfn> of a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>
<var title="">node</var>, given a command <var title="">command</var> and a new value
<var title="">new value</var>, it must run the following steps.
@@ -1051,7 +1122,7 @@
</ol>
-<h2 id=setting-the-value-of-a-node><span class=secno>8 </span>Setting the value of a node</h2>
+<h2 id=setting-the-value-of-a-node><span class=secno>9 </span>Setting the value of a node</h2>
<p>When a user agent is to <dfn id=set-the-value>set the value</dfn> of a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>
<var title="">node</var>, it must run the following steps. There are two inputs: a
command <var title="">command</var> and a new value <var title="">new value</var>.
@@ -1205,7 +1276,7 @@
</ol>
-<h2 id=commands><span class=secno>9 </span>Commands</h2>
+<h2 id=commands><span class=secno>10 </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
<code class=external data-anolis-spec=html><a href=http://www.whatwg.org/html/#htmldocument>HTMLDocument</a></code> interface allows scripts to
@@ -1727,82 +1798,42 @@
<dd><strong>Action</strong>:
+<p class=XXX>Handle corner cases: endpoints are detached, documents, document
+fragments, html/body, head or things in head . . .
+
<ol>
- <li>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 the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>.
-
- <p class=XXX>Handle corner cases: endpoints are detached, documents, document
- fragments, html/body, head or things in head . . .
-
- <li>Repeat the following steps:
-
- <ol>
- <li>If <var title="">start node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node or
- <var title="">start offset</var> is 0, set <var title="">start offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
- of <var title="">start node</var> and then set <var title="">start node</var> to its
- <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-
- <li>Otherwise, if <var title="">start offset</var> is equal to the
- <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">start node</var>, set <var title="">start offset</var> to one
- plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">start node</var> and then set <var title="">start
- node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-
- <li>Otherwise, if the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> 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> minus one is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node, or an
- (insert definition here), subtract one from <var title="">start offset</var>.
-
- <p class=XXX>The definition should include all inline elements except
- <br>. As elsewhere, we have trouble with the exact definition because
- HTML doesn't classify non-conforming elements, but those are common in
- editing and need to be handled correctly.
-
- <li>Otherwise, break from this loop.
- </ol>
-
- <li>Repeat the following steps:
+ <li><a href=#block-extend>Block-extend</a> the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>, and let <var title="">new range</var> be
+ the result.
+
+ <!-- If the first line box in a block box starts with a non-preserved line
+ break, the line break will create an empty line box. If the line line box in
+ a block box ends with a non-preserved line break, however, it will not create
+ an extra line box. This means that if you have foo<br>bar, there will be no
+ empty line in between, but if you change it to <div>foo</div><br>bar, you've
+ created an extra line. Chrome 12 dev thus deletes a <br> that immediately
+ follows a <blockquote> it creates. Firefox 4.0 and Opera 11.00 instead just
+ includes it in the blockquote, so it has no effect. The issue doesn't come
+ up for IE9, since it indents the whole block and doesn't treat <br>
+ differently from other inline elements. I follow WebKit because it produces
+ slightly neater markup. -->
+ <li>If the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">new range</var>'s <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>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-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-offset title=concept-boundary-point-offset>offset</a> is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>:
<ol>
- <li>If <var title="">end offset</var> is 0, set <var title="">end offset</var> to the
- <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end node</var> and then set <var title="">end node</var> to its
- <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-
- <li>Otherwise, if <var title="">end node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node or
- <var title="">end offset</var> is equal to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">end
- node</var>, set <var title="">end offset</var> to one plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end
- node</var> and then set <var title="">end node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-
- <li>Otherwise, if the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> 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> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node, or an (insert
- definition here), add one to <var title="">end offset</var>.
-
- <p class=XXX>Same definition as before.
-
- <li>Otherwise, if the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> 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> is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, remove it from its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> and break
- from this loop.
- <!-- If the first line box in a block box starts with a non-preserved line
- break, the line break will create an empty line box. If the line line box
- in a block box ends with a non-preserved line break, however, it will not
- create an extra line box. This means that if you have foo<br>bar, there
- will be no empty line in between, but if you change it to
- <div>foo</div><br>bar, you've created an extra line. Chrome 12 dev thus
- deletes a <br> that immediately follows a <blockquote> it creates. Firefox
- 4.0 and Opera 11.00 instead just includes it in the blockquote, so it has
- no effect. The issue doesn't come up for IE9, since it indents the whole
- block and doesn't treat <br> differently from other inline elements. I
- follow WebKit because it produces slightly neater markup. -->
-
- <li>Otherwise, break from this loop.
+ <li>Remove that <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> from its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>While the <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-offset title=concept-boundary-point-offset>offset</a> of <var title="">new range</var> is equal to
+ the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-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>node</a>, set the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> of
+ <var title="">new range</var> to (<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of <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>node</a>, 1 +
+ <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <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>node</a>).
</ol>
- <li>Let <var title="">new range</var> be a new <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> 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> 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> are <var title="">start node</var>,
- <var title="">start offset</var>, <var title="">end node</var>, and <var title="">end offset</var>.
-
- <li>Let <var title="">node list</var> be all <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new
- range</var>, omitting any that cannot be the child of a <code class=external data-anolis-spec=html title="the blockquote element"><a href=http://www.whatwg.org/html/#the-blockquote-element>blockquote</a></code> and
- omitting any with an <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> already in <var title="">node list</var>.
+ <li>Let <var title="">node list</var> be a list of <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a>, initially empty.
+
+ <li>For each <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> <var title="">node</var> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new range</var>,
+ if <var title="">node</var> can be the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of a <code class=external data-anolis-spec=html title="the blockquote element"><a href=http://www.whatwg.org/html/#the-blockquote-element>blockquote</a></code> and if no
+ <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> of <var title="">node</var> is in <var title="">node list</var>, append
+ <var title="">node</var> to <var title="">node list</var>.
<li>For each <var title="">node</var> in <var title="">node list</var>:
@@ -1837,7 +1868,17 @@
that requires care to get right in mixed-direction cases. Even once
browsers start to support margin-start and so on, we can't use them because
a) we have to work okay in legacy browsers and b) it doesn't help if a
- nested block has different direction (so should be indented the other way).
+ descendant block has different direction (so should be indented the other
+ way).
+
+ <p class=XXX>IE9 doesn't handle an explicit margin attribute very well when
+ outdenting: it propagates it to the parent when removing the element, which
+ doesn't actually remove the indentation. So indentation per spec (or
+ Gecko in CSS mode or WebKit) will not be removed correctly by IE. I'm
+ leaving the style in because this short-term incompatibility is preferable
+ to the long-term incorrectness of adding top/bottom margins or adding
+ margins on both sides. I can't think of any better way to do this at the
+ moment.
<li>Append <var title="">node</var> as the last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">new parent</var>,
<a href=#preserving-ranges>preserving ranges</a>.
@@ -1979,6 +2020,107 @@
<dd><strong>Relevant CSS Property</strong>: "font-style"
+<dt><code title=""><dfn id=command-outdent title=command-outdent>outdent</dfn></code>
+
+<dd><strong>Action</strong>:
+
+<ol>
+ <li><a href=#block-extend>Block-extend</a> the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>, and let <var title="">new range</var> be
+ the result.
+
+ <li>Let <var title="">node list</var> be all <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new
+ range</var>, omitting any whose <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is also <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new
+ range</var>.
+
+ <li>For each <var title="">node</var> in <var title="">node list</var>:
+
+ <!--
+ We need to remove all of the following:
+
+ * Plain <blockquote> (produced by Opera 11.00 and non-CSS Firefox 4.0)
+ * <blockquote style="margin-right: 0" dir="ltr"> and <blockquote
+ style="margin-left: 0" dir="rtl"> (IE9)
+ * <blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px;
+ border: none; padding: 0px"> (Chrome 12 dev)
+ * <div style="margin-left: 40px"> and <div style="margin-right: 40px">
+ (CSS Firefox 4.0 if no other element available)
+ * <blockquote style="margin: 0 40px"> and <div style="margin: 0 40px"> (spec)
+ * Other random things with display: block whose left or right margin was
+ increased by 40px (CSS Firefox 4.0)
+ -->
+ <ol>
+ <!-- The easy case is when the whole element is indented. In this case we
+ remove the whole thing indiscriminately. In the case of blockquotes
+ created by IE, this might change the direction of some children, but then
+ their direction was probably changed incorrectly in the first place, so no
+ harm. -->
+ <li>If <var title="">node</var> is a <code class=external data-anolis-spec=html title="the div element"><a href=http://www.whatwg.org/html/#the-div-element>div</a></code> or <code class=external data-anolis-spec=html title="the blockquote element"><a href=http://www.whatwg.org/html/#the-blockquote-element>blockquote</a></code>, and it has no
+ attributes other than one or more of
+
+ <ol type=a>
+ <li>a <code class=external data-anolis-spec=html title="the style attribute"><a href=http://www.whatwg.org/html/#the-style-attribute>style</a></code> attribute that sets no properties other than "margin",
+ "border", "padding", or subproperties of those;
+
+ <li>a <code class=external data-anolis-spec=html title=classes><a href=http://www.whatwg.org/html/#classes>class</a></code> attribute
+ that sets exactly one class;
+
+ <li>a <code class=external data-anolis-spec=html title="the dir attribute"><a href=http://www.whatwg.org/html/#the-dir-attribute>dir</a></code>
+ attribute;
+ </ol>
+
+ then:
+
+ <ol>
+ <li>If <var title="">node</var>'s last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> and <code class=external data-anolis-spec=domcore title=dom-Node-nextSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling>nextSibling</a></code> are both
+ <a href=#inline-node title="inline node">inline nodes</a> or its first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> and
+ <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> are both <a href=#inline-node title="inline node">inline
+ nodes</a>:
+
+ <ol>
+ <li>Let <var title="">new parent</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("div")</a></code> on the
+ <code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">node</var>.
+
+ <li>Insert <var title="">new parent</var> into <var title="">node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>
+ immediately before <var title="">node</var>.
+
+ <li>While <var title="">node</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, append its first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>
+ as <var title="">new parent</var>'s last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, <a href=#preserving-ranges>preserving
+ ranges</a>.
+ </ol>
+
+ <li>Otherwise, while <var title="">node</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, insert its first
+ <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> into its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> immediately before it, <a href=#preserving-ranges>preserving
+ ranges</a>.
+
+ <li>Remove <var title="">node</var> from its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Continue with the next <var title="">node</var>.
+ </ol>
+ </ol>
+
+ <!-- No browser handles the case of Firefox 4.0 in CSS mode, where it adds a
+ margin attribute to an existing element, including Firefox itself. So let's
+ just skip it. -->
+
+ <li class=XXX>If it's a blockquote element or an indented div that doesn't
+ otherwise meet our criteria, then . . .
+
+ <li class=XXX>If some ancestor is indenting us, then . . .
+
+ <!-- No indentation to remove from this node, but maybe some descendant has.
+ We only want to remove one level of indentation, so we only run this step if
+ we didn't remove indentation already. -->
+ <li>If <var title="">node</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, insert all of its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a> into
+ <var title="">node list</var> immediately after <var title="">node</var>, so that the next
+ <var title="">node</var> to be processed is the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of the current
+ <var title="">node</var>.
+</ol>
+
+<dd><strong>State</strong>:
+
+<dd><strong>Value</strong>:
+
+
<dt><code title=""><dfn id=command-removeformat title=command-removeformat>removeFormat</dfn></code>
<!--
Tested in IE 9, Firefox 4.0, Chrome 12 dev, Opera 11.00.
--- a/implementation.js Thu Apr 28 12:54:21 2011 -0600
+++ b/implementation.js Thu Apr 28 15:01:23 2011 -0600
@@ -327,6 +327,15 @@
&& isHtmlNamespace(node.namespaceURI);
}
+// "An inline node is either a Text node, or an Element whose "display"
+// property computes to "inline", "inline-block", or "inline-table"."
+function isInlineNode(node) {
+ return node
+ && (node.nodeType == Node.TEXT_NODE
+ || (node.nodeType == Node.ELEMENT_NODE
+ && ["inline", "inline-block", "inline-table"].indexOf(getComputedStyle(node).display) != -1));
+}
+
// "Something is editable if either it is an Element with a contenteditable
// attribute set to the true state; or it is a Document whose designMode is
// enabled; or it is a node whose parent is editable, but which does not have a
@@ -1034,6 +1043,87 @@
return ret;
}
+function blockExtendRange(range) {
+ // "Let start node, start offset, end node, and end offset be the start
+ // and end nodes and offsets of the range."
+ var startNode = range.startContainer;
+ var startOffset = range.startOffset;
+ var endNode = range.endContainer;
+ var endOffset = range.endOffset;
+
+ // "Repeat the following steps:"
+ while (true) {
+ // "If start node is a Text or Comment node or start offset is 0,
+ // set start offset to the index of start node and then set start
+ // node to its parent."
+ if (startNode.nodeType == Node.TEXT_NODE
+ || startNode.nodeType == Node.COMMENT_NODE
+ || startOffset == 0) {
+ startOffset = getNodeIndex(startNode);
+ startNode = startNode.parentNode;
+
+ // "Otherwise, if start offset is equal to the length of start
+ // node, set start offset to one plus the index of start node and
+ // then set start node to its parent."
+ } else if (startOffset == getNodeLength(startNode)) {
+ startOffset = 1 + getNodeIndex(startNode);
+ startNode = startNode.parentNode;
+
+ // "Otherwise, if the child of start node with index start offset
+ // minus one is a Text or Comment node, or an (insert definition
+ // here), subtract one from start offset."
+ } else if (startNode.childNodes[startOffset - 1].nodeType == Node.TEXT_NODE
+ || startNode.childNodes[startOffset - 1].nodeType == Node.COMMENT_NODE
+ || ["B", "I", "SPAN"].indexOf(startNode.childNodes[startOffset - 1].tagName) != -1) {
+ startOffset--;
+
+ // "Otherwise, break from this loop."
+ } else {
+ break;
+ }
+ }
+
+ // "Repeat the following steps:"
+ while (true) {
+ // "If end offset is 0, set end offset to the index of end node and
+ // then set end node to its parent."
+ if (endOffset == 0) {
+ endOffset = getNodeIndex(endNode);
+ endNode = endNode.parentNode;
+
+ // "Otherwise, if end node is a Text or Comment node or end offset
+ // is equal to the length of end node, set end offset to one plus
+ // the index of end node and then set end node to its parent."
+ } else if (endNode.nodeType == Node.TEXT_NODE
+ || endNode.nodeType == Node.COMMENT_NODE
+ || endOffset == getNodeLength(endNode)) {
+ endOffset = 1 + getNodeIndex(endNode);
+ endNode = endNode.parentNode;
+
+ // "Otherwise, if the child of end node with index end offset is a
+ // Text or Comment node, or an (insert definition here), add one to
+ // end offset."
+ } else if (endNode.childNodes[endOffset].nodeType == Node.TEXT_NODE
+ || endNode.childNodes[endOffset].nodeType == Node.COMMENT_NODE
+ || ["B", "I", "SPAN"].indexOf(endNode.childNodes[endOffset].tagName) != -1) {
+ endOffset++;
+
+ // "Otherwise, break from this loop."
+ } else {
+ break;
+ }
+ }
+
+ // "Let new range be a new range whose start and end nodes and offsets
+ // are start node, start offset, end node, and end offset."
+ var newRange = startNode.ownerDocument.createRange();
+ newRange.setStart(startNode, startOffset);
+ newRange.setEnd(endNode, endOffset);
+
+ // "Return new range."
+ return newRange;
+}
+
function clearValue(element, command) {
// "If element's specified value for command is null, return the empty
// list."
@@ -1939,93 +2029,30 @@
break;
case "indent":
- // "Let start node, start offset, end node, and end offset be the start
- // and end nodes and offsets of the range."
- var startNode = range.startContainer;
- var startOffset = range.startOffset;
- var endNode = range.endContainer;
- var endOffset = range.endOffset;
-
- // "Repeat the following steps:"
- while (true) {
- // "If start node is a Text or Comment node or start offset is 0,
- // set start offset to the index of start node and then set start
- // node to its parent."
- if (startNode.nodeType == Node.TEXT_NODE
- || startNode.nodeType == Node.COMMENT_NODE
- || startOffset == 0) {
- startOffset = getNodeIndex(startNode);
- startNode = startNode.parentNode;
+ // "Block-extend the range, and let new range be the result."
+ var newRange = blockExtendRange(range);
- // "Otherwise, if start offset is equal to the length of start
- // node, set start offset to one plus the index of start node and
- // then set start node to its parent."
- } else if (startOffset == getNodeLength(startNode)) {
- startOffset = 1 + getNodeIndex(startNode);
- startNode = startNode.parentNode;
+ // "If the child of new range's end node with index equal to its end
+ // offset is a br:"
+ var end = newRange.endContainer.childNodes[newRange.endOffset];
+ if (isHtmlElement(end) && end.tagName == "BR") {
+ // "Remove that br from its parent."
+ end.parentNode.removeChild(br);
- // "Otherwise, if the child of start node with index start offset
- // minus one is a Text or Comment node, or an (insert definition
- // here), subtract one from start offset."
- } else if (startNode.childNodes[startOffset - 1].nodeType == Node.TEXT_NODE
- || startNode.childNodes[startOffset - 1].nodeType == Node.COMMENT_NODE
- || ["B", "I", "SPAN"].indexOf(startNode.childNodes[startOffset - 1].tagName) != -1) {
- startOffset--;
-
- // "Otherwise, break from this loop."
- } else {
- break;
+ // "While the end offset of new range is equal to the length of its
+ // end node, set the end of new range to (parent of end node, 1 +
+ // index of end node)."
+ while (newRange.endOffset == getNodeLength(newRange.endContainer)) {
+ newRange.setEnd(newRange.endContainer.parentNode, 1 + getNodeIndex(newRange.endContainer));
}
}
- // "Repeat the following steps:"
- while (true) {
- // "If end offset is 0, set end offset to the index of end node and
- // then set end node to its parent."
- if (endOffset == 0) {
- endOffset = getNodeIndex(endNode);
- endNode = endNode.parentNode;
-
- // "Otherwise, if end node is a Text or Comment node or end offset
- // is equal to the length of end node, set end offset to one plus
- // the index of end node and then set end node to its parent."
- } else if (endNode.nodeType == Node.TEXT_NODE
- || endNode.nodeType == Node.COMMENT_NODE
- || endOffset == getNodeLength(endNode)) {
- endOffset = 1 + getNodeIndex(endNode);
- endNode = endNode.parentNode;
+ // "Let node list be a list of nodes, initially empty."
+ var nodeList = [];
- // "Otherwise, if the child of end node with index end offset is a
- // Text or Comment node, or an (insert definition here), add one to
- // end offset."
- } else if (endNode.childNodes[endOffset].nodeType == Node.TEXT_NODE
- || endNode.childNodes[endOffset].nodeType == Node.COMMENT_NODE
- || ["B", "I", "SPAN"].indexOf(endNode.childNodes[endOffset].tagName) != -1) {
- endOffset++;
-
- // "Otherwise, if the child of end node with index end offset is a br,
- // remove it from its parent and break from this loop."
- } else if (isHtmlElement(endNode.childNodes[endOffset])
- && endNode.childNodes[endOffset].tagName == "BR") {
- endNode.removeChild(endNode.childNodes[endOffset]);
- break;
-
- // "Otherwise, break from this loop."
- } else {
- break;
- }
- }
-
- // "Let new range be a new range whose start and end nodes and offsets
- // are start node, start offset, end node, and end offset."
- var newRange = startNode.ownerDocument.createRange();
- newRange.setStart(startNode, startOffset);
- newRange.setEnd(endNode, endOffset);
-
- // "Let node list be all nodes contained in new range, omitting any
- // that cannot be the child of a blockquote and omitting any with an
- // ancestor already in node list."
- var nodeList = [];
+ // "For each node node contained in new range, if node can be the child
+ // of a blockquote and if no ancestor of node is in node list, append
+ // node to node list."
for (var node = newRange.startContainer; node != nextNodeDescendants(newRange.endContainer); node = nextNode(node)) {
if (!isContained(node, newRange)) {
continue;
@@ -2036,6 +2063,8 @@
continue;
}
+ // We only need to check that the last member isn't an ancestor,
+ // because no ancestor of a member can be in the list.
if (nodeList.length
&& isAncestor(nodeList[nodeList.length - 1], node)) {
continue;
@@ -2084,7 +2113,6 @@
// ranges."
movePreservingRanges(node, newParent, 0);
}
-
break;
case "inserthorizontalrule":
@@ -2239,6 +2267,90 @@
}
break;
+ case "outdent":
+ // "Block-extend the range, and let new range be the result."
+ var newRange = blockExtendRange(range);
+
+ // "Let node list be all nodes contained in new range, omitting any
+ // whose parent is also contained in new range."
+ var nodeList = [];
+ for (
+ var node = newRange.startContainer;
+ node != nextNodeDescendants(newRange.endContainer);
+ node = nextNode(node)
+ ) {
+ if (isContained(node, newRange)
+ && !isContained(node.parentNode, newRange)) {
+ nodeList.push(node);
+ }
+ }
+
+ // "For each node in node list:"
+ for (var i = 0; i < nodeList.length; i++) {
+ var node = nodeList[i];
+
+ // "If node is a div or blockquote, and it has no attributes other
+ // than one or more of
+ // "a. a style attribute that sets no properties other than
+ // "margin", "border", "padding", or subproperties of those;
+ // "b. a class attribute that sets exactly one class;
+ // "c. a dir attribute;
+ // "then:"
+ //
+ // Not going to implement all the fancy checks, too much effort for
+ // a test implementation.
+ if (isHtmlElement(node)
+ && (node.tagName == "BLOCKQUOTE"
+ || (
+ node.tagName == "DIV"
+ && node.attributes.length == 1
+ && ["margin-left: 40px", "margin-right: 40px", "margin: 0 40px"].indexOf(node.getAttribute("style")) != -1
+ ))) {
+ // "If node's last child and nextSibling are both inline nodes
+ // or its first child and previousSibling are both inline
+ // nodes:"
+ if ((isInlineNode(node.lastChild) && isInlineNode(node.nextSibling))
+ || (isInlineNode(node.firstChild) && isInlineNode(node.previousSibling))) {
+ // "Let new parent be the result of calling
+ // createElement("div") on the ownerDocument of node."
+ var newParent = node.ownerDocument.createElement("div");
+
+ // "Insert new parent into node's parent immediately before
+ // node."
+ node.parentNode.insertBefore(newParent, node);
+
+ // "While node has children, append its first child as new
+ // parent's last child, preserving ranges."
+ while (node.firstChild) {
+ movePreservingRanges(node.firstChild, newParent, newParent.childNodes.length);
+ }
+
+ // "Otherwise, while node has children, insert its first child
+ // into its parent immediately before it, preserving ranges."
+ } else {
+ while (node.firstChild) {
+ movePreservingRanges(node.firstChild, node.parentNode, getNodeIndex(node));
+ }
+ }
+
+ // "Remove node from its parent."
+ node.parentNode.removeChild(node);
+
+ // "Continue with the next node."
+ continue;
+ }
+
+ // "If node has children, insert all of its children into node list
+ // immediately after node, so that the next node to be processed is
+ // the first child of the current node."
+ if (node.firstChild) {
+ nodeList = nodeList.slice(0, i + 1)
+ .concat(Array.prototype.slice.call(node.childNodes))
+ .concat(nodeList.slice(i + 1));
+ }
+ }
+ break;
+
case "removeformat":
// "Decompose the range, and let node list be the result."
var nodeList = decomposeRange(range);
--- a/preprocess Thu Apr 28 12:54:21 2011 -0600
+++ b/preprocess Thu Apr 28 15:01:23 2011 -0600
@@ -24,6 +24,8 @@
'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-tree-descendant>descendant</span>',
+ 'directionality': '<span data-anolis-spec=html title="the directionality">directionality</span>',
+ 'div': '<code data-anolis-spec=html title="the div element">div</code>',
'document': '<code data-anolis-spec=domcore>Document</code>',
'documentfragment': '<code data-anolis-spec=domcore>DocumentFragment</code>',
'element': '<code data-anolis-spec=domcore>Element</code>',
--- a/source.html Thu Apr 28 12:54:21 2011 -0600
+++ b/source.html Thu Apr 28 15:01:23 2011 -0600
@@ -169,6 +169,9 @@
<p>An <dfn>HTML element</dfn> is an [[element]] whose [[namespace]] is the
[[htmlnamespace]].
+<p>An <dfn>inline node</dfn> is either a [[text]] node, or an [[element]] whose
+"display" property computes to "inline", "inline-block", or "inline-table".
+
<p>Something is <dfn>editable</dfn> if either it is an [[element]] with a <code
data-anolis-spec=html title=attr-contenteditable>contenteditable</code>
attribute set to the true state; or it is a [[document]] whose <code
@@ -529,6 +532,73 @@
</ol>
+<h2>Block-extending a range</h2>
+<p>When a user agent is to <dfn>block-extend</dfn> a [[range]]
+<var>range</var>, it must run the following steps:
+
+<p class=XXX>Surely I can come up with a better name.
+
+<ol>
+ <li>Let <var>start node</var>, <var>start offset</var>, <var>end node</var>,
+ and <var>end offset</var> be the [[rangestart]] and [[rangeend]] [[bpnodes]]
+ and [[bpoffsets]] of <var>range</var>.
+
+ <li>Repeat the following steps:
+
+ <ol>
+ <li>If <var>start node</var> is a [[text]] or [[comment]] node or
+ <var>start offset</var> is 0, set <var>start offset</var> to the [[index]]
+ of <var>start node</var> and then set <var>start node</var> to its
+ [[parent]].
+
+ <li>Otherwise, if <var>start offset</var> is equal to the
+ [[nodelength]] of <var>start node</var>, set <var>start offset</var> to one
+ plus the [[index]] of <var>start node</var> and then set <var>start
+ node</var> to its [[parent]].
+
+ <li>Otherwise, if the [[child]] of <var>start node</var> with [[index]]
+ <var>start offset</var> minus one is a [[text]] or [[comment]] node, or an
+ (insert definition here), subtract one from <var>start offset</var>.
+
+ <p class=XXX>The definition should include all inline elements except
+ <br>. As elsewhere, we have trouble with the exact definition because
+ HTML doesn't classify non-conforming elements, but those are common in
+ editing and need to be handled correctly.
+ <!-- IE also includes <br> (at least for the purposes of the indent
+ command), but this is unlikely to match user expectations. -->
+
+ <li>Otherwise, break from this loop.
+ </ol>
+
+ <li>Repeat the following steps:
+
+ <ol>
+ <li>If <var>end offset</var> is 0, set <var>end offset</var> to the
+ [[index]] of <var>end node</var> and then set <var>end node</var> to its
+ [[parent]].
+
+ <li>Otherwise, if <var>end node</var> is a [[text]] or [[comment]] node or
+ <var>end offset</var> is equal to the [[nodelength]] of <var>end
+ node</var>, set <var>end offset</var> to one plus the [[index]] of <var>end
+ node</var> and then set <var>end node</var> to its [[parent]].
+
+ <li>Otherwise, if the [[child]] of <var>end node</var> with [[index]]
+ <var>end offset</var> is a [[text]] or [[comment]] node, or an (insert
+ definition here), add one to <var>end offset</var>.
+
+ <p class=XXX>Same definition as before.
+
+ <li>Otherwise, break from this loop.
+ </ol>
+
+ <li>Let <var>new range</var> be a new [[range]] whose [[rangestart]] and
+ [[rangeend]] [[bpnodes]] and [[bpoffsets]] are <var>start node</var>,
+ <var>start offset</var>, <var>end node</var>, and <var>end offset</var>.
+
+ <li>Return <var>new range</var>.
+</ol>
+
+
<h2>Clearing an element's value</h2>
<p>When a user agent is to <dfn>clear the value</dfn> of an element
<var>element</var>, it must run the following steps:
@@ -1743,82 +1813,42 @@
<dd><strong>Action</strong>:
+<p class=XXX>Handle corner cases: endpoints are detached, documents, document
+fragments, html/body, head or things in head . . .
+
<ol>
- <li>Let <var>start node</var>, <var>start offset</var>, <var>end node</var>,
- and <var>end offset</var> be the [[rangestart]] and [[rangeend]] [[bpnodes]]
- and [[bpoffsets]] of the [[range]].
-
- <p class=XXX>Handle corner cases: endpoints are detached, documents, document
- fragments, html/body, head or things in head . . .
-
- <li>Repeat the following steps:
-
- <ol>
- <li>If <var>start node</var> is a [[text]] or [[comment]] node or
- <var>start offset</var> is 0, set <var>start offset</var> to the [[index]]
- of <var>start node</var> and then set <var>start node</var> to its
- [[parent]].
-
- <li>Otherwise, if <var>start offset</var> is equal to the
- [[nodelength]] of <var>start node</var>, set <var>start offset</var> to one
- plus the [[index]] of <var>start node</var> and then set <var>start
- node</var> to its [[parent]].
-
- <li>Otherwise, if the [[child]] of <var>start node</var> with [[index]]
- <var>start offset</var> minus one is a [[text]] or [[comment]] node, or an
- (insert definition here), subtract one from <var>start offset</var>.
-
- <p class=XXX>The definition should include all inline elements except
- <br>. As elsewhere, we have trouble with the exact definition because
- HTML doesn't classify non-conforming elements, but those are common in
- editing and need to be handled correctly.
-
- <li>Otherwise, break from this loop.
- </ol>
-
- <li>Repeat the following steps:
+ <li><span>Block-extend</span> the [[range]], and let <var>new range</var> be
+ the result.
+
+ <!-- If the first line box in a block box starts with a non-preserved line
+ break, the line break will create an empty line box. If the line line box in
+ a block box ends with a non-preserved line break, however, it will not create
+ an extra line box. This means that if you have foo<br>bar, there will be no
+ empty line in between, but if you change it to <div>foo</div><br>bar, you've
+ created an extra line. Chrome 12 dev thus deletes a <br> that immediately
+ follows a <blockquote> it creates. Firefox 4.0 and Opera 11.00 instead just
+ includes it in the blockquote, so it has no effect. The issue doesn't come
+ up for IE9, since it indents the whole block and doesn't treat <br>
+ differently from other inline elements. I follow WebKit because it produces
+ slightly neater markup. -->
+ <li>If the [[child]] of <var>new range</var>'s [[rangeend]] [[bpnode]] with
+ [[index]] equal to its [[rangeend]] [[bpoffset]] is a [[br]]:
<ol>
- <li>If <var>end offset</var> is 0, set <var>end offset</var> to the
- [[index]] of <var>end node</var> and then set <var>end node</var> to its
- [[parent]].
-
- <li>Otherwise, if <var>end node</var> is a [[text]] or [[comment]] node or
- <var>end offset</var> is equal to the [[nodelength]] of <var>end
- node</var>, set <var>end offset</var> to one plus the [[index]] of <var>end
- node</var> and then set <var>end node</var> to its [[parent]].
-
- <li>Otherwise, if the [[child]] of <var>end node</var> with [[index]]
- <var>end offset</var> is a [[text]] or [[comment]] node, or an (insert
- definition here), add one to <var>end offset</var>.
-
- <p class=XXX>Same definition as before.
-
- <li>Otherwise, if the [[child]] of <var>end node</var> with [[index]]
- <var>end offset</var> is a [[br]], remove it from its [[parent]] and break
- from this loop.
- <!-- If the first line box in a block box starts with a non-preserved line
- break, the line break will create an empty line box. If the line line box
- in a block box ends with a non-preserved line break, however, it will not
- create an extra line box. This means that if you have foo<br>bar, there
- will be no empty line in between, but if you change it to
- <div>foo</div><br>bar, you've created an extra line. Chrome 12 dev thus
- deletes a <br> that immediately follows a <blockquote> it creates. Firefox
- 4.0 and Opera 11.00 instead just includes it in the blockquote, so it has
- no effect. The issue doesn't come up for IE9, since it indents the whole
- block and doesn't treat <br> differently from other inline elements. I
- follow WebKit because it produces slightly neater markup. -->
-
- <li>Otherwise, break from this loop.
+ <li>Remove that [[br]] from its [[parent]].
+
+ <li>While the [[rangeend]] [[bpoffset]] of <var>new range</var> is equal to
+ the [[nodelength]] of its [[rangeend]] [[bpnode]], set the [[rangeend]] of
+ <var>new range</var> to ([[parent]] of [[rangeend]] [[bpnode]], 1 +
+ [[index]] of [[rangeend]] [[bpnode]]).
</ol>
- <li>Let <var>new range</var> be a new [[range]] whose [[rangestart]] and
- [[rangeend]] [[bpnodes]] and [[bpoffsets]] are <var>start node</var>,
- <var>start offset</var>, <var>end node</var>, and <var>end offset</var>.
-
- <li>Let <var>node list</var> be all [[nodes]] [[contained]] in <var>new
- range</var>, omitting any that cannot be the child of a [[blockquote]] and
- omitting any with an [[ancestor]] already in <var>node list</var>.
+ <li>Let <var>node list</var> be a list of [[nodes]], initially empty.
+
+ <li>For each [[node]] <var>node</var> [[contained]] in <var>new range</var>,
+ if <var>node</var> can be the [[child]] of a [[blockquote]] and if no
+ [[ancestor]] of <var>node</var> is in <var>node list</var>, append
+ <var>node</var> to <var>node list</var>.
<li>For each <var>node</var> in <var>node list</var>:
@@ -1855,7 +1885,17 @@
that requires care to get right in mixed-direction cases. Even once
browsers start to support margin-start and so on, we can't use them because
a) we have to work okay in legacy browsers and b) it doesn't help if a
- nested block has different direction (so should be indented the other way).
+ descendant block has different direction (so should be indented the other
+ way).
+
+ <p class=XXX>IE9 doesn't handle an explicit margin attribute very well when
+ outdenting: it propagates it to the parent when removing the element, which
+ doesn't actually remove the indentation. So indentation per spec (or
+ Gecko in CSS mode or WebKit) will not be removed correctly by IE. I'm
+ leaving the style in because this short-term incompatibility is preferable
+ to the long-term incorrectness of adding top/bottom margins or adding
+ margins on both sides. I can't think of any better way to do this at the
+ moment.
<li>Append <var>node</var> as the last [[child]] of <var>new parent</var>,
<span>preserving ranges</span>.
@@ -2016,6 +2056,109 @@
<dd><strong>Relevant CSS Property</strong>: "font-style"
+<dt><code title><dfn title=command-outdent>outdent</dfn></code>
+
+<dd><strong>Action</strong>:
+
+<ol>
+ <li><span>Block-extend</span> the [[range]], and let <var>new range</var> be
+ the result.
+
+ <li>Let <var>node list</var> be all [[nodes]] [[contained]] in <var>new
+ range</var>, omitting any whose [[parent]] is also [[contained]] in <var>new
+ range</var>.
+
+ <li>For each <var>node</var> in <var>node list</var>:
+
+ <!--
+ We need to remove all of the following:
+
+ * Plain <blockquote> (produced by Opera 11.00 and non-CSS Firefox 4.0)
+ * <blockquote style="margin-right: 0" dir="ltr"> and <blockquote
+ style="margin-left: 0" dir="rtl"> (IE9)
+ * <blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px;
+ border: none; padding: 0px"> (Chrome 12 dev)
+ * <div style="margin-left: 40px"> and <div style="margin-right: 40px">
+ (CSS Firefox 4.0 if no other element available)
+ * <blockquote style="margin: 0 40px"> and <div style="margin: 0 40px"> (spec)
+ * Other random things with display: block whose left or right margin was
+ increased by 40px (CSS Firefox 4.0)
+ -->
+ <ol>
+ <!-- The easy case is when the whole element is indented. In this case we
+ remove the whole thing indiscriminately. In the case of blockquotes
+ created by IE, this might change the direction of some children, but then
+ their direction was probably changed incorrectly in the first place, so no
+ harm. -->
+ <li>If <var>node</var> is a [[div]] or [[blockquote]], and it has no
+ attributes other than one or more of
+
+ <ol type=a>
+ <li>a [[style]] attribute that sets no properties other than "margin",
+ "border", "padding", or subproperties of those;
+
+ <li>a <code data-anolis-spec=html title=classes>class</code> attribute
+ that sets exactly one class;
+
+ <li>a <code data-anolis-spec=html title="the dir attribute">dir</code>
+ attribute;
+ </ol>
+
+ then:
+
+ <ol>
+ <li>If <var>node</var>'s last [[child]] and [[nextsibling]] are both
+ <span title="inline node">inline nodes</span> or its first [[child]] and
+ [[previoussibling]] are both <span title="inline node">inline
+ nodes</span>:
+
+ <ol>
+ <li>Let <var>new parent</var> be the result of calling <code
+ data-anolis-spec=domcore
+ title=dom-Document-createElement>createElement("div")</code> on the
+ [[ownerdocument]] of <var>node</var>.
+
+ <li>Insert <var>new parent</var> into <var>node</var>'s [[parent]]
+ immediately before <var>node</var>.
+
+ <li>While <var>node</var> has [[children]], append its first [[child]]
+ as <var>new parent</var>'s last [[child]], <span>preserving
+ ranges</span>.
+ </ol>
+
+ <li>Otherwise, while <var>node</var> has [[children]], insert its first
+ [[child]] into its [[parent]] immediately before it, <span>preserving
+ ranges</span>.
+
+ <li>Remove <var>node</var> from its [[parent]].
+
+ <li>Continue with the next <var>node</var>.
+ </ol>
+ </ol>
+
+ <!-- No browser handles the case of Firefox 4.0 in CSS mode, where it adds a
+ margin attribute to an existing element, including Firefox itself. So let's
+ just skip it. -->
+
+ <li class=XXX>If it's a blockquote element or an indented div that doesn't
+ otherwise meet our criteria, then . . .
+
+ <li class=XXX>If some ancestor is indenting us, then . . .
+
+ <!-- No indentation to remove from this node, but maybe some descendant has.
+ We only want to remove one level of indentation, so we only run this step if
+ we didn't remove indentation already. -->
+ <li>If <var>node</var> has [[children]], insert all of its [[children]] into
+ <var>node list</var> immediately after <var>node</var>, so that the next
+ <var>node</var> to be processed is the first [[child]] of the current
+ <var>node</var>.
+</ol>
+
+<dd><strong>State</strong>:
+
+<dd><strong>Value</strong>:
+
+
<dt><code title><dfn title=command-removeformat>removeFormat</dfn></code>
<!--
Tested in IE 9, Firefox 4.0, Chrome 12 dev, Opera 11.00.