Class: LinkCheck
  
  
  
Defined Under Namespace
  
    
  
    
      Classes: InternalLink, XpathFunctions
    
  
  
    
      Constant Summary
      collapse
    
    
      
        - SRI_REL_TYPES =
          
  
 
- %(stylesheet)
 
Instance Attribute Summary
  
  
  #element, #external_urls, #html, #internal_urls, #issues, #node, #options, #path, #src
  
    
      Instance Method Summary
      collapse
    
    
      
        - 
  
    
      #check_internal_link(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #check_schemes(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #check_sri(line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #external_link_check(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #find_fragments(html, fragment_ids)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #handle_hash(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #handle_mailto(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #handle_tel(link, line, content)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #hash_exists?(html, href_hash)  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #missing_href?  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #placeholder?  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    
  
- 
  
    
      #run  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
  
#create_nokogiri, #pluralize, #swap
  
  
  
  
  
  
  
  
  
  #add_issue, #add_to_external_urls, #add_to_internal_urls, #blank?, #create_element, #initialize, subchecks
  
    Instance Method Details
    
      
  
  
    #check_internal_link(link, line, content)  ⇒ Object 
  
  
  
  
    | 
68
69
70
71
72
73
74
75
76
77
78
79 | # File 'lib/html-proofer/check/links.rb', line 68
def check_internal_link(link, line, content)
  
  if link.unslashed_directory?(link.absolute_path)
    add_issue("internally linking to a directory #{link.absolute_path} without trailing slash", line: line, content: content)
    return false
  end
  
  return handle_hash(link, line, content) if link.hash
  true
end
 | 
 
    
      
  
  
    #check_schemes(link, line, content)  ⇒ Object 
  
  
  
  
    | 
81
82
83
84
85
86
87
88
89
90
91
92 | # File 'lib/html-proofer/check/links.rb', line 81
def check_schemes(link, line, content)
  case link.scheme
  when 'mailto'
    handle_mailto(link, line, content)
  when 'tel'
    handle_tel(link, line, content)
  when 'http'
    return unless @options[:enforce_https]
    add_issue("#{link.href} is not an HTTPS link", line: line, content: content)
  end
end
 | 
 
    
      
  
  
    #check_sri(line, content)  ⇒ Object 
  
  
  
  
    | 
151
152
153
154
155
156
157
158
159
160
161 | # File 'lib/html-proofer/check/links.rb', line 151
def check_sri(line, content)
  return unless SRI_REL_TYPES.include?(@link.rel)
  if !defined?(@link.integrity) && !defined?(@link.crossorigin)
    add_issue("SRI and CORS not provided in: #{@link.src}", line: line, content: content)
  elsif !defined?(@link.integrity)
    add_issue("Integrity is missing in: #{@link.src}", line: line, content: content)
  elsif !defined?(@link.crossorigin)
    add_issue("CORS not provided for external resource in: #{@link.src}", line: line, content: content)
  end
end
 | 
 
    
      
  
  
    #external_link_check(link, line, content)  ⇒ Object 
  
  
  
  
    | 
116
117
118
119
120
121
122
123
124
125 | # File 'lib/html-proofer/check/links.rb', line 116
def external_link_check(link, line, content)
  if link.exists? 
    target_html = create_nokogiri(link.absolute_path)
    return add_issue("linking to #{link.href}, but #{link.hash} does not exist", line: line, content: content) unless hash_exists?(target_html, link.hash)
  else
    return add_issue("trying to find hash of #{link.href}, but #{link.absolute_path} does not exist", line: line, content: content)
  end
  true
end
 | 
 
    
      
  
  
    #find_fragments(html, fragment_ids)  ⇒ Object 
  
  
  
  
    | 
134
135
136
137
138
139
140
141
142
143
144
145 | # File 'lib/html-proofer/check/links.rb', line 134
def find_fragments(html, fragment_ids)
  xpaths = fragment_ids.flat_map do |frag_id|
    escaped_frag_id = "'#{frag_id.split("'").join("', \"'\", '")}', ''"
    [
      "//*[case_sensitive_equals(@id, concat(#{escaped_frag_id}))]",
      "//*[case_sensitive_equals(@name, concat(#{escaped_frag_id}))]"
    ]
  end
  xpaths << XpathFunctions.new
  html.xpath(*xpaths)
