hatemile 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +46 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE +202 -0
  5. data/Rakefile +64 -0
  6. data/hatemile.gemspec +37 -0
  7. data/lib/hatemile/accessible_association.rb +62 -0
  8. data/lib/hatemile/accessible_css.rb +43 -0
  9. data/lib/hatemile/accessible_display.rb +178 -0
  10. data/lib/hatemile/accessible_event.rb +95 -0
  11. data/lib/hatemile/accessible_form.rb +101 -0
  12. data/lib/hatemile/accessible_navigation.rb +82 -0
  13. data/lib/hatemile/helper.rb +58 -0
  14. data/lib/hatemile/implementation/accessible_association_implementation.rb +346 -0
  15. data/lib/hatemile/implementation/accessible_css_implementation.rb +772 -0
  16. data/lib/hatemile/implementation/accessible_display_implementation.rb +1362 -0
  17. data/lib/hatemile/implementation/accessible_event_implementation.rb +278 -0
  18. data/lib/hatemile/implementation/accessible_form_implementation.rb +386 -0
  19. data/lib/hatemile/implementation/accessible_navigation_implementation.rb +561 -0
  20. data/lib/hatemile/util/common_functions.rb +106 -0
  21. data/lib/hatemile/util/configure.rb +92 -0
  22. data/lib/hatemile/util/css/rcp/rcp_declaration.rb +77 -0
  23. data/lib/hatemile/util/css/rcp/rcp_parser.rb +115 -0
  24. data/lib/hatemile/util/css/rcp/rcp_rule.rb +86 -0
  25. data/lib/hatemile/util/css/style_sheet_declaration.rb +59 -0
  26. data/lib/hatemile/util/css/style_sheet_parser.rb +43 -0
  27. data/lib/hatemile/util/css/style_sheet_rule.rb +73 -0
  28. data/lib/hatemile/util/html/html_dom_element.rb +234 -0
  29. data/lib/hatemile/util/html/html_dom_node.rb +131 -0
  30. data/lib/hatemile/util/html/html_dom_parser.rb +150 -0
  31. data/lib/hatemile/util/html/html_dom_text_node.rb +43 -0
  32. data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_element.rb +302 -0
  33. data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_node.rb +112 -0
  34. data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_parser.rb +208 -0
  35. data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_text_node.rb +83 -0
  36. data/lib/hatemile/util/id_generator.rb +53 -0
  37. data/lib/js/common.js +98 -0
  38. data/lib/js/eventlistener.js +36 -0
  39. data/lib/js/include.js +292 -0
  40. data/lib/js/scriptlist_validation_fields.js +13 -0
  41. data/lib/js/validation.js +205 -0
  42. data/lib/locale/en-US.yml +388 -0
  43. data/lib/locale/pt-BR.yml +389 -0
  44. data/lib/skippers.xml +6 -0
  45. data/lib/symbols.xml +40 -0
  46. data/test/locale/en-US.yml +5 -0
  47. data/test/locale/pt-BR.yml +4 -0
  48. data/test/test_accessible_association_implementation.rb +258 -0
  49. data/test/test_accessible_css_implementation.rb +518 -0
  50. data/test/test_accessible_display_implementation.rb +873 -0
  51. data/test/test_accessible_form_implementation.rb +283 -0
  52. data/test/test_accessible_navigation_implementation.rb +228 -0
  53. data/test/test_common_functions.rb +128 -0
  54. data/test/test_configure.rb +73 -0
  55. data/test/test_nokogiri_html_dom_element.rb +586 -0
  56. data/test/test_nokogiri_html_dom_parser.rb +363 -0
  57. data/test/test_nokogiri_html_dom_text_node.rb +225 -0
  58. data/test/test_rcp_declaration.rb +103 -0
  59. data/test/test_rcp_parser.rb +86 -0
  60. data/test/test_rcp_rule.rb +89 -0
  61. metadata +199 -0
