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 # Require nokogiri and fall back on rexml
begin begin
require "nokogiri" require "nokogiri"
require "webrat/nokogiri"
rescue LoadError => e rescue LoadError => e
if require "rexml/document" 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\"") warn("Standard REXML library is slow. Please consider installing nokogiri.\nUse \"sudo gem install nokogiri\"")
end end
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