securemark 0.260.0 → 0.260.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.260.1
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.260.0
4
8
 
5
9
  - Change link parser to accept shortcut text.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.260.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.260.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("DOMPurify"), require("Prism"));
@@ -6155,12 +6155,8 @@ const link_1 = __webpack_require__(9628);
6155
6155
 
6156
6156
  const source_1 = __webpack_require__(6743);
6157
6157
 
6158
- const dom_1 = __webpack_require__(3252);
6159
-
6160
6158
  const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
6161
- exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['http://', 'https://'], (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)(/^https?:\/\/(?=[\x21-\x7E])/, (0, combinator_1.focus)(/^[\x21-\x7E]+/, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.unescsource, closer)])))), (0, combinator_1.convert)(url => `{ ${url} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
6162
- class: 'url'
6163
- })])));
6159
+ exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['http://', 'https://'], (0, combinator_1.rewrite)((0, combinator_1.open)(/^https?:\/\/(?=[\x21-\x7E])/, (0, combinator_1.focus)(/^[\x21-\x7E]+/, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.unescsource, closer)])))), (0, combinator_1.convert)(url => `{ ${url} }`, (0, combinator_1.union)([link_1.unsafelink])))));
6164
6160
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creation)((0, combinator_1.precedence)(2, (0, combinator_1.union)([(0, combinator_1.surround)('(', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')'), ')', true), (0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']'), ']', true), (0, combinator_1.surround)('{', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}'), '}', true), (0, combinator_1.surround)('"', (0, combinator_1.precedence)(8, (0, combinator_1.some)(source_1.unescsource, '"')), '"', true)]))));
6165
6161
 
6166
6162
  /***/ }),
@@ -6974,25 +6970,6 @@ function parse(content, params, rest, context) {
6974
6970
  return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
6975
6971
  }
6976
6972
 
6977
- function resolve(uri, host, source) {
6978
- switch (true) {
6979
- case uri.slice(0, 2) === '^/':
6980
- const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
6981
- return last.includes('.') // isFile
6982
- && /^[0-9]*[A-Za-z][0-9A-Za-z]*$/.test(last.slice(last.lastIndexOf('.') + 1)) ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}` : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
6983
-
6984
- case host.origin === source.origin && host.pathname === source.pathname:
6985
- case uri.slice(0, 2) === '//':
6986
- return uri;
6987
-
6988
- default:
6989
- const target = new url_1.ReadonlyURL(uri, source.href);
6990
- return target.origin === host.origin ? target.href.slice(target.origin.length) : target.href;
6991
- }
6992
- }
6993
-
6994
- exports.resolve = resolve;
6995
-
6996
6973
  function elem(INSECURE_URI, content, uri, origin) {
6997
6974
  let type;
6998
6975
  let message;
@@ -7007,7 +6984,7 @@ function elem(INSECURE_URI, content, uri, origin) {
7007
6984
  }
7008
6985
 
7009
6986
  return (0, dom_1.html)('a', {
7010
- class: 'link',
6987
+ class: content.length === 0 ? 'url' : 'link',
7011
6988
  href: uri.source,
7012
6989
  target: global_1.undefined || uri.origin !== origin || typeof content[0] === 'object' && content[0].classList.contains('media') ? '_blank' : global_1.undefined
7013
6990
  }, content.length === 0 ? decode(INSECURE_URI) : content);
@@ -7040,6 +7017,25 @@ function elem(INSECURE_URI, content, uri, origin) {
7040
7017
  }, content.length === 0 ? INSECURE_URI : content);
7041
7018
  }
7042
7019
 
7020
+ function resolve(uri, host, source) {
7021
+ switch (true) {
7022
+ case uri.slice(0, 2) === '^/':
7023
+ const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
7024
+ return last.includes('.') // isFile
7025
+ && /^[0-9]*[A-Za-z][0-9A-Za-z]*$/.test(last.slice(last.lastIndexOf('.') + 1)) ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}` : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
7026
+
7027
+ case host.origin === source.origin && host.pathname === source.pathname:
7028
+ case uri.slice(0, 2) === '//':
7029
+ return uri;
7030
+
7031
+ default:
7032
+ const target = new url_1.ReadonlyURL(uri, source.href);
7033
+ return target.origin === host.origin ? target.href.slice(target.origin.length) : target.href;
7034
+ }
7035
+ }
7036
+
7037
+ exports.resolve = resolve;
7038
+
7043
7039
  function decode(uri) {
7044
7040
  if (!uri.includes('%')) return uri;
7045
7041
 
@@ -9056,7 +9052,10 @@ function twitter(source, url) {
9056
9052
  status,
9057
9053
  statusText
9058
9054
  }) {
9059
- (0, dom_1.define)(el, [(0, parser_1.parse)(`*{ ${source.getAttribute('data-src')} }*\n\n\`\`\`\n${status}\n${statusText}\n\`\`\``)]);
9055
+ (0, dom_1.define)(el, [(0, dom_1.define)((0, parser_1.parse)(`{ ${source.getAttribute('data-src')} }`).querySelector('a'), {
9056
+ class: null,
9057
+ target: '_blank'
9058
+ }), (0, dom_1.html)('pre', `${status}\n${statusText}`)]);
9060
9059
  }
9061
9060
 
9062
9061
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.260.0",
3
+ "version": "0.260.1",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -127,13 +127,13 @@ describe('Unit: parser/api/parse', () => {
127
127
  '<figure data-type="math" data-label="$-a" data-group="$" data-number="1" id="label:$-a"><figcaption><span class="figindex">(1)</span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n$$</div></div></figure>',
128
128
  '<p><a class="label" data-label="$-a" href="#label:$-a">(1)</a></p>',
129
129
  '<p><sup class="annotation" id="annotation:ref:1" title="a"><span hidden="">a</span><a href="#annotation:def:1">*1</a></sup></p>',
130
- '<p><a class="link" href="https://source/x/a" target="_blank">a</a></p>',
131
- '<p><a class="link" href="https://source/a" target="_blank">/a</a></p>',
132
- '<p><a class="link" href="/z/a">^/a</a></p>',
133
- '<p><a class="link" href="https://source/x/a" target="_blank">./a</a></p>',
134
- '<p><a class="link" href="https://source/a" target="_blank">../a</a></p>',
135
- '<p><a class="link" href="https://source/a" target="_blank">../../a</a></p>',
136
- '<p><a class="link" href="//domain/a" target="_blank">//domain/a</a></p>',
130
+ '<p><a class="url" href="https://source/x/a" target="_blank">a</a></p>',
131
+ '<p><a class="url" href="https://source/a" target="_blank">/a</a></p>',
132
+ '<p><a class="url" href="/z/a">^/a</a></p>',
133
+ '<p><a class="url" href="https://source/x/a" target="_blank">./a</a></p>',
134
+ '<p><a class="url" href="https://source/a" target="_blank">../a</a></p>',
135
+ '<p><a class="url" href="https://source/a" target="_blank">../../a</a></p>',
136
+ '<p><a class="url" href="//domain/a" target="_blank">//domain/a</a></p>',
137
137
  '<p><a href="https://source/x/a" target="_blank"><img class="media" data-src="https://source/x/a" alt=""></a></p>',
138
138
  '<p><a href="/z/a" target="_blank"><img class="media" data-src="/z/a" alt=""></a></p>',
139
139
  '<p><a href="https://source/a" target="_blank"><img class="media" data-src="https://source/a" alt=""></a></p>',
@@ -151,8 +151,8 @@ describe('Unit: parser/api/parse', () => {
151
151
  ].join('\n\n'), { host: new URL(`${location.origin}/index.md`) }).children].map(el => el.outerHTML),
152
152
  [
153
153
  '<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span>\n</span></details></aside>',
154
- '<p><a class="link" href="/a">^/a</a></p>',
155
- '<p><a class="link" href="https://source/x/a" target="_blank">./a</a></p>',
154
+ '<p><a class="url" href="/a">^/a</a></p>',
155
+ '<p><a class="url" href="https://source/x/a" target="_blank">./a</a></p>',
156
156
  ]);
157
157
  assert.deepStrictEqual(
158
158
  [...parse([
@@ -166,8 +166,8 @@ describe('Unit: parser/api/parse', () => {
166
166
  ].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => el.outerHTML),
167
167
  [
168
168
  `<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="${location.origin}/x/y"><span class="field-name">URL</span>: <span class="field-value">${location.origin}/x/y</span>\n</span></details></aside>`,
169
- '<p><a class="link" href="/z/a">^/a</a></p>',
170
- '<p><a class="link" href="/x/a">./a</a></p>',
169
+ '<p><a class="url" href="/z/a">^/a</a></p>',
170
+ '<p><a class="url" href="/x/a">./a</a></p>',
171
171
  ]);
172
172
  });
173
173
 
@@ -198,8 +198,8 @@ describe('Unit: parser/api/parse', () => {
198
198
  [
199
199
  `<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://example/x"><span class="field-name">URL</span>: <span class="field-value">https://example/x</span>\n</span></details></aside>`,
200
200
  '<pre class="invalid" translate="no" data-invalid-syntax="header" data-invalid-type="syntax" data-invalid-message="Invalid syntax">---\nURL: https://example/y\n---\n</pre>',
201
- '<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://example/y"><span class="field-name">URL</span>: <span class="field-value">https://example/y</span>\n</span></details></aside><p><a class="link" href="https://example/y#" target="_blank">#</a></p><ol class="references"></ol></section></aside>',
202
- '<p><a class="link" href="https://example/x#" target="_blank">#</a></p>',
201
+ '<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://example/y"><span class="field-name">URL</span>: <span class="field-value">https://example/y</span>\n</span></details></aside><p><a class="url" href="https://example/y#" target="_blank">#</a></p><ol class="references"></ol></section></aside>',
202
+ '<p><a class="url" href="https://example/x#" target="_blank">#</a></p>',
203
203
  ]);
204
204
  });
205
205
 
@@ -47,7 +47,7 @@ describe('Unit: parser/inline/annotation', () => {
47
47
  assert.deepStrictEqual(inspect(parser('((`a`))')), [['<sup class="annotation"><span><code data-src="`a`">a</code></span></sup>'], '']);
48
48
  assert.deepStrictEqual(inspect(parser('((@a))')), [['<sup class="annotation"><span><a class="account" href="/@a">@a</a></span></sup>'], '']);
49
49
  assert.deepStrictEqual(inspect(parser('((http://host))')), [['<sup class="annotation"><span><a class="url" href="http://host" target="_blank">http://host</a></span></sup>'], '']);
50
- assert.deepStrictEqual(inspect(parser('((![]{a}))')), [['<sup class="annotation"><span>!<a class="link" href="a">a</a></span></sup>'], '']);
50
+ assert.deepStrictEqual(inspect(parser('((![]{a}))')), [['<sup class="annotation"><span>!<a class="url" href="a">a</a></span></sup>'], '']);
51
51
  assert.deepStrictEqual(inspect(parser('(((a)))')), [['<sup class="annotation"><span>(a)</span></sup>'], '']);
52
52
  assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
53
53
  assert.deepStrictEqual(inspect(parser('(([[a]]))')), [['<sup class="annotation"><span><sup class="reference"><span>a</span></sup></span></sup>'], '']);
@@ -1,19 +1,17 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, some, creation, precedence, validate, focus, rewrite, convert, surround, open, lazy, fmap } from '../../../combinator';
2
+ import { union, some, creation, precedence, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
3
3
  import { unsafelink } from '../link';
4
4
  import { unescsource } from '../../source';
5
- import { define } from 'typed-dom/dom';
6
5
 
7
6
  const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
8
7
 
9
- export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'https://'], fmap(rewrite(
8
+ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'https://'], rewrite(
10
9
  open(
11
10
  /^https?:\/\/(?=[\x21-\x7E])/,
12
11
  focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
13
12
  convert(
14
13
  url => `{ ${url} }`,
15
- union([unsafelink]))),
16
- ([el]) => [define(el, { class: 'url' })])));
14
+ union([unsafelink])))));
17
15
 
