~tsileo/blobstash

1043ddd1a192c2a84cc0821697c511d0fde1cca3 — Thomas Sileo a month ago ae2f65a
deps: update gluapp
M go.mod => go.mod +1 -2
@@ 4,7 4,7 @@ replace github.com/go-interpreter/wagon v0.0.0 => github.com/perlin-network/wago

require (
	a4.io/blobsfile v0.3.8
	a4.io/gluapp v0.0.0-20200202115504-51581a8e4642
	a4.io/gluapp v0.0.0-20200211221502-1cf72ec06507
	a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f
	a4.io/go/indieauth v1.0.0
	a4.io/ssse v0.0.0-20181202155639-1949828a8689


@@ 42,7 42,6 @@ require (
	gopkg.in/src-d/enry.v1 v1.7.3
	gopkg.in/src-d/go-git.v4 v4.13.1
	gopkg.in/yaml.v2 v2.2.8
	mvdan.cc/xurls/v2 v2.1.0 // indirect
)

go 1.13

M go.sum => go.sum +3 -48
@@ 2,18 2,6 @@ a4.io/blobsfile v0.0.0-20181029195936-c742249a3522 h1:QVL41vUxtKA6ceXG4W9ha6ZOER
a4.io/blobsfile v0.0.0-20181029195936-c742249a3522/go.mod h1:jTrsc9CgnEavpl6Tmowi2bZbGXldVGr5gvkFsS12bKs=
a4.io/blobsfile v0.1.0 h1:nE9v20zzfL7UKvZuA2J42ncYBCqZ9mgVU9am9+XZdZk=
a4.io/blobsfile v0.1.0/go.mod h1:kJFL3M8OxlvHZWzxZ6C9o+ky9iJHmx0jZj59nilWzJM=
a4.io/blobsfile v0.3.1 h1:7kcNkIS4wWyCLS+s4BIb86KdxtoC4BOt/KjCcFzJuwA=
a4.io/blobsfile v0.3.1/go.mod h1:Jy00okG1uHZQ5MN9WskYFFffuDFq4UbHNb0aWRqWo9c=
a4.io/blobsfile v0.3.2 h1:p+Wm8aOsV01kYhyJFCiNGga42fi+vAZ587ljb4bhK94=
a4.io/blobsfile v0.3.2/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobsfile v0.3.3 h1:0xmSR3jKnZUHjBml3YuWtA45YiLFHB3WkHObC2x2xLw=
a4.io/blobsfile v0.3.3/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobsfile v0.3.4 h1:1eP7TjcT6GPrr3JHqXuz2ZmJ4pZbDcrcMAt3BfABsQI=
a4.io/blobsfile v0.3.4/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobsfile v0.3.5 h1:ZxG7KJIHArvKnJP8nH8vwcH9S2JLtFYk3bE3PcRUCUI=
a4.io/blobsfile v0.3.5/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobsfile v0.3.7 h1:9Br96WvGit0rOOcLN2Eesk0PeXQlVZQea716AGYmleA=
a4.io/blobsfile v0.3.7/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobsfile v0.3.8 h1:JYsQQTe/chWmnZHxLM7ZQ91lkuoC/2ePr+Y8HbTTzHs=
a4.io/blobsfile v0.3.8/go.mod h1:ZHwdtHHOeCbaP/dpPnA1FAUrrwXG9GX2ju3OrbufCjQ=
a4.io/blobstash v0.0.0-20181216235946-aa2d4a59f200/go.mod h1:PVI3EM/VmUQAz7pbz/govGO4gHypTF5YWhS56qETj+M=


@@ 27,10 15,10 @@ a4.io/gluapp v0.0.0-20181217122610-c6ba9b02f21b/go.mod h1:hDz8O30eiYv+1bAFzssTvb
a4.io/gluapp v0.0.0-20181218195258-2be1706b2908/go.mod h1:hDz8O30eiYv+1bAFzssTvbRaLy27xwk7pdR7v2md7Ew=
a4.io/gluapp v0.0.0-20190530193846-2ad05291e3be/go.mod h1:46QpRqVnBeahZFsw+6+/NPhwex2jY7ZrFvPoRdhgu5Y=
a4.io/gluapp v0.0.0-20200131211012-723a51b0e790/go.mod h1:XvZKnPX9E8UAoNcbJ5ESYCr9c6yYTRyv/4iDsDt/Eyo=
a4.io/gluapp v0.0.0-20200202110637-971b705265cd h1:sEc8dYu2KxdjnHFyUW0YIQAXYlo9lo61Z7okKBsbS78=
a4.io/gluapp v0.0.0-20200202110637-971b705265cd/go.mod h1:XvZKnPX9E8UAoNcbJ5ESYCr9c6yYTRyv/4iDsDt/Eyo=
a4.io/gluapp v0.0.0-20200202115504-51581a8e4642 h1:NonKQZ70i96LEfpQWxytRMtvbmNMj6myyz62Xr5D56g=
a4.io/gluapp v0.0.0-20200202115504-51581a8e4642/go.mod h1:jgLJ6nULqKUJSJsT4W0KuNhh/lqXo6cS/pDuuESx4ko=
a4.io/gluapp v0.0.0-20200211221502-1cf72ec06507 h1:MPV/5i/k69WNBzMflw0tIE86qxA230wnIJDlCcCg38Q=
a4.io/gluapp v0.0.0-20200211221502-1cf72ec06507/go.mod h1:NubumC8nAuzD3ZuGMe29w/Ofai3+YBwMEP8oQyHCu0s=
a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f h1:mfEWN0Dd2AfIXU5WO5ZfqbFVk63Qz5M/CANs182pm+U=
a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f/go.mod h1:t7OhwCmPQfuUf8cjm7n8chSbZt5CTILu+dTLu1MQKjQ=
a4.io/go/indieauth v1.0.0 h1:ZZh1ilpNwwlzZ9xH2f0VeSN/dvP22Px5naoGVe4wJ5A=


@@ 51,18 39,10 @@ github.com/aws/aws-sdk-go v1.16.6 h1:pig/KdfESvIv4gUu1B8nVAJAURxbPCTt6e5u79Nqxqc
github.com/aws/aws-sdk-go v1.16.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.16.11 h1:g/c7gJeVyHoXCxM2fddS85bPGVkBF8s2q8t3fyElegc=
github.com/aws/aws-sdk-go v1.16.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.25 h1:j3HLOqcDWjNox1DyvJRs+kVQF42Ghtv6oL6cVBfXS3U=
github.com/aws/aws-sdk-go v1.25.25/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.27 h1:UANmajXi1Vn7eZ9GgdDtkFjxDiaHY6tUixCiB6Bj128=
github.com/aws/aws-sdk-go v1.25.27/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.30 h1:I9qj6zW3mMfsg91e+GMSN/INcaX9tTFvr/l/BAHKaIY=
github.com/aws/aws-sdk-go v1.25.30/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.31 h1:14mdh3HsTgRekePPkYcCbAaEXJknc3mN7f4XfsiMMDA=
github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.28.9 h1:grIuBQc+p3dTRXerh5+2OxSuWFi0iXuxbFdTSg0jaW0=
github.com/aws/aws-sdk-go v1.28.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.28.14 h1:ZeFS5GVtsJMZ0TBJ5n4HYwB/4MpY0hWkRthNNZkIzNo=
github.com/aws/aws-sdk-go v1.28.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.29.0 h1:UFxrMQhDyLak6kVtOcr4PZxNRQV0s7pY/vKAyzRvi8c=
github.com/aws/aws-sdk-go v1.29.0/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f h1:kqbi9lqXLLs+zfWlgo1PIiRQ86n33K1JKotjj4rSYOg=


@@ 193,8 173,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/justinas/nosurf v0.0.0-20181122113328-3af30e51c05b h1:fWjiIutptAhQwIoCjCEsyCx6KtaHJ6WyqCLdmFJ3udQ=
github.com/justinas/nosurf v0.0.0-20181122113328-3af30e51c05b/go.mod h1:Aucr5I5chr4OCuuVB4LTuHVrKHBuyRSo7vM2hqrcb7E=
github.com/justinas/nosurf v0.0.0-20190416172904-05988550ea18 h1:ci3v0mUqcCewO25ntt7hprt2ZMNA0AWI6s6qV0rSpc0=
github.com/justinas/nosurf v0.0.0-20190416172904-05988550ea18/go.mod h1:Aucr5I5chr4OCuuVB4LTuHVrKHBuyRSo7vM2hqrcb7E=
github.com/justinas/nosurf v1.1.0 h1:qqV6FJmnDBJ6F9pOzhZgZitAZWBYonMOXglof7TtdZw=
github.com/justinas/nosurf v1.1.0/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=


@@ 318,14 296,13 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2 h1:GnOzE5fEFN3b2zDhJJABEofdb51uMRNb8eqIVtdducs=
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/toqueteos/trie v0.0.0-20150530104557-56fed4a05683 h1:ej8ns+4aeQO+mm9VIzwnJElkqR0Vs6kTfIcvgyJFoMY=
github.com/toqueteos/trie v0.0.0-20150530104557-56fed4a05683/go.mod h1:Ywk48QhEqhU1+DwhMkJ2x7eeGxDHiGkAdc9+0DYcbsM=
github.com/toqueteos/trie v1.0.0 h1:8i6pXxNUXNRAqP246iibb7w/pSFquNTQ+uNfriG7vlk=
github.com/toqueteos/trie v1.0.0/go.mod h1:Ywk48QhEqhU1+DwhMkJ2x7eeGxDHiGkAdc9+0DYcbsM=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606 h1:dU9yXzNi9rl6Mou7+3npdfPyeFPb2+7BHs3zL47bhPY=
github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA=


@@ 353,8 330,6 @@ github.com/xeonx/timeago v1.0.0-rc4 h1:9rRzv48GlJC0vm+iBpLcWAr8YbETyN9Vij+7h2amm
github.com/xeonx/timeago v1.0.0-rc4/go.mod h1:qDLrYEFynLO7y5Ho7w3GwgtYgpy5UfhcXIIQvMKVDkA=
github.com/yuin/goldmark v1.0.5 h1:FRQrY00hoGAECny+OwWVcgfkkJL1exPC/xNjUHszljE=
github.com/yuin/goldmark v1.0.5/go.mod h1:GAOXQunDkMxip+WLt/Bb4n4TEwap/Bit20gguI0UhOE=
github.com/yuin/goldmark v1.1.1 h1:Nn9xYPltpahcbwhvB/Gml+KUhnIzZtcuQD2h7K1nFWE=
github.com/yuin/goldmark v1.1.1/go.mod h1:hDgn8A2EV4OniExoeJs1fSrmEc/T7w8+Teyq8YkThxQ=
github.com/yuin/goldmark v1.1.2 h1:grIesFXV7jYjICFHQUdmU1uRxwCoOagz1ciWfB3mK88=
github.com/yuin/goldmark v1.1.2/go.mod h1:hDgn8A2EV4OniExoeJs1fSrmEc/T7w8+Teyq8YkThxQ=
github.com/yuin/goldmark v1.1.22 h1:0e0f6Zee9SAQ5yOZGNMWaOxqVvcc/9/kUWu/Kl91Jk8=


@@ 374,12 349,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59 h1:PyXRxSVbvzDGuqYXjHndV7xDzJ7w2K8KD9Ef8GB7KOE=
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191107222254-f4817d981bb6 h1:VsmCukA2gDdC3Mu6evOIT0QjLSQWiJIwzv1Bdj4jdzU=
golang.org/x/crypto v0.0.0-20191107222254-f4817d981bb6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc=
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=


@@ 400,12 369,6 @@ golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 h1:DPz9iiH3YoKiKhX/ijjoZvT0VFwK2c6CWYWQ7Zyr8TU=
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191105084925-a882066a44e0 h1:QPlSTtPE2k6PZPasQUbzuK3p9JbS+vMXYVto8g/yrsg=
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191108174545-380dde419d29 h1:K1loz76fyOYZfdgWfckjyEXQnbrZOZp0Y4bNyk+WnRE=
golang.org/x/net v0.0.0-20191108174545-380dde419d29/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM=
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=


@@ 437,12 400,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105142833-ac3223d80179 h1:IqVhUQp5B9ARnZUcfqXy6zP+A+YuPpP7IFo8gFeCOzU=
golang.org/x/sys v0.0.0-20191105142833-ac3223d80179/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=


@@ 510,7 467,5 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
willnorris.com/go/microformats v1.0.0 h1:II6uDIJBPp6RpJQqRWm+6IN9lI00mN/jQAC5OHuF4HA=
willnorris.com/go/microformats v1.0.0/go.mod h1:AXRtimOA0J5fDmM2sxlka4G6PNLWC4bCNJcZjLvFdDw=

M vendor/a4.io/gluapp/go.mod => vendor/a4.io/gluapp/go.mod +2 -1
@@ 1,11 1,12 @@
module a4.io/gluapp

require (
	a4.io/blobstash v0.0.0-20200131212433-e97337e98c5e
	a4.io/blobstash v0.0.0-20200202192640-d62b4924ec01
	a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f
	github.com/yuin/goldmark v1.1.22
	github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
	gopkg.in/yaml.v2 v2.2.8
	mvdan.cc/xurls v1.1.0
)

go 1.13

M vendor/a4.io/gluapp/go.sum => vendor/a4.io/gluapp/go.sum +8 -0
@@ 9,11 9,14 @@ a4.io/blobstash v0.0.0-20191229152948-bc315003dfb2 h1:IrSuuUq3Ib1+DSOkqP3MOoasNG
a4.io/blobstash v0.0.0-20191229152948-bc315003dfb2/go.mod h1:YrXxObZG8guBDGhUolW6/MlbngxiUk7+H5liKBxDd3s=
a4.io/blobstash v0.0.0-20200131212433-e97337e98c5e h1:BoQS5Wt9GqjeNJhAI/5U9pzJgcPoQAiPE3xmhVKfFw8=
a4.io/blobstash v0.0.0-20200131212433-e97337e98c5e/go.mod h1:SrGKNSU1yJ6esqF0aqD53kjCrpHoEl28kuQdKHUvXsc=
a4.io/blobstash v0.0.0-20200202192640-d62b4924ec01 h1:Cxt+qvHHH+W20N1/9WhKEbYjlymaOIz3HPEGvDYdSN0=
a4.io/blobstash v0.0.0-20200202192640-d62b4924ec01/go.mod h1:38J1ivXRQHMj5UZasbPi7o/P5nc7snAeaAu7lIXL1gM=
a4.io/gluapp v0.0.0-20181203183836-c136dc4e9123/go.mod h1:rK/CQwI+tDICKCR1szNtBP0rJdH1LCrO/ZnculcIjWI=
a4.io/gluapp v0.0.0-20181217122610-c6ba9b02f21b/go.mod h1:hDz8O30eiYv+1bAFzssTvbRaLy27xwk7pdR7v2md7Ew=
a4.io/gluapp v0.0.0-20181218195258-2be1706b2908/go.mod h1:hDz8O30eiYv+1bAFzssTvbRaLy27xwk7pdR7v2md7Ew=
a4.io/gluapp v0.0.0-20190530193846-2ad05291e3be/go.mod h1:46QpRqVnBeahZFsw+6+/NPhwex2jY7ZrFvPoRdhgu5Y=
a4.io/gluapp v0.0.0-20200131211012-723a51b0e790/go.mod h1:XvZKnPX9E8UAoNcbJ5ESYCr9c6yYTRyv/4iDsDt/Eyo=
a4.io/gluapp v0.0.0-20200202115504-51581a8e4642/go.mod h1:jgLJ6nULqKUJSJsT4W0KuNhh/lqXo6cS/pDuuESx4ko=
a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f h1:mfEWN0Dd2AfIXU5WO5ZfqbFVk63Qz5M/CANs182pm+U=
a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f/go.mod h1:t7OhwCmPQfuUf8cjm7n8chSbZt5CTILu+dTLu1MQKjQ=
a4.io/go/indieauth v1.0.0/go.mod h1:yCJuSTw9d22VdPWrZ8frGLwVOdwscJTiXjG4IgVL0Vw=


@@ 69,6 72,7 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20181104084050-d1d0edeb5d85/go.mod h1:gmFANS06wAVmF0B9yi65QKsRmPQ97tze7FRLswua+OY=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/goods/httpbuf v0.0.0-20120503183857-5709e9bb814c/go.mod h1:cHMBumiwaaRxRQ6NT8sU3zQSkXbYaPjbBcXa8UgTzAE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=


@@ 186,6 190,7 @@ github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBU
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/zpatrick/rbac v0.0.0-20180829190353-d2c4f050cf28/go.mod h1:WBaExyQHBJO9SelgH0SNqmlwYKV62vfnHCX5lXii91c=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=


@@ 205,6 210,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=


@@ 259,4 265,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
willnorris.com/go/microformats v1.0.0/go.mod h1:AXRtimOA0J5fDmM2sxlka4G6PNLWC4bCNJcZjLvFdDw=

M vendor/a4.io/gluapp/template.go => vendor/a4.io/gluapp/template.go +14 -0
@@ 9,8 9,10 @@ import (
	"time"

	"a4.io/blobstash/pkg/apps/luautil"
	"mvdan.cc/xurls"

	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/gopher-lua"
)


@@ 20,6 22,18 @@ var mdc = goldmark.New(
		html.WithHardWraps(),
		html.WithUnsafe(),
	),
	goldmark.WithExtensions(
		extension.Table,
		extension.NewLinkify(
			extension.WithLinkifyAllowedProtocols([][]byte{
				[]byte("http:"),
				[]byte("https:"),
			}),
			extension.WithLinkifyURLRegexp(
				xurls.Strict,
			),
		),
	),
)

