vapir-common 1.7.0.rc1
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/History.txt +5 -0
- data/README.txt +11 -0
- data/lib/vapir/common.rb +1 -0
- data/lib/vapir-common/browser.rb +184 -0
- data/lib/vapir-common/browsers.rb +9 -0
- data/lib/vapir-common/container.rb +163 -0
- data/lib/vapir-common/element.rb +1078 -0
- data/lib/vapir-common/element_collection.rb +83 -0
- data/lib/vapir-common/elements/elements.rb +858 -0
- data/lib/vapir-common/elements.rb +2 -0
- data/lib/vapir-common/exceptions.rb +56 -0
- data/lib/vapir-common/handle_options.rb +15 -0
- data/lib/vapir-common/modal_dialog.rb +27 -0
- data/lib/vapir-common/options.rb +49 -0
- data/lib/vapir-common/specifier.rb +322 -0
- data/lib/vapir-common/testcase.rb +89 -0
- data/lib/vapir-common/waiter.rb +141 -0
- data/lib/vapir-common/win_window.rb +1227 -0
- data/lib/vapir-common.rb +7 -0
- data/lib/vapir.rb +1 -0
- data/lib/watir-vapir.rb +15 -0
- metadata +89 -0
@@ -0,0 +1,858 @@
|
|
1
|
+
require 'vapir-common/element'
|
2
|
+
require 'vapir-common/container'
|
3
|
+
|
4
|
+
module Vapir
|
5
|
+
module Frame
|
6
|
+
extend ElementHelper
|
7
|
+
|
8
|
+
add_specifier :tagName => 'frame'
|
9
|
+
add_specifier :tagName => 'iframe'
|
10
|
+
|
11
|
+
container_single_method :frame
|
12
|
+
container_collection_method :frames
|
13
|
+
default_how :name
|
14
|
+
|
15
|
+
dom_attr :name
|
16
|
+
dom_attr :src
|
17
|
+
inspect_these :name, :src
|
18
|
+
|
19
|
+
# TODO/FIX: move this onto common page container, when such a thing exists
|
20
|
+
element_collection proc{|frame| Element.object_collection_to_enumerable(frame.content_window_object.frames) }, :frames, Frame
|
21
|
+
end
|
22
|
+
module InputElement
|
23
|
+
extend ElementHelper
|
24
|
+
|
25
|
+
add_specifier :tagName => 'input'
|
26
|
+
add_specifier :tagName => 'textarea'
|
27
|
+
add_specifier :tagName => 'button'
|
28
|
+
add_specifier :tagName => 'select'
|
29
|
+
|
30
|
+
container_single_method :input, :input_element
|
31
|
+
container_collection_method :inputs, :input_elements
|
32
|
+
|
33
|
+
dom_attr :name, :value, :type
|
34
|
+
dom_attr :disabled => [:disabled, :disabled?]
|
35
|
+
dom_attr :readOnly => [:readonly, :readonly?]
|
36
|
+
dom_attr :defaultValue => :default_value
|
37
|
+
dom_function :focus
|
38
|
+
dom_setter :value
|
39
|
+
inspect_these :name, :value, :type
|
40
|
+
|
41
|
+
# Checks if this element is enabled or not. Raises ObjectDisabledException if this is disabled.
|
42
|
+
def assert_enabled
|
43
|
+
if disabled
|
44
|
+
raise Exception::ObjectDisabledException, "#{self.inspect} is disabled"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks if object is readonly or not. Raises ObjectReadOnlyException if this is readonly
|
49
|
+
def assert_not_readonly
|
50
|
+
if readonly
|
51
|
+
raise Exception::ObjectReadOnlyException, "#{self.inspect} is readonly"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns true if element is enabled, otherwise returns false.
|
56
|
+
def enabled?
|
57
|
+
!disabled
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
module TextField
|
62
|
+
extend ElementHelper
|
63
|
+
parent_element_module InputElement
|
64
|
+
|
65
|
+
add_specifier :tagName => 'textarea'
|
66
|
+
add_specifier :tagName => 'input', :types => ['text', 'password', 'hidden']
|
67
|
+
|
68
|
+
container_single_method :text_field
|
69
|
+
container_collection_method :text_fields
|
70
|
+
|
71
|
+
dom_attr :size, :maxLength => :maxlength
|
72
|
+
alias_deprecated :getContents, :value
|
73
|
+
|
74
|
+
# Clears the contents of the text field.
|
75
|
+
#
|
76
|
+
# to be consistent with similar methods #set and #append, returns the new value, though this will always be a blank string.
|
77
|
+
#
|
78
|
+
# takes options:
|
79
|
+
# :blur => true/false; whether or not to fire the onblur event when done.
|
80
|
+
# :highlight => true/false
|
81
|
+
#
|
82
|
+
# Raises UnknownObjectException if the object can't be found
|
83
|
+
# Raises ObjectDisabledException if the object is disabled
|
84
|
+
# Raises ObjectReadOnlyException if the object is read only
|
85
|
+
def clear(options={})
|
86
|
+
options={:blur => true, :change => true, :select => true, :focus => true}.merge(options)
|
87
|
+
assert_enabled
|
88
|
+
assert_not_readonly
|
89
|
+
with_highlight(options) do
|
90
|
+
if options[:focus]
|
91
|
+
element_object.focus
|
92
|
+
fire_event('onFocus')
|
93
|
+
assert_exists(:force => true)
|
94
|
+
end
|
95
|
+
if options[:select]
|
96
|
+
element_object.select
|
97
|
+
fire_event("onSelect")
|
98
|
+
assert_exists(:force => true)
|
99
|
+
end
|
100
|
+
element_object.value = ''
|
101
|
+
fire_event :onKeyDown # TODO/fix - keyCode for 'delete' key
|
102
|
+
assert_exists(:force => true)
|
103
|
+
fire_event :onKeyPress
|
104
|
+
assert_exists(:force => true)
|
105
|
+
fire_event :onKeyUp
|
106
|
+
assert_exists(:force => true)
|
107
|
+
if options[:change] && exists?
|
108
|
+
fire_event("onChange")
|
109
|
+
end
|
110
|
+
if options[:blur] && exists?
|
111
|
+
fire_event('onBlur')
|
112
|
+
end
|
113
|
+
self.value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# Appends the specified string value to the contents of the text box.
|
117
|
+
#
|
118
|
+
# returns the new value of the text field. this may not include all of what is given if there is a maxlength on the field.
|
119
|
+
#
|
120
|
+
# takes options:
|
121
|
+
# :blur => true/false; whether or not to file the onblur event when done.
|
122
|
+
# :highlight => true/false
|
123
|
+
#
|
124
|
+
# Raises UnknownObjectException if the object cant be found
|
125
|
+
# Raises ObjectDisabledException if the object is disabled
|
126
|
+
# Raises ObjectReadOnlyException if the object is read only
|
127
|
+
def append(value, options={})
|
128
|
+
options={:blur => true, :change => true, :select => true, :focus => true}.merge(options)
|
129
|
+
assert_enabled
|
130
|
+
assert_not_readonly
|
131
|
+
|
132
|
+
with_highlight(options) do
|
133
|
+
existing_value_chars=element_object.value.split(//)
|
134
|
+
new_value_chars=existing_value_chars+value.split(//)
|
135
|
+
#value_chars=value.split(//) # split on blank regexp (rather than iterating over each byte) for multibyte chars
|
136
|
+
if self.type.downcase=='text' && maxlength && maxlength >= 0 && new_value_chars.length > maxlength
|
137
|
+
new_value_chars=new_value_chars[0...maxlength]
|
138
|
+
end
|
139
|
+
element_object.scrollIntoView
|
140
|
+
type_keys=respond_to?(:type_keys) ? self.type_keys : true # TODO: FIX
|
141
|
+
typingspeed=respond_to?(:typingspeed) ? self.typingspeed : 0 # TODO: FIX
|
142
|
+
if type_keys
|
143
|
+
if options[:focus]
|
144
|
+
element_object.focus
|
145
|
+
fire_event('onFocus')
|
146
|
+
assert_exists(:force => true)
|
147
|
+
end
|
148
|
+
if options[:select]
|
149
|
+
element_object.select
|
150
|
+
fire_event("onSelect")
|
151
|
+
assert_exists(:force => true)
|
152
|
+
end
|
153
|
+
((existing_value_chars.length)...new_value_chars.length).each do |i|
|
154
|
+
# sleep typingspeed # TODO/FIX
|
155
|
+
element_object.value = new_value_chars[0..i].join('')
|
156
|
+
fire_event :onKeyDown # TODO/fix - keyCode for character typed
|
157
|
+
assert_exists(:force => true)
|
158
|
+
fire_event :onKeyPress
|
159
|
+
assert_exists(:force => true)
|
160
|
+
fire_event :onKeyUp
|
161
|
+
assert_exists(:force => true)
|
162
|
+
end
|
163
|
+
if options[:change] && exists?
|
164
|
+
fire_event("onChange")
|
165
|
+
end
|
166
|
+
if options[:blur] && exists?
|
167
|
+
fire_event('onBlur')
|
168
|
+
end
|
169
|
+
else
|
170
|
+
element_object.value = element_object.value + value
|
171
|
+
end
|
172
|
+
wait
|
173
|
+
self.value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# Sets the contents of the text field to the given value
|
177
|
+
#
|
178
|
+
# returns the new value of the text field. this may be shorter than what is given if there is a maxlength on the field.
|
179
|
+
#
|
180
|
+
# takes options:
|
181
|
+
# :blur => true/false; whether or not to file the onblur event when done.
|
182
|
+
# :highlight => true/false
|
183
|
+
#
|
184
|
+
# Raises UnknownObjectException if the object cant be found
|
185
|
+
# Raises ObjectDisabledException if the object is disabled
|
186
|
+
# Raises ObjectReadOnlyException if the object is read only
|
187
|
+
def set(value, options={})
|
188
|
+
with_highlight(options) do
|
189
|
+
clear(options.merge(:blur => false, :change => false))
|
190
|
+
assert_exists(:force => true)
|
191
|
+
append(value, options.merge(:focus => false, :select => false))
|
192
|
+
self.value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
module Hidden
|
197
|
+
extend ElementHelper
|
198
|
+
parent_element_module TextField
|
199
|
+
add_specifier :tagName => 'input', :type => 'hidden'
|
200
|
+
container_single_method :hidden
|
201
|
+
container_collection_method :hiddens
|
202
|
+
default_how :name
|
203
|
+
|
204
|
+
|
205
|
+
# Sets the value of this hidden field. Overriden from TextField, as there is no way to set focus and type to a hidden field
|
206
|
+
def set(value)
|
207
|
+
self.value=value
|
208
|
+
end
|
209
|
+
|
210
|
+
# Appends the value to the value of this hidden field.
|
211
|
+
def append(append_value)
|
212
|
+
self.value = self.value + append_value
|
213
|
+
end
|
214
|
+
|
215
|
+
# Clears the value of this hidden field.
|
216
|
+
def clear
|
217
|
+
self.value = ""
|
218
|
+
end
|
219
|
+
|
220
|
+
# Hidden element is never visible - returns false.
|
221
|
+
def visible?
|
222
|
+
assert_exists
|
223
|
+
false
|
224
|
+
end
|
225
|
+
end
|
226
|
+
module Button
|
227
|
+
extend ElementHelper
|
228
|
+
parent_element_module InputElement
|
229
|
+
add_specifier :tagName => 'input', :types => ['button', 'submit', 'image', 'reset']
|
230
|
+
add_specifier :tagName => 'button'
|
231
|
+
container_single_method :button
|
232
|
+
container_collection_method :buttons
|
233
|
+
default_how :value
|
234
|
+
|
235
|
+
dom_attr :src, :height, :width, :alt # these are used on <input type=image>
|
236
|
+
end
|
237
|
+
module FileField
|
238
|
+
extend ElementHelper
|
239
|
+
parent_element_module InputElement
|
240
|
+
add_specifier :tagName => 'input', :type => 'file'
|
241
|
+
container_single_method :file_field
|
242
|
+
container_collection_method :file_fields
|
243
|
+
default_how :name
|
244
|
+
end
|
245
|
+
module Option
|
246
|
+
extend ElementHelper
|
247
|
+
add_specifier :tagName => 'option'
|
248
|
+
container_single_method :option
|
249
|
+
container_collection_method :options
|
250
|
+
|
251
|
+
inspect_these :text, :value, :selected
|
252
|
+
dom_attr :text, :value, :selected
|
253
|
+
|
254
|
+
# sets this Option's selected state to the given (true or false).
|
255
|
+
# if this Option is aware of its select list (this will generally be the case if you
|
256
|
+
# got this Option from a SelectList container), will fire the onchange event on the
|
257
|
+
# select list if our state changes.
|
258
|
+
def set_selected(state, method_options={})
|
259
|
+
method_options={:highlight => true, :wait => true}.merge(method_options)
|
260
|
+
with_highlight(method_options) do
|
261
|
+
state_was=element_object.selected
|
262
|
+
element_object.selected=state
|
263
|
+
if @extra[:select_list] && state_was != state
|
264
|
+
@extra[:select_list].fire_event(:onchange, method_options)
|
265
|
+
end
|
266
|
+
wait if method_options[:wait]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def selected=(state)
|
271
|
+
set_selected(state)
|
272
|
+
end
|
273
|
+
#dom_setter :selected
|
274
|
+
|
275
|
+
# selects this option, firing the onchange event on the containing select list if we
|
276
|
+
# are aware of it (see #selected=)
|
277
|
+
def select
|
278
|
+
self.selected=true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
module SelectList
|
282
|
+
extend ElementHelper
|
283
|
+
parent_element_module InputElement
|
284
|
+
add_specifier :tagName => 'select'
|
285
|
+
# in IE, type attribute is one of "select-one", "select-multiple" - but all are still the 'select' tag
|
286
|
+
container_single_method :select_list
|
287
|
+
container_collection_method :select_lists
|
288
|
+
|
289
|
+
dom_attr :multiple => [:multiple, :multiple?]
|
290
|
+
|
291
|
+
# Returns an ElementCollection containing all the option elements of the select list
|
292
|
+
# Raises UnknownObjectException if the select box is not found
|
293
|
+
def options
|
294
|
+
assert_exists do
|
295
|
+
ElementCollection.new(self, element_class_for(Vapir::Option), extra_for_contained.merge(:candidates => :options, :select_list => self))
|
296
|
+
end
|
297
|
+
end
|
298
|
+
# note that the above is defined that way rather than with element_collection, as below, because adding :select_list => self to extra isn't implemented yet
|
299
|
+
#element_collection :options, :options, Option, proc { {:select_list => self} }
|
300
|
+
|
301
|
+
def [](index)
|
302
|
+
options[index]
|
303
|
+
end
|
304
|
+
|
305
|
+
# Clears the selected items in the select box.
|
306
|
+
def clear
|
307
|
+
with_highlight do
|
308
|
+
assert_enabled
|
309
|
+
changed=false
|
310
|
+
options.each do |option|
|
311
|
+
if option.selected
|
312
|
+
option.selected=false
|
313
|
+
changed=true
|
314
|
+
end
|
315
|
+
end
|
316
|
+
if changed
|
317
|
+
fire_event :onchange
|
318
|
+
wait
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
alias :clearSelection :clear
|
323
|
+
|
324
|
+
# selects options whose text matches the given text.
|
325
|
+
# Raises NoValueFoundException if the specified value is not found.
|
326
|
+
#
|
327
|
+
# takes method_options hash (note, these are flags for the function, not to be confused with the Options of the select list)
|
328
|
+
# - :wait => true/false default true. controls whether #wait is called and whether fire_event or fire_event_no_wait is
|
329
|
+
# used for the onchange event.
|
330
|
+
def select_text(option_text, method_options={})
|
331
|
+
select_options_if(method_options) {|option| Vapir::fuzzy_match(option.text, option_text) }
|
332
|
+
end
|
333
|
+
alias select select_text
|
334
|
+
alias set select_text
|
335
|
+
|
336
|
+
# selects options whose value matches the given value.
|
337
|
+
# Raises NoValueFoundException if the specified value is not found.
|
338
|
+
#
|
339
|
+
# takes options hash (note, these are flags for the function, not to be confused with the Options of the select list)
|
340
|
+
# - :wait => true/false default true. controls whether #wait is called and whether fire_event or fire_event_no_wait is
|
341
|
+
# used for the onchange event.
|
342
|
+
def select_value(option_value, method_options={})
|
343
|
+
select_options_if(method_options) {|option| Vapir::fuzzy_match(option.value, option_value) }
|
344
|
+
end
|
345
|
+
|
346
|
+
# Does the SelectList have an option whose text matches the given text or regexp?
|
347
|
+
def option_texts_include?(text_or_regexp)
|
348
|
+
option_texts.grep(text_or_regexp).size > 0
|
349
|
+
end
|
350
|
+
alias include? option_texts_include?
|
351
|
+
alias includes? option_texts_include?
|
352
|
+
|
353
|
+
# Is the specified option (text) selected? Raises exception of option does not exist.
|
354
|
+
def selected_option_texts_include?(text_or_regexp)
|
355
|
+
unless includes? text_or_regexp
|
356
|
+
raise Vapir::Exception::UnknownObjectException, "Option #{text_or_regexp.inspect} not found."
|
357
|
+
end
|
358
|
+
selected_option_texts.grep(text_or_regexp).size > 0
|
359
|
+
end
|
360
|
+
alias selected? selected_option_texts_include?
|
361
|
+
|
362
|
+
def option_texts
|
363
|
+
options.map{|o| o.text }
|
364
|
+
end
|
365
|
+
alias_deprecated :getAllContents, :option_texts
|
366
|
+
|
367
|
+
# Returns an array of selected option Elements in this select list.
|
368
|
+
# An empty array is returned if the select box has no selected item.
|
369
|
+
def selected_options
|
370
|
+
options.select{|o|o.selected}
|
371
|
+
end
|
372
|
+
|
373
|
+
def selected_option_texts
|
374
|
+
selected_options.map{|o| o.text }
|
375
|
+
end
|
376
|
+
|
377
|
+
alias_deprecated :getSelectedItems, :selected_option_texts
|
378
|
+
|
379
|
+
private
|
380
|
+
# yields each option, selects the option if the given block returns true. fires onchange event if
|
381
|
+
# any have changed. raises Vapir::Exception::NoValueFoundException if none matched.
|
382
|
+
# breaks after the first match found if this is not a multiple select list.
|
383
|
+
# takes options hash (note, these are flags for the function, not to be confused with the Options of the select list)
|
384
|
+
# - :wait => true/false default true. controls whether #wait is called and whether fire_event or fire_event_no_wait is
|
385
|
+
# used for the onchange event.
|
386
|
+
def select_options_if(method_options={})
|
387
|
+
method_options={:wait => true, :highlight => true}.merge(method_options)
|
388
|
+
raise ArgumentError, "no block given!" unless block_given?
|
389
|
+
assert_enabled
|
390
|
+
any_matched=false
|
391
|
+
with_highlight(method_options) do
|
392
|
+
# using #each_with_index (rather than #each) because sometimes the OLE object goes away when a
|
393
|
+
# new option is selected (seems to be related to javascript events) and it has to be relocated.
|
394
|
+
# see documentation on ElementCollection#each_with_index vs. #each.
|
395
|
+
self.options.each_with_index do |option,i|
|
396
|
+
# javascript events on previous option selections can cause the select list or its options to change, so this may not actually exist. but only check if we've actually done anything.
|
397
|
+
break if any_matched && !option.exists?
|
398
|
+
if yield option
|
399
|
+
any_matched=true
|
400
|
+
option.set_selected(true, method_options) # note that this fires the onchange event on this SelectList
|
401
|
+
if !self.exists? || !multiple? # javascript events firing can cause us to stop existing at this point. we should not continue if we don't exist.
|
402
|
+
break
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
if !any_matched
|
407
|
+
raise Vapir::Exception::NoValueFoundException, "Could not find any options matching those specified on #{self.inspect}"
|
408
|
+
end
|
409
|
+
self
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
module RadioCheckBoxCommon
|
415
|
+
extend ElementClassAndModuleMethods
|
416
|
+
dom_attr :checked => [:checked, :checked?, :set?]
|
417
|
+
alias_deprecated :isSet?, :checked
|
418
|
+
alias_deprecated :getState, :checked
|
419
|
+
|
420
|
+
# Unchecks the radio button or check box element.
|
421
|
+
# Raises ObjectDisabledException exception if element is disabled.
|
422
|
+
def clear
|
423
|
+
set(false)
|
424
|
+
end
|
425
|
+
|
426
|
+
end
|
427
|
+
|
428
|
+
module Radio
|
429
|
+
extend ElementHelper
|
430
|
+
parent_element_module InputElement
|
431
|
+
add_specifier :tagName => 'input', :type => 'radio'
|
432
|
+
container_single_method :radio
|
433
|
+
container_collection_method :radios
|
434
|
+
add_container_method_extra_args(:value)
|
435
|
+
|
436
|
+
include RadioCheckBoxCommon
|
437
|
+
inspect_these :checked
|
438
|
+
|
439
|
+
# Checks this radio, or clears (defaults to setting if no argument is given)
|
440
|
+
# Raises ObjectDisabledException exception if element is disabled.
|
441
|
+
#
|
442
|
+
# Fires the onchange event if value changes.
|
443
|
+
# Fires the onclick event the state is true.
|
444
|
+
def set(state=true)
|
445
|
+
with_highlight do
|
446
|
+
assert_enabled
|
447
|
+
if checked!=state
|
448
|
+
element_object.checked=state
|
449
|
+
fire_event :onchange if exists? # don't fire event if we stopped existing due to change in state
|
450
|
+
end
|
451
|
+
if state && exists?
|
452
|
+
fire_event :onclick # fire this even if the state doesn't change; javascript can respond to clicking an already-checked radio.
|
453
|
+
end
|
454
|
+
wait
|
455
|
+
end
|
456
|
+
return self
|
457
|
+
end
|
458
|
+
end
|
459
|
+
module CheckBox
|
460
|
+
extend ElementHelper
|
461
|
+
parent_element_module InputElement
|
462
|
+
add_specifier :tagName => 'input', :type => 'checkbox'
|
463
|
+
container_single_method :checkbox, :check_box
|
464
|
+
container_collection_method :checkboxes, :check_boxes
|
465
|
+
add_container_method_extra_args(:value)
|
466
|
+
|
467
|
+
|
468
|
+
include RadioCheckBoxCommon
|
469
|
+
inspect_these :checked
|
470
|
+
# Checks this check box, or clears (defaults to setting if no argument is given)
|
471
|
+
# Raises ObjectDisabledException exception if element is disabled.
|
472
|
+
def set(state=true)
|
473
|
+
with_highlight do
|
474
|
+
assert_enabled
|
475
|
+
if checked!=state
|
476
|
+
if browser_class.name != 'Vapir::Firefox' # compare by name to not trigger autoload or raise NameError if not loaded
|
477
|
+
# in firefox, firing the onclick event changes the state. in IE, it doesn't, so do that first
|
478
|
+
# todo/fix: this is browser-specific stuff, shouldn't it be in the browser-specific class?
|
479
|
+
element_object.checked=state
|
480
|
+
end
|
481
|
+
fire_event :onclick if exists? # sometimes previous actions can cause self to stop existing
|
482
|
+
fire_event :onchange if exists?
|
483
|
+
end
|
484
|
+
wait
|
485
|
+
end
|
486
|
+
return self
|
487
|
+
end
|
488
|
+
end
|
489
|
+
module Form
|
490
|
+
extend ElementHelper
|
491
|
+
add_specifier :tagName => 'form'
|
492
|
+
container_single_method :form
|
493
|
+
container_collection_method :forms
|
494
|
+
default_how :name
|
495
|
+
|
496
|
+
dom_attr :name, :action, :method => :form_method # can't use 'method' for the ruby method because it clobbers the rather important Object#method
|
497
|
+
inspect_these :name, :action
|
498
|
+
|
499
|
+
# Submit the form. Equivalent to pressing Enter or Return to submit a form.
|
500
|
+
dom_function :submit
|
501
|
+
|
502
|
+
private
|
503
|
+
# these are kind of slow for large forms.
|
504
|
+
def set_highlight(options={})
|
505
|
+
assert_exists do
|
506
|
+
@elements_for_highlighting=self.input_elements.to_a
|
507
|
+
@elements_for_highlighting.each do |element|
|
508
|
+
element.send(:set_highlight, options)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
def clear_highlight(options={})
|
513
|
+
assert_exists do
|
514
|
+
@elements_for_highlighting.each do |element|
|
515
|
+
if element.exists?
|
516
|
+
element.send(:clear_highlight, options)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
module Image
|
523
|
+
extend ElementHelper
|
524
|
+
add_specifier :tagName => 'IMG'
|
525
|
+
container_single_method :image
|
526
|
+
container_collection_method :images
|
527
|
+
default_how :name
|
528
|
+
|
529
|
+
dom_attr :src, :name, :width, :height, :alt, :border
|
530
|
+
dom_setter :border
|
531
|
+
inspect_these :src, :name, :width, :height, :alt
|
532
|
+
|
533
|
+
private
|
534
|
+
# note: can't use alias here because set_highlight_border is defined in the Element module, which isn't included here (but it will be on the receiver)
|
535
|
+
def set_highlight(options={})
|
536
|
+
set_highlight_border(options)
|
537
|
+
end
|
538
|
+
def clear_highlight(options={})
|
539
|
+
clear_highlight_border(options)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
module HasRowsAndColumns
|
543
|
+
# Returns a 2 dimensional array of text contents of each row and column of the table or tbody.
|
544
|
+
def to_a
|
545
|
+
rows.map{|row| row.cells.map{|cell| cell.text.strip}}
|
546
|
+
end
|
547
|
+
|
548
|
+
# iterates through the rows in the table. Yields a TableRow object
|
549
|
+
def each_row
|
550
|
+
rows.each do |row|
|
551
|
+
yield row
|
552
|
+
end
|
553
|
+
end
|
554
|
+
alias each each_row
|
555
|
+
|
556
|
+
# Returns the TableRow at the given index.
|
557
|
+
# indices start at 1.
|
558
|
+
def [](index)
|
559
|
+
rows[index]
|
560
|
+
end
|
561
|
+
|
562
|
+
# Returns the number of rows inside the table. does not recurse through
|
563
|
+
# nested tables. same as (object).rows.length
|
564
|
+
#
|
565
|
+
# if you want the row count including nested tables (which this brokenly used to return)
|
566
|
+
# use (object).table_rows.length
|
567
|
+
def row_count
|
568
|
+
element_object.rows.length
|
569
|
+
end
|
570
|
+
|
571
|
+
def row_count_excluding_nested_tables
|
572
|
+
raise NotImplementedError, "the method \#row_count_excluding_nested_tables is gone. the \#row_count method now returns the number of rows in this #{self.class}. for the number of rows including nested tables, use [this object].table_rows.length"
|
573
|
+
end
|
574
|
+
|
575
|
+
# returns all of the cells of this table. to get the cells including nested tables,
|
576
|
+
# use #table_cells, which is defined on all containers (including Table)
|
577
|
+
def cells
|
578
|
+
ElementCollection.new(self, element_class_for(Vapir::TableCell), extra_for_contained.merge(:candidates => proc do |container|
|
579
|
+
container_object=container.element_object
|
580
|
+
object_collection_to_enumerable(container_object.rows).inject([]) do |candidates, row|
|
581
|
+
candidates+object_collection_to_enumerable(row.cells).to_a
|
582
|
+
end
|
583
|
+
end))
|
584
|
+
end
|
585
|
+
|
586
|
+
# returns the number of columns of the table, either on the row at the given index
|
587
|
+
# or (by default) on the first row.
|
588
|
+
# takes into account any defined colSpans.
|
589
|
+
# returns nil if the table has no rows.
|
590
|
+
# (if you want the number of cells - not taking into account colspans - use #cell_count
|
591
|
+
# on the row in question)
|
592
|
+
def column_count(index=nil)
|
593
|
+
if index
|
594
|
+
rows[index].column_count
|
595
|
+
elsif row=rows.first
|
596
|
+
row.column_count
|
597
|
+
else
|
598
|
+
nil
|
599
|
+
end
|
600
|
+
end
|
601
|
+
# I was going to define #cell_count(index=nil) here as an alternative to #column_count
|
602
|
+
# but it seems confusing; to me #cell_count on a Table would count up all the cells in
|
603
|
+
# all rows, so going to avoid confusion and not do it.
|
604
|
+
|
605
|
+
# Returns an array of the text of each cell in the row at the given index.
|
606
|
+
def row_texts_at(row_index)
|
607
|
+
rows[row_index].cells.map do |cell|
|
608
|
+
cell.text
|
609
|
+
end
|
610
|
+
end
|
611
|
+
alias_deprecated :row_values, :row_texts_at
|
612
|
+
|
613
|
+
# Returns an array containing the text of the cell in the specified index in each row.
|
614
|
+
def column_texts_at(column_index)
|
615
|
+
rows.map do |row|
|
616
|
+
row.cells[column_index].text
|
617
|
+
end
|
618
|
+
end
|
619
|
+
alias_deprecated :column_values, :column_texts_at
|
620
|
+
end
|
621
|
+
module TableCell
|
622
|
+
extend ElementHelper
|
623
|
+
add_specifier :tagName => 'td'
|
624
|
+
add_specifier :tagName => 'th'
|
625
|
+
container_single_method :table_cell
|
626
|
+
container_collection_method :table_cells
|
627
|
+
|
628
|
+
dom_attr :colSpan => [:colSpan, :colspan], :rowSpan => [:rowSpan, :rowspan]
|
629
|
+
end
|
630
|
+
module TableRow
|
631
|
+
extend ElementHelper
|
632
|
+
add_specifier :tagName => 'tr'
|
633
|
+
container_single_method :table_row
|
634
|
+
container_collection_method :table_rows
|
635
|
+
|
636
|
+
# Returns an ElementCollection of cells in the row
|
637
|
+
element_collection :cells, :cells, TableCell
|
638
|
+
|
639
|
+
# Iterate over each cell in the row.
|
640
|
+
def each_cell
|
641
|
+
cells.each do |cell|
|
642
|
+
yield cell
|
643
|
+
end
|
644
|
+
end
|
645
|
+
alias each each_cell
|
646
|
+
|
647
|
+
# returns the TableCell at the specified index
|
648
|
+
def [](index)
|
649
|
+
cells[index]
|
650
|
+
end
|
651
|
+
|
652
|
+
def column_count
|
653
|
+
cells.inject(0) do |count, cell|
|
654
|
+
count+ cell.colSpan || 1
|
655
|
+
end
|
656
|
+
end
|
657
|
+
def cell_count
|
658
|
+
cells.length
|
659
|
+
end
|
660
|
+
|
661
|
+
# returns the cell of the current row at the given column index (starting from 1), taking
|
662
|
+
# into account conSpans of other cells.
|
663
|
+
#
|
664
|
+
# returns nil if index is greater than the number of columns of this row.
|
665
|
+
def cell_at_colum(index)
|
666
|
+
cells.detect do |cell|
|
667
|
+
index=index-(cell.colSpan || 1)
|
668
|
+
index <= 0
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
module TBody
|
673
|
+
extend ElementHelper
|
674
|
+
add_specifier :tagName => 'TBODY'
|
675
|
+
container_single_method :tbody
|
676
|
+
container_collection_method :tbodies
|
677
|
+
|
678
|
+
include HasRowsAndColumns
|
679
|
+
|
680
|
+
# returns an ElementCollection of rows in the tbody.
|
681
|
+
element_collection :rows, :rows, TableRow
|
682
|
+
end
|
683
|
+
module Table
|
684
|
+
def self.create_from_element(container, element)
|
685
|
+
Kernel.warn "DEPRECATION WARNING: create_from_element is deprecated. Please use (element).parent_table (element being the second argument to this function)\n(called from #{caller.map{|c|"\n"+c}})"
|
686
|
+
element.parent_table
|
687
|
+
end
|
688
|
+
|
689
|
+
extend ElementHelper
|
690
|
+
add_specifier :tagName => 'TABLE'
|
691
|
+
container_single_method :table
|
692
|
+
container_collection_method :tables
|
693
|
+
|
694
|
+
include HasRowsAndColumns
|
695
|
+
|
696
|
+
# returns an ElementCollection of rows in the table.
|
697
|
+
element_collection :rows, :rows, TableRow
|
698
|
+
|
699
|
+
private
|
700
|
+
def set_highlight(options={})
|
701
|
+
set_highlight_color(options)
|
702
|
+
set_highlight_border(options)
|
703
|
+
end
|
704
|
+
def clear_highlight(options={})
|
705
|
+
clear_highlight_color(options)
|
706
|
+
clear_highlight_border(options)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
module Link
|
710
|
+
extend ElementHelper
|
711
|
+
add_specifier :tagName => 'A'
|
712
|
+
container_single_method :a, :link
|
713
|
+
container_collection_method :as, :links
|
714
|
+
|
715
|
+
dom_attr :name, :href => [:href, :url]
|
716
|
+
inspect_these :href, :name
|
717
|
+
end
|
718
|
+
module Pre
|
719
|
+
extend ElementHelper
|
720
|
+
add_specifier :tagName => 'PRE'
|
721
|
+
container_single_method :pre
|
722
|
+
container_collection_method :pres
|
723
|
+
end
|
724
|
+
module P
|
725
|
+
extend ElementHelper
|
726
|
+
add_specifier :tagName => 'P'
|
727
|
+
container_single_method :p
|
728
|
+
container_collection_method :ps
|
729
|
+
end
|
730
|
+
module Div
|
731
|
+
extend ElementHelper
|
732
|
+
add_specifier :tagName => 'DIV'
|
733
|
+
container_single_method :div
|
734
|
+
container_collection_method :divs
|
735
|
+
end
|
736
|
+
module Span
|
737
|
+
extend ElementHelper
|
738
|
+
add_specifier :tagName => 'SPAN'
|
739
|
+
container_single_method :span
|
740
|
+
container_collection_method :spans
|
741
|
+
end
|
742
|
+
module Strong
|
743
|
+
extend ElementHelper
|
744
|
+
add_specifier :tagName => 'STRONG'
|
745
|
+
container_single_method :strong
|
746
|
+
container_collection_method :strongs
|
747
|
+
end
|
748
|
+
module Label
|
749
|
+
extend ElementHelper
|
750
|
+
add_specifier :tagName => 'LABEL'
|
751
|
+
container_single_method :label
|
752
|
+
container_collection_method :labels
|
753
|
+
|
754
|
+
dom_attr :htmlFor => [:html_for, :for, :htmlFor]
|
755
|
+
inspect_these :text, :for
|
756
|
+
|
757
|
+
def for_element
|
758
|
+
raise "document is not defined - cannot search for labeled element" unless document_object
|
759
|
+
if for_object=document_object.getElementById(element_object.htmlFor)
|
760
|
+
base_element_class.factory(for_object, container.extra_for_contained, :label, self)
|
761
|
+
else
|
762
|
+
raise Exception::UnknownObjectException, "no element found that #{self.inspect} is for!"
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|
766
|
+
module Ul
|
767
|
+
extend ElementHelper
|
768
|
+
add_specifier :tagName => 'UL'
|
769
|
+
container_single_method :ul
|
770
|
+
container_collection_method :uls
|
771
|
+
end
|
772
|
+
module Ol
|
773
|
+
extend ElementHelper
|
774
|
+
add_specifier :tagName => 'ol'
|
775
|
+
container_single_method :ol
|
776
|
+
container_collection_method :ols
|
777
|
+
end
|
778
|
+
module Li
|
779
|
+
extend ElementHelper
|
780
|
+
add_specifier :tagName => 'LI'
|
781
|
+
container_single_method :li
|
782
|
+
container_collection_method :lis
|
783
|
+
end
|
784
|
+
module Dl
|
785
|
+
extend ElementHelper
|
786
|
+
add_specifier :tagName => 'DL'
|
787
|
+
container_single_method :dl
|
788
|
+
container_collection_method :dls
|
789
|
+
end
|
790
|
+
module Dt
|
791
|
+
extend ElementHelper
|
792
|
+
add_specifier :tagName => 'DT'
|
793
|
+
container_single_method :dt
|
794
|
+
container_collection_method :dts
|
795
|
+
end
|
796
|
+
module Dd
|
797
|
+
extend ElementHelper
|
798
|
+
add_specifier :tagName => 'DD'
|
799
|
+
container_single_method :dd
|
800
|
+
container_collection_method :dds
|
801
|
+
end
|
802
|
+
module H1
|
803
|
+
extend ElementHelper
|
804
|
+
add_specifier :tagName => 'H1'
|
805
|
+
container_single_method :h1
|
806
|
+
container_collection_method :h1s
|
807
|
+
end
|
808
|
+
module H2
|
809
|
+
extend ElementHelper
|
810
|
+
add_specifier :tagName => 'H2'
|
811
|
+
container_single_method :h2
|
812
|
+
container_collection_method :h2s
|
813
|
+
end
|
814
|
+
module H3
|
815
|
+
extend ElementHelper
|
816
|
+
add_specifier :tagName => 'H3'
|
817
|
+
container_single_method :h3
|
818
|
+
container_collection_method :h3s
|
819
|
+
end
|
820
|
+
module H4
|
821
|
+
extend ElementHelper
|
822
|
+
add_specifier :tagName => 'H4'
|
823
|
+
container_single_method :h4
|
824
|
+
container_collection_method :h4s
|
825
|
+
end
|
826
|
+
module H5
|
827
|
+
extend ElementHelper
|
828
|
+
add_specifier :tagName => 'H5'
|
829
|
+
container_single_method :h5
|
830
|
+
container_collection_method :h5s
|
831
|
+
end
|
832
|
+
module H6
|
833
|
+
extend ElementHelper
|
834
|
+
add_specifier :tagName => 'H6'
|
835
|
+
container_single_method :h6
|
836
|
+
container_collection_method :h6s
|
837
|
+
end
|
838
|
+
module Map
|
839
|
+
extend ElementHelper
|
840
|
+
add_specifier :tagName => 'MAP'
|
841
|
+
container_single_method :map
|
842
|
+
container_collection_method :maps
|
843
|
+
end
|
844
|
+
module Area
|
845
|
+
extend ElementHelper
|
846
|
+
add_specifier :tagName => 'AREA'
|
847
|
+
container_single_method :area
|
848
|
+
container_collection_method :areas
|
849
|
+
|
850
|
+
dom_attr :alt, :href => [:url, :href]
|
851
|
+
end
|
852
|
+
module Em
|
853
|
+
extend ElementHelper
|
854
|
+
add_specifier :tagName => 'EM'
|
855
|
+
container_single_method :em
|
856
|
+
container_collection_method :ems
|
857
|
+
end
|
858
|
+
end
|