Fix preserve/restore overrides for delete
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Fri, 22 Jul 2011 11:43:07 -0600
changeset 444 de9cb2028556
parent 443 a38658e347fc
child 445 773713229861
Fix preserve/restore overrides for delete

Tested specifically for insertText. Results are all pretty much as
expected now, lots were unreasonable before.
editcommands.html
implementation.js
source.html
tests.css
tests.js
--- a/editcommands.html	Fri Jul 22 11:42:50 2011 -0600
+++ b/editcommands.html	Fri Jul 22 11:43:07 2011 -0600
@@ -38,7 +38,7 @@
 <body class=draft>
 <div class=head id=head>
 <h1>HTML Editing Commands</h1>
-<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-21-july-2011>Work in Progress &mdash; Last Update 21 July 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-22-july-2011>Work in Progress &mdash; Last Update 22 July 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;<a href=mailto:ayg@aryeh.name>ayg@aryeh.name</a>&gt;
@@ -973,7 +973,7 @@
 <a href=#remove-extraneous-line-breaks-before>remove extraneous line breaks before</a> it, then <a href=#remove-extraneous-line-breaks-at-the-end-of>remove
 extraneous line breaks at the end of</a> it.
 
-<p>To <dfn id=record-current-states-and-values>record current states and values</dfn>:
+<p>To <dfn id=record-current-overrides>record current overrides</dfn>:
 
 <ol>
   <li>Let <var title="">overrides</var> be a list of (string, string or boolean) ordered
@@ -1004,13 +1004,36 @@
   <li>Return <var title="">overrides</var>.
 </ol>
 
+<p>To <dfn id=record-current-states-and-values>record current states and values</dfn>:
+
+<ol>
+  <li>Let <var title="">overrides</var> be a list of (string, string or boolean) ordered
+  pairs, initially empty.
+
+  <li>Add ("createLink", <code title=queryCommandValue()><a href=#querycommandvalue()>queryCommandValue("createLink")</a></code>) to
+  <var title="">overrides</var>.
+
+  <li>For each <var title="">command</var> in the list "bold", "italic",
+  "strikethrough", "subscript", "superscript", "underline", in order: add
+  (<var title="">command</var>, <code title=queryCommandState()><a href=#querycommandstate()>queryCommandState(<var title="">command</var>)</a></code>) to
+  <var title="">overrides</var>.
+
+  <li>For each <var title="">command</var> in the list "fontName", "fontSize",
+  "foreColor", "hiliteColor", in order: add (<var title="">command</var>, <code title=queryCommandValue()><a href=#querycommandvalue()>queryCommandValue(<var title="">command</var>)</a></code>) to
+  <var title="">overrides</var>.
+
+  <li>Return <var title="">overrides</var>.
+</ol>
+
 <p>To <dfn id=restore-states-and-values>restore states and values</dfn> specified by a list
 <var title="">overrides</var> returned by the <a href=#record-current-states-and-values>record current states and
 values</a> algorithm:
 
 <ol>
-  <li>For each (<var title="">command</var>, <var title="">override</var>) pair in
-  <var title="">overrides</var>, in order:
+  <li>If there is some <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 <a href=#effectively-contained>effectively
+  contained</a> in the <a href=#active-range>active range</a>, then for each
+  (<var title="">command</var>, <var title="">override</var>) pair in <var title="">overrides</var>, in
+  order:
 
   <ol>
     <li>If <var title="">override</var> is a boolean, and <code title=queryCommandState()><a href=#querycommandstate()>queryCommandState(<var title="">command</var>)</a></code>
@@ -1020,6 +1043,17 @@
     returns something different from <var title="">override</var>, call <code title=execCommand()><a href=#execcommand()>execCommand(<var title="">command</var>, false,
     <var title="">override</var>)</a></code>.
   </ol>
+
+  <li>Otherwise, for each (<var title="">command</var>, <var title="">override</var>) pair in
+  <var title="">overrides</var>, in order:
+
+  <ol>
+    <li>If <var title="">override</var> is a boolean, set the <a href=#state-override>state
+    override</a> for <var title="">command</var> to <var title="">override</var>.
+
+    <li>If <var title="">override</var> is a string, set the <a href=#value-override>value override</a>
+    for <var title="">command</var> to <var title="">override</var>.
+  </ol>
 </ol>
 
 
@@ -3817,8 +3851,8 @@
   <var title="">end block</var>, or <var title="">end block</var> is a <code class=external data-anolis-spec=html title="the td element"><a href=http://www.whatwg.org/html/#the-td-element>td</a></code> or <code class=external data-anolis-spec=html title="the th element"><a href=http://www.whatwg.org/html/#the-th-element>th</a></code>, set
   <var title="">end block</var> to null.
 
-  <li><a href=#record-current-states-and-values>Record current states and values</a>, and let <var title="">record</var>
-  be the result.
+  <li><a href=#record-current-states-and-values>Record current states and values</a>, and let
+  <var title="">overrides</var> be the result.
 
   <!-- This is based on deleteContents() in DOM Range. -->
   <li>If <var title="">start node</var> and <var title="">end node</var> are the same, and
@@ -3833,7 +3867,7 @@
 
     <li>Set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to its <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><a href=#restore-states-and-values>Restore states and values</a> from <var title="">record</var>.
+    <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">overrides</var>.
     <!-- This is needed to restore any overrides that would otherwise be lost.
     TODO: In this and similar cases, we could optimize by saving only
     overrides, not the full state/value. -->
@@ -3919,7 +3953,7 @@
   <ol>
     <li>Set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to its <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><a href=#restore-states-and-values>Restore states and values</a> from <var title="">record</var>.
+    <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">overrides</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3967,7 +4001,7 @@
       <li>If <var title="">end block</var> is <a href=#editable>editable</a>, remove it 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><a href=#restore-states-and-values>Restore states and values</a> from <var title="">record</var>.
+      <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">overrides</var>.
 
       <li>Abort these steps.
     </ol>
@@ -4065,7 +4099,7 @@
   <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("br")</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a> and append the result as the
   last <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="">start block</var>.
 
-  <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">record</var>.
+  <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">overrides</var>.
 </ol>
 
 
@@ -6918,8 +6952,8 @@
   "pre-wrap", set <var title="">value</var> to a non-breaking space (U+00A0).
   <!-- This may change to a space when we canonicalize. -->
 
-  <li><a href=#record-current-states-and-values>Record current states and values</a>, and let <var title="">record</var>
-  be the result.
+  <li><a href=#record-current-overrides>Record current overrides</a>, and let <var title="">overrides</var> be
+  the result.
 
   <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:
 
@@ -6954,7 +6988,7 @@
     <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
   </ol>
 
-  <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">record</var>.
+  <li><a href=#restore-states-and-values>Restore states and values</a> from <var title="">overrides</var>.
 
   <li><a href=#canonicalize-whitespace>Canonicalize whitespace</a> at the <a href=#active-range>active 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>.
--- a/implementation.js	Fri Jul 22 11:42:50 2011 -0600
+++ b/implementation.js	Fri Jul 22 11:43:07 2011 -0600
@@ -1237,7 +1237,7 @@
 	removeExtraneousLineBreaksAtTheEndOf(node);
 }
 
-function recordCurrentStatesAndValues() {
+function recordCurrentOverrides() {
 	// "Let overrides be a list of (string, string or boolean) ordered pairs,
 	// initially empty."
 	var overrides = [];
@@ -1272,25 +1272,77 @@
 	return overrides;
 }
 
+function recordCurrentStatesAndValues() {
+	// "Let overrides be a list of (string, string or boolean) ordered pairs,
+	// initially empty."
+	var overrides = [];
+
+	// "Add ("createLink", queryCommandValue("createLink")) to overrides."
+	overrides.push(["createlink", myQueryCommandValue("createlink")]);
+
+	// "For each command in the list "bold", "italic", "strikethrough",
+	// "subscript", "superscript", "underline", in order: add (command,
+	// queryCommandState(command)) to overrides."
+	["bold", "italic", "strikethrough", "subscript", "superscript",
+	"underline"].forEach(function(command) {
+		overrides.push([command, myQueryCommandState(command)]);
+	});
+
+	// "For each command in the list "fontName", "fontSize", "foreColor",
+	// "hiliteColor", in order: add (command, queryCommandValue(command)) to
+	// overrides."
+	["fontname", "fontsize", "forecolor", "hilitecolor"].forEach(function(command) {
+		overrides.push([command, myQueryCommandValue(command)]);
+	});
+
+	// "Return overrides."
+	return overrides;
+}
+
 function restoreStatesAndValues(overrides) {
-	// "For each (command, override) pair in overrides, in order:"
-	for (var i = 0; i < overrides.length; i++) {
-		var command = overrides[i][0];
-		var override = overrides[i][1];
-
-		// "If override is a boolean, and queryCommandState(command) returns
-		// something different from override, call execCommand(command)."
-		if (typeof override == "boolean"
-		&& myQueryCommandState(command) != override) {
-			myExecCommand(command);
-		}
-
-		// "If override is a string, and queryCommandValue(command) returns
-		// something different from override, call execCommand(command, false,
-		// override)."
-		if (typeof override == "string"
-		&& !valuesEqual(command, myQueryCommandValue(command), override)) {
-			myExecCommand(command, false, override);
+	// "If there is some editable Text node effectively contained in the active
+	// range, then for each (command, override) pair in overrides, in order:"
+	if (getAllEffectivelyContainedNodes(getActiveRange()).some(function(node) {
+		return isEditable(node) && node.nodeType == Node.TEXT_NODE
+	})) {
+		for (var i = 0; i < overrides.length; i++) {
+			var command = overrides[i][0];
+			var override = overrides[i][1];
+
+			// "If override is a boolean, and queryCommandState(command)
+			// returns something different from override, call
+			// execCommand(command)."
+			if (typeof override == "boolean"
+			&& myQueryCommandState(command) != override) {
+				myExecCommand(command);
+			}
+
+			// "If override is a string, and queryCommandValue(command) returns
+			// something different from override, call execCommand(command,
+			// false, override)."
+			if (typeof override == "string"
+			&& !valuesEqual(command, myQueryCommandValue(command), override)) {
+				myExecCommand(command, false, override);
+			}
+		}
+
+	// "Otherwise, for each (command, override) pair in overrides, in order:"
+	} else {
+		for (var i = 0; i < overrides.length; i++) {
+			var command = overrides[i][0];
+			var override = overrides[i][1];
+
+			// "If override is a boolean, set the state override for command to
+			// override."
+			if (typeof override == "boolean") {
+				setStateOverride(command, override);
+			}
+
+			// "If override is a string, set the value override for command to
+			// override."
+			if (typeof override == "string") {
+				setValueOverride(command, override);
+			}
 		}
 	}
 }
@@ -4254,8 +4306,8 @@
 		endBlock = null;
 	}
 
-	// "Record current states and values, and let record be the result."
-	var record = recordCurrentStatesAndValues();
+	// "Record current states and values, and let overrides be the result."
+	var overrides = recordCurrentStatesAndValues();
 
 	// "If start node and end node are the same, and start node is an editable
 	// Text node:"
@@ -4272,8 +4324,8 @@
 		// "Set range's end to its start."
 		range.setEnd(range.startContainer, range.startOffset);
 
-		// "Restore states and values from record."
-		restoreStatesAndValues(record);
+		// "Restore states and values from overrides."
+		restoreStatesAndValues(overrides);
 
 		// "Abort these steps."
 		return;
@@ -4354,8 +4406,8 @@
 		// "Set range's end to its start."
 		range.setEnd(range.startContainer, range.startOffset);
 
-		// "Restore states and values from record."
-		restoreStatesAndValues(record);
+		// "Restore states and values from overrides."
+		restoreStatesAndValues(overrides);
 
 		// "Abort these steps."
 		return;
@@ -4421,17 +4473,17 @@
 				endBlock.parentNode.removeChild(endBlock);
 			}
 
-			// "Restore states and values from record."
-			restoreStatesAndValues(record);
+			// "Restore states and values from overrides."
+			restoreStatesAndValues(overrides);
 
 			// "Abort these steps."
 			return;
 		}
 
 		// "If end block's firstChild is not an inline node, restore states and
-		// values from record, then abort these steps."
+		// values from overrides, then abort these steps."
 		if (!isInlineNode(endBlock.firstChild)) {
-			restoreStatesAndValues(record);
+			restoreStatesAndValues(overrides);
 			return;
 		}
 
@@ -4567,8 +4619,8 @@
 		startBlock.appendChild(document.createElement("br"));
 	}
 
-	// "Restore states and values from record."
-	restoreStatesAndValues(record);
+	// "Restore states and values from overrides."
+	restoreStatesAndValues(overrides);
 }
 
 
@@ -7012,8 +7064,8 @@
 			value = "\xa0";
 		}
 
-		// "Record current states and values, and let record be the result."
-		var record = recordCurrentStatesAndValues();
+		// "Record current overrides, and let overrides be the result."
+		var overrides = recordCurrentOverrides();
 
 		// "If node is a Text node:"
 		if (node.nodeType == Node.TEXT_NODE) {
@@ -7053,8 +7105,8 @@
 			getActiveRange().setEnd(text, 1);
 		}
 
-		// "Restore states and values from record."
-		restoreStatesAndValues(record);
+		// "Restore states and values from overrides."
+		restoreStatesAndValues(overrides);
 
 		// "Canonicalize whitespace at the active range's start."
 		canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset);
--- a/source.html	Fri Jul 22 11:42:50 2011 -0600
+++ b/source.html	Fri Jul 22 11:43:07 2011 -0600
@@ -927,7 +927,7 @@
 <span>remove extraneous line breaks before</span> it, then <span>remove
 extraneous line breaks at the end of</span> it.
 
-<p>To <dfn>record current states and values</dfn>:
+<p>To <dfn>record current overrides</dfn>:
 
 <ol>
   <li>Let <var>overrides</var> be a list of (string, string or boolean) ordered
