@peaceroad/markdown-it-figure-with-p-caption 0.8.1 → 0.9.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/index.js CHANGED
@@ -1,573 +1,555 @@
1
1
  import mditPCaption from 'p7d-markdown-it-p-captions'
2
2
 
3
- const mditFigureWithPCaption = (md, option) => {
4
-
5
- let opt = {
6
- classPrefix: 'f',
7
- figureClassThatWrapsIframeTypeBlockquote: 'f-img',
8
- styleProcess : true,
9
- hasNumClass: false,
10
- scaleSuffix: false,
11
- dquoteFilename: false,
12
- strongFilename: false,
13
- bLabel: false,
14
- strongLabel: false,
15
- jointSpaceUseHalfWidth: false,
16
- oneImageWithoutCaption: false,
17
- iframeWithoutCaption: false,
18
- videoWithoutCaption: false,
19
- iframeTypeBlockquoteWithoutCaption: false,
20
- removeUnnumberedLabel: false,
21
- removeUnnumberedLabelExceptMarks: [],
22
- multipleImages: true,
23
- imgAltCaption: false,
24
- imgTitleCaption: false,
25
- roleDocExample: false,
26
- };
27
- if (option !== undefined) {
28
- for (let o in option) {
29
- opt[o] = option[o];
30
- }
3
+ const checkPrevCaption = (state, n, caption) => {
4
+ if(n < 3) return caption
5
+ const captionStartToken = state.tokens[n-3]
6
+ const captionEndToken = state.tokens[n-1]
7
+ if (captionStartToken === undefined || captionEndToken === undefined) return caption
8
+
9
+ if (captionStartToken.type !== 'paragraph_open' && captionEndToken.type !== 'paragraph_close') return caption
10
+ let captionName = ''
11
+ if (captionStartToken.attrs) {
12
+ captionStartToken.attrs.forEach(attr => {
13
+ let hasCaptionName = attr[1].match(/^f-(.+)$/)
14
+ if (attr[0] === 'class' && hasCaptionName) captionName = hasCaptionName[1]
15
+ })
31
16
  }
17
+ if(!captionName) return caption
18
+ caption.name = captionName
19
+ caption.hasPrev = true
20
+ return caption
21
+ }
32
22
 
33
- function checkPrevCaption(state, n, en, tagName, caption) {
34
- if(n < 3) {return caption;}
35
- const captionStartToken = state.tokens[n-3];
36
- const captionEndToken = state.tokens[n-1];
37
- if (captionStartToken === undefined || captionEndToken === undefined) {
38
- return caption;
39
- }
40
- if (captionStartToken.type !== 'paragraph_open'
41
- && captionEndToken.type !== 'paragraph_close') {
42
- return caption;
23
+ const changePrevCaptionPosition = (state, n, caption) => {
24
+ const captionStartToken = state.tokens[n-3]
25
+ const captionInlineToken = state.tokens[n-2]
26
+ const captionEndToken = state.tokens[n-1]
27
+ let isNoCaption = false
28
+ if (captionInlineToken.attrs) {
29
+ for (let attr of captionInlineToken.attrs) {
30
+ if (attr[0] === 'class' && attr[1] === 'nocaption') isNoCaption = true
43
31
  }
44
- let captionName = '';
45
- if (captionStartToken.attrs) {
46
- captionStartToken.attrs.forEach(attr => {
47
- let hasCaptionName = attr[1].match(/^f-(.+)$/);
48
- if (attr[0] === 'class' && hasCaptionName) {
49
- captionName = hasCaptionName[1];
50
- }
51
- });
52
- }
53
- if(!captionName) {return caption;}
54
- caption.name = captionName;
55
- caption.hasPrev = true;
56
- return caption;
32
+ }
33
+ if (isNoCaption) {
34
+ state.tokens.splice(n-3, 3)
35
+ return
57
36
  }
58
37
 
59
- function changePrevCaptionPosition(state, n, en, tagName, caption) {
60
- const captionStartToken = state.tokens[n-3];
61
- const captionInlineToken = state.tokens[n-2];
62
- const captionEndToken = state.tokens[n-1];
63
- let isNoCaption = false
64
- if (captionInlineToken.attrs) {
65
- for (let attr of captionInlineToken.attrs) {
66
- if (attr[0] === 'class' && attr[1] === 'nocaption') {
67
- isNoCaption = true
68
- }
38
+ captionStartToken.attrs.forEach(attr => {
39
+ if (attr[0] === 'class') {
40
+ attr[1] = attr[1].replace(new RegExp(' *?f-' + caption.name), '').trim()
41
+ if(attr[1] === '') {
42
+ captionStartToken.attrs.splice(captionStartToken.attrIndex('class'), 1)
69
43
  }
70
44
  }
71
- if (isNoCaption) {
72
- state.tokens.splice(n-3, 3)
73
- return
74
- }
75
-
76
- captionStartToken.attrs.forEach(attr => {
77
- if (attr[0] === 'class') {
78
- attr[1] = attr[1].replace(new RegExp(' *?f-' + caption.name), '').trim();
79
- if(attr[1] === '') {
80
- captionStartToken.attrs.splice(captionStartToken.attrIndex('class'), 1);
81
- }
82
- }
83
- });
84
- captionStartToken.type = 'figcaption_open';
85
- captionStartToken.tag = 'figcaption';
86
-
87
- captionEndToken.type = 'figcaption_close';
88
- captionEndToken.tag = 'figcaption';
89
- state.tokens.splice(n + 2, 0, captionStartToken, captionInlineToken, captionEndToken);
90
- state.tokens.splice(n-3, 3);
91
- return true;
92
- }
45
+ })
46
+ captionStartToken.type = 'figcaption_open'
47
+ captionStartToken.tag = 'figcaption'
48
+ captionEndToken.type = 'figcaption_close'
49
+ captionEndToken.tag = 'figcaption'
50
+ state.tokens.splice(n + 2, 0, captionStartToken, captionInlineToken, captionEndToken)
51
+ state.tokens.splice(n-3, 3)
52
+ return true
53
+ }
93
54
 
94
- function checkNextCaption(state, n, en, tagName, caption) {
95
- if (en + 2 > state.tokens.length) { return caption; }
96
- const captionStartToken = state.tokens[en+1];
97
- const captionEndToken = state.tokens[en+3];
98
- if (captionStartToken === undefined || captionEndToken === undefined) {
99
- return caption;
100
- }
101
- if (captionStartToken.type !== 'paragraph_open'
102
- && captionEndToken.type !== 'paragraph_close') {
103
- return caption;
104
- }
105
- let captionName = '';
106
- if (captionStartToken.attrs) {
107
- captionStartToken.attrs.forEach(attr => {
108
- let hasCaptionName = attr[1].match(/^f-(.+)$/);
109
- if (attr[0] === 'class' && hasCaptionName) {
110
- captionName = hasCaptionName[1];
111
- }
112
- });
113
- }
114
- if(!captionName) { return caption; }
115
- caption.name = captionName;
116
- caption.hasNext = true;
117
- return caption;
118
- }
55
+ const checkNextCaption = (state, en, caption) => {
56
+ if (en + 2 > state.tokens.length) return caption
57
+ const captionStartToken = state.tokens[en+1]
58
+ const captionEndToken = state.tokens[en+3]
59
+ if (captionStartToken === undefined || captionEndToken === undefined) return caption
60
+ if (captionStartToken.type !== 'paragraph_open' && captionEndToken.type !== 'paragraph_close') return caption
119
61
 
120
- function changeNextCaptionPosition(state, n, en, tagName, caption) {
121
- const captionStartToken = state.tokens[en+2]; // +1: text node for figure.
122
- const captionInlineToken = state.tokens[en+3];
123
- const captionEndToken = state.tokens[en+4];
62
+ let captionName = ''
63
+ if (captionStartToken.attrs) {
124
64
  captionStartToken.attrs.forEach(attr => {
125
- if (attr[0] === 'class') {
126
- attr[1] = attr[1].replace(new RegExp(' *?f-' + caption.name), '').trim();
127
- if(attr[1] === '') {
128
- captionStartToken.attrs.splice(captionStartToken.attrIndex('class'), 1);
129
- }
130
- }
131
- });
132
- captionStartToken.type = 'figcaption_open';
133
- captionStartToken.tag = 'figcaption';
134
-
135
- captionEndToken.type = 'figcaption_close';
136
- captionEndToken.tag = 'figcaption';
137
-
138
- state.tokens.splice(en, 0, captionStartToken, captionInlineToken, captionEndToken);
139
- state.tokens.splice(en+5, 3);
140
- return true;
65
+ let hasCaptionName = attr[1].match(/^f-(.+)$/)
66
+ if (attr[0] === 'class' && hasCaptionName) captionName = hasCaptionName[1]
67
+ })
141
68
  }
69
+ if(!captionName) return caption
70
+ caption.name = captionName
71
+ caption.hasNext = true
72
+ return caption
73
+ }
142
74
 
143
- const wrapWithFigure = (state, range, tagName, caption, replaceInsteadOfWrap, sp) => {
144
- let n = range.start;
145
- let en = range.end;
146
- const figureStartToken = new state.Token('figure_open', 'figure', 1);
147
- figureStartToken.attrSet('class', 'f-' + tagName);
148
- if (sp.isVideoIframe) {
149
- figureStartToken.attrSet('class', 'f-video');
150
- }
151
- if (sp.isIframeTypeBlockQuote) {
152
- let figureClassThatWrapsIframeTypeBlockquote = 'i-frame'
153
- if (caption.prev || caption.next) {
154
- if (caption.name === 'img') {
155
- figureClassThatWrapsIframeTypeBlockquote = 'f-img'
156
- }
157
- figureStartToken.attrSet('class', figureClassThatWrapsIframeTypeBlockquote)
158
- } else {
159
- console.log('else::')
160
- figureClassThatWrapsIframeTypeBlockquote = opt.figureClassThatWrapsIframeTypeBlockquote
161
- figureStartToken.attrSet('class', figureClassThatWrapsIframeTypeBlockquote)
75
+ const changeNextCaptionPosition = (state, en, caption) => {
76
+ const captionStartToken = state.tokens[en+2] // +1: text node for figure.
77
+ const captionInlineToken = state.tokens[en+3]
78
+ const captionEndToken = state.tokens[en+4]
79
+ captionStartToken.attrs.forEach(attr => {
80
+ if (attr[0] === 'class') {
81
+ attr[1] = attr[1].replace(new RegExp(' *?f-' + caption.name), '').trim()
82
+ if(attr[1] === '') {
83
+ captionStartToken.attrs.splice(captionStartToken.attrIndex('class'), 1)
162
84
  }
163
85
  }
164
- if(/pre-(?:code|samp)/.test(tagName) && opt.roleDocExample) {
165
- figureStartToken.attrSet('role', 'doc-example');
166
- }
167
- const figureEndToken = new state.Token('figure_close', 'figure', -1);
168
- const breakToken = new state.Token('text', '', 0);
169
- breakToken.content = '\n';
170
- if (opt.styleProcess && caption.hasNext && sp.attrs.length > 0) {
171
- for (let attr of sp.attrs) {
172
- figureStartToken.attrJoin(attr[0], attr[1]);
86
+ })
87
+ captionStartToken.type = 'figcaption_open'
88
+ captionStartToken.tag = 'figcaption'
89
+ captionEndToken.type = 'figcaption_close'
90
+ captionEndToken.tag = 'figcaption'
91
+ state.tokens.splice(en, 0, captionStartToken, captionInlineToken, captionEndToken)
92
+ state.tokens.splice(en+5, 3)
93
+ return true
94
+ }
95
+
96
+ const wrapWithFigure = (state, range, tagName, caption, replaceInsteadOfWrap, sp, opt) => {
97
+ let n = range.start
98
+ let en = range.end
99
+ const figureStartToken = new state.Token('figure_open', 'figure', 1)
100
+ figureStartToken.attrSet('class', 'f-' + tagName)
101
+ if (sp.isVideoIframe) {
102
+ figureStartToken.attrSet('class', 'f-video')
103
+ }
104
+ if (sp.isIframeTypeBlockQuote) {
105
+ let figureClassThatWrapsIframeTypeBlockquote = 'i-frame'
106
+ if (caption.prev || caption.next) {
107
+ if (caption.name === 'img') {
108
+ figureClassThatWrapsIframeTypeBlockquote = 'f-img'
173
109
  }
110
+ figureStartToken.attrSet('class', figureClassThatWrapsIframeTypeBlockquote)
111
+ } else {
112
+ figureClassThatWrapsIframeTypeBlockquote = opt.figureClassThatWrapsIframeTypeBlockquote
113
+ figureStartToken.attrSet('class', figureClassThatWrapsIframeTypeBlockquote)
174
114
  }
175
- // For vsce
176
- if(state.tokens[n].attrs) {
177
- for (let attr of state.tokens[n].attrs) {
178
- figureStartToken.attrJoin(attr[0], attr[1]);
179
- }
115
+ }
116
+ if(/pre-(?:code|samp)/.test(tagName) && opt.roleDocExample) {
117
+ figureStartToken.attrSet('role', 'doc-example')
118
+ }
119
+ const figureEndToken = new state.Token('figure_close', 'figure', -1)
120
+ const breakToken = new state.Token('text', '', 0)
121
+ breakToken.content = '\n'
122
+ if (opt.styleProcess && caption.hasNext && sp.attrs.length > 0) {
123
+ for (let attr of sp.attrs) {
124
+ figureStartToken.attrJoin(attr[0], attr[1])
180
125
  }
181
- ///
182
- if (replaceInsteadOfWrap) {
183
- state.tokens.splice(en, 1, breakToken, figureEndToken, breakToken);
184
- state.tokens.splice(n, 1, figureStartToken, breakToken);
185
- en = en + 2;
186
- //console.log(state.tokens[n].type, state.tokens[en].type);
187
- } else {
188
- state.tokens.splice(en+1, 0, figureEndToken, breakToken);
189
- state.tokens.splice(n, 0, figureStartToken, breakToken);
190
- en = en + 3;
191
- //console.log(state.tokens[n].type, state.tokens[en].type);
126
+ }
127
+ // For vsce
128
+ //console.log(caption)
129
+ if(state.tokens[n].attrs && caption.name === 'img') {
130
+ for (let attr of state.tokens[n].attrs) {
131
+ figureStartToken.attrJoin(attr[0], attr[1])
192
132
  }
193
- range.start = n;
194
- range.end = en;
195
- return range;
196
133
  }
197
-
198
- function checkCaption(state, n, en, tagName, caption) {
199
-
200
- caption = checkPrevCaption(state, n, en, tagName, caption);
201
- if (caption.hasPrev) return caption;
202
- caption = checkNextCaption(state, n, en, tagName, caption);
203
- return caption;
134
+ if (replaceInsteadOfWrap) {
135
+ state.tokens.splice(en, 1, breakToken, figureEndToken, breakToken)
136
+ state.tokens.splice(n, 1, figureStartToken, breakToken)
137
+ en = en + 2
138
+ //console.log(state.tokens[n].type, state.tokens[en].type)
139
+ } else {
140
+ state.tokens.splice(en+1, 0, figureEndToken, breakToken)
141
+ state.tokens.splice(n, 0, figureStartToken, breakToken)
142
+ en = en + 3
143
+ //console.log(state.tokens[n].type, state.tokens[en].type)
204
144
  }
145
+ range.start = n
146
+ range.end = en
147
+ return range
148
+ }
205
149
 
206
- function figureWithCaption(state) {
207
- let n = 0;
208
- while (n < state.tokens.length) {
209
- const token = state.tokens[n];
210
- const nextToken = state.tokens[n+1];
211
- let en = n;
212
- let range = {
213
- start: n,
214
- end: en,
215
- }
216
- let checkToken = false;
217
- let hasCloseTag = false;
218
- let tagName = '';
219
- let caption = {
220
- name: '',
221
- nameSuffix: '',
222
- hasPrev: false,
223
- hasNext: false,
224
- };
225
- const sp = {
226
- attrs: [],
227
- isVideoIframe: false,
228
- isIframeTypeBlockQuote: false,
229
- hasImgCaption: false,
230
- }
150
+ const checkCaption = (state, n, en, caption) => {
151
+ caption = checkPrevCaption(state, n, caption)
152
+ if (caption.hasPrev) return caption
153
+ caption = checkNextCaption(state, en, caption)
154
+ return caption
155
+ }
231
156
 
232
- const checkTags = ['table', 'pre', 'blockquote'];
233
- let cti = 0;
234
- while (cti < checkTags.length) {
235
- if (token.type === checkTags[cti] + '_open') {
236
- if (n > 1) {
237
- if (state.tokens[n-2].type === 'figure_open') { // +linebreak
238
- cti++; continue;
239
- }
240
- }
241
- checkToken = true;
242
- tagName = token.tag;
243
- while (en < state.tokens.length) {
244
- if(state.tokens[en].type === tagName + '_close') {
245
- hasCloseTag = true;
246
- break;
247
- };
248
- en++;
249
- }
250
- range.end = en;
251
- caption = checkCaption(state, n, en, tagName, caption);
252
- if (caption.hasPrev || caption.hasNext) {
253
- range = wrapWithFigure(state, range, tagName, caption, false, sp);
254
- break;
157
+ const figureWithCaption = (state, opt) => {
158
+ let n = 0
159
+ while (n < state.tokens.length) {
160
+ const token = state.tokens[n]
161
+ const nextToken = state.tokens[n+1]
162
+ let en = n
163
+ let range = {
164
+ start: n,
165
+ end: en,
166
+ }
167
+ let checkToken = false
168
+ let hasCloseTag = false
169
+ let tagName = ''
170
+ let caption = {
171
+ name: '',
172
+ nameSuffix: '',
173
+ hasPrev: false,
174
+ hasNext: false,
175
+ };
176
+ const sp = {
177
+ attrs: [],
178
+ isVideoIframe: false,
179
+ isIframeTypeBlockQuote: false,
180
+ hasImgCaption: false,
181
+ }
182
+
183
+ const checkTags = ['table', 'pre', 'blockquote']
184
+ let cti = 0
185
+ while (cti < checkTags.length) {
186
+ if (token.type === checkTags[cti] + '_open') {
187
+ if (n > 1) {
188
+ if (state.tokens[n-2].type === 'figure_open') { // +linebreak
189
+ cti++; continue;
255
190
  }
256
- break;
257
191
  }
258
-
259
- if(token.type === 'fence') {
260
- if (token.tag === 'code' && token.block) {
261
- checkToken = true;
262
- let isSampInfo = false
263
- if (/^ *(?:samp|shell|console)(?:(?= )|$)/.test(token.info)) {
264
- token.tag = 'samp';
265
- isSampInfo = true
266
- }
267
- if (isSampInfo) {
268
- tagName = 'pre-samp';
269
- } else {
270
- tagName = 'pre-code';
271
- }
272
- caption = checkCaption(state, n, en, tagName, caption);
273
- if (caption.hasPrev || caption.hasNext) {
274
- range = wrapWithFigure(state, range, tagName, caption, false, sp);
275
- break;
276
- }
192
+ checkToken = true
193
+ caption.name = checkTags
194
+ tagName = token.tag
195
+ while (en < state.tokens.length) {
196
+ if(state.tokens[en].type === tagName + '_close') {
197
+ hasCloseTag = true
198
+ break
277
199
  }
278
- break;
200
+ en++
279
201
  }
280
- cti++;
202
+ range.end = en
203
+ caption = checkCaption(state, n, en, caption)
204
+ if (caption.hasPrev || caption.hasNext) {
205
+ range = wrapWithFigure(state, range, tagName, caption, false, sp, opt)
206
+ }
207
+ break
281
208
  }
282
209
 
283
- if (token.type === 'html_block') {
284
- const tags = ['video', 'audio', 'iframe', 'blockquote'];
285
- let ctj = 0;
286
- while (ctj < tags.length) {
287
- const hasTag = token.content.match(new RegExp('^<'+ tags[ctj] + ' ?[^>]*?>[\\s\\S]*?<\\/' + tags[ctj] + '>(\\n| *?)(<script [^>]*?>(?:<\\/script>)?)? *(\\n|$)'));
288
- if (!hasTag) {
289
- ctj++;
290
- continue;
210
+ if(token.type === 'fence') {
211
+ if (token.tag === 'code' && token.block) {
212
+ checkToken = true
213
+ let isSampInfo = false
214
+ if (/^ *(?:samp|shell|console)(?:(?= )|$)/.test(token.info)) {
215
+ token.tag = 'samp'
216
+ isSampInfo = true
291
217
  }
292
- if ((hasTag[2] && hasTag[3] !== '\n') ||
293
- (hasTag[1] !== '\n' && hasTag[2] === undefined)) {
294
- token.content += '\n'
218
+ if (isSampInfo) {
219
+ tagName = 'pre-samp'
220
+ } else {
221
+ tagName = 'pre-code'
295
222
  }
296
- tagName = tags[ctj];
297
- checkToken = true;
298
- if (tagName === 'blockquote') {
299
- //text-post-media: threads
300
- if(/^<[^>]*? class="(?:twitter-tweet|instagram-media|text-post-media|bluesky-embed)"/.test(token.content)) {
301
- sp.isIframeTypeBlockQuote = true
302
- } else {
303
- ctj++;
304
- continue;
305
- }
223
+ caption = checkCaption(state, n, en, caption)
224
+ if (caption.hasPrev || caption.hasNext) {
225
+ range = wrapWithFigure(state, range, tagName, caption, false, sp, opt)
226
+ break
306
227
  }
307
- break;
308
228
  }
309
- if (!checkToken) {n++; continue;}
310
- if (tagName === 'iframe') {
311
- if(/^<[^>]*? src="https:\/\/(?:www.youtube-nocookie.com|player.vimeo.com)\//i.test(token.content)) {
312
- sp.isVideoIframe = true
229
+ break
230
+ }
231
+ cti++
232
+ }
233
+
234
+ if (token.type === 'html_block') {
235
+ const tags = ['video', 'audio', 'iframe', 'blockquote']
236
+ let ctj = 0
237
+ while (ctj < tags.length) {
238
+ const hasTag = token.content.match(new RegExp('^<'+ tags[ctj] + ' ?[^>]*?>[\\s\\S]*?<\\/' + tags[ctj] + '>(\\n| *?)(<script [^>]*?>(?:<\\/script>)?)? *(\\n|$)'))
239
+ if (!hasTag) {
240
+ ctj++
241
+ continue
242
+ }
243
+ if ((hasTag[2] && hasTag[3] !== '\n') || (hasTag[1] !== '\n' && hasTag[2] === undefined)) {
244
+ token.content += '\n'
245
+ }
246
+ tagName = tags[ctj]
247
+ caption.name = tags[ctj]
248
+ checkToken = true
249
+ if (tagName === 'blockquote') {
250
+ //text-post-media: threads
251
+ if(/^<[^>]*? class="(?:twitter-tweet|instagram-media|text-post-media|bluesky-embed)"/.test(token.content)) {
252
+ sp.isIframeTypeBlockQuote = true
253
+ } else {
254
+ ctj++
255
+ continue
313
256
  }
314
257
  }
315
- if(sp.isIframeTypeBlockQuote) {
316
- if(n > 2) {
317
- if (state.tokens[n-2].children) {
318
- if (state.tokens[n-2].children.length > 1) {
319
- if (state.tokens[n-2].children[1].attrs) {
320
- if (state.tokens[n-2].children[1].attrs[0][0] === 'class') {
321
- if (state.tokens[n-2].children[1].attrs[0][1] === 'f-img-label') {
322
- sp.hasImgCaption = true;
323
- /* For now, I think I should use figure instead of blockquoe for caption. */
324
- }
258
+ break
259
+ }
260
+ if (!checkToken) {n++; continue;}
261
+ if (tagName === 'iframe') {
262
+ if(/^<[^>]*? src="https:\/\/(?:www.youtube-nocookie.com|player.vimeo.com)\//i.test(token.content)) {
263
+ sp.isVideoIframe = true
264
+ }
265
+ }
266
+ if(sp.isIframeTypeBlockQuote) {
267
+ if(n > 2) {
268
+ if (state.tokens[n-2].children) {
269
+ if (state.tokens[n-2].children.length > 1) {
270
+ if (state.tokens[n-2].children[1].attrs) {
271
+ if (state.tokens[n-2].children[1].attrs[0][0] === 'class') {
272
+ if (state.tokens[n-2].children[1].attrs[0][1] === 'f-img-label') {
273
+ sp.hasImgCaption = true
274
+ /* For now, I think I should use figure instead of blockquoe for caption. */
325
275
  }
326
276
  }
327
277
  }
328
278
  }
329
279
  }
330
- if (n + 2 < state.tokens.length) {
331
- if (state.tokens[n+2].children) {
332
- if (state.tokens[n+2].children.length > 1) {
333
- if (state.tokens[n+2].children[1].attrs) {
334
- if (state.tokens[n+2].children[1].attrs[0][0] === 'class' &&
335
- state.tokens[n+2].children[1].attrs[0][1] === 'f-img-label') {
336
- sp.hasImgCaption = true;
337
- }
280
+ }
281
+ if (n + 2 < state.tokens.length) {
282
+ if (state.tokens[n+2].children) {
283
+ if (state.tokens[n+2].children.length > 1) {
284
+ if (state.tokens[n+2].children[1].attrs) {
285
+ if (state.tokens[n+2].children[1].attrs[0][0] === 'class' &&
286
+ state.tokens[n+2].children[1].attrs[0][1] === 'f-img-label') {
287
+ sp.hasImgCaption = true
338
288
  }
339
289
  }
340
290
  }
341
291
  }
342
292
  }
343
- caption = checkCaption(state, n, en, tagName, caption);
344
- if (caption.hasPrev || caption.hasNext) {
345
- range = wrapWithFigure(state, range, tagName, caption, false, sp);
346
- n = en + 2;
347
- } else if ((opt.iframeWithoutCaption && (tagName === 'iframe')) ||
348
- (opt.videoWithoutCaption && (tagName === 'video')) ||
349
- (opt.iframeTypeBlockquoteWithoutCaption && (tagName === 'blockquote'))) {
350
- range = wrapWithFigure(state, range, tagName, caption, false, sp);
351
- n = en + 2;
352
- }
353
293
  }
294
+ caption = checkCaption(state, n, en, caption)
295
+ if (caption.hasPrev || caption.hasNext) {
296
+ range = wrapWithFigure(state, range, tagName, caption, false, sp, opt)
297
+ n = en + 2
298
+ } else if ((opt.iframeWithoutCaption && (tagName === 'iframe')) ||
299
+ (opt.videoWithoutCaption && (tagName === 'video')) ||
300
+ (opt.iframeTypeBlockquoteWithoutCaption && (tagName === 'blockquote'))) {
301
+ range = wrapWithFigure(state, range, tagName, caption, false, sp, opt)
302
+ n = en + 2
303
+ }
304
+ }
354
305
 
355
-
356
- if (token.type === 'paragraph_open' && nextToken.type === 'inline' && nextToken.children[0].type === 'image') {
357
- let ntChildTokenIndex = 1
358
- let imageNum = 1
359
- let isMultipleImagesHorizontal = true
360
- let isMultipleImagesVertical = true
361
- checkToken = true
362
- while (ntChildTokenIndex < nextToken.children.length) {
363
- const ntChildToken = nextToken.children[ntChildTokenIndex]
364
- if (ntChildTokenIndex === nextToken.children.length - 1) {
365
- let imageAttrs = ntChildToken.content.match(/^ *\{(.*?)\} *$/)
366
- if(ntChildToken.type === 'text' && imageAttrs) {
367
- imageAttrs = imageAttrs[1].split(/ +/)
368
- let iai = 0
369
- while (iai < imageAttrs.length) {
370
- if (/^\./.test(imageAttrs[iai])) {
371
- imageAttrs[iai] = imageAttrs[iai].replace(/^\./, "class=")
372
- }
373
- if (/^#/.test(imageAttrs[iai])) {
374
- imageAttrs[iai] = imageAttrs[iai].replace(/^\#/, "id=")
375
- }
376
- let imageAttr = imageAttrs[iai].match(/^(.*?)="?(.*)"?$/)
377
- if (!imageAttr || !imageAttr[1]) {
378
- iai++
379
- continue
380
- }
381
- sp.attrs.push([imageAttr[1], imageAttr[2]])
306
+ if (token.type === 'paragraph_open' && nextToken.type === 'inline' && nextToken.children[0].type === 'image') {
307
+ let ntChildTokenIndex = 1
308
+ let imageNum = 1
309
+ let isMultipleImagesHorizontal = true
310
+ let isMultipleImagesVertical = true
311
+ checkToken = true
312
+ caption.name = 'img'
313
+ while (ntChildTokenIndex < nextToken.children.length) {
314
+ const ntChildToken = nextToken.children[ntChildTokenIndex]
315
+ if (ntChildTokenIndex === nextToken.children.length - 1) {
316
+ let imageAttrs = ntChildToken.content.match(/^ *\{(.*?)\} *$/)
317
+ if(ntChildToken.type === 'text' && imageAttrs) {
318
+ imageAttrs = imageAttrs[1].split(/ +/)
319
+ let iai = 0
320
+ while (iai < imageAttrs.length) {
321
+ if (/^\./.test(imageAttrs[iai])) {
322
+ imageAttrs[iai] = imageAttrs[iai].replace(/^\./, "class=")
323
+ }
324
+ if (/^#/.test(imageAttrs[iai])) {
325
+ imageAttrs[iai] = imageAttrs[iai].replace(/^\#/, "id=")
326
+ }
327
+ let imageAttr = imageAttrs[iai].match(/^(.*?)="?(.*)"?$/)
328
+ if (!imageAttr || !imageAttr[1]) {
382
329
  iai++
330
+ continue
383
331
  }
384
- break
332
+ sp.attrs.push([imageAttr[1], imageAttr[2]])
333
+ iai++
385
334
  }
386
- }
387
-
388
- if (!opt.multipleImages) {
389
- checkToken = false
390
335
  break
391
336
  }
392
- if (ntChildToken.type === 'image') {
393
- imageNum += 1
394
- } else if (ntChildToken.type === 'text' && /^ *$/.test(ntChildToken.content)) {
395
- isMultipleImagesVertical = false
396
- if (isMultipleImagesVertical) {
397
- isMultipleImagesHorizontal = false
398
- }
399
- } else if (ntChildToken.type === 'softbreak') {
337
+ }
338
+
339
+ if (!opt.multipleImages) {
340
+ checkToken = false
341
+ break
342
+ }
343
+ if (ntChildToken.type === 'image') {
344
+ imageNum += 1
345
+ } else if (ntChildToken.type === 'text' && /^ *$/.test(ntChildToken.content)) {
346
+ isMultipleImagesVertical = false
347
+ if (isMultipleImagesVertical) {
400
348
  isMultipleImagesHorizontal = false
401
- if (isMultipleImagesHorizontal) {
402
- isMultipleImagesVertical = false
403
- }
404
- } else {
405
- checkToken = false
406
- break
407
349
  }
408
- ntChildTokenIndex++
409
- }
410
- if (checkToken && imageNum > 1 && opt.multipleImages) {
350
+ } else if (ntChildToken.type === 'softbreak') {
351
+ isMultipleImagesHorizontal = false
411
352
  if (isMultipleImagesHorizontal) {
412
- caption.nameSuffix = '-horizontal'
413
- } else if (isMultipleImagesVertical) {
414
- caption.nameSuffix = '-vertical'
415
- } else {
416
- caption.nameSuffix = '-multiple'
417
- }
418
- ntChildTokenIndex = 0
419
- while (ntChildTokenIndex < nextToken.children.length) {
420
- const ccToken = nextToken.children[ntChildTokenIndex]
421
- if (ccToken.type === 'text' && /^ *$/.test(ccToken.content)) {
422
- ccToken.content = ''
423
- }
424
- ntChildTokenIndex++
353
+ isMultipleImagesVertical = false
425
354
  }
355
+ } else {
356
+ checkToken = false
357
+ break
426
358
  }
427
- en = n + 2;
428
- range.end = en;
429
- tagName = 'img';
430
- nextToken.children[0].type = 'image';
431
-
432
- if (opt.imgAltCaption) setAltToLabel(state, n, en, tagName, caption, opt)
433
- if (opt.imgTitleCaption) setTitleToLabel(state, n, en, tagName, caption, opt)
434
-
435
- caption = checkCaption(state, n, en, tagName, caption, opt);
436
-
437
- if (opt.oneImageWithoutCaption && state.tokens[n-1]) {
438
- if (state.tokens[n-1].type === 'list_item_open') {checkToken = false;}
359
+ ntChildTokenIndex++
360
+ }
361
+ if (checkToken && imageNum > 1 && opt.multipleImages) {
362
+ if (isMultipleImagesHorizontal) {
363
+ caption.nameSuffix = '-horizontal'
364
+ } else if (isMultipleImagesVertical) {
365
+ caption.nameSuffix = '-vertical'
366
+ } else {
367
+ caption.nameSuffix = '-multiple'
439
368
  }
440
- if (checkToken && (opt.oneImageWithoutCaption || caption.hasPrev || caption.hasNext)) {
441
- if (caption.nameSuffix) tagName += caption.nameSuffix
442
- range = wrapWithFigure(state, range, tagName, caption, true, sp)
369
+ ntChildTokenIndex = 0
370
+ while (ntChildTokenIndex < nextToken.children.length) {
371
+ const ccToken = nextToken.children[ntChildTokenIndex]
372
+ if (ccToken.type === 'text' && /^ *$/.test(ccToken.content)) {
373
+ ccToken.content = ''
374
+ }
375
+ ntChildTokenIndex++
443
376
  }
444
377
  }
378
+ en = n + 2
379
+ range.end = en
380
+ tagName = 'img'
381
+ nextToken.children[0].type = 'image'
445
382
 
446
- if (!checkToken || !caption.name) {n++; continue;}
383
+ if (opt.imgAltCaption) setAltToLabel(state, n, en, tagName, caption, opt)
384
+ if (opt.imgTitleCaption) setTitleToLabel(state, n, en, tagName, caption, opt)
385
+ caption = checkCaption(state, n, en, caption)
447
386
 
448
- n = range.start;
449
- en = range.end;
450
- if (caption.hasPrev) {
451
- changePrevCaptionPosition(state, n, en, tagName, caption);
452
- n = en + 1;
453
- continue;
387
+ if (opt.oneImageWithoutCaption && state.tokens[n-1]) {
388
+ if (state.tokens[n-1].type === 'list_item_open') checkToken = false
454
389
  }
455
- if (caption.hasNext) {
456
- changeNextCaptionPosition(state, n, en, tagName, caption);
457
- n = en + 4;
458
- continue;
390
+ if (checkToken && (opt.oneImageWithoutCaption || caption.hasPrev || caption.hasNext)) {
391
+ if (caption.nameSuffix) tagName += caption.nameSuffix
392
+ range = wrapWithFigure(state, range, tagName, caption, true, sp, opt)
459
393
  }
460
- n = en + 1;
461
394
  }
462
- return;
463
- }
464
395
 
465
- const setAltToLabel = (state, n, en, tagName, caption, opt) => {
466
- if (n < 2) return false
467
- if (state.tokens[n+1].children[0].type !== 'image' || !state.tokens[n-2].children) return false
468
- if (state.tokens[n-2].children[2]) {
469
- state.tokens[n+1].content = state.tokens[n+1].content.replace(/^!\[.*?\]/, '![' + state.tokens[n-2].children[2].content + ']')
470
- if (!state.tokens[n+1].children[0].children[0]) {
471
- const textToken = new state.Token('text', '', 0)
472
- state.tokens[n+1].children[0].children.push(textToken)
473
- }
474
- // Set figure label:
475
- //state.tokens[n+1].children[0].children[0].content = state.tokens[n-2].children[2].content
476
- // Set img alt to empty value:
477
- state.tokens[n+1].children[0].children[0].content = ''
396
+ if (!checkToken || !caption.name) {n++; continue;}
397
+
398
+ n = range.start
399
+ en = range.end
400
+ if (caption.hasPrev) {
401
+ changePrevCaptionPosition(state, n, caption)
402
+ n = en + 1
403
+ continue
478
404
  }
479
- // Set figure label:
480
- //state.tokens[n+1].children[0].content = state.tokens[n-2].children[2].content
481
- // Set img alt to empty value:
482
- state.tokens[n+1].children[0].content = ''
483
- //console.log(state.tokens[n+1].children[0])
484
- return true
405
+ if (caption.hasNext) {
406
+ changeNextCaptionPosition(state, en, caption)
407
+ n = en + 4
408
+ continue
409
+ }
410
+ n = en + 1
485
411
  }
412
+ return
413
+ }
486
414
 
487
- const setTitleToLabel = (state, n, en, tagName, caption, opt) => {
488
- if (n < 2) return false
489
- if (state.tokens[n+1].children[0].type !== 'image') return false
490
- if (!state.tokens[n-2].children[0]) return false
491
- state.tokens[n+1].children[0].attrSet('alt', state.tokens[n+1].children[0].content)
415
+ const setAltToLabel = (state, n, en, tagName, caption, opt) => {
416
+ if (n < 2) return false
417
+ if (state.tokens[n+1].children[0].type !== 'image' || !state.tokens[n-2].children) return false
418
+ if (state.tokens[n-2].children[2]) {
419
+ state.tokens[n+1].content = state.tokens[n+1].content.replace(/^!\[.*?\]/, '![' + state.tokens[n-2].children[2].content + ']')
492
420
  if (!state.tokens[n+1].children[0].children[0]) {
493
421
  const textToken = new state.Token('text', '', 0)
494
422
  state.tokens[n+1].children[0].children.push(textToken)
495
423
  }
496
- let i = 0
497
- while (0 < state.tokens[n+1].children[0].attrs.length) {
498
- if (state.tokens[n+1].children[0].attrs[i][0] === 'title') {
499
- state.tokens[n+1].children[0].attrs.splice(i, i + 1)
500
- break
501
- } else {
502
- state.tokens[n+1].children[0].attrJoin('title', '')
503
- }
504
- i++
505
- }
506
- //console.log(state.tokens[n+1].children[0])
507
- return true
424
+ // Set figure label:
425
+ //state.tokens[n+1].children[0].children[0].content = state.tokens[n-2].children[2].content
426
+ // Set img alt to empty value:
427
+ state.tokens[n+1].children[0].children[0].content = ''
508
428
  }
429
+ // Set figure label:
430
+ //state.tokens[n+1].children[0].content = state.tokens[n-2].children[2].content
431
+ // Set img alt to empty value:
432
+ state.tokens[n+1].children[0].content = ''
433
+ //console.log(state.tokens[n+1].children[0])
434
+ return true
435
+ }
509
436
 
510
- const imgAttrToPCaption = (state, startLine) => {
511
- let pos = state.bMarks[startLine] + state.tShift[startLine]
512
- let max = state.eMarks[startLine]
513
- let inline = state.src.slice(pos, max)
514
- let label = ''
515
- if (opt.imgAltCaption && typeof opt.imgAltCaption === 'string') label = opt.imgAltCaption
516
- if (opt.imgTitleCaption && typeof opt.imgTitleCaption === 'string') label = opt.imgTitleCaption
517
- let caption = ''
518
- let imgAttrUsedCaption = ''
519
-
520
- const img = inline.match(/^( *!\[)(.*?)\]\( *?((.*?)(?: +?\"(.*?)\")?) *?\)( *?\{.*?\})? *$/)
521
- if (!img) return
522
-
523
- let hasLabel
524
- if (opt.imgAltCaption) {
525
- caption = img[2]
526
- hasLabel = img[2].match(new RegExp('^' + opt.imgAltCaption))
527
- imgAttrUsedCaption = 'alt'
437
+ const setTitleToLabel = (state, n, en, tagName, caption, opt) => {
438
+ if (n < 2) return false
439
+ if (state.tokens[n+1].children[0].type !== 'image') return false
440
+ if (!state.tokens[n-2].children[0]) return false
441
+ state.tokens[n+1].children[0].attrSet('alt', state.tokens[n+1].children[0].content)
442
+ if (!state.tokens[n+1].children[0].children[0]) {
443
+ const textToken = new state.Token('text', '', 0)
444
+ state.tokens[n+1].children[0].children.push(textToken)
445
+ }
446
+ let i = 0
447
+ while (0 < state.tokens[n+1].children[0].attrs.length) {
448
+ if (state.tokens[n+1].children[0].attrs[i][0] === 'title') {
449
+ state.tokens[n+1].children[0].attrs.splice(i, i + 1)
450
+ break
451
+ } else {
452
+ state.tokens[n+1].children[0].attrJoin('title', '')
528
453
  }
529
- if (opt.imgTitleCaption) {
530
- if (!img[5]) img[5] = ''
531
- caption = img[5]
532
- hasLabel = img[5].match(new RegExp('^' + opt.imgTitleCaption))
533
- imgAttrUsedCaption = 'title'
454
+ i++
455
+ }
456
+ //console.log(state.tokens[n+1].children[0])
457
+ return true
458
+ }
459
+
460
+ const imgAttrToPCaption = (state, startLine, opt) => {
461
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
462
+ let max = state.eMarks[startLine]
463
+ let inline = state.src.slice(pos, max)
464
+ let label = ''
465
+ if (opt.imgAltCaption && typeof opt.imgAltCaption === 'string') label = opt.imgAltCaption
466
+ if (opt.imgTitleCaption && typeof opt.imgTitleCaption === 'string') label = opt.imgTitleCaption
467
+ let caption = ''
468
+ let imgAttrUsedCaption = ''
469
+
470
+ const img = inline.match(/^( *!\[)(.*?)\]\( *?((.*?)(?: +?\"(.*?)\")?) *?\)( *?\{.*?\})? *$/)
471
+ if (!img) return
472
+
473
+ let hasLabel
474
+ if (opt.imgAltCaption) {
475
+ caption = img[2]
476
+ hasLabel = img[2].match(new RegExp('^' + opt.imgAltCaption))
477
+ imgAttrUsedCaption = 'alt'
478
+ }
479
+ if (opt.imgTitleCaption) {
480
+ if (!img[5]) img[5] = ''
481
+ caption = img[5]
482
+ hasLabel = img[5].match(new RegExp('^' + opt.imgTitleCaption))
483
+ imgAttrUsedCaption = 'title'
484
+ }
485
+ let token
486
+ token = state.push('paragraph_open', 'p', 1)
487
+ token.map = [startLine, startLine + 1]
488
+ token = state.push('inline', '', 0)
489
+ if (hasLabel) {
490
+ token.content = caption
491
+ } else {
492
+ if (!label) {
493
+ if (imgAttrUsedCaption === 'alt') {
494
+ label = opt.imgAltCaption
495
+ } else if (imgAttrUsedCaption === 'title') {
496
+ label = opt.imgTitleCaption
497
+ } else if (imgAttrUsedCaption) {
498
+ label = 'Figure'
499
+ }
534
500
  }
535
- let token
536
- token = state.push('paragraph_open', 'p', 1)
537
- token.map = [startLine, startLine + 1]
538
- token = state.push('inline', '', 0)
539
- if (hasLabel) {
540
- token.content = caption
501
+ token.content = label
502
+ if (/[a-zA-Z]/.test(label)) {
503
+ token.content += '.'
504
+ if (caption) token.content += ' '
541
505
  } else {
542
- if (!label) {
543
- if (imgAttrUsedCaption === 'alt') {
544
- label = opt.imgAltCaption
545
- } else if (imgAttrUsedCaption === 'title') {
546
- label = opt.imgTitleCaption
547
- } else if (imgAttrUsedCaption) {
548
- label = 'Figure'
549
- }
550
- }
551
- token.content = label
552
- if (/[a-zA-Z]/.test(label)) {
553
- token.content += '.'
554
- if (caption) token.content += ' '
555
- } else {
556
- token.content += ' '
557
- }
558
- token.content += caption
506
+ token.content += ' '
559
507
  }
560
- token.map = [startLine, startLine + 1]
561
- token.children = []
562
- if (caption.length === 0) {
563
- token.attrs = [['class', 'nocaption']]
508
+ token.content += caption
509
+ }
510
+ token.map = [startLine, startLine + 1]
511
+ token.children = []
512
+ if (caption.length === 0) {
513
+ token.attrs = [['class', 'nocaption']]
514
+ }
515
+ token = state.push('paragraph_close', 'p', -1)
516
+ return
517
+ }
518
+
519
+ const mditFigureWithPCaption = (md, option) => {
520
+
521
+ let opt = {
522
+ classPrefix: 'f',
523
+ figureClassThatWrapsIframeTypeBlockquote: 'f-img',
524
+ styleProcess : true,
525
+ hasNumClass: false,
526
+ scaleSuffix: false,
527
+ dquoteFilename: false,
528
+ strongFilename: false,
529
+ bLabel: false,
530
+ strongLabel: false,
531
+ jointSpaceUseHalfWidth: false,
532
+ oneImageWithoutCaption: false,
533
+ iframeWithoutCaption: false,
534
+ videoWithoutCaption: false,
535
+ iframeTypeBlockquoteWithoutCaption: false,
536
+ removeUnnumberedLabel: false,
537
+ removeUnnumberedLabelExceptMarks: [],
538
+ multipleImages: true,
539
+ imgAltCaption: false,
540
+ imgTitleCaption: false,
541
+ roleDocExample: false,
542
+ }
543
+ if (option !== undefined) {
544
+ for (let o in option) {
545
+ opt[o] = option[o]
564
546
  }
565
- token = state.push('paragraph_close', 'p', -1)
566
- return
567
547
  }
568
548
 
569
549
  if (opt.imgAltCaption || opt.imgTitleCaption) {
570
- md.block.ruler.before('paragraph', 'img_attr_caption', imgAttrToPCaption)
550
+ md.block.ruler.before('paragraph', 'img_attr_caption', (state) => {
551
+ imgAttrToPCaption(state, state.line, opt)
552
+ })
571
553
  }
572
554
  md.use(mditPCaption, {
573
555
  classPrefix: opt.classPrefix,
@@ -580,7 +562,11 @@ const mditFigureWithPCaption = (md, option) => {
580
562
  removeUnnumberedLabel: opt.removeUnnumberedLabel,
581
563
  removeUnnumberedLabelExceptMarks: opt.removeUnnumberedLabelExceptMarks,
582
564
  })
583
- md.core.ruler.before('linkify', 'figure_with_caption', figureWithCaption);
565
+
566
+ //If nextCaption has `{}` style and `f-img-multipleImages`, when upgraded to markdown-it-attrs@4.2.0, the existing script will have `{}` style on nextCaption. Therefore, since markdown-it-attrs is md.core.ruler.before('linkify'), figure_with_caption will be processed after it.
567
+ md.core.ruler.before('replacements', 'figure_with_caption', (state) => {
568
+ figureWithCaption(state, opt)
569
+ })
584
570
  }
585
571
 
586
572
  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.8.1",
3
+ "version": "0.9.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",
@@ -15,11 +15,11 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@peaceroad/markdown-it-renderer-fence": "^0.1.1",
18
- "highlight.js": "^11.9.0",
18
+ "highlight.js": "^11.10.0",
19
19
  "markdown-it": "^14.1.0",
20
- "markdown-it-attrs": "^4.1.6"
20
+ "markdown-it-attrs": "^4.2.0"
21
21
  },
22
22
  "dependencies": {
23
- "p7d-markdown-it-p-captions": "^0.13.0"
23
+ "p7d-markdown-it-p-captions": "^0.14.0"
24
24
  }
25
25
  }
@@ -672,3 +672,31 @@ Slide. A caption.
672
672
  <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">XXXXX <a href="https://t.co/XXXXX">https://t.co/XXXXX</a></p>&mdash; User (@twitter) <a href="https://twitter.com/UserID/status/XXXXX">August 4, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
673
673
  </figure>
674
674
 
675
+
676
+ [Markdown]
677
+ コード Hello world
678
+
679
+ ```js {.style}
680
+ console.log('Hello world')
681
+ ```
682
+ [HTML]
683
+ <figure class="f-pre-code">
684
+ <figcaption><span class="f-pre-code-label">コード</span> Hello world</figcaption>
685
+ <pre><code class="language-js style">console.log('Hello world')
686
+ </code></pre>
687
+ </figure>
688
+
689
+
690
+ [Markdown]
691
+ ```js {.style}
692
+ console.log('Hello world')
693
+ ```
694
+
695
+ コード Hello world
696
+ [HTML]
697
+ <figure class="f-pre-code">
698
+ <pre><code class="language-js style">console.log('Hello world')
699
+ </code></pre>
700
+ <figcaption><span class="f-pre-code-label">コード</span> Hello world</figcaption>
701
+ </figure>
702
+