2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

GFM Auto-linking fixes

closes #577

- auto-linking was interfering with things that were already a link
- now checks to ensure we are not in a HTML or markdown link already
- added tonnes of new tests
This commit is contained in:
Hannah Wolfe 2013-09-01 16:32:54 +01:00
parent 0539fc2262
commit 684150d36d
3 changed files with 328 additions and 35 deletions

View file

@ -63,29 +63,34 @@
}
// filter out def urls
text = text.replace(/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/gim,
// from Marked https://github.com/chjj/marked/blob/master/lib/marked.js#L24
text = text.replace(/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/gmi,
function (x) {
var hash = hashId();
extractions[hash] = x;
return "{gfm-js-extract-ref-url-" + hash + "}";
});
// taken from https://gist.github.com/jorilallo/1283095#L158
text = text.replace(/https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/g, function (wholeMatch, matchIndex) {
var left = text.slice(0, matchIndex), right = text.slice(matchIndex),
href;
if (left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) {
return wholeMatch;
}
href = wholeMatch.replace(/^http:\/\/github.com\//, "https://github.com/");
return "<a href='" + href + "'>" + wholeMatch + "</a>";
});
// match a URL
// adapted from https://gist.github.com/jorilallo/1283095#L158
// and http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript
text = text.replace(/(\]\(|\]|\[|<a[^\>]*?\>)?https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/gmi,
function (wholeMatch, lookBehind, matchIndex) {
// Check we are not inside an HTML tag
var left = text.slice(0, matchIndex), right = text.slice(matchIndex);
if ((left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) || lookBehind) {
return wholeMatch;
}
// If we have a matching lookBehind, this is a failure, else wrap the match in <a> tag
return lookBehind ? wholeMatch : "<a href='" + wholeMatch + "'>" + wholeMatch + "</a>";
});
text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/ig, function (wholeMatch) {
// match emil
text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/gmi, function (wholeMatch) {
return "<a href='mailto:" + wholeMatch + "'>" + wholeMatch + "</a>";
});
text = text.replace(/\{gfm-js-extract-ref-url-([0-9]+)\}/gm, function (x, y) {
text = text.replace(/\{gfm-js-extract-ref-url-([0-9]+)\}/gi, function (x, y) {
return "\n\n" + extractions[y];
});

View file

@ -136,12 +136,43 @@ describe("Showdown client side converter", function () {
});
});
it("should auto-link URL", function () {
it("should auto-link URL in text with markdown syntax", function () {
var testPhrases = [
{input: "http://google.co.uk", output: /^<p><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>$/},
{
input: "http://google.co.uk",
output: /^<p><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: "https://atest.com/fizz/buzz?baz=fizzbuzz",
output: /^<p><a href=\'https:\/\/atest.com\/fizz\/buzz\?baz=fizzbuzz\'>https:\/\/atest.com\/fizz\/buzz\?baz=fizzbuzz<\/a><\/p>$/
},
{
input: "Some [ text (http://www.google.co.uk) some other text",
output: /^<p>Some \[ text \(<a href=\'http:\/\/www.google.co.uk\'>http:\/\/www.google.co.uk<\/a>\) some other text<\/p>$/
},
{
input: ">http://google.co.uk",
output: /^<blockquote>\n <p><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>\n<\/blockquote>$/
},
{
input: "> http://google.co.uk",
output: /^<blockquote>\n <p><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>\n<\/blockquote>$/
},
{
input: "<>>> http://google.co.uk",
output: /^<p>&lt;>>> <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: "<strong>http://google.co.uk",
output: /^<p><strong><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: "# http://google.co.uk",
output: /^<h1 id="ahrefhttpgooglecoukhttpgooglecouka"><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/h1>$/
},
{
input: "* http://google.co.uk",
output: /^<ul>\n<li><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/li>\n<\/ul>$/
}
],
processedMarkup;
@ -159,7 +190,6 @@ describe("Showdown client side converter", function () {
processedMarkup.should.match(testPhrase.output);
});
// Fails
it("should convert reference format URL", function () {
var testPhrases = [
{
@ -169,6 +199,10 @@ describe("Showdown client side converter", function () {
{
input: "[Google][1]\n\n[1]: http://google.co.uk \"some text\"",
output: /^<p><a href="http:\/\/google.co.uk" title="some text">Google<\/a><\/p>$/
},
{
input: "[http://google.co.uk]: http://google.co.uk\n\n[Hello][http://google.co.uk]",
output: /^<p><a href="http:\/\/google.co.uk">Hello<\/a><\/p>$/
}
],
processedMarkup;
@ -188,6 +222,10 @@ describe("Showdown client side converter", function () {
{
input: "![Google][1]\n\n[1]: http://dsurl.stuff/something.jpg \"some text\"",
output: /^<section.*?<img.*?src="http:\/\/dsurl.stuff\/something.jpg"\/>.*?<\/section>$/
},
{
input: "[http://www.google.co.uk]: http://www.google.co.uk\n\n![Hello][http://www.google.co.uk]",
output: /^<section.*?<img.*?src="http:\/\/www.google.co.uk"\/>.*?<\/section>$/
}
],
processedMarkup;
@ -198,19 +236,70 @@ describe("Showdown client side converter", function () {
});
});
it("should NOT auto-link reference URL", function () {
var testPhrase = {input: "[1]: http://google.co.uk", output: /^$/},
processedMarkup = converter.makeHtml(testPhrase.input);
it("should NOT auto-link URL in HTML", function () {
var testPhrases = [
{
input: '<img src="http://placekitten.com/50">',
output: /^<p><img src=\"http:\/\/placekitten.com\/50\"><\/p>$/
},
{
input: '<img src="http://placekitten.com/50" />',
output: /^<p><img src=\"http:\/\/placekitten.com\/50\" \/><\/p>$/
},
{
input: '<script type="text/javascript" src="http://google.co.uk"></script>',
output: /^<script type=\"text\/javascript\" src=\"http:\/\/google.co.uk\"><\/script>$/
},
{
input: '<a href="http://facebook.com">http://google.co.uk</a>',
output: /^<p><a href=\"http:\/\/facebook.com\">http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: '<a href="http://facebook.com">test</a> http://google.co.uk',
output: /^<p><a href=\"http:\/\/facebook.com\">test<\/a> <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a><\/p>$/
}
],
processedMarkup;
processedMarkup.should.match(testPhrase.output);
testPhrases.forEach(function (testPhrase) {
processedMarkup = converter.makeHtml(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
});
it("should not display anything for reference URL", function () {
var testPhrases = [
{
input: "[1]: http://www.google.co.uk",
output: /^$/
},
{
input: "[http://www.google.co.uk]: http://www.google.co.uk",
output: /^$/
},
{
input: "[1]: http://dsurl.stuff/something.jpg",
output: /^$/
},
{
input: "[1]:http://www.google.co.uk",
output: /^$/
},
{
input: " [1]:http://www.google.co.uk",
output: /^$/
},
{
input: "",
output: /^$/
}
],
processedMarkup;
it("should NOT auto-link image reference URL", function () {
var testPhrase = {input: "[1]: http://dsurl.stuff/something.jpg", output: /^$/},
testPhrases.forEach(function (testPhrase) {
processedMarkup = converter.makeHtml(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
processedMarkup.should.match(testPhrase.output);
});
});
it("should show placeholder for image markdown", function () {
@ -276,8 +365,41 @@ describe("Showdown client side converter", function () {
});
});
it("should NOT auto-link image URL", function () {
// it("should NOT auto-link URL in image", function () {
// var testPhrases = [
// {
// input: "![http://google.co.uk/kitten.jpg](http://google.co.uk/kitten.jpg)",
// output: /^<section.*?((?!<a href=\'http:\/\/google.co.uk\/kitten.jpg\').)*<\/section>$/
// },
// {
// input: "![image stuff](http://dsurl.stuff/something)",
// output: /^<section.*?((?!<a href=\'http:\/\/dsurl.stuff\/something\').)*<\/section>$/
// });
//
it("should correctly output link and image markdown without autolinks", function () {
var testPhrases = [
{
input: "[1](http://google.co.uk)",
output: /^<p><a href="http:\/\/google.co.uk">1<\/a><\/p>$/
},
{
input: " [1](http://google.co.uk)",
output: /^<p><a href="http:\/\/google.co.uk">1<\/a><\/p>$/
},
{
input: "[http://google.co.uk](http://google.co.uk)",
output: /^<p><a href="http:\/\/google.co.uk">http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: "[http://google.co.uk][id]\n\n[id]: http://google.co.uk",
output: /^<p><a href="http:\/\/google.co.uk">http:\/\/google.co.uk<\/a><\/p>$/
},
{
input: "![http://google.co.uk/kitten.jpg](http://google.co.uk/kitten.jpg)",
output: /^<section.*?((?!<a href=\'http:\/\/google.co.uk\/kitten.jpg\').)*<\/section>$/
},
{
input: "![image stuff](http://dsurl.stuff/something)",
output: /^<section.*?((?!<a href=\'http:\/\/dsurl.stuff\/something\').)*<\/section>$/
@ -290,5 +412,4 @@ describe("Showdown client side converter", function () {
processedMarkup.should.match(testPhrase.output);
});
});
});

View file

@ -46,12 +46,67 @@ describe("Github showdown extensions", function () {
processedMarkup.should.match(testPhrase.output);
});
it("should auto-link URL", function () {
it("should auto-link URL in text with markdown syntax", function () {
var testPhrases = [
{input: "http://google.co.uk", output: /^<a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/},
{
input: "http://google.co.uk",
output: /^<a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "https://atest.com/fizz/buzz?baz=fizzbuzz",
output: /^<a href=\'https:\/\/atest.com\/fizz\/buzz\?baz=fizzbuzz\'>https:\/\/atest.com\/fizz\/buzz\?baz=fizzbuzz<\/a>$/
},
{
input: "Some text http://www.google.co.uk some other text",
output: /^Some text <a href=\'http:\/\/www.google.co.uk\'>http:\/\/www.google.co.uk<\/a> some other text$/
},
{
input: "Some [ text http://www.google.co.uk some other text",
output: /^Some \[ text <a href=\'http:\/\/www.google.co.uk\'>http:\/\/www.google.co.uk<\/a> some other text$/
},
{
input: "Some [ text (http://www.google.co.uk) some other text",
output: /^Some \[ text \(<a href=\'http:\/\/www.google.co.uk\'>http:\/\/www.google.co.uk<\/a>\) some other text$/
},
{
input: " http://google.co.uk ",
output: /^ <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a> $/
},
{
input: ">http://google.co.uk",
output: /^><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "> http://google.co.uk",
output: /^> <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "<>>> http://google.co.uk",
output: /^<>>> <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "<>>>http://google.co.uk",
output: /^<>>><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "<some text>>>http://google.co.uk",
output: /^<some text>>><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "<strong>http://google.co.uk",
output: /^<strong><a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "# http://google.co.uk",
output: /^# <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "#http://google.co.uk",
output: /^#<a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
},
{
input: "* http://google.co.uk",
output: /^\* <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
}
],
processedMarkup;
@ -69,17 +124,129 @@ describe("Github showdown extensions", function () {
processedMarkup.should.match(testPhrase.output);
});
it("should NOT auto-link URL in HTML", function () {
var testPhrases = [
{
input: '<img src="http://placekitten.com/50">',
output: /^<img src=\"http:\/\/placekitten.com\/50\">$/
},
{
input: '<img src="http://placekitten.com/50" />',
output: /^<img src=\"http:\/\/placekitten.com\/50\" \/>$/
},
{
input: '<script type="text/javascript" src="http://google.co.uk"></script>',
output: /^<script type=\"text\/javascript\" src=\"http:\/\/google.co.uk\"><\/script>$/
},
{
input: '<a href="http://facebook.com">http://google.co.uk</a>',
output: /^<a href=\"http:\/\/facebook.com\">http:\/\/google.co.uk<\/a>$/
},
{
input: '<a href="http://facebook.com">test</a> http://google.co.uk',
output: /^<a href=\"http:\/\/facebook.com\">test<\/a> <a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>$/
}
],
processedMarkup;
testPhrases.forEach(function (testPhrase) {
processedMarkup = _ConvertPhrase(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
});
it("should NOT auto-link reference URL", function () {
var testPhrase = {input: "[1]: http://google.co.uk", output: /^\n\n\[1\]: http:\/\/google.co.uk$/},
processedMarkup = _ConvertPhrase(testPhrase.input);
var testPhrases = [
{
input: "[1]: http://www.google.co.uk",
output: /^\n\n\[1\]: http:\/\/www.google.co.uk$/
},
{
input: "[http://www.google.co.uk]: http://www.google.co.uk",
output: /^\n\n\[http:\/\/www.google.co.uk]: http:\/\/www.google.co.uk$/
},
{
input: "[1]: http://dsurl.stuff/something.jpg",
output: /^\n\n\[1\]: http:\/\/dsurl.stuff\/something.jpg$/
},
{
input: "[1]:http://www.google.co.uk",
output: /^\n\n\[1\]:http:\/\/www.google.co.uk$/
},
{
input: " [1]:http://www.google.co.uk",
output: /^\n\n \[1\]:http:\/\/www.google.co.uk$/
},
{
input: "[http://www.google.co.uk]: http://www.google.co.uk",
output: /^\n\n\[http:\/\/www.google.co.uk\]: http:\/\/www.google.co.uk$/
}
],
processedMarkup;
processedMarkup.should.match(testPhrase.output);
testPhrases.forEach(function (testPhrase) {
processedMarkup = _ConvertPhrase(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
});
it("should NOT auto-link image URL", function () {
var testPhrase = {input: "[1]: http://dsurl.stuff/something.jpg", output: /^\n\n\[1\]: http:\/\/dsurl.stuff\/something.jpg$/},
processedMarkup = _ConvertPhrase(testPhrase.input);
it("should NOT auto-link URL in link or image markdown", function () {
var testPhrases = [
{
input: "[1](http://google.co.uk)",
output: /^\[1\]\(http:\/\/google.co.uk\)$/
},
{
input: " [1](http://google.co.uk)",
output: /^ \[1\]\(http:\/\/google.co.uk\)$/
},
{
input: "[http://google.co.uk](http://google.co.uk)",
output: /^\[http:\/\/google.co.uk\]\(http:\/\/google.co.uk\)$/
},
{
input: "[http://google.co.uk][id]",
output: /^\[http:\/\/google.co.uk\]\[id\]$/
},
{
input: "![1](http://google.co.uk/kitten.jpg)",
output: /^!\[1\]\(http:\/\/google.co.uk\/kitten.jpg\)$/
},
{
input: " ![1](http://google.co.uk/kitten.jpg)",
output: /^ !\[1\]\(http:\/\/google.co.uk\/kitten.jpg\)$/
},
{
input: "![http://google.co.uk/kitten.jpg](http://google.co.uk/kitten.jpg)",
output: /^!\[http:\/\/google.co.uk\/kitten.jpg\]\(http:\/\/google.co.uk\/kitten.jpg\)$/
}
],
processedMarkup;
processedMarkup.should.match(testPhrase.output);
testPhrases.forEach(function (testPhrase) {
processedMarkup = _ConvertPhrase(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
});
// behaviour if you add a gap between [] and ()
it("should auto-link if markdown is invalid", function () {
var testPhrases = [
{
input: "[1] (http://google.co.uk)",
output: /^\[1\] \(<a href=\'http:\/\/google.co.uk\'>http:\/\/google.co.uk<\/a>\)$/
},
{
input: "![1] (http://google.co.uk/kitten.jpg)",
output: /^!\[1\] \(<a href=\'http:\/\/google.co.uk\/kitten.jpg\'>http:\/\/google.co.uk\/kitten.jpg<\/a>\)$/
}
],
processedMarkup;
testPhrases.forEach(function (testPhrase) {
processedMarkup = _ConvertPhrase(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
});
});