150 lines
4.7 KiB
Ruby
150 lines
4.7 KiB
Ruby
# -*- coding: utf-8; frozen_string_literal: true -*-
|
|
#
|
|
#--
|
|
# Copyright (C) 2009-2026 Thomas Leitner <t_leitner@gmx.at>
|
|
#
|
|
# This file is part of kramdown which is licensed under the MIT.
|
|
#++
|
|
#
|
|
|
|
require 'kramdown/parser/kramdown/escaped_chars'
|
|
|
|
module Kramdown
|
|
module Parser
|
|
class Kramdown
|
|
|
|
# Normalize the link identifier.
|
|
def normalize_link_id(id)
|
|
id.gsub(/\s+/, ' ').downcase
|
|
end
|
|
|
|
LINK_DEFINITION_START = /^#{OPT_SPACE}\[([^\n\]]+)\]:[ \t]*(?:<(.*?)>|([^\n]*?\S[^\n]*?))(?:(?:[ \t]*?\n|[ \t]+?)[ \t]*?(["'])(.+?)\4)?[ \t]*?\n/
|
|
|
|
# Parse the link definition at the current location.
|
|
def parse_link_definition
|
|
return false if @src[3].to_s.match?(/[ \t]+["']/)
|
|
@src.pos += @src.matched_size
|
|
link_id, link_url, link_title = normalize_link_id(@src[1]), @src[2] || @src[3], @src[5]
|
|
if @link_defs[link_id]
|
|
warning("Duplicate link ID '#{link_id}' on line #{@src.current_line_number} - overwriting")
|
|
end
|
|
@tree.children << new_block_el(:eob, :link_def)
|
|
@link_defs[link_id] = [link_url, link_title, @tree.children.last]
|
|
true
|
|
end
|
|
define_parser(:link_definition, LINK_DEFINITION_START)
|
|
|
|
# This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
|
|
# and the element itself to the @tree.
|
|
def add_link(el, href, title, alt_text = nil, ial = nil)
|
|
el.options[:ial] = ial
|
|
update_attr_with_ial(el.attr, ial) if ial
|
|
if el.type == :a
|
|
el.attr['href'] = href
|
|
else
|
|
el.attr['src'] = href
|
|
el.attr['alt'] = alt_text
|
|
el.children.clear
|
|
end
|
|
el.attr['title'] = title if title
|
|
@tree.children << el
|
|
end
|
|
|
|
LINK_BRACKET_STOP_RE = /(\])|!?\[/
|
|
LINK_PAREN_STOP_RE = /(\()|(\))|\s(?=['"])/
|
|
LINK_INLINE_ID_RE = /\s*?\[([^\]]+)?\]/
|
|
LINK_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/m
|
|
LINK_START = /!?\[(?=[^^])/
|
|
|
|
# Parse the link at the current scanner position. This method is used to parse normal links as
|
|
# well as image links.
|
|
def parse_link
|
|
start_line_number = @src.current_line_number
|
|
result = @src.scan(LINK_START)
|
|
cur_pos = @src.pos
|
|
saved_pos = @src.save_pos
|
|
|
|
link_type = (result.match?(/^!/) ? :img : :a)
|
|
|
|
# no nested links allowed
|
|
if link_type == :a && (@tree.type == :img || @tree.type == :a ||
|
|
@stack.any? {|t, _| t && (t.type == :img || t.type == :a) })
|
|
add_text(result)
|
|
return
|
|
end
|
|
el = Element.new(link_type, nil, nil, location: start_line_number)
|
|
|
|
count = 1
|
|
found = parse_spans(el, LINK_BRACKET_STOP_RE) do
|
|
count += (@src[1] ? -1 : 1)
|
|
count - el.children.count {|c| c.type == :img } == 0
|
|
end
|
|
unless found
|
|
@src.revert_pos(saved_pos)
|
|
add_text(result)
|
|
return
|
|
end
|
|
alt_text = extract_string(cur_pos...@src.pos, @src).gsub(ESCAPED_CHARS, '\1')
|
|
@src.scan(LINK_BRACKET_STOP_RE)
|
|
|
|
# reference style link or no link url
|
|
if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
|
|
emit_warning = !@src[1]
|
|
link_id = normalize_link_id(@src[1] || alt_text)
|
|
if @link_defs.key?(link_id)
|
|
link_def = @link_defs[link_id]
|
|
add_link(el, link_def[0], link_def[1], alt_text,
|
|
link_def[2] && link_def[2].options[:ial])
|
|
else
|
|
if emit_warning
|
|
warning("No link definition for link ID '#{link_id}' found on line #{start_line_number}")
|
|
end
|
|
@src.revert_pos(saved_pos)
|
|
add_text(result)
|
|
end
|
|
return
|
|
end
|
|
|
|
# link url in parentheses
|
|
if @src.scan(/\(<(.*?)>/)
|
|
link_url = @src[1]
|
|
if @src.scan(/\)/)
|
|
add_link(el, link_url, nil, alt_text)
|
|
return
|
|
end
|
|
else
|
|
link_url = +''
|
|
nr_of_brackets = 0
|
|
while (temp = @src.scan_until(LINK_PAREN_STOP_RE))
|
|
link_url << temp
|
|
if @src[2]
|
|
nr_of_brackets -= 1
|
|
break if nr_of_brackets == 0
|
|
elsif @src[1]
|
|
nr_of_brackets += 1
|
|
else
|
|
break
|
|
end
|
|
end
|
|
link_url = link_url[1..-2]
|
|
link_url.strip!
|
|
|
|
if nr_of_brackets == 0
|
|
add_link(el, link_url, nil, alt_text)
|
|
return
|
|
end
|
|
end
|
|
|
|
if @src.scan(LINK_INLINE_TITLE_RE)
|
|
add_link(el, link_url, @src[2], alt_text)
|
|
else
|
|
@src.revert_pos(saved_pos)
|
|
add_text(result)
|
|
end
|
|
end
|
|
define_parser(:link, LINK_START, '!?\[')
|
|
|
|
end
|
|
end
|
|
end
|