diff --git a/packages/linkify-html/package.json b/packages/linkify-html/package.json index 1a45f2c..7a4fc40 100644 --- a/packages/linkify-html/package.json +++ b/packages/linkify-html/package.json @@ -17,12 +17,7 @@ "url": "git+https://github.com/Hypercontext/linkifyjs.git", "directory": "packages/linkify-html" }, - "keywords": [ - "link", - "autolink", - "url", - "email" - ], + "keywords": ["link", "autolink", "url", "email"], "author": "Hypercontext", "license": "MIT", "bugs": { diff --git a/packages/linkify-html/src/linkify-html.js b/packages/linkify-html/src/linkify-html.js index db3d385..8f05afb 100644 --- a/packages/linkify-html/src/linkify-html.js +++ b/packages/linkify-html/src/linkify-html.js @@ -1,5 +1,5 @@ import { tokenize as htmlTokenize } from '@nfrasser/simple-html-tokenizer'; -import { tokenize, Options} from 'linkifyjs'; +import { tokenize, Options } from 'linkifyjs'; const LinkifyResult = 'LinkifyResult'; const StartTag = 'StartTag'; @@ -32,7 +32,9 @@ export default function linkifyHtml(str, opts = {}) { // Ignore all the contents of ignored tags const tagName = token.tagName.toUpperCase(); const isIgnored = tagName === 'A' || options.ignoreTags.indexOf(tagName) >= 0; - if (!isIgnored) { continue; } + if (!isIgnored) { + continue; + } let preskipLen = linkifiedTokens.length; skipTagTokens(tagName, tokens, ++i, linkifiedTokens); @@ -51,36 +53,42 @@ export default function linkifyHtml(str, opts = {}) { for (let i = 0; i < linkifiedTokens.length; i++) { const token = linkifiedTokens[i]; switch (token.type) { - case LinkifyResult: - linkified.push(token.rendered); - break; - case StartTag: { - let link = '<' + token.tagName; - if (token.attributes.length > 0) { - link += ' ' + attributeArrayToStrings(token.attributes).join(' '); + case LinkifyResult: + linkified.push(token.rendered); + break; + case StartTag: { + let link = '<' + token.tagName; + if (token.attributes.length > 0) { + link += ' ' + attributeArrayToStrings(token.attributes).join(' '); + } + if (token.selfClosing) { + link += ' /'; + } + link += '>'; + linkified.push(link); + break; + } + case EndTag: + linkified.push(``); + break; + case Chars: + linkified.push(escapeText(token.chars)); + break; + case Comment: + linkified.push(``); + break; + case Doctype: { + let doctype = `'; + linkified.push(doctype); + break; } - if (token.selfClosing) { link += ' /'; } - link += '>'; - linkified.push(link); - break; - } - case EndTag: - linkified.push(``); - break; - case Chars: - linkified.push(escapeText(token.chars)); - break; - case Comment: - linkified.push(``); - break; - case Doctype: { - let doctype = `'; - linkified.push(doctype); - break; - } } } @@ -104,14 +112,14 @@ function linkifyChars(str, options) { type: StartTag, tagName: 'br', attributes: [], - selfClosing: true + selfClosing: true, }); } else if (!token.isLink || !options.check(token)) { result.push({ type: Chars, chars: token.toString() }); } else { result.push({ type: LinkifyResult, - rendered: options.render(token) + rendered: options.render(token), }); } } @@ -134,7 +142,6 @@ function linkifyChars(str, options) { * Will track whether there is a nested tag of the same type */ function skipTagTokens(tagName, tokens, i, skippedTokens) { - // number of tokens of this type on the [fictional] stack let stackCount = 1; @@ -162,10 +169,7 @@ function defaultRender({ tagName, attributes, content }) { } function escapeText(text) { - return text - .replace(/&/g, '&') - .replace(//g, '>'); + return text.replace(//g, '>'); } function escapeAttr(attr) { diff --git a/test/spec/linkify-html.test.js b/test/spec/linkify-html.test.js index d14afd3..6680171 100644 --- a/test/spec/linkify-html.test.js +++ b/test/spec/linkify-html.test.js @@ -5,69 +5,77 @@ const svg = [ '', '', '', - '' + '', ].join(''); describe('linkify-html', () => { - // For each element in this array // [0] - Original text // [1] - Linkified with default options // [2] - Linkified with new options const tests = [ + ['Test with no links', 'Test with no links', 'Test with no links'], [ - 'Test with no links', - 'Test with no links', - 'Test with no links' - ], [ 'The URL is google.com and the email is test@example.com
', 'The URL is google.com and the email is test@example.com
', - 'The URL is google.com and the email is test@example.com
' - ], [ + 'The URL is google.com and the email is test@example.com
', + ], + [ 'Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!', 'Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!', 'Super long maps URL https://www.google.ca/maps/@43.472082,-8…, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!', - ], [ + ], + [ 'This link is already in an anchor tag google.com LOL and this one

isnt http://github.com


', 'This link is already in an anchor tag google.com LOL and this one

isnt http://github.com


', - 'This link is already in an anchor tag google.com LOL and this one

isnt http://github.com


' - ], [ + 'This link is already in an anchor tag google.com LOL and this one

isnt http://github.com


', + ], + [ + 'Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/', 'Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/', 'Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/', - 'Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/' - ], [ + ], + [ 'Ignore tags like and ', 'Ignore tags like and ', - 'Ignore tags like and ' - ], [ + 'Ignore tags like and ', + ], + [ 'Link followed by nbsp escape sequence https://github.com ', 'Link followed by nbsp escape sequence https://github.com\u00a0', - 'Link followed by nbsp escape sequence https://github.com\u00a0' - ], [ + 'Link followed by nbsp escape sequence https://github.com\u00a0', + ], + [ 'Link surrounded by encoded quotes "http://google.com"', 'Link surrounded by encoded quotes "http://google.com"', - 'Link surrounded by encoded quotes "http://google.com"' - ], [ + 'Link surrounded by encoded quotes "http://google.com"', + ], + [ 'https://html5-chat.com/', 'https://html5-chat.com/', - 'https://html5-chat.com/' - ], [ + 'https://html5-chat.com/', + ], + [ 'Surrounded by lt/gt symbols <http://nu.nl>', 'Surrounded by lt/gt symbols <http://nu.nl>', - 'Surrounded by lt/gt symbols <http://nu.nl>' - ], [ + 'Surrounded by lt/gt symbols <http://nu.nl>', + ], + [ 'http://xml.example.com/pub.dtd?a=1&b=2', - 'http://xml.example.com/pub.dtd?a=1&b=2', - 'http://xml.example.com/pub.dtd?a=1&b=2' - ], [ - svg, - svg, - svg - ], [ + 'http://xml.example.com/pub.dtd?a=1&b=2', + 'http://xml.example.com/pub.dtd?a=1&b=2', + ], + [svg, svg, svg], + [ 'Does nl2br.com work?\nYes', 'Does nl2br.com work?\nYes', 'Does nl2br.com work?
Yes', - ] + ], + [ + '

Here is a link and an extra space: google.com  

Here is a link and a greater-than: google.com >

Here is a link and an ellipsis: google.com …

', + '

Here is a link and an extra space: google.com \u00a0

Here is a link and a greater-than: google.com >

Here is a link and an ellipsis: google.com

', + '

Here is a link and an extra space: google.com \u00a0

Here is a link and a greater-than: google.com >

Here is a link and an ellipsis: google.com

', + ], ]; let options; @@ -80,18 +88,15 @@ describe('linkify-html', () => { defaultProtocol: 'https', rel: 'nofollow', attributes: { - onclick: 'console.log(\'Hello World!\')' + onclick: "console.log('Hello World!')", }, format(val) { return val.truncate(40); }, formatHref: { - email: (href) => href + '?subject=Hello%20from%20Linkify' + email: (href) => href + '?subject=Hello%20from%20Linkify', }, - ignoreTags: [ - 'script', - 'style' - ] + ignoreTags: ['script', 'style'], }; }); @@ -110,11 +115,10 @@ describe('linkify-html', () => { it('Works with truncate options (truncate has priority in formatting chars)', () => { options.truncate = 30; - expect(linkifyHtml( - 'Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en', - options - )).to.be.eql( - 'Super long maps URL https://www.google.ca/maps/@43…' + expect( + linkifyHtml('Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en', options), + ).to.be.eql( + 'Super long maps URL https://www.google.ca/maps/@43…', ); }); @@ -122,31 +126,33 @@ describe('linkify-html', () => { const optionsValidate = { validate: { url: function (text) { - return /^(http|ftp)s?:\/\//.test(text) || text.slice(0,3) === 'www'; - } - } + return /^(http|ftp)s?:\/\//.test(text) || text.slice(0, 3) === 'www'; + }, + }, }; const testsValidate = [ + ['1.Test with no links', '1.Test with no links'], [ - '1.Test with no links', - '1.Test with no links' - ], [ '2.The URL is google.com and the email is test@example.com', - '2.The URL is google.com and the email is test@example.com' - ], [ + '2.The URL is google.com and the email is test@example.com', + ], + [ '3.Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!', - '3.Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!' - ], [ + '3.Super long maps URL https://www.google.ca/maps/@43.472082,-80.5426668,18z?hl=en, a #hash-tag, and an email: test.wut.yo@gmail.co.uk!', + ], + [ '4a.This link is already in an anchor tag google.com LOL and this one

isnt http://github.com

', - '4a.This link is already in an anchor tag google.com LOL and this one

isnt http://github.com

' - ], [ + '4a.This link is already in an anchor tag google.com LOL and this one

isnt http://github.com

', + ], + [ + '4b.This link is already in an anchor tag google.com LOL and this one

isnt github.com

', '4b.This link is already in an anchor tag google.com LOL and this one

isnt github.com

', - '4b.This link is already in an anchor tag google.com LOL and this one

isnt github.com

' - ], [ + ], + [ '5.Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/', - '5.Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/' - ] + '5.Unterminated anchor tag This is a link google.com and this works!! https://reddit.com/r/photography/', + ], ]; testsValidate.map(function (test) { @@ -160,15 +166,12 @@ describe('linkify-html', () => { }); it('Works with HTML and overriden options', () => { - const linkified = linkifyHtml( - htmlOptions.original, - htmlOptions.altOptions - ); + const linkified = linkifyHtml(htmlOptions.original, htmlOptions.altOptions); expect(linkified).to.be.oneOf(htmlOptions.linkifiedAlt); }); it('Treats null target options properly', () => { - let linkified = linkifyHtml('http://google.com', { target: { url: null }}); + let linkified = linkifyHtml('http://google.com', { target: { url: null } }); expect(linkified).to.be.eql('http://google.com'); linkified = linkifyHtml('http://google.com', { target: null }); @@ -196,7 +199,8 @@ describe('linkify-html', () => { }); it('Handles mixed-language content', () => { - const input = '這禮拜是我們新的循環 (3/23-4/19), 我將於這週日給 Jeffrey 補課,並且我們會在這期間選另外一個可以上課的日期。'; + const input = + '這禮拜是我們新的循環 (3/23-4/19), 我將於這週日給 Jeffrey 補課,並且我們會在這期間選另外一個可以上課的日期。'; expect(linkifyHtml(input)).to.be.ok; });