|
|
@ -1,9 +1,19 @@ |
|
|
// Process links like https://example.org/
|
|
|
// Early process links like https://example.org/ to have priority
|
|
|
|
|
|
// over emphasis, etc.
|
|
|
|
|
|
|
|
|
import type StateInline from './state_inline.ts' |
|
|
import type StateInline from './state_inline.ts' |
|
|
|
|
|
|
|
|
// RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
|
// RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
|
const SCHEME_RE = /(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$/i |
|
|
function isAsciiAlpha (code: number): boolean { |
|
|
|
|
|
return (code >= 0x41 && code <= 0x5A) || (code >= 0x61 && code <= 0x7A) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function isSchemeChar (code: number): boolean { |
|
|
|
|
|
return (code >= 0x41 && code <= 0x5A) || |
|
|
|
|
|
(code >= 0x61 && code <= 0x7A) || |
|
|
|
|
|
(code >= 0x30 && code <= 0x39) || |
|
|
|
|
|
code === 0x2B || code === 0x2D || code === 0x2E |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
export default function linkify (state: StateInline, silent: boolean): boolean { |
|
|
export default function linkify (state: StateInline, silent: boolean): boolean { |
|
|
if (!state.md.options.linkify) return false |
|
|
if (!state.md.options.linkify) return false |
|
|
@ -17,19 +27,27 @@ export default function linkify (state: StateInline, silent: boolean): boolean { |
|
|
if (state.src.charCodeAt(pos + 1) !== 0x2F/* / */) return false |
|
|
if (state.src.charCodeAt(pos + 1) !== 0x2F/* / */) return false |
|
|
if (state.src.charCodeAt(pos + 2) !== 0x2F/* / */) return false |
|
|
if (state.src.charCodeAt(pos + 2) !== 0x2F/* / */) return false |
|
|
|
|
|
|
|
|
const match = state.pending.match(SCHEME_RE) |
|
|
// Search backwards for the scheme, but no more than 10 characters, the length
|
|
|
if (!match) return false |
|
|
// of the pending string, or the length of the source prefix.
|
|
|
|
|
|
// Use state.src instead of state.pending because it is faster to access.
|
|
|
|
|
|
const protoMin = pos - Math.min(10, state.pending.length, pos) |
|
|
|
|
|
let protoStart = pos |
|
|
|
|
|
while (protoStart > protoMin && isSchemeChar(state.src.charCodeAt(protoStart - 1))) { |
|
|
|
|
|
protoStart-- |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (protoStart === pos || !isAsciiAlpha(state.src.charCodeAt(protoStart))) return false |
|
|
|
|
|
|
|
|
const proto = match[1] |
|
|
const protoLength = pos - protoStart |
|
|
|
|
|
|
|
|
const link = state.md.linkify.matchAtStart(state.src.slice(pos - proto.length)) |
|
|
const link = state.md.linkify.matchAtStart(state.src.slice(protoStart)) |
|
|
if (!link) return false |
|
|
if (!link) return false |
|
|
|
|
|
|
|
|
let url = link.url |
|
|
let url = link.url |
|
|
|
|
|
|
|
|
// invalid link, but still detected by linkify somehow;
|
|
|
// invalid link, but still detected by linkify somehow;
|
|
|
// need to check to prevent infinite loop below
|
|
|
// need to check to prevent infinite loop below
|
|
|
if (url.length <= proto.length) return false |
|
|
if (url.length <= protoLength) return false |
|
|
|
|
|
|
|
|
// disallow '*' at the end of the link (conflicts with emphasis)
|
|
|
// disallow '*' at the end of the link (conflicts with emphasis)
|
|
|
// do manual backsearch to avoid perf issues with regex /\*+$/ on "****...****a".
|
|
|
// do manual backsearch to avoid perf issues with regex /\*+$/ on "****...****a".
|
|
|
@ -45,7 +63,7 @@ export default function linkify (state: StateInline, silent: boolean): boolean { |
|
|
if (!state.md.validateLink(fullUrl)) return false |
|
|
if (!state.md.validateLink(fullUrl)) return false |
|
|
|
|
|
|
|
|
if (!silent) { |
|
|
if (!silent) { |
|
|
state.pending = state.pending.slice(0, -proto.length) |
|
|
state.pending = state.pending.slice(0, -protoLength) |
|
|
|
|
|
|
|
|
const token_o = state.push('link_open', 'a', 1) |
|
|
const token_o = state.push('link_open', 'a', 1) |
|
|
token_o.attrs = [['href', fullUrl]] |
|
|
token_o.attrs = [['href', fullUrl]] |
|
|
@ -60,6 +78,6 @@ export default function linkify (state: StateInline, silent: boolean): boolean { |
|
|
token_c.info = 'auto' |
|
|
token_c.info = 'auto' |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
state.pos += url.length - proto.length |
|
|
state.pos += url.length - protoLength |
|
|
return true |
|
|
return true |
|
|
} |
|
|
} |
|
|
|