2018-04-18 20:45:35 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-09-22 23:55:54 +00:00
|
|
|
module Capybara
|
|
|
|
class Selector
|
|
|
|
class CSS
|
|
|
|
def self.escape(str)
|
|
|
|
value = str.dup
|
2018-07-10 21:18:39 +00:00
|
|
|
out = +''
|
2019-04-04 21:15:27 +00:00
|
|
|
out << value.slice!(0...1) if value.match?(/^[-_]/)
|
2019-03-25 16:37:25 +00:00
|
|
|
out << (value[0].match?(NMSTART) ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
2018-08-20 23:46:31 +00:00
|
|
|
out << value.gsub(/[^a-zA-Z0-9_-]/) { |char| escape_char char }
|
2016-09-22 23:55:54 +00:00
|
|
|
out
|
|
|
|
end
|
|
|
|
|
2018-08-20 23:46:31 +00:00
|
|
|
def self.escape_char(char)
|
2019-03-25 16:37:25 +00:00
|
|
|
char.match?(%r{[ -/:-~]}) ? "\\#{char}" : format('\\%06x', char.ord)
|
2016-09-22 23:55:54 +00:00
|
|
|
end
|
|
|
|
|
2018-06-04 21:56:05 +00:00
|
|
|
def self.split(css)
|
|
|
|
Splitter.new.split(css)
|
|
|
|
end
|
|
|
|
|
2018-05-10 20:20:23 +00:00
|
|
|
S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
|
2018-11-02 18:38:58 +00:00
|
|
|
H = /[0-9a-fA-F]/.freeze
|
|
|
|
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/.freeze
|
|
|
|
NONASCII = /[#{S}]/.freeze
|
|
|
|
ESCAPE = /#{UNICODE}|\\[ -~#{S}]/.freeze
|
|
|
|
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/.freeze
|
2018-06-04 21:56:05 +00:00
|
|
|
|
|
|
|
class Splitter
|
|
|
|
def split(css)
|
|
|
|
selectors = []
|
2018-09-04 03:38:41 +00:00
|
|
|
StringIO.open(css.to_s) do |str|
|
2018-11-19 23:44:51 +00:00
|
|
|
selector = +''
|
2018-08-20 23:46:31 +00:00
|
|
|
while (char = str.getc)
|
|
|
|
case char
|
2018-06-04 21:56:05 +00:00
|
|
|
when '['
|
2018-11-19 23:44:51 +00:00
|
|
|
selector << parse_square(str)
|
2018-06-04 21:56:05 +00:00
|
|
|
when '('
|
2018-11-19 23:44:51 +00:00
|
|
|
selector << parse_paren(str)
|
2018-06-04 21:56:05 +00:00
|
|
|
when '"', "'"
|
2018-11-19 23:44:51 +00:00
|
|
|
selector << parse_string(char, str)
|
2018-06-04 21:56:05 +00:00
|
|
|
when '\\'
|
2018-11-19 23:44:51 +00:00
|
|
|
selector << char + str.getc
|
2018-06-04 21:56:05 +00:00
|
|
|
when ','
|
|
|
|
selectors << selector.strip
|
2018-11-19 23:44:51 +00:00
|
|
|
selector.clear
|
2018-06-04 21:56:05 +00:00
|
|
|
else
|
2018-11-19 23:44:51 +00:00
|
|
|
selector << char
|
2018-06-04 21:56:05 +00:00
|
|
|
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
|
2018-08-20 23:46:31 +00:00
|
|
|
while (char = strio.getc)
|
|
|
|
case char
|
2018-06-04 21:56:05 +00:00
|
|
|
when final
|
2018-08-20 23:46:31 +00:00
|
|
|
return block + char
|
2018-06-04 21:56:05 +00:00
|
|
|
when '\\'
|
2018-08-20 23:46:31 +00:00
|
|
|
block += char + strio.getc
|
2018-06-04 21:56:05 +00:00
|
|
|
when '"', "'"
|
2018-08-20 23:46:31 +00:00
|
|
|
block += parse_string(char, strio)
|
2018-06-04 21:56:05 +00:00
|
|
|
else
|
2018-08-20 23:46:31 +00:00
|
|
|
block += char
|
2018-06-04 21:56:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
raise ArgumentError, "Invalid CSS Selector - Block end '#{final}' not found"
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_string(quote, strio)
|
|
|
|
string = quote
|
2018-08-20 23:46:31 +00:00
|
|
|
while (char = strio.getc)
|
|
|
|
string += char
|
|
|
|
case char
|
2018-06-04 21:56:05 +00:00
|
|
|
when quote
|
|
|
|
return string
|
|
|
|
when '\\'
|
|
|
|
string += strio.getc
|
|
|
|
end
|
|
|
|
end
|
|
|
|
raise ArgumentError, 'Invalid CSS Selector - string end not found'
|
|
|
|
end
|
|
|
|
end
|
2016-09-22 23:55:54 +00:00
|
|
|
end
|
|
|
|
end
|
2018-01-09 22:05:50 +00:00
|
|
|
end
|