securemark 0.258.6 → 0.258.9
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 +13 -5
- package/dist/index.js +74 -86
- package/markdown.d.ts +37 -20
- package/package.json +1 -1
- package/src/combinator/data/parser/context/memo.ts +1 -1
- package/src/combinator/data/parser/context.ts +1 -0
- package/src/parser/inline/annotation.test.ts +1 -1
- package/src/parser/inline/annotation.ts +2 -4
- 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/bracket.test.ts +1 -1
- package/src/parser/inline/extension/index.ts +1 -1
- package/src/parser/inline/extension/placeholder.ts +1 -1
- package/src/parser/inline/link.test.ts +2 -2
- package/src/parser/inline/link.ts +69 -84
- package/src/parser/inline/media.ts +10 -9
- package/src/parser/inline/reference.test.ts +1 -1
- package/src/parser/inline/reference.ts +2 -4
- package/src/parser/inline/template.test.ts +1 -1
- package/src/parser/inline/template.ts +1 -3
- package/src/parser/inline.test.ts +5 -5
- package/src/parser/inline.ts +0 -1
package/CHANGELOG.md
CHANGED
package/design.md
CHANGED
|
@@ -284,17 +284,25 @@ CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く
|
|
|
284
284
|
|
|
285
285
|
### バックトラック
|
|
286
286
|
|
|
287
|
-
SecuremarkのAnnotation
|
|
288
|
-
CommonMarkはLink
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
SecuremarkのAnnotation構文に典型的であるように文脈を変更する構文の中にその文脈に依存し変更される他の構文が存在する場合一般に文脈の相違から解析結果を再利用不能な(`αA'β | αAB`)バックトラックが生じる。文脈依存構文の解析中に文脈により解釈の異なる字句(`[a[`)または現在の文脈を終了する字句(ただの括弧がスコープを作らない場合の`(([a))`)に遭遇したとき、現在の文脈での解析の継続を試行する解析方法はメモ化なしではバックトラックが再帰的に生じ線形時間で解析できず、現在の文脈での解析を中止し直前の文脈を継続または新たな文脈を開始する解析方法はバックトラックなしで線形時間で解析できる。
|
|
288
|
+
CommonMarkはLink構文から明らかなように後者の解析方法により重篤なバックトラックなしで(インラインでは)ほぼ1回の走査で解析できる言語であるが同時にこれは文脈依存構文の正当な入れ子表現(`[<a@b>]`)を解析できないか不正な表現(`[<a@b>]()`)を除外できず二重リンク(`<a><a></a></a>`)を生成する、文脈依存構文の存在する数に応じて多項式的(おそらく$n^{2+c}$)に構文の壊れやすさの増す脆く拡張性の低い言語であることを意味する(構文の決定を遅延できれば解決できるがこれにより他の構文の解釈が非決定的にならない場合に限られる)。この問題はおそらくメモ化により解決できるがCommonMarkは実行性能追及のためメモ化を廃止しており二重リンクも放置しているためメモ化により性能を低下させてまで解決するつもりはないと思われる(すなわちCommonMarkは機械を至上とし人間に制約を課す低水準の言語であり人間の需要を至上とするSecuremarkとは対極に位置する)。
|
|
289
|
+
従ってほぼ1回の走査で解析可能な文法に制約されるCommonMarkには文脈依存構文を入れ子表現の広範な制限ならびに構文の可読性および開始記号の信頼性の多項式的な低下と引き換えにしか追加できないという拡張性の欠陥が存在する。CommonMarkの仕様策定者が構文の拡張に(名称を維持するか否かにかかわらず)不自然なまでに消極的または進展がないのは正当な理由や怠慢からでなく文脈依存構文を追加するにつれて構文解析戦略の欠陥が明白になっていくためおよび現在の高い実行性能を低下させたくないためである。`~~a~~`のような文脈自由構文は容易に追加できるがこうしたマージンを失えばもはや後はない。
|
|
290
|
+
Securemarkは線形時間で解析不能な前者の解析方法を各種最適化によりおおよそ4n以下の最悪計算量に改善しさらに解析時間と解析範囲の局限により一定時間内で解析不能な入力の影響を局限することでこれらの問題を解決している。この解析方法はほとんどの自然な入力に対して線形に近い時間で効率的に動作し、最悪計算量で低速に動作させる少数の機械的攻撃入力に対してもサーバーで多数のユーザーのリクエストに応じるには低速で脆弱性となる可能性があるがクライアントで単一のユーザーの操作に応じるには十分高速であるためクライアントで解析する限り解析の効率または速度が実用上問題となることはない。
|
|
291
|
+
|
|
292
|
+
### メモ化
|
|
293
|
+
|
|
294
|
+
一部の文脈依存言語を線形時間で解析できるようになるとしても状態数に応じて複数回走査が必要なこと、これにより時間効率が定性的には改善されても定量的には文脈自由言語より数倍悪いこと、オーバーヘッドが有意に大きいこと、時間の代わりに空間効率が非線形に悪化する可能性があることなどから理論上同じ線形時間だとしても実用上文脈自由言語と同等の実行性能にはならず文脈依存言語を文脈自由言語と等価の選択肢にするものではない。
|
|
295
|
+
|
|
296
|
+
### 最適化
|
|
297
|
+
|
|
298
|
+
現在Securemarkのボトルネックはプロファイルによると単なるパーサーの呼び出しが並んでおり引数の入力文字列を値渡しのためコピーするコストである可能性が疑われる。アルゴリズムとしてはメモ化したパース結果を複製せず永続データ構造として使用できるようおよび後処理に必要となるノードを収集しやすいようCSTを木構造に置き換える必要がある。またDOM出力の分離によりバックトラックのコストを削減する。
|
|
292
299
|
|
|
293
300
|
### 標準化
|
|
294
301
|
|
|
295
302
|
Markdownのように自然言語と自身の文法を分離する汎用構造がないメタ言語は構文を拡張する際に旧構文を破壊する新構文が自然言語の中に潜在せざるをえず後方互換性を保証することが不可能である。
|
|
296
303
|
このためCommonMarkは拡張構文を標準化した次期標準仕様との互換性を確保する役には立たずその有用性は構文の拡張を考慮しない範囲での効率的な実装方法の例示およびテストケースの集積による個別実装の支援ならびにその結果としての限定的互換性にとどまる。
|
|
297
304
|
さらにCommonMarkは前述の解析時間の制約から拡張性が低く、高度な構文や機能の実装可能性を考慮していないことから拡張仕様において準拠すべき技術的正当性もない。
|
|
305
|
+
実際GFMが二種類のフェンスドブロックのセマンティクスを分けずCommonMarkに準拠して単なるエイリアスとして有用な記号を浪費したのは顕著かつ明白な失敗だったと言えよう(Securemarkでは入力文字列の無変換表示(シンタックスハイライト)用と変換表示(拡張構文)用に分かれている)。
|
|
298
306
|
|
|
299
307
|
よってMarkdownの標準化は後方互換性確保が不可能であることから発展性がなくスナップショット以上の技術的意味を持たない。
|
|
300
308
|
MarkdownはGFMのように最初から高機能で完成度の高い拡張不要な独自実装のほうが標準としての互換性を確保でき、構文に曖昧さがない形式言語と異なりまず最小限の標準仕様を策定しのちに拡張していく通常の標準化方法が適さない特殊な言語である。
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.258.
|
|
1
|
+
/*! securemark v0.258.9 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 = {
|
|
@@ -3099,7 +3100,7 @@ class Memo {
|
|
|
3099
3100
|
|
|
3100
3101
|
for (let i = position + this.offset, len = memory.length; i < len; ++i) {
|
|
3101
3102
|
memory.pop();
|
|
3102
|
-
} //console.log('clear', position + 1);
|
|
3103
|
+
} //console.log('clear', position + this.offset + 1);
|
|
3103
3104
|
|
|
3104
3105
|
}
|
|
3105
3106
|
|
|
@@ -5694,25 +5695,23 @@ const combinator_1 = __webpack_require__(2087);
|
|
|
5694
5695
|
|
|
5695
5696
|
const inline_1 = __webpack_require__(1160);
|
|
5696
5697
|
|
|
5697
|
-
const link_1 = __webpack_require__(9628);
|
|
5698
|
-
|
|
5699
5698
|
const visibility_1 = __webpack_require__(7618);
|
|
5700
5699
|
|
|
5701
5700
|
const dom_1 = __webpack_require__(3252);
|
|
5702
5701
|
|
|
5703
5702
|
exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('((', (0, combinator_1.constraint)(64
|
|
5704
5703
|
/* State.annotation */
|
|
5705
|
-
, false, (0, combinator_1.
|
|
5706
|
-
/* Syntax.annotation */
|
|
5707
|
-
, 6, 1, (0, combinator_1.state)(64
|
|
5704
|
+
, false, (0, combinator_1.state)(64
|
|
5708
5705
|
/* State.annotation */
|
|
5709
5706
|
| 2
|
|
5710
5707
|
/* State.media */
|
|
5711
|
-
, (0,
|
|
5708
|
+
, (0, combinator_1.syntax)(32
|
|
5709
|
+
/* Syntax.annotation */
|
|
5710
|
+
, 6, 1, (0, visibility_1.startLoose)((0, combinator_1.context)({
|
|
5712
5711
|
delimiters: global_1.undefined
|
|
5713
5712
|
}, (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')')))), '))', false, ([, ns], rest) => [[(0, dom_1.html)('sup', {
|
|
5714
5713
|
class: 'annotation'
|
|
5715
|
-
}, [(0, dom_1.html)('span', (0, visibility_1.trimNode)((0, dom_1.defrag)(ns)))])], rest]
|
|
5714
|
+
}, [(0, dom_1.html)('span', (0, visibility_1.trimNode)((0, dom_1.defrag)(ns)))])], rest]));
|
|
5716
5715
|
|
|
5717
5716
|
/***/ }),
|
|
5718
5717
|
|
|
@@ -5779,7 +5778,7 @@ const source_1 = __webpack_require__(6743);
|
|
|
5779
5778
|
const dom_1 = __webpack_require__(3252); // https://example/@user must be a user page or a redirect page going there.
|
|
5780
5779
|
|
|
5781
5780
|
|
|
5782
|
-
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.
|
|
5781
|
+
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, {
|
|
5783
5782
|
class: 'account'
|
|
5784
5783
|
})]));
|
|
5785
5784
|
|
|
@@ -5808,7 +5807,7 @@ const dom_1 = __webpack_require__(3252); // Timeline(pseudonym): user/tid
|
|
|
5808
5807
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
5809
5808
|
|
|
5810
5809
|
|
|
5811
|
-
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.
|
|
5810
|
+
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, {
|
|
5812
5811
|
class: 'anchor'
|
|
5813
5812
|
})])));
|
|
5814
5813
|
|
|
@@ -5895,7 +5894,7 @@ const source_1 = __webpack_require__(6743);
|
|
|
5895
5894
|
|
|
5896
5895
|
const dom_1 = __webpack_require__(3252);
|
|
5897
5896
|
|
|
5898
|
-
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.
|
|
5897
|
+
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, {
|
|
5899
5898
|
class: 'hashnum',
|
|
5900
5899
|
href: null
|
|
5901
5900
|
})]));
|
|
@@ -5924,7 +5923,7 @@ const dom_1 = __webpack_require__(3252); // https://example/hashtags/a must be a
|
|
|
5924
5923
|
|
|
5925
5924
|
|
|
5926
5925
|
exports.emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
|
|
5927
|
-
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.
|
|
5926
|
+
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, {
|
|
5928
5927
|
class: 'hashtag'
|
|
5929
5928
|
}, el.innerText)]));
|
|
5930
5929
|
|
|
@@ -5948,7 +5947,7 @@ const link_1 = __webpack_require__(9628);
|
|
|
5948
5947
|
const source_1 = __webpack_require__(6743);
|
|
5949
5948
|
|
|
5950
5949
|
const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
|
|
5951
|
-
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.
|
|
5950
|
+
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])))));
|
|
5952
5951
|
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)]))));
|
|
5953
5952
|
|
|
5954
5953
|
/***/ }),
|
|
@@ -6254,9 +6253,7 @@ const dom_1 = __webpack_require__(3252);
|
|
|
6254
6253
|
|
|
6255
6254
|
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
|
|
6256
6255
|
/* State.index */
|
|
6257
|
-
, false, (0, combinator_1.
|
|
6258
|
-
/* Syntax.index */
|
|
6259
|
-
, 2, 1, (0, combinator_1.state)(64
|
|
6256
|
+
, false, (0, combinator_1.state)(64
|
|
6260
6257
|
/* State.annotation */
|
|
6261
6258
|
| 32
|
|
6262
6259
|
/* State.reference */
|
|
@@ -6270,7 +6267,9 @@ exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0
|
|
|
6270
6267
|
/* State.media */
|
|
6271
6268
|
| 1
|
|
6272
6269
|
/* State.autolink */
|
|
6273
|
-
, (0,
|
|
6270
|
+
, (0, combinator_1.syntax)(1024
|
|
6271
|
+
/* Syntax.index */
|
|
6272
|
+
, 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, {
|
|
6274
6273
|
id: el.id ? null : global_1.undefined,
|
|
6275
6274
|
class: 'index',
|
|
6276
6275
|
href: el.id ? `#${el.id}` : global_1.undefined
|
|
@@ -6467,7 +6466,7 @@ const visibility_1 = __webpack_require__(7618);
|
|
|
6467
6466
|
|
|
6468
6467
|
const dom_1 = __webpack_require__(3252);
|
|
6469
6468
|
|
|
6470
|
-
const array_1 = __webpack_require__(8112); // Don't use the symbols already used:
|
|
6469
|
+
const array_1 = __webpack_require__(8112); // Don't use the symbols already used: !#$%@&*+~=
|
|
6471
6470
|
// All syntax surrounded by square brackets shouldn't contain line breaks.
|
|
6472
6471
|
|
|
6473
6472
|
|
|
@@ -6651,7 +6650,7 @@ exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0,
|
|
|
6651
6650
|
Object.defineProperty(exports, "__esModule", ({
|
|
6652
6651
|
value: true
|
|
6653
6652
|
}));
|
|
6654
|
-
exports.
|
|
6653
|
+
exports.resolve = exports.option = exports.uri = exports.unsafelink = exports.link = void 0;
|
|
6655
6654
|
|
|
6656
6655
|
const global_1 = __webpack_require__(4128);
|
|
6657
6656
|
|
|
@@ -6665,8 +6664,6 @@ const html_1 = __webpack_require__(5994);
|
|
|
6665
6664
|
|
|
6666
6665
|
const autolink_1 = __webpack_require__(6578);
|
|
6667
6666
|
|
|
6668
|
-
const bracket_1 = __webpack_require__(5196);
|
|
6669
|
-
|
|
6670
6667
|
const source_1 = __webpack_require__(6743);
|
|
6671
6668
|
|
|
6672
6669
|
const visibility_1 = __webpack_require__(7618);
|
|
@@ -6681,13 +6678,14 @@ const optspec = {
|
|
|
6681
6678
|
rel: ['nofollow']
|
|
6682
6679
|
};
|
|
6683
6680
|
Object.setPrototypeOf(optspec, null);
|
|
6684
|
-
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.
|
|
6681
|
+
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.union)([medialink, textlink])));
|
|
6682
|
+
const textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4
|
|
6685
6683
|
/* State.link */
|
|
6686
|
-
, false, (0, combinator_1.
|
|
6687
|
-
/* Syntax.link */
|
|
6688
|
-
, 2, 10, (0, combinator_1.fmap)((0, combinator_1.subsequence)([(0, combinator_1.state)(4
|
|
6684
|
+
, false, (0, combinator_1.state)(4
|
|
6689
6685
|
/* State.link */
|
|
6690
|
-
|
|
6686
|
+
| 2
|
|
6687
|
+
/* State.media */
|
|
6688
|
+
| 64
|
|
6691
6689
|
/* State.annotation */
|
|
6692
6690
|
| 32
|
|
6693
6691
|
/* State.reference */
|
|
@@ -6695,35 +6693,56 @@ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'
|
|
|
6695
6693
|
/* State.index */
|
|
6696
6694
|
| 8
|
|
6697
6695
|
/* State.label */
|
|
6698
|
-
| 2
|
|
6699
|
-
/* State.media */
|
|
6700
6696
|
| 1
|
|
6701
6697
|
/* State.autolink */
|
|
6702
|
-
, (0, combinator_1.
|
|
6703
|
-
|
|
6704
|
-
|
|
6698
|
+
, (0, combinator_1.syntax)(256
|
|
6699
|
+
/* Syntax.link */
|
|
6700
|
+
, 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) => {
|
|
6705
6701
|
if (content.length !== 0 && (0, visibility_1.trimNode)(content).length === 0) return;
|
|
6706
6702
|
|
|
6707
6703
|
for (let source = (0, util_1.stringify)(content); source;) {
|
|
6708
|
-
const result = (0,
|
|
6704
|
+
const result = (0, combinator_1.state)(1
|
|
6705
|
+
/* State.autolink */
|
|
6706
|
+
, false, autolink_1.autolink)(source, context);
|
|
6709
6707
|
if (typeof (0, parser_1.eval)(result)[0] === 'object') return;
|
|
6710
6708
|
source = (0, parser_1.exec)(result);
|
|
6711
6709
|
}
|
|
6712
6710
|
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6711
|
+
return parse(content, params, rest, context);
|
|
6712
|
+
})))));
|
|
6713
|
+
const medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4
|
|
6714
|
+
/* State.link */
|
|
6715
|
+
| 2
|
|
6716
|
+
/* State.media */
|
|
6717
|
+
, false, (0, combinator_1.state)(4
|
|
6718
|
+
/* State.link */
|
|
6719
|
+
| 64
|
|
6720
|
+
/* State.annotation */
|
|
6721
|
+
| 32
|
|
6722
|
+
/* State.reference */
|
|
6723
|
+
| 16
|
|
6724
|
+
/* State.index */
|
|
6725
|
+
| 8
|
|
6726
|
+
/* State.label */
|
|
6727
|
+
| 1
|
|
6728
|
+
/* State.autolink */
|
|
6729
|
+
, (0, combinator_1.syntax)(256
|
|
6730
|
+
/* Syntax.link */
|
|
6731
|
+
, 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) => {
|
|
6732
|
+
return parse(content, params, rest, context);
|
|
6733
|
+
})))));
|
|
6734
|
+
exports.unsafelink = (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) => {
|
|
6735
|
+
return parse(content, params, rest, context);
|
|
6717
6736
|
})));
|
|
6718
|
-
exports.
|
|
6719
|
-
|
|
6720
|
-
|
|
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) {
|
|
6721
6741
|
const INSECURE_URI = params.shift();
|
|
6722
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);
|
|
6743
|
+
if (el.className === 'invalid') return [[el], rest];
|
|
6723
6744
|
return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
|
|
6724
|
-
}
|
|
6725
|
-
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]);
|
|
6726
|
-
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
|
+
}
|
|
6727
6746
|
|
|
6728
6747
|
function resolve(uri, host, source) {
|
|
6729
6748
|
switch (true) {
|
|
@@ -6799,34 +6818,6 @@ function decode(uri) {
|
|
|
6799
6818
|
}
|
|
6800
6819
|
}
|
|
6801
6820
|
|
|
6802
|
-
function optimize(opener, ns, rest, next, context) {
|
|
6803
|
-
if (next[+(next[0] === '\\')] === '\n') {
|
|
6804
|
-
if (rest[0] !== opener[0]) return;
|
|
6805
|
-
const delimiters = context.delimiters;
|
|
6806
|
-
delimiters?.push({
|
|
6807
|
-
signature: '!\n',
|
|
6808
|
-
matcher: source => !/^\\?\n/.test(source) && global_1.undefined,
|
|
6809
|
-
precedence: 9
|
|
6810
|
-
});
|
|
6811
|
-
const paired = (0, parser_1.eval)((0, combinator_1.state)(~0, bracket_1.bracket)(rest[0] + rest.slice(rest.search(`[^${rest[0]}]|$`)), context), [])[0] !== '';
|
|
6812
|
-
delimiters?.pop();
|
|
6813
|
-
if (paired) return;
|
|
6814
|
-
}
|
|
6815
|
-
|
|
6816
|
-
let count = 0;
|
|
6817
|
-
|
|
6818
|
-
for (let i = 0; i < ns.length - 1; i += 2) {
|
|
6819
|
-
const fst = ns[i];
|
|
6820
|
-
const snd = ns[i + 1];
|
|
6821
|
-
if (fst !== '' || snd[0] !== opener[0]) break;
|
|
6822
|
-
count += snd.length;
|
|
6823
|
-
}
|
|
6824
|
-
|
|
6825
|
-
return [['', opener[0].repeat(opener.length + count)], rest.slice(count)];
|
|
6826
|
-
}
|
|
6827
|
-
|
|
6828
|
-
exports.optimize = optimize;
|
|
6829
|
-
|
|
6830
6821
|
/***/ }),
|
|
6831
6822
|
|
|
6832
6823
|
/***/ 2480:
|
|
@@ -6931,11 +6922,11 @@ const optspec = {
|
|
|
6931
6922
|
rel: global_1.undefined
|
|
6932
6923
|
};
|
|
6933
6924
|
Object.setPrototypeOf(optspec, null);
|
|
6934
|
-
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
|
|
6935
6926
|
/* State.media */
|
|
6936
6927
|
, false, (0, combinator_1.syntax)(64
|
|
6937
6928
|
/* Syntax.media */
|
|
6938
|
-
, 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) => {
|
|
6939
6930
|
const INSECURE_URI = params.shift();
|
|
6940
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);
|
|
6941
6932
|
let cache;
|
|
@@ -6955,11 +6946,12 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '
|
|
|
6955
6946
|
|
|
6956
6947
|
if (context.state & 4
|
|
6957
6948
|
/* State.link */
|
|
6958
|
-
|
|
6959
|
-
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, {
|
|
6960
6952
|
target: '_blank'
|
|
6961
6953
|
}, [el])])(`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
|
|
6962
|
-
})));
|
|
6954
|
+
}))))));
|
|
6963
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)])));
|
|
6964
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]);
|
|
6965
6957
|
|
|
@@ -7022,8 +7014,6 @@ const combinator_1 = __webpack_require__(2087);
|
|
|
7022
7014
|
|
|
7023
7015
|
const inline_1 = __webpack_require__(1160);
|
|
7024
7016
|
|
|
7025
|
-
const link_1 = __webpack_require__(9628);
|
|
7026
|
-
|
|
7027
7017
|
const source_1 = __webpack_require__(6743);
|
|
7028
7018
|
|
|
7029
7019
|
const visibility_1 = __webpack_require__(7618);
|
|
@@ -7034,17 +7024,17 @@ const dom_1 = __webpack_require__(3252);
|
|
|
7034
7024
|
|
|
7035
7025
|
exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('[[', (0, combinator_1.constraint)(32
|
|
7036
7026
|
/* State.reference */
|
|
7037
|
-
, false, (0, combinator_1.
|
|
7038
|
-
/* Syntax.reference */
|
|
7039
|
-
, 6, 1, (0, combinator_1.state)(64
|
|
7027
|
+
, false, (0, combinator_1.state)(64
|
|
7040
7028
|
/* State.annotation */
|
|
7041
7029
|
| 32
|
|
7042
7030
|
/* State.reference */
|
|
7043
7031
|
| 2
|
|
7044
7032
|
/* State.media */
|
|
7045
|
-
, (0,
|
|
7033
|
+
, (0, combinator_1.syntax)(4096
|
|
7034
|
+
/* Syntax.reference */
|
|
7035
|
+
, 6, 1, (0, visibility_1.startLoose)((0, combinator_1.context)({
|
|
7046
7036
|
delimiters: global_1.undefined
|
|
7047
|
-
}, (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]
|
|
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]));
|
|
7048
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, '')]));
|
|
7049
7039
|
|
|
7050
7040
|
function attributes(ns) {
|
|
@@ -7233,8 +7223,6 @@ const global_1 = __webpack_require__(4128);
|
|
|
7233
7223
|
|
|
7234
7224
|
const combinator_1 = __webpack_require__(2087);
|
|
7235
7225
|
|
|
7236
|
-
const link_1 = __webpack_require__(9628);
|
|
7237
|
-
|
|
7238
7226
|
const source_1 = __webpack_require__(6743);
|
|
7239
7227
|
|
|
7240
7228
|
const dom_1 = __webpack_require__(3252);
|
|
@@ -7245,7 +7233,7 @@ exports.template = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('{{',
|
|
|
7245
7233
|
/* Syntax.none */
|
|
7246
7234
|
, 2, 1, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), '}')), '}}', true, ([, ns = []], rest) => [[(0, dom_1.html)('span', {
|
|
7247
7235
|
class: 'template'
|
|
7248
|
-
}, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]
|
|
7236
|
+
}, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]));
|
|
7249
7237
|
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)([bracket, source_1.escsource]), ')'), (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)([bracket, source_1.escsource]), ']'), (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)([bracket, source_1.escsource]), '}'), (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)(source_1.escsource, /^"|^\\?\n/)), (0, source_1.str)('"'), true)])));
|
|
7250
7238
|
|
|
7251
7239
|
/***/ }),
|
package/markdown.d.ts
CHANGED
|
@@ -660,7 +660,7 @@ export namespace MarkdownParser {
|
|
|
660
660
|
export interface AnnotationParser extends
|
|
661
661
|
// ((abc))
|
|
662
662
|
Inline<'annotation'>,
|
|
663
|
-
Parser<HTMLElement
|
|
663
|
+
Parser<HTMLElement, Context, [
|
|
664
664
|
InlineParser,
|
|
665
665
|
]> {
|
|
666
666
|
}
|
|
@@ -669,7 +669,7 @@ export namespace MarkdownParser {
|
|
|
669
669
|
// [[^abbr]]
|
|
670
670
|
// [[^abbr| abc]]
|
|
671
671
|
Inline<'reference'>,
|
|
672
|
-
Parser<HTMLElement
|
|
672
|
+
Parser<HTMLElement, Context, [
|
|
673
673
|
ReferenceParser.AbbrParser,
|
|
674
674
|
InlineParser,
|
|
675
675
|
InlineParser,
|
|
@@ -689,7 +689,7 @@ export namespace MarkdownParser {
|
|
|
689
689
|
export interface TemplateParser extends
|
|
690
690
|
// {{abc}}
|
|
691
691
|
Inline<'template'>,
|
|
692
|
-
Parser<HTMLSpanElement
|
|
692
|
+
Parser<HTMLSpanElement, Context, [
|
|
693
693
|
TemplateParser.BracketParser,
|
|
694
694
|
SourceParser.EscapableSourceParser,
|
|
695
695
|
]> {
|
|
@@ -842,21 +842,38 @@ export namespace MarkdownParser {
|
|
|
842
842
|
// { uri }
|
|
843
843
|
// [abc]{uri nofollow}
|
|
844
844
|
Inline<'link'>,
|
|
845
|
-
Parser<HTMLElement
|
|
846
|
-
LinkParser.
|
|
847
|
-
LinkParser.
|
|
848
|
-
]> {
|
|
849
|
-
}
|
|
850
|
-
export interface TextLinkParser extends
|
|
851
|
-
// { uri }
|
|
852
|
-
// [abc]{uri nofollow}
|
|
853
|
-
Inline<'textlink'>,
|
|
854
|
-
Parser<HTMLAnchorElement, Context, [
|
|
855
|
-
LinkParser.TextParser,
|
|
856
|
-
LinkParser.ParameterParser,
|
|
845
|
+
Parser<HTMLElement, Context, [
|
|
846
|
+
LinkParser.MediaLinkParser,
|
|
847
|
+
LinkParser.TextLinkParser,
|
|
857
848
|
]> {
|
|
858
849
|
}
|
|
859
850
|
export namespace LinkParser {
|
|
851
|
+
export interface TextLinkParser extends
|
|
852
|
+
Inline<'link/textlink'>,
|
|
853
|
+
Parser<HTMLElement, Context, [
|
|
854
|
+
Parser<(HTMLElement | string)[], Context, [
|
|
855
|
+
InlineParser,
|
|
856
|
+
]>,
|
|
857
|
+
LinkParser.ParameterParser,
|
|
858
|
+
]> {
|
|
859
|
+
}
|
|
860
|
+
export interface MediaLinkParser extends
|
|
861
|
+
Inline<'link/medialink'>,
|
|
862
|
+
Parser<HTMLElement, Context, [
|
|
863
|
+
Parser<HTMLElement[], Context, [
|
|
864
|
+
MediaParser,
|
|
865
|
+
ShortmediaParser,
|
|
866
|
+
]>,
|
|
867
|
+
LinkParser.ParameterParser,
|
|
868
|
+
]> {
|
|
869
|
+
}
|
|
870
|
+
export interface UnsafeLinkParser extends
|
|
871
|
+
Inline<'link/unsafelink'>,
|
|
872
|
+
Parser<HTMLAnchorElement, Context, [
|
|
873
|
+
LinkParser.TextParser,
|
|
874
|
+
LinkParser.ParameterParser,
|
|
875
|
+
]> {
|
|
876
|
+
}
|
|
860
877
|
export interface ContentParser extends
|
|
861
878
|
Inline<'link/content'>,
|
|
862
879
|
Parser<(HTMLElement | string)[], Context, [
|
|
@@ -1093,7 +1110,7 @@ export namespace MarkdownParser {
|
|
|
1093
1110
|
// https://host
|
|
1094
1111
|
Inline<'url'>,
|
|
1095
1112
|
Parser<HTMLAnchorElement, Context, [
|
|
1096
|
-
|
|
1113
|
+
LinkParser.UnsafeLinkParser,
|
|
1097
1114
|
]> {
|
|
1098
1115
|
}
|
|
1099
1116
|
export namespace UrlParser {
|
|
@@ -1135,28 +1152,28 @@ export namespace MarkdownParser {
|
|
|
1135
1152
|
// @user
|
|
1136
1153
|
Inline<'account'>,
|
|
1137
1154
|
Parser<HTMLAnchorElement, Context, [
|
|
1138
|
-
|
|
1155
|
+
LinkParser.UnsafeLinkParser,
|
|
1139
1156
|
]> {
|
|
1140
1157
|
}
|
|
1141
1158
|
export interface HashtagParser extends
|
|
1142
1159
|
// #tag
|
|
1143
1160
|
Inline<'hashtag'>,
|
|
1144
1161
|
Parser<HTMLAnchorElement, Context, [
|
|
1145
|
-
|
|
1162
|
+
LinkParser.UnsafeLinkParser,
|
|
1146
1163
|
]> {
|
|
1147
1164
|
}
|
|
1148
1165
|
export interface HashnumParser extends
|
|
1149
1166
|
// #1
|
|
1150
1167
|
Inline<'hashnum'>,
|
|
1151
1168
|
Parser<HTMLAnchorElement, Context, [
|
|
1152
|
-
|
|
1169
|
+
LinkParser.UnsafeLinkParser,
|
|
1153
1170
|
]> {
|
|
1154
1171
|
}
|
|
1155
1172
|
export interface AnchorParser extends
|
|
1156
1173
|
// >>1
|
|
1157
1174
|
Inline<'anchor'>,
|
|
1158
1175
|
Parser<HTMLAnchorElement, Context, [
|
|
1159
|
-
|
|
1176
|
+
LinkParser.UnsafeLinkParser,
|
|
1160
1177
|
]> {
|
|
1161
1178
|
}
|
|
1162
1179
|
}
|
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;
|
|
@@ -14,7 +14,7 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('(())')), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('(()))')), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('(( ))')), undefined);
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('(( (a')),
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('(( (a')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('((\\\na))')), undefined);
|
|
@@ -2,7 +2,6 @@ import { undefined } from 'spica/global';
|
|
|
2
2
|
import { AnnotationParser } from '../inline';
|
|
3
3
|
import { union, some, context, syntax, constraint, state, surround, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
|
-
import { optimize } from './link';
|
|
6
5
|
import { Syntax, State } from '../context';
|
|
7
6
|
import { startLoose, trimNode } from '../visibility';
|
|
8
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
@@ -10,12 +9,11 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
10
9
|
export const annotation: AnnotationParser = lazy(() => surround(
|
|
11
10
|
'((',
|
|
12
11
|
constraint(State.annotation, false,
|
|
13
|
-
syntax(Syntax.annotation, 6, 1,
|
|
14
12
|
state(State.annotation | State.media,
|
|
13
|
+
syntax(Syntax.annotation, 6, 1,
|
|
15
14
|
startLoose(
|
|
16
15
|
context({ delimiters: undefined },
|
|
17
16
|
some(union([inline]), ')', [[/^\\?\n/, 9], [')', 2], ['))', 6]])), ')')))),
|
|
18
17
|
'))',
|
|
19
18
|
false,
|
|
20
|
-
([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest]
|
|
21
|
-
([, ns, rest], next, context) => next[0] === ')' ? undefined : optimize('((', ns, rest, next, context)));
|
|
19
|
+
([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest]));
|
|
@@ -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),
|
|
@@ -75,7 +75,7 @@ describe('Unit: parser/inline/bracket', () => {
|
|
|
75
75
|
assert.deepStrictEqual(inspect(parser('"a')), [['"', 'a'], '']);
|
|
76
76
|
assert.deepStrictEqual(inspect(parser('"a"')), [['"', 'a', '"'], '']);
|
|
77
77
|
assert.deepStrictEqual(inspect(parser('"(")"')), [['"', '', '(', '"'], ')"']);
|
|
78
|
-
assert.deepStrictEqual(inspect(parser('"(("')), [['"', '', '((', '"'], '']);
|
|
78
|
+
assert.deepStrictEqual(inspect(parser('"(("')), [['"', '', '(', '', '(', '"'], '']);
|
|
79
79
|
assert.deepStrictEqual(inspect(parser('"(\\")"')), [['"', '<span class="paren">(")</span>', '"'], '']);
|
|
80
80
|
assert.deepStrictEqual(inspect(parser('"(\n)"')), [['"', '<span class="paren">(<br>)</span>', '"'], '']);
|
|
81
81
|
assert.deepStrictEqual(inspect(parser('"(\\\n)"')), [['"', '<span class="paren">(<span class="linebreak"> </span>)</span>', '"'], '']);
|
|
@@ -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,
|
|
@@ -7,7 +7,7 @@ import { startTight } from '../../visibility';
|
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
8
|
import { unshift } from 'spica/array';
|
|
9
9
|
|
|
10
|
-
// Don't use the symbols already used:
|
|
10
|
+
// Don't use the symbols already used: !#$%@&*+~=
|
|
11
11
|
|
|
12
12
|
// All syntax surrounded by square brackets shouldn't contain line breaks.
|
|
13
13
|
|
|
@@ -45,7 +45,7 @@ describe('Unit: parser/inline/link', () => {
|
|
|
45
45
|
it('invalid', () => {
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('{}')), undefined);
|
|
48
|
-
assert.deepStrictEqual(inspect(parser('[{b}')),
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('[{b}')), undefined);
|
|
49
49
|
assert.deepStrictEqual(inspect(parser('[]')), undefined);
|
|
50
50
|
assert.deepStrictEqual(inspect(parser('[]{}')), undefined);
|
|
51
51
|
assert.deepStrictEqual(inspect(parser('[]{ }')), undefined);
|
|
@@ -65,7 +65,7 @@ describe('Unit: parser/inline/link', () => {
|
|
|
65
65
|
assert.deepStrictEqual(inspect(parser('[\\ ]{b}')), undefined);
|
|
66
66
|
assert.deepStrictEqual(inspect(parser('[\\\n]{b}')), undefined);
|
|
67
67
|
assert.deepStrictEqual(inspect(parser('[	]{b}')), undefined);
|
|
68
|
-
assert.deepStrictEqual(inspect(parser('[[]{b}')),
|
|
68
|
+
assert.deepStrictEqual(inspect(parser('[[]{b}')), undefined);
|
|
69
69
|
assert.deepStrictEqual(inspect(parser('[]]{b}')), undefined);
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('[a]{}')), undefined);
|
|
71
71
|
assert.deepStrictEqual(inspect(parser('[a\nb]{b}')), undefined);
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
|
|
2
2
|
import { MarkdownParser } from '../../../markdown';
|
|
3
|
-
import { LinkParser
|
|
3
|
+
import { LinkParser } from '../inline';
|
|
4
4
|
import { Result, eval, exec } from '../../combinator/data/parser';
|
|
5
|
-
import { union, inits, tails,
|
|
5
|
+
import { union, inits, tails, sequence, some, constraint, syntax, creation, precedence, state, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
|
|
6
6
|
import { inline, media, shortmedia } from '../inline';
|
|
7
7
|
import { attributes } from './html';
|
|
8
8
|
import { autolink } from '../autolink';
|
|
9
|
-
import { bracket } from './bracket';
|
|
10
9
|
import { unescsource, str } from '../source';
|
|
11
10
|
import { Syntax, State } from '../context';
|
|
12
11
|
import { trimNode } from '../visibility';
|
|
@@ -19,83 +18,72 @@ const optspec = {
|
|
|
19
18
|
} as const;
|
|
20
19
|
Object.setPrototypeOf(optspec, null);
|
|
21
20
|
|
|
22
|
-
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(() =>
|
|
23
27
|
constraint(State.link, false,
|
|
28
|
+
state(State.link | State.media | State.annotation | State.reference | State.index | State.label | State.autolink,
|
|
24
29
|
syntax(Syntax.link, 2, 10,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'[',
|
|
32
|
-
state(State.annotation | State.reference | State.index | State.label | State.media | State.autolink,
|
|
33
|
-
some(inline, ']', [[/^\\?\n/, 9], [']', 2]])),
|
|
34
|
-
']',
|
|
35
|
-
true,
|
|
36
|
-
undefined,
|
|
37
|
-
([, ns = [], rest], next, context) => next[0] === ']' ? undefined : optimize('[', ns, rest, next, context)),
|
|
38
|
-
]))),
|
|
30
|
+
bind(reverse(tails([
|
|
31
|
+
dup(surround(
|
|
32
|
+
'[',
|
|
33
|
+
some(union([inline]), ']', [[/^\\?\n/, 9], [']', 2]]),
|
|
34
|
+
']',
|
|
35
|
+
true)),
|
|
39
36
|
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
40
|
-
]
|
|
41
|
-
([
|
|
42
|
-
|
|
43
|
-
assert(content[0] !== '' || params.length === 0);
|
|
44
|
-
if (content[0] === '') return [content, rest];
|
|
45
|
-
if (params.length === 0) return;
|
|
37
|
+
])),
|
|
38
|
+
([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
|
|
39
|
+
assert(params.length > 0);
|
|
46
40
|
assert(params.every(p => typeof p === 'string'));
|
|
47
41
|
if (content.length !== 0 && trimNode(content).length === 0) return;
|
|
48
42
|
for (let source = stringify(content); source;) {
|
|
49
|
-
const result = autolink(source, context);
|
|
43
|
+
const result = state(State.autolink, false, autolink)(source, context);
|
|
50
44
|
if (typeof eval(result!)[0] === 'object') return;
|
|
51
45
|
source = exec(result!);
|
|
52
46
|
}
|
|
53
|
-
assert(!html('div', content).querySelector('a, .media, .annotation, .reference')
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
assert(!INSECURE_URI.match(/\s/));
|
|
57
|
-
const el = elem(
|
|
58
|
-
INSECURE_URI,
|
|
59
|
-
defrag(content),
|
|
60
|
-
new ReadonlyURL(
|
|
61
|
-
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
62
|
-
context.host?.href || location.href),
|
|
63
|
-
context.host?.origin || location.origin);
|
|
64
|
-
if (el.className === 'invalid') return [[el], rest];
|
|
65
|
-
assert(el.classList.length === 0);
|
|
66
|
-
return [[define(el, attributes('link', [], optspec, params))], rest];
|
|
67
|
-
})));
|
|
47
|
+
assert(!html('div', content).querySelector('a, .media, .annotation, .reference'));
|
|
48
|
+
return parse(content, params, rest, context);
|
|
49
|
+
})))));
|
|
68
50
|
|
|
69
|
-
|
|
51
|
+
const medialink: LinkParser.MediaLinkParser = lazy(() =>
|
|
52
|
+
constraint(State.link | State.media, false,
|
|
53
|
+
state(State.link | State.annotation | State.reference | State.index | State.label | State.autolink,
|
|
54
|
+
syntax(Syntax.link, 2, 10,
|
|
55
|
+
bind(reverse(sequence([
|
|
56
|
+
dup(surround(
|
|
57
|
+
'[',
|
|
58
|
+
union([media, shortmedia]),
|
|
59
|
+
']')),
|
|
60
|
+
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
61
|
+
])),
|
|
62
|
+
([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
|
|
63
|
+
assert(params.length > 0);
|
|
64
|
+
assert(params.every(p => typeof p === 'string'));
|
|
65
|
+
assert(content.length === 1);
|
|
66
|
+
return parse(content, params, rest, context);
|
|
67
|
+
})))));
|
|
68
|
+
|
|
69
|
+
export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() => validate(['[', '{'], bind(
|
|
70
70
|
creation(10, precedence(2,
|
|
71
71
|
reverse(tails([
|
|
72
|
-
dup(surround(
|
|
72
|
+
dup(surround(
|
|
73
|
+
'[',
|
|
74
|
+
some(union([unescsource]), ']'),
|
|
75
|
+
']')),
|
|
73
76
|
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
|
|
74
77
|
])))),
|
|
75
78
|
([params, content = []], rest, context) => {
|
|
76
|
-
assert(params[0] === '\r');
|
|
77
|
-
params.shift();
|
|
78
79
|
assert(params.every(p => typeof p === 'string'));
|
|
79
|
-
|
|
80
|
-
const INSECURE_URI = params.shift()!;
|
|
81
|
-
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
82
|
-
assert(!INSECURE_URI.match(/\s/));
|
|
83
|
-
const el = elem(
|
|
84
|
-
INSECURE_URI,
|
|
85
|
-
defrag(content),
|
|
86
|
-
new ReadonlyURL(
|
|
87
|
-
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
88
|
-
context.host?.href || location.href),
|
|
89
|
-
context.host?.origin || location.origin);
|
|
90
|
-
assert(el.className !== 'invalid');
|
|
91
|
-
assert(el.classList.length === 0);
|
|
92
|
-
return [[define(el, attributes('link', [], optspec, params))], rest];
|
|
80
|
+
return parse(content, params, rest, context);
|
|
93
81
|
})));
|
|
94
82
|
|
|
95
|
-
export const uri: LinkParser.ParameterParser.UriParser =
|
|
83
|
+
export const uri: LinkParser.ParameterParser.UriParser = union([
|
|
96
84
|
open(/^[^\S\n]+/, str(/^\S+/)),
|
|
97
85
|
str(/^[^\s{}]+/),
|
|
98
|
-
])
|
|
86
|
+
]);
|
|
99
87
|
|
|
100
88
|
export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
101
89
|
fmap(str(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]),
|
|
@@ -103,6 +91,27 @@ export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
|
103
91
|
fmap(str(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`]),
|
|
104
92
|
]);
|
|
105
93
|
|
|
94
|
+
function parse(
|
|
95
|
+
content: (string | HTMLElement)[],
|
|
96
|
+
params: string[],
|
|
97
|
+
rest: string,
|
|
98
|
+
context: MarkdownParser.Context,
|
|
99
|
+
): Result<HTMLAnchorElement, MarkdownParser.Context> {
|
|
100
|
+
const INSECURE_URI = params.shift()!;
|
|
101
|
+
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
102
|
+
assert(!INSECURE_URI.match(/\s/));
|
|
103
|
+
const el = elem(
|
|
104
|
+
INSECURE_URI,
|
|
105
|
+
defrag(content),
|
|
106
|
+
new ReadonlyURL(
|
|
107
|
+
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
108
|
+
context.host?.href || location.href),
|
|
109
|
+
context.host?.origin || location.origin);
|
|
110
|
+
if (el.className === 'invalid') return [[el], rest];
|
|
111
|
+
assert(el.classList.length === 0);
|
|
112
|
+
return [[define(el, attributes('link', [], optspec, params))], rest];
|
|
113
|
+
}
|
|
114
|
+
|
|
106
115
|
export function resolve(uri: string, host: URL | Location, source: URL | Location): string {
|
|
107
116
|
assert(uri);
|
|
108
117
|
assert(uri === uri.trim());
|
|
@@ -193,27 +202,3 @@ function decode(uri: string): string {
|
|
|
193
202
|
return uri.replace(/\s+/g, encodeURI);
|
|
194
203
|
}
|
|
195
204
|
}
|
|
196
|
-
|
|
197
|
-
export function optimize(opener: string, ns: readonly (string | HTMLElement)[], rest: string, next: string, context: MarkdownParser.Context): Result<string> {
|
|
198
|
-
if (next[+(next[0] === '\\')] === '\n') {
|
|
199
|
-
if (rest[0] !== opener[0]) return;
|
|
200
|
-
const delimiters = context.delimiters;
|
|
201
|
-
delimiters?.push({
|
|
202
|
-
signature: '!\n',
|
|
203
|
-
matcher: source => !/^\\?\n/.test(source) && undefined,
|
|
204
|
-
precedence: 9,
|
|
205
|
-
});
|
|
206
|
-
const paired = eval(state(~0, bracket)(rest[0] + rest.slice(rest.search(`[^${rest[0]}]|$`)), context), [])[0] !== '';
|
|
207
|
-
delimiters?.pop();
|
|
208
|
-
if (paired) return;
|
|
209
|
-
}
|
|
210
|
-
let count = 0;
|
|
211
|
-
for (let i = 0; i < ns.length - 1; i += 2) {
|
|
212
|
-
const fst = ns[i];
|
|
213
|
-
const snd = ns[i + 1] as string;
|
|
214
|
-
assert(typeof snd === 'string');
|
|
215
|
-
if (fst !== '' || snd[0] !== opener[0]) break;
|
|
216
|
-
count += snd.length;
|
|
217
|
-
}
|
|
218
|
-
return [['', opener[0].repeat(opener.length + count)], rest.slice(count)];
|
|
219
|
-
}
|
|
@@ -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]),
|
|
@@ -14,7 +14,7 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('[[]]')), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('[[]]]')), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('[[ ]]')), undefined);
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('[[ [a')),
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('[[ [a')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('[[\n]]')), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('[[\\\na]]')), undefined);
|
|
@@ -2,7 +2,6 @@ import { undefined } from 'spica/global';
|
|
|
2
2
|
import { ReferenceParser } from '../inline';
|
|
3
3
|
import { union, subsequence, some, context, syntax, creation, constraint, state, surround, open, lazy, bind } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
|
-
import { optimize } from './link';
|
|
6
5
|
import { str, stropt } from '../source';
|
|
7
6
|
import { Syntax, State } from '../context';
|
|
8
7
|
import { regBlankStart, startLoose, trimNode } from '../visibility';
|
|
@@ -12,8 +11,8 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
12
11
|
export const reference: ReferenceParser = lazy(() => surround(
|
|
13
12
|
'[[',
|
|
14
13
|
constraint(State.reference, false,
|
|
15
|
-
syntax(Syntax.reference, 6, 1,
|
|
16
14
|
state(State.annotation | State.reference | State.media,
|
|
15
|
+
syntax(Syntax.reference, 6, 1,
|
|
17
16
|
startLoose(
|
|
18
17
|
context({ delimiters: undefined },
|
|
19
18
|
subsequence([
|
|
@@ -23,8 +22,7 @@ export const reference: ReferenceParser = lazy(() => surround(
|
|
|
23
22
|
])), ']')))),
|
|
24
23
|
']]',
|
|
25
24
|
false,
|
|
26
|
-
([, ns], rest) => [[html('sup', attributes(ns), [html('span', trimNode(defrag(ns)))])], rest]
|
|
27
|
-
([, ns, rest], next, context) => next[0] === ']' ? undefined : optimize('[[', ns, rest, next, context)));
|
|
25
|
+
([, ns], rest) => [[html('sup', attributes(ns), [html('span', trimNode(defrag(ns)))])], rest]));
|
|
28
26
|
|
|
29
27
|
const abbr: ReferenceParser.AbbrParser = creation(bind(surround(
|
|
30
28
|
'^',
|
|
@@ -10,7 +10,7 @@ describe('Unit: parser/inline/template', () => {
|
|
|
10
10
|
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
11
11
|
assert.deepStrictEqual(inspect(parser('{')), undefined);
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('{}')), undefined);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('{{')),
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('{{')), undefined);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('{{\\}}')), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('{{a}b}')), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('{{{a}}')), undefined);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { TemplateParser } from '../inline';
|
|
3
3
|
import { union, some, syntax, creation, precedence, surround, lazy } from '../../combinator';
|
|
4
|
-
import { optimize } from './link';
|
|
5
4
|
import { escsource, str } from '../source';
|
|
6
5
|
import { Syntax } from '../context';
|
|
7
6
|
import { html } from 'typed-dom/dom';
|
|
@@ -9,8 +8,7 @@ import { unshift } from 'spica/array';
|
|
|
9
8
|
|
|
10
9
|
export const template: TemplateParser = lazy(() => surround(
|
|
11
10
|
'{{', syntax(Syntax.none, 2, 1, some(union([bracket, escsource]), '}')), '}}', true,
|
|
12
|
-
([, ns = []], rest) => [[html('span', { class: 'template' }, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]
|
|
13
|
-
([, ns = [], rest], next, context) => next[0] === '}' ? undefined : optimize('{{', ns, rest, next, context)));
|
|
11
|
+
([, ns = []], rest) => [[html('span', { class: 'template' }, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]));
|
|
14
12
|
|
|
15
13
|
const bracket: TemplateParser.BracketParser = lazy(() => creation(union([
|
|
16
14
|
surround(str('('), some(union([bracket, escsource]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
|
|
@@ -143,23 +143,23 @@ describe('Unit: parser/inline', () => {
|
|
|
143
143
|
assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft')), [['Di', '$', 'ney', ' ', 'Micro', '$', 'oft'], '']);
|
|
144
144
|
assert.deepStrictEqual(inspect(parser('Di$ney, Micro$oft')), [['Di', '$', 'ney', ',', ' ', 'Micro', '$', 'oft'], '']);
|
|
145
145
|
assert.deepStrictEqual(inspect(parser('(((a))')), [['', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
|
|
146
|
-
assert.deepStrictEqual(inspect(parser('((((a))')), [['', '((', '<sup class="annotation"><span>a</span></sup>'], '']);
|
|
146
|
+
assert.deepStrictEqual(inspect(parser('((((a))')), [['', '(', '', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
|
|
147
147
|
assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
|
|
148
148
|
assert.deepStrictEqual(inspect(parser('((<bdi>))')), [['<sup class="annotation"><span><span class="invalid"><bdi></span></span></sup>'], '']);
|
|
149
|
-
assert.deepStrictEqual(inspect(parser('((${))}$')), [['', '((', '<span class="math" translate="no" data-src="${))}$">${))}$</span>'], '']);
|
|
149
|
+
assert.deepStrictEqual(inspect(parser('((${))}$')), [['', '(', '', '(', '<span class="math" translate="no" data-src="${))}$">${))}$</span>'], '']);
|
|
150
150
|
assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '<sup class="annotation"><span>""</span></sup>'], '']);
|
|
151
151
|
assert.deepStrictEqual(inspect(parser('[[[a]]')), [['', '[', '<sup class="reference"><span>a</span></sup>'], '']);
|
|
152
|
-
assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['', '[[', '<sup class="reference"><span>a</span></sup>'], '']);
|
|
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
155
|
assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a href="a">a</a></span></sup>'], '']);
|
|
156
156
|
assert.deepStrictEqual(inspect(parser('[[[a]{b}]]')), [['<sup class="reference"><span><a href="b">a</a></span></sup>'], '']);
|
|
157
157
|
assert.deepStrictEqual(inspect(parser('[(([a]{#}))]{#}')), [['<a 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"><bdi></span></span></sup>'], '']);
|
|
159
|
-
assert.deepStrictEqual(inspect(parser('[[${]]}$')), [['', '[[', '<span class="math" translate="no" data-src="${]]}$">${]]}$</span>'], '']);
|
|
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 href="c"><ruby>a<rp>(</rp><rt>b</rt><rp>)</rp></ruby></a>'], '']);
|
|
162
|
-
assert.deepStrictEqual(inspect(parser('[[[[[[[{a}')), [['', '[[[[[[[', '<a href="a">a</a>'], '']);
|
|
162
|
+
assert.deepStrictEqual(inspect(parser('[[[[[[[{a}')), [['', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '<a href="a">a</a>'], '']);
|
|
163
163
|
assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a href="http://host" target="_blank">http://host</a>', '>'], '']);
|
|
164
164
|
assert.deepStrictEqual(inspect(parser('[~http://host')), [['', '[', '~', '<a 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>'], '']);
|
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;
|