@@ -0,0 +1,561 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'rexml/document'
14
+ require File.join(File.dirname(File.dirname(__FILE__)), 'accessible_navigation')
15
+ require File.join(File.dirname(File.dirname(__FILE__)), 'helper')
16
+ require File.join(
17
+ File.dirname(File.dirname(__FILE__)),
18
+ 'util',
19
+ 'common_functions'
20
+ )
21
+ require File.join(File.dirname(File.dirname(__FILE__)), 'util', 'configure')
22
+ require File.join(File.dirname(File.dirname(__FILE__)), 'util', 'id_generator')
23
+ require File.join(
24
+ File.dirname(File.dirname(__FILE__)),
25
+ 'util',
26
+ 'html',
27
+ 'html_dom_parser'
28
+ )
29
+
30
+ ##
31
+ # The Hatemile module contains the interfaces with the acessibility solutions.
32
+ module Hatemile
33
+ ##
34
+ # The Hatemile::Implementation module contains the official implementation of
35
+ # interfaces solutions.
36
+ module Implementation
37
+ ##
38
+ # The AccessibleNavigationImplementation class is official implementation of
39
+ # AccessibleNavigation interface.
40
+ class AccessibleNavigationImplementation < AccessibleNavigation
41
+ public_class_method :new
42
+
43
+ ##
44
+ # The id of list element that contains the skippers.
45
+ ID_CONTAINER_SKIPPERS = 'container-skippers'.freeze
46
+
47
+ ##
48
+ # The id of list element that contains the links for the headings, before
49
+ # the whole content of page.
50
+ ID_CONTAINER_HEADING_BEFORE = 'container-heading-before'.freeze
51
+
52
+ ##
53
+ # The id of list element that contains the links for the headings, after
54
+ # the whole content of page.
55
+ ID_CONTAINER_HEADING_AFTER = 'container-heading-after'.freeze
56
+
57
+ ##
58
+ # The HTML class of text of description of container of heading links.
59
+ CLASS_TEXT_HEADING = 'text-heading'.freeze
60
+
61
+ ##
62
+ # The HTML class of anchor of skipper.
63
+ CLASS_SKIPPER_ANCHOR = 'skipper-anchor'.freeze
64
+
65
+ ##
66
+ # The HTML class of anchor of heading link.
67
+ CLASS_HEADING_ANCHOR = 'heading-anchor'.freeze
68
+
69
+ ##
70
+ # The HTML class of force link, before it.
71
+ CLASS_FORCE_LINK_BEFORE = 'force-link-before'.freeze
72
+
73
+ ##
74
+ # The HTML class of force link, after it.
75
+ CLASS_FORCE_LINK_AFTER = 'force-link-after'.freeze
76
+
77
+ ##
78
+ # The name of attribute that links the anchor of skipper with the element.
79
+ DATA_ANCHOR_FOR = 'data-anchorfor'.freeze
80
+
81
+ ##
82
+ # The name of attribute that links the anchor of heading link with
83
+ # heading.
84
+ DATA_HEADING_ANCHOR_FOR = 'data-headinganchorfor'.freeze
85
+
86
+ ##
87
+ # The name of attribute that indicates the level of heading of link.
88
+ DATA_HEADING_LEVEL = 'data-headinglevel'.freeze
89
+
90
+ ##
91
+ # The name of attribute that link the anchor of long description with the
92
+ # image.
93
+ DATA_ATTRIBUTE_LONG_DESCRIPTION_OF =
94
+ 'data-attributelongdescriptionof'.freeze
95
+
96
+ protected
97
+
98
+ ##
99
+ # Generate the list of skippers of page.
100
+ #
101
+ # @return [Hatemile::Util::Html::HTMLDOMElement] The list of skippers of
102
+ # page.
103
+ def generate_list_skippers
104
+ container = @parser.find("##{ID_CONTAINER_SKIPPERS}").first_result
105
+ html_list = nil
106
+ if container.nil?
107
+ local = @parser.find('body').first_result
108
+ unless local.nil?
109
+ container = @parser.create_element('div')
110
+ container.set_attribute('id', ID_CONTAINER_SKIPPERS)
111
+ local.prepend_element(container)
112
+ end
113
+ end
114
+ unless container.nil?
115
+ html_list = @parser.find(container).find_children('ul').first_result
116
+ if html_list.nil?
117
+ html_list = @parser.create_element('ul')
118
+ container.append_element(html_list)
119
+ end
120
+ end
121
+ @list_skippers_added = true
122
+
123
+ html_list
124
+ end
125
+
126
+ ##
127
+ # Generate the list of heading links of page.
128
+ def generate_list_heading
129
+ local = @parser.find('body').first_result
130
+
131
+ return if local.nil?
132
+
133
+ container_before = @parser.find(
134
+ "##{ID_CONTAINER_HEADING_BEFORE}"
135
+ ).first_result
136
+ if container_before.nil? && !@elements_heading_before.empty?
137
+ container_before = @parser.create_element('div')
138
+ container_before.set_attribute('id', ID_CONTAINER_HEADING_BEFORE)
139
+
140
+ text_container_before = @parser.create_element('span')
141
+ text_container_before.set_attribute('class', CLASS_TEXT_HEADING)
142
+ text_container_before.append_text(@elements_heading_before)
143
+
144
+ container_before.append_element(text_container_before)
145
+ local.prepend_element(container_before)
146
+ end
147
+ unless container_before.nil?
148
+ @list_heading_before = @parser.find(
149
+ container_before
150
+ ).find_children('ol').first_result
151
+ if @list_heading_before.nil?
152
+ @list_heading_before = @parser.create_element('ol')
153
+ container_before.append_element(@list_heading_before)
154
+ end
155
+ end
156
+
157
+ container_after = @parser.find(
158
+ "##{ID_CONTAINER_HEADING_AFTER}"
159
+ ).first_result
160
+ if container_after.nil? && !@elements_heading_after.empty?
161
+ container_after = @parser.create_element('div')
162
+ container_after.set_attribute('id', ID_CONTAINER_HEADING_AFTER)
163
+
164
+ text_container_after = @parser.create_element('span')
165
+ text_container_after.set_attribute('class', CLASS_TEXT_HEADING)
166
+ text_container_after.append_text(@elements_heading_after)
167
+
168
+ container_after.append_element(text_container_after)
169
+ local.append_element(container_after)
170
+ end
171
+ unless container_after.nil?
172
+ @list_heading_after = @parser.find(
173
+ container_after
174
+ ).find_children('ol').first_result
175
+ if @list_heading_after.nil?
176
+ @list_heading_after = @parser.create_element('ol')
177
+ container_after.append_element(@list_heading_after)
178
+ end
179
+ end
180
+
181
+ @list_heading_added = true
182
+ end
183
+
184
+ ##
185
+ # Returns the level of heading.
186
+ #
187
+ # @param element [Hatemile::Util::Html::HTMLDOMElement] The heading.
188
+ # @return [Integer] The level of heading.
189
+ def get_heading_level(element)
190
+ tag = element.get_tag_name
191
+ return 1 if tag == 'H1'
192
+ return 2 if tag == 'H2'
193
+ return 3 if tag == 'H3'
194
+ return 4 if tag == 'H4'
195
+ return 5 if tag == 'H5'
196
+ return 6 if tag == 'H6'
197
+ -1
198
+ end
199
+
200
+ ##
201
+ # Check that the headings of page are sintatic correct.
202
+ #
203
+ # @return [Boolean] True if the headings of page are sintatic correct or
204
+ # false if not.
205
+ def valid_heading?
206
+ elements = @parser.find('h1,h2,h3,h4,h5,h6').list_results
207
+ last_level = 0
208
+ count_main_heading = 0
209
+ @validate_heading = true
210
+ elements.each do |element|
211
+ level = get_heading_level(element)
212
+ if level == 1
213
+ return false if count_main_heading == 1
214
+ count_main_heading = 1
215
+ end
216
+ return false if (level - last_level) > 1
217
+ last_level = level
218
+ end
219
+ true
220
+ end
221
+
222
+ ##
223
+ # Generate an anchor for the element.
224
+ #
225
+ # @param element [Hatemile::Util::Html::HTMLDOMElement] The element.
226
+ # @param data_attribute [String] The name of attribute that links the
227
+ # element with the anchor.
228
+ # @param anchor_class [String] The HTML class of anchor.
229
+ # @return [Hatemile::Util::Html::HTMLDOMElement] The anchor.
230
+ def generate_anchor_for(element, data_attribute, anchor_class)
231
+ @id_generator.generate_id(element)
232
+ anchor = nil
233
+ if @parser.find(
234
+ "[#{data_attribute}=\"#{element.get_attribute('id')}\"]"
235
+ ).first_result.nil?
236
+ if element.get_tag_name == 'A'
237
+ anchor = element
238
+ else
239
+ anchor = @parser.create_element('a')
240
+ @id_generator.generate_id(anchor)
241
+ anchor.set_attribute('class', anchor_class)
242
+ element.insert_before(anchor)
243
+ end
244
+ unless anchor.has_attribute?('name')
245
+ anchor.set_attribute('name', anchor.get_attribute('id'))
246
+ end
247
+ anchor.set_attribute(data_attribute, element.get_attribute('id'))
248
+ end
249
+ anchor
250
+ end
251
+
252
+ ##
253
+ # Replace the shortcut of elements, that has the shortcut passed.
254
+ #
255
+ # @param shortcut [String] The shortcut.
256
+ # @return [void]
257
+ def free_shortcut(shortcut)
258
+ found = false
259
+ alpha_numbers = '1234567890abcdefghijklmnopqrstuvwxyz'
260
+ elements = @parser.find('[accesskey]').list_results
261
+ elements.each do |element|
262
+ shortcuts = element.get_attribute('accesskey').downcase
263
+
264
+ unless Hatemile::Util::CommonFunctions.in_list?(shortcuts, shortcut)
265
+ next
266
+ end
267
+
268
+ (0..alpha_numbers.length - 1).each do |i|
269
+ key = alpha_numbers[i, i + 1]
270
+ found = true
271
+ elements.each do |element_with_shortcuts|
272
+ shortcuts = element_with_shortcuts.get_attribute(
273
+ 'accesskey'
274
+ ).downcase
275
+
276
+ unless Hatemile::Util::CommonFunctions.in_list?(shortcuts, key)
277
+ next
278
+ end
279
+
280
+ element.set_attribute('accesskey', key)
281
+ found = false
282
+ break
283
+ end
284
+ break if found
285
+ end
286
+ break if found
287
+ end
288
+ end
289
+
290
+ ##
291
+ # Returns the skippers of configuration.
292
+ #
293
+ #
294
+ # @param configure [Hatemile::Util::Configure] The configuration of
295
+ # HaTeMiLe.
296
+ # @param file_name [String] The file path of skippers configuration.
297
+ # @return [Array<Hash>] The skippers of configuration.
298
+ def get_skippers(configure, file_name)
299
+ skippers = []
300
+ if file_name.nil?
301
+ file_name = File.join(
302
+ File.dirname(File.dirname(File.dirname(__FILE__))),
303
+ 'skippers.xml'
304
+ )
305
+ end
306
+ document = REXML::Document.new(File.read(file_name))
307
+ document.elements.each('skippers/skipper') do |skipper_xml|
308
+ skipper = {}
309
+ skipper[:selector] = skipper_xml.attribute('selector').value
310
+ skipper[:description] = configure.get_parameter(
311
+ skipper_xml.attribute('description').value
312
+ )
313
+ skipper[:shortcut] = skipper_xml.attribute('shortcut').value
314
+ skippers.push(skipper)
315
+ end
316
+ skippers
317
+ end
318
+
319
+ public
320
+
321
+ ##
322
+ # Initializes a new object that manipulate the accessibility of the
323
+ # navigation of parser.
324
+ #
325
+ # @param parser [Hatemile::Util::Html::HTMLDOMParser] The HTML parser.
326
+ # @param configure [Hatemile::Util::Configure] The configuration of
327
+ # HaTeMiLe.
328
+ # @param skipper_file_name [String] The file path of skippers
329
+ # configuration.
330
+ def initialize(parser, configure, skipper_file_name = nil)
331
+ Hatemile::Helper.require_not_nil(parser, configure)
332
+ Hatemile::Helper.require_valid_type(
333
+ parser,
334
+ Hatemile::Util::Html::HTMLDOMParser
335
+ )
336
+ Hatemile::Helper.require_valid_type(
337
+ configure,
338
+ Hatemile::Util::Configure
339
+ )
340
+ Hatemile::Helper.require_valid_type(skipper_file_name, String)
341
+
342
+ @parser = parser
343
+ @id_generator = Hatemile::Util::IDGenerator.new('navigation')
344
+ @elements_heading_before = configure.get_parameter(
345
+ 'elements-heading-before'
346
+ )
347
+ @elements_heading_after = configure.get_parameter(
348
+ 'elements-heading-after'
349
+ )
350
+ @attribute_long_description_prefix_before = configure.get_parameter(
351
+ 'attribute-longdescription-prefix-before'
352
+ )
353
+ @attribute_long_description_suffix_before = configure.get_parameter(
354
+ 'attribute-longdescription-suffix-before'
355
+ )
356
+ @attribute_long_description_prefix_after = configure.get_parameter(
357
+ 'attribute-longdescription-prefix-after'
358
+ )
359
+ @attribute_long_description_suffix_after = configure.get_parameter(
360
+ 'attribute-longdescription-suffix-after'
361
+ )
362
+ @skippers = get_skippers(configure, skipper_file_name)
363
+ @list_skippers_added = false
364
+ @list_heading_added = false
365
+ @validate_heading = false
366
+ @valid_heading = false
367
+ @list_skippers = nil
368
+ @list_heading_before = nil
369
+ @list_heading_after = nil
370
+ end
371
+
372
+ ##
373
+ # @see Hatemile::AccessibleNavigation#provide_navigation_by_skipper
374
+ def provide_navigation_by_skipper(element)
375
+ skipper = nil
376
+ @skippers.each do |auxiliar_skipper|
377
+ elements = @parser.find(auxiliar_skipper[:selector]).list_results
378
+ if elements.include?(element)
379
+ skipper = auxiliar_skipper
380
+ break
381
+ end
382
+ end
383
+
384
+ return if skipper.nil?
385
+
386
+ @list_skippers = generate_list_skippers unless @list_skippers_added
387
+
388
+ return if @list_skippers.nil?
389
+
390
+ anchor = generate_anchor_for(
391
+ element,
392
+ DATA_ANCHOR_FOR,
393
+ CLASS_SKIPPER_ANCHOR
394
+ )
395
+
396
+ return if anchor.nil?
397
+
398
+ item_link = @parser.create_element('li')
399
+ link = @parser.create_element('a')
400
+ link.set_attribute('href', "##{anchor.get_attribute('name')}")
401
+ link.append_text(skipper[:description])
402
+
403
+ free_shortcut(skipper[:shortcut])
404
+ link.set_attribute('accesskey', skipper[:shortcut])
405
+
406
+ @id_generator.generate_id(link)
407
+
408
+ item_link.append_element(link)
409
+ @list_skippers.append_element(item_link)
410
+ end
411
+
412
+ ##
413
+ # @see Hatemile::AccessibleNavigation#provide_navigation_by_all_skippers
414
+ def provide_navigation_by_all_skippers
415
+ @skippers.each do |skipper|
416
+ elements = @parser.find(skipper[:selector]).list_results
417
+ elements.each do |element|
418
+ next unless Hatemile::Util::CommonFunctions.is_valid_element?(
419
+ element
420
+ )
421
+
422
+ provide_navigation_by_skipper(element)
423
+ end
424
+ end
425
+ end
426
+
427
+ ##
428
+ # @see Hatemile::AccessibleNavigation#provide_navigation_by_heading
429
+ def provide_navigation_by_heading(heading)
430
+ @valid_heading = valid_heading? unless @validate_heading
431
+
432
+ return unless @valid_heading
433
+
434
+ anchor = generate_anchor_for(
435
+ heading,
436
+ DATA_HEADING_ANCHOR_FOR,
437
+ CLASS_HEADING_ANCHOR
438
+ )
439
+
440
+ return if anchor.nil?
441
+
442
+ generate_list_heading unless @list_heading_added
443
+ list_before = nil
444
+ list_after = nil
445
+ level = get_heading_level(heading)
446
+ if level == 1
447
+ list_before = @list_heading_before
448
+ list_after = @list_heading_after
449
+ else
450
+ selector = "[#{DATA_HEADING_LEVEL}=\"#{level - 1}\"]"
451
+ unless @list_heading_before.nil?
452
+ super_item_before = @parser.find(
453
+ @list_heading_before
454
+ ).find_descendants(selector).last_result
455
+ end
456
+ unless super_item_before.nil?
457
+ list_before = @parser.find(
458
+ super_item_before
459
+ ).find_children('ol').first_result
460
+ if list_before.nil?
461
+ list_before = @parser.create_element('ol')
462
+ super_item_before.append_element(list_before)
463
+ end
464
+ end
465
+ unless @list_heading_after.nil?
466
+ super_item_after = @parser.find(
467
+ @list_heading_after
468
+ ).find_descendants(selector).last_result
469
+ end
470
+ unless super_item_after.nil?
471
+ list_after = @parser.find(
472
+ super_item_after
473
+ ).find_children('ol').first_result
474
+ if list_after.nil?
475
+ list_after = @parser.create_element('ol')
476
+ super_item_after.append_element(list_after)
477
+ end
478
+ end
479
+ end
480
+
481
+ item = @parser.create_element('li')
482
+ item.set_attribute(DATA_HEADING_LEVEL, level.to_s)
483
+
484
+ link = @parser.create_element('a')
485
+ link.set_attribute('href', "##{anchor.get_attribute('name')}")
486
+ link.append_text(heading.get_text_content)
487
+ item.append_element(link)
488
+
489
+ list_before.append_element(item.clone_element) unless list_before.nil?
490
+ list_after.append_element(item.clone_element) unless list_after.nil?
491
+ end
492
+
493
+ ##
494
+ # @see Hatemile::AccessibleNavigation#provide_navigation_by_all_headings
495
+ def provide_navigation_by_all_headings
496
+ headings = @parser.find('h1,h2,h3,h4,h5,h6').list_results
497
+ headings.each do |heading|
498
+ if Hatemile::Util::CommonFunctions.is_valid_element?(heading)
499
+ provide_navigation_by_heading(heading)
500
+ end
501
+ end
502
+ end
503
+
504
+ ##
505
+ # @see Hatemile::AccessibleNavigation#provide_navigation_to_long_description
506
+ def provide_navigation_to_long_description(image)
507
+ unless image.has_attribute?('longdesc') && image.has_attribute?('alt')
508
+ return
509
+ end
510
+
511
+ @id_generator.generate_id(image)
512
+ id = image.get_attribute('id')
513
+
514
+ selector = "[#{DATA_ATTRIBUTE_LONG_DESCRIPTION_OF}=\"#{id}\"]"
515
+ return unless @parser.find(selector).first_result.nil?
516
+
517
+ alternative_text = image.get_attribute('alt').gsub(
518
+ /[ \n\t\r]+/,
519
+ ' '
520
+ ).strip
521
+ unless @attribute_long_description_prefix_before.empty? &&
522
+ @attribute_long_description_suffix_before.empty?
523
+ before_text = "#{@attribute_long_description_prefix_before}" \
524
+ "#{alternative_text}" \
525
+ "#{@attribute_long_description_suffix_before}"
526
+ before_anchor = @parser.create_element('a')
527
+ before_anchor.set_attribute('href', image.get_attribute('longdesc'))
528
+ before_anchor.set_attribute('target', '_blank')
529
+ before_anchor.set_attribute(DATA_ATTRIBUTE_LONG_DESCRIPTION_OF, id)
530
+ before_anchor.set_attribute('class', CLASS_FORCE_LINK_BEFORE)
531
+ before_anchor.append_text(before_text)
532
+ image.insert_after(before_anchor)
533
+ end
534
+ unless @attribute_long_description_prefix_after.empty? &&
535
+ @attribute_long_description_suffix_after.empty?
536
+ after_text = "#{@attribute_long_description_prefix_after}" \
537
+ "#{alternative_text}" \
538
+ "#{@attribute_long_description_suffix_after}"
539
+ after_anchor = @parser.create_element('a')
540
+ after_anchor.set_attribute('href', image.get_attribute('longdesc'))
541
+ after_anchor.set_attribute('target', '_blank')
542
+ after_anchor.set_attribute(DATA_ATTRIBUTE_LONG_DESCRIPTION_OF, id)
543
+ after_anchor.set_attribute('class', CLASS_FORCE_LINK_AFTER)
544
+ after_anchor.append_text(after_text)
545
+ image.insert_after(after_anchor)
546
+ end
547
+ end
548
+
549
+ ##
550
+ # @see Hatemile::AccessibleNavigation#provide_navigation_to_all_long_descriptions
551
+ def provide_navigation_to_all_long_descriptions
552
+ images = @parser.find('[alt][longdesc]').list_results
553
+ images.each do |image|
554
+ if Hatemile::Util::CommonFunctions.is_valid_element?(image)
555
+ provide_navigation_to_long_description(image)
556
+ end
557
+ end
558
+ end
559
+ end
560
+ end
561
+ end