--- a/autoimplementation.html Wed Apr 13 08:47:27 2011 -0600
+++ b/autoimplementation.html Wed Apr 13 10:28:40 2011 -0600
@@ -16,10 +16,10 @@
#purple { color: purple }
td > div:first-child {
padding-bottom: 0.2em;
- border-bottom: 1px solid black;
}
td > div:last-child {
padding-top: 0.2em;
+ border-top: 1px solid black;
}
/* https://bugs.webkit.org/show_bug.cgi?id=56670 */
dfn { font-style: italic }
@@ -397,6 +397,10 @@
inserthorizontalrule: [
'foo[]bar',
'<span>foo</span>{}<span>bar</span>',
+ '<div><b>foo</b>{}<b>bar</b></div>',
+ '<div><b>foo</b>{<b>bar</b>}<b>baz</b></div>',
+ '<b>foo[]bar</b>',
+ '<b id=abc>foo[]bar</b>',
["abc", 'foo[bar]baz'],
'foo[bar]baz',
'foo<b>{bar}</b>baz',
@@ -997,7 +1001,11 @@
myExecCommand("styleWithCSS", false, styleWithCss);
myExecCommand(command, false, value, range);
addBrackets(range);
- specCell.lastChild.textContent = specCell.firstChild.innerHTML;
+ if (specCell.childNodes.length == 2) {
+ specCell.lastChild.textContent = specCell.firstChild.innerHTML;
+ } else {
+ throw "The cell didn't have two children. Did something spill outside the test div?";
+ }
} catch (e) {
specCell.lastChild.style.color = "red";
specCell.lastChild.style.fontWeight = "bold";
@@ -1041,10 +1049,15 @@
tr.appendChild(browserCell);
try {
var points = setupCell(browserCell, test);
+ var testDiv = browserCell.firstChild;
// Work around weird Firefox bug:
// https://bugzilla.mozilla.org/show_bug.cgi?id=649138
- var testDiv = browserCell.firstChild;
- document.getElementById(command).appendChild(testDiv);
+ // But only with removeFormat, because it tends to cause weird behavior
+ // sometimes if content winds up being put outside the div for some
+ // reason: then it's mysteriously below the table.
+ if (command == "removeformat") {
+ document.getElementById(command).appendChild(testDiv);
+ }
setSelection(points[0], points[1], points[2], points[3]);
try {
document.execCommand("styleWithCSS", false, styleWithCss);
@@ -1053,12 +1066,20 @@
if (getSelection().rangeCount) {
addBrackets(getSelection().getRangeAt(0));
}
- browserCell.insertBefore(testDiv, browserCell.firstChild);
- browserCell.lastChild.textContent = browserCell.firstChild.innerHTML;
+ if (testDiv.parentNode != browserCell) {
+ browserCell.insertBefore(testDiv, browserCell.firstChild);
+ }
+ if (browserCell.childNodes.length == 2) {
+ browserCell.lastChild.textContent = browserCell.firstChild.innerHTML;
+ } else {
+ throw "The cell didn't have two children. Did something spill outside the test div?";
+ }
} catch (e) {
- browserCell.textContent = "Exception: " + e;
- if (testDiv && testDiv.parentNode) {
- testDiv.parentNode.removeChild(testDiv);
+ browserCell.lastChild.style.color = "red";
+ browserCell.lastChild.style.fontWeight = "bold";
+ browserCell.lastChild.textContent = "Exception: " + e;
+ if (testDiv && testDiv.parentNode != browserCell) {
+ browserCell.insertBefore(testDiv, browserCell.firstChild);
}
}
}
@@ -1085,6 +1106,7 @@
.replace(/<(\/?)strike/g, '<$1s')
.replace(/<(\/?)em/g, '<$1i')
.replace(/#[0-9a-fA-F]{6}/g, function(match) { return match.toUpperCase(); })
+ .replace(/ size="2" width="100%"/g, '')
.replace(/ class="Apple-style-span"/g, "");
if (navigator.userAgent.indexOf("MSIE") != -1) {
// IE produces <font style> instead of <span style>, so let's
@@ -1296,8 +1318,14 @@
&& range.endContainer.childNodes[range.endOffset - 1].nodeType == Node.TEXT_NODE) {
range.endContainer.childNodes[range.endOffset - 1].appendData("}");
} else {
- // No idea how this will work for tables . . .
- range.endContainer.insertBefore(document.createTextNode("}"), range.endContainer.childNodes[range.endOffset]);
+ // Seems to serialize as I'd want even for tables . . . IE doesn't
+ // allow undefined to be passed as the second argument (it throws
+ // an exception), so we have to explicitly check the number of
+ // children and pass null.
+ range.endContainer.insertBefore(document.createTextNode("}"),
+ range.endContainer.childNodes.length == range.endOffset
+ ? null
+ : range.endContainer.childNodes[range.endOffset]);
}
}
if (range.startContainer.nodeType == Node.TEXT_NODE) {
@@ -1310,7 +1338,10 @@
&& range.startContainer.childNodes[range.startOffset - 1].nodeType == Node.TEXT_NODE) {
range.startContainer.childNodes[range.startOffset - 1].appendData("{");
} else {
- range.startContainer.insertBefore(document.createTextNode("{"), range.startContainer.childNodes[range.startOffset]);
+ range.startContainer.insertBefore(document.createTextNode("{"),
+ range.startContainer.childNodes.length == range.startOffset
+ ? null
+ : range.startContainer.childNodes[range.startOffset]);
}
}
}
--- a/editcommands.html Wed Apr 13 08:47:27 2011 -0600
+++ b/editcommands.html Wed Apr 13 10:28:40 2011 -0600
@@ -1578,7 +1578,8 @@
acts more or less like the spec. IE9 and Chrome 12 dev treat the value as an
id, which is weird and probably useless, so I don't do it. Firefox 4.0
produces <hr size=2 width=100%> instead of <hr>, which is also weird and almost
-definitely useless, so I don't do it. -->
+definitely useless, so I don't do it. Then you have the varying behavior in
+splitting up parents to ensure validity . . . -->
<ol>
<li>Run <code class=external data-anolis-spec=domrange title=dom-Range-deleteContents><a href=http://html5.org/specs/dom-range.html#dom-range-deletecontents>deleteContents()</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>.
@@ -1587,7 +1588,7 @@
<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>.
<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> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node and 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>
- is null, abort these steps and do nothing.
+ is null, abort these steps and do nothing further.
<li>Let <var title="">hr</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("hr")</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> (or on <var title="">node</var> itself if it's a
@@ -1608,6 +1609,18 @@
<li>Otherwise, let <var title="">child</var> be the <var title="">offset</var>th child of
<var title="">node</var> (or null if there is no such child), and run <code class=external data-anolis-spec=domcore title=dom-Node-insertBefore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-insertbefore>insertBefore(<var title="">hr</var>,
<var title="">child</var>)</a></code> on <var title="">node</var>.
+
+ <p class=XXX>This is wrong: it can insert the new element inside an inline
+ element, which is invalid. Browsers break up ancestor inlines to avoid this.
+ So far I've managed to avoid having to do this, which is good because it
+ causes problems (e.g., what if an inline ancestor has an id?), but I don't
+ see any way around it here. Maybe I need to revisit the insistence on only
+ modifying "modifiable elements"; if non-modifiable elements sneak in somehow,
+ perhaps we need to break them up anyway sometimes.
+
+ <li>Run <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse()</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>, with
+ first argument equal to the <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="">hr</var> and the second
+ argument equal to one plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">hr</var>.
</ol>
<dd><strong>State</strong>:
@@ -1659,6 +1672,10 @@
<li>Otherwise, let <var title="">child</var> be the <var title="">offset</var>th child of
<var title="">node</var> (or null if there is no such child), and run <code class=external data-anolis-spec=domcore title=dom-Node-insertBefore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-insertbefore>insertBefore(<var title="">img</var>,
<var title="">child</var>)</a></code> on <var title="">node</var>.
+
+ <li>Run <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse()</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>, with
+ first argument equal to the <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="">img</var> and the second
+ argument equal to one plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">img</var>.
</ol>
<dd><strong>State</strong>:
@@ -1782,6 +1799,11 @@
of the resulting <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>, with <var title="">command</var> and <var title="">new value</var>
as given.
+ <p class=XXX>This has no relationship to what browsers actually do, although
+ it mostly works okay. If I don't throw it out and replace it with something
+ more like what browsers do, it still probably needs refinement to handle some
+ elements that it doesn't deal with yet.
+
<table>
<tr><th><var title="">command</var> <th><var title="">new value</var>
<tr><td>subscript <td>"baseline"
--- a/implementation.js Wed Apr 13 08:47:27 2011 -0600
+++ b/implementation.js Wed Apr 13 10:28:40 2011 -0600
@@ -173,9 +173,24 @@
// Functions for stuff in DOM Range
function getNodeIndex(node) {
var ret = 0;
- while (node != node.parentNode.childNodes[ret]) {
+ // These are no-ops to avoid a completely ridiculous bug in IE where
+ // sometimes a node is not actually equal to any of its parents' children.
+ // Somehow this makes it go away. Sigh.
+ if (node.nextSibling) {
+ node = node.nextSibling.previousSibling;
+ } else if (node.previousSibling) {
+ node = node.previousSibling.nextSibling;
+ } else {
+ node = node.parentNode.firstChild;
+ }
+ while (ret < node.parentNode.childNodes.length && node != node.parentNode.childNodes[ret]) {
ret++;
}
+ if (ret >= node.parentNode.childNodes.length) {
+ // This actually happens in IE sometimes (although hopefully not with
+ // my workaround in place).
+ throw "node is not equal to any of its parents' children";
+ }
return ret;
}
@@ -917,8 +932,13 @@
if (range.endContainer.nodeType == Node.TEXT_NODE
&& range.endOffset != 0
&& range.endOffset != getNodeLength(range.endContainer)) {
+ // IE seems to mutate the range incorrectly here, so we need correction
+ // here as well.
+ var newStart = [range.startContainer, range.endContainer];
+ var newEnd = [range.endContainer, range.endOffset];
range.endContainer.splitText(range.endOffset);
- // No correction should be needed here
+ range.setStart(newStart[0], newStart[1]);
+ range.setEnd(newEnd[0], newEnd[1]);
}
// "Let cloned range be the result of calling cloneRange() on range."
@@ -1878,6 +1898,18 @@
: node.childNodes[offset];
node.insertBefore(hr, child);
}
+
+ // "Run collapse() on the Selection, with first argument equal to the
+ // parent of hr and the second argument equal to one plus the index of
+ // hr."
+ //
+ // Not everyone actually supports collapse(), so we do it manually
+ // instead. Also, we need to modify the actual range we're given as
+ // well, for the sake of autoimplementation.html's range-filling-in.
+ range.setStart(hr.parentNode, 1 + getNodeIndex(hr));
+ range.setEnd(hr.parentNode, 1 + getNodeIndex(hr));
+ getSelection().removeAllRanges();
+ getSelection().addRange(range);
break;
case "insertimage":
@@ -1938,6 +1970,23 @@
: node.childNodes[offset];
node.insertBefore(img, child);
}
+
+ // "Run collapse() on the Selection, with first argument equal to the
+ // parent of img and the second argument equal to one plus the index of
+ // img."
+ //
+ // Not everyone actually supports collapse(), so we do it manually
+ // instead. Also, we need to modify the actual range we're given as
+ // well, for the sake of autoimplementation.html's range-filling-in.
+ range.setStart(img.parentNode, 1 + getNodeIndex(img));
+ range.setEnd(img.parentNode, 1 + getNodeIndex(img));
+ getSelection().removeAllRanges();
+ getSelection().addRange(range);
+
+ // IE adds width and height attributes for some reason, so remove those
+ // to actually do what the spec says.
+ img.removeAttribute("width");
+ img.removeAttribute("height");
break;
case "italic":
--- a/source.html Wed Apr 13 08:47:27 2011 -0600
+++ b/source.html Wed Apr 13 10:28:40 2011 -0600
@@ -1591,7 +1591,8 @@
acts more or less like the spec. IE9 and Chrome 12 dev treat the value as an
id, which is weird and probably useless, so I don't do it. Firefox 4.0
produces <hr size=2 width=100%> instead of <hr>, which is also weird and almost
-definitely useless, so I don't do it. -->
+definitely useless, so I don't do it. Then you have the varying behavior in
+splitting up parents to ensure validity . . . -->
<ol>
<li>Run <code data-anolis-spec=domrange
@@ -1601,7 +1602,7 @@
[[rangestart]].
<li>If <var>node</var> is a [[text]] or [[comment]] node and its [[parent]]
- is null, abort these steps and do nothing.
+ is null, abort these steps and do nothing further.
<li>Let <var>hr</var> be the result of calling <code
data-anolis-spec=domcore
@@ -1629,6 +1630,19 @@
data-anolis-spec=domcore
title=dom-Node-insertBefore>insertBefore(<var>hr</var>,
<var>child</var>)</code> on <var>node</var>.
+
+ <p class=XXX>This is wrong: it can insert the new element inside an inline
+ element, which is invalid. Browsers break up ancestor inlines to avoid this.
+ So far I've managed to avoid having to do this, which is good because it
+ causes problems (e.g., what if an inline ancestor has an id?), but I don't
+ see any way around it here. Maybe I need to revisit the insistence on only
+ modifying "modifiable elements"; if non-modifiable elements sneak in somehow,
+ perhaps we need to break them up anyway sometimes.
+
+ <li>Run <code data-anolis-spec=domrange
+ title=dom-Selection-collapse>collapse()</code> on the [[selection]], with
+ first argument equal to the [[parent]] of <var>hr</var> and the second
+ argument equal to one plus the [[index]] of <var>hr</var>.
</ol>
<dd><strong>State</strong>:
@@ -1689,6 +1703,11 @@
data-anolis-spec=domcore
title=dom-Node-insertBefore>insertBefore(<var>img</var>,
<var>child</var>)</code> on <var>node</var>.
+
+ <li>Run <code data-anolis-spec=domrange
+ title=dom-Selection-collapse>collapse()</code> on the [[selection]], with
+ first argument equal to the [[parent]] of <var>img</var> and the second
+ argument equal to one plus the [[index]] of <var>img</var>.
</ol>
<dd><strong>State</strong>: