First pass at autolink support
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 09 Nov 2011 12:42:35 -0700
changeset 674 9008a98bdbc9
parent 673 4615394f458a
child 675 9860f92c4b82
First pass at autolink support

This only works in insertText, so not for instance insertLineBreak or
insertParagraph. The changes to conformancetest/diff are because Gecko
URL-encodes <a href> in serialized HTML, so it doesn't round-trip a few
of the tests.

Fixes: http://www.w3.org/Bugs/Public/show_bug.cgi?id=13807
conformancetest/data.js
conformancetest/diff
editing.html
implementation.js
preprocess
source.html
tests.js
--- a/conformancetest/data.js	Wed Nov 09 12:23:23 2011 -0700
+++ b/conformancetest/data.js	Wed Nov 09 12:42:35 2011 -0700
@@ -18239,6 +18239,206 @@
 	[["stylewithcss","true"],["inserttext"," "]],
 	"<div style=\"white-space:nowrap\"> foo&nbsp;[]</div>",
 	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["ftp://a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"ftp://a\">ftp://a</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["ftp://a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"ftp://a\">ftp://a</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["quasit://a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"quasit://a\">quasit://a</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["quasit://a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"quasit://a\">quasit://a</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+[".x-++-.://a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	".<a href=\"x-++-.://a\">x-++-.://a</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+[".x-++-.://a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	".<a href=\"x-++-.://a\">x-++-.://a</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["(http://a)[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"(<a href=\"http://a\">http://a</a>)&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["(http://a)[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"(<a href=\"http://a\">http://a</a>)&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["&lt;http://a>[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"&lt;<a href=\"http://a\">http://a</a>&gt;&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["&lt;http://a>[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"&lt;<a href=\"http://a\">http://a</a>&gt;&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a![]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>!&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a![]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>!&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~http://a!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~<a href=\"http://a!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~\">http://a!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~http://a!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~<a href=\"http://a!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~\">http://a!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a!\"'(),-.:;&lt;>`[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>!\"'(),-.:;&lt;&gt;`&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a!\"'(),-.:;&lt;>`[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"http://a\">http://a</a>!\"'(),-.:;&lt;&gt;`&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a#$%&amp;*+/=?^_|~[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"http://a#$%&amp;*+/=?^_|~\">http://a#$%&amp;*+/=?^_|~</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a#$%&amp;*+/=?^_|~[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"http://a#$%&amp;*+/=?^_|~\">http://a#$%&amp;*+/=?^_|~</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["mailto:a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"mailto:a\">mailto:a</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["mailto:a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"mailto:a\">mailto:a</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["a@b[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\">a@b</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["a@b[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\">a@b</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["a@[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"a@&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["a@[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"a@&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["@b[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"@b&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["@b[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"@b&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["#@x[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"mailto:#@x\">#@x</a>&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["#@x[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"mailto:#@x\">#@x</a>&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["a@.[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"a@.&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["a@.[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"a@.&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~a@b!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;<a href=\"mailto:?^_`|~a@b\">?^_`|~a@b</a>!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~a@b!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;<a href=\"mailto:?^_`|~a@b\">?^_`|~a@b</a>!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["<b>a@b</b>{}",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\"><b>a@b</b></a> []",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["<b>a@b</b>{}",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\"><b>a@b</b></a> []",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["<b>a</b><i>@</i><u>b</u>{}",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<b>a</b><i>@</i><u>b</u> []",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["<b>a</b><i>@</i><u>b</u>{}",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<b>a</b><i>@</i><u>b</u> []",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["a@b<b>[]c</b>",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\">a@b</a><b> []c</b>",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["a@b<b>[]c</b>",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<a href=\"mailto:a@b\">a@b</a><b> []c</b>",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["<p>a@b</p><p>[]c</p>",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"<p>a@b</p><p>&nbsp;[]c</p>",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["<p>a@b</p><p>[]c</p>",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"<p>a@b</p><p>&nbsp;[]c</p>",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","false"],["inserttext","a"]],
+	"http://aa[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","true"],["inserttext","a"]],
+	"http://aa[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","false"],["inserttext","\t"]],
+	"<a href=\"http://a\">http://a</a>\t[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","true"],["inserttext","\t"]],
+	"<a href=\"http://a\">http://a</a>\t[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","false"],["inserttext","\f"]],
+	"<a href=\"http://a\">http://a</a>\f[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","true"],["inserttext","\f"]],
+	"<a href=\"http://a\">http://a</a>\f[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","false"],["inserttext"," "]],
+	"http://a&nbsp;[]",
+	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+["http://a[]",
+	[["stylewithcss","true"],["inserttext"," "]],
+	"http://a&nbsp;[]",
+	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
 ["foo[]",
 	[["stylewithcss","false"],["inserttext","   "]],
 	"foo &nbsp;&nbsp;[]",
--- a/conformancetest/diff	Wed Nov 09 12:23:23 2011 -0700
+++ b/conformancetest/diff	Wed Nov 09 12:42:35 2011 -0700
@@ -1,4 +1,4 @@
-@@ -1121,11 +1121,11 @@
+@@ -1137,11 +1137,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
  ["<font color=blue face=monospace><b>foo</b></font>[bar]",
  	[["stylewithcss","false"],["bold",""]],
@@ -12,7 +12,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
  ["foo<span style=\"font-weight: normal\"><b>{bar}</b></span>baz",
  	[["stylewithcss","false"],["bold",""]],
-@@ -1737,11 +1737,11 @@
+@@ -1761,11 +1761,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["foo&#x308;&#x327;[]bar",
  	[["stylewithcss","false"],["delete",""]],
@@ -26,7 +26,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["&ouml;[]bar",
  	[["stylewithcss","false"],["delete",""]],
-@@ -1761,11 +1761,11 @@
+@@ -1785,11 +1785,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["o&#x308;&#x327;[]bar",
  	[["stylewithcss","false"],["delete",""]],
@@ -40,7 +40,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["&#x5e9;&#x5c1;&#x5b8;[]&#x5dc;&#x5d5;&#x5b9;&#x5dd;",
  	[["stylewithcss","false"],["delete",""]],
-@@ -1777,11 +1777,11 @@
+@@ -1801,11 +1801,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["&#x5e9;&#x5c1;&#x5b8;&#x5dc;&#x5d5;&#x5b9;[]&#x5dd;",
  	[["stylewithcss","false"],["delete",""]],
@@ -54,7 +54,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"delete":[false,false,"",false,false,""]}],
  ["<p>foo</p><p>[]bar</p>",
  	[["stylewithcss","false"],["delete",""]],
-@@ -5650,11 +5650,11 @@
+@@ -5954,11 +5954,11 @@
  ["foo[bar]baz",
  	[["stylewithcss","false"],["forecolor","rgba(0, 0, 255, 0.0)"]],
  	"foo<span style=\"color:rgba(0, 0, 0, 0)\">[bar]</span>baz",
@@ -68,7 +68,7 @@
  ["foo[bar]baz",
  	[["stylewithcss","false"],["forecolor","rgb(15, -10, 375)"]],
  	"foo<font color=\"#0f00ff\">[bar]</font>baz",
-@@ -5946,11 +5946,11 @@
+@@ -6250,11 +6250,11 @@
  ["<span style=\"color: rgba(0, 0, 255, 0.0)\">[foo]</span>",
  	[["stylewithcss","false"],["forecolor","#0000FF"]],
  	"<font color=\"#0000ff\">[foo]</font>",
@@ -82,7 +82,7 @@
  ["<span style=\"color: rgb(15, -10, 375)\">[foo]</span>",
  	[["stylewithcss","false"],["forecolor","#0000FF"]],
  	"<font color=\"#0000ff\">[foo]</font>",
-@@ -8081,11 +8081,11 @@
+@@ -8385,11 +8385,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"forwarddelete":[false,false,"",false,false,""]}],
  ["&#x5e9;&#x5c1;&#x5b8;&#x5dc;[]&#x5d5;&#x5b9;&#x5dd;",
  	[["stylewithcss","false"],["forwarddelete",""]],
@@ -96,7 +96,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"forwarddelete":[false,false,"",false,false,""]}],
  ["<p>foo[]</p><p>bar</p>",
  	[["stylewithcss","false"],["forwarddelete",""]],
-@@ -12063,6 +12063,14 @@
+@@ -12639,6 +12639,14 @@
  	[["stylewithcss","true"],["inserthorizontalrule",""]],
  	"<b>foo</b><hr>{}<b>baz</b>",
  	{"stylewithcss":[false,false,"",false,true,""],"inserthorizontalrule":[false,false,"",false,false,""]}],
@@ -111,7 +111,7 @@
  ["<bdo dir=rtl>foo[bar]baz</bdo>",
  	[["stylewithcss","false"],["inserthorizontalrule",""]],
  	"<bdo dir=\"rtl\">foo</bdo><hr>{}<bdo dir=\"rtl\">baz</bdo>",
-@@ -12751,18 +12759,10 @@
+@@ -13327,18 +13335,10 @@
  	[["stylewithcss","true"],["inserthtml","abc"]],
  	"<xmp>fabc{}o</xmp>",
  	{"stylewithcss":[false,false,"",false,true,""],"inserthtml":[false,false,"",false,false,""]}],
@@ -131,7 +131,64 @@
  ["<script>f[o]o</script>bar",
  	[["stylewithcss","true"],["inserthtml","abc"]],
  	"<script>fabc{}o</script>bar",
-@@ -19849,11 +19849,11 @@
+@@ -18287,34 +18287,18 @@
+ 	[["stylewithcss","true"],["inserttext"," "]],
+ 	"<a href=\"http://a\">http://a</a>!&nbsp;[]",
+ 	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+-["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~http://a!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+-	[["stylewithcss","false"],["inserttext"," "]],
+-	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~<a href=\"http://a!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~\">http://a!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~</a>&nbsp;[]",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+-["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~http://a!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+-	[["stylewithcss","true"],["inserttext"," "]],
+-	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~<a href=\"http://a!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~\">http://a!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~</a>&nbsp;[]",
+-	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+ ["http://a!\"'(),-.:;&lt;>`[]",
+ 	[["stylewithcss","false"],["inserttext"," "]],
+ 	"<a href=\"http://a\">http://a</a>!\"'(),-.:;&lt;&gt;`&nbsp;[]",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
++	{"stylewithcss":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+ ["http://a!\"'(),-.:;&lt;>`[]",
+ 	[["stylewithcss","true"],["inserttext"," "]],
+ 	"<a href=\"http://a\">http://a</a>!\"'(),-.:;&lt;&gt;`&nbsp;[]",
+ 	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+-["http://a#$%&amp;*+/=?^_|~[]",
+-	[["stylewithcss","false"],["inserttext"," "]],
+-	"<a href=\"http://a#$%&amp;*+/=?^_|~\">http://a#$%&amp;*+/=?^_|~</a>&nbsp;[]",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+-["http://a#$%&amp;*+/=?^_|~[]",
+-	[["stylewithcss","true"],["inserttext"," "]],
+-	"<a href=\"http://a#$%&amp;*+/=?^_|~\">http://a#$%&amp;*+/=?^_|~</a>&nbsp;[]",
+-	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+ ["mailto:a[]",
+ 	[["stylewithcss","false"],["inserttext"," "]],
+ 	"<a href=\"mailto:a\">mailto:a</a>&nbsp;[]",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
++	{"stylewithcss":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+ ["mailto:a[]",
+ 	[["stylewithcss","true"],["inserttext"," "]],
+ 	"<a href=\"mailto:a\">mailto:a</a>&nbsp;[]",
+@@ -18359,18 +18343,10 @@
+ 	[["stylewithcss","true"],["inserttext"," "]],
+ 	"a@.&nbsp;[]",
+ 	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+-["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~a@b!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+-	[["stylewithcss","false"],["inserttext"," "]],
+-	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;<a href=\"mailto:?^_`|~a@b\">?^_`|~a@b</a>!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~&nbsp;[]",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+-["!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~a@b!\"#$%&amp;'()*+,-./:;&lt;=>?^_`|~[]",
+-	[["stylewithcss","true"],["inserttext"," "]],
+-	"!\"#$%&amp;'()*+,-./:;&lt;=&gt;<a href=\"mailto:?^_`|~a@b\">?^_`|~a@b</a>!\"#$%&amp;'()*+,-./:;&lt;=&gt;?^_`|~&nbsp;[]",
+-	{"stylewithcss":[false,false,"",false,true,""],"inserttext":[false,false,"",false,false,""]}],
+ ["<b>a@b</b>{}",
+ 	[["stylewithcss","false"],["inserttext"," "]],
+ 	"<a href=\"mailto:a@b\"><b>a@b</b></a> []",
+-	{"stylewithcss":[false,true,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
++	{"stylewithcss":[false,false,"",false,false,""],"inserttext":[false,false,"",false,false,""]}],
+ ["<b>a@b</b>{}",
+ 	[["stylewithcss","true"],["inserttext"," "]],
+ 	"<a href=\"mailto:a@b\"><b>a@b</b></a> []",
+@@ -20601,11 +20577,11 @@
  	{"stylewithcss":[false,false,"",false,true,""],"italic":[true,false,"",false,true,""]}],
  ["fo[o<span style=font-style:oblique>b]ar</span>baz",
  	[["stylewithcss","false"],["italic",""]],
@@ -145,7 +202,7 @@
  	{"stylewithcss":[false,false,"",false,true,""],"italic":[true,false,"",false,true,""]}],
  ["<span style=font-style:italic>fo[o</span><span style=font-style:oblique>b]ar</span>",
  	[["stylewithcss","false"],["italic",""]],
-@@ -22889,35 +22889,35 @@
+@@ -23641,35 +23617,35 @@
  	{"stylewithcss":[false,false,"",false,true,""],"outdent":[false,false,"",false,false,""]}],
  ["<blockquote class=\"webkit-indent-blockquote\" style=\"margin: 0 0 0 40px; border: none; padding: 0px;\"><p>foo[bar]</p><p>baz</p></blockquote><p>extra",
  	[["stylewithcss","false"],["outdent",""]],
--- a/editing.html	Wed Nov 09 12:23:23 2011 -0700
+++ b/editing.html	Wed Nov 09 12:42:35 2011 -0700
@@ -173,6 +173,7 @@
    <li><a href=#indenting-and-outdenting>Indenting and outdenting</a></li>
    <li><a href=#toggling-lists>Toggling lists</a></li>
    <li><a href=#justifying-the-selection>Justifying the selection</a></li>
+   <li><a href=#automatic-linking>Automatic linking</a></li>
    <li><a href=#the-delete-command>The <code title="">delete</code> command</a></li>
    <li><a href=#the-formatblock-command>The <code title="">formatBlock</code> command</a></li>
    <li><a href=#the-forwarddelete-command>The <code title="">forwardDelete</code> command</a></li>
@@ -7468,6 +7469,131 @@
 </ol>
 
 
+<h3 id=automatic-linking>Automatic linking</h3>
+
+<p class=comments><a href="http://www.w3.org/Bugs/Public/show_bug.cgi?id=13807">Bug 13807</a>.
+
+<p class=note>When the user inserts whitespace immediately following something
+that looks like a URL or e-mail address, we automatically run <a href=#the-createlink-command>the <code title="">createLink</code> command</a> on it.
+
+<p>An <dfn id=autolinkable-url>autolinkable URL</dfn> is a string of the following form:
+
+<ol>
+  <li>
+  <p class=comments>IE9 and LibreOffice 3.3.4 both have a whitelist of URL
+  schemes.  That would be complicated and involve political decisions, so
+  instead, we'll just accept anything that looks like a hierarchical URL
+  scheme.  Google Docs is similar (as of November 9, 2011), but it's too lax,
+  and allows characters in the scheme that can't be in a scheme.  For
+  non-hierarchical schemes, we just whitelist mailto:, since it's the only
+  common one that makes sense to autolink.
+
+  <p>Either a string matching the <var title="">scheme</var> pattern from <a href=http://tools.ietf.org/html/rfc3986#section-3.1>RFC 3986 section 3.1</a>
+  followed by the literal string <code title="">://</code>, or the literal string
+  <code title="">mailto:</code>; followed by
+
+  <li>
+  <p class=comments>We don't try to enforce that the URL is anything resembling
+  valid per spec.  Too complicated for not enough gain.
+
+  <p>Zero or more characters other than <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#space-character title="space character">space characters</a>; followed by
+
+  <li>
+  <div class=comments>
+  <p>If the user types a URL followed by some punctuation, we still want to
+  autolink, but we don't want to include the punctuation if it's probably not
+  meant as part of the URL.
+
+  <p>IE9 excludes <code title="">!#&amp;()*+,-.:;&lt;=?@[]^_`{|}~</code> as
+  trailing characters from both URLs and e-mails.  A trailing <code title="">"</code> or
+  <code title="">&gt;</code> will prevent autolinking, and a trailing <code title="">$%'/\</code> is
+  included in the link.
+
+  <p>LibreOffice 3.3.4 excludes trailing <code title="">!&rdquo;#'()*+,.:;&lt;=&gt;?[\]^_`{|}~</code>,
+  and prevents autolinking on <code title="">$%&amp;-@</code>.  It includes a trailing
+  <code title="">/</code> in URLs, but it inhibits linking for e-mails.
+
+  <p>Google Docs (as of November 9, 2011) is complicated.  Trailing
+  <code title="">&rdquo;&rsquo;,-.</code> always prevents autolinking of a URL, and trailing
+  <code title="">#%/?_</code> is always included in a URL.  Trailing <code title="">!&amp;=$()*+:;&lt;&gt;@[\]^`{|}~</code> prevent autolinking if there's no
+  <code title="">?</code> before them, but are included in the URL if there is a <code title="">?</code>.
+  For e-mails, <code title="">_</code> is included, and everything else prevents
+  autolinking.
+
+  <p>None of these behaviors makes maximal sense.  We should exclude characters
+  if they're more likely as delimiters than actual trailing characters; include
+  them if they're more likely as actual trailing characters; and prevent
+  autolinking if their presence suggests that we're not actually looking at a
+  link or e-mail address.  The lists I made up for URLs are: exclude trailing
+  <code title="">!"'(),-.:;&lt;&gt;[]`{}</code>, include anything else.  For e-mail,
+  exclude anything at all.
+  </div>
+
+  <p>A character that is not one of the ASCII characters <code title="">!"'(),-.:;&lt;&gt;[]`{}</code>.
+</ol>
+
+<p>To <dfn id=autolink>autolink</dfn> (<var title="">node</var>, <var title="">end offset</var>):
+
+<ol>
+  <li>While (<var title="">node</var>, <var title="">end offset</var>)'s <a href=#previous-equivalent-point>previous
+  equivalent point</a> is not null, set it to its <a href=#previous-equivalent-point>previous equivalent
+  point</a>.
+
+  <li>If <var title="">node</var> is not a <code class=external data-anolis-spec=dom><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node, or has an <code class=external data-anolis-spec=html title="the a element"><a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-a-element>a</a></code> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a>,
+  do nothing and abort these steps.
+
+  <li>Let <var title="">search</var> be the largest substring of <var title="">node</var>'s
+  <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> whose end is <var title="">end offset</var> and that contains no
+  <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#space-character title="space character">space characters</a>.
+
+  <li>If some substring of <var title="">search</var> is an <a href=#autolinkable-url>autolinkable
+  URL</a>:
+
+  <ol>
+    <li>While there is no substring of <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> ending at
+    <var title="">end offset</var> that is an <a href=#autolinkable-url>autolinkable URL</a>, decrement
+    <var title="">end offset</var>.
+
+    <li>Let <var title="">start offset</var> be the start index of the longest substring
+    of <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> that is an <a href=#autolinkable-url>autolinkable URL</a>
+    ending at <var title="">end offset</var>.
+
+    <li>Let <var title="">href</var> be the substring of <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code>
+    starting at <var title="">start offset</var> and ending at <var title="">end offset</var>.
+  </ol>
+
+  <li>Otherwise, if some substring of <var title="">search</var> is a <a class=external data-anolis-spec=html href=http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address>valid e-mail address</a>:
+
+  <ol>
+    <li>While there is no substring of <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> ending at
+    <var title="">end offset</var> that is a <a class=external data-anolis-spec=html href=http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address>valid e-mail
+    address</a>, decrement <var title="">end offset</var>.
+
+    <li>Let <var title="">start offset</var> be the start index of the longest substring
+    of <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> that is a <a class=external data-anolis-spec=html href=http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address>valid
+    e-mail address</a> ending at <var title="">end offset</var>.
+
+    <li>Let <var title="">href</var> be "mailto:" concatenated with the substring of
+    <var title="">node</var>'s <code class=external data-anolis-spec=dom title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> starting at <var title="">start offset</var> and ending
+    at <var title="">end offset</var>.
+  </ol>
+
+  <li>Otherwise, do nothing and abort these steps.
+
+  <li>Let <var title="">original range</var> be the <a href=#active-range>active range</a>.
+
+  <li>Create a new <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range title=concept-range>range</a> with <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-start title=concept-range-start>start</a> (<var title="">node</var>, <var title="">start
+  offset</var>) and <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-end title=concept-range-end>end</a> (<var title="">node</var>, <var title="">end offset</var>), and
+  set the <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>'s <a href=#concept-selection title=concept-selection>selection</a>'s <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range title=concept-range>range</a> to it.
+
+  <li>Call <code title=execCommand()><a href=#execcommand()>execCommand("createLink", false, <var title="">href</var>)</a></code> on the
+  <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>.
+
+  <li>Set the <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>'s <a href=#concept-selection title=concept-selection>selection</a>'s <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range title=concept-range>range</a> to <var title="">original
+  range</var>.
+</ol>
+
+
 <h3 id=the-delete-command><dfn>The <code title="">delete</code> command</dfn></h3>
 
 <div class=note>
@@ -9529,8 +9655,8 @@
     <p>If <var title="">node</var> has only one <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, which is a <a href=#collapsed-line-break>collapsed
     line break</a>, remove its <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from it.
 
-    <li>Let <var title="">text</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createTextNode><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createtextnode>createTextNode(<var title="">value</var>)</a></code> on
-    the <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>.
+    <li>Let <var title="">text</var> be the result of calling
+    <code class=external data-anolis-spec=dom title=dom-Document-createTextNode><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createtextnode>createTextNode(<var title="">value</var>)</a></code> on the <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>.
 
     <li>Call <code class=external data-anolis-spec=dom title=dom-Range-insertNode><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-range-insertnode>insertNode(<var title="">text</var>)</a></code> on the <a href=#active-range>active range</a>.
 
@@ -9549,6 +9675,9 @@
   <li><a href=#canonicalize-whitespace>Canonicalize whitespace</a> at the <a href=#active-range>active range</a>'s
   <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-end title=concept-range-end>end</a>, with <var title="">fix collapsed space</var> false.
 
+  <li>If <var title="">value</var> is a <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#space-character title="space character">space character</a>, <a href=#autolink>autolink</a> the
+  <a href=#active-range>active range</a>'s <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-start title=concept-range-start>start</a>.
+
   <li>Call <code title=dom-Selection-collapseToEnd><a href=#dom-selection-collapsetoend>collapseToEnd()</a></code> on the <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#context-object>context object</a>'s <a href=#concept-selection title=concept-selection>selection</a>.
 </ol>
 
--- a/implementation.js	Wed Nov 09 12:23:23 2011 -0700
+++ b/implementation.js	Wed Nov 09 12:42:35 2011 -0700
@@ -6025,6 +6025,112 @@
 
 
 //@}
+///// Automatic linking /////
+//@{
+// "An autolinkable URL is a string of the following form:"
+var autolinkableUrlRegexp =
+	// "Either a string matching the scheme pattern from RFC 3986 section 3.1
+	// followed by the literal string ://, or the literal string mailto:;
+	// followed by"
+	//
+	// From the RFC: scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+	"([a-zA-Z][a-zA-Z0-9+.-]*://|mailto:)"
+	// "Zero or more characters other than space characters; followed by"
+	+ "[^ \t\n\f\r]*"
+	// "A character that is not one of the ASCII characters !"'(),-.:;<>[]`{}."
+	+ "[^!\"'(),\\-.:;<>[\\]`{}]";
+
+// "A valid e-mail address is a string that matches the ABNF production 1*(
+// atext / "." ) "@" ldh-str *( "." ldh-str ) where atext is defined in RFC
+// 5322 section 3.2.3, and ldh-str is defined in RFC 1034 section 3.5."
+//
+// atext: ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" /
+// "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
+//
+//<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+//<let-dig-hyp> ::= <let-dig> | "-"
+//<let-dig> ::= <letter> | <digit>
+var validEmailRegexp =
+	"[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~.]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*";
+
+function autolink(node, endOffset) {
+	// "While (node, end offset)'s previous equivalent point is not null, set
+	// it to its previous equivalent point."
+	while (getPreviousEquivalentPoint(node, endOffset)) {
+		var prev = getPreviousEquivalentPoint(node, endOffset);
+		node = prev[0];
+		endOffset = prev[1];
+	}
+
+	// "If node is not a Text node, or has an a ancestor, do nothing and abort
+	// these steps."
+	if (node.nodeType != Node.TEXT_NODE
+	|| getAncestors(node).some(function(ancestor) { return isHtmlElement(ancestor, "a") })) {
+		return;
+	}
+
+	// "Let search be the largest substring of node's data whose end is end
+	// offset and that contains no space characters."
+	var search = /[^ \t\n\f\r]*$/.exec(node.substringData(0, endOffset))[0];
+
+	// "If some substring of search is an autolinkable URL:"
+	if (new RegExp(autolinkableUrlRegexp).test(search)) {
+		// "While there is no substring of node's data ending at end offset
+		// that is an autolinkable URL, decrement end offset."
+		while (!(new RegExp(autolinkableUrlRegexp + "$").test(node.substringData(0, endOffset)))) {
+			endOffset--;
+		}
+
+		// "Let start offset be the start index of the longest substring of
+		// node's data that is an autolinkable URL ending at end offset."
+		var startOffset = new RegExp(autolinkableUrlRegexp + "$").exec(node.substringData(0, endOffset)).index;
+
+		// "Let href be the substring of node's data starting at start offset
+		// and ending at end offset."
+		var href = node.substringData(startOffset, endOffset - startOffset);
+
+	// "Otherwise, if some substring of search is a valid e-mail address:"
+	} else if (new RegExp(validEmailRegexp).test(search)) {
+		// "While there is no substring of node's data ending at end offset
+		// that is a valid e-mail address, decrement end offset."
+		while (!(new RegExp(validEmailRegexp + "$").test(node.substringData(0, endOffset)))) {
+			endOffset--;
+		}
+
+		// "Let start offset be the start index of the longest substring of
+		// node's data that is a valid e-mail address ending at end offset."
+		var startOffset = new RegExp(validEmailRegexp + "$").exec(node.substringData(0, endOffset)).index;
+
+		// "Let href be "mailto:" concatenated with the substring of node's
+		// data starting at start offset and ending at end offset."
+		var href = "mailto:" + node.substringData(startOffset, endOffset - startOffset);
+
+	// "Otherwise, do nothing and abort these steps."
+	} else {
+		return;
+	}
+
+	// "Let original range be the active range."
+	var originalRange = getActiveRange();
+
+	// "Create a new range with start (node, start offset) and end (node, end
+	// offset), and set the context object's selection's range to it."
+	var newRange = document.createRange();
+	newRange.setStart(node, startOffset);
+	newRange.setEnd(node, endOffset);
+	getSelection().removeAllRanges();
+	getSelection().addRange(newRange);
+	globalRange = newRange;
+
+	// "Call execCommand("createLink", false, href) on the context object."
+	myExecCommand("createLink", false, href);
+
+	// "Set the context object's selection's range to original range."
+	getSelection().removeAllRanges();
+	getSelection().addRange(originalRange);
+	globalRange = originalRange;
+}
+//@}
 ///// The delete command /////
 //@{
 commands["delete"] = {
@@ -7761,6 +7867,11 @@
 		// collapsed space false."
 		canonicalizeWhitespace(getActiveRange().endContainer, getActiveRange().endOffset, false);
 
+		// "If value is a space character, autolink the active range's start."
+		if (/^[ \t\n\f\r]$/.test(value)) {
+			autolink(getActiveRange().startContainer, getActiveRange().startOffset);
+		}
+
 		// "Call collapseToEnd() on the context object's Selection."
 		//
 		// Work around WebKit bug: sometimes it blows up the selection and
--- a/preprocess	Wed Nov 09 12:23:23 2011 -0700
+++ b/preprocess	Wed Nov 09 12:42:35 2011 -0700
@@ -150,6 +150,7 @@
     'addrange': '<code title=dom-Selection-addRange>addRange(\\1)</code>',
     'appendchild': '<code data-anolis-spec=dom title=dom-Node-appendChild>appendChild(\\1)</code>',
     'createelement': '<code data-anolis-spec=dom title=dom-Document-createElement>createElement(\\1)</code>',
+    'createtextnode': '<code data-anolis-spec=dom title=dom-Document-createTextNode>createTextNode(\\1)</code>',
     'deletedata': '<code data-anolis-spec=dom title=dom-CharacterData-deleteData>deleteData(\\1)</code>',
     'execcommand': '<code title=execCommand()>execCommand(\\1)</code>',
     'extend': '<code title=dom-Selection-extend>extend(\\1)</code>',
--- a/source.html	Wed Nov 09 12:23:23 2011 -0700
+++ b/source.html	Wed Nov 09 12:42:35 2011 -0700
@@ -7548,6 +7548,137 @@
 </ol>
 
 <!-- @} -->
+<h3>Automatic linking</h3>
+<!-- @{ -->
+<p class=comments><a
+href=http://www.w3.org/Bugs/Public/show_bug.cgi?id=13807>Bug 13807</a>.
+
+<p class=note>When the user inserts whitespace immediately following something
+that looks like a URL or e-mail address, we automatically run <span>the <code
+title>createLink</code> command</span> on it.
+
+<p>An <dfn>autolinkable URL</dfn> is a string of the following form:
+
+<ol>
+  <li>
+  <p class=comments>IE9 and LibreOffice 3.3.4 both have a whitelist of URL
+  schemes.  That would be complicated and involve political decisions, so
+  instead, we'll just accept anything that looks like a hierarchical URL
+  scheme.  Google Docs is similar (as of November 9, 2011), but it's too lax,
+  and allows characters in the scheme that can't be in a scheme.  For
+  non-hierarchical schemes, we just whitelist mailto:, since it's the only
+  common one that makes sense to autolink.
+
+  <p>Either a string matching the <var>scheme</var> pattern from <a
+  href=http://tools.ietf.org/html/rfc3986#section-3.1>RFC 3986 section 3.1</a>
+  followed by the literal string {{code|://}}, or the literal string
+  {{code|mailto:}}; followed by
+
+  <li>
+  <p class=comments>We don't try to enforce that the URL is anything resembling
+  valid per spec.  Too complicated for not enough gain.
+
+  <p>Zero or more characters other than [[spacecharacters]]; followed by
+
+  <li>
+  <div class=comments>
+  <p>If the user types a URL followed by some punctuation, we still want to
+  autolink, but we don't want to include the punctuation if it's probably not
+  meant as part of the URL.
+
+  <p>IE9 excludes <code title>!#&amp;()*+,-.:;&lt;=?@[]^_`{|}~</code> as
+  trailing characters from both URLs and e-mails.  A trailing {{code|"}} or
+  {{code|>}} will prevent autolinking, and a trailing {{code|$%'/\}} is
+  included in the link.
+
+  <p>LibreOffice 3.3.4 excludes trailing {{code|!”#'()*+,.:;<=>?[\]^_`{|}~}},
+  and prevents autolinking on {{code|$%&-@ }}.  It includes a trailing
+  {{code|/}} in URLs, but it inhibits linking for e-mails.
+
+  <p>Google Docs (as of November 9, 2011) is complicated.  Trailing
+  {{code|”’,-.}} always prevents autolinking of a URL, and trailing
+  {{code|#%/?_}} is always included in a URL.  Trailing <code
+  title>!&amp;=$()*+:;&lt;>@[\]^`{|}~</code> prevent autolinking if there's no
+  {{code|?}} before them, but are included in the URL if there is a {{code|?}}.
+  For e-mails, {{code|_}} is included, and everything else prevents
+  autolinking.
+
+  <p>None of these behaviors makes maximal sense.  We should exclude characters
+  if they're more likely as delimiters than actual trailing characters; include
+  them if they're more likely as actual trailing characters; and prevent
+  autolinking if their presence suggests that we're not actually looking at a
+  link or e-mail address.  The lists I made up for URLs are: exclude trailing
+  <code title>!"'(),-.:;&lt;>[]`{}</code>, include anything else.  For e-mail,
+  exclude anything at all.
+  </div>
+
+  <p>A character that is not one of the ASCII characters <code
+  title>!"'(),-.:;&lt;>[]`{}</code>.
+</ol>
+
+<p>To <dfn>autolink</dfn> (<var>node</var>, <var>end offset</var>):
+
+<ol>
+  <li>While (<var>node</var>, <var>end offset</var>)'s <span>previous
+  equivalent point</span> is not null, set it to its <span>previous equivalent
+  point</span>.
+
+  <li>If <var>node</var> is not a [[text]] node, or has an [[a]] [[ancestor]],
+  do nothing and abort these steps.
+
+  <li>Let <var>search</var> be the largest substring of <var>node</var>'s
+  [[cddata]] whose end is <var>end offset</var> and that contains no
+  [[spacecharacters]].
+
+  <li>If some substring of <var>search</var> is an <span>autolinkable
+  URL</span>:
+
+  <ol>
+    <li>While there is no substring of <var>node</var>'s [[cddata]] ending at
+    <var>end offset</var> that is an <span>autolinkable URL</span>, decrement
+    <var>end offset</var>.
+
+    <li>Let <var>start offset</var> be the start index of the longest substring
+    of <var>node</var>'s [[cddata]] that is an <span>autolinkable URL</span>
+    ending at <var>end offset</var>.
+
+    <li>Let <var>href</var> be the substring of <var>node</var>'s [[cddata]]
+    starting at <var>start offset</var> and ending at <var>end offset</var>.
+  </ol>
+
+  <li>Otherwise, if some substring of <var>search</var> is a <span
+  data-anolis-spec=html>valid e-mail address</span>:
+
+  <ol>
+    <li>While there is no substring of <var>node</var>'s [[cddata]] ending at
+    <var>end offset</var> that is a <span data-anolis-spec=html>valid e-mail
+    address</span>, decrement <var>end offset</var>.
+
+    <li>Let <var>start offset</var> be the start index of the longest substring
+    of <var>node</var>'s [[cddata]] that is a <span data-anolis-spec=html>valid
+    e-mail address</span> ending at <var>end offset</var>.
+
+    <li>Let <var>href</var> be "mailto:" concatenated with the substring of
+    <var>node</var>'s [[cddata]] starting at <var>start offset</var> and ending
+    at <var>end offset</var>.
+  </ol>
+
+  <li>Otherwise, do nothing and abort these steps.
+
+  <li>Let <var>original range</var> be the <span>active range</span>.
+
+  <li>Create a new [[range]] with [[rangestart]] (<var>node</var>, <var>start
+  offset</var>) and [[rangeend]] (<var>node</var>, <var>end offset</var>), and
+  set the [[contextobject]]'s [[selection]]'s [[range]] to it.
+
+  <li>Call [[execcommand|"createLink", false, <var>href</var>]] on the
+  [[contextobject]].
+
+  <li>Set the [[contextobject]]'s [[selection]]'s [[range]] to <var>original
+  range</var>.
+</ol>
+
+<!-- @} -->
 <h3><dfn>The <code title>delete</code> command</dfn></h3>
 <!-- @{ -->
 <div class=note>
@@ -9625,10 +9756,8 @@
     <p>If <var>node</var> has only one [[child]], which is a <span>collapsed
     line break</span>, remove its [[child]] from it.
 
-    <li>Let <var>text</var> be the result of calling <code
-    data-anolis-spec=domcore
-    title=dom-Document-createTextNode>createTextNode(<var>value</var>)</code> on
-    the [[contextobject]].
+    <li>Let <var>text</var> be the result of calling
+    [[createtextnode|<var>value</var>]] on the [[contextobject]].
 
     <li>Call [[insertnode|<var>text</var>]] on the <span>active range</span>.
 
@@ -9647,6 +9776,9 @@
   <li><span>Canonicalize whitespace</span> at the <span>active range</span>'s
   [[rangeend]], with <var>fix collapsed space</var> false.
 
+  <li>If <var>value</var> is a [[spacecharacter]], <span>autolink</span> the
+  <span>active range</span>'s [[rangestart]].
+
   <li>Call [[collapsetoend]] on the [[contextobject]]'s [[selection]].
 </ol>
 
--- a/tests.js	Wed Nov 09 12:23:23 2011 -0700
+++ b/tests.js	Wed Nov 09 12:42:35 2011 -0700
@@ -2580,6 +2580,40 @@
 
 		// End whitespace tests
 
+		// Autolinking tests
+		[' ', 'http://a[]'],
+		[' ', 'ftp://a[]'],
+		[' ', 'quasit://a[]'],
+		[' ', '.x-++-.://a[]'],
+		[' ', '(http://a)[]'],
+		[' ', '&lt;http://a>[]'],
+		// http://www.w3.org/Bugs/Public/show_bug.cgi?id=14744
+		['! ', '&#x5b;http://a&#x5d;[]'],
+		['! ', '&#x7b;http://a&#x7d;[]'],
+		[' ', 'http://a![]'],
+		[' ', '!"#$%&amp;\'()*+,-./:;&lt;=>?\^_`|~http://a!"#$%&amp;\'()*+,-./:;&lt;=>?\^_`|~[]'],
+		[' ', 'http://a!"\'(),-.:;&lt;>`[]'],
+		[' ', 'http://a#$%&amp;*+/=?\^_|~[]'],
+		[' ', 'mailto:a[]'],
+		[' ', 'a@b[]'],
+		[' ', 'a@[]'],
+		[' ', '@b[]'],
+		[' ', '#@x[]'],
+		[' ', 'a@.[]'],
+		[' ', '!"#$%&amp;\'()*+,-./:;&lt;=>?\^_`|~a@b!"#$%&amp;\'()*+,-./:;&lt;=>?\^_`|~[]'],
+		[' ', '<b>a@b</b>{}'],
+		[' ', '<b>a</b><i>@</i><u>b</u>{}'],
+		[' ', 'a@b<b>[]c</b>'],
+		[' ', '<p>a@b</p><p>[]c</p>'],
+		['a', 'http://a[]'],
+		['\t', 'http://a[]'],
+		// http://www.w3.org/Bugs/Public/show_bug.cgi?id=14254
+		['!\r', 'http://a[]'],
+		// http://www.w3.org/Bugs/Public/show_bug.cgi?id=14745
+		['!\n', 'http://a[]'],
+		['\f', 'http://a[]'],
+		['\u00A0', 'http://a[]'],
+
 		['   ', 'foo[]'],
 
 		'foo[]bar',