zafu 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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