Various random adjustments
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 13 Apr 2011 10:28:40 -0600
changeset 64 a191bb9c310c
parent 63 a0154fcf0f57
child 65 bc18f082f7f4
Various random adjustments

Including fixing IE, again. Three times.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- 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>: