class Wunderbar::Node

Constants

ESCAPE
VOID

Attributes

attrs[RW]
children[RW]
name[RW]
node[RW]
parent[RW]
text[RW]

Public Class Methods

new(name, *args) click to toggle source
# File lib/wunderbar/node.rb, line 19
def initialize(name, *args)
  @name = name
  @text = nil
  @attrs = {}
  @children = []
  args -= symbols = args.find_all {|arg| Symbol === arg}
  @attrs = args.pop.to_hash if args.last.respond_to? :to_hash
  @text = args.shift.to_s unless args.empty?
  symbols.each {|sym| @attrs[sym] = true}
end
parse_css_selector(css) click to toggle source

parse a subset of css_selectors

# File lib/wunderbar/node.rb, line 199
def self.parse_css_selector(css)
  if css.include? ' '
    css.split(/\s+/).map {|token| parse_css_selector(token).first}
  else
    result = {}
    while css != ''
      if css =~/^([-\w]+)/
        result[:name] = $1
        css=css[$1.length..-1]
      elsif css =~/^#([-\w]+)/
        raise ArgumentError("duplicate id") if result[:id]
        result[:id] = $1
        css=css[$1.length+1..-1]
      elsif css =~/^\.([-\w]+)/
        result[:class] ||= []
        raise ArgumentError("duplicate class") if result[:class].include?  $1
        result[:class] << $1
        css=css[$1.length+1..-1]
      elsif css =~/^\[([-\w]+)=([-\w]+)\]/
        result[:attr] ||= {}
        raise ArgumentError("duplicate attribute") if result[:attr][$1]
        result[:attr][$1] = $2
        css=css[$1.length+$2.length+3..-1]
      elsif css =~/^\[([-\w]+)='([^']+)'\]/
        result[:attr] ||= {}
        raise ArgumentError("duplicate attribute") if result[:attr][$1]
        result[:attr][$1] = $2
        css=css[$1.length+$2.length+5..-1]
      elsif css =~/^\[([-\w]+)="([^"]+)"\]/
        result[:attr] ||= {}
        raise ArgumentError("duplicate attribute") if result[:attr][$1]
        result[:attr][$1] = $2
        css=css[$1.length+$2.length+5..-1]
      elsif css =~/^\*/
        css=css[1..-1]
      else
        raise ArgumentError("syntax error: #{css.inspect}")
      end
    end
    [result]
  end
end

Public Instance Methods

add_child(child) click to toggle source
# File lib/wunderbar/node.rb, line 41
def add_child(child)
  @children << child
  child.parent = self
end
at(css) click to toggle source
# File lib/wunderbar/node.rb, line 194
def at(css)
  search(css).first
end
method_missing(*args) click to toggle source
# File lib/wunderbar/node.rb, line 30
def method_missing(*args)
  if args.length == 0
    attrs[:class] = (attrs[:class].to_s.split(' ') + [name]).join(' ')
  else
    name = args.first.to_s
    err = NameError.new "undefined local variable or method `#{name}'", name
    err.set_backtrace caller
    raise err
  end
end
preserve_spaces?() click to toggle source
# File lib/wunderbar/node.rb, line 146
def preserve_spaces?
  false
end
root() click to toggle source
# File lib/wunderbar/node.rb, line 150
def root
  if parent
    parent.root
  else
    self
  end
end
serialize(options = {}, result = [], indent = '') click to toggle source
# File lib/wunderbar/node.rb, line 70
def serialize(options = {}, result = [], indent = '')
  line = "#{indent}<#{name}"

  attrs.each do |name, value| 
    next unless value
    name = name.to_s.gsub('_','-') if Symbol === name
    value=name if value==true
    line += " #{name}=\"#{value.to_s.gsub(/[&\"<>\u00A0]/,ESCAPE)}\""
  end

  if children.empty? 
    if options[:pre]
      line += ">#{options[:pre]}#{text}#{options[:post]}</#{name}>"
    else
      width = options[:width] unless preserve_spaces?

      if text
        line += ">#{text.to_s.gsub(/[&<>\u00A0]/,ESCAPE)}</#{name}>"
      elsif VOID.include? name.to_s
        line += "/>"
      else
        line += "></#{name}>"
      end

      if indent and width and (line.length > width or line.include? "\n")
        reflowed = IndentedTextNode.reflow(indent, line, width,
          options[:indent])
        line = reflowed.pop
        result.push(*reflowed)
      end
    end
  elsif CompactNode === self and not CompactNode === parent
    work = []
    walk(work, nil, options)
    width = options[:width]
    if width
      line += ">"
      work = work.join + "</#{name}>"
      if line.length + work.length <= width
        line += work
      else
        # split work into tokens with balanced <>
        tokens = work.split(' ')
        (tokens.length-1).downto(1)  do |i|
          if tokens[i].count('<') != tokens[i].count('>')
            tokens[i-1,2] = tokens[i-1] + ' ' + tokens[i]
          end
        end

        line += tokens.shift

        # add tokens to line, breaking when line length would exceed width
        tokens.each do |token|
          if line.length + token.length < width
            line += ' ' + token
          else
            result << line
            line = indent.to_s + options[:indent] + token
          end
        end
      end
    else
      line += ">#{work.join}</#{name}>"
    end
  else
    result <<  line+">#{options[:pre]}" if parent

    walk(result, indent, options) unless children.empty?

    line = "#{indent}#{options[:post]}</#{name}>"
  end

  result << line if parent
  result
end
walk(result, indent, options) click to toggle source
# File lib/wunderbar/node.rb, line 46
def walk(result, indent, options)
  indent += options[:indent] if indent and parent
  first = true
  spaced = false

  if preserve_spaces?
    options = options.dup
    options[:space] = :preserve
  end

  children.each do |child| 
    next unless child
    result << '' if (spaced or SpacedNode === child) and not first
    if String === child
      child = child.gsub(/\s+/, ' ') unless options[:space] == :preserve
      result << child
    else
      child.serialize(options, result, indent)
    end
    first = false
    spaced = (SpacedNode === child)
  end
end