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
data/lib/mullet/container.rb
CHANGED
@@ -8,24 +8,28 @@ module Mullet
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def add_child(child)
|
11
|
-
@children
|
11
|
+
@children.push(child)
|
12
12
|
end
|
13
13
|
|
14
14
|
def delete_child(child)
|
15
15
|
@children.delete(child);
|
16
16
|
end
|
17
17
|
|
18
|
+
def children()
|
19
|
+
return @children
|
20
|
+
end
|
21
|
+
|
18
22
|
def clear_children()
|
19
23
|
@children.clear()
|
20
24
|
end
|
21
25
|
|
22
26
|
# Renders children in order they were added.
|
23
27
|
#
|
24
|
-
# @param [RenderContext]
|
28
|
+
# @param [RenderContext] render_context
|
25
29
|
# render context
|
26
|
-
def render_children(
|
30
|
+
def render_children(render_context)
|
27
31
|
@children.each do |child|
|
28
|
-
child.render(
|
32
|
+
child.render(render_context)
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
data/lib/mullet/default_model.rb
CHANGED
@@ -22,27 +22,27 @@ module Mullet
|
|
22
22
|
@data = data
|
23
23
|
end
|
24
24
|
|
25
|
-
def fetch_impl(
|
26
|
-
if
|
25
|
+
def fetch_impl(name)
|
26
|
+
if name == :this
|
27
27
|
return @data
|
28
28
|
end
|
29
29
|
|
30
30
|
# Is the variable name a key in a Hash?
|
31
31
|
if @data.respond_to?(:fetch)
|
32
32
|
# Call the block if the key is not found.
|
33
|
-
return @data.fetch(
|
33
|
+
return @data.fetch(name) {|k| @data.fetch(k.to_s(), NOT_FOUND) }
|
34
34
|
end
|
35
35
|
|
36
36
|
# Does the variable name match a method name in the object?
|
37
|
-
if @data.respond_to?(
|
38
|
-
method = @data.method(
|
37
|
+
if @data.respond_to?(name)
|
38
|
+
method = @data.method(name)
|
39
39
|
if method.arity == 0
|
40
40
|
return method.call()
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
# Does the variable name match an instance variable name in the object?
|
45
|
-
variable = :"@#{
|
45
|
+
variable = :"@#{name}"
|
46
46
|
if @data.instance_variable_defined?(variable)
|
47
47
|
return @data.instance_variable_get(variable)
|
48
48
|
end
|
@@ -52,11 +52,11 @@ module Mullet
|
|
52
52
|
|
53
53
|
# Resolves variable name to value.
|
54
54
|
#
|
55
|
-
# @param [Symbol]
|
55
|
+
# @param [Symbol] name
|
56
56
|
# variable name
|
57
57
|
# @return variable value
|
58
|
-
def
|
59
|
-
value = fetch_impl(
|
58
|
+
def get_variable_value(name)
|
59
|
+
value = fetch_impl(name)
|
60
60
|
if value.is_a?(Proc)
|
61
61
|
value = value.call()
|
62
62
|
end
|
@@ -18,18 +18,18 @@ module Mullet
|
|
18
18
|
|
19
19
|
# Resolves variable name to value.
|
20
20
|
#
|
21
|
-
# @param [Symbol]
|
21
|
+
# @param [Symbol] name
|
22
22
|
# variable name
|
23
23
|
# @return variable value
|
24
|
-
def
|
24
|
+
def get_variable_value(name)
|
25
25
|
@scopes.reverse_each do |scope|
|
26
|
-
value = scope.
|
27
|
-
if value !=
|
26
|
+
value = scope.get_variable_value(name)
|
27
|
+
if value != NOT_FOUND
|
28
28
|
return value
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
return
|
32
|
+
return NOT_FOUND
|
33
33
|
end
|
34
34
|
|
35
35
|
# Adds a nested scope to search in subsequent lookups.
|
@@ -37,7 +37,8 @@ module Mullet
|
|
37
37
|
# @param data
|
38
38
|
# data object
|
39
39
|
def push_scope(data)
|
40
|
-
@scopes.push(
|
40
|
+
@scopes.push(
|
41
|
+
data.respond_to?(:get_variable_value) ? data : DefaultModel.new(data))
|
41
42
|
end
|
42
43
|
|
43
44
|
# Removes innermost nested scope.
|
@@ -1,6 +1,10 @@
|
|
1
|
-
|
1
|
+
require 'mullet/model'
|
2
2
|
|
3
|
-
|
3
|
+
module Mullet; module HTML
|
4
|
+
|
5
|
+
# Operation to set attribute value. Classes including this module are
|
6
|
+
# expected to respond to the get_value method returning the attribute value
|
7
|
+
# to set.
|
4
8
|
module AttributeCommand
|
5
9
|
|
6
10
|
# Constructor
|
@@ -11,7 +15,7 @@ module Mullet
|
|
11
15
|
@attribute_name = attribute_name
|
12
16
|
end
|
13
17
|
|
14
|
-
# Sets attribute in the
|
18
|
+
# Sets attribute in the _attributes_ collection.
|
15
19
|
#
|
16
20
|
# @param [RenderContext] render_context
|
17
21
|
# render context
|
@@ -29,4 +33,4 @@ module Mullet
|
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
end
|
36
|
+
end; end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Mullet; module HTML
|
2
|
+
|
3
|
+
# Maps attribute names to values. Also renders attributes to HTML.
|
4
|
+
class Attributes < Hash
|
5
|
+
|
6
|
+
def escape_quote(value)
|
7
|
+
return value.include?('"') ? value.gsub(/"/, '"') : value
|
8
|
+
end
|
9
|
+
|
10
|
+
# Renders attributes to HTML syntax.
|
11
|
+
#
|
12
|
+
# @return rendered HTML
|
13
|
+
def render()
|
14
|
+
output = ''
|
15
|
+
each do |key, value|
|
16
|
+
output << %( #{key}="#{escape_quote(value)}")
|
17
|
+
end
|
18
|
+
return output
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end; end
|
data/lib/mullet/html/command.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
module Mullet
|
1
|
+
module Mullet; module HTML
|
2
2
|
|
3
3
|
# Recognized template commands
|
4
4
|
module Command
|
5
|
-
|
5
|
+
DATA_PREFIX = 'data-'
|
6
|
+
NAMESPACE_PREFIX = 'mullet'
|
6
7
|
NAMESPACE_URI = 'http://pukkaone.github.com/mullet/1'
|
7
8
|
|
8
9
|
ACTION = 'action'
|
@@ -26,4 +27,4 @@ module Mullet
|
|
26
27
|
VALUE_MESSAGE = 'value-message'
|
27
28
|
end
|
28
29
|
|
29
|
-
end
|
30
|
+
end; end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Mullet; module HTML
|
2
|
+
|
3
|
+
# Answers true when asked if it has any command.
|
4
|
+
class CommandElementRenderer < ElementRenderer
|
5
|
+
|
6
|
+
# Constructor
|
7
|
+
#
|
8
|
+
# @param [Element] element
|
9
|
+
# element to render
|
10
|
+
def initialize(element)
|
11
|
+
super(element)
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_command()
|
15
|
+
return true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end; end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Mullet; module HTML
|
2
|
+
|
3
|
+
# HTML element
|
4
|
+
class Element
|
5
|
+
|
6
|
+
# attributes from template
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
# true if element has any child element or text content
|
10
|
+
attr_accessor :has_content
|
11
|
+
|
12
|
+
# true if element has any template command
|
13
|
+
attr_accessor :has_command
|
14
|
+
|
15
|
+
# Constructor
|
16
|
+
#
|
17
|
+
# @param [String] qualified_name
|
18
|
+
# tag name with namespace prefix
|
19
|
+
# @param [Attributes] attributes
|
20
|
+
# attributes from template
|
21
|
+
def initialize(qualified_name, attributes)
|
22
|
+
@name = qualified_name
|
23
|
+
@attributes = attributes
|
24
|
+
@has_content = false
|
25
|
+
@has_command = false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Renders start tag
|
29
|
+
#
|
30
|
+
# @param [Attributes] attributes
|
31
|
+
# attributes to render
|
32
|
+
def render_start_tag(attributes)
|
33
|
+
return "<#{@name}#{attributes.render()}>"
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_end_tag()
|
37
|
+
return "</#{@name}>"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end; end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'mullet/container'
|
2
|
+
require 'mullet/template_error'
|
3
|
+
require 'mullet/html/command'
|
4
|
+
require 'mullet/html/message'
|
5
|
+
require 'mullet/html/message_attribute_command'
|
6
|
+
require 'mullet/html/model_attribute_command'
|
7
|
+
|
8
|
+
module Mullet; module HTML
|
9
|
+
|
10
|
+
# Renders an HTML element.
|
11
|
+
class ElementRenderer
|
12
|
+
include Container
|
13
|
+
|
14
|
+
ATTRIBUTE_SEPARATOR = ';'
|
15
|
+
ATTRIBUTE_NAME_SEPARATOR = '='
|
16
|
+
ATTRIBUTE_SYNTAX_ERROR = "expected '%s' in '%s'"
|
17
|
+
ATTRIBUTE_NAME_MISSING_ERROR = "attribute name missing in '%s'"
|
18
|
+
CONVENIENT_ATTRIBUTE_COMMANDS = [
|
19
|
+
Command::ACTION,
|
20
|
+
Command::ALT,
|
21
|
+
Command::HREF,
|
22
|
+
Command::SRC,
|
23
|
+
Command::TITLE,
|
24
|
+
Command::VALUE ]
|
25
|
+
|
26
|
+
attr_reader :remove_mode
|
27
|
+
|
28
|
+
# Constructor
|
29
|
+
#
|
30
|
+
# @param [Element] element
|
31
|
+
# element to render
|
32
|
+
def initialize(element)
|
33
|
+
super()
|
34
|
+
|
35
|
+
@element = element
|
36
|
+
@attribute_commands = []
|
37
|
+
@remove_mode = nil
|
38
|
+
@text_variable_name = nil
|
39
|
+
@text_message = nil
|
40
|
+
@template = nil
|
41
|
+
@escape_xml = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_attribute_command(attribute_name, variable_name)
|
45
|
+
@attribute_commands <<
|
46
|
+
ModelAttributeCommand.new(attribute_name, variable_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_attribute_commands(attribute_variable_pairs)
|
50
|
+
attribute_variable_pairs.split(ATTRIBUTE_SEPARATOR).each do |command_text|
|
51
|
+
command_parts = command_text.split(ATTRIBUTE_NAME_SEPARATOR, 2)
|
52
|
+
if command_parts.length() < 2
|
53
|
+
raise TemplateError.new(
|
54
|
+
ATTRIBUTE_SYNTAX_ERROR % [ATTRIBUTE_NAME_SEPARATOR, command_text])
|
55
|
+
end
|
56
|
+
|
57
|
+
attribute_name = command_parts[0].strip()
|
58
|
+
if attribute_name.empty?()
|
59
|
+
raise TemplateError.new(ATTRIBUTE_NAME_MISSING_ERROR % command_text)
|
60
|
+
end
|
61
|
+
|
62
|
+
variable_name = command_parts[1].strip()
|
63
|
+
if variable_name.empty?()
|
64
|
+
raise TemplateError.new("variable name missing in '#{command_text}'")
|
65
|
+
end
|
66
|
+
|
67
|
+
add_attribute_command(attribute_name, variable_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def configure_attribute_commands(command_attributes)
|
72
|
+
value = command_attributes.fetch(Command::ATTR, nil)
|
73
|
+
if value != nil
|
74
|
+
add_attribute_commands(value)
|
75
|
+
end
|
76
|
+
|
77
|
+
CONVENIENT_ATTRIBUTE_COMMANDS.each do |attribute_name|
|
78
|
+
value = command_attributes.fetch(attribute_name, nil)
|
79
|
+
if value != nil
|
80
|
+
add_attribute_command(attribute_name, value)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
value = command_attributes.fetch(Command::REMOVE, nil)
|
85
|
+
if value != nil
|
86
|
+
value.strip!();
|
87
|
+
value.downcase!()
|
88
|
+
@remove_mode = RemoveMode.value_of(value)
|
89
|
+
if @remove_mode == nil
|
90
|
+
raise TemplateError.new("invalid remove argument '#{value}'")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_attribute_message_command(attribute_name, message_arguments)
|
96
|
+
@attribute_commands << MessageAttributeCommand.new(
|
97
|
+
attribute_name, Message.new(message_arguments))
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_attribute_message_commands(attribute_message_pairs)
|
101
|
+
attribute_message_pairs.split(ATTRIBUTE_SEPARATOR).each do |command_text|
|
102
|
+
command_parts = command_text.split(ATTRIBUTE_NAME_SEPARATOR, 2)
|
103
|
+
if command_parts.length() < 2
|
104
|
+
raise TemplateError.new(
|
105
|
+
ATTRIBUTE_SYNTAX_ERROR % [ATTRIBUTE_NAME_SEPARATOR, command_text])
|
106
|
+
end
|
107
|
+
|
108
|
+
attribute_name = command_parts[0].strip()
|
109
|
+
if attribute_name.empty?()
|
110
|
+
raise TemplateError.new(ATTRIBUTE_NAME_MISSING_ERROR % command_text)
|
111
|
+
end
|
112
|
+
|
113
|
+
message_arguments = command_parts[1].strip()
|
114
|
+
if message_arguments.empty?()
|
115
|
+
raise TemplateError.new(
|
116
|
+
"message arguments missing in '#{command_text}'")
|
117
|
+
end
|
118
|
+
|
119
|
+
add_attribute_message_command(attribute_name, message_arguments)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def configure_attribute_message_commands(command_attributes)
|
124
|
+
value = command_attributes.fetch(Command::ALT_MESSAGE, nil)
|
125
|
+
if value != nil
|
126
|
+
add_attribute_message_command(Command::ALT, value)
|
127
|
+
end
|
128
|
+
|
129
|
+
value = command_attributes.fetch(Command::ATTR_MESSAGE, nil)
|
130
|
+
if value != nil
|
131
|
+
add_attribute_message_commands(value)
|
132
|
+
end
|
133
|
+
|
134
|
+
value = command_attributes.fetch(Command::TITLE_MESSAGE, nil)
|
135
|
+
if value != nil
|
136
|
+
add_attribute_message_command(Command::TITLE, value)
|
137
|
+
end
|
138
|
+
|
139
|
+
value = command_attributes.fetch(Command::VALUE_MESSAGE, nil)
|
140
|
+
if value != nil
|
141
|
+
add_attribute_message_command(Command::VALUE, value)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def configure_content(command_attributes, template_loader)
|
146
|
+
@text_variable_name = command_attributes.fetch(Command::TEXT, nil)
|
147
|
+
if @text_variable_name != nil
|
148
|
+
@text_variable_name = @text_variable_name.to_sym()
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
text_message_arguments =
|
153
|
+
command_attributes.fetch(Command::TEXT_MESSAGE, nil)
|
154
|
+
if text_message_arguments != nil
|
155
|
+
@text_message = Message.new(text_message_arguments)
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
uri = command_attributes.fetch(Command::INCLUDE, nil)
|
160
|
+
if uri != nil
|
161
|
+
@template = template_loader.load(uri)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def configure_commands(command_attributes, template_loader)
|
166
|
+
configure_attribute_commands(command_attributes)
|
167
|
+
configure_attribute_message_commands(command_attributes)
|
168
|
+
|
169
|
+
configure_content(command_attributes, template_loader)
|
170
|
+
|
171
|
+
escape_xml_value = command_attributes.fetch(Command::ESCAPE_XML, nil)
|
172
|
+
if escape_xml_value != nil
|
173
|
+
@escape_xml = escape_xml_value != 'false'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Checks if this renderer has a command. If it has a command, then the
|
178
|
+
# template builder should not discard it.
|
179
|
+
def has_command()
|
180
|
+
return !@attribute_commands.empty?()
|
181
|
+
end
|
182
|
+
|
183
|
+
# Checks if the element content will be rendered by a command.
|
184
|
+
def has_dynamic_content()
|
185
|
+
return @text_variable_name != nil ||
|
186
|
+
@text_message != nil ||
|
187
|
+
@template != nil
|
188
|
+
end
|
189
|
+
|
190
|
+
def execute_attribute_commands(render_context)
|
191
|
+
if @attribute_commands.empty?()
|
192
|
+
return @element.attributes()
|
193
|
+
end
|
194
|
+
|
195
|
+
# Copy the original attributes. The commands modify the copy to
|
196
|
+
# produce the attributes to render.
|
197
|
+
render_attributes = @element.attributes().dup()
|
198
|
+
@attribute_commands.each do |command|
|
199
|
+
command.execute(render_context, render_attributes)
|
200
|
+
end
|
201
|
+
return render_attributes
|
202
|
+
end
|
203
|
+
|
204
|
+
def should_render_tag()
|
205
|
+
return @remove_mode == nil || @remove_mode == RemoveMode::CONTENT
|
206
|
+
end
|
207
|
+
|
208
|
+
def render_start_tag(render_context)
|
209
|
+
if should_render_tag()
|
210
|
+
attributes = execute_attribute_commands(render_context)
|
211
|
+
render_context << @element.render_start_tag(attributes)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def render_end_tag(render_context)
|
216
|
+
if should_render_tag()
|
217
|
+
render_context << @element.render_end_tag()
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def should_render_content()
|
222
|
+
return @remove_mode != RemoveMode::CONTENT
|
223
|
+
end
|
224
|
+
|
225
|
+
def render_content(render_context)
|
226
|
+
if should_render_content()
|
227
|
+
if @text_variable_name != nil
|
228
|
+
value = render_context.get_display_value(@text_variable_name)
|
229
|
+
text = render_context.escape_xml(value.to_s())
|
230
|
+
render_context << text
|
231
|
+
elsif @text_message != nil
|
232
|
+
text = @text_message.translate(render_context)
|
233
|
+
text = render_context.escape_xml(text)
|
234
|
+
render_context << text
|
235
|
+
elsif @template != nil
|
236
|
+
@template.render(render_context)
|
237
|
+
else
|
238
|
+
render_children(render_context)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def render(render_context)
|
244
|
+
# Process the command to change the escaping mode.
|
245
|
+
original_escape_xml_enabled = render_context.escape_xml_enabled()
|
246
|
+
if @escape_xml != nil
|
247
|
+
render_context.escape_xml_enabled = @escape_xml
|
248
|
+
end
|
249
|
+
|
250
|
+
render_start_tag(render_context)
|
251
|
+
render_content(render_context)
|
252
|
+
render_end_tag(render_context)
|
253
|
+
|
254
|
+
# Restore the original escaping mode.
|
255
|
+
if @escape_xml != nil
|
256
|
+
render_context.escape_xml_enabled = original_escape_xml_enabled
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end; end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'mullet/html/command'
|
2
|
+
require 'mullet/template_error'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Mullet; module HTML
|
7
|
+
|
8
|
+
# Proxy for a SAX event handler that forwards events only while the
|
9
|
+
# parser is within an element identified by an `id` attribute.
|
10
|
+
class FilteredElementHandler < Nokogiri::XML::SAX::Document
|
11
|
+
ID = 'id'
|
12
|
+
|
13
|
+
# Constructor
|
14
|
+
#
|
15
|
+
# @param [Document] handler
|
16
|
+
# event handler to forward events to
|
17
|
+
# @param [String] id
|
18
|
+
# id attribute value
|
19
|
+
def initialize(handler, id)
|
20
|
+
@handler = handler
|
21
|
+
@id = id
|
22
|
+
@depth = 0
|
23
|
+
@found_id = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def should_forward()
|
27
|
+
return @depth > 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_document()
|
31
|
+
@handler.start_document()
|
32
|
+
end
|
33
|
+
|
34
|
+
def end_document()
|
35
|
+
@handler.end_document()
|
36
|
+
|
37
|
+
if !@found_id
|
38
|
+
raise TemplateError.new("element with attribute id='#{@id}' not found")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_element_namespace(name, attributes, prefix, uri, namespaces)
|
43
|
+
if should_forward()
|
44
|
+
@depth += 1
|
45
|
+
return @handler.start_element_namespace(
|
46
|
+
name, attributes, prefix, uri, namespaces)
|
47
|
+
end
|
48
|
+
|
49
|
+
value = attributes.each do |attr|
|
50
|
+
qualified_name = [attr.prefix, attr.localname].compact.join(':')
|
51
|
+
if qualified_name == ID && attr.value == @id
|
52
|
+
# Enable event forwarding.
|
53
|
+
@depth = 1
|
54
|
+
@found_id = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def end_element_namespace(name, prefix, uri)
|
60
|
+
if should_forward()
|
61
|
+
@depth -= 1
|
62
|
+
if @depth > 0
|
63
|
+
@handler.end_element_namespace(name, prefix, uri)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def characters(data)
|
69
|
+
if should_forward()
|
70
|
+
@handler.characters(data)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def cdata_block(data)
|
75
|
+
if should_forward()
|
76
|
+
@handler.cdata_block(data)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def comment(data)
|
81
|
+
if should_forward()
|
82
|
+
@handler.comment(data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end; end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'mullet/html/command_element_renderer'
|
2
|
+
require 'mullet/model'
|
3
|
+
|
4
|
+
module Mullet; module HTML
|
5
|
+
|
6
|
+
# Renders an element for each item in a collection.
|
7
|
+
class ForElementRenderer < CommandElementRenderer
|
8
|
+
|
9
|
+
# Constructor
|
10
|
+
#
|
11
|
+
# @param [Element] element
|
12
|
+
# element to render
|
13
|
+
# @param [String] variable_name
|
14
|
+
# name of variable containing collection
|
15
|
+
def initialize(element, variable_name)
|
16
|
+
super(element)
|
17
|
+
@variable_name = variable_name.to_sym()
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :super_render :render
|
21
|
+
|
22
|
+
def render_nested_model(data, render_context)
|
23
|
+
render_context.push_scope(data)
|
24
|
+
super_render(render_context)
|
25
|
+
render_context.pop_scope()
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(render_context)
|
29
|
+
value = render_context.get_variable_value(@variable_name)
|
30
|
+
if value == Model::NOT_FOUND || value == nil || value == false
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
if value.respond_to?(:empty?) && value.empty?()
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
if value.respond_to?(:each)
|
39
|
+
value.each {|item| render_nested_model(item, render_context) }
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
render_nested_model(value, render_context)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end; end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'mullet/html/command_element_renderer'
|
2
|
+
require 'mullet/model'
|
3
|
+
|
4
|
+
module Mullet; module HTML
|
5
|
+
|
6
|
+
# Renders an element if variable is true.
|
7
|
+
class IfElementRenderer < CommandElementRenderer
|
8
|
+
|
9
|
+
# Constructor
|
10
|
+
#
|
11
|
+
# @param [Element] element
|
12
|
+
# element to render
|
13
|
+
# @param [String] variable_name
|
14
|
+
# name of variable containing condition
|
15
|
+
def initialize(element, variable_name)
|
16
|
+
super(element)
|
17
|
+
@variable_name = variable_name.to_sym()
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_render_element(render_context)
|
21
|
+
value = render_context.get_variable_value(@variable_name)
|
22
|
+
if value == Model::NOT_FOUND || value == nil
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
if value.is_a?(FalseClass) || value.is_a?(TrueClass)
|
27
|
+
return value
|
28
|
+
end
|
29
|
+
|
30
|
+
if value.respond_to?(:empty?)
|
31
|
+
return !value.empty?()
|
32
|
+
end
|
33
|
+
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :super_render :render
|
38
|
+
|
39
|
+
def render(render_context)
|
40
|
+
if should_render_element(render_context)
|
41
|
+
super_render(render_context)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end; end
|