url.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. 'use strict';
  2. var decode = require('parse-entities');
  3. var whitespace = require('is-whitespace-character');
  4. var locate = require('../locate/url');
  5. module.exports = url;
  6. url.locator = locate;
  7. url.notInLink = true;
  8. var C_BRACKET_OPEN = '[';
  9. var C_BRACKET_CLOSE = ']';
  10. var C_PAREN_OPEN = '(';
  11. var C_PAREN_CLOSE = ')';
  12. var C_LT = '<';
  13. var C_AT_SIGN = '@';
  14. var HTTP_PROTOCOL = 'http://';
  15. var HTTPS_PROTOCOL = 'https://';
  16. var MAILTO_PROTOCOL = 'mailto:';
  17. var PROTOCOLS = [
  18. HTTP_PROTOCOL,
  19. HTTPS_PROTOCOL,
  20. MAILTO_PROTOCOL
  21. ];
  22. var PROTOCOLS_LENGTH = PROTOCOLS.length;
  23. function url(eat, value, silent) {
  24. var self = this;
  25. var subvalue;
  26. var content;
  27. var character;
  28. var index;
  29. var position;
  30. var protocol;
  31. var match;
  32. var length;
  33. var queue;
  34. var parenCount;
  35. var nextCharacter;
  36. var exit;
  37. if (!self.options.gfm) {
  38. return;
  39. }
  40. subvalue = '';
  41. index = -1;
  42. length = PROTOCOLS_LENGTH;
  43. while (++index < length) {
  44. protocol = PROTOCOLS[index];
  45. match = value.slice(0, protocol.length);
  46. if (match.toLowerCase() === protocol) {
  47. subvalue = match;
  48. break;
  49. }
  50. }
  51. if (!subvalue) {
  52. return;
  53. }
  54. index = subvalue.length;
  55. length = value.length;
  56. queue = '';
  57. parenCount = 0;
  58. while (index < length) {
  59. character = value.charAt(index);
  60. if (whitespace(character) || character === C_LT) {
  61. break;
  62. }
  63. if (
  64. character === '.' ||
  65. character === ',' ||
  66. character === ':' ||
  67. character === ';' ||
  68. character === '"' ||
  69. character === '\'' ||
  70. character === ')' ||
  71. character === ']'
  72. ) {
  73. nextCharacter = value.charAt(index + 1);
  74. if (!nextCharacter || whitespace(nextCharacter)) {
  75. break;
  76. }
  77. }
  78. if (character === C_PAREN_OPEN || character === C_BRACKET_OPEN) {
  79. parenCount++;
  80. }
  81. if (character === C_PAREN_CLOSE || character === C_BRACKET_CLOSE) {
  82. parenCount--;
  83. if (parenCount < 0) {
  84. break;
  85. }
  86. }
  87. queue += character;
  88. index++;
  89. }
  90. if (!queue) {
  91. return;
  92. }
  93. subvalue += queue;
  94. content = subvalue;
  95. if (protocol === MAILTO_PROTOCOL) {
  96. position = queue.indexOf(C_AT_SIGN);
  97. if (position === -1 || position === length - 1) {
  98. return;
  99. }
  100. content = content.substr(MAILTO_PROTOCOL.length);
  101. }
  102. /* istanbul ignore if - never used (yet) */
  103. if (silent) {
  104. return true;
  105. }
  106. exit = self.enterLink();
  107. content = self.tokenizeInline(content, eat.now());
  108. exit();
  109. return eat(subvalue)({
  110. type: 'link',
  111. title: null,
  112. url: decode(subvalue, {nonTerminated: false}),
  113. children: content
  114. });
  115. }