vapir-common 1.7.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|