reference.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 T_LINK = 'link';
  8. var T_IMAGE = 'image';
  9. var T_FOOTNOTE = 'footnote';
  10. var REFERENCE_TYPE_SHORTCUT = 'shortcut';
  11. var REFERENCE_TYPE_COLLAPSED = 'collapsed';
  12. var REFERENCE_TYPE_FULL = 'full';
  13. var C_CARET = '^';
  14. var C_BACKSLASH = '\\';
  15. var C_BRACKET_OPEN = '[';
  16. var C_BRACKET_CLOSE = ']';
  17. function reference(eat, value, silent) {
  18. var self = this;
  19. var character = value.charAt(0);
  20. var index = 0;
  21. var length = value.length;
  22. var subvalue = '';
  23. var intro = '';
  24. var type = T_LINK;
  25. var referenceType = REFERENCE_TYPE_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 === '!') {
  36. type = T_IMAGE;
  37. intro = character;
  38. character = value.charAt(++index);
  39. }
  40. if (character !== C_BRACKET_OPEN) {
  41. return;
  42. }
  43. index++;
  44. intro += character;
  45. queue = '';
  46. /* Check whether we’re eating a footnote. */
  47. if (self.options.footnotes && value.charAt(index) === C_CARET) {
  48. /* Exit if `![^` is found, so the `!` will be seen as text after this,
  49. * and we’ll enter this function again when `[^` is found. */
  50. if (type === T_IMAGE) {
  51. return;
  52. }
  53. intro += C_CARET;
  54. index++;
  55. type = T_FOOTNOTE;
  56. }
  57. /* Eat the text. */
  58. depth = 0;
  59. while (index < length) {
  60. character = value.charAt(index);
  61. if (character === C_BRACKET_OPEN) {
  62. bracketed = true;
  63. depth++;
  64. } else if (character === C_BRACKET_CLOSE) {
  65. if (!depth) {
  66. break;
  67. }
  68. depth--;
  69. }
  70. if (character === C_BACKSLASH) {
  71. queue += C_BACKSLASH;
  72. character = value.charAt(++index);
  73. }
  74. queue += character;
  75. index++;
  76. }
  77. subvalue = queue;
  78. content = queue;
  79. character = value.charAt(index);
  80. if (character !== C_BRACKET_CLOSE) {
  81. return;
  82. }
  83. index++;
  84. subvalue += character;
  85. queue = '';
  86. while (index < length) {
  87. character = value.charAt(index);
  88. if (!whitespace(character)) {
  89. break;
  90. }
  91. queue += character;
  92. index++;
  93. }
  94. character = value.charAt(index);
  95. /* Inline footnotes cannot have an identifier. */
  96. if (type !== T_FOOTNOTE && character === C_BRACKET_OPEN) {
  97. identifier = '';
  98. queue += character;
  99. index++;
  100. while (index < length) {
  101. character = value.charAt(index);
  102. if (character === C_BRACKET_OPEN || character === C_BRACKET_CLOSE) {
  103. break;
  104. }
  105. if (character === C_BACKSLASH) {
  106. identifier += C_BACKSLASH;
  107. character = value.charAt(++index);
  108. }
  109. identifier += character;
  110. index++;
  111. }
  112. character = value.charAt(index);
  113. if (character === C_BRACKET_CLOSE) {
  114. referenceType = identifier ? REFERENCE_TYPE_FULL : REFERENCE_TYPE_COLLAPSED;
  115. queue += identifier + character;
  116. index++;
  117. } else {
  118. identifier = '';
  119. }
  120. subvalue += queue;
  121. queue = '';
  122. } else {
  123. if (!content) {
  124. return;
  125. }
  126. identifier = content;
  127. }
  128. /* Brackets cannot be inside the identifier. */
  129. if (referenceType !== REFERENCE_TYPE_FULL && bracketed) {
  130. return;
  131. }
  132. subvalue = intro + subvalue;
  133. if (type === T_LINK && self.inLink) {
  134. return null;
  135. }
  136. /* istanbul ignore if - never used (yet) */
  137. if (silent) {
  138. return true;
  139. }
  140. if (type === T_FOOTNOTE && content.indexOf(' ') !== -1) {
  141. return eat(subvalue)({
  142. type: 'footnote',
  143. children: this.tokenizeInline(content, eat.now())
  144. });
  145. }
  146. now = eat.now();
  147. now.column += intro.length;
  148. now.offset += intro.length;
  149. identifier = referenceType === REFERENCE_TYPE_FULL ? identifier : content;
  150. node = {
  151. type: type + 'Reference',
  152. identifier: normalize(identifier)
  153. };
  154. if (type === T_LINK || type === T_IMAGE) {
  155. node.referenceType = referenceType;
  156. }
  157. if (type === T_LINK) {
  158. exit = self.enterLink();
  159. node.children = self.tokenizeInline(content, now);
  160. exit();
  161. } else if (type === T_IMAGE) {
  162. node.alt = self.decode.raw(self.unescape(content), now) || null;
  163. }
  164. return eat(subvalue)(node);
  165. }