123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- 'use strict'
-
- var markdownLineEndingOrSpace = require('../character/markdown-line-ending-or-space.js')
- var chunkedPush = require('../util/chunked-push.js')
- var chunkedSplice = require('../util/chunked-splice.js')
- var normalizeIdentifier = require('../util/normalize-identifier.js')
- var resolveAll = require('../util/resolve-all.js')
- var shallow = require('../util/shallow.js')
- var factoryDestination = require('./factory-destination.js')
- var factoryLabel = require('./factory-label.js')
- var factoryTitle = require('./factory-title.js')
- var factoryWhitespace = require('./factory-whitespace.js')
-
- var labelEnd = {
- name: 'labelEnd',
- tokenize: tokenizeLabelEnd,
- resolveTo: resolveToLabelEnd,
- resolveAll: resolveAllLabelEnd
- }
- 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 === 'labelImage' ||
- token.type === 'labelLink' ||
- token.type === 'labelEnd')
- ) {
- // Remove the marker.
- events.splice(index + 1, token.type === 'labelImage' ? 4 : 2)
- token.type = '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 === 'link' ||
- (token.type === '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 === 'labelLink') {
- token._inactive = true
- }
- } else if (close) {
- if (
- events[index][0] === 'enter' &&
- (token.type === 'labelImage' || token.type === 'labelLink') &&
- !token._balanced
- ) {
- open = index
-
- if (token.type !== 'labelLink') {
- offset = 2
- break
- }
- }
- } else if (token.type === 'labelEnd') {
- close = index
- }
- }
-
- group = {
- type: events[open][1].type === 'labelLink' ? 'link' : 'image',
- start: shallow(events[open][1].start),
- end: shallow(events[events.length - 1][1].end)
- }
- label = {
- type: 'label',
- start: shallow(events[open][1].start),
- end: shallow(events[close][1].end)
- }
- text = {
- type: '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 === 'labelImage' ||
- self.events[index][1].type === 'labelLink') &&
- !self.events[index][1]._balanced
- ) {
- labelStart = self.events[index][1]
- break
- }
- }
-
- return start
-
- function start(code) {
- 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('labelEnd')
- effects.enter('labelMarker')
- effects.consume(code)
- effects.exit('labelMarker')
- effects.exit('labelEnd')
- return afterLabelEnd
- }
-
- function afterLabelEnd(code) {
- // Resource: `[asd](fgh)`.
- if (code === 40) {
- return effects.attempt(
- resourceConstruct,
- ok,
- defined ? ok : balanced
- )(code)
- } // Collapsed (`[asd][]`) or full (`[asd][fgh]`) reference?
-
- if (code === 91) {
- 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) {
- effects.enter('resource')
- effects.enter('resourceMarker')
- effects.consume(code)
- effects.exit('resourceMarker')
- return factoryWhitespace(effects, open)
- }
-
- function open(code) {
- if (code === 41) {
- return end(code)
- }
-
- return factoryDestination(
- effects,
- destinationAfter,
- nok,
- 'resourceDestination',
- 'resourceDestinationLiteral',
- 'resourceDestinationLiteralMarker',
- 'resourceDestinationRaw',
- 'resourceDestinationString',
- 3
- )(code)
- }
-
- function destinationAfter(code) {
- return markdownLineEndingOrSpace(code)
- ? factoryWhitespace(effects, between)(code)
- : end(code)
- }
-
- function between(code) {
- if (code === 34 || code === 39 || code === 40) {
- return factoryTitle(
- effects,
- factoryWhitespace(effects, end),
- nok,
- 'resourceTitle',
- 'resourceTitleMarker',
- 'resourceTitleString'
- )(code)
- }
-
- return end(code)
- }
-
- function end(code) {
- if (code === 41) {
- effects.enter('resourceMarker')
- effects.consume(code)
- effects.exit('resourceMarker')
- effects.exit('resource')
- return ok
- }
-
- return nok(code)
- }
- }
-
- function tokenizeFullReference(effects, ok, nok) {
- var self = this
- return start
-
- function start(code) {
- return factoryLabel.call(
- self,
- effects,
- afterLabel,
- nok,
- 'reference',
- 'referenceMarker',
- '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) {
- effects.enter('reference')
- effects.enter('referenceMarker')
- effects.consume(code)
- effects.exit('referenceMarker')
- return open
- }
-
- function open(code) {
- if (code === 93) {
- effects.enter('referenceMarker')
- effects.consume(code)
- effects.exit('referenceMarker')
- effects.exit('reference')
- return ok
- }
-
- return nok(code)
- }
- }
-
- module.exports = labelEnd
|