line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Jemplate::Directive; |
2
|
22
|
|
|
22
|
|
97
|
use strict; |
|
22
|
|
|
|
|
58
|
|
|
22
|
|
|
|
|
814
|
|
3
|
22
|
|
|
22
|
|
100
|
use warnings; |
|
22
|
|
|
|
|
35
|
|
|
22
|
|
|
|
|
45709
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $OUTPUT = 'output +='; |
6
|
|
|
|
|
|
|
our $WHILE_MAX = 1000; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# parser state variable |
9
|
|
|
|
|
|
|
# only true when inside JAVASCRIPT blocks |
10
|
|
|
|
|
|
|
our $INJAVASCRIPT = 0; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub template { |
13
|
0
|
|
|
0
|
0
|
|
my ($class, $block) = @_; |
14
|
|
|
|
|
|
|
|
15
|
0
|
0
|
|
|
|
|
return "function() { return ''; }" unless $block =~ /\S/; |
16
|
|
|
|
|
|
|
|
17
|
0
|
|
|
|
|
|
return <<"..."; |
18
|
|
|
|
|
|
|
function(context) { |
19
|
|
|
|
|
|
|
if (! context) throw('Jemplate function called without context\\n'); |
20
|
|
|
|
|
|
|
var stash = context.stash; |
21
|
|
|
|
|
|
|
var output = ''; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
try { |
24
|
|
|
|
|
|
|
$block |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
catch(e) { |
27
|
|
|
|
|
|
|
var error = context.set_error(e, output); |
28
|
|
|
|
|
|
|
throw(error); |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
return output; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
... |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Try to do 1 .. 10 expansions |
37
|
|
|
|
|
|
|
sub _attempt_range_expand_val ($) { |
38
|
0
|
|
|
0
|
|
|
my $val = shift; |
39
|
0
|
0
|
|
|
|
|
return $val unless |
40
|
|
|
|
|
|
|
my ( $from, $to ) = $val =~ m/\s*\[\s*(\S+)\s*\.\.\s*(\S+)\s*\]/; |
41
|
|
|
|
|
|
|
|
42
|
0
|
0
|
0
|
|
|
|
die "Range expansion is current supported for positive/negative integer values only (e.g. [ 1 .. 10 ])\nCannot expand: $val" unless $from =~ m/^-?\d+$/ && $to =~ m/^-?\d+$/; |
43
|
|
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
return join '', '[', join( ',', $from .. $to ), ']'; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
48
|
|
|
|
|
|
|
# textblock($text) |
49
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub textblock { |
52
|
0
|
|
|
0
|
0
|
|
my ($class, $text) = @_; |
53
|
0
|
0
|
|
|
|
|
return $text if $INJAVASCRIPT; |
54
|
0
|
|
|
|
|
|
return "$OUTPUT " . $class->text($text) . ';'; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
58
|
|
|
|
|
|
|
# text($text) |
59
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub text { |
62
|
0
|
|
|
0
|
0
|
|
my ($class, $text) = @_; |
63
|
0
|
|
|
|
|
|
for ($text) { |
64
|
0
|
|
|
|
|
|
s/([\'\\])/\\$1/g; |
65
|
0
|
|
|
|
|
|
s/\n/\\n/g; |
66
|
0
|
|
|
|
|
|
s/\r/\\r/g; |
67
|
|
|
|
|
|
|
} |
68
|
0
|
|
|
|
|
|
return "'" . $text . "'"; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
72
|
|
|
|
|
|
|
# ident(\@ident) foo.bar(baz) |
73
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub ident { |
76
|
0
|
|
|
0
|
0
|
|
my ($class, $ident) = @_; |
77
|
0
|
0
|
|
|
|
|
return "''" unless @$ident; |
78
|
0
|
|
|
|
|
|
my $ns; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# does the first element of the identifier have a NAMESPACE |
81
|
|
|
|
|
|
|
# handler defined? |
82
|
0
|
0
|
0
|
|
|
|
if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) { |
|
|
|
0
|
|
|
|
|
83
|
0
|
|
|
|
|
|
my $key = $ident->[0]; |
84
|
0
|
|
|
|
|
|
$key =~ s/^'(.+)'$/$1/s; |
85
|
0
|
0
|
|
|
|
|
if ($ns = $ns->{ $key }) { |
86
|
0
|
|
|
|
|
|
return $ns->ident($ident); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
0
|
0
|
0
|
|
|
|
if (scalar @$ident <= 2 && ! $ident->[1]) { |
91
|
0
|
|
|
|
|
|
$ident = $ident->[0]; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
else { |
94
|
0
|
|
|
|
|
|
$ident = '[' . join(', ', @$ident) . ']'; |
95
|
|
|
|
|
|
|
} |
96
|
0
|
|
|
|
|
|
return "stash.get($ident)"; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
101
|
|
|
|
|
|
|
# assign(\@ident, $value, $default) foo = bar |
102
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub assign { |
105
|
0
|
|
|
0
|
0
|
|
my ($class, $var, $val, $default) = @_; |
106
|
|
|
|
|
|
|
|
107
|
0
|
0
|
|
|
|
|
if (ref $var) { |
108
|
0
|
0
|
0
|
|
|
|
if (scalar @$var == 2 && ! $var->[1]) { |
109
|
0
|
|
|
|
|
|
$var = $var->[0]; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
else { |
112
|
0
|
|
|
|
|
|
$var = '[' . join(', ', @$var) . ']'; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
} |
115
|
0
|
|
|
|
|
|
$val = _attempt_range_expand_val $val; |
116
|
0
|
0
|
|
|
|
|
$val .= ', 1' if $default; |
117
|
0
|
|
|
|
|
|
return "stash.set($var, $val)"; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
122
|
|
|
|
|
|
|
# args(\@args) foo, bar, baz = qux |
123
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub args { |
126
|
0
|
|
|
0
|
0
|
|
my ($class, $args) = @_; |
127
|
0
|
|
|
|
|
|
my $hash = shift @$args; |
128
|
0
|
0
|
|
|
|
|
push(@$args, '{ ' . join(', ', @$hash) . ' }') |
129
|
|
|
|
|
|
|
if @$hash; |
130
|
|
|
|
|
|
|
|
131
|
0
|
0
|
|
|
|
|
return '[]' unless @$args; |
132
|
0
|
|
|
|
|
|
return '[ ' . join(', ', @$args) . ' ]'; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
137
|
|
|
|
|
|
|
# filenames(\@names) |
138
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub filenames { |
141
|
0
|
|
|
0
|
0
|
|
my ($class, $names) = @_; |
142
|
0
|
0
|
|
|
|
|
if (@$names > 1) { |
143
|
0
|
|
|
|
|
|
$names = '[ ' . join(', ', @$names) . ' ]'; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
else { |
146
|
0
|
|
|
|
|
|
$names = shift @$names; |
147
|
|
|
|
|
|
|
} |
148
|
0
|
|
|
|
|
|
return $names; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
153
|
|
|
|
|
|
|
# get($expr) [% foo %] |
154
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub get { |
157
|
0
|
|
|
0
|
0
|
|
my ($class, $expr) = @_; |
158
|
0
|
|
|
|
|
|
return "$OUTPUT $expr;"; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub block { |
162
|
0
|
|
|
0
|
0
|
|
my ($class, $block) = @_; |
163
|
0
|
|
|
|
|
|
return join "\n", map { |
164
|
0
|
0
|
|
|
|
|
s/^#(?=line \d+)/\/\//gm; |
165
|
0
|
|
|
|
|
|
$_; |
166
|
0
|
|
|
|
|
|
} @{ $block || [] }; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
170
|
|
|
|
|
|
|
# call($expr) [% CALL bar %] |
171
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub call { |
174
|
0
|
|
|
0
|
0
|
|
my ($class, $expr) = @_; |
175
|
0
|
|
|
|
|
|
$expr .= ';'; |
176
|
0
|
|
|
|
|
|
return $expr; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
181
|
|
|
|
|
|
|
# set(\@setlist) [% foo = bar, baz = qux %] |
182
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub set { |
185
|
0
|
|
|
0
|
0
|
|
my ($class, $setlist) = @_; |
186
|
0
|
|
|
|
|
|
my $output; |
187
|
0
|
|
|
|
|
|
while (my ($var, $val) = splice(@$setlist, 0, 2)) { |
188
|
0
|
|
|
|
|
|
$output .= $class->assign($var, $val) . ";\n"; |
189
|
|
|
|
|
|
|
} |
190
|
0
|
|
|
|
|
|
chomp $output; |
191
|
0
|
|
|
|
|
|
return $output; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
196
|
|
|
|
|
|
|
# default(\@setlist) [% DEFAULT foo = bar, baz = qux %] |
197
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub default { |
200
|
0
|
|
|
0
|
0
|
|
my ($class, $setlist) = @_; |
201
|
0
|
|
|
|
|
|
my $output; |
202
|
0
|
|
|
|
|
|
while (my ($var, $val) = splice(@$setlist, 0, 2)) { |
203
|
0
|
|
|
|
|
|
$output .= &assign($class, $var, $val, 1) . ";\n"; |
204
|
|
|
|
|
|
|
} |
205
|
0
|
|
|
|
|
|
chomp $output; |
206
|
0
|
|
|
|
|
|
return $output; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
211
|
|
|
|
|
|
|
# include(\@nameargs) [% INCLUDE template foo = bar %] |
212
|
|
|
|
|
|
|
# # => [ [ $file, ... ], \@args ] |
213
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub include { |
216
|
0
|
|
|
0
|
0
|
|
my ($class, $nameargs) = @_; |
217
|
0
|
|
|
|
|
|
my ($file, $args) = @$nameargs; |
218
|
0
|
|
|
|
|
|
my $hash = shift @$args; |
219
|
0
|
|
|
|
|
|
$file = $class->filenames($file); |
220
|
0
|
0
|
|
|
|
|
$file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; |
221
|
0
|
|
|
|
|
|
return "$OUTPUT context.include($file);"; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
226
|
|
|
|
|
|
|
# process(\@nameargs) [% PROCESS template foo = bar %] |
227
|
|
|
|
|
|
|
# # => [ [ $file, ... ], \@args ] |
228
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub process { |
231
|
0
|
|
|
0
|
0
|
|
my ($class, $nameargs) = @_; |
232
|
0
|
|
|
|
|
|
my ($file, $args) = @$nameargs; |
233
|
0
|
|
|
|
|
|
my $hash = shift @$args; |
234
|
0
|
|
|
|
|
|
$file = $class->filenames($file); |
235
|
0
|
0
|
|
|
|
|
$file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; |
236
|
0
|
|
|
|
|
|
return "$OUTPUT context.process($file);"; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
241
|
|
|
|
|
|
|
# if($expr, $block, $else) [% IF foo < bar %] |
242
|
|
|
|
|
|
|
# ... |
243
|
|
|
|
|
|
|
# [% ELSE %] |
244
|
|
|
|
|
|
|
# ... |
245
|
|
|
|
|
|
|
# [% END %] |
246
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub if { |
249
|
0
|
|
|
0
|
0
|
|
my ($class, $expr, $block, $else) = @_; |
250
|
0
|
0
|
|
|
|
|
my @else = $else ? @$else : (); |
251
|
0
|
|
|
|
|
|
$else = pop @else; |
252
|
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
|
my $output = "if ($expr) {\n$block\n}\n"; |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
foreach my $elsif (@else) { |
256
|
0
|
|
|
|
|
|
($expr, $block) = @$elsif; |
257
|
0
|
|
|
|
|
|
$output .= "else if ($expr) {\n$block\n}\n"; |
258
|
|
|
|
|
|
|
} |
259
|
0
|
0
|
|
|
|
|
if (defined $else) { |
260
|
0
|
|
|
|
|
|
$output .= "else {\n$else\n}\n"; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
0
|
|
|
|
|
|
return $output; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
267
|
|
|
|
|
|
|
# foreach($target, $list, $args, $block) [% FOREACH x = [ foo bar ] %] |
268
|
|
|
|
|
|
|
# ... |
269
|
|
|
|
|
|
|
# [% END %] |
270
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub foreach { |
273
|
0
|
|
|
0
|
0
|
|
my ($class, $target, $list, $args, $block) = @_; |
274
|
0
|
|
|
|
|
|
$args = shift @$args; |
275
|
0
|
0
|
|
|
|
|
$args = @$args ? ', { ' . join(', ', @$args) . ' }' : ''; |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
my ($loop_save, $loop_set, $loop_restore, $setiter); |
278
|
0
|
0
|
|
|
|
|
if ($target) { |
279
|
0
|
|
|
|
|
|
$loop_save = |
280
|
|
|
|
|
|
|
'try { oldloop = ' . $class->ident(["'loop'"]) . ' } finally {}'; |
281
|
0
|
|
|
|
|
|
$loop_set = "stash.data['$target'] = value"; |
282
|
0
|
|
|
|
|
|
$loop_restore = "stash.set('loop', oldloop)"; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
else { |
285
|
0
|
|
|
|
|
|
die "XXX - Not supported yet"; |
286
|
0
|
|
|
|
|
|
$loop_save = 'stash = context.localise()'; |
287
|
0
|
|
|
|
|
|
$loop_set = |
288
|
|
|
|
|
|
|
"stash.get(['import', [value]]) if typeof(value) == 'object'"; |
289
|
0
|
|
|
|
|
|
$loop_restore = 'stash = context.delocalise()'; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
0
|
|
|
|
|
|
$list = _attempt_range_expand_val $list; |
293
|
|
|
|
|
|
|
|
294
|
0
|
|
|
|
|
|
return <
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
// FOREACH |
297
|
|
|
|
|
|
|
(function() { |
298
|
|
|
|
|
|
|
var list = $list; |
299
|
|
|
|
|
|
|
list = new Jemplate.Iterator(list); |
300
|
|
|
|
|
|
|
var retval = list.get_first(); |
301
|
|
|
|
|
|
|
var value = retval[0]; |
302
|
|
|
|
|
|
|
var done = retval[1]; |
303
|
|
|
|
|
|
|
var oldloop; |
304
|
|
|
|
|
|
|
$loop_save |
305
|
|
|
|
|
|
|
stash.set('loop', list); |
306
|
|
|
|
|
|
|
try { |
307
|
|
|
|
|
|
|
while (! done) { |
308
|
|
|
|
|
|
|
$loop_set; |
309
|
|
|
|
|
|
|
$block; |
310
|
|
|
|
|
|
|
retval = list.get_next(); |
311
|
|
|
|
|
|
|
value = retval[0]; |
312
|
|
|
|
|
|
|
done = retval[1]; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
catch(e) { |
316
|
|
|
|
|
|
|
throw(context.set_error(e, output)); |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
$loop_restore; |
319
|
|
|
|
|
|
|
})(); |
320
|
|
|
|
|
|
|
EOF |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
325
|
|
|
|
|
|
|
# next() [% NEXT %] |
326
|
|
|
|
|
|
|
# |
327
|
|
|
|
|
|
|
# Next iteration of a FOREACH loop (experimental) |
328
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub next { |
331
|
0
|
|
|
0
|
0
|
|
return <
|
332
|
|
|
|
|
|
|
retval = list.get_next(); |
333
|
|
|
|
|
|
|
value = retval[0]; |
334
|
|
|
|
|
|
|
done = retval[1]; |
335
|
|
|
|
|
|
|
continue; |
336
|
|
|
|
|
|
|
EOF |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
340
|
|
|
|
|
|
|
# wrapper(\@nameargs, $block) [% WRAPPER template foo = bar %] |
341
|
|
|
|
|
|
|
# # => [ [$file,...], \@args ] |
342
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
343
|
|
|
|
|
|
|
sub wrapper { |
344
|
0
|
|
|
0
|
0
|
|
my ($class, $nameargs, $block) = @_; |
345
|
0
|
|
|
|
|
|
my ($file, $args) = @$nameargs; |
346
|
0
|
|
|
|
|
|
my $hash = shift @$args; |
347
|
|
|
|
|
|
|
|
348
|
0
|
|
|
|
|
|
s/ => /: / for @$hash; |
349
|
0
|
0
|
|
|
|
|
return $class->multi_wrapper($file, $hash, $block) |
350
|
|
|
|
|
|
|
if @$file > 1; |
351
|
0
|
|
|
|
|
|
$file = shift @$file; |
352
|
0
|
|
|
|
|
|
push(@$hash, "'content': output"); |
353
|
0
|
0
|
|
|
|
|
$file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; |
354
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
|
return <
|
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
// WRAPPER |
358
|
|
|
|
|
|
|
$OUTPUT (function() { |
359
|
|
|
|
|
|
|
var output = ''; |
360
|
|
|
|
|
|
|
$block; |
361
|
|
|
|
|
|
|
return context.include($file); |
362
|
|
|
|
|
|
|
})(); |
363
|
|
|
|
|
|
|
EOF |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub multi_wrapper { |
367
|
0
|
|
|
0
|
0
|
|
my ($class, $file, $hash, $block) = @_; |
368
|
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
push(@$hash, "'content': output"); |
370
|
0
|
0
|
|
|
|
|
$hash = @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; |
371
|
|
|
|
|
|
|
|
372
|
0
|
|
|
|
|
|
$file = join(', ', reverse @$file); |
373
|
|
|
|
|
|
|
# print STDERR "multi wrapper: $file\n"; |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
|
return <
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
// WRAPPER |
378
|
|
|
|
|
|
|
$OUTPUT (function() { |
379
|
|
|
|
|
|
|
var output = ''; |
380
|
|
|
|
|
|
|
$block; |
381
|
|
|
|
|
|
|
var files = new Array($file); |
382
|
|
|
|
|
|
|
for (var i = 0; i < files.length; i++) { |
383
|
|
|
|
|
|
|
output = context.include(files[i]$hash); |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
return output; |
386
|
|
|
|
|
|
|
})(); |
387
|
|
|
|
|
|
|
EOF |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
392
|
|
|
|
|
|
|
# while($expr, $block) [% WHILE x < 10 %] |
393
|
|
|
|
|
|
|
# ... |
394
|
|
|
|
|
|
|
# [% END %] |
395
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub while { |
398
|
0
|
|
|
0
|
0
|
|
my ($class, $expr, $block) = @_; |
399
|
|
|
|
|
|
|
|
400
|
0
|
|
|
|
|
|
return <
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
// WHILE |
403
|
|
|
|
|
|
|
var failsafe = $WHILE_MAX; |
404
|
|
|
|
|
|
|
while (--failsafe && ($expr)) { |
405
|
|
|
|
|
|
|
$block |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
if (! failsafe) |
408
|
|
|
|
|
|
|
throw("WHILE loop terminated (> $WHILE_MAX iterations)\\n") |
409
|
|
|
|
|
|
|
EOF |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
413
|
|
|
|
|
|
|
# javascript($script) [% JAVASCRIPT %] |
414
|
|
|
|
|
|
|
# ... |
415
|
|
|
|
|
|
|
# [% END %] |
416
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
417
|
|
|
|
|
|
|
sub javascript { |
418
|
0
|
|
|
0
|
0
|
|
my ( $class, $javascript ) = @_; |
419
|
0
|
|
|
|
|
|
return $javascript; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub no_javascript { |
423
|
0
|
|
|
0
|
0
|
|
my ( $class ) = @_; |
424
|
0
|
|
|
|
|
|
die "EVAL_JAVASCRIPT has not been enabled, cannot process [% JAVASCRIPT %] blocks"; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
428
|
|
|
|
|
|
|
# switch($expr, \@case) [% SWITCH %] |
429
|
|
|
|
|
|
|
# [% CASE foo %] |
430
|
|
|
|
|
|
|
# ... |
431
|
|
|
|
|
|
|
# [% END %] |
432
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub switch { |
435
|
0
|
|
|
0
|
0
|
|
my ($class, $expr, $case) = @_; |
436
|
0
|
|
|
|
|
|
my @case = @$case; |
437
|
0
|
|
|
|
|
|
my ($match, $block, $default); |
438
|
0
|
|
|
|
|
|
my $caseblock = ''; |
439
|
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
|
$default = pop @case; |
441
|
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
foreach $case (@case) { |
443
|
0
|
|
|
|
|
|
$match = $case->[0]; |
444
|
0
|
|
|
|
|
|
$block = $case->[1]; |
445
|
|
|
|
|
|
|
# $block = pad($block, 1) if $PRETTY; |
446
|
0
|
|
|
|
|
|
$caseblock .= <
|
447
|
|
|
|
|
|
|
case $match: |
448
|
|
|
|
|
|
|
$block |
449
|
|
|
|
|
|
|
break; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
EOF |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
0
|
0
|
|
|
|
|
if (defined $default) { |
455
|
0
|
|
|
|
|
|
$caseblock .= <
|
456
|
|
|
|
|
|
|
default: |
457
|
|
|
|
|
|
|
$default |
458
|
|
|
|
|
|
|
break; |
459
|
|
|
|
|
|
|
EOF |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
# $caseblock = pad($caseblock, 2) if $PRETTY; |
462
|
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
|
return <
|
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
switch($expr) { |
466
|
|
|
|
|
|
|
$caseblock |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
EOF |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
474
|
|
|
|
|
|
|
# throw(\@nameargs) [% THROW foo "bar error" %] |
475
|
|
|
|
|
|
|
# # => [ [$type], \@args ] |
476
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub throw { |
479
|
0
|
|
|
0
|
0
|
|
my ($class, $nameargs) = @_; |
480
|
0
|
|
|
|
|
|
my ($type, $args) = @$nameargs; |
481
|
0
|
|
|
|
|
|
my $hash = shift(@$args); |
482
|
0
|
|
|
|
|
|
my $info = shift(@$args); |
483
|
0
|
|
|
|
|
|
$type = shift @$type; |
484
|
|
|
|
|
|
|
|
485
|
0
|
|
|
|
|
|
return qq{throw([$type, $info]);}; |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
490
|
|
|
|
|
|
|
# clear() [% CLEAR %] |
491
|
|
|
|
|
|
|
# |
492
|
|
|
|
|
|
|
# NOTE: this is redundant, being hard-coded (for now) into Parser.yp |
493
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
sub clear { |
496
|
0
|
|
|
0
|
0
|
|
return "output = '';"; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
501
|
|
|
|
|
|
|
# break() [% BREAK %] |
502
|
|
|
|
|
|
|
# |
503
|
|
|
|
|
|
|
# NOTE: this is redundant, being hard-coded (for now) into Parser.yp |
504
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
sub break { |
507
|
0
|
|
|
0
|
0
|
|
return 'break;'; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
511
|
|
|
|
|
|
|
# return() [% RETURN %] |
512
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub return { |
515
|
0
|
|
|
0
|
0
|
|
return "return output;" |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
520
|
|
|
|
|
|
|
# stop() [% STOP %] |
521
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
sub stop { |
524
|
0
|
|
|
0
|
0
|
|
return "throw('Jemplate.STOP\\n' + output);"; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
529
|
|
|
|
|
|
|
# use(\@lnameargs) [% USE alias = plugin(args) %] |
530
|
|
|
|
|
|
|
# # => [ [$file, ...], \@args, $alias ] |
531
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub use { |
534
|
0
|
|
|
0
|
0
|
|
my ($class, $lnameargs) = @_; |
535
|
0
|
|
|
|
|
|
my ($file, $args, $alias) = @$lnameargs; |
536
|
0
|
|
|
|
|
|
$file = shift @$file; # same production rule as INCLUDE |
537
|
0
|
|
0
|
|
|
|
$alias ||= $file; |
538
|
0
|
|
|
|
|
|
$args = &args($class, $args); |
539
|
0
|
0
|
|
|
|
|
$file .= ", $args" if $args; |
540
|
0
|
|
|
|
|
|
return "// USE\n" |
541
|
|
|
|
|
|
|
. "stash.set($alias, context.plugin($file));"; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
546
|
|
|
|
|
|
|
# raw(\@lnameargs) [% RAW alias = plugin(args) %] |
547
|
|
|
|
|
|
|
# # => [ [$file, ...], \@args, $alias ] |
548
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
sub raw { |
551
|
0
|
|
|
0
|
0
|
|
my ($class, $lnameargs) = @_; |
552
|
0
|
|
|
|
|
|
my ($file, $args, $alias) = @$lnameargs; |
553
|
0
|
|
|
|
|
|
$file = shift @$file; # same production rule as INCLUDE |
554
|
0
|
|
0
|
|
|
|
$alias ||= $file; |
555
|
0
|
|
|
|
|
|
$args = &args($class, $args); |
556
|
|
|
|
|
|
|
# $file .= ", $args" if $args; |
557
|
0
|
|
|
|
|
|
$file =~ s/'|"//g; |
558
|
0
|
|
|
|
|
|
return "// RAW\n" |
559
|
|
|
|
|
|
|
. "stash.set($alias, $file);"; |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
564
|
|
|
|
|
|
|
# stubs() [% STOP %] |
565
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
sub filter { |
568
|
0
|
|
|
0
|
0
|
|
my ($class, $lnameargs, $block) = @_; |
569
|
0
|
|
|
|
|
|
my ($name, $args, $alias) = @$lnameargs; |
570
|
0
|
|
|
|
|
|
$name = shift @$name; |
571
|
0
|
|
|
|
|
|
$args = &args($class, $args); |
572
|
0
|
0
|
|
|
|
|
$args = $args ? "$args, $alias" : ", null, $alias" |
|
|
0
|
|
|
|
|
|
573
|
|
|
|
|
|
|
if $alias; |
574
|
0
|
0
|
|
|
|
|
$name .= ", $args" if $args; |
575
|
0
|
|
|
|
|
|
return <
|
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
// FILTER |
578
|
|
|
|
|
|
|
$OUTPUT (function() { |
579
|
|
|
|
|
|
|
var output = ''; |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
$block |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
return context.filter(output, $name); |
584
|
|
|
|
|
|
|
})(); |
585
|
|
|
|
|
|
|
EOF |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
sub quoted { |
589
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
590
|
0
|
0
|
0
|
|
|
|
if ( @_ && ref($_[0]) ) { |
591
|
0
|
|
|
|
|
|
return join( " + ", @{$_[0]} ); |
|
0
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
} |
593
|
0
|
|
|
|
|
|
return "throw('QUOTED called with unknown arguments in Jemplate');"; |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
597
|
|
|
|
|
|
|
# macro($name, $block, \@args) |
598
|
|
|
|
|
|
|
#------------------------------------------------------------------------ |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub macro { |
601
|
0
|
|
|
0
|
0
|
|
my ($class, $ident, $block, $args) = @_; |
602
|
|
|
|
|
|
|
|
603
|
0
|
0
|
|
|
|
|
if ($args) { |
604
|
0
|
|
|
|
|
|
$args = join(';', map { "args['$_'] = fargs.shift()" } @$args); |
|
0
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
|
return <
|
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
//MACRO |
609
|
|
|
|
|
|
|
stash.set('$ident', function () { |
610
|
|
|
|
|
|
|
var output = ''; |
611
|
|
|
|
|
|
|
var args = {}; |
612
|
|
|
|
|
|
|
var fargs = Array.prototype.slice.call(arguments); |
613
|
|
|
|
|
|
|
$args; |
614
|
|
|
|
|
|
|
args.arguments = Array.prototype.slice.call(arguments); |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
var params = fargs.shift() || {}; |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
for (var key in params) { |
619
|
|
|
|
|
|
|
args[key] = params[key]; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
context.stash.clone(args); |
623
|
|
|
|
|
|
|
try { |
624
|
|
|
|
|
|
|
$block |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
catch(e) { |
627
|
|
|
|
|
|
|
var error = context.set_error(e, output); |
628
|
|
|
|
|
|
|
throw(error); |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
context.stash.declone(); |
632
|
|
|
|
|
|
|
return output; |
633
|
|
|
|
|
|
|
}); |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
EOF |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
else { |
639
|
0
|
|
|
|
|
|
return <
|
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
//MACRO |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
stash.set('$ident', function () { |
644
|
|
|
|
|
|
|
var output = ''; |
645
|
|
|
|
|
|
|
var args = {}; |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
var fargs = Array.prototype.slice.call(arguments); |
648
|
|
|
|
|
|
|
args.arguments = Array.prototype.slice.call(arguments); |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
if (typeof arguments[0] == 'object') args = arguments[0]; |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
context.stash.clone(args); |
653
|
|
|
|
|
|
|
try { |
654
|
|
|
|
|
|
|
$block |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
catch(e) { |
657
|
|
|
|
|
|
|
var error = context.set_error(e, output); |
658
|
|
|
|
|
|
|
throw(error); |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
context.stash.declone(); |
662
|
|
|
|
|
|
|
return output;}); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
EOF |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub capture { |
669
|
0
|
|
|
0
|
0
|
|
my ($class, $name, $block) = @_; |
670
|
|
|
|
|
|
|
|
671
|
0
|
0
|
|
|
|
|
if (ref $name) { |
672
|
0
|
0
|
0
|
|
|
|
if (scalar @$name == 2 && ! $name->[1]) { |
673
|
0
|
|
|
|
|
|
$name = $name->[0]; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
else { |
676
|
0
|
|
|
|
|
|
$name = '[' . join(', ', @$name) . ']'; |
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
0
|
|
|
|
|
|
return <
|
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
// CAPTURE |
683
|
|
|
|
|
|
|
(function() { |
684
|
|
|
|
|
|
|
var output = ''; |
685
|
|
|
|
|
|
|
$block |
686
|
|
|
|
|
|
|
stash.set($name, output); |
687
|
|
|
|
|
|
|
})(); |
688
|
|
|
|
|
|
|
EOF |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
BEGIN { |
693
|
22
|
|
|
22
|
|
617
|
return; # Comment out this line to get callback traces |
694
|
22
|
|
|
22
|
|
137
|
no strict 'refs'; |
|
22
|
|
|
|
|
36
|
|
|
22
|
|
|
|
|
1209
|
|
695
|
0
|
|
|
|
|
|
my $pkg = __PACKAGE__ . '::'; |
696
|
0
|
|
|
|
|
|
my $stash = \ %$pkg; |
697
|
22
|
|
|
22
|
|
112
|
use strict 'refs'; |
|
22
|
|
|
|
|
33
|
|
|
22
|
|
|
|
|
1200
|
|
698
|
0
|
|
|
|
|
|
for my $name (keys %$stash) { |
699
|
0
|
|
|
|
|
|
my $glob = $stash->{$name}; |
700
|
0
|
0
|
|
|
|
|
if (*$glob{CODE}) { |
701
|
0
|
|
|
|
|
|
my $code = *$glob{CODE}; |
702
|
22
|
|
|
22
|
|
92
|
no warnings 'redefine'; |
|
22
|
|
|
|
|
40
|
|
|
22
|
|
|
|
|
2032
|
|
703
|
|
|
|
|
|
|
$stash->{$name} = sub { |
704
|
0
|
|
|
|
|
|
warn "Calling $name(@_)\n"; |
705
|
0
|
|
|
|
|
|
&$code(@_); |
706
|
0
|
|
|
|
|
|
}; |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
} |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
1; |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=head1 NAME |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
Jemplate::Directive - Jemplate Code Generating Backend |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
=head1 SYNOPSIS |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
use Jemplate::Directive; |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head1 DESCRIPTION |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Jemplate::Directive is the analog to Template::Directive, which is the |
725
|
|
|
|
|
|
|
module that produces that actual code that templates turn into. The |
726
|
|
|
|
|
|
|
Jemplate version obviously produces JavaScript code rather than Perl. |
727
|
|
|
|
|
|
|
Other than that the two modules are almost exactly the same. |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=head1 BUGS |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Unfortunately, some of the code generation seems to happen before |
732
|
|
|
|
|
|
|
Jemplate::Directive gets control. So it currently has heuristical code |
733
|
|
|
|
|
|
|
to rejigger Perl code snippets into JavaScript. This processing needs to |
734
|
|
|
|
|
|
|
happen upstream once I get more clarity on how Template::Toolkit works. |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head1 AUTHOR |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Ingy döt Net |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=head1 COPYRIGHT |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
Copyright (c) 2006-2008. Ingy döt Net. All rights reserved. |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
745
|
|
|
|
|
|
|
under the same terms as Perl itself. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
See L |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=cut |