reference.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. 'use strict'
  2. var whitespace = require('is-whitespace-character')
  3. var locate = require('../locate/link')
  4. var normalize = require('../util/normalize')
  5. module.exports = reference
  6. reference.locator = locate
  7. var link = 'link'
  8. var image = 'image'
  9. var shortcut = 'shortcut'
  10. var collapsed = 'collapsed'
  11. var full = 'full'
  12. var exclamationMark = '!'
  13. var leftSquareBracket = '['
  14. var backslash = '\\'
  15. var rightSquareBracket = ']'
  16. function reference(eat, value, silent) {
  17. var self = this
  18. var commonmark = self.options.commonmark
  19. var character = value.charAt(0)
  20. var index = 0
  21. var length = value.length
  22. var subvalue = ''
  23. var intro = ''
  24. var type = link
  25. var referenceType = shortcut
  26. var content
  27. var identifier
  28. var now
  29. var node
  30. var exit
  31. var queue
  32. var bracketed
  33. var depth
  34. // Check whether we’re eating an image.
  35. if (character === exclamationMark) {
  36. type = image
  37. intro = character
  38. character = value.charAt(++index)
  39. }
  40. if (character !== leftSquareBracket) {
  41. return
  42. }
  43. index++
  44. intro += character
  45. queue = ''
  46. // Eat the text.
  47. depth = 0
  48. while (index < length) {
  49. character = value.charAt(index)
  50. if (character === leftSquareBracket) {
  51. bracketed = true
  52. depth++
  53. } else if (character === rightSquareBracket) {
  54. if (!depth) {
  55. break
  56. }
  57. depth--
  58. }
  59. if (character === backslash) {
  60. queue += backslash
  61. character = value.charAt(++index)
  62. }
  63. queue += character
  64. index++
  65. }
  66. subvalue = queue
  67. content = queue
  68. character = value.charAt(index)
  69. if (character !== rightSquareBracket) {
  70. return
  71. }
  72. index++
  73. subvalue += character
  74. queue = ''
  75. if (!commonmark) {
  76. // The original markdown syntax definition explicitly allows for whitespace
  77. // between the link text and link label; commonmark departs from this, in
  78. // part to improve support for shortcut reference links
  79. while (index < length) {
  80. character = value.charAt(index)
  81. if (!whitespace(character)) {
  82. break
  83. }
  84. queue += character
  85. index++
  86. }
  87. }
  88. character = value.charAt(index)
  89. if (character === leftSquareBracket) {
  90. identifier = ''
  91. queue += character
  92. index++
  93. while (index < length) {
  94. character = value.charAt(index)
  95. if (character === leftSquareBracket || character === rightSquareBracket) {
  96. break
  97. }
  98. if (character === backslash) {
  99. identifier += backslash
  100. character = value.charAt(++index)
  101. }
  102. identifier += character
  103. index++
  104. }
  105. character = value.charAt(index)
  106. if (character === rightSquareBracket) {
  107. referenceType = identifier ? full : collapsed
  108. queue += identifier + character
  109. index++
  110. } else {
  111. identifier = ''
  112. }
  113. subvalue += queue
  114. queue = ''
  115. } else {
  116. if (!content) {
  117. return
  118. }
  119. identifier = content
  120. }
  121. // Brackets cannot be inside the identifier.
  122. if (referenceType !== full && bracketed) {
  123. return
  124. }
  125. subvalue = intro + subvalue
  126. if (type === link && self.inLink) {
  127. return null
  128. }
  129. /* istanbul ignore if - never used (yet) */
  130. if (silent) {
  131. return true
  132. }
  133. now = eat.now()
  134. now.column += intro.length
  135. now.offset += intro.length
  136. identifier = referenceType === full ? identifier : content
  137. node = {
  138. type: type + 'Reference',
  139. identifier: normalize(identifier),
  140. label: identifier,
  141. referenceType: referenceType
  142. }
  143. if (type === link) {
  144. exit = self.enterLink()
  145. node.children = self.tokenizeInline(content, now)
  146. exit()
  147. } else {
  148. node.alt = self.decode.raw(self.unescape(content), now) || null
  149. }
  150. return eat(subvalue)(node)
  151. }