var funcs = template.FuncMap{

A vendor/github.com/yuin/goldmark/extension/ast/definition_list.go => vendor/github.com/yuin/goldmark/extension/ast/definition_list.go +83 -0
@@ 0,0 1,83 @@
package ast

import (
	gast "github.com/yuin/goldmark/ast"
)

// A DefinitionList struct represents a definition list of Markdown
// (PHPMarkdownExtra) text.
type DefinitionList struct {
	gast.BaseBlock
	Offset             int
	TemporaryParagraph *gast.Paragraph
}

// Dump implements Node.Dump.
func (n *DefinitionList) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindDefinitionList is a NodeKind of the DefinitionList node.
var KindDefinitionList = gast.NewNodeKind("DefinitionList")

// Kind implements Node.Kind.
func (n *DefinitionList) Kind() gast.NodeKind {
	return KindDefinitionList
}

// NewDefinitionList returns a new DefinitionList node.
func NewDefinitionList(offset int, para *gast.Paragraph) *DefinitionList {
	return &DefinitionList{
		Offset:             offset,
		TemporaryParagraph: para,
	}
}

// A DefinitionTerm struct represents a definition list term of Markdown
// (PHPMarkdownExtra) text.
type DefinitionTerm struct {
	gast.BaseBlock
}

// Dump implements Node.Dump.
func (n *DefinitionTerm) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindDefinitionTerm is a NodeKind of the DefinitionTerm node.
var KindDefinitionTerm = gast.NewNodeKind("DefinitionTerm")

// Kind implements Node.Kind.
func (n *DefinitionTerm) Kind() gast.NodeKind {
	return KindDefinitionTerm
}

// NewDefinitionTerm returns a new DefinitionTerm node.
func NewDefinitionTerm() *DefinitionTerm {
	return &DefinitionTerm{}
}

// A DefinitionDescription struct represents a definition list description of Markdown
// (PHPMarkdownExtra) text.
type DefinitionDescription struct {
	gast.BaseBlock
	IsTight bool
}

// Dump implements Node.Dump.
func (n *DefinitionDescription) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindDefinitionDescription is a NodeKind of the DefinitionDescription node.
var KindDefinitionDescription = gast.NewNodeKind("DefinitionDescription")

// Kind implements Node.Kind.
func (n *DefinitionDescription) Kind() gast.NodeKind {
	return KindDefinitionDescription
}

// NewDefinitionDescription returns a new DefinitionDescription node.
func NewDefinitionDescription() *DefinitionDescription {
	return &DefinitionDescription{}
}

A vendor/github.com/yuin/goldmark/extension/ast/footnote.go => vendor/github.com/yuin/goldmark/extension/ast/footnote.go +125 -0
@@ 0,0 1,125 @@
package ast

import (
	"fmt"
	gast "github.com/yuin/goldmark/ast"
)

// A FootnoteLink struct represents a link to a footnote of Markdown
// (PHP Markdown Extra) text.
type FootnoteLink struct {
	gast.BaseInline
	Index int
}

// Dump implements Node.Dump.
func (n *FootnoteLink) Dump(source []byte, level int) {
	m := map[string]string{}
	m["Index"] = fmt.Sprintf("%v", n.Index)
	gast.DumpHelper(n, source, level, m, nil)
}

// KindFootnoteLink is a NodeKind of the FootnoteLink node.
var KindFootnoteLink = gast.NewNodeKind("FootnoteLink")

// Kind implements Node.Kind.
func (n *FootnoteLink) Kind() gast.NodeKind {
	return KindFootnoteLink
}

// NewFootnoteLink returns a new FootnoteLink node.
func NewFootnoteLink(index int) *FootnoteLink {
	return &FootnoteLink{
		Index: index,
	}
}

// A FootnoteBackLink struct represents a link to a footnote of Markdown
// (PHP Markdown Extra) text.
type FootnoteBackLink struct {
	gast.BaseInline
	Index int
}

// Dump implements Node.Dump.
func (n *FootnoteBackLink) Dump(source []byte, level int) {
	m := map[string]string{}
	m["Index"] = fmt.Sprintf("%v", n.Index)
	gast.DumpHelper(n, source, level, m, nil)
}

// KindFootnoteBackLink is a NodeKind of the FootnoteBackLink node.
var KindFootnoteBackLink = gast.NewNodeKind("FootnoteBackLink")

// Kind implements Node.Kind.
func (n *FootnoteBackLink) Kind() gast.NodeKind {
	return KindFootnoteBackLink
}

// NewFootnoteBackLink returns a new FootnoteBackLink node.
func NewFootnoteBackLink(index int) *FootnoteBackLink {
	return &FootnoteBackLink{
		Index: index,
	}
}

// A Footnote struct represents a footnote of Markdown
// (PHP Markdown Extra) text.
type Footnote struct {
	gast.BaseBlock
	Ref   []byte
	Index int
}

// Dump implements Node.Dump.
func (n *Footnote) Dump(source []byte, level int) {
	m := map[string]string{}
	m["Index"] = fmt.Sprintf("%v", n.Index)
	m["Ref"] = fmt.Sprintf("%s", n.Ref)
	gast.DumpHelper(n, source, level, m, nil)
}

// KindFootnote is a NodeKind of the Footnote node.
var KindFootnote = gast.NewNodeKind("Footnote")

// Kind implements Node.Kind.
func (n *Footnote) Kind() gast.NodeKind {
	return KindFootnote
}

// NewFootnote returns a new Footnote node.
func NewFootnote(ref []byte) *Footnote {
	return &Footnote{
		Ref:   ref,
		Index: -1,
	}
}

// A FootnoteList struct represents footnotes of Markdown
// (PHP Markdown Extra) text.
type FootnoteList struct {
	gast.BaseBlock
	Count int
}

// Dump implements Node.Dump.
func (n *FootnoteList) Dump(source []byte, level int) {
	m := map[string]string{}
	m["Count"] = fmt.Sprintf("%v", n.Count)
	gast.DumpHelper(n, source, level, m, nil)
}

// KindFootnoteList is a NodeKind of the FootnoteList node.
var KindFootnoteList = gast.NewNodeKind("FootnoteList")

// Kind implements Node.Kind.
func (n *FootnoteList) Kind() gast.NodeKind {
	return KindFootnoteList
}

// NewFootnoteList returns a new FootnoteList node.
func NewFootnoteList() *FootnoteList {
	return &FootnoteList{
		Count: 0,
	}
}

A vendor/github.com/yuin/goldmark/extension/ast/strikethrough.go => vendor/github.com/yuin/goldmark/extension/ast/strikethrough.go +29 -0
@@ 0,0 1,29 @@
// Package ast defines AST nodes that represents extension's elements
package ast

import (
	gast "github.com/yuin/goldmark/ast"
)

// A Strikethrough struct represents a strikethrough of GFM text.
type Strikethrough struct {
	gast.BaseInline
}

// Dump implements Node.Dump.
func (n *Strikethrough) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindStrikethrough is a NodeKind of the Strikethrough node.
var KindStrikethrough = gast.NewNodeKind("Strikethrough")

// Kind implements Node.Kind.
func (n *Strikethrough) Kind() gast.NodeKind {
	return KindStrikethrough
}

// NewStrikethrough returns a new Strikethrough node.
func NewStrikethrough() *Strikethrough {
	return &Strikethrough{}
}

A vendor/github.com/yuin/goldmark/extension/ast/table.go => vendor/github.com/yuin/goldmark/extension/ast/table.go +157 -0
@@ 0,0 1,157 @@
package ast

import (
	"fmt"
	gast "github.com/yuin/goldmark/ast"
	"strings"
)

// Alignment is a text alignment of table cells.
type Alignment int

const (
	// AlignLeft indicates text should be left justified.
	AlignLeft Alignment = iota + 1

	// AlignRight indicates text should be right justified.
	AlignRight

	// AlignCenter indicates text should be centered.
	AlignCenter

	// AlignNone indicates text should be aligned by default manner.
	AlignNone
)

func (a Alignment) String() string {
	switch a {
	case AlignLeft:
		return "left"
	case AlignRight:
		return "right"
	case AlignCenter:
		return "center"
	case AlignNone:
		return "none"
	}
	return ""
}

// A Table struct represents a table of Markdown(GFM) text.
type Table struct {
	gast.BaseBlock

	// Alignments returns alignments of the columns.
	Alignments []Alignment
}

// Dump implements Node.Dump
func (n *Table) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, func(level int) {
		indent := strings.Repeat("    ", level)
		fmt.Printf("%sAlignments {\n", indent)
		for i, alignment := range n.Alignments {
			indent2 := strings.Repeat("    ", level+1)
			fmt.Printf("%s%s", indent2, alignment.String())
			if i != len(n.Alignments)-1 {
				fmt.Println("")
			}
		}
		fmt.Printf("\n%s}\n", indent)
	})
}

// KindTable is a NodeKind of the Table node.
var KindTable = gast.NewNodeKind("Table")

// Kind implements Node.Kind.
func (n *Table) Kind() gast.NodeKind {
	return KindTable
}

// NewTable returns a new Table node.
func NewTable() *Table {
	return &Table{
		Alignments: []Alignment{},
	}
}

// A TableRow struct represents a table row of Markdown(GFM) text.
type TableRow struct {
	gast.BaseBlock
	Alignments []Alignment
}

// Dump implements Node.Dump.
func (n *TableRow) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindTableRow is a NodeKind of the TableRow node.
var KindTableRow = gast.NewNodeKind("TableRow")

// Kind implements Node.Kind.
func (n *TableRow) Kind() gast.NodeKind {
	return KindTableRow
}

// NewTableRow returns a new TableRow node.
func NewTableRow(alignments []Alignment) *TableRow {
	return &TableRow{}
}

// A TableHeader struct represents a table header of Markdown(GFM) text.
type TableHeader struct {
	gast.BaseBlock
	Alignments []Alignment
}

// KindTableHeader is a NodeKind of the TableHeader node.
var KindTableHeader = gast.NewNodeKind("TableHeader")

// Kind implements Node.Kind.
func (n *TableHeader) Kind() gast.NodeKind {
	return KindTableHeader
}

// Dump implements Node.Dump.
func (n *TableHeader) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// NewTableHeader returns a new TableHeader node.
func NewTableHeader(row *TableRow) *TableHeader {
	n := &TableHeader{}
	for c := row.FirstChild(); c != nil; {
		next := c.NextSibling()
		n.AppendChild(n, c)
		c = next
	}
	return n
}

// A TableCell struct represents a table cell of a Markdown(GFM) text.
type TableCell struct {
	gast.BaseBlock
	Alignment Alignment
}

// Dump implements Node.Dump.
func (n *TableCell) Dump(source []byte, level int) {
	gast.DumpHelper(n, source, level, nil, nil)
}

// KindTableCell is a NodeKind of the TableCell node.
var KindTableCell = gast.NewNodeKind("TableCell")

// Kind implements Node.Kind.
func (n *TableCell) Kind() gast.NodeKind {
	return KindTableCell
}

// NewTableCell returns a new TableCell node.
func NewTableCell() *TableCell {
	return &TableCell{
		Alignment: AlignNone,
	}
}

A vendor/github.com/yuin/goldmark/extension/ast/tasklist.go => vendor/github.com/yuin/goldmark/extension/ast/tasklist.go +35 -0
@@ 0,0 1,35 @@
package ast

import (
	"fmt"
	gast "github.com/yuin/goldmark/ast"
)

// A TaskCheckBox struct represents a checkbox of a task list.
type TaskCheckBox struct {
	gast.BaseInline
	IsChecked bool
}

// Dump impelemtns Node.Dump.
func (n *TaskCheckBox) Dump(source []byte, level int) {
	m := map[string]string{
		"Checked": fmt.Sprintf("%v", n.IsChecked),
	}
	gast.DumpHelper(n, source, level, m, nil)
}

// KindTaskCheckBox is a NodeKind of the TaskCheckBox node.
var KindTaskCheckBox = gast.NewNodeKind("TaskCheckBox")

// Kind implements Node.Kind.
func (n *TaskCheckBox) Kind() gast.NodeKind {
	return KindTaskCheckBox
}

