@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.
@@ -0,0 +1,62 @@
1
+ import {
2
+ getMarkRegStateForLanguages,
3
+ isCaptionLabelForMark,
4
+ } from 'p7d-markdown-it-p-captions'
5
+
6
+ const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key)
7
+
8
+ const validateCaptionDecision = (captionDecision) => {
9
+ if (!captionDecision || typeof captionDecision !== 'object' || Array.isArray(captionDecision)) {
10
+ throw new TypeError('captionDecision must be an object returned by p-captions.')
11
+ }
12
+ if (typeof captionDecision.mark !== 'string' || captionDecision.mark.length === 0) {
13
+ throw new TypeError('captionDecision.mark must be a non-empty canonical mark.')
14
+ }
15
+ if (captionDecision.mark === 'code' || captionDecision.mark === 'samp') {
16
+ throw new TypeError('captionDecision.mark must use the canonical pre-code or pre-samp mark.')
17
+ }
18
+ }
19
+
20
+ export const createFigureCaptionCounterKeyResolverFromMarkRegState = (markRegState) => {
21
+ const resolveCounterKey = (captionDecision) => {
22
+ const mark = captionDecision.mark
23
+ if (mark === 'img') return 'figure'
24
+ if (mark === 'pre-code') return 'listing'
25
+ if (mark === 'pre-samp') {
26
+ const labelText = captionDecision.labelText
27
+ if (isCaptionLabelForMark(labelText, 'img', markRegState)) return 'figure'
28
+ if (isCaptionLabelForMark(labelText, 'pre-code', markRegState)) return 'listing'
29
+ return 'samp'
30
+ }
31
+ return mark
32
+ }
33
+ return Object.freeze(resolveCounterKey)
34
+ }
35
+
36
+ export const createFigureCaptionCounterKeyResolver = (options) => {
37
+ if (options !== undefined && (
38
+ !options ||
39
+ typeof options !== 'object' ||
40
+ Array.isArray(options)
41
+ )) {
42
+ throw new TypeError('counter-key resolver options must be an object or undefined.')
43
+ }
44
+ const normalizedOptions = options || {}
45
+ const keys = Object.keys(normalizedOptions)
46
+ for (let index = 0; index < keys.length; index++) {
47
+ if (keys[index] !== 'languages') {
48
+ throw new TypeError(`counter-key resolver option "${keys[index]}" is not supported.`)
49
+ }
50
+ }
51
+ const languages = hasOwn(normalizedOptions, 'languages')
52
+ ? normalizedOptions.languages
53
+ : undefined
54
+ const resolveCounterKey = createFigureCaptionCounterKeyResolverFromMarkRegState(
55
+ getMarkRegStateForLanguages(languages),
56
+ )
57
+ const resolveValidatedCounterKey = (captionDecision) => {
58
+ validateCaptionDecision(captionDecision)
59
+ return resolveCounterKey(captionDecision)
60
+ }
61
+ return Object.freeze(resolveValidatedCounterKey)
62
+ }
@@ -0,0 +1,101 @@
1
+ import { markAfterNum } from 'p7d-markdown-it-p-captions'
2
+
3
+ const captionNumberReg = new RegExp('^(?:' + markAfterNum + ')$')
4
+ const numberingContexts = new WeakSet()
5
+
6
+ const parseAsciiPositiveIntegerOrNull = (text) => {
7
+ if (typeof text !== 'string' || text.length === 0) return null
8
+ let value = 0
9
+ for (let index = 0; index < text.length; index++) {
10
+ const code = text.charCodeAt(index)
11
+ if (code < 0x30 || code > 0x39) return null
12
+ value = value * 10 + code - 0x30
13
+ }
14
+ return Number.isSafeInteger(value) && value > 0 ? value : null
15
+ }
16
+
17
+ const freezeNumberingContext = (context) => {
18
+ const frozen = Object.freeze(context)
19
+ numberingContexts.add(frozen)
20
+ return frozen
21
+ }
22
+
23
+ export const createUnscopedNumberingContext = (separator) => freezeNumberingContext({
24
+ scoped: false,
25
+ scopeKey: null,
26
+ sequenceKey: null,
27
+ displayPrefix: '',
28
+ separator,
29
+ })
30
+
31
+ export const createScopedNumberingContext = (
32
+ scopeKey,
33
+ sequenceKey,
34
+ displayPrefix,
35
+ separator,
36
+ ) => freezeNumberingContext({
37
+ scoped: true,
38
+ scopeKey,
39
+ sequenceKey,
40
+ displayPrefix,
41
+ separator,
42
+ })
43
+
44
+ export const getFigureCaptionNumberingContext = (value) => {
45
+ if (!value || !numberingContexts.has(value)) {
46
+ throw new TypeError('numberingContext must be created by the figure caption-numbering API.')
47
+ }
48
+ return value
49
+ }
50
+
51
+ export const getCaptionNumberingContext = (captionContext) => {
52
+ return getFigureCaptionNumberingContext(captionContext && captionContext.numbering)
53
+ }
54
+
55
+ export const parseFigureCaptionExplicitNumberUnchecked = (number, numbering) => {
56
+ if (!numbering.scoped) return parseAsciiPositiveIntegerOrNull(number)
57
+ const prefix = numbering.displayPrefix + numbering.separator
58
+ if (!number.startsWith(prefix)) return null
59
+ return parseAsciiPositiveIntegerOrNull(number.slice(prefix.length))
60
+ }
61
+
62
+ export const formatFigureCaptionGeneratedNumberUnchecked = (sequence, numbering) => {
63
+ return numbering.scoped
64
+ ? numbering.displayPrefix + numbering.separator + sequence
65
+ : String(sequence)
66
+ }
67
+
68
+ export const parseFigureCaptionExplicitNumber = (number, context) => {
69
+ if (typeof number !== 'string') {
70
+ throw new TypeError('number must be a string.')
71
+ }
72
+ return parseFigureCaptionExplicitNumberUnchecked(
73
+ number,
74
+ getFigureCaptionNumberingContext(context),
75
+ )
76
+ }
77
+
78
+ export const formatFigureCaptionGeneratedNumber = (sequence, context) => {
79
+ if (!Number.isSafeInteger(sequence) || sequence < 1) {
80
+ throw new RangeError('sequence must be a positive safe integer.')
81
+ }
82
+ return formatFigureCaptionGeneratedNumberUnchecked(
83
+ sequence,
84
+ getFigureCaptionNumberingContext(context),
85
+ )
86
+ }
87
+
88
+ const numberCodec = Object.freeze({
89
+ parseExplicit(number, context) {
90
+ return parseFigureCaptionExplicitNumber(number, context)
91
+ },
92
+ format(sequence, context) {
93
+ const number = formatFigureCaptionGeneratedNumber(sequence, context)
94
+ if (!captionNumberReg.test(number)) {
95
+ throw new RangeError('The generated figure caption number exceeds the p-captions number grammar.')
96
+ }
97
+ return number
98
+ },
99
+ })
100
+
101
+ export const createFigureCaptionNumberCodec = () => numberCodec
@@ -0,0 +1,101 @@
1
+ const policyStateByPolicy = new WeakMap()
2
+
3
+ export const normalizeNumberingSeparator = (value, optionName) => {
4
+ if (value !== '-' && value !== '.') {
5
+ throw new TypeError(`${optionName} must be "-" or ".".`)
6
+ }
7
+ return value
8
+ }
9
+
10
+ const normalizeScopeSources = (value) => {
11
+ const source = value === undefined ? ['frontmatter', 'heading'] : value
12
+ if (!Array.isArray(source)) {
13
+ throw new TypeError('autoLabelNumberPolicy.scope.sources must be an array.')
14
+ }
15
+ const sources = []
16
+ for (let index = 0; index < source.length; index++) {
17
+ const entry = source[index]
18
+ if (entry !== 'frontmatter' && entry !== 'heading') {
19
+ throw new TypeError('autoLabelNumberPolicy.scope.sources entries must be "frontmatter" or "heading".')
20
+ }
21
+ if (sources.indexOf(entry) === -1) sources.push(entry)
22
+ }
23
+ return sources
24
+ }
25
+
26
+ const normalizeHeadingLevels = (value) => {
27
+ const source = value === undefined ? [1] : value
28
+ if (!Array.isArray(source)) {
29
+ throw new TypeError('autoLabelNumberPolicy.scope.headingLevels must be an array.')
30
+ }
31
+ const levels = []
32
+ for (let index = 0; index < source.length; index++) {
33
+ const level = source[index]
34
+ if (!Number.isInteger(level) || level < 1 || level > 6) {
35
+ throw new RangeError('autoLabelNumberPolicy.scope.headingLevels entries must be integers from 1 through 6.')
36
+ }
37
+ if (levels.indexOf(level) === -1) levels.push(level)
38
+ }
39
+ return levels
40
+ }
41
+
42
+ export const normalizeNumberingScopeMode = (value, optionName) => {
43
+ if (value !== 'auto' && value !== 'document') {
44
+ throw new TypeError(`${optionName} must be "auto" or "document".`)
45
+ }
46
+ return value
47
+ }
48
+
49
+ export const normalizeFigureCaptionNumberingPolicy = (value) => {
50
+ if (value === null) return null
51
+ const source = value === undefined ? {} : value
52
+ if (typeof source !== 'object' || Array.isArray(source)) {
53
+ throw new TypeError('autoLabelNumberPolicy must be an object or null.')
54
+ }
55
+ const separator = normalizeNumberingSeparator(
56
+ source.separator === undefined ? '.' : source.separator,
57
+ 'autoLabelNumberPolicy.separator',
58
+ )
59
+ let scope = null
60
+ const requestedScope = source.scope
61
+ if (requestedScope !== null && requestedScope !== 'document') {
62
+ const scopeValue = requestedScope === undefined || requestedScope === 'auto'
63
+ ? {}
64
+ : requestedScope
65
+ if (typeof scopeValue !== 'object' || Array.isArray(scopeValue)) {
66
+ throw new TypeError('autoLabelNumberPolicy.scope must be "auto", "document", an object, or null.')
67
+ }
68
+ const sources = normalizeScopeSources(scopeValue.sources)
69
+ const headingLevels = normalizeHeadingLevels(scopeValue.headingLevels)
70
+ const repeatScope = scopeValue.repeatScope === undefined ? 'continue' : scopeValue.repeatScope
71
+ if (repeatScope !== 'continue' && repeatScope !== 'reset') {
72
+ throw new TypeError('autoLabelNumberPolicy.scope.repeatScope must be "continue" or "reset".')
73
+ }
74
+ const resolveFrontmatterTitle = scopeValue.resolveFrontmatterTitle
75
+ if (resolveFrontmatterTitle !== undefined && resolveFrontmatterTitle !== null && typeof resolveFrontmatterTitle !== 'function') {
76
+ throw new TypeError('autoLabelNumberPolicy.scope.resolveFrontmatterTitle must be a function or null.')
77
+ }
78
+ scope = Object.freeze({
79
+ headingLevelLookup: Object.freeze(headingLevels.reduce((lookup, level) => {
80
+ lookup[level] = true
81
+ return lookup
82
+ }, {})),
83
+ repeatScope,
84
+ resolveFrontmatterTitle: resolveFrontmatterTitle || null,
85
+ usesFrontmatter: sources.indexOf('frontmatter') !== -1,
86
+ usesHeading: sources.indexOf('heading') !== -1,
87
+ })
88
+ }
89
+ const state = Object.freeze({ separator, scope })
90
+ const policy = Object.freeze({})
91
+ policyStateByPolicy.set(policy, state)
92
+ return policy
93
+ }
94
+
95
+ export const getFigureCaptionNumberingPolicyState = (policy) => {
96
+ const state = policy && policyStateByPolicy.get(policy)
97
+ if (!state) {
98
+ throw new TypeError('numberingPolicy must be created by normalizeFigureCaptionNumberingPolicy().')
99
+ }
100
+ return state
101
+ }