zafu 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,257 @@
1
+ require 'zafu/markup'
2
+
3
+ module Zafu
4
+ module ParsingRules
5
+ # The context informs the rendering element about the current Node, node class, existing ids, etc. The
6
+ # context is inherited by sub-elements.
7
+ attr_reader :context
8
+
9
+ # The helper is used to connect the compiler to the world of the application (read/write templates, access traductions, etc)
10
+ attr_reader :helper
11
+
12
+ # The markup (of class Markup) holds information on the tag (<li>), tag attributes (.. class='foo') and
13
+ # indentation information that should be used when rendered. This context is not inherited.
14
+ attr_accessor :markup
15
+
16
+ # This callback is run just after the block is initialized (Parser#initialize).
17
+ def start(mode)
18
+ # tag_context
19
+ @markup = Markup.new(@options.delete(:html_tag))
20
+
21
+ # html_tag
22
+ @markup.params = @options.delete(:html_tag_params) # @html_tag_params
23
+
24
+ # have we already wrapped the result with our tag ?
25
+ @markup.done = false # @html_tag_done
26
+
27
+ # end_tag is used to know when to close parsing in sub-do
28
+ # Example:
29
+ # <li do='each' do='images'>
30
+ # <ul>
31
+ # <li><r:link/></li> <!-- do not close outer LI now: @end_tag_count != 0 -->
32
+ # </ul>
33
+ # </li> <!-- close outer LI now: @end_tag_count == 0 -->
34
+ #
35
+ @end_tag = @markup.tag || @options.delete(:end_tag) || "r:#{@method}"
36
+ @end_tag_count = 1
37
+
38
+ # code indentation
39
+ @markup.space_before = @options.delete(:space_before) # @space_before
40
+
41
+ # form capture (input, select, textarea, form)
42
+ # FIXME: what is this ???
43
+ @options[:form] ||= true if @method == 'form'
44
+
45
+ # puts "[#{@markup[:space_before]}(#{@method})#{@markup[:space_after]}]"
46
+ if @params =~ /\A([^>]*?)do\s*=('|")([^\2]*?[^\\])\2([^>]*)\Z/
47
+ # we have a sub 'do'
48
+ @params = Markup.parse_params($1)
49
+ @sub_do = $3 # this is used by replace_with (FIXME)
50
+
51
+ opts = {:method=>$3, :params=>$4}
52
+
53
+ # the matching zafu tag will be parsed by the last 'do', we must inform it to halt properly :
54
+ opts[:end_tag] = @end_tag
55
+
56
+ sub = make(:void, opts)
57
+ @markup.space_after = sub.markup.space_after
58
+ sub.markup.space_after = ""
59
+ else
60
+ @params = Markup.parse_params(@params)
61
+ end
62
+
63
+ # set name used for include/replace from html_tag if not allready set by superclass
64
+ @name = @options[:name] || @params[:name] || @params[:id] || @markup.params[:id]
65
+
66
+ if !@markup.tag && (@markup.tag = @params.delete(:tag))
67
+ # Extract html tag parameters from @params
68
+ @markup.steal_html_params_from(@params)
69
+ end
70
+
71
+ if @method == 'include'
72
+ include_template
73
+ elsif mode == :tag && !sub
74
+ scan_tag
75
+ elsif !sub
76
+ enter(mode)
77
+ end
78
+ end
79
+
80
+ def before_parse(text)
81
+ text.gsub('<%', '&lt;%').gsub('%>', '%&gt;')
82
+ end
83
+
84
+ # scan rules
85
+ def scan
86
+ # puts "SCAN(#{@method}): [#{@text}]"
87
+ if @text =~ /\A([^<]*?)(^ *|)</m
88
+ flush $1
89
+ eat $2
90
+ if @text[1..1] == '/'
91
+ store $2
92
+ scan_close_tag
93
+ elsif @text[0..3] == '<!--'
94
+ scan_html_comment(:space_before=> $2)
95
+ elsif @text[0..8] == '<![CDATA['
96
+ flush '<![CDATA['
97
+ else
98
+ scan_tag(:space_before=> $2)
99
+ end
100
+ else
101
+ # no more tags
102
+ flush
103
+ end
104
+ end
105
+
106
+ def scan_close_tag
107
+ if @text =~ /\A<\/([^>]+)>( *\n+|)/m
108
+ # puts "CLOSE:[#{$&}]}" # ztag
109
+ # closing tag
110
+ if $1 == @end_tag
111
+ @end_tag_count -= 1
112
+ if @end_tag_count == 0
113
+ if @end_tag == 'script'
114
+ flush $& # keep closing tag
115
+ else
116
+ eat $&
117
+ end
118
+ @markup.space_after = $2
119
+ leave
120
+ else
121
+ # keep the tag (false alert)
122
+ flush $&
123
+ end
124
+ elsif $1[0..1] == 'r:'
125
+ # /rtag
126
+ eat $&
127
+ if $1 != @end_tag
128
+ # error bad closing rtag
129
+ store "<span class='parser_error'>#{$&.gsub('<', '&lt;').gsub('>','&gt;')} should be &lt;/#{@end_tag}&gt;</span>"
130
+ end
131
+ leave
132
+ else
133
+ # other html tag closing
134
+ flush $&
135
+ end
136
+ else
137
+ # error
138
+ flush
139
+ end
140
+ end
141
+
142
+ def scan_html_comment(opts={})
143
+ if @text =~ /\A<!--\|(.*?)-->/m
144
+ # zafu html escaped
145
+ eat $&
146
+ @text = opts[:space_before] + $1 + @text
147
+ elsif @text =~ /\A<!--.*?-->/m
148
+ # html comment
149
+ flush $&
150
+ else
151
+ # error
152
+ flush
153
+ end
154
+ end
155
+
156
+ def scan_tag(opts={})
157
+ #puts "TAG(#{@method}): [#{@text}]"
158
+ if @text =~ /\A<r:([\w_]+)([^>]*?)(\/?)>/
159
+ #puts "RTAG:#{$~.to_a.inspect}" # ztag
160
+ eat $&
161
+ opts.merge!(:method=>$1, :params=>$2)
162
+ opts.merge!(:text=>'') if $3 != ''
163
+ make(:void, opts)
164
+ elsif @text =~ /\A<(\w+)([^>]*?)do\s*=('([^>]*?[^\\]|)'|"([^>]*?[^\\]|)")([^>]*?)(\/?)>/
165
+ #puts "DO:#{$~.to_a.inspect}" # do tag
166
+ eat $&
167
+ opts.merge!(:method=>($4||$5), :html_tag=>$1, :html_tag_params=>$2, :params=>$6)
168
+ opts.merge!(:text=>'') if $7 != ''
169
+ make(:void, opts)
170
+ elsif @options[:form] && @text =~ /\A<(input|select|textarea|form)([^>]*?)(\/?)>/
171
+ eat $&
172
+ method = $1 == 'form' ? 'form_tag' : $1 # <form> ==> r_form_tag, <r:form> ==> r_form
173
+ opts.merge!(:method=>method, :params=>$2)
174
+ opts.merge!(:text=>'') if $3 != ''
175
+ opts.merge!(:end_tag=>'form') if method == 'form_tag'
176
+ make(:void, opts)
177
+ elsif @text =~ /\A<(\w+)(([^>]*?)\#\{([^>]*?))(\/?)>/
178
+ # html tag with dynamic params
179
+ #puts "OTHER_DYN:[#{$&}]"
180
+
181
+ eat $&
182
+ opts.merge!(:method => 'void', :html_tag => $1, :html_tag_params => $2, :params => {})
183
+ opts.merge!(:text=>'') if $5 != ''
184
+ make(:void, opts)
185
+ elsif @text =~ /\A<(\w+)([^>]*?)id\s*=('[^>]*?[^\\]'|"[^>]*?[^\\]")([^>]*?)(\/?)>/
186
+ #puts "ID:#{$~.to_a.inspect}" # id tag
187
+ eat $&
188
+ opts.merge!(:method=>'void', :html_tag=>$1, :params=>{:id => $3[1..-2]}, :html_tag_params=>"#{$2}id=#{$3}#{$4}")
189
+ opts.merge!(:text=>'') if $5 != ''
190
+ make(:void, opts)
191
+ elsif @end_tag && @text =~ /\A<#{@end_tag}([^>]*?)(\/?)>/
192
+ #puts "SAME:#{$~.to_a.inspect}" # simple html tag same as end_tag
193
+ flush $&
194
+ @end_tag_count += 1 unless $2 == '/'
195
+ elsif @text =~ /\A<(link|img|script)/
196
+ #puts "HTML:[#{$&}]" # html
197
+ make(:asset)
198
+ elsif @text =~ /\A<style>/
199
+ flush $&
200
+ make(:style)
201
+ elsif @text =~ /\A[^>]*?>/
202
+ # html tag
203
+ #puts "OTHER:[#{$&}]"
204
+ store opts[:space_before]
205
+ flush $&
206
+ else
207
+ # never closed tag
208
+ flush
209
+ end
210
+ end
211
+
212
+ def scan_asset
213
+ # puts "ASSET(#{object_id}) [#{@text}]"
214
+ if @text =~ /\A<(\w*)([^>]*?)(\/?)>/
215
+ eat $&
216
+ @method = 'rename_asset'
217
+ @markup.tag = @end_tag = $1
218
+ closed = ($3 != '')
219
+ @params = parse_params($2)
220
+ if closed
221
+ leave(:asset)
222
+ elsif @markup.tag == 'script'
223
+ enter(:void)
224
+ else
225
+ enter(:inside_asset)
226
+ end
227
+ else
228
+ # error
229
+ @method = 'void'
230
+ flush
231
+ end
232
+ end
233
+
234
+ def scan_inside_asset
235
+ if @text =~ /\A(.*?)<\/#{@end_tag}>/m
236
+ flush $&
237
+ leave(:asset)
238
+ else
239
+ # never ending asset
240
+ flush
241
+ end
242
+ end
243
+
244
+ def scan_style
245
+ if @text =~ /\A(.*?)<\/style>/m
246
+ flush $&
247
+ @method = 'rename_asset'
248
+ @markup.tag = 'style'
249
+ leave(:style)
250
+ else
251
+ # error
252
+ @method = 'void'
253
+ flush
254
+ end
255
+ end
256
+ end # ParsingRules
257
+ end # Zafu
@@ -0,0 +1,90 @@
1
+ module Zafu
2
+ module Process
3
+ module Ajax
4
+
5
+ # Store a context as a sub-template that can be used in ajax calls
6
+ def r_block
7
+
8
+ # 1. store template
9
+ store_block(self)
10
+ out expand_with
11
+ end
12
+
13
+
14
+ private
15
+ def store_block(block)
16
+ # Save current rendering information (id, html_tag_done, html_tag_params)
17
+ markup_bak = block.markup.dup
18
+
19
+ # Creatae new node context
20
+ node_context = node.as_main
21
+
22
+ # Change rendering context
23
+ block.markup.done = false
24
+ block.markup.set_id(node.dom_id) # "<%= dom_id(#{node}) %>"
25
+ end
26
+
27
+ template = expand_block(self, :)
28
+
29
+ if @context[:block] == self
30
+ # called from self (storing template)
31
+ @context.reject! do |k,v|
32
+ # FIXME: reject all stored elements in a better way then this
33
+ k.kind_of?(String) && k =~ /\ANode_\w/
34
+ end
35
+ @markup.done = false
36
+ @markup.params.merge!(:id=>erb_dom_id)
37
+ @context[:scope_node] = node if @context[:scope_node]
38
+ out expand_with(:node => node)
39
+ if @method == 'drop' && !@context[:make_form]
40
+ out drop_javascript
41
+ end
42
+ else
43
+ if parent.method == 'each' && @method == parent.single_child_method
44
+ # use parent as block
45
+ # FIXME: will not work with block as distant target...
46
+ # do nothing
47
+ else
48
+ @markup.tag ||= 'div'
49
+ new_dom_scope
50
+
51
+ unless @context[:make_form]
52
+ # STORE TEMPLATE ========
53
+
54
+ context_bak = @context.dup # avoid side effects when rendering the same block
55
+ ignore_list = @method == 'block' ? ['form'] : [] # do not show the form in the normal template of a block
56
+ template = expand_block(self, :block=>self, :list=>false, :saved_template=>true, :ignore => ignore_list)
57
+ @context = context_bak
58
+ @result = ''
59
+ out helper.save_erb_to_url(template, template_url)
60
+
61
+ # STORE FORM ============
62
+ if edit = descendant('edit')
63
+ publish_after_save = (edit.params[:publish] == 'true')
64
+ if form = descendant('form')
65
+ # USE BLOCK FORM ========
66
+ form_text = expand_block(form, :saved_template=>true, :publish_after_save => publish_after_save)
67
+ else
68
+ # MAKE A FORM FROM BLOCK ========
69
+ form = self.dup
70
+ form.method = 'form'
71
+ form_text = expand_block(form, :make_form => true, :list => false, :saved_template => true, :publish_after_save => publish_after_save)
72
+ end
73
+ out helper.save_erb_to_url(form_text, form_url)
74
+ end
75
+ end
76
+
77
+ # RENDER
78
+ @markup.done = false
79
+ @markup.params.merge!(:id=>erb_dom_id)
80
+ end
81
+
82
+ out expand_with
83
+ if @method == 'drop' && !@context[:make_form]
84
+ out drop_javascript
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,45 @@
1
+ module Zafu
2
+ module Process
3
+ # This module manages conditional rendering (if, else, elsif, case, when).
4
+ module Conditional
5
+ def r_if
6
+ "<% if true -%>#{expand_with(:in_if => true)}<% end -%>"
7
+ end
8
+
9
+ def r_else
10
+ return nil unless @context[:in_if]
11
+ # We use 'elsif' just in case there are more then one 'else' clause
12
+ out "<% elsif true -%>#{expand_with(:in_if => false)}" # do not propagate
13
+ end
14
+
15
+ def helper
16
+ @context[:helper]
17
+ end
18
+
19
+ # Return the node context for a given class (looks up into the hierarchy) or the
20
+ # current node context if klass is nil.
21
+ def node(klass = nil)
22
+ return @context[:node] if !klass
23
+ @context[:node].get(klass)
24
+ end
25
+
26
+ def expand_with_node(name, klass)
27
+ expand_with(:node => @context[:node].move_to(name, klass))
28
+ end
29
+
30
+ # def context_with_node(name, klass)
31
+ # context = @context.dup
32
+ # context[:node] = context[:node].move_to(name, klass)
33
+ # end
34
+
35
+ def var
36
+ return @var if @var
37
+ if node.name =~ /^var(\d+)$/
38
+ @var = "var#{$1.to_i + 1}"
39
+ else
40
+ @var = "var1"
41
+ end
42
+ end
43
+ end # Context
44
+ end # Process
45
+ end # Zafu
@@ -0,0 +1,45 @@
1
+ module Zafu
2
+ module Process
3
+ # This module manages the change of contexts by opening (each) or moving into the NodeContext.
4
+ # The '@context' holds many information on the current compilation environment. Inside this
5
+ # context, the "node" context holds information on the type of "this" (first responder).
6
+ module Context
7
+ def r_each
8
+ if node.klass.kind_of?(Array)
9
+ out "<% #{node}.each do |#{var}| -%>"
10
+ out render_html_tag(expand_with_node(var, node.klass.first))
11
+ out "<% end -%>"
12
+ end
13
+ end
14
+
15
+ def helper
16
+ @context[:helper]
17
+ end
18
+
19
+ # Return the node context for a given class (looks up into the hierarchy) or the
20
+ # current node context if klass is nil.
21
+ def node(klass = nil)
22
+ return @context[:node] if !klass
23
+ @context[:node].get(klass)
24
+ end
25
+
26
+ def expand_with_node(name, klass)
27
+ expand_with(:node => @context[:node].move_to(name, klass))
28
+ end
29
+
30
+ # def context_with_node(name, klass)
31
+ # context = @context.dup
32
+ # context[:node] = context[:node].move_to(name, klass)
33
+ # end
34
+
35
+ def var
36
+ return @var if @var
37
+ if node.name =~ /^var(\d+)$/
38
+ @var = "var#{$1.to_i + 1}"
39
+ else
40
+ @var = "var1"
41
+ end
42
+ end
43
+ end # Context
44
+ end # Process
45
+ end # Zafu
@@ -0,0 +1,168 @@
1
+ module Zafu
2
+ module Process
3
+ module HTML
4
+ attr_accessor :html_tag, :html_tag_params, :name, :sub_do
5
+
6
+ # Replace the 'original' element in the included template with our new version.
7
+ def replace_with(new_obj)
8
+ super
9
+ html_tag_params = new_obj.html_tag_params
10
+ [:class, :id].each do |sym|
11
+ html_tag_params[sym] = new_obj.params[sym] if new_obj.params.include?(sym)
12
+ end
13
+ @markup.tag = new_obj.html_tag || @markup.tag
14
+ @markup.params.merge!(html_tag_params)
15
+ if new_obj.params[:method]
16
+ @method = new_obj.params[:method] if new_obj.params[:method]
17
+ elsif new_obj.sub_do
18
+ @method = 'void'
19
+ end
20
+ end
21
+
22
+ # Pass the caller's 'html_tag' and 'html_tag_params' to the included part.
23
+ def include_part(obj)
24
+ obj.html_tag = @markup.tag || obj.html_tag
25
+ obj.html_tag_params = !@markup.params.empty? ? @markup.params : obj.html_tag_params
26
+ @markup.tag = nil
27
+ @markup.params = {}
28
+ super(obj)
29
+ end
30
+
31
+ def empty?
32
+ super && @markup.params == {} && @markup.tag.nil?
33
+ end
34
+
35
+ def before_process
36
+ return unless super
37
+ @markup.done = false
38
+ unless @markup.tag
39
+ if @markup.tag = @params.delete(:tag)
40
+ @markup.params = {}
41
+ [:id, :class].each do |k|
42
+ next unless @params[k]
43
+ @markup.params[k] = @params.delete(k)
44
+ end
45
+ end
46
+ end
47
+ # Translate dynamic params such as <tt>class='#{visitor.lang}'</tt> in the context
48
+ # of the current parser
49
+ @markup.compile_params(self)
50
+
51
+ # [each] is run many times with different roles. Some of these change html_tag_params.
52
+ @markup_bak = @markup.dup
53
+ true
54
+ end
55
+
56
+ def after_process(text)
57
+ res = @markup.wrap(super)
58
+ @markup = @markup_bak
59
+ res
60
+ end
61
+
62
+ def inspect
63
+ @markup.done = false
64
+ res = super
65
+ if @markup.tag
66
+ if res =~ /\A\[(\w+)(.*)\/\]\Z/m
67
+ res = "[#{$1}#{$2}]<#{@markup.tag}/>[/#{$1}]"
68
+ elsif res =~ /\A\[([^\]]+)\](.*)\[\/(\w+)\]\Z/m
69
+ res = "[#{$1}]#{render_html_tag($2)}[/#{$3}]"
70
+ end
71
+ end
72
+ res
73
+ end
74
+
75
+ def r_ignore
76
+ @markup.done = true
77
+ ''
78
+ end
79
+
80
+ alias r_ r_ignore
81
+
82
+ def r_rename_asset
83
+ return expand_with unless @markup.tag
84
+ case @markup.tag
85
+ when 'link'
86
+ key = :href
87
+ if @params[:rel].downcase == 'stylesheet'
88
+ type = :stylesheet
89
+ else
90
+ type = :link
91
+ end
92
+ when 'style'
93
+ @markup.done = true
94
+ return expand_with.gsub(/url\(('|")(.*?)\1\)/) do
95
+ if $2[0..6] == 'http://'
96
+ $&
97
+ else
98
+ quote = $1
99
+ new_src = helper.send(:template_url_for_asset, :base_path=>@options[:base_path], :src => $2)
100
+ "url(#{quote}#{new_src}#{quote})"
101
+ end
102
+ end
103
+ else
104
+ key = :src
105
+ type = @markup.tag.to_sym
106
+ end
107
+
108
+ src = @params[key]
109
+ if src && src[0..0] != '/' && src[0..6] != 'http://'
110
+ @params[key] = helper.send(:template_url_for_asset, :src => src, :base_path => @options[:base_path], :type => type)
111
+ end
112
+
113
+ res = "<#{@markup.tag}#{params_to_html(@params)}"
114
+ @markup.done = true
115
+ inner = expand_with
116
+ if inner == ''
117
+ res + "/>"
118
+ else
119
+ res + ">#{inner}"
120
+ end
121
+ end
122
+
123
+ def r_form
124
+ res = "<#{@markup.tag}#{params_to_html(@params)}"
125
+ @markup.done = true
126
+ inner = expand_with
127
+ if inner == ''
128
+ res + "/>"
129
+ else
130
+ res + ">#{inner}"
131
+ end
132
+ end
133
+
134
+ def r_select
135
+ res = "<#{@markup.tag}#{params_to_html(@params)}"
136
+ @markup.done = true
137
+ inner = expand_with
138
+ if inner == ''
139
+ res + "></#{@markup.tag}>"
140
+ else
141
+ res + ">#{inner}"
142
+ end
143
+ end
144
+
145
+ def r_input
146
+ res = "<#{@markup.tag}#{params_to_html(@params)}"
147
+ @markup.done = true
148
+ inner = expand_with
149
+ if inner == ''
150
+ res + "/>"
151
+ else
152
+ res + ">#{inner}"
153
+ end
154
+ end
155
+
156
+ def r_textarea
157
+ res = "<#{@markup.tag}#{params_to_html(@params)}"
158
+ @markup.done = true
159
+ inner = expand_with
160
+ if inner == ''
161
+ res + "/>"
162
+ else
163
+ res + ">#{inner}"
164
+ end
165
+ end
166
+ end # HTML
167
+ end # Process
168
+ end # Zafu