// NewTaskCheckBox returns a new TaskCheckBox node.
func NewTaskCheckBox(checked bool) *TaskCheckBox {
	return &TaskCheckBox{
		IsChecked: checked,
	}
}

A vendor/github.com/yuin/goldmark/extension/definition_list.go => vendor/github.com/yuin/goldmark/extension/definition_list.go +270 -0
@@ 0,0 1,270 @@
package extension

import (
	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

type definitionListParser struct {
}

var defaultDefinitionListParser = &definitionListParser{}

// NewDefinitionListParser return a new parser.BlockParser that
// can parse PHP Markdown Extra Definition lists.
func NewDefinitionListParser() parser.BlockParser {
	return defaultDefinitionListParser
}

func (b *definitionListParser) Trigger() []byte {
	return []byte{':'}
}

func (b *definitionListParser) Open(parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
	if _, ok := parent.(*ast.DefinitionList); ok {
		return nil, parser.NoChildren
	}
	line, _ := reader.PeekLine()
	pos := pc.BlockOffset()
	indent := pc.BlockIndent()
	if pos < 0 || line[pos] != ':' || indent != 0 {
		return nil, parser.NoChildren
	}

	last := parent.LastChild()
	// need 1 or more spaces after ':'
	w, _ := util.IndentWidth(line[pos+1:], pos+1)
	if w < 1 {
		return nil, parser.NoChildren
	}
	if w >= 8 { // starts with indented code
		w = 5
	}
	w += pos + 1 /* 1 = ':' */

	para, lastIsParagraph := last.(*gast.Paragraph)
	var list *ast.DefinitionList
	status := parser.HasChildren
	var ok bool
	if lastIsParagraph {
		list, ok = last.PreviousSibling().(*ast.DefinitionList)
		if ok { // is not first item
			list.Offset = w
			list.TemporaryParagraph = para
		} else { // is first item
			list = ast.NewDefinitionList(w, para)
			status |= parser.RequireParagraph
		}
	} else if list, ok = last.(*ast.DefinitionList); ok { // multiple description
		list.Offset = w
		list.TemporaryParagraph = nil
	} else {
		return nil, parser.NoChildren
	}

	return list, status
}

func (b *definitionListParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
	line, _ := reader.PeekLine()
	if util.IsBlank(line) {
		return parser.Continue | parser.HasChildren
	}
	list, _ := node.(*ast.DefinitionList)
	w, _ := util.IndentWidth(line, reader.LineOffset())
	if w < list.Offset {
		return parser.Close
	}
	pos, padding := util.IndentPosition(line, reader.LineOffset(), list.Offset)
	reader.AdvanceAndSetPadding(pos, padding)
	return parser.Continue | parser.HasChildren
}

func (b *definitionListParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
	// nothing to do
}

func (b *definitionListParser) CanInterruptParagraph() bool {
	return true
}

func (b *definitionListParser) CanAcceptIndentedLine() bool {
	return false
}

type definitionDescriptionParser struct {
}

var defaultDefinitionDescriptionParser = &definitionDescriptionParser{}

// NewDefinitionDescriptionParser return a new parser.BlockParser that
// can parse definition description starts with ':'.
func NewDefinitionDescriptionParser() parser.BlockParser {
	return defaultDefinitionDescriptionParser
}

func (b *definitionDescriptionParser) Trigger() []byte {
	return []byte{':'}
}

func (b *definitionDescriptionParser) Open(parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
	line, _ := reader.PeekLine()
	pos := pc.BlockOffset()
	indent := pc.BlockIndent()
	if pos < 0 || line[pos] != ':' || indent != 0 {
		return nil, parser.NoChildren
	}
	list, _ := parent.(*ast.DefinitionList)
	if list == nil {
		return nil, parser.NoChildren
	}
	para := list.TemporaryParagraph
	list.TemporaryParagraph = nil
	if para != nil {
		lines := para.Lines()
		l := lines.Len()
		for i := 0; i < l; i++ {
			term := ast.NewDefinitionTerm()
			segment := lines.At(i)
			term.Lines().Append(segment.TrimRightSpace(reader.Source()))
			list.AppendChild(list, term)
		}
		para.Parent().RemoveChild(para.Parent(), para)
	}
	cpos, padding := util.IndentPosition(line[pos+1:], pos+1, list.Offset-pos-1)
	reader.AdvanceAndSetPadding(cpos, padding)

	return ast.NewDefinitionDescription(), parser.HasChildren
}

func (b *definitionDescriptionParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
	// definitionListParser detects end of the description.
	// so this method will never be called.
	return parser.Continue | parser.HasChildren
}

func (b *definitionDescriptionParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
	desc := node.(*ast.DefinitionDescription)
	desc.IsTight = !desc.HasBlankPreviousLines()
	if desc.IsTight {
		for gc := desc.FirstChild(); gc != nil; gc = gc.NextSibling() {
			paragraph, ok := gc.(*gast.Paragraph)
			if ok {
				textBlock := gast.NewTextBlock()
				textBlock.SetLines(paragraph.Lines())
				desc.ReplaceChild(desc, paragraph, textBlock)
			}
		}
	}
}

func (b *definitionDescriptionParser) CanInterruptParagraph() bool {
	return true
}

func (b *definitionDescriptionParser) CanAcceptIndentedLine() bool {
	return false
}

// DefinitionListHTMLRenderer is a renderer.NodeRenderer implementation that
// renders DefinitionList nodes.
type DefinitionListHTMLRenderer struct {
	html.Config
}

// NewDefinitionListHTMLRenderer returns a new DefinitionListHTMLRenderer.
func NewDefinitionListHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
	r := &DefinitionListHTMLRenderer{
		Config: html.NewConfig(),
	}
	for _, opt := range opts {
		opt.SetHTMLOption(&r.Config)
	}
	return r
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *DefinitionListHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindDefinitionList, r.renderDefinitionList)
	reg.Register(ast.KindDefinitionTerm, r.renderDefinitionTerm)
	reg.Register(ast.KindDefinitionDescription, r.renderDefinitionDescription)
}

// DefinitionListAttributeFilter defines attribute names which dl elements can have.
var DefinitionListAttributeFilter = html.GlobalAttributeFilter

func (r *DefinitionListHTMLRenderer) renderDefinitionList(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		if n.Attributes() != nil {
			_, _ = w.WriteString("<dl")
			html.RenderAttributes(w, n, DefinitionListAttributeFilter)
			_, _ = w.WriteString(">\n")
		} else {
			_, _ = w.WriteString("<dl>\n")
		}
	} else {
		_, _ = w.WriteString("</dl>\n")
	}
	return gast.WalkContinue, nil
}

// DefinitionTermAttributeFilter defines attribute names which dd elements can have.
var DefinitionTermAttributeFilter = html.GlobalAttributeFilter

func (r *DefinitionListHTMLRenderer) renderDefinitionTerm(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		if n.Attributes() != nil {
			_, _ = w.WriteString("<dt")
			html.RenderAttributes(w, n, DefinitionTermAttributeFilter)
			_ = w.WriteByte('>')
		} else {
			_, _ = w.WriteString("<dt>")
		}
	} else {
		_, _ = w.WriteString("</dt>\n")
	}
	return gast.WalkContinue, nil
}

// DefinitionDescriptionAttributeFilter defines attribute names which dd elements can have.
var DefinitionDescriptionAttributeFilter = html.GlobalAttributeFilter

func (r *DefinitionListHTMLRenderer) renderDefinitionDescription(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		n := node.(*ast.DefinitionDescription)
		_, _ = w.WriteString("<dd")
		if n.Attributes() != nil {
			html.RenderAttributes(w, n, DefinitionDescriptionAttributeFilter)
		}
		if n.IsTight {
			_, _ = w.WriteString(">")
		} else {
			_, _ = w.WriteString(">\n")
		}
	} else {
		_, _ = w.WriteString("</dd>\n")
	}
	return gast.WalkContinue, nil
}

type definitionList struct {
}

// DefinitionList is an extension that allow you to use PHP Markdown Extra Definition lists.
var DefinitionList = &definitionList{}

func (e *definitionList) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(parser.WithBlockParsers(
		util.Prioritized(NewDefinitionListParser(), 101),
		util.Prioritized(NewDefinitionDescriptionParser(), 102),
	))
	m.Renderer().AddOptions(renderer.WithNodeRenderers(
		util.Prioritized(NewDefinitionListHTMLRenderer(), 500),
	))
}

A vendor/github.com/yuin/goldmark/extension/footnote.go => vendor/github.com/yuin/goldmark/extension/footnote.go +336 -0
@@ 0,0 1,336 @@
package extension

import (
	"bytes"
	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
	"strconv"
)

var footnoteListKey = parser.NewContextKey()

type footnoteBlockParser struct {
}

var defaultFootnoteBlockParser = &footnoteBlockParser{}

// NewFootnoteBlockParser returns a new parser.BlockParser that can parse
// footnotes of the Markdown(PHP Markdown Extra) text.
func NewFootnoteBlockParser() parser.BlockParser {
	return defaultFootnoteBlockParser
}

func (b *footnoteBlockParser) Trigger() []byte {
	return []byte{'['}
}

func (b *footnoteBlockParser) Open(parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
	line, segment := reader.PeekLine()
	pos := pc.BlockOffset()
	if pos < 0 || line[pos] != '[' {
		return nil, parser.NoChildren
	}
	pos++
	if pos > len(line)-1 || line[pos] != '^' {
		return nil, parser.NoChildren
	}
	open := pos + 1
	closes := 0
	closure := util.FindClosure(line[pos+1:], '[', ']', false, false)
	closes = pos + 1 + closure
	next := closes + 1
	if closure > -1 {
		if next >= len(line) || line[next] != ':' {
			return nil, parser.NoChildren
		}
	} else {
		return nil, parser.NoChildren
	}
	padding := segment.Padding
	label := reader.Value(text.NewSegment(segment.Start+open-padding, segment.Start+closes-padding))
	if util.IsBlank(label) {
		return nil, parser.NoChildren
	}
	item := ast.NewFootnote(label)

	pos = next + 1 - padding
	if pos >= len(line) {
		reader.Advance(pos)
		return item, parser.NoChildren
	}
	reader.AdvanceAndSetPadding(pos, padding)
	return item, parser.HasChildren
}

func (b *footnoteBlockParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
	line, _ := reader.PeekLine()
	if util.IsBlank(line) {
		return parser.Continue | parser.HasChildren
	}
	childpos, padding := util.IndentPosition(line, reader.LineOffset(), 4)
	if childpos < 0 {
		return parser.Close
	}
	reader.AdvanceAndSetPadding(childpos, padding)
	return parser.Continue | parser.HasChildren
}

func (b *footnoteBlockParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
	var list *ast.FootnoteList
	if tlist := pc.Get(footnoteListKey); tlist != nil {
		list = tlist.(*ast.FootnoteList)
	} else {
		list = ast.NewFootnoteList()
		pc.Set(footnoteListKey, list)
		node.Parent().InsertBefore(node.Parent(), node, list)
	}
	node.Parent().RemoveChild(node.Parent(), node)
	list.AppendChild(list, node)
}

func (b *footnoteBlockParser) CanInterruptParagraph() bool {
	return true
}

func (b *footnoteBlockParser) CanAcceptIndentedLine() bool {
	return false
}

type footnoteParser struct {
}

var defaultFootnoteParser = &footnoteParser{}

// NewFootnoteParser returns a new parser.InlineParser that can parse
// footnote links of the Markdown(PHP Markdown Extra) text.
func NewFootnoteParser() parser.InlineParser {
	return defaultFootnoteParser
}

func (s *footnoteParser) Trigger() []byte {
	// footnote syntax probably conflict with the image syntax.
	// So we need trigger this parser with '!'.
	return []byte{'!', '['}
}

func (s *footnoteParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
	line, segment := block.PeekLine()
	pos := 1
	if len(line) > 0 && line[0] == '!' {
		pos++
	}
	if pos >= len(line) || line[pos] != '^' {
		return nil
	}
	pos++
	if pos >= len(line) {
		return nil
	}
	open := pos
	closure := util.FindClosure(line[pos:], '[', ']', false, false)
	if closure < 0 {
		return nil
	}
	closes := pos + closure
	value := block.Value(text.NewSegment(segment.Start+open, segment.Start+closes))
	block.Advance(closes + 1)

	var list *ast.FootnoteList
	if tlist := pc.Get(footnoteListKey); tlist != nil {
		list = tlist.(*ast.FootnoteList)
	}
	if list == nil {
		return nil
	}
	index := 0
	for def := list.FirstChild(); def != nil; def = def.NextSibling() {
		d := def.(*ast.Footnote)
		if bytes.Equal(d.Ref, value) {
			if d.Index < 0 {
				list.Count += 1
				d.Index = list.Count
			}
			index = d.Index
			break
		}
	}
	if index == 0 {
		return nil
	}

	return ast.NewFootnoteLink(index)
}

type footnoteASTTransformer struct {
}

var defaultFootnoteASTTransformer = &footnoteASTTransformer{}

// NewFootnoteASTTransformer returns a new parser.ASTTransformer that
// insert a footnote list to the last of the document.
func NewFootnoteASTTransformer() parser.ASTTransformer {
	return defaultFootnoteASTTransformer
}

func (a *footnoteASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
	var list *ast.FootnoteList
	if tlist := pc.Get(footnoteListKey); tlist != nil {
		list = tlist.(*ast.FootnoteList)
	} else {
		return
	}
	pc.Set(footnoteListKey, nil)
	for footnote := list.FirstChild(); footnote != nil; {
		var container gast.Node = footnote
		next := footnote.NextSibling()
		if fc := container.LastChild(); fc != nil && gast.IsParagraph(fc) {
			container = fc
		}
		index := footnote.(*ast.Footnote).Index
		if index < 0 {
			list.RemoveChild(list, footnote)
		} else {
			container.AppendChild(container, ast.NewFootnoteBackLink(index))
		}
		footnote = next
	}
	list.SortChildren(func(n1, n2 gast.Node) int {
		if n1.(*ast.Footnote).Index < n2.(*ast.Footnote).Index {
			return -1
		}
		return 1
	})
	if list.Count <= 0 {
		list.Parent().RemoveChild(list.Parent(), list)
		return
	}

	node.AppendChild(node, list)
}

// FootnoteHTMLRenderer is a renderer.NodeRenderer implementation that
// renders FootnoteLink nodes.
type FootnoteHTMLRenderer struct {
	html.Config
}

// NewFootnoteHTMLRenderer returns a new FootnoteHTMLRenderer.
func NewFootnoteHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
	r := &FootnoteHTMLRenderer{
		Config: html.NewConfig(),
	}
	for _, opt := range opts {
		opt.SetHTMLOption(&r.Config)
	}
	return r
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *FootnoteHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindFootnoteLink, r.renderFootnoteLink)
	reg.Register(ast.KindFootnoteBackLink, r.renderFootnoteBackLink)
	reg.Register(ast.KindFootnote, r.renderFootnote)
	reg.Register(ast.KindFootnoteList, r.renderFootnoteList)
}

