@peaceroad/markdown-it-figure-with-p-caption 0.19.0 → 0.20.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 +259 -475
- package/caption-numbering/counter-series.js +62 -0
- package/caption-numbering/number-codec.js +101 -0
- package/caption-numbering/options.js +101 -0
- package/caption-numbering/scope.js +466 -0
- package/caption-numbering.js +12 -0
- package/docs/examples.md +571 -0
- package/docs/numbering.md +215 -0
- package/docs/reference.md +148 -0
- package/embeds/detect.js +85 -63
- package/index.js +387 -305
- package/package.json +15 -7
package/index.js
CHANGED
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import {
|
|
2
|
+
analyzeCaptionParagraph,
|
|
2
3
|
analyzeCaptionStart,
|
|
3
|
-
|
|
4
|
+
applyCaptionParagraph,
|
|
4
5
|
buildLabelPrefixMarkerRegFromMarkers,
|
|
6
|
+
canonicalizeCaptionNumberingMark,
|
|
7
|
+
createCaptionNumberingPolicy,
|
|
8
|
+
createCaptionNumberingRuntime,
|
|
5
9
|
getGeneratedLabelDefaults,
|
|
10
|
+
normalizeAutoLabelNumberSets,
|
|
6
11
|
normalizeLabelPrefixMarkers,
|
|
7
12
|
setCaptionParagraph,
|
|
8
13
|
getMarkRegStateForLanguages,
|
|
9
14
|
stripLabelPrefixMarker,
|
|
10
15
|
} from 'p7d-markdown-it-p-captions'
|
|
11
|
-
import { detectHtmlFigureCandidate } from './embeds/detect.js'
|
|
16
|
+
import { applyHtmlFigureTransform, detectHtmlFigureCandidate } from './embeds/detect.js'
|
|
17
|
+
import {
|
|
18
|
+
createFigureCaptionCounterKeyResolverFromMarkRegState,
|
|
19
|
+
} from './caption-numbering/counter-series.js'
|
|
20
|
+
import {
|
|
21
|
+
createUnscopedNumberingContext,
|
|
22
|
+
formatFigureCaptionGeneratedNumberUnchecked,
|
|
23
|
+
getCaptionNumberingContext,
|
|
24
|
+
parseFigureCaptionExplicitNumberUnchecked,
|
|
25
|
+
} from './caption-numbering/number-codec.js'
|
|
26
|
+
import {
|
|
27
|
+
getFigureCaptionNumberingPolicyState,
|
|
28
|
+
normalizeFigureCaptionNumberingPolicy,
|
|
29
|
+
} from './caption-numbering/options.js'
|
|
30
|
+
import {
|
|
31
|
+
createFigureCaptionScopeRuntime,
|
|
32
|
+
getFigureCaptionScopeNestedContainerType,
|
|
33
|
+
updateFigureCaptionScopeFromHeading,
|
|
34
|
+
} from './caption-numbering/scope.js'
|
|
12
35
|
|
|
13
36
|
const imageAttrsReg = /^ *\{(.*?)\} *$/
|
|
14
|
-
const classAttrReg = /^\./
|
|
15
|
-
const idAttrReg = /^#/
|
|
16
37
|
const sampLangReg = /^ *(?:samp|shell|console)(?:(?= )|$)/
|
|
17
38
|
const asciiLabelReg = /^[A-Za-z]/
|
|
18
39
|
const attrNameReg = /^[^\s=]+$/
|
|
19
|
-
const labelTrailingJointReg = /[.\u3002\uff0e
|
|
40
|
+
const labelTrailingJointReg = /[.\u3002\uff0e::\u3000]\s*$/
|
|
41
|
+
const installedKey = Symbol.for('p7d-markdown-it-figure-with-p-caption.installed')
|
|
20
42
|
const CHECK_TYPE_TOKEN_MAP = {
|
|
21
43
|
table_open: 'table',
|
|
22
44
|
pre_open: 'pre',
|
|
@@ -34,11 +56,10 @@ const normalizeLanguageCode = (value) => {
|
|
|
34
56
|
return separatorIndex === -1 ? normalized : normalized.slice(0, separatorIndex)
|
|
35
57
|
}
|
|
36
58
|
const appendAvailableLanguage = (target, lang, availableLanguages) => {
|
|
37
|
-
if (!lang) return
|
|
38
|
-
if (availableLanguages.indexOf(lang) === -1) return
|
|
39
|
-
if (target.indexOf(lang) !== -1) return
|
|
59
|
+
if (!lang) return
|
|
60
|
+
if (availableLanguages.indexOf(lang) === -1) return
|
|
61
|
+
if (target.indexOf(lang) !== -1) return
|
|
40
62
|
target.push(lang)
|
|
41
|
-
return true
|
|
42
63
|
}
|
|
43
64
|
const normalizePreferredLanguages = (value, availableLanguages) => {
|
|
44
65
|
if (!Array.isArray(availableLanguages) || availableLanguages.length === 0) return []
|
|
@@ -103,31 +124,30 @@ const isHyphenFenceLine = (src, lineStart) => {
|
|
|
103
124
|
if (index >= src.length || src.charCodeAt(index) !== 0x0a) return 0
|
|
104
125
|
return hyphenCount
|
|
105
126
|
}
|
|
106
|
-
const
|
|
107
|
-
if (typeof src !== 'string' || isHyphenFenceLine(src, 0) === 0) return
|
|
127
|
+
const getBodyStartAfterLeadingFrontmatter = (src) => {
|
|
128
|
+
if (typeof src !== 'string' || isHyphenFenceLine(src, 0) === 0) return 0
|
|
108
129
|
let lineStart = src.indexOf('\n')
|
|
109
|
-
if (lineStart === -1) return
|
|
130
|
+
if (lineStart === -1) return 0
|
|
110
131
|
lineStart++
|
|
111
132
|
while (lineStart < src.length) {
|
|
112
133
|
if (isHyphenFenceLine(src, lineStart) > 0) {
|
|
113
134
|
const nextLineStart = src.indexOf('\n', lineStart)
|
|
114
|
-
|
|
115
|
-
return src.slice(nextLineStart + 1)
|
|
135
|
+
return nextLineStart === -1 ? src.length : nextLineStart + 1
|
|
116
136
|
}
|
|
117
137
|
const nextLineStart = src.indexOf('\n', lineStart)
|
|
118
138
|
if (nextLineStart === -1) break
|
|
119
139
|
lineStart = nextLineStart + 1
|
|
120
140
|
}
|
|
121
|
-
return
|
|
141
|
+
return 0
|
|
122
142
|
}
|
|
123
143
|
const detectDocumentPrimaryLanguage = (src, availableLanguages) => {
|
|
124
144
|
if (!src || availableLanguages.indexOf('ja') === -1) return ''
|
|
125
|
-
const
|
|
126
|
-
const limit = Math.min(
|
|
145
|
+
const bodyStart = getBodyStartAfterLeadingFrontmatter(src)
|
|
146
|
+
const limit = Math.min(src.length, bodyStart + 8192)
|
|
127
147
|
let japaneseCount = 0
|
|
128
148
|
let asciiAlphaCount = 0
|
|
129
|
-
for (let i =
|
|
130
|
-
const code =
|
|
149
|
+
for (let i = bodyStart; i < limit; i++) {
|
|
150
|
+
const code = src.charCodeAt(i)
|
|
131
151
|
if (isJapaneseCharCode(code)) {
|
|
132
152
|
japaneseCount++
|
|
133
153
|
continue
|
|
@@ -140,10 +160,6 @@ const detectDocumentPrimaryLanguage = (src, availableLanguages) => {
|
|
|
140
160
|
if (asciiAlphaCount === 0) return 'ja'
|
|
141
161
|
return japaneseCount * 2 >= asciiAlphaCount ? 'ja' : ''
|
|
142
162
|
}
|
|
143
|
-
const sourceMayNeedPreferredLanguages = (state) => {
|
|
144
|
-
const src = state && typeof state.src === 'string' ? state.src : ''
|
|
145
|
-
return src.indexOf('![') !== -1
|
|
146
|
-
}
|
|
147
163
|
const resolvePreferredLanguagesForState = (state, opt) => {
|
|
148
164
|
const availableLanguages = (
|
|
149
165
|
opt &&
|
|
@@ -152,10 +168,7 @@ const resolvePreferredLanguagesForState = (state, opt) => {
|
|
|
152
168
|
) ? opt.markRegState.languages : []
|
|
153
169
|
if (availableLanguages.length === 0) return []
|
|
154
170
|
|
|
155
|
-
const
|
|
156
|
-
? opt.normalizedOptionLanguages
|
|
157
|
-
: []
|
|
158
|
-
const baseLanguages = optionLanguages.length > 0 ? optionLanguages : availableLanguages
|
|
171
|
+
const baseLanguages = availableLanguages
|
|
159
172
|
const env = state && state.env ? state.env : null
|
|
160
173
|
|
|
161
174
|
const envLocale = normalizeLanguageCode(env && env.locale)
|
|
@@ -199,13 +212,9 @@ const needsPreferredLanguagesResolution = (opt) => {
|
|
|
199
212
|
}
|
|
200
213
|
const normalizeOptionalClassName = (value) => {
|
|
201
214
|
if (value === null || value === undefined) return ''
|
|
202
|
-
|
|
203
|
-
return normalized || ''
|
|
204
|
-
}
|
|
205
|
-
const buildClassPrefix = (value) => {
|
|
206
|
-
const normalized = normalizeOptionalClassName(value)
|
|
207
|
-
return normalized ? normalized + '-' : ''
|
|
215
|
+
return String(value).trim()
|
|
208
216
|
}
|
|
217
|
+
const buildClassPrefix = (normalized) => normalized ? normalized + '-' : ''
|
|
209
218
|
const normalizeClassOptionWithFallback = (value, fallbackValue) => {
|
|
210
219
|
const normalized = normalizeOptionalClassName(value)
|
|
211
220
|
return normalized || fallbackValue
|
|
@@ -287,10 +296,11 @@ const parseImageAttrs = (raw) => {
|
|
|
287
296
|
for (let i = 0; i < parts.length; i++) {
|
|
288
297
|
let entry = parts[i]
|
|
289
298
|
if (!entry) continue
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
299
|
+
const firstCode = entry.charCodeAt(0)
|
|
300
|
+
if (firstCode === 0x2e) {
|
|
301
|
+
entry = 'class=' + entry.slice(1)
|
|
302
|
+
} else if (firstCode === 0x23) {
|
|
303
|
+
entry = 'id=' + entry.slice(1)
|
|
294
304
|
}
|
|
295
305
|
const equalIndex = entry.indexOf('=')
|
|
296
306
|
if (equalIndex === -1) {
|
|
@@ -305,22 +315,50 @@ const parseImageAttrs = (raw) => {
|
|
|
305
315
|
return attrs
|
|
306
316
|
}
|
|
307
317
|
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
318
|
+
const getEnabledCaptionNumberingMarks = (sets, opt) => {
|
|
319
|
+
if (!Array.isArray(sets) || sets.length === 0) return []
|
|
320
|
+
const exceptMarks = opt && Array.isArray(opt.removeUnnumberedLabelExceptMarks)
|
|
321
|
+
? opt.removeUnnumberedLabelExceptMarks
|
|
322
|
+
: []
|
|
323
|
+
if (!opt || !opt.removeUnnumberedLabel) return sets
|
|
324
|
+
return sets.filter(mark => exceptMarks.indexOf(mark) !== -1)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const createFigureCaptionNumberingPolicy = (enabledMarks, advancedPolicy, markRegState) => {
|
|
328
|
+
if (enabledMarks.length === 0) return null
|
|
329
|
+
const resolveCounterKey = createFigureCaptionCounterKeyResolverFromMarkRegState(markRegState)
|
|
330
|
+
const getCounterKey = ({ captionDecision }) => resolveCounterKey(captionDecision)
|
|
331
|
+
if (!advancedPolicy) {
|
|
332
|
+
return createCaptionNumberingPolicy({
|
|
333
|
+
enabledMarks,
|
|
334
|
+
explicitCounter: 'max',
|
|
335
|
+
generatedNumberHasNumClass: false,
|
|
336
|
+
getCounterKey,
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
return createCaptionNumberingPolicy({
|
|
340
|
+
enabledMarks,
|
|
341
|
+
explicitCounter: 'max',
|
|
342
|
+
generatedNumberHasNumClass: false,
|
|
343
|
+
getCounterKey,
|
|
344
|
+
getSequenceKey({ captionContext }) {
|
|
345
|
+
return getCaptionNumberingContext(captionContext).sequenceKey
|
|
346
|
+
},
|
|
347
|
+
// p-captions resolves getSequenceKey first, so the following callbacks can
|
|
348
|
+
// reuse that already-branded context without a second WeakSet lookup.
|
|
349
|
+
parseExplicitNumber({ number, captionContext }) {
|
|
350
|
+
return parseFigureCaptionExplicitNumberUnchecked(
|
|
351
|
+
number,
|
|
352
|
+
captionContext && captionContext.numbering,
|
|
353
|
+
)
|
|
354
|
+
},
|
|
355
|
+
formatGeneratedNumber({ sequence, captionContext }) {
|
|
356
|
+
return formatFigureCaptionGeneratedNumberUnchecked(
|
|
357
|
+
sequence,
|
|
358
|
+
captionContext && captionContext.numbering,
|
|
359
|
+
)
|
|
360
|
+
},
|
|
361
|
+
})
|
|
324
362
|
}
|
|
325
363
|
|
|
326
364
|
const isOnlySpacesText = (token) => {
|
|
@@ -422,7 +460,24 @@ const buildCaptionWithFallback = (text, fallbackOption, mark, markRegState, pref
|
|
|
422
460
|
return label + getFallbackStringLabelJoint(label) + trimmedText
|
|
423
461
|
}
|
|
424
462
|
|
|
463
|
+
const resolveCaptionPreferredLanguages = (captionState, opt) => {
|
|
464
|
+
const renderState = captionState.preferredLanguageState
|
|
465
|
+
if (!renderState) return captionState.preferredLanguages
|
|
466
|
+
captionState.preferredLanguages = resolvePreferredLanguagesForState(renderState, opt)
|
|
467
|
+
captionState.preferredLanguageState = null
|
|
468
|
+
return captionState.preferredLanguages
|
|
469
|
+
}
|
|
470
|
+
|
|
425
471
|
const validateFallbackCaptionLabelOption = (optionName, fallbackOption, markRegState) => {
|
|
472
|
+
if (
|
|
473
|
+
fallbackOption !== undefined &&
|
|
474
|
+
fallbackOption !== null &&
|
|
475
|
+
fallbackOption !== false &&
|
|
476
|
+
fallbackOption !== true &&
|
|
477
|
+
typeof fallbackOption !== 'string'
|
|
478
|
+
) {
|
|
479
|
+
throw new TypeError(`${optionName} must be false, true, a string label, or null.`)
|
|
480
|
+
}
|
|
426
481
|
if (typeof fallbackOption !== 'string') return
|
|
427
482
|
const sampleCaption = buildCaptionWithFallback('caption', fallbackOption, 'img', markRegState, null)
|
|
428
483
|
const analysis = analyzeCaptionStart(sampleCaption, {
|
|
@@ -448,103 +503,6 @@ const createAutoCaptionParagraph = (captionText, TokenConstructor) => {
|
|
|
448
503
|
return [paragraphOpen, inlineToken, paragraphClose]
|
|
449
504
|
}
|
|
450
505
|
|
|
451
|
-
const getCaptionInlineToken = (tokens, range, caption) => {
|
|
452
|
-
if (caption.isPrev) {
|
|
453
|
-
const inlineIndex = range.start - 2
|
|
454
|
-
if (inlineIndex >= 0) return tokens[inlineIndex]
|
|
455
|
-
} else if (caption.isNext) {
|
|
456
|
-
return tokens[range.end + 2]
|
|
457
|
-
}
|
|
458
|
-
return null
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const hasClassName = (classAttr, className) => {
|
|
462
|
-
if (!classAttr || !className) return false
|
|
463
|
-
let index = 0
|
|
464
|
-
while (index < classAttr.length) {
|
|
465
|
-
index = classAttr.indexOf(className, index)
|
|
466
|
-
if (index === -1) return false
|
|
467
|
-
const end = index + className.length
|
|
468
|
-
const beforeBoundary = index === 0 || classAttr.charCodeAt(index - 1) <= 0x20
|
|
469
|
-
const afterBoundary = end >= classAttr.length || classAttr.charCodeAt(end) <= 0x20
|
|
470
|
-
if (beforeBoundary && afterBoundary) return true
|
|
471
|
-
index = end
|
|
472
|
-
}
|
|
473
|
-
return false
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const hasAnyClassName = (classAttr, classNames) => {
|
|
477
|
-
for (let i = 0; i < classNames.length; i++) {
|
|
478
|
-
if (hasClassName(classAttr, classNames[i])) return true
|
|
479
|
-
}
|
|
480
|
-
return false
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const getInlineLabelTextToken = (inlineToken, type, opt) => {
|
|
484
|
-
if (!inlineToken || !inlineToken.children) return null
|
|
485
|
-
const children = inlineToken.children
|
|
486
|
-
const classNames = opt.labelClassLookup[type] || opt.labelClassLookup.default
|
|
487
|
-
for (let i = 0; i < children.length; i++) {
|
|
488
|
-
const child = children[i]
|
|
489
|
-
if (!child || !child.attrs) continue
|
|
490
|
-
const classAttr = getTokenAttr(child, 'class')
|
|
491
|
-
if (!classAttr) continue
|
|
492
|
-
if (!hasAnyClassName(classAttr, classNames)) continue
|
|
493
|
-
const textToken = children[i + 1]
|
|
494
|
-
if (textToken && textToken.type === 'text') {
|
|
495
|
-
return textToken
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return null
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const updateInlineTokenContent = (inlineToken, originalText, newText) => {
|
|
502
|
-
if (!inlineToken || typeof inlineToken.content !== 'string') return
|
|
503
|
-
if (!originalText) return
|
|
504
|
-
const index = inlineToken.content.indexOf(originalText)
|
|
505
|
-
if (index === -1) return
|
|
506
|
-
inlineToken.content =
|
|
507
|
-
inlineToken.content.slice(0, index) +
|
|
508
|
-
newText +
|
|
509
|
-
inlineToken.content.slice(index + originalText.length)
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const parsePositiveInteger = (text) => {
|
|
513
|
-
if (typeof text !== 'string' || text.length === 0) return null
|
|
514
|
-
let value = 0
|
|
515
|
-
for (let i = 0; i < text.length; i++) {
|
|
516
|
-
const code = text.charCodeAt(i)
|
|
517
|
-
if (code < 0x30 || code > 0x39) return null
|
|
518
|
-
value = value * 10 + code - 0x30
|
|
519
|
-
}
|
|
520
|
-
return value
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const ensureAutoFigureNumbering = (tokens, range, caption, figureNumberState, sp, opt) => {
|
|
524
|
-
const captionType = caption.name === 'img' ? 'img' : (caption.name === 'table' ? 'table' : '')
|
|
525
|
-
if (!captionType) return
|
|
526
|
-
if (!shouldApplyLabelNumbering(captionType, opt)) return
|
|
527
|
-
const inlineToken = getCaptionInlineToken(tokens, range, caption)
|
|
528
|
-
if (!inlineToken) return
|
|
529
|
-
const labelTextToken = getInlineLabelTextToken(inlineToken, captionType, opt)
|
|
530
|
-
if (!labelTextToken || typeof labelTextToken.content !== 'string') return
|
|
531
|
-
const originalText = labelTextToken.content
|
|
532
|
-
if (sp && sp.captionDecision && sp.captionDecision.hasExplicitNumber) {
|
|
533
|
-
const explicitValue = parsePositiveInteger(sp.captionDecision.number)
|
|
534
|
-
if (explicitValue !== null && explicitValue > (figureNumberState[captionType] || 0)) {
|
|
535
|
-
figureNumberState[captionType] = explicitValue
|
|
536
|
-
}
|
|
537
|
-
return
|
|
538
|
-
}
|
|
539
|
-
figureNumberState[captionType] = (figureNumberState[captionType] || 0) + 1
|
|
540
|
-
const baseLabel = originalText.trim()
|
|
541
|
-
if (!baseLabel) return
|
|
542
|
-
const joint = asciiLabelReg.test(baseLabel) ? ' ' : ''
|
|
543
|
-
const newLabelText = baseLabel + joint + figureNumberState[captionType]
|
|
544
|
-
labelTextToken.content = newLabelText
|
|
545
|
-
updateInlineTokenContent(inlineToken, originalText, newLabelText)
|
|
546
|
-
}
|
|
547
|
-
|
|
548
506
|
const matchAutoCaptionText = (text, opt, preferredMark = 'img') => {
|
|
549
507
|
if (!text || !opt || !opt.markRegState) return ''
|
|
550
508
|
const trimmed = text.trim()
|
|
@@ -557,7 +515,7 @@ const matchAutoCaptionText = (text, opt, preferredMark = 'img') => {
|
|
|
557
515
|
return ''
|
|
558
516
|
}
|
|
559
517
|
|
|
560
|
-
const getAutoCaptionFromImage = (imageToken, opt,
|
|
518
|
+
const getAutoCaptionFromImage = (imageToken, opt, captionState) => {
|
|
561
519
|
if (!opt.autoAltCaption && !opt.autoTitleCaption && !(opt.markRegState && opt.markRegState.markReg && opt.markRegState.markReg.img)) return null
|
|
562
520
|
|
|
563
521
|
const altText = getImageAltText(imageToken)
|
|
@@ -566,9 +524,10 @@ const getAutoCaptionFromImage = (imageToken, opt, preferredLanguages) => {
|
|
|
566
524
|
return { text: caption, source: 'alt' }
|
|
567
525
|
}
|
|
568
526
|
if (opt.autoAltCaption) {
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
527
|
+
const preferredLanguages = opt.autoAltCaption === true
|
|
528
|
+
? resolveCaptionPreferredLanguages(captionState, opt)
|
|
529
|
+
: captionState.preferredLanguages
|
|
530
|
+
caption = buildCaptionWithFallback(altText, opt.autoAltCaption, 'img', opt.markRegState, preferredLanguages)
|
|
572
531
|
}
|
|
573
532
|
if (caption) return { text: caption, source: 'alt' }
|
|
574
533
|
|
|
@@ -578,9 +537,10 @@ const getAutoCaptionFromImage = (imageToken, opt, preferredLanguages) => {
|
|
|
578
537
|
return { text: caption, source: 'title' }
|
|
579
538
|
}
|
|
580
539
|
if (opt.autoTitleCaption) {
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
540
|
+
const preferredLanguages = opt.autoTitleCaption === true
|
|
541
|
+
? resolveCaptionPreferredLanguages(captionState, opt)
|
|
542
|
+
: captionState.preferredLanguages
|
|
543
|
+
caption = buildCaptionWithFallback(titleText, opt.autoTitleCaption, 'img', opt.markRegState, preferredLanguages)
|
|
584
544
|
}
|
|
585
545
|
return caption ? { text: caption, source: 'title' } : null
|
|
586
546
|
}
|
|
@@ -594,6 +554,33 @@ const consumeAutoCaptionSource = (imageToken, autoCaption) => {
|
|
|
594
554
|
}
|
|
595
555
|
}
|
|
596
556
|
|
|
557
|
+
const setFigureCaptionParagraph = (index, captionState, caption, sp, opt) => {
|
|
558
|
+
const needsCaptionDrivenClassBeforeApply = !!(
|
|
559
|
+
opt.labelClassFollowsFigure &&
|
|
560
|
+
caption.name === 'iframe' &&
|
|
561
|
+
!opt.allIframeTypeFigureClassName
|
|
562
|
+
)
|
|
563
|
+
if (!needsCaptionDrivenClassBeforeApply) {
|
|
564
|
+
return setCaptionParagraph(index, captionState, caption, captionState.numberingRuntime, sp, opt)
|
|
565
|
+
}
|
|
566
|
+
const decision = analyzeCaptionParagraph(index, captionState, {
|
|
567
|
+
captionName: caption.name,
|
|
568
|
+
isIframeTypeBlockquote: sp.isIframeTypeBlockquote,
|
|
569
|
+
isVideoIframe: sp.isVideoIframe,
|
|
570
|
+
}, opt)
|
|
571
|
+
if (!decision) return false
|
|
572
|
+
// Figure-following label classes need the final caption-driven wrapper class
|
|
573
|
+
// before p-captions constructs the label/body spans.
|
|
574
|
+
applyCaptionDrivenFigureClass(caption, sp, opt, decision)
|
|
575
|
+
return applyCaptionParagraph(
|
|
576
|
+
decision,
|
|
577
|
+
captionState,
|
|
578
|
+
sp,
|
|
579
|
+
captionState.numberingRuntime,
|
|
580
|
+
opt,
|
|
581
|
+
)
|
|
582
|
+
}
|
|
583
|
+
|
|
597
584
|
const checkPrevCaption = (tokens, n, caption, sp, opt, captionState) => {
|
|
598
585
|
if (n < 3) return
|
|
599
586
|
const captionStartToken = tokens[n - 3]
|
|
@@ -601,7 +588,7 @@ const checkPrevCaption = (tokens, n, caption, sp, opt, captionState) => {
|
|
|
601
588
|
const captionEndToken = tokens[n - 1]
|
|
602
589
|
if (captionStartToken === undefined || captionEndToken === undefined) return
|
|
603
590
|
if (captionStartToken.type !== 'paragraph_open' || captionEndToken.type !== 'paragraph_close') return
|
|
604
|
-
|
|
591
|
+
setFigureCaptionParagraph(n - 3, captionState, caption, sp, opt)
|
|
605
592
|
const captionName = sp && sp.captionDecision ? sp.captionDecision.mark : ''
|
|
606
593
|
if (!captionName) {
|
|
607
594
|
if (opt.labelPrefixMarkerWithoutLabelPrevReg) {
|
|
@@ -624,7 +611,7 @@ const checkNextCaption = (tokens, en, caption, sp, opt, captionState) => {
|
|
|
624
611
|
const captionEndToken = tokens[en + 3]
|
|
625
612
|
if (captionStartToken === undefined || captionEndToken === undefined) return
|
|
626
613
|
if (captionStartToken.type !== 'paragraph_open' || captionEndToken.type !== 'paragraph_close') return
|
|
627
|
-
|
|
614
|
+
setFigureCaptionParagraph(en + 1, captionState, caption, sp, opt)
|
|
628
615
|
const captionName = sp && sp.captionDecision ? sp.captionDecision.mark : ''
|
|
629
616
|
if (!captionName) {
|
|
630
617
|
if (opt.labelPrefixMarkerWithoutLabelNextReg) {
|
|
@@ -685,24 +672,26 @@ const resolveFigureClassName = (checkTokenTagName, sp, opt) => {
|
|
|
685
672
|
return className
|
|
686
673
|
}
|
|
687
674
|
|
|
688
|
-
const applyCaptionDrivenFigureClass = (caption, sp, opt) => {
|
|
675
|
+
const applyCaptionDrivenFigureClass = (caption, sp, opt, decision = sp && sp.captionDecision) => {
|
|
689
676
|
if (!sp) return
|
|
690
677
|
const figureClassForSlides = opt.figureClassThatWrapsSlides
|
|
691
678
|
if (!figureClassForSlides) return
|
|
692
|
-
const detectedMark = (
|
|
679
|
+
const detectedMark = (decision && decision.mark) || (caption && caption.name) || ''
|
|
693
680
|
if (detectedMark !== 'slide') return
|
|
694
681
|
if (opt.allIframeTypeFigureClassName && sp.figureClassName === opt.allIframeTypeFigureClassName) return
|
|
682
|
+
if (sp.figureClassName === figureClassForSlides) return
|
|
695
683
|
sp.figureClassName = figureClassForSlides
|
|
696
684
|
}
|
|
697
685
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
686
|
+
const convertCaptionTokens = (
|
|
687
|
+
captionStartToken,
|
|
688
|
+
captionInlineToken,
|
|
689
|
+
captionEndToken,
|
|
690
|
+
figureBaseLevel,
|
|
691
|
+
captionName,
|
|
692
|
+
opt,
|
|
693
|
+
) => {
|
|
694
|
+
cleanCaptionTokenAttrs(captionStartToken, captionName, opt)
|
|
706
695
|
captionStartToken.type = 'figcaption_open'
|
|
707
696
|
captionStartToken.tag = 'figcaption'
|
|
708
697
|
captionStartToken.block = true
|
|
@@ -712,31 +701,61 @@ const changePrevCaptionPosition = (tokens, n, caption, opt) => {
|
|
|
712
701
|
captionEndToken.tag = 'figcaption'
|
|
713
702
|
captionEndToken.block = true
|
|
714
703
|
captionEndToken.level = figureBaseLevel + 1
|
|
715
|
-
|
|
716
|
-
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const changePrevCaptionPosition = (tokens, n, caption, opt) => {
|
|
707
|
+
const captionStartToken = tokens[n-3]
|
|
708
|
+
const captionInlineToken = tokens[n-2]
|
|
709
|
+
const captionEndToken = tokens[n-1]
|
|
710
|
+
const figureStartToken = tokens[n]
|
|
711
|
+
const figureStartPaddingToken = tokens[n+1]
|
|
712
|
+
convertCaptionTokens(
|
|
713
|
+
captionStartToken,
|
|
714
|
+
captionInlineToken,
|
|
715
|
+
captionEndToken,
|
|
716
|
+
getTokenLevel(tokens[n]),
|
|
717
|
+
caption.name,
|
|
718
|
+
opt,
|
|
719
|
+
)
|
|
720
|
+
tokens[n-3] = figureStartToken
|
|
721
|
+
tokens[n-2] = figureStartPaddingToken
|
|
722
|
+
tokens[n-1] = captionStartToken
|
|
723
|
+
tokens[n] = captionInlineToken
|
|
724
|
+
tokens[n+1] = captionEndToken
|
|
717
725
|
}
|
|
718
726
|
|
|
719
727
|
const changeNextCaptionPosition = (tokens, en, caption, opt) => {
|
|
728
|
+
const figureEndToken = tokens[en]
|
|
720
729
|
const captionStartToken = tokens[en+1]
|
|
721
730
|
const captionInlineToken = tokens[en+2]
|
|
722
731
|
const captionEndToken = tokens[en+3]
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
tokens
|
|
735
|
-
tokens.splice(en+4, 3)
|
|
732
|
+
convertCaptionTokens(
|
|
733
|
+
captionStartToken,
|
|
734
|
+
captionInlineToken,
|
|
735
|
+
captionEndToken,
|
|
736
|
+
getTokenLevel(tokens[en]),
|
|
737
|
+
caption.name,
|
|
738
|
+
opt,
|
|
739
|
+
)
|
|
740
|
+
tokens[en] = captionStartToken
|
|
741
|
+
tokens[en+1] = captionInlineToken
|
|
742
|
+
tokens[en+2] = captionEndToken
|
|
743
|
+
tokens[en+3] = figureEndToken
|
|
736
744
|
}
|
|
737
745
|
|
|
738
746
|
const getTokenMap = (token) => {
|
|
739
|
-
|
|
747
|
+
if (!token || !Array.isArray(token.map) || token.map.length !== 2) return null
|
|
748
|
+
const startLine = token.map[0]
|
|
749
|
+
const endLine = token.map[1]
|
|
750
|
+
if (
|
|
751
|
+
!Number.isSafeInteger(startLine) ||
|
|
752
|
+
!Number.isSafeInteger(endLine) ||
|
|
753
|
+
startLine < 0 ||
|
|
754
|
+
endLine < startLine
|
|
755
|
+
) {
|
|
756
|
+
return null
|
|
757
|
+
}
|
|
758
|
+
return token.map
|
|
740
759
|
}
|
|
741
760
|
|
|
742
761
|
const findNearestMapInRange = (tokens, start, end, step) => {
|
|
@@ -755,9 +774,6 @@ const getRangeMap = (tokens, start, end) => {
|
|
|
755
774
|
const endMap = getTokenMap(tokens[end]) || findNearestMapInRange(tokens, end, start, -1) || startMap
|
|
756
775
|
const startLine = startMap[0]
|
|
757
776
|
const endLine = Math.max(startMap[1], endMap[1])
|
|
758
|
-
if (typeof startLine !== 'number' || typeof endLine !== 'number' || endLine < startLine) {
|
|
759
|
-
return [startMap[0], startMap[1]]
|
|
760
|
-
}
|
|
761
777
|
return [startLine, endLine]
|
|
762
778
|
}
|
|
763
779
|
|
|
@@ -775,14 +791,27 @@ const adjustTokenLevels = (tokens, start, end, delta) => {
|
|
|
775
791
|
}
|
|
776
792
|
}
|
|
777
793
|
|
|
794
|
+
const createTextToken = (TokenConstructor, content, level) => {
|
|
795
|
+
const token = new TokenConstructor('text', '', 0)
|
|
796
|
+
token.content = content
|
|
797
|
+
token.level = level
|
|
798
|
+
return token
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const joinTokenAttrs = (token, attrs) => {
|
|
802
|
+
if (!attrs || attrs.length === 0) return
|
|
803
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
804
|
+
token.attrJoin(attrs[i][0], attrs[i][1])
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
778
808
|
const wrapWithFigure = (tokens, range, checkTokenTagName, caption, replaceInsteadOfWrap, sp, opt, TokenConstructor) => {
|
|
779
809
|
let n = range.start
|
|
780
810
|
let en = range.end
|
|
781
811
|
const baseLevel = getTokenLevel(tokens[n])
|
|
782
812
|
const childLevel = baseLevel + 1
|
|
783
813
|
const figureStartToken = new TokenConstructor('figure_open', 'figure', 1)
|
|
784
|
-
|
|
785
|
-
figureStartToken.attrSet('class', figureClassName)
|
|
814
|
+
figureStartToken.attrSet('class', sp.figureClassName)
|
|
786
815
|
figureStartToken.block = true
|
|
787
816
|
figureStartToken.level = baseLevel
|
|
788
817
|
|
|
@@ -797,39 +826,40 @@ const wrapWithFigure = (tokens, range, checkTokenTagName, caption, replaceInstea
|
|
|
797
826
|
figureStartToken.map = [rangeMap[0], rangeMap[1]]
|
|
798
827
|
figureEndToken.map = [rangeMap[0], rangeMap[1]]
|
|
799
828
|
}
|
|
800
|
-
const createBreakToken = () => {
|
|
801
|
-
const breakToken = new TokenConstructor('text', '', 0)
|
|
802
|
-
breakToken.content = '\n'
|
|
803
|
-
breakToken.level = childLevel
|
|
804
|
-
return breakToken
|
|
805
|
-
}
|
|
806
|
-
const createEmptyTextToken = () => {
|
|
807
|
-
const emptyToken = new TokenConstructor('text', '', 0)
|
|
808
|
-
emptyToken.content = ''
|
|
809
|
-
emptyToken.level = childLevel
|
|
810
|
-
return emptyToken
|
|
811
|
-
}
|
|
812
829
|
if (caption.name === 'img') {
|
|
813
|
-
const joinAttrs = (attrs) => {
|
|
814
|
-
if (!attrs || attrs.length === 0) return
|
|
815
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
816
|
-
const attr = attrs[i]
|
|
817
|
-
figureStartToken.attrJoin(attr[0], attr[1])
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
830
|
// `styleProcess` should keep working even when markdown-it-attrs is absent.
|
|
821
|
-
if (opt.styleProcess)
|
|
831
|
+
if (opt.styleProcess) joinTokenAttrs(figureStartToken, sp.attrs)
|
|
822
832
|
// Forward attrs already materialized by markdown-it-attrs on the image paragraph.
|
|
823
|
-
|
|
833
|
+
joinTokenAttrs(figureStartToken, tokens[n].attrs)
|
|
824
834
|
}
|
|
825
835
|
if (replaceInsteadOfWrap) {
|
|
826
|
-
tokens
|
|
827
|
-
tokens.splice(
|
|
836
|
+
const contentToken = tokens[n + 1]
|
|
837
|
+
tokens.splice(
|
|
838
|
+
n,
|
|
839
|
+
en - n + 1,
|
|
840
|
+
figureStartToken,
|
|
841
|
+
createTextToken(TokenConstructor, '', childLevel),
|
|
842
|
+
contentToken,
|
|
843
|
+
createTextToken(TokenConstructor, '\n', childLevel),
|
|
844
|
+
figureEndToken,
|
|
845
|
+
)
|
|
828
846
|
en = en + 2
|
|
847
|
+
} else if (n === en) {
|
|
848
|
+
const contentToken = tokens[n]
|
|
849
|
+
adjustTokenLevels(tokens, n, en, 1)
|
|
850
|
+
tokens.splice(
|
|
851
|
+
n,
|
|
852
|
+
1,
|
|
853
|
+
figureStartToken,
|
|
854
|
+
createTextToken(TokenConstructor, '', childLevel),
|
|
855
|
+
contentToken,
|
|
856
|
+
figureEndToken,
|
|
857
|
+
)
|
|
858
|
+
en = en + 3
|
|
829
859
|
} else {
|
|
830
860
|
adjustTokenLevels(tokens, n, en, 1)
|
|
831
861
|
tokens.splice(en+1, 0, figureEndToken)
|
|
832
|
-
tokens.splice(n, 0, figureStartToken,
|
|
862
|
+
tokens.splice(n, 0, figureStartToken, createTextToken(TokenConstructor, '', childLevel))
|
|
833
863
|
en = en + 3
|
|
834
864
|
}
|
|
835
865
|
range.start = n
|
|
@@ -842,20 +872,6 @@ const checkCaption = (tokens, n, en, caption, sp, opt, captionState) => {
|
|
|
842
872
|
checkNextCaption(tokens, en, caption, sp, opt, captionState)
|
|
843
873
|
}
|
|
844
874
|
|
|
845
|
-
const getNestedContainerType = (token) => {
|
|
846
|
-
if (!token) return null
|
|
847
|
-
switch (token.type) {
|
|
848
|
-
case 'blockquote_open':
|
|
849
|
-
return 'blockquote'
|
|
850
|
-
case 'list_item_open':
|
|
851
|
-
return 'list_item'
|
|
852
|
-
case 'dd_open':
|
|
853
|
-
return 'dd'
|
|
854
|
-
default:
|
|
855
|
-
return null
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
875
|
const resetRangeState = (range, start) => {
|
|
860
876
|
range.start = start
|
|
861
877
|
range.end = start
|
|
@@ -874,6 +890,7 @@ const resetSpecialState = (sp) => {
|
|
|
874
890
|
sp.isIframeTypeBlockquote = false
|
|
875
891
|
sp.figureClassName = ''
|
|
876
892
|
sp.captionDecision = null
|
|
893
|
+
sp.numbering = null
|
|
877
894
|
}
|
|
878
895
|
|
|
879
896
|
const findClosingTokenIndex = (tokens, startIndex, tag) => {
|
|
@@ -890,19 +907,20 @@ const findClosingTokenIndex = (tokens, startIndex, tag) => {
|
|
|
890
907
|
}
|
|
891
908
|
i++
|
|
892
909
|
}
|
|
893
|
-
return
|
|
910
|
+
return -1
|
|
894
911
|
}
|
|
895
912
|
|
|
896
913
|
const detectCheckTypeOpen = (tokens, token, n, caption, baseType) => {
|
|
897
914
|
if (!token || !baseType) return null
|
|
898
915
|
if (n > 1 && tokens[n - 2] && tokens[n - 2].type === 'figure_open') return null
|
|
899
|
-
|
|
916
|
+
const tagName = token.tag
|
|
900
917
|
caption.name = baseType
|
|
901
918
|
if (baseType === 'pre') {
|
|
902
919
|
if (tokens[n + 1] && tokens[n + 1].tag === 'code') caption.name = 'pre-code'
|
|
903
920
|
if (tokens[n + 1] && tokens[n + 1].tag === 'samp') caption.name = 'pre-samp'
|
|
904
921
|
}
|
|
905
922
|
const en = findClosingTokenIndex(tokens, n, tagName)
|
|
923
|
+
if (en < 0) return null
|
|
906
924
|
return {
|
|
907
925
|
type: 'block',
|
|
908
926
|
tagName,
|
|
@@ -915,11 +933,8 @@ const detectCheckTypeOpen = (tokens, token, n, caption, baseType) => {
|
|
|
915
933
|
|
|
916
934
|
const detectFenceToken = (token, n, caption) => {
|
|
917
935
|
if (!token || token.type !== 'fence' || token.tag !== 'code' || !token.block) return null
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
token.tag = 'samp'
|
|
921
|
-
tagName = 'pre-samp'
|
|
922
|
-
}
|
|
936
|
+
const useSampTag = sampLangReg.test(token.info)
|
|
937
|
+
const tagName = useSampTag ? 'pre-samp' : 'pre-code'
|
|
923
938
|
caption.name = tagName
|
|
924
939
|
return {
|
|
925
940
|
type: 'fence',
|
|
@@ -928,6 +943,8 @@ const detectFenceToken = (token, n, caption) => {
|
|
|
928
943
|
replaceInsteadOfWrap: false,
|
|
929
944
|
wrapWithoutCaption: false,
|
|
930
945
|
canWrap: true,
|
|
946
|
+
token,
|
|
947
|
+
useSampTag,
|
|
931
948
|
}
|
|
932
949
|
}
|
|
933
950
|
|
|
@@ -1056,28 +1073,37 @@ const applyImageParagraphTransform = (detection) => {
|
|
|
1056
1073
|
}
|
|
1057
1074
|
}
|
|
1058
1075
|
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1076
|
+
const applyWrappedCandidateTransform = (detection) => {
|
|
1077
|
+
if (detection.type === 'image') {
|
|
1078
|
+
applyImageParagraphTransform(detection)
|
|
1079
|
+
} else if (detection.type === 'fence' && detection.useSampTag) {
|
|
1080
|
+
detection.token.tag = 'samp'
|
|
1063
1081
|
}
|
|
1082
|
+
}
|
|
1064
1083
|
|
|
1084
|
+
const figureWithCaption = (state, opt) => {
|
|
1085
|
+
const numberingRuntime = opt.captionNumberingPolicy
|
|
1086
|
+
? createCaptionNumberingRuntime(opt.captionNumberingPolicy, { env: state.env })
|
|
1087
|
+
: null
|
|
1088
|
+
const numberingScopeState = numberingRuntime
|
|
1089
|
+
? createFigureCaptionScopeRuntime(
|
|
1090
|
+
state,
|
|
1091
|
+
opt.normalizedAutoLabelNumberPolicy,
|
|
1092
|
+
opt.unscopedNumberingContext,
|
|
1093
|
+
)
|
|
1094
|
+
: null
|
|
1065
1095
|
const captionState = {
|
|
1066
1096
|
tokens: state.tokens,
|
|
1067
1097
|
Token: state.Token,
|
|
1068
1098
|
preferredLanguages: opt.preferredLanguages,
|
|
1099
|
+
numberingRuntime,
|
|
1100
|
+
numberingScopeState,
|
|
1069
1101
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
sourceMayNeedPreferredLanguages(state)
|
|
1073
|
-
)
|
|
1074
|
-
if (shouldResolvePreferredLanguages) {
|
|
1075
|
-
captionState.preferredLanguages = resolvePreferredLanguagesForState(state, opt)
|
|
1076
|
-
}
|
|
1077
|
-
figureWithCaptionCore(state.tokens, opt, figureNumberState, state.Token, captionState, null, 0)
|
|
1102
|
+
if (opt.shouldResolvePreferredLanguages) captionState.preferredLanguageState = state
|
|
1103
|
+
figureWithCaptionCore(state.tokens, opt, state.Token, captionState, null, 0)
|
|
1078
1104
|
}
|
|
1079
1105
|
|
|
1080
|
-
const figureWithCaptionCore = (tokens, opt,
|
|
1106
|
+
const figureWithCaptionCore = (tokens, opt, TokenConstructor, captionState, parentType = null, startIndex = 0) => {
|
|
1081
1107
|
const rRange = { start: startIndex, end: startIndex }
|
|
1082
1108
|
const rCaption = {
|
|
1083
1109
|
name: '', nameSuffix: '', isPrev: false, isNext: false
|
|
@@ -1087,21 +1113,31 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
|
|
|
1087
1113
|
isVideoIframe: false,
|
|
1088
1114
|
isIframeTypeBlockquote: false,
|
|
1089
1115
|
figureClassName: '',
|
|
1090
|
-
captionDecision: null
|
|
1091
|
-
|
|
1116
|
+
captionDecision: null,
|
|
1117
|
+
numbering: null,
|
|
1118
|
+
}
|
|
1119
|
+
const numberingScopeState = parentType ? null : captionState.numberingScopeState
|
|
1120
|
+
const headingScopeState = numberingScopeState &&
|
|
1121
|
+
!numberingScopeState.fixed &&
|
|
1122
|
+
numberingScopeState.scopeConfig.usesHeading
|
|
1123
|
+
? numberingScopeState
|
|
1124
|
+
: null
|
|
1125
|
+
const parentCloseType = parentType ? parentType + '_close' : ''
|
|
1092
1126
|
let n = startIndex
|
|
1093
1127
|
while (n < tokens.length) {
|
|
1094
1128
|
const token = tokens[n]
|
|
1095
|
-
|
|
1129
|
+
if (headingScopeState && token.type === 'heading_open') {
|
|
1130
|
+
updateFigureCaptionScopeFromHeading(tokens, n, headingScopeState)
|
|
1131
|
+
}
|
|
1132
|
+
const containerType = getFigureCaptionScopeNestedContainerType(token)
|
|
1096
1133
|
|
|
1097
1134
|
if (containerType && containerType !== 'blockquote') {
|
|
1098
|
-
const closeIndex = figureWithCaptionCore(tokens, opt,
|
|
1099
|
-
n =
|
|
1135
|
+
const closeIndex = figureWithCaptionCore(tokens, opt, TokenConstructor, captionState, containerType, n + 1)
|
|
1136
|
+
n = closeIndex + 1
|
|
1100
1137
|
continue
|
|
1101
1138
|
}
|
|
1102
1139
|
|
|
1103
|
-
|
|
1104
|
-
if (parentType && token.type === `${parentType}_close`) {
|
|
1140
|
+
if (parentCloseType && token.type === parentCloseType) {
|
|
1105
1141
|
return n
|
|
1106
1142
|
}
|
|
1107
1143
|
|
|
@@ -1140,8 +1176,8 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
|
|
|
1140
1176
|
|
|
1141
1177
|
if (!detection) {
|
|
1142
1178
|
if (containerType === 'blockquote') {
|
|
1143
|
-
const closeIndex = figureWithCaptionCore(tokens, opt,
|
|
1144
|
-
n =
|
|
1179
|
+
const closeIndex = figureWithCaptionCore(tokens, opt, TokenConstructor, captionState, containerType, n + 1)
|
|
1180
|
+
n = closeIndex + 1
|
|
1145
1181
|
} else {
|
|
1146
1182
|
n++
|
|
1147
1183
|
}
|
|
@@ -1150,61 +1186,77 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
|
|
|
1150
1186
|
|
|
1151
1187
|
rRange.end = detection.en
|
|
1152
1188
|
|
|
1189
|
+
if (detection.canWrap === false || (detection.type === 'image' && token.hidden === true)) {
|
|
1190
|
+
let nextIndex = rRange.end + 1
|
|
1191
|
+
if (containerType === 'blockquote') {
|
|
1192
|
+
const closeIndex = figureWithCaptionCore(tokens, opt, TokenConstructor, captionState, containerType, rRange.start + 1)
|
|
1193
|
+
nextIndex = Math.max(nextIndex, closeIndex + 1)
|
|
1194
|
+
}
|
|
1195
|
+
n = nextIndex
|
|
1196
|
+
continue
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
if (detection.type === 'html') {
|
|
1200
|
+
rRange.end = applyHtmlFigureTransform(tokens, detection)
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (captionState.numberingRuntime) {
|
|
1204
|
+
rSp.numbering = captionState.numberingScopeState
|
|
1205
|
+
? captionState.numberingScopeState.currentContext
|
|
1206
|
+
: opt.unscopedNumberingContext
|
|
1207
|
+
}
|
|
1153
1208
|
rSp.figureClassName = resolveFigureClassName(detection.tagName, rSp, opt)
|
|
1154
1209
|
checkCaption(tokens, rRange.start, rRange.end, rCaption, rSp, opt, captionState)
|
|
1155
|
-
applyCaptionDrivenFigureClass(rCaption, rSp, opt)
|
|
1156
1210
|
|
|
1157
1211
|
let hasCaption = rCaption.isPrev || rCaption.isNext
|
|
1212
|
+
if (hasCaption) applyCaptionDrivenFigureClass(rCaption, rSp, opt)
|
|
1158
1213
|
let pendingAutoCaption = null
|
|
1159
1214
|
if (!hasCaption && detection.type === 'image' && opt.autoCaptionDetection) {
|
|
1160
|
-
pendingAutoCaption = getAutoCaptionFromImage(detection.imageToken, opt, captionState
|
|
1215
|
+
pendingAutoCaption = getAutoCaptionFromImage(detection.imageToken, opt, captionState)
|
|
1161
1216
|
if (pendingAutoCaption) {
|
|
1162
1217
|
hasCaption = true
|
|
1163
1218
|
}
|
|
1164
1219
|
}
|
|
1165
1220
|
|
|
1166
|
-
if (detection.canWrap === false) {
|
|
1167
|
-
let nextIndex = rRange.end + 1
|
|
1168
|
-
if (containerType === 'blockquote') {
|
|
1169
|
-
const closeIndex = figureWithCaptionCore(tokens, opt, figureNumberState, TokenConstructor, captionState, containerType, rRange.start + 1)
|
|
1170
|
-
nextIndex = Math.max(nextIndex, (typeof closeIndex === 'number' ? closeIndex : rRange.end) + 1)
|
|
1171
|
-
}
|
|
1172
|
-
n = nextIndex
|
|
1173
|
-
continue
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
1221
|
let shouldWrap = hasCaption
|
|
1177
1222
|
if (detection.type === 'html' || detection.type === 'image') {
|
|
1178
1223
|
shouldWrap = shouldWrap || detection.wrapWithoutCaption
|
|
1179
1224
|
}
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const insertedLength = captionTokens.length
|
|
1190
|
-
rRange.start += insertedLength
|
|
1191
|
-
rRange.end += insertedLength
|
|
1192
|
-
n += insertedLength
|
|
1225
|
+
if (pendingAutoCaption) {
|
|
1226
|
+
const captionTokens = createAutoCaptionParagraph(pendingAutoCaption.text, TokenConstructor)
|
|
1227
|
+
const insertIndex = rRange.start
|
|
1228
|
+
const insertedLength = captionTokens.length
|
|
1229
|
+
tokens.splice(insertIndex, 0, ...captionTokens)
|
|
1230
|
+
rRange.start += insertedLength
|
|
1231
|
+
rRange.end += insertedLength
|
|
1232
|
+
n += insertedLength
|
|
1233
|
+
try {
|
|
1193
1234
|
checkCaption(tokens, rRange.start, rRange.end, rCaption, rSp, opt, captionState)
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
tokens.splice(insertIndex, insertedLength)
|
|
1237
|
+
throw error
|
|
1238
|
+
}
|
|
1239
|
+
if (rCaption.isPrev || rCaption.isNext) {
|
|
1240
|
+
consumeAutoCaptionSource(detection.imageToken, pendingAutoCaption)
|
|
1194
1241
|
applyCaptionDrivenFigureClass(rCaption, rSp, opt)
|
|
1242
|
+
} else {
|
|
1243
|
+
tokens.splice(insertIndex, insertedLength)
|
|
1244
|
+
rRange.start -= insertedLength
|
|
1245
|
+
rRange.end -= insertedLength
|
|
1246
|
+
n -= insertedLength
|
|
1247
|
+
shouldWrap = detection.wrapWithoutCaption
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (shouldWrap) {
|
|
1251
|
+
if (detection.type !== 'html') {
|
|
1252
|
+
applyWrappedCandidateTransform(detection)
|
|
1195
1253
|
}
|
|
1196
|
-
applyImageParagraphTransform(detection)
|
|
1197
|
-
ensureAutoFigureNumbering(tokens, rRange, rCaption, figureNumberState, rSp, opt)
|
|
1198
1254
|
wrapWithFigure(tokens, rRange, detection.tagName, rCaption, detection.replaceInsteadOfWrap, rSp, opt, TokenConstructor)
|
|
1199
1255
|
}
|
|
1200
1256
|
|
|
1201
1257
|
let nextIndex
|
|
1202
1258
|
if (!rCaption.isPrev && !rCaption.isNext) {
|
|
1203
|
-
|
|
1204
|
-
nextIndex = rRange.end + 1
|
|
1205
|
-
} else {
|
|
1206
|
-
nextIndex = n + 1
|
|
1207
|
-
}
|
|
1259
|
+
nextIndex = shouldWrap ? rRange.end + 1 : n + 1
|
|
1208
1260
|
} else {
|
|
1209
1261
|
const en = rRange.end
|
|
1210
1262
|
if (rCaption.isPrev) {
|
|
@@ -1217,9 +1269,16 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
|
|
|
1217
1269
|
}
|
|
1218
1270
|
|
|
1219
1271
|
if (containerType === 'blockquote') {
|
|
1220
|
-
const
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1272
|
+
const nestedContentStart = rRange.start + (shouldWrap ? 3 : 1)
|
|
1273
|
+
const closeIndex = figureWithCaptionCore(
|
|
1274
|
+
tokens,
|
|
1275
|
+
opt,
|
|
1276
|
+
TokenConstructor,
|
|
1277
|
+
captionState,
|
|
1278
|
+
containerType,
|
|
1279
|
+
nestedContentStart,
|
|
1280
|
+
)
|
|
1281
|
+
nextIndex = Math.max(nextIndex, closeIndex + 1)
|
|
1223
1282
|
}
|
|
1224
1283
|
|
|
1225
1284
|
n = nextIndex
|
|
@@ -1228,6 +1287,10 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
|
|
|
1228
1287
|
}
|
|
1229
1288
|
|
|
1230
1289
|
const mditFigureWithPCaption = (md, option) => {
|
|
1290
|
+
if (md[installedKey]) return
|
|
1291
|
+
if (hasOwnOption(option, 'setFigureNumber')) {
|
|
1292
|
+
throw new Error('setFigureNumber is not supported by figure-with-p-caption; use autoLabelNumber or autoLabelNumberSets')
|
|
1293
|
+
}
|
|
1231
1294
|
const opt = {
|
|
1232
1295
|
// Caption languages delegated to p-captions.
|
|
1233
1296
|
languages: ['en', 'ja'],
|
|
@@ -1261,7 +1324,8 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1261
1324
|
|
|
1262
1325
|
// --- numbering controls ---
|
|
1263
1326
|
autoLabelNumber: false, // shorthand for enabling numbering for both img/table unless autoLabelNumberSets is provided explicitly
|
|
1264
|
-
autoLabelNumberSets: [], // preferred; supports
|
|
1327
|
+
autoLabelNumberSets: [], // preferred; supports img/table/code/samp/video marks
|
|
1328
|
+
autoLabelNumberPolicy: undefined, // omitted: automatic Chapter/Appendix scope; explicit null: document-wide numbering
|
|
1265
1329
|
|
|
1266
1330
|
// --- caption text formatting (delegated to p7d-markdown-it-p-captions) ---
|
|
1267
1331
|
hasNumClass: false,
|
|
@@ -1282,10 +1346,12 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1282
1346
|
const hasExplicitFigureClassThatWrapsIframeTypeBlockquote = hasOwnOption(option, 'figureClassThatWrapsIframeTypeBlockquote')
|
|
1283
1347
|
const hasExplicitFigureClassThatWrapsSlides = hasOwnOption(option, 'figureClassThatWrapsSlides')
|
|
1284
1348
|
const hasExplicitLabelClassFollowsFigure = hasOwnOption(option, 'labelClassFollowsFigure')
|
|
1285
|
-
if (hasOwnOption(option, 'setFigureNumber')) {
|
|
1286
|
-
throw new Error('setFigureNumber is not supported by figure-with-p-caption; use autoLabelNumber or autoLabelNumberSets')
|
|
1287
|
-
}
|
|
1288
1349
|
if (option) Object.assign(opt, option)
|
|
1350
|
+
if (Array.isArray(opt.removeUnnumberedLabelExceptMarks)) {
|
|
1351
|
+
opt.removeUnnumberedLabelExceptMarks = opt.removeUnnumberedLabelExceptMarks.map(
|
|
1352
|
+
canonicalizeCaptionNumberingMark,
|
|
1353
|
+
)
|
|
1354
|
+
}
|
|
1289
1355
|
opt.imageOnlyParagraphWithoutCaption = hasExplicitImageOnlyParagraphWithoutCaption
|
|
1290
1356
|
? !!opt.imageOnlyParagraphWithoutCaption
|
|
1291
1357
|
: !!opt.oneImageWithoutCaption
|
|
@@ -1297,7 +1363,6 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1297
1363
|
opt.markRegState = getMarkRegStateForLanguages(opt.languages)
|
|
1298
1364
|
opt.preferredLanguages = normalizePreferredLanguages(opt.preferredLanguages, opt.markRegState.languages)
|
|
1299
1365
|
if (opt.preferredLanguages.length === 0) opt.preferredLanguages = null
|
|
1300
|
-
opt.normalizedOptionLanguages = normalizePreferredLanguages(opt.languages, opt.markRegState.languages)
|
|
1301
1366
|
opt.shouldResolvePreferredLanguages = needsPreferredLanguagesResolution(opt)
|
|
1302
1367
|
validateFallbackCaptionLabelOption('autoAltCaption', opt.autoAltCaption, opt.markRegState)
|
|
1303
1368
|
validateFallbackCaptionLabelOption('autoTitleCaption', opt.autoTitleCaption, opt.markRegState)
|
|
@@ -1307,12 +1372,27 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1307
1372
|
audio: !!opt.audioWithoutCaption,
|
|
1308
1373
|
iframeTypeBlockquote: !!opt.iframeTypeBlockquoteWithoutCaption,
|
|
1309
1374
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1375
|
+
const requestedAutoLabelNumberSets = hasExplicitAutoLabelNumberSets
|
|
1376
|
+
? option.autoLabelNumberSets
|
|
1377
|
+
: opt.autoLabelNumber
|
|
1378
|
+
? ['img', 'table']
|
|
1379
|
+
: []
|
|
1380
|
+
opt.autoLabelNumberSets = normalizeAutoLabelNumberSets(requestedAutoLabelNumberSets)
|
|
1381
|
+
const enabledNumberingMarks = getEnabledCaptionNumberingMarks(opt.autoLabelNumberSets, opt)
|
|
1382
|
+
opt.normalizedAutoLabelNumberPolicy = enabledNumberingMarks.length === 0 &&
|
|
1383
|
+
opt.autoLabelNumberPolicy === undefined
|
|
1384
|
+
? null
|
|
1385
|
+
: normalizeFigureCaptionNumberingPolicy(opt.autoLabelNumberPolicy)
|
|
1386
|
+
opt.captionNumberingPolicy = createFigureCaptionNumberingPolicy(
|
|
1387
|
+
enabledNumberingMarks,
|
|
1388
|
+
opt.normalizedAutoLabelNumberPolicy,
|
|
1389
|
+
opt.markRegState,
|
|
1390
|
+
)
|
|
1391
|
+
opt.unscopedNumberingContext = opt.captionNumberingPolicy && opt.normalizedAutoLabelNumberPolicy
|
|
1392
|
+
? createUnscopedNumberingContext(
|
|
1393
|
+
getFigureCaptionNumberingPolicyState(opt.normalizedAutoLabelNumberPolicy).separator,
|
|
1394
|
+
)
|
|
1395
|
+
: null
|
|
1316
1396
|
const classPrefix = buildClassPrefix(opt.classPrefix)
|
|
1317
1397
|
opt.figureClassPrefix = classPrefix
|
|
1318
1398
|
opt.captionClassPrefix = classPrefix
|
|
@@ -1334,8 +1414,6 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1334
1414
|
defaultSlideFigureClass,
|
|
1335
1415
|
)
|
|
1336
1416
|
}
|
|
1337
|
-
// Precompute label-class permutations so numbering lookup doesn't rebuild arrays per caption.
|
|
1338
|
-
opt.labelClassLookup = buildLabelClassLookup(opt)
|
|
1339
1417
|
const markerList = normalizeLabelPrefixMarkers(opt.labelPrefixMarker)
|
|
1340
1418
|
opt.labelPrefixMarkerReg = buildLabelPrefixMarkerRegFromMarkers(markerList)
|
|
1341
1419
|
opt.cleanCaptionRegCache = new Map()
|
|
@@ -1352,6 +1430,10 @@ const mditFigureWithPCaption = (md, option) => {
|
|
|
1352
1430
|
md.core.ruler.before('replacements', 'figure_with_caption', (state) => {
|
|
1353
1431
|
figureWithCaption(state, opt)
|
|
1354
1432
|
})
|
|
1433
|
+
Object.defineProperty(md, installedKey, {
|
|
1434
|
+
value: true,
|
|
1435
|
+
configurable: true,
|
|
1436
|
+
})
|
|
1355
1437
|
}
|
|
1356
1438
|
|
|
1357
1439
|
export default mditFigureWithPCaption
|