@@ -958,13 +958,39 @@
   <li>Return <var>overrides</var>.
 </ol>
 
+<p>To <dfn>record current states and values</dfn>:
+
+<ol>
+  <li>Let <var>overrides</var> be a list of (string, string or boolean) ordered
+  pairs, initially empty.
+
+  <li>Add ("createLink", <code
+  title=queryCommandValue()>queryCommandValue("createLink")</code>) to
+  <var>overrides</var>.
+
+  <li>For each <var>command</var> in the list "bold", "italic",
+  "strikethrough", "subscript", "superscript", "underline", in order: add
+  (<var>command</var>, <code
+  title=queryCommandState()>queryCommandState(<var>command</var>)</code>) to
+  <var>overrides</var>.
+
+  <li>For each <var>command</var> in the list "fontName", "fontSize",
+  "foreColor", "hiliteColor", in order: add (<var>command</var>, <code
+  title=queryCommandValue()>queryCommandValue(<var>command</var>)</code>) to
+  <var>overrides</var>.
+
+  <li>Return <var>overrides</var>.
+</ol>
+
 <p>To <dfn>restore states and values</dfn> specified by a list
 <var>overrides</var> returned by the <span>record current states and
 values</span> algorithm:
 
 <ol>
-  <li>For each (<var>command</var>, <var>override</var>) pair in
-  <var>overrides</var>, in order:
+  <li>If there is some <span>editable</span> [[text]] node <span>effectively
+  contained</span> in the <span>active range</span>, then for each
+  (<var>command</var>, <var>override</var>) pair in <var>overrides</var>, in
+  order:
 
   <ol>
     <li>If <var>override</var> is a boolean, and <code
