class Nokogiri::XML::NodeSet

def self.new document, list = [] # :nodoc:

:nodoc:
def self.new document, list = [] # :nodoc:
  set = NodeSet.wrap(LibXML.xmlXPathNodeSetCreate(nil))
  set.document = document
  list.each { |x| set << x }
  yield set if block_given?
  set
end

def self.wrap(ptr) # :nodoc:

:nodoc:
def self.wrap(ptr) # :nodoc:
  set = allocate
  set.cstruct = LibXML::XmlNodeSet.new(ptr)
  set
end

def &(node_set) # :nodoc:

:nodoc:
def &(node_set) # :nodoc:
  raise(ArgumentError, "node_set must be a Nokogiri::XML::NodeSet") unless node_set.is_a?(XML::NodeSet)
  new_set_ptr = LibXML.xmlXPathIntersection(cstruct, node_set.cstruct)
  NodeSet.wrap(new_set_ptr)
end

def +(node_set) # :nodoc:

:nodoc:
def +(node_set) # :nodoc:
  raise(ArgumentError, "node_set must be a Nokogiri::XML::NodeSet") unless node_set.is_a?(XML::NodeSet)
  new_set_ptr = LibXML::xmlXPathNodeSetMerge(nil, self.cstruct)
  new_set_ptr = LibXML::xmlXPathNodeSetMerge(new_set_ptr, node_set.cstruct)
  NodeSet.wrap(new_set_ptr)
end

def -(node_set) # :nodoc:

:nodoc:
def -(node_set) # :nodoc:
  raise(ArgumentError, "node_set must be a Nokogiri::XML::NodeSet") unless node_set.is_a?(XML::NodeSet)
  new_set_ptr = LibXML.xmlXPathNodeSetMerge(nil, self.cstruct)
  other_nodetab = node_set.cstruct.nodeTab
  node_set.cstruct[:nodeNr].times do |j|
    LibXML.xmlXPathNodeSetDel(new_set_ptr, other_nodetab[j])
  end        
  NodeSet.wrap(new_set_ptr)
end

def == other

element in the other NodeSet
of elements and if each element is equal to the corresponding
Equality -- Two NodeSets are equal if the contain the same number
##
def == other
  return false unless other.is_a?(Nokogiri::XML::NodeSet)
  return false unless length == other.length
  each_with_index do |node, i|
    return false unless node == other[i]
  end
  true
end

def [](*args) # :nodoc:

:nodoc:
def [](*args) # :nodoc:
  raise(ArgumentError, "got #{args.length} arguments, expected 1 (or 2)") if args.length > 2
  if args.length == 2
    beg = args[0]
    len = args[1]
    beg += cstruct[:nodeNr] if beg < 0
    return subseq(beg, len)
  end
  arg = args[0]
  return subseq(arg.first, arg.last-arg.first+1) if arg.is_a?(Range)
  index_at(arg)
end

def add_class name

Append the class attribute +name+ to all Node objects in the NodeSet.
##
def add_class name
  each do |el|
    next unless el.respond_to? :get_attribute
    classes = el.get_attribute('class').to_s.split(" ")
    el.set_attribute('class', classes.push(name).uniq.join(" "))
  end
  self
end

def after datum

Insert +datum+ after the last Node in this NodeSet
##
def after datum
  last.after datum
end

def at path, ns = document.root ? document.root.namespaces : {}

first Node. Otherwise, index in to the array with +path+.
If path is a string, search this document for +path+ returning the
##
def at path, ns = document.root ? document.root.namespaces : {}
  return self[path] if path.is_a?(Numeric)
  search(path, ns).first
end

def attr key, value = nil, &blk

on all Node objects in the NodeSet.
Set the attribute +key+ to +value+ or the return value of +blk+
##
def attr key, value = nil, &blk
  if value or blk
    each do |el|
      el.set_attribute(key, value || blk[el])
    end
    return self
  end
  if key.is_a? Hash
    key.each { |k,v| self.attr(k,v) }
    return self
  else
    return self[0].get_attribute(key)
  end
end

def before datum

Insert +datum+ before the first Node in this NodeSet
##
def before datum
  first.before datum
end

def delete(node) # :nodoc:

:nodoc:
def delete(node) # :nodoc:
  raise(ArgumentError, "node must be a Nokogiri::XML::Node") unless node.is_a?(XML::Node)
  if LibXML.xmlXPathNodeSetContains(cstruct, node.cstruct) != 0
    LibXML.xmlXPathNodeSetDel(cstruct, node.cstruct)
    return node
  end
  return nil
end

def dup # :nodoc:

:nodoc:
def dup # :nodoc:
  dup = LibXML.xmlXPathNodeSetMerge(nil, self.cstruct)
  NodeSet.wrap(dup)
end

def each(&block)

Iterate over each node, yielding to +block+
##
def each(&block)
  0.upto(length - 1) do |x|
    yield self[x]
  end
end

def empty?

Is this NodeSet empty?
##
def empty?
  length == 0
end

def first n = nil

Get the first element of the NodeSet.
##
def first n = nil
  return self[0] unless n
  list = []
  0.upto(n - 1) do |i|
    list << self[i]
  end
  list
