--- a/autoimplementation.html Wed Apr 13 10:53:15 2011 -0600
+++ b/autoimplementation.html Wed Apr 13 12:30:04 2011 -0600
@@ -80,6 +80,13 @@
'{<p><p> <p>foo</p>}',
'foo[bar<i>baz]qoz</i>quz',
+ 'foo<span contenteditable=false>[bar]</span>baz',
+ 'fo[o<span contenteditable=false>bar</span>b]az',
+ 'fo[<b>o</b><span contenteditable=false>bar</span><b>b</b>]az',
+ '<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>',
+ '<span contenteditable=false>fo[o<span contenteditable=true>bar</span>b]az</span>',
+ '<span contenteditable=false>fo[<b>o<span contenteditable=true>bar</span>b</b>]az</span>',
+
'<table><tbody><tr><td>foo<td>b[a]r<td>baz</table>',
'<table><tbody><tr data-start=1 data-end=2><td>foo<td>bar<td>baz</table>',
'<table><tbody><tr data-start=0 data-end=2><td>foo<td>bar<td>baz</table>',
--- a/editcommands.html Wed Apr 13 10:53:15 2011 -0600
+++ b/editcommands.html Wed Apr 13 12:30:04 2011 -0600
@@ -165,10 +165,6 @@
Document, comments that are children of a Document, that sort of thing. Not
essential for prototyping, but needs to be cleaned up eventually.
- <li>I don't pay attention to whether designMode/contenteditable is actually
- set. I should be doing things like not doing anything if the selection isn't
- editable, making sure not to break out of contenteditable regions, etc.
-
<li>I haven't yet paid any attention to value parsing in most cases. This is
going to be important to specify exactly in some cases, like with colors.
@@ -185,10 +181,16 @@
<h2 id=definitions><span class=secno>3 </span>Definitions</h2>
-<p>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> is an <dfn id=html-element>HTML element</dfn> if 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> 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>. An <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#attr>Attr</a></code> is an <dfn id=html-attribute>HTML attribute</dfn> if its
-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-attr-namespace title=concept-attr-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=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>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>
+attribute set to the false state.
+
+<p>An <dfn id=editing-host>editing host</dfn> 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> that is <a href=#editable>editable</a>, and
+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 not <a href=#editable>editable</a>.
<p>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> is <dfn id=effectively-contained>effectively contained</dfn> in a <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> if either it
is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>; or it is the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
@@ -204,11 +206,11 @@
if they're effectively contained: it might be that the new node created by
splitText() is returned. -->
-<p>An <dfn id=unwrappable-element>unwrappable element</dfn> is an <a href=#html-element>HTML element</a> which may
-not be used where only <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#phrasing-content>phrasing content</a> is expected (not counting unknown or
+<p>An <dfn id=unwrappable-node>unwrappable node</dfn> is an <a href=#html-element>HTML element</a> which may not
+be used where only <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#phrasing-content>phrasing content</a> is expected (not counting unknown or
obsolete elements, which cannot be used at all); or any <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 something other than "inline", "inline-block", or
-"inline-table".
+"inline-table"; or any <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 not <a href=#editable>editable</a>.
<p class=XXX>Currently when we hit an unwrappable element, we ignore it and
alter its children. Alternatively, if we would otherwise create a span with a
@@ -536,8 +538,8 @@
<h2 id="clearing-an-element's-value"><span class=secno>5 </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, it must run
-the following steps:
+<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:
<p class=note>Clearing the value of an element can remove it from its parent
and put other nodes in its place. When implementations do something like clear
@@ -550,10 +552,6 @@
-->
<ol>
- <li>Let <var title="">element</var> be the <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> to be cleared.
-
- <li>Let <var title="">command</var> be as in the invoking algorithm.
-
<li>If <var title="">element</var>'s <a href=#specified-value>specified value</a> for
<var title="">command</var> is null, return the empty list. <!-- We want to abort
early so that we don't try unsetting background-color on a non-inline
@@ -677,8 +675,8 @@
<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 <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> and the
- <a href=#effective-value>effective value</a> of <var title="">property</var> is not <var title="">new
+ <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>
+ and the <a href=#effective-value>effective value</a> of <var title="">property</var> is not <var title="">new
value</var> on it, append <var title="">current ancestor</var> to <var title="">ancestor
list</var>, 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>.
@@ -764,8 +762,8 @@
<li>If <var title="">new value</var> is null, abort this algorithm.
<li>If <var title="">node</var> 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>, <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code>, <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code>, or
- <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#processinginstruction>ProcessingInstruction</a></code> node, and is not an <a href=#unwrappable-element>unwrappable
- element</a>:
+ <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#processinginstruction>ProcessingInstruction</a></code> node, and is not an <a href=#unwrappable-node>unwrappable
+ node</a>:
<ol>
<!-- Even if the value matches, we stick it in a preceding sibling if
@@ -868,7 +866,7 @@
<li>If the <a href=#effective-value>effective value</a> of <var title="">command</var> is <var title="">new
value</var> on <var title="">node</var>, abort this algorithm.
- <li>If <var title="">node</var> is an <a href=#unwrappable-element>unwrappable element</a>:
+ <li>If <var title="">node</var> is an <a href=#unwrappable-node>unwrappable node</a>:
<ol>
<li>Let <var title="">children</var> be all <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="">node</var>,
@@ -1102,8 +1100,8 @@
often we'll style several consecutive siblings in succession. In that case,
the element created for the first can be reused for the later ones.
-<p>This last step works a bit differently if the node is an <a href=#unwrappable-element>unwrappable
-element</a>. In that case, wrapping it in a simple styling element would
+<p>This last step works a bit differently if the node is an <a href=#unwrappable-node>unwrappable
+node</a>. In that case, wrapping it in a simple styling element would
make the document less conforming than it already was. Instead, we recursively
force style on its children. The recursion will terminate when we hit a node
that's wrappable, or when there are no further descendants.
@@ -1133,6 +1131,49 @@
effort. Is execCommand() even supposed to work on things that don't descend
from a document? Needs investigation.
+ <li>If <var title="">node</var> is not <a href=#editable>editable</a>:
+ <!--
+ IE9: Allows everything to be modified by execCommand(), regardless of whether
+ it's editable.
+ Firefox 4.0: Ignores execCommand() if the start and end of the selection are
+ not both editable. If the start and end are editable but something in the
+ middle is not, seems to relocate the non-editable part in the middle or
+ something like that.
+ Chrome 12 dev: Ignores execCommand() if the start and end of the selection
+ are not both editable. If the start and end are editable but something in
+ the middle is not, applies the given command but skips the non-editable
+ parts. But the state doesn't ignore the non-editable parts, so if you bold
+ such a selection you can't unbold it, for instance, since the middle part
+ will remain bold (so it will keep on trying to bold it instead of switching
+ to unbold).
+ Opera 11.00: Ignores execCommand() if the start and end of the selection are
+ not both editable. If the start and end are editable but something in the
+ middle is not, applies the command to everything, even the non-editable
+ part.
+
+ I chose to go with the non-IE behavior, per this discussion:
+ http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-April/031147.html
+ Ignoring non-editable things is convenient for the common use-case of an
+ editor, where you don't want the user to bold random parts of the UI when
+ they hit the bold button. For cases where it's not desired, you can always
+ turn designMode on briefly before using execCommand(), so the non-IE behavior
+ is a lot easier to work around than the IE behavior.
+
+ I don't see the value in ever just ignoring execCommand(). If the start and
+ end are not editable, I'm going to say you should still style any editable
+ nodes in between. I'm also going to ignore non-editable nodes for the
+ purposes of determining state, so (for instance) if all the editable nodes
+ are bolded, it will unbold instead of bolding.
+ -->
+
+ <ol>
+ <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="">node</var>.
+
+ <li><a href=#set-the-value>Set the value</a> of each member of <var title="">children</var>.
+
+ <li>Abort this algorithm.
+ </ol>
+
<li>If <var title="">node</var> 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>:
<ol>
@@ -1140,8 +1181,7 @@
nodes</var> be the result.
<li>For each <var title="">new node</var> in <var title="">new nodes</var>,
- <a href=#set-the-value>set the value</a> of <var title="">new node</var>, with the same inputs as
- this invocation of the algorithm.
+ <a href=#set-the-value>set the value</a> of <var title="">new node</var>.
<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, abort this algorithm.
</ol>
@@ -1274,8 +1314,8 @@
Otherwise, <a href=#set-the-value title="set the value">set their value</a> with <var title="">new
value</var> "bold".
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> at least 700. Otherwise false.
<!-- For bold and similar commands, IE 9 RC seems to consider the state true or
false depending on the first element. All other browsers follow the same
@@ -1695,8 +1735,8 @@
Otherwise, <a href=#set-the-value title="set the value">set their value</a> with <var title="">new
value</var> "italic".
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> either "italic" or "oblique". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.
@@ -1838,8 +1878,8 @@
<p class=XXX>Has all the same problems as underline.
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> "line-through". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.
@@ -1869,8 +1909,8 @@
<a href=#set-the-value>set the value</a> of each returned <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> with <var title="">new value</var>
"sub".
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> "sub". Otherwise false.
<dd><strong>Value</strong>:
@@ -1888,8 +1928,8 @@
<a href=#set-the-value>set the value</a> of each returned <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> with <var title="">new value</var>
"super".
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> "super". Otherwise false.
<dd><strong>Value</strong>:
@@ -1938,7 +1978,7 @@
underline letter-by-letter, it probably will vary. But sometimes when they
underline a bunch of text at once it will also vary, if the algorithm decides
to create multiple elements for whatever reason (like an intervening
- unwrappable element). This is unlikely to match user expectations. There's
+ unwrappable node). This is unlikely to match user expectations. There's
not much we can do about this without entirely revamping text-decoration, so
we'll have to live with it.
@@ -1953,8 +1993,8 @@
may be.
</div>
-<dd><strong>State</strong>: True if every <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 that is
-<a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
+<dd><strong>State</strong>: True if every <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#text>Text</a></code> node
+that is <a href=#effectively-contained>effectively contained</a> in the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> has <a href=#effective-value>effective
value</a> "underline". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.
--- a/implementation.js Wed Apr 13 10:53:15 2011 -0600
+++ b/implementation.js Wed Apr 13 12:30:04 2011 -0600
@@ -286,14 +286,32 @@
// Things defined in the edit command spec (i.e., the interesting stuff)
-// "A Node is an HTML element if it is an Element whose namespace is the HTML
-// namespace."
+// "An HTML element is an Element whose namespace is the HTML namespace."
function isHtmlElement(node) {
return node
&& node.nodeType == Node.ELEMENT_NODE
&& isHtmlNamespace(node.namespaceURI);
}
+// "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
+// contenteditable attribute set to the false state."
+function isEditable(node) {
+ // This is slightly a lie, because we're excluding non-HTML elements with
+ // contentEditable attributes. Maybe we want to, though . . .
+ return (node instanceof Element && node.contentEditable == "true")
+ || (node instanceof Document && node.designMode == "on")
+ || (node instanceof Node && node.contentEditable !== "false" && isEditable(node.parentNode));
+}
+
+// "An editing host is a node that is editable, and whose parent is not
+// editable."
+function isEditingHost(node) {
+ return node instanceof Node
+ && isEditable(node)
+ && !isEditable(node.parentNode);
+}
/**
* "A Node is effectively contained in a Range if either it is contained in the
@@ -327,14 +345,23 @@
return false;
}
-// "An unwrappable element is an HTML element which may not be used where only
+// "An unwrappable node is an HTML element which may not be used where only
// phrasing content is expected (not counting unknown or obsolete elements,
// which cannot be used at all); or any Element whose display property computes
-// to something other than "inline", "inline-block", or "inline-table"."
+// to something other than "inline", "inline-block", or "inline-table"; or any
+// node whose parent is not editable."
//
// I don't bother implementing this exactly, just well enough for testing.
-function isUnwrappableElement(node) {
- if (!node || node.nodeType != Node.ELEMENT_NODE) {
+function isUnwrappableNode(node) {
+ if (!node) {
+ return false;
+ }
+
+ if (!isEditable(node.parentNode)) {
+ return true;
+ }
+
+ if (node.nodeType != Node.ELEMENT_NODE) {
return false;
}
@@ -1112,10 +1139,10 @@
// "Let ancestor list be a list of Nodes, initially empty."
var ancestorList = [];
- // "While current ancestor is an Element and the effective value of
- // command is not new value on it, append current ancestor to ancestor
+ // "While current ancestor is an editable Element and the effective value
+ // of command is not new value on it, append current ancestor to ancestor
// list, then set current ancestor to its parent."
- while (currentAncestor
+ while (isEditable(currentAncestor)
&& currentAncestor.nodeType == Node.ELEMENT_NODE
&& !valuesEqual(command, getEffectiveValue(currentAncestor, command), newValue)) {
ancestorList.push(currentAncestor);
@@ -1209,12 +1236,12 @@
}
// "If node is an Element, Text, Comment, or ProcessingInstruction node,
- // and is not an unwrappable element:"
+ // and is not an unwrappable node:"
if ((node.nodeType == Node.ELEMENT_NODE
|| node.nodeType == Node.TEXT_NODE
|| node.nodeType == Node.COMMENT_NODE
|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE)
- && !isUnwrappableElement(node)) {
+ && !isUnwrappableNode(node)) {
// "Let candidate be node's previousSibling."
var candidate = node.previousSibling;
@@ -1331,8 +1358,8 @@
return;
}
- // "If node is an unwrappable element:"
- if (isUnwrappableElement(node)) {
+ // "If node is an unwrappable node:"
+ if (isUnwrappableNode(node)) {
// "Let children be all children of node, omitting any that are
// Elements whose specified value for command is neither null nor
// equal to new value."
@@ -1615,6 +1642,20 @@
return;
}
+ // "If node is not editable:"
+ if (!isEditable(node)) {
+ // "Let children be the children of node."
+ var children = Array.prototype.slice.call(node.childNodes);
+
+ // "Set the value of each member of children."
+ for (var i = 0; i < children.length; i++) {
+ setNodeValue(children[i], command, newValue);
+ }
+
+ // "Abort this algorithm."
+ return;
+ }
+
// "If node is an Element:"
if (node.nodeType == Node.ELEMENT_NODE) {
// "Clear the value of node, and let new nodes be the result."
@@ -2209,9 +2250,13 @@
continue;
}
+ if (!isEditable(node)) {
+ continue;
+ }
+
if (command == "bold") {
- // "True if every Text node that is effectively contained in the
- // range has effective value at least 700. Otherwise false."
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value at least 700. Otherwise false."
var fontWeight = getEffectiveValue(node, command);
if (fontWeight !== "bold"
&& fontWeight !== "700"
@@ -2220,8 +2265,8 @@
return false;
}
} else if (command == "italic") {
- // "True if every Text node that is effectively contained in the
- // range has effective value either "italic" or "oblique".
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value either "italic" or "oblique".
// Otherwise false."
var fontStyle = getEffectiveValue(node, command);
if (fontStyle !== "italic"
@@ -2229,29 +2274,30 @@
return false;
}
} else if (command == "strikethrough") {
- // "True if every Text node that is effectively contained in the
- // range has effective value "line-through". Otherwise false."
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value "line-through". Otherwise
+ // false."
var textDecoration = getEffectiveValue(node, command);
if (textDecoration !== "line-through") {
return false;
}
} else if (command == "underline") {
- // "True if every Text node that is effectively contained in the
- // range has effective value "underline". Otherwise false."
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value "underline". Otherwise false."
var textDecoration = getEffectiveValue(node, command);
if (textDecoration !== "underline") {
return false;
}
} else if (command == "subscript") {
- // "True if every Text node that is effectively contained in the
- // range has effective value "sub". Otherwise false."
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value "sub". Otherwise false."
var verticalAlign = getEffectiveValue(node, command);
if (verticalAlign !== "sub") {
return false;
}
} else if (command == "superscript") {
- // "True if every Text node that is effectively contained in the
- // range has effective value "super". Otherwise false."
+ // "True if every editable Text node that is effectively contained
+ // in the range has effective value "super". Otherwise false."
var verticalAlign = getEffectiveValue(node, command);
if (verticalAlign !== "super") {
return false;
--- a/source.html Wed Apr 13 10:53:15 2011 -0600
+++ b/source.html Wed Apr 13 12:30:04 2011 -0600
@@ -153,10 +153,6 @@
Document, comments that are children of a Document, that sort of thing. Not
essential for prototyping, but needs to be cleaned up eventually.
- <li>I don't pay attention to whether designMode/contenteditable is actually
- set. I should be doing things like not doing anything if the selection isn't
- editable, making sure not to break out of contenteditable regions, etc.
-
<li>I haven't yet paid any attention to value parsing in most cases. This is
going to be important to specify exactly in some cases, like with colors.
@@ -173,11 +169,19 @@
<h2>Definitions</h2>
-<p>A [[node]] is an <dfn>HTML element</dfn> if it is an [[element]] whose
-[[namespace]] is the [[htmlnamespace]]. An <code
-data-anolis-spec=domcore>Attr</code> is an <dfn>HTML attribute</dfn> if its
-<span data-anolis-spec=domcore title=concept-attr-namespace>namespace</span> is
-the [[htmlnamespace]].
+<p>An <dfn>HTML element</dfn> is an [[element]] whose [[namespace]] is the
+[[htmlnamespace]].
+
+<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
+data-anolis-spec=html>designMode</code> is enabled; or it is a [[node]] whose
+[[parent]] is <span>editable</span>, but which does not have a <code
+data-anolis-spec=html title=attr-contenteditable>contenteditable</code>
+attribute set to the false state.
+
+<p>An <dfn>editing host</dfn> is a [[node]] that is <span>editable</span>, and
+whose [[parent]] is not <span>editable</span>.
<p>A [[node]] is <dfn>effectively contained</dfn> in a [[range]] if either it
is [[contained]] in the [[range]]; or it is the [[range]]'s [[rangestart]]
@@ -193,11 +197,11 @@
if they're effectively contained: it might be that the new node created by
splitText() is returned. -->
-<p>An <dfn>unwrappable element</dfn> is an <span>HTML element</span> which may
-not be used where only [[phrasingcontent]] is expected (not counting unknown or
+<p>An <dfn>unwrappable node</dfn> is an <span>HTML element</span> which may not
+be used where only [[phrasingcontent]] is expected (not counting unknown or
obsolete elements, which cannot be used at all); or any [[element]] whose
display property computes to something other than "inline", "inline-block", or
-"inline-table".
+"inline-table"; or any [[node]] whose [[parent]] is not <span>editable</span>.
<p class=XXX>Currently when we hit an unwrappable element, we ignore it and
alter its children. Alternatively, if we would otherwise create a span with a
@@ -529,8 +533,8 @@
<h2>Clearing an element's value</h2>
-<p>When a user agent is to <dfn>clear the value</dfn> of an element, it must run
-the following steps:
+<p>When a user agent is to <dfn>clear the value</dfn> of an element
+<var>element</var>, it must run the following steps:
<p class=note>Clearing the value of an element can remove it from its parent
and put other nodes in its place. When implementations do something like clear
@@ -543,10 +547,6 @@
-->
<ol>
- <li>Let <var>element</var> be the [[element]] to be cleared.
-
- <li>Let <var>command</var> be as in the invoking algorithm.
-
<li>If <var>element</var>'s <span>specified value</span> for
<var>command</var> is null, return the empty list. <!-- We want to abort
early so that we don't try unsetting background-color on a non-inline
@@ -670,8 +670,8 @@
<li>Let <var>ancestor list</var> be a list of [[nodes]], initially empty.
- <li>While <var>current ancestor</var> is an [[element]] and the
- <span>effective value</span> of <var>property</var> is not <var>new
+ <li>While <var>current ancestor</var> is an <span>editable</span> [[element]]
+ and the <span>effective value</span> of <var>property</var> is not <var>new
value</var> on it, append <var>current ancestor</var> to <var>ancestor
list</var>, then set <var>current ancestor</var> to its [[parent]].
@@ -759,7 +759,7 @@
<li>If <var>node</var> is an [[element]], [[text]], [[comment]], or
[[processinginstruction]] node, and is not an <span>unwrappable
- element</span>:
+ node</span>:
<ol>
<!-- Even if the value matches, we stick it in a preceding sibling if
@@ -862,7 +862,7 @@
<li>If the <span>effective value</span> of <var>command</var> is <var>new
value</var> on <var>node</var>, abort this algorithm.
- <li>If <var>node</var> is an <span>unwrappable element</span>:
+ <li>If <var>node</var> is an <span>unwrappable node</span>:
<ol>
<li>Let <var>children</var> be all [[children]] of <var>node</var>,
@@ -1113,7 +1113,7 @@
the element created for the first can be reused for the later ones.
<p>This last step works a bit differently if the node is an <span>unwrappable
-element</span>. In that case, wrapping it in a simple styling element would
+node</span>. In that case, wrapping it in a simple styling element would
make the document less conforming than it already was. Instead, we recursively
force style on its children. The recursion will terminate when we hit a node
that's wrappable, or when there are no further descendants.
@@ -1144,6 +1144,49 @@
effort. Is execCommand() even supposed to work on things that don't descend
from a document? Needs investigation.
+ <li>If <var>node</var> is not <span>editable</span>:
+ <!--
+ IE9: Allows everything to be modified by execCommand(), regardless of whether
+ it's editable.
+ Firefox 4.0: Ignores execCommand() if the start and end of the selection are
+ not both editable. If the start and end are editable but something in the
+ middle is not, seems to relocate the non-editable part in the middle or
+ something like that.
+ Chrome 12 dev: Ignores execCommand() if the start and end of the selection
+ are not both editable. If the start and end are editable but something in
+ the middle is not, applies the given command but skips the non-editable
+ parts. But the state doesn't ignore the non-editable parts, so if you bold
+ such a selection you can't unbold it, for instance, since the middle part
+ will remain bold (so it will keep on trying to bold it instead of switching
+ to unbold).
+ Opera 11.00: Ignores execCommand() if the start and end of the selection are
+ not both editable. If the start and end are editable but something in the
+ middle is not, applies the command to everything, even the non-editable
+ part.
+
+ I chose to go with the non-IE behavior, per this discussion:
+ http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-April/031147.html
+ Ignoring non-editable things is convenient for the common use-case of an
+ editor, where you don't want the user to bold random parts of the UI when
+ they hit the bold button. For cases where it's not desired, you can always
+ turn designMode on briefly before using execCommand(), so the non-IE behavior
+ is a lot easier to work around than the IE behavior.
+
+ I don't see the value in ever just ignoring execCommand(). If the start and
+ end are not editable, I'm going to say you should still style any editable
+ nodes in between. I'm also going to ignore non-editable nodes for the
+ purposes of determining state, so (for instance) if all the editable nodes
+ are bolded, it will unbold instead of bolding.
+ -->
+
+ <ol>
+ <li>Let <var>children</var> be the [[children]] of <var>node</var>.
+
+ <li><span>Set the value</span> of each member of <var>children</var>.
+
+ <li>Abort this algorithm.
+ </ol>
+
<li>If <var>node</var> is an [[element]]:
<ol>
@@ -1151,8 +1194,7 @@
nodes</var> be the result.
<li>For each <var>new node</var> in <var>new nodes</var>,
- <span>set the value</span> of <var>new node</var>, with the same inputs as
- this invocation of the algorithm.
+ <span>set the value</span> of <var>new node</var>.
<li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
</ol>
@@ -1287,8 +1329,8 @@
Otherwise, <span title="set the value">set their value</span> with <var>new
value</var> "bold".
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> at least 700. Otherwise false.
<!-- For bold and similar commands, IE 9 RC seems to consider the state true or
false depending on the first element. All other browsers follow the same
@@ -1727,8 +1769,8 @@
Otherwise, <span title="set the value">set their value</span> with <var>new
value</var> "italic".
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> either "italic" or "oblique". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.
@@ -1870,8 +1912,8 @@
<p class=XXX>Has all the same problems as underline.
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> "line-through". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.
@@ -1901,8 +1943,8 @@
<span>set the value</span> of each returned [[node]] with <var>new value</var>
"sub".
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> "sub". Otherwise false.
<dd><strong>Value</strong>:
@@ -1920,8 +1962,8 @@
<span>set the value</span> of each returned [[node]] with <var>new value</var>
"super".
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> "super". Otherwise false.
<dd><strong>Value</strong>:
@@ -1971,7 +2013,7 @@
underline letter-by-letter, it probably will vary. But sometimes when they
underline a bunch of text at once it will also vary, if the algorithm decides
to create multiple elements for whatever reason (like an intervening
- unwrappable element). This is unlikely to match user expectations. There's
+ unwrappable node). This is unlikely to match user expectations. There's
not much we can do about this without entirely revamping text-decoration, so
we'll have to live with it.
@@ -1987,8 +2029,8 @@
may be.
</div>
-<dd><strong>State</strong>: True if every [[text]] node that is
-<span>effectively contained</span> in the [[range]] has <span>effective
+<dd><strong>State</strong>: True if every <span>editable</span> [[text]] node
+that is <span>effectively contained</span> in the [[range]] has <span>effective
value</span> "underline". Otherwise false.
<dd><strong>Value</strong>: Always the empty string.