vapir-common 1.7.0.rc1

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