vapir-common 1.8.1 → 1.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/lib/vapir-common.rb +2 -0
- data/lib/vapir-common/config.rb +29 -7
- data/lib/vapir-common/container.rb +124 -56
- data/lib/vapir-common/element.rb +11 -2
- data/lib/vapir-common/element_class_and_module.rb +1 -1
- data/lib/vapir-common/elements/elements.rb +1 -0
- data/lib/vapir-common/external/core_extensions.rb +14 -4
- data/lib/vapir-common/specifier.rb +20 -7
- data/lib/vapir-common/version.rb +1 -1
- metadata +5 -5
data/lib/vapir-common.rb
CHANGED
@@ -4,6 +4,8 @@ require 'vapir-common/browser'
|
|
4
4
|
require 'vapir-common/exceptions'
|
5
5
|
require 'vapir-common/config'
|
6
6
|
module Vapir
|
7
|
+
# requires the winwindow library, raising a more informative error message than the default
|
8
|
+
# on failure.
|
7
9
|
def self.require_winwindow
|
8
10
|
begin
|
9
11
|
require 'winwindow'
|
data/lib/vapir-common/config.rb
CHANGED
@@ -5,6 +5,7 @@ module Vapir
|
|
5
5
|
class Error < StandardError; end
|
6
6
|
class BadKeyError < Error; end
|
7
7
|
class NoValueError < Error; end
|
8
|
+
class InvalidValueError < Error; end
|
8
9
|
|
9
10
|
# represents a valid option on a Configuration. consists of a key and criteria
|
10
11
|
# for which a value is valid for that key.
|
@@ -34,7 +35,7 @@ module Vapir
|
|
34
35
|
when 'false', false
|
35
36
|
false
|
36
37
|
else
|
37
|
-
raise
|
38
|
+
raise InvalidValueError, "value should look like a boolean for key #{key}; instead got #{value.inspect}"
|
38
39
|
end
|
39
40
|
when :numeric
|
40
41
|
case value
|
@@ -44,13 +45,21 @@ module Vapir
|
|
44
45
|
begin
|
45
46
|
Float(value)
|
46
47
|
rescue ArgumentError
|
47
|
-
raise
|
48
|
+
raise InvalidValueError, "value should look like a number for key #{key}; instead got #{value.inspect}"
|
48
49
|
end
|
49
50
|
else
|
50
|
-
raise
|
51
|
+
raise InvalidValueError, "value should look like a number for key #{key}; instead got #{value.inspect}"
|
52
|
+
end
|
53
|
+
when :positive_integer
|
54
|
+
if value.is_a?(Integer) && value > 0
|
55
|
+
value
|
56
|
+
elsif value.is_a?(String) && value.strip =~ /\A\d+\z/ && value.to_i > 0
|
57
|
+
value.to_i
|
58
|
+
else
|
59
|
+
raise InvalidValueError, "value should be a positive integer; got #{value.inspect}"
|
51
60
|
end
|
52
61
|
else
|
53
|
-
raise ArgumentError, "invalid validator given: #{@
|
62
|
+
raise ArgumentError, "invalid validator given: #{@validator.inspect}\nvalidator should be nil for unspecified, a Proc, or a symbol indicating a known validator type"
|
54
63
|
end
|
55
64
|
end
|
56
65
|
end
|
@@ -129,6 +138,14 @@ module Vapir
|
|
129
138
|
def defined_key?(key)
|
130
139
|
locally_defined_key?(key) || (parent && parent.defined_key?(key))
|
131
140
|
end
|
141
|
+
# returns an array of keys defined on the current configuration
|
142
|
+
def defined_keys
|
143
|
+
recognized_keys.select{|key| defined_key?(key) }
|
144
|
+
end
|
145
|
+
# returns a hash of currently defined configuration keys and values
|
146
|
+
def defined_hash
|
147
|
+
(@parent ? @parent.defined_hash : {}).merge(@config_hash).freeze
|
148
|
+
end
|
132
149
|
# raises an error if the given key is not in an acceptable format. the key should be a string
|
133
150
|
# or symbol consisting of alphanumerics and underscorse, beginning with an alpha or underscore.
|
134
151
|
def validate_key_format!(key)
|
@@ -212,7 +229,7 @@ module Vapir
|
|
212
229
|
end
|
213
230
|
# see Configuration#with_config
|
214
231
|
def with_config(hash, &block)
|
215
|
-
|
232
|
+
config.with_config(hash, &block)
|
216
233
|
end
|
217
234
|
private
|
218
235
|
# takes a hash of given options, a map of config keys, and a list of other allowed keys.
|
@@ -287,8 +304,9 @@ module Vapir
|
|
287
304
|
end
|
288
305
|
end
|
289
306
|
|
307
|
+
# :stopdoc:
|
290
308
|
@configurations = []
|
291
|
-
def (@configurations).update_from_source
|
309
|
+
def (@configurations).update_from_source # :nodoc:
|
292
310
|
self.each do |c|
|
293
311
|
if c.respond_to?(:update_from_source)
|
294
312
|
c.update_from_source
|
@@ -298,15 +316,18 @@ module Vapir
|
|
298
316
|
|
299
317
|
@configurations.push(@base_configuration=Configuration.new(nil) do |config|
|
300
318
|
config.create_update(:attach_timeout, 30, :validator => :numeric)
|
319
|
+
config.create_update(:close_timeout, 16, :validator => :numeric)
|
320
|
+
config.create_update(:quit_timeout, 8, :validator => :numeric)
|
301
321
|
config.create(:default_browser, :validator => proc do |val|
|
302
322
|
require 'vapir-common/browsers'
|
303
323
|
unless (val.is_a?(String) || val.is_a?(Symbol)) && (real_key = Vapir::SupportedBrowsers.keys.detect{|key| key.to_s==val.to_s })
|
304
|
-
raise
|
324
|
+
raise Vapir::Configuration::InvalidValueError, "default_browser should be a string or symbol matching a supported browser - one of: #{Vapir::SupportedBrowsers.keys.join(', ')}. instead got #{val.inspect}"
|
305
325
|
end
|
306
326
|
real_key
|
307
327
|
end)
|
308
328
|
config.create_update(:highlight_color, 'yellow')
|
309
329
|
config.create_update(:wait, true, :validator => :boolean)
|
330
|
+
config.create_update(:wait_timeout, 120, :validator => :numeric)
|
310
331
|
config.create_update(:type_keys, false, :validator => :boolean)
|
311
332
|
config.create_update(:typing_interval, 0, :validator => :numeric)
|
312
333
|
config.create_update(:warn_deprecated, true, :validator => :boolean)
|
@@ -337,5 +358,6 @@ module Vapir
|
|
337
358
|
@env_configuration.update_from_source
|
338
359
|
|
339
360
|
@configuration_parent = @configurations.last
|
361
|
+
# :startdoc:
|
340
362
|
extend Configurable # makes Vapir.config which is the in-process user-configurable one, overriding base, yaml, and env
|
341
363
|
end
|
@@ -180,63 +180,8 @@ module Vapir
|
|
180
180
|
# returns an array of text nodes below this element in the DOM heirarchy which are visible -
|
181
181
|
# that is, their parent element is visible.
|
182
182
|
def visible_text_nodes
|
183
|
-
# TODO: needs tests
|
184
183
|
assert_exists do
|
185
|
-
|
186
|
-
proc do |node, parent_visibility|
|
187
|
-
case node.nodeType
|
188
|
-
when 1, 9 # TODO: name a constant ELEMENT_NODE, rather than magic number
|
189
|
-
style= node.nodeType==1 ? base_element_class.element_object_style(node, document_object) : nil
|
190
|
-
our_visibility = style && (visibility=style.invoke('visibility'))
|
191
|
-
unless our_visibility && ['hidden', 'collapse', 'visible'].include?(our_visibility=our_visibility.strip.downcase)
|
192
|
-
our_visibility = parent_visibility
|
193
|
-
end
|
194
|
-
display = style && style.invoke('display')
|
195
|
-
if display && display.strip.downcase=='none'
|
196
|
-
[]
|
197
|
-
else
|
198
|
-
Vapir::Element.object_collection_to_enumerable(node.childNodes).inject([]) do |result, child_node|
|
199
|
-
result + recurse.call(child_node, our_visibility)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
when 3 # TODO: name a constant TEXT_NODE, rather than magic number
|
203
|
-
if parent_visibility && ['hidden','collapse'].include?(parent_visibility.downcase)
|
204
|
-
[]
|
205
|
-
else
|
206
|
-
[node.data]
|
207
|
-
end
|
208
|
-
else
|
209
|
-
#Kernel.warn("ignoring node of type #{node.nodeType}")
|
210
|
-
[]
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
# determine the current visibility and display. TODO: this is copied/adapted from #visible?; should DRY
|
216
|
-
element_to_check=containing_object
|
217
|
-
real_visibility=nil
|
218
|
-
while element_to_check #&& !element_to_check.instanceof(nsIDOMDocument)
|
219
|
-
if (style=base_element_class.element_object_style(element_to_check, document_object))
|
220
|
-
# only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements),
|
221
|
-
# or 'visible'. ignore 'inherit'; keep looking upward.
|
222
|
-
# this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up.
|
223
|
-
# this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
|
224
|
-
if real_visibility==nil && (visibility=style.invoke('visibility'))
|
225
|
-
visibility=visibility.strip.downcase
|
226
|
-
if ['hidden', 'collapse', 'visible'].include?(visibility)
|
227
|
-
real_visibility=visibility
|
228
|
-
end
|
229
|
-
end
|
230
|
-
# check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible'
|
231
|
-
display=style.invoke('display')
|
232
|
-
if display && (display=display.strip.downcase)=='none'
|
233
|
-
# if display is none, then this element is not visible, and thus has no visible text nodes underneath.
|
234
|
-
return []
|
235
|
-
end
|
236
|
-
end
|
237
|
-
element_to_check=element_to_check.parentNode
|
238
|
-
end
|
239
|
-
recurse_text_nodes.call(containing_object, real_visibility)
|
184
|
+
visible_text_nodes_method.call(containing_object, document_object)
|
240
185
|
end
|
241
186
|
end
|
242
187
|
# returns an visible text inside this element by concatenating text nodes below this element in the DOM heirarchy which are visible.
|
@@ -286,6 +231,128 @@ module Vapir
|
|
286
231
|
element_class
|
287
232
|
end
|
288
233
|
|
234
|
+
# takes one argument, a proc.
|
235
|
+
# this will be yielded successive dom nodes, and should return true if the node matches whatever
|
236
|
+
# criteria you care to match; false otherwise.
|
237
|
+
#
|
238
|
+
# returns an ElementCollection consisting of the deepest elements within the dom heirarchy
|
239
|
+
# which match the given match_proc_or_function.
|
240
|
+
def base_innermost_by_node(match_proc)
|
241
|
+
ElementCollection.new(self, base_element_class, extra_for_contained.merge(:candidates => proc do |container|
|
242
|
+
ycomb do |innermost_matching_nodes|
|
243
|
+
proc do |container_node|
|
244
|
+
child_nodes = Vapir::Element.object_collection_to_enumerable(container_node.childNodes)
|
245
|
+
matched_child_elements=child_nodes.select do |node|
|
246
|
+
node.nodeType==1 && match_proc.call(node)
|
247
|
+
end
|
248
|
+
if matched_child_elements.empty?
|
249
|
+
[container_node]
|
250
|
+
else
|
251
|
+
matched_child_elements.map do |matched_child_element|
|
252
|
+
innermost_matching_nodes.call(matched_child_element)
|
253
|
+
end.inject([], &:+)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end.call(container.containing_object)
|
257
|
+
end))
|
258
|
+
end
|
259
|
+
alias innermost_by_node base_innermost_by_node
|
260
|
+
# takes text or regexp, and returns an ElementCollection consisting of deepest (innermost) elements in the dom heirarchy whose visible text
|
261
|
+
# matches what's given (by substring for text; by regexp match for regexp)
|
262
|
+
def base_innermost_matching_visible_text(text_or_regexp)
|
263
|
+
innermost_by_node(proc do |node|
|
264
|
+
visible_text_nodes_method.call(node, document_object).join('')[text_or_regexp] # String#[] works with either text or regexp - returns the matched substring or nil
|
265
|
+
end)
|
266
|
+
end
|
267
|
+
alias innermost_matching_visible_text base_innermost_matching_visible_text
|
268
|
+
|
269
|
+
private
|
270
|
+
# returns a proc that takes a node and a document object, and returns
|
271
|
+
# true if the element's display property will allow it to be displayed; false if not.
|
272
|
+
def element_displayed_method
|
273
|
+
@element_displayed_method ||= proc do |node, document_object|
|
274
|
+
style= node.nodeType==1 ? base_element_class.element_object_style(node, document_object) : nil
|
275
|
+
display = style && style.invoke('display')
|
276
|
+
displayed = display ? display.strip.downcase!='none' : true
|
277
|
+
displayed
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# returns a proc that takes a node and a document object, and returns
|
282
|
+
# the visibility of that node, obtained by ascending the dom until an explicit
|
283
|
+
# definition for visibility is found.
|
284
|
+
def element_real_visibility_method
|
285
|
+
@element_real_visibility_method ||= proc do |element_to_check, document_object|
|
286
|
+
real_visibility=nil
|
287
|
+
while element_to_check && real_visibility==nil
|
288
|
+
style = base_element_class.element_object_style(element_to_check, document_object)
|
289
|
+
if style
|
290
|
+
# only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements),
|
291
|
+
# or 'visible'. ignore 'inherit'; keep looking upward.
|
292
|
+
# this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up.
|
293
|
+
# this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
|
294
|
+
if style.invoke('visibility')
|
295
|
+
visibility=style.invoke('visibility').strip.downcase
|
296
|
+
if ['hidden', 'collapse', 'visible'].include?(visibility)
|
297
|
+
real_visibility=visibility
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
element_to_check=element_to_check.parentNode
|
302
|
+
end
|
303
|
+
real_visibility
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# returns a proc that takes a node and a document object, and returns
|
308
|
+
# an Array of strings, each of which is the data of a text node beneath the given node which
|
309
|
+
# is visible.
|
310
|
+
def visible_text_nodes_method
|
311
|
+
@visible_text_nodes_method ||= proc do |element_object, document_object|
|
312
|
+
recurse_text_nodes=ycomb do |recurse|
|
313
|
+
proc do |node, parent_visibility|
|
314
|
+
case node.nodeType
|
315
|
+
when 1, 9 # TODO: name a constant ELEMENT_NODE, rather than magic number
|
316
|
+
style= node.nodeType==1 ? base_element_class.element_object_style(node, document_object) : nil
|
317
|
+
our_visibility = style && (visibility=style.invoke('visibility'))
|
318
|
+
unless our_visibility && ['hidden', 'collapse', 'visible'].include?(our_visibility=our_visibility.strip.downcase)
|
319
|
+
our_visibility = parent_visibility
|
320
|
+
end
|
321
|
+
if !element_displayed_method.call(node, document_object)
|
322
|
+
[]
|
323
|
+
else
|
324
|
+
Vapir::Element.object_collection_to_enumerable(node.childNodes).inject([]) do |result, child_node|
|
325
|
+
result + recurse.call(child_node, our_visibility)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
when 3 # TODO: name a constant TEXT_NODE, rather than magic number
|
329
|
+
if parent_visibility && ['hidden','collapse'].include?(parent_visibility.downcase)
|
330
|
+
[]
|
331
|
+
else
|
332
|
+
[node.data]
|
333
|
+
end
|
334
|
+
else
|
335
|
+
#Kernel.warn("ignoring node of type #{node.nodeType}")
|
336
|
+
[]
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# determine the current visibility and display.
|
342
|
+
element_to_check=element_object
|
343
|
+
while element_to_check
|
344
|
+
if !element_displayed_method.call(element_to_check, document_object)
|
345
|
+
# check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible'
|
346
|
+
# if display is none, then this element is not visible, and thus has no visible text nodes underneath.
|
347
|
+
return []
|
348
|
+
end
|
349
|
+
element_to_check=element_to_check.parentNode
|
350
|
+
end
|
351
|
+
recurse_text_nodes.call(element_object, element_real_visibility_method.call(element_object, document_object))
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
public
|
289
356
|
# shows the available objects on the current container.
|
290
357
|
# This is usually only used for debugging or writing new test scripts.
|
291
358
|
# This is a nice feature to help find out what HTML objects are on a page
|
@@ -333,3 +400,4 @@ module Vapir
|
|
333
400
|
end
|
334
401
|
end
|
335
402
|
end
|
403
|
+
|
data/lib/vapir-common/element.rb
CHANGED
@@ -449,7 +449,7 @@ module Vapir
|
|
449
449
|
def visible?
|
450
450
|
assert_exists do
|
451
451
|
element_to_check=element_object
|
452
|
-
#nsIDOMDocument=
|
452
|
+
#nsIDOMDocument=firefox_socket.Components.interfaces.nsIDOMDocument
|
453
453
|
really_visible=nil
|
454
454
|
while element_to_check #&& !element_to_check.instanceof(nsIDOMDocument)
|
455
455
|
if (style=element_object_style(element_to_check, document_object))
|
@@ -579,22 +579,28 @@ module Vapir
|
|
579
579
|
@container
|
580
580
|
end
|
581
581
|
|
582
|
+
# the Vapir::Browser this element is on
|
582
583
|
attr_reader :browser
|
584
|
+
# the Vapir::PageContainer containing this element (a Browser, Frame, or ModalDialogDocument)
|
583
585
|
attr_reader :page_container
|
584
586
|
|
585
587
|
def document_object
|
586
588
|
assert_container
|
587
589
|
@container.document_object
|
588
590
|
end
|
591
|
+
# returns the content window object of the current page on the browser (this is the 'window'
|
592
|
+
# object in javascript).
|
589
593
|
def content_window_object
|
590
594
|
assert_container
|
591
595
|
@container.content_window_object
|
592
596
|
end
|
597
|
+
# returns the underlying object representing the browser.
|
593
598
|
def browser_window_object
|
594
599
|
assert_container
|
595
600
|
@container.browser_window_object
|
596
601
|
end
|
597
602
|
|
603
|
+
# used by inspect, to_s, and pretty_print to determine what to show
|
598
604
|
def attributes_for_stringifying
|
599
605
|
attributes_to_inspect=self.class.attributes_to_inspect
|
600
606
|
unless exists?
|
@@ -621,6 +627,9 @@ module Vapir
|
|
621
627
|
" "+attr.first+'='+attr.last.inspect
|
622
628
|
end.join('') + ">"
|
623
629
|
end
|
630
|
+
# returns a string representation of this element with each attribute on its own line. this
|
631
|
+
# returns the same information as #inspect, but formatted somewhat more readably. you might
|
632
|
+
# also be interested in pretty-printing the element; see the pp library.
|
624
633
|
def to_s
|
625
634
|
attrs=attributes_for_stringifying
|
626
635
|
longest_label=attrs.inject(0) {|max, attr| [max, attr.first.size].max }
|
@@ -647,7 +656,7 @@ module Vapir
|
|
647
656
|
def object_collection_to_enumerable(object)
|
648
657
|
if object.is_a?(Enumerable)
|
649
658
|
object
|
650
|
-
elsif Object.const_defined?('
|
659
|
+
elsif Object.const_defined?('JavascriptObject') && object.is_a?(JavascriptObject)
|
651
660
|
object.to_array
|
652
661
|
elsif Object.const_defined?('WIN32OLE') && object.is_a?(WIN32OLE)
|
653
662
|
array=[]
|
@@ -4,7 +4,7 @@ module Vapir
|
|
4
4
|
# this module is for methods that should go on both common element modules (ie, TextField) as well
|
5
5
|
# as browser-specific element classes (ie, Firefox::TextField).
|
6
6
|
module ElementClassAndModuleMethods
|
7
|
-
# takes an element_object (
|
7
|
+
# takes an element_object (JavascriptObject or WIN32OLE), and finds the most specific class
|
8
8
|
# that is < self whose specifiers match it. Returns an instance of that class using the given
|
9
9
|
# element_object.
|
10
10
|
#
|
@@ -178,6 +178,7 @@ module Vapir
|
|
178
178
|
# Raises ObjectDisabledException if the object is disabled
|
179
179
|
# Raises ObjectReadOnlyException if the object is read only
|
180
180
|
def append(value, options={})
|
181
|
+
raise ArgumentError, "Text field value must be a string! Got #{value.inspect}" unless value.is_a?(String)
|
181
182
|
options={:blur => true, :change => true, :select => true, :focus => true}.merge(options)
|
182
183
|
assert_enabled
|
183
184
|
assert_not_readonly
|
@@ -34,13 +34,23 @@ unless :to_proc.respond_to?(:to_proc)
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
class Hash
|
38
|
+
# returns a hash whose keys are the intersection of the keys of this hash and the keys given
|
39
|
+
# as arguments to this function. values are the same as in this hash.
|
40
|
+
def select_keys(*keys)
|
41
|
+
keys.inject(self.class.new) do |hash,key|
|
42
|
+
self.key?(key) ? hash.merge(key => self[key]) : hash
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
37
47
|
module Kernel
|
38
48
|
# this is the Y-combinator, which allows anonymous recursive functions. for a simple example,
|
39
49
|
# to define a recursive function to return the length of an array:
|
40
50
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
51
|
+
# length = ycomb do |len|
|
52
|
+
# proc{|list| list == [] ? 0 : len.call(list[1..-1]) }
|
53
|
+
# end
|
44
54
|
#
|
45
55
|
# see https://secure.wikimedia.org/wikipedia/en/wiki/Fixed_point_combinator#Y_combinator
|
46
56
|
# and chapter 9 of the little schemer, available as the sample chapter at http://www.ccs.neu.edu/home/matthias/BTLS/
|
@@ -50,7 +60,7 @@ module Kernel
|
|
50
60
|
module_function :ycomb
|
51
61
|
|
52
62
|
def warn_with_caller(message)
|
53
|
-
Kernel.warn "#{message}\ncalled from: #{caller[1..-1].map{|c|"\n\t"+c}}"
|
63
|
+
Kernel.warn "#{message}\ncalled from: #{caller[1..-1].map{|c|"\n\t"+c}.join('')}"
|
54
64
|
end
|
55
65
|
module_function :warn_with_caller
|
56
66
|
end
|
@@ -109,6 +109,16 @@ module Vapir
|
|
109
109
|
names.first.is_a?(String) &&
|
110
110
|
container.containing_object.object_respond_to?(:getElementsByName) &&
|
111
111
|
specifiers.all?{|specifier| specifier[:tagName].is_a?(String) && %w(BUTTON TEXTAREA APPLET SELECT FORM FRAME IFRAME IMG A INPUT OBJECT MAP PARAM META).include?(specifier[:tagName].upcase) }
|
112
|
+
# thing to determine if object_respond_to?(:getElementsByClassName) is lying. returns
|
113
|
+
# true if the thing is full of lies.
|
114
|
+
getElementsByClassName_lies = proc do
|
115
|
+
Object.const_defined?('WIN32OLERuntimeError') && begin
|
116
|
+
container.containing_object.getElementsByClassName('dummy')
|
117
|
+
false
|
118
|
+
rescue WIN32OLERuntimeError, NoMethodError
|
119
|
+
true
|
120
|
+
end
|
121
|
+
end
|
112
122
|
if can_use_getElementById
|
113
123
|
candidates= if by_id=container.containing_object.getElementById(ids.first)
|
114
124
|
[by_id]
|
@@ -117,7 +127,7 @@ module Vapir
|
|
117
127
|
end
|
118
128
|
elsif can_use_getElementsByName
|
119
129
|
candidates=container.containing_object.getElementsByName(names.first)#.to_array
|
120
|
-
elsif classNames.size==1 && classNames.first.is_a?(String) && container.containing_object.object_respond_to?(:getElementsByClassName)
|
130
|
+
elsif classNames.size==1 && classNames.first.is_a?(String) && container.containing_object.object_respond_to?(:getElementsByClassName) && !getElementsByClassName_lies.call
|
121
131
|
candidates=container.containing_object.getElementsByClassName(classNames.first)
|
122
132
|
elsif tags.size==1 && tags.first.is_a?(String)
|
123
133
|
candidates=container.containing_object.getElementsByTagName(tags.first)
|
@@ -127,7 +137,7 @@ module Vapir
|
|
127
137
|
# return:
|
128
138
|
if candidates.is_a?(Array)
|
129
139
|
candidates
|
130
|
-
elsif Object.const_defined?('
|
140
|
+
elsif Object.const_defined?('JavascriptObject') && candidates.is_a?(JavascriptObject)
|
131
141
|
candidates.to_array
|
132
142
|
elsif Object.const_defined?('WIN32OLE') && candidates.is_a?(WIN32OLE)
|
133
143
|
candidates.send :extend, Enumerable
|
@@ -141,15 +151,15 @@ module Vapir
|
|
141
151
|
unless specifiers_list.is_a?(Enumerable) && specifiers_list.all?{|spec| spec.is_a?(Hash)}
|
142
152
|
raise ArgumentError, "specifiers_list should be a list of Hashes!"
|
143
153
|
end
|
144
|
-
if Object.const_defined?('
|
145
|
-
# optimize for
|
154
|
+
if Object.const_defined?('JavascriptObject') && (candidates.is_a?(JavascriptObject) || (candidates.length != 0 && candidates.all?{|c| c.is_a?(JavascriptObject)}))
|
155
|
+
# optimize for firefox by moving code to the other side of the socket, rather than talking across it a whole lot
|
146
156
|
# this javascript should be exactly the same as the ruby in the else (minus WIN32OLE optimization) -
|
147
157
|
# just written in javascript instead of ruby.
|
148
158
|
#
|
149
159
|
# Note that the else block works perfectly fine, but is much much slower due to the amount of
|
150
160
|
# socket activity.
|
151
|
-
|
152
|
-
match_candidates_js=
|
161
|
+
firefox_socket= candidates.is_a?(JavascriptObject) ? candidates.firefox_socket : candidates.first.firefox_socket
|
162
|
+
match_candidates_js=JavascriptObject.new("
|
153
163
|
(function(candidates, specifiers_list, aliases)
|
154
164
|
{ candidates=$A(candidates);
|
155
165
|
specifiers_list=$A(specifiers_list);
|
@@ -214,7 +224,7 @@ module Vapir
|
|
214
224
|
});
|
215
225
|
return matched_candidates;
|
216
226
|
})
|
217
|
-
",
|
227
|
+
", firefox_socket, :debug_name => 'match_candidates_function')
|
218
228
|
matched_candidates=match_candidates_js.call(candidates, specifiers_list, aliases)
|
219
229
|
if block_given?
|
220
230
|
matched_candidates.to_array.each do |matched_candidate|
|
@@ -301,6 +311,9 @@ module Vapir
|
|
301
311
|
|
302
312
|
# This is on the Vapir module itself because it's used in a number of other places, should be in a broad namespace.
|
303
313
|
module_function
|
314
|
+
# fuzzily matches the given attribute with the given 'what'. if 'what' is a regexp, matches it
|
315
|
+
# against attr; if it's a string, downcases and strips before comparing; tries a couple other
|
316
|
+
# things; falls back to normal equality-checking. read the source for more information.
|
304
317
|
def fuzzy_match(attr, what)
|
305
318
|
# IF YOU CHANGE THIS, CHANGE THE JAVASCRIPT REIMPLEMENTATION IN match_candidates
|
306
319
|
case what
|
data/lib/vapir-common/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vapir-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 51
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 1.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ethan
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-08-04 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|