securemark 0.258.7 → 0.259.0
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 +12 -0
- package/design.md +4 -4
- package/dist/index.js +69 -45
- package/markdown.d.ts +37 -19
- package/package.json +1 -1
- package/src/combinator/data/parser/context.ts +1 -0
- package/src/parser/block/reply/cite.test.ts +3 -1
- package/src/parser/block/reply/cite.ts +1 -0
- package/src/parser/inline/annotation.ts +1 -1
- package/src/parser/inline/autolink/account.ts +2 -2
- package/src/parser/inline/autolink/anchor.ts +2 -2
- package/src/parser/inline/autolink/hashnum.ts +2 -2
- package/src/parser/inline/autolink/hashtag.ts +2 -2
- package/src/parser/inline/autolink/url.ts +2 -2
- package/src/parser/inline/extension/index.ts +1 -1
- package/src/parser/inline/link.ts +71 -64
- package/src/parser/inline/media.ts +10 -9
- package/src/parser/inline/reference.ts +1 -1
- package/src/parser/inline.ts +0 -1
package/CHANGELOG.md
CHANGED
package/design.md
CHANGED
|
@@ -285,13 +285,13 @@ CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く
|
|
|
285
285
|
### バックトラック
|
|
286
286
|
|
|
287
287
|
SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合一般に文脈の相違から解析結果を再利用不能な(`αA'β | αAB`)バックトラックが生じる。文脈依存構文の解析中に文脈により解釈の異なる字句(`[a[`)または現在の文脈を終了する字句(ただの括弧がスコープを作らない場合の`(([a))`)に遭遇したとき、現在の文脈での解析の継続を試行する解析方法はメモ化なしではバックトラックが再帰的に生じ線形時間で解析できず、現在の文脈での解析を中止し直前の文脈を継続または新たな文脈を開始する解析方法はバックトラックなしで線形時間で解析できる。
|
|
288
|
-
CommonMarkはLink
|
|
289
|
-
|
|
290
|
-
Securemark
|
|
288
|
+
CommonMarkはLink構文から明らかなように後者の解析方法により重篤なバックトラックなし(作者によると、だがHTML構文とAutolink構文では再帰的バックトラックが生じるのはないか?)で(インラインでは)ほぼ1回の走査で解析できる言語であるが同時にこれは文脈依存構文の正当な入れ子表現(`[<a@b>]`)を解析できないか不正な表現(`[<a@b>]()`)を除外できず二重リンク(`<a><a></a></a>`)を生成する、文脈依存構文の存在する数に応じて多項式的(おそらく$n^{2+c}$)に構文の壊れやすさの増す脆く拡張性の低い言語であることを意味する(構文の決定を遅延できれば解決できるがこれにより他の構文の解釈が非決定的にならない場合に限られる。そしてCommonMarkは角括弧(`<>`)の対応を取っていないためAutolink構文は自身の解釈が非決定的になると後続の構文の解釈も非決定的になる)。この問題はおそらくメモ化により解決できるがCommonMarkは実行性能追及のためメモ化を廃止しており二重リンクも放置しているためメモ化により性能を低下させてまで解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。
|
|
289
|
+
従って現在のほぼ1回の走査で解析可能な文法に制約されるCommonMarkには文脈依存構文を入れ子表現の広範な制限ならびに構文の可読性および開始記号の信頼性の多項式的な低下と引き換えにしか追加できないという拡張性の欠陥が存在する。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の欠陥が明白になっていくためおよび現在の高い実行性能を低下させたくないためである。`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はない。CommonMarkは小さく単純であるがゆえに正しくいられる象牙の塔であり仕様策定者はこの正しさを失わず正しいままでいたいがために象牙の塔に引きこもり小さな完全性を固持し続けているのである(でなければ何年も隠さず速やかにこの拡張性の欠如を公表して助力を求めていなければならない)。
|
|
290
|
+
Securemarkは線形時間で解析不能な前者の解析方法をメモ化によりおおよそ4n以下の最悪計算量に改善しさらに解析時間と解析範囲の局限により一定時間内で解析不能な入力の影響を局限することでこれらの問題を解決している。この解析方法はほとんどの自然な入力に対して1nに近い時間で効率的に動作し、最悪計算量で低速に動作させる少数の機械的攻撃入力に対してもサーバーで多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで単一のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはない。
|
|
291
291
|
|
|
292
292
|
### メモ化
|
|
293
293
|
|
|
294
|
-
|
|
294
|
+
一部の文脈依存言語を線形時間で解析できるようになるとしても状態数に応じて複数回走査が必要なこと、このため時間効率が定性的には改善されても定量的には文脈自由言語より数倍悪いこと、オーバーヘッドが有意に大きいことなどから理論上同じ線形時間だとしても実用上文脈自由言語と同等の実行性能にはならず文脈依存言語と文脈自由言語を等価の選択肢にするものではない。
|
|
295
295
|
|
|
296
296
|
### 最適化
|
|
297
297
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.259.0 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"));
|
|
@@ -2884,6 +2884,7 @@ exports.syntax = syntax;
|
|
|
2884
2884
|
|
|
2885
2885
|
function creation(cost, parser) {
|
|
2886
2886
|
if (typeof cost === 'function') return creation(1, cost);
|
|
2887
|
+
if (cost === 0) return parser;
|
|
2887
2888
|
return (source, context) => {
|
|
2888
2889
|
const {
|
|
2889
2890
|
resources = {
|
|
@@ -5286,6 +5287,10 @@ exports.cite = (0, combinator_1.creation)((0, combinator_1.line)((0, combinator_
|
|
|
5286
5287
|
class: 'anchor'
|
|
5287
5288
|
}, '>>.')], '']), (0, combinator_1.focus)(/^>>#\S*[^\S\n]*(?:$|\n)/, source => [[(0, dom_1.html)('a', {
|
|
5288
5289
|
class: 'anchor'
|
|
5290
|
+
}, source)], '']), (0, combinator_1.focus)(/^>>https?:\/\/\w\S*[^\S\n]*(?:$|\n)/, source => [[(0, dom_1.html)('a', {
|
|
5291
|
+
class: 'anchor',
|
|
5292
|
+
href: source.slice(2).trimEnd(),
|
|
5293
|
+
target: '_blank'
|
|
5289
5294
|
}, source)], ''])])]))), ([el, quotes = '']) => [(0, dom_1.html)('span', {
|
|
5290
5295
|
class: 'cite'
|
|
5291
5296
|
}, (0, dom_1.defrag)([`${quotes}>`, (0, dom_1.define)(el, {
|
|
@@ -5700,13 +5705,13 @@ const dom_1 = __webpack_require__(3252);
|
|
|
5700
5705
|
|
|
5701
5706
|
exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('((', (0, combinator_1.constraint)(64
|
|
5702
5707
|
/* State.annotation */
|
|
5703
|
-
, false, (0, combinator_1.
|
|
5704
|
-
/* Syntax.annotation */
|
|
5705
|
-
, 6, 1, (0, combinator_1.state)(64
|
|
5708
|
+
, false, (0, combinator_1.state)(64
|
|
5706
5709
|
/* State.annotation */
|
|
5707
5710
|
| 2
|
|
5708
5711
|
/* State.media */
|
|
5709
|
-
, (0,
|
|
5712
|
+
, (0, combinator_1.syntax)(32
|
|
5713
|
+
/* Syntax.annotation */
|
|
5714
|
+
, 6, 1, (0, visibility_1.startLoose)((0, combinator_1.context)({
|
|
5710
5715
|
delimiters: global_1.undefined
|
|
5711
5716
|
}, (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')')))), '))', false, ([, ns], rest) => [[(0, dom_1.html)('sup', {
|
|
5712
5717
|
class: 'annotation'
|
|
@@ -5777,7 +5782,7 @@ const source_1 = __webpack_require__(6743);
|
|
|
5777
5782
|
const dom_1 = __webpack_require__(3252); // https://example/@user must be a user page or a redirect page going there.
|
|
5778
5783
|
|
|
5779
5784
|
|
|
5780
|
-
exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('@', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(/^[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/), ([source]) => source.length <= 64)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.
|
|
5785
|
+
exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('@', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(/^[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/), ([source]) => source.length <= 64)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5781
5786
|
class: 'account'
|
|
5782
5787
|
})]));
|
|
5783
5788
|
|
|
@@ -5806,7 +5811,7 @@ const dom_1 = __webpack_require__(3252); // Timeline(pseudonym): user/tid
|
|
|
5806
5811
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
5807
5812
|
|
|
5808
5813
|
|
|
5809
|
-
exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.focus)(/^>>(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*\/)?[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*(?![0-9A-Za-z@#:])/, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline/')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.
|
|
5814
|
+
exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.focus)(/^>>(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*\/)?[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*(?![0-9A-Za-z@#:])/, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline/')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5810
5815
|
class: 'anchor'
|
|
5811
5816
|
})])));
|
|
5812
5817
|
|
|
@@ -5893,7 +5898,7 @@ const source_1 = __webpack_require__(6743);
|
|
|
5893
5898
|
|
|
5894
5899
|
const dom_1 = __webpack_require__(3252);
|
|
5895
5900
|
|
|
5896
|
-
exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.
|
|
5901
|
+
exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5897
5902
|
class: 'hashnum',
|
|
5898
5903
|
href: null
|
|
5899
5904
|
})]));
|
|
@@ -5922,7 +5927,7 @@ const dom_1 = __webpack_require__(3252); // https://example/hashtags/a must be a
|
|
|
5922
5927
|
|
|
5923
5928
|
|
|
5924
5929
|
exports.emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
|
|
5925
|
-
exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(new RegExp([/^(?=[0-9]{0,127}_?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|_(?=[^\p{C}\p{S}\p{P}\s]|emoji)){1,128}/u.source, /(?!_?(?:[^\p{C}\p{S}\p{P}\s]|emoji)|')/u.source].join('').replace(/emoji/g, exports.emoji), 'u')), ([source]) => source.length <= 128)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/hashtags/')}` : `/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.
|
|
5930
|
+
exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(new RegExp([/^(?=[0-9]{0,127}_?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|_(?=[^\p{C}\p{S}\p{P}\s]|emoji)){1,128}/u.source, /(?!_?(?:[^\p{C}\p{S}\p{P}\s]|emoji)|')/u.source].join('').replace(/emoji/g, exports.emoji), 'u')), ([source]) => source.length <= 128)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/hashtags/')}` : `/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5926
5931
|
class: 'hashtag'
|
|
5927
5932
|
}, el.innerText)]));
|
|
5928
5933
|
|
|
@@ -5946,7 +5951,7 @@ const link_1 = __webpack_require__(9628);
|
|
|
5946
5951
|
const source_1 = __webpack_require__(6743);
|
|
5947
5952
|
|
|
5948
5953
|
const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
|
|
5949
|
-
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.
|
|
5954
|
+
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])))));
|
|
5950
5955
|
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)]))));
|
|
5951
5956
|
|
|
5952
5957
|
/***/ }),
|
|
@@ -6252,9 +6257,7 @@ const dom_1 = __webpack_require__(3252);
|
|
|
6252
6257
|
|
|
6253
6258
|
exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0, combinator_1.fmap)((0, indexee_1.indexee)((0, combinator_1.surround)('[#', (0, combinator_1.constraint)(16
|
|
6254
6259
|
/* State.index */
|
|
6255
|
-
, false, (0, combinator_1.
|
|
6256
|
-
/* Syntax.index */
|
|
6257
|
-
, 2, 1, (0, combinator_1.state)(64
|
|
6260
|
+
, false, (0, combinator_1.state)(64
|
|
6258
6261
|
/* State.annotation */
|
|
6259
6262
|
| 32
|
|
6260
6263
|
/* State.reference */
|
|
@@ -6268,7 +6271,9 @@ exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0
|
|
|
6268
6271
|
/* State.media */
|
|
6269
6272
|
| 1
|
|
6270
6273
|
/* State.autolink */
|
|
6271
|
-
, (0,
|
|
6274
|
+
, (0, combinator_1.syntax)(1024
|
|
6275
|
+
/* Syntax.index */
|
|
6276
|
+
, 2, 1, (0, visibility_1.startTight)((0, combinator_1.open)((0, source_1.stropt)(/^\|?/), (0, visibility_1.trimBlankEnd)((0, combinator_1.some)((0, combinator_1.union)([signature, inline_1.inline]), ']', [[/^\\?\n/, 9], [']', 2]])), true))))), ']', false, ([, ns], rest) => [[(0, dom_1.html)('a', (0, dom_1.defrag)(ns))], rest])), ([el]) => [(0, dom_1.define)(el, {
|
|
6272
6277
|
id: el.id ? null : global_1.undefined,
|
|
6273
6278
|
class: 'index',
|
|
6274
6279
|
href: el.id ? `#${el.id}` : global_1.undefined
|
|
@@ -6649,7 +6654,7 @@ exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0,
|
|
|
6649
6654
|
Object.defineProperty(exports, "__esModule", ({
|
|
6650
6655
|
value: true
|
|
6651
6656
|
}));
|
|
6652
|
-
exports.resolve = exports.option = exports.uri = exports.
|
|
6657
|
+
exports.resolve = exports.option = exports.uri = exports.unsafelink = exports.link = void 0;
|
|
6653
6658
|
|
|
6654
6659
|
const global_1 = __webpack_require__(4128);
|
|
6655
6660
|
|
|
@@ -6677,13 +6682,14 @@ const optspec = {
|
|
|
6677
6682
|
rel: ['nofollow']
|
|
6678
6683
|
};
|
|
6679
6684
|
Object.setPrototypeOf(optspec, null);
|
|
6680
|
-
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.
|
|
6685
|
+
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.union)([medialink, textlink])));
|
|
6686
|
+
const textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4
|
|
6681
6687
|
/* State.link */
|
|
6682
|
-
, false, (0, combinator_1.
|
|
6683
|
-
/* Syntax.link */
|
|
6684
|
-
, 2, 10, (0, combinator_1.fmap)((0, combinator_1.subsequence)([(0, combinator_1.state)(4
|
|
6688
|
+
, false, (0, combinator_1.state)(4
|
|
6685
6689
|
/* State.link */
|
|
6686
|
-
|
|
6690
|
+
| 2
|
|
6691
|
+
/* State.media */
|
|
6692
|
+
| 64
|
|
6687
6693
|
/* State.annotation */
|
|
6688
6694
|
| 32
|
|
6689
6695
|
/* State.reference */
|
|
@@ -6691,35 +6697,52 @@ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'
|
|
|
6691
6697
|
/* State.index */
|
|
6692
6698
|
| 8
|
|
6693
6699
|
/* State.label */
|
|
6694
|
-
| 2
|
|
6695
|
-
/* State.media */
|
|
6696
6700
|
| 1
|
|
6697
6701
|
/* State.autolink */
|
|
6698
|
-
, (0, combinator_1.
|
|
6699
|
-
|
|
6700
|
-
|
|
6702
|
+
, (0, combinator_1.syntax)(256
|
|
6703
|
+
/* Syntax.link */
|
|
6704
|
+
, 2, 10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', [[/^\\?\n/, 9], [']', 2]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => {
|
|
6701
6705
|
if (content.length !== 0 && (0, visibility_1.trimNode)(content).length === 0) return;
|
|
6702
6706
|
|
|
6703
6707
|
for (let source = (0, util_1.stringify)(content); source;) {
|
|
6704
|
-
const result = (0,
|
|
6708
|
+
const result = (0, combinator_1.state)(1
|
|
6709
|
+
/* State.autolink */
|
|
6710
|
+
, false, autolink_1.autolink)(source, context);
|
|
6705
6711
|
if (typeof (0, parser_1.eval)(result)[0] === 'object') return;
|
|
6706
6712
|
source = (0, parser_1.exec)(result);
|
|
6707
6713
|
}
|
|
6708
6714
|
|
|
6715
|
+
return parse(content, params, rest, context);
|
|
6716
|
+
})))));
|
|
6717
|
+
const medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4
|
|
6718
|
+
/* State.link */
|
|
6719
|
+
| 2
|
|
6720
|
+
/* State.media */
|
|
6721
|
+
, false, (0, combinator_1.state)(4
|
|
6722
|
+
/* State.link */
|
|
6723
|
+
| 64
|
|
6724
|
+
/* State.annotation */
|
|
6725
|
+
| 32
|
|
6726
|
+
/* State.reference */
|
|
6727
|
+
| 16
|
|
6728
|
+
/* State.index */
|
|
6729
|
+
| 8
|
|
6730
|
+
/* State.label */
|
|
6731
|
+
| 1
|
|
6732
|
+
/* State.autolink */
|
|
6733
|
+
, (0, combinator_1.syntax)(256
|
|
6734
|
+
/* Syntax.link */
|
|
6735
|
+
, 2, 10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.union)([inline_1.media, inline_1.shortmedia]), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => parse(content, params, rest, context))))));
|
|
6736
|
+
exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.precedence)(2, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => parse(content, params, rest, context)))));
|
|
6737
|
+
exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/^[^\S\n]+/, (0, source_1.str)(/^\S+/)), (0, source_1.str)(/^[^\s{}]+/)]);
|
|
6738
|
+
exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]), (0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/), (0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`])]);
|
|
6739
|
+
|
|
6740
|
+
function parse(content, params, rest, context) {
|
|
6709
6741
|
const INSECURE_URI = params.shift();
|
|
6710
6742
|
const el = elem(INSECURE_URI, (0, dom_1.defrag)(content), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href), context.host?.origin || global_1.location.origin);
|
|
6711
6743
|
if (el.className === 'invalid') return [[el], rest];
|
|
6712
6744
|
return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
|
|
6713
|
-
}
|
|
6714
|
-
exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.bind)((0, combinator_1.creation)(10, (0, combinator_1.precedence)(2, (0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])))), ([params, content = []], rest, context) => {
|
|
6715
|
-
params.shift();
|
|
6716
|
-
(0, visibility_1.trimNode)(content);
|
|
6717
|
-
const INSECURE_URI = params.shift();
|
|
6718
|
-
const el = elem(INSECURE_URI, (0, dom_1.defrag)(content), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href), context.host?.origin || global_1.location.origin);
|
|
6719
|
-
return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
|
|
6720
|
-
})));
|
|
6721
|
-
exports.uri = (0, combinator_1.fmap)((0, combinator_1.union)([(0, combinator_1.open)(/^[^\S\n]+/, (0, source_1.str)(/^\S+/)), (0, source_1.str)(/^[^\s{}]+/)]), ([uri]) => ['\r', uri]);
|
|
6722
|
-
exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]), (0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/), (0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`])]);
|
|
6745
|
+
}
|
|
6723
6746
|
|
|
6724
6747
|
function resolve(uri, host, source) {
|
|
6725
6748
|
switch (true) {
|
|
@@ -6899,11 +6922,11 @@ const optspec = {
|
|
|
6899
6922
|
rel: global_1.undefined
|
|
6900
6923
|
};
|
|
6901
6924
|
Object.setPrototypeOf(optspec, null);
|
|
6902
|
-
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.
|
|
6925
|
+
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.open)('!', (0, combinator_1.constraint)(2
|
|
6903
6926
|
/* State.media */
|
|
6904
6927
|
, false, (0, combinator_1.syntax)(64
|
|
6905
6928
|
/* Syntax.media */
|
|
6906
|
-
, 2, 10, (0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']', [[/^\\?\n/, 9]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /^[^\S\n]*}/))])
|
|
6929
|
+
, 2, 10, (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']', [[/^\\?\n/, 9]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /^[^\S\n]*}/))]), ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
|
|
6907
6930
|
const INSECURE_URI = params.shift();
|
|
6908
6931
|
const url = new url_1.ReadonlyURL((0, link_1.resolve)(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href);
|
|
6909
6932
|
let cache;
|
|
@@ -6923,11 +6946,12 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '
|
|
|
6923
6946
|
|
|
6924
6947
|
if (context.state & 4
|
|
6925
6948
|
/* State.link */
|
|
6926
|
-
|
|
6927
|
-
return (0, combinator_1.
|
|
6949
|
+
) return [[el], rest];
|
|
6950
|
+
if (cache && cache.tagName !== 'IMG') return (0, combinator_1.creation)(10, (..._) => [[el], rest])('!', context);
|
|
6951
|
+
return (0, combinator_1.fmap)(link_1.unsafelink, ([link]) => [(0, dom_1.define)(link, {
|
|
6928
6952
|
target: '_blank'
|
|
6929
6953
|
}, [el])])(`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
|
|
6930
|
-
})));
|
|
6954
|
+
}))))));
|
|
6931
6955
|
const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creation)((0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ')'), (0, source_1.str)(')'), true, global_1.undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']'), (0, source_1.str)(']'), true, global_1.undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), '}'), (0, source_1.str)('}'), true, global_1.undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(8, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, source_1.txt]), '"')), (0, source_1.str)('"'), true)])));
|
|
6932
6956
|
const option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+[1-9][0-9]*x[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` width="${opt.slice(1).split('x')[0]}"`, ` height="${opt.slice(1).split('x')[1]}"`]), (0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+[1-9][0-9]*:[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` aspect-ratio="${opt.slice(1).split(':').join('/')}"`]), link_1.option]);
|
|
6933
6957
|
|
|
@@ -7000,15 +7024,15 @@ const dom_1 = __webpack_require__(3252);
|
|
|
7000
7024
|
|
|
7001
7025
|
exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('[[', (0, combinator_1.constraint)(32
|
|
7002
7026
|
/* State.reference */
|
|
7003
|
-
, false, (0, combinator_1.
|
|
7004
|
-
/* Syntax.reference */
|
|
7005
|
-
, 6, 1, (0, combinator_1.state)(64
|
|
7027
|
+
, false, (0, combinator_1.state)(64
|
|
7006
7028
|
/* State.annotation */
|
|
7007
7029
|
| 32
|
|
7008
7030
|
/* State.reference */
|
|
7009
7031
|
| 2
|
|
7010
7032
|
/* State.media */
|
|
7011
|
-
, (0,
|
|
7033
|
+
, (0, combinator_1.syntax)(4096
|
|
7034
|
+
/* Syntax.reference */
|
|
7035
|
+
, 6, 1, (0, visibility_1.startLoose)((0, combinator_1.context)({
|
|
7012
7036
|
delimiters: global_1.undefined
|
|
7013
7037
|
}, (0, combinator_1.subsequence)([abbr, (0, combinator_1.open)((0, source_1.stropt)(/^(?=\^)/), (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 2], [']]', 6]])), (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 2], [']]', 6]])])), ']')))), ']]', false, ([, ns], rest) => [[(0, dom_1.html)('sup', attributes(ns), [(0, dom_1.html)('span', (0, visibility_1.trimNode)((0, dom_1.defrag)(ns)))])], rest]));
|
|
7014
7038
|
const abbr = (0, combinator_1.creation)((0, combinator_1.bind)((0, combinator_1.surround)('^', (0, combinator_1.union)([(0, source_1.str)(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(?:-|(?=\W)(?!'\d)'?(?!\.\d)\.?(?!,\S),? ?)[0-9A-Za-z]+)*(?:-|'?\.?,? ?)?/)]), /^\|?(?=]])|^\|[^\S\n]*/), ([source], rest) => [[(0, dom_1.html)('abbr', source)], rest.replace(visibility_1.regBlankStart, '')]));
|
package/markdown.d.ts
CHANGED
|
@@ -577,6 +577,7 @@ export namespace MarkdownParser {
|
|
|
577
577
|
InlineParser.AutolinkParser.AnchorParser,
|
|
578
578
|
Parser<HTMLAnchorElement, Context, []>,
|
|
579
579
|
Parser<HTMLAnchorElement, Context, []>,
|
|
580
|
+
Parser<HTMLAnchorElement, Context, []>,
|
|
580
581
|
]>,
|
|
581
582
|
]> {
|
|
582
583
|
}
|
|
@@ -660,7 +661,7 @@ export namespace MarkdownParser {
|
|
|
660
661
|
export interface AnnotationParser extends
|
|
661
662
|
// ((abc))
|
|
662
663
|
Inline<'annotation'>,
|
|
663
|
-
Parser<HTMLElement
|
|
664
|
+
Parser<HTMLElement, Context, [
|
|
664
665
|
InlineParser,
|
|
665
666
|
]> {
|
|
666
667
|
}
|
|
@@ -669,7 +670,7 @@ export namespace MarkdownParser {
|
|
|
669
670
|
// [[^abbr]]
|
|
670
671
|
// [[^abbr| abc]]
|
|
671
672
|
Inline<'reference'>,
|
|
672
|
-
Parser<HTMLElement
|
|
673
|
+
Parser<HTMLElement, Context, [
|
|
673
674
|
ReferenceParser.AbbrParser,
|
|
674
675
|
InlineParser,
|
|
675
676
|
InlineParser,
|
|
@@ -689,7 +690,7 @@ export namespace MarkdownParser {
|
|
|
689
690
|
export interface TemplateParser extends
|
|
690
691
|
// {{abc}}
|
|
691
692
|
Inline<'template'>,
|
|
692
|
-
Parser<HTMLSpanElement
|
|
693
|
+
Parser<HTMLSpanElement, Context, [
|
|
693
694
|
TemplateParser.BracketParser,
|
|
694
695
|
SourceParser.EscapableSourceParser,
|
|
695
696
|
]> {
|
|
@@ -842,21 +843,38 @@ export namespace MarkdownParser {
|
|
|
842
843
|
// { uri }
|
|
843
844
|
// [abc]{uri nofollow}
|
|
844
845
|
Inline<'link'>,
|
|
845
|
-
Parser<HTMLElement | string, Context, [
|
|
846
|
-
LinkParser.ContentParser,
|
|
847
|
-
LinkParser.ParameterParser,
|
|
848
|
-
]> {
|
|
849
|
-
}
|
|
850
|
-
export interface TextLinkParser extends
|
|
851
|
-
// { uri }
|
|
852
|
-
// [abc]{uri nofollow}
|
|
853
|
-
Inline<'textlink'>,
|
|
854
846
|
Parser<HTMLAnchorElement, Context, [
|
|
855
|
-
LinkParser.
|
|
856
|
-
LinkParser.
|
|
847
|
+
LinkParser.MediaLinkParser,
|
|
848
|
+
LinkParser.TextLinkParser,
|
|
857
849
|
]> {
|
|
858
850
|
}
|
|
859
851
|
export namespace LinkParser {
|
|
852
|
+
export interface TextLinkParser extends
|
|
853
|
+
Inline<'link/textlink'>,
|
|
854
|
+
Parser<HTMLAnchorElement, Context, [
|
|
855
|
+
Parser<(HTMLElement | string)[], Context, [
|
|
856
|
+
InlineParser,
|
|
857
|
+
]>,
|
|
858
|
+
LinkParser.ParameterParser,
|
|
859
|
+
]> {
|
|
860
|
+
}
|
|
861
|
+
export interface MediaLinkParser extends
|
|
862
|
+
Inline<'link/medialink'>,
|
|
863
|
+
Parser<HTMLAnchorElement, Context, [
|
|
864
|
+
Parser<HTMLElement[], Context, [
|
|
865
|
+
MediaParser,
|
|
866
|
+
ShortmediaParser,
|
|
867
|
+
]>,
|
|
868
|
+
LinkParser.ParameterParser,
|
|
869
|
+
]> {
|
|
870
|
+
}
|
|
871
|
+
export interface UnsafeLinkParser extends
|
|
872
|
+
Inline<'link/unsafelink'>,
|
|
873
|
+
Parser<HTMLAnchorElement, Context, [
|
|
874
|
+
LinkParser.TextParser,
|
|
875
|
+
LinkParser.ParameterParser,
|
|
876
|
+
]> {
|
|
877
|
+
}
|
|
860
878
|
export interface ContentParser extends
|
|
861
879
|
Inline<'link/content'>,
|
|
862
880
|
Parser<(HTMLElement | string)[], Context, [
|
|
@@ -1093,7 +1111,7 @@ export namespace MarkdownParser {
|
|
|
1093
1111
|
// https://host
|
|
1094
1112
|
Inline<'url'>,
|
|
1095
1113
|
Parser<HTMLAnchorElement, Context, [
|
|
1096
|
-
|
|
1114
|
+
LinkParser.UnsafeLinkParser,
|
|
1097
1115
|
]> {
|
|
1098
1116
|
}
|
|
1099
1117
|
export namespace UrlParser {
|
|
@@ -1135,28 +1153,28 @@ export namespace MarkdownParser {
|
|
|
1135
1153
|
// @user
|
|
1136
1154
|
Inline<'account'>,
|
|
1137
1155
|
Parser<HTMLAnchorElement, Context, [
|
|
1138
|
-
|
|
1156
|
+
LinkParser.UnsafeLinkParser,
|
|
1139
1157
|
]> {
|
|
1140
1158
|
}
|
|
1141
1159
|
export interface HashtagParser extends
|
|
1142
1160
|
// #tag
|
|
1143
1161
|
Inline<'hashtag'>,
|
|
1144
1162
|
Parser<HTMLAnchorElement, Context, [
|
|
1145
|
-
|
|
1163
|
+
LinkParser.UnsafeLinkParser,
|
|
1146
1164
|
]> {
|
|
1147
1165
|
}
|
|
1148
1166
|
export interface HashnumParser extends
|
|
1149
1167
|
// #1
|
|
1150
1168
|
Inline<'hashnum'>,
|
|
1151
1169
|
Parser<HTMLAnchorElement, Context, [
|
|
1152
|
-
|
|
1170
|
+
LinkParser.UnsafeLinkParser,
|
|
1153
1171
|
]> {
|
|
1154
1172
|
}
|
|
1155
1173
|
export interface AnchorParser extends
|
|
1156
1174
|
// >>1
|
|
1157
1175
|
Inline<'anchor'>,
|
|
1158
1176
|
Parser<HTMLAnchorElement, Context, [
|
|
1159
|
-
|
|
1177
|
+
LinkParser.UnsafeLinkParser,
|
|
1160
1178
|
]> {
|
|
1161
1179
|
}
|
|
1162
1180
|
}
|
package/package.json
CHANGED
|
@@ -89,6 +89,7 @@ export function creation<P extends Parser<unknown>>(parser: P): P;
|
|
|
89
89
|
export function creation<P extends Parser<unknown>>(cost: number, parser: P): P;
|
|
90
90
|
export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown>): Parser<unknown> {
|
|
91
91
|
if (typeof cost === 'function') return creation(1, cost);
|
|
92
|
+
if (cost === 0) return parser!;
|
|
92
93
|
assert(cost >= 0);
|
|
93
94
|
return (source, context) => {
|
|
94
95
|
const { resources = { clock: 1, recursion: 1 } } = context;
|
|
@@ -18,7 +18,7 @@ describe('Unit: parser/block/reply/cite', () => {
|
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('>>\\')), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('>>01#')), undefined);
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('>>01@')), undefined);
|
|
21
|
-
assert.deepStrictEqual(inspect(parser('>>
|
|
21
|
+
assert.deepStrictEqual(inspect(parser('>>http://')), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('>>tel:1234567890')), undefined);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('>>..')), undefined);
|
|
24
24
|
assert.deepStrictEqual(inspect(parser('>> 0')), undefined);
|
|
@@ -43,6 +43,8 @@ describe('Unit: parser/block/reply/cite', () => {
|
|
|
43
43
|
assert.deepStrictEqual(inspect(parser('>>#a')), [['<span class="cite">><a class="anchor" data-depth="1">>#a</a></span>', '<br>'], '']);
|
|
44
44
|
assert.deepStrictEqual(inspect(parser('>>#index:a')), [['<span class="cite">><a class="anchor" data-depth="1">>#index:a</a></span>', '<br>'], '']);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('>>#:~:text=a')), [['<span class="cite">><a class="anchor" data-depth="1">>#:~:text=a</a></span>', '<br>'], '']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser('>>http://host')), [['<span class="cite">><a class="anchor" href="http://host" target="_blank" data-depth="1">>http://host</a></span>', '<br>'], '']);
|
|
47
|
+
assert.deepStrictEqual(inspect(parser('>>https://host')), [['<span class="cite">><a class="anchor" href="https://host" target="_blank" data-depth="1">>https://host</a></span>', '<br>'], '']);
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
});
|
|
@@ -14,6 +14,7 @@ export const cite: ReplyParser.CiteParser = creation(line(fmap(validate(
|
|
|
14
14
|
// リンクの実装は後で検討
|
|
15
15
|
focus(/^>>\.[^\S\n]*(?:$|\n)/, () => [[html('a', { class: 'anchor' }, '>>.')], '']),
|
|
16
16
|
focus(/^>>#\S*[^\S\n]*(?:$|\n)/, source => [[html('a', { class: 'anchor' }, source)], '']),
|
|
17
|
+
focus(/^>>https?:\/\/\w\S*[^\S\n]*(?:$|\n)/, source => [[html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)], '']),
|
|
17
18
|
]),
|
|
18
19
|
]))),
|
|
19
20
|
([el, quotes = '']: [HTMLElement, string?]) => [
|
|
@@ -9,8 +9,8 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
9
9
|
export const annotation: AnnotationParser = lazy(() => surround(
|
|
10
10
|
'((',
|
|
11
11
|
constraint(State.annotation, false,
|
|
12
|
-
syntax(Syntax.annotation, 6, 1,
|
|
13
12
|
state(State.annotation | State.media,
|
|
13
|
+
syntax(Syntax.annotation, 6, 1,
|
|
14
14
|
startLoose(
|
|
15
15
|
context({ delimiters: undefined },
|
|
16
16
|
some(union([inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')')))),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { union, tails, verify, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
|
-
import {
|
|
3
|
+
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { define } from 'typed-dom/dom';
|
|
6
6
|
|
|
@@ -24,5 +24,5 @@ export const account: AutolinkParser.AccountParser = lazy(() => fmap(rewrite(
|
|
|
24
24
|
? `https://${source.slice(1).replace('/', '/@')}`
|
|
25
25
|
: `/${source}`
|
|
26
26
|
} }`,
|
|
27
|
-
union([
|
|
27
|
+
union([unsafelink]))),
|
|
28
28
|
([el]) => [define(el, { class: 'account' })]));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { union, validate, focus, convert, fmap, lazy } from '../../../combinator';
|
|
3
|
-
import {
|
|
3
|
+
import { unsafelink } from '../link';
|
|
4
4
|
import { define } from 'typed-dom/dom';
|
|
5
5
|
|
|
6
6
|
// Timeline(pseudonym): user/tid
|
|
@@ -21,5 +21,5 @@ export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>', fma
|
|
|
21
21
|
? `/@${source.slice(2).replace('/', '/timeline/')}`
|
|
22
22
|
: `?at=${source.slice(2)}`
|
|
23
23
|
} }`,
|
|
24
|
-
union([
|
|
24
|
+
union([unsafelink]))),
|
|
25
25
|
([el]) => [define(el, { class: 'anchor' })])));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { union, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
|
-
import {
|
|
3
|
+
import { unsafelink } from '../link';
|
|
4
4
|
import { emoji } from './hashtag';
|
|
5
5
|
import { str } from '../../source';
|
|
6
6
|
import { define } from 'typed-dom/dom';
|
|
@@ -9,5 +9,5 @@ export const hashnum: AutolinkParser.HashnumParser = lazy(() => fmap(rewrite(
|
|
|
9
9
|
open('#', str(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
|
|
10
10
|
convert(
|
|
11
11
|
source => `[${source}]{ ${source.slice(1)} }`,
|
|
12
|
-
union([
|
|
12
|
+
union([unsafelink]))),
|
|
13
13
|
([el]) => [define(el, { class: 'hashnum', href: null })]));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { union, tails, verify, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
|
-
import {
|
|
3
|
+
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { define } from 'typed-dom/dom';
|
|
6
6
|
|
|
@@ -31,5 +31,5 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
|
|
|
31
31
|
? `https://${source.slice(1).replace('/', '/hashtags/')}`
|
|
32
32
|
: `/hashtags/${source.slice(1)}`
|
|
33
33
|
} }`,
|
|
34
|
-
union([
|
|
34
|
+
union([unsafelink]))),
|
|
35
35
|
([el]) => [define(el, { class: 'hashtag' }, el.innerText)]));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { union, some, creation, precedence, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
|
|
3
|
-
import {
|
|
3
|
+
import { unsafelink } from '../link';
|
|
4
4
|
import { unescsource } from '../../source';
|
|
5
5
|
|
|
6
6
|
const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
|
|
@@ -11,7 +11,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
|
|
|
11
11
|
focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
|
|
12
12
|
convert(
|
|
13
13
|
url => `{ ${url} }`,
|
|
14
|
-
union([
|
|
14
|
+
union([unsafelink])))));
|
|
15
15
|
|
|
16
16
|
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(precedence(2, union([
|
|
17
17
|
surround('(', some(union([bracket, unescsource]), ')'), ')', true),
|
|
@@ -13,8 +13,8 @@ import IndexParser = ExtensionParser.IndexParser;
|
|
|
13
13
|
export const index: IndexParser = lazy(() => validate('[#', fmap(indexee(surround(
|
|
14
14
|
'[#',
|
|
15
15
|
constraint(State.index, false,
|
|
16
|
-
syntax(Syntax.index, 2, 1,
|
|
17
16
|
state(State.annotation | State.reference | State.index | State.label | State.link | State.media | State.autolink,
|
|
17
|
+
syntax(Syntax.index, 2, 1,
|
|
18
18
|
startTight(
|
|
19
19
|
open(stropt(/^\|?/), trimBlankEnd(some(union([
|
|
20
20
|
signature,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { MarkdownParser } from '../../../markdown';
|
|
3
|
+
import { LinkParser } from '../inline';
|
|
4
|
+
import { Result, eval, exec } from '../../combinator/data/parser';
|
|
5
|
+
import { union, inits, tails, sequence, some, constraint, syntax, creation, precedence, state, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
|
|
5
6
|
import { inline, media, shortmedia } from '../inline';
|
|
6
7
|
import { attributes } from './html';
|
|
7
8
|
import { autolink } from '../autolink';
|
|
@@ -17,81 +18,64 @@ const optspec = {
|
|
|
17
18
|
} as const;
|
|
18
19
|
Object.setPrototypeOf(optspec, null);
|
|
19
20
|
|
|
20
|
-
export const link: LinkParser = lazy(() => validate(['[', '{'],
|
|
21
|
+
export const link: LinkParser = lazy(() => validate(['[', '{'], union([
|
|
22
|
+
medialink,
|
|
23
|
+
textlink,
|
|
24
|
+
])));
|
|
25
|
+
|
|
26
|
+
const textlink: LinkParser.TextLinkParser = lazy(() =>
|
|
21
27
|
constraint(State.link, false,
|
|
28
|
+
state(State.link | State.media | State.annotation | State.reference | State.index | State.label | State.autolink,
|
|
22
29
|
syntax(Syntax.link, 2, 10,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'[',
|
|
30
|
-
state(State.annotation | State.reference | State.index | State.label | State.media | State.autolink,
|
|
31
|
-
some(inline, ']', [[/^\\?\n/, 9], [']', 2]])),
|
|
32
|
-
']',
|
|
33
|
-
true),
|
|
34
|
-
]))),
|
|
30
|
+
bind(reverse(tails([
|
|
31
|
+
dup(surround(
|
|
32
|
+
'[',
|
|
33
|
+
some(union([inline]), ']', [[/^\\?\n/, 9], [']', 2]]),
|
|
34
|
+
']',
|
|
35
|
+
true)),
|
|
35
36
|
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
36
|
-
]
|
|
37
|
-
([
|
|
38
|
-
|
|
39
|
-
assert(content[0] !== '' || params.length === 0);
|
|
40
|
-
if (content[0] === '') return [content, rest];
|
|
41
|
-
if (params.length === 0) return;
|
|
42
|
-
assert(params.every(p => typeof p === 'string'));
|
|
37
|
+
])),
|
|
38
|
+
([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
|
|
39
|
+
assert(!html('div', content).querySelector('a, .media, .annotation, .reference'));
|
|
43
40
|
if (content.length !== 0 && trimNode(content).length === 0) return;
|
|
44
41
|
for (let source = stringify(content); source;) {
|
|
45
|
-
const result = autolink(source, context);
|
|
42
|
+
const result = state(State.autolink, false, autolink)(source, context);
|
|
46
43
|
if (typeof eval(result!)[0] === 'object') return;
|
|
47
44
|
source = exec(result!);
|
|
48
45
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
return parse(content, params, rest, context);
|
|
47
|
+
})))));
|
|
48
|
+
|
|
49
|
+
const medialink: LinkParser.MediaLinkParser = lazy(() =>
|
|
50
|
+
constraint(State.link | State.media, false,
|
|
51
|
+
state(State.link | State.annotation | State.reference | State.index | State.label | State.autolink,
|
|
52
|
+
syntax(Syntax.link, 2, 10,
|
|
53
|
+
bind(reverse(sequence([
|
|
54
|
+
dup(surround(
|
|
55
|
+
'[',
|
|
56
|
+
union([media, shortmedia]),
|
|
57
|
+
']')),
|
|
58
|
+
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
59
|
+
])),
|
|
60
|
+
([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) =>
|
|
61
|
+
parse(content, params, rest, context))))));
|
|
64
62
|
|
|
65
|
-
export const
|
|
63
|
+
export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
|
|
66
64
|
creation(10, precedence(2,
|
|
67
|
-
reverse(tails([
|
|
68
|
-
dup(surround(
|
|
65
|
+
bind(reverse(tails([
|
|
66
|
+
dup(surround(
|
|
67
|
+
'[',
|
|
68
|
+
some(union([unescsource]), ']'),
|
|
69
|
+
']')),
|
|
69
70
|
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
70
|
-
]))
|
|
71
|
-
([params, content = []], rest, context) =>
|
|
72
|
-
|
|
73
|
-
params.shift();
|
|
74
|
-
assert(params.every(p => typeof p === 'string'));
|
|
75
|
-
trimNode(content);
|
|
76
|
-
const INSECURE_URI = params.shift()!;
|
|
77
|
-
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
78
|
-
assert(!INSECURE_URI.match(/\s/));
|
|
79
|
-
const el = elem(
|
|
80
|
-
INSECURE_URI,
|
|
81
|
-
defrag(content),
|
|
82
|
-
new ReadonlyURL(
|
|
83
|
-
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
84
|
-
context.host?.href || location.href),
|
|
85
|
-
context.host?.origin || location.origin);
|
|
86
|
-
assert(el.className !== 'invalid');
|
|
87
|
-
assert(el.classList.length === 0);
|
|
88
|
-
return [[define(el, attributes('link', [], optspec, params))], rest];
|
|
89
|
-
})));
|
|
71
|
+
])),
|
|
72
|
+
([params, content = []], rest, context) =>
|
|
73
|
+
parse(content, params, rest, context)))));
|
|
90
74
|
|
|
91
|
-
export const uri: LinkParser.ParameterParser.UriParser =
|
|
75
|
+
export const uri: LinkParser.ParameterParser.UriParser = union([
|
|
92
76
|
open(/^[^\S\n]+/, str(/^\S+/)),
|
|
93
77
|
str(/^[^\s{}]+/),
|
|
94
|
-
])
|
|
78
|
+
]);
|
|
95
79
|
|
|
96
80
|
export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
97
81
|
fmap(str(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]),
|
|
@@ -99,6 +83,29 @@ export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
|
99
83
|
fmap(str(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`]),
|
|
100
84
|
]);
|
|
101
85
|
|
|
86
|
+
function parse(
|
|
87
|
+
content: (string | HTMLElement)[],
|
|
88
|
+
params: string[],
|
|
89
|
+
rest: string,
|
|
90
|
+
context: MarkdownParser.Context,
|
|
91
|
+
): Result<HTMLAnchorElement, MarkdownParser.Context> {
|
|
92
|
+
assert(params.length > 0);
|
|
93
|
+
assert(params.every(p => typeof p === 'string'));
|
|
94
|
+
const INSECURE_URI = params.shift()!;
|
|
95
|
+
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
96
|
+
assert(!INSECURE_URI.match(/\s/));
|
|
97
|
+
const el = elem(
|
|
98
|
+
INSECURE_URI,
|
|
99
|
+
defrag(content),
|
|
100
|
+
new ReadonlyURL(
|
|
101
|
+
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
102
|
+
context.host?.href || location.href),
|
|
103
|
+
context.host?.origin || location.origin);
|
|
104
|
+
if (el.className === 'invalid') return [[el], rest];
|
|
105
|
+
assert(el.classList.length === 0);
|
|
106
|
+
return [[define(el, attributes('link', [], optspec, params))], rest];
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
export function resolve(uri: string, host: URL | Location, source: URL | Location): string {
|
|
103
110
|
assert(uri);
|
|
104
111
|
assert(uri === uri.trim());
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { undefined, location } from 'spica/global';
|
|
2
2
|
import { MediaParser } from '../inline';
|
|
3
3
|
import { union, inits, tails, some, syntax, creation, precedence, constraint, validate, verify, surround, open, dup, lazy, fmap, bind } from '../../combinator';
|
|
4
|
-
import {
|
|
4
|
+
import { unsafelink, uri, option as linkoption, resolve } from './link';
|
|
5
5
|
import { attributes } from './html';
|
|
6
6
|
import { unsafehtmlentity } from './htmlentity';
|
|
7
7
|
import { txt, str } from '../source';
|
|
8
8
|
import { Syntax, State } from '../context';
|
|
9
9
|
import { html, define } from 'typed-dom/dom';
|
|
10
10
|
import { ReadonlyURL } from 'spica/url';
|
|
11
|
-
import { unshift,
|
|
11
|
+
import { unshift, push } from 'spica/array';
|
|
12
12
|
|
|
13
13
|
const optspec = {
|
|
14
14
|
'width': [],
|
|
@@ -18,19 +18,19 @@ const optspec = {
|
|
|
18
18
|
} as const;
|
|
19
19
|
Object.setPrototypeOf(optspec, null);
|
|
20
20
|
|
|
21
|
-
export const media: MediaParser = lazy(() => validate(['![', '!{'],
|
|
21
|
+
export const media: MediaParser = lazy(() => validate(['![', '!{'], open(
|
|
22
22
|
'!',
|
|
23
23
|
constraint(State.media, false,
|
|
24
24
|
syntax(Syntax.media, 2, 10,
|
|
25
|
-
tails([
|
|
25
|
+
bind(verify(fmap(tails([
|
|
26
26
|
dup(surround(
|
|
27
27
|
'[',
|
|
28
28
|
some(union([unsafehtmlentity, bracket, txt]), ']', [[/^\\?\n/, 9]]),
|
|
29
29
|
']',
|
|
30
30
|
true)),
|
|
31
31
|
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
32
|
-
])
|
|
33
|
-
([as, bs]) => bs ? [[as.join('').trim() || as.join('')],
|
|
32
|
+
]),
|
|
33
|
+
([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]),
|
|
34
34
|
([[text]]) => text === '' || text.trim() !== ''),
|
|
35
35
|
([[text], params], rest, context) => {
|
|
36
36
|
assert(text === text.trim());
|
|
@@ -54,12 +54,13 @@ export const media: MediaParser = lazy(() => validate(['![', '!{'], bind(verify(
|
|
|
54
54
|
if (el.hasAttribute('aspect-ratio')) {
|
|
55
55
|
el.style.aspectRatio = el.getAttribute('aspect-ratio')!;
|
|
56
56
|
}
|
|
57
|
-
if (context.state! & State.link
|
|
57
|
+
if (context.state! & State.link) return [[el], rest];
|
|
58
|
+
if (cache && cache.tagName !== 'IMG') return creation(10, (..._) => [[el!], rest])('!', context);
|
|
58
59
|
return fmap(
|
|
59
|
-
|
|
60
|
+
unsafelink as MediaParser,
|
|
60
61
|
([link]) => [define(link, { target: '_blank' }, [el])])
|
|
61
62
|
(`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
|
|
62
|
-
})));
|
|
63
|
+
}))))));
|
|
63
64
|
|
|
64
65
|
const bracket: MediaParser.TextParser.BracketParser = lazy(() => creation(union([
|
|
65
66
|
surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
@@ -11,8 +11,8 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
11
11
|
export const reference: ReferenceParser = lazy(() => surround(
|
|
12
12
|
'[[',
|
|
13
13
|
constraint(State.reference, false,
|
|
14
|
-
syntax(Syntax.reference, 6, 1,
|
|
15
14
|
state(State.annotation | State.reference | State.media,
|
|
15
|
+
syntax(Syntax.reference, 6, 1,
|
|
16
16
|
startLoose(
|
|
17
17
|
context({ delimiters: undefined },
|
|
18
18
|
subsequence([
|
package/src/parser/inline.ts
CHANGED
|
@@ -34,7 +34,6 @@ export import MathParser = InlineParser.MathParser;
|
|
|
34
34
|
export import ExtensionParser = InlineParser.ExtensionParser;
|
|
35
35
|
export import RubyParser = InlineParser.RubyParser;
|
|
36
36
|
export import LinkParser = InlineParser.LinkParser;
|
|
37
|
-
export import TextLinkParser = InlineParser.TextLinkParser;
|
|
38
37
|
export import HTMLParser = InlineParser.HTMLParser;
|
|
39
38
|
export import InsertionParser = InlineParser.InsertionParser;
|
|
40
39
|
export import DeletionParser = InlineParser.DeletionParser;
|