@peaceroad/markdown-it-figure-with-p-caption 0.12.0 → 0.13.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/README.md +10 -4
- package/index.js +227 -121
- package/package.json +8 -2
- package/.vscode/settings.json +0 -5
- package/test/examples-all-iframe-type-figure-class-name.txt +0 -192
- package/test/examples-console.txt +0 -125
- package/test/examples-has-num-class.txt +0 -31
- package/test/examples-iframe-type-blockquote-without-caption.txt +0 -92
- package/test/examples-iframe-without-caption.txt +0 -64
- package/test/examples-img-alt-caption-number.en.txt +0 -51
- package/test/examples-img-alt-caption.en.txt +0 -60
- package/test/examples-img-alt-caption.ja.txt +0 -84
- package/test/examples-img-title-caption-number.en.txt +0 -60
- package/test/examples-img-title-caption.en.txt +0 -60
- package/test/examples-img-title-caption.ja.txt +0 -30
- package/test/examples-multiple-images.txt +0 -140
- package/test/examples-no-option.txt +0 -770
- package/test/examples-one-image-without-caption.txt +0 -59
- package/test/examples-set-figure-number.en.txt +0 -21
- package/test/examples-video-without-caption.txt +0 -52
- package/test/test.js +0 -208
package/README.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This is a markdown-it plugin.
|
|
4
4
|
|
|
5
|
-
For
|
|
5
|
+
For paragraphs containing only images, tables, code blocks, blockquotes, or iframes, this plugin converts them into figure elements with figcaption elements when a caption paragraph is written immediately before or after.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The conversion process:
|
|
8
|
+
1. Detect supported elements: image paragraphs, tables, code/samp blocks, blockquotes, videos, and iframes
|
|
9
|
+
2. Check for caption paragraphs immediately before or after the element
|
|
10
|
+
3. Convert both elements into a figure with figcaption structure
|
|
10
11
|
|
|
11
12
|
The figcaption behavior of this plugin depends on [p7d-markdown-it-p-captions](https://www.npmjs.com/package/p7d-markdown-it-p-captions).
|
|
12
13
|
|
|
@@ -36,6 +37,11 @@ Also, It is recommended to set the width and height attributes of the images at
|
|
|
36
37
|
|
|
37
38
|
It could be applied to table, codeblock(pre > code, pre > samp), video as well.
|
|
38
39
|
|
|
40
|
+
These elements are also supported within the following structure. [0.13.0+]
|
|
41
|
+
|
|
42
|
+
- Blockquote
|
|
43
|
+
- loose list (with blank lines between items), not tight list (no blank lines)
|
|
44
|
+
- Description list block (`<dl>` markup, markdown-it-deflist)
|
|
39
45
|
|
|
40
46
|
## Example
|
|
41
47
|
|
package/index.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import { setCaptionParagraph } from 'p7d-markdown-it-p-captions'
|
|
2
2
|
import { imgAttrToPCaption, setAltToLabel, setTitleToLabel } from './imgAttrToPCaption.js'
|
|
3
3
|
|
|
4
|
-
const htmlRegCache =
|
|
4
|
+
const htmlRegCache = new Map()
|
|
5
5
|
const classReg = /^f-(.+)$/
|
|
6
6
|
const blueskyEmbedReg = /^<blockquote class="bluesky-embed"[^]*?>[\s\S]*?$/
|
|
7
|
+
const videoIframeReg = /^<[^>]*? src="https:\/\/(?:www.youtube-nocookie.com|player.vimeo.com)\//i
|
|
8
|
+
const classNameReg = /^<[^>]*? class="(twitter-tweet|instagram-media|text-post-media|bluesky-embed|mastodon-embed)"/
|
|
9
|
+
const imageAttrsReg = /^ *\{(.*?)\} *$/
|
|
10
|
+
const classAttrReg = /^\./
|
|
11
|
+
const idAttrReg = /^#/
|
|
12
|
+
const attrParseReg = /^(.*?)="?(.*)"?$/
|
|
13
|
+
const whitespaceReg = /^ *$/
|
|
14
|
+
const sampLangReg = /^ *(?:samp|shell|console)(?:(?= )|$)/
|
|
15
|
+
const endBlockquoteScriptReg = /<\/blockquote> *<script[^>]*?><\/script>$/
|
|
7
16
|
|
|
8
17
|
const getHtmlReg = (tag) => {
|
|
9
|
-
if (htmlRegCache
|
|
10
|
-
|
|
18
|
+
if (htmlRegCache.has(tag)) return htmlRegCache.get(tag)
|
|
19
|
+
const regexStr = `^<${tag} ?[^>]*?>[\\s\\S]*?<\\/${tag}>(\\n| *?)(<script [^>]*?>(?:<\\/script>)?)? *(\\n|$)`
|
|
11
20
|
const reg = new RegExp(regexStr)
|
|
12
|
-
htmlRegCache
|
|
21
|
+
htmlRegCache.set(tag, reg)
|
|
13
22
|
return reg
|
|
14
23
|
}
|
|
15
24
|
|
|
@@ -26,13 +35,13 @@ const getCaptionName = (token) => {
|
|
|
26
35
|
return ''
|
|
27
36
|
}
|
|
28
37
|
|
|
29
|
-
const checkPrevCaption = (
|
|
38
|
+
const checkPrevCaption = (tokens, n, caption, fNum, sp, opt, TokenConstructor) => {
|
|
30
39
|
if(n < 3) return caption
|
|
31
|
-
const captionStartToken =
|
|
32
|
-
const captionEndToken =
|
|
40
|
+
const captionStartToken = tokens[n-3]
|
|
41
|
+
const captionEndToken = tokens[n-1]
|
|
33
42
|
if (captionStartToken === undefined || captionEndToken === undefined) return
|
|
34
43
|
if (captionStartToken.type !== 'paragraph_open' && captionEndToken.type !== 'paragraph_close') return
|
|
35
|
-
setCaptionParagraph(n-3,
|
|
44
|
+
setCaptionParagraph(n-3, { tokens, Token: TokenConstructor }, caption, fNum, sp, opt)
|
|
36
45
|
const captionName = getCaptionName(captionStartToken)
|
|
37
46
|
if(!captionName) return
|
|
38
47
|
caption.name = captionName
|
|
@@ -40,13 +49,13 @@ const checkPrevCaption = (state, n, caption, fNum, sp, opt) => {
|
|
|
40
49
|
return
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
const checkNextCaption = (
|
|
44
|
-
if (en + 2 >
|
|
45
|
-
const captionStartToken =
|
|
46
|
-
const captionEndToken =
|
|
52
|
+
const checkNextCaption = (tokens, en, caption, fNum, sp, opt, TokenConstructor) => {
|
|
53
|
+
if (en + 2 > tokens.length) return
|
|
54
|
+
const captionStartToken = tokens[en+1]
|
|
55
|
+
const captionEndToken = tokens[en+3]
|
|
47
56
|
if (captionStartToken === undefined || captionEndToken === undefined) return
|
|
48
57
|
if (captionStartToken.type !== 'paragraph_open' && captionEndToken.type !== 'paragraph_close') return
|
|
49
|
-
setCaptionParagraph(en+1,
|
|
58
|
+
setCaptionParagraph(en+1, { tokens, Token: TokenConstructor }, caption, fNum, sp, opt)
|
|
50
59
|
const captionName = getCaptionName(captionStartToken)
|
|
51
60
|
if(!captionName) return
|
|
52
61
|
caption.name = captionName
|
|
@@ -54,9 +63,15 @@ const checkNextCaption = (state, en, caption, fNum, sp, opt) => {
|
|
|
54
63
|
return
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
const cleanCaptionRegCache = new Map()
|
|
67
|
+
|
|
57
68
|
const cleanCaptionTokenAttrs = (token, captionName) => {
|
|
58
|
-
const reg = new RegExp(' *?f-' + captionName)
|
|
59
69
|
if (!token.attrs) return
|
|
70
|
+
let reg = cleanCaptionRegCache.get(captionName)
|
|
71
|
+
if (!reg) {
|
|
72
|
+
reg = new RegExp(' *?f-' + captionName)
|
|
73
|
+
cleanCaptionRegCache.set(captionName, reg)
|
|
74
|
+
}
|
|
60
75
|
for (let i = token.attrs.length - 1; i >= 0; i--) {
|
|
61
76
|
if (token.attrs[i][0] === 'class') {
|
|
62
77
|
token.attrs[i][1] = token.attrs[i][1].replace(reg, '').trim()
|
|
@@ -65,10 +80,10 @@ const cleanCaptionTokenAttrs = (token, captionName) => {
|
|
|
65
80
|
}
|
|
66
81
|
}
|
|
67
82
|
|
|
68
|
-
const changePrevCaptionPosition = (
|
|
69
|
-
const captionStartToken =
|
|
70
|
-
const captionInlineToken =
|
|
71
|
-
const captionEndToken =
|
|
83
|
+
const changePrevCaptionPosition = (tokens, n, caption, opt) => {
|
|
84
|
+
const captionStartToken = tokens[n-3]
|
|
85
|
+
const captionInlineToken = tokens[n-2]
|
|
86
|
+
const captionEndToken = tokens[n-1]
|
|
72
87
|
|
|
73
88
|
if (opt.imgAltCaption || opt.imgTitleCaption) {
|
|
74
89
|
let isNoCaption = false
|
|
@@ -83,7 +98,7 @@ const changePrevCaptionPosition = (state, n, caption, opt) => {
|
|
|
83
98
|
}
|
|
84
99
|
}
|
|
85
100
|
if (isNoCaption) {
|
|
86
|
-
|
|
101
|
+
tokens.splice(n-3, 3)
|
|
87
102
|
return false
|
|
88
103
|
}
|
|
89
104
|
}
|
|
@@ -93,29 +108,29 @@ const changePrevCaptionPosition = (state, n, caption, opt) => {
|
|
|
93
108
|
captionStartToken.tag = 'figcaption'
|
|
94
109
|
captionEndToken.type = 'figcaption_close'
|
|
95
110
|
captionEndToken.tag = 'figcaption'
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
tokens.splice(n + 2, 0, captionStartToken, captionInlineToken, captionEndToken)
|
|
112
|
+
tokens.splice(n-3, 3)
|
|
98
113
|
return true
|
|
99
114
|
}
|
|
100
115
|
|
|
101
|
-
const changeNextCaptionPosition = (
|
|
102
|
-
const captionStartToken =
|
|
103
|
-
const captionInlineToken =
|
|
104
|
-
const captionEndToken =
|
|
116
|
+
const changeNextCaptionPosition = (tokens, en, caption) => {
|
|
117
|
+
const captionStartToken = tokens[en+2] // +1: text node for figure.
|
|
118
|
+
const captionInlineToken = tokens[en+3]
|
|
119
|
+
const captionEndToken = tokens[en+4]
|
|
105
120
|
cleanCaptionTokenAttrs(captionStartToken, caption.name)
|
|
106
121
|
captionStartToken.type = 'figcaption_open'
|
|
107
122
|
captionStartToken.tag = 'figcaption'
|
|
108
123
|
captionEndToken.type = 'figcaption_close'
|
|
109
124
|
captionEndToken.tag = 'figcaption'
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
tokens.splice(en, 0, captionStartToken, captionInlineToken, captionEndToken)
|
|
126
|
+
tokens.splice(en+5, 3)
|
|
112
127
|
return true
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
const wrapWithFigure = (
|
|
130
|
+
const wrapWithFigure = (tokens, range, checkTokenTagName, caption, replaceInsteadOfWrap, sp, opt, TokenConstructor) => {
|
|
116
131
|
let n = range.start
|
|
117
132
|
let en = range.end
|
|
118
|
-
const figureStartToken = new
|
|
133
|
+
const figureStartToken = new TokenConstructor('figure_open', 'figure', 1)
|
|
119
134
|
figureStartToken.attrSet('class', 'f-' + checkTokenTagName)
|
|
120
135
|
|
|
121
136
|
if (opt.allIframeTypeFigureClassName === '') {
|
|
@@ -143,8 +158,8 @@ const wrapWithFigure = (state, range, checkTokenTagName, caption, replaceInstead
|
|
|
143
158
|
if(/pre-(?:code|samp)/.test(checkTokenTagName) && opt.roleDocExample) {
|
|
144
159
|
figureStartToken.attrSet('role', 'doc-example')
|
|
145
160
|
}
|
|
146
|
-
const figureEndToken = new
|
|
147
|
-
const breakToken = new
|
|
161
|
+
const figureEndToken = new TokenConstructor('figure_close', 'figure', -1)
|
|
162
|
+
const breakToken = new TokenConstructor('text', '', 0)
|
|
148
163
|
breakToken.content = '\n'
|
|
149
164
|
if (opt.styleProcess && caption.isNext && sp.attrs.length > 0) {
|
|
150
165
|
for (let attr of sp.attrs) {
|
|
@@ -152,18 +167,18 @@ const wrapWithFigure = (state, range, checkTokenTagName, caption, replaceInstead
|
|
|
152
167
|
}
|
|
153
168
|
}
|
|
154
169
|
// For vsce
|
|
155
|
-
if(
|
|
156
|
-
for (let attr of
|
|
170
|
+
if(tokens[n].attrs && caption.name === 'img') {
|
|
171
|
+
for (let attr of tokens[n].attrs) {
|
|
157
172
|
figureStartToken.attrJoin(attr[0], attr[1])
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
175
|
if (replaceInsteadOfWrap) {
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
tokens.splice(en, 1, breakToken, figureEndToken, breakToken)
|
|
177
|
+
tokens.splice(n, 1, figureStartToken, breakToken)
|
|
163
178
|
en = en + 2
|
|
164
179
|
} else {
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
tokens.splice(en+1, 0, figureEndToken, breakToken)
|
|
181
|
+
tokens.splice(n, 0, figureStartToken, breakToken)
|
|
167
182
|
en = en + 3
|
|
168
183
|
}
|
|
169
184
|
range.start = n
|
|
@@ -171,46 +186,101 @@ const wrapWithFigure = (state, range, checkTokenTagName, caption, replaceInstead
|
|
|
171
186
|
return
|
|
172
187
|
}
|
|
173
188
|
|
|
174
|
-
const checkCaption = (
|
|
175
|
-
checkPrevCaption(
|
|
189
|
+
const checkCaption = (tokens, n, en, caption, fNum, sp, opt, TokenConstructor) => {
|
|
190
|
+
checkPrevCaption(tokens, n, caption, fNum, sp, opt, TokenConstructor)
|
|
176
191
|
if (caption.isPrev) return
|
|
177
|
-
checkNextCaption(
|
|
192
|
+
checkNextCaption(tokens, en, caption, fNum, sp, opt, TokenConstructor)
|
|
178
193
|
return
|
|
179
194
|
}
|
|
180
195
|
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
const checkTypes = ['table', 'pre', 'blockquote']
|
|
184
|
-
const htmlTags = ['video', 'audio', 'iframe', 'blockquote', 'div']
|
|
196
|
+
const processTokensRecursively = (tokens, opt, fNum, TokenConstructor, parentType) => {
|
|
197
|
+
const nestedContainers = ['blockquote', 'list_item', 'dd']
|
|
185
198
|
|
|
186
|
-
|
|
199
|
+
figureWithCaptionCore(tokens, opt, fNum, TokenConstructor, parentType)
|
|
200
|
+
|
|
201
|
+
const nestedRanges = []
|
|
202
|
+
let i = 0
|
|
203
|
+
while (i < tokens.length) {
|
|
204
|
+
const token = tokens[i]
|
|
205
|
+
let containerType = null
|
|
206
|
+
for (const container of nestedContainers) {
|
|
207
|
+
if (token.type === `${container}_open`) {
|
|
208
|
+
containerType = container
|
|
209
|
+
break
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (containerType) {
|
|
213
|
+
let depth = 1
|
|
214
|
+
let endIndex = i + 1
|
|
215
|
+
while (endIndex < tokens.length && depth > 0) {
|
|
216
|
+
if (tokens[endIndex].type === `${containerType}_open`) depth++
|
|
217
|
+
if (tokens[endIndex].type === `${containerType}_close`) depth--
|
|
218
|
+
endIndex++
|
|
219
|
+
}
|
|
220
|
+
if (depth === 0 && endIndex - i > 2) {
|
|
221
|
+
nestedRanges.push({
|
|
222
|
+
start: i + 1,
|
|
223
|
+
end: endIndex - 1,
|
|
224
|
+
type: containerType
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
i = endIndex
|
|
228
|
+
} else {
|
|
229
|
+
i++
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
for (let j = nestedRanges.length - 1; j >= 0; j--) {
|
|
234
|
+
const range = nestedRanges[j]
|
|
235
|
+
const innerTokens = tokens.slice(range.start, range.end)
|
|
236
|
+
if (innerTokens.length > 0) {
|
|
237
|
+
processTokensRecursively(innerTokens, opt, fNum, TokenConstructor, range.type)
|
|
238
|
+
tokens.splice(range.start, range.end - range.start, ...innerTokens)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const figureWithCaption = (state, opt) => {
|
|
187
244
|
let fNum = {
|
|
188
245
|
img: 0,
|
|
189
246
|
table: 0,
|
|
190
247
|
}
|
|
248
|
+
|
|
249
|
+
processTokensRecursively(state.tokens, opt, fNum, state.Token, null)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const figureWithCaptionCore = (tokens, opt, fNum, TokenConstructor, parentType) => {
|
|
253
|
+
const checkTypes = ['table', 'pre', 'blockquote']
|
|
254
|
+
const htmlTags = ['video', 'audio', 'iframe', 'blockquote', 'div']
|
|
255
|
+
|
|
256
|
+
const rRange = { start: 0, end: 0 }
|
|
257
|
+
const rCaption = {
|
|
258
|
+
mark: '', name: '', nameSuffix: '', isPrev: false, isNext: false
|
|
259
|
+
}
|
|
260
|
+
const rSp = {
|
|
261
|
+
attrs: [], isVideoIframe: false, isIframeTypeBlockquote: false, hasImgCaption: false
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let n = 0
|
|
191
265
|
while (n < tokens.length) {
|
|
192
266
|
const token = tokens[n]
|
|
193
267
|
const nextToken = tokens[n+1]
|
|
194
268
|
let en = n
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
269
|
+
|
|
270
|
+
rRange.start = n
|
|
271
|
+
rRange.end = en
|
|
199
272
|
let checkToken = false
|
|
200
273
|
let checkTokenTagName = ''
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
isIframeTypeBlockquote: false,
|
|
212
|
-
hasImgCaption: false,
|
|
213
|
-
}
|
|
274
|
+
rCaption.mark = ''
|
|
275
|
+
rCaption.name = ''
|
|
276
|
+
rCaption.nameSuffix = ''
|
|
277
|
+
rCaption.isPrev = false
|
|
278
|
+
rCaption.isNext = false
|
|
279
|
+
|
|
280
|
+
rSp.attrs.length = 0
|
|
281
|
+
rSp.isVideoIframe = false
|
|
282
|
+
rSp.isIframeTypeBlockquote = false
|
|
283
|
+
rSp.hasImgCaption = false
|
|
214
284
|
|
|
215
285
|
let cti = 0
|
|
216
286
|
while (cti < checkTypes.length) {
|
|
@@ -221,11 +291,11 @@ const figureWithCaption = (state, opt) => {
|
|
|
221
291
|
}
|
|
222
292
|
checkToken = true
|
|
223
293
|
checkTokenTagName = token.tag
|
|
224
|
-
|
|
294
|
+
rCaption.name = checkTypes[cti]
|
|
225
295
|
if (checkTypes[cti] === 'pre') {
|
|
226
|
-
if (tokens[n+1].tag === 'code')
|
|
227
|
-
if (tokens[n+1].tag === 'samp')
|
|
228
|
-
|
|
296
|
+
if (tokens[n+1].tag === 'code') rCaption.mark = 'pre-code'
|
|
297
|
+
if (tokens[n+1].tag === 'samp') rCaption.mark = 'pre-samp'
|
|
298
|
+
rCaption.name = rCaption.mark
|
|
229
299
|
}
|
|
230
300
|
while (en < tokens.length) {
|
|
231
301
|
if(tokens[en].type === checkTokenTagName + '_close') {
|
|
@@ -233,10 +303,10 @@ const figureWithCaption = (state, opt) => {
|
|
|
233
303
|
}
|
|
234
304
|
en++
|
|
235
305
|
}
|
|
236
|
-
|
|
237
|
-
checkCaption(
|
|
238
|
-
if (
|
|
239
|
-
wrapWithFigure(
|
|
306
|
+
rRange.end = en
|
|
307
|
+
checkCaption(tokens, n, en, rCaption, fNum, rSp, opt, TokenConstructor)
|
|
308
|
+
if (rCaption.isPrev || rCaption.isNext) {
|
|
309
|
+
wrapWithFigure(tokens, rRange, checkTokenTagName, rCaption, false, rSp, opt, TokenConstructor)
|
|
240
310
|
}
|
|
241
311
|
break
|
|
242
312
|
}
|
|
@@ -245,20 +315,20 @@ const figureWithCaption = (state, opt) => {
|
|
|
245
315
|
if (token.tag === 'code' && token.block) {
|
|
246
316
|
checkToken = true
|
|
247
317
|
let isSamp = false
|
|
248
|
-
if (
|
|
318
|
+
if (sampLangReg.test(token.info)) {
|
|
249
319
|
token.tag = 'samp'
|
|
250
320
|
isSamp = true
|
|
251
321
|
}
|
|
252
322
|
if (isSamp) {
|
|
253
323
|
checkTokenTagName = 'pre-samp'
|
|
254
|
-
|
|
324
|
+
rCaption.name = 'pre-samp'
|
|
255
325
|
} else {
|
|
256
326
|
checkTokenTagName = 'pre-code'
|
|
257
|
-
|
|
327
|
+
rCaption.name = 'pre-code'
|
|
258
328
|
}
|
|
259
|
-
checkCaption(
|
|
260
|
-
if (
|
|
261
|
-
wrapWithFigure(
|
|
329
|
+
checkCaption(tokens, n, en, rCaption, fNum, rSp, opt, TokenConstructor)
|
|
330
|
+
if (rCaption.isPrev || rCaption.isNext) {
|
|
331
|
+
wrapWithFigure(tokens, rRange, checkTokenTagName, rCaption, false, rSp, opt, TokenConstructor)
|
|
262
332
|
break
|
|
263
333
|
}
|
|
264
334
|
}
|
|
@@ -275,7 +345,7 @@ const figureWithCaption = (state, opt) => {
|
|
|
275
345
|
// for vimeo
|
|
276
346
|
hasTag = token.content.match(getHtmlReg('div'))
|
|
277
347
|
htmlTags[ctj] = 'iframe'
|
|
278
|
-
|
|
348
|
+
rSp.isVideoIframe = true
|
|
279
349
|
} else {
|
|
280
350
|
hasTag = token.content.match(getHtmlReg(htmlTags[ctj]))
|
|
281
351
|
}
|
|
@@ -296,7 +366,7 @@ const figureWithCaption = (state, opt) => {
|
|
|
296
366
|
let hasEndBlockquote = true
|
|
297
367
|
while (j < tokensLength) {
|
|
298
368
|
const nextToken = tokens[j]
|
|
299
|
-
if (nextToken.type === 'inline' &&
|
|
369
|
+
if (nextToken.type === 'inline' && endBlockquoteScriptReg.test(nextToken.content)) {
|
|
300
370
|
addedCont += nextToken.content + '\n'
|
|
301
371
|
if (tokens[j + 1] && tokens[j + 1].type === 'paragraph_close') {
|
|
302
372
|
tokens.splice(j + 1, 1)
|
|
@@ -322,13 +392,12 @@ const figureWithCaption = (state, opt) => {
|
|
|
322
392
|
}
|
|
323
393
|
|
|
324
394
|
checkTokenTagName = htmlTags[ctj]
|
|
325
|
-
|
|
395
|
+
rCaption.name = htmlTags[ctj]
|
|
326
396
|
checkToken = true
|
|
327
397
|
if (checkTokenTagName === 'blockquote') {
|
|
328
|
-
const classNameReg = /^<[^>]*? class="(twitter-tweet|instagram-media|text-post-media|bluesky-embed|mastodon-embed)"/
|
|
329
398
|
const isIframeTypeBlockquote = token.content.match(classNameReg)
|
|
330
399
|
if(isIframeTypeBlockquote) {
|
|
331
|
-
|
|
400
|
+
rSp.isIframeTypeBlockquote = true
|
|
332
401
|
} else {
|
|
333
402
|
ctj++
|
|
334
403
|
continue
|
|
@@ -338,19 +407,19 @@ const figureWithCaption = (state, opt) => {
|
|
|
338
407
|
}
|
|
339
408
|
if (!checkToken) {n++; continue;}
|
|
340
409
|
if (checkTokenTagName === 'iframe') {
|
|
341
|
-
if(
|
|
342
|
-
|
|
410
|
+
if(videoIframeReg.test(token.content)) {
|
|
411
|
+
rSp.isVideoIframe = true
|
|
343
412
|
}
|
|
344
413
|
}
|
|
345
414
|
|
|
346
|
-
checkCaption(
|
|
347
|
-
if (
|
|
348
|
-
wrapWithFigure(
|
|
415
|
+
checkCaption(tokens, n, en, rCaption, fNum, rSp, opt, TokenConstructor)
|
|
416
|
+
if (rCaption.isPrev || rCaption.isNext) {
|
|
417
|
+
wrapWithFigure(tokens, rRange, checkTokenTagName, rCaption, false, rSp, opt, TokenConstructor)
|
|
349
418
|
n = en + 2
|
|
350
419
|
} else if ((opt.iframeWithoutCaption && (checkTokenTagName === 'iframe')) ||
|
|
351
420
|
(opt.videoWithoutCaption && (checkTokenTagName === 'video')) ||
|
|
352
421
|
(opt.iframeTypeBlockquoteWithoutCaption && (checkTokenTagName === 'blockquote'))) {
|
|
353
|
-
wrapWithFigure(
|
|
422
|
+
wrapWithFigure(tokens, rRange, checkTokenTagName, rCaption, false, rSp, opt, TokenConstructor)
|
|
354
423
|
n = en + 2
|
|
355
424
|
}
|
|
356
425
|
}
|
|
@@ -361,30 +430,30 @@ const figureWithCaption = (state, opt) => {
|
|
|
361
430
|
let isMultipleImagesHorizontal = true
|
|
362
431
|
let isMultipleImagesVertical = true
|
|
363
432
|
checkToken = true
|
|
364
|
-
|
|
433
|
+
rCaption.name = 'img'
|
|
365
434
|
const children = nextToken.children
|
|
366
435
|
const childrenLength = children.length
|
|
367
436
|
while (ntChildTokenIndex < childrenLength) {
|
|
368
437
|
const ntChildToken = children[ntChildTokenIndex]
|
|
369
438
|
if (ntChildTokenIndex === childrenLength - 1) {
|
|
370
|
-
|
|
439
|
+
let imageAttrs = ntChildToken.content.match(imageAttrsReg)
|
|
371
440
|
if(ntChildToken.type === 'text' && imageAttrs) {
|
|
372
441
|
imageAttrs = imageAttrs[1].split(/ +/)
|
|
373
442
|
let iai = 0
|
|
374
443
|
const attrsLength = imageAttrs.length
|
|
375
444
|
while (iai < attrsLength) {
|
|
376
|
-
if (
|
|
377
|
-
imageAttrs[iai] = imageAttrs[iai].replace(
|
|
445
|
+
if (classAttrReg.test(imageAttrs[iai])) {
|
|
446
|
+
imageAttrs[iai] = imageAttrs[iai].replace(classAttrReg, "class=")
|
|
378
447
|
}
|
|
379
|
-
if (
|
|
380
|
-
imageAttrs[iai] = imageAttrs[iai].replace(
|
|
448
|
+
if (idAttrReg.test(imageAttrs[iai])) {
|
|
449
|
+
imageAttrs[iai] = imageAttrs[iai].replace(idAttrReg, "id=")
|
|
381
450
|
}
|
|
382
|
-
let imageAttr = imageAttrs[iai].match(
|
|
451
|
+
let imageAttr = imageAttrs[iai].match(attrParseReg)
|
|
383
452
|
if (!imageAttr || !imageAttr[1]) {
|
|
384
453
|
iai++
|
|
385
454
|
continue
|
|
386
455
|
}
|
|
387
|
-
|
|
456
|
+
rSp.attrs.push([imageAttr[1], imageAttr[2]])
|
|
388
457
|
iai++
|
|
389
458
|
}
|
|
390
459
|
break
|
|
@@ -397,7 +466,7 @@ const figureWithCaption = (state, opt) => {
|
|
|
397
466
|
}
|
|
398
467
|
if (ntChildToken.type === 'image') {
|
|
399
468
|
imageNum += 1
|
|
400
|
-
} else if (ntChildToken.type === 'text' &&
|
|
469
|
+
} else if (ntChildToken.type === 'text' && whitespaceReg.test(ntChildToken.content)) {
|
|
401
470
|
isMultipleImagesVertical = false
|
|
402
471
|
if (isMultipleImagesVertical) {
|
|
403
472
|
isMultipleImagesHorizontal = false
|
|
@@ -415,50 +484,58 @@ const figureWithCaption = (state, opt) => {
|
|
|
415
484
|
}
|
|
416
485
|
if (checkToken && imageNum > 1 && opt.multipleImages) {
|
|
417
486
|
if (isMultipleImagesHorizontal) {
|
|
418
|
-
|
|
487
|
+
rCaption.nameSuffix = '-horizontal'
|
|
419
488
|
} else if (isMultipleImagesVertical) {
|
|
420
|
-
|
|
489
|
+
rCaption.nameSuffix = '-vertical'
|
|
421
490
|
} else {
|
|
422
|
-
|
|
491
|
+
rCaption.nameSuffix = '-multiple'
|
|
423
492
|
}
|
|
424
493
|
ntChildTokenIndex = 0
|
|
425
494
|
while (ntChildTokenIndex < childrenLength) {
|
|
426
495
|
const ccToken = children[ntChildTokenIndex]
|
|
427
|
-
if (ccToken.type === 'text' &&
|
|
496
|
+
if (ccToken.type === 'text' && whitespaceReg.test(ccToken.content)) {
|
|
428
497
|
ccToken.content = ''
|
|
429
498
|
}
|
|
430
499
|
ntChildTokenIndex++
|
|
431
500
|
}
|
|
432
501
|
}
|
|
433
502
|
en = n + 2
|
|
434
|
-
|
|
503
|
+
rRange.end = en
|
|
435
504
|
checkTokenTagName = 'img'
|
|
436
505
|
nextToken.children[0].type = 'image'
|
|
437
506
|
|
|
438
|
-
if (opt.imgAltCaption) setAltToLabel(
|
|
439
|
-
if (opt.imgTitleCaption) setTitleToLabel(
|
|
440
|
-
checkCaption(
|
|
507
|
+
if (opt.imgAltCaption) setAltToLabel({ tokens, Token: TokenConstructor }, n)
|
|
508
|
+
if (opt.imgTitleCaption) setTitleToLabel({ tokens, Token: TokenConstructor }, n)
|
|
509
|
+
checkCaption(tokens, n, en, rCaption, fNum, rSp, opt, TokenConstructor)
|
|
441
510
|
|
|
442
|
-
if (
|
|
443
|
-
|
|
511
|
+
if (parentType === 'list_item' || isInListItem(tokens, n)) {
|
|
512
|
+
const isInTightList = token.hidden === true
|
|
513
|
+
if (isInTightList) {
|
|
514
|
+
checkToken = false
|
|
515
|
+
} else {
|
|
516
|
+
if (!opt.oneImageWithoutCaption && !rCaption.isPrev && !rCaption.isNext) {
|
|
517
|
+
checkToken = false
|
|
518
|
+
}
|
|
519
|
+
}
|
|
444
520
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
521
|
+
|
|
522
|
+
if (checkToken && (opt.oneImageWithoutCaption || rCaption.isPrev || rCaption.isNext)) {
|
|
523
|
+
if (rCaption.nameSuffix) checkTokenTagName += rCaption.nameSuffix
|
|
524
|
+
wrapWithFigure(tokens, rRange, checkTokenTagName, rCaption, true, rSp, opt, TokenConstructor)
|
|
448
525
|
}
|
|
449
526
|
}
|
|
450
527
|
|
|
451
|
-
if (!checkToken || !
|
|
528
|
+
if (!checkToken || !rCaption.name) {n++; continue;}
|
|
452
529
|
|
|
453
|
-
n =
|
|
454
|
-
en =
|
|
455
|
-
if (
|
|
456
|
-
changePrevCaptionPosition(
|
|
530
|
+
n = rRange.start
|
|
531
|
+
en = rRange.end
|
|
532
|
+
if (rCaption.isPrev) {
|
|
533
|
+
changePrevCaptionPosition(tokens, n, rCaption, opt)
|
|
457
534
|
n = en + 1
|
|
458
535
|
continue
|
|
459
536
|
}
|
|
460
|
-
if (
|
|
461
|
-
changeNextCaptionPosition(
|
|
537
|
+
if (rCaption.isNext) {
|
|
538
|
+
changeNextCaptionPosition(tokens, en, rCaption)
|
|
462
539
|
n = en + 4
|
|
463
540
|
continue
|
|
464
541
|
}
|
|
@@ -467,6 +544,36 @@ const figureWithCaption = (state, opt) => {
|
|
|
467
544
|
return
|
|
468
545
|
}
|
|
469
546
|
|
|
547
|
+
const isInListItem = (() => {
|
|
548
|
+
const cache = new WeakMap()
|
|
549
|
+
return (tokens, idx) => {
|
|
550
|
+
if (cache.has(tokens)) {
|
|
551
|
+
const cachedResult = cache.get(tokens)
|
|
552
|
+
if (cachedResult[idx] !== undefined) {
|
|
553
|
+
return cachedResult[idx]
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
cache.set(tokens, {})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const result = cache.get(tokens)
|
|
560
|
+
|
|
561
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
562
|
+
if (tokens[i].type === 'list_item_open') {
|
|
563
|
+
result[idx] = true
|
|
564
|
+
return true
|
|
565
|
+
}
|
|
566
|
+
if (tokens[i].type === 'list_item_close' || tokens[i].type === 'list_open') {
|
|
567
|
+
result[idx] = false
|
|
568
|
+
return false
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
result[idx] = false
|
|
573
|
+
return false
|
|
574
|
+
}
|
|
575
|
+
})()
|
|
576
|
+
|
|
470
577
|
const mditFigureWithPCaption = (md, option) => {
|
|
471
578
|
let opt = {
|
|
472
579
|
classPrefix: 'f',
|
|
@@ -499,10 +606,9 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
499
606
|
opt.oneImageWithoutCaption = true
|
|
500
607
|
opt.multipleImages = false
|
|
501
608
|
if (opt.setFigureNumber) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
609
|
+
opt.removeUnnumberedLabelExceptMarks = opt.removeUnnumberedLabelExceptMarks.filter(
|
|
610
|
+
mark => mark !== 'img' && mark !== 'table'
|
|
611
|
+
)
|
|
506
612
|
}
|
|
507
613
|
md.block.ruler.before('paragraph', 'img_attr_caption', (state) => {
|
|
508
614
|
imgAttrToPCaption(state, state.line, opt)
|
|
@@ -515,4 +621,4 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
515
621
|
})
|
|
516
622
|
}
|
|
517
623
|
|
|
518
|
-
export default mditFigureWithPCaption
|
|
624
|
+
export default mditFigureWithPCaption
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-figure-with-p-caption",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A markdown-it plugin. For a paragraph with only one image, a table or code block or blockquote, and by writing a caption paragraph immediately before or after, they are converted into the figure element with the figcaption element.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,5 +24,11 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"p7d-markdown-it-p-captions": "^0.17.0"
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"index.js",
|
|
30
|
+
"imgAttrToPCaption.js",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
]
|
|
28
34
|
}
|