func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		n := node.(*ast.FootnoteLink)
		is := strconv.Itoa(n.Index)
		_, _ = w.WriteString(`<sup id="fnref:`)
		_, _ = w.WriteString(is)
		_, _ = w.WriteString(`"><a href="#fn:`)
		_, _ = w.WriteString(is)
		_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`)
		_, _ = w.WriteString(is)
		_, _ = w.WriteString(`</a></sup>`)
	}
	return gast.WalkContinue, nil
}

func (r *FootnoteHTMLRenderer) renderFootnoteBackLink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		n := node.(*ast.FootnoteBackLink)
		is := strconv.Itoa(n.Index)
		_, _ = w.WriteString(` <a href="#fnref:`)
		_, _ = w.WriteString(is)
		_, _ = w.WriteString(`" class="footnote-backref" role="doc-backlink">`)
		_, _ = w.WriteString("&#x21a9;&#xfe0e;")
		_, _ = w.WriteString(`</a>`)
	}
	return gast.WalkContinue, nil
}

func (r *FootnoteHTMLRenderer) renderFootnote(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	n := node.(*ast.Footnote)
	is := strconv.Itoa(n.Index)
	if entering {
		_, _ = w.WriteString(`<li id="fn:`)
		_, _ = w.WriteString(is)
		_, _ = w.WriteString(`" role="doc-endnote"`)
		if node.Attributes() != nil {
			html.RenderAttributes(w, node, html.ListItemAttributeFilter)
		}
		_, _ = w.WriteString(">\n")
	} else {
		_, _ = w.WriteString("</li>\n")
	}
	return gast.WalkContinue, nil
}

func (r *FootnoteHTMLRenderer) renderFootnoteList(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	tag := "section"
	if r.Config.XHTML {
		tag = "div"
	}
	if entering {
		_, _ = w.WriteString("<")
		_, _ = w.WriteString(tag)
		_, _ = w.WriteString(` class="footnotes" role="doc-endnotes"`)
		if node.Attributes() != nil {
			html.RenderAttributes(w, node, html.GlobalAttributeFilter)
		}
		_ = w.WriteByte('>')
		if r.Config.XHTML {
			_, _ = w.WriteString("\n<hr />\n")
		} else {
			_, _ = w.WriteString("\n<hr>\n")
		}
		_, _ = w.WriteString("<ol>\n")
	} else {
		_, _ = w.WriteString("</ol>\n")
		_, _ = w.WriteString("</")
		_, _ = w.WriteString(tag)
		_, _ = w.WriteString(">\n")
	}
	return gast.WalkContinue, nil
}

type footnote struct {
}

// Footnote is an extension that allow you to use PHP Markdown Extra Footnotes.
var Footnote = &footnote{}

func (e *footnote) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(
		parser.WithBlockParsers(
			util.Prioritized(NewFootnoteBlockParser(), 999),
		),
		parser.WithInlineParsers(
			util.Prioritized(NewFootnoteParser(), 101),
		),
		parser.WithASTTransformers(
			util.Prioritized(NewFootnoteASTTransformer(), 999),
		),
	)
	m.Renderer().AddOptions(renderer.WithNodeRenderers(
		util.Prioritized(NewFootnoteHTMLRenderer(), 500),
	))
}

A vendor/github.com/yuin/goldmark/extension/gfm.go => vendor/github.com/yuin/goldmark/extension/gfm.go +18 -0
@@ 0,0 1,18 @@
package extension

import (
	"github.com/yuin/goldmark"
)

type gfm struct {
}

// GFM is an extension that provides Github Flavored markdown functionalities.
var GFM = &gfm{}

func (e *gfm) Extend(m goldmark.Markdown) {
	Linkify.Extend(m)
	Table.Extend(m)
	Strikethrough.Extend(m)
	TaskList.Extend(m)
}

A vendor/github.com/yuin/goldmark/extension/linkify.go => vendor/github.com/yuin/goldmark/extension/linkify.go +303 -0
@@ 0,0 1,303 @@
package extension

