--- a/editcommands.html Tue Jun 07 12:59:08 2011 -0600
+++ b/editcommands.html Tue Jun 07 13:25:30 2011 -0600
@@ -401,6 +401,16 @@
I think. Plus table stuff, since that can't be a descendant of a p either,
although it won't auto-close it. -->
+<p>A <dfn id=visible-node>visible node</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 either is a <a href=#prohibited-paragraph-child>prohibited
+paragraph child</a>, or 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 whose <code class=external data-anolis-spec=domcore title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> is not empty, or a
+<code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, 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> with a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendant</a> that is a
+<a href=#visible-node>visible node</a>. An <dfn id=invisible-node>invisible node</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
+not a <a href=#visible-node>visible node</a>.
+
+<p class=XXX>I don't know if this definition is really the way we want to do
+things. If it is, it needs some adjustment, like to handle collapsed
+whitespace nodes and collapsed br's.
+
<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> or string <var title="">child</var> is an <dfn id=allowed-child>allowed child</dfn> of a
<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> or string <var title="">parent</var> if the following algorithm returns true:
@@ -2478,14 +2488,38 @@
<!-- First go up as high as possible within the current block, then drill
down to the lowest possible level, in the hopes that we'll wind up at the end
of a text node, or maybe in a br or hr. -->
- <li>While <var title="">offset</var> is zero and <var title="">node</var> is not a
- <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, set <var title="">offset</var> to the
- <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">node</var>, then set <var title="">node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-
- <li>While <var title="">node</var> has a <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> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">offset</var>
- − 1 and that <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> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
- child</a>, set <var title="">node</var> to that <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>, then set
- <var title="">offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">node</var>.
+ <li>Repeat the following steps:
+
+ <ol>
+ <!--
+ If there's an invisible node somewhere, Firefox 5.0a2 removes that node and
+ then stops, so each backspace removes one invisible node. All others
+ remove the invisible node and then continue on looking for something
+ visible to remove. The spec follows the latter behavior, since it makes
+ more sense to the user. Of course, the definition of "invisible node" is
+ not necessarily anything like the spec's.
+ -->
+ <li>If <var title="">offset</var> is zero and <var title="">node</var>'s <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>
+ is an <a href=#editable>editable</a> <a href=#invisible-node>invisible node</a>, remove
+ <var title="">node</var>'s <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> 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>Otherwise, if <var title="">node</var> has a <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> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+ <var title="">offset</var> − 1 and that <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> is an <a href=#editable>editable</a>
+ <a href=#invisible-node>invisible node</a>, remove that <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> from <var title="">node</var>,
+ then subtract one from <var title="">offset</var>.
+
+ <li>Otherwise, if <var title="">offset</var> is zero and <var title="">node</var> is not a
+ <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, set <var title="">offset</var> to the
+ <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">node</var>, then set <var title="">node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+ <li>Otherwise, if <var title="">node</var> has a <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> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+ <var title="">offset</var> − 1 and that <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> is not a <a href=#prohibited-paragraph-child>prohibited
+ paragraph child</a> or a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> or an <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, set <var title="">node</var> to
+ that <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>, then set <var title="">offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of
+ <var title="">node</var>.
+
+ <li>Otherwise, break from this loop.
+ </ol>
<li>If <var title="">node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node and <var title="">offset</var> is not zero,
call <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, <var title="">offset</var>)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
@@ -2494,14 +2528,11 @@
(<var title="">node</var>, <var title="">offset</var>) and abort these steps.
<li>If <var title="">node</var> has a <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> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">offset</var>
- − 1 and that <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> is an <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code>, set <var title="">node</var> to that
- <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>.
-
- <li>If <var title="">node</var> is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> or <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, call
- <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, 0)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>. Then <a href=#delete-the-contents>delete
- the contents</a> of the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> (<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of
- <var title="">node</var>, <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">node</var>) and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> (<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>
- of <var title="">node</var>, 1 + <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">node</var>) and abort these steps.
+ − 1 and that <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> is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> or <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, call
+ <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, <var title="">offset</var>)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
+ Then <a href=#delete-the-contents>delete the contents</a> of the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
+ (<var title="">node</var>, <var title="">offset</var> − 1) and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+ (<var title="">node</var>, <var title="">offset</var>) and abort these steps.
<li>If <var title="">node</var> is a <a href=#prohibited-paragraph-child>prohibited paragraph child</a> and
<var title="">offset</var> is zero:
--- a/implementation.js Tue Jun 07 12:59:08 2011 -0600
+++ b/implementation.js Tue Jun 07 13:25:30 2011 -0600
@@ -1295,6 +1295,31 @@
return isHtmlElement(node, prohibitedParagraphChildNames);
}
+// "A visible node is a node that either is a prohibited paragraph child, or a
+// Text node whose data is not empty, or a br or img, or any node with a
+// descendant that is a visible node."
+function isVisibleNode(node) {
+ if (!node) {
+ return false;
+ }
+ if (isProhibitedParagraphChild(node)
+ || (node.nodeType == Node.TEXT_NODE && node.length)
+ || isHtmlElement(node, ["br", "img"])) {
+ return true;
+ }
+ for (var i = 0; i < node.childNodes.length; i++) {
+ if (isVisibleNode(node.childNodes[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// "An invisible node is a node that is not a visible node."
+function isInvisibleNode(node) {
+ return node && !isVisibleNode(node);
+}
+
function isAllowedChild(child, parent_) {
// "If parent is "colgroup", "table", "tbody", "tfoot", "thead", "tr", or
// an HTML element with local name equal to one of those, and child is a
@@ -3167,22 +3192,47 @@
var node = range.startContainer;
var offset = range.startOffset;
- // "While offset is zero and node is not a prohibited paragraph child,
- // set offset to the index of node, then set node to its parent."
- while (offset == 0
- && !isProhibitedParagraphChild(node)) {
- offset = getNodeIndex(node);
- node = node.parentNode;
- }
-
- // "While node has a child with index offset − 1 and that child is not
- // a prohibited paragraph child, set node to that child, then set
- // offset to the length of node."
- while (0 <= offset - 1
- && offset - 1 < node.childNodes.length
- && !isProhibitedParagraphChild(node.childNodes[offset - 1])) {
- node = node.childNodes[offset - 1];
- offset = getNodeLength(node);
+ // "Repeat the following steps:"
+ while (true) {
+ // "If offset is zero and node's previousSibling is an editable
+ // invisible node, remove node's previousSibling from its parent."
+ if (offset == 0
+ && isEditable(node.previousSibling)
+ && isInvisibleNode(node.previousSibling)) {
+ node.parentNode.removeChild(node.previousSibling);
+
+ // "Otherwise, if node has a child with index offset − 1 and that
+ // child is an editable invisible node, remove that child from
+ // node, then subtract one from offset."
+ } else if (0 <= offset - 1
+ && offset - 1 < node.childNodes.length
+ && isEditable(node.childNodes[offset - 1])
+ && isInvisibleNode(node.childNodes[offset - 1])) {
+ node.removeChild(node.childNodes[offset - 1]);
+ offset--;
+
+ // "Otherwise, if offset is zero and node is not a prohibited
+ // paragraph child, set offset to the index of node, then set node
+ // to its parent."
+ } else if (offset == 0
+ && !isProhibitedParagraphChild(node)) {
+ offset = getNodeIndex(node);
+ node = node.parentNode;
+
+ // "Otherwise, if node has a child with index offset − 1 and that
+ // child is not a prohibited paragraph child or a br or an img, set
+ // node to that child, then set offset to the length of node."
+ } else if (0 <= offset - 1
+ && offset - 1 < node.childNodes.length
+ && !isProhibitedParagraphChild(node.childNodes[offset - 1])
+ && !isHtmlElement(node.childNodes[offset - 1], ["br", "img"])) {
+ node = node.childNodes[offset - 1];
+ offset = getNodeLength(node);
+
+ // "Otherwise, break from this loop."
+ } else {
+ break;
+ }
}
// "If node is a Text node and offset is not zero, call collapse(node,
@@ -3197,22 +3247,16 @@
return;
}
- // "If node has a child with index offset − 1 and that child is an hr,
- // set node to that child."
- if (0 <= offset -1
+ // "If node has a child with index offset − 1 and that child is a br or
+ // hr or img, call collapse(node, offset) on the Selection. Then delete
+ // the contents of the range with start (node, offset − 1) and end
+ // (node, offset) and abort these steps."
+ if (0 <= offset - 1
&& offset - 1 < node.childNodes.length
- && isHtmlElement(node.childNodes[offset - 1], "hr")) {
- node = node.childNodes[offset - 1];
- }
-
- // "If node is a br or hr or img, call collapse(node, 0) on the
- // Selection. Then delete the contents of the range with start (parent
- // of node, index of node) and end (parent of node, 1 + index of node)
- // and abort these steps."
- if (isHtmlElement(node, ["br", "hr", "img"])) {
- range.setStart(node, 0);
- range.setEnd(node, 0);
- deleteContents(node.parentNode, getNodeIndex(node), node.parentNode, 1 + getNodeIndex(node));
+ && isHtmlElement(node.childNodes[offset - 1], ["br", "hr", "img"])) {
+ range.setStart(node, offset);
+ range.setEnd(node, offset);
+ deleteContents(node, offset - 1, node, offset);
return;
}
--- a/source.html Tue Jun 07 12:59:08 2011 -0600
+++ b/source.html Tue Jun 07 13:25:30 2011 -0600
@@ -345,6 +345,16 @@
I think. Plus table stuff, since that can't be a descendant of a p either,
although it won't auto-close it. -->
+<p>A <dfn>visible node</dfn> is a [[node]] that either is a <span>prohibited
+paragraph child</span>, or a [[text]] node whose [[cddata]] is not empty, or a
+[[br]] or [[img]], or any [[node]] with a [[descendant]] that is a
+<span>visible node</span>. An <dfn>invisible node</dfn> is a [[node]] that is
+not a <span>visible node</span>.
+
+<p class=XXX>I don't know if this definition is really the way we want to do
+things. If it is, it needs some adjustment, like to handle collapsed
+whitespace nodes and collapsed br's.
+
<p>A [[node]] or string <var>child</var> is an <dfn>allowed child</dfn> of a
[[node]] or string <var>parent</var> if the following algorithm returns true:
@@ -2459,14 +2469,38 @@
<!-- First go up as high as possible within the current block, then drill
down to the lowest possible level, in the hopes that we'll wind up at the end
of a text node, or maybe in a br or hr. -->
- <li>While <var>offset</var> is zero and <var>node</var> is not a
- <span>prohibited paragraph child</span>, set <var>offset</var> to the
- [[index]] of <var>node</var>, then set <var>node</var> to its [[parent]].
-
- <li>While <var>node</var> has a [[child]] with [[index]] <var>offset</var>
- − 1 and that [[child]] is not a <span>prohibited paragraph
- child</span>, set <var>node</var> to that [[child]], then set
- <var>offset</var> to the [[nodelength]] of <var>node</var>.
+ <li>Repeat the following steps:
+
+ <ol>
+ <!--
+ If there's an invisible node somewhere, Firefox 5.0a2 removes that node and
+ then stops, so each backspace removes one invisible node. All others
+ remove the invisible node and then continue on looking for something
+ visible to remove. The spec follows the latter behavior, since it makes
+ more sense to the user. Of course, the definition of "invisible node" is
+ not necessarily anything like the spec's.
+ -->
+ <li>If <var>offset</var> is zero and <var>node</var>'s [[previoussibling]]
+ is an <span>editable</span> <span>invisible node</span>, remove
+ <var>node</var>'s [[previoussibling]] from its [[parent]].
+
+ <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+ <var>offset</var> − 1 and that [[child]] is an <span>editable</span>
+ <span>invisible node</span>, remove that [[child]] from <var>node</var>,
+ then subtract one from <var>offset</var>.
+
+ <li>Otherwise, if <var>offset</var> is zero and <var>node</var> is not a
+ <span>prohibited paragraph child</span>, set <var>offset</var> to the
+ [[index]] of <var>node</var>, then set <var>node</var> to its [[parent]].
+
+ <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+ <var>offset</var> − 1 and that [[child]] is not a <span>prohibited
+ paragraph child</span> or a [[br]] or an [[img]], set <var>node</var> to
+ that [[child]], then set <var>offset</var> to the [[nodelength]] of
+ <var>node</var>.
+
+ <li>Otherwise, break from this loop.
+ </ol>
<li>If <var>node</var> is a [[text]] node and <var>offset</var> is not zero,
call [[selcollapse|<var>node</var>, <var>offset</var>]] on the [[selection]].
@@ -2475,14 +2509,11 @@
(<var>node</var>, <var>offset</var>) and abort these steps.
<li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var>
- − 1 and that [[child]] is an [[hr]], set <var>node</var> to that
- [[child]].
-
- <li>If <var>node</var> is a [[br]] or [[hr]] or [[img]], call
- [[selcollapse|<var>node</var>, 0]] on the [[selection]]. Then <span>delete
- the contents</span> of the [[range]] with [[rangestart]] ([[parent]] of
- <var>node</var>, [[index]] of <var>node</var>) and [[rangeend]] ([[parent]]
- of <var>node</var>, 1 + [[index]] of <var>node</var>) and abort these steps.
+ − 1 and that [[child]] is a [[br]] or [[hr]] or [[img]], call
+ [[selcollapse|<var>node</var>, <var>offset</var>]] on the [[selection]].
+ Then <span>delete the contents</span> of the [[range]] with [[rangestart]]
+ (<var>node</var>, <var>offset</var> − 1) and [[rangeend]]
+ (<var>node</var>, <var>offset</var>) and abort these steps.
<li>If <var>node</var> is a <span>prohibited paragraph child</span> and
<var>offset</var> is zero:
--- a/tests.js Tue Jun 07 12:59:08 2011 -0600
+++ b/tests.js Tue Jun 07 13:25:30 2011 -0600
@@ -250,9 +250,14 @@
'<p>foo</p><br><br><p>[]bar</p>',
'<p>foo</p><img src=/img/lion.svg><p>[]bar',
'foo<img src=/img/lion.svg>[]bar',
+
+ // Invisible stuff
'foo<span></span>[]bar',
'foo<span><span></span></span>[]bar',
'foo<quasit></quasit>[]bar',
+ 'foo<br><span></span>[]bar',
+ '<span>foo<span></span></span>[]bar',
+ 'foo<span></span><span>[]bar</span>',
'foo[bar]baz',