--- a/autoimplementation.html Mon Apr 11 14:48:46 2011 -0600
+++ b/autoimplementation.html Tue Apr 12 14:55:38 2011 -0600
@@ -25,6 +25,7 @@
dfn { font-style: italic }
/* We don't want test cells to not wrap */
listing, plaintext, pre, xmp { white-space: pre-wrap }
+video { width: 50px }
</style>
<p>Legend: {[ are the selection anchor, }] are the selection focus, {}
represent an element boundary point, [] represent a text node boundary point.
@@ -50,6 +51,7 @@
<li><a href=#forecolor>forecolor</a>
<li><a href=#hilitecolor>hilitecolor</a>
<li><a href=#italic>italic</a>
+ <li><a href=#removeformat>removeformat</a>
<li><a href=#strikethrough>strikethrough</a>
<li><a href=#subscript>subscript</a>
<li><a href=#superscript>superscript</a>
@@ -160,6 +162,17 @@
<button onclick="addTest('italic')">Add test</button>
</div>
+<div id=removeformat>
+<h1>removeformat</h1>
+
+<button onclick="runTests('removeformat')">Run tests</button>
+
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+
+<p><label>New test input: <input></label>
+<button onclick="addTest('removeformat')">Add test</button>
+</div>
+
<div id=strikethrough>
<h1>strikethrough</h1>
@@ -496,6 +509,11 @@
// Minor algorithm bug: this changes the size of the "b" and "r" in
// "bar" when we pull down styles
["3", '<font size=6>foo <span style="font-size: 2em">b[a]r</span> baz</font>'],
+
+ ["3", 'foo<big>[bar]</big>baz'],
+ ["3", 'foo<big>b[a]r</big>baz'],
+ ["3", 'foo<small>[bar]</small>baz'],
+ ["3", 'foo<small>b[a]r</small>baz'],
],
forecolor: [
'foo[bar]baz',
@@ -613,6 +631,115 @@
'foo [bar <i>baz] qoz</i> quz sic',
'foo bar <i>baz [qoz</i> quz] sic',
],
+ removeformat: [
+ '[foo<b>bar</b>baz]',
+ 'foo[<b>bar</b>baz]',
+ 'foo[<b>bar</b>]baz',
+ 'foo<b>[bar]</b>baz',
+ 'foo<b>b[a]r</b>baz',
+ '[foo<strong>bar</strong>baz]',
+ '[foo<span style="font-weight: bold">bar</span>baz]',
+ 'foo<span style="font-weight: bold">b[a]r</span>baz',
+ '[foo<b id=foo>bar</b>baz]',
+ 'foo<b id=foo>b[a]r</b>baz',
+
+ // HTML has lots of inline elements, doesn't it?
+ '[foo<a>bar</a>baz]',
+ 'foo<a>b[a]r</a>baz',
+ '[foo<a href=foo>bar</a>baz]',
+ 'foo<a href=foo>b[a]r</a>baz',
+ '[foo<abbr>bar</abbr>baz]',
+ 'foo<abbr>b[a]r</abbr>baz',
+ '[foo<acronym>bar</acronym>baz]',
+ 'foo<acronym>b[a]r</acronym>baz',
+ '[foo<b>bar</b>baz]',
+ 'foo<b>b[a]r</b>baz',
+ '[foo<bdi dir=rtl>bar</bdi>baz]',
+ 'foo<bdi dir=rtl>b[a]r</bdi>baz',
+ '[foo<bdo dir=rtl>bar</bdo>baz]',
+ 'foo<bdo dir=rtl>b[a]r</bdo>baz',
+ '[foo<big>bar</big>baz]',
+ 'foo<big>b[a]r</big>baz',
+ '[foo<blink>bar</blink>baz]',
+ 'foo<blink>b[a]r</blink>baz',
+ '[foo<cite>bar</cite>baz]',
+ 'foo<cite>b[a]r</cite>baz',
+ '[foo<code>bar</code>baz]',
+ 'foo<code>b[a]r</code>baz',
+ '[foo<del>bar</del>baz]',
+ 'foo<del>b[a]r</del>baz',
+ '[foo<dfn>bar</dfn>baz]',
+ 'foo<dfn>b[a]r</dfn>baz',
+ '[foo<em>bar</em>baz]',
+ 'foo<em>b[a]r</em>baz',
+ '[foo<font>bar</font>baz]',
+ 'foo<font>b[a]r</font>baz',
+ '[foo<font color=red>bar</font>baz]',
+ 'foo<font color=red>b[a]r</font>baz',
+ '[foo<i>bar</i>baz]',
+ 'foo<i>b[a]r</i>baz',
+ '[foo<ins>bar</ins>baz]',
+ 'foo<ins>b[a]r</ins>baz',
+ '[foo<kbd>bar</kbd>baz]',
+ 'foo<kbd>b[a]r</kbd>baz',
+ '[foo<mark>bar</mark>baz]',
+ 'foo<mark>b[a]r</mark>baz',
+ '[foo<nobr>bar</nobr>baz]',
+ 'foo<nobr>b[a]r</nobr>baz',
+ '[foo<q>bar</q>baz]',
+ 'foo<q>b[a]r</q>baz',
+ '[foo<samp>bar</samp>baz]',
+ 'foo<samp>b[a]r</samp>baz',
+ '[foo<s>bar</s>baz]',
+ 'foo<s>b[a]r</s>baz',
+ '[foo<small>bar</small>baz]',
+ 'foo<small>b[a]r</small>baz',
+ '[foo<span>bar</span>baz]',
+ 'foo<span>b[a]r</span>baz',
+ '[foo<strike>bar</strike>baz]',
+ 'foo<strike>b[a]r</strike>baz',
+ '[foo<strong>bar</strong>baz]',
+ 'foo<strong>b[a]r</strong>baz',
+ '[foo<sub>bar</sub>baz]',
+ 'foo<sub>b[a]r</sub>baz',
+ '[foo<sup>bar</sup>baz]',
+ 'foo<sup>b[a]r</sup>baz',
+ '[foo<tt>bar</tt>baz]',
+ 'foo<tt>b[a]r</tt>baz',
+ '[foo<u>bar</u>baz]',
+ 'foo<u>b[a]r</u>baz',
+ '[foo<var>bar</var>baz]',
+ 'foo<var>b[a]r</var>baz',
+
+ // Empty and replaced elements
+ '[foo<br>bar]',
+ '[foo<hr>bar]',
+ '[foo<wbr>bar]',
+ '[foo<img>bar]',
+ '[foo<img src=abc>bar]',
+ '[foo<video></video>bar]',
+ '[foo<video src=abc></video>bar]',
+ '[foo<svg><circle fill=red r=20 cx=20 cy=20 /></svg>bar]',
+
+ // Unrecognized elements
+ '[foo<nonexistentelement>bar</nonexistentelement>baz]',
+ 'foo<nonexistentelement>b[a]r</nonexistentelement>baz',
+ '[foo<nonexistentelement style="display: block">bar</nonexistentelement>baz]',
+ 'foo<nonexistentelement style="display: block">b[a]r</nonexistentelement>baz',
+
+ '[foo<span id=foo>bar</span>baz]',
+ 'foo<span id=foo>b[a]r</span>baz',
+ '[foo<span class=foo>bar</span>baz]',
+ 'foo<span class=foo>b[a]r</span>baz',
+ '[foo<b style="font-weight: normal">bar</b>baz]',
+ 'foo<b style="font-weight: normal">b[a]r</b>baz',
+ '<p style="background-color: red">foo[bar]baz</p>',
+ '<p><span style="background-color: red">foo[bar]baz</span></p>',
+ '<p style="font-weight: bold">foo[bar]baz</p>',
+ '<b><p style="font-weight: bold">foo[bar]baz</p></b>',
+ '<p style="font-variant: small-caps">foo[bar]baz</p>',
+ '<p style="text-indent: 2em">foo[bar]baz</p>',
+ ],
strikethrough: [
'foo[bar]baz',
'foo]bar[baz',
@@ -944,7 +1071,7 @@
}
var key = "execcommand-" + command
- + "-" + Number(myQueryCommandState("styleWithCSS"))
+ + "-" + Number(styleWithCss)
+ "-" + tr.firstChild.lastChild.textContent;
var oldValue = localStorage[key];
@@ -980,6 +1107,10 @@
tr.appendChild(browserCell);
try {
var points = setupCell(browserCell, test);
+ // Work around weird Firefox bug:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=649138
+ var testDiv = browserCell.firstChild;
+ document.getElementById(command).appendChild(testDiv);
setSelection(points[0], points[1], points[2], points[3]);
try {
document.execCommand("styleWithCSS", false, styleWithCss);
@@ -988,9 +1119,13 @@
if (getSelection().rangeCount) {
addBrackets(getSelection().getRangeAt(0));
}
+ browserCell.insertBefore(testDiv, browserCell.firstChild);
browserCell.lastChild.textContent = browserCell.firstChild.innerHTML;
} catch (e) {
browserCell.textContent = "Exception: " + e;
+ if (testDiv && testDiv.parentNode) {
+ testDiv.parentNode.removeChild(testDiv);
+ }
}
}
--- a/editcommands.html Mon Apr 11 14:48:46 2011 -0600
+++ b/editcommands.html Tue Apr 12 14:55:38 2011 -0600
@@ -27,7 +27,7 @@
<body class=draft>
<div class=head id=head>
<h1>HTML Editing Commands</h1>
-<h2 class="no-num no-toc" id=work-in-progress-—-last-update-11-april-2011>Work in Progress — Last Update 11 April 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-—-last-update-12-april-2011>Work in Progress — Last Update 12 April 2011</h2>
<dl>
<dt>Editor
<dd>Aryeh Gregor <ayg+spec@aryeh.name>
@@ -174,8 +174,14 @@
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. This is going to be
- important to specify exactly in some cases, like with colors.
+ <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.
+
+ <li>I haven't paid much attention to performance. The algorithms here aren't
+ performance-critical in most cases, but I might have accidentally included
+ some algorithms that are too slow anyway on large pages. Generally I haven't
+ worried about throwing nodes away and recreating them multiple times or
+ things like that, as long as it produces the correct result.
</ul>
<p class=XXX>A variety of other issues are also noted in the text, formatted
@@ -693,9 +699,11 @@
<!-- If we go all the way up to the root and still don't have the desired
value, pushing down values is pointless. It will create extra markup for no
- purpose. -->
+ purpose. Except if the value is null, which basically just means "try to get
+ rid of anything affecting the current element but don't aim for any specific
+ value". -->
<li>If 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 the last member of <var title="">ancestor list</var> is not
- 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>, abort this algorithm.
+ 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 <var title="">new value</var> is not null, abort this algorithm.
<li>While <var title="">ancestor list</var> is not empty:
@@ -712,7 +720,9 @@
<li>Let <var title="">children</var> be the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a> of <var title="">current
ancestor</var>.
- <li><a href=#clear-the-value>Clear the value</a> of <var title="">current ancestor</var>.
+ <li>If the <a href=#specified-value>specified value</a> of <var title="">current ancestor</var> for
+ <var title="">command</var> is not null, <a href=#clear-the-value>clear the value</a> of
+ <var title="">current ancestor</var>.
<li>For every <var title="">child</var> in <var title="">children</var>:
@@ -960,12 +970,14 @@
contradicting WebKit. This is because <span value="vertical-align:
sub/super">, the obvious equivalent (and what WebKit uses), behaves quite
differently: it doesn't reduce font-size, which is ugly. -->
- <li>If <var title="">command</var> is "subscript" and <var title="">new value</var> is
- "sub", let <var title="">new parent</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("sub")</a></code> on the
+ <li>If <var title="">command</var> is "subscript" or "superscript" and <var title="">new
+ value</var> is "sub", let <var title="">new parent</var> be the result of calling
+ <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("sub")</a></code> on the
<code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">node</var>.
- <li>If <var title="">command</var> is "superscript" and <var title="">new value</var> is
- "super", let <var title="">new parent</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("sup")</a></code> on the
+ <li>If <var title="">command</var> is "subscript" or "superscript" and <var title="">new
+ value</var> is "super", let <var title="">new parent</var> be the result of calling
+ <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("sup")</a></code> on the
<code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">node</var>.
<li>If <var title="">new parent</var> is null, let <var title="">new parent</var> be the result
@@ -1455,7 +1467,7 @@
<dl class=switch>
<dt>1 <dd>xx-small
<dt>2 <dd>small
- <dt>3 <dd>normal
+ <dt>3 <dd>medium
<dt>4 <dd>large
<dt>5 <dd>x-large
<dt>6 <dd>xx-large
@@ -1616,6 +1628,120 @@
<dd><strong>Relevant CSS Property</strong>: "font-style"
+<dt><code title=""><dfn id=command-removeformat title=command-removeformat>removeFormat</dfn></code>
+<!--
+Tested in IE 9, Firefox 4.0, Chrome 12 dev, Opera 11.00.
+
+Tags stripped by everyone: b big cite code dfn em font i ins kbd samp s small
+ strike strong sub sup tt u var
+Tags left alone by everyone: br hr img
+
+Unrecognized elements: stripped by Firefox and Opera, left alone by IE and
+ Chrome.
+
+blink: stripped only by IE
+abbr: stripped only by Firefox
+a, wbr: stripped only by Opera
+
+nobr: left alone only by Firefox
+acronym, bdo, q: left alone only by Opera
+
+bdi, del, mark, span, svg: treated the same as unknown elements
+
+All elements whose default rendering is display: block are left untouched by
+all browsers (although IE seems to throw an exception on <marquee> for some
+reason).
+
+It's not clear to me why we should leave <a> alone, but everyone but Opera
+does. In OpenOffice.org 3.2.1, doing "Default Formatting (Ctrl+M)" doesn't
+remove links. In Microsoft Word 2007, doing "Clear Formatting" also doesn't
+remove links. Verdict: don't remove links. Apparently they don't logically
+qualify as "formatting".
+
+Conclusion: leave alone a, br, hr, img, wbr. Strip everything else, including
+unrecognized elements, although of course not block elements. Also we should
+probably treat all replaced elements the same as <img>, although I didn't test
+that (somehow I doubt it will come up much). <video> behaves the same as
+<img>, although Firefox adds tabindex=0 (???), so I'm assuming the rest are
+similar. Also, I'll keep all foreign elements and form elements.
+
+
+Browsers will split up all these inline elements if the selection is contained
+within them. Opera does strip unrecognized elements with display: block if
+they're within the selection, but doesn't split them up if they contain the
+selection.
+
+Upon consideration, I've decided to go for something for now that's totally
+different from what any browser does: get rid of all elements actually
+contained in the selection (pretty much matching browsers), but for elements
+containing the selection, I'll just run all the other styling commands in a
+fashion that will reset the style in normal cases. This avoids having to
+create a lot of new logic to decide exactly what we can split up or not, and
+should be able to correctly remove anything that can actually be created by
+these algorithms.
+
+This approach currently results in incorrect behavior in some cases for
+non-modifiable elements with default styling, like <code>. The correct
+approach is probably to declare these elements modifiable; this would roughly
+match what browsers do. I'm ignoring the issue for now, because such elements
+cannot actually be created by implementations of execCommand(), so they're not
+likely to be common.
+-->
+
+<dd><strong>Action</strong>:
+
+<ol>
+ <li><a href=#decompose>Decompose</a> the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>, and let <var title="">node list</var> be the
+ result.
+
+ <li>Let <var title="">affected elements</var> be a list of all <a href=#html-element title="HTML
+ element">HTML elements</a> that are the same as or <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>descendants</a> of some
+ member of <var title="">node list</var> and have non-null <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>parents</a> and satisfy
+ (insert conditions here).
+
+ <p class=XXX>The conditions are not so simple to define, because we want to
+ include non-conforming elements, which HTML doesn't give content models. If
+ everything had categories, we'd want something like "either it's
+ unrecognized, or it's phrasing content that's not also embedded or
+ interactive. Except this has weird corner-cases like ins and del that are
+ sometimes phrasing and sometimes flow.
+
+ <li>For each <var title="">element</var> in <var title="">affected elements</var>:
+
+ <ol>
+ <li>While <var title="">element</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, insert the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>
+ of <var title="">element</var> into 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="">element</var> immediately
+ before <var title="">element</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+ <li>Remove <var title="">element</var> from its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+ </ol>
+
+ <li>For each of the entries in the following table, in the given order:
+ <a href=#decompose>decompose</a> the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> again; then <a href=#set-the-value>set the value</a>
+ 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.
+
+ <table>
+ <tr><th><var title="">command</var> <th><var title="">new value</var>
+ <tr><td>subscript <td>"baseline"
+ <!-- superscript not needed, subscript does the same thing. We run this
+ first so <sub>/<sup> won't upset fontSize. -->
+ <tr><td>bold <td>"normal"
+ <tr><td>fontName <td>null
+ <tr><td>fontSize <td>null
+ <tr><td>foreColor <td>null
+ <tr><td>hiliteColor <td>null
+ <tr><td>italic <td>"normal"
+ <tr><td>strikethrough <td>null
+ <tr><td>underline <td>null
+ </table>
+</ol>
+
+<dd><strong>State</strong>:
+
+<dd><strong>Value</strong>:
+
+
<dt><code title=""><dfn id=command-strikethrough title=command-strikethrough>strikethrough</dfn></code>
<dd><strong>Action</strong>: <a href=#decompose>Decompose</a> the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a>. If the
--- a/implementation.js Mon Apr 11 14:48:46 2011 -0600
+++ b/implementation.js Tue Apr 12 14:55:38 2011 -0600
@@ -1118,9 +1118,10 @@
}
// "If the parent of the last member of ancestor list is not an Element,
- // abort this algorithm."
- if (!ancestorList[ancestorList.length - 1].parentNode
- || ancestorList[ancestorList.length - 1].parentNode.nodeType != Node.ELEMENT_NODE) {
+ // and new value is not null, abort this algorithm."
+ if (newValue !== null
+ && (!ancestorList[ancestorList.length - 1].parentNode
+ || ancestorList[ancestorList.length - 1].parentNode.nodeType != Node.ELEMENT_NODE)) {
return;
}
@@ -1139,8 +1140,11 @@
// "Let children be the children of current ancestor."
var children = Array.prototype.slice.call(currentAncestor.childNodes);
- // "Clear the value of current ancestor."
- clearValue(currentAncestor, command);
+ // "If the specified value of current ancestor for command is not null,
+ // clear the value of current ancestor."
+ if (getSpecifiedValue(currentAncestor, command) !== null) {
+ clearValue(currentAncestor, command);
+ }
// "For every child in children:"
for (var i = 0; i < children.length; i++) {
@@ -1425,16 +1429,19 @@
}[newValue];
}
- // "If command is "subscript" and new value is "sub", let new parent be the
- // result of calling createElement("sub") on the ownerDocument of node."
- if (command == "subscript" && newValue == "sub") {
+ // "If command is "subscript" or "superscript" and new value is "sub", let
+ // new parent be the result of calling createElement("sub") on the
+ // ownerDocument of node."
+ if ((command == "subscript" || command == "superscript")
+ && newValue == "sub") {
newParent = node.ownerDocument.createElement("sub");
}
- // "If command is "superscript" and new value is "super", let new parent be
- // the result of calling createElement("sup") on the ownerDocument of
- // node."
- if (command == "superscript" && newValue == "super") {
+ // "If command is "subscript" or "superscript" and new value is "super",
+ // let new parent be the result of calling createElement("sup") on the
+ // ownerDocument of node."
+ if ((command == "subscript" || command == "superscript")
+ && newValue == "super") {
newParent = node.ownerDocument.createElement("sup");
}
@@ -1832,6 +1839,66 @@
}
break;
+ case "removeformat":
+ // "Decompose the range, and let node list be the result."
+ var nodeList = decomposeRange(range);
+
+ // "Let affected elements be a list of all HTML elements that are the
+ // same as or descendants of some member of node list and have non-null
+ // parents and satisfy (insert conditions here)."
+ var affectedElements = [];
+ for (var i = 0; i < nodeList.length; i++) {
+ for (
+ var node = nodeList[i];
+ node == nodeList[i] || isDescendant(node, nodeList[i]);
+ node = nextNode(node)
+ ) {
+ if (isHtmlElement(node)
+ && node.parentNode
+ // FIXME: Extremely partial list for testing
+ && ["A", "AUDIO", "BR", "DIV", "HR", "IMG", "P", "TD", "VIDEO", "WBR"].indexOf(node.tagName) == -1) {
+ affectedElements.push(node);
+ }
+ }
+ }
+
+ // "For each element in affected elements:"
+ for (var i = 0; i < affectedElements.length; i++) {
+ var element = affectedElements[i];
+
+ // "While element has children, insert the first child of element
+ // into the parent of element immediately before element,
+ // preserving ranges."
+ while (element.childNodes.length) {
+ movePreservingRanges(element.firstChild, element.parentNode, getNodeIndex(element));
+ }
+
+ // "Remove element from its parent."
+ element.parentNode.removeChild(element);
+ }
+
+ // "For each of the entries in the following table, in the given order:
+ // decompose the range again; then set the value of the resulting
+ // nodes, with command and new value as given."
+ var table = {
+ "subscript": "baseline",
+ "bold": "normal",
+ "fontname": null,
+ "fontsize": null,
+ "forecolor": null,
+ "hilitecolor": null,
+ "italic": "normal",
+ "strikethrough": null,
+ "underline": null,
+ };
+ for (var command in table) {
+ var nodeList = decomposeRange(range);
+ for (var i = 0; i < nodeList.length; i++) {
+ setNodeValue(nodeList[i], command, table[command]);
+ }
+ }
+ break;
+
case "strikethrough":
// "Decompose the range. If the state of the range for this command is
// then true, set the value of each returned node to null. Otherwise,
@@ -1956,9 +2023,9 @@
while (node.parentNode && node.parentNode.firstChild == node) {
node = node.parentNode;
}
- var stop = nextNode(range.endContainer);
+ var stop = nextNodeDescendants(range.endContainer);
- for (; node && node != nextNodeDescendants(range.endContainer); node = nextNode(node)) {
+ for (; node && node != stop; node = nextNode(node)) {
if (!isEffectivelyContained(node, range)) {
continue;
}
--- a/source.html Mon Apr 11 14:48:46 2011 -0600
+++ b/source.html Tue Apr 12 14:55:38 2011 -0600
@@ -162,8 +162,14 @@
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. This is going to be
- important to specify exactly in some cases, like with colors.
+ <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.
+
+ <li>I haven't paid much attention to performance. The algorithms here aren't
+ performance-critical in most cases, but I might have accidentally included
+ some algorithms that are too slow anyway on large pages. Generally I haven't
+ worried about throwing nodes away and recreating them multiple times or
+ things like that, as long as it produces the correct result.
</ul>
<p class=XXX>A variety of other issues are also noted in the text, formatted
@@ -686,9 +692,11 @@
<!-- If we go all the way up to the root and still don't have the desired
value, pushing down values is pointless. It will create extra markup for no
- purpose. -->
+ purpose. Except if the value is null, which basically just means "try to get
+ rid of anything affecting the current element but don't aim for any specific
+ value". -->
<li>If the [[parent]] of the last member of <var>ancestor list</var> is not
- an [[element]], abort this algorithm.
+ an [[element]], and <var>new value</var> is not null, abort this algorithm.
<li>While <var>ancestor list</var> is not empty:
@@ -705,7 +713,9 @@
<li>Let <var>children</var> be the [[children]] of <var>current
ancestor</var>.
- <li><span>Clear the value</span> of <var>current ancestor</var>.
+ <li>If the <span>specified value</span> of <var>current ancestor</var> for
+ <var>command</var> is not null, <span>clear the value</span> of
+ <var>current ancestor</var>.
<li>For every <var>child</var> in <var>children</var>:
@@ -966,15 +976,15 @@
contradicting WebKit. This is because <span value="vertical-align:
sub/super">, the obvious equivalent (and what WebKit uses), behaves quite
differently: it doesn't reduce font-size, which is ugly. -->
- <li>If <var>command</var> is "subscript" and <var>new value</var> is
- "sub", let <var>new parent</var> be the result of calling <code
- data-anolis-spec=domcore
+ <li>If <var>command</var> is "subscript" or "superscript" and <var>new
+ value</var> is "sub", let <var>new parent</var> be the result of calling
+ <code data-anolis-spec=domcore
title=dom-Document-createElement>createElement("sub")</code> on the
[[ownerdocument]] of <var>node</var>.
- <li>If <var>command</var> is "superscript" and <var>new value</var> is
- "super", let <var>new parent</var> be the result of calling <code
- data-anolis-spec=domcore
+ <li>If <var>command</var> is "subscript" or "superscript" and <var>new
+ value</var> is "super", let <var>new parent</var> be the result of calling
+ <code data-anolis-spec=domcore
title=dom-Document-createElement>createElement("sup")</code> on the
[[ownerdocument]] of <var>node</var>.
@@ -1470,7 +1480,7 @@
<dl class=switch>
<dt>1 <dd>xx-small
<dt>2 <dd>small
- <dt>3 <dd>normal
+ <dt>3 <dd>medium
<dt>4 <dd>large
<dt>5 <dd>x-large
<dt>6 <dd>xx-large
@@ -1637,6 +1647,120 @@
<dd><strong>Relevant CSS Property</strong>: "font-style"
+<dt><code title><dfn title=command-removeformat>removeFormat</dfn></code>
+<!--
+Tested in IE 9, Firefox 4.0, Chrome 12 dev, Opera 11.00.
+
+Tags stripped by everyone: b big cite code dfn em font i ins kbd samp s small
+ strike strong sub sup tt u var
+Tags left alone by everyone: br hr img
+
+Unrecognized elements: stripped by Firefox and Opera, left alone by IE and
+ Chrome.
+
+blink: stripped only by IE
+abbr: stripped only by Firefox
+a, wbr: stripped only by Opera
+
+nobr: left alone only by Firefox
+acronym, bdo, q: left alone only by Opera
+
+bdi, del, mark, span, svg: treated the same as unknown elements
+
+All elements whose default rendering is display: block are left untouched by
+all browsers (although IE seems to throw an exception on <marquee> for some
+reason).
+
+It's not clear to me why we should leave <a> alone, but everyone but Opera
+does. In OpenOffice.org 3.2.1, doing "Default Formatting (Ctrl+M)" doesn't
+remove links. In Microsoft Word 2007, doing "Clear Formatting" also doesn't
+remove links. Verdict: don't remove links. Apparently they don't logically
+qualify as "formatting".
+
+Conclusion: leave alone a, br, hr, img, wbr. Strip everything else, including
+unrecognized elements, although of course not block elements. Also we should
+probably treat all replaced elements the same as <img>, although I didn't test
+that (somehow I doubt it will come up much). <video> behaves the same as
+<img>, although Firefox adds tabindex=0 (???), so I'm assuming the rest are
+similar. Also, I'll keep all foreign elements and form elements.
+
+
+Browsers will split up all these inline elements if the selection is contained
+within them. Opera does strip unrecognized elements with display: block if
+they're within the selection, but doesn't split them up if they contain the
+selection.
+
+Upon consideration, I've decided to go for something for now that's totally
+different from what any browser does: get rid of all elements actually
+contained in the selection (pretty much matching browsers), but for elements
+containing the selection, I'll just run all the other styling commands in a
+fashion that will reset the style in normal cases. This avoids having to
+create a lot of new logic to decide exactly what we can split up or not, and
+should be able to correctly remove anything that can actually be created by
+these algorithms.
+
+This approach currently results in incorrect behavior in some cases for
+non-modifiable elements with default styling, like <code>. The correct
+approach is probably to declare these elements modifiable; this would roughly
+match what browsers do. I'm ignoring the issue for now, because such elements
+cannot actually be created by implementations of execCommand(), so they're not
+likely to be common.
+-->
+
+<dd><strong>Action</strong>:
+
+<ol>
+ <li><span>Decompose</span> the [[range]], and let <var>node list</var> be the
+ result.
+
+ <li>Let <var>affected elements</var> be a list of all <span title="HTML
+ element">HTML elements</span> that are the same as or [[descendants]] of some
+ member of <var>node list</var> and have non-null [[parents]] and satisfy
+ (insert conditions here).
+
+ <p class=XXX>The conditions are not so simple to define, because we want to
+ include non-conforming elements, which HTML doesn't give content models. If
+ everything had categories, we'd want something like "either it's
+ unrecognized, or it's phrasing content that's not also embedded or
+ interactive. Except this has weird corner-cases like ins and del that are
+ sometimes phrasing and sometimes flow.
+
+ <li>For each <var>element</var> in <var>affected elements</var>:
+
+ <ol>
+ <li>While <var>element</var> has [[children]], insert the first [[child]]
+ of <var>element</var> into the [[parent]] of <var>element</var> immediately
+ before <var>element</var>, <span>preserving ranges</span>.
+
+ <li>Remove <var>element</var> from its [[parent]].
+ </ol>
+
+ <li>For each of the entries in the following table, in the given order:
+ <span>decompose</span> the [[range]] again; then <span>set the value</span>
+ of the resulting [[nodes]], with <var>command</var> and <var>new value</var>
+ as given.
+
+ <table>
+ <tr><th><var>command</var> <th><var>new value</var>
+ <tr><td>subscript <td>"baseline"
+ <!-- superscript not needed, subscript does the same thing. We run this
+ first so <sub>/<sup> won't upset fontSize. -->
+ <tr><td>bold <td>"normal"
+ <tr><td>fontName <td>null
+ <tr><td>fontSize <td>null
+ <tr><td>foreColor <td>null
+ <tr><td>hiliteColor <td>null
+ <tr><td>italic <td>"normal"
+ <tr><td>strikethrough <td>null
+ <tr><td>underline <td>null
+ </table>
+</ol>
+
+<dd><strong>State</strong>:
+
+<dd><strong>Value</strong>:
+
+
<dt><code title><dfn title=command-strikethrough>strikethrough</dfn></code>
<dd><strong>Action</strong>: <span>Decompose</span> the [[range]]. If the