import (
	"bytes"
	"regexp"

	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?:(?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)

var urlRegexp = regexp.MustCompile(`^(?:http|https|ftp):\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?:(?:/|[#?])[-a-zA-Z0-9@:%_+.~#$!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)

// An LinkifyConfig struct is a data structure that holds configuration of the
// Linkify extension.
type LinkifyConfig struct {
	AllowedProtocols [][]byte
	URLRegexp        *regexp.Regexp
	WWWRegexp        *regexp.Regexp
	EmailRegexp      *regexp.Regexp
}

const optLinkifyAllowedProtocols parser.OptionName = "LinkifyAllowedProtocols"
const optLinkifyURLRegexp parser.OptionName = "LinkifyURLRegexp"
const optLinkifyWWWRegexp parser.OptionName = "LinkifyWWWRegexp"
const optLinkifyEmailRegexp parser.OptionName = "LinkifyEmailRegexp"

// SetOption implements SetOptioner.
func (c *LinkifyConfig) SetOption(name parser.OptionName, value interface{}) {
	switch name {
	case optLinkifyAllowedProtocols:
		c.AllowedProtocols = value.([][]byte)
	case optLinkifyURLRegexp:
		c.URLRegexp = value.(*regexp.Regexp)
	case optLinkifyWWWRegexp:
		c.WWWRegexp = value.(*regexp.Regexp)
	case optLinkifyEmailRegexp:
		c.EmailRegexp = value.(*regexp.Regexp)
	}
}

// A LinkifyOption interface sets options for the LinkifyOption.
type LinkifyOption interface {
	parser.Option
	SetLinkifyOption(*LinkifyConfig)
}

type withLinkifyAllowedProtocols struct {
	value [][]byte
}

func (o *withLinkifyAllowedProtocols) SetParserOption(c *parser.Config) {
	c.Options[optLinkifyAllowedProtocols] = o.value
}

func (o *withLinkifyAllowedProtocols) SetLinkifyOption(p *LinkifyConfig) {
	p.AllowedProtocols = o.value
}

// WithLinkifyAllowedProtocols is a functional otpion that specify allowed
// protocols in autolinks. Each protocol must end with ':' like
// 'http:' .
func WithLinkifyAllowedProtocols(value [][]byte) LinkifyOption {
	return &withLinkifyAllowedProtocols{
		value: value,
	}
}

type withLinkifyURLRegexp struct {
	value *regexp.Regexp
}

func (o *withLinkifyURLRegexp) SetParserOption(c *parser.Config) {
	c.Options[optLinkifyURLRegexp] = o.value
}

func (o *withLinkifyURLRegexp) SetLinkifyOption(p *LinkifyConfig) {
	p.URLRegexp = o.value
}

// WithLinkifyURLRegexp is a functional otpion that specify
// a pattern of the URL including a protocol.
func WithLinkifyURLRegexp(value *regexp.Regexp) LinkifyOption {
	return &withLinkifyURLRegexp{
		value: value,
	}
}

// WithLinkifyWWWRegexp is a functional otpion that specify
// a pattern of the URL without a protocol.
// This pattern must start with 'www.' .
type withLinkifyWWWRegexp struct {
	value *regexp.Regexp
}

func (o *withLinkifyWWWRegexp) SetParserOption(c *parser.Config) {
	c.Options[optLinkifyWWWRegexp] = o.value
}

func (o *withLinkifyWWWRegexp) SetLinkifyOption(p *LinkifyConfig) {
	p.WWWRegexp = o.value
}

func WithLinkifyWWWRegexp(value *regexp.Regexp) LinkifyOption {
	return &withLinkifyWWWRegexp{
		value: value,
	}
}

// WithLinkifyWWWRegexp is a functional otpion that specify
// a pattern of the email address.
type withLinkifyEmailRegexp struct {
	value *regexp.Regexp
}

func (o *withLinkifyEmailRegexp) SetParserOption(c *parser.Config) {
	c.Options[optLinkifyEmailRegexp] = o.value
}

func (o *withLinkifyEmailRegexp) SetLinkifyOption(p *LinkifyConfig) {
	p.EmailRegexp = o.value
}

func WithLinkifyEmailRegexp(value *regexp.Regexp) LinkifyOption {
	return &withLinkifyEmailRegexp{
		value: value,
	}
}

type linkifyParser struct {
	LinkifyConfig
}

// NewLinkifyParser return a new InlineParser can parse
// text that seems like a URL.
func NewLinkifyParser(opts ...LinkifyOption) parser.InlineParser {
	p := &linkifyParser{
		LinkifyConfig: LinkifyConfig{
			AllowedProtocols: nil,
			URLRegexp:        urlRegexp,
			WWWRegexp:        wwwURLRegxp,
		},
	}
	for _, o := range opts {
		o.SetLinkifyOption(&p.LinkifyConfig)
	}
	return p
}

func (s *linkifyParser) Trigger() []byte {
	// ' ' indicates any white spaces and a line head
	return []byte{' ', '*', '_', '~', '('}
}

var protoHTTP = []byte("http:")
var protoHTTPS = []byte("https:")
var protoFTP = []byte("ftp:")
var domainWWW = []byte("www.")

func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
	if pc.IsInLinkLabel() {
		return nil
	}
	line, segment := block.PeekLine()
	consumes := 0
	start := segment.Start
	c := line[0]
	// advance if current position is not a line head.
	if c == ' ' || c == '*' || c == '_' || c == '~' || c == '(' {
		consumes++
		start++
		line = line[1:]
	}

	var m []int
	var protocol []byte
	var typ ast.AutoLinkType = ast.AutoLinkURL
	if s.LinkifyConfig.AllowedProtocols == nil {
		if bytes.HasPrefix(line, protoHTTP) || bytes.HasPrefix(line, protoHTTPS) || bytes.HasPrefix(line, protoFTP) {
			m = s.LinkifyConfig.URLRegexp.FindSubmatchIndex(line)
		}
	} else {
		for _, prefix := range s.LinkifyConfig.AllowedProtocols {
			if bytes.HasPrefix(line, prefix) {
				m = s.LinkifyConfig.URLRegexp.FindSubmatchIndex(line)
				break
			}
		}
	}
	if m == nil && bytes.HasPrefix(line, domainWWW) {
		m = s.LinkifyConfig.WWWRegexp.FindSubmatchIndex(line)
		protocol = []byte("http")
	}
	if m != nil && m[0] != 0 {
		m = nil
	}
	if m != nil && m[0] == 0 {
		lastChar := line[m[1]-1]
		if lastChar == '.' {
			m[1]--
		} else if lastChar == ')' {
			closing := 0
			for i := m[1] - 1; i >= m[0]; i-- {
				if line[i] == ')' {
					closing++
				} else if line[i] == '(' {
					closing--
				}
			}
			if closing > 0 {
				m[1] -= closing
			}
		} else if lastChar == ';' {
			i := m[1] - 2
			for ; i >= m[0]; i-- {
				if util.IsAlphaNumeric(line[i]) {
					continue
				}
				break
			}
			if i != m[1]-2 {
				if line[i] == '&' {
					m[1] -= m[1] - i
				}
			}
		}
	}
	if m == nil {
		if len(line) > 0 && util.IsPunct(line[0]) {
			return nil
		}
		typ = ast.AutoLinkEmail
		stop := -1
		if s.LinkifyConfig.EmailRegexp == nil {
			stop = util.FindEmailIndex(line)
		} else {
			m := s.LinkifyConfig.EmailRegexp.FindSubmatchIndex(line)
			if m != nil && m[0] == 0 {
				stop = m[1]
			}
		}
		if stop < 0 {
			return nil
		}
		at := bytes.IndexByte(line, '@')
		m = []int{0, stop, at, stop - 1}
		if m == nil || bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
			return nil
		}
		lastChar := line[m[1]-1]
		if lastChar == '.' {
			m[1]--
		}
		if m[1] < len(line) {
			nextChar := line[m[1]]
			if nextChar == '-' || nextChar == '_' {
				return nil
			}
		}
	}
	if m == nil {
		return nil
	}
	if consumes != 0 {
		s := segment.WithStop(segment.Start + 1)
		ast.MergeOrAppendTextSegment(parent, s)
	}
	consumes += m[1]
	block.Advance(consumes)
	n := ast.NewTextSegment(text.NewSegment(start, start+m[1]))
	link := ast.NewAutoLink(typ, n)
	link.Protocol = protocol
	return link
}

func (s *linkifyParser) CloseBlock(parent ast.Node, pc parser.Context) {
	// nothing to do
}

type linkify struct {
	options []LinkifyOption
}

// Linkify is an extension that allow you to parse text that seems like a URL.
var Linkify = &linkify{}

func NewLinkify(opts ...LinkifyOption) goldmark.Extender {
	return &linkify{
		options: opts,
	}
}

func (e *linkify) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(
		parser.WithInlineParsers(
			util.Prioritized(NewLinkifyParser(e.options...), 999),
		),
	)
}

A vendor/github.com/yuin/goldmark/extension/strikethrough.go => vendor/github.com/yuin/goldmark/extension/strikethrough.go +116 -0
@@ 0,0 1,116 @@
package extension

import (
	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

type strikethroughDelimiterProcessor struct {
}

func (p *strikethroughDelimiterProcessor) IsDelimiter(b byte) bool {
	return b == '~'
}

func (p *strikethroughDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
	return opener.Char == closer.Char
}

func (p *strikethroughDelimiterProcessor) OnMatch(consumes int) gast.Node {
	return ast.NewStrikethrough()
}

var defaultStrikethroughDelimiterProcessor = &strikethroughDelimiterProcessor{}

type strikethroughParser struct {
}

var defaultStrikethroughParser = &strikethroughParser{}

// NewStrikethroughParser return a new InlineParser that parses
// strikethrough expressions.
func NewStrikethroughParser() parser.InlineParser {
	return defaultStrikethroughParser
}

func (s *strikethroughParser) Trigger() []byte {
	return []byte{'~'}
}

func (s *strikethroughParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
	before := block.PrecendingCharacter()
	line, segment := block.PeekLine()
	node := parser.ScanDelimiter(line, before, 2, defaultStrikethroughDelimiterProcessor)
	if node == nil {
		return nil
	}
	node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
	block.Advance(node.OriginalLength)
	pc.PushDelimiter(node)
	return node
}

func (s *strikethroughParser) CloseBlock(parent gast.Node, pc parser.Context) {
	// nothing to do
}

// StrikethroughHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Strikethrough nodes.
type StrikethroughHTMLRenderer struct {
	html.Config
}

// NewStrikethroughHTMLRenderer returns a new StrikethroughHTMLRenderer.
func NewStrikethroughHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
	r := &StrikethroughHTMLRenderer{
		Config: html.NewConfig(),
	}
	for _, opt := range opts {
		opt.SetHTMLOption(&r.Config)
	}
	return r
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *StrikethroughHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindStrikethrough, r.renderStrikethrough)
}

// StrikethroughAttributeFilter defines attribute names which dd elements can have.
var StrikethroughAttributeFilter = html.GlobalAttributeFilter

func (r *StrikethroughHTMLRenderer) renderStrikethrough(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		if n.Attributes() != nil {
			_, _ = w.WriteString("<del")
			html.RenderAttributes(w, n, StrikethroughAttributeFilter)
			_ = w.WriteByte('>')
		} else {
			_, _ = w.WriteString("<del>")
		}
	} else {
		_, _ = w.WriteString("</del>")
	}
	return gast.WalkContinue, nil
}

type strikethrough struct {
}

// Strikethrough is an extension that allow you to use strikethrough expression like '~~text~~' .
var Strikethrough = &strikethrough{}

func (e *strikethrough) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(parser.WithInlineParsers(
		util.Prioritized(NewStrikethroughParser(), 500),
	))
	m.Renderer().AddOptions(renderer.WithNodeRenderers(
		util.Prioritized(NewStrikethroughHTMLRenderer(), 500),
	))
}

A vendor/github.com/yuin/goldmark/extension/table.go => vendor/github.com/yuin/goldmark/extension/table.go +319 -0
@@ 0,0 1,319 @@
package extension

import (
	"bytes"
	"fmt"
	"regexp"

	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

var tableDelimRegexp = regexp.MustCompile(`^[\s\-\|\:]+$`)
var tableDelimLeft = regexp.MustCompile(`^\s*\:\-+\s*$`)
var tableDelimRight = regexp.MustCompile(`^\s*\-+\:\s*$`)
var tableDelimCenter = regexp.MustCompile(`^\s*\:\-+\:\s*$`)
var tableDelimNone = regexp.MustCompile(`^\s*\-+\s*$`)

type tableParagraphTransformer struct {
}

var defaultTableParagraphTransformer = &tableParagraphTransformer{}

// NewTableParagraphTransformer returns  a new ParagraphTransformer
// that can transform pargraphs into tables.
func NewTableParagraphTransformer() parser.ParagraphTransformer {
	return defaultTableParagraphTransformer
}

func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.Reader, pc parser.Context) {
	lines := node.Lines()
	if lines.Len() < 2 {
		return
	}
	alignments := b.parseDelimiter(lines.At(1), reader)
	if alignments == nil {
		return
	}
	header := b.parseRow(lines.At(0), alignments, true, reader)
	if header == nil || len(alignments) != header.ChildCount() {
		return
	}
	table := ast.NewTable()
	table.Alignments = alignments
	table.AppendChild(table, ast.NewTableHeader(header))
	for i := 2; i < lines.Len(); i++ {
		table.AppendChild(table, b.parseRow(lines.At(i), alignments, false, reader))
	}
	node.Parent().InsertBefore(node.Parent(), node, table)
	node.Parent().RemoveChild(node.Parent(), node)
}

func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader) *ast.TableRow {
	source := reader.Source()
	line := segment.Value(source)
	pos := 0
	pos += util.TrimLeftSpaceLength(line)
	limit := len(line)
	limit -= util.TrimRightSpaceLength(line)
	row := ast.NewTableRow(alignments)
	if len(line) > 0 && line[pos] == '|' {
		pos++
	}
	if len(line) > 0 && line[limit-1] == '|' {
		limit--
	}
	i := 0
	for ; pos < limit; i++ {
		alignment := ast.AlignNone
		if i >= len(alignments) {
			if !isHeader {
				return row
			}
		} else {
			alignment = alignments[i]
		}
		closure := util.FindClosure(line[pos:], byte(0), '|', true, false)
		if closure < 0 {
			closure = len(line[pos:])
		}
		node := ast.NewTableCell()
		seg := text.NewSegment(segment.Start+pos, segment.Start+pos+closure)
		seg = seg.TrimLeftSpace(source)
		seg = seg.TrimRightSpace(source)
		node.Lines().Append(seg)
		node.Alignment = alignment
		row.AppendChild(row, node)
		pos += closure + 1
	}
	for ; i < len(alignments); i++ {
		row.AppendChild(row, ast.NewTableCell())
	}
	return row
}

func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader text.Reader) []ast.Alignment {
	line := segment.Value(reader.Source())
	if !tableDelimRegexp.Match(line) {
		return nil
	}
	cols := bytes.Split(line, []byte{'|'})
	if util.IsBlank(cols[0]) {
		cols = cols[1:]
	}
	if len(cols) > 0 && util.IsBlank(cols[len(cols)-1]) {
		cols = cols[:len(cols)-1]
	}

	var alignments []ast.Alignment
	for _, col := range cols {
		if tableDelimLeft.Match(col) {
			alignments = append(alignments, ast.AlignLeft)
		} else if tableDelimRight.Match(col) {
			alignments = append(alignments, ast.AlignRight)
		} else if tableDelimCenter.Match(col) {
			alignments = append(alignments, ast.AlignCenter)
		} else if tableDelimNone.Match(col) {
			alignments = append(alignments, ast.AlignNone)
		} else {
			return nil
		}
	}
	return alignments
}

// TableHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Table nodes.
type TableHTMLRenderer struct {
	html.Config
}

// NewTableHTMLRenderer returns a new TableHTMLRenderer.
func NewTableHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
	r := &TableHTMLRenderer{
		Config: html.NewConfig(),
	}
	for _, opt := range opts {
		opt.SetHTMLOption(&r.Config)
	}
	return r
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *TableHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindTable, r.renderTable)
	reg.Register(ast.KindTableHeader, r.renderTableHeader)
	reg.Register(ast.KindTableRow, r.renderTableRow)
	reg.Register(ast.KindTableCell, r.renderTableCell)
}

// TableAttributeFilter defines attribute names which table elements can have.
var TableAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),       // [Deprecated]
	[]byte("bgcolor"),     // [Deprecated]
	[]byte("border"),      // [Deprecated]
	[]byte("cellpadding"), // [Deprecated]
	[]byte("cellspacing"), // [Deprecated]
	[]byte("frame"),       // [Deprecated]
	[]byte("rules"),       // [Deprecated]
	[]byte("summary"),     // [Deprecated]
	[]byte("width"),       // [Deprecated]
)

func (r *TableHTMLRenderer) renderTable(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		_, _ = w.WriteString("<table")
		if n.Attributes() != nil {
			html.RenderAttributes(w, n, TableAttributeFilter)
		}
		_, _ = w.WriteString(">\n")
	} else {
		_, _ = w.WriteString("</table>\n")
	}
	return gast.WalkContinue, nil
}

// TableHeaderAttributeFilter defines attribute names which <thead> elements can have.
var TableHeaderAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),   // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("charoff"), // [Deprecated since HTML4] [Obsolete since HTML5]
	[]byte("valign"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)

func (r *TableHTMLRenderer) renderTableHeader(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		_, _ = w.WriteString("<thead")
		if n.Attributes() != nil {
			html.RenderAttributes(w, n, TableHeaderAttributeFilter)
		}
		_, _ = w.WriteString(">\n")
		_, _ = w.WriteString("<tr>\n") // Header <tr> has no separate handle
	} else {
		_, _ = w.WriteString("</tr>\n")
		_, _ = w.WriteString("</thead>\n")
		if n.NextSibling() != nil {
			_, _ = w.WriteString("<tbody>\n")
		}
	}
	return gast.WalkContinue, nil
}

// TableRowAttributeFilter defines attribute names which <tr> elements can have.
var TableRowAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Obsolete since HTML5]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]
	[]byte("valign"),  // [Obsolete since HTML5]
)

func (r *TableHTMLRenderer) renderTableRow(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
	if entering {
		_, _ = w.WriteString("<tr")
		if n.Attributes() != nil {
			html.RenderAttributes(w, n, TableRowAttributeFilter)
		}
		_, _ = w.WriteString(">\n")
	} else {
		_, _ = w.WriteString("</tr>\n")
		if n.Parent().LastChild() == n {
			_, _ = w.WriteString("</tbody>\n")
		}
	}
	return gast.WalkContinue, nil
}

// TableThCellAttributeFilter defines attribute names which table <th> cells can have.
var TableThCellAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("abbr"), // [OK] Contains a short abbreviated description of the cell's content [NOT OK in <td>]

	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("axis"),    // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]

	[]byte("colspan"), // [OK] Number of columns that the cell is to span
	[]byte("headers"), // [OK] This attribute contains a list of space-separated strings, each corresponding to the id attribute of the <th> elements that apply to this element

	[]byte("height"), // [Deprecated since HTML4] [Obsolete since HTML5]

	[]byte("rowspan"), // [OK] Number of rows that the cell is to span
	[]byte("scope"),   // [OK] This enumerated attribute defines the cells that the header (defined in the <th>) element relates to [NOT OK in <td>]

	[]byte("valign"), // [Obsolete since HTML5]
	[]byte("width"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)

// TableTdCellAttributeFilter defines attribute names which table <td> cells can have.
var TableTdCellAttributeFilter = html.GlobalAttributeFilter.Extend(
	[]byte("abbr"),    // [Obsolete since HTML5] [OK in <th>]
	[]byte("align"),   // [Obsolete since HTML5]
	[]byte("axis"),    // [Obsolete since HTML5]
	[]byte("bgcolor"), // [Not Standardized]
	[]byte("char"),    // [Obsolete since HTML5]
	[]byte("charoff"), // [Obsolete since HTML5]

	[]byte("colspan"), // [OK] Number of columns that the cell is to span
	[]byte("headers"), // [OK] This attribute contains a list of space-separated strings, each corresponding to the id attribute of the <th> elements that apply to this element

	[]byte("height"), // [Deprecated since HTML4] [Obsolete since HTML5]

	[]byte("rowspan"), // [OK] Number of rows that the cell is to span

	[]byte("scope"),  // [Obsolete since HTML5] [OK in <th>]
	[]byte("valign"), // [Obsolete since HTML5]
	[]byte("width"),  // [Deprecated since HTML4] [Obsolete since HTML5]
)

func (r *TableHTMLRenderer) renderTableCell(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	n := node.(*ast.TableCell)
	tag := "td"
	if n.Parent().Kind() == ast.KindTableHeader {
		tag = "th"
	}
	if entering {
		align := ""
		if n.Alignment != ast.AlignNone {
			if _, ok := n.AttributeString("align"); !ok { // Skip align render if overridden
				// TODO: "align" is deprecated. style="text-align:%s" instead?
				align = fmt.Sprintf(` align="%s"`, n.Alignment.String())
			}
		}
		fmt.Fprintf(w, "<%s", tag)
		if n.Attributes() != nil {
			if tag == "td" {
				html.RenderAttributes(w, n, TableTdCellAttributeFilter) // <td>
			} else {
				html.RenderAttributes(w, n, TableThCellAttributeFilter) // <th>
			}
		}
		fmt.Fprintf(w, "%s>", align)
	} else {
		fmt.Fprintf(w, "</%s>\n", tag)
	}
	return gast.WalkContinue, nil
}

type table struct {
}

// Table is an extension that allow you to use GFM tables .
var Table = &table{}

func (e *table) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(parser.WithParagraphTransformers(
		util.Prioritized(NewTableParagraphTransformer(), 200),
	))
	m.Renderer().AddOptions(renderer.WithNodeRenderers(
		util.Prioritized(NewTableHTMLRenderer(), 500),
	))
}

A vendor/github.com/yuin/goldmark/extension/tasklist.go => vendor/github.com/yuin/goldmark/extension/tasklist.go +115 -0
@@ 0,0 1,115 @@
package extension

import (
	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
	"regexp"
)

var taskListRegexp = regexp.MustCompile(`^\[([\sxX])\]\s*`)

type taskCheckBoxParser struct {
}

var defaultTaskCheckBoxParser = &taskCheckBoxParser{}

// NewTaskCheckBoxParser returns a new  InlineParser that can parse
// checkboxes in list items.
// This parser must take precedence over the parser.LinkParser.
func NewTaskCheckBoxParser() parser.InlineParser {
	return defaultTaskCheckBoxParser
}

func (s *taskCheckBoxParser) Trigger() []byte {
	return []byte{'['}
}

func (s *taskCheckBoxParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
	// Given AST structure must be like
	// - List
	//   - ListItem         : parent.Parent
	//     - TextBlock      : parent
	//       (current line)
	if parent.Parent() == nil || parent.Parent().FirstChild() != parent {
		return nil
	}

	if _, ok := parent.Parent().(*gast.ListItem); !ok {
		return nil
	}
	line, _ := block.PeekLine()
	m := taskListRegexp.FindSubmatchIndex(line)
	if m == nil {
		return nil
	}
	value := line[m[2]:m[3]][0]
	block.Advance(m[1])
	checked := value == 'x' || value == 'X'
	return ast.NewTaskCheckBox(checked)
}

func (s *taskCheckBoxParser) CloseBlock(parent gast.Node, pc parser.Context) {
	// nothing to do
}

// TaskCheckBoxHTMLRenderer is a renderer.NodeRenderer implementation that
// renders checkboxes in list items.
type TaskCheckBoxHTMLRenderer struct {
	html.Config
}

// NewTaskCheckBoxHTMLRenderer returns a new TaskCheckBoxHTMLRenderer.
func NewTaskCheckBoxHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
	r := &TaskCheckBoxHTMLRenderer{
		Config: html.NewConfig(),
	}
	for _, opt := range opts {
		opt.SetHTMLOption(&r.Config)
	}
	return r
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *TaskCheckBoxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindTaskCheckBox, r.renderTaskCheckBox)
}

func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
	if !entering {
		return gast.WalkContinue, nil
	}
	n := node.(*ast.TaskCheckBox)

	if n.IsChecked {
		w.WriteString(`<input checked="" disabled="" type="checkbox"`)
	} else {
		w.WriteString(`<input disabled="" type="checkbox"`)
	}
	if r.XHTML {
		w.WriteString(" />")
	} else {
		w.WriteString(">")
	}
	return gast.WalkContinue, nil
}

type taskList struct {
}

// TaskList is an extension that allow you to use GFM task lists.
var TaskList = &taskList{}

func (e *taskList) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(parser.WithInlineParsers(
		util.Prioritized(NewTaskCheckBoxParser(), 0),
	))
	m.Renderer().AddOptions(renderer.WithNodeRenderers(
		util.Prioritized(NewTaskCheckBoxHTMLRenderer(), 500),
	))
}

A vendor/github.com/yuin/goldmark/extension/typographer.go => vendor/github.com/yuin/goldmark/extension/typographer.go +270 -0
@@ 0,0 1,270 @@
package extension

import (
	"unicode"

	"github.com/yuin/goldmark"
	gast "github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

// TypographicPunctuation is a key of the punctuations that can be replaced with
// typographic entities.
type TypographicPunctuation int

const (
	// LeftSingleQuote is '
	LeftSingleQuote TypographicPunctuation = iota + 1
	// RightSingleQuote is '
	RightSingleQuote
	// LeftDoubleQuote is "
	LeftDoubleQuote
	// RightDoubleQuote is "
	RightDoubleQuote
	// EnDash is --
	EnDash
	// EmDash is ---
	EmDash
	// Ellipsis is ...
	Ellipsis
	// LeftAngleQuote is <<
	LeftAngleQuote
	// RightAngleQuote is >>
	RightAngleQuote
	// Apostrophe is '
	Apostrophe

	typographicPunctuationMax
)

// An TypographerConfig struct is a data structure that holds configuration of the
// Typographer extension.
type TypographerConfig struct {
	Substitutions [][]byte
}

func newDefaultSubstitutions() [][]byte {
	replacements := make([][]byte, typographicPunctuationMax)
	replacements[LeftSingleQuote] = []byte("&lsquo;")
	replacements[RightSingleQuote] = []byte("&rsquo;")
	replacements[LeftDoubleQuote] = []byte("&ldquo;")
	replacements[RightDoubleQuote] = []byte("&rdquo;")
	replacements[EnDash] = []byte("&ndash;")
	replacements[EmDash] = []byte("&mdash;")
	replacements[Ellipsis] = []byte("&hellip;")
	replacements[LeftAngleQuote] = []byte("&laquo;")
	replacements[RightAngleQuote] = []byte("&raquo;")
	replacements[Apostrophe] = []byte("&rsquo;")

	return replacements
}

// SetOption implements SetOptioner.
func (b *TypographerConfig) SetOption(name parser.OptionName, value interface{}) {
	switch name {
	case optTypographicSubstitutions:
		b.Substitutions = value.([][]byte)
	}
}

// A TypographerOption interface sets options for the TypographerParser.
type TypographerOption interface {
	parser.Option
	SetTypographerOption(*TypographerConfig)
}

const optTypographicSubstitutions parser.OptionName = "TypographicSubstitutions"

// TypographicSubstitutions is a list of the substitutions for the Typographer extension.
type TypographicSubstitutions map[TypographicPunctuation][]byte

type withTypographicSubstitutions struct {
	value [][]byte
}

func (o *withTypographicSubstitutions) SetParserOption(c *parser.Config) {
	c.Options[optTypographicSubstitutions] = o.value
}

func (o *withTypographicSubstitutions) SetTypographerOption(p *TypographerConfig) {
	p.Substitutions = o.value
}

// WithTypographicSubstitutions is a functional otpion that specify replacement text
// for punctuations.
func WithTypographicSubstitutions(values map[TypographicPunctuation][]byte) TypographerOption {
	replacements := newDefaultSubstitutions()
	for k, v := range values {
		replacements[k] = v
	}

	return &withTypographicSubstitutions{replacements}
}

type typographerDelimiterProcessor struct {
}

func (p *typographerDelimiterProcessor) IsDelimiter(b byte) bool {
	return b == '\'' || b == '"'
}

func (p *typographerDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
	return opener.Char == closer.Char
}

func (p *typographerDelimiterProcessor) OnMatch(consumes int) gast.Node {
	return nil
}

var defaultTypographerDelimiterProcessor = &typographerDelimiterProcessor{}

type typographerParser struct {
	TypographerConfig
}

// NewTypographerParser return a new InlineParser that parses
// typographer expressions.
func NewTypographerParser(opts ...TypographerOption) parser.InlineParser {
	p := &typographerParser{
		TypographerConfig: TypographerConfig{
			Substitutions: newDefaultSubstitutions(),
		},
	}
	for _, o := range opts {
		o.SetTypographerOption(&p.TypographerConfig)
	}
	return p
}

func (s *typographerParser) Trigger() []byte {
	return []byte{'\'', '"', '-', '.', '<', '>'}
}

func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
	before := block.PrecendingCharacter()
	line, _ := block.PeekLine()
	c := line[0]
	if len(line) > 2 {
		if c == '-' {
			if s.Substitutions[EmDash] != nil && line[1] == '-' && line[2] == '-' { // ---
				node := gast.NewString(s.Substitutions[EmDash])
				node.SetCode(true)
				block.Advance(3)
				return node
			}
		} else if c == '.' {
			if s.Substitutions[Ellipsis] != nil && line[1] == '.' && line[2] == '.' { // ...
				node := gast.NewString(s.Substitutions[Ellipsis])
				node.SetCode(true)
				block.Advance(3)
				return node
			}
			return nil
		}
	}
	if len(line) > 1 {
		if c == '<' {
			if s.Substitutions[LeftAngleQuote] != nil && line[1] == '<' { // <<
				node := gast.NewString(s.Substitutions[LeftAngleQuote])
				node.SetCode(true)
				block.Advance(2)
				return node
			}
			return nil
		} else if c == '>' {
			if s.Substitutions[RightAngleQuote] != nil && line[1] == '>' { // >>
				node := gast.NewString(s.Substitutions[RightAngleQuote])
				node.SetCode(true)
				block.Advance(2)
				return node
			}
			return nil
		} else if s.Substitutions[EnDash] != nil && c == '-' && line[1] == '-' { // --
			node := gast.NewString(s.Substitutions[EnDash])
			node.SetCode(true)
			block.Advance(2)
			return node
		}
	}
	if c == '\'' || c == '"' {
		d := parser.ScanDelimiter(line, before, 1, defaultTypographerDelimiterProcessor)
		if d == nil {
			return nil
		}
		if c == '\'' {
			if s.Substitutions[Apostrophe] != nil {
				// Handle decade abbrevations such as '90s
				if d.CanOpen && !d.CanClose && len(line) > 3 && util.IsNumeric(line[1]) && util.IsNumeric(line[2]) && line[3] == 's' {
					after := util.ToRune(line, 4)
					if len(line) == 3 || unicode.IsSpace(after) || unicode.IsPunct(after) {
						node := gast.NewString(s.Substitutions[Apostrophe])
						node.SetCode(true)
						block.Advance(1)
						return node
					}
				}
				// Convert normal apostrophes. This is probably more flexible than necessary but
				// converts any apostrophe in between two alphanumerics.
				if len(line) > 1 && (unicode.IsDigit(before) || unicode.IsLetter(before)) && (util.IsAlphaNumeric(line[1])) {
					node := gast.NewString(s.Substitutions[Apostrophe])
					node.SetCode(true)
					block.Advance(1)
					return node
				}
			}
			if s.Substitutions[LeftSingleQuote] != nil && d.CanOpen && !d.CanClose {
				node := gast.NewString(s.Substitutions[LeftSingleQuote])
				node.SetCode(true)
				block.Advance(1)
				return node
			}
			if s.Substitutions[RightSingleQuote] != nil && d.CanClose && !d.CanOpen {
				node := gast.NewString(s.Substitutions[RightSingleQuote])
				node.SetCode(true)
				block.Advance(1)
				return node
			}
		}
		if c == '"' {
			if s.Substitutions[LeftDoubleQuote] != nil && d.CanOpen && !d.CanClose {
				node := gast.NewString(s.Substitutions[LeftDoubleQuote])
				node.SetCode(true)
				block.Advance(1)
				return node
			}
			if s.Substitutions[RightDoubleQuote] != nil && d.CanClose && !d.CanOpen {
				node := gast.NewString(s.Substitutions[RightDoubleQuote])
				node.SetCode(true)
				block.Advance(1)
				return node
			}
		}
	}
	return nil
}

func (s *typographerParser) CloseBlock(parent gast.Node, pc parser.Context) {
	// nothing to do
}

type typographer struct {
	options []TypographerOption
}

// Typographer is an extension that repalace punctuations with typographic entities.
var Typographer = &typographer{}

// NewTypographer returns a new Entender that repalace punctuations with typographic entities.
func NewTypographer(opts ...TypographerOption) goldmark.Extender {
	return &typographer{
		options: opts,
	}
}

func (e *typographer) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(parser.WithInlineParsers(
		util.Prioritized(NewTypographerParser(e.options...), 9999),
	))
}

M vendor/modules.txt => vendor/modules.txt +5 -1
@@ 1,6 1,6 @@
# a4.io/blobsfile v0.3.8
a4.io/blobsfile
# a4.io/gluapp v0.0.0-20200202115504-51581a8e4642
# a4.io/gluapp v0.0.0-20200211221502-1cf72ec06507
a4.io/gluapp
a4.io/gluapp/util
# a4.io/gluarequire2 v0.0.0-20170611121149-66e0eb2c6a9f


@@ 187,6 187,8 @@ github.com/xeonx/timeago
# github.com/yuin/goldmark v1.1.22
github.com/yuin/goldmark
github.com/yuin/goldmark/ast
github.com/yuin/goldmark/extension
github.com/yuin/goldmark/extension/ast
github.com/yuin/goldmark/parser
github.com/yuin/goldmark/renderer
github.com/yuin/goldmark/renderer/html


@@ 315,5 317,7 @@ gopkg.in/toqueteos/substring.v1
gopkg.in/warnings.v0
# gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2
# mvdan.cc/xurls v1.1.0
mvdan.cc/xurls
# willnorris.com/go/microformats v1.0.0
willnorris.com/go/microformats

A vendor/mvdan.cc/xurls/.gitignore => vendor/mvdan.cc/xurls/.gitignore +3 -0
@@ 0,0 1,3 @@
cmd/xurls/xurls
generate/tldsgen/tldsgen
generate/regexgen/regexgen

A vendor/mvdan.cc/xurls/.travis.yml => vendor/mvdan.cc/xurls/.travis.yml +4 -0
@@ 0,0 1,4 @@
language: go

go:
  - 1.7.x

A vendor/mvdan.cc/xurls/LICENSE => vendor/mvdan.cc/xurls/LICENSE +27 -0
@@ 0,0 1,27 @@
Copyright (c) 2015, Daniel Martí. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

   * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
   * Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

A vendor/mvdan.cc/xurls/README.md => vendor/mvdan.cc/xurls/README.md +40 -0
@@ 0,0 1,40 @@
# xurls

[![GoDoc](https://godoc.org/github.com/mvdan/xurls?status.svg)](https://godoc.org/github.com/mvdan/xurls)
[![Travis](https://travis-ci.org/mvdan/xurls.svg?branch=master)](https://travis-ci.org/mvdan/xurls)

Extract urls from text using regular expressions.

	go get -u github.com/mvdan/xurls

```go
import "github.com/mvdan/xurls"

func main() {
	xurls.Relaxed.FindString("Do gophers live in golang.org?")
	// "golang.org"
	xurls.Strict.FindAllString("foo.com is http://foo.com/.", -1)
	// []string{"http://foo.com/"}
}
```

`Relaxed` is around five times slower than `Strict` since it does more
work to find the URLs without relying on the scheme:

```
BenchmarkStrictEmpty-4           1000000              1885 ns/op
BenchmarkStrictSingle-4           200000              8356 ns/op
BenchmarkStrictMany-4             100000             22547 ns/op
BenchmarkRelaxedEmpty-4           200000              7284 ns/op
BenchmarkRelaxedSingle-4           30000             58557 ns/op
BenchmarkRelaxedMany-4             10000            130251 ns/op
```

#### cmd/xurls

	go get -u github.com/mvdan/xurls/cmd/xurls

```shell
$ echo "Do gophers live in http://golang.org?" | xurls
http://golang.org
```

A vendor/mvdan.cc/xurls/regex.go => vendor/mvdan.cc/xurls/regex.go +8 -0
@@ 0,0 1,8 @@
// Generated by regexgen

package xurls

const (
	gtld        = `(?i)(aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|adult|ae|aeg|aero|aetna|af|afamilycompany|afl|africa|ag|agakhan|agency|ai|aig|aigo|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|am|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|aq|aquarelle|ar|arab|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|bit|biz|bj|black|blackfriday|blanco|blockbuster|blog|bloomberg|blue|bm|bms|bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|boots|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|cartier|casa|case|caseih|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|chase|chat|cheap|chintai|chloe|christmas|chrome|chrysler|church|ci|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dj|dk|dm|dnp|do|docs|doctor|dodge|dog|doha|domains|dot|download|drive|dtv|dubai|duck|dunlop|duns|dupont|durban|dvag|dvr|dwg|dz|earth|eat|ec|eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epost|epson|equipment|er|ericsson|erni|es|esq|estate|esurance|et|etisalat|eu|eurovision|eus|events|everbank|example|exchange|exit|expert|exposed|express|extraspace|fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|game|games|gap|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glade|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|gnu|godaddy|gold|goldpoint|golf|goo|goodhands|goodyear|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|grocery|group|gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|honeywell|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hr|hsbc|ht|htc|hu|hughes|hyatt|hyundai|i2p|ibm|icbc|ice|icu|id|ie|ieee|ifm|iinet|ikano|il|im|imamat|imdb|immo|immobilien|in|industries|infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|intuit|invalid|investments|io|ipiranga|iq|ir|irish|is|iselect|ismaili|ist|istanbul|it|itau|itv|iveco|iwc|jaguar|java|jcb|jcp|je|jeep|jetzt|jewelry|jio|jlc|jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|ladbrokes|lamborghini|lamer|lancaster|lancia|lancome|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|liaison|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lixil|lk|loan|loans|local|localhost|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|lupin|luxe|luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mcd|mcdonalds|mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|merckmsd|metlife|mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|mn|mo|mobi|mobile|mobily|moda|moe|moi|mom|monash|money|monster|montblanc|mopar|mormon|mortgage|moscow|moto|motorcycles|mov|movie|movistar|mp|mq|mr|ms|msd|mt|mtn|mtpc|mtr|mu|museum|mutual|mutuelle|mv|mw|mx|my|mz|na|nab|nadex|nagoya|name|nationwide|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|neustar|new|newholland|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|off|office|okinawa|olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onion|onl|online|onyourside|ooo|open|oracle|orange|org|organic|orientexpress|origins|osaka|otsuka|ott|ovh|pa|page|pamperedchef|panasonic|panerai|paris|pars|partners|parts|party|passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|phd|philips|phone|photo|photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|qvc|racing|radio|raid|re|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|rightathome|ril|rio|rip|rmit|ro|rocher|rocks|rodeo|rogers|room|rs|rsvp|ru|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scjohnson|scor|scot|sd|se|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|spiegel|spot|spreadbetting|sr|srl|srt|st|stada|staples|star|starhub|statebank|statefarm|statoil|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiftcover|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|tech|technology|tel|telecity|telefonica|temasek|tennis|test|teva|tf|tg|th|thd|theater|theatre|theguardian|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|ubs|uconnect|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|vana|vanguard|vc|ve|vegas|ventures|verisign|vermögensberater|vermögensberatung|versicherung|vet|vg|vi|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|vista|vistaprint|viva|vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|wales|walmart|walter|wang|wanggou|warman|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--3oq18vl8pn36a|xn--3pxu8k|xn--42c2d9a|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--4gq48lf9j|xn--54b7fta0cc|xn--55qw42g|xn--55qx5d|xn--5su34j936bgsg|xn--5tzm5g|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|xn--80ao21a|xn--80aqecdr1a|xn--80asehdb|xn--80aswg|xn--8y0a063a|xn--90a3ac|xn--90ais|xn--9dbq2a|xn--9et52u|xn--9krt00a|xn--b4w605ferd|xn--bck1b9a5dre4c|xn--c1avg|xn--c2br7g|xn--cck2b3b|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--e1a4c|xn--eckvdtc9d|xn--efvy88h|xn--estv75g|xn--fct429k|xn--fhbei|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--fzys8d69uvgm|xn--g2xx48c|xn--gckr3f0f|xn--gecrj9c|xn--gk3at1e|xn--h2brj9c|xn--hxt814e|xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1aef|xn--j1amh|xn--j6w193g|xn--jlq61u9w7b|xn--jvr189m|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kpu716f|xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb2ddes|xn--mgb9awbf|xn--mgba3a3ejt|xn--mgba3a4f16a|xn--mgba3a4fra|xn--mgba7c0bbn0a|xn--mgbaakc7dvf|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbai9a5eva00b|xn--mgbai9azgqp6j|xn--mgbayh7gpa|xn--mgbb9fbpob|xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgbca7dzdo|xn--mgberp4a5d4a87g|xn--mgberp4a5d4ar|xn--mgbi4ecexp|xn--mgbpl2fh|xn--mgbqly7c0a67fbc|xn--mgbqly7cvafr|xn--mgbt3dhd|xn--mgbtf8fl|xn--mgbtx2b|xn--mgbx4cd0ab|xn--mix082f|xn--mix891f|xn--mk1bu44c|xn--mxtq1m|xn--ngbc5azd|xn--ngbe9e0a|xn--ngbrx|xn--nnx388a|xn--node|xn--nqv7f|xn--nqv7fs00ema|xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pbt977c|xn--pgbs0dh|xn--pssy2u|xn--q9jyb4c|xn--qcka1pmc|xn--qxam|xn--rhqv96g|xn--rovu88b|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--tckwe|xn--tiq49xqyj|xn--unup4y|xn--vermgensberater-ctb|xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--w4r85el8fhu5dnra|xn--w4rs40l|xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b|xperia|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|zip|zippo|zkey|zm|zone|zuerich|zw|ελ|бел|дети|ею|католик|ком|мкд|мон|москва|онлайн|орг|рус|рф|сайт|срб|укр|қаз|հայ|קום|ابوظبي|اتصالات|ارامكو|الاردن|الجزائر|السعودية|السعوديه|السعودیة|السعودیۃ|العليان|المغرب|اليمن|امارات|ايران|ایران|بازار|بيتك|بھارت|تونس|سودان|سوريا|سورية|شبكة|عراق|عرب|عمان|فلسطين|قطر|كاثوليك|كوم|مصر|مليسيا|موبايلي|موقع|همراه|پاكستان|پاکستان|कॉम|नेट|भारत|संगठन|বাংলা|ভারত|ਭਾਰਤ|ભારત|இந்தியா|இலங்கை|சிங்கப்பூர்|భారత్|ලංකා|คอม|ไทย|გე|みんな|クラウド|グーグル|コム|ストア|セール|ファッション|ポイント|一号店|世界|中信|中国|中國|中文网|企业|佛山|信息|健康|八卦|公司|公益|台湾|台灣|商城|商店|商标|嘉里|嘉里大酒店|在线|大众汽车|大拿|天主教|娱乐|家電|工行|广东|微博|慈善|我爱你|手机|手表|政务|政府|新加坡|新闻|时尚|書籍|机构|淡马锡|游戏|澳門|澳门|点看|珠宝|移动|组织机构|网址|网店|网站|网络|联通|臺灣|诺基亚|谷歌|购物|通販|集团|電訊盈科|飞利浦|食品|餐厅|香格里拉|香港|닷넷|닷컴|삼성|한국)(?-i)`
	otherScheme = `(?i)(bitcoin|file|magnet|mailto|sms|tel|xmpp)(?-i):`
)

A vendor/mvdan.cc/xurls/schemes.go => vendor/mvdan.cc/xurls/schemes.go +17 -0
@@ 0,0 1,17 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package xurls

// SchemesNoAuthority is a sorted list of some well-known url schemes that are
// followed by ":" instead of "://". Since these are more prone to false
// positives, we limit their matching.
var SchemesNoAuthority = []string{
	`bitcoin`, // Bitcoin
	`file`,    // Files
	`magnet`,  // Torrent magnets
	`mailto`,  // Mail
	`sms`,     // SMS
	`tel`,     // Telephone
	`xmpp`,    // XMPP
}

A vendor/mvdan.cc/xurls/tlds.go => vendor/mvdan.cc/xurls/tlds.go +1565 -0
@@ 0,0 1,1565 @@
// Generated by tldsgen

package xurls

// TLDs is a sorted list of all public top-level domains.
//
// Sources:
//  * https://data.iana.org/TLD/tlds-alpha-by-domain.txt
//  * https://publicsuffix.org/list/effective_tld_names.dat
var TLDs = []string{
	`aaa`,
	`aarp`,
	`abarth`,
	`abb`,
	`abbott`,
	`abbvie`,
	`abc`,
	`able`,
	`abogado`,
	`abudhabi`,
	`ac`,
	`academy`,
	`accenture`,
	`accountant`,
	`accountants`,
	`aco`,
	`active`,
	`actor`,
	`ad`,
	`adac`,
	`ads`,
	`adult`,
	`ae`,
	`aeg`,
	`aero`,
	`aetna`,
	`af`,
	`afamilycompany`,
	`afl`,
	`africa`,
	`ag`,
	`agakhan`,
	`agency`,
	`ai`,
	`aig`,
	`aigo`,
	`airbus`,
	`airforce`,
	`airtel`,
	`akdn`,
	`al`,
	`alfaromeo`,
	`alibaba`,
	`alipay`,
	`allfinanz`,
	`allstate`,
	`ally`,
	`alsace`,
	`alstom`,
	`am`,
	`americanexpress`,
	`americanfamily`,
	`amex`,
	`amfam`,
	`amica`,
	`amsterdam`,
	`analytics`,
	`android`,
	`anquan`,
	`anz`,
	`ao`,
	`aol`,
	`apartments`,
	`app`,
	`apple`,
	`aq`,
	`aquarelle`,
	`ar`,
	`arab`,
	`aramco`,
	`archi`,
	`army`,
	`arpa`,
	`art`,
	`arte`,
	`as`,
	`asda`,
	`asia`,
	`associates`,
	`at`,
	`athleta`,
	`attorney`,
	`au`,
	`auction`,
	`audi`,
	`audible`,
	`audio`,
	`auspost`,
	`author`,
	`auto`,
	`autos`,
	`avianca`,
	`aw`,
	`aws`,
	`ax`,
	`axa`,
	`az`,
	`azure`,
	`ba`,
	`baby`,
	`baidu`,
	`banamex`,
	`bananarepublic`,
	`band`,
	`bank`,
	`bar`,
	`barcelona`,
	`barclaycard`,
	`barclays`,
	`barefoot`,
	`bargains`,
	`baseball`,
	`basketball`,
	`bauhaus`,
	`bayern`,
	`bb`,
	`bbc`,
	`bbt`,
	`bbva`,
	`bcg`,
	`bcn`,
	`bd`,
	`be`,
	`beats`,
	`beauty`,
	`beer`,
	`bentley`,
	`berlin`,
	`best`,
	`bestbuy`,
	`bet`,
	`bf`,
	`bg`,
	`bh`,
	`bharti`,
	`bi`,
	`bible`,
	`bid`,
	`bike`,
	`bing`,
	`bingo`,
	`bio`,
	`biz`,
	`bj`,
	`black`,
	`blackfriday`,
	`blanco`,
	`blockbuster`,
	`blog`,
	`bloomberg`,
	`blue`,
	`bm`,
	`bms`,
	`bmw`,
	`bn`,
	`bnl`,
	`bnpparibas`,
	`bo`,
	`boats`,
	`boehringer`,
	`bofa`,
	`bom`,
	`bond`,
	`boo`,
	`book`,
	`booking`,
	`boots`,
	`bosch`,
	`bostik`,
	`boston`,
	`bot`,
	`boutique`,
	`box`,
	`br`,
	`bradesco`,
	`bridgestone`,
	`broadway`,
	`broker`,
	`brother`,
	`brussels`,
	`bs`,
	`bt`,
	`budapest`,
	`bugatti`,
	`build`,
	`builders`,
	`business`,
	`buy`,
	`buzz`,
	`bv`,
	`bw`,
	`by`,
	`bz`,
	`bzh`,
	`ca`,
	`cab`,
	`cafe`,
	`cal`,
	`call`,
	`calvinklein`,
	`cam`,
	`camera`,
	`camp`,
	`cancerresearch`,
	`canon`,
	`capetown`,
	`capital`,
	`capitalone`,
	`car`,
	`caravan`,
	`cards`,
	`care`,
	`career`,
	`careers`,
	`cars`,
	`cartier`,
	`casa`,
	`case`,
	`caseih`,
	`cash`,
	`casino`,
	`cat`,
	`catering`,
	`catholic`,
	`cba`,
	`cbn`,
	`cbre`,
	`cbs`,
	`cc`,
	`cd`,
	`ceb`,
	`center`,
	`ceo`,
	`cern`,
	`cf`,
	`cfa`,
	`cfd`,
	`cg`,
	`ch`,
	`chanel`,
	`channel`,
	`chase`,
	`chat`,
	`cheap`,
	`chintai`,
	`chloe`,
	`christmas`,
	`chrome`,
	`chrysler`,
	`church`,
	`ci`,
	`cipriani`,
	`circle`,
	`cisco`,
	`citadel`,
	`citi`,
	`citic`,
	`city`,
	`cityeats`,
	`ck`,
	`cl`,
	`claims`,
	`cleaning`,
	`click`,
	`clinic`,
	`clinique`,
	`clothing`,
	`cloud`,
	`club`,
	`clubmed`,
	`cm`,
	`cn`,
	`co`,
	`coach`,
	`codes`,
	`coffee`,
	`college`,
	`cologne`,
	`com`,
	`comcast`,
	`commbank`,
	`community`,
	`company`,
	`compare`,
	`computer`,
	`comsec`,
	`condos`,
	`construction`,
	`consulting`,
	`contact`,
	`contractors`,
	`cooking`,
	`cookingchannel`,
	`cool`,
	`coop`,
	`corsica`,
	`country`,
	`coupon`,
	`coupons`,
	`courses`,
	`cr`,
	`credit`,
	`creditcard`,
	`creditunion`,
	`cricket`,
	`crown`,
	`crs`,
	`cruise`,
	`cruises`,
	`csc`,
	`cu`,
	`cuisinella`,
	`cv`,
	`cw`,
	`cx`,
	`cy`,
	`cymru`,
	`cyou`,
	`cz`,
	`dabur`,
	`dad`,
	`dance`,
	`data`,
	`date`,
	`dating`,
	`datsun`,
	`day`,
	`dclk`,
	`dds`,
	`de`,
	`deal`,
	`dealer`,
	`deals`,
	`degree`,
	`delivery`,
	`dell`,
	`deloitte`,
	`delta`,
	`democrat`,
	`dental`,
	`dentist`,
	`desi`,
	`design`,
	`dev`,
	`dhl`,
	`diamonds`,
	`diet`,
	`digital`,
	`direct`,
	`directory`,
	`discount`,
	`discover`,
	`dish`,
	`diy`,
	`dj`,
	`dk`,
	`dm`,
	`dnp`,
	`do`,
	`docs`,
	`doctor`,
	`dodge`,
	`dog`,
	`doha`,
	`domains`,
	`dot`,
	`download`,
	`drive`,
	`dtv`,
	`dubai`,
	`duck`,
	`dunlop`,
	`duns`,
	`dupont`,
	`durban`,
	`dvag`,
	`dvr`,
	`dwg`,
	`dz`,
	`earth`,
	`eat`,
	`ec`,
	`eco`,
	`edeka`,
	`edu`,
	`education`,
	`ee`,
	`eg`,
	`email`,
	`emerck`,
	`energy`,
	`engineer`,
	`engineering`,
	`enterprises`,
	`epost`,
	`epson`,
	`equipment`,
	`er`,
	`ericsson`,
	`erni`,
	`es`,
	`esq`,
	`estate`,
	`esurance`,
	`et`,
	`etisalat`,
	`eu`,
	`eurovision`,
	`eus`,
	`events`,
	`everbank`,
	`exchange`,
	`expert`,
	`exposed`,
	`express`,
	`extraspace`,
	`fage`,
	`fail`,
	`fairwinds`,
	`faith`,
	`family`,
	`fan`,
	`fans`,
	`farm`,
	`farmers`,
	`fashion`,
	`fast`,
	`fedex`,
	`feedback`,
	`ferrari`,
	`ferrero`,
	`fi`,
	`fiat`,
	`fidelity`,
	`fido`,
	`film`,
	`final`,
	`finance`,
	`financial`,
	`fire`,
	`firestone`,
	`firmdale`,
	`fish`,
	`fishing`,
	`fit`,
	`fitness`,
	`fj`,
	`fk`,
	`flickr`,
	`flights`,
	`flir`,
	`florist`,
	`flowers`,
	`fly`,
	`fm`,
	`fo`,
	`foo`,
	`food`,
	`foodnetwork`,
	`football`,
	`ford`,
	`forex`,
	`forsale`,
	`forum`,
	`foundation`,
	`fox`,
	`fr`,
	`free`,
	`fresenius`,
	`frl`,
	`frogans`,
	`frontdoor`,
	`frontier`,
	`ftr`,
	`fujitsu`,
	`fujixerox`,
	`fun`,
	`fund`,
	`furniture`,
	`futbol`,
	`fyi`,
	`ga`,
	`gal`,
	`gallery`,
	`gallo`,
	`gallup`,
	`game`,
	`games`,
	`gap`,
	`garden`,
	`gb`,
	`gbiz`,
	`gd`,
	`gdn`,
	`ge`,
	`gea`,
	`gent`,
	`genting`,
	`george`,
	`gf`,
	`gg`,
	`ggee`,
	`gh`,
	`gi`,
	`gift`,
	`gifts`,
	`gives`,
	`giving`,
	`gl`,
	`glade`,
	`glass`,
	`gle`,
	`global`,
	`globo`,
	`gm`,
	`gmail`,
	`gmbh`,
	`gmo`,
	`gmx`,
	`gn`,
	`godaddy`,
	`gold`,
	`goldpoint`,
	`golf`,
	`goo`,
	`goodhands`,
	`goodyear`,
	`goog`,
	`google`,
	`gop`,
	`got`,
	`gov`,
	`gp`,
	`gq`,
	`gr`,
	`grainger`,
	`graphics`,
	`gratis`,
	`green`,
	`gripe`,
	`grocery`,
	`group`,
	`gs`,
	`gt`,
	`gu`,
	`guardian`,
	`gucci`,
	`guge`,
	`guide`,
	`guitars`,
	`guru`,
	`gw`,
	`gy`,
	`hair`,
	`hamburg`,
	`hangout`,
	`haus`,
	`hbo`,
	`hdfc`,
	`hdfcbank`,
	`health`,
	`healthcare`,
	`help`,
	`helsinki`,
	`here`,
	`hermes`,
	`hgtv`,
	`hiphop`,
	`hisamitsu`,
	`hitachi`,
	`hiv`,
	`hk`,
	`hkt`,
	`hm`,
	`hn`,
	`hockey`,
	`holdings`,
	`holiday`,
	`homedepot`,
	`homegoods`,
	`homes`,
	`homesense`,
	`honda`,
	`honeywell`,
	`horse`,
	`hospital`,
	`host`,
	`hosting`,
	`hot`,
	`hoteles`,
	`hotels`,
	`hotmail`,
	`house`,
	`how`,
	`hr`,
	`hsbc`,
	`ht`,
	`htc`,
	`hu`,
	`hughes`,
	`hyatt`,
	`hyundai`,
	`ibm`,
	`icbc`,
	`ice`,
	`icu`,
	`id`,
	`ie`,
	`ieee`,
	`ifm`,
	`iinet`,
	`ikano`,
	`il`,
	`im`,
	`imamat`,
	`imdb`,
	`immo`,
	`immobilien`,
	`in`,
	`industries`,
	`infiniti`,
	`info`,
	`ing`,
	`ink`,
	`institute`,
	`insurance`,
	`insure`,
	`int`,
	`intel`,
	`international`,
	`intuit`,
	`investments`,
	`io`,
	`ipiranga`,
	`iq`,
	`ir`,
	`irish`,
	`is`,
	`iselect`,
	`ismaili`,
	`ist`,
	`istanbul`,
	`it`,
	`itau`,
	`itv`,
	`iveco`,
	`iwc`,
	`jaguar`,
	`java`,
	`jcb`,
	`jcp`,
	`je`,
	`jeep`,
	`jetzt`,
	`jewelry`,
	`jio`,
	`jlc`,
	`jll`,
	`jm`,
	`jmp`,
	`jnj`,
	`jo`,
	`jobs`,
	`joburg`,
	`jot`,
	`joy`,
	`jp`,
	`jpmorgan`,
	`jprs`,
	`juegos`,
	`juniper`,
	`kaufen`,
	`kddi`,
	`ke`,
	`kerryhotels`,
	`kerrylogistics`,
	`kerryproperties`,
	`kfh`,
	`kg`,
	`kh`,
	`ki`,
	`kia`,
	`kim`,
	`kinder`,
	`kindle`,
	`kitchen`,
	`kiwi`,
	`km`,
	`kn`,
	`koeln`,
	`komatsu`,
	`kosher`,
	`kp`,
	`kpmg`,
	`kpn`,
	`kr`,
	`krd`,
	`kred`,
	`kuokgroup`,
	`kw`,
	`ky`,
	`kyoto`,
	`kz`,
	`la`,
	`lacaixa`,
	`ladbrokes`,
	`lamborghini`,
	`lamer`,
	`lancaster`,
	`lancia`,
	`lancome`,
	`land`,
	`landrover`,
	`lanxess`,
	`lasalle`,
	`lat`,
	`latino`,
	`latrobe`,
	`law`,
	`lawyer`,
	`lb`,
	`lc`,
	`lds`,
	`lease`,
	`leclerc`,
	`lefrak`,
	`legal`,
	`lego`,
	`lexus`,
	`lgbt`,
	`li`,
	`liaison`,
	`lidl`,
	`life`,
	`lifeinsurance`,
	`lifestyle`,
	`lighting`,
	`like`,
	`lilly`,
	`limited`,
	`limo`,
	`lincoln`,
	`linde`,
	`link`,
	`lipsy`,
	`live`,
	`living`,
	`lixil`,
	`lk`,
	`loan`,
	`loans`,
	`locker`,
	`locus`,
	`loft`,
	`lol`,
	`london`,
	`lotte`,
	`lotto`,
	`love`,
	`lpl`,
	`lplfinancial`,
	`lr`,
	`ls`,
	`lt`,
	`ltd`,
	`ltda`,
	`lu`,
	`lundbeck`,
	`lupin`,
	`luxe`,
	`luxury`,
	`lv`,
	`ly`,
	`ma`,
	`macys`,
	`madrid`,
	`maif`,
	`maison`,
	`makeup`,
	`man`,
	`management`,
	`mango`,
	`map`,
	`market`,
	`marketing`,
	`markets`,
	`marriott`,
	`marshalls`,
	`maserati`,
	`mattel`,
	`mba`,
	`mc`,
	`mcd`,
	`mcdonalds`,
	`mckinsey`,
	`md`,
	`me`,
	`med`,
	`media`,
	`meet`,
	`melbourne`,
	`meme`,
	`memorial`,
	`men`,
	`menu`,
	`meo`,
	`merckmsd`,
	`metlife`,
	`mg`,
	`mh`,
	`miami`,
	`microsoft`,
	`mil`,
	`mini`,
	`mint`,
	`mit`,
	`mitsubishi`,
	`mk`,
	`ml`,
	`mlb`,
	`mls`,
	`mm`,
	`mma`,
	`mn`,
	`mo`,
	`mobi`,
	`mobile`,
	`mobily`,
	`moda`,
	`moe`,
	`moi`,
	`mom`,
	`monash`,
	`money`,
	`monster`,
	`montblanc`,
	`mopar`,
	`mormon`,
	`mortgage`,
	`moscow`,
	`moto`,
	`motorcycles`,
	`mov`,
	`movie`,
	`movistar`,
	`mp`,
	`mq`,
	`mr`,
	`ms`,
	`msd`,
	`mt`,
	`mtn`,
	`mtpc`,
	`mtr`,
	`mu`,
	`museum`,
	`mutual`,
	`mutuelle`,
	`mv`,
	`mw`,
	`mx`,
	`my`,
	`mz`,
	`na`,
	`nab`,
	`nadex`,
	`nagoya`,
	`name`,
	`nationwide`,
	`natura`,
	`navy`,
	`nba`,
	`nc`,
	`ne`,
	`nec`,
	`net`,
	`netbank`,
	`netflix`,
	`network`,
	`neustar`,
	`new`,
	`newholland`,
	`news`,
	`next`,
	`nextdirect`,
	`nexus`,
	`nf`,
	`nfl`,
	`ng`,
	`ngo`,
	`nhk`,
	`ni`,
	`nico`,
	`nike`,
	`nikon`,
	`ninja`,
	`nissan`,
	`nissay`,
	`nl`,
	`no`,
	`nokia`,
	`northwesternmutual`,
	`norton`,
	`now`,
	`nowruz`,
	`nowtv`,
	`np`,
	`nr`,
	`nra`,
	`nrw`,
	`ntt`,
	`nu`,
	`nyc`,
	`nz`,
	`obi`,
	`observer`,
	`off`,
	`office`,
	`okinawa`,
	`olayan`,
	`olayangroup`,
	`oldnavy`,
	`ollo`,
	`om`,
	`omega`,
	`one`,
	`ong`,
	`onion`,
	`onl`,
	`online`,
	`onyourside`,
	`ooo`,
	`open`,
	`oracle`,
	`orange`,
	`org`,
	`organic`,
	`orientexpress`,
	`origins`,
	`osaka`,
	`otsuka`,
	`ott`,
	`ovh`,
	`pa`,
	`page`,
	`pamperedchef`,
	`panasonic`,
	`panerai`,
	`paris`,
	`pars`,
	`partners`,
	`parts`,
	`party`,
	`passagens`,
	`pay`,
	`pccw`,
	`pe`,
	`pet`,
	`pf`,
	`pfizer`,
	`pg`,
	`ph`,
	`pharmacy`,
	`phd`,
	`philips`,
	`phone`,
	`photo`,
	`photography`,
	`photos`,
	`physio`,
	`piaget`,
	`pics`,
	`pictet`,
	`pictures`,
	`pid`,
	`pin`,
	`ping`,
	`pink`,
	`pioneer`,
	`pizza`,
	`pk`,
	`pl`,
	`place`,
	`play`,
	`playstation`,
	`plumbing`,
	`plus`,
	`pm`,
	`pn`,
	`pnc`,
	`pohl`,
	`poker`,
	`politie`,
	`porn`,
	`post`,
	`pr`,
	`pramerica`,
	`praxi`,
	`press`,
	`prime`,
	`pro`,
	`prod`,
	`productions`,
	`prof`,
	`progressive`,
	`promo`,
	`properties`,
	`property`,
	`protection`,
	`pru`,
	`prudential`,
	`ps`,
	`pt`,
	`pub`,
	`pw`,
	`pwc`,
	`py`,
	`qa`,
	`qpon`,
	`quebec`,
	`quest`,
	`qvc`,
	`racing`,
	`radio`,
	`raid`,
	`re`,
	`read`,
	`realestate`,
	`realtor`,
	`realty`,
	`recipes`,
	`red`,
	`redstone`,
	`redumbrella`,
	`rehab`,
	`reise`,
	`reisen`,
	`reit`,
	`reliance`,
	`ren`,
	`rent`,
	`rentals`,
	`repair`,
	`report`,
	`republican`,
	`rest`,
	`restaurant`,
	`review`,
	`reviews`,
	`rexroth`,
	`rich`,
	`richardli`,
	`ricoh`,
	`rightathome`,
	`ril`,
	`rio`,
	`rip`,
	`rmit`,
	`ro`,
	`rocher`,
	`rocks`,
	`rodeo`,
	`rogers`,
	`room`,
	`rs`,
	`rsvp`,
	`ru`,
	`ruhr`,
	`run`,
	`rw`,
	`rwe`,
	`ryukyu`,
	`sa`,
	`saarland`,
	`safe`,
	`safety`,
	`sakura`,
	`sale`,
	`salon`,
	`samsclub`,
	`samsung`,
	`sandvik`,
	`sandvikcoromant`,
	`sanofi`,
	`sap`,
	`sapo`,
	`sarl`,
	`sas`,
	`save`,
	`saxo`,
	`sb`,
	`sbi`,
	`sbs`,
	`sc`,
	`sca`,
	`scb`,
	`schaeffler`,
	`schmidt`,
	`scholarships`,
	`school`,
	`schule`,
	`schwarz`,
	`science`,
	`scjohnson`,
	`scor`,
	`scot`,
	`sd`,
	`se`,
	`search`,
	`seat`,
	`secure`,
	`security`,
	`seek`,
	`select`,
	`sener`,
	`services`,
	`ses`,
	`seven`,
	`sew`,
	`sex`,
	`sexy`,
	`sfr`,
	`sg`,
	`sh`,
	`shangrila`,
	`sharp`,
	`shaw`,
	`shell`,
	`shia`,
	`shiksha`,
	`shoes`,
	`shop`,
	`shopping`,
	`shouji`,
	`show`,
	`showtime`,
	`shriram`,
	`si`,
	`silk`,
	`sina`,
	`singles`,
	`site`,
	`sj`,
	`sk`,
	`ski`,
	`skin`,
	`sky`,
	`skype`,
	`sl`,
	`sling`,
	`sm`,
	`smart`,
	`smile`,
	`sn`,
	`sncf`,
	`so`,
	`soccer`,
	`social`,
	`softbank`,
	`software`,
	`sohu`,
	`solar`,
	`solutions`,
	`song`,
	`sony`,
	`soy`,
	`space`,
	`spiegel`,
	`spot`,
	`spreadbetting`,
	`sr`,
	`srl`,
	`srt`,
	`st`,
	`stada`,
	`staples`,
	`star`,
	`starhub`,
	`statebank`,
	`statefarm`,
	`statoil`,
	`stc`,
	`stcgroup`,
	`stockholm`,
	`storage`,
	`store`,
	`stream`,
	`studio`,
	`study`,
	`style`,
	`su`,
	`sucks`,
	`supplies`,
	`supply`,
	`support`,
	`surf`,
	`surgery`,
	`suzuki`,
	`sv`,
	`swatch`,
	`swiftcover`,
	`swiss`,
	`sx`,
	`sy`,
	`sydney`,
	`symantec`,
	`systems`,
	`sz`,
	`tab`,
	`taipei`,
	`talk`,
	`taobao`,
	`target`,
	`tatamotors`,
	`tatar`,
	`tattoo`,
	`tax`,
	`taxi`,
	`tc`,
	`tci`,
	`td`,
	`tdk`,
	`team`,
	`tech`,
	`technology`,
	`tel`,
	`telecity`,
	`telefonica`,
	`temasek`,
	`tennis`,
	`teva`,
	`tf`,
	`tg`,
	`th`,
	`thd`,
	`theater`,
	`theatre`,
	`theguardian`,
	`tiaa`,
	`tickets`,
	`tienda`,
	`tiffany`,
	`tips`,
	`tires`,
	`tirol`,
	`tj`,
	`tjmaxx`,
	`tjx`,
	`tk`,
	`tkmaxx`,
	`tl`,
	`tm`,
	`tmall`,
	`tn`,
	`to`,
	`today`,
	`tokyo`,
	`tools`,
	`top`,
	`toray`,
	`toshiba`,
	`total`,
	`tours`,
	`town`,
	`toyota`,
	`toys`,
	`tr`,
	`trade`,
	`trading`,
	`training`,
	`travel`,
	`travelchannel`,
	`travelers`,
	`travelersinsurance`,
	`trust`,
	`trv`,
	`tt`,
	`tube`,
	`tui`,
	`tunes`,
	`tushu`,
	`tv`,
	`tvs`,
	`tw`,
	`tz`,
	`ua`,
	`ubank`,
	`ubs`,
	`uconnect`,
	`ug`,
	`uk`,
	`unicom`,
	`university`,
	`uno`,
	`uol`,
	`ups`,
	`us`,
	`uy`,
	`uz`,
	`va`,
	`vacations`,
	`vana`,
	`vanguard`,
	`vc`,
	`ve`,
	`vegas`,
	`ventures`,
	`verisign`,
	`vermögensberater`,
	`vermögensberatung`,
	`versicherung`,
	`vet`,
	`vg`,
	`vi`,
	`viajes`,
	`video`,
	`vig`,
	`viking`,
	`villas`,
	`vin`,
	`vip`,
	`virgin`,
	`visa`,
	`vision`,
	`vista`,
	`vistaprint`,
	`viva`,
	`vivo`,
	`vlaanderen`,
	`vn`,
	`vodka`,
	`volkswagen`,
	`volvo`,
	`vote`,
	`voting`,
	`voto`,
	`voyage`,