@@ -978,6 +1004,17 @@
     title=execCommand()>execCommand(<var>command</var>, false,
     <var>override</var>)</code>.
   </ol>
+
+  <li>Otherwise, for each (<var>command</var>, <var>override</var>) pair in
+  <var>overrides</var>, in order:
+
+  <ol>
+    <li>If <var>override</var> is a boolean, set the <span>state
+    override</span> for <var>command</var> to <var>override</var>.
+
+    <li>If <var>override</var> is a string, set the <span>value override</span>
+    for <var>command</var> to <var>override</var>.
+  </ol>
 </ol>
 
 <!-- @} -->
@@ -3808,8 +3845,8 @@
   <var>end block</var>, or <var>end block</var> is a [[td]] or [[th]], set
   <var>end block</var> to null.
 
-  <li><span>Record current states and values</span>, and let <var>record</var>
-  be the result.
+  <li><span>Record current states and values</span>, and let
+  <var>overrides</var> be the result.
 
   <!-- This is based on deleteContents() in DOM Range. -->
   <li>If <var>start node</var> and <var>end node</var> are the same, and
@@ -3824,7 +3861,7 @@
 
     <li>Set <var>range</var>'s [[rangeend]] to its [[rangestart]].
 
-    <li><span>Restore states and values</span> from <var>record</var>.
+    <li><span>Restore states and values</span> from <var>overrides</var>.
     <!-- This is needed to restore any overrides that would otherwise be lost.
     TODO: In this and similar cases, we could optimize by saving only
     overrides, not the full state/value. -->
@@ -3910,7 +3947,7 @@
   <ol>
     <li>Set <var>range</var>'s [[rangeend]] to its [[rangestart]].
 
-    <li><span>Restore states and values</span> from <var>record</var>.
+    <li><span>Restore states and values</span> from <var>overrides</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3958,7 +3995,7 @@
       <li>If <var>end block</var> is <span>editable</span>, remove it from its
       [[parent]].
 
-      <li><span>Restore states and values</span> from <var>record</var>.
+      <li><span>Restore states and values</span> from <var>overrides</var>.
 
       <li>Abort these steps.
     </ol>
@@ -4056,7 +4093,7 @@
   [[createelement|"br"]] on the [[contextobject]] and append the result as the
   last [[child]] of <var>start block</var>.
 
-  <li><span>Restore states and values</span> from <var>record</var>.
+  <li><span>Restore states and values</span> from <var>overrides</var>.
 </ol>
 
 <!-- @} -->
@@ -6924,8 +6961,8 @@
   "pre-wrap", set <var>value</var> to a non-breaking space (U+00A0).
   <!-- This may change to a space when we canonicalize. -->
 
-  <li><span>Record current states and values</span>, and let <var>record</var>
-  be the result.
+  <li><span>Record current overrides</span>, and let <var>overrides</var> be
+  the result.
 
   <li>If <var>node</var> is a [[text]] node:
 
@@ -6962,7 +6999,7 @@
     [[selection]].
   </ol>
 
-  <li><span>Restore states and values</span> from <var>record</var>.
+  <li><span>Restore states and values</span> from <var>overrides</var>.
 
   <li><span>Canonicalize whitespace</span> at the <span>active range</span>'s
   [[rangestart]].
--- a/tests.css	Fri Jul 22 11:42:50 2011 -0600
+++ b/tests.css	Fri Jul 22 11:43:07 2011 -0600
@@ -64,6 +64,7 @@
 body > div > table > tbody > tr > th:last-child {
 	width: 10%;
 }
+body > div > p > label > input { width: 30% }
 #toolbar {
 	position: fixed;
 	top: 0;
--- a/tests.js	Fri Jul 22 11:42:50 2011 -0600
+++ b/tests.js	Fri Jul 22 11:43:07 2011 -0600
@@ -2110,6 +2110,7 @@
 		'{}<br>',
 		'<p>{}<br>',
 		'<p><span>{}<br></span>',
