# File lib/html/selector.rb, line 517
517:     def simple_selector(statement, values, can_negate = true)
518:       tag_name = nil
519:       attributes = []
520:       pseudo = []
521:       negation = []
522: 
523:       # Element name. (Note that in negation, this can come at
524:       # any order, but for simplicity we allow if only first).
525:       statement.sub!(/^(\*|[[:alpha:]][\w\-]*)/) do |match|
526:         match.strip!
527:         tag_name = match.downcase unless match == "*"
528:         @source << match
529:         "" # Remove
530:       end
531: 
532:       # Get identifier, class, attribute name, pseudo or negation.
533:       while true
534:         # Element identifier.
535:         next if statement.sub!(/^#(\?|[\w\-]+)/) do |match|
536:           id = $1
537:           if id == "?"
538:             id = values.shift
539:           end
540:           @source << "##{id}"
541:           id = Regexp.new("^#{Regexp.escape(id.to_s)}$") unless id.is_a?(Regexp)
542:           attributes << ["id", id]
543:           "" # Remove
544:         end
545: 
546:         # Class name.
547:         next if statement.sub!(/^\.([\w\-]+)/) do |match|
548:           class_name = $1
549:           @source << ".#{class_name}"
550:           class_name = Regexp.new("(^|\s)#{Regexp.escape(class_name)}($|\s)") unless class_name.is_a?(Regexp)
551:           attributes << ["class", class_name]
552:           "" # Remove
553:         end
554: 
555:         # Attribute value.
556:         next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
557:           name, equality, value = $1, $2, $3
558:           if value == "?"
559:             value = values.shift
560:           else
561:             # Handle single and double quotes.
562:             value.strip!
563:             if (value[0] == ?" or value[0] == ?') and value[0] == value[-1]
564:               value = value[1..-2]
565:             end
566:           end
567:           @source << "[#{name}#{equality}'#{value}']"
568:           attributes << [name.downcase.strip, attribute_match(equality, value)]
569:           "" # Remove
570:         end
571: 
572:         # Root element only.
573:         next if statement.sub!(/^:root/) do |match|
574:           pseudo << lambda do |element|
575:             element.parent.nil? or not element.parent.tag?
576:           end
577:           @source << ":root"
578:           "" # Remove
579:         end
580: 
581:         # Nth-child including last and of-type.
582:         next if statement.sub!(/^:nth-(last-)?(child|of-type)\((odd|even|(\d+|\?)|(-?\d*|\?)?n([+\-]\d+|\?)?)\)/) do |match|
583:           reverse = $1 == "last-"
584:           of_type = $2 == "of-type"
585:           @source << ":nth-#{$1}#{$2}("
586:           case $3
587:             when "odd"
588:               pseudo << nth_child(2, 1, of_type, reverse)
589:               @source << "odd)"
590:             when "even"
591:               pseudo << nth_child(2, 2, of_type, reverse)
592:               @source << "even)"
593:             when /^(\d+|\?)$/  # b only
594:               b = ($1 == "?" ? values.shift : $1).to_i
595:               pseudo << nth_child(0, b, of_type, reverse)
596:               @source << "#{b})"
597:             when /^(-?\d*|\?)?n([+\-]\d+|\?)?$/
598:               a = ($1 == "?" ? values.shift :
599:                    $1 == "" ? 1 : $1 == "-" ? -1 : $1).to_i
600:               b = ($2 == "?" ? values.shift : $2).to_i
601:               pseudo << nth_child(a, b, of_type, reverse)
602:               @source << (b >= 0 ? "#{a}n+#{b})" : "#{a}n#{b})")
603:             else
604:               raise ArgumentError, "Invalid nth-child #{match}"
605:           end
606:           "" # Remove
607:         end
608:         # First/last child (of type).
609:         next if statement.sub!(/^:(first|last)-(child|of-type)/) do |match|
610:           reverse = $1 == "last"
611:           of_type = $2 == "of-type"
612:           pseudo << nth_child(0, 1, of_type, reverse)
613:           @source << ":#{$1}-#{$2}"
614:           "" # Remove
615:         end
616:         # Only child (of type).
617:         next if statement.sub!(/^:only-(child|of-type)/) do |match|
618:           of_type = $1 == "of-type"
619:           pseudo << only_child(of_type)
620:           @source << ":only-#{$1}"
621:           "" # Remove
622:         end
623: 
624:         # Empty: no child elements or meaningful content (whitespaces
625:         # are ignored).
626:         next if statement.sub!(/^:empty/) do |match|
627:           pseudo << lambda do |element|
628:             empty = true
629:             for child in element.children
630:               if child.tag? or !child.content.strip.empty?
631:                 empty = false
632:                 break
633:               end
634:             end
635:             empty
636:           end
637:           @source << ":empty"
638:           "" # Remove
639:         end
640:         # Content: match the text content of the element, stripping
641:         # leading and trailing spaces.
642:         next if statement.sub!(/^:content\(\s*(\?|'[^']*'|"[^"]*"|[^)]*)\s*\)/) do |match|
643:           content = $1
644:           if content == "?"
645:             content = values.shift
646:           elsif (content[0] == ?" or content[0] == ?') and content[0] == content[-1]
647:             content = content[1..-2]
648:           end
649:           @source << ":content('#{content}')"
650:           content = Regexp.new("^#{Regexp.escape(content.to_s)}$") unless content.is_a?(Regexp)
651:           pseudo << lambda do |element|
652:             text = ""
653:             for child in element.children
654:               unless child.tag?
655:                 text << child.content
656:               end
657:             end
658:             text.strip =~ content
659:           end
660:           "" # Remove
661:         end
662: 
663:         # Negation. Create another simple selector to handle it.
664:         if statement.sub!(/^:not\(\s*/, "")
665:           raise ArgumentError, "Double negatives are not missing feature" unless can_negate
666:           @source << ":not("
667:           negation << simple_selector(statement, values, false)
668:           raise ArgumentError, "Negation not closed" unless statement.sub!(/^\s*\)/, "")
669:           @source << ")"
670:           next
671:         end
672: 
673:         # No match: moving on.
674:         break
675:       end
676: 
677:       # Return hash. The keys are mapped to instance variables.
678:       {:tag_name=>tag_name, :attributes=>attributes, :pseudo=>pseudo, :negation=>negation}
679:     end