@peaceroad/markdown-it-figure-with-p-caption 0.18.0 → 0.19.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 CHANGED
@@ -52,6 +52,7 @@ Optionally, you can auto-number image and table caption paragraphs starting from
52
52
 
53
53
  - Inline HTML `<iframe>` elements become `<figure class="f-video">` when they point to known video hosts (YouTube `www.youtube.com`, `youtube.com`, `www.youtube-nocookie.com`, `youtube-nocookie.com`, Vimeo `player.vimeo.com`).
54
54
  - `<div>` wrappers are treated as iframe-type embeds only when the same HTML block contains an `<iframe ...>` tag (for example common video wrapper markup).
55
+ - `videoWithoutCaption` classifies an iframe (including one nested in a `<div>` wrapper) as video only when its `src` host is one of the known YouTube/Vimeo hosts. A generic iframe wrapper still requires `iframeWithoutCaption`.
55
56
  - Blockquote-based social embeds (Twitter/X `twitter-tweet`, Mastodon `mastodon-embed`, Bluesky `bluesky-embed`, Instagram `instagram-media`, Tumblr `text-post-media`) are treated like iframe-type embeds when their class list contains one of those provider classes. Extra classes on the same blockquote do not block detection. By default they become `<figure class="f-img">` so the caption label behaves like an image label (Labels can also use quote labels). You can override that figure class with `figureClassThatWrapsIframeTypeBlockquote` or the global `allIframeTypeFigureClassName`.
56
57
  - `p7d-markdown-it-p-captions` ships with a `Slide.` label. When you use it (for example with Speaker Deck or SlideShare iframes), the `<figure>` wrapper automatically switches to `f-slide` (or whatever you set via `figureClassThatWrapsSlides`) so slides can get their own layout. If `allIframeTypeFigureClassName` is also configured, that class takes precedence even for slides, so you get a uniform embed wrapper without touching the slide option.
57
58
  - All other iframes fall back to `<figure class="f-iframe">` unless you override the class via `allIframeTypeFigureClassName`.
@@ -102,9 +103,10 @@ Every option below is forwarded verbatim to `p7d-markdown-it-p-captions`, which
102
103
 
103
104
  - `autoLabelNumberSets`: enable numbering per media type. Pass an array such as `['img']`, `['table']`, or `['img', 'table']`.
104
105
  - `autoLabelNumber`: shorthand for turning numbering on for both images and tables without passing the array yourself. Provide `autoLabelNumberSets` explicitly (e.g., `['img']`) when you need finer control—the explicit array always wins.
106
+ - Do not pass p-captions' lower-level `setFigureNumber` option to this plugin. Figure numbering is owned by `autoLabelNumber` / `autoLabelNumberSets`; `setFigureNumber` is rejected during setup to prevent two numbering systems from mutating the same caption.
105
107
  - Counters start at `1` near the top of the document and increment sequentially per media type. Figures and tables keep independent counters even when mixed together.
106
108
  - The counter only advances when a real caption exists (paragraph, auto-detected alt/title, or fallback text). Figures emitted solely because of `imageOnlyParagraphWithoutCaption` / `oneImageWithoutCaption` stay unnumbered.
107
- - Manual numbers inside the caption text (e.g., `Figure 5.`) always win. The plugin updates its internal counter so the next automatic number becomes `6`. This applies to captions sourced from paragraphs, auto detection, and fallback captions.
109
+ - Manual numbers inside the caption text (e.g., `Figure 5.`) always win. The plugin reads the exact `captionDecision.number` supplied by `p7d-markdown-it-p-captions` and updates its decimal counter so the next automatic number becomes `6`. Explicit compound/alphanumeric numbers (e.g., `Figure A.` or `Figure A.5.`) are preserved without appending an automatic number and do not seed the decimal counter. This applies to captions sourced from paragraphs, auto detection, and fallback captions.
108
110
 
109
111
  ## Basic Usage
110
112
 
@@ -362,6 +364,8 @@ Slide. A Speaker Deck.
362
364
 
363
365
  ### Auto alt/title detection
364
366
 
