image.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Process ![image](<src> "title")
  2. import { normalizeReference, isSpace } from '../common/utils.mjs'
  3. export default function image (state, silent) {
  4. let code, content, label, pos, ref, res, title, start
  5. let href = ''
  6. const oldPos = state.pos
  7. const max = state.posMax
  8. if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false }
  9. if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false }
  10. const labelStart = state.pos + 2
  11. const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false)
  12. // parser failed to find ']', so it's not a valid link
  13. if (labelEnd < 0) { return false }
  14. pos = labelEnd + 1
  15. if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
  16. //
  17. // Inline link
  18. //
  19. // [link]( <href> "title" )
  20. // ^^ skipping these spaces
  21. pos++
  22. for (; pos < max; pos++) {
  23. code = state.src.charCodeAt(pos)
  24. if (!isSpace(code) && code !== 0x0A) { break }
  25. }
  26. if (pos >= max) { return false }
  27. // [link]( <href> "title" )
  28. // ^^^^^^ parsing link destination
  29. start = pos
  30. res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
  31. if (res.ok) {
  32. href = state.md.normalizeLink(res.str)
  33. if (state.md.validateLink(href)) {
  34. pos = res.pos
  35. } else {
  36. href = ''
  37. }
  38. }
  39. // [link]( <href> "title" )
  40. // ^^ skipping these spaces
  41. start = pos
  42. for (; pos < max; pos++) {
  43. code = state.src.charCodeAt(pos)
  44. if (!isSpace(code) && code !== 0x0A) { break }
  45. }
  46. // [link]( <href> "title" )
  47. // ^^^^^^^ parsing link title
  48. res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
  49. if (pos < max && start !== pos && res.ok) {
  50. title = res.str
  51. pos = res.pos
  52. // [link]( <href> "title" )
  53. // ^^ skipping these spaces
  54. for (; pos < max; pos++) {
  55. code = state.src.charCodeAt(pos)
  56. if (!isSpace(code) && code !== 0x0A) { break }
  57. }
  58. } else {
  59. title = ''
  60. }
  61. if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
  62. state.pos = oldPos
  63. return false
  64. }
  65. pos++
  66. } else {
  67. //
  68. // Link reference
  69. //
  70. if (typeof state.env.references === 'undefined') { return false }
  71. if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
  72. start = pos + 1
  73. pos = state.md.helpers.parseLinkLabel(state, pos)
  74. if (pos >= 0) {
  75. label = state.src.slice(start, pos++)
  76. } else {
  77. pos = labelEnd + 1
  78. }
  79. } else {
  80. pos = labelEnd + 1
  81. }
  82. // covers label === '' and label === undefined
  83. // (collapsed reference link and shortcut reference link respectively)
  84. if (!label) { label = state.src.slice(labelStart, labelEnd) }
  85. ref = state.env.references[normalizeReference(label)]
  86. if (!ref) {
  87. state.pos = oldPos
  88. return false
  89. }
  90. href = ref.href
  91. title = ref.title
  92. }
  93. //
  94. // We found the end of the link, and know for a fact it's a valid link;
  95. // so all that's left to do is to call tokenizer.
  96. //
  97. if (!silent) {
  98. content = state.src.slice(labelStart, labelEnd)
  99. const tokens = []
  100. state.md.inline.parse(
  101. content,
  102. state.md,
  103. state.env,
  104. tokens
  105. )
  106. const token = state.push('image', 'img', 0)
  107. const attrs = [['src', href], ['alt', '']]
  108. token.attrs = attrs
  109. token.children = tokens
  110. token.content = content
  111. if (title) {
  112. attrs.push(['title', title])
  113. }
  114. }
  115. state.pos = pos
  116. state.posMax = max
  117. return true
  118. }