diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index 548c6f36..0f937a4d 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -615,32 +615,516 @@ test('Test wrapped URLs', () => {
});
describe('Test long input candidate parsing', () => {
- const anchor = (url, label = url) => `${label} `;
+ const anchor = (url, label = url) => `${label} `;
+ const invalidUrlSuffixesWithNestedDomain = [
+ '/path/example.org',
+ '?next=example.org',
+ '#example.org',
+ ':1/path/example.org',
+ ':8080?next=example.org',
+ ':65535#example.org',
+ '/path(a)example.org',
+ '/path/(example.org)',
+ '/path/http://example.org',
+ ];
- test('autolinks a long domain without parsing the full input as a URL', () => {
- const domain = `${'a'.repeat(70)}.com`;
+ test('autolinks a domain with the maximum hostname label length without parsing the full input as a URL', () => {
+ // Given a valid 63-character hostname label after a long plain-text prefix.
+ const domain = `${'a'.repeat(63)}.com`;
const prefix = 'a'.repeat(14500);
- expect(parser.replace(`${prefix} ${domain}`)).toBe(`${prefix} ${anchor(domain)}`);
+
+ // When ExpensiMark parses the complete message.
+ const result = parser.replace(`${prefix} ${domain}`);
+
+ // Then it autolinks the valid domain without treating the long prefix as part of the URL.
+ expect(result).toBe(`${prefix} ${anchor(domain)}`);
});
- test('handles long valid and invalid bare-domain candidates without changing their output', () => {
- // Given long valid and invalid domain-shaped values, because the optimization must not change what users see while typing.
- const hostname = 'a'.repeat(8496);
- const validUrl = `${hostname}.com`;
- const invalidUrl = `${validUrl}x`;
+ test('does not autolink bare domains whose hostname label exceeds 63 characters', () => {
+ // Given domains at and above the DNS hostname-label limit, including the long value from the reported composer issue.
+ const validUrl = `${'a'.repeat(63)}.com`;
+ const invalidUrl = `${'a'.repeat(64)}.com`;
+ const reportedInvalidUrl = `${'a'.repeat(8496)}.com`;
const repeatedDots = `${'a.'.repeat(4499)}a`;
// When ExpensiMark parses each value.
const validResult = parser.replace(validUrl);
const invalidResult = parser.replace(invalidUrl);
+ const reportedInvalidResult = parser.replace(reportedInvalidUrl);
const repeatedDotsResult = parser.replace(repeatedDots);
- // Then it links only the valid domain, which protects both the expected output and responsive typing for invalid long input.
+ // Then it links only the domain whose labels are valid and leaves oversized or unknown domains as plain text.
expect(validResult).toBe(anchor(validUrl));
expect(invalidResult).toBe(invalidUrl);
+ expect(reportedInvalidResult).toBe(reportedInvalidUrl);
expect(repeatedDotsResult).toBe(repeatedDots);
});
+ test('does not autolink a long protocol hostname whose label exceeds the limit', () => {
+ // Given a protocol URL whose single hostname label is close to the App parser limit.
+ const input = `https://${'a'.repeat(9960)}.com`;
+
+ // When ExpensiMark parses the invalid URL candidate.
+ const result = parser.replace(input);
+
+ // Then it leaves the complete candidate plain without sending the oversized hostname to URL matching.
+ expect(result).toBe(input);
+ });
+
+ test('skips URL matching for a long protocol hostname whose label exceeds the limit', () => {
+ // Given an oversized protocol hostname and a URL matcher that records when it runs.
+ const input = `https://${'a'.repeat(9960)}.com`;
+ const urlRegex = /example/g;
+ const urlRegexExecSpy = jest.spyOn(urlRegex, 'exec');
+
+ // When the candidate scanner processes the invalid URL.
+ const result = parser.modifyTextForUrlLinks(urlRegex, input, jest.fn(), true);
+
+ // Then it leaves the input unchanged and rejects it before running the URL matcher.
+ expect(result).toBe(input);
+ expect(urlRegexExecSpy).not.toHaveBeenCalled();
+ });
+
+ test.each([
+ ['a valid hyphenated label', 'valid-label', true],
+ ['a leading hyphen', '-invalid', false],
+ ['an oversized label', 'a'.repeat(64), false],
+ ])('keeps URL parsing paths aligned for %s', (_name, label, shouldLink) => {
+ // Given the same hostname label in escaped text and alongside raw HTML.
+ const url = `https://${label}.local`;
+ const rawHtmlPrefix = 'x ';
+ const expectedUrl = shouldLink ? anchor(url) : url;
+
+ // When ExpensiMark parses it through candidate scanning and full-text URL matching.
+ const candidateScanningResult = parser.replace(url);
+ const fullTextResult = parser.replace(`${rawHtmlPrefix}${url}`, {shouldEscapeText: false});
+
+ // Then both parsing paths apply the same hostname-label rules.
+ expect(candidateScanningResult).toBe(expectedUrl);
+ expect(fullTextResult).toBe(`${rawHtmlPrefix}${expectedUrl}`);
+ });
+
+ test.each([
+ ['bare domain', (hostname) => `${hostname}.example.com`],
+ ['protocol URL', (hostname) => `https://${hostname}-valid.com`],
+ ['protocol URL with a valid prefix', (hostname) => `https://valid-${hostname}.com`],
+ ['bare URL with a known-TLD prefix', (hostname) => `example.com.${hostname}`],
+ ['protocol URL with a known-TLD prefix', (hostname) => `https://example.com.${hostname}`],
+ ['labeled link', (hostname) => `[label](https://${hostname}-valid.com)`],
+ ['image', (hostname) => ``],
+ ['video', (hostname) => ``],
+ ])('does not autolink a valid suffix inside an invalid %s', (_name, createInput) => {
+ // Given URL-based text containing an oversized hostname label before a valid-looking suffix.
+ const input = createInput('a'.repeat(64));
+
+ // When ExpensiMark parses the complete input.
+ const result = parser.replace(input);
+
+ // Then it leaves the whole invalid value unchanged instead of linking only its valid suffix.
+ expect(result).toBe(input);
+ });
+
+ test.each(['foo.-valid.com', 'foo.--valid.com', '--valid.com'])('does not autolink a suffix after invalid leading hyphens in %s', (input) => {
+ // Given a hostname whose later valid-looking label starts after invalid leading hyphens.
+
+ // When ExpensiMark parses the complete hostname.
+ const result = parser.replace(input);
+
+ // Then it leaves the whole invalid hostname plain instead of linking the suffix.
+ expect(result).toBe(input);
+ });
+
+ test('keeps a standalone boundary hyphen outside a maximum-length hostname label', () => {
+ // Given a valid 63-character hostname label preceded by a standalone punctuation hyphen.
+ const domain = `${'a'.repeat(63)}.com`;
+ const input = `-${domain}`;
+
+ // When ExpensiMark parses the domain.
+ const result = parser.replace(input);
+
+ // Then it preserves the hyphen and autolinks the valid hostname after it.
+ expect(result).toBe(`-${anchor(domain)}`);
+ });
+
+ test('still autolinks a separate valid domain after an invalid hostname', () => {
+ // Given an invalid hostname followed by a separate valid domain.
+ const invalidHostname = `${'a'.repeat(64)}.example.com`;
+ const validDomain = 'valid.com';
+ const input = `${invalidHostname} ${validDomain}`;
+
+ // When ExpensiMark parses both values.
+ const result = parser.replace(input);
+
+ // Then it leaves the invalid hostname plain and autolinks only the separate valid domain.
+ expect(result).toBe(`${invalidHostname} ${anchor(validDomain)}`);
+ });
+
+ test.each([
+ ['protocol URL followed by repeated dots', 'https://example.com', '..nonsense'],
+ ['protocol URL followed by several dots', 'https://example.com', '...next'],
+ ['protocol URL followed by a dot and hyphen', 'https://example.com', '.-nonsense'],
+ ['protocol URL followed by a hyphen and dot', 'https://example.com', '-.nonsense'],
+ ['bare URL followed by repeated dots', 'example.com', '..nonsense'],
+ ['bare URL followed by several dots', 'example.com', '...next'],
+ ['bare URL followed by a dot and hyphen', 'example.com', '.-nonsense'],
+ ['bare URL followed by a hyphen and dot', 'example.com', '-.nonsense'],
+ ['protocol URL followed by long text after repeated dots', 'https://example.com', `..${'a'.repeat(300)}`],
+ ['bare URL followed by long text after repeated dots', 'example.com', `..${'a'.repeat(300)}`],
+ ['protocol URL followed by an oversized malformed label', 'https://example.com', `.-${'a'.repeat(63)}`],
+ ['bare URL followed by an oversized malformed label', 'example.com', `.-${'a'.repeat(63)}`],
+ ['protocol URL followed by long text after a trailing hyphen', 'https://example.com', `-.${'a'.repeat(300)}`],
+ ['bare URL followed by long text after a trailing hyphen', 'example.com', `-.${'a'.repeat(300)}`],
+ ['maximum-length protocol hostname followed by repeated dots', `https://${'a.'.repeat(125)}com`, '..nonsense'],
+ ['maximum-length bare hostname followed by repeated dots', `${'a.'.repeat(125)}com`, '..nonsense'],
+ ])('still autolinks a valid %s', (_name, validUrl, continuation) => {
+ // Given a valid URL followed without whitespace by punctuation that cannot form another hostname label.
+ const input = `${validUrl}${continuation}`;
+
+ // When ExpensiMark parses the complete hostname-shaped run through candidate scanning and full-text matching.
+ const result = parser.replace(input);
+ const rawHtmlResult = parser.replace(`x ${input}`, {shouldEscapeText: false});
+
+ // Then it links the valid URL and leaves only the malformed continuation as plain text.
+ expect(result).toBe(`${anchor(validUrl)}${continuation}`);
+ expect(rawHtmlResult).toBe(`x ${anchor(validUrl)}${continuation}`);
+ });
+
+ test.each([
+ ['https://example.com', '..', 'http://', true],
+ ['example.com', '..', 'https://', true],
+ ['https://example.com', '.-', 'ftp://', true],
+ ['example.com', '-.', 'ftps://', true],
+ ['https://example.com', '..nonsense-', 'HTTP://', true],
+ ['https://example.com', '..nonsense', 'http://', true],
+ [`https://${'a'.repeat(64)}.com`, '..', 'http://', false],
+ ])('preserves a separate URL after %s%s with protocol %s', (firstUrl, continuation, protocol, shouldLinkFirstUrl) => {
+ // Given a malformed hostname continuation followed immediately by a separate protocol URL.
+ const validUrl = `${protocol}valid.org/path`;
+ const input = `${firstUrl}${continuation}${validUrl}`;
+ const firstResult = shouldLinkFirstUrl ? anchor(firstUrl) : firstUrl;
+
+ // When ExpensiMark parses both URLs through candidate scanning and full-text matching.
+ const result = parser.replace(input);
+ const rawHtmlResult = parser.replace(`x ${input}`, {shouldEscapeText: false});
+
+ // Then the next URL keeps its complete protocol instead of being skipped or linked as a bare domain.
+ const expected = `${firstResult}${continuation}${anchor(`${protocol.toLowerCase()}valid.org/path`, validUrl)}`;
+ expect(result).toBe(expected);
+ expect(rawHtmlResult).toBe(`x ${expected}`);
+ });
+
+ test.each([
+ ['https://example.com..nonsense', 'https://example.com', '/path'],
+ ['example.com.-nonsense', 'example.com', '/path/example.org'],
+ ['https://example.com-.nonsense', 'https://example.com', '?next=example.org'],
+ ['example.com..nonsense', 'example.com', '#example.org'],
+ [`https://${'a'.repeat(64)}.com`, undefined, '/path/example.org'],
+ [`${'a'.repeat(64)}.com`, undefined, '/path(a)'],
+ ])('links a separate domain after an unmatched parenthesis following %s', (invalidUrl, validPrefix, suffix) => {
+ // Given an invalid URL range followed by an unmatched closing parenthesis and a separate valid domain.
+ const validDomain = 'valid.org';
+ const input = `${invalidUrl}${suffix})${validDomain}`;
+
+ // When ExpensiMark parses the boundary through candidate scanning and full-text matching.
+ const result = parser.replace(input);
+ const rawHtmlResult = parser.replace(`x ${input}`, {shouldEscapeText: false});
+
+ // Then the unmatched parenthesis ends the invalid range, so the separate domain remains linkable.
+ const firstResult = validPrefix ? `${anchor(validPrefix)}${invalidUrl.slice(validPrefix.length)}` : invalidUrl;
+ const expected = `${firstResult}${suffix})${anchor(validDomain)}`;
+ expect(result).toBe(expected);
+ expect(rawHtmlResult).toBe(`x ${expected}`);
+ });
+
+ test.each([
+ ['sentence-like text', '.Thanks!'],
+ ['hyphenated text', '-thanks'],
+ ['text after repeated hyphens', '--next'],
+ ])('preserves a bare URL before adjacent %s', (_name, continuation) => {
+ // Given a bare URL followed without whitespace by text that is not part of its known TLD.
+ const validUrl = 'example.com';
+ const input = `${validUrl}${continuation}`;
+
+ // When ExpensiMark parses the complete hostname-shaped run.
+ const result = parser.replace(input);
+
+ // Then it preserves the existing link for the known-TLD URL and leaves the adjacent text plain.
+ expect(result).toBe(`${anchor(validUrl)}${continuation}`);
+ });
+
+ test.each([
+ ['a sentence-like label', 'example.com.Thanks!'],
+ ['a hyphenated label', 'example.com-thanks'],
+ ['a label with repeated hyphens', 'example.com--next'],
+ ])('preserves protocol URL parsing with %s', (_name, hostname) => {
+ // Given a protocol URL whose complete hostname remains valid under the loose URL rules.
+ const input = `https://${hostname}`;
+ const trailingPunctuation = input.endsWith('!') ? '!' : '';
+ const linkedUrl = trailingPunctuation ? input.slice(0, -1) : input;
+
+ // When ExpensiMark parses the complete URL.
+ const result = parser.replace(input);
+
+ // Then it keeps linking the complete valid hostname rather than stopping at the earlier known TLD.
+ expect(result).toBe(`${anchor(linkedUrl)}${trailingPunctuation}`);
+ });
+
+ test.each(['https://example.com', 'example.com'])('preserves a valid %s before a malformed raw-HTML URL continuation', (validUrl) => {
+ // Given raw HTML followed by a valid URL, malformed hostname punctuation, a nested domain, and a separate valid domain.
+ const continuation = '..nonsense/path/example.org';
+ const separateDomain = 'valid.net';
+ const input = `x ${validUrl}${continuation} ${separateDomain}`;
+
+ // When ExpensiMark parses the input without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then it links the valid prefix and separate domain without linking the domain inside the malformed continuation.
+ expect(result).toBe(`x ${anchor(validUrl)}${continuation} ${anchor(separateDomain)}`);
+ });
+
+ test.each([
+ ['an oversized dot-separated label', `https://valid.${'a'.repeat(64)}.com`],
+ ['an oversized hyphenated label', `https://valid-${'a'.repeat(64)}.com`],
+ ['an oversized complete hostname', `https://valid.${'a.'.repeat(123)}com`],
+ ])('does not autolink a valid prefix inside a raw-HTML URL with %s', (_name, invalidUrl) => {
+ // Given raw HTML followed by an invalid URL and a separate valid domain.
+ const validDomain = 'example.org';
+ const input = `x ${invalidUrl} ${validDomain}`;
+
+ // When ExpensiMark parses the input without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then it leaves the invalid URL plain and autolinks only the separate valid domain.
+ expect(result).toBe(`x ${invalidUrl} ${anchor(validDomain)}`);
+ });
+
+ test.each([
+ ['a path', `https://${'a'.repeat(64)}.com/path/example.org`],
+ ['a query', `https://${'a'.repeat(64)}.com?next=example.org`],
+ ['a fragment', `https://${'a'.repeat(64)}.com#example.org`],
+ ['an oversized complete hostname', `https://aa.${'a.'.repeat(124)}com/path/example.org`],
+ ])('does not autolink a domain in %s of an oversized raw-HTML URL', (_name, invalidUrl) => {
+ // Given an invitation heading that generates HTML, followed by an oversized URL and a separate valid domain.
+ const validDomain = 'valid.net';
+ const input = `# Invitation\n\n${invalidUrl} ${validDomain}`;
+
+ // When ExpensiMark parses the invitation without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then the oversized URL stays plain while the separate valid domain is still autolinked.
+ expect(result).toBe(`
Invitation ${invalidUrl} ${anchor(validDomain)}`);
+ });
+
+ test('does not autolink inside an oversized raw-HTML URL after a separate URL without whitespace', () => {
+ // Given a valid URL followed by an oversized URL in the same non-whitespace token.
+ const validUrl = 'https://valid.com';
+ const invalidUrl = `https://${'a'.repeat(64)}.com/path/example.org`;
+ const input = `# Invitation\n\n${validUrl},${invalidUrl}`;
+
+ // When ExpensiMark parses the invitation without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then it autolinks the first URL without linking any part of the oversized URL.
+ expect(result).toBe(`Invitation ${anchor(validUrl)},${invalidUrl}`);
+ });
+
+ test('still autolinks a valid domain after an invalid protocol URL without whitespace', () => {
+ // Given an invalid protocol URL followed by a comma and a separate valid domain.
+ const invalidUrl = `https://${'a'.repeat(64)}.com`;
+ const validDomain = 'example.org';
+ const input = `${invalidUrl},${validDomain}`;
+
+ // When ExpensiMark parses both values as one non-whitespace token.
+ const result = parser.replace(input);
+
+ // Then it leaves the invalid URL plain and autolinks the valid domain after the comma.
+ expect(result).toBe(`${invalidUrl},${anchor(validDomain)}`);
+ });
+
+ test.each([
+ ['https://', ','],
+ ['https://', ';'],
+ ['https://', '|'],
+ ['https://', ')'],
+ ['https://', ']'],
+ ['https://', '}'],
+ ['https://', '_'],
+ ['http://', ','],
+ ['ftp://', ','],
+ ['ftps://', ','],
+ ])('still autolinks a valid domain after malformed %s followed by "%s"', (protocol, separator) => {
+ // Given a protocol without a hostname, followed by a URL boundary and a separate valid domain.
+ const validDomain = 'valid.org';
+ const input = `${protocol}${separator}${validDomain}`;
+
+ // When ExpensiMark parses the complete non-whitespace token.
+ const result = parser.replace(input);
+
+ // Then it leaves the malformed protocol plain and autolinks the separate valid domain.
+ expect(result).toBe(`${protocol}${separator}${anchor(validDomain)}`);
+ });
+
+ test.each([
+ ['https://foo..bar.com', 'https://foo'],
+ ['https://foo.-bar.com', 'https://foo'],
+ ['https://-bar.com', undefined],
+ ['https://invalid-.local', 'https://invalid'],
+ ['https://local.url..nonsense', 'https://local.url'],
+ ['http://localhost..nonsense', 'http://localhost'],
+ ['https://example.internal.-nonsense', 'https://example.internal'],
+ ['https://example.internal-.nonsense', 'https://example.internal'],
+ ])('preserves valid prefixes and separate URLs when parsing %s', (invalidUrl, validPrefix) => {
+ // Given malformed hostname punctuation before a path containing another domain and a separate URL after a semicolon.
+ const validDomain = 'valid.org';
+ const suffix = '/path/example.org';
+ const input = `${invalidUrl}${suffix};${validDomain}`;
+ const rawHtmlPrefix = 'x ';
+
+ // When ExpensiMark uses candidate scanning or full-text matching alongside unescaped HTML.
+ const candidateScanningResult = parser.replace(input);
+ const fullTextResult = parser.replace(`${rawHtmlPrefix}${input}`, {shouldEscapeText: false});
+
+ // Then supported prefixes remain linked, connected invalid text stays plain, and the separate domain still links.
+ const expectedUrl = validPrefix ? `${anchor(validPrefix)}${invalidUrl.slice(validPrefix.length)}` : invalidUrl;
+ const expected = `${expectedUrl}${suffix};${anchor(validDomain)}`;
+ expect(candidateScanningResult).toBe(expected);
+ expect(fullTextResult).toBe(`${rawHtmlPrefix}${expected}`);
+ });
+
+ test.each(['https:///path/example.org', 'https://?next=example.org', 'https://#example.org'])('does not autolink a domain inside malformed URL %s', (invalidUrl) => {
+ // Given a protocol without a hostname whose connected path, query, or fragment contains a domain.
+
+ // When ExpensiMark parses the complete malformed URL.
+ const result = parser.replace(invalidUrl);
+
+ // Then it keeps the complete connected URL text plain instead of linking the nested domain.
+ expect(result).toBe(invalidUrl);
+ });
+
+ test.each(invalidUrlSuffixesWithNestedDomain)('does not autolink a domain inside an invalid protocol URL suffix in %s', (suffix) => {
+ // Given an invalid protocol hostname followed by a path, query, or fragment containing another domain, optionally after a valid port.
+ const input = `https://${'a'.repeat(64)}.com${suffix}`;
+
+ // When ExpensiMark parses the complete invalid URL through candidate scanning and full-text matching.
+ const result = parser.replace(input);
+ const rawHtmlResult = parser.replace(`x ${input}`, {shouldEscapeText: false});
+
+ // Then it leaves the complete URL plain instead of linking a domain that still belongs to that URL.
+ expect(result).toBe(input);
+ expect(rawHtmlResult).toBe(`x ${input}`);
+ });
+
+ test.each(invalidUrlSuffixesWithNestedDomain)('does not autolink a domain inside an invalid bare URL suffix in %s', (suffix) => {
+ // Given an invalid bare hostname followed by a path, query, or fragment containing another domain, optionally after a valid port.
+ const input = `${'a'.repeat(64)}.com${suffix}`;
+
+ // When ExpensiMark parses the complete invalid URL through candidate scanning and full-text matching.
+ const result = parser.replace(input);
+ const rawHtmlResult = parser.replace(`x ${input}`, {shouldEscapeText: false});
+
+ // Then it leaves the complete URL plain instead of linking a domain that still belongs to that URL.
+ expect(result).toBe(input);
+ expect(rawHtmlResult).toBe(`x ${input}`);
+ });
+
+ test.each([
+ ['labeled link', (url) => `[label](${url})`],
+ ['image', (url) => ``],
+ ['video', (url) => ``],
+ ])('does not autolink a domain inside an invalid bare URL used by a %s', (_name, createInput) => {
+ // Given URL-based Markdown containing a domain in the path of an oversized bare hostname.
+ const invalidUrl = `${'a'.repeat(64)}.com/path/example.org`;
+ const input = createInput(invalidUrl);
+
+ // When ExpensiMark parses the complete Markdown input.
+ const result = parser.replace(input);
+
+ // Then it leaves the complete value unchanged instead of linking the domain inside the invalid URL.
+ expect(result).toBe(input);
+ });
+
+ test('does not autolink a domain inside an invalid bare URL when raw HTML disables candidate scanning', () => {
+ // Given raw HTML followed by an oversized bare URL whose path contains another domain.
+ const invalidUrl = `${'a'.repeat(64)}.com/path/example.org`;
+ const validDomain = 'valid.net';
+ const input = `x ${invalidUrl} ${validDomain}`;
+
+ // When ExpensiMark parses the input without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then it keeps the invalid URL plain while still autolinking the separate valid domain.
+ expect(result).toBe(`x ${invalidUrl} ${anchor(validDomain)}`);
+ });
+
+ test.each([
+ ['bare URL', `${'a'.repeat(64)}.com`],
+ ['protocol URL', `https://${'a'.repeat(64)}.com`],
+ ])('still autolinks a valid domain after an invalid %s path', (_name, invalidUrl) => {
+ // Given an oversized URL followed by a path boundary and a separate valid domain without whitespace.
+ const validDomain = 'valid.org';
+ const input = `${invalidUrl}/path;${validDomain}`;
+
+ // When ExpensiMark parses the complete non-whitespace token.
+ const result = parser.replace(input);
+
+ // Then it keeps the invalid URL path plain and autolinks the domain after the URL boundary.
+ expect(result).toBe(`${invalidUrl}/path;${anchor(validDomain)}`);
+ });
+
+ test('still autolinks a valid domain after an invalid raw-HTML URL path', () => {
+ // Given raw HTML followed by an oversized URL path and a separate valid domain without whitespace.
+ const invalidUrl = `https://${'a'.repeat(64)}.com/path`;
+ const validDomain = 'valid.org';
+ const input = `x ${invalidUrl};${validDomain}`;
+
+ // When ExpensiMark parses the input without escaping HTML.
+ const result = parser.replace(input, {shouldEscapeText: false});
+
+ // Then it keeps the invalid URL path plain and autolinks the domain after the URL boundary.
+ expect(result).toBe(`x ${invalidUrl};${anchor(validDomain)}`);
+ });
+
+ test('does not autolink URLs whose complete hostname exceeds 253 characters', () => {
+ // Given hostnames at and above the DNS length limit, with every individual label remaining valid.
+ const validHostname = `${'a.'.repeat(125)}com`;
+ const invalidHostname = `aa.${'a.'.repeat(124)}com`;
+ const validProtocolUrl = `https://${validHostname}`;
+ const trailingHyphens = '-'.repeat(300);
+ const invalidProtocolUrl = `https://${invalidHostname}/path`;
+
+ // When ExpensiMark parses bare and protocol URLs at those boundaries.
+ const validResult = parser.replace(validHostname);
+ const validWithPunctuationResult = parser.replace(`${validHostname}.`);
+ const validProtocolWithPunctuationResult = parser.replace(`${validProtocolUrl}.`);
+ const validProtocolWithTrailingHyphensResult = parser.replace(`${validProtocolUrl}${trailingHyphens}`);
+ const invalidResult = parser.replace(invalidHostname);
+ const invalidProtocolResult = parser.replace(invalidProtocolUrl);
+
+ // Then it links the valid hostname without counting punctuation and leaves oversized hostnames plain.
+ expect(validResult).toBe(anchor(validHostname));
+ expect(validWithPunctuationResult).toBe(`${anchor(validHostname)}.`);
+ expect(validProtocolWithPunctuationResult).toBe(`${anchor(validProtocolUrl)}.`);
+ expect(validProtocolWithTrailingHyphensResult).toBe(`${anchor(validProtocolUrl)}${trailingHyphens}`);
+ expect(invalidResult).toBe(invalidHostname);
+ expect(invalidProtocolResult).toBe(invalidProtocolUrl);
+ });
+
+ test.each([
+ ['labeled link', (url) => `[label](${url}/path)`],
+ ['image', (url) => ``],
+ ['video', (url) => ``],
+ ])('does not parse a %s whose complete hostname exceeds 253 characters', (_name, createInput) => {
+ // Given URL-based Markdown whose hostname exceeds the DNS limit without exceeding any individual label limit.
+ const hostname = `aa.${'a.'.repeat(124)}com`;
+ const input = createInput(`https://${hostname}`);
+
+ // When ExpensiMark parses the Markdown.
+ const result = parser.replace(input);
+
+ // Then it leaves the invalid URL-based Markdown unchanged instead of generating HTML with that hostname.
+ expect(result).toBe(input);
+ });
+
test('preserves the ftps protocol when autolinking a candidate', () => {
const input = 'ftps://example.com/file';
expect(parser.replace(input)).toBe('ftps://example.com/file ');
diff --git a/__tests__/URL-test.js b/__tests__/URL-test.js
index dd42d45a..ec32a218 100644
--- a/__tests__/URL-test.js
+++ b/__tests__/URL-test.js
@@ -1,4 +1,7 @@
-import {URL_REGEX_WITH_REQUIRED_PROTOCOL, URL_REGEX, LOOSE_URL_REGEX} from '../lib/Url';
+import fs from 'node:fs';
+import * as UrlPatterns from '../lib/Url';
+
+const {URL_REGEX_WITH_REQUIRED_PROTOCOL, URL_REGEX, LOOSE_URL_REGEX, MARKDOWN_URL_REGEX} = UrlPatterns;
describe('Strict URL validation', () => {
describe('Mandatory protocol for URL', () => {
@@ -65,3 +68,180 @@ describe('Loose URL validation', () => {
expect(regexToTest.test('http://my.localhost....local-domain:8080')).toBeFalsy();
});
});
+
+describe('Hostname label length validation', () => {
+ it.each([
+ ['URL_REGEX', URL_REGEX, ''],
+ ['LOOSE_URL_REGEX', LOOSE_URL_REGEX, 'https://'],
+ ])('%s preserves valid prefixes before long malformed hostname continuations', (_name, pattern, protocol) => {
+ // Given valid hostnames followed by malformed punctuation whose text would exceed a DNS length limit.
+ const regexToTest = new RegExp(pattern, 'i');
+ const validUrl = `${protocol}example.com`;
+ const maximumLengthUrl = `${protocol}${'a.'.repeat(125)}com`;
+ const inputs = [`${validUrl}..${'a'.repeat(300)}`, `${validUrl}.-${'a'.repeat(63)}`, `${validUrl}-.${'a'.repeat(300)}`, `${maximumLengthUrl}..nonsense`];
+
+ // When URL matching reaches malformed punctuation after the valid hostname.
+ const matches = inputs.map((input) => {
+ const match = regexToTest.exec(input);
+ return match && match[0];
+ });
+
+ // Then only the actual hostname counts toward the limits, so each valid prefix still matches.
+ expect(matches).toEqual([validUrl, validUrl, validUrl, maximumLengthUrl]);
+ });
+
+ it.each([
+ ['URL_REGEX', URL_REGEX, ''],
+ ['LOOSE_URL_REGEX', LOOSE_URL_REGEX, 'https://'],
+ ])('%s rejects oversized hostnames even before malformed continuations', (_name, pattern, protocol) => {
+ // Given an oversized hostname containing a known-TLD prefix, followed by malformed punctuation.
+ const input = `${protocol}example.com${'.a'.repeat(122)}..nonsense`;
+ const regexToTest = new RegExp(pattern, 'i');
+
+ // When URL matching checks the hostname before the malformed continuation.
+ const match = regexToTest.exec(input);
+
+ // Then the whole oversized hostname remains rejected rather than matching its earlier valid-looking prefix.
+ expect(match).toBeNull();
+ });
+
+ it.each([
+ ['URL_REGEX', URL_REGEX, `${'a'.repeat(63)}.com`, `${'a'.repeat(64)}.com`],
+ ['URL_REGEX_WITH_REQUIRED_PROTOCOL', URL_REGEX_WITH_REQUIRED_PROTOCOL, `https://${'a'.repeat(63)}.com`, `https://${'a'.repeat(64)}.com`],
+ ['LOOSE_URL_REGEX', LOOSE_URL_REGEX, `https://${'a'.repeat(63)}.local`, `https://${'a'.repeat(64)}.local`],
+ ])('%s accepts 63-character labels and rejects longer labels', (_name, pattern, validUrl, invalidUrl) => {
+ // Given a URL whose hostname label is at the DNS limit and one whose label exceeds it.
+ const regexToTest = new RegExp(pattern, 'i');
+
+ // When each complete URL is checked by the corresponding URL pattern.
+ const validMatch = regexToTest.exec(validUrl);
+ regexToTest.lastIndex = 0;
+ const invalidMatch = regexToTest.exec(invalidUrl);
+
+ // Then the valid URL matches fully and the invalid URL cannot be partially matched from inside its hostname label.
+ expect(validMatch && validMatch[0]).toBe(validUrl);
+ expect(invalidMatch).toBeNull();
+ });
+
+ it.each([
+ ['URL_REGEX', URL_REGEX, ''],
+ ['URL_REGEX_WITH_REQUIRED_PROTOCOL', URL_REGEX_WITH_REQUIRED_PROTOCOL, 'https://'],
+ ['LOOSE_URL_REGEX', LOOSE_URL_REGEX, 'https://'],
+ ])('%s accepts 253-character hostnames and rejects longer hostnames', (_name, pattern, prefix) => {
+ // Given hostnames at and above the DNS length limit, with every individual label remaining valid.
+ const validHostname = `${'a.'.repeat(125)}com`;
+ const invalidHostname = `aa.${'a.'.repeat(124)}com`;
+ const regexToTest = new RegExp(pattern, 'i');
+
+ // When each complete URL is checked by the corresponding URL pattern.
+ const validUrl = `${prefix}${validHostname}`;
+ const invalidUrl = `${prefix}${invalidHostname}`;
+ const validMatch = regexToTest.exec(validUrl);
+ regexToTest.lastIndex = 0;
+ const invalidMatch = regexToTest.exec(invalidUrl);
+
+ // Then only the URL whose complete hostname is within the limit matches.
+ expect(validMatch && validMatch[0]).toBe(validUrl);
+ expect(invalidMatch).toBeNull();
+ });
+
+ it.each([
+ ['an invalid dot-separated label', `${'a'.repeat(64)}.example.com`],
+ ['an invalid hyphenated label after a protocol', `https://${'a'.repeat(64)}-valid.com`],
+ ['a leading hyphen attached to another hostname label', 'foo.-valid.com'],
+ ['multiple leading hyphens', '--valid.com'],
+ ])('does not match a valid hostname suffix inside %s', (_name, invalidUrl) => {
+ // Given an invalid hostname that contains a shorter valid-looking domain suffix.
+ const regexToTest = new RegExp(MARKDOWN_URL_REGEX, 'i');
+
+ // When the URL pattern searches the complete invalid value.
+ const match = regexToTest.exec(invalidUrl);
+
+ // Then it rejects the whole value instead of matching only the valid suffix.
+ expect(match).toBeNull();
+ });
+
+ it('keeps matching a domain after a standalone boundary hyphen', () => {
+ // Given a valid domain preceded by one standalone hyphen used as punctuation.
+ const input = '-example.com';
+ const regexToTest = new RegExp(MARKDOWN_URL_REGEX, 'i');
+
+ // When the URL pattern searches the complete text.
+ const match = regexToTest.exec(input);
+
+ // Then it links the valid domain after the boundary without including the hyphen.
+ expect(match && match[0]).toBe('example.com');
+ });
+
+ it('does not count a URL path toward the hostname limit', () => {
+ // Given a URL with a valid hostname and a path that makes the complete URL longer than 253 characters.
+ const url = `https://example.com/${'a'.repeat(1000)}`;
+ const regexToTest = new RegExp(`^${URL_REGEX_WITH_REQUIRED_PROTOCOL}$`, 'i');
+
+ // When the complete URL is checked.
+ const isValid = regexToTest.test(url);
+
+ // Then it remains valid because only the hostname is subject to the 253-character limit.
+ expect(isValid).toBeTruthy();
+ });
+
+ it('keeps protocol IPv4 URLs valid', () => {
+ // Given an IPv4 URL that is supported by the loose URL pattern.
+ const ipv4Url = 'http://127.0.0.1/path';
+ const regexToTest = new RegExp(`^${LOOSE_URL_REGEX}$`, 'i');
+
+ // When the URL is checked after applying hostname-label limits.
+ const isValid = regexToTest.test(ipv4Url);
+
+ // Then its numeric labels remain valid because none exceeds the new limit.
+ expect(isValid).toBeTruthy();
+ });
+});
+
+describe('URL pattern consistency', () => {
+ it.each([
+ ['a one-character label', 'a', true],
+ ['a hyphen inside a label', 'valid-label', true],
+ ['a leading hyphen', '-invalid', false],
+ ['a trailing hyphen', 'invalid-', false],
+ ['an underscore', 'invalid_label', false],
+ ])('keeps strict and loose URL patterns aligned for %s', (_name, label, shouldMatch) => {
+ // Given the same hostname label used by strict and loose URL patterns.
+ const labelRegex = new RegExp(`^${UrlPatterns.URL_HOSTNAME_LABEL_REGEX}$`, 'i');
+ const strictUrlRegex = new RegExp(`^${URL_REGEX}$`, 'i');
+ const looseUrlRegex = new RegExp(`^${LOOSE_URL_REGEX}$`, 'i');
+
+ // When each URL pattern validates the label.
+ const results = [labelRegex.test(label), strictUrlRegex.test(`${label}.com`), looseUrlRegex.test(`https://${label}.local`)];
+
+ // Then every pattern applies the same label syntax and length limits.
+ expect(results).toEqual([shouldMatch, shouldMatch, shouldMatch]);
+ });
+
+ it('keeps published URL pattern declarations aligned with their runtime values', () => {
+ // Given the URL patterns exported at runtime and their published literal declarations.
+ const declaration = fs.readFileSync('lib/Url.d.ts', 'utf8');
+ const literalPatternNames = [
+ 'URL_PROTOCOL_REGEX',
+ 'URL_PORT_REGEX',
+ 'URL_HOSTNAME_LABEL_REGEX',
+ 'URL_WEBSITE_REGEX',
+ 'URL_PATH_REGEX',
+ 'URL_PARAM_REGEX',
+ 'URL_FRAGMENT_REGEX',
+ 'URL_REGEX',
+ 'LOOSE_URL_WEBSITE_REGEX',
+ 'LOOSE_URL_REGEX',
+ 'MARKDOWN_URL_REGEX',
+ ];
+
+ // When each runtime value is escaped as the matching TypeScript string literal.
+ const staleDeclarations = literalPatternNames.filter((name) => {
+ const escapedPattern = UrlPatterns[name].replaceAll('\\', '\\\\').replaceAll("'", "\\'");
+ return !declaration.includes(`declare const ${name}: '${escapedPattern}';`);
+ });
+
+ // Then Url.d.ts describes the exact patterns exported by Url.js.
+ expect(staleDeclarations).toEqual([]);
+ });
+});
diff --git a/lib/CONST.ts b/lib/CONST.ts
index 8d98ca5b..808ca943 100644
--- a/lib/CONST.ts
+++ b/lib/CONST.ts
@@ -3,6 +3,12 @@
const EMAIL_BASE_REGEX =
"(?=((?=[\\w'#%+-]+(?:\\.[\\w'#%+-]+)*@)[\\w\\.'#%+-]{1,64}@(?:(?=[a-z\\d]+(?:-+[a-z\\d]+)*\\.)(?:[a-z\\d-]{1,63}\\.)+[a-z]{2,63})(?= |_|\\b))(?.*))\\S{3,254}(?=\\k$)";
+// DNS hostname labels are limited to 63 octets. URL hostnames are ASCII here, so characters and octets are equivalent.
+const MAX_URL_HOSTNAME_LABEL_LENGTH = 63;
+
+// DNS names can use 255 wire octets, which allows at most 253 visible ASCII characters without a trailing root dot.
+const MAX_URL_HOSTNAME_LENGTH = 253;
+
const MOMENT_FORMAT_STRING = 'YYYY-MM-DD';
/**
@@ -1940,4 +1946,4 @@ const PUBLIC_DOMAINS_SET = new Set([
'ymail.com',
]);
-export {g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS_SET};
+export {g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS_SET, MAX_URL_HOSTNAME_LABEL_LENGTH, MAX_URL_HOSTNAME_LENGTH};
diff --git a/lib/ExpensiMark.ts b/lib/ExpensiMark.ts
index 94ce51f2..bda4c137 100644
--- a/lib/ExpensiMark.ts
+++ b/lib/ExpensiMark.ts
@@ -51,12 +51,20 @@ const URL_TLD_LIST = TLD_REGEX.toLowerCase().split('|');
const URL_TLDS = new Set(URL_TLD_LIST);
// Caps TLD scanning at the longest known TLD so long invalid URL-like text avoids expensive regex work.
const MAX_URL_TLD_LENGTH = Math.max(...URL_TLD_LIST.map((tld) => tld.length));
+const URL_HOSTNAME_LABEL_AT_POSITION_REGEX = new RegExp(UrlPatterns.URL_HOSTNAME_LABEL_REGEX, 'iy');
+const URL_SUFFIX_AT_POSITION_REGEX = new RegExp(
+ `(?:\\:${UrlPatterns.URL_PORT_REGEX}(?![0-9]))?${UrlPatterns.URL_PATH_REGEX}(?:${UrlPatterns.URL_PARAM_REGEX}|${UrlPatterns.URL_FRAGMENT_REGEX})*`,
+ 'iy',
+);
const PROTECTED_TAG_NAMES = new Set(['a', 'code', 'pre', 'video']);
type ReplacementFn = (extras: Extras, ...matches: string[]) => string;
type Replacement = ReplacementFn | string;
type ProcessFn = (textToProcess: string, replacement: Replacement, shouldKeepRawInput: boolean, shouldEscapeText: boolean) => string;
-type UrlCandidate = {start: number; end: number};
+type TextRange = {start: number; end: number};
+type UrlCandidate = TextRange;
+type HostnameAnalysis = {isValid: boolean; validHostnamePrefixEnd?: number};
+type UrlRangeAnalysis = {hostnameEnd: number; validHostnamePrefixEnd?: number; invalidRange?: TextRange};
type MarkdownMarkerCharacter = '*' | '~';
type MarkdownMarker = {position: number; isProtected: boolean};
type CanOpenMarkdown = (text: string, position: number, isProtected: boolean) => boolean;
@@ -263,66 +271,33 @@ function getProtocolAt(text: string, position: number) {
}
/**
- * Returns whether one dot-separated hostname label is valid.
+ * Finds the valid prefix of a hostname label using the shared URL label pattern.
*
* @param text - Candidate URL text containing the label.
* @param start - Index of the label's first character.
- * @param end - Index immediately after the label's last character.
+ * @returns The index after the valid label prefix, or undefined when the label starts with invalid syntax.
*/
-function isValidHostnameLabel(text: string, start: number, end: number): boolean {
- if (start >= end || !isAsciiAlphaNumeric(text[start]) || !isAsciiAlphaNumeric(text[end - 1])) {
- return false;
- }
-
- for (let index = start + 1; index < end - 1; index++) {
- if (!isAsciiAlphaNumeric(text[index]) && text[index] !== '-') {
- return false;
- }
- }
- return true;
+function findValidHostnameLabelEnd(text: string, start: number): number | undefined {
+ URL_HOSTNAME_LABEL_AT_POSITION_REGEX.lastIndex = start;
+ const match = URL_HOSTNAME_LABEL_AT_POSITION_REGEX.exec(text);
+ return match === null ? undefined : URL_HOSTNAME_LABEL_AT_POSITION_REGEX.lastIndex;
}
/**
- * Finds the first character of a valid hostname before the dot that starts its top-level domain.
+ * Finds the first character after a URL's hostname.
*
- * @param text - Candidate URL text containing the hostname.
- * @param dotPosition - Index of the dot immediately before the top-level domain.
- * @returns The hostname's first-character index, or undefined when no valid hostname precedes the dot.
+ * @param text - Text containing the URL candidate.
+ * @param hostnameStart - Index of the hostname's first character.
+ * @param candidateEnd - Index immediately after the URL candidate.
+ * @returns The index immediately after the hostname.
*/
-function findHostnameStart(text: string, dotPosition: number): number | undefined {
- let hostnameStart = dotPosition;
- let labelEnd = dotPosition;
-
- while (labelEnd > 0) {
- let rawLabelStart = labelEnd - 1;
- while (rawLabelStart >= 0 && text[rawLabelStart] !== '.' && isHostnameCharacter(text[rawLabelStart])) {
- rawLabelStart--;
- }
- rawLabelStart++;
-
- let labelStart = rawLabelStart;
- while (labelStart < labelEnd && text[labelStart] === '-') {
- labelStart++;
- }
-
- if (!isValidHostnameLabel(text, labelStart, labelEnd)) {
- break;
- }
- hostnameStart = labelStart;
-
- // A leading hyphen ends the hostname, but the existing URL regex can still match the valid suffix after it.
- if (labelStart !== rawLabelStart) {
- break;
- }
-
- const separatorPosition = labelStart - 1;
- if (separatorPosition < 0 || text[separatorPosition] !== '.') {
- break;
- }
- labelEnd = separatorPosition;
+function findHostnameEnd(text: string, hostnameStart: number, candidateEnd: number): number {
+ let hostnameEnd = hostnameStart;
+ while (hostnameEnd < candidateEnd && isHostnameCharacter(text[hostnameEnd])) {
+ hostnameEnd++;
}
- return hostnameStart === dotPosition ? undefined : hostnameStart;
+ return hostnameEnd;
}
/**
@@ -351,6 +326,66 @@ function findKnownTldEnd(text: string, dotPosition: number): number | undefined
return undefined;
}
+/**
+ * Validates a hostname and records a linkable prefix before a later malformed label.
+ * Counts only the hostname before malformed punctuation; oversized hostname labels still cannot produce a partial link.
+ *
+ * @param text - Text containing the URL candidate.
+ * @param hostnameStart - Index of the hostname's first character.
+ * @param hostnameEnd - Index immediately after the hostname.
+ * @param hasProtocol - Whether the URL allows hostnames without a known TLD, including single-label hosts.
+ * @returns The hostname validity and any prefix that can be linked before malformed punctuation.
+ */
+function analyzeHostname(text: string, hostnameStart: number, hostnameEnd: number, hasProtocol: boolean): HostnameAnalysis {
+ let hostnameContentEnd = hostnameEnd;
+ while (hostnameContentEnd > hostnameStart && (text[hostnameContentEnd - 1] === '.' || text[hostnameContentEnd - 1] === '-')) {
+ hostnameContentEnd--;
+ }
+
+ if (hostnameContentEnd === hostnameStart) {
+ return {isValid: false};
+ }
+
+ let labelStart = hostnameStart;
+ let validHostnamePrefixEnd: number | undefined;
+ for (let index = hostnameStart; index <= hostnameContentEnd; index++) {
+ if (index !== hostnameContentEnd && text[index] !== '.') {
+ continue;
+ }
+
+ const validLabelEnd = findValidHostnameLabelEnd(text, labelStart);
+ if (validLabelEnd === undefined) {
+ return {isValid: false, validHostnamePrefixEnd};
+ }
+
+ // Measure the valid label prefix before any trailing hyphens that end it.
+ let labelContentEnd = index;
+ while (labelContentEnd > labelStart && text[labelContentEnd - 1] === '-') {
+ labelContentEnd--;
+ }
+ if (labelContentEnd - labelStart > Constants.MAX_URL_HOSTNAME_LABEL_LENGTH || labelContentEnd - hostnameStart > Constants.MAX_URL_HOSTNAME_LENGTH) {
+ return {isValid: false};
+ }
+ // Protocol URLs can end at any valid label; bare URLs still need a known TLD.
+ if (hasProtocol) {
+ validHostnamePrefixEnd = validLabelEnd;
+ }
+ if (validLabelEnd !== index) {
+ return {isValid: false, validHostnamePrefixEnd};
+ }
+
+ if (!hasProtocol && index !== hostnameContentEnd) {
+ const tldEnd = findKnownTldEnd(text, index);
+ if (tldEnd !== undefined && tldEnd <= hostnameContentEnd) {
+ validHostnamePrefixEnd = tldEnd;
+ }
+ }
+ labelStart = index + 1;
+ }
+
+ return {isValid: true};
+}
+
/** Expands example.com to include nearby @, *, _, or ~ in any order, plus its path, until whitespace or HTML. */
function extendUrlCandidateBoundaries(text: string, start: number, end: number): UrlCandidate {
let candidateStart = start;
@@ -376,6 +411,146 @@ function startsWithIgnoreCase(text: string, expected: string, position: number):
return text.slice(position, position + expected.length).toLowerCase() === expected;
}
+/**
+ * Finds the first unmatched closing parenthesis, which ends a URL rather than belonging to its path.
+ *
+ * @param text - Text containing the URL or its suffix.
+ * @param start - First index to inspect, inclusive.
+ * @param end - Last index to inspect, exclusive.
+ * @returns The unmatched parenthesis index, or end when all closing parentheses are paired.
+ */
+function findUrlEndBeforeUnmatchedParenthesis(text: string, start: number, end: number): number {
+ let unmatchedOpenParentheses = 0;
+ for (let index = start; index < end; index++) {
+ if (text[index] === '(') {
+ unmatchedOpenParentheses++;
+ } else if (text[index] === ')') {
+ if (unmatchedOpenParentheses === 0) {
+ return index;
+ }
+ unmatchedOpenParentheses--;
+ }
+ }
+ return end;
+}
+
+/**
+ * Finds where a URL ends by matching its optional port, path, query, and fragment after the hostname.
+ *
+ * @param text - Text containing the URL candidate.
+ * @param hostnameEnd - Index immediately after the hostname.
+ * @returns The first index after the URL recognized by ExpensiMark.
+ */
+function findUrlEnd(text: string, hostnameEnd: number): number {
+ URL_SUFFIX_AT_POSITION_REGEX.lastIndex = hostnameEnd;
+ const match = URL_SUFFIX_AT_POSITION_REGEX.exec(text);
+ return findUrlEndBeforeUnmatchedParenthesis(text, hostnameEnd, hostnameEnd + (match?.[0].length ?? 0));
+}
+
+/**
+ * Analyzes a URL candidate and returns the connected range when its hostname is invalid.
+ *
+ * @param text - Text containing the URL candidate.
+ * @param urlStart - Index where the URL starts, excluding Markdown punctuation.
+ * @param hostnameStart - Index where the hostname starts.
+ * @param candidate - Broad candidate range found by the scanner.
+ * @returns The hostname end and its connected URL range when the hostname is invalid.
+ */
+function analyzeUrlRange(text: string, urlStart: number, hostnameStart: number, candidate: UrlCandidate): UrlRangeAnalysis {
+ const hostnameEnd = findHostnameEnd(text, hostnameStart, candidate.end);
+ const hostnameAnalysis = analyzeHostname(text, hostnameStart, hostnameEnd, hostnameStart !== urlStart);
+ if (hostnameAnalysis.isValid) {
+ return {hostnameEnd};
+ }
+
+ let invalidRangeEnd = findUrlEnd(text, hostnameEnd);
+ if (text[hostnameEnd] === ':') {
+ // The broad hostname scan can include the letters of the next protocol, as in example.com..http://valid.org.
+ for (const protocol of URL_PROTOCOLS) {
+ const protocolStart = hostnameEnd - protocol.indexOf(':');
+ if (protocolStart >= hostnameStart && startsWithIgnoreCase(text, protocol, protocolStart)) {
+ invalidRangeEnd = protocolStart;
+ break;
+ }
+ }
+ }
+
+ return {
+ hostnameEnd,
+ validHostnamePrefixEnd: hostnameAnalysis.validHostnamePrefixEnd,
+ invalidRange: {start: hostnameAnalysis.validHostnamePrefixEnd ?? urlStart, end: invalidRangeEnd},
+ };
+}
+
+/**
+ * Finds the first hostname character before a known top-level domain, including invalid labels.
+ *
+ * @param text - Text containing the bare hostname.
+ * @param dotPosition - Index of the dot immediately before the known top-level domain.
+ * @returns The hostname start, excluding a standalone punctuation hyphen.
+ */
+function findRawHostnameStart(text: string, dotPosition: number): number {
+ let hostnameStart = dotPosition;
+ while (hostnameStart > 0 && isHostnameCharacter(text[hostnameStart - 1])) {
+ hostnameStart--;
+ }
+
+ const hasStandaloneBoundaryHyphen = text[hostnameStart] === '-' && text[hostnameStart + 1] !== '-' && !isHostnameCharacter(text[hostnameStart - 1]);
+ if (hasStandaloneBoundaryHyphen) {
+ hostnameStart++;
+ }
+ return hostnameStart;
+}
+
+/**
+ * Finds URL ranges whose hostname is malformed or exceeds the supported DNS length limits.
+ * These ranges keep both URL parsing paths from matching another domain inside the same invalid URL.
+ *
+ * @param text - Text containing possible protocol and bare URLs.
+ * @returns Connected invalid URL ranges that must remain plain text.
+ */
+function findInvalidUrlRanges(text: string): TextRange[] {
+ const ranges: TextRange[] = [];
+ let index = 0;
+
+ while (index < text.length) {
+ const matchedProtocol = getProtocolAt(text, index);
+ let analysis: UrlRangeAnalysis;
+ let nextIndex: number;
+
+ if (matchedProtocol) {
+ const candidate = extendUrlCandidateBoundaries(text, index, index + matchedProtocol.length);
+ analysis = analyzeUrlRange(text, index, index + matchedProtocol.length, candidate);
+ nextIndex = Math.max(index + matchedProtocol.length, analysis.hostnameEnd);
+ } else {
+ if (text[index] !== '.') {
+ index++;
+ continue;
+ }
+
+ const tldEnd = findKnownTldEnd(text, index);
+ if (tldEnd === undefined) {
+ index++;
+ continue;
+ }
+
+ const hostnameStart = findRawHostnameStart(text, index);
+ const candidate = extendUrlCandidateBoundaries(text, hostnameStart, tldEnd);
+ analysis = analyzeUrlRange(text, hostnameStart, hostnameStart, candidate);
+ nextIndex = tldEnd;
+ }
+
+ if (analysis.invalidRange) {
+ ranges.push(analysis.invalidRange);
+ index = analysis.invalidRange.end;
+ } else {
+ index = nextIndex;
+ }
+ }
+
+ return ranges;
+}
+
/**
* Removes candidates that the original full-text URL regex would reject because of later HTML.
* It scans right to left so each candidate can use the nearest later HTML boundary without copying a long suffix.
@@ -432,7 +607,7 @@ function filterUrlCandidatesBlockedByFollowingHtml(text: string, candidates: Url
return validCandidates.reverse();
}
-/** Finds possible URL ranges, skips URL-looking text inside protected tags, and leaves final validity to the existing regex. */
+/** Finds possible URL ranges, skips URL-looking text inside protected tags, rejects malformed or oversized hostnames, and leaves remaining validation to the existing regex. */
function findUrlCandidates(text: string): UrlCandidate[] {
const candidates: UrlCandidate[] = [];
const protectedTags: string[] = [];
@@ -457,6 +632,14 @@ function findUrlCandidates(text: string): UrlCandidate[] {
const matchedProtocol = getProtocolAt(text, index);
if (matchedProtocol) {
const candidate = extendUrlCandidateBoundaries(text, index, index + matchedProtocol.length);
+ const analysis = analyzeUrlRange(text, index, index + matchedProtocol.length, candidate);
+ if (analysis.invalidRange) {
+ if (analysis.validHostnamePrefixEnd !== undefined) {
+ candidates.push({start: candidate.start, end: analysis.validHostnamePrefixEnd});
+ }
+ index = analysis.invalidRange.end;
+ continue;
+ }
candidates.push(candidate);
index = candidate.end;
continue;
@@ -468,15 +651,24 @@ function findUrlCandidates(text: string): UrlCandidate[] {
}
const tldEnd = findKnownTldEnd(text, index);
- const hostnameStart = tldEnd === undefined ? undefined : findHostnameStart(text, index);
- if (tldEnd === undefined || hostnameStart === undefined) {
+ if (tldEnd === undefined) {
index++;
continue;
}
- const candidate = extendUrlCandidateBoundaries(text, hostnameStart, tldEnd);
- candidates.push(candidate);
- index = candidate.end;
+ const rawHostnameStart = findRawHostnameStart(text, index);
+ const broadCandidate = extendUrlCandidateBoundaries(text, rawHostnameStart, tldEnd);
+ const analysis = analyzeUrlRange(text, rawHostnameStart, rawHostnameStart, broadCandidate);
+ if (analysis.invalidRange) {
+ if (analysis.validHostnamePrefixEnd !== undefined) {
+ candidates.push({start: broadCandidate.start, end: analysis.validHostnamePrefixEnd});
+ }
+ index = analysis.invalidRange.end;
+ continue;
+ }
+
+ candidates.push(broadCandidate);
+ index = broadCandidate.end;
}
return filterUrlCandidatesBlockedByFollowingHtml(text, candidates);
@@ -1143,7 +1335,7 @@ export default class ExpensiMark {
process: (textToProcess, replacement, _shouldKeepRawInput, shouldEscapeText) => {
const regex = new RegExp(`(?![^<]*>|[^<>]*<\\/(?!h1>))([_*~]*?)${UrlPatterns.MARKDOWN_URL_REGEX}\\1(?!((?:(?!|[^<]*(<\\/pre>|<\\/code>))`, 'gi');
// Raw HTML depends on complete-text lookaheads. Text without user-provided HTML can safely use the faster candidate scanner.
- return this.modifyTextForUrlLinks(regex, textToProcess, replacement as ReplacementFn, canUseCandidateScanning(textToProcess, shouldEscapeText));
+ return this.modifyTextForUrlLinks(regex, textToProcess, replacement as ReplacementFn, canUseCandidateScanning(textToProcess, shouldEscapeText), true);
},
replacement: (_extras, _match, g1, g2) => {
@@ -1755,9 +1947,15 @@ export default class ExpensiMark {
}
/**
- * Checks matched URLs for validity and replace valid links with html elements
+ * Replaces valid URL matches and optionally narrows parsing to URL candidates.
+ *
+ * @param regex - The URL pattern used for the final match.
+ * @param textToCheck - The text containing possible URLs.
+ * @param replacement - The replacement applied to each accepted URL.
+ * @param shouldScanForUrls - Whether to scan URL candidates before running the regex.
+ * @param shouldRejectPartialHostnameMatches - Whether to reject matches inside invalid URL ranges.
*/
- modifyTextForUrlLinks(regex: RegExp, textToCheck: string, replacement: ReplacementFn, shouldScanForUrls = false): string {
+ modifyTextForUrlLinks(regex: RegExp, textToCheck: string, replacement: ReplacementFn, shouldScanForUrls = false, shouldRejectPartialHostnameMatches = false): string {
if (shouldScanForUrls) {
const candidates = findUrlCandidates(textToCheck);
if (candidates.length === 0) {
@@ -1772,7 +1970,7 @@ export default class ExpensiMark {
const candidate = textToCheck.slice(start, end);
candidateRegex.lastIndex = 0;
output.push(textToCheck.slice(outputStart, start));
- output.push(this.modifyTextForUrlLinks(candidateRegex, candidate, replacement));
+ output.push(this.modifyTextForUrlLinks(candidateRegex, candidate, replacement, false, shouldRejectPartialHostnameMatches));
outputStart = end;
}
@@ -1780,30 +1978,36 @@ export default class ExpensiMark {
return output.join('');
}
- let match = regex.exec(textToCheck);
+ const matchingRegex = regex;
+ let match = matchingRegex.exec(textToCheck);
let replacedText = '';
let startIndex = 0;
+ const invalidUrlRanges = shouldRejectPartialHostnameMatches ? findInvalidUrlRanges(textToCheck) : [];
+ let invalidUrlRangeIndex = 0;
while (match !== null) {
- // We end the link at the last closing parenthesis that matches an opening parenthesis because unmatched closing parentheses are unlikely to be in the url
- // and can be part of markdown for example
- let unmatchedOpenParentheses = 0;
- let url = match[2];
- for (let i = 0; i < url.length; i++) {
- if (url[i] === '(') {
- unmatchedOpenParentheses++;
- } else if (url[i] === ')') {
- // Unmatched closing parenthesis
- if (unmatchedOpenParentheses <= 0) {
- const numberOfCharsToRemove = url.length - i;
- match[0] = match[0].substr(0, match[0].length - numberOfCharsToRemove);
- url = url.substr(0, url.length - numberOfCharsToRemove);
- break;
- }
- unmatchedOpenParentheses--;
+ if (shouldRejectPartialHostnameMatches) {
+ const matchedUrlOffset = match.index + match[1].length;
+ while (invalidUrlRangeIndex < invalidUrlRanges.length && invalidUrlRanges[invalidUrlRangeIndex].end <= matchedUrlOffset) {
+ invalidUrlRangeIndex++;
+ }
+ const invalidUrlRange = invalidUrlRanges[invalidUrlRangeIndex];
+ const isInsideInvalidUrl = invalidUrlRange !== undefined && invalidUrlRange.start <= matchedUrlOffset && matchedUrlOffset < invalidUrlRange.end;
+ if (isInsideInvalidUrl) {
+ // The rejected match may extend beyond the invalid range and include a separate URL.
+ matchingRegex.lastIndex = invalidUrlRange.end;
+ match = matchingRegex.exec(textToCheck);
+ continue;
}
}
+ let url = match[2];
+ const urlEnd = findUrlEndBeforeUnmatchedParenthesis(url, 0, url.length);
+ if (urlEnd < url.length) {
+ match[0] = match[0].slice(0, match[0].length - (url.length - urlEnd));
+ url = url.slice(0, urlEnd);
+ }
+
// Because we are removing ) parenthesis, some special characters that shouldn't be in the href are in the href
// For example google.com/toto.) is accepted by the regular expression above and we remove the ) parenthesis, so the link becomes google.com/toto. which is not a valid link
// In that case we should also remove the "."
@@ -1870,7 +2074,7 @@ export default class ExpensiMark {
startIndex = match.index + match[0].length;
// Now we move to the next match that the js regex found in the text
- match = regex.exec(textToCheck);
+ match = matchingRegex.exec(textToCheck);
}
if (startIndex < textToCheck.length) {
replacedText = replacedText.concat(textToCheck.substr(startIndex));
diff --git a/lib/Url.d.ts b/lib/Url.d.ts
index fadb39de..10da2e40 100644
--- a/lib/Url.d.ts
+++ b/lib/Url.d.ts
@@ -1,14 +1,17 @@
declare const URL_PROTOCOL_REGEX: '((ht|f)tps?:\\/\\/)';
-declare const URL_WEBSITE_REGEX: '((ht|f)tps?:\\/\\/)?((?:www\\.)?[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)+(?:XN--VERMGENSBERATUNG-PWB|XN--VERMGENSBERATER-CTB|XN--CLCHC0EA0B2G2A9GCD|XN--W4R85EL8FHU5DNRA|TRAVELERSINSURANCE|NORTHWESTERNMUTUAL|XN--XKC2DL3A5EE0H|XN--MGBERP4A5D4AR|XN--MGBAI9AZGQP6J|XN--MGBAH1A3HJKRD|XN--BCK1B9A5DRE4C|XN--5SU34J936BGSG|XN--3OQ18VL8PN36A|XN--XKC2AL3HYE2A|XN--MGBCPQ6GPA1A|XN--MGBA7C0BBN0A|XN--FZYS8D69UVGM|XN--NQV7FS00EMA|XN--MGBC0A9AZCG|XN--MGBAAKC7DVF|XN--MGBA3A4F16A|XN--LGBBAT1AD8J|XN--KCRX77D1X4A|XN--I1B6B1A6A2E|SANDVIKCOROMANT|KERRYPROPERTIES|AMERICANEXPRESS|XN--RVC1E0AM3E|XN--MGBX4CD0AB|XN--MGBI4ECEXP|XN--MGBCA7DZDO|XN--MGBBH1A71E|XN--MGBAYH7GPA|XN--MGBAAM7A8H|XN--MGBA3A3EJT|XN--JLQ61U9W7B|XN--JLQ480N2RG|XN--H2BREG3EVE|XN--FIQ228C5HS|XN--B4W605FERD|XN--80AQECDR1A|XN--6QQ986B3XL|XN--54B7FTA0CC|WEATHERCHANNEL|KERRYLOGISTICS|COOKINGCHANNEL|CANCERRESEARCH|BANANAREPUBLIC|AMERICANFAMILY|AFAMILYCOMPANY|XN--YGBI2AMMX|XN--YFRO4I67O|XN--TIQ49XQYJ|XN--H2BRJ9C8C|XN--FZC2C9E2C|XN--FPCRJ9C3D|XN--ECKVDTC9D|XN--CCKWCXETD|WOLTERSKLUWER|TRAVELCHANNEL|SPREADBETTING|LIFEINSURANCE|INTERNATIONAL|XN--QCKA1PMC|XN--OGBPF8FL|XN--NGBE9E0A|XN--NGBC5AZD|XN--MK1BU44C|XN--MGBT3DHD|XN--MGBPL2FH|XN--MGBGU82A|XN--MGBAB2BD|XN--MGB9AWBF|XN--GCKR3F0F|XN--8Y0A063A|XN--80ASEHDB|XN--80ADXHKS|XN--4DBRK0CE|XN--45BR5CYL|XN--3E0B707E|VERSICHERUNG|SCHOLARSHIPS|LPLFINANCIAL|CONSTRUCTION|XN--ZFR164B|XN--XHQ521B|XN--W4RS40L|XN--VUQ861B|XN--T60B56A|XN--SES554G|XN--S9BRJ9C|XN--ROVU88B|XN--RHQV96G|XN--Q9JYB4C|XN--PGBS0DH|XN--OTU796D|XN--NYQY26A|XN--MIX891F|XN--MGBTX2B|XN--MGBBH1A|XN--KPRY57D|XN--KPRW13D|XN--JVR189M|XN--J6W193G|XN--IMR513N|XN--HXT814E|XN--H2BRJ9C|XN--GK3AT1E|XN--GECRJ9C|XN--G2XX48C|XN--FLW351E|XN--FJQ720A|XN--FCT429K|XN--EFVY88H|XN--D1ACJ3B|XN--CZR694B|XN--CCK2B3B|XN--9KRT00A|XN--80AO21A|XN--6FRZ82G|XN--55QW42G|XN--45BRJ9C|XN--42C2D9A|XN--3HCRJ9C|XN--3DS443G|XN--3BST00M|XN--2SCRJ9C|XN--1QQW23A|XN--1CK2E1B|XN--11B4C3D|WILLIAMHILL|REDUMBRELLA|PROGRESSIVE|PRODUCTIONS|PLAYSTATION|PHOTOGRAPHY|OLAYANGROUP|MOTORCYCLES|LAMBORGHINI|KERRYHOTELS|INVESTMENTS|FOODNETWORK|ENTERPRISES|ENGINEERING|CREDITUNION|CONTRACTORS|CALVINKLEIN|BRIDGESTONE|BLOCKBUSTER|BLACKFRIDAY|BARCLAYCARD|ACCOUNTANTS|XN--Y9A3AQ|XN--WGBL6A|XN--WGBH1C|XN--UNUP4Y|XN--Q7CE6A|XN--PSSY2U|XN--O3CW4H|XN--MXTQ1M|XN--KPUT3I|XN--IO0A7I|XN--FIQZ9S|XN--FIQS8S|XN--FIQ64B|XN--CZRU2D|XN--CZRS0T|XN--CG4BKI|XN--C2BR7G|XN--9ET52U|XN--9DBQ2A|XN--90A3AC|XN--80ASWG|XN--5TZM5G|XN--55QX5D|XN--4GBRIM|XN--45Q11C|XN--3PXU8K|XN--30RR7Y|VOLKSWAGEN|VLAANDEREN|UNIVERSITY|TECHNOLOGY|TATAMOTORS|SWIFTCOVER|SCHAEFFLER|RESTAURANT|REPUBLICAN|REALESTATE|PRUDENTIAL|PROTECTION|PROPERTIES|ONYOURSIDE|NEXTDIRECT|NATIONWIDE|MITSUBISHI|MANAGEMENT|INDUSTRIES|IMMOBILIEN|HEALTHCARE|FOUNDATION|EXTRASPACE|EUROVISION|CUISINELLA|CREDITCARD|CONSULTING|CAPITALONE|BOEHRINGER|BNPPARIBAS|BASKETBALL|ASSOCIATES|APARTMENTS|ACCOUNTANT|YODOBASHI|XN--VHQUV|XN--TCKWE|XN--QXA6A|XN--P1ACF|XN--NQV7F|XN--NGBRX|XN--L1ACC|XN--J1AMH|XN--J1AEF|XN--FHBEI|XN--E1A4C|XN--D1ALF|XN--C1AVG|XN--90AIS|VACATIONS|TRAVELERS|STOCKHOLM|STATEFARM|STATEBANK|SOLUTIONS|SHANGRILA|SCJOHNSON|RICHARDLI|PRAMERICA|PASSAGENS|PANASONIC|MICROSOFT|MELBOURNE|MARSHALLS|MARKETING|LIFESTYLE|LANDROVER|LANCASTER|KUOKGROUP|INSURANCE|INSTITUTE|HOMESENSE|HOMEGOODS|HOMEDEPOT|HISAMITSU|GOLDPOINT|FURNITURE|FUJIXEROX|FRONTDOOR|FRESENIUS|FIRESTONE|FINANCIAL|FAIRWINDS|EQUIPMENT|EDUCATION|DIRECTORY|COMMUNITY|CHRISTMAS|BLOOMBERG|BARCELONA|AQUARELLE|ANALYTICS|AMSTERDAM|ALLFINANZ|ALFAROMEO|ACCENTURE|YOKOHAMA|XN--QXAM|XN--P1AI|XN--NODE|XN--90AE|WOODSIDE|VERISIGN|VENTURES|VANGUARD|TRAINING|SUPPLIES|STCGROUP|SOFTWARE|SOFTBANK|SHOWTIME|SHOPPING|SERVICES|SECURITY|SAMSCLUB|SAARLAND|RELIANCE|REDSTONE|PROPERTY|PLUMBING|PICTURES|PHARMACY|PARTNERS|OBSERVER|MORTGAGE|MERCKMSD|MEMORIAL|MCKINSEY|MASERATI|MARRIOTT|LUNDBECK|LIGHTING|JPMORGAN|ISTANBUL|IPIRANGA|INFINITI|HOSPITAL|HOLDINGS|HELSINKI|HDFCBANK|GUARDIAN|GRAPHICS|GRAINGER|GOODYEAR|FRONTIER|FOOTBALL|FIRMDALE|FIDELITY|FEEDBACK|EXCHANGE|ETISALAT|ERICSSON|ENGINEER|DOWNLOAD|DISCOVER|DISCOUNT|DIAMONDS|DEMOCRAT|DELOITTE|DELIVERY|COMPUTER|COMMBANK|CLOTHING|CLINIQUE|CLEANING|CITYEATS|CIPRIANI|CATHOLIC|CATERING|CAPETOWN|BUSINESS|BUILDERS|BUDAPEST|BRUSSELS|BROADWAY|BRADESCO|BOUTIQUE|BASEBALL|BARGAINS|BAREFOOT|BARCLAYS|ATTORNEY|ALLSTATE|AIRFORCE|ABUDHABI|ZUERICH|YOUTUBE|YAMAXUN|XFINITY|WINNERS|WINDOWS|WHOSWHO|WEDDING|WEBSITE|WEATHER|WATCHES|WANGGOU|WALMART|TRADING|TOSHIBA|TIFFANY|TICKETS|THEATRE|THEATER|TEMASEK|SYSTEMS|SURGERY|SUPPORT|STORAGE|STAPLES|SINGLES|SHIKSHA|SCIENCE|SCHWARZ|SCHMIDT|SANDVIK|SAMSUNG|REXROTH|REVIEWS|RENTALS|RECIPES|REALTOR|POLITIE|PIONEER|PHILIPS|ORIGINS|ORGANIC|OLDNAVY|OKINAWA|NEUSTAR|NETWORK|NETFLIX|NETBANK|MONSTER|MARKETS|LINCOLN|LIMITED|LECLERC|LATROBE|LASALLE|LANXESS|LACAIXA|KOMATSU|KITCHEN|JUNIPER|JEWELRY|ISMAILI|HYUNDAI|HOTMAIL|HOTELES|HOSTING|HOLIDAY|HITACHI|HANGOUT|HAMBURG|GUITARS|GROCERY|GODADDY|GENTING|GALLERY|FUJITSU|FROGANS|FORSALE|FLOWERS|FLORIST|FLIGHTS|FITNESS|FISHING|FINANCE|FERRERO|FERRARI|FASHION|FARMERS|EXPRESS|EXPOSED|DOMAINS|DIGITAL|DENTIST|CRUISES|CRICKET|COURSES|COUPONS|COUNTRY|CORSICA|COOKING|CONTACT|COMPARE|COMPANY|COMCAST|COLOGNE|COLLEGE|CLUBMED|CITADEL|CHINTAI|CHARITY|CHANNEL|CAREERS|CARAVAN|CAPITAL|BUGATTI|BROTHER|BOOKING|BESTBUY|BENTLEY|BAUHAUS|BANAMEX|AVIANCA|AUSPOST|AUDIBLE|AUCTION|ATHLETA|ANDROID|ALIBABA|AGAKHAN|ACADEMY|ABOGADO|ZAPPOS|YANDEX|YACHTS|XIHUAN|WEBCAM|WALTER|VUELOS|VOYAGE|VOTING|VISION|VIRGIN|VILLAS|VIKING|VIAJES|UNICOM|TRAVEL|TOYOTA|TKMAXX|TJMAXX|TIENDA|TENNIS|TATTOO|TARGET|TAOBAO|TAIPEI|SYDNEY|SWATCH|SUZUKI|SUPPLY|STUDIO|STREAM|SOCIAL|SOCCER|SHOUJI|SELECT|SECURE|SEARCH|SCHULE|SCHOOL|SANOFI|SAKURA|SAFETY|RYUKYU|ROGERS|ROCHER|REVIEW|REPORT|REPAIR|REISEN|REALTY|RACING|QUEBEC|PICTET|PHYSIO|PHOTOS|PFIZER|OTSUKA|ORANGE|ORACLE|ONLINE|OLAYAN|OFFICE|NOWRUZ|NORTON|NISSAY|NISSAN|NATURA|NAGOYA|MUTUAL|MUSEUM|MOSCOW|MORMON|MONASH|MOBILE|MATTEL|MARKET|MAKEUP|MAISON|MADRID|LUXURY|LONDON|LOCKER|LIVING|LEFRAK|LAWYER|LATINO|LANCIA|KOSHER|KINDLE|KINDER|KAUFEN|JUEGOS|JOBURG|JAGUAR|INTUIT|INSURE|IMAMAT|HUGHES|HOTELS|HOCKEY|HIPHOP|HERMES|HEALTH|GRATIS|GOOGLE|GLOBAL|GIVING|GEORGE|GARDEN|GALLUP|FUTBOL|FLICKR|FAMILY|EXPERT|EVENTS|ESTATE|ENERGY|EMERCK|DURBAN|DUPONT|DUNLOP|DOCTOR|DIRECT|DESIGN|DENTAL|DEGREE|DEALER|DATSUN|DATING|CRUISE|CREDIT|COUPON|CONDOS|COMSEC|COFFEE|CLINIC|CLAIMS|CIRCLE|CHURCH|CHROME|CHANEL|CENTER|CASINO|CAREER|CAMERA|BROKER|BOSTON|BOSTIK|BHARTI|BERLIN|BEAUTY|BAYERN|AUTHOR|ARAMCO|ANQUAN|AMAZON|ALSTOM|ALSACE|ALIPAY|AIRTEL|AIRBUS|AGENCY|AFRICA|ABBVIE|ABBOTT|ABARTH|YAHOO|XEROX|WORLD|WORKS|WEIBO|WEBER|WATCH|WALES|VOLVO|VODKA|VIDEO|VEGAS|UBANK|TUSHU|TUNES|TRUST|TRADE|TOURS|TOTAL|TORAY|TOOLS|TOKYO|TODAY|TMALL|TIROL|TIRES|TATAR|SWISS|SUCKS|STYLE|STUDY|STORE|STADA|SPORT|SPACE|SOLAR|SMILE|SMART|SLING|SKYPE|SHOES|SHELL|SHARP|SEVEN|SENER|SALON|RUGBY|RODEO|ROCKS|RICOH|REISE|REHAB|RADIO|QUEST|PROMO|PRIME|PRESS|PRAXI|POKER|PLACE|PIZZA|PHOTO|PHONE|PARTY|PARTS|PARIS|OSAKA|OMEGA|NOWTV|NOKIA|NINJA|NIKON|NEXUS|MOVIE|MONEY|MIAMI|MEDIA|MANGO|MACYS|LOTTO|LOTTE|LOCUS|LOANS|LIXIL|LIPSY|LINDE|LILLY|LEXUS|LEGAL|LEASE|LAMER|KYOTO|KOELN|JETZT|IVECO|IRISH|IKANO|HYATT|HOUSE|HORSE|HONDA|HOMES|GUIDE|GUCCI|GROUP|GRIPE|GREEN|GMAIL|GLOBO|GLASS|GLADE|GIVES|GIFTS|GAMES|GALLO|FORUM|FOREX|FINAL|FEDEX|FAITH|EPSON|EMAIL|EDEKA|EARTH|DUBAI|DRIVE|DELTA|DEALS|DANCE|DABUR|CYMRU|CROWN|CODES|COACH|CLOUD|CLICK|CITIC|CISCO|CHEAP|CHASE|CARDS|CANON|BUILD|BOSCH|BOATS|BLACK|BINGO|BIBLE|BEATS|BAIDU|AZURE|AUTOS|AUDIO|ARCHI|APPLE|AMICA|AMFAM|AETNA|ADULT|ACTOR|ZONE|ZERO|ZARA|YOGA|XBOX|WORK|WINE|WIKI|WIEN|WEIR|WANG|VOTO|VOTE|VIVO|VIVA|VISA|VANA|TUBE|TOYS|TOWN|TIPS|TIAA|TEVA|TECH|TEAM|TAXI|TALK|SURF|STAR|SPOT|SONY|SONG|SOHU|SNCF|SKIN|SITE|SINA|SILK|SHOW|SHOP|SHIA|SHAW|SEXY|SEEK|SEAT|SCOT|SAXO|SAVE|SARL|SALE|SAFE|RUHR|RSVP|ROOM|RMIT|RICH|REST|RENT|REIT|READ|RAID|QPON|PROF|PROD|POST|PORN|POHL|PLUS|PLAY|PINK|PING|PICS|PCCW|PARS|PAGE|OPEN|OLLO|NIKE|NICO|NEXT|NEWS|NAVY|NAME|MOTO|MODA|MOBI|MINT|MINI|MENU|MEME|MEET|MAIF|LUXE|LTDA|LOVE|LOFT|LOAN|LIVE|LINK|LIMO|LIKE|LIFE|LIDL|LGBT|LEGO|LAND|KRED|KPMG|KIWI|KDDI|JPRS|JOBS|JEEP|JAVA|ITAU|INFO|IMMO|IMDB|IEEE|ICBC|HSBC|HOST|HGTV|HERE|HELP|HDFC|HAUS|HAIR|GURU|GUGE|GOOG|GOLF|GOLD|GMBH|GIFT|GGEE|GENT|GBIZ|GAME|FUND|FREE|FORD|FOOD|FLIR|FISH|FIRE|FILM|FIDO|FIAT|FAST|FARM|FANS|FAIL|FAGE|ERNI|DVAG|DUCK|DOCS|DISH|DIET|DESI|DELL|DEAL|DCLK|DATE|DATA|CYOU|COOP|COOL|CLUB|CITY|CITI|CHAT|CERN|CBRE|CASH|CASE|CASA|CARS|CARE|CAMP|CALL|CAFE|BUZZ|BOOK|BOND|BOFA|BLUE|BLOG|BING|BIKE|BEST|BEER|BBVA|BANK|BAND|BABY|AUTO|AUDI|ASIA|ASDA|ARTE|ARPA|ARMY|ARAB|AMEX|ALLY|AKDN|AERO|ADAC|ABLE|AARP|ZIP|YUN|YOU|XYZ|XXX|XIN|WTF|WTC|WOW|WME|WIN|WED|VIP|VIN|VIG|VET|UPS|UOL|UNO|UBS|TVS|TUI|TRV|TOP|TJX|THD|TEL|TDK|TCI|TAX|TAB|STC|SRL|SPA|SOY|SKY|SKI|SFR|SEX|SEW|SES|SCB|SCA|SBS|SBI|SAS|SAP|RWE|RUN|RIP|RIO|RIL|REN|RED|QVC|PWC|PUB|PRU|PRO|PNC|PIN|PID|PHD|PET|PAY|OVH|OTT|ORG|OOO|ONL|ONG|ONE|OFF|OBI|NYC|NTT|NRW|NRA|NOW|NHK|NGO|NFL|NEW|NET|NEC|NBA|NAB|MTR|MTN|MSD|MOV|MOM|MOI|MOE|MMA|MLS|MLB|MIT|MIL|MEN|MED|MBA|MAP|MAN|LTD|LPL|LOL|LLP|LLC|LDS|LAW|LAT|KRD|KPN|KIM|KIA|KFH|JOY|JOT|JNJ|JMP|JLL|JIO|JCB|ITV|IST|INT|INK|ING|INC|IFM|ICU|ICE|IBM|HOW|HOT|HKT|HIV|HBO|GOV|GOT|GOP|GOO|GMX|GMO|GLE|GEA|GDN|GAY|GAP|GAL|FYI|FUN|FTR|FRL|FOX|FOO|FLY|FIT|FAN|EUS|ESQ|EDU|ECO|EAT|DVR|DTV|DOT|DOG|DNP|DIY|DHL|DEV|DDS|DAY|DAD|CSC|CRS|CPA|COM|CFD|CFA|CEO|CBS|CBN|CBA|CAT|CAR|CAM|CAL|CAB|BZH|BUY|BOX|BOT|BOO|BOM|BMW|BMS|BIZ|BIO|BID|BET|BCN|BCG|BBT|BBC|BAR|AXA|AWS|ART|APP|AOL|ANZ|AIG|AFL|AEG|ADS|ACO|ABC|ABB|AAA|ZW|ZM|ZA|YT|YE|WS|WF|VU|VN|VI|VG|VE|VC|VA|UZ|UY|US|UK|UG|UA|TZ|TW|TV|TT|TR|TO|TN|TM|TL|TK|TJ|TH|TG|TF|TD|TC|SZ|SY|SX|SV|SU|ST|SS|SR|SO|SN|SM|SL|SK|SJ|SI|SH|SG|SE|SD|SC|SB|SA|RW|RU|RS|RO|RE|QA|PY|PW|PT|PS|PR|PN|PM|PL|PK|PH|PG|PF|PE|PA|OM|NZ|NU|NR|NP|NO|NL|NI|NG|NF|NE|NC|NA|MZ|MY|MX|MW|MV|MU|MT|MS|MR|MQ|MP|MO|MN|MM|ML|MK|MH|MG|ME|MD|MC|MA|LY|LV|LU|LT|LS|LR|LK|LI|LC|LB|LA|KZ|KY|KW|KR|KP|KN|KM|KI|KH|KG|KE|JP|JO|JM|JE|IT|IS|IR|IQ|IO|IN|IM|IL|IE|ID|HU|HT|HR|HN|HM|HK|GY|GW|GU|GT|GS|GR|GQ|GP|GN|GM|GL|GI|GH|GG|GF|GE|GD|GB|GA|FR|FO|FM|FK|FJ|FI|EU|ET|ES|ER|EG|EE|EC|DZ|DO|DM|DK|DJ|DE|CZ|CY|CX|CW|CV|CU|CR|CO|CN|CM|CL|CK|CI|CH|CG|CF|CD|CC|CA|BZ|BY|BW|BV|BT|BS|BR|BO|BN|BM|BJ|BI|BH|BG|BF|BE|BD|BB|BA|AZ|AX|AW|AU|AT|AS|AR|AQ|AO|AM|AL|AI|AG|AF|AE|AD|AC|SJC|RNO|LAX)(?:\\:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\\b|(?=_))';
-declare const URL_PATH_REGEX: '(?:(?:[.,=(+$!*]|&(?:amp|quot|#x27);)?\\/(?:[-\\w$@.+!*:(),=%~]|&(?:amp|quot|#x27);)*(?:[-\\w~@:%)]|&(?:amp|quot|#x27);)|\\/)*';
-declare const URL_PARAM_REGEX: '(?:\\?(?:[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]|&(?:amp|quot|#x27);)*)?';
-declare const URL_FRAGMENT_REGEX: '(?:#(?:[-\\w$@.+!*()[\\],=%;\\/:~]|&(?:amp|quot|#x27);)*)?';
-declare const URL_REGEX: '((((ht|f)tps?:\\/\\/)?((?:www\\.)?[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)+(?:XN--VERMGENSBERATUNG-PWB|XN--VERMGENSBERATER-CTB|XN--CLCHC0EA0B2G2A9GCD|XN--W4R85EL8FHU5DNRA|TRAVELERSINSURANCE|NORTHWESTERNMUTUAL|XN--XKC2DL3A5EE0H|XN--MGBERP4A5D4AR|XN--MGBAI9AZGQP6J|XN--MGBAH1A3HJKRD|XN--BCK1B9A5DRE4C|XN--5SU34J936BGSG|XN--3OQ18VL8PN36A|XN--XKC2AL3HYE2A|XN--MGBCPQ6GPA1A|XN--MGBA7C0BBN0A|XN--FZYS8D69UVGM|XN--NQV7FS00EMA|XN--MGBC0A9AZCG|XN--MGBAAKC7DVF|XN--MGBA3A4F16A|XN--LGBBAT1AD8J|XN--KCRX77D1X4A|XN--I1B6B1A6A2E|SANDVIKCOROMANT|KERRYPROPERTIES|AMERICANEXPRESS|XN--RVC1E0AM3E|XN--MGBX4CD0AB|XN--MGBI4ECEXP|XN--MGBCA7DZDO|XN--MGBBH1A71E|XN--MGBAYH7GPA|XN--MGBAAM7A8H|XN--MGBA3A3EJT|XN--JLQ61U9W7B|XN--JLQ480N2RG|XN--H2BREG3EVE|XN--FIQ228C5HS|XN--B4W605FERD|XN--80AQECDR1A|XN--6QQ986B3XL|XN--54B7FTA0CC|WEATHERCHANNEL|KERRYLOGISTICS|COOKINGCHANNEL|CANCERRESEARCH|BANANAREPUBLIC|AMERICANFAMILY|AFAMILYCOMPANY|XN--YGBI2AMMX|XN--YFRO4I67O|XN--TIQ49XQYJ|XN--H2BRJ9C8C|XN--FZC2C9E2C|XN--FPCRJ9C3D|XN--ECKVDTC9D|XN--CCKWCXETD|WOLTERSKLUWER|TRAVELCHANNEL|SPREADBETTING|LIFEINSURANCE|INTERNATIONAL|XN--QCKA1PMC|XN--OGBPF8FL|XN--NGBE9E0A|XN--NGBC5AZD|XN--MK1BU44C|XN--MGBT3DHD|XN--MGBPL2FH|XN--MGBGU82A|XN--MGBAB2BD|XN--MGB9AWBF|XN--GCKR3F0F|XN--8Y0A063A|XN--80ASEHDB|XN--80ADXHKS|XN--4DBRK0CE|XN--45BR5CYL|XN--3E0B707E|VERSICHERUNG|SCHOLARSHIPS|LPLFINANCIAL|CONSTRUCTION|XN--ZFR164B|XN--XHQ521B|XN--W4RS40L|XN--VUQ861B|XN--T60B56A|XN--SES554G|XN--S9BRJ9C|XN--ROVU88B|XN--RHQV96G|XN--Q9JYB4C|XN--PGBS0DH|XN--OTU796D|XN--NYQY26A|XN--MIX891F|XN--MGBTX2B|XN--MGBBH1A|XN--KPRY57D|XN--KPRW13D|XN--JVR189M|XN--J6W193G|XN--IMR513N|XN--HXT814E|XN--H2BRJ9C|XN--GK3AT1E|XN--GECRJ9C|XN--G2XX48C|XN--FLW351E|XN--FJQ720A|XN--FCT429K|XN--EFVY88H|XN--D1ACJ3B|XN--CZR694B|XN--CCK2B3B|XN--9KRT00A|XN--80AO21A|XN--6FRZ82G|XN--55QW42G|XN--45BRJ9C|XN--42C2D9A|XN--3HCRJ9C|XN--3DS443G|XN--3BST00M|XN--2SCRJ9C|XN--1QQW23A|XN--1CK2E1B|XN--11B4C3D|WILLIAMHILL|REDUMBRELLA|PROGRESSIVE|PRODUCTIONS|PLAYSTATION|PHOTOGRAPHY|OLAYANGROUP|MOTORCYCLES|LAMBORGHINI|KERRYHOTELS|INVESTMENTS|FOODNETWORK|ENTERPRISES|ENGINEERING|CREDITUNION|CONTRACTORS|CALVINKLEIN|BRIDGESTONE|BLOCKBUSTER|BLACKFRIDAY|BARCLAYCARD|ACCOUNTANTS|XN--Y9A3AQ|XN--WGBL6A|XN--WGBH1C|XN--UNUP4Y|XN--Q7CE6A|XN--PSSY2U|XN--O3CW4H|XN--MXTQ1M|XN--KPUT3I|XN--IO0A7I|XN--FIQZ9S|XN--FIQS8S|XN--FIQ64B|XN--CZRU2D|XN--CZRS0T|XN--CG4BKI|XN--C2BR7G|XN--9ET52U|XN--9DBQ2A|XN--90A3AC|XN--80ASWG|XN--5TZM5G|XN--55QX5D|XN--4GBRIM|XN--45Q11C|XN--3PXU8K|XN--30RR7Y|VOLKSWAGEN|VLAANDEREN|UNIVERSITY|TECHNOLOGY|TATAMOTORS|SWIFTCOVER|SCHAEFFLER|RESTAURANT|REPUBLICAN|REALESTATE|PRUDENTIAL|PROTECTION|PROPERTIES|ONYOURSIDE|NEXTDIRECT|NATIONWIDE|MITSUBISHI|MANAGEMENT|INDUSTRIES|IMMOBILIEN|HEALTHCARE|FOUNDATION|EXTRASPACE|EUROVISION|CUISINELLA|CREDITCARD|CONSULTING|CAPITALONE|BOEHRINGER|BNPPARIBAS|BASKETBALL|ASSOCIATES|APARTMENTS|ACCOUNTANT|YODOBASHI|XN--VHQUV|XN--TCKWE|XN--QXA6A|XN--P1ACF|XN--NQV7F|XN--NGBRX|XN--L1ACC|XN--J1AMH|XN--J1AEF|XN--FHBEI|XN--E1A4C|XN--D1ALF|XN--C1AVG|XN--90AIS|VACATIONS|TRAVELERS|STOCKHOLM|STATEFARM|STATEBANK|SOLUTIONS|SHANGRILA|SCJOHNSON|RICHARDLI|PRAMERICA|PASSAGENS|PANASONIC|MICROSOFT|MELBOURNE|MARSHALLS|MARKETING|LIFESTYLE|LANDROVER|LANCASTER|KUOKGROUP|INSURANCE|INSTITUTE|HOMESENSE|HOMEGOODS|HOMEDEPOT|HISAMITSU|GOLDPOINT|FURNITURE|FUJIXEROX|FRONTDOOR|FRESENIUS|FIRESTONE|FINANCIAL|FAIRWINDS|EQUIPMENT|EDUCATION|DIRECTORY|COMMUNITY|CHRISTMAS|BLOOMBERG|BARCELONA|AQUARELLE|ANALYTICS|AMSTERDAM|ALLFINANZ|ALFAROMEO|ACCENTURE|YOKOHAMA|XN--QXAM|XN--P1AI|XN--NODE|XN--90AE|WOODSIDE|VERISIGN|VENTURES|VANGUARD|TRAINING|SUPPLIES|STCGROUP|SOFTWARE|SOFTBANK|SHOWTIME|SHOPPING|SERVICES|SECURITY|SAMSCLUB|SAARLAND|RELIANCE|REDSTONE|PROPERTY|PLUMBING|PICTURES|PHARMACY|PARTNERS|OBSERVER|MORTGAGE|MERCKMSD|MEMORIAL|MCKINSEY|MASERATI|MARRIOTT|LUNDBECK|LIGHTING|JPMORGAN|ISTANBUL|IPIRANGA|INFINITI|HOSPITAL|HOLDINGS|HELSINKI|HDFCBANK|GUARDIAN|GRAPHICS|GRAINGER|GOODYEAR|FRONTIER|FOOTBALL|FIRMDALE|FIDELITY|FEEDBACK|EXCHANGE|ETISALAT|ERICSSON|ENGINEER|DOWNLOAD|DISCOVER|DISCOUNT|DIAMONDS|DEMOCRAT|DELOITTE|DELIVERY|COMPUTER|COMMBANK|CLOTHING|CLINIQUE|CLEANING|CITYEATS|CIPRIANI|CATHOLIC|CATERING|CAPETOWN|BUSINESS|BUILDERS|BUDAPEST|BRUSSELS|BROADWAY|BRADESCO|BOUTIQUE|BASEBALL|BARGAINS|BAREFOOT|BARCLAYS|ATTORNEY|ALLSTATE|AIRFORCE|ABUDHABI|ZUERICH|YOUTUBE|YAMAXUN|XFINITY|WINNERS|WINDOWS|WHOSWHO|WEDDING|WEBSITE|WEATHER|WATCHES|WANGGOU|WALMART|TRADING|TOSHIBA|TIFFANY|TICKETS|THEATRE|THEATER|TEMASEK|SYSTEMS|SURGERY|SUPPORT|STORAGE|STAPLES|SINGLES|SHIKSHA|SCIENCE|SCHWARZ|SCHMIDT|SANDVIK|SAMSUNG|REXROTH|REVIEWS|RENTALS|RECIPES|REALTOR|POLITIE|PIONEER|PHILIPS|ORIGINS|ORGANIC|OLDNAVY|OKINAWA|NEUSTAR|NETWORK|NETFLIX|NETBANK|MONSTER|MARKETS|LINCOLN|LIMITED|LECLERC|LATROBE|LASALLE|LANXESS|LACAIXA|KOMATSU|KITCHEN|JUNIPER|JEWELRY|ISMAILI|HYUNDAI|HOTMAIL|HOTELES|HOSTING|HOLIDAY|HITACHI|HANGOUT|HAMBURG|GUITARS|GROCERY|GODADDY|GENTING|GALLERY|FUJITSU|FROGANS|FORSALE|FLOWERS|FLORIST|FLIGHTS|FITNESS|FISHING|FINANCE|FERRERO|FERRARI|FASHION|FARMERS|EXPRESS|EXPOSED|DOMAINS|DIGITAL|DENTIST|CRUISES|CRICKET|COURSES|COUPONS|COUNTRY|CORSICA|COOKING|CONTACT|COMPARE|COMPANY|COMCAST|COLOGNE|COLLEGE|CLUBMED|CITADEL|CHINTAI|CHARITY|CHANNEL|CAREERS|CARAVAN|CAPITAL|BUGATTI|BROTHER|BOOKING|BESTBUY|BENTLEY|BAUHAUS|BANAMEX|AVIANCA|AUSPOST|AUDIBLE|AUCTION|ATHLETA|ANDROID|ALIBABA|AGAKHAN|ACADEMY|ABOGADO|ZAPPOS|YANDEX|YACHTS|XIHUAN|WEBCAM|WALTER|VUELOS|VOYAGE|VOTING|VISION|VIRGIN|VILLAS|VIKING|VIAJES|UNICOM|TRAVEL|TOYOTA|TKMAXX|TJMAXX|TIENDA|TENNIS|TATTOO|TARGET|TAOBAO|TAIPEI|SYDNEY|SWATCH|SUZUKI|SUPPLY|STUDIO|STREAM|SOCIAL|SOCCER|SHOUJI|SELECT|SECURE|SEARCH|SCHULE|SCHOOL|SANOFI|SAKURA|SAFETY|RYUKYU|ROGERS|ROCHER|REVIEW|REPORT|REPAIR|REISEN|REALTY|RACING|QUEBEC|PICTET|PHYSIO|PHOTOS|PFIZER|OTSUKA|ORANGE|ORACLE|ONLINE|OLAYAN|OFFICE|NOWRUZ|NORTON|NISSAY|NISSAN|NATURA|NAGOYA|MUTUAL|MUSEUM|MOSCOW|MORMON|MONASH|MOBILE|MATTEL|MARKET|MAKEUP|MAISON|MADRID|LUXURY|LONDON|LOCKER|LIVING|LEFRAK|LAWYER|LATINO|LANCIA|KOSHER|KINDLE|KINDER|KAUFEN|JUEGOS|JOBURG|JAGUAR|INTUIT|INSURE|IMAMAT|HUGHES|HOTELS|HOCKEY|HIPHOP|HERMES|HEALTH|GRATIS|GOOGLE|GLOBAL|GIVING|GEORGE|GARDEN|GALLUP|FUTBOL|FLICKR|FAMILY|EXPERT|EVENTS|ESTATE|ENERGY|EMERCK|DURBAN|DUPONT|DUNLOP|DOCTOR|DIRECT|DESIGN|DENTAL|DEGREE|DEALER|DATSUN|DATING|CRUISE|CREDIT|COUPON|CONDOS|COMSEC|COFFEE|CLINIC|CLAIMS|CIRCLE|CHURCH|CHROME|CHANEL|CENTER|CASINO|CAREER|CAMERA|BROKER|BOSTON|BOSTIK|BHARTI|BERLIN|BEAUTY|BAYERN|AUTHOR|ARAMCO|ANQUAN|AMAZON|ALSTOM|ALSACE|ALIPAY|AIRTEL|AIRBUS|AGENCY|AFRICA|ABBVIE|ABBOTT|ABARTH|YAHOO|XEROX|WORLD|WORKS|WEIBO|WEBER|WATCH|WALES|VOLVO|VODKA|VIDEO|VEGAS|UBANK|TUSHU|TUNES|TRUST|TRADE|TOURS|TOTAL|TORAY|TOOLS|TOKYO|TODAY|TMALL|TIROL|TIRES|TATAR|SWISS|SUCKS|STYLE|STUDY|STORE|STADA|SPORT|SPACE|SOLAR|SMILE|SMART|SLING|SKYPE|SHOES|SHELL|SHARP|SEVEN|SENER|SALON|RUGBY|RODEO|ROCKS|RICOH|REISE|REHAB|RADIO|QUEST|PROMO|PRIME|PRESS|PRAXI|POKER|PLACE|PIZZA|PHOTO|PHONE|PARTY|PARTS|PARIS|OSAKA|OMEGA|NOWTV|NOKIA|NINJA|NIKON|NEXUS|MOVIE|MONEY|MIAMI|MEDIA|MANGO|MACYS|LOTTO|LOTTE|LOCUS|LOANS|LIXIL|LIPSY|LINDE|LILLY|LEXUS|LEGAL|LEASE|LAMER|KYOTO|KOELN|JETZT|IVECO|IRISH|IKANO|HYATT|HOUSE|HORSE|HONDA|HOMES|GUIDE|GUCCI|GROUP|GRIPE|GREEN|GMAIL|GLOBO|GLASS|GLADE|GIVES|GIFTS|GAMES|GALLO|FORUM|FOREX|FINAL|FEDEX|FAITH|EPSON|EMAIL|EDEKA|EARTH|DUBAI|DRIVE|DELTA|DEALS|DANCE|DABUR|CYMRU|CROWN|CODES|COACH|CLOUD|CLICK|CITIC|CISCO|CHEAP|CHASE|CARDS|CANON|BUILD|BOSCH|BOATS|BLACK|BINGO|BIBLE|BEATS|BAIDU|AZURE|AUTOS|AUDIO|ARCHI|APPLE|AMICA|AMFAM|AETNA|ADULT|ACTOR|ZONE|ZERO|ZARA|YOGA|XBOX|WORK|WINE|WIKI|WIEN|WEIR|WANG|VOTO|VOTE|VIVO|VIVA|VISA|VANA|TUBE|TOYS|TOWN|TIPS|TIAA|TEVA|TECH|TEAM|TAXI|TALK|SURF|STAR|SPOT|SONY|SONG|SOHU|SNCF|SKIN|SITE|SINA|SILK|SHOW|SHOP|SHIA|SHAW|SEXY|SEEK|SEAT|SCOT|SAXO|SAVE|SARL|SALE|SAFE|RUHR|RSVP|ROOM|RMIT|RICH|REST|RENT|REIT|READ|RAID|QPON|PROF|PROD|POST|PORN|POHL|PLUS|PLAY|PINK|PING|PICS|PCCW|PARS|PAGE|OPEN|OLLO|NIKE|NICO|NEXT|NEWS|NAVY|NAME|MOTO|MODA|MOBI|MINT|MINI|MENU|MEME|MEET|MAIF|LUXE|LTDA|LOVE|LOFT|LOAN|LIVE|LINK|LIMO|LIKE|LIFE|LIDL|LGBT|LEGO|LAND|KRED|KPMG|KIWI|KDDI|JPRS|JOBS|JEEP|JAVA|ITAU|INFO|IMMO|IMDB|IEEE|ICBC|HSBC|HOST|HGTV|HERE|HELP|HDFC|HAUS|HAIR|GURU|GUGE|GOOG|GOLF|GOLD|GMBH|GIFT|GGEE|GENT|GBIZ|GAME|FUND|FREE|FORD|FOOD|FLIR|FISH|FIRE|FILM|FIDO|FIAT|FAST|FARM|FANS|FAIL|FAGE|ERNI|DVAG|DUCK|DOCS|DISH|DIET|DESI|DELL|DEAL|DCLK|DATE|DATA|CYOU|COOP|COOL|CLUB|CITY|CITI|CHAT|CERN|CBRE|CASH|CASE|CASA|CARS|CARE|CAMP|CALL|CAFE|BUZZ|BOOK|BOND|BOFA|BLUE|BLOG|BING|BIKE|BEST|BEER|BBVA|BANK|BAND|BABY|AUTO|AUDI|ASIA|ASDA|ARTE|ARPA|ARMY|ARAB|AMEX|ALLY|AKDN|AERO|ADAC|ABLE|AARP|ZIP|YUN|YOU|XYZ|XXX|XIN|WTF|WTC|WOW|WME|WIN|WED|VIP|VIN|VIG|VET|UPS|UOL|UNO|UBS|TVS|TUI|TRV|TOP|TJX|THD|TEL|TDK|TCI|TAX|TAB|STC|SRL|SPA|SOY|SKY|SKI|SFR|SEX|SEW|SES|SCB|SCA|SBS|SBI|SAS|SAP|RWE|RUN|RIP|RIO|RIL|REN|RED|QVC|PWC|PUB|PRU|PRO|PNC|PIN|PID|PHD|PET|PAY|OVH|OTT|ORG|OOO|ONL|ONG|ONE|OFF|OBI|NYC|NTT|NRW|NRA|NOW|NHK|NGO|NFL|NEW|NET|NEC|NBA|NAB|MTR|MTN|MSD|MOV|MOM|MOI|MOE|MMA|MLS|MLB|MIT|MIL|MEN|MED|MBA|MAP|MAN|LTD|LPL|LOL|LLP|LLC|LDS|LAW|LAT|KRD|KPN|KIM|KIA|KFH|JOY|JOT|JNJ|JMP|JLL|JIO|JCB|ITV|IST|INT|INK|ING|INC|IFM|ICU|ICE|IBM|HOW|HOT|HKT|HIV|HBO|GOV|GOT|GOP|GOO|GMX|GMO|GLE|GEA|GDN|GAY|GAP|GAL|FYI|FUN|FTR|FRL|FOX|FOO|FLY|FIT|FAN|EUS|ESQ|EDU|ECO|EAT|DVR|DTV|DOT|DOG|DNP|DIY|DHL|DEV|DDS|DAY|DAD|CSC|CRS|CPA|COM|CFD|CFA|CEO|CBS|CBN|CBA|CAT|CAR|CAM|CAL|CAB|BZH|BUY|BOX|BOT|BOO|BOM|BMW|BMS|BIZ|BIO|BID|BET|BCN|BCG|BBT|BBC|BAR|AXA|AWS|ART|APP|AOL|ANZ|AIG|AFL|AEG|ADS|ACO|ABC|ABB|AAA|ZW|ZM|ZA|YT|YE|WS|WF|VU|VN|VI|VG|VE|VC|VA|UZ|UY|US|UK|UG|UA|TZ|TW|TV|TT|TR|TO|TN|TM|TL|TK|TJ|TH|TG|TF|TD|TC|SZ|SY|SX|SV|SU|ST|SS|SR|SO|SN|SM|SL|SK|SJ|SI|SH|SG|SE|SD|SC|SB|SA|RW|RU|RS|RO|RE|QA|PY|PW|PT|PS|PR|PN|PM|PL|PK|PH|PG|PF|PE|PA|OM|NZ|NU|NR|NP|NO|NL|NI|NG|NF|NE|NC|NA|MZ|MY|MX|MW|MV|MU|MT|MS|MR|MQ|MP|MO|MN|MM|ML|MK|MH|MG|ME|MD|MC|MA|LY|LV|LU|LT|LS|LR|LK|LI|LC|LB|LA|KZ|KY|KW|KR|KP|KN|KM|KI|KH|KG|KE|JP|JO|JM|JE|IT|IS|IR|IQ|IO|IN|IM|IL|IE|ID|HU|HT|HR|HN|HM|HK|GY|GW|GU|GT|GS|GR|GQ|GP|GN|GM|GL|GI|GH|GG|GF|GE|GD|GB|GA|FR|FO|FM|FK|FJ|FI|EU|ET|ES|ER|EG|EE|EC|DZ|DO|DM|DK|DJ|DE|CZ|CY|CX|CW|CV|CU|CR|CO|CN|CM|CL|CK|CI|CH|CG|CF|CD|CC|CA|BZ|BY|BW|BV|BT|BS|BR|BO|BN|BM|BJ|BI|BH|BG|BF|BE|BD|BB|BA|AZ|AX|AW|AU|AT|AS|AR|AQ|AO|AM|AL|AI|AG|AF|AE|AD|AC|SJC|RNO|LAX)(?:\\:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\\b|(?=_)))(?:(?:[.,=(+$!*]|&(?:amp|quot|#x27);)?\\/(?:[-\\w$@.+!*:(),=%~]|&(?:amp|quot|#x27);)*(?:[-\\w~@:%)]|&(?:amp|quot|#x27);)|\\/)*(?:(?:\\?(?:[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]|&(?:amp|quot|#x27);)*)?|(?:#(?:[-\\w$@.+!*()[\\],=%;\\/:~]|&(?:amp|quot|#x27);)*)?)*)';
+declare const URL_PORT_REGEX: '([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])';
+declare const URL_HOSTNAME_LABEL_REGEX: '[a-z0-9](?=(?[-a-z0-9]{0,61}[a-z0-9])?)\\k';
+declare const URL_WEBSITE_REGEX: '((ht|f)tps?:\\/\\/)?(?[-a-z0-9]{0,61}[a-z0-9])?)\\k\\.)+(?:XN--VERMGENSBERATUNG-PWB|XN--VERMGENSBERATER-CTB|XN--CLCHC0EA0B2G2A9GCD|XN--W4R85EL8FHU5DNRA|TRAVELERSINSURANCE|NORTHWESTERNMUTUAL|XN--XKC2DL3A5EE0H|XN--MGBERP4A5D4AR|XN--MGBAI9AZGQP6J|XN--MGBAH1A3HJKRD|XN--BCK1B9A5DRE4C|XN--5SU34J936BGSG|XN--3OQ18VL8PN36A|XN--XKC2AL3HYE2A|XN--MGBCPQ6GPA1A|XN--MGBA7C0BBN0A|XN--FZYS8D69UVGM|XN--NQV7FS00EMA|XN--MGBC0A9AZCG|XN--MGBAAKC7DVF|XN--MGBA3A4F16A|XN--LGBBAT1AD8J|XN--KCRX77D1X4A|XN--I1B6B1A6A2E|SANDVIKCOROMANT|KERRYPROPERTIES|AMERICANEXPRESS|XN--RVC1E0AM3E|XN--MGBX4CD0AB|XN--MGBI4ECEXP|XN--MGBCA7DZDO|XN--MGBBH1A71E|XN--MGBAYH7GPA|XN--MGBAAM7A8H|XN--MGBA3A3EJT|XN--JLQ61U9W7B|XN--JLQ480N2RG|XN--H2BREG3EVE|XN--FIQ228C5HS|XN--B4W605FERD|XN--80AQECDR1A|XN--6QQ986B3XL|XN--54B7FTA0CC|WEATHERCHANNEL|KERRYLOGISTICS|COOKINGCHANNEL|CANCERRESEARCH|BANANAREPUBLIC|AMERICANFAMILY|AFAMILYCOMPANY|XN--YGBI2AMMX|XN--YFRO4I67O|XN--TIQ49XQYJ|XN--H2BRJ9C8C|XN--FZC2C9E2C|XN--FPCRJ9C3D|XN--ECKVDTC9D|XN--CCKWCXETD|WOLTERSKLUWER|TRAVELCHANNEL|SPREADBETTING|LIFEINSURANCE|INTERNATIONAL|XN--QCKA1PMC|XN--OGBPF8FL|XN--NGBE9E0A|XN--NGBC5AZD|XN--MK1BU44C|XN--MGBT3DHD|XN--MGBPL2FH|XN--MGBGU82A|XN--MGBAB2BD|XN--MGB9AWBF|XN--GCKR3F0F|XN--8Y0A063A|XN--80ASEHDB|XN--80ADXHKS|XN--4DBRK0CE|XN--45BR5CYL|XN--3E0B707E|VERSICHERUNG|SCHOLARSHIPS|LPLFINANCIAL|CONSTRUCTION|XN--ZFR164B|XN--XHQ521B|XN--W4RS40L|XN--VUQ861B|XN--T60B56A|XN--SES554G|XN--S9BRJ9C|XN--ROVU88B|XN--RHQV96G|XN--Q9JYB4C|XN--PGBS0DH|XN--OTU796D|XN--NYQY26A|XN--MIX891F|XN--MGBTX2B|XN--MGBBH1A|XN--KPRY57D|XN--KPRW13D|XN--JVR189M|XN--J6W193G|XN--IMR513N|XN--HXT814E|XN--H2BRJ9C|XN--GK3AT1E|XN--GECRJ9C|XN--G2XX48C|XN--FLW351E|XN--FJQ720A|XN--FCT429K|XN--EFVY88H|XN--D1ACJ3B|XN--CZR694B|XN--CCK2B3B|XN--9KRT00A|XN--80AO21A|XN--6FRZ82G|XN--55QW42G|XN--45BRJ9C|XN--42C2D9A|XN--3HCRJ9C|XN--3DS443G|XN--3BST00M|XN--2SCRJ9C|XN--1QQW23A|XN--1CK2E1B|XN--11B4C3D|WILLIAMHILL|REDUMBRELLA|PROGRESSIVE|PRODUCTIONS|PLAYSTATION|PHOTOGRAPHY|OLAYANGROUP|MOTORCYCLES|LAMBORGHINI|KERRYHOTELS|INVESTMENTS|FOODNETWORK|ENTERPRISES|ENGINEERING|CREDITUNION|CONTRACTORS|CALVINKLEIN|BRIDGESTONE|BLOCKBUSTER|BLACKFRIDAY|BARCLAYCARD|ACCOUNTANTS|XN--Y9A3AQ|XN--WGBL6A|XN--WGBH1C|XN--UNUP4Y|XN--Q7CE6A|XN--PSSY2U|XN--O3CW4H|XN--MXTQ1M|XN--KPUT3I|XN--IO0A7I|XN--FIQZ9S|XN--FIQS8S|XN--FIQ64B|XN--CZRU2D|XN--CZRS0T|XN--CG4BKI|XN--C2BR7G|XN--9ET52U|XN--9DBQ2A|XN--90A3AC|XN--80ASWG|XN--5TZM5G|XN--55QX5D|XN--4GBRIM|XN--45Q11C|XN--3PXU8K|XN--30RR7Y|VOLKSWAGEN|VLAANDEREN|UNIVERSITY|TECHNOLOGY|TATAMOTORS|SWIFTCOVER|SCHAEFFLER|RESTAURANT|REPUBLICAN|REALESTATE|PRUDENTIAL|PROTECTION|PROPERTIES|ONYOURSIDE|NEXTDIRECT|NATIONWIDE|MITSUBISHI|MANAGEMENT|INDUSTRIES|IMMOBILIEN|HEALTHCARE|FOUNDATION|EXTRASPACE|EUROVISION|CUISINELLA|CREDITCARD|CONSULTING|CAPITALONE|BOEHRINGER|BNPPARIBAS|BASKETBALL|ASSOCIATES|APARTMENTS|ACCOUNTANT|YODOBASHI|XN--VHQUV|XN--TCKWE|XN--QXA6A|XN--P1ACF|XN--NQV7F|XN--NGBRX|XN--L1ACC|XN--J1AMH|XN--J1AEF|XN--FHBEI|XN--E1A4C|XN--D1ALF|XN--C1AVG|XN--90AIS|VACATIONS|TRAVELERS|STOCKHOLM|STATEFARM|STATEBANK|SOLUTIONS|SHANGRILA|SCJOHNSON|RICHARDLI|PRAMERICA|PASSAGENS|PANASONIC|MICROSOFT|MELBOURNE|MARSHALLS|MARKETING|LIFESTYLE|LANDROVER|LANCASTER|KUOKGROUP|INSURANCE|INSTITUTE|HOMESENSE|HOMEGOODS|HOMEDEPOT|HISAMITSU|GOLDPOINT|FURNITURE|FUJIXEROX|FRONTDOOR|FRESENIUS|FIRESTONE|FINANCIAL|FAIRWINDS|EQUIPMENT|EDUCATION|DIRECTORY|COMMUNITY|CHRISTMAS|BLOOMBERG|BARCELONA|AQUARELLE|ANALYTICS|AMSTERDAM|ALLFINANZ|ALFAROMEO|ACCENTURE|YOKOHAMA|XN--QXAM|XN--P1AI|XN--NODE|XN--90AE|WOODSIDE|VERISIGN|VENTURES|VANGUARD|TRAINING|SUPPLIES|STCGROUP|SOFTWARE|SOFTBANK|SHOWTIME|SHOPPING|SERVICES|SECURITY|SAMSCLUB|SAARLAND|RELIANCE|REDSTONE|PROPERTY|PLUMBING|PICTURES|PHARMACY|PARTNERS|OBSERVER|MORTGAGE|MERCKMSD|MEMORIAL|MCKINSEY|MASERATI|MARRIOTT|LUNDBECK|LIGHTING|JPMORGAN|ISTANBUL|IPIRANGA|INFINITI|HOSPITAL|HOLDINGS|HELSINKI|HDFCBANK|GUARDIAN|GRAPHICS|GRAINGER|GOODYEAR|FRONTIER|FOOTBALL|FIRMDALE|FIDELITY|FEEDBACK|EXCHANGE|ETISALAT|ERICSSON|ENGINEER|DOWNLOAD|DISCOVER|DISCOUNT|DIAMONDS|DEMOCRAT|DELOITTE|DELIVERY|COMPUTER|COMMBANK|CLOTHING|CLINIQUE|CLEANING|CITYEATS|CIPRIANI|CATHOLIC|CATERING|CAPETOWN|BUSINESS|BUILDERS|BUDAPEST|BRUSSELS|BROADWAY|BRADESCO|BOUTIQUE|BASEBALL|BARGAINS|BAREFOOT|BARCLAYS|ATTORNEY|ALLSTATE|AIRFORCE|ABUDHABI|ZUERICH|YOUTUBE|YAMAXUN|XFINITY|WINNERS|WINDOWS|WHOSWHO|WEDDING|WEBSITE|WEATHER|WATCHES|WANGGOU|WALMART|TRADING|TOSHIBA|TIFFANY|TICKETS|THEATRE|THEATER|TEMASEK|SYSTEMS|SURGERY|SUPPORT|STORAGE|STAPLES|SINGLES|SHIKSHA|SCIENCE|SCHWARZ|SCHMIDT|SANDVIK|SAMSUNG|REXROTH|REVIEWS|RENTALS|RECIPES|REALTOR|POLITIE|PIONEER|PHILIPS|ORIGINS|ORGANIC|OLDNAVY|OKINAWA|NEUSTAR|NETWORK|NETFLIX|NETBANK|MONSTER|MARKETS|LINCOLN|LIMITED|LECLERC|LATROBE|LASALLE|LANXESS|LACAIXA|KOMATSU|KITCHEN|JUNIPER|JEWELRY|ISMAILI|HYUNDAI|HOTMAIL|HOTELES|HOSTING|HOLIDAY|HITACHI|HANGOUT|HAMBURG|GUITARS|GROCERY|GODADDY|GENTING|GALLERY|FUJITSU|FROGANS|FORSALE|FLOWERS|FLORIST|FLIGHTS|FITNESS|FISHING|FINANCE|FERRERO|FERRARI|FASHION|FARMERS|EXPRESS|EXPOSED|DOMAINS|DIGITAL|DENTIST|CRUISES|CRICKET|COURSES|COUPONS|COUNTRY|CORSICA|COOKING|CONTACT|COMPARE|COMPANY|COMCAST|COLOGNE|COLLEGE|CLUBMED|CITADEL|CHINTAI|CHARITY|CHANNEL|CAREERS|CARAVAN|CAPITAL|BUGATTI|BROTHER|BOOKING|BESTBUY|BENTLEY|BAUHAUS|BANAMEX|AVIANCA|AUSPOST|AUDIBLE|AUCTION|ATHLETA|ANDROID|ALIBABA|AGAKHAN|ACADEMY|ABOGADO|ZAPPOS|YANDEX|YACHTS|XIHUAN|WEBCAM|WALTER|VUELOS|VOYAGE|VOTING|VISION|VIRGIN|VILLAS|VIKING|VIAJES|UNICOM|TRAVEL|TOYOTA|TKMAXX|TJMAXX|TIENDA|TENNIS|TATTOO|TARGET|TAOBAO|TAIPEI|SYDNEY|SWATCH|SUZUKI|SUPPLY|STUDIO|STREAM|SOCIAL|SOCCER|SHOUJI|SELECT|SECURE|SEARCH|SCHULE|SCHOOL|SANOFI|SAKURA|SAFETY|RYUKYU|ROGERS|ROCHER|REVIEW|REPORT|REPAIR|REISEN|REALTY|RACING|QUEBEC|PICTET|PHYSIO|PHOTOS|PFIZER|OTSUKA|ORANGE|ORACLE|ONLINE|OLAYAN|OFFICE|NOWRUZ|NORTON|NISSAY|NISSAN|NATURA|NAGOYA|MUTUAL|MUSEUM|MOSCOW|MORMON|MONASH|MOBILE|MATTEL|MARKET|MAKEUP|MAISON|MADRID|LUXURY|LONDON|LOCKER|LIVING|LEFRAK|LAWYER|LATINO|LANCIA|KOSHER|KINDLE|KINDER|KAUFEN|JUEGOS|JOBURG|JAGUAR|INTUIT|INSURE|IMAMAT|HUGHES|HOTELS|HOCKEY|HIPHOP|HERMES|HEALTH|GRATIS|GOOGLE|GLOBAL|GIVING|GEORGE|GARDEN|GALLUP|FUTBOL|FLICKR|FAMILY|EXPERT|EVENTS|ESTATE|ENERGY|EMERCK|DURBAN|DUPONT|DUNLOP|DOCTOR|DIRECT|DESIGN|DENTAL|DEGREE|DEALER|DATSUN|DATING|CRUISE|CREDIT|COUPON|CONDOS|COMSEC|COFFEE|CLINIC|CLAIMS|CIRCLE|CHURCH|CHROME|CHANEL|CENTER|CASINO|CAREER|CAMERA|BROKER|BOSTON|BOSTIK|BHARTI|BERLIN|BEAUTY|BAYERN|AUTHOR|ARAMCO|ANQUAN|AMAZON|ALSTOM|ALSACE|ALIPAY|AIRTEL|AIRBUS|AGENCY|AFRICA|ABBVIE|ABBOTT|ABARTH|YAHOO|XEROX|WORLD|WORKS|WEIBO|WEBER|WATCH|WALES|VOLVO|VODKA|VIDEO|VEGAS|UBANK|TUSHU|TUNES|TRUST|TRADE|TOURS|TOTAL|TORAY|TOOLS|TOKYO|TODAY|TMALL|TIROL|TIRES|TATAR|SWISS|SUCKS|STYLE|STUDY|STORE|STADA|SPORT|SPACE|SOLAR|SMILE|SMART|SLING|SKYPE|SHOES|SHELL|SHARP|SEVEN|SENER|SALON|RUGBY|RODEO|ROCKS|RICOH|REISE|REHAB|RADIO|QUEST|PROMO|PRIME|PRESS|PRAXI|POKER|PLACE|PIZZA|PHOTO|PHONE|PARTY|PARTS|PARIS|OSAKA|OMEGA|NOWTV|NOKIA|NINJA|NIKON|NEXUS|MOVIE|MONEY|MIAMI|MEDIA|MANGO|MACYS|LOTTO|LOTTE|LOCUS|LOANS|LIXIL|LIPSY|LINDE|LILLY|LEXUS|LEGAL|LEASE|LAMER|KYOTO|KOELN|JETZT|IVECO|IRISH|IKANO|HYATT|HOUSE|HORSE|HONDA|HOMES|GUIDE|GUCCI|GROUP|GRIPE|GREEN|GMAIL|GLOBO|GLASS|GLADE|GIVES|GIFTS|GAMES|GALLO|FORUM|FOREX|FINAL|FEDEX|FAITH|EPSON|EMAIL|EDEKA|EARTH|DUBAI|DRIVE|DELTA|DEALS|DANCE|DABUR|CYMRU|CROWN|CODES|COACH|CLOUD|CLICK|CITIC|CISCO|CHEAP|CHASE|CARDS|CANON|BUILD|BOSCH|BOATS|BLACK|BINGO|BIBLE|BEATS|BAIDU|AZURE|AUTOS|AUDIO|ARCHI|APPLE|AMICA|AMFAM|AETNA|ADULT|ACTOR|ZONE|ZERO|ZARA|YOGA|XBOX|WORK|WINE|WIKI|WIEN|WEIR|WANG|VOTO|VOTE|VIVO|VIVA|VISA|VANA|TUBE|TOYS|TOWN|TIPS|TIAA|TEVA|TECH|TEAM|TAXI|TALK|SURF|STAR|SPOT|SONY|SONG|SOHU|SNCF|SKIN|SITE|SINA|SILK|SHOW|SHOP|SHIA|SHAW|SEXY|SEEK|SEAT|SCOT|SAXO|SAVE|SARL|SALE|SAFE|RUHR|RSVP|ROOM|RMIT|RICH|REST|RENT|REIT|READ|RAID|QPON|PROF|PROD|POST|PORN|POHL|PLUS|PLAY|PINK|PING|PICS|PCCW|PARS|PAGE|OPEN|OLLO|NIKE|NICO|NEXT|NEWS|NAVY|NAME|MOTO|MODA|MOBI|MINT|MINI|MENU|MEME|MEET|MAIF|LUXE|LTDA|LOVE|LOFT|LOAN|LIVE|LINK|LIMO|LIKE|LIFE|LIDL|LGBT|LEGO|LAND|KRED|KPMG|KIWI|KDDI|JPRS|JOBS|JEEP|JAVA|ITAU|INFO|IMMO|IMDB|IEEE|ICBC|HSBC|HOST|HGTV|HERE|HELP|HDFC|HAUS|HAIR|GURU|GUGE|GOOG|GOLF|GOLD|GMBH|GIFT|GGEE|GENT|GBIZ|GAME|FUND|FREE|FORD|FOOD|FLIR|FISH|FIRE|FILM|FIDO|FIAT|FAST|FARM|FANS|FAIL|FAGE|ERNI|DVAG|DUCK|DOCS|DISH|DIET|DESI|DELL|DEAL|DCLK|DATE|DATA|CYOU|COOP|COOL|CLUB|CITY|CITI|CHAT|CERN|CBRE|CASH|CASE|CASA|CARS|CARE|CAMP|CALL|CAFE|BUZZ|BOOK|BOND|BOFA|BLUE|BLOG|BING|BIKE|BEST|BEER|BBVA|BANK|BAND|BABY|AUTO|AUDI|ASIA|ASDA|ARTE|ARPA|ARMY|ARAB|AMEX|ALLY|AKDN|AERO|ADAC|ABLE|AARP|ZIP|YUN|YOU|XYZ|XXX|XIN|WTF|WTC|WOW|WME|WIN|WED|VIP|VIN|VIG|VET|UPS|UOL|UNO|UBS|TVS|TUI|TRV|TOP|TJX|THD|TEL|TDK|TCI|TAX|TAB|STC|SRL|SPA|SOY|SKY|SKI|SFR|SEX|SEW|SES|SCB|SCA|SBS|SBI|SAS|SAP|RWE|RUN|RIP|RIO|RIL|REN|RED|QVC|PWC|PUB|PRU|PRO|PNC|PIN|PID|PHD|PET|PAY|OVH|OTT|ORG|OOO|ONL|ONG|ONE|OFF|OBI|NYC|NTT|NRW|NRA|NOW|NHK|NGO|NFL|NEW|NET|NEC|NBA|NAB|MTR|MTN|MSD|MOV|MOM|MOI|MOE|MMA|MLS|MLB|MIT|MIL|MEN|MED|MBA|MAP|MAN|LTD|LPL|LOL|LLP|LLC|LDS|LAW|LAT|KRD|KPN|KIM|KIA|KFH|JOY|JOT|JNJ|JMP|JLL|JIO|JCB|ITV|IST|INT|INK|ING|INC|IFM|ICU|ICE|IBM|HOW|HOT|HKT|HIV|HBO|GOV|GOT|GOP|GOO|GMX|GMO|GLE|GEA|GDN|GAY|GAP|GAL|FYI|FUN|FTR|FRL|FOX|FOO|FLY|FIT|FAN|EUS|ESQ|EDU|ECO|EAT|DVR|DTV|DOT|DOG|DNP|DIY|DHL|DEV|DDS|DAY|DAD|CSC|CRS|CPA|COM|CFD|CFA|CEO|CBS|CBN|CBA|CAT|CAR|CAM|CAL|CAB|BZH|BUY|BOX|BOT|BOO|BOM|BMW|BMS|BIZ|BIO|BID|BET|BCN|BCG|BBT|BBC|BAR|AXA|AWS|ART|APP|AOL|ANZ|AIG|AFL|AEG|ADS|ACO|ABC|ABB|AAA|ZW|ZM|ZA|YT|YE|WS|WF|VU|VN|VI|VG|VE|VC|VA|UZ|UY|US|UK|UG|UA|TZ|TW|TV|TT|TR|TO|TN|TM|TL|TK|TJ|TH|TG|TF|TD|TC|SZ|SY|SX|SV|SU|ST|SS|SR|SO|SN|SM|SL|SK|SJ|SI|SH|SG|SE|SD|SC|SB|SA|RW|RU|RS|RO|RE|QA|PY|PW|PT|PS|PR|PN|PM|PL|PK|PH|PG|PF|PE|PA|OM|NZ|NU|NR|NP|NO|NL|NI|NG|NF|NE|NC|NA|MZ|MY|MX|MW|MV|MU|MT|MS|MR|MQ|MP|MO|MN|MM|ML|MK|MH|MG|ME|MD|MC|MA|LY|LV|LU|LT|LS|LR|LK|LI|LC|LB|LA|KZ|KY|KW|KR|KP|KN|KM|KI|KH|KG|KE|JP|JO|JM|JE|IT|IS|IR|IQ|IO|IN|IM|IL|IE|ID|HU|HT|HR|HN|HM|HK|GY|GW|GU|GT|GS|GR|GQ|GP|GN|GM|GL|GI|GH|GG|GF|GE|GD|GB|GA|FR|FO|FM|FK|FJ|FI|EU|ET|ES|ER|EG|EE|EC|DZ|DO|DM|DK|DJ|DE|CZ|CY|CX|CW|CV|CU|CR|CO|CN|CM|CL|CK|CI|CH|CG|CF|CD|CC|CA|BZ|BY|BW|BV|BT|BS|BR|BO|BN|BM|BJ|BI|BH|BG|BF|BE|BD|BB|BA|AZ|AX|AW|AU|AT|AS|AR|AQ|AO|AM|AL|AI|AG|AF|AE|AD|AC|SJC|RNO|LAX)(?:\\:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\\b|(?=_))(?!@(?:[a-z\\d-]+\\.)+[a-z]{2,})';
+declare const URL_PATH_REGEX: '(?:(?:[.,=(+$!*]|&(?:amp|#x27);)?\\/(?:[-\\w$@.+!*:(),=%~]|&(?:amp|#x27);)*(?:[-\\w~@:%)]|&(?:amp|#x27);)|\\/)*';
+declare const URL_PARAM_REGEX: '(?:\\?(?:[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]|&(?:amp|#x27);)+)?';
+declare const URL_FRAGMENT_REGEX: '(?:#(?:[-\\w$@.+!*()[\\],=%;\\/:~]|&(?:amp|#x27);)*)?';
+declare const URL_REGEX: '((((ht|f)tps?:\\/\\/)?(?[-a-z0-9]{0,61}[a-z0-9])?)\\k\\.)+(?:XN--VERMGENSBERATUNG-PWB|XN--VERMGENSBERATER-CTB|XN--CLCHC0EA0B2G2A9GCD|XN--W4R85EL8FHU5DNRA|TRAVELERSINSURANCE|NORTHWESTERNMUTUAL|XN--XKC2DL3A5EE0H|XN--MGBERP4A5D4AR|XN--MGBAI9AZGQP6J|XN--MGBAH1A3HJKRD|XN--BCK1B9A5DRE4C|XN--5SU34J936BGSG|XN--3OQ18VL8PN36A|XN--XKC2AL3HYE2A|XN--MGBCPQ6GPA1A|XN--MGBA7C0BBN0A|XN--FZYS8D69UVGM|XN--NQV7FS00EMA|XN--MGBC0A9AZCG|XN--MGBAAKC7DVF|XN--MGBA3A4F16A|XN--LGBBAT1AD8J|XN--KCRX77D1X4A|XN--I1B6B1A6A2E|SANDVIKCOROMANT|KERRYPROPERTIES|AMERICANEXPRESS|XN--RVC1E0AM3E|XN--MGBX4CD0AB|XN--MGBI4ECEXP|XN--MGBCA7DZDO|XN--MGBBH1A71E|XN--MGBAYH7GPA|XN--MGBAAM7A8H|XN--MGBA3A3EJT|XN--JLQ61U9W7B|XN--JLQ480N2RG|XN--H2BREG3EVE|XN--FIQ228C5HS|XN--B4W605FERD|XN--80AQECDR1A|XN--6QQ986B3XL|XN--54B7FTA0CC|WEATHERCHANNEL|KERRYLOGISTICS|COOKINGCHANNEL|CANCERRESEARCH|BANANAREPUBLIC|AMERICANFAMILY|AFAMILYCOMPANY|XN--YGBI2AMMX|XN--YFRO4I67O|XN--TIQ49XQYJ|XN--H2BRJ9C8C|XN--FZC2C9E2C|XN--FPCRJ9C3D|XN--ECKVDTC9D|XN--CCKWCXETD|WOLTERSKLUWER|TRAVELCHANNEL|SPREADBETTING|LIFEINSURANCE|INTERNATIONAL|XN--QCKA1PMC|XN--OGBPF8FL|XN--NGBE9E0A|XN--NGBC5AZD|XN--MK1BU44C|XN--MGBT3DHD|XN--MGBPL2FH|XN--MGBGU82A|XN--MGBAB2BD|XN--MGB9AWBF|XN--GCKR3F0F|XN--8Y0A063A|XN--80ASEHDB|XN--80ADXHKS|XN--4DBRK0CE|XN--45BR5CYL|XN--3E0B707E|VERSICHERUNG|SCHOLARSHIPS|LPLFINANCIAL|CONSTRUCTION|XN--ZFR164B|XN--XHQ521B|XN--W4RS40L|XN--VUQ861B|XN--T60B56A|XN--SES554G|XN--S9BRJ9C|XN--ROVU88B|XN--RHQV96G|XN--Q9JYB4C|XN--PGBS0DH|XN--OTU796D|XN--NYQY26A|XN--MIX891F|XN--MGBTX2B|XN--MGBBH1A|XN--KPRY57D|XN--KPRW13D|XN--JVR189M|XN--J6W193G|XN--IMR513N|XN--HXT814E|XN--H2BRJ9C|XN--GK3AT1E|XN--GECRJ9C|XN--G2XX48C|XN--FLW351E|XN--FJQ720A|XN--FCT429K|XN--EFVY88H|XN--D1ACJ3B|XN--CZR694B|XN--CCK2B3B|XN--9KRT00A|XN--80AO21A|XN--6FRZ82G|XN--55QW42G|XN--45BRJ9C|XN--42C2D9A|XN--3HCRJ9C|XN--3DS443G|XN--3BST00M|XN--2SCRJ9C|XN--1QQW23A|XN--1CK2E1B|XN--11B4C3D|WILLIAMHILL|REDUMBRELLA|PROGRESSIVE|PRODUCTIONS|PLAYSTATION|PHOTOGRAPHY|OLAYANGROUP|MOTORCYCLES|LAMBORGHINI|KERRYHOTELS|INVESTMENTS|FOODNETWORK|ENTERPRISES|ENGINEERING|CREDITUNION|CONTRACTORS|CALVINKLEIN|BRIDGESTONE|BLOCKBUSTER|BLACKFRIDAY|BARCLAYCARD|ACCOUNTANTS|XN--Y9A3AQ|XN--WGBL6A|XN--WGBH1C|XN--UNUP4Y|XN--Q7CE6A|XN--PSSY2U|XN--O3CW4H|XN--MXTQ1M|XN--KPUT3I|XN--IO0A7I|XN--FIQZ9S|XN--FIQS8S|XN--FIQ64B|XN--CZRU2D|XN--CZRS0T|XN--CG4BKI|XN--C2BR7G|XN--9ET52U|XN--9DBQ2A|XN--90A3AC|XN--80ASWG|XN--5TZM5G|XN--55QX5D|XN--4GBRIM|XN--45Q11C|XN--3PXU8K|XN--30RR7Y|VOLKSWAGEN|VLAANDEREN|UNIVERSITY|TECHNOLOGY|TATAMOTORS|SWIFTCOVER|SCHAEFFLER|RESTAURANT|REPUBLICAN|REALESTATE|PRUDENTIAL|PROTECTION|PROPERTIES|ONYOURSIDE|NEXTDIRECT|NATIONWIDE|MITSUBISHI|MANAGEMENT|INDUSTRIES|IMMOBILIEN|HEALTHCARE|FOUNDATION|EXTRASPACE|EUROVISION|CUISINELLA|CREDITCARD|CONSULTING|CAPITALONE|BOEHRINGER|BNPPARIBAS|BASKETBALL|ASSOCIATES|APARTMENTS|ACCOUNTANT|YODOBASHI|XN--VHQUV|XN--TCKWE|XN--QXA6A|XN--P1ACF|XN--NQV7F|XN--NGBRX|XN--L1ACC|XN--J1AMH|XN--J1AEF|XN--FHBEI|XN--E1A4C|XN--D1ALF|XN--C1AVG|XN--90AIS|VACATIONS|TRAVELERS|STOCKHOLM|STATEFARM|STATEBANK|SOLUTIONS|SHANGRILA|SCJOHNSON|RICHARDLI|PRAMERICA|PASSAGENS|PANASONIC|MICROSOFT|MELBOURNE|MARSHALLS|MARKETING|LIFESTYLE|LANDROVER|LANCASTER|KUOKGROUP|INSURANCE|INSTITUTE|HOMESENSE|HOMEGOODS|HOMEDEPOT|HISAMITSU|GOLDPOINT|FURNITURE|FUJIXEROX|FRONTDOOR|FRESENIUS|FIRESTONE|FINANCIAL|FAIRWINDS|EQUIPMENT|EDUCATION|DIRECTORY|COMMUNITY|CHRISTMAS|BLOOMBERG|BARCELONA|AQUARELLE|ANALYTICS|AMSTERDAM|ALLFINANZ|ALFAROMEO|ACCENTURE|YOKOHAMA|XN--QXAM|XN--P1AI|XN--NODE|XN--90AE|WOODSIDE|VERISIGN|VENTURES|VANGUARD|TRAINING|SUPPLIES|STCGROUP|SOFTWARE|SOFTBANK|SHOWTIME|SHOPPING|SERVICES|SECURITY|SAMSCLUB|SAARLAND|RELIANCE|REDSTONE|PROPERTY|PLUMBING|PICTURES|PHARMACY|PARTNERS|OBSERVER|MORTGAGE|MERCKMSD|MEMORIAL|MCKINSEY|MASERATI|MARRIOTT|LUNDBECK|LIGHTING|JPMORGAN|ISTANBUL|IPIRANGA|INFINITI|HOSPITAL|HOLDINGS|HELSINKI|HDFCBANK|GUARDIAN|GRAPHICS|GRAINGER|GOODYEAR|FRONTIER|FOOTBALL|FIRMDALE|FIDELITY|FEEDBACK|EXCHANGE|ETISALAT|ERICSSON|ENGINEER|DOWNLOAD|DISCOVER|DISCOUNT|DIAMONDS|DEMOCRAT|DELOITTE|DELIVERY|COMPUTER|COMMBANK|CLOTHING|CLINIQUE|CLEANING|CITYEATS|CIPRIANI|CATHOLIC|CATERING|CAPETOWN|BUSINESS|BUILDERS|BUDAPEST|BRUSSELS|BROADWAY|BRADESCO|BOUTIQUE|BASEBALL|BARGAINS|BAREFOOT|BARCLAYS|ATTORNEY|ALLSTATE|AIRFORCE|ABUDHABI|ZUERICH|YOUTUBE|YAMAXUN|XFINITY|WINNERS|WINDOWS|WHOSWHO|WEDDING|WEBSITE|WEATHER|WATCHES|WANGGOU|WALMART|TRADING|TOSHIBA|TIFFANY|TICKETS|THEATRE|THEATER|TEMASEK|SYSTEMS|SURGERY|SUPPORT|STORAGE|STAPLES|SINGLES|SHIKSHA|SCIENCE|SCHWARZ|SCHMIDT|SANDVIK|SAMSUNG|REXROTH|REVIEWS|RENTALS|RECIPES|REALTOR|POLITIE|PIONEER|PHILIPS|ORIGINS|ORGANIC|OLDNAVY|OKINAWA|NEUSTAR|NETWORK|NETFLIX|NETBANK|MONSTER|MARKETS|LINCOLN|LIMITED|LECLERC|LATROBE|LASALLE|LANXESS|LACAIXA|KOMATSU|KITCHEN|JUNIPER|JEWELRY|ISMAILI|HYUNDAI|HOTMAIL|HOTELES|HOSTING|HOLIDAY|HITACHI|HANGOUT|HAMBURG|GUITARS|GROCERY|GODADDY|GENTING|GALLERY|FUJITSU|FROGANS|FORSALE|FLOWERS|FLORIST|FLIGHTS|FITNESS|FISHING|FINANCE|FERRERO|FERRARI|FASHION|FARMERS|EXPRESS|EXPOSED|DOMAINS|DIGITAL|DENTIST|CRUISES|CRICKET|COURSES|COUPONS|COUNTRY|CORSICA|COOKING|CONTACT|COMPARE|COMPANY|COMCAST|COLOGNE|COLLEGE|CLUBMED|CITADEL|CHINTAI|CHARITY|CHANNEL|CAREERS|CARAVAN|CAPITAL|BUGATTI|BROTHER|BOOKING|BESTBUY|BENTLEY|BAUHAUS|BANAMEX|AVIANCA|AUSPOST|AUDIBLE|AUCTION|ATHLETA|ANDROID|ALIBABA|AGAKHAN|ACADEMY|ABOGADO|ZAPPOS|YANDEX|YACHTS|XIHUAN|WEBCAM|WALTER|VUELOS|VOYAGE|VOTING|VISION|VIRGIN|VILLAS|VIKING|VIAJES|UNICOM|TRAVEL|TOYOTA|TKMAXX|TJMAXX|TIENDA|TENNIS|TATTOO|TARGET|TAOBAO|TAIPEI|SYDNEY|SWATCH|SUZUKI|SUPPLY|STUDIO|STREAM|SOCIAL|SOCCER|SHOUJI|SELECT|SECURE|SEARCH|SCHULE|SCHOOL|SANOFI|SAKURA|SAFETY|RYUKYU|ROGERS|ROCHER|REVIEW|REPORT|REPAIR|REISEN|REALTY|RACING|QUEBEC|PICTET|PHYSIO|PHOTOS|PFIZER|OTSUKA|ORANGE|ORACLE|ONLINE|OLAYAN|OFFICE|NOWRUZ|NORTON|NISSAY|NISSAN|NATURA|NAGOYA|MUTUAL|MUSEUM|MOSCOW|MORMON|MONASH|MOBILE|MATTEL|MARKET|MAKEUP|MAISON|MADRID|LUXURY|LONDON|LOCKER|LIVING|LEFRAK|LAWYER|LATINO|LANCIA|KOSHER|KINDLE|KINDER|KAUFEN|JUEGOS|JOBURG|JAGUAR|INTUIT|INSURE|IMAMAT|HUGHES|HOTELS|HOCKEY|HIPHOP|HERMES|HEALTH|GRATIS|GOOGLE|GLOBAL|GIVING|GEORGE|GARDEN|GALLUP|FUTBOL|FLICKR|FAMILY|EXPERT|EVENTS|ESTATE|ENERGY|EMERCK|DURBAN|DUPONT|DUNLOP|DOCTOR|DIRECT|DESIGN|DENTAL|DEGREE|DEALER|DATSUN|DATING|CRUISE|CREDIT|COUPON|CONDOS|COMSEC|COFFEE|CLINIC|CLAIMS|CIRCLE|CHURCH|CHROME|CHANEL|CENTER|CASINO|CAREER|CAMERA|BROKER|BOSTON|BOSTIK|BHARTI|BERLIN|BEAUTY|BAYERN|AUTHOR|ARAMCO|ANQUAN|AMAZON|ALSTOM|ALSACE|ALIPAY|AIRTEL|AIRBUS|AGENCY|AFRICA|ABBVIE|ABBOTT|ABARTH|YAHOO|XEROX|WORLD|WORKS|WEIBO|WEBER|WATCH|WALES|VOLVO|VODKA|VIDEO|VEGAS|UBANK|TUSHU|TUNES|TRUST|TRADE|TOURS|TOTAL|TORAY|TOOLS|TOKYO|TODAY|TMALL|TIROL|TIRES|TATAR|SWISS|SUCKS|STYLE|STUDY|STORE|STADA|SPORT|SPACE|SOLAR|SMILE|SMART|SLING|SKYPE|SHOES|SHELL|SHARP|SEVEN|SENER|SALON|RUGBY|RODEO|ROCKS|RICOH|REISE|REHAB|RADIO|QUEST|PROMO|PRIME|PRESS|PRAXI|POKER|PLACE|PIZZA|PHOTO|PHONE|PARTY|PARTS|PARIS|OSAKA|OMEGA|NOWTV|NOKIA|NINJA|NIKON|NEXUS|MOVIE|MONEY|MIAMI|MEDIA|MANGO|MACYS|LOTTO|LOTTE|LOCUS|LOANS|LIXIL|LIPSY|LINDE|LILLY|LEXUS|LEGAL|LEASE|LAMER|KYOTO|KOELN|JETZT|IVECO|IRISH|IKANO|HYATT|HOUSE|HORSE|HONDA|HOMES|GUIDE|GUCCI|GROUP|GRIPE|GREEN|GMAIL|GLOBO|GLASS|GLADE|GIVES|GIFTS|GAMES|GALLO|FORUM|FOREX|FINAL|FEDEX|FAITH|EPSON|EMAIL|EDEKA|EARTH|DUBAI|DRIVE|DELTA|DEALS|DANCE|DABUR|CYMRU|CROWN|CODES|COACH|CLOUD|CLICK|CITIC|CISCO|CHEAP|CHASE|CARDS|CANON|BUILD|BOSCH|BOATS|BLACK|BINGO|BIBLE|BEATS|BAIDU|AZURE|AUTOS|AUDIO|ARCHI|APPLE|AMICA|AMFAM|AETNA|ADULT|ACTOR|ZONE|ZERO|ZARA|YOGA|XBOX|WORK|WINE|WIKI|WIEN|WEIR|WANG|VOTO|VOTE|VIVO|VIVA|VISA|VANA|TUBE|TOYS|TOWN|TIPS|TIAA|TEVA|TECH|TEAM|TAXI|TALK|SURF|STAR|SPOT|SONY|SONG|SOHU|SNCF|SKIN|SITE|SINA|SILK|SHOW|SHOP|SHIA|SHAW|SEXY|SEEK|SEAT|SCOT|SAXO|SAVE|SARL|SALE|SAFE|RUHR|RSVP|ROOM|RMIT|RICH|REST|RENT|REIT|READ|RAID|QPON|PROF|PROD|POST|PORN|POHL|PLUS|PLAY|PINK|PING|PICS|PCCW|PARS|PAGE|OPEN|OLLO|NIKE|NICO|NEXT|NEWS|NAVY|NAME|MOTO|MODA|MOBI|MINT|MINI|MENU|MEME|MEET|MAIF|LUXE|LTDA|LOVE|LOFT|LOAN|LIVE|LINK|LIMO|LIKE|LIFE|LIDL|LGBT|LEGO|LAND|KRED|KPMG|KIWI|KDDI|JPRS|JOBS|JEEP|JAVA|ITAU|INFO|IMMO|IMDB|IEEE|ICBC|HSBC|HOST|HGTV|HERE|HELP|HDFC|HAUS|HAIR|GURU|GUGE|GOOG|GOLF|GOLD|GMBH|GIFT|GGEE|GENT|GBIZ|GAME|FUND|FREE|FORD|FOOD|FLIR|FISH|FIRE|FILM|FIDO|FIAT|FAST|FARM|FANS|FAIL|FAGE|ERNI|DVAG|DUCK|DOCS|DISH|DIET|DESI|DELL|DEAL|DCLK|DATE|DATA|CYOU|COOP|COOL|CLUB|CITY|CITI|CHAT|CERN|CBRE|CASH|CASE|CASA|CARS|CARE|CAMP|CALL|CAFE|BUZZ|BOOK|BOND|BOFA|BLUE|BLOG|BING|BIKE|BEST|BEER|BBVA|BANK|BAND|BABY|AUTO|AUDI|ASIA|ASDA|ARTE|ARPA|ARMY|ARAB|AMEX|ALLY|AKDN|AERO|ADAC|ABLE|AARP|ZIP|YUN|YOU|XYZ|XXX|XIN|WTF|WTC|WOW|WME|WIN|WED|VIP|VIN|VIG|VET|UPS|UOL|UNO|UBS|TVS|TUI|TRV|TOP|TJX|THD|TEL|TDK|TCI|TAX|TAB|STC|SRL|SPA|SOY|SKY|SKI|SFR|SEX|SEW|SES|SCB|SCA|SBS|SBI|SAS|SAP|RWE|RUN|RIP|RIO|RIL|REN|RED|QVC|PWC|PUB|PRU|PRO|PNC|PIN|PID|PHD|PET|PAY|OVH|OTT|ORG|OOO|ONL|ONG|ONE|OFF|OBI|NYC|NTT|NRW|NRA|NOW|NHK|NGO|NFL|NEW|NET|NEC|NBA|NAB|MTR|MTN|MSD|MOV|MOM|MOI|MOE|MMA|MLS|MLB|MIT|MIL|MEN|MED|MBA|MAP|MAN|LTD|LPL|LOL|LLP|LLC|LDS|LAW|LAT|KRD|KPN|KIM|KIA|KFH|JOY|JOT|JNJ|JMP|JLL|JIO|JCB|ITV|IST|INT|INK|ING|INC|IFM|ICU|ICE|IBM|HOW|HOT|HKT|HIV|HBO|GOV|GOT|GOP|GOO|GMX|GMO|GLE|GEA|GDN|GAY|GAP|GAL|FYI|FUN|FTR|FRL|FOX|FOO|FLY|FIT|FAN|EUS|ESQ|EDU|ECO|EAT|DVR|DTV|DOT|DOG|DNP|DIY|DHL|DEV|DDS|DAY|DAD|CSC|CRS|CPA|COM|CFD|CFA|CEO|CBS|CBN|CBA|CAT|CAR|CAM|CAL|CAB|BZH|BUY|BOX|BOT|BOO|BOM|BMW|BMS|BIZ|BIO|BID|BET|BCN|BCG|BBT|BBC|BAR|AXA|AWS|ART|APP|AOL|ANZ|AIG|AFL|AEG|ADS|ACO|ABC|ABB|AAA|ZW|ZM|ZA|YT|YE|WS|WF|VU|VN|VI|VG|VE|VC|VA|UZ|UY|US|UK|UG|UA|TZ|TW|TV|TT|TR|TO|TN|TM|TL|TK|TJ|TH|TG|TF|TD|TC|SZ|SY|SX|SV|SU|ST|SS|SR|SO|SN|SM|SL|SK|SJ|SI|SH|SG|SE|SD|SC|SB|SA|RW|RU|RS|RO|RE|QA|PY|PW|PT|PS|PR|PN|PM|PL|PK|PH|PG|PF|PE|PA|OM|NZ|NU|NR|NP|NO|NL|NI|NG|NF|NE|NC|NA|MZ|MY|MX|MW|MV|MU|MT|MS|MR|MQ|MP|MO|MN|MM|ML|MK|MH|MG|ME|MD|MC|MA|LY|LV|LU|LT|LS|LR|LK|LI|LC|LB|LA|KZ|KY|KW|KR|KP|KN|KM|KI|KH|KG|KE|JP|JO|JM|JE|IT|IS|IR|IQ|IO|IN|IM|IL|IE|ID|HU|HT|HR|HN|HM|HK|GY|GW|GU|GT|GS|GR|GQ|GP|GN|GM|GL|GI|GH|GG|GF|GE|GD|GB|GA|FR|FO|FM|FK|FJ|FI|EU|ET|ES|ER|EG|EE|EC|DZ|DO|DM|DK|DJ|DE|CZ|CY|CX|CW|CV|CU|CR|CO|CN|CM|CL|CK|CI|CH|CG|CF|CD|CC|CA|BZ|BY|BW|BV|BT|BS|BR|BO|BN|BM|BJ|BI|BH|BG|BF|BE|BD|BB|BA|AZ|AX|AW|AU|AT|AS|AR|AQ|AO|AM|AL|AI|AG|AF|AE|AD|AC|SJC|RNO|LAX)(?:\\:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\\b|(?=_))(?!@(?:[a-z\\d-]+\\.)+[a-z]{2,}))(?:(?:[.,=(+$!*]|&(?:amp|#x27);)?\\/(?:[-\\w$@.+!*:(),=%~]|&(?:amp|#x27);)*(?:[-\\w~@:%)]|&(?:amp|#x27);)|\\/)*(?:(?:\\?(?:[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]|&(?:amp|#x27);)+)?|(?:#(?:[-\\w$@.+!*()[\\],=%;\\/:~]|&(?:amp|#x27);)*)?)*)';
declare const URL_REGEX_WITH_REQUIRED_PROTOCOL: string;
-declare const LOOSE_URL_WEBSITE_REGEX: '((ht|f)tps?:\\/\\/)((?!-)[-a-z0-9]+(?[-a-z0-9]{0,61}[a-z0-9])?)\\k\\.)+(?:XN--VERMGENSBERATUNG-PWB|XN--VERMGENSBERATER-CTB|XN--CLCHC0EA0B2G2A9GCD|XN--W4R85EL8FHU5DNRA|TRAVELERSINSURANCE|NORTHWESTERNMUTUAL|XN--XKC2DL3A5EE0H|XN--MGBERP4A5D4AR|XN--MGBAI9AZGQP6J|XN--MGBAH1A3HJKRD|XN--BCK1B9A5DRE4C|XN--5SU34J936BGSG|XN--3OQ18VL8PN36A|XN--XKC2AL3HYE2A|XN--MGBCPQ6GPA1A|XN--MGBA7C0BBN0A|XN--FZYS8D69UVGM|XN--NQV7FS00EMA|XN--MGBC0A9AZCG|XN--MGBAAKC7DVF|XN--MGBA3A4F16A|XN--LGBBAT1AD8J|XN--KCRX77D1X4A|XN--I1B6B1A6A2E|SANDVIKCOROMANT|KERRYPROPERTIES|AMERICANEXPRESS|XN--RVC1E0AM3E|XN--MGBX4CD0AB|XN--MGBI4ECEXP|XN--MGBCA7DZDO|XN--MGBBH1A71E|XN--MGBAYH7GPA|XN--MGBAAM7A8H|XN--MGBA3A3EJT|XN--JLQ61U9W7B|XN--JLQ480N2RG|XN--H2BREG3EVE|XN--FIQ228C5HS|XN--B4W605FERD|XN--80AQECDR1A|XN--6QQ986B3XL|XN--54B7FTA0CC|WEATHERCHANNEL|KERRYLOGISTICS|COOKINGCHANNEL|CANCERRESEARCH|BANANAREPUBLIC|AMERICANFAMILY|AFAMILYCOMPANY|XN--YGBI2AMMX|XN--YFRO4I67O|XN--TIQ49XQYJ|XN--H2BRJ9C8C|XN--FZC2C9E2C|XN--FPCRJ9C3D|XN--ECKVDTC9D|XN--CCKWCXETD|WOLTERSKLUWER|TRAVELCHANNEL|SPREADBETTING|LIFEINSURANCE|INTERNATIONAL|XN--QCKA1PMC|XN--OGBPF8FL|XN--NGBE9E0A|XN--NGBC5AZD|XN--MK1BU44C|XN--MGBT3DHD|XN--MGBPL2FH|XN--MGBGU82A|XN--MGBAB2BD|XN--MGB9AWBF|XN--GCKR3F0F|XN--8Y0A063A|XN--80ASEHDB|XN--80ADXHKS|XN--4DBRK0CE|XN--45BR5CYL|XN--3E0B707E|VERSICHERUNG|SCHOLARSHIPS|LPLFINANCIAL|CONSTRUCTION|XN--ZFR164B|XN--XHQ521B|XN--W4RS40L|XN--VUQ861B|XN--T60B56A|XN--SES554G|XN--S9BRJ9C|XN--ROVU88B|XN--RHQV96G|XN--Q9JYB4C|XN--PGBS0DH|XN--OTU796D|XN--NYQY26A|XN--MIX891F|XN--MGBTX2B|XN--MGBBH1A|XN--KPRY57D|XN--KPRW13D|XN--JVR189M|XN--J6W193G|XN--IMR513N|XN--HXT814E|XN--H2BRJ9C|XN--GK3AT1E|XN--GECRJ9C|XN--G2XX48C|XN--FLW351E|XN--FJQ720A|XN--FCT429K|XN--EFVY88H|XN--D1ACJ3B|XN--CZR694B|XN--CCK2B3B|XN--9KRT00A|XN--80AO21A|XN--6FRZ82G|XN--55QW42G|XN--45BRJ9C|XN--42C2D9A|XN--3HCRJ9C|XN--3DS443G|XN--3BST00M|XN--2SCRJ9C|XN--1QQW23A|XN--1CK2E1B|XN--11B4C3D|WILLIAMHILL|REDUMBRELLA|PROGRESSIVE|PRODUCTIONS|PLAYSTATION|PHOTOGRAPHY|OLAYANGROUP|MOTORCYCLES|LAMBORGHINI|KERRYHOTELS|INVESTMENTS|FOODNETWORK|ENTERPRISES|ENGINEERING|CREDITUNION|CONTRACTORS|CALVINKLEIN|BRIDGESTONE|BLOCKBUSTER|BLACKFRIDAY|BARCLAYCARD|ACCOUNTANTS|XN--Y9A3AQ|XN--WGBL6A|XN--WGBH1C|XN--UNUP4Y|XN--Q7CE6A|XN--PSSY2U|XN--O3CW4H|XN--MXTQ1M|XN--KPUT3I|XN--IO0A7I|XN--FIQZ9S|XN--FIQS8S|XN--FIQ64B|XN--CZRU2D|XN--CZRS0T|XN--CG4BKI|XN--C2BR7G|XN--9ET52U|XN--9DBQ2A|XN--90A3AC|XN--80ASWG|XN--5TZM5G|XN--55QX5D|XN--4GBRIM|XN--45Q11C|XN--3PXU8K|XN--30RR7Y|VOLKSWAGEN|VLAANDEREN|UNIVERSITY|TECHNOLOGY|TATAMOTORS|SWIFTCOVER|SCHAEFFLER|RESTAURANT|REPUBLICAN|REALESTATE|PRUDENTIAL|PROTECTION|PROPERTIES|ONYOURSIDE|NEXTDIRECT|NATIONWIDE|MITSUBISHI|MANAGEMENT|INDUSTRIES|IMMOBILIEN|HEALTHCARE|FOUNDATION|EXTRASPACE|EUROVISION|CUISINELLA|CREDITCARD|CONSULTING|CAPITALONE|BOEHRINGER|BNPPARIBAS|BASKETBALL|ASSOCIATES|APARTMENTS|ACCOUNTANT|YODOBASHI|XN--VHQUV|XN--TCKWE|XN--QXA6A|XN--P1ACF|XN--NQV7F|XN--NGBRX|XN--L1ACC|XN--J1AMH|XN--J1AEF|XN--FHBEI|XN--E1A4C|XN--D1ALF|XN--C1AVG|XN--90AIS|VACATIONS|TRAVELERS|STOCKHOLM|STATEFARM|STATEBANK|SOLUTIONS|SHANGRILA|SCJOHNSON|RICHARDLI|PRAMERICA|PASSAGENS|PANASONIC|MICROSOFT|MELBOURNE|MARSHALLS|MARKETING|LIFESTYLE|LANDROVER|LANCASTER|KUOKGROUP|INSURANCE|INSTITUTE|HOMESENSE|HOMEGOODS|HOMEDEPOT|HISAMITSU|GOLDPOINT|FURNITURE|FUJIXEROX|FRONTDOOR|FRESENIUS|FIRESTONE|FINANCIAL|FAIRWINDS|EQUIPMENT|EDUCATION|DIRECTORY|COMMUNITY|CHRISTMAS|BLOOMBERG|BARCELONA|AQUARELLE|ANALYTICS|AMSTERDAM|ALLFINANZ|ALFAROMEO|ACCENTURE|YOKOHAMA|XN--QXAM|XN--P1AI|XN--NODE|XN--90AE|WOODSIDE|VERISIGN|VENTURES|VANGUARD|TRAINING|SUPPLIES|STCGROUP|SOFTWARE|SOFTBANK|SHOWTIME|SHOPPING|SERVICES|SECURITY|SAMSCLUB|SAARLAND|RELIANCE|REDSTONE|PROPERTY|PLUMBING|PICTURES|PHARMACY|PARTNERS|OBSERVER|MORTGAGE|MERCKMSD|MEMORIAL|MCKINSEY|MASERATI|MARRIOTT|LUNDBECK|LIGHTING|JPMORGAN|ISTANBUL|IPIRANGA|INFINITI|HOSPITAL|HOLDINGS|HELSINKI|HDFCBANK|GUARDIAN|GRAPHICS|GRAINGER|GOODYEAR|FRONTIER|FOOTBALL|FIRMDALE|FIDELITY|FEEDBACK|EXCHANGE|ETISALAT|ERICSSON|ENGINEER|DOWNLOAD|DISCOVER|DISCOUNT|DIAMONDS|DEMOCRAT|DELOITTE|DELIVERY|COMPUTER|COMMBANK|CLOTHING|CLINIQUE|CLEANING|CITYEATS|CIPRIANI|CATHOLIC|CATERING|CAPETOWN|BUSINESS|BUILDERS|BUDAPEST|BRUSSELS|BROADWAY|BRADESCO|BOUTIQUE|BASEBALL|BARGAINS|BAREFOOT|BARCLAYS|ATTORNEY|ALLSTATE|AIRFORCE|ABUDHABI|ZUERICH|YOUTUBE|YAMAXUN|XFINITY|WINNERS|WINDOWS|WHOSWHO|WEDDING|WEBSITE|WEATHER|WATCHES|WANGGOU|WALMART|TRADING|TOSHIBA|TIFFANY|TICKETS|THEATRE|THEATER|TEMASEK|SYSTEMS|SURGERY|SUPPORT|STORAGE|STAPLES|SINGLES|SHIKSHA|SCIENCE|SCHWARZ|SCHMIDT|SANDVIK|SAMSUNG|REXROTH|REVIEWS|RENTALS|RECIPES|REALTOR|POLITIE|PIONEER|PHILIPS|ORIGINS|ORGANIC|OLDNAVY|OKINAWA|NEUSTAR|NETWORK|NETFLIX|NETBANK|MONSTER|MARKETS|LINCOLN|LIMITED|LECLERC|LATROBE|LASALLE|LANXESS|LACAIXA|KOMATSU|KITCHEN|JUNIPER|JEWELRY|ISMAILI|HYUNDAI|HOTMAIL|HOTELES|HOSTING|HOLIDAY|HITACHI|HANGOUT|HAMBURG|GUITARS|GROCERY|GODADDY|GENTING|GALLERY|FUJITSU|FROGANS|FORSALE|FLOWERS|FLORIST|FLIGHTS|FITNESS|FISHING|FINANCE|FERRERO|FERRARI|FASHION|FARMERS|EXPRESS|EXPOSED|DOMAINS|DIGITAL|DENTIST|CRUISES|CRICKET|COURSES|COUPONS|COUNTRY|CORSICA|COOKING|CONTACT|COMPARE|COMPANY|COMCAST|COLOGNE|COLLEGE|CLUBMED|CITADEL|CHINTAI|CHARITY|CHANNEL|CAREERS|CARAVAN|CAPITAL|BUGATTI|BROTHER|BOOKING|BESTBUY|BENTLEY|BAUHAUS|BANAMEX|AVIANCA|AUSPOST|AUDIBLE|AUCTION|ATHLETA|ANDROID|ALIBABA|AGAKHAN|ACADEMY|ABOGADO|ZAPPOS|YANDEX|YACHTS|XIHUAN|WEBCAM|WALTER|VUELOS|VOYAGE|VOTING|VISION|VIRGIN|VILLAS|VIKING|VIAJES|UNICOM|TRAVEL|TOYOTA|TKMAXX|TJMAXX|TIENDA|TENNIS|TATTOO|TARGET|TAOBAO|TAIPEI|SYDNEY|SWATCH|SUZUKI|SUPPLY|STUDIO|STREAM|SOCIAL|SOCCER|SHOUJI|SELECT|SECURE|SEARCH|SCHULE|SCHOOL|SANOFI|SAKURA|SAFETY|RYUKYU|ROGERS|ROCHER|REVIEW|REPORT|REPAIR|REISEN|REALTY|RACING|QUEBEC|PICTET|PHYSIO|PHOTOS|PFIZER|OTSUKA|ORANGE|ORACLE|ONLINE|OLAYAN|OFFICE|NOWRUZ|NORTON|NISSAY|NISSAN|NATURA|NAGOYA|MUTUAL|MUSEUM|MOSCOW|MORMON|MONASH|MOBILE|MATTEL|MARKET|MAKEUP|MAISON|MADRID|LUXURY|LONDON|LOCKER|LIVING|LEFRAK|LAWYER|LATINO|LANCIA|KOSHER|KINDLE|KINDER|KAUFEN|JUEGOS|JOBURG|JAGUAR|INTUIT|INSURE|IMAMAT|HUGHES|HOTELS|HOCKEY|HIPHOP|HERMES|HEALTH|GRATIS|GOOGLE|GLOBAL|GIVING|GEORGE|GARDEN|GALLUP|FUTBOL|FLICKR|FAMILY|EXPERT|EVENTS|ESTATE|ENERGY|EMERCK|DURBAN|DUPONT|DUNLOP|DOCTOR|DIRECT|DESIGN|DENTAL|DEGREE|DEALER|DATSUN|DATING|CRUISE|CREDIT|COUPON|CONDOS|COMSEC|COFFEE|CLINIC|CLAIMS|CIRCLE|CHURCH|CHROME|CHANEL|CENTER|CASINO|CAREER|CAMERA|BROKER|BOSTON|BOSTIK|BHARTI|BERLIN|BEAUTY|BAYERN|AUTHOR|ARAMCO|ANQUAN|AMAZON|ALSTOM|ALSACE|ALIPAY|AIRTEL|AIRBUS|AGENCY|AFRICA|ABBVIE|ABBOTT|ABARTH|YAHOO|XEROX|WORLD|WORKS|WEIBO|WEBER|WATCH|WALES|VOLVO|VODKA|VIDEO|VEGAS|UBANK|TUSHU|TUNES|TRUST|TRADE|TOURS|TOTAL|TORAY|TOOLS|TOKYO|TODAY|TMALL|TIROL|TIRES|TATAR|SWISS|SUCKS|STYLE|STUDY|STORE|STADA|SPORT|SPACE|SOLAR|SMILE|SMART|SLING|SKYPE|SHOES|SHELL|SHARP|SEVEN|SENER|SALON|RUGBY|RODEO|ROCKS|RICOH|REISE|REHAB|RADIO|QUEST|PROMO|PRIME|PRESS|PRAXI|POKER|PLACE|PIZZA|PHOTO|PHONE|PARTY|PARTS|PARIS|OSAKA|OMEGA|NOWTV|NOKIA|NINJA|NIKON|NEXUS|MOVIE|MONEY|MIAMI|MEDIA|MANGO|MACYS|LOTTO|LOTTE|LOCUS|LOANS|LIXIL|LIPSY|LINDE|LILLY|LEXUS|LEGAL|LEASE|LAMER|KYOTO|KOELN|JETZT|IVECO|IRISH|IKANO|HYATT|HOUSE|HORSE|HONDA|HOMES|GUIDE|GUCCI|GROUP|GRIPE|GREEN|GMAIL|GLOBO|GLASS|GLADE|GIVES|GIFTS|GAMES|GALLO|FORUM|FOREX|FINAL|FEDEX|FAITH|EPSON|EMAIL|EDEKA|EARTH|DUBAI|DRIVE|DELTA|DEALS|DANCE|DABUR|CYMRU|CROWN|CODES|COACH|CLOUD|CLICK|CITIC|CISCO|CHEAP|CHASE|CARDS|CANON|BUILD|BOSCH|BOATS|BLACK|BINGO|BIBLE|BEATS|BAIDU|AZURE|AUTOS|AUDIO|ARCHI|APPLE|AMICA|AMFAM|AETNA|ADULT|ACTOR|ZONE|ZERO|ZARA|YOGA|XBOX|WORK|WINE|WIKI|WIEN|WEIR|WANG|VOTO|VOTE|VIVO|VIVA|VISA|VANA|TUBE|TOYS|TOWN|TIPS|TIAA|TEVA|TECH|TEAM|TAXI|TALK|SURF|STAR|SPOT|SONY|SONG|SOHU|SNCF|SKIN|SITE|SINA|SILK|SHOW|SHOP|SHIA|SHAW|SEXY|SEEK|SEAT|SCOT|SAXO|SAVE|SARL|SALE|SAFE|RUHR|RSVP|ROOM|RMIT|RICH|REST|RENT|REIT|READ|RAID|QPON|PROF|PROD|POST|PORN|POHL|PLUS|PLAY|PINK|PING|PICS|PCCW|PARS|PAGE|OPEN|OLLO|NIKE|NICO|NEXT|NEWS|NAVY|NAME|MOTO|MODA|MOBI|MINT|MINI|MENU|MEME|MEET|MAIF|LUXE|LTDA|LOVE|LOFT|LOAN|LIVE|LINK|LIMO|LIKE|LIFE|LIDL|LGBT|LEGO|LAND|KRED|KPMG|KIWI|KDDI|JPRS|JOBS|JEEP|JAVA|ITAU|INFO|IMMO|IMDB|IEEE|ICBC|HSBC|HOST|HGTV|HERE|HELP|HDFC|HAUS|HAIR|GURU|GUGE|GOOG|GOLF|GOLD|GMBH|GIFT|GGEE|GENT|GBIZ|GAME|FUND|FREE|FORD|FOOD|FLIR|FISH|FIRE|FILM|FIDO|FIAT|FAST|FARM|FANS|FAIL|FAGE|ERNI|DVAG|DUCK|DOCS|DISH|DIET|DESI|DELL|DEAL|DCLK|DATE|DATA|CYOU|COOP|COOL|CLUB|CITY|CITI|CHAT|CERN|CBRE|CASH|CASE|CASA|CARS|CARE|CAMP|CALL|CAFE|BUZZ|BOOK|BOND|BOFA|BLUE|BLOG|BING|BIKE|BEST|BEER|BBVA|BANK|BAND|BABY|AUTO|AUDI|ASIA|ASDA|ARTE|ARPA|ARMY|ARAB|AMEX|ALLY|AKDN|AERO|ADAC|ABLE|AARP|ZIP|YUN|YOU|XYZ|XXX|XIN|WTF|WTC|WOW|WME|WIN|WED|VIP|VIN|VIG|VET|UPS|UOL|UNO|UBS|TVS|TUI|TRV|TOP|TJX|THD|TEL|TDK|TCI|TAX|TAB|STC|SRL|SPA|SOY|SKY|SKI|SFR|SEX|SEW|SES|SCB|SCA|SBS|SBI|SAS|SAP|RWE|RUN|RIP|RIO|RIL|REN|RED|QVC|PWC|PUB|PRU|PRO|PNC|PIN|PID|PHD|PET|PAY|OVH|OTT|ORG|OOO|ONL|ONG|ONE|OFF|OBI|NYC|NTT|NRW|NRA|NOW|NHK|NGO|NFL|NEW|NET|NEC|NBA|NAB|MTR|MTN|MSD|MOV|MOM|MOI|MOE|MMA|MLS|MLB|MIT|MIL|MEN|MED|MBA|MAP|MAN|LTD|LPL|LOL|LLP|LLC|LDS|LAW|LAT|KRD|KPN|KIM|KIA|KFH|JOY|JOT|JNJ|JMP|JLL|JIO|JCB|ITV|IST|INT|INK|ING|INC|IFM|ICU|ICE|IBM|HOW|HOT|HKT|HIV|HBO|GOV|GOT|GOP|GOO|GMX|GMO|GLE|GEA|GDN|GAY|GAP|GAL|FYI|FUN|FTR|FRL|FOX|FOO|FLY|FIT|FAN|EUS|ESQ|EDU|ECO|EAT|DVR|DTV|DOT|DOG|DNP|DIY|DHL|DEV|DDS|DAY|DAD|CSC|CRS|CPA|COM|CFD|CFA|CEO|CBS|CBN|CBA|CAT|CAR|CAM|CAL|CAB|BZH|BUY|BOX|BOT|BOO|BOM|BMW|BMS|BIZ|BIO|BID|BET|BCN|BCG|BBT|BBC|BAR|AXA|AWS|ART|APP|AOL|ANZ|AIG|AFL|AEG|ADS|ACO|ABC|ABB|AAA|ZW|ZM|ZA|YT|YE|WS|WF|VU|VN|VI|VG|VE|VC|VA|UZ|UY|US|UK|UG|UA|TZ|TW|TV|TT|TR|TO|TN|TM|TL|TK|TJ|TH|TG|TF|TD|TC|SZ|SY|SX|SV|SU|ST|SS|SR|SO|SN|SM|SL|SK|SJ|SI|SH|SG|SE|SD|SC|SB|SA|RW|RU|RS|RO|RE|QA|PY|PW|PT|PS|PR|PN|PM|PL|PK|PH|PG|PF|PE|PA|OM|NZ|NU|NR|NP|NO|NL|NI|NG|NF|NE|NC|NA|MZ|MY|MX|MW|MV|MU|MT|MS|MR|MQ|MP|MO|MN|MM|ML|MK|MH|MG|ME|MD|MC|MA|LY|LV|LU|LT|LS|LR|LK|LI|LC|LB|LA|KZ|KY|KW|KR|KP|KN|KM|KI|KH|KG|KE|JP|JO|JM|JE|IT|IS|IR|IQ|IO|IN|IM|IL|IE|ID|HU|HT|HR|HN|HM|HK|GY|GW|GU|GT|GS|GR|GQ|GP|GN|GM|GL|GI|GH|GG|GF|GE|GD|GB|GA|FR|FO|FM|FK|FJ|FI|EU|ET|ES|ER|EG|EE|EC|DZ|DO|DM|DK|DJ|DE|CZ|CY|CX|CW|CV|CU|CR|CO|CN|CM|CL|CK|CI|CH|CG|CF|CD|CC|CA|BZ|BY|BW|BV|BT|BS|BR|BO|BN|BM|BJ|BI|BH|BG|BF|BE|BD|BB|BA|AZ|AX|AW|AU|AT|AS|AR|AQ|AO|AM|AL|AI|AG|AF|AE|AD|AC|SJC|RNO|LAX)(?:\\:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\\b|(?=_))(?!@(?:[a-z\\d-]+\\.)+[a-z]{2,}))(?:(?:[.,=(+$!*]|&(?:amp|#x27);)?\\/(?:[-\\w$@.+!*:(),=%~]|&(?:amp|#x27);)*(?:[-\\w~@:%)]|&(?:amp|#x27);)|\\/)*(?:(?:\\?(?:[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]|&(?:amp|#x27);)+)?|(?:#(?:[-\\w$@.+!*()[\\],=%;\\/:~]|&(?:amp|#x27);)*)?)*))';
export {
+ URL_HOSTNAME_LABEL_REGEX,
URL_WEBSITE_REGEX,
URL_PATH_REGEX,
URL_PARAM_REGEX,
@@ -16,6 +19,7 @@ export {
URL_REGEX,
URL_REGEX_WITH_REQUIRED_PROTOCOL,
URL_PROTOCOL_REGEX,
+ URL_PORT_REGEX,
LOOSE_URL_REGEX,
LOOSE_URL_WEBSITE_REGEX,
MARKDOWN_URL_REGEX,
diff --git a/lib/Url.js b/lib/Url.js
index d2873f63..f41d459d 100644
--- a/lib/Url.js
+++ b/lib/Url.js
@@ -1,9 +1,19 @@
import TLD_REGEX from './tlds';
+import {MAX_URL_HOSTNAME_LABEL_LENGTH, MAX_URL_HOSTNAME_LENGTH} from './CONST';
-const ALLOWED_PORTS = '([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])';
+const URL_PORT_REGEX = '([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])';
const URL_PROTOCOL_REGEX = '((ht|f)tps?:\\/\\/)';
-const URL_WEBSITE_REGEX = `${URL_PROTOCOL_REGEX}?((?:www\\.)?[a-z0-9](?=(?[-a-z0-9]*[a-z0-9])?)\\k\\.)+(?:${TLD_REGEX})\
-(?:\\:${ALLOWED_PORTS}|\\b|(?=_))(?!@(?:[a-z\\d-]+\\.)+[a-z]{2,})`;
+const MAX_URL_HOSTNAME_LABEL_BODY_LENGTH = MAX_URL_HOSTNAME_LABEL_LENGTH - 2;
+const URL_HOSTNAME_LABEL_REGEX = `[a-z0-9](?=(?[-a-z0-9]{0,${MAX_URL_HOSTNAME_LABEL_BODY_LENGTH}}[a-z0-9])?)\\k`;
+const LOOSE_URL_HOSTNAME_LABEL_REGEX = `(?!-)[-a-z0-9]{1,${MAX_URL_HOSTNAME_LABEL_LENGTH}}(? `(?:${reg}|&(?:amp|#x27);)`;
const URL_PATH_REGEX = `(?:${addEscapedChar('[.,=(+$!*]')}?\\/${addEscapedChar('[-\\w$@.+!*:(),=%~]')}*${addEscapedChar('[-\\w~@:%)]')}|\\/)*`;
const URL_PARAM_REGEX = `(?:\\?${addEscapedChar('[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]')}+)?`;
@@ -12,12 +22,13 @@ const URL_REGEX = `((${URL_WEBSITE_REGEX})${URL_PATH_REGEX}(?:${URL_PARAM_REGEX}
const URL_REGEX_WITH_REQUIRED_PROTOCOL = URL_REGEX.replace(`${URL_PROTOCOL_REGEX}?`, URL_PROTOCOL_REGEX);
-const LOOSE_URL_WEBSITE_REGEX = `${URL_PROTOCOL_REGEX}((?!-)[-a-z0-9]+(?