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,278 @@
|
|
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 File.join(File.dirname(File.dirname(__FILE__)), 'accessible_event')
|
14
|
+
require File.join(File.dirname(File.dirname(__FILE__)), 'helper')
|
15
|
+
require File.join(
|
16
|
+
File.dirname(File.dirname(__FILE__)),
|
17
|
+
'util',
|
18
|
+
'common_functions'
|
19
|
+
)
|
20
|
+
require File.join(
|
21
|
+
File.dirname(File.dirname(__FILE__)),
|
22
|
+
'util',
|
23
|
+
'html',
|
24
|
+
'html_dom_parser'
|
25
|
+
)
|
26
|
+
require File.join(File.dirname(File.dirname(__FILE__)), 'util', 'id_generator')
|
27
|
+
|
28
|
+
##
|
29
|
+
# The Hatemile module contains the interfaces with the acessibility solutions.
|
30
|
+
module Hatemile
|
31
|
+
##
|
32
|
+
# The Hatemile::Implementation module contains the official implementation of
|
33
|
+
# interfaces solutions.
|
34
|
+
module Implementation
|
35
|
+
##
|
36
|
+
# The AccessibleEventImplementation class is official implementation of
|
37
|
+
# AccessibleEvent interface.
|
38
|
+
class AccessibleEventImplementation < AccessibleEvent
|
39
|
+
public_class_method :new
|
40
|
+
|
41
|
+
##
|
42
|
+
# The id of script element that replace the event listener methods.
|
43
|
+
ID_SCRIPT_EVENT_LISTENER = 'script-eventlistener'.freeze
|
44
|
+
|
45
|
+
##
|
46
|
+
# The id of script element that contains the list of elements that has
|
47
|
+
# inaccessible events.
|
48
|
+
ID_LIST_IDS_SCRIPT = 'list-ids-script'.freeze
|
49
|
+
|
50
|
+
##
|
51
|
+
# The id of script element that modify the events of elements.
|
52
|
+
ID_FUNCTION_SCRIPT_FIX = 'id-function-script-fix'.freeze
|
53
|
+
|
54
|
+
##
|
55
|
+
# The ID of script element that contains the common functions of scripts.
|
56
|
+
ID_SCRIPT_COMMON_FUNCTIONS = 'hatemile-common-functions'.freeze
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
##
|
61
|
+
# Provide keyboard access for element, if it not has.
|
62
|
+
#
|
63
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The element.
|
64
|
+
# @return [void]
|
65
|
+
def keyboard_access(element)
|
66
|
+
return if element.has_attribute?('tabindex')
|
67
|
+
|
68
|
+
tag = element.get_tag_name
|
69
|
+
if (tag == 'A') && !element.has_attribute?('href')
|
70
|
+
element.set_attribute('tabindex', '0')
|
71
|
+
elsif (tag != 'A') &&
|
72
|
+
(tag != 'INPUT') &&
|
73
|
+
(tag != 'BUTTON') &&
|
74
|
+
(tag != 'SELECT') &&
|
75
|
+
(tag != 'TEXTAREA')
|
76
|
+
element.set_attribute('tabindex', '0')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Include the scripts used by solutions.
|
82
|
+
#
|
83
|
+
# @return [void]
|
84
|
+
def generate_main_scripts
|
85
|
+
head = @parser.find('head').first_result
|
86
|
+
unless head.nil?
|
87
|
+
common_functions_script = @parser.find(
|
88
|
+
"##{ID_SCRIPT_COMMON_FUNCTIONS}"
|
89
|
+
).first_result
|
90
|
+
if common_functions_script.nil?
|
91
|
+
common_functions_script = @parser.create_element('script')
|
92
|
+
common_functions_script.set_attribute(
|
93
|
+
'id',
|
94
|
+
ID_SCRIPT_COMMON_FUNCTIONS
|
95
|
+
)
|
96
|
+
common_functions_script.set_attribute('type', 'text/javascript')
|
97
|
+
common_functions_script.append_text(
|
98
|
+
File.read(
|
99
|
+
File.join(
|
100
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
101
|
+
'js',
|
102
|
+
'common.js'
|
103
|
+
)
|
104
|
+
)
|
105
|
+
)
|
106
|
+
head.prepend_element(common_functions_script)
|
107
|
+
end
|
108
|
+
|
109
|
+
if @parser.find("##{ID_SCRIPT_EVENT_LISTENER}").first_result.nil?
|
110
|
+
script = @parser.create_element('script')
|
111
|
+
script.set_attribute('id', ID_SCRIPT_EVENT_LISTENER)
|
112
|
+
script.set_attribute('type', 'text/javascript')
|
113
|
+
script.append_text(
|
114
|
+
File.read(
|
115
|
+
File.join(
|
116
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
117
|
+
'js',
|
118
|
+
'eventlistener.js'
|
119
|
+
)
|
120
|
+
)
|
121
|
+
)
|
122
|
+
common_functions_script.insert_after(script)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
local = @parser.find('body').first_result
|
126
|
+
unless local.nil?
|
127
|
+
@script_list = @parser.find("##{ID_LIST_IDS_SCRIPT}").first_result
|
128
|
+
if @script_list.nil?
|
129
|
+
@script_list = @parser.create_element('script')
|
130
|
+
@script_list.set_attribute('id', ID_LIST_IDS_SCRIPT)
|
131
|
+
@script_list.set_attribute('type', 'text/javascript')
|
132
|
+
@script_list.append_text('var activeElements = [];')
|
133
|
+
@script_list.append_text('var hoverElements = [];')
|
134
|
+
@script_list.append_text('var dragElements = [];')
|
135
|
+
@script_list.append_text('var dropElements = [];')
|
136
|
+
local.append_element(@script_list)
|
137
|
+
end
|
138
|
+
if @parser.find("##{ID_FUNCTION_SCRIPT_FIX}").first_result.nil?
|
139
|
+
script_function = @parser.create_element('script')
|
140
|
+
script_function.set_attribute('id', ID_FUNCTION_SCRIPT_FIX)
|
141
|
+
script_function.set_attribute('type', 'text/javascript')
|
142
|
+
script_function.append_text(
|
143
|
+
File.read(
|
144
|
+
File.join(
|
145
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
146
|
+
'js',
|
147
|
+
'include.js'
|
148
|
+
)
|
149
|
+
)
|
150
|
+
)
|
151
|
+
local.append_element(script_function)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
@main_script_added = true
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Add a type of event in element.
|
159
|
+
#
|
160
|
+
# @param element [Hatemile::Util::Html::HTMLDOMElement] The element.
|
161
|
+
# @param event [String] The type of event.
|
162
|
+
# @return [void]
|
163
|
+
def add_event_in_element(element, event)
|
164
|
+
generate_main_scripts unless @main_script_added
|
165
|
+
|
166
|
+
return if @script_list.nil?
|
167
|
+
|
168
|
+
@id_generator.generate_id(element)
|
169
|
+
@script_list.append_text(
|
170
|
+
"#{event}Elements.push('#{element.get_attribute('id')}');"
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
public
|
175
|
+
|
176
|
+
##
|
177
|
+
# Initializes a new object that manipulate the accessibility of the
|
178
|
+
# Javascript events of elements of parser.
|
179
|
+
#
|
180
|
+
# @param parser [Hatemile::Util::Html::HTMLDOMParser] The HTML parser.
|
181
|
+
def initialize(parser)
|
182
|
+
Hatemile::Helper.require_not_nil(parser)
|
183
|
+
Hatemile::Helper.require_valid_type(
|
184
|
+
parser,
|
185
|
+
Hatemile::Util::Html::HTMLDOMParser
|
186
|
+
)
|
187
|
+
|
188
|
+
@parser = parser
|
189
|
+
@id_generator = Hatemile::Util::IDGenerator.new('event')
|
190
|
+
@main_script_added = false
|
191
|
+
@script_list = nil
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# @see Hatemile::AccessibleEvent#make_accessible_drop_events
|
196
|
+
def make_accessible_drop_events(element)
|
197
|
+
element.set_attribute('aria-dropeffect', 'none')
|
198
|
+
|
199
|
+
add_event_in_element(element, 'drop')
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# @see Hatemile::AccessibleEvent#make_accessible_drag_events
|
204
|
+
def make_accessible_drag_events(element)
|
205
|
+
keyboard_access(element)
|
206
|
+
|
207
|
+
element.set_attribute('aria-grabbed', 'false')
|
208
|
+
|
209
|
+
add_event_in_element(element, 'drag')
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# @see Hatemile::AccessibleEvent#make_accessible_all_drag_and_drop_events
|
214
|
+
def make_accessible_all_drag_and_drop_events
|
215
|
+
draggable_elements = @parser.find(
|
216
|
+
'[ondrag],[ondragstart],[ondragend]'
|
217
|
+
).list_results
|
218
|
+
draggable_elements.each do |draggable_element|
|
219
|
+
next unless Hatemile::Util::CommonFunctions.is_valid_element?(
|
220
|
+
draggable_element
|
221
|
+
)
|
222
|
+
|
223
|
+
make_accessible_drag_events(draggable_element)
|
224
|
+
end
|
225
|
+
droppable_elements = @parser.find(
|
226
|
+
'[ondrop],[ondragenter],[ondragleave],[ondragover]'
|
227
|
+
).list_results
|
228
|
+
droppable_elements.each do |droppable_element|
|
229
|
+
next unless Hatemile::Util::CommonFunctions.is_valid_element?(
|
230
|
+
droppable_element
|
231
|
+
)
|
232
|
+
|
233
|
+
make_accessible_drop_events(droppable_element)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# @see Hatemile::AccessibleEvent#make_accessible_hover_events
|
239
|
+
def make_accessible_hover_events(element)
|
240
|
+
keyboard_access(element)
|
241
|
+
|
242
|
+
add_event_in_element(element, 'hover')
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# @see Hatemile::AccessibleEvent#make_accessible_all_hover_events
|
247
|
+
def make_accessible_all_hover_events
|
248
|
+
elements = @parser.find('[onmouseover],[onmouseout]').list_results
|
249
|
+
elements.each do |element|
|
250
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
251
|
+
make_accessible_hover_events(element)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# @see Hatemile::AccessibleEvent#make_accessible_click_events
|
258
|
+
def make_accessible_click_events(element)
|
259
|
+
keyboard_access(element)
|
260
|
+
|
261
|
+
add_event_in_element(element, 'active')
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# @see Hatemile::AccessibleEvent#make_accessible_all_click_events
|
266
|
+
def make_accessible_all_click_events
|
267
|
+
elements = @parser.find(
|
268
|
+
'[onclick],[onmousedown],[onmouseup],[ondblclick]'
|
269
|
+
).list_results
|
270
|
+
elements.each do |element|
|
271
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
272
|
+
make_accessible_click_events(element)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,386 @@
|
|
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 File.join(File.dirname(File.dirname(__FILE__)), 'accessible_form')
|
14
|
+
require File.join(File.dirname(File.dirname(__FILE__)), 'helper')
|
15
|
+
require File.join(File.dirname(__FILE__), 'accessible_event_implementation')
|
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', 'id_generator')
|
22
|
+
require File.join(
|
23
|
+
File.dirname(File.dirname(__FILE__)),
|
24
|
+
'util',
|
25
|
+
'html',
|
26
|
+
'html_dom_parser'
|
27
|
+
)
|
28
|
+
|
29
|
+
##
|
30
|
+
# The Hatemile module contains the interfaces with the acessibility solutions.
|
31
|
+
module Hatemile
|
32
|
+
##
|
33
|
+
# The Hatemile::Implementation module contains the official implementation of
|
34
|
+
# interfaces solutions.
|
35
|
+
module Implementation
|
36
|
+
##
|
37
|
+
# The AccessibleFormImplementation class is official implementation of
|
38
|
+
# AccessibleForm interface.
|
39
|
+
class AccessibleFormImplementation < AccessibleForm
|
40
|
+
public_class_method :new
|
41
|
+
|
42
|
+
##
|
43
|
+
# The ID of script element that contains the list of IDs of fields with
|
44
|
+
# validation.
|
45
|
+
ID_SCRIPT_LIST_VALIDATION_FIELDS =
|
46
|
+
'hatemile-scriptlist-validation-fields'.freeze
|
47
|
+
|
48
|
+
##
|
49
|
+
# The ID of script element that execute validations on fields.
|
50
|
+
ID_SCRIPT_EXECUTE_VALIDATION = 'hatemile-validation-script'.freeze
|
51
|
+
|
52
|
+
##
|
53
|
+
# The client-site required fields list.
|
54
|
+
REQUIRED_FIELDS_LIST = 'required_fields'.freeze
|
55
|
+
|
56
|
+
##
|
57
|
+
# The client-site pattern fields list.
|
58
|
+
PATTERN_FIELDS_LIST = 'pattern_fields'.freeze
|
59
|
+
|
60
|
+
##
|
61
|
+
# The client-site fields with length list.
|
62
|
+
LIMITED_FIELDS_LIST = 'fields_with_length'.freeze
|
63
|
+
|
64
|
+
##
|
65
|
+
# The client-site range fields list.
|
66
|
+
RANGE_FIELDS_LIST = 'range_fields'.freeze
|
67
|
+
|
68
|
+
##
|
69
|
+
# The client-site week fields list.
|
70
|
+
WEEK_FIELDS_LIST = 'week_fields'.freeze
|
71
|
+
|
72
|
+
##
|
73
|
+
# The client-site month fields list.
|
74
|
+
MONTH_FIELDS_LIST = 'month_fields'.freeze
|
75
|
+
|
76
|
+
##
|
77
|
+
# The client-site datetime fields list.
|
78
|
+
DATETIME_FIELDS_LIST = 'datetime_fields'.freeze
|
79
|
+
|
80
|
+
##
|
81
|
+
# The client-site time fields list.
|
82
|
+
TIME_FIELDS_LIST = 'time_fields'.freeze
|
83
|
+
|
84
|
+
##
|
85
|
+
# The client-site date fields list.
|
86
|
+
DATE_FIELDS_LIST = 'date_fields'.freeze
|
87
|
+
|
88
|
+
##
|
89
|
+
# The client-site email fields list.
|
90
|
+
EMAIL_FIELDS_LIST = 'email_fields'.freeze
|
91
|
+
|
92
|
+
##
|
93
|
+
# The client-site URL fields list.
|
94
|
+
URL_FIELDS_LIST = 'url_fields'.freeze
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
##
|
99
|
+
# Returns the appropriate value for attribute aria-autocomplete of field.
|
100
|
+
#
|
101
|
+
# @param field [Hatemile::Util::Html::HTMLDOMElement] The field.
|
102
|
+
# @return [String] The ARIA value of field.
|
103
|
+
def get_aria_autocomplete(field)
|
104
|
+
tag_name = field.get_tag_name
|
105
|
+
type = nil
|
106
|
+
if field.has_attribute?('type')
|
107
|
+
type = field.get_attribute('type').downcase
|
108
|
+
end
|
109
|
+
if (tag_name == 'TEXTAREA') ||
|
110
|
+
(
|
111
|
+
(tag_name == 'INPUT') &&
|
112
|
+
!(
|
113
|
+
(type == 'button') ||
|
114
|
+
(type == 'submit') ||
|
115
|
+
(type == 'reset') ||
|
116
|
+
(type == 'image') ||
|
117
|
+
(type == 'file') ||
|
118
|
+
(type == 'checkbox') ||
|
119
|
+
(type == 'radio') ||
|
120
|
+
(type == 'hidden')
|
121
|
+
)
|
122
|
+
)
|
123
|
+
value = nil
|
124
|
+
if field.has_attribute?('list')
|
125
|
+
list_id = field.get_attribute('list')
|
126
|
+
unless @parser.find("datalist[id=\"#{list_id}\"]").first_result.nil?
|
127
|
+
return 'list'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
if field.has_attribute?('autocomplete')
|
131
|
+
value = field.get_attribute('autocomplete').downcase
|
132
|
+
else
|
133
|
+
form = @parser.find(field).find_ancestors('form').first_result
|
134
|
+
if form.nil? && field.has_attribute?('form')
|
135
|
+
form = @parser.find(
|
136
|
+
"##{field.get_attribute('form')}"
|
137
|
+
).first_result
|
138
|
+
end
|
139
|
+
if !form.nil? && form.has_attribute?('autocomplete')
|
140
|
+
value = form.get_attribute('autocomplete').downcase
|
141
|
+
end
|
142
|
+
end
|
143
|
+
return 'both' if value == 'on'
|
144
|
+
return 'none' if value == 'off'
|
145
|
+
end
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Include the scripts used by solutions.
|
151
|
+
def generate_validation_scripts
|
152
|
+
local = @parser.find('head,body').first_result
|
153
|
+
unless local.nil?
|
154
|
+
if @parser.find(
|
155
|
+
"##{AccessibleEventImplementation::ID_SCRIPT_COMMON_FUNCTIONS}"
|
156
|
+
).first_result.nil?
|
157
|
+
common_functions_script = @parser.create_element('script')
|
158
|
+
common_functions_script.set_attribute(
|
159
|
+
'id',
|
160
|
+
AccessibleEventImplementation::ID_SCRIPT_COMMON_FUNCTIONS
|
161
|
+
)
|
162
|
+
common_functions_script.set_attribute('type', 'text/javascript')
|
163
|
+
common_functions_script.append_text(
|
164
|
+
File.read(
|
165
|
+
File.join(
|
166
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
167
|
+
'js',
|
168
|
+
'common.js'
|
169
|
+
)
|
170
|
+
)
|
171
|
+
)
|
172
|
+
local.prepend_element(common_functions_script)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
@script_list_fields_with_validation = @parser.find(
|
176
|
+
"##{ID_SCRIPT_LIST_VALIDATION_FIELDS}"
|
177
|
+
).first_result
|
178
|
+
if @script_list_fields_with_validation.nil?
|
179
|
+
@script_list_fields_with_validation = @parser.create_element('script')
|
180
|
+
@script_list_fields_with_validation.set_attribute(
|
181
|
+
'id',
|
182
|
+
ID_SCRIPT_LIST_VALIDATION_FIELDS
|
183
|
+
)
|
184
|
+
@script_list_fields_with_validation.set_attribute(
|
185
|
+
'type',
|
186
|
+
'text/javascript'
|
187
|
+
)
|
188
|
+
@script_list_fields_with_validation.append_text(
|
189
|
+
File.read(
|
190
|
+
File.join(
|
191
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
192
|
+
'js',
|
193
|
+
'scriptlist_validation_fields.js'
|
194
|
+
)
|
195
|
+
)
|
196
|
+
)
|
197
|
+
local.append_element(@script_list_fields_with_validation)
|
198
|
+
end
|
199
|
+
if @parser.find("##{ID_SCRIPT_EXECUTE_VALIDATION}").first_result.nil?
|
200
|
+
script_function = @parser.create_element('script')
|
201
|
+
script_function.set_attribute('id', ID_SCRIPT_EXECUTE_VALIDATION)
|
202
|
+
script_function.set_attribute('type', 'text/javascript')
|
203
|
+
script_function.append_text(
|
204
|
+
File.read(
|
205
|
+
File.join(
|
206
|
+
File.dirname(File.dirname(File.dirname(__FILE__))),
|
207
|
+
'js',
|
208
|
+
'validation.js'
|
209
|
+
)
|
210
|
+
)
|
211
|
+
)
|
212
|
+
@parser.find('body').first_result.append_element(script_function)
|
213
|
+
end
|
214
|
+
@scripts_added = true
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Validate the field when its value change.
|
219
|
+
#
|
220
|
+
# @param field [Hatemile::Util::Html::HTMLDOMElement] The field.
|
221
|
+
# @param list_attribute [String] The list attribute of field with
|
222
|
+
# validation.
|
223
|
+
def validate(field, list_attribute)
|
224
|
+
generate_validation_scripts unless @scripts_added
|
225
|
+
@id_generator.generate_id(field)
|
226
|
+
@script_list_fields_with_validation.append_text(
|
227
|
+
"hatemileValidationList.#{list_attribute}.push(" \
|
228
|
+
"'#{field.get_attribute('id')}');"
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
public
|
233
|
+
|
234
|
+
##
|
235
|
+
# Initializes a new object that manipulate the accessibility of the forms
|
236
|
+
# of parser.
|
237
|
+
#
|
238
|
+
# @param parser [Hatemile::Util::Html::HTMLDOMParser] The HTML parser.
|
239
|
+
def initialize(parser)
|
240
|
+
Hatemile::Helper.require_not_nil(parser)
|
241
|
+
Hatemile::Helper.require_valid_type(
|
242
|
+
parser,
|
243
|
+
Hatemile::Util::Html::HTMLDOMParser
|
244
|
+
)
|
245
|
+
|
246
|
+
@parser = parser
|
247
|
+
@id_generator = Hatemile::Util::IDGenerator.new('form')
|
248
|
+
@scripts_added = false
|
249
|
+
@script_list_fields_with_validation = nil
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# @see Hatemile::AccessibleForm#mark_required_field
|
254
|
+
def mark_required_field(required_field)
|
255
|
+
return unless required_field.has_attribute?('required')
|
256
|
+
|
257
|
+
required_field.set_attribute('aria-required', 'true')
|
258
|
+
end
|
259
|
+
|
260
|
+
##
|
261
|
+
# @see Hatemile::AccessibleForm#mark_all_required_fields
|
262
|
+
def mark_all_required_fields
|
263
|
+
required_fields = @parser.find('[required]').list_results
|
264
|
+
required_fields.each do |required_field|
|
265
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(required_field)
|
266
|
+
mark_required_field(required_field)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
##
|
272
|
+
# @see Hatemile::AccessibleForm#mark_range_field
|
273
|
+
def mark_range_field(range_field)
|
274
|
+
if range_field.has_attribute?('min')
|
275
|
+
range_field.set_attribute(
|
276
|
+
'aria-valuemin',
|
277
|
+
range_field.get_attribute('min')
|
278
|
+
)
|
279
|
+
end
|
280
|
+
|
281
|
+
return unless range_field.has_attribute?('max')
|
282
|
+
|
283
|
+
range_field.set_attribute(
|
284
|
+
'aria-valuemax',
|
285
|
+
range_field.get_attribute('max')
|
286
|
+
)
|
287
|
+
end
|
288
|
+
|
289
|
+
##
|
290
|
+
# @see Hatemile::AccessibleForm#mark_all_range_fields
|
291
|
+
def mark_all_range_fields
|
292
|
+
range_fields = @parser.find('[min],[max]').list_results
|
293
|
+
range_fields.each do |range_field|
|
294
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(range_field)
|
295
|
+
mark_range_field(range_field)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# @see Hatemile::AccessibleForm#mark_autocomplete_field
|
302
|
+
def mark_autocomplete_field(autocomplete_field)
|
303
|
+
aria_autocomplete = get_aria_autocomplete(autocomplete_field)
|
304
|
+
|
305
|
+
return if aria_autocomplete.nil?
|
306
|
+
|
307
|
+
autocomplete_field.set_attribute('aria-autocomplete', aria_autocomplete)
|
308
|
+
end
|
309
|
+
|
310
|
+
##
|
311
|
+
# @see Hatemile::AccessibleForm#mark_all_autocomplete_fields
|
312
|
+
def mark_all_autocomplete_fields
|
313
|
+
elements = @parser.find(
|
314
|
+
'input[autocomplete],textarea[autocomplete],form[autocomplete] ' \
|
315
|
+
'input,form[autocomplete] textarea,[list],[form]'
|
316
|
+
).list_results
|
317
|
+
elements.each do |element|
|
318
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(element)
|
319
|
+
mark_autocomplete_field(element)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
##
|
325
|
+
# @see Hatemile::AccessibleForm#mark_invalid_field
|
326
|
+
def mark_invalid_field(field)
|
327
|
+
if field.has_attribute?('required') ||
|
328
|
+
(
|
329
|
+
field.has_attribute?('aria-required') &&
|
330
|
+
field.get_attribute('aria-required').casecmp('true').zero?
|
331
|
+
)
|
332
|
+
validate(field, REQUIRED_FIELDS_LIST)
|
333
|
+
end
|
334
|
+
validate(field, PATTERN_FIELDS_LIST) if field.has_attribute?('pattern')
|
335
|
+
if field.has_attribute?('minlength') ||
|
336
|
+
field.has_attribute?('maxlength')
|
337
|
+
validate(field, LIMITED_FIELDS_LIST)
|
338
|
+
end
|
339
|
+
if field.has_attribute?('aria-valuemin') ||
|
340
|
+
field.has_attribute?('aria-valuemax')
|
341
|
+
validate(field, RANGE_FIELDS_LIST)
|
342
|
+
end
|
343
|
+
|
344
|
+
return unless field.has_attribute?('type')
|
345
|
+
|
346
|
+
type_field = field.get_attribute('type').downcase
|
347
|
+
if type_field == 'week'
|
348
|
+
validate(field, WEEK_FIELDS_LIST)
|
349
|
+
elsif type_field == 'month'
|
350
|
+
validate(field, MONTH_FIELDS_LIST)
|
351
|
+
elsif %w[datetime-local datetime].include?(type_field)
|
352
|
+
validate(field, DATETIME_FIELDS_LIST)
|
353
|
+
elsif type_field == 'time'
|
354
|
+
validate(field, TIME_FIELDS_LIST)
|
355
|
+
elsif type_field == 'date'
|
356
|
+
validate(field, DATE_FIELDS_LIST)
|
357
|
+
elsif %w[number range].include?(type_field)
|
358
|
+
validate(field, RANGE_FIELDS_LIST)
|
359
|
+
elsif type_field == 'email'
|
360
|
+
validate(field, EMAIL_FIELDS_LIST)
|
361
|
+
elsif type_field == 'url'
|
362
|
+
validate(field, AccessibleFormImplementation.URL_FIELDS_LIST)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
##
|
367
|
+
# @see Hatemile::AccessibleForm#mark_all_invalid_fields
|
368
|
+
def mark_all_invalid_fields
|
369
|
+
fields = @parser.find(
|
370
|
+
'[required],input[pattern],input[minlength],input[maxlength],' \
|
371
|
+
'textarea[minlength],textarea[maxlength],input[type=week],' \
|
372
|
+
'input[type=month],input[type=datetime-local],' \
|
373
|
+
'input[type=datetime],input[type=time],input[type=date],' \
|
374
|
+
'input[type=number],input[type=range],input[type=email],' \
|
375
|
+
'input[type=url],[aria-required=true],input[aria-valuemin],' \
|
376
|
+
'input[aria-valuemax]'
|
377
|
+
).list_results
|
378
|
+
fields.each do |field|
|
379
|
+
if Hatemile::Util::CommonFunctions.is_valid_element?(field)
|
380
|
+
mark_invalid_field(field)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|