el-beeswarm
Version:
<div style="display: flex; padding: 1rem; flex-direction: column; align-items: center; justify-content: center; height: 100vh; text-align: center; display: flex;
351 lines (300 loc) • 8.49 kB
JavaScript
var labelEnd = {
name: 'labelEnd',
tokenize: tokenizeLabelEnd,
resolveTo: resolveToLabelEnd,
resolveAll: resolveAllLabelEnd
}
export default labelEnd
import assert from 'assert'
import codes from '../character/codes.mjs'
import markdownLineEndingOrSpace from '../character/markdown-line-ending-or-space.mjs'
import constants from '../constant/constants.mjs'
import types from '../constant/types.mjs'
import chunkedPush from '../util/chunked-push.mjs'
import chunkedSplice from '../util/chunked-splice.mjs'
import normalizeIdentifier from '../util/normalize-identifier.mjs'
import resolveAll from '../util/resolve-all.mjs'
import shallow from '../util/shallow.mjs'
import destinationFactory from './factory-destination.mjs'
import labelFactory from './factory-label.mjs'
import titleFactory from './factory-title.mjs'
import whitespaceFactory from './factory-whitespace.mjs'
var resourceConstruct = {tokenize: tokenizeResource}
var fullReferenceConstruct = {tokenize: tokenizeFullReference}
var collapsedReferenceConstruct = {tokenize: tokenizeCollapsedReference}
function resolveAllLabelEnd(events) {
var index = -1
var token
while (++index < events.length) {
token = events[index][1]
if (
!token._used &&
(token.type === types.labelImage ||
token.type === types.labelLink ||
token.type === types.labelEnd)
) {
// Remove the marker.
events.splice(index + 1, token.type === types.labelImage ? 4 : 2)
token.type = types.data
index++
}
}
return events
}
function resolveToLabelEnd(events, context) {
var index = events.length
var offset = 0
var group
var label
var text
var token
var open
var close
var media
// Find an opening.
while (index--) {
token = events[index][1]
if (open) {
// If we see another link, or inactive link label, we’ve been here before.
if (
token.type === types.link ||
(token.type === types.labelLink && token._inactive)
) {
break
}
// Mark other link openings as inactive, as we can’t have links in
// links.
if (events[index][0] === 'enter' && token.type === types.labelLink) {
token._inactive = true
}
} else if (close) {
if (
events[index][0] === 'enter' &&
(token.type === types.labelImage || token.type === types.labelLink) &&
!token._balanced
) {
open = index
if (token.type !== types.labelLink) {
offset = 2
break
}
}
} else if (token.type === types.labelEnd) {
close = index
}
}
group = {
type: events[open][1].type === types.labelLink ? types.link : types.image,
start: shallow(events[open][1].start),
end: shallow(events[events.length - 1][1].end)
}
label = {
type: types.label,
start: shallow(events[open][1].start),
end: shallow(events[close][1].end)
}
text = {
type: types.labelText,
start: shallow(events[open + offset + 2][1].end),
end: shallow(events[close - 2][1].start)
}
media = [
['enter', group, context],
['enter', label, context]
]
// Opening marker.
media = chunkedPush(media, events.slice(open + 1, open + offset + 3))
// Text open.
media = chunkedPush(media, [['enter', text, context]])
// Between.
media = chunkedPush(
media,
resolveAll(
context.parser.constructs.insideSpan.null,
events.slice(open + offset + 4, close - 3),
context
)
)
// Text close, marker close, label close.
media = chunkedPush(media, [
['exit', text, context],
events[close - 2],
events[close - 1],
['exit', label, context]
])
// Reference, resource, or so.
media = chunkedPush(media, events.slice(close + 1))
// Media close.
media = chunkedPush(media, [['exit', group, context]])
chunkedSplice(events, open, events.length, media)
return events
}
function tokenizeLabelEnd(effects, ok, nok) {
var self = this
var index = self.events.length
var labelStart
var defined
// Find an opening.
while (index--) {
if (
(self.events[index][1].type === types.labelImage ||
self.events[index][1].type === types.labelLink) &&
!self.events[index][1]._balanced
) {
labelStart = self.events[index][1]
break
}
}
return start
function start(code) {
assert(code === codes.rightSquareBracket, 'expected `]`')
if (!labelStart) {
return nok(code)
}
// It’s a balanced bracket, but contains a link.
if (labelStart._inactive) return balanced(code)
defined =
self.parser.defined.indexOf(
normalizeIdentifier(
self.sliceSerialize({start: labelStart.end, end: self.now()})
)
) > -1
effects.enter(types.labelEnd)
effects.enter(types.labelMarker)
effects.consume(code)
effects.exit(types.labelMarker)
effects.exit(types.labelEnd)
return afterLabelEnd
}
function afterLabelEnd(code) {
// Resource: `[asd](fgh)`.
if (code === codes.leftParenthesis) {
return effects.attempt(
resourceConstruct,
ok,
defined ? ok : balanced
)(code)
}
// Collapsed (`[asd][]`) or full (`[asd][fgh]`) reference?
if (code === codes.leftSquareBracket) {
return effects.attempt(
fullReferenceConstruct,
ok,
defined
? effects.attempt(collapsedReferenceConstruct, ok, balanced)
: balanced
)(code)
}
// Shortcut reference: `[asd]`?
return defined ? ok(code) : balanced(code)
}
function balanced(code) {
labelStart._balanced = true
return nok(code)
}
}
function tokenizeResource(effects, ok, nok) {
return start
function start(code) {
assert.equal(code, codes.leftParenthesis, 'expected left paren')
effects.enter(types.resource)
effects.enter(types.resourceMarker)
effects.consume(code)
effects.exit(types.resourceMarker)
return whitespaceFactory(effects, open)
}
function open(code) {
if (code === codes.rightParenthesis) {
return end(code)
}
return destinationFactory(
effects,
destinationAfter,
nok,
types.resourceDestination,
types.resourceDestinationLiteral,
types.resourceDestinationLiteralMarker,
types.resourceDestinationRaw,
types.resourceDestinationString,
constants.linkResourceDestinationBalanceMax
)(code)
}
function destinationAfter(code) {
return markdownLineEndingOrSpace(code)
? whitespaceFactory(effects, between)(code)
: end(code)
}
function between(code) {
if (
code === codes.quotationMark ||
code === codes.apostrophe ||
code === codes.leftParenthesis
) {
return titleFactory(
effects,
whitespaceFactory(effects, end),
nok,
types.resourceTitle,
types.resourceTitleMarker,
types.resourceTitleString
)(code)
}
return end(code)
}
function end(code) {
if (code === codes.rightParenthesis) {
effects.enter(types.resourceMarker)
effects.consume(code)
effects.exit(types.resourceMarker)
effects.exit(types.resource)
return ok
}
return nok(code)
}
}
function tokenizeFullReference(effects, ok, nok) {
var self = this
return start
function start(code) {
assert.equal(code, codes.leftSquareBracket, 'expected left bracket')
return labelFactory.call(
self,
effects,
afterLabel,
nok,
types.reference,
types.referenceMarker,
types.referenceString
)(code)
}
function afterLabel(code) {
return self.parser.defined.indexOf(
normalizeIdentifier(
self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1)
)
) < 0
? nok(code)
: ok(code)
}
}
function tokenizeCollapsedReference(effects, ok, nok) {
return start
function start(code) {
assert.equal(code, codes.leftSquareBracket, 'expected left bracket')
effects.enter(types.reference)
effects.enter(types.referenceMarker)
effects.consume(code)
effects.exit(types.referenceMarker)
return open
}
function open(code) {
if (code === codes.rightSquareBracket) {
effects.enter(types.referenceMarker)
effects.consume(code)
effects.exit(types.referenceMarker)
effects.exit(types.reference)
return ok
}
return nok(code)
}
}