markdown-it
Advanced tools
Comparing version 3.0.7 to 3.1.0
@@ -0,1 +1,13 @@ | ||
3.1.0 / 2015-03-05 | ||
------------------ | ||
- Significantly improved autolinking quality (use `linkify-it` package), #2. | ||
- Rewritten links normalizer to solve different edge cases (use `mdurl` | ||
package), #29. | ||
- Moved link title entities replace out of renderer. | ||
- Fixed escaped entities in links (`foo\&/bar`). | ||
- Improved smartquotes logic, #61. | ||
- Spec conformance update to 0.18. | ||
3.0.7 / 2015-02-22 | ||
@@ -2,0 +14,0 @@ ------------------ |
@@ -44,11 +44,2 @@ // Utilities | ||
var UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g; | ||
function unescapeMd(str) { | ||
if (str.indexOf('\\') < 0) { return str; } | ||
return str.replace(UNESCAPE_MD_RE, '$1'); | ||
} | ||
//////////////////////////////////////////////////////////////////////////////// | ||
function isValidEntityCode(c) { | ||
@@ -83,4 +74,9 @@ /*eslint no-bitwise:0*/ | ||
var NAMED_ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; | ||
var UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g; | ||
var ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; | ||
var UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi'); | ||
var DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i; | ||
var entities = require('./entities'); | ||
@@ -108,5 +104,19 @@ | ||
return str.replace(NAMED_ENTITY_RE, replaceEntityPattern); | ||
return str.replace(ENTITY_RE, replaceEntityPattern); | ||
} | ||
function unescapeMd(str) { | ||
if (str.indexOf('\\') < 0) { return str; } | ||
return str.replace(UNESCAPE_MD_RE, '$1'); | ||
} | ||
function unescapeAll(str) { | ||
if (str.indexOf('\\') < 0 && str.indexOf('&') < 0) { return str; } | ||
return str.replace(UNESCAPE_ALL_RE, function(match, escaped, entity) { | ||
if (escaped) { return escaped; } | ||
return replaceEntityPattern(match, entity); | ||
}); | ||
} | ||
//////////////////////////////////////////////////////////////////////////////// | ||
@@ -136,33 +146,4 @@ | ||
var SURRORATE_TEST_RE = /[\uD800-\uDFFF]/; | ||
var SURRORATE_SEARCH_RE = /[\uD800-\uDFFF]/g; | ||
var encode = require('mdurl/encode'); | ||
function replaceBadSurrogate(ch, pos, orig) { | ||
var code = ch.charCodeAt(0); | ||
if (code >= 0xD800 && code <= 0xDBFF) { | ||
// high surrogate | ||
if (pos >= orig.length - 1) { return '\uFFFD'; } | ||
code = orig.charCodeAt(pos + 1); | ||
if (code < 0xDC00 || code > 0xDFFF) { return '\uFFFD'; } | ||
return ch; | ||
} | ||
// low surrogate | ||
if (pos === 0) { return '\uFFFD'; } | ||
code = orig.charCodeAt(pos - 1); | ||
if (code < 0xD800 || code > 0xDBFF) { return '\uFFFD'; } | ||
return ch; | ||
} | ||
function fixBrokenSurrogates(str) { | ||
if (!SURRORATE_TEST_RE.test(str)) { return str; } | ||
return str.replace(SURRORATE_SEARCH_RE, replaceBadSurrogate); | ||
} | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// Incoming link can be partially encoded. Convert possible combinations to | ||
@@ -178,20 +159,3 @@ // unified form. | ||
function normalizeLink(url) { | ||
var normalized = replaceEntities(url); | ||
// We don't care much about result of mailformed URIs, | ||
// but shoud not throw exception. | ||
try { | ||
normalized = decodeURI(normalized); | ||
} catch (__) {} | ||
// Encoder throws exception on broken surrogate pairs. | ||
// Fix those first. | ||
try { | ||
return encodeURI(fixBrokenSurrogates(normalized)); | ||
} catch (__) { | ||
// This should never happen and left for safety only. | ||
/*istanbul ignore next*/ | ||
return ''; | ||
} | ||
return encode(url); | ||
} | ||
@@ -302,2 +266,3 @@ | ||
exports.unescapeMd = unescapeMd; | ||
exports.unescapeAll = unescapeAll; | ||
exports.isValidEntityCode = isValidEntityCode; | ||
@@ -314,4 +279,1 @@ exports.fromCodePoint = fromCodePoint; | ||
exports.normalizeReference = normalizeReference; | ||
// for testing only | ||
exports.fixBrokenSurrogates = fixBrokenSurrogates; |
@@ -7,3 +7,3 @@ // Parse link destination | ||
var normalizeLink = require('../common/utils').normalizeLink; | ||
var unescapeMd = require('../common/utils').unescapeMd; | ||
var unescapeAll = require('../common/utils').unescapeAll; | ||
@@ -29,3 +29,3 @@ | ||
result.pos = pos + 1; | ||
result.str = normalizeLink(unescapeMd(str.slice(start + 1, pos))); | ||
result.str = normalizeLink(unescapeAll(str.slice(start + 1, pos))); | ||
result.ok = true; | ||
@@ -77,3 +77,3 @@ return result; | ||
result.str = normalizeLink(unescapeMd(str.slice(start, pos))); | ||
result.str = normalizeLink(unescapeAll(str.slice(start, pos))); | ||
result.lines = lines; | ||
@@ -80,0 +80,0 @@ result.pos = pos; |
@@ -6,3 +6,3 @@ // Parse link title | ||
var unescapeMd = require('../common/utils').unescapeMd; | ||
var unescapeAll = require('../common/utils').unescapeAll; | ||
@@ -38,3 +38,3 @@ | ||
result.lines = lines; | ||
result.str = unescapeMd(str.slice(start + 1, pos)); | ||
result.str = unescapeAll(str.slice(start + 1, pos)); | ||
result.ok = true; | ||
@@ -41,0 +41,0 @@ return result; |
@@ -12,2 +12,3 @@ // Main perser class | ||
var ParserInline = require('./parser_inline'); | ||
var LinkifyIt = require('linkify-it'); | ||
@@ -196,2 +197,11 @@ var config = { | ||
/** | ||
* MarkdownIt#linkify -> LinkifyIt | ||
* | ||
* [linkify-it](https://github.com/markdown-it/linkify-it) instance. | ||
* Used by [linkify](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/linkify.js) | ||
* rule. | ||
**/ | ||
this.linkify = new LinkifyIt(); | ||
// Expose utils & helpers for easy acces from plugins | ||
@@ -376,6 +386,6 @@ | ||
* | ||
* `env` is used to pass data between "distributed" rules (`{}` by default). | ||
* For example, references are parsed in different chains, and need sandbox | ||
* to store intermediate results. Can be used to inject data in specific cases. | ||
* You will not need it with high probability. | ||
* `env` is used to pass data between "distributed" rules and return additional | ||
* metadata like reference info, needed for for renderer. It also can be used to | ||
* inject data in specific cases. Usually, you will be ok to pass `{}`, | ||
* and then pass updated object to renderer. | ||
**/ | ||
@@ -415,4 +425,4 @@ MarkdownIt.prototype.parse = function (src, env) { | ||
* The same as [[MarkdownIt.parse]] but skip all block rules. It returns the | ||
* block tokens list with th single `inline` element, containing parsed inline | ||
* tokens in `children` property. | ||
* block tokens list with the single `inline` element, containing parsed inline | ||
* tokens in `children` property. Also updates `env` object. | ||
**/ | ||
@@ -419,0 +429,0 @@ MarkdownIt.prototype.parseInline = function (src, env) { |
@@ -12,4 +12,3 @@ /** | ||
var assign = require('./common/utils').assign; | ||
var unescapeMd = require('./common/utils').unescapeMd; | ||
var replaceEntities = require('./common/utils').replaceEntities; | ||
var unescapeAll = require('./common/utils').unescapeAll; | ||
var escapeHtml = require('./common/utils').escapeHtml; | ||
@@ -43,3 +42,3 @@ | ||
if (token.params) { | ||
langName = escapeHtml(replaceEntities(unescapeMd(token.params.split(/\s+/g)[0]))); | ||
langName = escapeHtml(unescapeAll(token.params.split(/\s+/g)[0])); | ||
langClass = ' class="' + langPrefix + langName + '"'; | ||
@@ -106,3 +105,3 @@ } | ||
rules.link_open = function (tokens, idx /*, options, env */) { | ||
var title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : ''; | ||
var title = tokens[idx].title ? (' title="' + escapeHtml(tokens[idx].title) + '"') : ''; | ||
var target = tokens[idx].target ? (' target="' + escapeHtml(tokens[idx].target) + '"') : ''; | ||
@@ -118,3 +117,3 @@ return '<a href="' + escapeHtml(tokens[idx].href) + '"' + title + target + '>'; | ||
var src = ' src="' + escapeHtml(tokens[idx].src) + '"'; | ||
var title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : ''; | ||
var title = tokens[idx].title ? (' title="' + escapeHtml(tokens[idx].title) + '"') : ''; | ||
var alt = ' alt="' + self.renderInlineAsText(tokens[idx].tokens, options, env) + '"'; | ||
@@ -121,0 +120,0 @@ var suffix = options.xhtmlOut ? ' /' : ''; |
@@ -149,2 +149,4 @@ 'use strict'; | ||
// Reference can not terminate anything. This check is for safety only. | ||
/*istanbul ignore if*/ | ||
if (silent) { return true; } | ||
@@ -151,0 +153,0 @@ |
@@ -8,9 +8,6 @@ // Replace link-like texts with link nodes. | ||
var Autolinker = require('autolinker'); | ||
var arrayReplaceAt = require('../common/utils').arrayReplaceAt; | ||
var normalizeLink = require('../common/utils').normalizeLink; | ||
var LINK_SCAN_RE = /www|@|\:\/\//; | ||
function isLinkOpen(str) { | ||
@@ -23,45 +20,7 @@ return /^<a[>\s]/i.test(str); | ||
// Stupid fabric to avoid singletons, for thread safety. | ||
// Required for engines like Nashorn. | ||
// | ||
function createLinkifier() { | ||
var links = []; | ||
var autolinker = new Autolinker({ | ||
stripPrefix: false, | ||
url: true, | ||
email: true, | ||
twitter: false, | ||
replaceFn: function (__, match) { | ||
// Only collect matched strings but don't change anything. | ||
switch (match.getType()) { | ||
/*eslint default-case:0*/ | ||
case 'url': | ||
links.push({ | ||
text: match.matchedText, | ||
url: match.getUrl() | ||
}); | ||
break; | ||
case 'email': | ||
links.push({ | ||
text: match.matchedText, | ||
// normalize email protocol | ||
url: 'mailto:' + match.getEmail().replace(/^mailto:/i, '') | ||
}); | ||
break; | ||
} | ||
return false; | ||
} | ||
}); | ||
return { | ||
links: links, | ||
autolinker: autolinker | ||
}; | ||
} | ||
module.exports = function linkify(state) { | ||
var i, j, l, tokens, token, text, nodes, ln, pos, level, htmlLinkLevel, | ||
var i, j, l, tokens, token, nodes, ln, text, pos, lastPos, level, htmlLinkLevel, | ||
blockTokens = state.tokens, | ||
linkifier = null, links, autolinker; | ||
links; | ||
@@ -72,2 +31,3 @@ if (!state.md.options.linkify) { return; } | ||
if (blockTokens[j].type !== 'inline') { continue; } | ||
tokens = blockTokens[j].children; | ||
@@ -102,20 +62,11 @@ | ||
if (token.type === 'text' && LINK_SCAN_RE.test(token.content)) { | ||
if (token.type === 'text' && state.md.linkify.test(token.content)) { | ||
// Init linkifier in lazy manner, only if required. | ||
if (!linkifier) { | ||
linkifier = createLinkifier(); | ||
links = linkifier.links; | ||
autolinker = linkifier.autolinker; | ||
} | ||
text = token.content; | ||
links.length = 0; | ||
autolinker.link(text); | ||
links = state.md.linkify.match(text); | ||
if (!links.length) { continue; } | ||
// Now split string to nodes | ||
nodes = []; | ||
level = token.level; | ||
lastPos = 0; | ||
@@ -126,9 +77,9 @@ for (ln = 0; ln < links.length; ln++) { | ||
pos = text.indexOf(links[ln].text); | ||
pos = links[ln].index; | ||
if (pos) { | ||
if (pos > lastPos) { | ||
level = level; | ||
nodes.push({ | ||
type: 'text', | ||
content: text.slice(0, pos), | ||
content: text.slice(lastPos, pos), | ||
level: level | ||
@@ -139,3 +90,3 @@ }); | ||
type: 'link_open', | ||
href: links[ln].url, | ||
href: normalizeLink(links[ln].url), | ||
target: '', | ||
@@ -154,8 +105,8 @@ title: '', | ||
}); | ||
text = text.slice(pos + links[ln].text.length); | ||
lastPos = links[ln].lastIndex; | ||
} | ||
if (text.length) { | ||
if (lastPos < text.length) { | ||
nodes.push({ | ||
type: 'text', | ||
content: text, | ||
content: text.slice(lastPos), | ||
level: level | ||
@@ -162,0 +113,0 @@ }); |
@@ -6,15 +6,11 @@ // Convert straight quotation marks to typographic ones | ||
var isWhiteSpace = require('../common/utils').isWhiteSpace; | ||
var isPunctChar = require('../common/utils').isPunctChar; | ||
var isMdAsciiPunct = require('../common/utils').isMdAsciiPunct; | ||
var QUOTE_TEST_RE = /['"]/; | ||
var QUOTE_RE = /['"]/g; | ||
var PUNCT_RE = /[-\s()\[\]]/; | ||
var APOSTROPHE = '\u2019'; /* ’ */ | ||
// This function returns true if the character at `pos` | ||
// could be inside a word. | ||
function isLetter(str, pos) { | ||
if (pos < 0 || pos >= str.length) { return false; } | ||
return !PUNCT_RE.test(str[pos]); | ||
} | ||
function replaceAt(str, index, ch) { | ||
@@ -27,5 +23,5 @@ return str.substr(0, index) + ch + str.substr(index + 1); | ||
/*eslint max-depth:0*/ | ||
var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item, | ||
canOpen, canClose, j, isSingle, blkIdx, tokens, | ||
stack; | ||
var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar, | ||
isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace, | ||
canOpen, canClose, j, isSingle, blkIdx, tokens, stack; | ||
@@ -66,8 +62,47 @@ if (!state.md.options.typographer) { return; } | ||
lastSpace = !isLetter(text, t.index - 1); | ||
canOpen = canClose = true; | ||
pos = t.index + 1; | ||
isSingle = (t[0] === "'"); | ||
nextSpace = !isLetter(text, pos); | ||
if (!nextSpace && !lastSpace) { | ||
lastChar = t.index - 1 >= 0 ? text.charCodeAt(t.index - 1) : -1; | ||
nextChar = pos < max ? text.charCodeAt(pos) : -1; | ||
isLastPunctChar = lastChar >= 0 && | ||
(isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))); | ||
isNextPunctChar = nextChar >= 0 && | ||
(isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))); | ||
// begin/end of the line counts as a whitespace too | ||
isLastWhiteSpace = lastChar < 0 || isWhiteSpace(lastChar); | ||
isNextWhiteSpace = nextChar < 0 || isWhiteSpace(nextChar); | ||
if (isNextWhiteSpace) { | ||
canOpen = false; | ||
} else if (isNextPunctChar) { | ||
if (!(isLastWhiteSpace || isLastPunctChar)) { | ||
canOpen = false; | ||
} | ||
} | ||
if (isLastWhiteSpace) { | ||
canClose = false; | ||
} else if (isLastPunctChar) { | ||
if (!(isNextWhiteSpace || isNextPunctChar)) { | ||
canClose = false; | ||
} | ||
} | ||
if (nextChar === 0x22 /* " */ && t[0] === '"') { | ||
if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) { | ||
// special case: 1"" - count first quote as an inch | ||
canClose = canOpen = false; | ||
} | ||
} | ||
if (canOpen && canClose) { | ||
// treat this as the middle of the word | ||
canOpen = canClose = false; | ||
} | ||
if (!canOpen && !canClose) { | ||
// middle of word | ||
@@ -80,5 +115,2 @@ if (isSingle) { | ||
canOpen = !nextSpace; | ||
canClose = !lastSpace; | ||
if (canClose) { | ||
@@ -85,0 +117,0 @@ // this could be a closing quote, rewind the stack to get a match |
@@ -11,8 +11,2 @@ // Process *this* and _that_ | ||
function isAlphaNum(code) { | ||
return (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || | ||
(code >= 0x41 /* A */ && code <= 0x5A /* Z */) || | ||
(code >= 0x61 /* a */ && code <= 0x7A /* z */); | ||
} | ||
// parse sequence of emphasis markers, | ||
@@ -41,9 +35,11 @@ // "start" should point at a valid marker | ||
(isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))); | ||
isLastWhiteSpace = lastChar >= 0 && isWhiteSpace(lastChar); | ||
isNextWhiteSpace = nextChar >= 0 && isWhiteSpace(nextChar); | ||
// begin/end of the line counts as a whitespace too | ||
isLastWhiteSpace = lastChar < 0 || isWhiteSpace(lastChar); | ||
isNextWhiteSpace = nextChar < 0 || isWhiteSpace(nextChar); | ||
if (isNextWhiteSpace) { | ||
can_open = false; | ||
} else if (isNextPunctChar) { | ||
if (!(isLastWhiteSpace || isLastPunctChar || lastChar === -1)) { | ||
if (!(isLastWhiteSpace || isLastPunctChar)) { | ||
can_open = false; | ||
@@ -56,3 +52,3 @@ } | ||
} else if (isLastPunctChar) { | ||
if (!(isNextWhiteSpace || isNextPunctChar || nextChar === -1)) { | ||
if (!(isNextWhiteSpace || isNextPunctChar)) { | ||
can_close = false; | ||
@@ -63,5 +59,6 @@ } | ||
if (marker === 0x5F /* _ */) { | ||
// check if we aren't inside the word | ||
if (isAlphaNum(lastChar)) { can_open = false; } | ||
if (isAlphaNum(nextChar)) { can_close = false; } | ||
if (can_open && can_close) { | ||
// "_" inside a word can neither open nor close an emphasis | ||
can_open = can_close = false; | ||
} | ||
} | ||
@@ -68,0 +65,0 @@ |
{ | ||
"name": "markdown-it", | ||
"version": "3.0.7", | ||
"version": "3.1.0", | ||
"description": "Markdown-it - modern pluggable markdown parser.", | ||
@@ -13,9 +13,3 @@ "keywords": [ | ||
"homepage": "https://github.com/markdown-it/markdown-it", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/markdown-it/markdown-it.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/markdown-it/markdown-it/issues" | ||
}, | ||
"repository": "markdown-it/markdown-it", | ||
"license": "MIT", | ||
@@ -29,3 +23,4 @@ "main": "index.js", | ||
"argparse": "~ 1.0.0", | ||
"autolinker": "~ 0.15.2", | ||
"linkify-it": "~ 0.1.2", | ||
"mdurl": "~ 1.0.0", | ||
"uc.micro": "~ 0.1.0" | ||
@@ -38,4 +33,4 @@ }, | ||
"browserify": "*", | ||
"chai": "~1.10.0", | ||
"commonmark": "~ 0.17.1", | ||
"chai": "~2.1.0", | ||
"commonmark": "~0.18.1", | ||
"coveralls": "~2.11.2", | ||
@@ -42,0 +37,0 @@ "eslint": "0.13.0", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
492027
4
13605
2
6
+ Addedlinkify-it@~ 0.1.2
+ Addedmdurl@~ 1.0.0
+ Addedlinkify-it@0.1.5(transitive)
+ Addedmdurl@1.0.1(transitive)
+ Addeduc.micro@1.0.6(transitive)
- Removedautolinker@~ 0.15.2
- Removedautolinker@0.15.3(transitive)