Class: Dommy::TreeWalker

Inherits:
Object
  • Object
show all
Includes:
Bridge::Methods, TreeTraversalCore
Defined in:
lib/dommy/tree_walker.rb

Overview

TreeWalker — stateful traversal with ‘next_node` / `previous_node` / `parent_node` / `first_child` / `last_child` / `next_sibling` / `previous_sibling` and a mutable `current_node` cursor.

Wraps Nokogiri descent; doesn’t snapshot the tree, so mutations during traversal are visible (matches DOM spec).

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Bridge::Methods

included

Constructor Details

#initialize(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil) ⇒ TreeWalker

Returns a new instance of TreeWalker.



104
105
106
107
108
109
# File 'lib/dommy/tree_walker.rb', line 104

def initialize(root, what_to_show = NodeFilter::SHOW_ALL, filter = nil)
  @root = root
  @what_to_show = what_to_show.to_i
  @filter = filter
  @current_node = root
end

Instance Attribute Details

#current_nodeObject

Returns the value of attribute current_node.



102
103
104
# File 'lib/dommy/tree_walker.rb', line 102

def current_node
  @current_node
end

#filterObject (readonly)

Returns the value of attribute filter.



101
102
103
# File 'lib/dommy/tree_walker.rb', line 101

def filter
  @filter
end

#rootObject (readonly)

Returns the value of attribute root.



101
102
103
# File 'lib/dommy/tree_walker.rb', line 101

def root
  @root
end

#what_to_showObject (readonly)

Returns the value of attribute what_to_show.



101
102
103
# File 'lib/dommy/tree_walker.rb', line 101

def what_to_show
  @what_to_show
end

Instance Method Details

#__js_call__(method, _args) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/dommy/tree_walker.rb', line 218

def __js_call__(method, _args)
  case method
  when "nextNode"
    next_node
  when "previousNode"
    previous_node
  when "parentNode"
    parent_node
  when "firstChild"
    first_child
  when "lastChild"
    last_child
  when "nextSibling"
    next_sibling
  when "previousSibling"
    previous_sibling
  end
end

#__js_get__(key) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/dommy/tree_walker.rb', line 193

def __js_get__(key)
  case key
  when "root"
    @root
  when "whatToShow"
    @what_to_show
  when "filter"
    @filter
  when "currentNode"
    @current_node
  end
end

#__js_set__(key, value) ⇒ Object

Raises:



206
207
208
209
210
211
212
213
214
# File 'lib/dommy/tree_walker.rb', line 206

def __js_set__(key, value)
  return Bridge::UNHANDLED unless key == "currentNode"

  # currentNode is a non-null `Node`; non-Node values are a TypeError.
  raise Bridge::TypeError, "currentNode must be a Node" unless value.is_a?(Dommy::Node)

  @current_node = value
  nil
end

#first_childObject



177
178
179
# File 'lib/dommy/tree_walker.rb', line 177

def first_child
  traverse_children(:first_wrapped_child, :next_sibling_wrapped)
end

#last_childObject



181
182
183
# File 'lib/dommy/tree_walker.rb', line 181

def last_child
  traverse_children(:last_wrapped_child, :previous_sibling_wrapped)
end

#next_nodeObject



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/dommy/tree_walker.rb', line 111

def next_node
  node = @current_node
  result = NodeFilter::FILTER_ACCEPT
  loop do
    while result != NodeFilter::FILTER_REJECT && (child = first_wrapped_child(node))
      node = child
      result = accept(node)
      return @current_node = node if result == NodeFilter::FILTER_ACCEPT
    end

    sibling = nil
    temp = node
    while temp
      return nil if temp == @root

      sibling = next_sibling_wrapped(temp)
      if sibling
        node = sibling
        break
      end
      temp = wrapped_parent(temp)
    end
    return nil unless sibling

    result = accept(node)
    return @current_node = node if result == NodeFilter::FILTER_ACCEPT
  end
end

#next_siblingObject



185
186
187
# File 'lib/dommy/tree_walker.rb', line 185

def next_sibling
  traverse_siblings(:next_sibling_wrapped, :first_wrapped_child)
end

#parent_nodeObject



166
167
168
169
170
171
172
173
174
175
# File 'lib/dommy/tree_walker.rb', line 166

def parent_node
  node = wrapped_parent(@current_node)
  while node && reachable_from_root?(node)
    return @current_node = node if accept(node) == NodeFilter::FILTER_ACCEPT

    node = wrapped_parent(node)
  end

  nil
end

#previous_nodeObject



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/dommy/tree_walker.rb', line 140

def previous_node
  node = @current_node
  while node != @root
    sibling = previous_sibling_wrapped(node)
    while sibling
      node = sibling
      result = accept(node)
      while result != NodeFilter::FILTER_REJECT && (child = last_wrapped_child(node))
        node = child
        result = accept(node)
      end
      return @current_node = node if result == NodeFilter::FILTER_ACCEPT

      sibling = previous_sibling_wrapped(node)
    end

    parent = wrapped_parent(node)
    return nil if node == @root || parent.nil?

    node = parent
    return @current_node = node if accept(node) == NodeFilter::FILTER_ACCEPT
  end

  nil
end

#previous_siblingObject



189
190
191
# File 'lib/dommy/tree_walker.rb', line 189

def previous_sibling
  traverse_siblings(:previous_sibling_wrapped, :last_wrapped_child)
end