367
+ The consumed `alt` or `title` value is cleared only after the image paragraph is confirmed to be wrappable. Tight-list images and image paragraphs with trailing non-image text keep their original accessibility attributes and baseline rendering.
368
+
365
369
  ```
366
370
  [Markdown]
367
371
  ![Figure. A cat.](cat.jpg)
package/embeds/detect.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
 
7
7
  const htmlRegCache = new Map()
8
8
  const openingClassAttrReg = /^<[^>]*?\bclass=(?:"([^"]*)"|'([^']*)')/i
9
- const openingSrcAttrReg = /^<[^>]*?\bsrc=(?:"([^"]*)"|'([^']*)')/i
9
+ const iframeSrcAttrReg = /<iframe\b[^>]*?\bsrc=(?:"([^"]*)"|'([^']*)')/i
10
10
  const endBlockquoteScriptReg = /<\/blockquote> *<script[^>]*?><\/script>$/i
11
11
  const targetHtmlHintReg = /<(?:video|audio|iframe|blockquote|div)\b/i
12
12
  const blueskyEmbedHintReg = /bluesky-embed/i
@@ -90,6 +90,13 @@ const getOpeningAttrValue = (content, reg) => {
90
90
  return match[1] || match[2] || ''
91
91
  }
92
92
 
93
+ const getAttrValue = (content, reg) => {
94
+ if (typeof content !== 'string') return ''
95
+ const match = content.match(reg)
96
+ if (!match) return ''
97
+ return match[1] || match[2] || ''
98
+ }
99
+
93
100
  const hasKnownBlockquoteEmbedClass = (content) => {
94
101
  const classAttr = getOpeningAttrValue(content, openingClassAttrReg)
95
102
  if (!classAttr) return false
@@ -106,14 +113,14 @@ const hasKnownBlockquoteEmbedClass = (content) => {
106
113
  }
107
114
 
108
115
  const isKnownVideoIframe = (content) => {
109
- const src = getOpeningAttrValue(content, openingSrcAttrReg)
116
+ const src = getAttrValue(content, iframeSrcAttrReg)
110
117
  if (!src || src.slice(0, 8).toLowerCase() !== 'https://') return false
111
118
  const slashIndex = src.indexOf('/', 8)
112
119
  const host = (slashIndex === -1 ? src.slice(8) : src.slice(8, slashIndex)).toLowerCase()
113
120
  return VIDEO_IFRAME_HOSTS.has(host)
114
121
  }
115
122
 
116
- const detectHtmlTagCandidate = (tokens, token, startIndex, detector, hints, result) => {
123
+ const detectHtmlTagCandidate = (tokens, token, startIndex, detector, hints) => {
117
124
  if (detector.requiresIframeTag && !hints.hasIframeTag) return ''
118
125
  const hasTagHint = !!(detector.hintKey && hints[detector.hintKey])
119
126
  const allowBlueskyFallback = detector.candidate === 'blockquote' && hints.hasBlueskyHint
@@ -123,9 +130,6 @@ const detectHtmlTagCandidate = (tokens, token, startIndex, detector, hints, resu
123
130
  if (!hasTag && !isBlueskyFallback) return ''
124
131
  if (hasTag) {
125
132
  appendHtmlBlockNewlineIfNeeded(token, hasTag)
126
- if (detector.treatAsVideoIframe) {
127
- result.isVideoIframe = true
128
- }
129
133
  return detector.matchedTag || detector.candidate
130
134
  }
131
135
  consumeBlockquoteEmbedScript(tokens, token, startIndex)
@@ -155,7 +159,7 @@ export const detectHtmlFigureCandidate = (tokens, token, startIndex, htmlWrapWit
155
159
 
156
160
  let matchedTag = ''
157
161
  for (let i = 0; i < HTML_EMBED_CANDIDATES.length; i++) {
158
- matchedTag = detectHtmlTagCandidate(tokens, token, startIndex, HTML_EMBED_CANDIDATES[i], hints, result)
162
+ matchedTag = detectHtmlTagCandidate(tokens, token, startIndex, HTML_EMBED_CANDIDATES[i], hints)
159
163
  if (matchedTag) break
160
164
  }
161
165
  if (!matchedTag) return null
@@ -9,7 +9,6 @@ export const HTML_EMBED_CANDIDATES = Object.freeze([
9
9
  hintKey: 'hasDivHint',
10
10
  requiresIframeTag: true,
11
11
  matchedTag: 'iframe',
12
- treatAsVideoIframe: true,
13
12
  },
14
13
  ])
15
14
 
package/index.js CHANGED
@@ -22,6 +22,9 @@ const CHECK_TYPE_TOKEN_MAP = {
22
22
  pre_open: 'pre',
23
23
  blockquote_open: 'blockquote',
24
24
  }
25
+ const hasOwnOption = (option, name) => !!(
26
+ option && Object.prototype.hasOwnProperty.call(option, name)
27
+ )
25
28
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
26
29
  const normalizeLanguageCode = (value) => {
27
30
  if (value === null || value === undefined) return ''
@@ -190,6 +193,7 @@ const resolvePreferredLanguagesForState = (state, opt) => {
190
193
  }
191
194
  const needsPreferredLanguagesResolution = (opt) => {
192
195
  if (!opt || !opt.markRegState || !Array.isArray(opt.markRegState.languages)) return false
196
+ if (!opt.autoCaptionDetection) return false
193
197
  if (opt.markRegState.languages.length <= 1) return false
194
198
  return opt.autoAltCaption === true || opt.autoTitleCaption === true
195
199
  }
@@ -306,7 +310,7 @@ const normalizeAutoLabelNumberSets = (value) => {
306
310
  if (!value) return normalized
307
311
  if (Array.isArray(value)) {
308
312
  for (const entry of value) {
309
- if (normalized.hasOwnProperty(entry)) normalized[entry] = true
313
+ if (entry === 'img' || entry === 'table') normalized[entry] = true
310
314
  }
311
315
  return normalized
312
316
  }
@@ -505,29 +509,18 @@ const updateInlineTokenContent = (inlineToken, originalText, newText) => {
505
509
  inlineToken.content.slice(index + originalText.length)
506
510
  }
507
511
 
508
- const parseTrailingPositiveInteger = (text) => {
512
+ const parsePositiveInteger = (text) => {
509
513
  if (typeof text !== 'string' || text.length === 0) return null
510
- let end = text.length - 1
511
- while (end >= 0 && text.charCodeAt(end) === 0x20) end--
512
- if (end < 0) return null
513
- const lastCode = text.charCodeAt(end)
514
- if (lastCode < 0x30 || lastCode > 0x39) return null
515
- let start = end
516
- while (start >= 0) {
517
- const code = text.charCodeAt(start)
518
- if (code < 0x30 || code > 0x39) break
519
- start--
520
- }
521
514
  let value = 0
522
- let digitBase = 1
523
- for (let i = end; i > start; i--) {
524
- value += (text.charCodeAt(i) - 0x30) * digitBase
525
- digitBase *= 10
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
526
519
  }
527
520
  return value
528
521
  }
529
522
 
530
- const ensureAutoFigureNumbering = (tokens, range, caption, figureNumberState, opt) => {
523
+ const ensureAutoFigureNumbering = (tokens, range, caption, figureNumberState, sp, opt) => {
531
524
  const captionType = caption.name === 'img' ? 'img' : (caption.name === 'table' ? 'table' : '')
532
525
  if (!captionType) return
533
526
  if (!shouldApplyLabelNumbering(captionType, opt)) return
@@ -536,19 +529,12 @@ const ensureAutoFigureNumbering = (tokens, range, caption, figureNumberState, op
536
529
  const labelTextToken = getInlineLabelTextToken(inlineToken, captionType, opt)
537
530
  if (!labelTextToken || typeof labelTextToken.content !== 'string') return
538
531
  const originalText = labelTextToken.content
539
- let end = originalText.length - 1
540
- while (end >= 0 && originalText.charCodeAt(end) === 0x20) end--
541
- if (end >= 0) {
542
- const code = originalText.charCodeAt(end)
543
- if (code >= 0x30 && code <= 0x39) {
544
- const explicitValue = parseTrailingPositiveInteger(originalText)
545
- if (explicitValue !== null) {
546
- if (explicitValue > (figureNumberState[captionType] || 0)) {
547
- figureNumberState[captionType] = explicitValue
548
- }
549
- return
550
- }
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
551
536
  }
537
+ return
552
538
  }
553
539
  figureNumberState[captionType] = (figureNumberState[captionType] || 0) + 1
554
540
  const baseLabel = originalText.trim()
@@ -571,89 +557,87 @@ const matchAutoCaptionText = (text, opt, preferredMark = 'img') => {
571
557
  return ''
572
558
  }
573
559
 
574
- const getAutoCaptionFromImage = (imageToken, opt) => {
575
- if (!opt.autoCaptionDetection) return ''
576
- if (!opt.autoAltCaption && !opt.autoTitleCaption && !(opt.markRegState && opt.markRegState.markReg && opt.markRegState.markReg.img)) return ''
560
+ const getAutoCaptionFromImage = (imageToken, opt, preferredLanguages) => {
561
+ if (!opt.autoAltCaption && !opt.autoTitleCaption && !(opt.markRegState && opt.markRegState.markReg && opt.markRegState.markReg.img)) return null
577
562
 
578
563
  const altText = getImageAltText(imageToken)
579
564
  let caption = matchAutoCaptionText(altText, opt)
580
565
  if (caption) {
581
- clearImageAltAttr(imageToken)
582
- return caption
566
+ return { text: caption, source: 'alt' }
583
567
  }
584
- if (!caption && opt.autoAltCaption) {
568
+ if (opt.autoAltCaption) {
585
569
  const altForFallback = altText || ''
586
- const fallbackCaption = buildCaptionWithFallback(altForFallback, opt.autoAltCaption, 'img', opt.markRegState, opt.preferredLanguages)
587
- if (fallbackCaption && imageToken) {
588
- clearImageAltAttr(imageToken)
589
- }
570
+ const fallbackCaption = buildCaptionWithFallback(altForFallback, opt.autoAltCaption, 'img', opt.markRegState, preferredLanguages)
590
571
  caption = fallbackCaption
591
572
  }
592
- if (caption) return caption
573
+ if (caption) return { text: caption, source: 'alt' }
593
574
 
594
575
  const titleText = getImageTitleText(imageToken)
595
576
  caption = matchAutoCaptionText(titleText, opt)
596
577
  if (caption) {
597
- clearImageTitleAttr(imageToken)
598
- return caption
578
+ return { text: caption, source: 'title' }
599
579
  }
600
- if (!caption && opt.autoTitleCaption) {
580
+ if (opt.autoTitleCaption) {
601
581
  const titleForFallback = titleText || ''
602
- const fallbackCaption = buildCaptionWithFallback(titleForFallback, opt.autoTitleCaption, 'img', opt.markRegState, opt.preferredLanguages)
603
- if (fallbackCaption && imageToken) {
604
- clearImageTitleAttr(imageToken)
605
- }
582
+ const fallbackCaption = buildCaptionWithFallback(titleForFallback, opt.autoTitleCaption, 'img', opt.markRegState, preferredLanguages)
606
583
  caption = fallbackCaption
607
584
  }
608
- return caption
585
+ return caption ? { text: caption, source: 'title' } : null
586
+ }
587
+
588
+ const consumeAutoCaptionSource = (imageToken, autoCaption) => {
589
+ if (!imageToken || !autoCaption) return
590
+ if (autoCaption.source === 'alt') {
591
+ clearImageAltAttr(imageToken)
592
+ } else if (autoCaption.source === 'title') {
593
+ clearImageTitleAttr(imageToken)
594
+ }
609
595
  }
610
596
 
611
597
  const checkPrevCaption = (tokens, n, caption, sp, opt, captionState) => {
612
- if(n < 3) return caption
613
- const captionStartToken = tokens[n-3]
614
- const captionInlineToken = tokens[n-2]
615
- const captionEndToken = tokens[n-1]
598
+ if (n < 3) return
599
+ const captionStartToken = tokens[n - 3]
600
+ const captionInlineToken = tokens[n - 2]
601
+ const captionEndToken = tokens[n - 1]
616
602
  if (captionStartToken === undefined || captionEndToken === undefined) return
617
603
  if (captionStartToken.type !== 'paragraph_open' || captionEndToken.type !== 'paragraph_close') return
618
- setCaptionParagraph(n-3, captionState, caption, null, sp, opt)
604
+ setCaptionParagraph(n - 3, captionState, caption, null, sp, opt)
619
605
  const captionName = sp && sp.captionDecision ? sp.captionDecision.mark : ''
620
- if(!captionName) {
606
+ if (!captionName) {
621
607
  if (opt.labelPrefixMarkerWithoutLabelPrevReg) {
622
- const markerMatch = getLabelPrefixMarkerMatch(captionInlineToken, opt.labelPrefixMarkerWithoutLabelPrevReg)
623
- if (markerMatch) {
624
- stripLabelPrefixMarker(captionInlineToken, markerMatch)
625
- caption.isPrev = true
626
- }
608
+ const markerMatch = getLabelPrefixMarkerMatch(captionInlineToken, opt.labelPrefixMarkerWithoutLabelPrevReg)
609
+ if (markerMatch) {
610
+ stripLabelPrefixMarker(captionInlineToken, markerMatch)
611
+ caption.isPrev = true
612
+ }
627
613
  }
628
614
  return
629
615
  }
630
616
  caption.name = captionName
631
617
  caption.isPrev = true
632
- return
633
618
  }
634
619
 
635
620
  const checkNextCaption = (tokens, en, caption, sp, opt, captionState) => {
636
- if (en + 2 > tokens.length) return
637
- const captionStartToken = tokens[en+1]
638
- const captionInlineToken = tokens[en+2]
639
- const captionEndToken = tokens[en+3]
621
+ if (en + 3 >= tokens.length) return
622
+ const captionStartToken = tokens[en + 1]
623
+ const captionInlineToken = tokens[en + 2]
624
+ const captionEndToken = tokens[en + 3]
640
625
  if (captionStartToken === undefined || captionEndToken === undefined) return
641
626
  if (captionStartToken.type !== 'paragraph_open' || captionEndToken.type !== 'paragraph_close') return
642
- setCaptionParagraph(en+1, captionState, caption, null, sp, opt)
627
+ setCaptionParagraph(en + 1, captionState, caption, null, sp, opt)
643
628
  const captionName = sp && sp.captionDecision ? sp.captionDecision.mark : ''
644
- if(!captionName) {
629
+ if (!captionName) {
645
630
  if (opt.labelPrefixMarkerWithoutLabelNextReg) {
646
- const markerMatch = getLabelPrefixMarkerMatch(captionInlineToken, opt.labelPrefixMarkerWithoutLabelNextReg)
647
- if (markerMatch) {
648
- stripLabelPrefixMarker(captionInlineToken, markerMatch)
649
- caption.isNext = true
650
- }
631
+ const markerMatch = getLabelPrefixMarkerMatch(captionInlineToken, opt.labelPrefixMarkerWithoutLabelNextReg)
632
+ if (markerMatch) {
633
+ stripLabelPrefixMarker(captionInlineToken, markerMatch)
634
+ caption.isNext = true
635
+ }
651
636
  }
652
637
  return
653
638
  }
654
639
  caption.name = captionName
655
640
  caption.isNext = true
656
- return
657
641
  }
658
642
 
659
643
  const cleanCaptionTokenAttrs = (token, captionName, opt) => {
@@ -730,7 +714,6 @@ const changePrevCaptionPosition = (tokens, n, caption, opt) => {
730
714
  captionEndToken.level = figureBaseLevel + 1
731
715
  tokens.splice(n + 2, 0, captionStartToken, captionInlineToken, captionEndToken)
732
716
  tokens.splice(n-3, 3)
733
- return true
734
717
  }
735
718
 
736
719
  const changeNextCaptionPosition = (tokens, en, caption, opt) => {
@@ -750,7 +733,6 @@ const changeNextCaptionPosition = (tokens, en, caption, opt) => {
750
733
  captionEndToken.level = figureBaseLevel + 1
751
734
  tokens.splice(en, 0, captionStartToken, captionInlineToken, captionEndToken)
752
735
  tokens.splice(en+4, 3)
753
- return true
754
736
  }
755
737
 
756
738
  const getTokenMap = (token) => {
@@ -852,14 +834,12 @@ const wrapWithFigure = (tokens, range, checkTokenTagName, caption, replaceInstea
852
834
  }
853
835
  range.start = n
854
836
  range.end = en
855
- return
856
837
  }
857
838
 
858
839
  const checkCaption = (tokens, n, en, caption, sp, opt, captionState) => {
859
840
  checkPrevCaption(tokens, n, caption, sp, opt, captionState)
860
841
  if (caption.isPrev) return
861
842
  checkNextCaption(tokens, en, caption, sp, opt, captionState)
862
- return
863
843
  }
864
844
 
865
845
  const getNestedContainerType = (token) => {
@@ -971,6 +951,7 @@ const detectImageParagraph = (nextToken, n, caption, sp, opt) => {
971
951
  let isMultipleImagesHorizontal = true
972
952
  let isMultipleImagesVertical = true
973
953
  let isValid = true
954
+ let trailingAttrToken = null
974
955
  caption.name = 'img'
975
956
  if (childrenLength === 1) {
976
957
  return {
@@ -1006,7 +987,7 @@ const detectImageParagraph = (nextToken, n, caption, sp, opt) => {
1006
987
  for (let i = 0; i < parsedAttrs.length; i++) {
1007
988
  sp.attrs.push(parsedAttrs[i])
1008
989
  }
1009
- child.content = ''
990
+ trailingAttrToken = child
1010
991
  break
1011
992
  }
1012
993
  }
@@ -1044,12 +1025,6 @@ const detectImageParagraph = (nextToken, n, caption, sp, opt) => {
1044
1025
  } else {
1045
1026
  caption.nameSuffix = '-multiple'
1046
1027
  }
1047
- for (let i = 0; i < childrenLength; i++) {
1048
- const child = children[i]
1049
- if (isOnlySpacesText(child)) {
1050
- child.content = ''
1051
- }
1052
- }
1053
1028
  }
1054
1029
  const en = n + 2
1055
1030
  let tagName = 'img'
@@ -1062,6 +1037,22 @@ const detectImageParagraph = (nextToken, n, caption, sp, opt) => {
1062
1037
  wrapWithoutCaption: isValid && allowImageParagraphWithoutCaption,
1063
1038
  canWrap: isValid,
1064
1039
  imageToken,
1040
+ inlineToken: nextToken,
1041
+ trailingAttrToken,
1042
+ }
1043
+ }
1044
+
1045
+ const applyImageParagraphTransform = (detection) => {
1046
+ if (!detection || detection.type !== 'image') return
1047
+ if (detection.trailingAttrToken) {
1048
+ detection.trailingAttrToken.content = ''
1049
+ }
1050
+ if (!detection.inlineToken || detection.tagName === 'img') return
1051
+ const children = detection.inlineToken.children
1052
+ for (let i = 0; i < children.length; i++) {
1053
+ if (isOnlySpacesText(children[i])) {
1054
+ children[i].content = ''
1055
+ }
1065
1056
  }
1066
1057
  }
1067
1058
 
@@ -1071,16 +1062,19 @@ const figureWithCaption = (state, opt) => {
1071
1062
  table: 0,
1072
1063
  }
1073
1064
 
1074
- const captionState = { tokens: state.tokens, Token: state.Token }
1065
+ const captionState = {
1066
+ tokens: state.tokens,
1067
+ Token: state.Token,
1068
+ preferredLanguages: opt.preferredLanguages,
1069
+ }
1075
1070
  const shouldResolvePreferredLanguages = !!(
1076
1071
  opt.shouldResolvePreferredLanguages &&
1077
1072
  sourceMayNeedPreferredLanguages(state)
1078
1073
  )
1079
- const renderOpt = shouldResolvePreferredLanguages ? Object.create(opt) : opt
1080
1074
  if (shouldResolvePreferredLanguages) {
1081
- renderOpt.preferredLanguages = resolvePreferredLanguagesForState(state, opt)
1075
+ captionState.preferredLanguages = resolvePreferredLanguagesForState(state, opt)
1082
1076
  }
1083
- figureWithCaptionCore(state.tokens, renderOpt, figureNumberState, state.Token, captionState, null, 0)
1077
+ figureWithCaptionCore(state.tokens, opt, figureNumberState, state.Token, captionState, null, 0)
1084
1078
  }
1085
1079
 
1086
1080
  const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor, captionState, parentType = null, startIndex = 0) => {
@@ -1161,9 +1155,9 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
1161
1155
  applyCaptionDrivenFigureClass(rCaption, rSp, opt)
1162
1156
 
1163
1157
  let hasCaption = rCaption.isPrev || rCaption.isNext
1164
- let pendingAutoCaption = ''
1158
+ let pendingAutoCaption = null
1165
1159
  if (!hasCaption && detection.type === 'image' && opt.autoCaptionDetection) {
1166
- pendingAutoCaption = getAutoCaptionFromImage(detection.imageToken, opt)
1160
+ pendingAutoCaption = getAutoCaptionFromImage(detection.imageToken, opt, captionState.preferredLanguages)
1167
1161
  if (pendingAutoCaption) {
1168
1162
  hasCaption = true
1169
1163
  }
@@ -1179,21 +1173,18 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
1179
1173
  continue
1180
1174
  }
1181
1175
 
1182
- let shouldWrap = false
1183
- if (detection.type === 'html') {
1184
- shouldWrap = detection.canWrap !== false && (hasCaption || detection.wrapWithoutCaption)
1185
- } else if (detection.type === 'image') {
1186
- shouldWrap = detection.canWrap !== false && (hasCaption || detection.wrapWithoutCaption)
1187
- if (token.hidden === true) {
1188
- shouldWrap = false
1189
- }
1190
- } else {
1191
- shouldWrap = detection.canWrap !== false && hasCaption
1176
+ let shouldWrap = hasCaption
1177
+ if (detection.type === 'html' || detection.type === 'image') {
1178
+ shouldWrap = shouldWrap || detection.wrapWithoutCaption
1179
+ }
1180
+ if (detection.type === 'image' && token.hidden === true) {
1181
+ shouldWrap = false
1192
1182
  }
1193
1183
 
1194
1184
  if (shouldWrap) {
1195
1185
  if (pendingAutoCaption) {
1196
- const captionTokens = createAutoCaptionParagraph(pendingAutoCaption, TokenConstructor)
1186
+ consumeAutoCaptionSource(detection.imageToken, pendingAutoCaption)
1187
+ const captionTokens = createAutoCaptionParagraph(pendingAutoCaption.text, TokenConstructor)
1197
1188
  tokens.splice(rRange.start, 0, ...captionTokens)
1198
1189
  const insertedLength = captionTokens.length
1199
1190
  rRange.start += insertedLength
@@ -1202,7 +1193,8 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
1202
1193
  checkCaption(tokens, rRange.start, rRange.end, rCaption, rSp, opt, captionState)
1203
1194
  applyCaptionDrivenFigureClass(rCaption, rSp, opt)
1204
1195
  }
1205
- ensureAutoFigureNumbering(tokens, rRange, rCaption, figureNumberState, opt)
1196
+ applyImageParagraphTransform(detection)
1197
+ ensureAutoFigureNumbering(tokens, rRange, rCaption, figureNumberState, rSp, opt)
1206
1198
  wrapWithFigure(tokens, rRange, detection.tagName, rCaption, detection.replaceInsteadOfWrap, rSp, opt, TokenConstructor)
1207
1199
  }
1208
1200
 
@@ -1221,8 +1213,6 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
1221
1213
  } else if (rCaption.isNext) {
1222
1214
  changeNextCaptionPosition(tokens, en, rCaption, opt)
1223
1215
  nextIndex = en + 4
1224
- } else {
1225
- nextIndex = en + 1
1226
1216
  }
1227
1217
  }
1228
1218
 
@@ -1238,7 +1228,7 @@ const figureWithCaptionCore = (tokens, opt, figureNumberState, TokenConstructor,
1238
1228
  }
1239
1229
 
1240
1230
  const mditFigureWithPCaption = (md, option) => {
1241
- let opt = {
1231
+ const opt = {
1242
1232
  // Caption languages delegated to p-captions.
1243
1233
  languages: ['en', 'ja'],
1244
1234
  preferredLanguages: null, // compatibility tie-break for generated fallback labels; prefer env.locale / env.preferredLocales per render
@@ -1287,16 +1277,18 @@ const mditFigureWithPCaption = (md, option) => {
1287
1277
  labelClassFollowsFigure: false,
1288
1278
  figureToLabelClassMap: null,
1289
1279
  }
1290
- const hasExplicitAutoLabelNumberSets = option && Object.prototype.hasOwnProperty.call(option, 'autoLabelNumberSets')
1291
- const hasExplicitImageOnlyParagraphWithoutCaption = option && Object.prototype.hasOwnProperty.call(option, 'imageOnlyParagraphWithoutCaption')
1292
- const hasExplicitFigureClassThatWrapsIframeTypeBlockquote = option && Object.prototype.hasOwnProperty.call(option, 'figureClassThatWrapsIframeTypeBlockquote')
1293
- const hasExplicitFigureClassThatWrapsSlides = option && Object.prototype.hasOwnProperty.call(option, 'figureClassThatWrapsSlides')
1294
- const hasExplicitLabelClassFollowsFigure = option && Object.prototype.hasOwnProperty.call(option, 'labelClassFollowsFigure')
1280
+ const hasExplicitAutoLabelNumberSets = hasOwnOption(option, 'autoLabelNumberSets')
1281
+ const hasExplicitImageOnlyParagraphWithoutCaption = hasOwnOption(option, 'imageOnlyParagraphWithoutCaption')
1282
+ const hasExplicitFigureClassThatWrapsIframeTypeBlockquote = hasOwnOption(option, 'figureClassThatWrapsIframeTypeBlockquote')
1283
+ const hasExplicitFigureClassThatWrapsSlides = hasOwnOption(option, 'figureClassThatWrapsSlides')
1284
+ 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
+ }
1295
1288
  if (option) Object.assign(opt, option)
1296
1289
  opt.imageOnlyParagraphWithoutCaption = hasExplicitImageOnlyParagraphWithoutCaption
1297
1290
  ? !!opt.imageOnlyParagraphWithoutCaption
1298
1291
  : !!opt.oneImageWithoutCaption
1299
- opt.oneImageWithoutCaption = !!opt.oneImageWithoutCaption
1300
1292
  if (!hasExplicitLabelClassFollowsFigure && opt.figureToLabelClassMap) {
1301
1293
  opt.labelClassFollowsFigure = true
1302
1294
  }
@@ -1356,7 +1348,7 @@ const mditFigureWithPCaption = (md, option) => {
1356
1348
  opt.labelPrefixMarkerWithoutLabelNextReg = null
1357
1349
  }
1358
1350
 
1359
- //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.
1351
+ // Run after markdown-it-attrs has attached paragraph attrs, but before text replacements.
1360
1352
  md.core.ruler.before('replacements', 'figure_with_caption', (state) => {
1361
1353
  figureWithCaption(state, opt)
1362
1354
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peaceroad/markdown-it-figure-with-p-caption",
3
- "version": "0.18.0",
3
+ "version": "0.19.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",
@@ -20,16 +20,16 @@
20
20
  "url": "https://github.com/peaceroad/p7d-markdown-it-figure-with-p-caption/issues"
21
21
  },
22
22
  "devDependencies": {
23
- "@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.11",
24
- "@peaceroad/markdown-it-renderer-fence": "^0.7.0",
25
- "@peaceroad/markdown-it-renderer-image": "^0.15.0",
26
- "@peaceroad/markdown-it-strong-ja": "^0.9.1",
23
+ "@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.14",
24
+ "@peaceroad/markdown-it-renderer-fence": "^0.8.0",
25
+ "@peaceroad/markdown-it-renderer-image": "^0.16.0",
26
+ "@peaceroad/markdown-it-strong-ja": "^0.9.4",
27
27
  "highlight.js": "^11.11.1",
28
- "markdown-it": "^14.1.0",
29
- "markdown-it-attrs": "^4.3.1"
28
+ "markdown-it": "^14.3.0",
29
+ "markdown-it-attrs": "^5.0.0"
30
30
  },
31
31
  "dependencies": {
32
- "p7d-markdown-it-p-captions": "0.23.0"
32
+ "p7d-markdown-it-p-captions": "0.24.0"
33
33
  },
34
34
  "files": [
35
35
  "index.js",