/** * @author Titus Wormer * @copyright 2015 Titus Wormer * @license MIT * @module remark:parse:tokenize:url * @fileoverview Tokenise a URL. */ 'use strict'; var decode = require('parse-entities'); var whitespace = require('is-whitespace-character'); var locate = require('../locate/url'); module.exports = url; url.locator = locate; url.notInLink = true; var C_BRACKET_OPEN = '['; var C_BRACKET_CLOSE = ']'; var C_PAREN_OPEN = '('; var C_PAREN_CLOSE = ')'; var C_LT = '<'; var C_AT_SIGN = '@'; var HTTP_PROTOCOL = 'http://'; var HTTPS_PROTOCOL = 'https://'; var MAILTO_PROTOCOL = 'mailto:'; var PROTOCOLS = [ HTTP_PROTOCOL, HTTPS_PROTOCOL, MAILTO_PROTOCOL ]; var PROTOCOLS_LENGTH = PROTOCOLS.length; /* Tokenise a URL. */ function url(eat, value, silent) { var self = this; var subvalue; var content; var character; var index; var position; var protocol; var match; var length; var queue; var parenCount; var nextCharacter; var exit; if (!self.options.gfm) { return; } subvalue = ''; index = -1; length = PROTOCOLS_LENGTH; while (++index < length) { protocol = PROTOCOLS[index]; match = value.slice(0, protocol.length); if (match.toLowerCase() === protocol) { subvalue = match; break; } } if (!subvalue) { return; } index = subvalue.length; length = value.length; queue = ''; parenCount = 0; while (index < length) { character = value.charAt(index); if (whitespace(character) || character === C_LT) { break; } if ( character === '.' || character === ',' || character === ':' || character === ';' || character === '"' || character === '\'' || character === ')' || character === ']' ) { nextCharacter = value.charAt(index + 1); if (!nextCharacter || whitespace(nextCharacter)) { break; } } if (character === C_PAREN_OPEN || character === C_BRACKET_OPEN) { parenCount++; } if (character === C_PAREN_CLOSE || character === C_BRACKET_CLOSE) { parenCount--; if (parenCount < 0) { break; } } queue += character; index++; } if (!queue) { return; } subvalue += queue; content = subvalue; if (protocol === MAILTO_PROTOCOL) { position = queue.indexOf(C_AT_SIGN); if (position === -1 || position === length - 1) { return; } content = content.substr(MAILTO_PROTOCOL.length); } /* istanbul ignore if - never used (yet) */ if (silent) { return true; } exit = self.enterLink(); content = self.tokenizeInline(content, eat.now()); exit(); return eat(subvalue)({ type: 'link', title: null, url: decode(subvalue), children: content }); }