Moving over Nokogiri extensions and vendoring nokogiri CSS support for people without nokogiri installed

This commit is contained in:
Bryan Helmkamp 2008-11-06 15:37:16 -05:00
parent 11f291ceb3
commit a8e0e7578a
12 changed files with 1458 additions and 1 deletions

View File

@ -6,9 +6,10 @@ module Webrat
# Require nokogiri and fall back on rexml
begin
require "nokogiri"
require "webrat/nokogiri"
rescue LoadError => e
if require "rexml/document"
require "merb-core/vendor/nokogiri/css"
require "webrat/vendor/nokogiri/css"
warn("Standard REXML library is slow. Please consider installing nokogiri.\nUse \"sudo gem install nokogiri\"")
end
end

15
lib/webrat/nokogiri.rb Normal file
View File

@ -0,0 +1,15 @@
module Nokogiri
module CSS
class XPathVisitor
def visit_pseudo_class_text(node)
"@type='text'"
end
def visit_pseudo_class_password(node)
"@type='password'"
end
end
end
end

6
lib/webrat/vendor/nokogiri/css.rb vendored Normal file
View File

@ -0,0 +1,6 @@
require 'merb-core/vendor/nokogiri/css/node'
require 'merb-core/vendor/nokogiri/css/xpath_visitor'
require 'merb-core/vendor/nokogiri/css/generated_tokenizer'
require 'merb-core/vendor/nokogiri/css/generated_parser'
require 'merb-core/vendor/nokogiri/css/tokenizer'
require 'merb-core/vendor/nokogiri/css/parser'

View File

@ -0,0 +1,653 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by racc 1.4.5
# from racc grammer file "lib/nokogiri/css/parser.y".
#
require 'racc/parser'
module Nokogiri
module CSS
class GeneratedParser < Racc::Parser
##### racc 1.4.5 generates ###
racc_reduce_table = [
0, 0, :racc_error,
4, 46, :_reduce_1,
1, 46, :_reduce_2,
2, 49, :_reduce_3,
2, 49, :_reduce_4,
2, 49, :_reduce_5,
1, 49, :_reduce_6,
2, 49, :_reduce_7,
2, 49, :_reduce_8,
2, 50, :_reduce_9,
2, 50, :_reduce_10,
1, 50, :_reduce_none,
2, 50, :_reduce_12,
1, 50, :_reduce_13,
3, 48, :_reduce_14,
1, 48, :_reduce_none,
2, 57, :_reduce_16,
1, 51, :_reduce_17,
1, 51, :_reduce_18,
6, 55, :_reduce_19,
6, 55, :_reduce_20,
5, 55, :_reduce_21,
2, 54, :_reduce_22,
3, 54, :_reduce_23,
3, 54, :_reduce_24,
3, 54, :_reduce_25,
1, 59, :_reduce_none,
1, 59, :_reduce_none,
4, 60, :_reduce_28,
3, 60, :_reduce_29,
2, 60, :_reduce_30,
1, 60, :_reduce_31,
2, 61, :_reduce_32,
2, 61, :_reduce_33,
1, 52, :_reduce_none,
0, 52, :_reduce_none,
2, 56, :_reduce_36,
2, 56, :_reduce_37,
2, 56, :_reduce_38,
2, 56, :_reduce_39,
1, 56, :_reduce_none,
1, 56, :_reduce_none,
1, 56, :_reduce_none,
1, 56, :_reduce_none,
1, 62, :_reduce_44,
4, 58, :_reduce_45,
4, 58, :_reduce_46,
0, 58, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
1, 63, :_reduce_none,
5, 53, :_reduce_55,
1, 64, :_reduce_none,
2, 47, :_reduce_none,
0, 47, :_reduce_none ]
racc_reduce_n = 59
racc_shift_n = 97
racc_action_table = [
5, 79, 81, 27, 10, 26, 94, 93, 42, 2,
42, 30, 56, 5, 5, 28, 79, 81, 10, 42,
42, 42, 47, 2, 51, 42, 42, 10, 82, 84,
85, 52, 78, 10, 10, 7, 9, 12, 15, 42,
10, 17, 77, 82, 84, 85, 10, 78, 7, 7,
9, 12, 15, 42, 5, 17, 68, 77, 10, 9,
69, 15, 10, 2, 17, 9, 9, 15, 15, 50,
17, 17, 9, 5, 15, 26, 21, 17, 9, 54,
15, 23, 63, 17, 37, 38, 39, 64, 42, 7,
9, 12, 15, 44, 9, 17, 15, 42, 15, 17,
76, 42, 86, 31, 42, 89, 42, 25, 7, 91,
33, 92, 34, 35, 53, 42, 42 ]
racc_action_check = [
0, 73, 73, 6, 0, 7, 90, 90, 63, 0,
28, 7, 29, 17, 55, 6, 71, 71, 55, 33,
34, 35, 17, 55, 22, 37, 38, 4, 73, 73,
73, 23, 73, 1, 11, 0, 0, 0, 0, 42,
67, 0, 73, 71, 71, 71, 14, 71, 17, 55,
55, 55, 55, 44, 36, 55, 50, 71, 36, 4,
52, 4, 16, 36, 4, 1, 11, 1, 11, 21,
1, 11, 67, 41, 67, 5, 5, 67, 14, 27,
14, 5, 41, 14, 13, 13, 13, 41, 64, 36,
36, 36, 36, 16, 16, 36, 16, 65, 18, 16,
69, 15, 72, 9, 75, 80, 83, 5, 41, 87,
13, 88, 13, 13, 24, 93, 94 ]
racc_action_pointer = [
-2, 27, nil, nil, 21, 65, 3, -5, nil, 92,
nil, 28, nil, 77, 40, 92, 56, 11, 58, nil,
nil, 62, -18, 20, 72, nil, nil, 79, 1, -30,
nil, nil, nil, 10, 11, 12, 52, 16, 17, nil,
nil, 71, 30, nil, 44, nil, nil, nil, nil, nil,
40, nil, 53, nil, nil, 12, nil, nil, nil, nil,
nil, nil, nil, -1, 79, 88, nil, 34, nil, 84,
nil, 13, 61, -2, nil, 95, nil, nil, nil, nil,
64, nil, nil, 97, nil, nil, nil, 68, 69, nil,
-4, nil, nil, 106, 107, nil, nil ]
racc_action_default = [
-59, -42, -17, -13, -41, -59, -59, -59, -2, -59,
-44, -43, -18, -15, -40, -58, -35, -59, -11, -38,
-37, -31, -59, -26, -59, -22, -27, -59, -58, -59,
-26, -16, -39, -58, -58, -58, -59, -58, -58, -6,
-36, -59, -58, -34, -58, -9, -10, -33, -32, -12,
-59, -23, -30, -24, 97, -59, -25, -5, -8, -7,
-14, -3, -4, -58, -58, -58, -57, -59, -29, -59,
-1, -47, -59, -47, -56, -58, -28, -48, -52, -53,
-59, -54, -49, -58, -50, -51, -21, -59, -59, -19,
-59, -20, -55, -58, -58, -45, -46 ]
racc_goto_table = [
41, 19, 8, 80, 20, 87, 48, 22, 6, 29,
49, 32, 46, 55, 40, 45, 43, 36, 57, 58,
59, 24, 61, 62, 75, nil, nil, 66, nil, 67,
65, nil, nil, nil, nil, nil, nil, nil, 60, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 71, 72,
73, nil, nil, nil, nil, nil, nil, 70, nil, nil,
88, nil, nil, nil, nil, nil, nil, 74, 90, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 95, 96 ]
racc_goto_check = [
2, 11, 3, 13, 11, 13, 9, 14, 1, 14,
10, 11, 8, 2, 11, 7, 11, 4, 2, 2,
2, 15, 2, 2, 19, nil, nil, 2, nil, 2,
9, nil, nil, nil, nil, nil, nil, nil, 3, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 2, 2,
2, nil, nil, nil, nil, nil, nil, 3, nil, nil,
2, nil, nil, nil, nil, nil, nil, 11, 2, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
racc_goto_pointer = [
nil, 8, -15, 2, 4, nil, nil, -1, -4, -11,
-8, 0, nil, -68, 2, 16, nil, nil, nil, -43 ]
racc_goto_default = [
nil, nil, nil, nil, nil, 13, 16, nil, nil, 18,
1, 3, 4, nil, nil, nil, 11, 14, 83, nil ]
racc_token_table = {
false => 0,
Object.new => 1,
:FUNCTION => 2,
:INCLUDES => 3,
:DASHMATCH => 4,
:LBRACE => 5,
:HASH => 6,
:PLUS => 7,
:GREATER => 8,
:S => 9,
:STRING => 10,
:IDENT => 11,
:COMMA => 12,
:URI => 13,
:CDO => 14,
:CDC => 15,
:NUMBER => 16,
:PERCENTAGE => 17,
:LENGTH => 18,
:EMS => 19,
:EXS => 20,
:ANGLE => 21,
:TIME => 22,
:FREQ => 23,
:IMPORTANT_SYM => 24,
:IMPORT_SYM => 25,
:MEDIA_SYM => 26,
:PAGE_SYM => 27,
:CHARSET_SYM => 28,
:DIMENSION => 29,
:PREFIXMATCH => 30,
:SUFFIXMATCH => 31,
:SUBSTRINGMATCH => 32,
:TILDE => 33,
:NOT_EQUAL => 34,
:SLASH => 35,
:DOUBLESLASH => 36,
:NOT => 37,
"." => 38,
"*" => 39,
"[" => 40,
"]" => 41,
")" => 42,
":" => 43,
"=" => 44 }
racc_use_result_var = true
racc_nt_base = 45
Racc_arg = [
racc_action_table,
racc_action_check,
racc_action_default,
racc_action_pointer,
racc_goto_table,
racc_goto_check,
racc_goto_default,
racc_goto_pointer,
racc_nt_base,
racc_reduce_table,
racc_token_table,
racc_shift_n,
racc_reduce_n,
racc_use_result_var ]
Racc_token_to_s_table = [
'$end',
'error',
'FUNCTION',
'INCLUDES',
'DASHMATCH',
'LBRACE',
'HASH',
'PLUS',
'GREATER',
'S',
'STRING',
'IDENT',
'COMMA',
'URI',
'CDO',
'CDC',
'NUMBER',
'PERCENTAGE',
'LENGTH',
'EMS',
'EXS',
'ANGLE',
'TIME',
'FREQ',
'IMPORTANT_SYM',
'IMPORT_SYM',
'MEDIA_SYM',
'PAGE_SYM',
'CHARSET_SYM',
'DIMENSION',
'PREFIXMATCH',
'SUFFIXMATCH',
'SUBSTRINGMATCH',
'TILDE',
'NOT_EQUAL',
'SLASH',
'DOUBLESLASH',
'NOT',
'"."',
'"*"',
'"["',
'"]"',
'")"',
'":"',
'"="',
'$start',
'selector',
's_0toN',
'simple_selector_1toN',
'combinator',
'simple_selector',
'element_name',
'hcap_0toN',
'negation',
'function',
'attrib',
'hcap_1toN',
'class',
'attrib_val_0or1',
'expr',
'an_plus_b',
'pseudo',
'attribute_id',
'eql_incl_dash',
'negation_arg']
Racc_debug_parser = false
##### racc system variables end #####
# reduce 0 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 13
def _reduce_1( val, _values, result )
result = [val.first, val.last].flatten
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 13
def _reduce_2( val, _values, result )
result = val.flatten
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 16
def _reduce_3( val, _values, result )
result = :DIRECT_ADJACENT_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 17
def _reduce_4( val, _values, result )
result = :CHILD_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 18
def _reduce_5( val, _values, result )
result = :PRECEDING_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 19
def _reduce_6( val, _values, result )
result = :DESCENDANT_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 20
def _reduce_7( val, _values, result )
result = :DESCENDANT_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 21
def _reduce_8( val, _values, result )
result = :CHILD_SELECTOR
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 31
def _reduce_9( val, _values, result )
result = if val[1].nil?
val.first
else
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
end
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 34
def _reduce_10( val, _values, result )
result = Node.new(:CONDITIONAL_SELECTOR, val)
result
end
.,.,
# reduce 11 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 38
def _reduce_12( val, _values, result )
result = Node.new(:CONDITIONAL_SELECTOR, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 43
def _reduce_13( val, _values, result )
result = Node.new(:CONDITIONAL_SELECTOR,
[Node.new(:ELEMENT_NAME, ['*']), val.first]
)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 48
def _reduce_14( val, _values, result )
result = Node.new(val[1], [val.first, val.last])
result
end
.,.,
# reduce 15 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 51
def _reduce_16( val, _values, result )
result = Node.new(:CLASS_CONDITION, [val[1]])
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 54
def _reduce_17( val, _values, result )
result = Node.new(:ELEMENT_NAME, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 55
def _reduce_18( val, _values, result )
result = Node.new(:ELEMENT_NAME, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 63
def _reduce_19( val, _values, result )
result = Node.new(:ATTRIBUTE_CONDITION,
[Node.new(:ELEMENT_NAME, [val[2]])] + (val[4] || [])
)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 68
def _reduce_20( val, _values, result )
result = Node.new(:ATTRIBUTE_CONDITION,
[val[2]] + (val[4] || [])
)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 74
def _reduce_21( val, _values, result )
# Non standard, but hpricot supports it.
result = Node.new(:PSEUDO_CLASS,
[Node.new(:FUNCTION, ['nth-child(', val[2]])]
)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 79
def _reduce_22( val, _values, result )
result = Node.new(:FUNCTION, [val.first.strip])
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 82
def _reduce_23( val, _values, result )
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 85
def _reduce_24( val, _values, result )
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 88
def _reduce_25( val, _values, result )
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
# reduce 26 omitted
# reduce 27 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 102
def _reduce_28( val, _values, result )
if val[1] == 'n'
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 113
def _reduce_29( val, _values, result )
# n+3, -n+3
if val[0] == 'n'
val.unshift("1")
result = Node.new(:AN_PLUS_B, val)
elsif val[0] == '-n'
val[0] = 'n'
val.unshift("-1")
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 124
def _reduce_30( val, _values, result )
if val[1] == 'n'
val << "+"
val << "0"
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 136
def _reduce_31( val, _values, result )
if val[0] == 'even'
val = ["2","n","+","0"]
result = Node.new(:AN_PLUS_B, val)
elsif val[0] == 'odd'
val = ["2","n","+","1"]
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[0]}'"
end
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 141
def _reduce_32( val, _values, result )
result = Node.new(:PSEUDO_CLASS, [val[1]])
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 141
def _reduce_33( val, _values, result )
result = Node.new(:PSEUDO_CLASS, [val[1]])
result
end
.,.,
# reduce 34 omitted
# reduce 35 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 151
def _reduce_36( val, _values, result )
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 154
def _reduce_37( val, _values, result )
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 157
def _reduce_38( val, _values, result )
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 160
def _reduce_39( val, _values, result )
result = Node.new(:COMBINATOR, val)
result
end
.,.,
# reduce 40 omitted
# reduce 41 omitted
# reduce 42 omitted
# reduce 43 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 166
def _reduce_44( val, _values, result )
result = Node.new(:ID, val)
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 169
def _reduce_45( val, _values, result )
result = [val.first, val[2]]
result
end
.,.,
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 170
def _reduce_46( val, _values, result )
result = [val.first, val[2]]
result
end
.,.,
# reduce 47 omitted
# reduce 48 omitted
# reduce 49 omitted
# reduce 50 omitted
# reduce 51 omitted
# reduce 52 omitted
# reduce 53 omitted
# reduce 54 omitted
module_eval <<'.,.,', 'lib/nokogiri/css/parser.y', 186
def _reduce_55( val, _values, result )
result = Node.new(:NOT, [val[2]])
result
end
.,.,
# reduce 56 omitted
# reduce 57 omitted
# reduce 58 omitted
def _reduce_none( val, _values, result )
result
end
end # class GeneratedParser
end # module CSS
end # module Nokogiri

View File

@ -0,0 +1,159 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by rex 1.0.1
# from lexical definition file "lib/nokogiri/css/tokenizer.rex".
#
module Nokogiri
module CSS
class GeneratedTokenizer
require 'strscan'
class ScanError < StandardError ; end
attr_reader :lineno
attr_reader :filename
def scan_setup ; end
def action &block
yield
end
def scan_str( str )
scan_evaluate str
do_parse
end
def load_file( filename )
@filename = filename
open(filename, "r") do |f|
scan_evaluate f.read
end
end
def scan_file( filename )
load_file filename
do_parse
end
def next_token
@rex_tokens.shift
end
def scan_evaluate( str )
scan_setup
@rex_tokens = []
@lineno = 1
ss = StringScanner.new(str)
state = nil
until ss.eos?
text = ss.peek(1)
@lineno += 1 if text == "\n"
case state
when nil
case
when (text = ss.scan(/~=/i))
@rex_tokens.push action { [:INCLUDES, text] }
when (text = ss.scan(/\|=/i))
@rex_tokens.push action { [:DASHMATCH, text] }
when (text = ss.scan(/\^=/i))
@rex_tokens.push action { [:PREFIXMATCH, text] }
when (text = ss.scan(/\$=/i))
@rex_tokens.push action { [:SUFFIXMATCH, text] }
when (text = ss.scan(/\*=/i))
@rex_tokens.push action { [:SUBSTRINGMATCH, text] }
when (text = ss.scan(/!=/i))
@rex_tokens.push action { [:NOT_EQUAL, text] }
when (text = ss.scan(/[-]?([_a-z]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*\(\s*/i))
@rex_tokens.push action { [:FUNCTION, text] }
when (text = ss.scan(/@[-]?([_a-z]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*/i))
@rex_tokens.push action { [:IDENT, text] }
when (text = ss.scan(/[-]?([_a-z]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*/i))
@rex_tokens.push action { [:IDENT, text] }
when (text = ss.scan(/-?([0-9]+|[0-9]*\.[0-9]+)/i))
@rex_tokens.push action { [:NUMBER, text] }
when (text = ss.scan(/\#([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])+/i))
@rex_tokens.push action { [:HASH, text] }
when (text = ss.scan(/[\s\r\n\f]*\+/i))
@rex_tokens.push action { [:PLUS, text] }
when (text = ss.scan(/[\s\r\n\f]*>/i))
@rex_tokens.push action { [:GREATER, text] }
when (text = ss.scan(/[\s\r\n\f]*,/i))
@rex_tokens.push action { [:COMMA, text] }
when (text = ss.scan(/[\s\r\n\f]*~/i))
@rex_tokens.push action { [:TILDE, text] }
when (text = ss.scan(/\:not\(/i))
@rex_tokens.push action { [:NOT, text] }
when (text = ss.scan(/@[-]?([_a-z]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*/i))
@rex_tokens.push action { [:ATKEYWORD, text] }
when (text = ss.scan(/-?([0-9]+|[0-9]*\.[0-9]+)%/i))
@rex_tokens.push action { [:PERCENTAGE, text] }
when (text = ss.scan(/-?([0-9]+|[0-9]*\.[0-9]+)[-]?([_a-z]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])([_a-z0-9-]|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*/i))
@rex_tokens.push action { [:DIMENSION, text] }
when (text = ss.scan(/<!--/i))
@rex_tokens.push action { [:CDO, text] }
when (text = ss.scan(/-->/i))
@rex_tokens.push action { [:CDC, text] }
when (text = ss.scan(/[\s\r\n\f]*\/\//i))
@rex_tokens.push action { [:DOUBLESLASH, text] }
when (text = ss.scan(/[\s\r\n\f]*\//i))
@rex_tokens.push action { [:SLASH, text] }
when (text = ss.scan(/U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?/i))
@rex_tokens.push action {[:UNICODE_RANGE, text] }
when (text = ss.scan(/\/\*(.|[\r\n])*?\*\//i))
;
when (text = ss.scan(/[\s\t\r\n\f]+/i))
@rex_tokens.push action { [:S, text] }
when (text = ss.scan(/[\.*:\[\]=\)]/i))
@rex_tokens.push action { [text, text] }
when (text = ss.scan(/"([^\n\r\f"]|\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*"|'([^\n\r\f']|\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*'/i))
@rex_tokens.push action { [:STRING, text] }
when (text = ss.scan(/\"([^\n\r\f\"]|\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*|([^\n\r\f\']|\\n|\r\n|\r|\f|[^\0-\177]|\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?|\\[^\n\r\f0-9a-f])*/i))
@rex_tokens.push action { [:INVALID, text] }
when (text = ss.scan(/./i))
@rex_tokens.push action { [text, text] }
else
text = ss.string[ss.pos .. -1]
raise ScanError, "can not match: '" + text + "'"
end # if
else
raise ScanError, "undefined state: '" + state.to_s + "'"
end # case state
end # until ss
end # def scan_evaluate
end # class
end
end

95
lib/webrat/vendor/nokogiri/css/node.rb vendored Normal file
View File

@ -0,0 +1,95 @@
module Nokogiri
module CSS
class Node
attr_accessor :type, :value
def initialize type, value
@type = type
@value = value
end
def accept visitor
visitor.send(:"visit_#{type.to_s.downcase}", self)
end
def to_xpath prefix = '//', preprocess = true
self.preprocess! if preprocess
prefix + XPathVisitor.new.accept(self)
end
def preprocess!
### Deal with nth-child
matches = find_by_type(
[:CONDITIONAL_SELECTOR,
[:ELEMENT_NAME],
[:PSEUDO_CLASS,
[:FUNCTION]
]
]
)
matches.each do |match|
if match.value[1].value[0].value[0] =~ /^nth-child/
tag_name = match.value[0].value.first
match.value[0].value = ['*']
match.value[1] = Node.new(:COMBINATOR, [
match.value[1].value[0],
Node.new(:FUNCTION, ['self(', tag_name])
])
end
if match.value[1].value[0].value[0] =~ /^nth-last-child/
tag_name = match.value[0].value.first
match.value[0].value = ['*']
match.value[1] = Node.new(:COMBINATOR, [
match.value[1].value[0],
Node.new(:FUNCTION, ['self(', tag_name])
])
end
end
### Deal with first-child, last-child
matches = find_by_type(
[:CONDITIONAL_SELECTOR,
[:ELEMENT_NAME], [:PSEUDO_CLASS]
])
matches.each do |match|
if ['first-child', 'last-child'].include?(match.value[1].value.first)
which = match.value[1].value.first.gsub(/-\w*$/, '')
tag_name = match.value[0].value.first
match.value[0].value = ['*']
match.value[1] = Node.new(:COMBINATOR, [
Node.new(:FUNCTION, ["#{which}("]),
Node.new(:FUNCTION, ['self(', tag_name])
])
elsif 'only-child' == match.value[1].value.first
tag_name = match.value[0].value.first
match.value[0].value = ['*']
match.value[1] = Node.new(:COMBINATOR, [
Node.new(:FUNCTION, ["#{match.value[1].value.first}("]),
Node.new(:FUNCTION, ['self(', tag_name])
])
end
end
self
end
def find_by_type(types)
matches = []
matches << self if to_type == types
@value.each do |v|
matches += v.find_by_type(types) if v.respond_to?(:find_by_type)
end
matches
end
def to_type
[@type] + @value.map { |n|
n.to_type if n.respond_to?(:to_type)
}.compact
end
def to_a
[@type] + @value.map { |n| n.respond_to?(:to_a) ? n.to_a : [n] }
end
end
end
end

View File

@ -0,0 +1,24 @@
module Nokogiri
module CSS
class Parser < GeneratedParser
class << self
def parse string
new.parse(string)
end
end
def initialize
@tokenizer = Tokenizer.new
end
def parse string
@tokenizer.scan string
do_parse
end
def next_token
@tokenizer.next_token
end
end
end
end

198
lib/webrat/vendor/nokogiri/css/parser.y vendored Normal file
View File

@ -0,0 +1,198 @@
class Nokogiri::CSS::GeneratedParser
token FUNCTION INCLUDES DASHMATCH LBRACE HASH PLUS GREATER S STRING IDENT
token COMMA URI CDO CDC NUMBER PERCENTAGE LENGTH EMS EXS ANGLE TIME FREQ
token IMPORTANT_SYM IMPORT_SYM MEDIA_SYM PAGE_SYM CHARSET_SYM DIMENSION
token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL SLASH DOUBLESLASH
token NOT
rule
selector
: selector COMMA s_0toN simple_selector_1toN {
result = [val.first, val.last].flatten
}
| simple_selector_1toN { result = val.flatten }
;
combinator
: PLUS s_0toN { result = :DIRECT_ADJACENT_SELECTOR }
| GREATER s_0toN { result = :CHILD_SELECTOR }
| TILDE s_0toN { result = :PRECEDING_SELECTOR }
| S { result = :DESCENDANT_SELECTOR }
| DOUBLESLASH s_0toN { result = :DESCENDANT_SELECTOR }
| SLASH s_0toN { result = :CHILD_SELECTOR }
;
simple_selector
: element_name hcap_0toN {
result = if val[1].nil?
val.first
else
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
end
}
| element_name negation {
result = Node.new(:CONDITIONAL_SELECTOR, val)
}
| function
| function attrib {
result = Node.new(:CONDITIONAL_SELECTOR, val)
}
| hcap_1toN {
result = Node.new(:CONDITIONAL_SELECTOR,
[Node.new(:ELEMENT_NAME, ['*']), val.first]
)
}
;
simple_selector_1toN
: simple_selector combinator simple_selector_1toN {
result = Node.new(val[1], [val.first, val.last])
}
| simple_selector
;
class
: '.' IDENT { result = Node.new(:CLASS_CONDITION, [val[1]]) }
;
element_name
: IDENT { result = Node.new(:ELEMENT_NAME, val) }
| '*' { result = Node.new(:ELEMENT_NAME, val) }
;
attrib
: '[' s_0toN IDENT s_0toN attrib_val_0or1 ']' {
result = Node.new(:ATTRIBUTE_CONDITION,
[Node.new(:ELEMENT_NAME, [val[2]])] + (val[4] || [])
)
}
| '[' s_0toN function s_0toN attrib_val_0or1 ']' {
result = Node.new(:ATTRIBUTE_CONDITION,
[val[2]] + (val[4] || [])
)
}
| '[' s_0toN NUMBER s_0toN ']' {
# Non standard, but hpricot supports it.
result = Node.new(:PSEUDO_CLASS,
[Node.new(:FUNCTION, ['nth-child(', val[2]])]
)
}
;
function
: FUNCTION ')' {
result = Node.new(:FUNCTION, [val.first.strip])
}
| FUNCTION expr ')' {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
| FUNCTION an_plus_b ')' {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
| NOT expr ')' {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
;
expr
: NUMBER
| STRING
;
an_plus_b
: NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
{
if val[1] == 'n'
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
| IDENT PLUS NUMBER { # n+3, -n+3
if val[0] == 'n'
val.unshift("1")
result = Node.new(:AN_PLUS_B, val)
elsif val[0] == '-n'
val[0] = 'n'
val.unshift("-1")
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
| NUMBER IDENT # 5n, -5n
{
if val[1] == 'n'
val << "+"
val << "0"
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
| IDENT # even, odd
{
if val[0] == 'even'
val = ["2","n","+","0"]
result = Node.new(:AN_PLUS_B, val)
elsif val[0] == 'odd'
val = ["2","n","+","1"]
result = Node.new(:AN_PLUS_B, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[0]}'"
end
}
;
pseudo
: ':' function {
result = Node.new(:PSEUDO_CLASS, [val[1]])
}
| ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
;
hcap_0toN
: hcap_1toN
|
;
hcap_1toN
: attribute_id hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| class hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| attrib hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| pseudo hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| attribute_id
| class
| attrib
| pseudo
;
attribute_id
: HASH { result = Node.new(:ID, val) }
;
attrib_val_0or1
: eql_incl_dash s_0toN IDENT s_0toN { result = [val.first, val[2]] }
| eql_incl_dash s_0toN STRING s_0toN { result = [val.first, val[2]] }
|
;
eql_incl_dash
: '='
| PREFIXMATCH
| SUFFIXMATCH
| SUBSTRINGMATCH
| NOT_EQUAL
| INCLUDES
| DASHMATCH
;
negation
: NOT s_0toN negation_arg s_0toN ')' {
result = Node.new(:NOT, [val[2]])
}
;
negation_arg
: hcap_1toN
;
s_0toN
: S s_0toN
|
;
end
---- header

View File

@ -0,0 +1,9 @@
module Nokogiri
module CSS
class Tokenizer < GeneratedTokenizer
def scan(str)
scan_evaluate(str)
end
end
end
end

View File

@ -0,0 +1,63 @@
module Nokogiri
module CSS
class GeneratedTokenizer
macro
nl \n|\r\n|\r|\f
w [\s\r\n\f]*
nonascii [^\\\\0-\\\\177]
num -?([0-9]+|[0-9]*\.[0-9]+)
unicode \\\\\\\\\[0-9a-f]{1,6}(\r\n|[\s\n\r\t\f])?
escape {unicode}|\\\\\\\[^\n\r\f0-9a-f]
nmchar [_a-z0-9-]|{nonascii}|{escape}
nmstart [_a-z]|{nonascii}|{escape}
ident [-]?({nmstart})({nmchar})*
name ({nmchar})+
string1 "([^\n\r\f"]|\\{nl}|{nonascii}|{escape})*"
string2 '([^\n\r\f']|\\{nl}|{nonascii}|{escape})*'
string {string1}|{string2}
invalid1 \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
invalid2 \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
invalid {invalid1}|{invalid2}
Comment \/\*(.|[\r\n])*?\*\/
rule
# [:state] pattern [actions]
~= { [:INCLUDES, text] }
\|= { [:DASHMATCH, text] }
\^= { [:PREFIXMATCH, text] }
\$= { [:SUFFIXMATCH, text] }
\*= { [:SUBSTRINGMATCH, text] }
!= { [:NOT_EQUAL, text] }
{ident}\(\s* { [:FUNCTION, text] }
@{ident} { [:IDENT, text] }
{ident} { [:IDENT, text] }
{num} { [:NUMBER, text] }
\#{name} { [:HASH, text] }
{w}\+ { [:PLUS, text] }
{w}> { [:GREATER, text] }
{w}, { [:COMMA, text] }
{w}~ { [:TILDE, text] }
\:not\( { [:NOT, text] }
@{ident} { [:ATKEYWORD, text] }
{num}% { [:PERCENTAGE, text] }
{num}{ident} { [:DIMENSION, text] }
<!-- { [:CDO, text] }
--> { [:CDC, text] }
{w}\/\/ { [:DOUBLESLASH, text] }
{w}\/ { [:SLASH, text] }
U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})? {[:UNICODE_RANGE, text] }
{Comment} /* ignore comments */
[\s\t\r\n\f]+ { [:S, text] }
[\.*:\[\]=\)] { [text, text] }
{string} { [:STRING, text] }
{invalid} { [:INVALID, text] }
. { [text, text] }
end
end
end

View File

@ -0,0 +1,159 @@
module Nokogiri
module CSS
class XPathVisitor
def visit_function node
# note that nth-child and nth-last-child are preprocessed in css/node.rb.
case node.value.first
when /^text\(/
'child::text()'
when /^self\(/
"self::#{node.value[1]}"
when /^(eq|nth|nth-of-type|nth-child)\(/
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :AN_PLUS_B
an_plus_b(node.value[1])
else
"position() = " + node.value[1]
end
when /^(first|first-of-type)\(/
"position() = 1"
when /^(last|last-of-type)\(/
"position() = last()"
when /^(nth-last-child|nth-last-of-type)\(/
"position() = last() - #{node.value[1]}"
when /^contains\(/
"contains(., #{node.value[1]})"
when /^gt\(/
"position() > #{node.value[1]}"
when /^only-child\(/
"last() = 1"
else
node.value.first + ')'
end
end
def visit_not node
'not(' + node.value.first.accept(self) + ')'
end
def visit_preceding_selector node
node.value.last.accept(self) +
'[preceding-sibling::' +
node.value.first.accept(self) +
']'
end
def visit_direct_adjacent_selector node
node.value.last.accept(self) +
'[preceding-sibling::' +
node.value.first.accept(self) +
'][position()=1]'
end
def visit_id node
node.value.first =~ /^#(.*)$/
"@id = '#{$1}'"
end
def visit_attribute_condition node
attribute = if (node.value.first.type == :FUNCTION) or (node.value.first.value.first =~ /::/)
''
else
'@'
end
attribute += node.value.first.accept(self)
# Support non-standard css
attribute.gsub!(/^@@/, '@')
return attribute unless node.value.length == 3
value = node.value.last
value = "'#{value}'" if value !~ /^['"]/
case node.value[1]
when '*='
"contains(#{attribute}, #{value})"
when '^='
"starts-with(#{attribute}, #{value})"
when '|='
"#{attribute} = #{value} or starts-with(#{attribute}, concat(#{value}, '-'))"
when '~='
"contains(concat(\" \", #{attribute}, \" \"),concat(\" \", #{value}, \" \"))"
when '$='
"substring(#{attribute}, string-length(#{attribute}) - " +
"string-length(#{value}) + 1, string-length(#{value})) = #{value}"
else
attribute + " #{node.value[1]} " + "#{value}"
end
end
def visit_pseudo_class node
if node.value.first.is_a?(Nokogiri::CSS::Node) and node.value.first.type == :FUNCTION
node.value.first.accept(self)
else
case node.value.first
when "first" then "position() = 1"
when "last" then "position() = last()"
when "first-of-type" then "position() = 1"
when "last-of-type" then "position() = last()"
when "only-of-type" then "last() = 1"
when "empty" then "not(node())"
when "parent" then "node()"
else
'1 = 1'
end
end
end
def visit_class_condition node
"contains(concat(' ', @class, ' '),concat(' ', '#{node.value.first}', ' '))"
end
def visit_combinator node
node.value.first.accept(self) + ' and ' +
node.value.last.accept(self)
end
def visit_conditional_selector node
node.value.first.accept(self) + '[' +
node.value.last.accept(self) + ']'
end
def visit_descendant_selector node
node.value.first.accept(self) +
'//' +
node.value.last.accept(self)
end
def visit_child_selector node
node.value.first.accept(self) +
'/' +
node.value.last.accept(self)
end
def visit_element_name node
node.value.first
end
def accept node
node.accept(self)
end
private
def an_plus_b node
raise ArgumentError, "expected an+b node to contain 4 tokens, but is #{node.value.inspect}" unless node.value.size == 4
a = node.value[0].to_i
b = node.value[3].to_i
if (b == 0)
return "(position() mod #{a}) = 0"
else
compare = (a < 0) ? "<=" : ">="
return "(position() #{compare} #{b}) and (((position()-#{b}) mod #{a.abs}) = 0)"
end
end
end
end
end

View File

@ -0,0 +1,75 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "Nokogiri Extension" do
include Webrat::Matchers
def fail
raise_error(Spec::Expectations::ExpectationNotMetError)
end
before(:each) do
@text_and_password = <<-HTML
<div>
<input type="text"/>
<input type="password"/>
<span type="text"/>
</div>
HTML
@text_only = <<-HTML
<div>
<input type="text" disabled="disabled" />
</div>
HTML
@password_only = <<-HTML
<div>
<input type="password"/>
<div>
HTML
end
describe ":text" do
it "passes have_selector(:text) if a node with type=text exists" do
@text_and_password.should have_selector(":text")
end
it "passes not have_selector(:text) if no node with text=text exists" do
@password_only.should_not have_selector(":text")
end
it "fails have_selector(:text) if no node with type=text exists" do
lambda { @password_only.should have_selector(":text") }.should fail
end
it "fails not have_selector(:text) if a node with type=text exists" do
lambda { @text_only.should_not have_selector(":text") }.should fail
end
it "works together with other selectors" do
@text_and_password.should have_selector("input:text[type*='te']")
end
end
describe ":password" do
it "passes have_selector(:password) if a node with type=password exists" do
@text_and_password.should have_selector(":password")
end
it "passes not have_selector(:text) if no node with text=text exists" do
@text_only.should_not have_selector(":password")
end
it "fails have_selector(:password) if no node with type=password exists" do
lambda { @text_only.should have_selector(":password") }.should fail
end
it "fails not have_selector(:password) if a node with type=password exists" do
lambda { @password_only.should_not have_selector(":password") }.should fail
end
it "works together with other selectors" do
@text_and_password.should have_selector("input:password[type*='pa']")
end
end
end