class RSpecHtmlMatchers::HaveTag

@private
@api

def classes_to_selector(classes)

def classes_to_selector(classes)
  case classes
  when Array
    classes.join('.')
  when String
    classes.gsub(/\s+/, '.')
  end
end

def count_right?

def count_right?
  case @options[:count]
  when Integer
    ((@failure_message_when_negated=MESSAGES[:unexpected_count] % [@document,@count,@tag]) && @count == @options[:count]) || (@failure_message=MESSAGES[:expected_count] % [@document,@options[:count],@tag,@count]; false)
  when Range
    ((@failure_message_when_negated=MESSAGES[:unexpected_btw_count] % [@document,@options[:count].min,@options[:count].max,@tag,@count]) && @options[:count].member?(@count)) || (@failure_message=MESSAGES[:expected_btw_count] % [@document,@options[:count].min,@options[:count].max,@tag,@count]; false)
  when nil
    if @options[:maximum]
      ((@failure_message_when_negated=MESSAGES[:unexpected_at_most] % [@document,@options[:maximum],@tag,@count]) && @count <= @options[:maximum]) || (@failure_message=MESSAGES[:expected_at_most] % [@document,@options[:maximum],@tag,@count]; false)
    elsif @options[:minimum]
      ((@failure_message_when_negated=MESSAGES[:unexpected_at_least] % [@document,@options[:minimum],@tag,@count]) && @count >= @options[:minimum]) || (@failure_message=MESSAGES[:expected_at_least] % [@document,@options[:minimum],@tag,@count]; false)
    else
      true
    end
  end
end

def description

def description
  # TODO should it be more complicated?
  if @options.has_key?(:count)
    DESCRIPTIONS[:have_n] % [@options[:count],@tag]
  else
    DESCRIPTIONS[:have_at_least_1] % @tag
  end
end

def initialize tag, options={}, &block

def initialize tag, options={}, &block
  @tag, @options, @block = tag.to_s, options, block
  if with_attrs = @options.delete(:with)
    if classes = with_attrs.delete(:class)
      @tag << '.' + classes_to_selector(classes)
    end
    selector = with_attrs.inject('') do |html_attrs_string, (k, v)|
      html_attrs_string << "[#{k}='#{v}']"
    html_attrs_string
    end
    @tag << selector
  end
  if without_attrs = @options.delete(:without)
    if classes = without_attrs.delete(:class)
      @tag << ":not(.#{classes_to_selector(classes)})"
    end
  end
  validate_options!
end

def matches? document, &block

def matches? document, &block
  @block = block if block
  document = document.html if defined?(Capybara::Session) && document.is_a?(Capybara::Session)
  case document
  when String
    @parent_scope = @current_scope = Nokogiri::HTML(document).css(@tag)
    @document     = document
  else
    @parent_scope  = document.current_scope
    @current_scope = begin
                       document.parent_scope.css(@tag)
                       # on jruby this produce exception if css was not found:
                       # undefined method `decorate' for nil:NilClass
                     rescue NoMethodError
                       Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new)
                     end
    @document      = @parent_scope.to_html
  end
  if tag_presents? and text_right? and count_right?
    @current_scope = @parent_scope
    @block.call if @block
    true
  else
    false
  end
end

def tag_presents?

def tag_presents?
  if @current_scope.first
    @count = @current_scope.count
    @failure_message_when_negated = MESSAGES[:unexpected_tag] % [@document, @tag, @count]
    true
  else
    @failure_message          = MESSAGES[:expected_tag] % [@document, @tag]
    false
  end
end

def text_right?

def text_right?
  return true unless @options[:text]
  case text=@options[:text]
  when Regexp
    new_scope = @current_scope.css(':regexp()',NokogiriRegexpHelper.new(text))
    unless new_scope.empty?
      @count = new_scope.count
      @failure_message_when_negated = MESSAGES[:unexpected_regexp] % [text.inspect,@tag,@document]
      true
    else
      @failure_message          = MESSAGES[:expected_regexp] % [text.inspect,@tag,@document]
      false
    end
  else
    new_scope = @current_scope.css(':content()',NokogiriTextHelper.new(text))
    unless new_scope.empty?
      @count = new_scope.count
      @failure_message_when_negated = MESSAGES[:unexpected_text] % [text,@tag,@document]
      true
    else
      @failure_message          = MESSAGES[:expected_text] % [text,@tag,@document]
      false
    end
  end
end

def validate_options!

def validate_options!
  raise 'wrong :count specified' unless [Range, NilClass].include?(@options[:count].class) or @options[:count].is_a?(Integer)
  [:min, :minimum, :max, :maximum].each do |key|
    raise MESSAGES[:wrong_count_error] if @options.has_key?(key) and @options.has_key?(:count)
  end
  begin
    raise MESSAGES[:min_max_error] if @options[:minimum] > @options[:maximum]
  rescue NoMethodError # nil > 4
  rescue ArgumentError # 2 < nil
  end
  begin
    begin
      raise MESSAGES[:bad_range_error] % [@options[:count].to_s] if @options[:count] && @options[:count].is_a?(Range) && (@options[:count].min.nil? or @options[:count].min < 0)
    rescue ArgumentError, "comparison of String with" # if @options[:count] == 'a'..'z'
      raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
    end
  rescue TypeError # fix for 1.8.7 for 'rescue ArgumentError, "comparison of String with"' stroke
    raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
  end
  @options[:minimum] ||= @options.delete(:min)
  @options[:maximum] ||= @options.delete(:max)
  @options[:text] = @options[:text].to_s if @options.has_key?(:text) && !@options[:text].is_a?(Regexp)
end