Moving over Nokogiri extensions and vendoring nokogiri CSS support for people without nokogiri installed
This commit is contained in:
parent
11f291ceb3
commit
a8e0e7578a
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
module Nokogiri
|
||||
module CSS
|
||||
class Tokenizer < GeneratedTokenizer
|
||||
def scan(str)
|
||||
scan_evaluate(str)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue