Module: Vapir::Element

Extended by:
ElementHelper
Includes:
ElementObjectCandidates
Defined in:
lib/vapir-common/element.rb

Overview

this is included by every Element. it relies on the including class implementing a #element_object method some stuff assumes the element has a defined @container.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ElementHelper

add_specifier, container_collection_method, container_single_method, included

Methods included from ElementClassAndModuleMethods

#add_container_method_extra_args, #all_dom_attr_aliases, #all_dom_attrs, #class_array_append, #class_array_get, #class_hash_get, #class_hash_merge, #container_collection_methods, #container_method_extra_args, #container_single_methods, #default_how, #dom_attr, #dom_attr_locate_alias, #dom_function, #dom_setter, #element_collection, #factory, #inspect_these, #inspect_this_if, #parent_element_module, #set_or_get_class_var, #specifiers

Instance Attribute Details

#browserObject (readonly)

Returns the value of attribute browser.



1056
1057
1058
# File 'lib/vapir-common/element.rb', line 1056

def browser
  @browser
end

#howObject (readonly)

Returns the value of attribute how.



482
483
484
# File 'lib/vapir-common/element.rb', line 482

def how
  @how
end

#indexObject (readonly)

Returns the value of attribute index.



484
485
486
# File 'lib/vapir-common/element.rb', line 484

def index
  @index
end

#page_containerObject (readonly)

Returns the value of attribute page_container.



1057
1058
1059
# File 'lib/vapir-common/element.rb', line 1057

def page_container
  @page_container
end

#whatObject (readonly)

Returns the value of attribute what.



483
484
485
# File 'lib/vapir-common/element.rb', line 483

def what
  @what
end

Class Method Details

.object_collection_to_enumerable(object) ⇒ Object



1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
# File 'lib/vapir-common/element.rb', line 1137

def object_collection_to_enumerable(object)
  if object.is_a?(Enumerable)
    object
  elsif Object.const_defined?('JsshObject') && object.is_a?(JsshObject)
    object.to_array
  elsif Object.const_defined?('WIN32OLE') && object.is_a?(WIN32OLE)
    array=[]
    length = object.length
    (0...length).each do |i|
      begin
        array << object.item(i)
      rescue WIN32OLERuntimeError
        # not rescuing, just adding information
        raise $!.class, "accessing item #{i} of #{length}, encountered:\n"+$!.message, $!.backtrace
      end
    end
    array
  else
    raise TypeError, "Don't know how to make enumerable from given object #{object.inspect} (#{object.class})"
  end
end

Instance Method Details

#attr(attribute) ⇒ Object

method to access dom attributes by defined aliases. unlike get_attribute, this only looks at the specific dom attributes that Watir knows about, and the aliases for those that Watir defines.



683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# File 'lib/vapir-common/element.rb', line 683

def attr(attribute)
  unless attribute.is_a?(String) || attribute.is_a?(Symbol)
    raise TypeError, "attribute should be string or symbol; got #{attribute.inspect}"
  end
  attribute=attribute.to_sym
  all_aliases=self.class.all_dom_attr_aliases
  dom_attrs=all_aliases.reject{|dom_attr, attr_aliases| !attr_aliases.include?(attribute) }.keys
  case dom_attrs.size
  when 0
    raise ArgumentError, "Not a recognized attribute: #{attribute}"
  when 1
    method_from_element_object(dom_attrs.first)
  else
    raise ArgumentError, "Ambiguously aliased attribute #{attribute} may refer to any of: #{dom_attrs.join(', ')}"
  end
end

#attributes_for_stringifyingObject



1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
# File 'lib/vapir-common/element.rb', line 1072

def attributes_for_stringifying
  attributes_to_inspect=self.class.attributes_to_inspect
  unless exists?
    attributes_to_inspect=[{:value => :exists?, :label => :exists?}]+attributes_to_inspect.select{|inspect_hash| [:how, :what, :index].include?(inspect_hash[:label]) }
  end
  attributes_to_inspect.map do |inspect_hash|
    if !inspect_hash[:if] || inspect_hash[:if].call(self)
      value=case inspect_hash[:value]
      when /\A@/ # starts with @, look for instance variable
        instance_variable_get(inspect_hash[:value]).inspect
      when Symbol
        send(inspect_hash[:value])
      when Proc
        inspect_hash[:value].call(self)
      else
        inspect_hash[:value]
      end
      [inspect_hash[:label].to_s, value]
    end
  end.compact
end

#browser_window_objectObject



1067
1068
1069
1070
# File 'lib/vapir-common/element.rb', line 1067

def browser_window_object
  assert_container
  @container.browser_window_object
end

#client_centerObject

returns a two-element Vector with the position of the center of this element on the client area. intended to be used with mouse events’ clientX and clientY. developer.mozilla.org/en/DOM/event.clientX developer.mozilla.org/en/DOM/event.clientY



999
1000
1001
# File 'lib/vapir-common/element.rb', line 999

def client_center
  client_offset+dimensions.map{|dim| dim/2}
end

#client_offsetObject

returns a two-element Vector containing the offset of this element on the client area. see also #client_center



990
991
992
# File 'lib/vapir-common/element.rb', line 990

def client_offset
  document_offset-scroll_offset
end

#containerObject



1051
1052
1053
1054
# File 'lib/vapir-common/element.rb', line 1051

def container
  assert_container
  @container
end

#content_window_objectObject



1063
1064
1065
1066
# File 'lib/vapir-common/element.rb', line 1063

def content_window_object
  assert_container
  @container.content_window_object
end

#default_initialize(how, what, extra = {}) ⇒ Object Also known as: initialize

the class-specific Elements may implement their own #initialize, but should call to this after they’ve done their stuff

Raises:

  • (ArgumentError)


497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/vapir-common/element.rb', line 497

def default_initialize(how, what, extra={})
  @how, @what=how, what
  raise ArgumentError, "how (first argument) should be a Symbol, not: #{how.inspect}" unless how.is_a?(Symbol)
  @extra=extra
  @index=begin
    valid_symbols=[:first, :last]
    if valid_symbols.include?(@extra[:index]) || @extra[:index].nil? || (@extra[:index].is_a?(Integer) && @extra[:index] > 0)
      @extra[:index]
    elsif valid_symbols.map{|sym| sym.to_s}.include?(@extra[:index])
      @extra[:index].to_sym
    elsif @extra[:index] =~ /\A\d+\z/
      Integer(@extra[:index])
    else
      raise ArgumentError, "expected extra[:index] to be a positive integer, a string that looks like a positive integer, :first, or :last. received #{@extra[:index]} (#{@extra[:index].class})"
    end
  end
  @container=extra[:container]
  @browser=extra[:browser]
  @page_container=extra[:page_container]
  @element_object=extra[:element_object] # this will in most cases not be set, but may be set in some cases from ElementCollection enumeration 
  extra[:locate]=true unless @extra.key?(:locate) # set default 
  case extra[:locate]
  when :assert
    locate!
  when true
    locate
  when false
  else
    raise ArgumentError, "Unrecognized value given for extra[:locate]: #{extra[:locate].inspect} (#{extra[:locate].class})"
  end
end

#dimensionsObject

returns a two-element Vector with the width and height of this element.



1037
1038
1039
# File 'lib/vapir-common/element.rb', line 1037

def dimensions
  Vector[element_object.offsetWidth, element_object.offsetHeight]
end

#document_centerObject

returns a two-element Vector with the position of the center of this element on the document.



1042
1043
1044
# File 'lib/vapir-common/element.rb', line 1042

def document_center
  document_offset+dimensions.map{|dim| dim/2}
end

#document_objectObject



1059
1060
1061
1062
# File 'lib/vapir-common/element.rb', line 1059

def document_object
  assert_container
  @container.document_object
end

#document_offsetObject

returns a Vector with two elements, the x,y coordinates of this element (its top left point) from the top left edge of the window



978
979
980
981
982
983
984
985
986
# File 'lib/vapir-common/element.rb', line 978

def document_offset
  xy=Vector[0,0]
  el=element_object
  begin
    xy+=Vector[el.offsetLeft, el.offsetTop]
    el=el.offsetParent
  end while el
  xy
end

#element_class_for(common_module) ⇒ Object

for a common module, such as a TextField, returns an elements-specific class (such as Firefox::TextField) that inherits from the base_element_class of self. That is, this returns a sibling class, as it were, of whatever class inheriting from Element is instantiated.



1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
# File 'lib/vapir-common/element.rb', line 1123

def element_class_for(common_module)
  element_class=nil
  ObjectSpace.each_object(Class) do |klass|
    if klass < common_module && klass < base_element_class
      element_class= klass
    end
  end
  unless element_class
    raise RuntimeError, "No class found that inherits from both #{common_module} and #{base_element_class}"
  end
  element_class
end

#element_objectObject

accesses the object representing this Element in the DOM.



1047
1048
1049
1050
# File 'lib/vapir-common/element.rb', line 1047

def element_object
  assert_exists
  @element_object
end

#exists?Boolean Also known as: exist?

Returns whether this element actually exists.

Returns:

  • (Boolean)


673
674
675
676
677
# File 'lib/vapir-common/element.rb', line 673

def exists?
  handling_existence_failure(:handle => proc { return false }) do
    return !!locate
  end
end

#flash(options = {}) ⇒ Object

Flash the element the specified number of times. Defaults to 10 flashes.



814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
# File 'lib/vapir-common/element.rb', line 814

def flash(options={})
  if options.is_a?(Fixnum)
    options={:count => options}
    Kernel.warn "DEPRECATION WARNING: #{self.class.name}\#flash takes an options hash - passing a number is deprecated. Please use #{self.class.name}\#flash(:count => #{options[:count]})\n(called from #{caller.map{|c|"\n"+c}})"
  end
  options={:count => 10, :sleep => 0.05}.merge(options)
  #options=handle_options(options, {:count => 10, :sleep => 0.05}, [:color])
  assert_exists do
    options[:count].times do
      with_highlight(options) do
        sleep options[:sleep]
      end
      sleep options[:sleep]
    end
  end
  nil
end

#htmlObject



486
487
488
489
# File 'lib/vapir-common/element.rb', line 486

def html
  Kernel.warn "#html is deprecated, please use #outer_html or #inner_html. #html currently returns #outer_html (note that it previously returned inner_html on firefox)\n(called from #{caller.map{|c|"\n"+c}})"
  outer_html
end

#inspectObject



1093
1094
1095
1096
1097
# File 'lib/vapir-common/element.rb', line 1093

def inspect
  "\#<#{self.class.name}:0x#{"%.8x"%(self.hash*2)}"+attributes_for_stringifying.map do |attr|
    " "+attr.first+'='+attr.last.inspect
  end.join('') + ">"
end

#locate(options = {}) ⇒ Object

locates the element object for this element

takes options hash. currently the only option is

  • :relocate => nil, :recursive, true, false

    • nil or not set (default): this Element is only relocated if the browser is updated (in firefox) or the WIN32OLE stops existing (IE).

    • :recursive: this element and its containers are relocated, recursively up to the containing browser.

    • false: no relocating is done even if the browser is updated or the element_object stops existing.

    • true: this Element is relocated. the container is relocated only if the browser is updated or the element_object stops existing.



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/vapir-common/element.rb', line 572

def locate(options={})
  if options[:relocate]==nil && @element_object # don't override if it is set to false; only if it's nil, and don't set :relocate there's no @element_object (that's an initial locate, not a relocate) 
    if @browser && @updated_at && @browser.respond_to?(:updated_at) && @browser.updated_at > @updated_at # TODO: implement this for IE; only exists for Firefox now. 
      options[:relocate]=:recursive
    elsif !element_object_exists?
      options[:relocate]=true
    end
  end
  container_locate_options={}
  if options[:relocate]==:recursive
    container_locate_options[:relocate]= options[:relocate]
  end
  if options[:relocate]
    @element_object=nil
  end
  element_object_existed=!!@element_object
  @element_object||= begin
    case @how
    when :element_object
      assert_no_index
      @element_object=@what # this is needed for checking its existence 
      if options[:relocate] && !element_object_exists?
        raise Vapir::Exception::UnableToRelocateException, "This #{self.class.name} was specified using #{how.inspect} and cannot be relocated."
      end
      @what
    when :xpath
      assert_container
      @container.locate!(container_locate_options)
      unless @container.respond_to?(:element_object_by_xpath)
        raise Vapir::Exception::MissingWayOfFindingObjectException, "Locating by xpath is not supported on the container #{@container.inspect}"
      end
      # todo/fix: implement index for this, using element_objects_by_xpath ? 
      assert_no_index
      by_xpath=@container.element_object_by_xpath(@what)
      match_candidates(by_xpath ? [by_xpath] : [], self.class.specifiers, self.class.all_dom_attr_aliases).first
    when :label
      assert_no_index
      unless document_object
        raise "No document object found for this #{self.inspect} - needed to search by id for label from #{@container.inspect}"
      end
      unless what.is_a?(Label)
        raise "how=:label specified on this #{self.class}, but 'what' is not a Label! what=#{what.inspect} (#{what.class})"
      end
      what.locate!(container_locate_options) # 'what' is not the container; our container is the label's container, but the options for locating should be the same. 
      by_label=document_object.getElementById(what.for)
      match_candidates(by_label ? [by_label] : [], self.class.specifiers, self.class.all_dom_attr_aliases).first
    when :attributes
      assert_container
      @container.locate!(container_locate_options)
      specified_attributes=@what
      specifiers=self.class.specifiers.map{|spec| spec.merge(specified_attributes)}
      
      candidate_match_at_index(@index, method(:matched_candidates), specifiers, self.class.all_dom_attr_aliases)
    when :index
      unless @what.nil?
        raise ArgumentError, "'what' was specified, but when 'how'=:index, no 'what' is used (just extra[:index])"
      end
      unless @index
        raise ArgumentError, "'how' was given as :index but no index was given"
      end
      candidate_match_at_index(@index, method(:matched_candidates), self.class.specifiers, self.class.all_dom_attr_aliases)
    when :custom
      # this allows a proc to be given as 'what', which is called yielding candidates, each being 
      # an instanted Element of this class. this might seem a bit odd - instantiating a bunch 
      # of elements in order to figure out which element_object to use in locating this one. 
      # the purpose is so that this Element can be relocated if we lose the element_object. 
      # the Elements that are yielded are instantiated by :element object which cannot be 
      # relocated. 
      #
      # the alternative to this would be for the calling code to loop over the element collection
      # for this class on the container - that is, instead of:
      #   found_div=frame.divs.detect{|div| weird_criteria_for(div) }
      # which can't be relocated - since element collections use :element object - you'd do
      #   found_div=frame.div(:custom, proc{|div| weird_criteria_for(div) })
      # this way, found_div can be relocated. yay! 
      # 
      # the proc should return true (that is, not false or nil) when it likes the given Element - 
      # when it matches what it expects of this Element. 
      candidate_match_at_index(@index, method(:matched_candidates), self.class.specifiers, self.class.all_dom_attr_aliases) do |candidate|
        what.call(self.class.new(:element_object, candidate, @extra))
      end
    else
      raise Vapir::Exception::MissingWayOfFindingObjectException, "Unknown 'how' given: #{@how.inspect} (#{@how.class}). 'what' was #{@what.inspect} (#{@what.class})"
    end
  end
  if !element_object_existed && @element_object
    @updated_at=Time.now
  end
  @element_object
end

#locate!(options = {}) ⇒ Object



662
663
664
665
666
667
668
669
# File 'lib/vapir-common/element.rb', line 662

def locate!(options={})
  locate(options) || begin
    klass=self.is_a?(Frame) ? Vapir::Exception::UnknownFrameException : Vapir::Exception::UnknownObjectException
    message="Unable to locate #{self.class}, using #{@how}"+(@what ? ": "+@what.inspect : '')+(@index ? ", index #{@index}" : "")
    message+="\non container: #{@container.inspect}" if @container
    raise(klass, message)
  end
end

#parent(options = {}) ⇒ Object

Return the element immediately containing this element. returns nil if there is no parent, or if the parent is the document.

this is cached; call parent(:reload => true) if you wish to uncache it.



836
837
838
839
840
841
842
843
844
845
846
# File 'lib/vapir-common/element.rb', line 836

def parent(options={})
  @parent=nil if options[:reload]
  @parent||=begin
    parentNode=element_object.parentNode
    if parentNode && parentNode != document_object # don't ascend up to the document. #TODO/Fix - for IE, comparing WIN32OLEs doesn't really work, this comparison is pointless. 
      base_element_class.factory(parentNode, extra_for_contained) # this is a little weird, passing extra_for_contained so that this is the container of its parent. 
    else
      nil
    end
  end
end

#pretty_print(pp) ⇒ Object



1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
# File 'lib/vapir-common/element.rb', line 1106

def pretty_print(pp)
  pp.object_address_group(self) do
    pp.seplist(attributes_for_stringifying, lambda { pp.text ',' }) do |attr|
      pp.breakable ' '
      pp.group(0) do
        pp.text attr.first
        pp.text ':'
        pp.breakable
        pp.pp attr.last
      end
    end
  end
end

#screen_centerObject

returns a two-element Vector containing the current position of the center of this element on the screen. intended to be used with mouse events’ screenX and screenY. developer.mozilla.org/en/DOM/event.screenX developer.mozilla.org/en/DOM/event.screenY

not yet implemented.



1032
1033
1034
# File 'lib/vapir-common/element.rb', line 1032

def screen_center
  screen_offset+dimensions.map{|dim| dim/2}
end

#screen_offsetObject

returns a two-element Vector containing the position of this element on the screen. see also #screen_center not yet implemented.

Raises:

  • (NotImplementedError)


1021
1022
1023
# File 'lib/vapir-common/element.rb', line 1021

def screen_offset
  raise NotImplementedError
end

#scroll_offsetObject

returns a two-element Vector containing the current scroll offset of this element relative to any scrolling parents. this is basically stolen from prototype - see www.prototypejs.org/api/element/cumulativescrolloffset



1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
# File 'lib/vapir-common/element.rb', line 1006

def scroll_offset
  xy=Vector[0,0]
  el=element_object
  begin
    if el.respond_to?(:scrollLeft) && el.respond_to?(:scrollTop) && (scroll_left=el.scrollLeft).is_a?(Numeric) && (scroll_top=el.scrollTop).is_a?(Numeric)
      xy+=Vector[scroll_left, scroll_top]
    end
    el=el.parentNode
  end while el
  xy
end

#text_nodesObject

returns an array of all text nodes below this element in the DOM heirarchy



890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
# File 'lib/vapir-common/element.rb', line 890

def text_nodes
  # TODO: needs tests 
  assert_exists do
    recurse_text_nodes=proc do |rproc, e_obj|
      case e_obj.nodeType
      when 1 # TODO: name a constant ELEMENT_NODE, rather than magic number 
        object_collection_to_enumerable(e_obj.childNodes).inject([]) do |result, c_obj|
          result + rproc.call(rproc, c_obj)
        end
      when 3 # TODO: name a constant TEXT_NODE, rather than magic number 
        [e_obj.data]
      else
        #Kernel.warn("ignoring node of type #{e_obj.nodeType}")
        []
      end
    end
    recurse_text_nodes.call(recurse_text_nodes, element_object)
  end
end

#to_factoryObject

returns an Element that represents the same object as self, but is an instance of the most-specific class < self.class that can represent that object.

For example, if we have a table, get its first element, and call #to_factory on it:

a_table=browser.tables.first

> #<Vapir::IE::Table:0x071bc70c how=:index index=:first tagName=“TABLE”>

a_element=a_table.elements.first

> #<Vapir::IE::Element:0x071b856c how=:index index=:first tagName=“TBODY” id=“”>

a_element.to_factory

> #<Vapir::IE::TableBody:0x071af78c how=:index index=:first tagName=“TBODY” id=“”>

we get back a Vapir::TableBody.



713
714
715
# File 'lib/vapir-common/element.rb', line 713

def to_factory
  self.class.factory(element_object, @extra, @how, @what)
end

#to_sObject



1098
1099
1100
1101
1102
1103
1104
# File 'lib/vapir-common/element.rb', line 1098

def to_s
  attrs=attributes_for_stringifying
  longest_label=attrs.inject(0) {|max, attr| [max, attr.first.size].max }
  "#{self.class.name}:0x#{"%.8x"%(self.hash*2)}\n"+attrs.map do |attr|
    (attr.first+": ").ljust(longest_label+2)+attr.last.inspect+"\n"
  end.join('')
end

#visible?Boolean

Checks this element and its parents for display: none or visibility: hidden, these are the most common methods to hide an html element. Returns false if this seems to be hidden or a parent is hidden.

Returns:

  • (Boolean)


851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
# File 'lib/vapir-common/element.rb', line 851

def visible? 
  assert_exists do
    element_to_check=element_object
    #nsIDOMDocument=jssh_socket.Components.interfaces.nsIDOMDocument
    really_visible=nil
    while element_to_check #&& !element_to_check.instanceof(nsIDOMDocument)
      if (style=element_object_style(element_to_check, document_object))
        # only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements), 
        # or 'visible'. ignore 'inherit'; keep looking upward. 
        # this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up. 
        # this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
        if really_visible==nil && (visibility=style.invoke('visibility'))
          visibility=visibility.strip.downcase
          if visibility=='hidden' || visibility=='collapse'
            really_visible=false
            return false # don't need to continue knowing it's not visible. 
          elsif visibility=='visible'
            really_visible=true # we don't return true yet because a parent with display of 'none' can override 
          end
        end
        # check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible' 
        display=style.invoke('display')
        if display && display.strip.downcase=='none'
          return false
        end
      end
      element_to_check=element_to_check.parentNode
    end
  end
  return true
end

#visible_textObject

returns an visible text inside this element by concatenating text nodes below this element in the DOM heirarchy which are visible.



970
971
972
973
# File 'lib/vapir-common/element.rb', line 970

def visible_text
  # TODO: needs tests 
  visible_text_nodes.join('')
end

#visible_text_nodesObject

returns an array of text nodes below this element in the DOM heirarchy which are visible - that is, their parent element is visible.



911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
# File 'lib/vapir-common/element.rb', line 911

def visible_text_nodes
  # TODO: needs tests 
  assert_exists do
    # define a nice recursive function to iterate down through the children 
    recurse_text_nodes=proc do |rproc, e_obj, parent_visibility|
      case e_obj.nodeType
      when 1 # TODO: name a constant ELEMENT_NODE, rather than magic number 
        style=element_object_style(e_obj, document_object)
        our_visibility = style && (visibility=style.invoke('visibility'))
        unless our_visibility && ['hidden', 'collapse', 'visible'].include?(our_visibility=our_visibility.strip.downcase)
          our_visibility = parent_visibility
        end
        if (display=style.invoke('display')) && display.strip.downcase=='none'
          []
        else
          object_collection_to_enumerable(e_obj.childNodes).inject([]) do |result, c_obj|
            result + rproc.call(rproc, c_obj, our_visibility)
          end
        end
      when 3 # TODO: name a constant TEXT_NODE, rather than magic number 
        if ['hidden','collapse'].include?(parent_visibility)
          []
        else
          [e_obj.data]
        end
      else
        #Kernel.warn("ignoring node of type #{e_obj.nodeType}")
        []
      end
    end
  
    # determine the current visibility and display. TODO: this is copied/adapted from #visible?; should DRY 
    element_to_check=element_object
    real_visibility=nil
    while element_to_check #&& !element_to_check.instanceof(nsIDOMDocument)
      if (style=element_object_style(element_to_check, document_object))
        # only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements), 
        # or 'visible'. ignore 'inherit'; keep looking upward. 
        # this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up. 
        # this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
        if real_visibility==nil && (visibility=style.invoke('visibility'))
          visibility=visibility.strip.downcase
          if ['hidden', 'collapse', 'visible'].include?(visibility)
            real_visibility=visibility
          end
        end
        # check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible' 
        display=style.invoke('display')
        if display && (display=display.strip.downcase)=='none'
          # if display is none, then this element is not visible, and thus has no visible text nodes underneath. 
          return []
        end
      end
      element_to_check=element_to_check.parentNode
    end
    recurse_text_nodes.call(recurse_text_nodes, element_object, real_visibility)
  end
end

#with_highlight(options = {}) ⇒ Object

takes a block. sets highlight on this element; calls the block; clears the highlight. the clear is in an ensure block so that you can call return from the given block. doesn’t actually perform the highlighting if argument do_highlight is false.

also, you can nest these safely; it checks if you’re already highlighting before trying to set and subsequently clear the highlight.

the block is called within an assert_exists block, so for methods that highlight, the assert_exists can generally be omitted from there.



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
# File 'lib/vapir-common/element.rb', line 726

def with_highlight(options={})
  highlight_option_keys=[:color]
  #options=handle_options(options, {:highlight => true}, highlight_option_keys)
  options={:highlight => true}.merge(options)
  highlight_options=options.reject{|(k,v)| !highlight_option_keys.include?(k) }
  assert_exists do
    was_highlighting=@highlighting
    if (!@highlighting && options[:highlight])
      set_highlight(highlight_options)
    end
    @highlighting=true
    begin
      result=yield
    ensure
      @highlighting=was_highlighting
      if !@highlighting && options[:highlight]
        handling_existence_failure do
          assert_exists :force => true
          clear_highlight(options)
        end
      end
    end
    result
  end
end