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.



122
123
124
125
126
127
# File 'lib/dommy/tree_walker.rb', line 122

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.



120
121
122
# File 'lib/dommy/tree_walker.rb', line 120

def current_node
  @current_node
end

#filterObject (readonly)

Returns the value of attribute filter.



119
120
121
# File 'lib/dommy/tree_walker.rb', line 119

def filter
  @filter
end

#rootObject (readonly)

Returns the value of attribute root.



119
120
121
# File 'lib/dommy/tree_walker.rb', line 119

def root
  @root
end

#what_to_showObject (readonly)

Returns the value of attribute what_to_show.



119
120
121
# File 'lib/dommy/tree_walker.rb', line 119

def what_to_show
  @what_to_show
end

Instance Method Details

#__js_call__(method, _args) ⇒ Object



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/dommy/tree_walker.rb', line 238

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



211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/dommy/tree_walker.rb', line 211

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

#__js_set__(key, value) ⇒ Object

Raises:



226
227
228
229
230
231
232
233
234
# File 'lib/dommy/tree_walker.rb', line 226

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



195
196
197
# File 'lib/dommy/tree_walker.rb', line 195

def first_child
  traverse_children(:first_wrapped_child, :next_sibling_wrapped)
end

#last_childObject



199
200
201
# File 'lib/dommy/tree_walker.rb', line 199

def last_child
  traverse_children(:last_wrapped_child, :previous_sibling_wrapped)
end

#next_nodeObject



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/dommy/tree_walker.rb', line 129

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



203
204
205
# File 'lib/dommy/tree_walker.rb', line 203

def next_sibling
  traverse_siblings(:next_sibling_wrapped, :first_wrapped_child)
end

#parent_nodeObject



184
185
186
187
188
189
190
191
192
193
# File 'lib/dommy/tree_walker.rb', line 184

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



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/dommy/tree_walker.rb', line 158

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



207
208
209
# File 'lib/dommy/tree_walker.rb', line 207

def previous_sibling
  traverse_siblings(:previous_sibling_wrapped, :last_wrapped_child)
end