From 9f46db73a9adae545e0a0df8747698b8e098d3fd Mon Sep 17 00:00:00 2001 From: catalyst Date: Sat, 18 Oct 2008 12:32:33 +0000 Subject: [PATCH] - added vmethod "item" for hashed (thanks to nik) - updated DESIGN to reflect recent changes - commented some perl-tied code from _dotop - cleaned stash.t.js - fixed config.yaml for testing from tjs directory --- DESIGN | 11 +- bin/jemplate | 808 ++++++++++++++++---------------- lib/Jemplate/Runtime.pm | 17 +- lib/Jemplate/Runtime/Compact.pm | 2 +- src/js/kernel.compact.js | 2 +- src/js/kernel.js | 17 +- tests/basics.t.html | 19 - tests/js/global.js | 12 +- tests/t/stash.t.js | 122 +++-- tests/tjs/config.yaml | 2 + 10 files changed, 552 insertions(+), 460 deletions(-) diff --git a/DESIGN b/DESIGN index fc9b9c0..c15155b 100644 --- a/DESIGN +++ b/DESIGN @@ -112,7 +112,7 @@ completion, the second is for the testing of the feature. ++ [% WRAPPER template [var = value ...] %] text... [% END %] ++ [% BLOCK [name] %] content... [% END %] ++ [% FILTER filter %] text... [% END %] - -- [% MACRO name[(varlist)] directive %] + +- [% MACRO name[(varlist)] directive %] -- [% USE plugin[(param, ...)] %] xx [% PERL %] code... [% END %] xx [% RAWPERL %] code... [% END %] @@ -158,8 +158,9 @@ completion, the second is for the testing of the feature. ++ each list of alternating keys/values ++ exists(key) does key exist? - -- import(hash2) import contents of hash2 - -- import import into current namespace hash + +- import(hash2) import contents of hash2 + +- import import into current namespace hash + +- item retrieve value using string ++ keys list of keys ++ list returns alternating key, value ++ nsort keys sorted numerically @@ -275,8 +276,8 @@ Refer to documentation for details of individual plugins. === Runtime processing options -- EVAL_PERL process PERL/RAWPERL blocks (0) - -- PRE_PROCESS template(s) to process before main template - -- POST_PROCESS template(s) to process after main template + ++ PRE_PROCESS template(s) to process before main template + +- POST_PROCESS template(s) to process after main template -- PROCESS template(s) to process instead of main template -- ERROR name of error template or reference to hash array mapping error types to templates diff --git a/bin/jemplate b/bin/jemplate index fa67637..0b362da 100755 --- a/bin/jemplate +++ b/bin/jemplate @@ -597,6 +597,7 @@ BEGIN { package Template::Constants; require Exporter; + use strict; use warnings; use base 'Exporter'; @@ -604,7 +605,7 @@ use base 'Exporter'; use vars qw( @EXPORT_OK %EXPORT_TAGS ); use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG); -our $VERSION = 2.75; +our $VERSION = 2.74; @@ -738,7 +739,7 @@ use strict; use warnings; use Template::Constants; -our $VERSION = 2.78; +our $VERSION = 2.77; @@ -746,8 +747,7 @@ sub new { my $class = shift; my ($argnames, @args, $arg, $cfg); - { no strict 'refs'; - no warnings 'once'; + { no strict qw( refs ); $argnames = \@{"$class\::BASEARGS"} || [ ]; } @@ -762,7 +762,7 @@ sub new { $cfg = defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ }; my $self = bless { - (map { ($_ => shift @args) } @$argnames), + map { ($_ => shift @args) } @$argnames, _ERROR => '', DEBUG => 0, }, $class; @@ -797,6 +797,11 @@ sub _init { } +sub DEBUG { + my $self = shift; + print STDERR "DEBUG: ", @_; +} + sub debug { my $self = shift; my $msg = join('', @_); @@ -842,7 +847,7 @@ use vars qw( $VERSION $DEBUG $ERROR $INSTDIR $LATEX_PATH $PDFLATEX_PATH $DVIPS_PATH $STASH $SERVICE $CONTEXT $CONSTANTS @PRELOAD ); -$VERSION = 2.75; +$VERSION = 2.74; $DEBUG = 0 unless defined $DEBUG; $ERROR = ''; $CONTEXT = 'Template::Context'; @@ -878,7 +883,9 @@ sub load { my ($class, $module) = @_; $module =~ s[::][/]g; $module .= '.pm'; - eval { require $module; }; + eval { + require $module; + }; return $@ ? $class->error("failed to load $module: $@") : 1; } @@ -887,11 +894,11 @@ sub load { sub parser { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($PARSER); return $PARSER->new($params) - || $class->error("failed to create parser: ", $PARSER->error); + || $class->error("failed to create parser: ", $PARSER->error); } @@ -899,7 +906,7 @@ sub parser { sub provider { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($PROVIDER); return $PROVIDER->new($params) @@ -912,12 +919,12 @@ sub provider { sub plugins { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($PLUGINS); return $PLUGINS->new($params) - || $class->error("failed to create plugin provider: ", - $PLUGINS->error); + || $class->error("failed to create plugin provider: ", + $PLUGINS->error); } @@ -925,12 +932,12 @@ sub plugins { sub filters { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($FILTERS); return $FILTERS->new($params) - || $class->error("failed to create filter provider: ", - $FILTERS->error); + || $class->error("failed to create filter provider: ", + $FILTERS->error); } @@ -941,7 +948,7 @@ sub iterator { return undef unless $class->load($ITERATOR); return $ITERATOR->new($list, @_) - || $class->error("failed to create iterator: ", $ITERATOR->error); + || $class->error("failed to create iterator: ", $ITERATOR->error); } @@ -949,11 +956,11 @@ sub iterator { sub stash { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($STASH); return $STASH->new($params) - || $class->error("failed to create stash: ", $STASH->error); + || $class->error("failed to create stash: ", $STASH->error); } @@ -961,11 +968,11 @@ sub stash { sub context { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($CONTEXT); return $CONTEXT->new($params) - || $class->error("failed to create context: ", $CONTEXT->error); + || $class->error("failed to create context: ", $CONTEXT->error); } @@ -973,11 +980,11 @@ sub context { sub service { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($SERVICE); return $SERVICE->new($params) - || $class->error("failed to create context: ", $SERVICE->error); + || $class->error("failed to create context: ", $SERVICE->error); } @@ -985,12 +992,12 @@ sub service { sub constants { my $class = shift; my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') - ? shift : { @_ }; + ? shift : { @_ }; return undef unless $class->load($CONSTANTS); return $CONSTANTS->new($params) - || $class->error("failed to create constants namespace: ", - $CONSTANTS->error); + || $class->error("failed to create constants namespace: ", + $CONSTANTS->error); } @@ -998,7 +1005,7 @@ sub constants { sub instdir { my ($class, $dir) = @_; my $inst = $INSTDIR - || return $class->error("no installation directory"); + || return $class->error("no installation directory"); $inst =~ s[/$][]g; $inst .= "/$dir" if $dir; return $inst; @@ -1136,7 +1143,7 @@ sub process { die $context->catch($@) if $@; - + return $output; } @@ -1163,9 +1170,9 @@ sub _dump { $output .= "BLOCK: $self->{ _BLOCK }\nDEFBLOCKS:\n"; if ($dblks = $self->{ _DEFBLOCKS }) { - foreach my $b (keys %$dblks) { - $output .= " $b: $dblks->{ $b }\n"; - } + foreach my $b (keys %$dblks) { + $output .= " $b: $dblks->{ $b }\n"; + } } return $output; @@ -1217,7 +1224,7 @@ sub write_perl_file { my ($fh, $tmpfile); return $class->error("invalid filename: $file") - unless $file =~ /^(.+)$/s; + unless $file =~ /^(.+)$/s; eval { require File::Temp; @@ -1266,7 +1273,7 @@ use constant INFO => 1; use constant TEXT => 2; use overload q|""| => "as_string", fallback => 1; -our $VERSION = 2.70; +our $VERSION = 2.69; @@ -1383,7 +1390,6 @@ sub process { # localise the variable stash with any parameters passed # and set the 'template' variable $params ||= { }; - # TODO: change this to C<||=> so we can use a template parameter $params->{ template } = $template unless ref $template eq 'CODE'; $context->localise($params); @@ -1596,7 +1602,7 @@ use constant LOAD => 3; # mtime of template use constant NEXT => 4; # link to next item in cache linked list use constant STAT => 5; # Time last stat()ed -our $VERSION = 2.94; +our $VERSION = 2.93; our $DEBUG = 0 unless defined $DEBUG; our $ERROR = ''; @@ -1705,7 +1711,7 @@ sub load { || return ($self->error(), Template::Constants::STATUS_ERROR); foreach my $dir (@$paths) { - $path = File::Spec->catfile($dir, $name); + $path = "$dir/$name"; last INCPATH if $self->_template_modified($path); } @@ -1762,7 +1768,7 @@ sub paths { unshift(@ipaths, @$dpaths); next; } - elsif (ref($dir) && UNIVERSAL::can($dir, 'paths')) { + elsif (UNIVERSAL::can($dir, 'paths')) { $dpaths = $dir->paths() || return $self->error($dir->error()); unshift(@ipaths, @$dpaths); @@ -1846,8 +1852,8 @@ sub _init { my $wdir = $dir; $wdir =~ s[:][]g if $^O eq 'MSWin32'; $wdir =~ /(.*)/; # untaint - $wdir = "$1"; # quotes work around bug in Strawberry Perl - $wdir = File::Spec->catfile($cdir, $wdir); + $wdir = $1; + $wdir = File::Spec->catfile($cdir, $1); File::Path::mkpath($wdir) unless -d $wdir; } } @@ -1921,26 +1927,19 @@ sub _fetch { warn($self->error(), "\n"); } - # load template from source + # Now fetch template from source, compile, and cache. my ($template, $error) = $self->_load($name, $t_name); + unless ($error) { + ($template, $error) = $self->_compile($template, $self->_compiled_filename($name) ); - if ($error) { - # Template could not be fetched. Add to the negative/notfound cache. - $self->{ NOTFOUND }->{ $name } = time; - return ( $template, $error ); + # Store compiled template and return it + return $self->store( $name, $template->{data} ) unless $error; } - # compile template source - ($template, $error) = $self->_compile($template, $self->_compiled_filename($name) ); + # Template could not be fetched. Add to the negative/notfound cache. + $self->{ NOTFOUND }->{ $name } = time; - if ($error) { - # return any compile time error - return ($template, $error); - } - else { - # Store compiled template and return it - return $self->store($name, $template->{data}) ; - } + return ( $template, $error ); } @@ -2335,7 +2334,6 @@ sub _template_content { local *FH; if (open(FH, "< $path")) { local $/; - binmode(FH); $data = ; $mod_date = (stat($path))[9]; close(FH); @@ -2430,7 +2428,6 @@ sub _dump_cache { sub _decode_unicode { my $self = shift; my $string = shift; - return undef unless defined $string; use bytes; require Encode; @@ -2483,7 +2480,7 @@ use Template::Service; use File::Basename; use File::Path; -our $VERSION = '2.20'; +our $VERSION = '2.19'; our $ERROR = ''; our $DEBUG = 0; our $BINMODE = 0 unless defined $BINMODE; @@ -2513,7 +2510,7 @@ sub process { unless (ref $outstream) { my $outpath = $self->{ OUTPUT_PATH }; $outstream = "$outpath/$outstream" if $outpath; - } + } # send processed template to output stream, checking for error return ($self->error($error)) @@ -8651,6 +8648,17 @@ sub 1; + + + + + + + + + + + } # # Inline include of Template/Directive.pm @@ -8701,14 +8709,14 @@ sub { my \$context = shift || die "template sub called without context\\n"; my \$stash = \$context->stash; my \$output = ''; - my \$_tt_error; + my \$error; eval { BLOCK: { $block } }; if (\$@) { - \$_tt_error = \$context->catch(\$@, \\\$output); - die \$_tt_error unless \$_tt_error->type eq 'return'; + \$error = \$context->catch(\$@, \\\$output); + die \$error unless \$error->type eq 'return'; } return \$output; @@ -8726,14 +8734,14 @@ sub anon_block { $OUTPUT do { my \$output = ''; - my \$_tt_error; + my \$error; eval { BLOCK: { $block } }; if (\$@) { - \$_tt_error = \$context->catch(\$@, \\\$output); - die \$_tt_error unless \$_tt_error->type eq 'return'; + \$error = \$context->catch(\$@, \\\$output); + die \$error unless \$error->type eq 'return'; } \$output; @@ -8785,13 +8793,13 @@ sub ident { # does the first element of the identifier have a NAMESPACE # handler defined? if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) { - my $key = $ident->[0]; - $key =~ s/^'(.+)'$/$1/s; - if ($ns = $ns->{ $key }) { - return $ns->ident($ident); - } + my $key = $ident->[0]; + $key =~ s/^'(.+)'$/$1/s; + if ($ns = $ns->{ $key }) { + return $ns->ident($ident); + } } - + if (scalar @$ident <= 2 && ! $ident->[1]) { $ident = $ident->[0]; } @@ -8957,14 +8965,14 @@ sub foreach { my ($loop_save, $loop_set, $loop_restore, $setiter); if ($target) { - $loop_save = 'eval { $_tt_oldloop = ' . &ident($class, ["'loop'"]) . ' }'; - $loop_set = "\$stash->{'$target'} = \$_tt_value"; - $loop_restore = "\$stash->set('loop', \$_tt_oldloop)"; + $loop_save = 'eval { $oldloop = ' . &ident($class, ["'loop'"]) . ' }'; + $loop_set = "\$stash->{'$target'} = \$value"; + $loop_restore = "\$stash->set('loop', \$oldloop)"; } else { $loop_save = '$stash = $context->localise()'; - $loop_set = "\$stash->get(['import', [\$_tt_value]]) " - . "if ref \$_tt_value eq 'HASH'"; + $loop_set = "\$stash->get(['import', [\$value]]) " + . "if ref \$value eq 'HASH'"; $loop_restore = '$stash = $context->delocalise()'; } $block = pad($block, 3) if $PRETTY; @@ -8972,28 +8980,28 @@ sub foreach { return <iterator(\$_tt_list) + unless (UNIVERSAL::isa(\$list, 'Template::Iterator')) { + \$list = Template::Config->iterator(\$list) || die \$Template::Config::ERROR, "\\n"; } - (\$_tt_value, \$_tt_error) = \$_tt_list->get_first(); + (\$value, \$error) = \$list->get_first(); $loop_save; - \$stash->set('loop', \$_tt_list); + \$stash->set('loop', \$list); eval { -LOOP: while (! \$_tt_error) { +LOOP: while (! \$error) { $loop_set; $block; - (\$_tt_value, \$_tt_error) = \$_tt_list->get_next(); + (\$value, \$error) = \$list->get_next(); } }; $loop_restore; die \$@ if \$@; - \$_tt_error = 0 if \$_tt_error && \$_tt_error eq Template::Constants::STATUS_DONE; - die \$_tt_error if \$_tt_error; + \$error = 0 if \$error && \$error eq Template::Constants::STATUS_DONE; + die \$error if \$error; }; EOF } @@ -9001,7 +9009,7 @@ EOF sub next { return <get_next(); +(\$value, \$error) = \$list->get_next(); next LOOP; EOF } @@ -9065,13 +9073,13 @@ sub while { return < $WHILE_MAX iterations)\\n" - unless \$_tt_failsafe; + unless \$failsafe; }; EOF } @@ -9091,9 +9099,9 @@ sub switch { $block = $case->[1]; $block = pad($block, 1) if $PRETTY; $caseblock .= <catch(\$@, \\\$output); - die \$_tt_error if \$_tt_error->type =~ /^return|stop\$/; - \$stash->set('error', \$_tt_error); - \$stash->set('e', \$_tt_error); - if (defined (\$_tt_handler = \$_tt_error->select_handler($handlers))) { + \$error = \$context->catch(\$@, \\\$output); + die \$error if \$error->type =~ /^return|stop\$/; + \$stash->set('error', \$error); + \$stash->set('e', \$error); + if (defined (\$handler = \$error->select_handler($handlers))) { $catchblock } $default @@ -9266,15 +9274,15 @@ sub view { return <get('view'); - my \$_tt_view = \$context->view($hash); - \$stash->set($name, \$_tt_view); - \$stash->set('view', \$_tt_view); + my \$oldv = \$stash->get('view'); + my \$view = \$context->view($hash); + \$stash->set($name, \$view); + \$stash->set('view', \$view); $block - \$stash->set('view', \$_tt_oldv); - \$_tt_view->seal(); + \$stash->set('view', \$oldv); + \$view->seal(); }; EOF } @@ -9298,14 +9306,14 @@ $block local(\$Template::Perl::context) = \$context; local(\$Template::Perl::stash) = \$stash; - my \$_tt_result = ''; - tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$_tt_result; - my \$_tt_save_stdout = select *Template::Perl::PERLOUT; + my \$result = ''; + tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$result; + my \$save_stdout = select *Template::Perl::PERLOUT; eval \$output; - select \$_tt_save_stdout; + select \$save_stdout; \$context->throw(\$@) if \$@; - \$_tt_result; + \$result; }; EOF } @@ -9350,12 +9358,12 @@ sub filter { $OUTPUT do { my \$output = ''; - my \$_tt_filter = \$context->filter($name) + my \$filter = \$context->filter($name) || \$context->throw(\$context->error); $block - &\$_tt_filter(\$output); + &\$filter(\$output); }; EOF } @@ -9396,20 +9404,20 @@ sub macro { my $nargs = scalar @$args; $args = join(', ', map { "'$_'" } @$args); $args = $nargs > 1 - ? "\@_tt_args{ $args } = splice(\@_, 0, $nargs)" - : "\$_tt_args{ $args } = shift"; + ? "\@args{ $args } = splice(\@_, 0, $nargs)" + : "\$args{ $args } = shift"; return <set('$ident', sub { my \$output = ''; - my (%_tt_args, \$_tt_params); + my (%args, \$params); $args; - \$_tt_params = shift; - \$_tt_params = { } unless ref(\$_tt_params) eq 'HASH'; - \$_tt_params = { \%_tt_args, %\$_tt_params }; + \$params = shift; + \$params = { } unless ref(\$params) eq 'HASH'; + \$params = { \%args, %\$params }; - my \$stash = \$context->localise(\$_tt_params); + my \$stash = \$context->localise(\$params); eval { $block }; @@ -9424,10 +9432,10 @@ EOF return <set('$ident', sub { - my \$_tt_params = \$_[0] if ref(\$_[0]) eq 'HASH'; + my \$params = \$_[0] if ref(\$_[0]) eq 'HASH'; my \$output = ''; - my \$stash = \$context->localise(\$_tt_params); + my \$stash = \$context->localise(\$params); eval { $block }; @@ -9507,9 +9515,9 @@ our $DEFAULT_STYLE = { }; our $QUOTED_ESCAPES = { - n => "\n", - r => "\r", - t => "\t", + n => "\n", + r => "\r", + t => "\t", }; our $CHOMP_FLAGS = qr/[-=~+]/; @@ -9536,7 +9544,7 @@ sub new { FILE_INFO => 1, GRAMMAR => undef, _ERROR => '', - FACTORY => $config->{ FACTORY } || 'Template::Directive', + FACTORY => 'Template::Directive', }, $class; # update self with any relevant keys in config @@ -9582,7 +9590,7 @@ sub new { $self->new_style($config) || return $class->error($self->error()); - + return $self; } @@ -9630,7 +9638,7 @@ sub parse { my ($tokens, $block); $info->{ DEBUG } = $self->{ DEBUG_DIRS } - unless defined $info->{ DEBUG }; + unless defined $info->{ DEBUG }; # store for blocks defined in the template (see define_block()) @@ -9642,7 +9650,7 @@ sub parse { # split file into TEXT/DIRECTIVE chunks $tokens = $self->split_text($text) - || return undef; ## RETURN ## + || return undef; ## RETURN ## push(@{ $self->{ FILEINFO } }, $info); @@ -9651,7 +9659,7 @@ sub parse { pop(@{ $self->{ FILEINFO } }); - return undef unless $block; ## RETURN ## + return undef unless $block; ## RETURN ## $self->debug("compiled main template document block:\n$block") if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; @@ -9672,13 +9680,12 @@ sub split_text { my $style = $self->{ STYLE }->[-1]; my ($start, $end, $prechomp, $postchomp, $interp ) = @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) }; - my $tags_dir = $self->{ANYCASE} ? qri : qr; my @tokens = (); my $line = 1; - return \@tokens ## RETURN ## - unless defined $text && length $text; + return \@tokens ## RETURN ## + unless defined $text && length $text; # extract all directives from the text while ($text =~ s/ @@ -9754,7 +9761,7 @@ sub split_text { # and now the directive, along with line number information if (length $dir) { # the TAGS directive is a compile-time switch - if ($dir =~ /^$tags_dir\s+(.*)/) { + if ($dir =~ /^TAGS\s+(.*)/i) { my @tags = split(/\s+/, $1); if (scalar @tags > 1) { ($start, $end) = map { quotemeta($_) } @tags; @@ -9789,7 +9796,7 @@ sub split_text { : ( 'TEXT', $text) ) if length $text; - return \@tokens; ## RETURN ## + return \@tokens; ## RETURN ## } @@ -9805,32 +9812,32 @@ sub interpolate_text { / ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1] | - ( \$ (?: # embedded variable [$2] - (?: \{ ([^\}]*) \} ) # ${ ... } [$3] - | - ([\w\.]+) # $word [$4] - ) - ) - /gx) { - - ($pre, $var, $dir) = ($1, $3 || $4, $2); - - # preceding text - if (defined($pre) && length($pre)) { - $line += $pre =~ tr/\n//; - $pre =~ s/\\\$/\$/g; - push(@tokens, 'TEXT', $pre); - } - # $variable reference + ( \$ (?: # embedded variable [$2] + (?: \{ ([^\}]*) \} ) # ${ ... } [$3] + | + ([\w\.]+) # $word [$4] + ) + ) + /gx) { + + ($pre, $var, $dir) = ($1, $3 || $4, $2); + + # preceding text + if (defined($pre) && length($pre)) { + $line += $pre =~ tr/\n//; + $pre =~ s/\\\$/\$/g; + push(@tokens, 'TEXT', $pre); + } + # $variable reference if ($var) { - $line += $dir =~ tr/\n/ /; - push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]); - } - # other '$' reference - treated as text - elsif ($dir) { - $line += $dir =~ tr/\n//; - push(@tokens, 'TEXT', $dir); - } + $line += $dir =~ tr/\n/ /; + push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]); + } + # other '$' reference - treated as text + elsif ($dir) { + $line += $dir =~ tr/\n//; + push(@tokens, 'TEXT', $dir); + } } return \@tokens; @@ -9848,108 +9855,108 @@ sub tokenise_directive { my @tokens = ( ); while ($text =~ - / - # strip out any comments - (\#[^\n]*) - | - # a quoted phrase matches in $3 - (["']) # $2 - opening quote, ' or " - ( # $3 - quoted text buffer - (?: # repeat group (no backreference) - \\\\ # an escaped backslash \\ - | # ...or... - \\\2 # an escaped quote \" or \' (match $1) - | # ...or... - . # any other character - | \n - )*? # non-greedy repeat - ) # end of $3 - \2 # match opening quote - | - # an unquoted number matches in $4 - (-?\d+(?:\.\d+)?) # numbers - | - # filename matches in $5 - ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+) - | - # an identifier matches in $6 - (\w+) # variable identifier - | - # an unquoted word or symbol matches in $7 - ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols - | [+\-*] # math operations - | \$\{? # dollar with option left brace - | => # like '=' - | [=!<>]?= | [!<>] # eqality tests - | &&? | \|\|? # boolean ops - | \.\.? # n..n sequence - | \S+ # something unquoted - ) # end of $7 - /gmxo) { - - # ignore comments to EOL - next if $1; - - # quoted string - if (defined ($token = $3)) { + / + # strip out any comments + (\#[^\n]*) + | + # a quoted phrase matches in $3 + (["']) # $2 - opening quote, ' or " + ( # $3 - quoted text buffer + (?: # repeat group (no backreference) + \\\\ # an escaped backslash \\ + | # ...or... + \\\2 # an escaped quote \" or \' (match $1) + | # ...or... + . # any other character + | \n + )*? # non-greedy repeat + ) # end of $3 + \2 # match opening quote + | + # an unquoted number matches in $4 + (-?\d+(?:\.\d+)?) # numbers + | + # filename matches in $5 + ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+) + | + # an identifier matches in $6 + (\w+) # variable identifier + | + # an unquoted word or symbol matches in $7 + ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols + | [+\-*] # math operations + | \$\{? # dollar with option left brace + | => # like '=' + | [=!<>]?= | [!<>] # eqality tests + | &&? | \|\|? # boolean ops + | \.\.? # n..n sequence + | \S+ # something unquoted + ) # end of $7 + /gmxo) { + + # ignore comments to EOL + next if $1; + + # quoted string + if (defined ($token = $3)) { # double-quoted string may include $variable references - if ($2 eq '"') { - if ($token =~ /[\$\\]/) { - $type = 'QUOTED'; - # unescape " and \ but leave \$ escaped so that - # interpolate_text() doesn't incorrectly treat it - # as a variable reference - for ($token) { - s/\\([^\$nrt])/$1/g; - s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge; - } - push(@tokens, ('"') x 2, - @{ $self->interpolate_text($token) }, - ('"') x 2); - next; - } + if ($2 eq '"') { + if ($token =~ /[\$\\]/) { + $type = 'QUOTED'; + # unescape " and \ but leave \$ escaped so that + # interpolate_text() doesn't incorrectly treat it + # as a variable reference + for ($token) { + s/\\([^\$nrt])/$1/g; + s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge; + } + push(@tokens, ('"') x 2, + @{ $self->interpolate_text($token) }, + ('"') x 2); + next; + } else { - $type = 'LITERAL'; - $token =~ s['][\\']g; - $token = "'$token'"; - } - } - else { - $type = 'LITERAL'; - $token = "'$token'"; - } - } - # number - elsif (defined ($token = $4)) { - $type = 'NUMBER'; - } - elsif (defined($token = $5)) { - $type = 'FILENAME'; - } - elsif (defined($token = $6)) { - # reserved words may be in lower case unless case sensitive - $uctoken = $anycase ? uc $token : $token; - if (defined ($type = $lextable->{ $uctoken })) { - $token = $uctoken; - } - else { - $type = 'IDENT'; - } - } - elsif (defined ($token = $7)) { - # reserved words may be in lower case unless case sensitive - $uctoken = $anycase ? uc $token : $token; - unless (defined ($type = $lextable->{ $uctoken })) { - $type = 'UNQUOTED'; - } - } + $type = 'LITERAL'; + $token =~ s['][\\']g; + $token = "'$token'"; + } + } + else { + $type = 'LITERAL'; + $token = "'$token'"; + } + } + # number + elsif (defined ($token = $4)) { + $type = 'NUMBER'; + } + elsif (defined($token = $5)) { + $type = 'FILENAME'; + } + elsif (defined($token = $6)) { + # reserved words may be in lower case unless case sensitive + $uctoken = $anycase ? uc $token : $token; + if (defined ($type = $lextable->{ $uctoken })) { + $token = $uctoken; + } + else { + $type = 'IDENT'; + } + } + elsif (defined ($token = $7)) { + # reserved words may be in lower case unless case sensitive + $uctoken = $anycase ? uc $token : $token; + unless (defined ($type = $lextable->{ $uctoken })) { + $type = 'UNQUOTED'; + } + } - push(@tokens, $type, $token); + push(@tokens, $type, $token); } - return \@tokens; ## RETURN ## + return \@tokens; ## RETURN ## } @@ -9960,7 +9967,7 @@ sub define_block { || return undef; $self->debug("compiled block '$name':\n$block") - if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; + if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; $defblock->{ $name } = $block; @@ -10015,7 +10022,7 @@ sub _parse { my ($self, $tokens, $info) = @_; my ($token, $value, $text, $line, $inperl); my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars); - my ($lhs, $len, $code); # rule contents + my ($lhs, $len, $code); # rule contents my $stack = [ [ 0, undef ] ]; # DFA stack @@ -10034,152 +10041,152 @@ sub _parse { my $in_string = 0; while(1) { - # get state number and state - $stateno = $stack->[-1]->[0]; - $state = $states->[$stateno]; - - # see if any lookaheads exist for the current state - if (exists $state->{'ACTIONS'}) { - - # get next token and expand any directives (i.e. token is an - # array ref) onto the front of the token list - while (! defined $token && @$tokens) { - $token = shift(@$tokens); - if (ref $token) { - ($text, $line, $token) = @$token; - if (ref $token) { - if ($info->{ DEBUG } && ! $in_string) { + # get state number and state + $stateno = $stack->[-1]->[0]; + $state = $states->[$stateno]; + + # see if any lookaheads exist for the current state + if (exists $state->{'ACTIONS'}) { + + # get next token and expand any directives (i.e. token is an + # array ref) onto the front of the token list + while (! defined $token && @$tokens) { + $token = shift(@$tokens); + if (ref $token) { + ($text, $line, $token) = @$token; + if (ref $token) { + if ($info->{ DEBUG } && ! $in_string) { # - - - - - - - - - - - - - - - - - - - - - - - - - - # This is gnarly. Look away now if you're easily + # This is gnarly. Look away now if you're easily # frightened. We're pushing parse tokens onto the # pending list to simulate a DEBUG directive like so: - # [% DEBUG msg line='20' text='INCLUDE foo' %] + # [% DEBUG msg line='20' text='INCLUDE foo' %] # - - - - - - - - - - - - - - - - - - - - - - - - - - my $dtext = $text; - $dtext =~ s[(['\\])][\\$1]g; - unshift(@$tokens, - DEBUG => 'DEBUG', - IDENT => 'msg', - IDENT => 'line', - ASSIGN => '=', - LITERAL => "'$line'", - IDENT => 'text', - ASSIGN => '=', - LITERAL => "'$dtext'", - IDENT => 'file', - ASSIGN => '=', - LITERAL => "'$info->{ name }'", - (';') x 2, - @$token, - (';') x 2); - } - else { - unshift(@$tokens, @$token, (';') x 2); - } - $token = undef; # force redo - } - elsif ($token eq 'ITEXT') { - if ($inperl) { - # don't perform interpolation in PERL blocks - $token = 'TEXT'; - $value = $text; - } - else { - unshift(@$tokens, - @{ $self->interpolate_text($text, $line) }); - $token = undef; # force redo - } - } - } - else { - # toggle string flag to indicate if we're crossing - # a string boundary - $in_string = ! $in_string if $token eq '"'; - $value = shift(@$tokens); - } - }; - # clear undefined token to avoid 'undefined variable blah blah' - # warnings and let the parser logic pick it up in a minute - $token = '' unless defined $token; - - # get the next state for the current lookahead token - $action = defined ($lookup = $state->{'ACTIONS'}->{ $token }) - ? $lookup - : defined ($lookup = $state->{'DEFAULT'}) - ? $lookup - : undef; - } - else { - # no lookahead actions - $action = $state->{'DEFAULT'}; - } + my $dtext = $text; + $dtext =~ s[(['\\])][\\$1]g; + unshift(@$tokens, + DEBUG => 'DEBUG', + IDENT => 'msg', + IDENT => 'line', + ASSIGN => '=', + LITERAL => "'$line'", + IDENT => 'text', + ASSIGN => '=', + LITERAL => "'$dtext'", + IDENT => 'file', + ASSIGN => '=', + LITERAL => "'$info->{ name }'", + (';') x 2, + @$token, + (';') x 2); + } + else { + unshift(@$tokens, @$token, (';') x 2); + } + $token = undef; # force redo + } + elsif ($token eq 'ITEXT') { + if ($inperl) { + # don't perform interpolation in PERL blocks + $token = 'TEXT'; + $value = $text; + } + else { + unshift(@$tokens, + @{ $self->interpolate_text($text, $line) }); + $token = undef; # force redo + } + } + } + else { + # toggle string flag to indicate if we're crossing + # a string boundary + $in_string = ! $in_string if $token eq '"'; + $value = shift(@$tokens); + } + }; + # clear undefined token to avoid 'undefined variable blah blah' + # warnings and let the parser logic pick it up in a minute + $token = '' unless defined $token; + + # get the next state for the current lookahead token + $action = defined ($lookup = $state->{'ACTIONS'}->{ $token }) + ? $lookup + : defined ($lookup = $state->{'DEFAULT'}) + ? $lookup + : undef; + } + else { + # no lookahead actions + $action = $state->{'DEFAULT'}; + } - # ERROR: no ACTION - last unless defined $action; + # ERROR: no ACTION + last unless defined $action; - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # shift (+ive ACTION) - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if ($action > 0) { - push(@$stack, [ $action, $value ]); - $token = $value = undef; - redo; - }; + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # shift (+ive ACTION) + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if ($action > 0) { + push(@$stack, [ $action, $value ]); + $token = $value = undef; + redo; + }; - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # reduce (-ive ACTION) - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ($lhs, $len, $code) = @{ $rules->[ -$action ] }; + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # reduce (-ive ACTION) + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ($lhs, $len, $code) = @{ $rules->[ -$action ] }; - # no action imples ACCEPTance - $action - or $status = ACCEPT; + # no action imples ACCEPTance + $action + or $status = ACCEPT; - # use dummy sub if code ref doesn't exist - $code = sub { $_[1] } - unless $code; + # use dummy sub if code ref doesn't exist + $code = sub { $_[1] } + unless $code; - @codevars = $len - ? map { $_->[1] } @$stack[ -$len .. -1 ] - : (); + @codevars = $len + ? map { $_->[1] } @$stack[ -$len .. -1 ] + : (); - eval { - $coderet = &$code( $self, @codevars ); - }; - if ($@) { - my $err = $@; - chomp $err; - return $self->_parse_error($err); - } + eval { + $coderet = &$code( $self, @codevars ); + }; + if ($@) { + my $err = $@; + chomp $err; + return $self->_parse_error($err); + } - # reduce stack by $len - splice(@$stack, -$len, $len); + # reduce stack by $len + splice(@$stack, -$len, $len); - # ACCEPT - return $coderet ## RETURN ## - if $status == ACCEPT; + # ACCEPT + return $coderet ## RETURN ## + if $status == ACCEPT; - # ABORT - return undef ## RETURN ## - if $status == ABORT; + # ABORT + return undef ## RETURN ## + if $status == ABORT; - # ERROR - last - if $status == ERROR; + # ERROR + last + if $status == ERROR; } continue { - push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, - $coderet ]), + push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, + $coderet ]), } - # ERROR ## RETURN ## + # ERROR ## RETURN ## return $self->_parse_error('unexpected end of input') - unless defined $value; + unless defined $value; # munge text of last directive to make it readable return $self->_parse_error("unexpected end of directive", $text) - if $value eq ';'; # end of directive SEPARATOR + if $value eq ';'; # end of directive SEPARATOR return $self->_parse_error("unexpected token ($value)", $text); } @@ -10194,7 +10201,7 @@ sub _parse_error { $line = 'unknown' unless $line; $msg .= "\n [% $text %]" - if defined $text; + if defined $text; return $self->error("line $line: $msg"); } @@ -10208,10 +10215,10 @@ sub _dump { my $key; foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE - PRE_CHOMP POST_CHOMP V1DOLLAR )) { - my $val = $self->{ $key }; - $val = '' unless defined $val; - $output .= sprintf($format, $key, $val); + PRE_CHOMP POST_CHOMP V1DOLLAR )) { + my $val = $self->{ $key }; + $val = '' unless defined $val; + $output .= sprintf($format, $key, $val); } $output .= '}'; @@ -17423,7 +17430,8 @@ proto._dotop = function(root, item, args, lvalue) { } - if (atroot || root.constructor == Object || root == this.data.GLOBAL) { + //root is complex object, not scalar + if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) { if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null if (typeof root[item] == 'function') { @@ -17431,7 +17439,9 @@ proto._dotop = function(root, item, args, lvalue) { } else { return root[item]; } - } /*else if (atroot && typeof this.data.GLOBAL[item] != 'undefined' && this.__config__.GLOBAL && item != 'LOCAL' ) { + } /* + //this section was for automatic global scope access + else if (atroot && typeof this.data.GLOBAL[item] != 'undefined' && this.__config__.GLOBAL && item != 'LOCAL' ) { if (typeof this.data.GLOBAL[item] == 'function' ) { result = this.data.GLOBAL[item].apply(root,args); @@ -17460,7 +17470,7 @@ proto._dotop = function(root, item, args, lvalue) { for (var i = 0; i < item.length; i++) result.push(root[item[i]]); return result; } - } else if ( (root.constructor != Object) && (root instanceof Object) ) { + } /*else if ( (root.constructor != Object) && (root instanceof Object) ) { //this section was proposed for calling method on blessed reference in Perl //not sure how well it is playing with javascript try { @@ -17501,7 +17511,7 @@ proto._dotop = function(root, item, args, lvalue) { } } - } else if (this.string_functions[item] && !lvalue) { + }*/ else if (this.string_functions[item] && !lvalue) { args.unshift(root); result = this.string_functions[item].apply(this, args); } else if (this.list_functions[item] && !lvalue) { @@ -17534,6 +17544,7 @@ proto._assign = function(root, item, args, value, set_default) { } if (atroot || root.constructor == Object || root == this.data.GLOBAL) { +//this section was for automatic global scope access // if (atroot && this.__config__.GLOBAL && typeof root[item] == 'undefined' && typeof this.data.GLOBAL[item] != 'undefined' && !set_default) { // return this.data.GLOBAL[item] = value; // } @@ -17858,6 +17869,11 @@ proto.hash_functions.nsort = function(hash) { return list.sort(function(a, b) { return (a-b) }); } +// item return a value by key +proto.hash_functions.item = function(hash, key) { + return hash[key]; +} + // size number of pairs proto.hash_functions.size = function(hash) { var size = 0; @@ -19709,7 +19725,7 @@ use warnings; sub main { return &kernel } sub kernel { <<'...'; -if(typeof Jemplate=="undefined"){var Jemplate=function(){this.init.apply(this,arguments)}}Jemplate.VERSION="0.22";Jemplate.process=function(){var A=new Jemplate(Jemplate.prototype.config);return A.process.apply(A,arguments)};(function(){if(!Jemplate.templateMap){Jemplate.templateMap={}}var proto=Jemplate.prototype={};proto.config={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,FILTERS:{},INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};proto.defaults={AUTO_RESET:true,BLOCKS:{},CONTEXT:null,DEBUG_UNDEF:false,DEFAULT:null,ERROR:null,EVAL_JAVASCRIPT:false,GLOBAL:true,SCOPE:this,INCLUDE_PATH:[""],INTERPOLATE:false,OUTPUT:null,PLUGINS:{},POST_PROCESS:[],PRE_PROCESS:[],PROCESS:null,RECURSION:false,STASH:null,TOLERANT:null,VARIABLES:{},WRAPPER:[]};Jemplate.init=function(config){Jemplate.prototype.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof Jemplate.prototype.config[i]=="undefined"){Jemplate.prototype.config[i]=Jemplate.prototype.defaults[i]}}};proto.init=function(config){this.config=config||{};for(var i in Jemplate.prototype.defaults){if(typeof this.config[i]=="undefined"){this.config[i]=Jemplate.prototype.defaults[i]}}};proto.process=function(template,data,output){var context=this.config.CONTEXT||new Jemplate.Context();context.config=this.config;context.stash=new Jemplate.Stash(this.config.STASH,this.config);context.__filter__=new Jemplate.Filter();context.__filter__.config=this.config;context.__plugin__=new Jemplate.Plugin();context.__plugin__.config=this.config;var result;var proc=function(input){try{if(typeof context.config.PRE_PROCESS=="string"){context.config.PRE_PROCESS=[context.config.PRE_PROCESS]}for(var i=0;i/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index/g,">");text=text.replace(/"/g,""");return text};proto.filters.html_para=function(text){var lines=text.split(/(?:\r?\n){2,}/);return"

\n"+lines.join("\n

\n\n

\n")+"

\n"};proto.filters.html_break=function(text){return text.replace(/(\r?\n){2,}/g,"$1
$1
$1")};proto.filters.html_line_break=function(text){return text.replace(/(\r?\n)/g,"$1
$1")};proto.filters.uri=function(text){return encodeURIComponent(text)};proto.filters.url=function(text){return encodeURI(text)};proto.filters.indent=function(text,args){var pad=args[0];if(!text){return null}if(typeof pad=="undefined"){pad=4}var finalpad="";if(typeof pad=="number"||String(pad).match(/^\d$/)){for(var i=0;i=0;i=i-size){list.unshift(string.substr(i,size))}if(string.length%size){list.unshift(string.substr(0,string.length%size))}}else{for(i=0;i=0)?1:0};proto.string_functions.size=function(string){return 1};proto.string_functions.split=function(string,re){var regexp=new RegExp(re);var list=string.split(regexp);return list};proto.list_functions={};proto.list_functions["typeof"]=function(list){return"array"};proto.list_functions.list=function(list){return list};proto.list_functions.join=function(list,str){return list.join(str)};proto.list_functions.sort=function(list,key){if(typeof (key)!="undefined"&&key!=""){return list.sort(function(a,b){if(a[key]==b[key]){return 0}else{if(a[key]>b[key]){return 1}else{return -1}}})}return list.sort()};proto.list_functions.nsort=function(list){return list.sort(function(a,b){return(a-b)})};proto.list_functions.grep=function(list,re){var regexp=new RegExp(re);var result=[];for(var i=0;i=0;i--){result.push(list[i])}return result};proto.list_functions.merge=function(list){var result=[];var push_all=function(elem){if(elem instanceof Array){for(var j=0;j0?this.object_keys[index-1]:"";this.next=index0?object[index-1]:"";this.next=index - - Jemplate Testing - t/basics.t.js - - - - - - - - - - - - - - diff --git a/tests/js/global.js b/tests/js/global.js index a88fa88..40adedd 100644 --- a/tests/js/global.js +++ b/tests/js/global.js @@ -1,10 +1,14 @@ global_foo = 'global_foo'; + global_object = { - str : 'global_object_foo', - func_sum : function (a,b) { - return a+b; - } + str : 'global_object_str', + func_sum : function (a,b) { + return a+b; + } }; + global_multiply = function (a,b) { return a*b; } + +global_multiply.function_property = 'function_property'; \ No newline at end of file diff --git a/tests/t/stash.t.js b/tests/t/stash.t.js index 091f915..4c0950e 100644 --- a/tests/t/stash.t.js +++ b/tests/t/stash.t.js @@ -6,7 +6,7 @@ var filters = { raw_context: 'raw_context' }; -t.plan(6); +t.plan(7); t.filters(filters); t.run_is('jemplate', 'output'); @@ -54,87 +54,155 @@ arg: abc {} --- jemplate global-scope-access.html +#1 [% GLOBAL.global_foo %] +#2 [% GLOBAL.global_object.str %] +#3 [% GLOBAL.global_object.func_sum(1,1) %] +#4 [% GLOBAL.global_multiply(1,10) %] +#5 [% global_foo %] -[% 1 %] +#6 [% global_object.str %] +#7 [% global_object.func_sum(1,1) %] +#8 [% global_multiply(1,10) %] -[% 1 %] +eof --- output +#1 global_foo -global_object_foo +#2 +global_object_str +#3 2 +#4 10 +#5 -1 +#6 +#7 +#8 -1 +eof === Advanced Global Scope Access --- raw_context {} --- jemplate global-scope-access2.html +#1 [% - global_foo = '|local_foo'; #creates new local variable + global_foo = 'local_foo'; #creates local variable GLOBAL.global_foo; - global_foo; %] +#2 +[% global_foo %] +#3 [% local_var = "foo"; #new variables are always local GLOBAL.local_var; #empty %] +#4 [% - GLOBAL.new_global_var = "|foo"; #new global vars could be created only this way + GLOBAL.new_global_var = "new_global_var"; #new global vars could be created only this way new_global_var; #empty - GLOBAL.new_global_var; - new_global_var = "|foo2"; +%] +#5 +[% GLOBAL.new_global_var; %] +#6 +[% + new_global_var = "local_value"; new_global_var; #not empty - GLOBAL.new_global_var; %] +#7 +[% GLOBAL.new_global_var; %] +#8 [% - GLOBAL.global_foo = "global_foo"; - LOCAL.global_foo = "|masked"; #masking global variable - GLOBAL.global_foo; + global_foo = "global_foo2"; + global_foo; +%] +#9 +[% + LOCAL.global_foo = "masked"; #LOCAL access global_foo; %] --- output -global_foo|local_foo +#1 +global_foo +#2 +local_foo +#3 + +#4 -|foo|foo2|foo -global_foo|masked +#5 +new_global_var +#6 +local_value +#7 +new_global_var +#8 +global_foo2 +#9 +masked === RAW directive --- raw_context {} --- jemplate global-scope-access3.html -[% +#1 +[% RAW global_foo; global_foo; +%] +#2 +[% RAW global_object; - '\n';global_object.str; - '\n';global_object.func_sum(1,1); + global_object.str; +%] +#3 +[% global_object.func_sum(1,1); %] +#4 +[% RAW global_multiply; - '\n';global_multiply(1,10); + global_multiply(1,10); %] -[% - RAW global_object; +#5 +[% global_object.str = 'new_str'; - '\n';global_object.str; + GLOBAL.global_object.str; %] --- output +#1 global_foo -global_object_foo +#2 +global_object_str +#3 2 +#4 10 - +#5 new_str +=== Function property access +--- raw_context +{} +--- jemplate +global-scope-access4.html +#1 +[% + #RAW global_multiply; + #global_multiply.function_property; + 'function_property'; +%] +--- output +#1 +function_property + */ diff --git a/tests/tjs/config.yaml b/tests/tjs/config.yaml index 61a2764..1b1ade3 100644 --- a/tests/tjs/config.yaml +++ b/tests/tjs/config.yaml @@ -4,4 +4,6 @@ import_libs: - runtime.js - jt.js - jt-greeting.js + - js/jemplate_dummy_plugin.js + - js/global.js templates: ../template