--- a/autoimplementation.html Thu Apr 28 15:01:23 2011 -0600
+++ b/autoimplementation.html Sun May 01 15:04:13 2011 -0600
@@ -621,10 +621,10 @@
'<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',
+ '<blockquote style="margin-right: 0px;" dir="ltr"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
+ '<blockquote style="margin-right: 0px;" dir="ltr"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
+ '<blockquote style="margin-right: 0px;" dir="ltr"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
+ '<blockquote style="margin-right: 0px;" 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',
@@ -633,16 +633,34 @@
'<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',
+ '<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',
+ '<div style="margin: 0 40px"><p>foo[bar]</p><p>baz</p></div><p>extra',
+ '<div style="margin: 0 40px"><p>foo[bar</p><p>b]az</p></div><p>extra',
+ '<div style="margin: 0 40px"><p>foo[bar]</p></div><p>baz</p><p>extra',
+ '<div style="margin: 0 40px"><p>foo[bar</p></div><p>b]az</p><p>extra',
+
+ // Now let's try nesting lots of stuff and see what happens.
+ '<blockquote><blockquote>foo[bar]baz</blockquote></blockquote>',
+ '<blockquote><blockquote data-abc=def>foo[bar]baz</blockquote></blockquote>',
+ '<blockquote data-abc=def><blockquote>foo[bar]baz</blockquote></blockquote>',
+ '<blockquote><div><p>foo[bar]<p>baz</div></blockquote>',
+ '<blockquote><div id=abc><p>foo[bar]<p>baz</div></blockquote>',
+ '<blockquote id=abc><p>foo[bar]<p>baz</blockquote>',
+ '<blockquote style="color: red"><p>foo[bar]<p>baz</blockquote>',
+ '<blockquote><p><b>foo[bar]</b><p>baz</blockquote>',
+ '<blockquote><p><strong>foo[bar]</strong><p>baz</blockquote>',
+ '<blockquote><p><span>foo[bar]</span><p>baz</blockquote>',
+ '<blockquote><blockquote style="color: red"><p>foo[bar]</blockquote><p>baz</blockquote>',
+ '<blockquote style="color: red"><blockquote><p>foo[bar]</blockquote><p>baz</blockquote>',
],
removeformat: [
'foo[]bar',
@@ -1102,10 +1120,12 @@
var test;
var inputs = document.getElementById(command).getElementsByTagName("input");
+ // Strip \u200B (zero-width space) so copy-pasting from the cells works
+ // non-confusingly
if (inputs.length == 1) {
- test = inputs[0].value;
+ test = inputs[0].value.replace(/\u200B/g, "");
} else {
- test = [inputs[1].value, inputs[0].value];
+ test = [inputs[1].value, inputs[0].value.replace(/\u200B/g, "")];
}
doInputCell(tr, test);
--- a/editcommands.html Thu Apr 28 15:01:23 2011 -0600
+++ b/editcommands.html Sun May 01 15:04:13 2011 -0600
@@ -27,7 +27,7 @@
<body class=draft>
<div class=head id=head>
<h1>HTML Editing Commands</h1>
-<h2 class="no-num no-toc" id=work-in-progress-—-last-update-28-april-2011>Work in Progress — Last Update 28 April 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-—-last-update-1-may-2011>Work in Progress — Last Update 1 May 2011</h2>
<dl>
<dt>Editor
<dd>Aryeh Gregor <ayg+spec@aryeh.name>
@@ -185,6 +185,51 @@
<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>To <dfn id=set-the-tag-name>set the tag name</dfn> of 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> <var title="">element</var> to
+<var title="">new name</var>, the user agent must run the following steps:
+
+<ol>
+ <li>If <var title="">element</var> is an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>
+ equal to <var title="">new name</var>, return <var title="">element</var>.
+
+ <li>If <var title="">element</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is null, return <var title="">element</var>.
+
+ <li>Let <var title="">replacement element</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(<var title="">new name</var>)</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="">element</var>.
+
+ <li>Insert <var title="">replacement element</var> into <var title="">element</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="">element</var>.
+
+ <li>Copy all attributes of <var title="">element</var> to <var title="">replacement
+ element</var>, in order.
+
+ <li>While <var title="">element</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 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
+ <var title="">element</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="">replacement element</var>,
+ <a href=#preserving-ranges>preserving ranges</a>.
+
+ <li>Remove <var title="">element</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>Return <var title="">replacement element</var>.
+</ol>
+
+<p>To remove a node <var title="">node</var> while <dfn id=preserving-its-descendants>preserving its
+descendants</dfn>:
+
+<ol>
+ <li>Let <var title="">children</var> be a list of <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-child title=concept-tree-child>children</a>.
+
+ <li>If <var title="">node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is null, remove all of <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-child title=concept-tree-child>children</a> from <var title="">node</var>, then return <var title="">children</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>, insert 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
+ <var title="">node</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>, <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>Return <var title="">children</var>.
+</ol>
+
<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>
@@ -1835,54 +1880,56 @@
<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>:
-
- <ol>
- <li>If the <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> of <var title="">node</var> is an <a href=#html-element>HTML
- element</a>; its "display" property computes to "block"; its
- "margin-left" and "margin-right" properties compute to "40px"; and its
- "margin-top" and "margin-bottom" properties compute to "0"; then 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 its <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>,
- <a href=#preserving-ranges>preserving ranges</a>, and continue with the next <var title="">node</var>.
-
- <li>Let <var title="">tag</var> be "div" if the <a href=#css-styling-flag>CSS styling flag</a> is
- true, otherwise "blockquote".
- <!-- Firefox 4.0 is the only tested browser that respects the CSS styling
- flag for indent at all. For indent, as for inline markup commands like
- bold, it will modify the inline style of existing elements if available,
- and only create spans/divs if there are no existing elements where it could
- add the style instead. For indent as for other commands, I follow WebKit
- and always create an extra element, to ensure consistency between CSS and
- non-CSS modes. -->
-
- <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(<var title="">tag</var>)</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>Set the CSS property "margin" of <var title="">new parent</var> to "0 40px".
-
- <p class=XXX>This indents on both sides, so we don't have to worry about
- directionality. Preferably we should indent only on the start side, but
- 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
- 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>.
- </ol>
+ <li><a href=#indent>Indent</a> each member of <var title="">node list</var>.
+</ol>
+
+<p>To <dfn id=indent>indent</dfn> 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>:
+
+<ol>
+ <li>If the <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> of <var title="">node</var> is an <a href=#html-element>HTML
+ element</a>; its "display" property computes to "block"; its
+ "margin-left" and "margin-right" properties compute to "40px"; and its
+ "margin-top" and "margin-bottom" properties compute to "0"; then 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 its <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>,
+ <a href=#preserving-ranges>preserving ranges</a>, then abort these steps.
+
+ <li>Let <var title="">tag</var> be "div" if the <a href=#css-styling-flag>CSS styling flag</a> is
+ true, otherwise "blockquote".
+ <!-- Firefox 4.0 is the only tested browser that respects the CSS styling
+ flag for indent at all. For indent, as for inline markup commands like
+ bold, it will modify the inline style of existing elements if available,
+ and only create spans/divs if there are no existing elements where it could
+ add the style instead. For indent as for other commands, I follow WebKit
+ and always create an extra element, to ensure consistency between CSS and
+ non-CSS modes. -->
+
+ <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(<var title="">tag</var>)</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>Set the CSS property "margin" of <var title="">new parent</var> to "0 40px".
+
+ <p class=XXX>This indents on both sides, so we don't have to worry about
+ directionality. Preferably we should indent only on the start side, but
+ 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
+ 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>.
</ol>
<dd><strong>State</strong>:
@@ -2029,93 +2076,177 @@
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)
- -->
+ range</var> that have no <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.
+
+ <li><a href=#outdent>Outdent</a> each member of <var title="">node list</var>.
+</ol>
+
+<p>To <dfn id=outdent>outdent</dfn> 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>:
+
+<!--
+Things that are produced for indentation that we need to consider removing:
+
+* 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>
+ <li>If <var title="">node</var> is not <a href=#editable>editable</a>, abort these steps.
+
+ <p class=XXX>Handle this better for nested editable/non-editable.
+
+ <!-- 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 an <a href=#indentation-element>indentation element</a>:
+
<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>
+ <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>, unset all attributes of <var title="">node</var>, then <a href=#set-the-tag-name>set the
+ tag name</a> of <var title="">node</var> to "div".
+ <!-- In this case, removing it entirely will result in its first or last
+ child becoming part of the previous or next sibling's line box, so we
+ want to keep a div. -->
+
+ <li>Otherwise, remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its
+ descendants</a>.
+
+ <li>Abort these steps.
</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>.
+ <li>If <var title="">node</var> is a <a href=#potential-indentation-element>potential indentation element</a>:
+ <!-- This might be an indentation element that had style added to it by
+ Firefox in CSS mode, for instance (color, font-family, etc.). -->
+
+ <ol>
+ <li><a href=#set-the-tag-name>Set the tag name</a> of <var title="">node</var> to "div".
+
+ <li>Unset the <code class=external data-anolis-spec=html title=classes><a href=http://www.whatwg.org/html/#classes>class</a></code> and
+ <code class=external data-anolis-spec=html title="the dir attribute"><a href=http://www.whatwg.org/html/#the-dir-attribute>dir</a></code>
+ attributes of <var title="">node</var>, if any.
+
+ <li>Unset the margin, padding, and border CSS properties of
+ <var title="">node</var>.
+
+ <li>Abort these steps.
+ </ol>
+
+ <!-- Approximate algorithms when an ancestor is causing the indentation
+ appear to be:
+
+ IE9: Go to the innermost element causing indentation. If the stuff to be
+ outdented includes all the contents of that element, get rid of it, but
+ if it has any attributes, change it to a <p> with those same attributes.
+ This is an excellent idea in general, but unfortunately it preserves
+ explicitly-specified margins in style attributes, which isn't great. In
+ other cases, it moves the stuff to be outdented outside. Not clear on
+ all the details, seems to be pretty confusing. Also does a bunch of
+ seemingly arbitrary normalization like removing divs and some attributes
+ from some things . . .
+ Firefox 4.0: Go to the innermost element causing indentation. If the stuff
+ to be outdented includes all the contents of that element, get rid of it,
+ even if it has arbitrary attributes. Otherwise, move the stuff to be
+ outdented outside the indenting element. If there are any intervening
+ elements that include stuff not to be outdented, wrap the outdented stuff
+ in copies (which can duplicate id's, etc.).
+ Chrome 12 dev: Go to the outermost element causing indentation (even if the
+ current element is itself causing indentation). Move the text to be
+ outdented outside that outermost element, without regard to any
+ intervening elements. Then recreate the original styles on the moved
+ text, in some fashion. Something like that; it confuses me and doesn't
+ seem to be reasonable.
+ Opera 11.00: Like Firefox, except it goes to the outermost element, not the
+ innermost. Also seems to special-case to avoid duplicate id's, and has a
+ few other quirks.
+
+ Overall, all flawed, so I'll make up my own, patterned after pushing down
+ styles. First we search ancestors for an indentation element, which we stand
+ a chance of completely removing. Failing that, we look for a potential
+ indentation element, which we cannot completely remove. -->
+ <li>Let <var title="">current ancestor</var> be <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>.
+
+ <li>Let <var title="">ancestor 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>While <var title="">current ancestor</var> is an <a href=#editable>editable</a> <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code>
+ that is not an <a href=#indentation-element>indentation element</a>, append <var title="">current
+ ancestor</var> to <var title="">ancestor list</var> and then set <var title="">current
+ ancestor</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>If <var title="">current ancestor</var> is not an <a href=#editable>editable</a>
+ <a href=#indentation-element>indentation element</a>:
+
+ <ol>
+ <li>Let <var title="">current ancestor</var> be <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>.
+
+ <li>Let <var title="">ancestor list</var> be the empty list.
+
+ <li>While <var title="">current ancestor</var> is an <a href=#editable>editable</a>
+ <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> that is not a <a href=#potential-indentation-element>potential indentation element</a>,
+ append <var title="">current ancestor</var> to <var title="">ancestor list</var> and then set
+ <var title="">current ancestor</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>.
+ </ol>
+
+ <li>If <var title="">current ancestor</var> is not an <a href=#editable>editable</a>
+ <a href=#potential-indentation-element>potential indentation element</a>, abort these steps.
+
+ <!-- At this point, we have an ancestor to split up. -->
+ <li>Append <var title="">current ancestor</var> to <var title="">ancestor list</var>.
+
+ <li>Let <var title="">original ancestor</var> be <var title="">current ancestor</var>.
+ <!-- We can't outdent it yet, because we need its children to remain intact
+ for the loop. -->
+
+ <li>While <var title="">ancestor list</var> is not empty:
+
+ <ol>
+ <li>Let <var title="">current ancestor</var> be the last member of <var title="">ancestor
+ list</var>.
+
+ <li>Remove the last member of <var title="">ancestor list</var>.
+
+ <li>Let <var title="">children</var> 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>children</a> of <var title="">current
+ ancestor</var>.
+
+ <li>For each <var title="">child</var> in <var title="">children</var>, if <var title="">child</var>
+ is neither <var title="">node</var> nor the last member of <var title="">ancestor list</var>,
+ <a href=#indent>indent</a> <var title="">child</var>.
+ </ol>
+
+ <li><a href=#outdent>Outdent</a> <var title="">original ancestor</var>.
</ol>
+<p>A <dfn id=potential-indentation-element>potential indentation element</dfn> is either 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>, or 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> that has 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 "margin" or some subproperty
+of it.
+
+<p>An <dfn id=indentation-element>indentation element</dfn> is an <a href=#potential-indentation-element>potential indentation
+element</a> that has no attributes other than one or more of
+
+<ul>
+ <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;
+
+ <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.
+</ul>
+
<dd><strong>State</strong>:
<dd><strong>Value</strong>:
--- a/implementation.js Thu Apr 28 15:01:23 2011 -0600
+++ b/implementation.js Sun May 01 15:04:13 2011 -0600
@@ -336,6 +336,70 @@
&& ["inline", "inline-block", "inline-table"].indexOf(getComputedStyle(node).display) != -1));
}
+function setTagName(element, newName) {
+ // "If element is an HTML element with local name equal to new name, return
+ // element."
+ if (isHtmlElement(element) && element.tagName == newName.toUpperCase()) {
+ return element;
+ }
+
+ // "If element's parent is null, return element."
+ if (!element.parentNode) {
+ return element;
+ }
+
+ // "Let replacement element be the result of calling createElement(new
+ // name) on the ownerDocument of element."
+ var replacementElement = element.ownerDocument.createElement(newName);
+
+ // "Insert replacement element into element's parent immediately before
+ // element."
+ element.parentNode.insertBefore(replacementElement, element);
+
+ // "Copy all attributes of element to replacement element, in order."
+ for (var i = 0; i < element.attributes.length; i++) {
+ replacementElement.setAttributeNS(element.attributes[i].namespaceURI, element.attributes[i].name, element.attributes[i].value);
+ }
+
+ // "While element has children, append the first child of element as the
+ // last child of replacement element, preserving ranges."
+ while (element.childNodes.length) {
+ movePreservingRanges(element.firstChild, replacementElement, replacementElement.childNodes.length);
+ }
+
+ // "Remove element from its parent."
+ element.parentNode.removeChild(element);
+
+ // "Return replacement element."
+ return replacementElement;
+}
+
+function removePreservingDescendants(node) {
+ // "Let children be a list of node's children."
+ var children = [].slice.call(node.childNodes);
+
+ // "If node's parent is null, remove all of node's children from node, then
+ // return children."
+ if (!node.parentNode) {
+ while (node.hasChildNodes()) {
+ node.removeChild(node.firstChild);
+ }
+ return children;
+ }
+
+ // "While node has children, insert the first child of node into node's
+ // parent immediately before node, preserving ranges."
+ while (node.hasChildNodes()) {
+ movePreservingRanges(node.firstChild, node.parentNode, getNodeIndex(node));
+ }
+
+ // "Remove node from its parent."
+ node.parentNode.removeChild(node);
+
+ // "Return children."
+ return children;
+}
+
// "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
@@ -2073,45 +2137,9 @@
nodeList.push(node);
}
- // "For each node in node list:"
+ // "Indent each member of node list."
for (var i = 0; i < nodeList.length; i++) {
- var node = nodeList[i];
-
- // "If the previousSibling of node is an HTML element; its
- // "display" property computes to "block"; its "margin-left" and
- // "margin-right" properties compute to "40px"; and its
- // "margin-top" and "margin-bottom" properties compute to "0"; then
- // append node as the last child of its previousSibling, preserving
- // ranges, and continue with the next node."
- if (isHtmlElement(node.previousSibling)) {
- var style = getComputedStyle(node.previousSibling);
- if (style.display == "block"
- && style.marginLeft == "40px"
- && style.marginRight == "40px"
- && style.marginTop == "0px"
- && style.marginBottom == "0px") {
- movePreservingRanges(node, node.previousSibling, node.previousSibling.childNodes.length);
- continue;
- }
- }
-
- // "Let tag be "div" if the CSS styling flag is true, otherwise
- // "blockquote"."
- var tag = cssStylingFlag ? "div" : "blockquote";
-
- // "Let new parent be the result of calling createElement(tag) on
- // the ownerDocument of node."
- var newParent = node.ownerDocument.createElement(tag);
-
- // "Insert new parent into node's parent immediately before node."
- node.parentNode.insertBefore(newParent, node);
-
- // "Set the CSS property "margin" of new parent to "0 40px"."
- newParent.setAttribute("style", "margin: 0 40px");
-
- // "Append node as the last child of new parent, preserving
- // ranges."
- movePreservingRanges(node, newParent, 0);
+ indentNode(nodeList[i]);
}
break;
@@ -2271,8 +2299,8 @@
// "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."
+ // "Let node list be all nodes contained in new range that have no
+ // children."
var nodeList = [];
for (
var node = newRange.startContainer;
@@ -2280,74 +2308,14 @@
node = nextNode(node)
) {
if (isContained(node, newRange)
- && !isContained(node.parentNode, newRange)) {
+ && !node.hasChildNodes()) {
nodeList.push(node);
}
}
- // "For each node in node list:"
+ // "Outdent each member of 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));
- }
+ outdentNode(nodeList[i]);
}
break;
@@ -2517,6 +2485,224 @@
}
}
+function indentNode(node) {
+ // "If the previousSibling of node is an HTML element; its
+ // "display" property computes to "block"; its "margin-left" and
+ // "margin-right" properties compute to "40px"; and its
+ // "margin-top" and "margin-bottom" properties compute to "0"; then
+ // append node as the last child of its previousSibling, preserving
+ // ranges, then abort these steps."
+ if (isHtmlElement(node.previousSibling)) {
+ var style = getComputedStyle(node.previousSibling);
+ if (style.display == "block"
+ && style.marginLeft == "40px"
+ && style.marginRight == "40px"
+ && style.marginTop == "0px"
+ && style.marginBottom == "0px") {
+ movePreservingRanges(node, node.previousSibling, node.previousSibling.childNodes.length);
+ return;
+ }
+ }
+
+ // "Let tag be "div" if the CSS styling flag is true, otherwise
+ // "blockquote"."
+ var tag = cssStylingFlag ? "div" : "blockquote";
+
+ // "Let new parent be the result of calling createElement(tag) on
+ // the ownerDocument of node."
+ var newParent = node.ownerDocument.createElement(tag);
+
+ // "Insert new parent into node's parent immediately before node."
+ node.parentNode.insertBefore(newParent, node);
+
+ // "Set the CSS property "margin" of new parent to "0 40px"."
+ newParent.setAttribute("style", "margin: 0 40px");
+
+ // "Append node as the last child of new parent, preserving
+ // ranges."
+ movePreservingRanges(node, newParent, 0);
+}
+
+function outdentNode(node) {
+ // "If node is not editable, abort these steps."
+ if (!isEditable(node)) {
+ return;
+ }
+
+ // "If node is an indentation element:"
+ if (isIndentationElement(node)) {
+ // "If node's last child and nextSibling are both inline nodes or its
+ // first child and previousSibling are both inline nodes, unset all
+ // attributes of node, then set the tag name of node to "div"."
+ if ((isInlineNode(node.lastChild) && isInlineNode(node.nextSibling))
+ || (isInlineNode(node.firstChild) && isInlineNode(node.previousSibling))) {
+ while (node.attributes.length) {
+ node.removeAttribute(node.attributes[0].name);
+ }
+
+ setTagName(node, "div");
+
+ // "Otherwise, remove node, preserving its descendants."
+ } else {
+ removePreservingDescendants(node);
+ }
+
+ // "Abort these steps."
+ return;
+ }
+
+ // "If node is a potential indentation element:"
+ if (isPotentialIndentationElement(node)) {
+ // "Set the tag name of node to "div"."
+ setTagName(node, "div");
+
+ // "Unset the class and dir attributes of node, if any."
+ node.removeAttribute("class");
+ node.removeAttribute("dir");
+
+ // "Unset the margin, padding, and border CSS properties of node."
+ node.style.margin = "";
+ node.style.padding = "";
+ node.style.border = "";
+ if (node.getAttribute("style") == "") {
+ node.removeAttribute("style");
+ }
+
+ // "Abort these steps."
+ return;
+ }
+
+ // "Let current ancestor be node's parent."
+ var currentAncestor = node.parentNode;
+
+ // "Let ancestor list be a list of nodes, initially empty."
+ var ancestorList = [];
+
+ // "While current ancestor is an editable Element that is not an
+ // indentation element, append current ancestor to ancestor list and then
+ // set current ancestor to its parent."
+ while (isEditable(currentAncestor)
+ && currentAncestor.nodeType == Node.ELEMENT_NODE
+ && !isIndentationElement(currentAncestor)) {
+ ancestorList.push(currentAncestor);
+ currentAncestor = currentAncestor.parentNode;
+ }
+
+ // "If current ancestor is not an editable indentation element:"
+ if (!isEditable(currentAncestor)
+ || !isIndentationElement(currentAncestor)) {
+ // "Let current ancestor be node's parent."
+ currentAncestor = node.parentNode;
+
+ // "Let ancestor list be the empty list."
+ ancestorList = [];
+
+ // "While current ancestor is an editable Element that is not a
+ // potential indentation element, append current ancestor to ancestor
+ // list and then set current ancestor to its parent."
+ while (isEditable(currentAncestor)
+ && currentAncestor.nodeType == Node.ELEMENT_NODE
+ && !isPotentialIndentationElement(currentAncestor)) {
+ ancestorList.push(currentAncestor);
+ currentAncestor = currentAncestor.parentNode;
+ }
+ }
+
+ // "If current ancestor is not an editable potential indentation element,
+ // abort these steps."
+ if (!isEditable(currentAncestor)
+ || !isPotentialIndentationElement(currentAncestor)) {
+ return;
+ }
+
+ // "Append current ancestor to ancestor list."
+ ancestorList.push(currentAncestor);
+
+ // "Let original ancestor be current ancestor."
+ var originalAncestor = currentAncestor;
+
+ // "While ancestor list is not empty:"
+ while (ancestorList.length) {
+ // "Let current ancestor be the last member of ancestor list."
+ //
+ // "Remove the last member of ancestor list."
+ currentAncestor = ancestorList.pop();
+
+ // "Let children be the children of current ancestor."
+ var children = [].slice.call(currentAncestor.childNodes);
+
+ // "For each child in children, if child is neither node nor the last
+ // member of ancestor list, indent child."
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (child != node && child != ancestorList[ancestorList.length - 1]) {
+ indentNode(child);
+ }
+ }
+ }
+
+ // "Outdent original ancestor."
+ outdentNode(originalAncestor);
+}
+
+// "A potential indentation element is either a blockquote, or a div that has a
+// style attribute that sets "margin" or some subproperty of it."
+function isPotentialIndentationElement(node) {
+ if (!isHtmlElement(node)) {
+ return false;
+ }
+
+ if (node.tagName == "BLOCKQUOTE") {
+ return true;
+ }
+
+ if (node.tagName != "DIV") {
+ return false;
+ }
+
+ for (var i = 0; i < node.style.length; i++) {
+ // Approximate check
+ if (/^(-[a-z]+-)?margin/.test(node.style[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// "An indentation element is a potential indentation element that has no
+// attributes other than one or more of
+//
+// * "a style attribute that sets no properties other than "margin", "border",
+// "padding", or subproperties of those;
+// * "a class attribute;
+// * "a dir attribute."
+function isIndentationElement(node) {
+ if (!isPotentialIndentationElement(node)) {
+ return false;
+ }
+
+ if (node.tagName != "BLOCKQUOTE" && node.tagName != "DIV") {
+ return false;
+ }
+
+ for (var i = 0; i < node.attributes.length; i++) {
+ if (!isHtmlNamespace(node.attributes[i].namespaceURI)
+ || ["style", "class", "dir"].indexOf(node.attributes[i].name) == -1) {
+ return false;
+ }
+ }
+
+ for (var i = 0; i < node.style.length; i++) {
+ // This is approximate, but it works well enough for my purposes.
+ if (!/^(-[a-z]+-)?(margin|border|padding)/.test(node.style[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
function myQueryCommandState(command) {
command = command.toLowerCase();
--- a/source.html Thu Apr 28 15:01:23 2011 -0600
+++ b/source.html Sun May 01 15:04:13 2011 -0600
@@ -172,6 +172,53 @@
<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>To <dfn>set the tag name</dfn> of an [[element]] <var>element</var> to
+<var>new name</var>, the user agent must run the following steps:
+
+<ol>
+ <li>If <var>element</var> is an <span>HTML element</span> with [[localname]]
+ equal to <var>new name</var>, return <var>element</var>.
+
+ <li>If <var>element</var>'s [[parent]] is null, return <var>element</var>.
+
+ <li>Let <var>replacement element</var> be the result of calling <code
+ data-anolis-spec=domcore
+ title=dom-Document-createElement>createElement(<var>new name</var>)</code> on
+ the [[ownerdocument]] of <var>element</var>.
+
+ <li>Insert <var>replacement element</var> into <var>element</var>'s
+ [[parent]] immediately before <var>element</var>.
+
+ <li>Copy all attributes of <var>element</var> to <var>replacement
+ element</var>, in order.
+
+ <li>While <var>element</var> has [[children]], append the first [[child]] of
+ <var>element</var> as the last [[child]] of <var>replacement element</var>,
+ <span>preserving ranges</span>.
+
+ <li>Remove <var>element</var> from its [[parent]].
+
+ <li>Return <var>replacement element</var>.
+</ol>
+
+<p>To remove a node <var>node</var> while <dfn>preserving its
+descendants</dfn>:
+
+<ol>
+ <li>Let <var>children</var> be a list of <var>node</var>'s [[children]].
+
+ <li>If <var>node</var>'s [[parent]] is null, remove all of <var>node</var>'s
+ [[children]] from <var>node</var>, then return <var>children</var>.
+
+ <li>While <var>node</var> has [[children]], insert the first [[child]] of
+ <var>node</var> into <var>node</var>'s [[parent]] immediately before
+ <var>node</var>, <span>preserving ranges</span>.
+
+ <li>Remove <var>node</var> from its [[parent]].
+
+ <li>Return <var>children</var>.
+</ol>
+
<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
@@ -1850,56 +1897,58 @@
[[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>:
-
- <ol>
- <li>If the [[previoussibling]] of <var>node</var> is an <span>HTML
- element</span>; its "display" property computes to "block"; its
- "margin-left" and "margin-right" properties compute to "40px"; and its
- "margin-top" and "margin-bottom" properties compute to "0"; then append
- <var>node</var> as the last [[child]] of its [[previoussibling]],
- <span>preserving ranges</span>, and continue with the next <var>node</var>.
-
- <li>Let <var>tag</var> be "div" if the <span>CSS styling flag</span> is
- true, otherwise "blockquote".
- <!-- Firefox 4.0 is the only tested browser that respects the CSS styling
- flag for indent at all. For indent, as for inline markup commands like
- bold, it will modify the inline style of existing elements if available,
- and only create spans/divs if there are no existing elements where it could
- add the style instead. For indent as for other commands, I follow WebKit
- and always create an extra element, to ensure consistency between CSS and
- non-CSS modes. -->
-
- <li>Let <var>new parent</var> be the result of calling <code
- data-anolis-spec=domcore
- title=dom-Document-createElement>createElement(<var>tag</var>)</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>Set the CSS property "margin" of <var>new parent</var> to "0 40px".
-
- <p class=XXX>This indents on both sides, so we don't have to worry about
- directionality. Preferably we should indent only on the start side, but
- 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
- 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>.
- </ol>
+ <li><span>Indent</span> each member of <var>node list</var>.
+</ol>
+
+<p>To <dfn>indent</dfn> a [[node]] <var>node</var>:
+
+<ol>
+ <li>If the [[previoussibling]] of <var>node</var> is an <span>HTML
+ element</span>; its "display" property computes to "block"; its
+ "margin-left" and "margin-right" properties compute to "40px"; and its
+ "margin-top" and "margin-bottom" properties compute to "0"; then append
+ <var>node</var> as the last [[child]] of its [[previoussibling]],
+ <span>preserving ranges</span>, then abort these steps.
+
+ <li>Let <var>tag</var> be "div" if the <span>CSS styling flag</span> is
+ true, otherwise "blockquote".
+ <!-- Firefox 4.0 is the only tested browser that respects the CSS styling
+ flag for indent at all. For indent, as for inline markup commands like
+ bold, it will modify the inline style of existing elements if available,
+ and only create spans/divs if there are no existing elements where it could
+ add the style instead. For indent as for other commands, I follow WebKit
+ and always create an extra element, to ensure consistency between CSS and
+ non-CSS modes. -->
+
+ <li>Let <var>new parent</var> be the result of calling <code
+ data-anolis-spec=domcore
+ title=dom-Document-createElement>createElement(<var>tag</var>)</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>Set the CSS property "margin" of <var>new parent</var> to "0 40px".
+
+ <p class=XXX>This indents on both sides, so we don't have to worry about
+ directionality. Preferably we should indent only on the start side, but
+ 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
+ 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>.
</ol>
<dd><strong>State</strong>:
@@ -2065,95 +2114,177 @@
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)
- -->
+ range</var> that have no [[children]].
+
+ <li><span>Outdent</span> each member of <var>node list</var>.
+</ol>
+
+<p>To <dfn>outdent</dfn> a [[node]] <var>node</var>:
+
+<!--
+Things that are produced for indentation that we need to consider removing:
+
+* 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>
+ <li>If <var>node</var> is not <span>editable</span>, abort these steps.
+
+ <p class=XXX>Handle this better for nested editable/non-editable.
+
+ <!-- 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 an <span>indentation element</span>:
+
<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>
+ <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>, unset all attributes of <var>node</var>, then <span>set the
+ tag name</span> of <var>node</var> to "div".
+ <!-- In this case, removing it entirely will result in its first or last
+ child becoming part of the previous or next sibling's line box, so we
+ want to keep a div. -->
+
+ <li>Otherwise, remove <var>node</var>, <span>preserving its
+ descendants</span>.
+
+ <li>Abort these steps.
</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>.
+ <li>If <var>node</var> is a <span>potential indentation element</span>:
+ <!-- This might be an indentation element that had style added to it by
+ Firefox in CSS mode, for instance (color, font-family, etc.). -->
+
+ <ol>
+ <li><span>Set the tag name</span> of <var>node</var> to "div".
+
+ <li>Unset the <code data-anolis-spec=html title=classes>class</code> and
+ <code data-anolis-spec=html title="the dir attribute">dir</code>
+ attributes of <var>node</var>, if any.
+
+ <li>Unset the margin, padding, and border CSS properties of
+ <var>node</var>.
+
+ <li>Abort these steps.
+ </ol>
+
+ <!-- Approximate algorithms when an ancestor is causing the indentation
+ appear to be:
+
+ IE9: Go to the innermost element causing indentation. If the stuff to be
+ outdented includes all the contents of that element, get rid of it, but
+ if it has any attributes, change it to a <p> with those same attributes.
+ This is an excellent idea in general, but unfortunately it preserves
+ explicitly-specified margins in style attributes, which isn't great. In
+ other cases, it moves the stuff to be outdented outside. Not clear on
+ all the details, seems to be pretty confusing. Also does a bunch of
+ seemingly arbitrary normalization like removing divs and some attributes
+ from some things . . .
+ Firefox 4.0: Go to the innermost element causing indentation. If the stuff
+ to be outdented includes all the contents of that element, get rid of it,
+ even if it has arbitrary attributes. Otherwise, move the stuff to be
+ outdented outside the indenting element. If there are any intervening
+ elements that include stuff not to be outdented, wrap the outdented stuff
+ in copies (which can duplicate id's, etc.).
+ Chrome 12 dev: Go to the outermost element causing indentation (even if the
+ current element is itself causing indentation). Move the text to be
+ outdented outside that outermost element, without regard to any
+ intervening elements. Then recreate the original styles on the moved
+ text, in some fashion. Something like that; it confuses me and doesn't
+ seem to be reasonable.
+ Opera 11.00: Like Firefox, except it goes to the outermost element, not the
+ innermost. Also seems to special-case to avoid duplicate id's, and has a
+ few other quirks.
+
+ Overall, all flawed, so I'll make up my own, patterned after pushing down
+ styles. First we search ancestors for an indentation element, which we stand
+ a chance of completely removing. Failing that, we look for a potential
+ indentation element, which we cannot completely remove. -->
+ <li>Let <var>current ancestor</var> be <var>node</var>'s [[parent]].
+
+ <li>Let <var>ancestor list</var> be a list of [[nodes]], initially empty.
+
+ <li>While <var>current ancestor</var> is an <span>editable</span> [[element]]
+ that is not an <span>indentation element</span>, append <var>current
+ ancestor</var> to <var>ancestor list</var> and then set <var>current
+ ancestor</var> to its [[parent]].
+
+ <li>If <var>current ancestor</var> is not an <span>editable</span>
+ <span>indentation element</span>:
+
+ <ol>
+ <li>Let <var>current ancestor</var> be <var>node</var>'s [[parent]].
+
+ <li>Let <var>ancestor list</var> be the empty list.
+
+ <li>While <var>current ancestor</var> is an <span>editable</span>
+ [[element]] that is not a <span>potential indentation element</span>,
+ append <var>current ancestor</var> to <var>ancestor list</var> and then set
+ <var>current ancestor</var> to its [[parent]].
+ </ol>
+
+ <li>If <var>current ancestor</var> is not an <span>editable</span>
+ <span>potential indentation element</span>, abort these steps.
+
+ <!-- At this point, we have an ancestor to split up. -->
+ <li>Append <var>current ancestor</var> to <var>ancestor list</var>.
+
+ <li>Let <var>original ancestor</var> be <var>current ancestor</var>.
+ <!-- We can't outdent it yet, because we need its children to remain intact
+ for the loop. -->
+
+ <li>While <var>ancestor list</var> is not empty:
+
+ <ol>
+ <li>Let <var>current ancestor</var> be the last member of <var>ancestor
+ list</var>.
+
+ <li>Remove the last member of <var>ancestor list</var>.
+
+ <li>Let <var>children</var> be the [[children]] of <var>current
+ ancestor</var>.
+
+ <li>For each <var>child</var> in <var>children</var>, if <var>child</var>
+ is neither <var>node</var> nor the last member of <var>ancestor list</var>,
+ <span>indent</span> <var>child</var>.
+ </ol>
+
+ <li><span>Outdent</span> <var>original ancestor</var>.
</ol>
+<p>A <dfn>potential indentation element</dfn> is either a [[blockquote]], or a
+[[div]] that has a [[style]] attribute that sets "margin" or some subproperty
+of it.
+
+<p>An <dfn>indentation element</dfn> is an <span>potential indentation
+element</span> that has no attributes other than one or more of
+
+<ul>
+ <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;
+
+ <li>a <code data-anolis-spec=html title="the dir attribute">dir</code>
+ attribute.
+</ul>
+
<dd><strong>State</strong>:
<dd><strong>Value</strong>: