File: css.rb

package info (click to toggle)
ruby-capybara 3.40.0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,368 kB
  • sloc: ruby: 23,988; javascript: 752; makefile: 11
file content (102 lines) | stat: -rw-r--r-- 2,730 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# frozen_string_literal: true

require 'capybara/selector/selector'

module Capybara
  class Selector
    class CSS
      def self.escape(str)
        value = str.dup
        out = +''
        out << value.slice!(0...1) if value.match?(/^[-_]/)
        out << (value[0].match?(NMSTART) ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
        out << value.gsub(/[^a-zA-Z0-9_-]/) { |char| escape_char char }
        out
      end

      def self.escape_char(char)
        char.match?(%r{[ -/:-~]}) ? "\\#{char}" : format('\\%06<hex>x', hex: char.ord)
      end

      def self.split(css)
        Splitter.new.split(css)
      end

      S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
      H = /[0-9a-fA-F]/
      UNICODE  = /\\#{H}{1,6}[ \t\r\n\f]?/
      NONASCII = /[#{S}]/
      ESCAPE   = /#{UNICODE}|\\[ -~#{S}]/
      NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/

      class Splitter
        def split(css)
          selectors = []
          StringIO.open(css.to_s) do |str|
            selector = +''
            while (char = str.getc)
              case char
              when '['
                selector << parse_square(str)
              when '('
                selector << parse_paren(str)
              when '"', "'"
                selector << parse_string(char, str)
              when '\\'
                selector << (char + str.getc)
              when ','
                selectors << selector.strip
                selector.clear
              else
                selector << char
              end
            end
            selectors << selector.strip
          end
          selectors
        end

      private

        def parse_square(strio)
          parse_block('[', ']', strio)
        end

        def parse_paren(strio)
          parse_block('(', ')', strio)
        end

        def parse_block(start, final, strio)
          block = start
          while (char = strio.getc)
            case char
            when final
              return block + char
            when '\\'
              block += char + strio.getc
            when '"', "'"
              block += parse_string(char, strio)
            else
              block += char
            end
          end
          raise ArgumentError, "Invalid CSS Selector - Block end '#{final}' not found"
        end

        def parse_string(quote, strio)
          string = quote
          while (char = strio.getc)
            string += char
            case char
            when quote
              return string
            when '\\'
              string += strio.getc
            end
          end
          raise ArgumentError, 'Invalid CSS Selector - string end not found'
        end
      end
    end
  end
end