Skip to content

Commit

Permalink
fix(simpleAutoLinks): URLs with emphasis/strikethrough are parsed
Browse files Browse the repository at this point in the history
correctly

When a user enters a URL with emphasis or strikethrough, the html output
were incorrect.
Now, URLs inside emphasis or strikethrough are parsed corerctly

Closes #347
  • Loading branch information
tivie committed Feb 26, 2017
1 parent 1ebc195 commit 5c50675
Show file tree
Hide file tree
Showing 25 changed files with 382 additions and 178 deletions.
143 changes: 92 additions & 51 deletions dist/showdown.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/showdown.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/showdown.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/showdown.min.js.map

Large diffs are not rendered by default.

102 changes: 61 additions & 41 deletions src/subParsers/autoLinks.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,74 @@
// url allowed chars [a-z\d_.~:/?#[]@!$&'()*+,;=-]

var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)()(?=\s|$)(?!["<>])/gi,
simpleURLRegex2 = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?()]?)(?=\s|$)(?!["<>])/gi,
//simpleURLRegex3 = /\b(((https?|ftp):\/\/|www\.)[a-z\d.-]+\.[a-z\d_.~:/?#\[\]@!$&'()*+,;=-]+?)([.!?()]?)(?=\s|$)(?!["<>])/gi,
delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
simpleMailRegex = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi,
delimMailRegex = /<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,

replaceLink = function (options) {
'use strict';

return function (wm, link, m2, m3, trailingPunctuation) {
var lnkTxt = link,
append = '';
if (/^www\./i.test(link)) {
link = link.replace(/^www\./i, 'http://www.');
}
if (options.excludeTrailingPunctuationFromURLs && trailingPunctuation) {
append = trailingPunctuation;
}
return '<a href="' + link + '">' + lnkTxt + '</a>' + append;
};
},

replaceMail = function (options, globals) {
'use strict';
return function (wholeMatch, b, mail) {
var href = 'mailto:';
b = b || '';
mail = showdown.subParser('unescapeSpecialChars')(mail, options, globals);
if (options.encodeEmails) {
href = showdown.helper.encodeEmailAddress(href + mail);
mail = showdown.helper.encodeEmailAddress(mail);
} else {
href = href + mail;
}
return b + '<a href="' + href + '">' + mail + '</a>';
};
};

showdown.subParser('autoLinks', function (text, options, globals) {
'use strict';

text = globals.converter._dispatch('autoLinks.before', text, options, globals);

var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)()(?=\s|$)(?!["<>])/gi,
simpleURLRegex2 = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?()]?)(?=\s|$)(?!["<>])/gi,
delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
simpleMailRegex = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi,
delimMailRegex = /<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;

text = text.replace(delimUrlRegex, replaceLink);
text = text.replace(delimMailRegex, replaceMail);
// simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
// Email addresses: <address@domain.foo>

if (options.simplifiedAutoLink) {
if (options.excludeTrailingPunctuationFromURLs) {
text = text.replace(simpleURLRegex2, replaceLink);
} else {
text = text.replace(simpleURLRegex, replaceLink);
}
text = text.replace(simpleMailRegex, replaceMail);
}
text = text.replace(delimUrlRegex, replaceLink(options));
text = text.replace(delimMailRegex, replaceMail(options, globals));

text = globals.converter._dispatch('autoLinks.after', text, options, globals);

function replaceLink (wm, link, m2, m3, trailingPunctuation) {
var lnkTxt = link,
append = '';
if (/^www\./i.test(link)) {
link = link.replace(/^www\./i, 'http://www.');
}
if (options.excludeTrailingPunctuationFromURLs && trailingPunctuation) {
append = trailingPunctuation;
}
return '<a href="' + link + '">' + lnkTxt + '</a>' + append;
return text;
});

showdown.subParser('simplifiedAutoLinks', function (text, options, globals) {
'use strict';

if (!options.simplifiedAutoLink) {
return text;
}

function replaceMail (wholeMatch, b, mail) {
var href = 'mailto:';
b = b || '';
mail = showdown.subParser('unescapeSpecialChars')(mail, options, globals);
if (options.encodeEmails) {
href = showdown.helper.encodeEmailAddress(href + mail);
mail = showdown.helper.encodeEmailAddress(mail);
} else {
href = href + mail;
}
return b + '<a href="' + href + '">' + mail + '</a>';
text = globals.converter._dispatch('simplifiedAutoLinks.before', text, options, globals);

if (options.excludeTrailingPunctuationFromURLs) {
text = text.replace(simpleURLRegex2, replaceLink(options));
} else {
text = text.replace(simpleURLRegex, replaceLink(options));
}
text = text.replace(simpleMailRegex, replaceMail(options, globals));

text = globals.converter._dispatch('autoLinks.after', text, options, globals);
text = globals.converter._dispatch('simplifiedAutoLinks.after', text, options, globals);

return text;
});
31 changes: 22 additions & 9 deletions src/subParsers/italicsAndBold.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,47 @@ showdown.subParser('italicsAndBold', function (text, options, globals) {
// because of backtracing, in some cases, it could lead to an exponential effect
// called "catastrophic backtrace". Ominous!

function parseInside (txt, left, right) {
if (options.simplifiedAutoLink) {
txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
}
return left + txt + right;
}

// Parse underscores
if (options.literalMidWordUnderscores) {
text = text.replace(/\b___(\S[\s\S]*)___\b/g, '<strong><em>$1</em></strong>');
text = text.replace(/\b__(\S[\s\S]*)__\b/g, '<strong>$1</strong>');
text = text.replace(/\b_(\S[\s\S]*?)_\b/g, '<em>$1</em>');
text = text.replace(/\b___(\S[\s\S]*)___\b/g, function (wm, txt) {
return parseInside (txt, '<strong><em>', '</em></strong>');
});
text = text.replace(/\b__(\S[\s\S]*)__\b/g, function (wm, txt) {
return parseInside (txt, '<strong>', '</strong>');
});
text = text.replace(/\b_(\S[\s\S]*?)_\b/g, function (wm, txt) {
return parseInside (txt, '<em>', '</em>');
});
} else {
text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
return (/\S$/.test(m)) ? '<strong><em>' + m + '</em></strong>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
});
text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
return (/\S$/.test(m)) ? '<strong>' + m + '</strong>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
});
text = text.replace(/_([^\s_][\s\S]*?)_/g, function (wm, m) {
// !/^_[^_]/.test(m) - test if it doesn't start with __ (since it seems redundant, we removed it)
return (/\S$/.test(m)) ? '<em>' + m + '</em>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
});
}

// Now parse asterisks
text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
return (/\S$/.test(m)) ? '<strong><em>' + m + '</em></strong>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
});
text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
return (/\S$/.test(m)) ? '<strong>' + m + '</strong>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
});
text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
// !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
return (/\S$/.test(m)) ? '<em>' + m + '</em>' : wm;
return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
});

text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
Expand Down
3 changes: 2 additions & 1 deletion src/subParsers/spanGamut.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ showdown.subParser('spanGamut', function (text, options, globals) {
text = showdown.subParser('anchors')(text, options, globals);

// Make links out of things like `<http://example.com/>`
// Must come after _DoAnchors(), because you can use < and >
// Must come after anchors, because you can use < and >
// delimiters in inline links like [this](<url>).
text = showdown.subParser('autoLinks')(text, options, globals);
text = showdown.subParser('italicsAndBold')(text, options, globals);
text = showdown.subParser('strikethrough')(text, options, globals);
text = showdown.subParser('simplifiedAutoLinks')(text, options, globals);

// we need to hash HTML tags inside spans
text = showdown.subParser('hashHTMLSpans')(text, options, globals);
Expand Down
Loading

0 comments on commit 5c50675

Please sign in to comment.