end

def include?(node) # :nodoc:

:nodoc:
def include?(node) # :nodoc:
  raise(ArgumentError, "node must be a Nokogiri::XML::Node") unless node.is_a?(XML::Node)
  (LibXML.xmlXPathNodeSetContains(cstruct, node.cstruct) != 0) ? true : false
end

def index(node)

Returns the index of the first node in self that is == to +node+. Returns nil if no match is found.
##
def index(node)
  each_with_index { |member, j| return j if member == node }
  nil
end

def index_at(number) # :nodoc:

:nodoc:
def index_at(number) # :nodoc:
  return nil if (number >= cstruct[:nodeNr] || number.abs > cstruct[:nodeNr])
  number = number + cstruct[:nodeNr] if number < 0
  Node.wrap(cstruct.nodeAt(number))
end

def initialize document, list = []

Create a NodeSet with +document+ defaulting to +list+
def initialize document, list = []
  @document = document
  list.each { |x| self << x }
  yield self if block_given?
end

def inner_html

Get the inner html of all contained Node objects
##
def inner_html
  collect{|j| j.inner_html}.join('')
end

def inner_text

Get the inner text of all contained Node objects
##
def inner_text
  collect{|j| j.inner_text}.join('')
end

def last

Get the last element of the NodeSet.
##
def last
  self[length - 1]
end

def length # :nodoc:

:nodoc:
def length # :nodoc:
  cstruct.pointer.null? ? 0 : cstruct[:nodeNr]
end

def pop

the set is empty
Removes the last element from set and returns it, or +nil+ if
##
def pop
  return nil if length == 0
  delete last
end

def push(node) # :nodoc:

:nodoc:
def push(node) # :nodoc:
  raise(ArgumentError, "node must be a Nokogiri::XML::Node") unless node.is_a?(XML::Node)
  LibXML.xmlXPathNodeSetAdd(cstruct, node.cstruct)
  self
end

def remove_attr name

Remove the attributed named +name+ from all Node objects in the NodeSet
##
def remove_attr name
  each do |el|
    next unless el.respond_to? :remove_attribute
    el.remove_attribute(name)
  end
  self
end

def remove_class name = nil

Remove the class attribute +name+ from all Node objects in the NodeSet.
##
def remove_class name = nil
  each do |el|
    next unless el.respond_to? :get_attribute
    if name
      classes = el.get_attribute('class').to_s.split(" ")
      el.set_attribute('class', (classes - [name]).uniq.join(" "))
    else
      el.remove_attribute("class")
    end
  end
  self
end

def search *paths

Nokogiri::XML::Node#xpath
For more information see Nokogiri::XML::Node#css and

Search this document for +paths+
##
def search *paths
  ns = paths.last.is_a?(Hash) ? paths.pop : document.root.namespaces
  sub_set = NodeSet.new(document)
  document.decorate(sub_set)
  each do |node|
    node.search(*(paths + [ns])).each do |sub_node|
      sub_set << sub_node
    end
  end
  sub_set
end

def shift

+nil+ if the set is empty.
Returns the first element of the NodeSet and removes it. Returns
##
def shift
  return nil if length == 0
  delete first
end

def subseq(beg, len) # :nodoc:

:nodoc:
def subseq(beg, len) # :nodoc:
  return nil if beg > cstruct[:nodeNr]
  return nil if beg < 0 || len < 0
  set = NodeSet.wrap(LibXML.xmlXPathNodeSetCreate(nil))
  beg.upto(beg+len-1) do |j|
    LibXML.xmlXPathNodeSetAdd(set.cstruct, cstruct.nodeAt(j));
  end
  set
end

def to_a # :nodoc:

:nodoc:
def to_a # :nodoc:
  cstruct.nodeTab.collect { |node| Node.wrap(node) }
end

def to_html *args

Convert this NodeSet to HTML
##
def to_html *args
  map { |x| x.to_html(*args) }.join
end

def to_s

Convert this NodeSet to a string.
##
def to_s
  map { |x| x.to_s }.join
end

def to_xhtml *args

Convert this NodeSet to XHTML
##
def to_xhtml *args
  map { |x| x.to_xhtml(*args) }.join
end

def to_xml *args

Convert this NodeSet to XML
##
def to_xml *args
  map { |x| x.to_xml(*args) }.join
end

def unlink # :nodoc:

:nodoc:
def unlink # :nodoc:
  # TODO: is this simpler implementation viable:
  #  cstruct.nodeTab.collect {|node| Node.wrap(node)}.each(&:unlink)
  # ?
  nodetab = cstruct.nodeTab
  cstruct[:nodeNr].times do |j|
    node = Node.wrap(nodetab[j])
    node.unlink
    nodetab[j] = node.cstruct.pointer
  end
  cstruct.nodeTab = nodetab
  self
end

def wrap(html, &blk)

Wrap this NodeSet with +html+ or the results of the builder in +blk+
##
def wrap(html, &blk)
  each do |j|
    new_parent = Nokogiri.make(html, &blk)
    j.parent.add_child(new_parent)
    new_parent.add_child(j)
  end
  self
end