+		'foo<a href=http://www.google.com/><font color=black>[bar]</font></a>baz',
 	],
 	//@}
 	insertunorderedlist: [
@@ -3332,6 +3333,42 @@
 		['foo[]bar', ['fontsize', '2'], 'subscript', ['inserttext', 'a']],
 		['foo[]bar', 'subscript', ['fontsize', '3'], ['inserttext', 'a']],
 		['foo[]bar', ['fontsize', '3'], 'subscript', ['inserttext', 'a']],
+
+
+		// Now the same tests, but with a nonempty selection
+		['foo[bar]baz', 'bold', ['inserttext', 'a']],
+		['foo[bar]baz', 'italic', ['inserttext', 'a']],
+		['foo[bar]baz', 'strikethrough', ['inserttext', 'a']],
+		['foo[bar]baz', 'subscript', ['inserttext', 'a']],
+		['foo[bar]baz', 'superscript', ['inserttext', 'a']],
+		['foo[bar]baz', 'underline', ['inserttext', 'a']],
+
+		['foo[bar]baz', 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'fontname', ['inserttext', 'a']],
+		['foo[bar]baz', 'fontsize', ['inserttext', 'a']],
+		['foo[bar]baz', 'forecolor', ['inserttext', 'a']],
+		['foo[bar]baz', 'hilitecolor', ['inserttext', 'a']],
+
+		['foo[bar]baz', 'superscript', 'subscript', ['inserttext', 'a']],
+		['foo[bar]baz', 'subscript', 'superscript', ['inserttext', 'a']],
+
+		['foo[bar]baz', 'createlink', ['forecolor', '#0000FF'], ['inserttext', 'a']],
+		['foo[bar]baz', ['forecolor', '#0000FF'], 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'createlink', ['forecolor', 'blue'], ['inserttext', 'a']],
+		['foo[bar]baz', ['forecolor', 'blue'], 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'createlink', ['forecolor', 'brown'], ['inserttext', 'a']],
+		['foo[bar]baz', ['forecolor', 'brown'], 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'createlink', ['forecolor', 'black'], ['inserttext', 'a']],
+		['foo[bar]baz', ['forecolor', 'black'], 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'createlink', 'underline', ['inserttext', 'a']],
+		['foo[bar]baz', 'underline', 'createlink', ['inserttext', 'a']],
+		['foo[bar]baz', 'createlink', 'underline', 'underline', ['inserttext', 'a']],
+		['foo[bar]baz', 'underline', 'underline', 'createlink', ['inserttext', 'a']],
+
+		['foo[bar]baz', 'subscript', ['fontsize', '2'], ['inserttext', 'a']],
+		['foo[bar]baz', ['fontsize', '2'], 'subscript', ['inserttext', 'a']],
+		['foo[bar]baz', 'subscript', ['fontsize', '3'], ['inserttext', 'a']],
+		['foo[bar]baz', ['fontsize', '3'], 'subscript', ['inserttext', 'a']],
 	],
 	//@}
 };
@@ -3610,6 +3647,15 @@
 	if (command == "multitest") {
 		// Magic
 		test = JSON.parse(test);
+		for (var i = 1; i < test.length; i++) {
+			if (typeof test[i] == "string"
+			&& test[i] in defaultValues) {
+				test[i] = [test[i], defaultValues[test[i]]];
+			} else if (typeof test[i] == "string") {
+				test[i] = [test[i], ""];
+			}
+		}
+		return test;
 	}
 
 	if (typeof test == "string") {
@@ -3620,16 +3666,9 @@
 		}
 	} else if (test.length == 2) {
 		test = [test[1], [command, String(test[0])]];
-	} else for (var i = 1; i < test.length; i++) {
-		if (typeof test[i] == "string"
-		&& test[i] in defaultValues) {
-			test[i] = [test[i], defaultValues[test[i]]];
-		} else if (typeof test[i] == "string") {
-			test[i] = [test[i], ""];
-		}
 	}
 
-	if (command != "multitest") {
+	if (styleWithCss !== undefined) {
 		test.splice(1, 0, ["stylewithcss", String(styleWithCss)]);
 	}