hatemile 2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +9 -0
- data/LICENSE +202 -0
- data/Rakefile +64 -0
- data/hatemile.gemspec +37 -0
- data/lib/hatemile/accessible_association.rb +62 -0
- data/lib/hatemile/accessible_css.rb +43 -0
- data/lib/hatemile/accessible_display.rb +178 -0
- data/lib/hatemile/accessible_event.rb +95 -0
- data/lib/hatemile/accessible_form.rb +101 -0
- data/lib/hatemile/accessible_navigation.rb +82 -0
- data/lib/hatemile/helper.rb +58 -0
- data/lib/hatemile/implementation/accessible_association_implementation.rb +346 -0
- data/lib/hatemile/implementation/accessible_css_implementation.rb +772 -0
- data/lib/hatemile/implementation/accessible_display_implementation.rb +1362 -0
- data/lib/hatemile/implementation/accessible_event_implementation.rb +278 -0
- data/lib/hatemile/implementation/accessible_form_implementation.rb +386 -0
- data/lib/hatemile/implementation/accessible_navigation_implementation.rb +561 -0
- data/lib/hatemile/util/common_functions.rb +106 -0
- data/lib/hatemile/util/configure.rb +92 -0
- data/lib/hatemile/util/css/rcp/rcp_declaration.rb +77 -0
- data/lib/hatemile/util/css/rcp/rcp_parser.rb +115 -0
- data/lib/hatemile/util/css/rcp/rcp_rule.rb +86 -0
- data/lib/hatemile/util/css/style_sheet_declaration.rb +59 -0
- data/lib/hatemile/util/css/style_sheet_parser.rb +43 -0
- data/lib/hatemile/util/css/style_sheet_rule.rb +73 -0
- data/lib/hatemile/util/html/html_dom_element.rb +234 -0
- data/lib/hatemile/util/html/html_dom_node.rb +131 -0
- data/lib/hatemile/util/html/html_dom_parser.rb +150 -0
- data/lib/hatemile/util/html/html_dom_text_node.rb +43 -0
- data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_element.rb +302 -0
- data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_node.rb +112 -0
- data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_parser.rb +208 -0
- data/lib/hatemile/util/html/nokogiri/nokogiri_html_dom_text_node.rb +83 -0
- data/lib/hatemile/util/id_generator.rb +53 -0
- data/lib/js/common.js +98 -0
- data/lib/js/eventlistener.js +36 -0
- data/lib/js/include.js +292 -0
- data/lib/js/scriptlist_validation_fields.js +13 -0
- data/lib/js/validation.js +205 -0
- data/lib/locale/en-US.yml +388 -0
- data/lib/locale/pt-BR.yml +389 -0
- data/lib/skippers.xml +6 -0
- data/lib/symbols.xml +40 -0
- data/test/locale/en-US.yml +5 -0
- data/test/locale/pt-BR.yml +4 -0
- data/test/test_accessible_association_implementation.rb +258 -0
- data/test/test_accessible_css_implementation.rb +518 -0
- data/test/test_accessible_display_implementation.rb +873 -0
- data/test/test_accessible_form_implementation.rb +283 -0
- data/test/test_accessible_navigation_implementation.rb +228 -0
- data/test/test_common_functions.rb +128 -0
- data/test/test_configure.rb +73 -0
- data/test/test_nokogiri_html_dom_element.rb +586 -0
- data/test/test_nokogiri_html_dom_parser.rb +363 -0
- data/test/test_nokogiri_html_dom_text_node.rb +225 -0
- data/test/test_rcp_declaration.rb +103 -0
- data/test/test_rcp_parser.rb +86 -0
- data/test/test_rcp_rule.rb +89 -0
- metadata +199 -0
@@ -0,0 +1,1362 @@
|
|
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_display')
|
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 AccessibleDisplayImplementation class is official implementation of
|
39
|
+
# Hatemile::AccessibleDisplay for screen readers.
|
40
|
+
class AccessibleDisplayImplementation < AccessibleDisplay
|
41
|
+
public_class_method :new
|
42
|
+
|
43
|
+
##
|
44
|
+
# The id of list element that contains the description of shortcuts,
|
45
|
+
# before the whole content of page.
|
46
|
+
ID_CONTAINER_SHORTCUTS_BEFORE = 'container-shortcuts-before'.freeze
|
47
|
+
|
48
|
+
##
|
49
|
+
# The id of list element that contains the description of shortcuts, after
|
50
|
+
# the whole content of page.
|
51
|
+
ID_CONTAINER_SHORTCUTS_AFTER = 'container-shortcuts-after'.freeze
|
52
|
+
|
53
|
+
##
|
54
|
+
# The HTML class of text of description of container of shortcuts
|
55
|
+
# descriptions.
|
56
|
+
CLASS_TEXT_SHORTCUTS = 'text-shortcuts'.freeze
|
57
|
+
|
58
|
+
##
|
59
|
+
# The HTML class of content to force the screen reader show the current
|
60
|
+
# state of element, before it.
|
61
|
+
CLASS_FORCE_READ_BEFORE = 'force-read-before'.freeze
|
62
|
+
|
63
|
+
##
|
64
|
+
# The HTML class of content to force the screen reader show the current
|
65
|
+
# state of element, after it.
|
66
|
+
CLASS_FORCE_READ_AFTER = 'force-read-after'.freeze
|
67
|
+
|
68
|
+
##
|
69
|
+
# The name of attribute that links the content of autocomplete state of
|
70
|
+
# field.
|
71
|
+
DATA_ARIA_AUTOCOMPLETE_OF = 'data-ariaautocompleteof'.freeze
|
72
|
+
|
73
|
+
##
|
74
|
+
# The name of attribute that links the content of busy state of element.
|
75
|
+
DATA_ARIA_BUSY_OF = 'data-ariabusyof'.freeze
|
76
|
+
|
77
|
+
##
|
78
|
+
# The name of attribute that links the content of checked state field.
|
79
|
+
DATA_ARIA_CHECKED_OF = 'data-ariacheckedof'.freeze
|
80
|
+
|
81
|
+
##
|
82
|
+
# The name of attribute that links the content of drop effect state of
|
83
|
+
# element.
|
84
|
+
DATA_ARIA_DROPEFFECT_OF = 'data-ariadropeffectof'.freeze
|
85
|
+
|
86
|
+
##
|
87
|
+
# The name of attribute that links the content of expanded state of
|
88
|
+
# element.
|
89
|
+
DATA_ARIA_EXPANDED_OF = 'data-ariaexpandedof'.freeze
|
90
|
+
|
91
|
+
##
|
92
|
+
# The name of attribute that links the content of grabbed state of
|
93
|
+
# element.
|
94
|
+
DATA_ARIA_GRABBED_OF = 'data-ariagrabbedof'.freeze
|
95
|
+
|
96
|
+
##
|
97
|
+
# The name of attribute that links the content that show if the field has
|
98
|
+
# popup.
|
99
|
+
DATA_ARIA_HASPOPUP_OF = 'data-ariahaspopupof'.freeze
|
100
|
+
|
101
|
+
##
|
102
|
+
# The name of attribute that links the content of level state of element.
|
103
|
+
DATA_ARIA_LEVEL_OF = 'data-arialevelof'.freeze
|
104
|
+
|
105
|
+
##
|
106
|
+
# The name of attribute that links the content of orientation state of
|
107
|
+
# element.
|
108
|
+
DATA_ARIA_ORIENTATION_OF = 'data-ariaorientationof'.freeze
|
109
|
+
|
110
|
+
##
|
111
|
+
# The name of attribute that links the content of pressed state of field.
|
112
|
+
DATA_ARIA_PRESSED_OF = 'data-ariapressedof'.freeze
|
113
|
+
|
114
|
+
##
|
115
|
+
# The name of attribute that links the content of minimum range state of
|
116
|
+
# field.
|
117
|
+
DATA_ARIA_RANGE_MIN_OF = 'data-attributevalueminof'.freeze
|
118
|
+
|
119
|
+
##
|
120
|
+
# The name of attribute that links the content of maximum range state of
|
121
|
+
# field.
|
122
|
+
DATA_ARIA_RANGE_MAX_OF = 'data-attributevaluemaxof'.freeze
|
123
|
+
|
124
|
+
##
|
125
|
+
# The name of attribute that links the content of required state of field.
|
126
|
+
DATA_ARIA_REQUIRED_OF = 'data-attributerequiredof'.freeze
|
127
|
+
|
128
|
+
##
|
129
|
+
# The name of attribute that links the content of selected state of field.
|
130
|
+
DATA_ARIA_SELECTED_OF = 'data-ariaselectedof'.freeze
|
131
|
+
|
132
|
+
##
|
133
|
+
# The name of attribute that links the content of sort state of element.
|
134
|
+
DATA_ARIA_SORT_OF = 'data-ariasortof'.freeze
|
135
|
+
|
136
|
+
##
|
137
|
+
# The name of attribute that links the description of shortcut of element.
|
138
|
+
DATA_ATTRIBUTE_ACCESSKEY_OF = 'data-attributeaccesskeyof'.freeze
|
139
|
+
|
140
|
+
##
|
141
|
+
# The name of attribute that links the content of download link.
|
142
|
+
DATA_ATTRIBUTE_DOWNLOAD_OF = 'data-attributedownloadof'.freeze
|
143
|
+
|
144
|
+
##
|
145
|
+
# The name of attribute that links the content of header cell with the
|
146
|
+
# data cell.
|
147
|
+
DATA_ATTRIBUTE_HEADERS_OF = 'data-headersof'.freeze
|
148
|
+
|
149
|
+
##
|
150
|
+
# The name of attribute that links the description of language with the
|
151
|
+
# element.
|
152
|
+
DATA_ATTRIBUTE_LANGUAGE_OF = 'data-languageof'.freeze
|
153
|
+
|
154
|
+
##
|
155
|
+
# The name of attribute that links the content of link that open a new
|
156
|
+
# instance.
|
157
|
+
DATA_ATTRIBUTE_TARGET_OF = 'data-attributetargetof'.freeze
|
158
|
+
|
159
|
+
##
|
160
|
+
# The name of attribute that links the content of title of element.
|
161
|
+
DATA_ATTRIBUTE_TITLE_OF = 'data-attributetitleof'.freeze
|
162
|
+
|
163
|
+
##
|
164
|
+
# The name of attribute that links the content of role of element with the
|
165
|
+
# element.
|
166
|
+
DATA_ROLE_OF = 'data-roleof'.freeze
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
##
|
171
|
+
# Returns the description of element.
|
172
|
+
#
|
173
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The element.
|
174
|
+
# @return [String] The description of element.
|
175
|
+
def get_description(element)
|
176
|
+
description = nil
|
177
|
+
tag_name = element.get_tag_name
|
178
|
+
if element.has_attribute?('title')
|
179
|
+
description = element.get_attribute('title')
|
180
|
+
elsif element.has_attribute?('aria-label')
|
181
|
+
description = element.get_attribute('aria-label')
|
182
|
+
elsif element.has_attribute?('alt')
|
183
|
+
description = element.get_attribute('alt')
|
184
|
+
elsif element.has_attribute?('label')
|
185
|
+
description = element.get_attribute('label')
|
186
|
+
elsif element.has_attribute?('aria-labelledby') ||
|
187
|
+
element.has_attribute?('aria-describedby')
|
188
|
+
description_ids = if element.has_attribute?('aria-labelledby')
|
189
|
+
element.get_attribute(
|
190
|
+
'aria-labelledby'
|
191
|
+
).split(/[ \n\t\r]+/)
|
192
|
+
else
|
193
|
+
element.get_attribute(
|
194
|
+
'aria-describedby'
|
195
|
+
).split(/[ \n\t\r]+/)
|
196
|
+
end
|
197
|
+
description_ids.each do |description_id|
|
198
|
+
element_description = @parser.find(
|
199
|
+
"##{description_id}"
|
200
|
+
).first_result
|
201
|
+
unless element_description.nil?
|
202
|
+
description = element_description.get_text_content
|
203
|
+
break
|
204
|
+
end
|
205
|
+
end
|
206
|
+
elsif %w[INPUT TEXTAREA OPTION].include?(tag_name)
|
207
|
+
if element.has_attribute?('type') &&
|
208
|
+
%w[button submit reset].include?(
|
209
|
+
element.get_attribute('type').downcase
|
210
|
+
) &&
|
211
|
+
element.has_attribute?('value')
|
212
|
+
description = element.get_attribute('value')
|
213
|
+
else
|
214
|
+
label = nil
|
215
|
+
if element.has_attribute?('id')
|
216
|
+
field_id = element.get_attribute('id')
|
217
|
+
label = @parser.find("label[for=\"#{field_id}\"]").first_result
|
218
|
+
end
|
219
|
+
if label.nil?
|
220
|
+
label = @parser.find(element).find_ancestors('label').first_result
|
221
|
+
end
|
222
|
+
description = label.get_text_content unless label.nil?
|
223
|
+
end
|
224
|
+
end
|
225
|
+
description = element.get_text_content if description.nil?
|
226
|
+
description.gsub(/[ \n\t\r]+/, ' ').strip
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Generate the list of shortcuts of page.
|
231
|
+
#
|
232
|
+
# @return [void]
|
233
|
+
def generate_list_shortcuts
|
234
|
+
local = @parser.find('body').first_result
|
235
|
+
|
236
|
+
return if local.nil?
|
237
|
+
|
238
|
+
container_before = @parser.find(
|
239
|
+
"##{ID_CONTAINER_SHORTCUTS_BEFORE}"
|
240
|
+
).first_result
|
241
|
+
if container_before.nil? && !@attribute_accesskey_before.empty?
|
242
|
+
container_before = @parser.create_element('div')
|
243
|
+
container_before.set_attribute('id', ID_CONTAINER_SHORTCUTS_BEFORE)
|
244
|
+
|
245
|
+
text_container_before = @parser.create_element('span')
|
246
|
+
text_container_before.set_attribute('class', CLASS_TEXT_SHORTCUTS)
|
247
|
+
text_container_before.append_text(@attribute_accesskey_before)
|
248
|
+
|
249
|
+
container_before.append_element(text_container_before)
|
250
|
+
local.prepend_element(container_before)
|
251
|
+
end
|
252
|
+
unless container_before.nil?
|
253
|
+
@list_shortcuts_before = @parser.find(
|
254
|
+
container_before
|
255
|
+
).find_children('ul').first_result
|
256
|
+
if @list_shortcuts_before.nil?
|
257
|
+
@list_shortcuts_before = @parser.create_element('ul')
|
258
|
+
container_before.append_element(@list_shortcuts_before)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
container_after = @parser.find(
|
262
|
+
"##{ID_CONTAINER_SHORTCUTS_AFTER}"
|
263
|
+
).first_result
|
264
|
+
if container_after.nil? && !@attribute_accesskey_after.empty?
|
265
|
+
container_after = @parser.create_element('div')
|
266
|
+
container_after.set_attribute('id', ID_CONTAINER_SHORTCUTS_AFTER)
|
267
|
+
|
268
|
+
text_container_after = @parser.create_element('span')
|
269
|
+
text_container_after.set_attribute('class', CLASS_TEXT_SHORTCUTS)
|
270
|
+
text_container_after.append_text(@attribute_accesskey_after)
|
271
|
+
|
272
|
+
container_after.append_element(text_container_after)
|
273
|
+
local.append_element(container_after)
|
274
|
+
end
|
275
|
+
unless container_after.nil?
|
276
|
+
@list_shortcuts_after = @parser.find(
|
277
|
+
container_after
|
278
|
+
).find_children('ul').first_result
|
279
|
+
if @list_shortcuts_after.nil?
|
280
|
+
@list_shortcuts_after = @parser.create_element('ul')
|
281
|
+
container_after.append_element(@list_shortcuts_after)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
@list_shortcuts_added = true
|
285
|
+
end
|
286
|
+
|
287
|
+
##
|
288
|
+
# Returns the shortcut prefix of browser.
|
289
|
+
#
|
290
|
+
# @param user_agent [String] The user agent of browser.
|
291
|
+
# @param standart_prefix [String] The default prefix.
|
292
|
+
# @return [String] The shortcut prefix of browser.
|
293
|
+
def get_shortcut_prefix(user_agent, standart_prefix)
|
294
|
+
return standart_prefix if user_agent.nil?
|
295
|
+
|
296
|
+
user_agent = user_agent.downcase
|
297
|
+
opera = user_agent.include?('opera')
|
298
|
+
mac = user_agent.include?('mac')
|
299
|
+
konqueror = user_agent.include?('konqueror')
|
300
|
+
spoofer = user_agent.include?('spoofer')
|
301
|
+
safari = user_agent.include?('applewebkit')
|
302
|
+
windows = user_agent.include?('windows')
|
303
|
+
chrome = user_agent.include?('chrome')
|
304
|
+
firefox = user_agent.include?('firefox') ||
|
305
|
+
user_agent.include?('minefield')
|
306
|
+
ie = user_agent.include?('msie') || user_agent.include?('trident')
|
307
|
+
|
308
|
+
return 'SHIFT + ESC' if opera
|
309
|
+
return 'CTRL + OPTION' if chrome && mac && !spoofer
|
310
|
+
return 'CTRL + ALT' if safari && !windows && !spoofer
|
311
|
+
return 'CTRL' if !windows && (safari || mac || konqueror)
|
312
|
+
return 'ALT + SHIFT' if firefox
|
313
|
+
return 'ALT' if chrome || ie
|
314
|
+
|
315
|
+
standart_prefix
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Returns the description of role.
|
320
|
+
#
|
321
|
+
# @param role [String] The role.
|
322
|
+
# @return [String] The description of role.
|
323
|
+
def get_role_description(role)
|
324
|
+
parameter = "role-#{role.downcase}"
|
325
|
+
if @configure.has_parameter?(parameter)
|
326
|
+
return @configure.get_parameter(parameter)
|
327
|
+
end
|
328
|
+
nil
|
329
|
+
end
|
330
|
+
|
331
|
+
##
|
332
|
+
# Returns the description of language.
|
333
|
+
#
|
334
|
+
# @param language_code [String] The BCP 47 code language.
|
335
|
+
# @return [String] The description of language.
|
336
|
+
def get_language_description(language_code)
|
337
|
+
language = language_code.downcase
|
338
|
+
p = "language-#{language}"
|
339
|
+
return @configure.get_parameter(p) if @configure.has_parameter?(p)
|
340
|
+
if language.include?('-')
|
341
|
+
codes = language.split(/\-/)
|
342
|
+
p = "language-#{codes.first}"
|
343
|
+
return @configure.get_parameter(p) if @configure.has_parameter?(p)
|
344
|
+
end
|
345
|
+
nil
|
346
|
+
end
|
347
|
+
|
348
|
+
##
|
349
|
+
# Insert a element before or after other element.
|
350
|
+
#
|
351
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The reference
|
352
|
+
# element.
|
353
|
+
# @param new_element [Hatemile::Util::Html::HTMLDOMElement] The element
|
354
|
+
# that be inserted.
|
355
|
+
# @param before [Boolean] To insert the element before the other element.
|
356
|
+
# @return [void]
|
357
|
+
def insert(element, new_element, before)
|
358
|
+
return unless Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
359
|
+
|
360
|
+
tag_name = element.get_tag_name
|
361
|
+
append_tags = %w[BODY A FIGCAPTION LI DT DD LABEL OPTION TD TH]
|
362
|
+
controls = %w[INPUT SELECT TEXTAREA]
|
363
|
+
if tag_name == 'HTML'
|
364
|
+
body = @parser.find('body').first_result
|
365
|
+
insert(body, new_element, before) unless body.nil?
|
366
|
+
elsif append_tags.include?(tag_name)
|
367
|
+
element.prepend_element(new_element) if before
|
368
|
+
element.append_element(new_element) unless before
|
369
|
+
elsif controls.include?(tag_name)
|
370
|
+
labels = []
|
371
|
+
if element.has_attribute?('id')
|
372
|
+
labels = @parser.find(
|
373
|
+
"label[for=\"#{element.get_attribute('id')}\"]"
|
374
|
+
).list_results
|
375
|
+
end
|
376
|
+
if labels.empty?
|
377
|
+
labels = @parser.find(element).find_ancestors('label').list_results
|
378
|
+
end
|
379
|
+
labels.each do |label|
|
380
|
+
insert(label, new_element, before)
|
381
|
+
end
|
382
|
+
elsif before
|
383
|
+
element.insert_before(new_element)
|
384
|
+
else
|
385
|
+
element.insert_after(new_element)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
##
|
390
|
+
# Force the screen reader display an information of element.
|
391
|
+
#
|
392
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The reference
|
393
|
+
# element.
|
394
|
+
# @param text_before [String] The text content to show before the element.
|
395
|
+
# @param text_after [String] The text content to show after the element.
|
396
|
+
# @param data_of [String] The name of attribute that links the content
|
397
|
+
# with element.
|
398
|
+
# @return [void]
|
399
|
+
def force_read_simple(element, text_before, text_after, data_of)
|
400
|
+
@id_generator.generate_id(element)
|
401
|
+
identifier = element.get_attribute('id')
|
402
|
+
selector = "[#{data_of}=\"#{identifier}\"]"
|
403
|
+
|
404
|
+
reference_before = @parser.find(
|
405
|
+
".#{CLASS_FORCE_READ_BEFORE}#{selector}"
|
406
|
+
).first_result
|
407
|
+
reference_after = @parser.find(
|
408
|
+
".#{CLASS_FORCE_READ_AFTER}#{selector}"
|
409
|
+
).first_result
|
410
|
+
references = @parser.find(selector).list_results
|
411
|
+
if references.include?(reference_before)
|
412
|
+
references.delete(reference_before)
|
413
|
+
end
|
414
|
+
if references.include?(reference_after)
|
415
|
+
references.delete(reference_after)
|
416
|
+
end
|
417
|
+
|
418
|
+
return unless references.empty?
|
419
|
+
|
420
|
+
unless text_before.empty?
|
421
|
+
reference_before.remove_node unless reference_before.nil?
|
422
|
+
|
423
|
+
span = @parser.create_element('span')
|
424
|
+
span.set_attribute('class', CLASS_FORCE_READ_BEFORE)
|
425
|
+
span.set_attribute(data_of, identifier)
|
426
|
+
span.append_text(text_before)
|
427
|
+
insert(element, span, true)
|
428
|
+
end
|
429
|
+
|
430
|
+
return if text_after.empty?
|
431
|
+
|
432
|
+
reference_after.remove_node unless reference_after.nil?
|
433
|
+
|
434
|
+
span = @parser.create_element('span')
|
435
|
+
span.set_attribute('class', CLASS_FORCE_READ_AFTER)
|
436
|
+
span.set_attribute(data_of, identifier)
|
437
|
+
span.append_text(text_after)
|
438
|
+
insert(element, span, false)
|
439
|
+
end
|
440
|
+
|
441
|
+
##
|
442
|
+
# Force the screen reader display an information of element with prefixes
|
443
|
+
# or suffixes.
|
444
|
+
#
|
445
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The reference
|
446
|
+
# element.
|
447
|
+
# @param value [String] The value to be show.
|
448
|
+
# @param text_prefix_before [String] The prefix of value to show before
|
449
|
+
# the element.
|
450
|
+
# @param text_suffix_before [String] The suffix of value to show before
|
451
|
+
# the element.
|
452
|
+
# @param text_prefix_after [String] The prefix of value to show after the
|
453
|
+
# element.
|
454
|
+
# @param text_suffix_after [String] The suffix of value to show after the
|
455
|
+
# element.
|
456
|
+
# @param data_of [String] The name of attribute that links the content
|
457
|
+
# with element.
|
458
|
+
# @return [void]
|
459
|
+
def force_read(
|
460
|
+
element,
|
461
|
+
value,
|
462
|
+
text_prefix_before,
|
463
|
+
text_suffix_before,
|
464
|
+
text_prefix_after,
|
465
|
+
text_suffix_after,
|
466
|
+
data_of
|
467
|
+
)
|
468
|
+
text_before = if !text_prefix_before.empty? ||
|
469
|
+
!text_suffix_before.empty?
|
470
|
+
text_prefix_before + value + text_suffix_before
|
471
|
+
else
|
472
|
+
''
|
473
|
+
end
|
474
|
+
text_after = if !text_prefix_after.empty? || !text_suffix_after.empty?
|
475
|
+
text_prefix_after + value + text_suffix_after
|
476
|
+
else
|
477
|
+
''
|
478
|
+
end
|
479
|
+
force_read_simple(element, text_before, text_after, data_of)
|
480
|
+
end
|
481
|
+
|
482
|
+
public
|
483
|
+
|
484
|
+
##
|
485
|
+
# Initializes a new object that manipulate the display for screen readers
|
486
|
+
# of parser.
|
487
|
+
#
|
488
|
+
# @param parser [Hatemile::Util::Html::HTMLDOMParser] The HTML parser.
|
489
|
+
# @param configure [Hatemile::Util::Configure] The configuration of
|
490
|
+
# HaTeMiLe.
|
491
|
+
# @param user_agent [String] The user agent of the user.
|
492
|
+
def initialize(parser, configure, user_agent = nil)
|
493
|
+
Hatemile::Helper.require_not_nil(parser, configure)
|
494
|
+
Hatemile::Helper.require_valid_type(
|
495
|
+
parser,
|
496
|
+
Hatemile::Util::Html::HTMLDOMParser
|
497
|
+
)
|
498
|
+
Hatemile::Helper.require_valid_type(
|
499
|
+
configure,
|
500
|
+
Hatemile::Util::Configure
|
501
|
+
)
|
502
|
+
Hatemile::Helper.require_valid_type(user_agent, String)
|
503
|
+
|
504
|
+
@parser = parser
|
505
|
+
@configure = configure
|
506
|
+
@id_generator = Hatemile::Util::IDGenerator.new('display')
|
507
|
+
@shortcut_prefix = get_shortcut_prefix(
|
508
|
+
user_agent,
|
509
|
+
configure.get_parameter('attribute-accesskey-default')
|
510
|
+
)
|
511
|
+
@aria_autocomplete_both_before = configure.get_parameter(
|
512
|
+
'aria-autocomplete-both-before'
|
513
|
+
)
|
514
|
+
@aria_autocomplete_both_after = configure.get_parameter(
|
515
|
+
'aria-autocomplete-both-after'
|
516
|
+
)
|
517
|
+
@aria_autocomplete_inline_before = configure.get_parameter(
|
518
|
+
'aria-autocomplete-inline-before'
|
519
|
+
)
|
520
|
+
@aria_autocomplete_inline_after = configure.get_parameter(
|
521
|
+
'aria-autocomplete-inline-after'
|
522
|
+
)
|
523
|
+
@aria_autocomplete_list_before = configure.get_parameter(
|
524
|
+
'aria-autocomplete-list-before'
|
525
|
+
)
|
526
|
+
@aria_autocomplete_list_after = configure.get_parameter(
|
527
|
+
'aria-autocomplete-list-after'
|
528
|
+
)
|
529
|
+
@aria_busy_true_before = configure.get_parameter(
|
530
|
+
'aria-busy-true-before'
|
531
|
+
)
|
532
|
+
@aria_busy_true_after = configure.get_parameter('aria-busy-true-after')
|
533
|
+
@aria_checked_false_before = configure.get_parameter(
|
534
|
+
'aria-checked-false-before'
|
535
|
+
)
|
536
|
+
@aria_checked_false_after = configure.get_parameter(
|
537
|
+
'aria-checked-false-after'
|
538
|
+
)
|
539
|
+
@aria_checked_mixed_before = configure.get_parameter(
|
540
|
+
'aria-checked-mixed-before'
|
541
|
+
)
|
542
|
+
@aria_checked_mixed_after = configure.get_parameter(
|
543
|
+
'aria-checked-mixed-after'
|
544
|
+
)
|
545
|
+
@aria_checked_true_before = configure.get_parameter(
|
546
|
+
'aria-checked-true-before'
|
547
|
+
)
|
548
|
+
@aria_checked_true_after = configure.get_parameter(
|
549
|
+
'aria-checked-true-after'
|
550
|
+
)
|
551
|
+
@aria_dropeffect_copy_before = configure.get_parameter(
|
552
|
+
'aria-dropeffect-copy-before'
|
553
|
+
)
|
554
|
+
@aria_dropeffect_copy_after = configure.get_parameter(
|
555
|
+
'aria-dropeffect-copy-after'
|
556
|
+
)
|
557
|
+
@aria_dropeffect_execute_before = configure.get_parameter(
|
558
|
+
'aria-dropeffect-execute-before'
|
559
|
+
)
|
560
|
+
@aria_dropeffect_execute_after = configure.get_parameter(
|
561
|
+
'aria-dropeffect-execute-after'
|
562
|
+
)
|
563
|
+
@aria_dropeffect_link_before = configure.get_parameter(
|
564
|
+
'aria-dropeffect-link-before'
|
565
|
+
)
|
566
|
+
@aria_dropeffect_link_after = configure.get_parameter(
|
567
|
+
'aria-dropeffect-link-after'
|
568
|
+
)
|
569
|
+
@aria_dropeffect_move_before = configure.get_parameter(
|
570
|
+
'aria-dropeffect-move-before'
|
571
|
+
)
|
572
|
+
@aria_dropeffect_move_after = configure.get_parameter(
|
573
|
+
'aria-dropeffect-move-after'
|
574
|
+
)
|
575
|
+
@aria_dropeffect_popup_before = configure.get_parameter(
|
576
|
+
'aria-dropeffect-popup-before'
|
577
|
+
)
|
578
|
+
@aria_dropeffect_popup_after = configure.get_parameter(
|
579
|
+
'aria-dropeffect-popup-after'
|
580
|
+
)
|
581
|
+
@aria_expanded_false_before = configure.get_parameter(
|
582
|
+
'aria-expanded-false-before'
|
583
|
+
)
|
584
|
+
@aria_expanded_false_after = configure.get_parameter(
|
585
|
+
'aria-expanded-false-after'
|
586
|
+
)
|
587
|
+
@aria_expanded_true_before = configure.get_parameter(
|
588
|
+
'aria-expanded-true-before'
|
589
|
+
)
|
590
|
+
@aria_expanded_true_after = configure.get_parameter(
|
591
|
+
'aria-expanded-true-after'
|
592
|
+
)
|
593
|
+
@aria_grabbed_false_before = configure.get_parameter(
|
594
|
+
'aria-grabbed-false-before'
|
595
|
+
)
|
596
|
+
@aria_grabbed_false_after = configure.get_parameter(
|
597
|
+
'aria-grabbed-false-after'
|
598
|
+
)
|
599
|
+
@aria_grabbed_true_before = configure.get_parameter(
|
600
|
+
'aria-grabbed-true-before'
|
601
|
+
)
|
602
|
+
@aria_grabbed_true_after = configure.get_parameter(
|
603
|
+
'aria-grabbed-true-after'
|
604
|
+
)
|
605
|
+
@aria_haspopup_true_before = configure.get_parameter(
|
606
|
+
'aria-haspopup-true-before'
|
607
|
+
)
|
608
|
+
@aria_haspopup_true_after = configure.get_parameter(
|
609
|
+
'aria-haspopup-true-after'
|
610
|
+
)
|
611
|
+
@aria_level_prefix_before = configure.get_parameter(
|
612
|
+
'aria-level-prefix-before'
|
613
|
+
)
|
614
|
+
@aria_level_suffix_before = configure.get_parameter(
|
615
|
+
'aria-level-suffix-before'
|
616
|
+
)
|
617
|
+
@aria_level_prefix_after = configure.get_parameter(
|
618
|
+
'aria-level-prefix-after'
|
619
|
+
)
|
620
|
+
@aria_level_suffix_after = configure.get_parameter(
|
621
|
+
'aria-level-suffix-after'
|
622
|
+
)
|
623
|
+
@aria_value_maximum_prefix_before = configure.get_parameter(
|
624
|
+
'aria-value-maximum-prefix-before'
|
625
|
+
)
|
626
|
+
@aria_value_maximum_suffix_before = configure.get_parameter(
|
627
|
+
'aria-value-maximum-suffix-before'
|
628
|
+
)
|
629
|
+
@aria_value_maximum_prefix_after = configure.get_parameter(
|
630
|
+
'aria-value-maximum-prefix-after'
|
631
|
+
)
|
632
|
+
@aria_value_maximum_suffix_after = configure.get_parameter(
|
633
|
+
'aria-value-maximum-suffix-after'
|
634
|
+
)
|
635
|
+
@aria_value_minimum_prefix_before = configure.get_parameter(
|
636
|
+
'aria-value-minimum-prefix-before'
|
637
|
+
)
|
638
|
+
@aria_value_minimum_suffix_before = configure.get_parameter(
|
639
|
+
'aria-value-minimum-suffix-before'
|
640
|
+
)
|
641
|
+
@aria_value_minimum_prefix_after = configure.get_parameter(
|
642
|
+
'aria-value-minimum-prefix-after'
|
643
|
+
)
|
644
|
+
@aria_value_minimum_suffix_after = configure.get_parameter(
|
645
|
+
'aria-value-minimum-suffix-after'
|
646
|
+
)
|
647
|
+
@aria_orientation_horizontal_before = configure.get_parameter(
|
648
|
+
'aria-orientation-horizontal-before'
|
649
|
+
)
|
650
|
+
@aria_orientation_horizontal_after = configure.get_parameter(
|
651
|
+
'aria-orientation-horizontal-after'
|
652
|
+
)
|
653
|
+
@aria_orientation_vertical_before = configure.get_parameter(
|
654
|
+
'aria-orientation-vertical-before'
|
655
|
+
)
|
656
|
+
@aria_orientation_vertical_after = configure.get_parameter(
|
657
|
+
'aria-orientation-vertical-after'
|
658
|
+
)
|
659
|
+
@aria_pressed_false_before = configure.get_parameter(
|
660
|
+
'aria-pressed-false-before'
|
661
|
+
)
|
662
|
+
@aria_pressed_false_after = configure.get_parameter(
|
663
|
+
'aria-pressed-false-after'
|
664
|
+
)
|
665
|
+
@aria_pressed_mixed_before = configure.get_parameter(
|
666
|
+
'aria-pressed-mixed-before'
|
667
|
+
)
|
668
|
+
@aria_pressed_mixed_after = configure.get_parameter(
|
669
|
+
'aria-pressed-mixed-after'
|
670
|
+
)
|
671
|
+
@aria_pressed_true_before = configure.get_parameter(
|
672
|
+
'aria-pressed-true-before'
|
673
|
+
)
|
674
|
+
@aria_pressed_true_after = configure.get_parameter(
|
675
|
+
'aria-pressed-true-after'
|
676
|
+
)
|
677
|
+
@aria_required_true_before = configure.get_parameter(
|
678
|
+
'aria-required-true-before'
|
679
|
+
)
|
680
|
+
@aria_required_true_after = configure.get_parameter(
|
681
|
+
'aria-required-true-after'
|
682
|
+
)
|
683
|
+
@aria_selected_false_before = configure.get_parameter(
|
684
|
+
'aria-selected-false-before'
|
685
|
+
)
|
686
|
+
@aria_selected_false_after = configure.get_parameter(
|
687
|
+
'aria-selected-false-after'
|
688
|
+
)
|
689
|
+
@aria_selected_true_before = configure.get_parameter(
|
690
|
+
'aria-selected-true-before'
|
691
|
+
)
|
692
|
+
@aria_selected_true_after = configure.get_parameter(
|
693
|
+
'aria-selected-true-after'
|
694
|
+
)
|
695
|
+
@aria_sort_ascending_before = configure.get_parameter(
|
696
|
+
'aria-sort-ascending-before'
|
697
|
+
)
|
698
|
+
@aria_sort_ascending_after = configure.get_parameter(
|
699
|
+
'aria-sort-ascending-after'
|
700
|
+
)
|
701
|
+
@aria_sort_descending_before = configure.get_parameter(
|
702
|
+
'aria-sort-descending-before'
|
703
|
+
)
|
704
|
+
@aria_sort_descending_after = configure.get_parameter(
|
705
|
+
'aria-sort-descending-after'
|
706
|
+
)
|
707
|
+
@aria_sort_other_before = configure.get_parameter(
|
708
|
+
'aria-sort-other-before'
|
709
|
+
)
|
710
|
+
@aria_sort_other_after = configure.get_parameter(
|
711
|
+
'aria-sort-other-after'
|
712
|
+
)
|
713
|
+
@attribute_accesskey_before = configure.get_parameter(
|
714
|
+
'attribute-accesskey-before'
|
715
|
+
)
|
716
|
+
@attribute_accesskey_after = configure.get_parameter(
|
717
|
+
'attribute-accesskey-after'
|
718
|
+
)
|
719
|
+
@attribute_accesskey_prefix_before = configure.get_parameter(
|
720
|
+
'attribute-accesskey-prefix-before'
|
721
|
+
)
|
722
|
+
@attribute_accesskey_suffix_before = configure.get_parameter(
|
723
|
+
'attribute-accesskey-suffix-before'
|
724
|
+
)
|
725
|
+
@attribute_accesskey_prefix_after = configure.get_parameter(
|
726
|
+
'attribute-accesskey-prefix-after'
|
727
|
+
)
|
728
|
+
@attribute_accesskey_suffix_after = configure.get_parameter(
|
729
|
+
'attribute-accesskey-suffix-after'
|
730
|
+
)
|
731
|
+
@attribute_download_before = configure.get_parameter(
|
732
|
+
'attribute-download-before'
|
733
|
+
)
|
734
|
+
@attribute_download_after = configure.get_parameter(
|
735
|
+
'attribute-download-after'
|
736
|
+
)
|
737
|
+
@attribute_headers_prefix_before = configure.get_parameter(
|
738
|
+
'attribute-headers-prefix-before'
|
739
|
+
)
|
740
|
+
@attribute_headers_suffix_before = configure.get_parameter(
|
741
|
+
'attribute-headers-suffix-before'
|
742
|
+
)
|
743
|
+
@attribute_headers_prefix_after = configure.get_parameter(
|
744
|
+
'attribute-headers-prefix-after'
|
745
|
+
)
|
746
|
+
@attribute_headers_suffix_after = configure.get_parameter(
|
747
|
+
'attribute-headers-suffix-after'
|
748
|
+
)
|
749
|
+
@attribute_language_prefix_before = configure.get_parameter(
|
750
|
+
'attribute-language-prefix-before'
|
751
|
+
)
|
752
|
+
@attribute_language_suffix_before = configure.get_parameter(
|
753
|
+
'attribute-language-suffix-before'
|
754
|
+
)
|
755
|
+
@attribute_language_prefix_after = configure.get_parameter(
|
756
|
+
'attribute-language-prefix-after'
|
757
|
+
)
|
758
|
+
@attribute_language_suffix_after = configure.get_parameter(
|
759
|
+
'attribute-language-suffix-after'
|
760
|
+
)
|
761
|
+
@attribute_role_prefix_before = configure.get_parameter(
|
762
|
+
'attribute-role-prefix-before'
|
763
|
+
)
|
764
|
+
@attribute_role_suffix_before = configure.get_parameter(
|
765
|
+
'attribute-role-suffix-before'
|
766
|
+
)
|
767
|
+
@attribute_role_prefix_after = configure.get_parameter(
|
768
|
+
'attribute-role-prefix-after'
|
769
|
+
)
|
770
|
+
@attribute_role_suffix_after = configure.get_parameter(
|
771
|
+
'attribute-role-suffix-after'
|
772
|
+
)
|
773
|
+
@attribute_target_blank_before = configure.get_parameter(
|
774
|
+
'attribute-target-blank-before'
|
775
|
+
)
|
776
|
+
@attribute_target_blank_after = configure.get_parameter(
|
777
|
+
'attribute-target-blank-after'
|
778
|
+
)
|
779
|
+
@attribute_title_prefix_before = configure.get_parameter(
|
780
|
+
'attribute-title-prefix-before'
|
781
|
+
)
|
782
|
+
@attribute_title_suffix_before = configure.get_parameter(
|
783
|
+
'attribute-title-suffix-before'
|
784
|
+
)
|
785
|
+
@attribute_title_prefix_after = configure.get_parameter(
|
786
|
+
'attribute-title-prefix-after'
|
787
|
+
)
|
788
|
+
@attribute_title_suffix_after = configure.get_parameter(
|
789
|
+
'attribute-title-suffix-after'
|
790
|
+
)
|
791
|
+
@list_shortcuts_added = false
|
792
|
+
@list_shortcuts_before = nil
|
793
|
+
@list_shortcuts_after = nil
|
794
|
+
end
|
795
|
+
|
796
|
+
##
|
797
|
+
# @see Hatemile::AccessibleDisplay#display_shortcut
|
798
|
+
def display_shortcut(element)
|
799
|
+
return unless element.has_attribute?('accesskey')
|
800
|
+
|
801
|
+
description = get_description(element)
|
802
|
+
unless element.has_attribute?('title')
|
803
|
+
element.set_attribute('title', description)
|
804
|
+
|
805
|
+
@id_generator.generate_id(element)
|
806
|
+
element.set_attribute(
|
807
|
+
DATA_ATTRIBUTE_TITLE_OF,
|
808
|
+
element.get_attribute('id')
|
809
|
+
)
|
810
|
+
end
|
811
|
+
|
812
|
+
generate_list_shortcuts unless @list_shortcuts_added
|
813
|
+
|
814
|
+
keys = element.get_attribute('accesskey').upcase.split(/[ \n\t\r]+/)
|
815
|
+
keys.each do |key|
|
816
|
+
shortcut = "#{@shortcut_prefix} + #{key}"
|
817
|
+
selector = "[#{DATA_ATTRIBUTE_ACCESSKEY_OF}=\"#{key}\"]"
|
818
|
+
|
819
|
+
force_read(
|
820
|
+
element,
|
821
|
+
shortcut,
|
822
|
+
@attribute_accesskey_prefix_before,
|
823
|
+
@attribute_accesskey_suffix_before,
|
824
|
+
@attribute_accesskey_prefix_after,
|
825
|
+
@attribute_accesskey_suffix_after,
|
826
|
+
DATA_ATTRIBUTE_ACCESSKEY_OF
|
827
|
+
)
|
828
|
+
|
829
|
+
item = @parser.create_element('li')
|
830
|
+
item.set_attribute(DATA_ATTRIBUTE_ACCESSKEY_OF, key)
|
831
|
+
item.append_text("#{shortcut}: #{description}")
|
832
|
+
|
833
|
+
if !@list_shortcuts_before.nil? &&
|
834
|
+
@parser.find(@list_shortcuts_before).find_children(
|
835
|
+
selector
|
836
|
+
).first_result.nil?
|
837
|
+
@list_shortcuts_before.append_element(item.clone_element)
|
838
|
+
end
|
839
|
+
unless !@list_shortcuts_after.nil? &&
|
840
|
+
@parser.find(@list_shortcuts_after).find_children(
|
841
|
+
selector
|
842
|
+
).first_result.nil?
|
843
|
+
next
|
844
|
+
end
|
845
|
+
@list_shortcuts_after.append_element(item.clone_element)
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
##
|
850
|
+
# @see Hatemile::AccessibleDisplay#display_all_shortcuts
|
851
|
+
def display_all_shortcuts
|
852
|
+
elements = @parser.find('[accesskey]').list_results
|
853
|
+
elements.each do |element|
|
854
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
855
|
+
display_shortcut(element)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
##
|
861
|
+
# @see Hatemile::AccessibleDisplay#display_role
|
862
|
+
def display_role(element)
|
863
|
+
return unless element.has_attribute?('role')
|
864
|
+
|
865
|
+
role_description = get_role_description(element.get_attribute('role'))
|
866
|
+
|
867
|
+
return if role_description.nil?
|
868
|
+
|
869
|
+
force_read(
|
870
|
+
element,
|
871
|
+
role_description,
|
872
|
+
@attribute_role_prefix_before,
|
873
|
+
@attribute_role_suffix_before,
|
874
|
+
@attribute_role_prefix_after,
|
875
|
+
@attribute_role_suffix_after,
|
876
|
+
DATA_ROLE_OF
|
877
|
+
)
|
878
|
+
end
|
879
|
+
|
880
|
+
##
|
881
|
+
# @see Hatemile::AccessibleDisplay#display_all_roles
|
882
|
+
def display_all_roles
|
883
|
+
elements = @parser.find('[role]').list_results
|
884
|
+
elements.each do |element|
|
885
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
886
|
+
display_role(element)
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
##
|
892
|
+
# @see Hatemile::AccessibleDisplay#display_cell_header
|
893
|
+
def display_cell_header(table_cell)
|
894
|
+
return unless table_cell.has_attribute?('headers')
|
895
|
+
|
896
|
+
text_header = ''
|
897
|
+
ids_headers = table_cell.get_attribute('headers').split(/[ \n\t\r]+/)
|
898
|
+
ids_headers.each do |id_header|
|
899
|
+
header = @parser.find("##{id_header}").first_result
|
900
|
+
|
901
|
+
next if header.nil?
|
902
|
+
|
903
|
+
text_header = if text_header.empty?
|
904
|
+
header.get_text_content.strip
|
905
|
+
else
|
906
|
+
"#{text_header} #{header.get_text_content.strip}"
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
return if text_header.strip.empty?
|
911
|
+
|
912
|
+
force_read(
|
913
|
+
table_cell,
|
914
|
+
text_header.gsub(/[ \n\t\r]+/, ' ').strip,
|
915
|
+
@attribute_headers_prefix_before,
|
916
|
+
@attribute_headers_suffix_before,
|
917
|
+
@attribute_headers_prefix_after,
|
918
|
+
@attribute_headers_suffix_after,
|
919
|
+
DATA_ATTRIBUTE_HEADERS_OF
|
920
|
+
)
|
921
|
+
end
|
922
|
+
|
923
|
+
##
|
924
|
+
# @see Hatemile::AccessibleDisplay#display_all_cell_headers
|
925
|
+
def display_all_cell_headers
|
926
|
+
elements = @parser.find('td[headers],th[headers]').list_results
|
927
|
+
elements.each do |element|
|
928
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
929
|
+
display_cell_header(element)
|
930
|
+
end
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
##
|
935
|
+
# @see Hatemile::AccessibleDisplay#display_waiaria_states
|
936
|
+
def display_waiaria_states(element)
|
937
|
+
if element.has_attribute?('aria-busy') &&
|
938
|
+
element.get_attribute('aria-busy') == 'true'
|
939
|
+
force_read_simple(
|
940
|
+
element,
|
941
|
+
@aria_busy_true_before,
|
942
|
+
@aria_busy_true_after,
|
943
|
+
DATA_ARIA_BUSY_OF
|
944
|
+
)
|
945
|
+
end
|
946
|
+
if element.has_attribute?('aria-checked')
|
947
|
+
attribute_value = element.get_attribute('aria-checked')
|
948
|
+
if attribute_value == 'true'
|
949
|
+
force_read_simple(
|
950
|
+
element,
|
951
|
+
@aria_checked_true_before,
|
952
|
+
@aria_checked_true_after,
|
953
|
+
DATA_ARIA_CHECKED_OF
|
954
|
+
)
|
955
|
+
elsif attribute_value == 'false'
|
956
|
+
force_read_simple(
|
957
|
+
element,
|
958
|
+
@aria_checked_false_before,
|
959
|
+
@aria_checked_false_after,
|
960
|
+
DATA_ARIA_CHECKED_OF
|
961
|
+
)
|
962
|
+
elsif attribute_value == 'mixed'
|
963
|
+
force_read_simple(
|
964
|
+
element,
|
965
|
+
@aria_checked_mixed_before,
|
966
|
+
@aria_checked_mixed_after,
|
967
|
+
DATA_ARIA_CHECKED_OF
|
968
|
+
)
|
969
|
+
end
|
970
|
+
end
|
971
|
+
if element.has_attribute?('aria-expanded')
|
972
|
+
attribute_value = element.get_attribute('aria-expanded')
|
973
|
+
if attribute_value == 'true'
|
974
|
+
force_read_simple(
|
975
|
+
element,
|
976
|
+
@aria_expanded_true_before,
|
977
|
+
@aria_expanded_true_after,
|
978
|
+
DATA_ARIA_EXPANDED_OF
|
979
|
+
)
|
980
|
+
elsif attribute_value == 'false'
|
981
|
+
force_read_simple(
|
982
|
+
element,
|
983
|
+
@aria_expanded_false_before,
|
984
|
+
@aria_expanded_false_after,
|
985
|
+
DATA_ARIA_EXPANDED_OF
|
986
|
+
)
|
987
|
+
end
|
988
|
+
end
|
989
|
+
if element.has_attribute?('aria-haspopup') &&
|
990
|
+
element.get_attribute('aria-haspopup') == 'true'
|
991
|
+
force_read_simple(
|
992
|
+
element,
|
993
|
+
@aria_haspopup_true_before,
|
994
|
+
@aria_haspopup_true_after,
|
995
|
+
DATA_ARIA_HASPOPUP_OF
|
996
|
+
)
|
997
|
+
end
|
998
|
+
if element.has_attribute?('aria-level')
|
999
|
+
force_read(
|
1000
|
+
element,
|
1001
|
+
element.get_attribute('aria-level'),
|
1002
|
+
@aria_level_prefix_before,
|
1003
|
+
@aria_level_suffix_before,
|
1004
|
+
@aria_level_prefix_after,
|
1005
|
+
@aria_level_suffix_after,
|
1006
|
+
DATA_ARIA_LEVEL_OF
|
1007
|
+
)
|
1008
|
+
end
|
1009
|
+
if element.has_attribute?('aria-orientation')
|
1010
|
+
attribute_value = element.get_attribute('aria-orientation')
|
1011
|
+
if attribute_value == 'vertical'
|
1012
|
+
force_read_simple(
|
1013
|
+
element,
|
1014
|
+
@aria_orientation_vertical_before,
|
1015
|
+
@aria_orientation_vertical_after,
|
1016
|
+
DATA_ARIA_ORIENTATION_OF
|
1017
|
+
)
|
1018
|
+
elsif attribute_value == 'horizontal'
|
1019
|
+
force_read_simple(
|
1020
|
+
element,
|
1021
|
+
@aria_orientation_horizontal_before,
|
1022
|
+
@aria_orientation_horizontal_after,
|
1023
|
+
DATA_ARIA_ORIENTATION_OF
|
1024
|
+
)
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
if element.has_attribute?('aria-pressed')
|
1028
|
+
attribute_value = element.get_attribute('aria-pressed')
|
1029
|
+
if attribute_value == 'true'
|
1030
|
+
force_read_simple(
|
1031
|
+
element,
|
1032
|
+
@aria_pressed_true_before,
|
1033
|
+
@aria_pressed_true_after,
|
1034
|
+
DATA_ARIA_PRESSED_OF
|
1035
|
+
)
|
1036
|
+
elsif attribute_value == 'false'
|
1037
|
+
force_read_simple(
|
1038
|
+
element,
|
1039
|
+
@aria_pressed_false_before,
|
1040
|
+
@aria_pressed_false_after,
|
1041
|
+
DATA_ARIA_PRESSED_OF
|
1042
|
+
)
|
1043
|
+
elsif attribute_value == 'mixed'
|
1044
|
+
force_read_simple(
|
1045
|
+
element,
|
1046
|
+
@aria_pressed_mixed_before,
|
1047
|
+
@aria_pressed_mixed_after,
|
1048
|
+
DATA_ARIA_PRESSED_OF
|
1049
|
+
)
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
if element.has_attribute?('aria-selected')
|
1053
|
+
attribute_value = element.get_attribute('aria-selected')
|
1054
|
+
if attribute_value == 'true'
|
1055
|
+
force_read_simple(
|
1056
|
+
element,
|
1057
|
+
@aria_selected_true_before,
|
1058
|
+
@aria_selected_true_after,
|
1059
|
+
DATA_ARIA_SELECTED_OF
|
1060
|
+
)
|
1061
|
+
elsif attribute_value == 'false'
|
1062
|
+
force_read_simple(
|
1063
|
+
element,
|
1064
|
+
@aria_selected_false_before,
|
1065
|
+
@aria_selected_false_after,
|
1066
|
+
DATA_ARIA_SELECTED_OF
|
1067
|
+
)
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
if element.has_attribute?('aria-sort')
|
1071
|
+
attribute_value = element.get_attribute('aria-sort')
|
1072
|
+
if attribute_value == 'ascending'
|
1073
|
+
force_read_simple(
|
1074
|
+
element,
|
1075
|
+
@aria_sort_ascending_before,
|
1076
|
+
@aria_sort_ascending_after,
|
1077
|
+
DATA_ARIA_SORT_OF
|
1078
|
+
)
|
1079
|
+
elsif attribute_value == 'descending'
|
1080
|
+
force_read_simple(
|
1081
|
+
element,
|
1082
|
+
@aria_sort_descending_before,
|
1083
|
+
@aria_sort_descending_after,
|
1084
|
+
DATA_ARIA_SORT_OF
|
1085
|
+
)
|
1086
|
+
elsif attribute_value == 'other'
|
1087
|
+
force_read_simple(
|
1088
|
+
element,
|
1089
|
+
@aria_sort_other_before,
|
1090
|
+
@aria_sort_other_after,
|
1091
|
+
DATA_ARIA_SORT_OF
|
1092
|
+
)
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
if element.has_attribute?('aria-required') &&
|
1096
|
+
element.get_attribute('aria-required') == 'true'
|
1097
|
+
force_read_simple(
|
1098
|
+
element,
|
1099
|
+
@aria_required_true_before,
|
1100
|
+
@aria_required_true_after,
|
1101
|
+
DATA_ARIA_REQUIRED_OF
|
1102
|
+
)
|
1103
|
+
end
|
1104
|
+
if element.has_attribute?('aria-valuemin')
|
1105
|
+
force_read(
|
1106
|
+
element,
|
1107
|
+
element.get_attribute('aria-valuemin'),
|
1108
|
+
@aria_value_minimum_prefix_before,
|
1109
|
+
@aria_value_minimum_suffix_before,
|
1110
|
+
@aria_value_minimum_prefix_after,
|
1111
|
+
@aria_value_minimum_suffix_after,
|
1112
|
+
DATA_ARIA_RANGE_MIN_OF
|
1113
|
+
)
|
1114
|
+
end
|
1115
|
+
if element.has_attribute?('aria-valuemax')
|
1116
|
+
force_read(
|
1117
|
+
element,
|
1118
|
+
element.get_attribute('aria-valuemax'),
|
1119
|
+
@aria_value_maximum_prefix_before,
|
1120
|
+
@aria_value_maximum_suffix_before,
|
1121
|
+
@aria_value_maximum_prefix_after,
|
1122
|
+
@aria_value_maximum_suffix_after,
|
1123
|
+
DATA_ARIA_RANGE_MAX_OF
|
1124
|
+
)
|
1125
|
+
end
|
1126
|
+
if element.has_attribute?('aria-autocomplete')
|
1127
|
+
attribute_value = element.get_attribute('aria-autocomplete')
|
1128
|
+
if attribute_value == 'both'
|
1129
|
+
force_read_simple(
|
1130
|
+
element,
|
1131
|
+
@aria_autocomplete_both_before,
|
1132
|
+
@aria_autocomplete_both_after,
|
1133
|
+
DATA_ARIA_AUTOCOMPLETE_OF
|
1134
|
+
)
|
1135
|
+
elsif attribute_value == 'inline'
|
1136
|
+
force_read_simple(
|
1137
|
+
element,
|
1138
|
+
@aria_autocomplete_list_before,
|
1139
|
+
@aria_autocomplete_list_after,
|
1140
|
+
DATA_ARIA_AUTOCOMPLETE_OF
|
1141
|
+
)
|
1142
|
+
elsif attribute_value == 'list'
|
1143
|
+
force_read_simple(
|
1144
|
+
element,
|
1145
|
+
@aria_autocomplete_inline_before,
|
1146
|
+
@aria_autocomplete_inline_after,
|
1147
|
+
DATA_ARIA_AUTOCOMPLETE_OF
|
1148
|
+
)
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
if element.has_attribute?('aria-dropeffect')
|
1152
|
+
attribute_value = element.get_attribute('aria-dropeffect')
|
1153
|
+
if attribute_value == 'copy'
|
1154
|
+
force_read_simple(
|
1155
|
+
element,
|
1156
|
+
@aria_dropeffect_copy_before,
|
1157
|
+
@aria_dropeffect_copy_after,
|
1158
|
+
DATA_ARIA_DROPEFFECT_OF
|
1159
|
+
)
|
1160
|
+
elsif attribute_value == 'move'
|
1161
|
+
force_read_simple(
|
1162
|
+
element,
|
1163
|
+
@aria_dropeffect_move_before,
|
1164
|
+
@aria_dropeffect_move_after,
|
1165
|
+
DATA_ARIA_DROPEFFECT_OF
|
1166
|
+
)
|
1167
|
+
elsif attribute_value == 'link'
|
1168
|
+
force_read_simple(
|
1169
|
+
element,
|
1170
|
+
@aria_dropeffect_link_before,
|
1171
|
+
@aria_dropeffect_link_after,
|
1172
|
+
DATA_ARIA_DROPEFFECT_OF
|
1173
|
+
)
|
1174
|
+
elsif attribute_value == 'execute'
|
1175
|
+
force_read_simple(
|
1176
|
+
element,
|
1177
|
+
@aria_dropeffect_execute_before,
|
1178
|
+
@aria_dropeffect_execute_after,
|
1179
|
+
DATA_ARIA_DROPEFFECT_OF
|
1180
|
+
)
|
1181
|
+
elsif attribute_value == 'popup'
|
1182
|
+
force_read_simple(
|
1183
|
+
element,
|
1184
|
+
@aria_dropeffect_popup_before,
|
1185
|
+
@aria_dropeffect_popup_after,
|
1186
|
+
DATA_ARIA_DROPEFFECT_OF
|
1187
|
+
)
|
1188
|
+
end
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
return unless element.has_attribute?('aria-grabbed')
|
1192
|
+
|
1193
|
+
attribute_value = element.get_attribute('aria-grabbed')
|
1194
|
+
if attribute_value == 'true'
|
1195
|
+
force_read_simple(
|
1196
|
+
element,
|
1197
|
+
@aria_grabbed_true_before,
|
1198
|
+
@aria_grabbed_true_after,
|
1199
|
+
DATA_ARIA_GRABBED_OF
|
1200
|
+
)
|
1201
|
+
elsif attribute_value == 'false'
|
1202
|
+
force_read_simple(
|
1203
|
+
element,
|
1204
|
+
@aria_grabbed_false_before,
|
1205
|
+
@aria_grabbed_false_after,
|
1206
|
+
DATA_ARIA_GRABBED_OF
|
1207
|
+
)
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
##
|
1212
|
+
# @see Hatemile::AccessibleDisplay#display_all_waiaria_states
|
1213
|
+
def display_all_waiaria_states
|
1214
|
+
elements = @parser.find(
|
1215
|
+
'[aria-busy=true],[aria-checked],[aria-dropeffect],' \
|
1216
|
+
'[aria-expanded],[aria-grabbed],[aria-haspopup],[aria-level],' \
|
1217
|
+
'[aria-orientation],[aria-pressed],[aria-selected],[aria-sort],' \
|
1218
|
+
'[aria-required=true],[aria-valuemin],[aria-valuemax],' \
|
1219
|
+
'[aria-autocomplete]'
|
1220
|
+
).list_results
|
1221
|
+
elements.each do |element|
|
1222
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
1223
|
+
display_waiaria_states(element)
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
##
|
1229
|
+
# @see Hatemile::AccessibleDisplay#display_link_attributes
|
1230
|
+
def display_link_attributes(link)
|
1231
|
+
if link.has_attribute?('download')
|
1232
|
+
force_read_simple(
|
1233
|
+
link,
|
1234
|
+
@attribute_download_before,
|
1235
|
+
@attribute_download_after,
|
1236
|
+
DATA_ATTRIBUTE_DOWNLOAD_OF
|
1237
|
+
)
|
1238
|
+
end
|
1239
|
+
if link.has_attribute?('target') &&
|
1240
|
+
link.get_attribute('target') == '_blank'
|
1241
|
+
force_read_simple(
|
1242
|
+
link,
|
1243
|
+
@attribute_target_blank_before,
|
1244
|
+
@attribute_target_blank_after,
|
1245
|
+
DATA_ATTRIBUTE_TARGET_OF
|
1246
|
+
)
|
1247
|
+
end
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
##
|
1251
|
+
# @see Hatemile::AccessibleDisplay#display_all_links_attributes
|
1252
|
+
def display_all_links_attributes
|
1253
|
+
links = @parser.find('a[download],a[target="_blank"]').list_results
|
1254
|
+
links.each do |link|
|
1255
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(link)
|
1256
|
+
display_link_attributes(link)
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
##
|
1262
|
+
# @see Hatemile::AccessibleDisplay#display_title
|
1263
|
+
def display_title(element)
|
1264
|
+
if element.get_tag_name == 'IMG'
|
1265
|
+
display_alternative_text_image(element)
|
1266
|
+
elsif element.has_attribute?('title') &&
|
1267
|
+
!element.get_attribute('title').empty?
|
1268
|
+
force_read(
|
1269
|
+
element,
|
1270
|
+
element.get_attribute('title').gsub(/[ \n\t\r]+/, ' ').strip,
|
1271
|
+
@attribute_title_prefix_before,
|
1272
|
+
@attribute_title_suffix_before,
|
1273
|
+
@attribute_title_prefix_after,
|
1274
|
+
@attribute_title_suffix_after,
|
1275
|
+
DATA_ATTRIBUTE_TITLE_OF
|
1276
|
+
)
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
##
|
1281
|
+
# @see Hatemile::AccessibleDisplay#display_all_titles
|
1282
|
+
def display_all_titles
|
1283
|
+
elements = @parser.find('body [title]').list_results
|
1284
|
+
elements.each do |element|
|
1285
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
1286
|
+
display_title(element)
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
##
|
1292
|
+
# @see Hatemile::AccessibleDisplay#display_language
|
1293
|
+
def display_language(element)
|
1294
|
+
language_code = if element.has_attribute?('lang')
|
1295
|
+
element.get_attribute('lang')
|
1296
|
+
elsif element.has_attribute?('hreflang')
|
1297
|
+
element.get_attribute('hreflang')
|
1298
|
+
end
|
1299
|
+
language = get_language_description(language_code)
|
1300
|
+
|
1301
|
+
return if language.nil?
|
1302
|
+
|
1303
|
+
force_read(
|
1304
|
+
element,
|
1305
|
+
language,
|
1306
|
+
@attribute_language_prefix_before,
|
1307
|
+
@attribute_language_suffix_before,
|
1308
|
+
@attribute_language_prefix_after,
|
1309
|
+
@attribute_language_suffix_after,
|
1310
|
+
DATA_ATTRIBUTE_LANGUAGE_OF
|
1311
|
+
)
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
##
|
1315
|
+
# @see Hatemile::AccessibleDisplay#display_all_languages
|
1316
|
+
def display_all_languages
|
1317
|
+
elements = @parser.find(
|
1318
|
+
'html[lang],body[lang],body [lang],body [hreflang]'
|
1319
|
+
).list_results
|
1320
|
+
elements.each do |element|
|
1321
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
1322
|
+
display_language(element)
|
1323
|
+
end
|
1324
|
+
end
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
##
|
1328
|
+
# @see Hatemile::AccessibleDisplay#display_alternative_text_image
|
1329
|
+
def display_alternative_text_image(image)
|
1330
|
+
if image.has_attribute?('alt') || image.has_attribute?('title')
|
1331
|
+
if image.has_attribute?('alt') &&
|
1332
|
+
!image.has_attribute?('title')
|
1333
|
+
image.set_attribute('title', image.get_attribute('alt'))
|
1334
|
+
elsif image.has_attribute?('title') &&
|
1335
|
+
!image.has_attribute?('alt')
|
1336
|
+
image.set_attribute('alt', image.get_attribute('title'))
|
1337
|
+
end
|
1338
|
+
@id_generator.generate_id(image)
|
1339
|
+
image.set_attribute(
|
1340
|
+
DATA_ATTRIBUTE_TITLE_OF,
|
1341
|
+
image.get_attribute('id')
|
1342
|
+
)
|
1343
|
+
else
|
1344
|
+
image.set_attribute('alt', '')
|
1345
|
+
image.set_attribute('role', 'presentation')
|
1346
|
+
image.set_attribute('aria-hidden', 'true')
|
1347
|
+
end
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
##
|
1351
|
+
# @see Hatemile::AccessibleDisplay#display_all_alternative_text_images
|
1352
|
+
def display_all_alternative_text_images
|
1353
|
+
images = @parser.find('img').list_results
|
1354
|
+
images.each do |image|
|
1355
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(image)
|
1356
|
+
display_alternative_text_image(image)
|
1357
|
+
end
|
1358
|
+
end
|
1359
|
+
end
|
1360
|
+
end
|
1361
|
+
end
|
1362
|
+
end
|