end
 | 
 
    
      
  
  
    #handle_hash(link, line, content)  ⇒ Object 
  
  
  
  
    | 
106
107
108
109
110
111
112
113
114 | # File 'lib/html-proofer/check/links.rb', line 106
def handle_hash(link, line, content)
  if link.internal? && !hash_exists?(link.html, link.hash) 
    return add_issue("linking to internal hash ##{link.hash} that does not exist", line: line, content: content)
  elsif link.external?
    return external_link_check(link, line, content)
  end
  true
end
 | 
 
    
      
  
  
    #handle_mailto(link, line, content)  ⇒ Object 
  
  
  
  
    | 
94
95
96
97
98
99
100 | # File 'lib/html-proofer/check/links.rb', line 94
def handle_mailto(link, line, content)
  if link.path.empty?
    add_issue("#{link.href} contains no email address", line: line, content: content)
  elsif !link.path.include?('@')
    add_issue("#{link.href} contains an invalid email address", line: line, content: content)
  end
end
 | 
 
    
      
  
  
    #handle_tel(link, line, content)  ⇒ Object 
  
  
  
  
    | 
102
103
104 | # File 'lib/html-proofer/check/links.rb', line 102
def handle_tel(link, line, content)
  add_issue("#{link.href} contains no phone number", line: line, content: content) if link.path.empty?
end
 | 
 
    
      
  
  
    #hash_exists?(html, href_hash)  ⇒ Boolean 
  
  
  
  
    | 
127
128
129
130
131
132 | # File 'lib/html-proofer/check/links.rb', line 127
def hash_exists?(html, href_hash)
  decoded_href_hash = Addressable::URI.unescape(href_hash)
  fragment_ids = [href_hash, decoded_href_hash]
  
  fragment_ids.include?('top') || !find_fragments(html, fragment_ids).empty?
end
 | 
 
    
      
  
  
    #missing_href?  ⇒ Boolean 
  
  
  
  
    | 
6
7
8
9
10 | # File 'lib/html-proofer/check/links.rb', line 6
def missing_href?
  return blank?(@link.src) if @node.name == 'source'
  blank?(@link.href) && blank?(@link.name) && blank?(@link.id)
end
 | 
 
    
      
  
  
    #placeholder?  ⇒ Boolean 
  
  
  
  
    | 
12
13
14 | # File 'lib/html-proofer/check/links.rb', line 12
def placeholder?
  (!blank?(@link.id) || !blank?(@link.name)) && @link.href.nil?
end
 | 
 
    
      
  
  
    #run  ⇒ Object 
  
  
  
  
    | 
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 | # File 'lib/html-proofer/check/links.rb', line 16
def run
  @html.css('a, link, source').each do |node|
    @link = create_element(node)
    line = node.line
    content = node.to_s
    next if @link.ignore?
    next if placeholder?
    next if @link.allow_hash_href? && @link.href == '#'
    
    unless @link.valid?
      add_issue("#{@link.href} is an invalid URL", line: line, content: content)
      next
    end
    check_schemes(@link, line, content)
    
    if missing_href?
      next if @link.allow_missing_href?
      
      next if @html.internal_subset.nil? || (@html.internal_subset.name == 'html' && @html.internal_subset.external_id.nil?)
      add_issue('anchor has no href attribute', line: line, content: content)
      next
    end
    
    next if @link.non_http_remote?
    if !@link.internal? && @link.remote?
      check_sri(line, content) if @link.check_sri? && node.name == 'link'
      
      
      next if @link.respond_to?(:rel) && @link.rel == 'dns-prefetch'
      add_to_external_urls(@link.href || @link.src)
      next
    elsif @link.internal?
      if @link.exists?
        add_to_internal_urls(@link.href, InternalLink.new(@link, @path, line, content))
      else
        add_issue("internally linking to #{@link.href}, which does not exist", line: line, content: content)
      end
    end
  end
  external_urls
end
 |