18
16
  const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(precedence(2, union([
19
17
  surround('(', some(union([bracket, unescsource]), ')'), ')', true),
@@ -12,11 +12,11 @@ describe('Unit: parser/inline/link', () => {
12
12
  assert.deepStrictEqual(inspect(parser('[]{vbscript:alert}')), [['<a class="invalid">vbscript:alert</a>'], '']);
13
13
  assert.deepStrictEqual(inspect(parser('[]{data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K}')), [['<a class="invalid">data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K</a>'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('[]{any:alert}')), [['<a class="invalid">any:alert</a>'], '']);
15
- assert.deepStrictEqual(inspect(parser('[]{"}')), [['<a class="link" href="&quot;">"</a>'], '']);
16
- assert.deepStrictEqual(inspect(parser('[]{<}')), [['<a class="link" href="<">&lt;</a>'], '']);
17
- assert.deepStrictEqual(inspect(parser('[]{\\}')), [['<a class="link" href="\\">\\</a>'], '']);
18
- assert.deepStrictEqual(inspect(parser('[]{\\"}')), [['<a class="link" href="\\&quot;">\\"</a>'], '']);
19
- assert.deepStrictEqual(inspect(parser('[]{\\<}')), [['<a class="link" href="\\<">\\&lt;</a>'], '']);
15
+ assert.deepStrictEqual(inspect(parser('[]{"}')), [['<a class="url" href="&quot;">"</a>'], '']);
16
+ assert.deepStrictEqual(inspect(parser('[]{<}')), [['<a class="url" href="<">&lt;</a>'], '']);
17
+ assert.deepStrictEqual(inspect(parser('[]{\\}')), [['<a class="url" href="\\">\\</a>'], '']);
18
+ assert.deepStrictEqual(inspect(parser('[]{\\"}')), [['<a class="url" href="\\&quot;">\\"</a>'], '']);
19
+ assert.deepStrictEqual(inspect(parser('[]{\\<}')), [['<a class="url" href="\\<">\\&lt;</a>'], '']);
20
20
  });
21
21
 
22
22
  it('fishing', () => {
@@ -87,34 +87,34 @@ describe('Unit: parser/inline/link', () => {
87
87
  });
88
88
 
89
89
  it('basic', () => {
90
- assert.deepStrictEqual(inspect(parser('[]{b}')), [['<a class="link" href="b">b</a>'], '']);
91
- assert.deepStrictEqual(inspect(parser('[]{b }')), [['<a class="link" href="b">b</a>'], '']);
92
- assert.deepStrictEqual(inspect(parser('[]{b }')), [['<a class="link" href="b">b</a>'], '']);
93
- assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="link" href="b">b</a>'], '']);
94
- assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="link" href="b">b</a>'], '']);
95
- assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="link" href="b">b</a>'], '']);
96
- assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="link" href="b">b</a>'], '']);
97
- assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="link" href="b">b</a>'], '']);
98
- assert.deepStrictEqual(inspect(parser('[]{\\}')), [[`<a class="link" href="\\">\\</a>`], '']);
99
- assert.deepStrictEqual(inspect(parser('[]{\\ }')), [[`<a class="link" href="\\">\\</a>`], '']);
100
- assert.deepStrictEqual(inspect(parser('[]{\\b}')), [[`<a class="link" href="\\b">\\b</a>`], '']);
101
- assert.deepStrictEqual(inspect(parser('[]{?b=c+d&\\#}')), [['<a class="link" href="?b=c+d&amp;\\#">?b=c+d&amp;\\#</a>'], '']);
102
- assert.deepStrictEqual(inspect(parser('[]{?&copy;}')), [['<a class="link" href="?&amp;copy;">?&amp;copy;</a>'], '']);
103
- assert.deepStrictEqual(inspect(parser('[]{#}')), [['<a class="link" href="#">#</a>'], '']);
104
- assert.deepStrictEqual(inspect(parser('[]{#b}')), [['<a class="link" href="#b">#b</a>'], '']);
105
- assert.deepStrictEqual(inspect(parser('[]{./b}')), [['<a class="link" href="./b">./b</a>'], '']);
106
- assert.deepStrictEqual(inspect(parser('[]{^/b}')), [[`<a class="link" href="/b">^/b</a>`], '']);
107
- assert.deepStrictEqual(inspect(parser('[]{^/b?/../}')), [[`<a class="link" href="/b?/../">^/b?/../</a>`], '']);
108
- assert.deepStrictEqual(inspect(parser('[]{^/b#/../}')), [[`<a class="link" href="/b#/../">^/b#/../</a>`], '']);
109
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/dir', location.origin) })), [[`<a class="link" href="/dir/b">^/b</a>`], '']);
110
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/dir/', location.origin) })), [[`<a class="link" href="/dir/b">^/b</a>`], '']);
111
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/folder/doc.md', location.origin) })), [[`<a class="link" href="/folder/b">^/b</a>`], '']);
112
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/folder/doc.md/', location.origin) })), [[`<a class="link" href="/folder/doc.md/b">^/b</a>`], '']);
113
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/.file', location.origin) })), [[`<a class="link" href="/b">^/b</a>`], '']);
114
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0a', location.origin) })), [[`<a class="link" href="/b">^/b</a>`], '']);
115
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.a0', location.origin) })), [[`<a class="link" href="/b">^/b</a>`], '']);
116
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0', location.origin) })), [[`<a class="link" href="/0.0/b">^/b</a>`], '']);
117
- assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0,0.0,0z', location.origin) })), [[`<a class="link" href="/0.0,0.0,0z/b">^/b</a>`], '']);
90
+ assert.deepStrictEqual(inspect(parser('[]{b}')), [['<a class="url" href="b">b</a>'], '']);
91
+ assert.deepStrictEqual(inspect(parser('[]{b }')), [['<a class="url" href="b">b</a>'], '']);
92
+ assert.deepStrictEqual(inspect(parser('[]{b }')), [['<a class="url" href="b">b</a>'], '']);
93
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="url" href="b">b</a>'], '']);
94
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="url" href="b">b</a>'], '']);
95
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="url" href="b">b</a>'], '']);
96
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="url" href="b">b</a>'], '']);
97
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a class="url" href="b">b</a>'], '']);
98
+ assert.deepStrictEqual(inspect(parser('[]{\\}')), [[`<a class="url" href="\\">\\</a>`], '']);
99
+ assert.deepStrictEqual(inspect(parser('[]{\\ }')), [[`<a class="url" href="\\">\\</a>`], '']);
100
+ assert.deepStrictEqual(inspect(parser('[]{\\b}')), [[`<a class="url" href="\\b">\\b</a>`], '']);
101
+ assert.deepStrictEqual(inspect(parser('[]{?b=c+d&\\#}')), [['<a class="url" href="?b=c+d&amp;\\#">?b=c+d&amp;\\#</a>'], '']);
102
+ assert.deepStrictEqual(inspect(parser('[]{?&copy;}')), [['<a class="url" href="?&amp;copy;">?&amp;copy;</a>'], '']);
103
+ assert.deepStrictEqual(inspect(parser('[]{#}')), [['<a class="url" href="#">#</a>'], '']);
104
+ assert.deepStrictEqual(inspect(parser('[]{#b}')), [['<a class="url" href="#b">#b</a>'], '']);
105
+ assert.deepStrictEqual(inspect(parser('[]{./b}')), [['<a class="url" href="./b">./b</a>'], '']);
106
+ assert.deepStrictEqual(inspect(parser('[]{^/b}')), [[`<a class="url" href="/b">^/b</a>`], '']);
107
+ assert.deepStrictEqual(inspect(parser('[]{^/b?/../}')), [[`<a class="url" href="/b?/../">^/b?/../</a>`], '']);
108
+ assert.deepStrictEqual(inspect(parser('[]{^/b#/../}')), [[`<a class="url" href="/b#/../">^/b#/../</a>`], '']);
109
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/dir', location.origin) })), [[`<a class="url" href="/dir/b">^/b</a>`], '']);
110
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/dir/', location.origin) })), [[`<a class="url" href="/dir/b">^/b</a>`], '']);
111
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/folder/doc.md', location.origin) })), [[`<a class="url" href="/folder/b">^/b</a>`], '']);
112
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/folder/doc.md/', location.origin) })), [[`<a class="url" href="/folder/doc.md/b">^/b</a>`], '']);
113
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/.file', location.origin) })), [[`<a class="url" href="/b">^/b</a>`], '']);
114
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0a', location.origin) })), [[`<a class="url" href="/b">^/b</a>`], '']);
115
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.a0', location.origin) })), [[`<a class="url" href="/b">^/b</a>`], '']);
116
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0', location.origin) })), [[`<a class="url" href="/0.0/b">^/b</a>`], '']);
117
+ assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0,0.0,0z', location.origin) })), [[`<a class="url" href="/0.0,0.0,0z/b">^/b</a>`], '']);
118
118
  assert.deepStrictEqual(inspect(parser('[ a]{b}')), [['<a class="link" href="b">a</a>'], '']);
119
119
  assert.deepStrictEqual(inspect(parser('[ a ]{b}')), [['<a class="link" href="b">a</a>'], '']);
120
120
  assert.deepStrictEqual(inspect(parser('[\\ a]{b}')), [['<a class="link" href="b">a</a>'], '']);
@@ -125,11 +125,11 @@ describe('Unit: parser/inline/link', () => {
125
125
  assert.deepStrictEqual(inspect(parser('[a]{#}')), [['<a class="link" href="#">a</a>'], '']);
126
126
  assert.deepStrictEqual(inspect(parser('[a]{#b}')), [['<a class="link" href="#b">a</a>'], '']);
127
127
  assert.deepStrictEqual(inspect(parser('[a b]{c}')), [['<a class="link" href="c">a b</a>'], '']);
128
- assert.deepStrictEqual(inspect(parser(`[]{?#${encodeURIComponent(':/[]{}<>?#=& ')}}`)), [['<a class="link" href="?#%3A%2F%5B%5D%7B%7D%3C%3E%3F%23%3D%26%20">?#%3A%2F[]{}&lt;&gt;%3F%23%3D%26%20</a>'], '']);
129
- assert.deepStrictEqual(inspect(parser('{b}')), [['<a class="link" href="b">b</a>'], '']);
130
- assert.deepStrictEqual(inspect(parser('{ ][ }')), [['<a class="link" href="][">][</a>'], '']);
131
- assert.deepStrictEqual(inspect(parser('{ }{ }')), [['<a class="link" href="}{">}{</a>'], '']);
132
- assert.deepStrictEqual(inspect(parser('{Http://host}')), [['<a class="link" href="Http://host" target="_blank">Http://host</a>'], '']);
128
+ assert.deepStrictEqual(inspect(parser(`[]{?#${encodeURIComponent(':/[]{}<>?#=& ')}}`)), [['<a class="url" href="?#%3A%2F%5B%5D%7B%7D%3C%3E%3F%23%3D%26%20">?#%3A%2F[]{}&lt;&gt;%3F%23%3D%26%20</a>'], '']);
129
+ assert.deepStrictEqual(inspect(parser('{b}')), [['<a class="url" href="b">b</a>'], '']);
130
+ assert.deepStrictEqual(inspect(parser('{ ][ }')), [['<a class="url" href="][">][</a>'], '']);
131
+ assert.deepStrictEqual(inspect(parser('{ }{ }')), [['<a class="url" href="}{">}{</a>'], '']);
132
+ assert.deepStrictEqual(inspect(parser('{Http://host}')), [['<a class="url" href="Http://host" target="_blank">Http://host</a>'], '']);
133
133
  });
134
134
 
135
135
  it('tel', () => {
@@ -167,8 +167,8 @@ describe('Unit: parser/inline/link', () => {
167
167
  });
168
168
 
169
169
  it('external', () => {
170
- assert.deepStrictEqual(inspect(parser('[]{//host}')), [['<a class="link" href="//host" target="_blank">//host</a>'], '']);
171
- assert.deepStrictEqual(inspect(parser('[]{//[::]}')), [['<a class="link" href="//[::]" target="_blank">//[::]</a>'], '']);
170
+ assert.deepStrictEqual(inspect(parser('[]{//host}')), [['<a class="url" href="//host" target="_blank">//host</a>'], '']);
171
+ assert.deepStrictEqual(inspect(parser('[]{//[::]}')), [['<a class="url" href="//[::]" target="_blank">//[::]</a>'], '']);
172
172
  });
173
173
 
174
174
  it('attribute', () => {
@@ -195,11 +195,11 @@ describe('Unit: parser/inline/link', () => {
195
195
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow=""}')), [['<a class="invalid" href="/">/</a>'], '']);
196
196
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow="nofollow"}')), [['<a class="invalid" href="/">/</a>'], '']);
197
197
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow nofollow}')), [['<a class="invalid" href="/" rel="nofollow">/</a>'], '']);
198
- assert.deepStrictEqual(inspect(parser('[]{/ nofollow}')), [['<a class="link" href="/" rel="nofollow">/</a>'], '']);
199
- assert.deepStrictEqual(inspect(parser('[]{/ nofollow }')), [['<a class="link" href="/" rel="nofollow">/</a>'], '']);
200
- assert.deepStrictEqual(inspect(parser('[]{ / nofollow}')), [['<a class="link" href="/" rel="nofollow">/</a>'], '']);
201
- assert.deepStrictEqual(inspect(parser('[]{ / nofollow }')), [['<a class="link" href="/" rel="nofollow">/</a>'], '']);
202
- assert.deepStrictEqual(inspect(parser('[]{http://host nofollow}')), [['<a class="link" href="http://host" target="_blank" rel="nofollow">http://host</a>'], '']);
198
+ assert.deepStrictEqual(inspect(parser('[]{/ nofollow}')), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
199
+ assert.deepStrictEqual(inspect(parser('[]{/ nofollow }')), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
200
+ assert.deepStrictEqual(inspect(parser('[]{ / nofollow}')), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
201
+ assert.deepStrictEqual(inspect(parser('[]{ / nofollow }')), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
202
+ assert.deepStrictEqual(inspect(parser('[]{http://host nofollow}')), [['<a class="url" href="http://host" target="_blank" rel="nofollow">http://host</a>'], '']);
203
203
  assert.deepStrictEqual(inspect(parser('[!http://host]{http://host nofollow}')), [['<a class="link" href="http://host" target="_blank" rel="nofollow"><img class="media" data-src="http://host" alt=""></a>'], '']);
204
204
  });
205
205
 
@@ -105,28 +105,6 @@ function parse(
105
105
  return [[define(el, attributes('link', [], optspec, params))], rest];
106
106
  }
107
107
 
108
- export function resolve(uri: string, host: URL | Location, source: URL | Location): string {
109
- assert(uri);
110
- assert(uri === uri.trim());
111
- switch (true) {
112
- case uri.slice(0, 2) === '^/':
113
- const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
114
- return last.includes('.') // isFile
115
- && /^[0-9]*[A-Za-z][0-9A-Za-z]*$/.test(last.slice(last.lastIndexOf('.') + 1))
116
- ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}`
117
- : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
118
- case host.origin === source.origin
119
- && host.pathname === source.pathname:
120
- case uri.slice(0, 2) === '//':
121
- return uri;
122
- default:
123
- const target = new ReadonlyURL(uri, source.href);
124
- return target.origin === host.origin
125
- ? target.href.slice(target.origin.length)
126
- : target.href;
127
- }
128
- }
129
-
130
108
  function elem(
131
109
  INSECURE_URI: string,
132
110
  content: readonly (string | HTMLElement)[],
@@ -147,7 +125,7 @@ function elem(
147
125
  }
148
126
  return html('a',
149
127
  {
150
- class: 'link',
128
+ class: content.length === 0 ? 'url' : 'link',
151
129
  href: uri.source,
152
130
  target: undefined
153
131
  || uri.origin !== origin
@@ -187,6 +165,28 @@ function elem(
187
165
  : content);
188
166
  }
189
167
 
168
+ export function resolve(uri: string, host: URL | Location, source: URL | Location): string {
169
+ assert(uri);
170
+ assert(uri === uri.trim());
171
+ switch (true) {
172
+ case uri.slice(0, 2) === '^/':
173
+ const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
174
+ return last.includes('.') // isFile
175
+ && /^[0-9]*[A-Za-z][0-9A-Za-z]*$/.test(last.slice(last.lastIndexOf('.') + 1))
176
+ ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}`
177
+ : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
178
+ case host.origin === source.origin
179
+ && host.pathname === source.pathname:
180
+ case uri.slice(0, 2) === '//':
181
+ return uri;
182
+ default:
183
+ const target = new ReadonlyURL(uri, source.href);
184
+ return target.origin === host.origin
185
+ ? target.href.slice(target.origin.length)
186
+ : target.href;
187
+ }
188
+ }
189
+
190
190
  function decode(uri: string): string {
191
191
  if (!uri.includes('%')) return uri;
192
192
  try {
@@ -46,7 +46,7 @@ describe('Unit: parser/inline/reference', () => {
46
46
  assert.deepStrictEqual(inspect(parser('[[`a`]]')), [['<sup class="reference"><span><code data-src="`a`">a</code></span></sup>'], '']);
47
47
  assert.deepStrictEqual(inspect(parser('[[@a]]')), [['<sup class="reference"><span><a class="account" href="/@a">@a</a></span></sup>'], '']);
48
48
  assert.deepStrictEqual(inspect(parser('[[http://host]]')), [['<sup class="reference"><span><a class="url" href="http://host" target="_blank">http://host</a></span></sup>'], '']);
49
- assert.deepStrictEqual(inspect(parser('[[![]{a}]]')), [['<sup class="reference"><span>!<a class="link" href="a">a</a></span></sup>'], '']);
49
+ assert.deepStrictEqual(inspect(parser('[[![]{a}]]')), [['<sup class="reference"><span>!<a class="url" href="a">a</a></span></sup>'], '']);
50
50
  assert.deepStrictEqual(inspect(parser('[[[a]]]')), [['<sup class="reference"><span>[a]</span></sup>'], '']);
51
51
  assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
52
52
  assert.deepStrictEqual(inspect(parser('[[((a))]]')), [['<sup class="reference"><span><span class="paren">((a))</span></span></sup>'], '']);
@@ -117,27 +117,27 @@ describe('Unit: parser/inline', () => {
117
117
  assert.deepStrictEqual(inspect(parser('$$-1')), [['$', '<a class="label" data-label="$-1">$-1</a>'], '']);
118
118
  assert.deepStrictEqual(inspect(parser('[[#a]]')), [['<sup class="reference"><span><a class="hashtag" href="/hashtags/a">#a</a></span></sup>'], '']);
119
119
  assert.deepStrictEqual(inspect(parser('[[$-1]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
120
- assert.deepStrictEqual(inspect(parser('[[#-1]]{b}')), [['<sup class="reference"><span>#-1</span></sup>', '<a class="link" href="b">b</a>'], '']);
120
+ assert.deepStrictEqual(inspect(parser('[[#-1]]{b}')), [['<sup class="reference"><span>#-1</span></sup>', '<a class="url" href="b">b</a>'], '']);
121
121
  assert.deepStrictEqual(inspect(parser('[[#-1]](b)')), [['<sup class="reference"><span>#-1</span></sup>', '(', 'b', ')'], '']);
122
122
  assert.deepStrictEqual(inspect(parser('[[#-1]a]{b}')), [['<a class="link" href="b">[#-1]a</a>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('[[#-1]a](b)')), [['[', '<a class="index" href="#index:-1">-1</a>', 'a', ']', '(', 'b', ')'], '']);
124
- assert.deepStrictEqual(inspect(parser('[#a]{b}')), [['<a class="index" href="#index:a">a</a>', '<a class="link" href="b">b</a>'], '']);
124
+ assert.deepStrictEqual(inspect(parser('[#a]{b}')), [['<a class="index" href="#index:a">a</a>', '<a class="url" href="b">b</a>'], '']);
125
125
  assert.deepStrictEqual(inspect(parser('[@a]{b}')), [['<a class="link" href="b">@a</a>'], '']);
126
- assert.deepStrictEqual(inspect(parser('[http://host]{http://evil}')), [['[', '<a class="url" href="http://host" target="_blank">http://host</a>', ']', '<a class="link" href="http://evil" target="_blank">http://evil</a>'], '']);
127
- assert.deepStrictEqual(inspect(parser('[http://host]{http://host}')), [['[', '<a class="url" href="http://host" target="_blank">http://host</a>', ']', '<a class="link" href="http://host" target="_blank">http://host</a>'], '']);
126
+ assert.deepStrictEqual(inspect(parser('[http://host]{http://evil}')), [['[', '<a class="url" href="http://host" target="_blank">http://host</a>', ']', '<a class="url" href="http://evil" target="_blank">http://evil</a>'], '']);
127
+ assert.deepStrictEqual(inspect(parser('[http://host]{http://host}')), [['[', '<a class="url" href="http://host" target="_blank">http://host</a>', ']', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
128
128
  assert.deepStrictEqual(inspect(parser('[]{{a}}')), [['[', ']', '<span class="template">{{a}}</span>'], '']);
129
129
  assert.deepStrictEqual(inspect(parser('![]{{a}}')), [['!', '[', ']', '<span class="template">{{a}}</span>'], '']);
130
- assert.deepStrictEqual(inspect(parser('[\n]{a}')), [['[', '<br>', ']', '<a class="link" href="a">a</a>'], '']);
131
- assert.deepStrictEqual(inspect(parser('[\\\n]{a}')), [['[', '<span class="linebreak"> </span>', ']', '<a class="link" href="a">a</a>'], '']);
130
+ assert.deepStrictEqual(inspect(parser('[\n]{a}')), [['[', '<br>', ']', '<a class="url" href="a">a</a>'], '']);
131
+ assert.deepStrictEqual(inspect(parser('[\\\n]{a}')), [['[', '<span class="linebreak"> </span>', ']', '<a class="url" href="a">a</a>'], '']);
132
132
  assert.deepStrictEqual(inspect(parser('{}')), [['{', '}'], '']);
133
- assert.deepStrictEqual(inspect(parser('{a}')), [['<a class="link" href="a">a</a>'], '']);
133
+ assert.deepStrictEqual(inspect(parser('{a}')), [['<a class="url" href="a">a</a>'], '']);
134
134
  assert.deepStrictEqual(inspect(parser('{{a}}')), [['<span class="template">{{a}}</span>'], '']);
135
135
  assert.deepStrictEqual(inspect(parser('!{}')), [['!', '{', '}'], '']);
136
136
  assert.deepStrictEqual(inspect(parser('!{a}')), [['<a href="a" target="_blank"><img class="media" data-src="a" alt=""></a>'], '']);
137
137
  assert.deepStrictEqual(inspect(parser('!{{a}}')), [['!', '<span class="template">{{a}}</span>'], '']);
138
138
  assert.deepStrictEqual(inspect(parser('!{{{a}}}')), [['!', '<span class="template">{{{a}}}</span>'], '']);
139
139
  assert.deepStrictEqual(inspect(parser('!!{a}')), [['!', '<a href="a" target="_blank"><img class="media" data-src="a" alt=""></a>'], '']);
140
- assert.deepStrictEqual(inspect(parser('${a}')), [['$', '<a class="link" href="a">a</a>'], '']);
140
+ assert.deepStrictEqual(inspect(parser('${a}')), [['$', '<a class="url" href="a">a</a>'], '']);
141
141
  assert.deepStrictEqual(inspect(parser('${{a}}')), [['$', '<span class="template">{{a}}</span>'], '']);
142
142
  assert.deepStrictEqual(inspect(parser('${{{a}}}')), [['$', '<span class="template">{{{a}}}</span>'], '']);
143
143
  assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft')), [['Di', '$', 'ney', ' ', 'Micro', '$', 'oft'], '']);
@@ -152,14 +152,14 @@ describe('Unit: parser/inline', () => {
152
152
  assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['', '[', '', '[', '<sup class="reference"><span>a</span></sup>'], '']);
153
153
  assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
154
154
  assert.deepStrictEqual(inspect(parser('[[[$-1]]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
155
- assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a class="link" href="a">a</a></span></sup>'], '']);
155
+ assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a class="url" href="a">a</a></span></sup>'], '']);
156
156
  assert.deepStrictEqual(inspect(parser('[[[a]{b}]]')), [['<sup class="reference"><span><a class="link" href="b">a</a></span></sup>'], '']);
157
157
  assert.deepStrictEqual(inspect(parser('[(([a]{#}))]{#}')), [['<a class="link" href="#"><span class="paren">(<span class="paren">([a]{#})</span>)</span></a>'], '']);
158
158
  assert.deepStrictEqual(inspect(parser('[[<bdi>]]')), [['<sup class="reference"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
159
159
  assert.deepStrictEqual(inspect(parser('[[${]]}$')), [['', '[', '', '[', '<span class="math" translate="no" data-src="${]]}$">${]]}$</span>'], '']);
160
160
  assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference"><span>""</span></sup>'], '']);
161
161
  assert.deepStrictEqual(inspect(parser('[[a](b)]{c}')), [['<a class="link" href="c"><ruby>a<rp>(</rp><rt>b</rt><rp>)</rp></ruby></a>'], '']);
162
- assert.deepStrictEqual(inspect(parser('[[[[[[[{a}')), [['', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '<a class="link" href="a">a</a>'], '']);
162
+ assert.deepStrictEqual(inspect(parser('[[[[[[[{a}')), [['', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '<a class="url" href="a">a</a>'], '']);
163
163
  assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a class="url" href="http://host" target="_blank">http://host</a>', '>'], '']);
164
164
  assert.deepStrictEqual(inspect(parser('[~http://host')), [['', '[', '~', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
165
165
  assert.deepStrictEqual(inspect(parser('[~a@b')), [['', '[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
@@ -167,7 +167,7 @@ describe('Unit: parser/inline', () => {
167
167
  assert.deepStrictEqual(inspect(parser('[^http://host')), [['[^', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
168
168
  assert.deepStrictEqual(inspect(parser('[^a@b')), [['[^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
169
169
  assert.deepStrictEqual(inspect(parser('[#a*b\nc*]')), [['[', '<a class="hashtag" href="/hashtags/a">#a</a>', '<em>b<br>c</em>', ']'], '']);
170
- assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<em>a<br>b</em>', ']', '<a class="link" href="/">/</a>'], '']);
170
+ assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<em>a<br>b</em>', ']', '<a class="url" href="/">/</a>'], '']);
171
171
  assert.deepStrictEqual(inspect(parser('[*a\nb]*')), [['[', '*', 'a', '<br>', 'b', ']', '*'], '']);
172
172
  assert.deepStrictEqual(inspect(parser('[*[a\nb*]')), [['', '[', '*', '[', 'a', '<br>', 'b', '*', ']'], '']);
173
173
  assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[', '[', '<em>a<br>b</em>', ']', ']'], '']);
@@ -37,7 +37,13 @@ export function twitter(source: HTMLImageElement, url: URL): HTMLElement | undef
37
37
  },
38
38
  error({ status, statusText }) {
39
39
  assert(Number.isSafeInteger(status));
40
- define(el, [parse(`*{ ${source.getAttribute('data-src')} }*\n\n\`\`\`\n${status}\n${statusText}\n\`\`\``)]);
40
+ define(el, [
41
+ define(parse(`{ ${source.getAttribute('data-src')} }`).querySelector('a')!, {
42
+ class: null,
43
+ target: '_blank',
44
+ }),
45
+ h('pre', `${status}\n${statusText}`),
46
+ ]);
41
47
  },
42
48
  });
43
49
  return el;