AXElements 0.8.1 → 0.9.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.
- data/.yardopts +4 -0
- data/History.markdown +41 -0
- data/README.markdown +59 -62
- data/Rakefile +1 -1
- data/ext/accessibility/key_coder/extconf.rb +1 -1
- data/ext/accessibility/key_coder/key_coder.c +8 -5
- data/lib/accessibility/dsl.rb +261 -87
- data/lib/accessibility/enumerators.rb +14 -11
- data/lib/accessibility/errors.rb +4 -3
- data/lib/accessibility/factory.rb +159 -108
- data/lib/accessibility/graph.rb +13 -9
- data/lib/accessibility/{pp_inspector.rb → pretty_printer.rb} +4 -5
- data/lib/accessibility/qualifier.rb +23 -13
- data/lib/accessibility/string.rb +4 -4
- data/lib/accessibility/system_info.rb +230 -0
- data/lib/accessibility/translator.rb +38 -28
- data/lib/accessibility/version.rb +24 -2
- data/lib/accessibility.rb +25 -8
- data/lib/ax/application.rb +207 -77
- data/lib/ax/element.rb +62 -65
- data/lib/ax/menu.rb +5 -1
- data/lib/ax/row.rb +1 -1
- data/lib/ax/scroll_area.rb +7 -6
- data/lib/ax/systemwide.rb +38 -5
- data/lib/ax_elements/active_support_selections.rb +10 -0
- data/lib/ax_elements/mri.rb +57 -0
- data/lib/ax_elements/nsarray_compat.rb +97 -17
- data/lib/ax_elements.rb +9 -1
- data/rakelib/gem.rake +11 -11
- data/rakelib/test.rake +0 -9
- data/test/helper.rb +10 -18
- data/test/integration/accessibility/test_dsl.rb +52 -42
- data/test/integration/accessibility/test_enumerators.rb +0 -1
- data/test/integration/accessibility/test_graph.rb +1 -0
- data/test/integration/accessibility/test_qualifier.rb +2 -2
- data/test/integration/ax/test_application.rb +9 -2
- data/test/integration/ax/test_element.rb +41 -1
- data/test/sanity/accessibility/test_factory.rb +23 -56
- data/test/sanity/accessibility/{test_pp_inspector.rb → test_pretty_printer.rb} +9 -9
- data/test/sanity/accessibility/test_translator.rb +2 -5
- data/test/sanity/accessibility/test_version.rb +15 -0
- data/test/sanity/ax/test_application.rb +17 -2
- data/test/sanity/ax/test_element.rb +2 -2
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +4 -2
- data/test/sanity/test_ax_elements.rb +1 -0
- metadata +69 -39
- data/lib/accessibility/core.rb +0 -973
- data/lib/accessibility/highlighter.rb +0 -86
- data/lib/ax_elements/vendor/inflection_data.rb +0 -66
- data/lib/ax_elements/vendor/inflections.rb +0 -172
- data/lib/ax_elements/vendor/inflector.rb +0 -306
- data/lib/minitest/ax_elements.rb +0 -175
- data/lib/mouse.rb +0 -223
- data/lib/rspec/expectations/ax_elements.rb +0 -234
- data/test/integration/accessibility/test_core.rb +0 -18
- data/test/integration/minitest/test_ax_elements.rb +0 -89
- data/test/integration/rspec/expectations/test_ax_elements.rb +0 -102
- data/test/sanity/accessibility/test_core.rb +0 -561
- data/test/sanity/accessibility/test_highlighter.rb +0 -56
- data/test/sanity/minitest/test_ax_elements.rb +0 -17
- data/test/sanity/rspec/expectations/test_ax_elements.rb +0 -15
- data/test/sanity/test_mouse.rb +0 -19
@@ -8,7 +8,7 @@ module Accessibility::Enumerators
|
|
8
8
|
class BreadthFirst
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
# @param [#children]
|
11
|
+
# @param root [#children]
|
12
12
|
def initialize root
|
13
13
|
@root = root
|
14
14
|
end
|
@@ -16,16 +16,14 @@ module Accessibility::Enumerators
|
|
16
16
|
##
|
17
17
|
# Semi-lazily iterate through the tree.
|
18
18
|
#
|
19
|
+
# @yield An element in the UI hierarchy
|
19
20
|
# @yieldparam [AX::Element,AXUIElementRef]
|
20
21
|
def each
|
21
|
-
# @todo mutate the array less, perhaps use an index instead
|
22
|
-
# of #shift, then the array only grows
|
23
22
|
queue = [@root]
|
24
23
|
until queue.empty?
|
25
|
-
queue.shift.children
|
26
|
-
|
27
|
-
|
28
|
-
end
|
24
|
+
kids = queue.shift.children
|
25
|
+
kids.each do |x| yield x end
|
26
|
+
queue.concat kids
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
@@ -38,6 +36,9 @@ module Accessibility::Enumerators
|
|
38
36
|
# being used...yet.
|
39
37
|
#
|
40
38
|
# Override `Enumerable#find` for performance reasons.
|
39
|
+
#
|
40
|
+
# @yield An element in the UI hierarchy
|
41
|
+
# @yieldparam [AX::Element,AXUIElementRef]
|
41
42
|
def find
|
42
43
|
each { |x| return x if yield x }
|
43
44
|
end
|
@@ -50,11 +51,12 @@ module Accessibility::Enumerators
|
|
50
51
|
class DepthFirst
|
51
52
|
include Enumerable
|
52
53
|
|
53
|
-
# @param [#children]
|
54
|
+
# @param root [#children]
|
54
55
|
def initialize root
|
55
56
|
@root = root
|
56
57
|
end
|
57
58
|
|
59
|
+
# @yield An element in the UI hierarchy
|
58
60
|
# @yieldparam [AX::Element,AXUIElementRef]
|
59
61
|
def each
|
60
62
|
stack = @root.children
|
@@ -70,6 +72,7 @@ module Accessibility::Enumerators
|
|
70
72
|
# Walk the UI element tree and yield both the element and the
|
71
73
|
# level that the element is at relative to the root.
|
72
74
|
#
|
75
|
+
# @yield An element in the UI hierarchy
|
73
76
|
# @yieldparam [AX::Element,AXUIElementRef]
|
74
77
|
# @yieldparam [Number]
|
75
78
|
def each_with_level &block
|
@@ -85,9 +88,9 @@ module Accessibility::Enumerators
|
|
85
88
|
##
|
86
89
|
# Recursive implementation of a depth first iterator.
|
87
90
|
#
|
88
|
-
# @param [AX::Element]
|
89
|
-
# @param [Number]
|
90
|
-
# @param [#call]
|
91
|
+
# @param element [AX::Element]
|
92
|
+
# @param depth [Number]
|
93
|
+
# @param block [#call]
|
91
94
|
def recursive_each_with_level element, depth, block
|
92
95
|
block.call element, depth
|
93
96
|
element.children.each do |x|
|
data/lib/accessibility/errors.rb
CHANGED
@@ -4,9 +4,10 @@ require 'accessibility/qualifier'
|
|
4
4
|
# Error raised when an implicit search fails to return a result.
|
5
5
|
class Accessibility::SearchFailure < NoMethodError
|
6
6
|
|
7
|
-
# @param [AX::Element]
|
8
|
-
# @param [#to_s]
|
9
|
-
# @param [Hash{Symbol=>Object}]
|
7
|
+
# @param searcher [AX::Element]
|
8
|
+
# @param searchee [#to_s]
|
9
|
+
# @param filters [Hash{Symbol=>Object}]
|
10
|
+
# @yield Optional block that would have been used for a search filter
|
10
11
|
def initialize searcher, searchee, filters, &block
|
11
12
|
filters = {} unless filters.kind_of? Hash
|
12
13
|
msg = "Could not find `#{pp_searchee searchee, filters, &block}` "
|
@@ -3,112 +3,27 @@ require 'accessibility/translator'
|
|
3
3
|
|
4
4
|
##
|
5
5
|
# Namespace container for all the accessibility objects.
|
6
|
-
module AX; end
|
6
|
+
module AX; class Element; end end
|
7
7
|
|
8
|
-
##
|
9
|
-
# Mixin made for processing low level data from AXAPI methods.
|
10
|
-
module Accessibility::Factory
|
11
|
-
|
12
|
-
# @todo This should provide alternate #to_ruby functionality for
|
13
|
-
# the __NSCFType class in order to avoid the overhead of
|
14
|
-
# checking type information (or at least reducing it).
|
15
|
-
# However, it will force the lower level to always wrap
|
16
|
-
# element references; this should be ok most of the time
|
17
|
-
# but makes testing a bit of a pain...hmmm
|
18
|
-
|
19
|
-
##
|
20
|
-
# Processes any given data from an AXAPI function and wraps it if
|
21
|
-
# needed. Meant for taking a return value from
|
22
|
-
# {Accessibility::Core#attribute} and friends.
|
23
|
-
#
|
24
|
-
# Generally, used to process an `AXUIElementRef` into a some kind
|
25
|
-
# of {AX::Element} subclass.
|
26
|
-
def process value
|
27
|
-
return nil if value.nil? # CFGetTypeID(nil) crashes runtime
|
28
|
-
case CFGetTypeID(value)
|
29
|
-
when ARRAY_TYPE then process_array value
|
30
|
-
when REF_TYPE then process_element value
|
31
|
-
else
|
32
|
-
value
|
33
|
-
end
|
34
|
-
end
|
35
8
|
|
36
|
-
|
37
|
-
private
|
9
|
+
class << AX
|
38
10
|
|
39
11
|
##
|
40
12
|
# @private
|
41
13
|
#
|
42
|
-
#
|
14
|
+
# Mutex to make sure we only create one class at a time.
|
43
15
|
#
|
44
|
-
# @return [
|
45
|
-
|
16
|
+
# @return [Mutex]
|
17
|
+
MUTEX = Mutex.new
|
46
18
|
|
47
19
|
##
|
48
20
|
# @private
|
49
21
|
#
|
50
|
-
#
|
22
|
+
# Find the class for a given role
|
51
23
|
#
|
52
|
-
#
|
53
|
-
REF_TYPE = AXUIElementGetTypeID()
|
54
|
-
|
55
|
-
##
|
56
|
-
# @private
|
57
|
-
#
|
58
|
-
# Type ID for `CFArrayRef` objects.
|
24
|
+
# If the class does not exist it will be created.
|
59
25
|
#
|
60
|
-
# @
|
61
|
-
ARRAY_TYPE = CFArrayGetTypeID()
|
62
|
-
|
63
|
-
##
|
64
|
-
# @todo Should we handle cases where a subrole has a value of
|
65
|
-
# 'Unknown'? What is the performance impact?
|
66
|
-
#
|
67
|
-
# Takes an `AXUIElementRef` and gives you some kind of wrapped
|
68
|
-
# accessibility object.
|
69
|
-
#
|
70
|
-
# Some code paths have been unrolled for efficiency. Don't hate player,
|
71
|
-
# hate the game.
|
72
|
-
#
|
73
|
-
# @param [AXUIElementRef]
|
74
|
-
# @return [AX::Element]
|
75
|
-
def process_element ref
|
76
|
-
if role = ref.role
|
77
|
-
role = TRANSLATOR.unprefix role
|
78
|
-
attrs = ref.attributes
|
79
|
-
if attrs.include? KAXSubroleAttribute
|
80
|
-
subrole = ref.subrole
|
81
|
-
# Some objects claim to have a subrole but return nil
|
82
|
-
if subrole
|
83
|
-
class_for2(TRANSLATOR.unprefix(subrole), role).new ref
|
84
|
-
else
|
85
|
-
class_for(role).new ref
|
86
|
-
end
|
87
|
-
else
|
88
|
-
class_for(role).new ref
|
89
|
-
end
|
90
|
-
else # failsafe in case object dies before we even get the role
|
91
|
-
AX::Element.new ref
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
##
|
96
|
-
# We assume a homogeneous array and only wrap element arrays right now.
|
97
|
-
#
|
98
|
-
# @return [Array]
|
99
|
-
def process_array vals
|
100
|
-
return vals if vals.empty?
|
101
|
-
return vals if CFGetTypeID(vals.first) != REF_TYPE
|
102
|
-
return vals.map { |val| process_element val }
|
103
|
-
end
|
104
|
-
|
105
|
-
##
|
106
|
-
# @todo Consider using {AX.const_missing} instead.
|
107
|
-
#
|
108
|
-
# Find the class for a given role. If the class does not exist it will
|
109
|
-
# be created on demand.
|
110
|
-
#
|
111
|
-
# @param [#to_s]
|
26
|
+
# @param role [#to_s]
|
112
27
|
# @return [Class]
|
113
28
|
def class_for role
|
114
29
|
if AX.const_defined? role, false
|
@@ -119,14 +34,16 @@ module Accessibility::Factory
|
|
119
34
|
end
|
120
35
|
|
121
36
|
##
|
122
|
-
#
|
123
|
-
# exist it will be created on demand.
|
37
|
+
# @private
|
124
38
|
#
|
125
|
-
#
|
126
|
-
#
|
39
|
+
# Find the class for a given subrole and role
|
40
|
+
#
|
41
|
+
# If the class does not exist it will be created on demand.
|
42
|
+
#
|
43
|
+
# @param subrole [#to_s]
|
44
|
+
# @param role [#to_s]
|
127
45
|
# @return [Class]
|
128
46
|
def class_for2 subrole, role
|
129
|
-
# @todo it would be nice if we didn't have to lookup twice
|
130
47
|
if AX.const_defined? subrole, false
|
131
48
|
AX.const_get subrole
|
132
49
|
else
|
@@ -135,29 +52,163 @@ module Accessibility::Factory
|
|
135
52
|
end
|
136
53
|
|
137
54
|
##
|
138
|
-
#
|
139
|
-
#
|
55
|
+
# @private
|
56
|
+
#
|
57
|
+
# Create a class in the {AX} namespace that has {AX::Element} as the
|
58
|
+
# superclass
|
140
59
|
#
|
141
|
-
# @param [#to_s]
|
60
|
+
# @param name [#to_s]
|
142
61
|
# @return [Class]
|
143
62
|
def create_class name
|
144
|
-
|
145
|
-
|
63
|
+
MUTEX.synchronize do
|
64
|
+
# re-check now that we are in the critical section
|
65
|
+
@klass = if AX.const_defined? name, false
|
66
|
+
AX.const_get name
|
67
|
+
else
|
68
|
+
klass = Class.new AX::Element
|
69
|
+
AX.const_set name, klass
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@klass
|
146
73
|
end
|
147
74
|
|
148
75
|
##
|
76
|
+
# @private
|
77
|
+
#
|
149
78
|
# Create a new class in the {AX} namesapce that has the given
|
150
|
-
# `superklass` as the superclass
|
79
|
+
# `superklass` as the superclass
|
151
80
|
#
|
152
|
-
# @param [#to_s]
|
153
|
-
# @param [#to_s]
|
81
|
+
# @param name [#to_s]
|
82
|
+
# @param superklass [#to_s]
|
154
83
|
# @return [Class]
|
155
84
|
def create_class2 name, superklass
|
156
85
|
unless AX.const_defined? superklass, false
|
157
86
|
create_class superklass
|
158
87
|
end
|
159
|
-
|
160
|
-
|
88
|
+
MUTEX.synchronize do
|
89
|
+
# re-check now that we are in the critical section
|
90
|
+
@klass = if AX.const_defined? name, false
|
91
|
+
AX.const_get name
|
92
|
+
else
|
93
|
+
klass = Class.new AX.const_get(superklass)
|
94
|
+
AX.const_set name, klass
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@klass
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
if on_macruby?
|
104
|
+
|
105
|
+
##
|
106
|
+
# Extensions to {Accessibility::Element} for the higher level abstractions
|
107
|
+
#
|
108
|
+
# These extensions only make sense in the context of the high level API
|
109
|
+
# but needs to be applied on the lower level class, so the code has been
|
110
|
+
# placed in its own file.
|
111
|
+
module Accessibility::Element
|
112
|
+
|
113
|
+
##
|
114
|
+
# @todo Should we handle cases where a subrole has a value of
|
115
|
+
# 'Unknown'? What is the performance impact?
|
116
|
+
#
|
117
|
+
# Wrap the low level wrapper with the appropriate high level wrapper.
|
118
|
+
# This involves determining the proper class in the {AX} namespace,
|
119
|
+
# possibly creating it on demand, and then instantiating the class to
|
120
|
+
# wrap the low level object.
|
121
|
+
#
|
122
|
+
# Some code paths have been unrolled for efficiency. Don't hate player,
|
123
|
+
# hate the game.
|
124
|
+
#
|
125
|
+
# @return [AX::Element]
|
126
|
+
def to_ruby
|
127
|
+
type = AXValueGetType(self)
|
128
|
+
if type.zero?
|
129
|
+
to_element
|
130
|
+
else
|
131
|
+
to_box type
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
##
|
139
|
+
# @private
|
140
|
+
#
|
141
|
+
# Reference to the singleton instance of the translator.
|
142
|
+
#
|
143
|
+
# @return [Accessibility::Translator]
|
144
|
+
TRANSLATOR = Accessibility::Translator.instance
|
145
|
+
|
146
|
+
def to_box type
|
147
|
+
ptr = Pointer.new ValueWrapper::BOX_TYPES[type]
|
148
|
+
AXValueGetValue(self, type, ptr)
|
149
|
+
ptr.value.to_ruby
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_element
|
153
|
+
if roll = self.role
|
154
|
+
roll = TRANSLATOR.unprefix roll
|
155
|
+
if attributes.include? KAXSubroleAttribute
|
156
|
+
subroll = self.subrole
|
157
|
+
# Some objects claim to have a subrole but return nil
|
158
|
+
if subroll
|
159
|
+
AX.class_for2(TRANSLATOR.unprefix(subroll), roll).new self
|
160
|
+
else
|
161
|
+
AX.class_for(roll).new self
|
162
|
+
end
|
163
|
+
else
|
164
|
+
AX.class_for(roll).new self
|
165
|
+
end
|
166
|
+
else # failsafe in case object dies before we get the role
|
167
|
+
AX::Element.new self
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
else
|
175
|
+
|
176
|
+
|
177
|
+
##
|
178
|
+
# `AXElements` extensions to the `Accessibility::Element` class
|
179
|
+
class Accessibility::Element
|
180
|
+
|
181
|
+
##
|
182
|
+
# Override the default `#to_ruby` so that proper classes are
|
183
|
+
# chosen for each object.
|
184
|
+
#
|
185
|
+
# @return [AX::Element]
|
186
|
+
def to_ruby
|
187
|
+
if roll = self.role
|
188
|
+
roll = TRANSLATOR.unprefix roll
|
189
|
+
if attributes.include? KAXSubroleAttribute
|
190
|
+
subroll = self.subrole
|
191
|
+
# Some objects claim to have a subrole but return nil
|
192
|
+
if subroll
|
193
|
+
AX.class_for2(TRANSLATOR.unprefix(subroll), roll).new self
|
194
|
+
else
|
195
|
+
AX.class_for(roll).new self
|
196
|
+
end
|
197
|
+
else
|
198
|
+
AX.class_for(roll).new self
|
199
|
+
end
|
200
|
+
else # failsafe in case object dies before we get the role
|
201
|
+
AX::Element.new self
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# @private
|
207
|
+
#
|
208
|
+
# Reference to the singleton instance of the translator.
|
209
|
+
#
|
210
|
+
# @return [Accessibility::Translator]
|
211
|
+
TRANSLATOR = Accessibility::Translator.instance
|
161
212
|
end
|
162
213
|
|
163
214
|
end
|
data/lib/accessibility/graph.rb
CHANGED
@@ -31,7 +31,7 @@ class Accessibility::Graph
|
|
31
31
|
# @return [AX::Element]
|
32
32
|
attr_reader :element
|
33
33
|
|
34
|
-
# @param [AX::Element]
|
34
|
+
# @param element [AX::Element]
|
35
35
|
def initialize element
|
36
36
|
@element = element
|
37
37
|
@id = "element_#{element.object_id}"
|
@@ -47,7 +47,7 @@ class Accessibility::Graph
|
|
47
47
|
|
48
48
|
def identifier
|
49
49
|
klass = @element.class.to_s.split(NAMESPACE).last
|
50
|
-
ident = @element.pp_identifier.dup
|
50
|
+
ident = @element.pp_identifier.to_s.dup
|
51
51
|
if ident.length > 12
|
52
52
|
ident = "#{ident[0...12]}..."
|
53
53
|
end
|
@@ -58,9 +58,13 @@ class Accessibility::Graph
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def shape
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
if @element.attributes.include?(:focused) && @element.attribute(:focused)
|
62
|
+
OCTAGON
|
63
|
+
elsif @element.actions.empty?
|
64
|
+
OVAL
|
65
|
+
else
|
66
|
+
BOX
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
70
|
def style
|
@@ -69,7 +73,7 @@ class Accessibility::Graph
|
|
69
73
|
return FILLED unless @element.attribute(:enabled)
|
70
74
|
end
|
71
75
|
# bold if focused and no children
|
72
|
-
if @element.attribute(:focused)
|
76
|
+
if @element.attributes.include?(:focused) && @element.attribute(:focused)
|
73
77
|
return BOLD if @element.size_of(:children).zero?
|
74
78
|
end
|
75
79
|
SOLID
|
@@ -128,8 +132,8 @@ class Accessibility::Graph
|
|
128
132
|
# @return [String]
|
129
133
|
attr_accessor :style
|
130
134
|
|
131
|
-
# @param [Accessibility::Graph::Node]
|
132
|
-
# @param [Accessibility::Graph::Node]
|
135
|
+
# @param head [Accessibility::Graph::Node]
|
136
|
+
# @param tail [Accessibility::Graph::Node]
|
133
137
|
def initialize head, tail
|
134
138
|
@head, @tail = head, tail
|
135
139
|
end
|
@@ -155,7 +159,7 @@ class Accessibility::Graph
|
|
155
159
|
# @return [Array<Accessibility::Graph::Edge>]
|
156
160
|
attr_reader :edges
|
157
161
|
|
158
|
-
# @param [AX::Element]
|
162
|
+
# @param root [AX::Element]
|
159
163
|
def initialize root
|
160
164
|
root_node = Node.new(root)
|
161
165
|
@nodes = [root_node]
|
@@ -10,13 +10,13 @@
|
|
10
10
|
# - `#attribute` returns the value of a given attribute
|
11
11
|
# - `#size_of` returns the size for an attribute
|
12
12
|
#
|
13
|
-
module Accessibility::
|
13
|
+
module Accessibility::PrettyPrinter
|
14
14
|
|
15
15
|
##
|
16
16
|
# Create an identifier for the receiver by using various attributes
|
17
17
|
# that should make it very easy to identify the element.
|
18
18
|
#
|
19
|
-
# @return [String]
|
19
|
+
# @return [String,#to_s]
|
20
20
|
def pp_identifier
|
21
21
|
# @note use, or lack of use, of #inspect is intentional for visual effect
|
22
22
|
|
@@ -49,8 +49,7 @@ module Accessibility::PPInspector
|
|
49
49
|
return " id=#{attribute(:identifier)}"
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
return EMPTY_STRING
|
52
|
+
rescue NoMethodError
|
54
53
|
end
|
55
54
|
|
56
55
|
##
|
@@ -88,7 +87,7 @@ module Accessibility::PPInspector
|
|
88
87
|
# is the given attribute, and the check box value will be
|
89
88
|
# determined by the value of the attribute.
|
90
89
|
#
|
91
|
-
# @param [Symbol]
|
90
|
+
# @param attr [Symbol]
|
92
91
|
# @return [String]
|
93
92
|
def pp_checkbox attr
|
94
93
|
" #{attr}[#{attribute(attr) ? CHECKMARK : CROSS }]"
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/blank'
|
2
4
|
require 'accessibility/translator'
|
3
5
|
|
4
6
|
##
|
@@ -22,21 +24,21 @@ class Accessibility::Qualifier
|
|
22
24
|
# element.children.size > 5 && NSContainsRect(element.bounds, rect)
|
23
25
|
# end
|
24
26
|
#
|
25
|
-
# @param [#to_s]
|
26
|
-
# @param [Hash]
|
27
|
+
# @param klass [#to_s]
|
28
|
+
# @param criteria [Hash]
|
27
29
|
# @yield Optional block that can qualify an element
|
28
30
|
def initialize klass, criteria
|
29
31
|
@klass = TRANSLATOR.classify(klass)
|
30
32
|
@criteria = criteria
|
31
33
|
@block = Proc.new if block_given?
|
32
|
-
compile
|
34
|
+
compile!
|
33
35
|
end
|
34
36
|
|
35
37
|
##
|
36
38
|
# Whether or not a candidate object matches the criteria given
|
37
39
|
# at initialization.
|
38
40
|
#
|
39
|
-
# @param [AX::Element]
|
41
|
+
# @param element [AX::Element]
|
40
42
|
def qualifies? element
|
41
43
|
the_right_type?(element) && meets_criteria?(element)
|
42
44
|
end
|
@@ -62,9 +64,9 @@ class Accessibility::Qualifier
|
|
62
64
|
# array. This is done to avoid checking types for each call to
|
63
65
|
# {#qualifies?}.
|
64
66
|
#
|
65
|
-
# @param [Hash]
|
66
|
-
def compile
|
67
|
-
@filters = criteria.map do |key, value|
|
67
|
+
# @param criteria [Hash]
|
68
|
+
def compile!
|
69
|
+
@filters = @criteria.map do |key, value|
|
68
70
|
if value.kind_of? Hash
|
69
71
|
[:subsearch, key, value]
|
70
72
|
elsif key.kind_of? Array
|
@@ -84,7 +86,7 @@ class Accessibility::Qualifier
|
|
84
86
|
# Checks if a candidate object is of the correct class, respecting
|
85
87
|
# that that the class being searched for may not be defined yet.
|
86
88
|
#
|
87
|
-
# @param [AX::Element]
|
89
|
+
# @param element [AX::Element]
|
88
90
|
def the_right_type? element
|
89
91
|
unless @const
|
90
92
|
if AX.const_defined? @klass
|
@@ -100,7 +102,7 @@ class Accessibility::Qualifier
|
|
100
102
|
# Determines if the element meets all the criteria of the filters,
|
101
103
|
# spawning sub-searches if necessary.
|
102
104
|
#
|
103
|
-
# @param [AX::Element]
|
105
|
+
# @param element [AX::Element]
|
104
106
|
def meets_criteria? element
|
105
107
|
@filters.all? do |filter|
|
106
108
|
self.send *filter, element
|
@@ -112,19 +114,27 @@ class Accessibility::Qualifier
|
|
112
114
|
end
|
113
115
|
|
114
116
|
def match attr, regexp, element
|
115
|
-
element.
|
117
|
+
if element.attributes.include? attr
|
118
|
+
element.attribute(attr).to_s.match regexp
|
119
|
+
end
|
116
120
|
end
|
117
121
|
|
118
122
|
def equality attr, value, element
|
119
|
-
element.
|
123
|
+
if element.attributes.include? attr
|
124
|
+
element.attribute(attr) == value
|
125
|
+
end
|
120
126
|
end
|
121
127
|
|
122
128
|
def parameterized_match attr, param, regexp, element
|
123
|
-
element.
|
129
|
+
if element.parameterized_attributes.include? attr
|
130
|
+
element.parameterized_attribute(attr, param).to_s.match regexp
|
131
|
+
end
|
124
132
|
end
|
125
133
|
|
126
134
|
def parameterized_equality attr, param, value, element
|
127
|
-
element.
|
135
|
+
if element.parameterized_attributes.include? attr
|
136
|
+
element.parameterized_attribute(attr, param) == value
|
137
|
+
end
|
128
138
|
end
|
129
139
|
|
130
140
|
def block_check element
|
data/lib/accessibility/string.rb
CHANGED
@@ -26,9 +26,9 @@ module Accessibility::String
|
|
26
26
|
# {Accessibility::String::EventGenerator::CUSTOM}.
|
27
27
|
#
|
28
28
|
# For more details on event generation, read the
|
29
|
-
# [Keyboarding
|
29
|
+
# [Keyboarding wiki](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
|
30
30
|
#
|
31
|
-
# @param [
|
31
|
+
# @param string [#to_s]
|
32
32
|
# @return [Array<Array(Fixnum,Boolean)>]
|
33
33
|
def keyboard_events_for string
|
34
34
|
EventGenerator.new(Lexer.new(string).lex).generate
|
@@ -55,7 +55,7 @@ module Accessibility::String
|
|
55
55
|
# @return [Array<String,Array<String,...>]
|
56
56
|
attr_accessor :tokens
|
57
57
|
|
58
|
-
# @param [#to_s]
|
58
|
+
# @param string [#to_s]
|
59
59
|
def initialize string
|
60
60
|
@chars = string.to_s
|
61
61
|
@tokens = []
|
@@ -395,7 +395,7 @@ module Accessibility::String
|
|
395
395
|
# @return [Array<Array(Fixnum,Boolean)>]
|
396
396
|
attr_reader :events
|
397
397
|
|
398
|
-
# @param [Array<String,Array<String,Array...>>]
|
398
|
+
# @param tokens [Array<String,Array<String,Array...>>]
|
399
399
|
def initialize tokens
|
400
400
|
@tokens = tokens
|
401
401
|
# *3 since the output array will be at least *2 the
|