amrita2 1.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1897 @@
1
+ require 'rexml/document'
2
+ require 'enumerator'
3
+ require 'erb'
4
+
5
+ # Amrita2 adds methods for sanitize support to Object and String.
6
+ # Every added method name has prefix like amrita_xxxxx
7
+ class Object
8
+ # sanitize any object before insertion to HTML document text
9
+ def amrita_sanitize
10
+ Amrita2::Sanitizer::sanitize_text(to_s)
11
+ end
12
+
13
+ # sanitize any object before insertion to HTML tag attribute
14
+ def amrita_sanitize_as_attribute
15
+ Amrita2::Sanitizer::sanitize_attribute_value(to_s)
16
+ end
17
+
18
+ # sanitize any object before insertion to HTML tag attribute for URL
19
+ def amrita_sanitize_as_url
20
+ Amrita2::Sanitizer::sanitize_url(to_s)
21
+ end
22
+ end
23
+
24
+ class Symbol
25
+ # treat Symbol and String equally
26
+ def intern
27
+ self
28
+ end
29
+ end
30
+
31
+ # Amrita2 adds methods for sanitize support to Object and String.
32
+ # Every added method name has prefix like amrita_xxxxx
33
+ class String
34
+ # sanitize self before insertion to HTML document text
35
+ def amrita_sanitize
36
+ Amrita2::Sanitizer::sanitize_text(self)
37
+ end
38
+
39
+ # sanitize self before insertion to HTML tag attribute
40
+ def amrita_sanitize_as_attribute
41
+ Amrita2::Sanitizer::sanitize_attribute_value(self)
42
+ end
43
+
44
+ # sanitize self before insertion to HTML tag attribute for URL
45
+ def amrita_sanitize_as_url
46
+ Amrita2::Sanitizer::sanitize_url(self)
47
+ end
48
+ end
49
+
50
+
51
+ # Amrita2 adds methods for support of internal compilation XML to Ruby conde
52
+ # Every added method name has prefix like amrita_xxxxx.
53
+ # Don't call them.
54
+ module REXML
55
+ class Child
56
+ attr_accessor :amrita_no_spec
57
+ def amrita_compile_main_code(cg)
58
+ #cg.put_constant(to_s)
59
+ cg.put_rexml_node(self)
60
+ end
61
+
62
+ def amrita_has_cdata?
63
+ false
64
+ end
65
+ end
66
+
67
+
68
+ class Parent < Child
69
+ def amrita_compile_main_code(cg)
70
+ if amrita_no_spec and not amrita_has_cdata?
71
+ super
72
+ else
73
+ each_child do |c|
74
+ c.amrita_compile_main_code(cg)
75
+ end
76
+ end
77
+ end
78
+
79
+ def amrita_has_cdata?
80
+ each_child do |c|
81
+ case c
82
+ when CData
83
+ return true
84
+ when Parent
85
+ return true if c.amrita_has_cdata?
86
+ else
87
+ end
88
+ end
89
+ false
90
+ end
91
+ end
92
+
93
+ class XMLDecl < Child # :nodoc:
94
+ def amrita_compile_main_code(cg)
95
+ # don't output XMLDecl
96
+ end
97
+ end
98
+
99
+ class DocType < Parent # :nodoc:
100
+ def amrita_compile_main_code(cg)
101
+ cg.put_constant(to_s)
102
+ cg.put_constant("\n")
103
+ end
104
+ end
105
+
106
+ class Comment < Child
107
+ def amrita_compile_main_code(cg)
108
+ cg.put_constant("<!--#{ to_s}-->")
109
+ end
110
+ end
111
+
112
+ class Element < Parent
113
+ attr_accessor :amrita_spec
114
+ def amrita_compile_main_code(cg)
115
+ if amrita_no_spec and not amrita_has_cdata?
116
+ super
117
+ else
118
+ cg.compile_element_main_code(self) do
119
+ super
120
+ end
121
+ end
122
+ end
123
+
124
+ def amrita_compile_sub_code(cg)
125
+ cg.compile_element_sub_code(self) do
126
+ elements.each do |e|
127
+ e.amrita_compile_sub_code(cg)
128
+ end
129
+ end
130
+ end
131
+
132
+ alias to_s_org to_s
133
+ def to_s
134
+ Amrita2::SanitizedString[to_s_org]
135
+ end
136
+ end
137
+
138
+ class CData < Text
139
+ def amrita_compile_main_code(cg)
140
+ cg.put_cdata(self)
141
+ end
142
+ end
143
+ end
144
+
145
+ module Amrita2 # :nodoc:
146
+ module SanitizeSupport # :nodoc:
147
+
148
+ # Amrita2 sanitize anything except for SanitizedString
149
+ # If you want to sanitize yourself and don't want to Amrita2 sanitize your object,
150
+ # pass SanitizedString[x] as model data.
151
+ class SanitizedString < String
152
+ def SanitizedString::[](s)
153
+ new(s).freeze
154
+ end
155
+
156
+ def amrita_sanitize
157
+ self
158
+ end
159
+
160
+ def amrita_sanitize_as_attribute
161
+ self
162
+ end
163
+
164
+ def amrita_sanitize_as_url
165
+ self
166
+ end
167
+
168
+ def to_s
169
+ self
170
+ end
171
+
172
+ def inspect
173
+ %[Amrita2::SanitizedString[#{super}]]
174
+ end
175
+ end
176
+
177
+ # This module provide methods for avoid XSS vulnerability
178
+ # taken from IPA home page(Japanese)
179
+ # http://www.ipa.go.jp/security/awareness/vendor/programming/a01_02.html
180
+ module Sanitizer # :nodoc:
181
+ NAMECHAR = '[-\w\d\.:]'
182
+ NAME = "([\\w:]#{NAMECHAR}*)"
183
+ NOT_REFERENCE = "(?!#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" # borrowed from rexml
184
+ AMP_WITHOUT_REFRENCE = /&#{NOT_REFERENCE}/
185
+ # escape &<>
186
+ def sanitize_text(text)
187
+ s = text.dup
188
+ s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
189
+ s.gsub!("<", '&lt;')
190
+ s.gsub!(">", '&gt;')
191
+ s
192
+ end
193
+
194
+ # escape &<>"'
195
+ def sanitize_attribute_value(text)
196
+ s = text.dup
197
+ s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
198
+ s.gsub!("<", '&lt;')
199
+ s.gsub!(">", '&gt;')
200
+ s.gsub!('"', '&quot;')
201
+ s.gsub!("'", '&#39;')
202
+ s
203
+ end
204
+
205
+ DefaultAllowedScheme = {
206
+ 'http' => true,
207
+ 'https' => true,
208
+ 'ftp' => true,
209
+ 'mailto' => true,
210
+ }
211
+
212
+ #UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%]|)
213
+ UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%#]|) #'
214
+
215
+ # +sanitize_url+ accepts only these characters
216
+ # --- http://www.ietf.org/rfc/rfc2396.txt ---
217
+ # uric = reserved | unreserved | escaped
218
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
219
+ # unreserved = alphanum | mark
220
+ # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
221
+ # escaped = "%" hex hex
222
+ #
223
+ # +sanitize_url+ accepts only schems specified by +allowd_scheme+
224
+ #
225
+ # The default is http: https: ftp: mailt:
226
+
227
+ def sanitize_url(text, allowd_scheme = DefaultAllowedScheme)
228
+ # return nil if text has characters not allowd for URL
229
+
230
+ return nil if text =~ UrlInvalidChar
231
+
232
+ # return '' if text has an unknown scheme
233
+ # --- http://www.ietf.org/rfc/rfc2396.txt ---
234
+ # scheme = alpha *( alpha | digit | "+" | "-" | "." )
235
+
236
+ if text =~ %r|^([A-Za-z][A-Za-z0-9+\-.]*):|
237
+ return nil unless allowd_scheme[$1]
238
+ end
239
+
240
+ # escape HTML
241
+ # special = "&" | "<" | ">" | '"' | "'"
242
+ # But I checked "<" | ">" | '"' before.
243
+ s = text.dup
244
+ #s.gsub!("&", '&amp;')
245
+ s.gsub!("'", '&#39;')
246
+
247
+ s
248
+ end
249
+
250
+ module_function :sanitize_text, :sanitize_attribute_value, :sanitize_url
251
+ end
252
+ end
253
+ include SanitizeSupport
254
+
255
+ module Runtime # :nodoc:
256
+ include SanitizeSupport
257
+ def new_element(tag, attrs={})
258
+ ret = REXML::Element.new(tag.to_s)
259
+ attrs.each do |k,v|
260
+ ret.attributes[k.to_s] = v.to_s
261
+ end
262
+ ret
263
+ end
264
+
265
+ def start_tag(e, out="")
266
+ out << "<#{e.expanded_name}"
267
+ e.attributes.each_attribute do |attr|
268
+ next if attr.value == ""
269
+ out << " "
270
+ attr.write(out)
271
+ end unless e.attributes.empty?
272
+ out << ">"
273
+ out
274
+ end
275
+
276
+ def end_tag(e, out="")
277
+ out << "</#{e.expanded_name}>"
278
+ out
279
+ end
280
+
281
+ class Context # :nodoc:
282
+ attr_reader :main_stream, :sub_streams, :mv_index
283
+ attr_accessor :current_data, :current_binding
284
+
285
+ def initialize(stream="", old_context=nil, opt={})
286
+ @sub_streams = {}
287
+ if old_context
288
+ @sub_streams = old_context.sub_streams if opt[:inherit_sub_streams]
289
+ @main_stream = (stream or old_context.main_stream)
290
+ @current_binding = (opt[:binding] or old_context.current_binding)
291
+ @current_data = (opt[:data] or old_context.current_data)
292
+ @mv_index = old_context.mv_index
293
+ else
294
+ @main_stream = (stream or "")
295
+ @current_binding = TOPLEVEL_BINDING
296
+ @current_data = nil
297
+ @mv_index = {}
298
+ end
299
+ end
300
+
301
+ def sub_stream(key)
302
+ @sub_streams[key] ||= ""
303
+ end
304
+
305
+ def clear_substream(key)
306
+ @sub_streams[key] = ""
307
+ end
308
+ end
309
+
310
+ def context_stack
311
+ Thread::current[:amrita_context_stack] ||= [ Context.new ]
312
+ end
313
+
314
+ def current_context
315
+ context_stack.first
316
+ end
317
+
318
+ def new_context(stream = "", opt = {}, &block)
319
+ context_stack.unshift Context.new(stream, context_stack[0], opt)
320
+ yield
321
+ if stream.kind_of?(String)
322
+ SanitizedString[stream]
323
+ else
324
+ stream
325
+ end
326
+ ensure
327
+ context_stack.shift
328
+ end
329
+
330
+ def new_binding(b, &block)
331
+ new_context(nil, :binding => b, :inherit_sub_streams => true, &block)
332
+ end
333
+
334
+ def new_context_data(d, &block)
335
+ new_context(nil, :data => d, :inherit_sub_streams => true, &block)
336
+ end
337
+
338
+ def get_mainstream
339
+ current_context.main_stream
340
+ end
341
+
342
+ def get_substream(key)
343
+ current_context.sub_stream(key)
344
+ end
345
+
346
+ def output_substream(key)
347
+ c = current_context
348
+ s = c.sub_stream(key)
349
+ c.main_stream << SanitizedString[s]
350
+ c.clear_substream(key)
351
+ end
352
+
353
+ def set_binding(b)
354
+ current_context.current_binding = b
355
+ end
356
+
357
+ def get_binding
358
+ current_context.current_binding
359
+ end
360
+
361
+ def set_context_data(d)
362
+ current_context.current_data = d
363
+ end
364
+
365
+ def get_context_data
366
+ current_context.current_data
367
+ end
368
+ end
369
+ include Runtime
370
+
371
+ module Core # :nodoc:
372
+ class CodeGenerator # :nodoc:
373
+ attr_accessor :current_stream, :ie_hack, :eval_cdata_as_erb
374
+
375
+ def initialize(stream = "get_mainstream")
376
+ @current_stream = stream
377
+ init_results
378
+ init_strbuf
379
+ @current_indent = 0
380
+ @varcnt=0
381
+ @const_cnt=0
382
+ @ie_hack = false
383
+ @eval_cdata_as_erb = false
384
+ @module_stack = [CGModule.new('X')]
385
+ end
386
+
387
+ def put_rexml_node(node)
388
+ s = ""
389
+ node.write(s, -1, false, @ie_hack)
390
+ put_constant(s)
391
+ end
392
+
393
+ def put_constant(msg)
394
+ @strbuf.concat msg
395
+ end
396
+
397
+ def flush
398
+ if @strbuf.size > 0
399
+ @results << [@current_indent, "#{current_stream} << " + @strbuf.inspect]
400
+ init_strbuf
401
+ end
402
+ end
403
+
404
+ def put_expression(exp)
405
+ code("#{current_stream} << #{exp}")
406
+ end
407
+
408
+ def code(l)
409
+ flush
410
+ @results << [@current_indent, l]
411
+ end
412
+
413
+ def new_var
414
+ @varcnt += 1
415
+ "v#{@varcnt}"
416
+ end
417
+
418
+ def results
419
+ flush
420
+ m = @module_stack.shift
421
+ if m
422
+ m.constants.each do |name, defs|
423
+ code("#{name} = #{defs}")
424
+ end
425
+ end
426
+ @results.collect do |level, line|
427
+ " " * level + line
428
+ end
429
+ end
430
+
431
+ def if_(cond)
432
+ code("if #{cond}")
433
+ level_up do
434
+ yield
435
+ end
436
+ code("end")
437
+ end
438
+
439
+ def else_
440
+ level_down do
441
+ code("else")
442
+ end
443
+ yield
444
+ end
445
+
446
+ def level_up
447
+ flush
448
+ @current_indent += 1
449
+ yield
450
+ ensure
451
+ flush
452
+ @current_indent -= 1
453
+ end
454
+
455
+ def level_down
456
+ flush
457
+ @current_indent -= 1
458
+ yield
459
+ ensure
460
+ flush
461
+ @current_indent += 1
462
+ end
463
+
464
+ def define_method(name, *params)
465
+ if params.size == 0
466
+ code("def self.#{name}")
467
+ else
468
+ code("def self.#{name}(#{params.join(',')})")
469
+ end
470
+ level_up do
471
+ yield
472
+ end
473
+ code("end")
474
+ end
475
+
476
+ def define_element_method(name, new_stream, &block)
477
+ define_method(name, "val=nil", "attrs={}", "&block") do
478
+ unless @eval_cdata_as_erb
479
+ code("attrs = val if val.kind_of?(Hash)")
480
+ end
481
+ new_context("get_substream(:#{new_stream})") do
482
+ yield
483
+ end
484
+ end
485
+ end
486
+
487
+ def define_element_method_old(name, new_stream, &block)
488
+ define_method(name, "val=nil", "attrs={}", "&block") do
489
+ if_("val.kind_of?(AttrArray)") do
490
+ code("attrs = val ; val = val.body ")
491
+ unless @eval_cdata_as_erb
492
+ else_ do
493
+ code("attrs = val if val.kind_of?(Hash)")
494
+ end
495
+ end
496
+ end
497
+ new_context("get_substream(:#{new_stream})") do
498
+ yield
499
+ end
500
+ end
501
+ end
502
+
503
+ def define_module(name)
504
+ @module_stack.unshift CGModule.new(name)
505
+ code("module #{name}")
506
+ level_up do
507
+ yield
508
+ m = @module_stack.shift
509
+ m.end_of_module(self)
510
+ end
511
+ code("end")
512
+ end
513
+
514
+ def define_method_and_call(*vars, &block)
515
+ m = @module_stack[0]
516
+ meth_name = m.define_method(vars, &block)
517
+ meth_call_code = "#{meth_name}(#{(vars).join(',')})"
518
+ code(meth_call_code)
519
+ meth_call_code
520
+ end
521
+
522
+ def convert(encto, encfrom, *args)
523
+ a = args.join(",")
524
+ code("#{a} = *Iconv::iconv(#{encto.inspect}, #{encfrom.inspect}, #{a})")
525
+ end
526
+
527
+ def convert_hash(encto, encfrom, hash)
528
+ code("#{hash}.each do |k, v|")
529
+ level_up do
530
+ code("v = *Iconv::iconv(#{encto.inspect}, #{encfrom.inspect}, v)")
531
+ code("#{hash}[k] = v")
532
+ end
533
+ code("end")
534
+ end
535
+
536
+ def compile_element_main_code(element, &block)
537
+ spec = element.amrita_spec
538
+ if spec
539
+ spec.compile_element_main_code(element, self, &block)
540
+ else
541
+ generate_static_element_code(element, &block)
542
+ end
543
+ end
544
+
545
+ def compile_element_sub_code(element, &block)
546
+ spec = element.amrita_spec
547
+ if spec
548
+ spec.compile_element_sub_code(element, self, &block)
549
+ else
550
+ yield
551
+ end
552
+ end
553
+
554
+ def init_results
555
+ @results = []
556
+ end
557
+
558
+ def init_strbuf
559
+ @strbuf = ""
560
+ end
561
+
562
+ def generate_static_element_code(e, option={}, &block)
563
+ if e.amrita_no_spec and not e.amrita_has_cdata?
564
+ put_rexml_node(e)
565
+ elsif option[:mv_attr]
566
+ put_constant("<#{e.name} ")
567
+ attr_to_output = e.attributes.to_a
568
+ key_attr_nm = option[:key_attr_name].to_s
569
+ if option[:delete_id]
570
+ attr_to_output.reject! do |attr|
571
+ attr.name == key_attr_nm
572
+ end
573
+ end
574
+ a = [e.name] + attr_to_output.collect do |attr|
575
+ put_constant(attr.to_string+" ")
576
+ end
577
+ option[:mv_attr].each do |k, v|
578
+ put_constant "#{k}='"
579
+ c = define_constant("Amrita2::MultiValue.new(*#{v.inspect})")
580
+ put_expression("#{c}.value")
581
+ end
582
+ put_constant("'>")
583
+ yield
584
+ put_constant("</#{e.name}>")
585
+ elsif e.name and e.name.size > 0
586
+ no_tag = option[:delete_tag]
587
+ attr_to_output = e.attributes.to_a
588
+ unless no_tag
589
+ key_attr_nm = option[:key_attr_name].to_s
590
+ if option[:delete_id]
591
+ attr_to_output.reject! do |attr|
592
+ attr.name == key_attr_nm
593
+ end
594
+ end
595
+ end
596
+ no_tag ||= (e.name == 'span' and attr_to_output.size == 0)
597
+ unless no_tag
598
+ a = [e.name] + attr_to_output.collect do |attr|
599
+ attr.to_string
600
+ end
601
+ put_constant("<#{a.join(' ')}>")
602
+ end
603
+ yield
604
+ put_constant("</#{e.name}>") unless no_tag
605
+ else
606
+ yield
607
+ end
608
+ end
609
+
610
+ def new_context(new_stream="nil", opt={}, &block)
611
+ code("new_context(#{new_stream}, #{opt.inspect}) do")
612
+ level_up do
613
+ yield
614
+ end
615
+ code("end")
616
+ end
617
+
618
+ def put_cdata(cdata)
619
+ if @eval_cdata_as_erb
620
+ src = "<% $_ = get_context_data; %>" + cdata.to_s
621
+ erb = define_constant %[ERB.new(#{src.inspect})]
622
+ put_expression %[#{erb}.result(get_binding, &block)]
623
+ else
624
+ put_rexml_node(cdata)
625
+ end
626
+ end
627
+
628
+ def define_constant(const_def)
629
+ m = @module_stack[0]
630
+ ret = m.define_constant(const_def, @const_cnt)
631
+ @const_cnt += 1
632
+ ret
633
+ end
634
+
635
+ class CGModule # :nodoc:
636
+ attr_reader :name
637
+ attr_reader :constants
638
+ attr_reader :methods
639
+
640
+ def initialize(name)
641
+ @name = name
642
+ @constants = []
643
+ @methods = []
644
+ end
645
+
646
+ def define_constant(const_def, cnt)
647
+ name = "C#{sprintf("%03d", cnt)}"
648
+ @constants << [name, const_def]
649
+ name
650
+ end
651
+
652
+ def define_method(vars, &block)
653
+ name = "m#{sprintf("%03d", @methods.size)}"
654
+ @methods << [name, vars, block]
655
+ name
656
+ end
657
+
658
+ def end_of_module(cg)
659
+ @constants.each do |name, defs|
660
+ cg.code("#{name} = #{defs}")
661
+ end
662
+ @methods.each do |name, vars, body|
663
+ cg.define_method(name, vars) do
664
+ body.call
665
+ end
666
+ end
667
+ end
668
+ end
669
+ end
670
+
671
+ class Matcher # :nodoc:
672
+ def find(e)
673
+ return e if match(e)
674
+ e.elements.each do |c|
675
+ x = find(c)
676
+ return x if x
677
+ end
678
+ nil
679
+ end
680
+ end
681
+
682
+ class AttrMatcher < Matcher # :nodoc:
683
+ attr_reader :sym
684
+ def initialize(sym, val)
685
+ @sym = sym
686
+ @sym_s = sym.to_s
687
+ @val = val
688
+ end
689
+
690
+ def match(e)
691
+ if @val != '*'
692
+ @val == e.attributes[@sym_s]
693
+ else
694
+ e.attributes[@sym_s]
695
+ end
696
+ end
697
+ end
698
+
699
+ class TagMatcher < Matcher # :nodoc:
700
+ def initialize(tag)
701
+ @tag = tag.to_s
702
+ end
703
+
704
+ def match(e)
705
+ e.name == @tag
706
+ end
707
+ end
708
+
709
+ class AnyMatcher < Matcher # :nodoc:
710
+ def match(e)
711
+ true
712
+ end
713
+ end
714
+
715
+
716
+ class SpecOptionMeta # :nodoc:
717
+ module MetaData # :nodoc:
718
+ def check_option(spec, k, v)
719
+ end
720
+ end
721
+
722
+ @@options = {}
723
+ def self.get_option_meta(name)
724
+ @@options[name]
725
+ end
726
+
727
+ def self.define_option(name, meta=nil, *args)
728
+ case meta
729
+ when nil
730
+ meta = Object.new
731
+ meta.extend MetaData
732
+ when MetaData
733
+ # do nothing
734
+ when Module
735
+ m = Object.new
736
+ m.extend meta
737
+ meta = m
738
+ end
739
+ args.each do |mod|
740
+ meta.extend mod
741
+ end
742
+ @@options[name] = meta
743
+ end
744
+
745
+ def self.check_options(spec)
746
+ spec.option.each do |k, v|
747
+ meta = @@options[k]
748
+ case meta
749
+ when nil
750
+ raise NameError, "option #{k} was not defined"
751
+ else
752
+ meta.check_option(spec, k, v)
753
+ end
754
+ end
755
+ end
756
+
757
+ def self.setup_compiler(spec, compiler)
758
+ spec.option.each do |k, v|
759
+ next unless v
760
+ meta = @@options[k]
761
+ case meta
762
+ when CompilerOption
763
+ compiler.extend meta
764
+ end
765
+ end
766
+ end
767
+
768
+ def self.inherit_option(spec, child_opt)
769
+ spec.option.each do |k, v|
770
+ meta = @@options[k]
771
+ case meta
772
+ when InheritableOption
773
+ meta.inherite_option(k, v, child_opt)
774
+ end
775
+ end
776
+ end
777
+
778
+ def self.define_element_ext(spec, name, sym, params, &block)
779
+ meta = @@options[name]
780
+ if meta
781
+ meta.define_element_ext(spec, name, sym, params || {}, &block)
782
+ end
783
+ end
784
+
785
+ module SymbolOption # :nodoc:
786
+ def check_option(spec, k, v)
787
+ case v
788
+ when Symbol, String
789
+ else
790
+ raise "option #{k} must be a Symbol"
791
+ end
792
+ end
793
+ end
794
+
795
+ module InheritableOption # :nodoc:
796
+ include MetaData
797
+ def inherite_option(k, v, child_opt)
798
+ return if child_opt.has_key?(k)
799
+ child_opt[k] = v if v
800
+ end
801
+ end
802
+
803
+ module CompilerOption # :nodoc:
804
+ include MetaData
805
+ end
806
+
807
+ module ElementExt # :nodoc:
808
+ include MetaData
809
+ def define_element_ext(spec, name, sym, params, &block)
810
+ pp = params.clone
811
+ pp[name] = true
812
+ spec.dynamic_element(sym, pp, &block)
813
+ end
814
+ end
815
+
816
+ define_option(:key_attr_name, SymbolOption, InheritableOption)
817
+ define_option(:delete_tag)
818
+ define_option(:matcher)
819
+ define_option(:output_stream, SymbolOption)
820
+ define_option(:delete_id, InheritableOption)
821
+ define_option(:place_holder)
822
+ define_option(:match_many)
823
+ define_option(:generate_template, InheritableOption)
824
+ define_option(:encoding, InheritableOption)
825
+ define_option(:dummy_element, ElementExt)
826
+ define_option(:ie_hack)
827
+ define_option(:mv_attr)
828
+ define_option(:debug_source)
829
+ end
830
+
831
+ class DynamicElementSpec # :nodoc:
832
+ attr_accessor :cloned, :parent
833
+ attr_reader :sym, :matcher, :children, :option, :assigned_elements
834
+
835
+ def initialize(sym, parent, option={}, &block)
836
+ case sym
837
+ when Symbol
838
+ @sym = sym
839
+ when nil
840
+ @sym = nil
841
+ else
842
+ @sym = sym.to_s.intern
843
+ end
844
+ @parent = parent
845
+ @option, @children = option, []
846
+
847
+ SpecOptionMeta::check_options(self)
848
+ @direct_define = @option[:direct_define]
849
+ setup_matcher
850
+ setup_compiler
851
+ if block_given?
852
+ @in_setup = true
853
+ self.instance_eval(&block)
854
+ @in_setup = false
855
+ end
856
+
857
+ check_double_id
858
+ end
859
+
860
+ def inspect(lvl=0)
861
+ spc = " " * lvl
862
+ if children.size > 0
863
+ "#{spc}dynamic_element(#{sym.inspect}, #{@option.inspect}, #{self.class.name}) do\n" +
864
+ children.collect {|c| c.inspect(lvl+1)} .join +
865
+ "#{spc}end\n"
866
+ else
867
+ "#{spc}dynamic_element(#{sym.inspect}, #{@option.inspect}, #{self.class.name})\n"
868
+ end
869
+ end
870
+
871
+ def name_for(element)
872
+ @sym
873
+ end
874
+
875
+ def dynamic_element(name, opt={}, cls=DynamicElementSpec, &block)
876
+ SpecOptionMeta::inherit_option(self, opt)
877
+ new_spec = cls.new(name, self, opt, &block)
878
+ @children << new_spec
879
+ new_spec
880
+ end
881
+
882
+
883
+ def compile_element_main_code(element, cg, &block)
884
+ return if @option[:dummy_element]
885
+ @compiler.compile_main(element, self, cg, &block)
886
+ end
887
+
888
+ def compile_element_sub_code(element, cg, &block)
889
+ return if @option[:dummy_element]
890
+ @compiler.compile_sub(element, self, cg, &block)
891
+ end
892
+
893
+ def method_missing(*args, &block)
894
+ name = nil
895
+ name = args.shift if args.first.kind_of?(Symbol)
896
+ return super unless @in_setup
897
+ if @direct_define
898
+ params = args[-1] || {}
899
+ dynamic_element(name, params, &block)
900
+ else
901
+ sym = nil
902
+ sym = args.shift if args.first.kind_of?(Symbol)
903
+ SpecOptionMeta::define_element_ext(self, name , sym, args.first, &block) or
904
+ super
905
+ end
906
+ end
907
+
908
+ def module_name
909
+ sym.to_s.capitalize
910
+ end
911
+
912
+ def output_stream
913
+ @option[:output_stream] or sym
914
+ end
915
+
916
+ def get_methods
917
+ h = {}
918
+ ret = children.find_all do |c|
919
+ ret = h[c.sym]
920
+ h[c.sym] = true
921
+ ret != true and c.sym
922
+ end.collect {|c| c.sym }
923
+ if ret.size == 0
924
+ children.each do |c|
925
+ ret.concat c.get_methods
926
+ end
927
+ end
928
+ ret
929
+ end
930
+
931
+ def make_default_template
932
+ tag = @option[:html_tag]
933
+ tag = 'div' if @option[:div]
934
+ unless sym or tag
935
+ ret = children.collect do |c|
936
+ c.make_default_template
937
+ end.join('')
938
+ return ret
939
+ end
940
+
941
+ attr = @option[:html_attr]
942
+ a = nil
943
+ if attr
944
+ a = attr.collect do |k,v|
945
+ "#{k}='#{v}'"
946
+ end.join(' ')
947
+ end
948
+ if @option[:static_data]
949
+ tag_and_attr = tag.to_s
950
+ tag_and_attr += ' ' + a if attr and attr.size > 0
951
+ "<#{tag_and_attr}>#{@option[:static_data]}</#{tag}>"
952
+ else
953
+ tag ||= 'span'
954
+ tag_and_attr = tag.to_s
955
+ tag_and_attr += ' ' + a if attr and attr.size > 0
956
+ if sym or tag
957
+ idparam = ""
958
+ idparam = " #{@option[:key_attr_name] }='#{sym}'" if sym
959
+ tag = 'span' unless tag
960
+ if children.size > 0
961
+ "<#{tag_and_attr}#{idparam}>" +
962
+ children.collect do |c|
963
+ c.make_default_template
964
+ end.join('') + "</#{tag}>"
965
+ else
966
+ "<#{tag_and_attr} #{idparam} />"
967
+ end
968
+ else
969
+ end
970
+ end
971
+ end
972
+
973
+ def generate_expander(mod, parent=nil)
974
+ @compiler.generate_expander(self, mod, parent)
975
+ end
976
+
977
+ def get_dynamic_specs
978
+ if sym
979
+ [self]
980
+ else
981
+ children.collect do |c|
982
+ c.get_dynamic_specs
983
+ end.flatten
984
+ end
985
+ end
986
+
987
+ def check_double_id
988
+ h = {}
989
+ children.each do |c|
990
+ next unless c.sym
991
+ raise "dobule ID #{c.sym} in #{sym || 'ROOT'}" if h[c.sym] and not c.option[:dummy_element]
992
+ h[c.sym] = true
993
+ end
994
+ end
995
+
996
+ private
997
+ def setup_matcher
998
+ if sym
999
+ @matcher = @option[:matcher] || AttrMatcher.new(@option[:key_attr_name], sym.to_s)
1000
+ else
1001
+ @matcher = AnyMatcher.new
1002
+ end
1003
+ end
1004
+
1005
+ def setup_compiler
1006
+ if @option[:compiler]
1007
+ @compiler = @option[:compiler]
1008
+ else
1009
+ @compiler = DefaultCompiler.new
1010
+ SpecOptionMeta::setup_compiler(self, @compiler)
1011
+ end
1012
+ end
1013
+
1014
+ end
1015
+
1016
+ class RootSpec < DynamicElementSpec # :nodoc:
1017
+ def initialize(option={}, &block)
1018
+ option[:key_attr_name] = :id unless option[:key_attr_name]
1019
+ super(nil, nil, option, &block)
1020
+ end
1021
+
1022
+ def module_name
1023
+ "MainContext"
1024
+ end
1025
+
1026
+ def create_template_module(e, doc=nil)
1027
+ m = Module.new
1028
+ result = create_template_module_text(e)
1029
+ @option[:debug_source].puts result if @option[:debug_source]
1030
+ m.module_eval result
1031
+ if doc
1032
+ m.const_set(:XmlDecl, doc.xml_decl.to_s)
1033
+ m.const_set(:DocType, doc.doctype.to_s)
1034
+ end
1035
+ m
1036
+ end
1037
+
1038
+ def create_template_module_text(e)
1039
+ specs = [self]
1040
+
1041
+ traverse_and_search_match(specs, e)
1042
+ # not match check
1043
+ raise "SPEC #{specs.first.sym} was not matched" if specs.size > 0
1044
+
1045
+ cg = CodeGenerator.new
1046
+ cg.ie_hack = option[:ie_hack]
1047
+ cg.eval_cdata_as_erb = option[:eval_cdata_as_erb]
1048
+ cg.code("include Amrita2")
1049
+ cg.code("extend Amrita2")
1050
+
1051
+ cg.define_method(:expand_template, :out, "&block") do
1052
+ define_expand_template(cg, e)
1053
+ end
1054
+
1055
+ cg.define_module(module_name) do
1056
+ cg.code("Methods = #{get_methods.inspect}")
1057
+ cg.code("include Amrita2")
1058
+ cg.code("extend Amrita2")
1059
+ define_sub_modules(cg, e)
1060
+ end
1061
+
1062
+ cg.define_method(:xml_decl) do
1063
+ cg.code("XmlDecl.dup")
1064
+ end
1065
+
1066
+ cg.define_method(:doctype) do
1067
+ cg.code("DocType.dup")
1068
+ end
1069
+
1070
+ cg.results.join("\n")
1071
+ end
1072
+
1073
+ def define_expand_template(cg, e)
1074
+ cg.code("new_context(out) do")
1075
+ cg.level_up do
1076
+ cg.code("block.call(#{module_name or 'nil'}) if block_given? ")
1077
+ e.amrita_compile_main_code(cg)
1078
+ end
1079
+ cg.code("current_context.mv_index.clear")
1080
+ cg.code("end")
1081
+ end
1082
+
1083
+ def define_sub_modules(cg, e)
1084
+ e.amrita_compile_sub_code(cg)
1085
+ end
1086
+
1087
+ def traverse_and_search_match(child_specs, element)
1088
+ spec = child_specs.first
1089
+ unless spec
1090
+ element.amrita_no_spec = true unless element.amrita_spec
1091
+ return false
1092
+ end
1093
+ if spec.matcher.match(element)
1094
+ element.amrita_no_spec = false
1095
+ element.amrita_spec = spec if spec.sym
1096
+ child_specs.shift unless spec.option[:match_many]
1097
+ specs = spec.children.collect do |s|
1098
+ s.get_dynamic_specs
1099
+ end.flatten
1100
+ if spec.option[:generate_template] and specs.size > 0 and element.elements.size == 0
1101
+ generate_template(spec, element)
1102
+ end
1103
+ traverse_and_search_match(specs, element)
1104
+
1105
+ # not match check
1106
+ specs.each do |s|
1107
+ next if s.option[:match_many]
1108
+ next if s.option[:static_data]
1109
+ raise "SPEC #{s.sym} was not matched"
1110
+ end
1111
+ else
1112
+ # check children has spec and set it to amrita_no_spec and return the result
1113
+ f = false
1114
+ element.elements.each do |ce|
1115
+ f |= traverse_and_search_match(child_specs, ce)
1116
+ end
1117
+ element.amrita_no_spec = (not f)
1118
+ f
1119
+ end
1120
+ end
1121
+
1122
+ def generate_template(spec, element)
1123
+ src = spec.make_default_template
1124
+ doc = REXML::Document.new src
1125
+ doc.root.elements.each do |c|
1126
+ element.add(c)
1127
+ end
1128
+ end
1129
+
1130
+ def generate_expander(mod, parent=nil)
1131
+ Expander::Root.new(self, mod)
1132
+ end
1133
+ end
1134
+
1135
+ class DefaultCompiler # :nodoc:
1136
+ def compile_main(element, dyn_spec, cg, &block)
1137
+ if dyn_spec.option[:place_holder]
1138
+ cg.generate_static_element_code(element) do
1139
+ end
1140
+ elsif dyn_spec.option[:static_element]
1141
+ cg.generate_static_element_code(element) do
1142
+ element.each_child do |c|
1143
+ c.amrita_compile_main_code(cg)
1144
+ end
1145
+ end
1146
+ else
1147
+ sym = element.amrita_spec.sym
1148
+ compile_main_code(element, dyn_spec, cg, sym.to_s, &block)
1149
+ end
1150
+ end
1151
+
1152
+ def compile_sub(element, dyn_spec, cg, &block)
1153
+ if dyn_spec.option[:static_element]
1154
+ element.elements.each do |c|
1155
+ c.compile_sub_code(cg)
1156
+ end
1157
+ else
1158
+ compile_dynamic_element_code(element, dyn_spec, cg, &block)
1159
+ end
1160
+ end
1161
+
1162
+ def generate_expander(spec, mod, parent=nil)
1163
+ if spec.children.size > 0
1164
+ Expander::Node.new(parent, spec, mod)
1165
+ else
1166
+ Expander::Leaf.new(parent, spec, mod)
1167
+ end
1168
+ end
1169
+
1170
+ private
1171
+ def compile_main_code(element, dyn_spec, cg, sym)
1172
+ cg.code("output_substream(:#{sym})")
1173
+ end
1174
+
1175
+ def compile_dynamic_element_code(element, dyn_spec, cg, &block)
1176
+ sym = dyn_spec.sym
1177
+ if dyn_spec.children.size == 0
1178
+ if dyn_spec.option[:eval_cdata_as_erb] and element.amrita_has_cdata?
1179
+ code_for_not_leaf_element(element, dyn_spec, cg, &block)
1180
+ else
1181
+ code_for_leaf_element(element, dyn_spec, cg, &block)
1182
+ end
1183
+ else
1184
+ code_for_not_leaf_element(element, dyn_spec, cg, &block)
1185
+ end
1186
+ end
1187
+
1188
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1189
+ sym = dyn_spec.sym
1190
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1191
+ enc = dyn_spec.option[:encoding]
1192
+ if enc
1193
+ cg.convert("utf-8", enc, "sym", "val")
1194
+ cg.convert_hash("utf-8", enc, "attrs")
1195
+ end
1196
+ code_for_element_and_scalar(element, dyn_spec, cg)
1197
+ end
1198
+ end
1199
+
1200
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1201
+ sym = dyn_spec.name_for(element)
1202
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1203
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1204
+ end
1205
+ cg.define_module(dyn_spec.module_name) do
1206
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1207
+ cg.code("include Amrita2")
1208
+ cg.code("extend Amrita2")
1209
+ yield
1210
+ end
1211
+ end
1212
+
1213
+ def code_for_element_and_scalar(element, dyn_spec, cg)
1214
+ cg.if_("val == true") do
1215
+ t = element.enum_for(:each_child).collect do |c|
1216
+ c.to_s
1217
+ end.join
1218
+ cc = cg.define_constant("SanitizedString[#{t.inspect}]")
1219
+ cg.code("val = #{cc}")
1220
+ end
1221
+ cg.if_("attrs.size == 0") do
1222
+ cg.generate_static_element_code(element, dyn_spec.option) do
1223
+ cg.put_expression("val.amrita_sanitize")
1224
+ end
1225
+ cg.else_ do
1226
+ gen_element_with_attr(element, dyn_spec, cg)
1227
+ cg.code("e.delete_attribute(#{dyn_spec.option[:key_attr_name].to_s.inspect})") if dyn_spec.option[:delete_id]
1228
+ cg.put_expression("start_tag(e)")
1229
+ cg.put_expression("val.amrita_sanitize")
1230
+ cg.put_expression("end_tag(e)")
1231
+ end
1232
+ end
1233
+ end
1234
+
1235
+ def code_for_element_with_child(element, dyn_spec, cg)
1236
+ cg.if_("val == true") do
1237
+ cg.if_("attrs.size == 0") do
1238
+ cg.put_constant(element.to_s)
1239
+ cg.else_ do
1240
+ gen_element_with_attr(element, dyn_spec, cg)
1241
+ cg.put_expression("start_tag(e)")
1242
+ element.each_child do |c|
1243
+ cg.put_constant(c.to_s)
1244
+ end
1245
+ cg.put_expression("end_tag(e)")
1246
+ end
1247
+ end
1248
+ cg.else_ do
1249
+ cg.if_("attrs.size == 0") do
1250
+ sub_method = nil
1251
+ cg.generate_static_element_code(element, dyn_spec.option) do
1252
+ sub_method = cg.define_method_and_call(:val, :attrs, "&block") do
1253
+ yield_block(cg, dyn_spec)
1254
+ element.each_child do |e|
1255
+ e.amrita_compile_main_code(cg)
1256
+ end
1257
+ end
1258
+ end
1259
+ cg.else_ do
1260
+ gen_element_with_attr(element, dyn_spec, cg)
1261
+ cg.put_expression("start_tag(e)")
1262
+ cg.code(sub_method)
1263
+ cg.put_expression("end_tag(e)")
1264
+ end
1265
+ end
1266
+ end
1267
+ end
1268
+ end
1269
+
1270
+ def gen_element_with_attr(element,dyn_spec, cg, varname='e')
1271
+ a = element.attributes.collect do |k, v|
1272
+ next if dyn_spec.option[:delete_id] and k == dyn_spec.option[:key_attr_name].to_s
1273
+ "#{k.intern.inspect} => #{v.inspect}, "
1274
+ end
1275
+ default_value = "{ #{a.join(' ')}}"
1276
+ cg.code("#{varname} = new_element(#{element.name.inspect}, #{default_value}.merge(attrs))")
1277
+ end
1278
+
1279
+ def yield_block(cg, dyn_spec)
1280
+ cg.code("yield(#{dyn_spec.module_name}) if block_given?")
1281
+ end
1282
+ end
1283
+ end
1284
+
1285
+ def self.define_template_spec(option={}, &block)
1286
+ Core::RootSpec.new(option, &block)
1287
+ end
1288
+
1289
+
1290
+ module ModelData # :nodoc:
1291
+ class AttrArray < Hash
1292
+ attr_reader :body
1293
+ def initialize(h, &block)
1294
+ h.each do |k, v|
1295
+ self[k] = v
1296
+ end
1297
+ if block_given?
1298
+ @body = yield
1299
+ else
1300
+ @body = true
1301
+ end
1302
+ end
1303
+ end
1304
+
1305
+ class Tuple < Array # :nodoc:
1306
+ def self.[](*args)
1307
+ self.new(args)
1308
+ end
1309
+ end
1310
+
1311
+ class MultiValue # :nodoc:
1312
+ include Runtime
1313
+ def initialize(*v)
1314
+ @values = v
1315
+ end
1316
+
1317
+ def value
1318
+ current_context.mv_index[object_id] ||= 0
1319
+ cnt = current_context.mv_index[object_id]
1320
+ v = @values[cnt]
1321
+ current_context.mv_index[object_id] = ((cnt+1) % @values.size)
1322
+ v
1323
+ end
1324
+ end
1325
+
1326
+ def e(tag, hash={})
1327
+ ret = REXML::Element.new(tag.to_s)
1328
+ hash.each do |k, v|
1329
+ ret.attributes[k.to_s] = v
1330
+ end
1331
+ if block_given?
1332
+ case child = yield
1333
+ when REXML::Element
1334
+ ret.add child
1335
+ else
1336
+ ret.text = child
1337
+ end
1338
+ end
1339
+ ret
1340
+ end
1341
+
1342
+ def a(hash={}, &block)
1343
+ AttrArray.new(hash, &block)
1344
+ end
1345
+
1346
+ def t(*args)
1347
+ Tuple[*args]
1348
+ end
1349
+ end
1350
+ include ModelData
1351
+
1352
+ module Expander # :nodoc:
1353
+ include ModelData
1354
+ class Base # :nodoc:
1355
+ include Runtime
1356
+ attr_reader :children, :name, :mod, :spec, :parent
1357
+ def initialize(parent, spec, pmod)
1358
+ @parent = parent
1359
+ @spec = spec
1360
+ @children = []
1361
+ @name = @spec.sym
1362
+ @method = pmod.method(@spec.sym || :expand_template) unless spec.option[:dummy_element]
1363
+ end
1364
+
1365
+ def call_template_method(data=nil, context_data=data, attrs=nil, &block)
1366
+ args = [data]
1367
+ args << attrs if attrs
1368
+ if @spec.option[:eval_cdata_as_erb] and context_data
1369
+ new_context_data(context_data) do
1370
+ @method.call(*args, &block)
1371
+ end
1372
+ else
1373
+ @method.call(*args, &block)
1374
+ end
1375
+ end
1376
+ end
1377
+
1378
+ class Leaf < Base # :nodoc:
1379
+ include Amrita2
1380
+ def expand(data, i=nil)
1381
+ case data
1382
+ when nil
1383
+ when String, Integer, true, false, Struct
1384
+ call_template_method(data)
1385
+ when REXML::Element
1386
+ if data.name
1387
+ call_template_method(SanitizedString[data.to_s])
1388
+ else
1389
+ call_template_method(data.text, data, data.attributes)
1390
+ end
1391
+ when AttrArray
1392
+ call_template_method(data.body, data, data)
1393
+ when Hash
1394
+ call_template_method(data)
1395
+ when Tuple
1396
+ @method.call(*data)
1397
+ when Enumerable
1398
+ data.each_with_index do |d, i|
1399
+ expand(d, i)
1400
+ end
1401
+ when Proc
1402
+ data.call(@parent.mod)
1403
+ else
1404
+ call_template_method(data)
1405
+ end
1406
+ end
1407
+ end
1408
+
1409
+ class Node < Base # :nodoc:
1410
+ include Amrita2
1411
+ def initialize(parent, spec, pmod)
1412
+ super
1413
+ @mod = pmod::const_get(spec.module_name)
1414
+ traverse_children(spec)
1415
+ end
1416
+
1417
+ def traverse_children(spec)
1418
+ spec.children.each do |c|
1419
+ if c.sym and not c.option[:dummy_element]
1420
+ @children << c.generate_expander(@mod, self)
1421
+ else
1422
+ traverse_children(c)
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ def each(&block)
1428
+ @children.each do |c|
1429
+ next unless c.spec.sym
1430
+ next if c.spec.option[:dummy_element]
1431
+ yield(c)
1432
+ end
1433
+ end
1434
+
1435
+ def expand(data)
1436
+ case data
1437
+ when nil
1438
+ when Proc
1439
+ data.call(@mod)
1440
+ when AttrArray
1441
+ call_template_method(data) do
1442
+ data = data.body
1443
+ case data
1444
+ when Hash
1445
+ each do |c|
1446
+ k = c.name
1447
+ d = data[k] || data[k.to_s]
1448
+ c.expand(d)
1449
+ end
1450
+ when Struct, Enumerable
1451
+ raise 'not implemented'
1452
+ else
1453
+ expand_po(data)
1454
+ end
1455
+ end
1456
+ when Hash
1457
+ call_template_method(nil, data) do
1458
+ each do |c|
1459
+ k = c.name
1460
+ d = data[k] || data[k.to_s]
1461
+ c.expand(d)
1462
+ end
1463
+ end
1464
+ when Struct
1465
+ call_template_method(nil, data) do
1466
+ expand_po(data)
1467
+ end
1468
+ when String
1469
+ raise "can not expand s String(#{data})"
1470
+ when Tuple, true
1471
+ call_template_method do
1472
+ each do |c|
1473
+ c.expand(data)
1474
+ end
1475
+ end
1476
+ when Enumerable
1477
+ data.each do |d|
1478
+ expand(d)
1479
+ end
1480
+ else
1481
+ call_template_method do
1482
+ expand_po(data)
1483
+ end
1484
+ end
1485
+ end
1486
+
1487
+ def expand_po(data)
1488
+ if data.kind_of?(Binding)
1489
+ new_binding(data) do
1490
+ each do |c|
1491
+ c.expand(eval(c.name.to_s, data))
1492
+ end
1493
+ end
1494
+ else
1495
+ each do |c|
1496
+ mid = c.name
1497
+ begin
1498
+ meth = data.method(mid)
1499
+ case meth.arity
1500
+ when 0
1501
+ c.expand(meth.call)
1502
+ else
1503
+ meth.call(@mod)
1504
+ end
1505
+ rescue NameError
1506
+ c.expand(data.send(mid))
1507
+ end
1508
+ end
1509
+ end
1510
+ end
1511
+ end
1512
+
1513
+ class Root < Node # :nodoc:
1514
+ def initialize(spec, pmod)
1515
+ super(nil, spec, pmod)
1516
+ end
1517
+
1518
+ def expand_template(out,data)
1519
+ new_context_data(data) do
1520
+ case data
1521
+ when Proc
1522
+ data.call(@mod)
1523
+ when Hash
1524
+ @method.call(out) do
1525
+ @children.each do |c|
1526
+ k = c.name
1527
+ d = (data[k] || data[k.to_s])
1528
+ c.expand(d)
1529
+ end
1530
+ end
1531
+ else
1532
+ @method.call(out) do
1533
+ expand_po(data)
1534
+ end
1535
+ end
1536
+ end
1537
+ end
1538
+ end
1539
+ end
1540
+
1541
+ module Extention # :nodoc:
1542
+
1543
+ module UseOriginalElement # :nodoc:
1544
+ extend Core::SpecOptionMeta::CompilerOption
1545
+ include Amrita2::Runtime
1546
+
1547
+ def self.check_option(spec, k, v)
1548
+ end
1549
+
1550
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1551
+ sym = dyn_spec.sym
1552
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1553
+ cg.if_("block_given?") do
1554
+ gen_element_with_attr(element, dyn_spec, cg)
1555
+ cg.code("e.text = #{element.text.inspect}")
1556
+ cg.code("get_mainstream << yield(e).to_s")
1557
+ cg.else_ do
1558
+ code_for_element_and_scalar(element, dyn_spec, cg)
1559
+ end
1560
+ end
1561
+ end
1562
+ end
1563
+
1564
+ def code_for_element_with_child(element, dyn_spec, cg)
1565
+ cg.code("yield(#{dyn_spec.module_name})")
1566
+ cg.if_("get_substream(:#{dyn_spec.output_stream}).size == 0") do
1567
+ cg.generate_static_element_code(element, dyn_spec.option) do
1568
+ element.each_child do |e|
1569
+ e.amrita_compile_main_code(cg)
1570
+ end
1571
+ end
1572
+ cg.else_ do
1573
+ cg.code("output_substream(:#{dyn_spec.name_for(element)})")
1574
+ element.each_child do |e|
1575
+ e.amrita_compile_main_code(cg)
1576
+ end
1577
+ cg.put_constant(end_tag(element))
1578
+ end
1579
+ end
1580
+ end
1581
+
1582
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1583
+ sym = dyn_spec.name_for(element)
1584
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1585
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1586
+ end
1587
+
1588
+ cg.define_module(dyn_spec.module_name) do
1589
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1590
+ cg.code("include Amrita2")
1591
+ cg.code("extend Amrita2")
1592
+ cg.define_method(sym, "val=nil", "attrs={}") do
1593
+ cg.new_context("get_substream(:#{sym})") do
1594
+ cg.code("e = yield(new_element(#{element.name.inspect}))")
1595
+ cg.code("get_mainstream << start_tag(e)")
1596
+ end
1597
+ yield
1598
+ end
1599
+ #cg.code("module_function :#{sym}")
1600
+ end
1601
+ end
1602
+
1603
+ Core::SpecOptionMeta::define_option(:use_original_element, self)
1604
+ public
1605
+ class Expander < Expander::Leaf # :nodoc:
1606
+ def expand(data, i=nil)
1607
+ case data
1608
+ when Proc
1609
+ @method.call do |e|
1610
+ data.call(e)
1611
+ end
1612
+ else
1613
+ super
1614
+ end
1615
+ end
1616
+ end
1617
+ public
1618
+ def generate_expander(spec, mod, parent=nil)
1619
+ Expander.new(parent, spec, mod)
1620
+ end
1621
+ end
1622
+
1623
+ module ExpandByMember # :nodoc:
1624
+ extend Core::SpecOptionMeta::CompilerOption
1625
+ def self.check_option(spec, k, v)
1626
+ end
1627
+
1628
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1629
+ sym = dyn_spec.name_for(element)
1630
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1631
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1632
+ end
1633
+ cg.define_module(dyn_spec.module_name) do
1634
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1635
+ cg.code("include Amrita2")
1636
+ cg.code("extend Amrita2")
1637
+ yield
1638
+ end
1639
+ end
1640
+
1641
+ def code_for_element_with_child(element, dyn_spec, cg)
1642
+ cg.generate_static_element_code(element, dyn_spec.option) do
1643
+ cg.if_("block_given?") do
1644
+ cg.code("yield(#{dyn_spec.module_name})")
1645
+ cg.else_ do
1646
+ dyn_spec.children.each do |c|
1647
+ cg.code("#{dyn_spec.module_name}::#{c.sym}(val.#{c.sym})")
1648
+ end
1649
+ end
1650
+ end
1651
+ element.each_child do |e|
1652
+ e.amrita_compile_main_code(cg)
1653
+ end
1654
+ end
1655
+ end
1656
+ Core::SpecOptionMeta::define_option(:expand_by_member, self)
1657
+
1658
+ class Expander < Expander::Node # :nodoc:
1659
+ def expand(data, i=0)
1660
+ if data.kind_of?(Enumerable)
1661
+ data.each_with_index do |d, i|
1662
+ expand(d, i)
1663
+ end
1664
+ else
1665
+ @method.call(i) do
1666
+ each do |c|
1667
+ dd = data.send(c.name)
1668
+ c.expand(dd)
1669
+ end
1670
+ end
1671
+ end
1672
+ end
1673
+ end
1674
+
1675
+ public
1676
+ def generate_expander(spec, mod, parent=nil)
1677
+ Expander.new(parent, spec, mod)
1678
+ end
1679
+ end
1680
+
1681
+ module EvalCDataAsErb # :nodoc:
1682
+ include Core::SpecOptionMeta::InheritableOption
1683
+ extend Core::SpecOptionMeta::CompilerOption
1684
+ def self.check_option(spec, k, v)
1685
+ end
1686
+ def code_for_not_leaf_element(element, dyn_spec, cg, &block)
1687
+ sym = dyn_spec.name_for(element)
1688
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1689
+ cg.code('new_context_data(val) do')
1690
+ cg.level_up do
1691
+ cg.eval_cdata_as_erb = dyn_spec.option[:eval_cdata_as_erb]
1692
+ code_for_element_with_child(element, dyn_spec, cg, &block)
1693
+ end
1694
+ cg.code('end')
1695
+ end
1696
+ cg.define_module(dyn_spec.module_name) do
1697
+ cg.code("Methods = #{dyn_spec.get_methods.inspect}")
1698
+ cg.code("include Amrita2")
1699
+ cg.code("extend Amrita2")
1700
+ yield
1701
+ end
1702
+ end
1703
+ Core::SpecOptionMeta::define_option(:eval_cdata_as_erb, self)
1704
+ end
1705
+
1706
+ module UseArgs # :nodoc:
1707
+ extend Core::SpecOptionMeta::CompilerOption
1708
+ def self.check_option(spec, k, v)
1709
+ end
1710
+
1711
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1712
+ cg.code('include Amrita2::Runtime')
1713
+ sym = dyn_spec.sym
1714
+ cg.define_method(sym, "*args", "&block") do
1715
+ cg.new_context("get_substream(:#{dyn_spec.output_stream})") do
1716
+ complie_for_args(element, cg, dyn_spec.option[:delete_id])
1717
+ end
1718
+ end
1719
+ #cg.code("module_function :#{sym}")
1720
+ end
1721
+
1722
+ private
1723
+
1724
+ def complie_for_args(element, cg, delete_id)
1725
+ replace_args(element)
1726
+ #cg.code("get_mainstream << \"#{element.to_s}\"")
1727
+ s = ""
1728
+ element.write(s, -1, false, cg.ie_hack)
1729
+ cg.code("get_mainstream << \"#{s}\"")
1730
+ end
1731
+
1732
+ def replace_args(element)
1733
+ element.delete_attribute('id')
1734
+ element.attributes.each do|k,v|
1735
+ replace_attr_args(v)
1736
+ end
1737
+ element.each_child do |c|
1738
+ case c
1739
+ when REXML::Text
1740
+ c.value = replace_text_args(c.value) if c.value
1741
+ when REXML::Element
1742
+ replace_args(c)
1743
+ end
1744
+ end
1745
+ end
1746
+
1747
+ def replace_attr_args(s)
1748
+ s.gsub!(/\$(\d)/) do |ss|
1749
+ case $1
1750
+ when "0"
1751
+ '#{block.call.amrita_sanitize_as_attribute if block}'
1752
+ else
1753
+ '#{args[' + ($1.to_i-1).to_s + '].amrita_sanitize_as_attribute}'
1754
+ end
1755
+ end
1756
+ end
1757
+
1758
+ def replace_text_args(s)
1759
+ s.gsub(/\$(\d)/) do |ss|
1760
+ case $1
1761
+ when "0"
1762
+ '#{block.call.amrita_sanitize if block}'
1763
+ else
1764
+ '#{args[' + ($1.to_i-1).to_s + '].amrita_sanitize}'
1765
+ end
1766
+ end
1767
+ end
1768
+
1769
+ Core::SpecOptionMeta::define_option(:use_args, self)
1770
+ public
1771
+ end
1772
+
1773
+ module EnumerateChildren # :nodoc:
1774
+ extend Core::SpecOptionMeta::CompilerOption
1775
+ def self.check_option(spec, k, v)
1776
+ end
1777
+
1778
+ def compile_main(element, dyn_spec, cg, &block)
1779
+ cg.code('#compile_main')
1780
+ sym = element.amrita_spec.sym
1781
+ cg.generate_static_element_code(element, dyn_spec.option) do
1782
+ compile_main_code(element, dyn_spec, cg, sym.to_s, &block)
1783
+ end
1784
+ end
1785
+
1786
+ def code_for_element_and_scalar(element, dyn_spec, cg)
1787
+ cg.put_expression("val.amrita_sanitize")
1788
+ end
1789
+
1790
+ def code_for_element_with_child(element, dyn_spec, cg)
1791
+ yield_block(cg, dyn_spec)
1792
+ element.each_child do |e|
1793
+ e.amrita_compile_main_code(cg)
1794
+ end
1795
+ end
1796
+
1797
+ Core::SpecOptionMeta::define_option(:enumerate_chidren, self)
1798
+ end
1799
+
1800
+
1801
+ module ValueAttr # :nodoc:
1802
+ extend Core::SpecOptionMeta::CompilerOption
1803
+ def self.check_option(spec, k, v)
1804
+ end
1805
+
1806
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1807
+ cg.code('include Amrita2::Runtime')
1808
+ sym = dyn_spec.sym
1809
+ cg.define_element_method(sym, dyn_spec.output_stream) do
1810
+ complie_for_single(element, dyn_spec, cg, dyn_spec.option[:value_attr])
1811
+ end
1812
+ end
1813
+
1814
+ def generate_expander(spec, mod, parent=nil)
1815
+ ret = super
1816
+ def ret.expand(data)
1817
+ case data
1818
+ when false, nil, "false"
1819
+ @method.call(nil)
1820
+ else
1821
+ super
1822
+ end
1823
+ end
1824
+ ret
1825
+ end
1826
+
1827
+ private
1828
+
1829
+ def complie_for_single(element, dyn_spec, cg, value_attr)
1830
+ gen_element_with_attr(element, dyn_spec, cg)
1831
+ cg.code("e.attributes['#{value_attr}'] = val")
1832
+ cg.code("e.text = #{element.text.inspect}") if element.text
1833
+ cg.code("get_mainstream << e.to_s")
1834
+ end
1835
+
1836
+ Core::SpecOptionMeta::define_option(:value_attr, self)
1837
+ end
1838
+
1839
+ module LinkAttr # :nodoc:
1840
+ extend Core::SpecOptionMeta::CompilerOption
1841
+ def self.check_option(spec, k, v)
1842
+ end
1843
+
1844
+ def code_for_leaf_element(element, dyn_spec, cg, &block)
1845
+ cg.code('include Amrita2::Runtime')
1846
+ sym = dyn_spec.sym
1847
+ cg.define_method(sym, "href", "val=nil", "attrs={}") do
1848
+ cg.if_("val.kind_of?(Hash)") do
1849
+ cg.code("attrs = val")
1850
+ cg.code("val = nil")
1851
+ end
1852
+ cg.new_context("get_substream(:#{dyn_spec.output_stream})") do
1853
+ complie_for_link(element, dyn_spec, cg, dyn_spec.option[:link_attr])
1854
+ end
1855
+ end
1856
+ end
1857
+
1858
+ private
1859
+
1860
+ def complie_for_link(element, dyn_spec, cg, link_attr)
1861
+ href, checks = *link_attr
1862
+ gen_element_with_attr(element, dyn_spec, cg)
1863
+ checks.each do |r|
1864
+ cg.code("href = '' if #{r.inspect} =~ href")
1865
+ end
1866
+ cg.code("e.attributes['#{href}'] = href if href")
1867
+ cg.code("e.delete_attribute('#{dyn_spec.option[:key_attr_name]}')") if dyn_spec.option[:delete_id]
1868
+ cg.code("e.text = (val || #{element.text.inspect}) ")
1869
+ cg.code("get_mainstream << e.to_s")
1870
+ end
1871
+
1872
+ Core::SpecOptionMeta::define_option(:link_attr, self)
1873
+ class LinkExpander < Expander::Leaf
1874
+ def expand(data, i=nil)
1875
+ case data
1876
+ when Array
1877
+ case data.first
1878
+ when Array, AttrArray, Hash
1879
+ data.each do |d|
1880
+ expand(d)
1881
+ end
1882
+ else
1883
+ @method.call(*data)
1884
+ end
1885
+ else
1886
+ super
1887
+ end
1888
+ end
1889
+ end
1890
+
1891
+ public
1892
+ def generate_expander(spec, mod, parent=nil)
1893
+ LinkExpander.new(parent, spec, mod)
1894
+ end
1895
+ end
1896
+ end
1897
+ end