mullet 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mullet/container.rb +8 -4
- data/lib/mullet/default_model.rb +9 -9
- data/lib/mullet/default_nested_model.rb +7 -6
- data/lib/mullet/html/attribute_command.rb +8 -4
- data/lib/mullet/html/attributes.rb +22 -0
- data/lib/mullet/html/command.rb +4 -3
- data/lib/mullet/html/command_element_renderer.rb +19 -0
- data/lib/mullet/html/element.rb +41 -0
- data/lib/mullet/html/element_renderer.rb +261 -0
- data/lib/mullet/html/filtered_element_handler.rb +87 -0
- data/lib/mullet/html/for_element_renderer.rb +47 -0
- data/lib/mullet/html/if_element_renderer.rb +46 -0
- data/lib/mullet/html/layout.rb +48 -0
- data/lib/mullet/html/message.rb +55 -0
- data/lib/mullet/html/message_attribute_command.rb +30 -0
- data/lib/mullet/html/model_attribute_command.rb +30 -0
- data/lib/mullet/html/page_builder.rb +152 -0
- data/lib/mullet/html/parser/attribute.rb +8 -0
- data/lib/mullet/html/parser/constants.rb +1061 -0
- data/lib/mullet/html/parser/default_handler.rb +27 -0
- data/lib/mullet/html/parser/input_stream.rb +711 -0
- data/lib/mullet/html/parser/open_element.rb +77 -0
- data/lib/mullet/html/parser/simple_parser.rb +128 -0
- data/lib/mullet/html/parser/tokenizer.rb +1085 -0
- data/lib/mullet/html/remove_mode.rb +30 -0
- data/lib/mullet/html/static_text_renderer.rb +20 -0
- data/lib/mullet/html/template.rb +44 -0
- data/lib/mullet/html/template_builder.rb +208 -63
- data/lib/mullet/html/template_loader.rb +77 -39
- data/lib/mullet/html/template_parser.rb +48 -0
- data/lib/mullet/html/unless_element_renderer.rb +24 -0
- data/lib/mullet/model.rb +2 -5
- data/lib/mullet/render_context.rb +24 -18
- data/lib/mullet/tilt.rb +37 -0
- data/lib/mullet/version.rb +2 -1
- data/lib/mullet.rb +1 -0
- metadata +58 -11
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'mullet/html/parser/attribute'
|
2
|
+
require 'mullet/html/parser/constants'
|
3
|
+
require 'mullet/html/parser/input_stream'
|
4
|
+
|
5
|
+
module Mullet; module HTML; module Parser
|
6
|
+
|
7
|
+
# Element where the parser has seen the start tag but not yet seen the end
|
8
|
+
# tag.
|
9
|
+
class OpenElement
|
10
|
+
|
11
|
+
XMLNS_ATTRIBUTE = 'xmlns'
|
12
|
+
XMLNS_ATTRIBUTE_PREFIX = XMLNS_ATTRIBUTE + ':'
|
13
|
+
|
14
|
+
attr_reader :parent, :qualified_name, :prefix, :local_name, :uri
|
15
|
+
attr_reader :attributes, :namespace_declarations
|
16
|
+
|
17
|
+
def initialize(parent, qualified_name, raw_attributes)
|
18
|
+
# namespace prefix to URI map
|
19
|
+
@namespace_declarations = Hash.new()
|
20
|
+
|
21
|
+
@parent = parent
|
22
|
+
|
23
|
+
process_namespace_declarations(raw_attributes)
|
24
|
+
@qualified_name = qualified_name
|
25
|
+
@prefix, @local_name = extract_prefix_and_local_name(qualified_name)
|
26
|
+
@uri = resolve_prefix_to_uri(@prefix)
|
27
|
+
|
28
|
+
@attributes = to_namespace_aware_attributes(raw_attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def resolve_prefix_to_uri(prefix)
|
32
|
+
uri = @namespace_declarations.fetch(prefix, nil)
|
33
|
+
if uri == nil && @parent != nil
|
34
|
+
return @parent.resolve_prefix_to_uri(prefix)
|
35
|
+
end
|
36
|
+
return uri
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def declare_namespace(raw_attribute_name, uri)
|
42
|
+
if raw_attribute_name.start_with?(XMLNS_ATTRIBUTE_PREFIX)
|
43
|
+
prefix = raw_attribute_name[XMLNS_ATTRIBUTE_PREFIX.length()..-1]
|
44
|
+
else
|
45
|
+
prefix = nil
|
46
|
+
end
|
47
|
+
@namespace_declarations.store(prefix, uri)
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_namespace_declarations(raw_attributes)
|
51
|
+
raw_attributes.each do |attribute_name, attribute_value|
|
52
|
+
if attribute_name.start_with?(XMLNS_ATTRIBUTE)
|
53
|
+
declare_namespace(attribute_name, attribute_value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def extract_prefix_and_local_name(qualified_name)
|
59
|
+
name_parts = qualified_name.split(':', 2)
|
60
|
+
prefix = (name_parts.size() > 1) ? name_parts[0] : nil
|
61
|
+
local_name = (name_parts.size() > 1) ? name_parts[1] : qualified_name
|
62
|
+
return [prefix, local_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_namespace_aware_attributes(raw_attributes)
|
66
|
+
attributes = []
|
67
|
+
raw_attributes.each do |qualified_name, attribute_value|
|
68
|
+
prefix, local_name = extract_prefix_and_local_name(qualified_name)
|
69
|
+
uri = resolve_prefix_to_uri(prefix)
|
70
|
+
attributes << Attribute.new(local_name, prefix, uri, attribute_value)
|
71
|
+
end
|
72
|
+
|
73
|
+
return attributes
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end; end; end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'mullet/html/parser/open_element'
|
2
|
+
require 'mullet/html/parser/tokenizer'
|
3
|
+
require 'mullet/template_error'
|
4
|
+
|
5
|
+
module Mullet; module HTML; module Parser
|
6
|
+
|
7
|
+
# SAX parser that reports elements in the order they appear in the input.
|
8
|
+
# Does not move elements to another parent.
|
9
|
+
class SimpleParser
|
10
|
+
|
11
|
+
# special namespace URI indicating parser generated an implicit end tag
|
12
|
+
IMPLICIT_END_TAG_NS_URI = 'http://pukkaone.github.com/mullet/end-tag'
|
13
|
+
|
14
|
+
# Constructor
|
15
|
+
#
|
16
|
+
# @param [DefaultHandler] handler
|
17
|
+
# object to receive events
|
18
|
+
def initialize(handler)
|
19
|
+
@handler = handler
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(input)
|
23
|
+
@open_element = nil
|
24
|
+
@handler.start_document()
|
25
|
+
|
26
|
+
tokenizer = Tokenizer.new(input)
|
27
|
+
tokenizer.each do |token|
|
28
|
+
case token[:type]
|
29
|
+
when :Doctype
|
30
|
+
doctype(token[:name], token[:publicId], token[:systemId])
|
31
|
+
when :StartTag
|
32
|
+
start_tag(token[:name], token[:data])
|
33
|
+
when :EndTag
|
34
|
+
end_tag(token[:name])
|
35
|
+
when :Characters, :SpaceCharacters
|
36
|
+
characters(token[:data])
|
37
|
+
when :CDATA
|
38
|
+
cdata(token[:data])
|
39
|
+
when :Comment
|
40
|
+
comment(token[:data])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@handler.end_document()
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def doctype(name, public_id, system_id)
|
50
|
+
@handler.doctype(name, public_id, system_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_tag(name, attributes)
|
54
|
+
# Push open element onto stack.
|
55
|
+
element = OpenElement.new(@open_element, name, attributes)
|
56
|
+
@open_element = element
|
57
|
+
|
58
|
+
@handler.start_element_namespace(
|
59
|
+
element.local_name,
|
60
|
+
element.attributes,
|
61
|
+
element.prefix,
|
62
|
+
element.uri,
|
63
|
+
element.namespace_declarations)
|
64
|
+
end
|
65
|
+
|
66
|
+
def pop_open_element()
|
67
|
+
element = @open_element
|
68
|
+
if @open_element != nil
|
69
|
+
@open_element = @open_element.parent
|
70
|
+
end
|
71
|
+
return element
|
72
|
+
end
|
73
|
+
|
74
|
+
def raise_mismatched_end_tag(name)
|
75
|
+
raise TemplateError.new("End tag </#{name}> has no matching start tag")
|
76
|
+
end
|
77
|
+
|
78
|
+
def end_tag(name)
|
79
|
+
element = pop_open_element()
|
80
|
+
if element == nil
|
81
|
+
raise_mismatched_end_tag(name)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Generate implicit end tags up to this element.
|
85
|
+
while element.qualified_name != name
|
86
|
+
# KLUDGE: Indicate a generated implicit end tag by passing a special
|
87
|
+
# value for the namespace URI.
|
88
|
+
@handler.end_element_namespace(name, nil, IMPLICIT_END_TAG_NS_URI)
|
89
|
+
|
90
|
+
element = pop_open_element()
|
91
|
+
if element == nil
|
92
|
+
raise_mismatched_end_tag(name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
@handler.end_element_namespace(element.qualified_name, nil, nil)
|
97
|
+
end
|
98
|
+
|
99
|
+
def characters(data)
|
100
|
+
@handler.characters(data)
|
101
|
+
end
|
102
|
+
|
103
|
+
def cdata(data)
|
104
|
+
@handler.cdata_block(data)
|
105
|
+
end
|
106
|
+
|
107
|
+
def processing_instruction(data)
|
108
|
+
@handler.processing_instruction(data)
|
109
|
+
end
|
110
|
+
|
111
|
+
def comment(data)
|
112
|
+
# FIXME: The HTML5 tokenizer treats <? as a bogus comment and emits it as
|
113
|
+
# a comment. If the comment begins with ? then turn it into a processing
|
114
|
+
# instruction. The problem is any legitimate comment beginning with ?
|
115
|
+
# will also be turned into a processing instruction.
|
116
|
+
if data.start_with?('?')
|
117
|
+
last_index = data.length() - 1
|
118
|
+
if last_index > 0 && data[last_index] == '?'
|
119
|
+
last_index -= 1
|
120
|
+
end
|
121
|
+
processing_instruction(data[1..last_index])
|
122
|
+
else
|
123
|
+
@handler.comment(data)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end; end; end
|