Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge remote branch 'remotes/petdance/master'

Conflicts:
	crank
  • Loading branch information...
commit 87b70b834bae203f8a7dcb406cccf53233892f73 2 parents 1054f29 + 8b6be7d
Shlomi Fish authored February 13, 2010
3  .gitignore
... ...
@@ -0,0 +1,3 @@
  1
+*~
  2
+build/
  3
+.DS_Store
25  INSTALL
... ...
@@ -0,0 +1,25 @@
  1
+These are short installation instructions. They contain what needs to be done
  2
+in order to install and play around with the Perl101.org codebase.
  3
+
  4
+- Install a webserver (apache, lighttpd, etc.).
  5
+- Create the "build" folder for the "crank" script.
  6
+- Make sure that the "href" path defined in "section.tt"'s javascript matches
  7
+  where your CGI folder will be. It assumes it well be in "/cgi-bin".
  8
+- Make sure that the path for the captcha pictures hardcoded in
  9
+  "tt/show_captcha.tt" is good for you.
  10
+- If you need to adjust the path in "show_captcha.tt", recompile the templates
  11
+  using the following command:
  12
+  $ jemplate --compile tt/show_captcha.tt tt/show_result.tt > static/js/jmpls.js
  13
+  (you must have Jemplate installed)
  14
+- Run the crank script.
  15
+- Copy the built HTML files in your "build" folder into the document root of
  16
+  your Perl101.
  17
+- Copy the "static" folder to the same document root.
  18
+- Copy over the formmail.pl and auth.pl into the appropriate CGI folder
  19
+  (usually this would be "cgi-bin")
  20
+- Make sure the paths for $data_folder in both files reflects where you want
  21
+  to keep the captchas. I would recommend writing a full path.
  22
+- Change the $recipient to your email address in "formmail.pl".
  23
+  (you can comment off the entire Email::Stuff part to avoid emails entirely)
  24
+
  25
+Written by Sawyer X (2009)
27  Makefile
@@ -2,24 +2,23 @@
2 2
 	crank \
3 3
 	clean
4 4
 
  5
+BUILD=build
  6
+SOURCE=s
  7
+
5 8
 default: crank
6 9
 
7  
-crank:
8  
-	rm -fr 101/*.html
9  
-	mkdir 101/ || true > /dev/null 2>&1
10  
-	perl crank 101.pod
11  
-	rsync -azu --delete \
12  
-		--exclude=.svn --exclude='*~' \
13  
-		static/ 101/static/
14  
-	rsync -azu --delete \
15  
-		--exclude=.svn --exclude='*~' \
16  
-		s/ 101/s/
17  
-	cp s/*.ico 101/
  10
+crank: clean
  11
+	mkdir -p $(BUILD)/ || true > /dev/null 2>&1
  12
+	perl crank --podpath=$(SOURCE) --buildpath=$(BUILD)
  13
+	cp -R static/* $(BUILD)/
18 14
 
19 15
 clean:
20  
-	rm -fr 101/
  16
+	rm -fr $(BUILD)
  17
+
  18
+test: crank
  19
+	prove t/html.t
21 20
 
22 21
 # This is only useful for Andy
23 22
 rsync:
24  
-	rsync -azu -e ssh --delete \
25  
-	    101/ petdance@midhae.pair.com:~/p/
  23
+	rsync -azu -e ssh --delete --verbose \
  24
+	    $(BUILD)/ andy@huggy.petdance.com:/srv/p101
59  README
... ...
@@ -0,0 +1,59 @@
  1
+These should be instructions for how the crank operation works,
  2
+what the directory structure is and how to generate perl101.org.
  3
+
  4
+If anything is unclear, feel free to email me at xsawyerx@cpan.org or send
  5
+a patch for the documentation.
  6
+
  7
+Perl101.org code has several components:
  8
+* The (source) POD files under the "s" folder:
  9
+    The POD files are the core content of perl101.org. It's written in
  10
+    POD form. You can read the documentation of how to write POD on perldoc,
  11
+    using either the command "perldoc perlpod" or googling for "perlpod".
  12
+    These POD files are renderes to HTML by the generator script documented
  13
+    below.
  14
+
  15
+* The static JS, images and design under the "static" folder:
  16
+    Some things are static, such as images, the CSS layout, javascript
  17
+    functions and the sorts. You can find those in the "static" folder.
  18
+    There is basic CSS for handheld, print and web.
  19
+    Currently the only image is the Perl101 logo.
  20
+    The Javascript files are the Jemplate engine (Jemplate can be found on
  21
+    CPAN) and a compiled Template Toolkit template file to Javascript.
  22
+
  23
+* The Template::Toolkit templates under the "tt" folder:
  24
+    The generation engine uses the HTMLified POD content in templates in order
  25
+    to render the complete website. These are the templates in the "tt" folder.
  26
+    There is one single template called "show.tt" which is not directly used.
  27
+    It is compiled to javascript using "jemplate" command line tool and then
  28
+    put in the JS folder in "static".
  29
+
  30
+    Each section in Perl101.org is rendered independently using the
  31
+    "section.tt" template. If you want to control each section, that's the
  32
+    template you want to check out.
  33
+
  34
+    It's simple HTML with Template::Toolkit tags. Template::Toolkit can be
  35
+    found on CPAN.
  36
+
  37
+* All the generated files under the "build" folder:
  38
+    Once the generation script generated the files, it saves them in the
  39
+    "build" folder. The folder doesn't exist by default (Perl101.org is on
  40
+    Github.com and Git cannot monitor empty folders so you'll need to create
  41
+    this folder yourself. The generation script will alert you if it is
  42
+    missing.
  43
+
  44
+    This folder can be seen as the "htdocs" or "httpdocs" people are used
  45
+    to.
  46
+
  47
+* The CGI scripts used for mailing and image creation, in the main folder:
  48
+    Right now there are only two scripts:
  49
+        - "auth.pl": this script is not production-ready yet. It's creates
  50
+          captchas for the feedback form. It will try to create them in the
  51
+          default 'captcha' folder. You'll probably have to create it.
  52
+        - "formmail.pl": this script sends the form submitted (via AJAX)
  53
+          and returns an answer or error.
  54
+
  55
+* The generator script in the main folder:
  56
+    The generator script goes over all the POD files in "s", renders them using
  57
+    the templates in folder "tt" and then puts them as static files in the
  58
+    "build" folder. You can specify a different "build" folder or it will
  59
+    by default look for a folder named "build".
91  auth.pl
... ...
@@ -0,0 +1,91 @@
  1
+#!/usr/bin/perl
  2
+
  3
+use strict;
  4
+use warnings;
  5
+
  6
+use File::stat;
  7
+use File::Spec;
  8
+use File::Slurp;
  9
+use Digest::MD5 'md5_hex';
  10
+use CGI::Simple;
  11
+use Authen::Captcha;
  12
+use GD::SecurityImage;
  13
+
  14
+sub create_formula {
  15
+    my @numbers   = ( 0 .. 20 );
  16
+    my %operators = (
  17
+        '+' => sub { $_[0] + $_[1] },
  18
+        '-' => sub { $_[0] - $_[1] },
  19
+        '*' => sub { $_[0] * $_[1] },
  20
+        '/' => sub { $_[0] * $_[1] },
  21
+    );
  22
+    my ( $num1, $op, $num2, $accepted );
  23
+
  24
+    while ( ! $accepted ) {
  25
+        $num1 = $numbers[ rand scalar @numbers ];
  26
+        $num2 = $numbers[ rand scalar @numbers ];
  27
+        $op   = ( keys %operators )[ rand scalar keys %operators ];
  28
+
  29
+        # avoiding edge cases in division
  30
+        if ( $op eq '/' ) {
  31
+            if ( $num2 == 0 ) {
  32
+                # avoiding division by zero
  33
+                next;
  34
+            } elsif ( $num1 % $num2 != 0 ) {
  35
+                # check if easily divisable
  36
+                next;
  37
+            }
  38
+        }
  39
+
  40
+        $accepted++;
  41
+    }
  42
+
  43
+    return [ "$num1  $op  $num2", $operators{$op}->( $num1, $num2 ) ];
  44
+}
  45
+
  46
+sub create_image {
  47
+    my $text = shift;
  48
+
  49
+    # Create a normal image
  50
+    my $image = GD::SecurityImage->new(
  51
+        width   => 100,
  52
+        height  => 50,
  53
+        lines   => 3,
  54
+        #gd_font => 'giant',
  55
+        rndmax  => 3,
  56
+    );
  57
+
  58
+    $image->random($text);
  59
+    $image->create(normal => 'rect');
  60
+
  61
+    return $image;
  62
+}
  63
+
  64
+my $cgi                  = CGI::Simple->new();
  65
+my $data_folder          = 'captchas';
  66
+my ( $formula, $result ) = @{ create_formula() };
  67
+my $md5sum               = md5_hex($result);
  68
+my $image                = create_image($formula);
  69
+my $really_delete        = 1;  # set to 0 or '' for testing purposes
  70
+my $captcha_timeout      = 10; # allow them to exit for 1 minute
  71
+
  72
+-d $data_folder || die "Cannot find data folder ($data_folder)";
  73
+
  74
+my ( $image_data, $mime_type, $random_number ) = $image->out;
  75
+my $image_filename = File::Spec->catfile( $data_folder, "$md5sum.png" );
  76
+
  77
+# write the new file
  78
+write_file( $image_filename, { binmode => ':raw' }, $image_data );
  79
+
  80
+# delete older files
  81
+my $captcha_regex = qr/^\w+\.png$/;
  82
+
  83
+foreach my $file ( read_dir($data_folder) ) {
  84
+    $file =~ $captcha_regex || next;
  85
+    my $filename = File::Spec->catfile( $data_folder, $file );
  86
+
  87
+    if ( stat($filename)->ctime() < ( time() - $captcha_timeout ) ) {
  88
+        $really_delete && unlink $filename;
  89
+    }
  90
+}
  91
+
54  crank
@@ -3,20 +3,42 @@
3 3
 use strict;
4 4
 use warnings;
5 5
 
6  
-BEGIN {
7  
-    eval 'use Carp::Always';
8  
-}
  6
+use Carp::Always;
  7
+use Getopt::Long;
  8
+use File::Slurp;
  9
+use Pod::Simple 3.13;
9 10
 
10 11
 use Template ();
11 12
 use Template::Constants qw( :debug :chomp );
12 13
 
  14
+my $podpath   = 's';
  15
+my $buildpath = 'build';
  16
+
  17
+GetOptions(
  18
+    'podpath:s'   => \$podpath,
  19
+    'buildpath:s' => \$buildpath,
  20
+) or exit;
  21
+
  22
+if ( ! -d $buildpath || ! -w $buildpath ) {
  23
+    die "Buildpath ($buildpath) doesn't exist or is not writable";
  24
+}
  25
+
13 26
 my %defaults = (
  27
+<<<<<<< HEAD
14 28
     INCLUDE_PATH    => [ qw( tt ) ],
15 29
     OUTPUT_PATH     => $ENV{'PERL101_OUT_PATH'} || '/srv/p101/',
16 30
     DEBUG           => DEBUG_UNDEF,
17 31
     TRIM            => CHOMP_ALL,
18 32
     PRE_CHOMP       => 1,
19 33
     POST_CHOMP      => 1,
  34
+=======
  35
+    INCLUDE_PATH => [ qw( tt ) ],
  36
+    OUTPUT_PATH  => $buildpath,
  37
+    DEBUG        => DEBUG_UNDEF,
  38
+    TRIM         => CHOMP_ALL,
  39
+    PRE_CHOMP    => 1,
  40
+    POST_CHOMP   => 1,
  41
+>>>>>>> remotes/petdance/master
20 42
 );
21 43
 
22 44
 my $tt = Template->new( \%defaults );
@@ -25,18 +47,18 @@ my $vars = {};
25 47
 my @podfiles;
26 48
 my @sidelinks;
27 49
 
28  
-for ( get_sections( 's/' ) ) {
  50
+for ( get_sections( $podpath ) ) {
29 51
     my ($sectionfile, $sectiontext) = @{$_};
30 52
 
31  
-    my $podfile = "s/$sectionfile.pod";
  53
+    my $podfile  = "$podpath/$sectionfile.pod";
32 54
     my $htmlfile = "$sectionfile.html";
33 55
     push( @sidelinks, {
34 56
         filename => $htmlfile,
35  
-        text => $sectiontext,
  57
+        text     => $sectiontext,
36 58
     } );
37 59
     push( @podfiles, {
38  
-        section => $sectiontext,
39  
-        podfile => $podfile,
  60
+        section  => $sectiontext,
  61
+        podfile  => $podfile,
40 62
         htmlfile => $htmlfile,
41 63
     } );
42 64
 }
@@ -44,7 +66,7 @@ for ( get_sections( 's/' ) ) {
44 66
 for my $vars ( @podfiles ) {
45 67
     $vars->{content} = pod2html( $vars->{podfile} );
46 68
     $vars->{sidelinks} = \@sidelinks;
47  
-    $tt->process( 'section.tt', $vars, $vars->{htmlfile} ) || die $tt->error;
  69
+    $tt->process( 'page.ttml', $vars, $vars->{htmlfile} ) || die $tt->error;
48 70
 }
49 71
 
50 72
 sub get_sections {
@@ -87,13 +109,16 @@ sub pod2html {
87 109
     $parser->html_header_after_title( '' );
88 110
     $parser->html_footer( '' );
89 111
 
  112
+    # Manually adjust the stuff we passed thru earlier
  113
+    my $podtext = read_file( $podfile );
  114
+
  115
+    $podtext =~ s{P<(.+?)>}{L<$1|http://perldoc.perl.org/$1.html>}g;
  116
+    $podtext =~ s{M<(.+?)>}{L<$1|http://search.cpan.org/perldoc?$1>}g;
  117
+
90 118
     $parser->complain_stderr( 1 );
91 119
     $parser->output_string( \$html );
92  
-    $parser->parse_file( $podfile );
  120
+    $parser->parse_string_document( $podtext );
93 121
 
94  
-    # Manually adjust the stuff we passed thru earlier
95  
-    $html =~ s{P<(.+?)>}{<a href="http://perldoc.perl.org/$1.html">$1</a>}g;
96  
-    $html =~ s{M<(.+?)>}{<a href="http://search.cpan.org/perldoc?$1">$1</a>}g;
97 122
 
98 123
     return $html;
99 124
 }
@@ -120,6 +145,9 @@ sub new {
120 145
         $tagmap->{"/$code"} = ">";
121 146
     }
122 147
 
  148
+    $tagmap->{'VerbatimFormatted'} =
  149
+        qq{\n<pre class="prettyprint lang-perl">\n};
  150
+
123 151
     return $self;
124 152
 }
125 153
 
48  formmail.pl
... ...
@@ -0,0 +1,48 @@
  1
+#!/usr/bin/perl
  2
+
  3
+use strict;
  4
+use warnings;
  5
+
  6
+use CGI; # or CGI::Simple ?
  7
+use JSON::XS;
  8
+use Email::Stuff;
  9
+
  10
+sub error {
  11
+    my $error_msg = shift;
  12
+
  13
+    # we don't really need those since we're updating the page with Jemplate
  14
+    #my $return_link_url   = $cgi->param('return_link_url')   || q{};
  15
+    #my $return_link_title = $cgi->param('return_link_title') || q{};
  16
+
  17
+    print encode_json { error => $error_msg }; 
  18
+
  19
+    exit 0;
  20
+}
  21
+
  22
+my $cgi = CGI->new();
  23
+
  24
+print $cgi->header( -charset => 'UTF-8' );
  25
+
  26
+my $subject   = $cgi->param('subject')  || q{};
  27
+my $name      = $cgi->param('realname') || q{};
  28
+my $email     = $cgi->param('email')    || q{};
  29
+my $text      = $cgi->param('text')     || q{};
  30
+my $recipient = q{andy@petdance.com}; # this shouldn't be in the form
  31
+my $from      = qq{$name <$email>};
  32
+
  33
+if ( !$name || !$text ) {
  34
+    # a name and text are essential
  35
+    error('Missing name or text');
  36
+}
  37
+
  38
+# this should work but it didn't for me
  39
+# maybe it was my sendmail definitions
  40
+# but i didn't have the time to debug it
  41
+Email::Stuff->from($from)
  42
+            ->to($recipient)
  43
+            ->text_body($text)
  44
+            ->subject($subject)
  45
+            ->send;
  46
+
  47
+print encode_json { success => 'imminent' };
  48
+
17  s/arrays.pod
Source Rendered
@@ -5,8 +5,9 @@
5 5
 The C<qw> operator makes creating arrays easy.  It means "quote on
6 6
 whitespace into a list":
7 7
 
  8
+    # Perl 5
8 9
     my @stooges = qw( Larry Curly Moe Iggy );
9  
-
  10
+    # or
10 11
     my @stooges = qw(
11 12
         Larry
12 13
         Curly
@@ -14,9 +15,21 @@ whitespace into a list":
14 15
         Iggy
15 16
     );
16 17
 
  18
+In Perl 6, the C<qw> is simplified:
  19
+
  20
+    my @stooges = < Larry Curly Moe Iggy >;
  21
+    # or
  22
+    my @stooges = <
  23
+        Larry
  24
+        Curly
  25
+        Moe
  26
+        Iggy
  27
+    >;
  28
+
17 29
 The elements do not interpolate, so:
18 30
 
19  
-    my @array = qw( $100,000 );
  31
+    my @array = qw( $100,000 ); # Perl 5
  32
+    my @array = < $100,000 >;   # Perl 6
20 33
 
21 34
 The single element of C<@array> is '$100,000'.
22 35
 
71  s/debugging.pod
Source Rendered
@@ -2,18 +2,77 @@
2 2
 
3 3
 =head2 Turn on C<strict> and C<warnings>
4 4
 
5  
-The very first thing you must always check when debugging your code
6  
-is whether the C<strict> and C<warnings> pragmata are turned on.
7  
-Make sure you have
  5
+Whenever debugging code, make sure the C<strict> and C<warnings>
  6
+pragmata are turned on.
  7
+
  8
+Put these two lines
8 9
 
9 10
     use strict;
10 11
     use warnings;
11 12
 
12 13
 at the top of any program you're trying to debug, or may want to
13  
-debug in the future.  That really means every program ever, no?
  14
+debug in the future.  (If you think about it, that's every program
  15
+ever, isn't it?)
  16
+
  17
+The L<http://perldoc.perl.org/strict.html> strict pragma forces you
  18
+to use a number of features that allow Perl to find errors during
  19
+compile time.  First and foremost, under C<strict>, variables must
  20
+be declared before they are used; in most cases, this means using
  21
+C<my>:
  22
+
  23
+    use strict;
  24
+    my $foo = 7;                # OK, normal variable
  25
+    print "foo is $fooo\n";     # Perl complains and aborts compilation
  26
+
  27
+Without C<strict>, Perl would happily run the above programming,
  28
+and you would be sad, wondering why C<$foo> didn't have a value.
  29
+Enabling strictures can save a lot of headache induced by typos.
  30
+
  31
+Additionally, C<strict> disallows the use of most barewords:
  32
+
  33
+    no strict;
  34
+    $foo = Lorem;
  35
+    print "$foo\n";         # Prints "Lorem"
  36
+
  37
+    use strict;
  38
+    my $foo = ipsum;        # Complains about bareword
  39
+    $foo = (
  40
+        Lorem   => 'ipsum'  # OK, barewords allowed on left of =>
  41
+    );
  42
+
  43
+    $SIG{PIPE} = handler;   # Complains
  44
+    $SIG{PIPE} = \&handler; # OK
  45
+    $SIG{PIPE} = "handler"; # Also, OK, but above is preferred
  46
+
  47
+Finally, enabling C<strict> throws a runtime error if you use
  48
+L<http://perldoc.perl.org/perlref.html#Symbolic-references> symbolic
  49
+references:
  50
+
  51
+    no strict;
  52
+    $name = "foo";
  53
+    $$name = "bar";             # Sets the variable $foo to 1
  54
+    print "$name $$name\n";     # Prints "foo bar"
  55
+
  56
+    use strict;
  57
+    my $name = "foo";
  58
+    $$name = "bar";             # Complains: can't use "foo" as ref
  59
+
  60
+The C<warnings> pragma enables a whole host of useful complaints from
  61
+Perl, letting you know about things in your program that you most likely
  62
+didn't intend:
  63
+
  64
+    use warnings;
  65
+    my $foo = ;
  66
+    $foo += 3;
  67
+    my $foo = 1;            # Compains: redeclaration of variable
  68
+
  69
+    my $bar = '12fred34';
  70
+    my $baz = $bar + 1;     # Complains: Argument "12fred34" isn't numeric
  71
+                            # Complains: Name "main::baz" used only once
14 72
 
15  
-See L<http://www.perlmonks.org/?node_id=482733> for another story
16  
-of C<strict> and C<warnings>.
  73
+See the strict documentation L<strict> and L<warnings> for futher
  74
+information.  For some horror stories about leaving out L<strict>,
  75
+see L<http://www.perlmonks.org/?node_id=482733>.
17 76
 
18 77
 =head2 Check the return of every C<open>
19 78
 
7  s/email.pod
Source Rendered
@@ -4,8 +4,7 @@
4 4
 
5 5
 In general, you can't.  There are several methods available to see
6 6
 if it looks reasonable, but there is no way to determine if an
7  
-address is actually deliverable without actually attempting 
8  
-delivery.
  7
+address is actually deliverable without actually attempting delivery.
9 8
 
10 9
 Using regular expressions:
11 10
 
@@ -25,14 +24,14 @@ the modules available on L<http://search.cpan.org>, such as:
25 24
 
26 25
 C<Email::Valid> makes it easy to determine if an email address is
27 26
 well-formed:
28  
-    
  27
+
29 28
     use Email::Valid;
30 29
 
31 30
     print ( Email::Valid->address( 'someone@gmail.com' ) ? 'Yes' : 'No' ); # prints Yes
32 31
     print ( Email::Valid->address( 'someone#gmail.com' ) ? 'Yes' : 'No' ); # prints No
33 32
 
34 33
 C<Email::Valid> can also tell you why an address is invalid:
35  
-    
  34
+
36 35
     print "Invalid address: $Email::Valid::Details \n"
37 36
         unless Email::Valid->address( 'you#foo.bar' );
38 37
 
8  s/index.pod
Source Rendered
@@ -9,15 +9,17 @@ what they don't know.  Perl101.org hopes to change that.
9 9
 
10 10
 =head1 perl101.org source
11 11
 
12  
-The source for this site is kept at Google Code, at
13  
-L<http://code.google.com/p/perl101/>.  If you'd like to help add
  12
+The source for this site is kept on github at
  13
+L<http://github.com/petdance/perl101>.
  14
+If you'd like to help add
14 15
 to this site, drop Andy Lester a note at andy [at] perl.org.
15 16
 
16 17
 =head1 Thanks
17 18
 
18 19
 Thanks to the following folks for their contributions:
  20
+Drew Stephens,
19 21
 Usman Jan,
20  
-Adam Sjøgren,
  22
+Adam SjE<oslash>gren,
21 23
 Jeremiah Foster,
22 24
 Ben Hengst,
23 25
 Amanda with no last name,
0  s/favicon.ico → static/favicon.ico
File renamed without changes
BIN  static/images/perl101-header.png
BIN  static/images/perl101logo.png
32  static/js/iepnghack.js
... ...
@@ -0,0 +1,32 @@
  1
+<script language="JavaScript">
  2
+function correctPNG() // correctly handle PNG transparency in Win IE 5.5 & 6.
  3
+{
  4
+    var arVersion = navigator.appVersion.split("MSIE")
  5
+    var version = parseFloat(arVersion[1])
  6
+    if ((version >= 5.5) && (document.body.filters)) 
  7
+    {
  8
+      for(var i=0; i<document.images.length; i++)
  9
+      {
  10
+        var img = document.images[i]
  11
+        var imgName = img.src.toUpperCase()
  12
+        if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
  13
+        {
  14
+           var imgID = (img.id) ? "id='" + img.id + "' " : ""
  15
+           var imgClass = (img.className) ? "class='" + img.className + "' " : ""
  16
+           var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
  17
+           var imgStyle = "display:inline-block;" + img.style.cssText 
  18
+           if (img.align == "left") imgStyle = "float:left;" + imgStyle
  19
+           if (img.align == "right") imgStyle = "float:right;" + imgStyle
  20
+           if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
  21
+           var strNewHTML = "<span " + imgID + imgClass + imgTitle
  22
+           + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
  23
+           + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
  24
+           + "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>" 
  25
+           img.outerHTML = strNewHTML
  26
+           i = i-1
  27
+          }
  28
+      }
  29
+   }    
  30
+}
  31
+window.attachEvent("onload", correctPNG);
  32
+</script>
1,653  static/js/jemplate.js
... ...
@@ -0,0 +1,1653 @@
  1
+/*------------------------------------------------------------------------------
  2
+Jemplate - Template Toolkit for JavaScript
  3
+
  4
+DESCRIPTION - This module provides the runtime JavaScript support for
  5
+compiled Jemplate templates.
  6
+
  7
+AUTHOR - Ingy döt Net <ingy@cpan.org>
  8
+
  9
+Copyright 2006,2008 Ingy döt Net.
  10
+
  11
+This module is free software; you can redistribute it and/or
  12
+modify it under the same terms as Perl itself.
  13
+------------------------------------------------------------------------------*/
  14
+
  15
+//------------------------------------------------------------------------------
  16
+// Main Jemplate class
  17
+//------------------------------------------------------------------------------
  18
+
  19
+if (typeof Jemplate == 'undefined') {
  20
+    var Jemplate = function() {
  21
+        this.init.apply(this, arguments);
  22
+    };
  23
+}
  24
+
  25
+Jemplate.VERSION = '0.22';
  26
+
  27
+Jemplate.process = function() {
  28
+    var jemplate = new Jemplate();
  29
+    return jemplate.process.apply(jemplate, arguments);
  30
+}
  31
+
  32
+;(function(){
  33
+
  34
+if (! Jemplate.templateMap)
  35
+    Jemplate.templateMap = {};
  36
+
  37
+var proto = Jemplate.prototype = {};
  38
+
  39
+proto.init = function(config) {
  40
+    this.config = config ||
  41
+    {
  42
+        AUTO_RESET: true,
  43
+        BLOCKS: {},
  44
+        CONTEXT: null,
  45
+        DEBUG_UNDEF: false,
  46
+        DEFAULT: null,
  47
+        ERROR: null,
  48
+        EVAL_JAVASCRIPT: false,
  49
+        FILTERS: {},
  50
+        INCLUDE_PATH: [''],
  51
+        INTERPOLATE: false,
  52
+        OUTPUT: null,
  53
+        PLUGINS: {},
  54
+        POST_PROCESS: [],
  55
+        PRE_PROCESS: [],
  56
+        PROCESS: null,
  57
+        RECURSION: false,
  58
+        STASH: null,
  59
+        TOLERANT: null,
  60
+        VARIABLES: {},
  61
+        WRAPPER: []
  62
+    };
  63
+}
  64
+
  65
+proto.process = function(template, data, output) {
  66
+    var context = this.config.CONTEXT || new Jemplate.Context();
  67
+    context.config = this.config;
  68
+
  69
+    context.stash = this.config.STASH || new Jemplate.Stash();
  70
+    context.stash.__config__ = this.config;
  71
+
  72
+    context.__filter__ = new Jemplate.Filter();
  73
+    context.__filter__.config = this.config;
  74
+
  75
+    context.__plugin__ = new Jemplate.Plugin();
  76
+    context.__plugin__.config = this.config;
  77
+
  78
+    var result;
  79
+
  80
+    var proc = function(input) {
  81
+        try {
  82
+            result = context.process(template, input);
  83
+        }
  84
+        catch(e) {
  85
+            if (! String(e).match(/Jemplate\.STOP\n/))
  86
+                throw(e);
  87
+            result = e.toString().replace(/Jemplate\.STOP\n/, '');
  88
+        }
  89
+
  90
+        if (typeof output == 'undefined')
  91
+            return result;
  92
+        if (typeof output == 'function') {
  93
+            output(result);
  94
+            return null;
  95
+        }
  96
+        if (typeof(output) == 'string' || output instanceof String) {
  97
+            if (output.match(/^#[\w\-]+$/)) {
  98
+                var id = output.replace(/^#/, '');
  99
+                var element = document.getElementById(id);
  100
+                if (typeof element == 'undefined')
  101
+                    throw('No element found with id="' + id + '"');
  102
+                element.innerHTML = result;
  103
+                return null;
  104
+            }
  105
+        }
  106
+        else {
  107
+            output.innerHTML = result;
  108
+            return null;
  109
+        }
  110
+
  111
+        throw("Invalid arguments in call to Jemplate.process");
  112
+
  113
+        return 1;
  114
+    }
  115
+
  116
+    if (typeof data == 'function')
  117
+        data = data();
  118
+    else if (typeof data == 'string') {
  119
+//        Jemplate.Ajax.get(data, function(r) { proc(Jemplate.JSON.parse(r)) });
  120
+        var url = data;
  121
+        Jemplate.Ajax.processGet(url, function(data) { proc(data) });
  122
+        return null;
  123
+    }
  124
+
  125
+    return proc(data);
  126
+}
  127
+
  128
+//------------------------------------------------------------------------------
  129
+// Jemplate.Context class
  130
+//------------------------------------------------------------------------------
  131
+if (typeof Jemplate.Context == 'undefined')
  132
+    Jemplate.Context = function() {};
  133
+
  134
+proto = Jemplate.Context.prototype;
  135
+
  136
+proto.include = function(template, args) {
  137
+    return this.process(template, args, true);
  138
+}
  139
+
  140
+proto.process = function(template, args, localise) {
  141
+    if (localise)
  142
+        this.stash.clone(args);
  143
+    else
  144
+        this.stash.update(args);
  145
+    var func = Jemplate.templateMap[template];
  146
+    if (typeof func == 'undefined')
  147
+        throw('No Jemplate template named "' + template + '" available');
  148
+    var output = func(this);
  149
+    if (localise)
  150
+        this.stash.declone();
  151
+    return output;
  152
+}
  153
+
  154
+proto.set_error = function(error, output) {
  155
+    this._error = [error, output];
  156
+    return error;
  157
+}
  158
+
  159
+proto.plugin = function(name, args) {
  160
+    if (typeof name == 'undefined')
  161
+        throw "Unknown plugin name ':" + name + "'";
  162
+
  163
+    // The Context object (this) is passed as the first argument to the plugin.
  164
+    return new window[name](this, args);
  165
+}
  166
+
  167
+proto.filter = function(text, name, args) {
  168
+    if (name == 'null')
  169
+        name = "null_filter";
  170
+    if (typeof this.__filter__.filters[name] == "function")
  171
+        return this.__filter__.filters[name](text, args, this);
  172
+    else
  173
+        throw "Unknown filter name ':" + name + "'";
  174
+}
  175
+
  176
+//------------------------------------------------------------------------------
  177
+// Jemplate.Plugin class
  178
+//------------------------------------------------------------------------------
  179
+if (typeof Jemplate.Plugin == 'undefined') {
  180
+    Jemplate.Plugin = function() { };
  181
+}
  182
+
  183
+proto = Jemplate.Plugin.prototype;
  184
+
  185
+proto.plugins = {};
  186
+
  187
+//------------------------------------------------------------------------------
  188
+// Jemplate.Filter class
  189
+//------------------------------------------------------------------------------
  190
+if (typeof Jemplate.Filter == 'undefined') {
  191
+    Jemplate.Filter = function() { };
  192
+}
  193
+
  194
+proto = Jemplate.Filter.prototype;
  195
+
  196
+proto.filters = {};
  197
+
  198
+proto.filters.null_filter = function(text) {
  199
+    return '';
  200
+}
  201
+
  202
+proto.filters.upper = function(text) {
  203
+    return text.toUpperCase();
  204
+}
  205
+
  206
+proto.filters.lower = function(text) {
  207
+    return text.toLowerCase();
  208
+}
  209
+
  210
+proto.filters.ucfirst = function(text) {
  211
+    var first = text.charAt(0);
  212
+    var rest = text.substr(1);
  213
+    return first.toUpperCase() + rest;
  214
+}
  215
+
  216
+proto.filters.lcfirst = function(text) {
  217
+    var first = text.charAt(0);
  218
+    var rest = text.substr(1);
  219
+    return first.toLowerCase() + rest;
  220
+}
  221
+
  222
+proto.filters.trim = function(text) {
  223
+    return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" );
  224
+}
  225
+
  226
+proto.filters.collapse = function(text) {
  227
+    return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" ).replace(/\s+/, " ");
  228
+}
  229
+
  230
+proto.filters.html = function(text) {
  231
+    text = text.replace(/&/g, '&amp;');
  232
+    text = text.replace(/</g, '&lt;');
  233
+    text = text.replace(/>/g, '&gt;');
  234
+    text = text.replace(/"/g, '&quot;'); // " end quote for emacs
  235
+    return text;
  236
+}
  237
+
  238
+proto.filters.html_para = function(text) {
  239
+    var lines = text.split(/(?:\r?\n){2,}/);
  240
+    return "<p>\n" + lines.join("\n</p>\n\n<p>\n") + "</p>\n";
  241
+}
  242
+
  243
+proto.filters.html_break = function(text) {
  244
+    return text.replace(/(\r?\n){2,}/g, "$1<br />$1<br />$1");
  245
+}
  246
+
  247
+proto.filters.html_line_break = function(text) {
  248
+    return text.replace(/(\r?\n)/g, "$1<br />$1");
  249
+}
  250
+
  251
+proto.filters.uri = function(text) {
  252
+     return encodeURIComponent(text);
  253
+}
  254
+ 
  255
+proto.filters.url = function(text) {
  256
+    return encodeURI(text);
  257
+}
  258
+
  259
+proto.filters.indent = function(text, args) {
  260
+    var pad = args[0];
  261
+    if (! text) return null;
  262
+    if (typeof pad == 'undefined')
  263
+        pad = 4;
  264
+
  265
+    var finalpad = '';
  266
+    if (typeof pad == 'number' || String(pad).match(/^\d$/)) {
  267
+        for (var i = 0; i < pad; i++) {
  268
+            finalpad += ' ';
  269
+        }
  270
+    } else {
  271
+        finalpad = pad;
  272
+    }
  273
+    var output = text.replace(/^/gm, finalpad);
  274
+    return output;
  275
+}
  276
+
  277
+proto.filters.truncate = function(text, args) {
  278
+    var len = args[0];
  279
+    if (! text) return null;
  280
+    if (! len)
  281
+        len = 32;
  282
+    // This should probably be <=, but TT just uses <
  283
+    if (text.length < len)
  284
+        return text;
  285
+    var newlen = len - 3;
  286
+    return text.substr(0,newlen) + '...';
  287
+}
  288
+
  289
+proto.filters.repeat = function(text, iter) {
  290
+    if (! text) return null;
  291
+    if (! iter || iter == 0)
  292
+        iter = 1;
  293
+    if (iter == 1) return text
  294
+
  295
+    var output = text;
  296
+    for (var i = 1; i < iter; i++) {
  297
+        output += text;
  298
+    }
  299
+    return output;
  300
+}
  301
+
  302
+proto.filters.replace = function(text, args) {
  303
+    if (! text) return null;
  304
+    var re_search = args[0];
  305
+    var text_replace = args[1];
  306
+    if (! re_search)
  307
+        re_search = '';
  308
+    if (! text_replace)
  309
+        text_replace = '';
  310
+    var re = new RegExp(re_search, 'g');
  311
+    return text.replace(re, text_replace);
  312
+}
  313
+
  314
+//------------------------------------------------------------------------------
  315
+// Jemplate.Stash class
  316
+//------------------------------------------------------------------------------
  317
+if (typeof Jemplate.Stash == 'undefined') {
  318
+    Jemplate.Stash = function() {
  319
+        this.data = {};
  320
+    };
  321
+}
  322
+
  323
+proto = Jemplate.Stash.prototype;
  324
+
  325
+proto.clone = function(args) {
  326
+    var data = this.data;
  327
+    this.data = {};
  328
+    this.update(data);
  329
+    this.update(args);
  330
+    this.data._PARENT = data;
  331
+}
  332
+
  333
+proto.declone = function(args) {
  334
+    this.data = this.data._PARENT || this.data;
  335
+}
  336
+
  337
+proto.update = function(args) {
  338
+    if (typeof args == 'undefined') return;
  339
+    for (var key in args) {
  340
+        var value = args[key];
  341
+        this.set(key, value);
  342
+    }
  343
+}
  344
+
  345
+proto.get = function(key) {
  346
+    var root = this.data;
  347
+    if (key instanceof Array) {
  348
+        for (var i = 0; i < key.length; i += 2) {
  349
+            var args = key.slice(i, i+2);
  350
+            args.unshift(root);
  351
+            value = this._dotop.apply(this, args);
  352
+            if (typeof value == 'undefined')
  353
+                break;
  354
+            root = value;
  355
+        }
  356
+    }
  357
+    else {
  358
+        value = this._dotop(root, key);
  359
+    }
  360
+
  361
+    if (typeof value == 'undefined') {
  362
+        if (this.__config__.DEBUG_UNDEF)
  363
+            throw("undefined value found while using DEGUG_UNDEF");
  364
+        value = '';
  365
+    }
  366
+
  367
+    return value;
  368
+}
  369
+
  370
+proto.set = function(key, value, set_default) {
  371
+    if (key instanceof Array) {
  372
+        var data = this.get(key[0]) || {};
  373
+        key = key[2];
  374
+    }
  375
+    else {
  376
+        data = this.data;
  377
+    }
  378
+    if (! (set_default && (typeof data[key] != 'undefined')))
  379
+        data[key] = value;
  380
+}
  381
+
  382
+proto._dotop = function(root, item, args) {
  383
+    if (typeof item == 'undefined' ||
  384
+        typeof item == 'string' && item.match(/^[\._]/)) {
  385
+        return undefined;
  386
+    }
  387
+
  388
+    if ((! args) &&
  389
+        (typeof root == 'object') &&
  390
+        (!(root instanceof Array) || (typeof item == 'number')) &&
  391
+        (typeof root[item] != 'undefined')) {
  392
+        var value = root[item];
  393
+        if (typeof value == 'function')
  394
+            value = value.apply(root);
  395
+        return value;
  396
+    }
  397
+
  398
+    if (typeof root == 'string' && this.string_functions[item])
  399
+        return this.string_functions[item](root, args);
  400
+    if (root instanceof Array && this.list_functions[item])
  401
+        return this.list_functions[item](root, args);
  402
+    if (typeof root == 'object' && this.hash_functions[item])
  403
+        return this.hash_functions[item](root, args);
  404
+    if (typeof root[item] == 'function')
  405
+        return root[item].apply(root, args);
  406
+
  407
+    return undefined;
  408
+}
  409
+
  410
+proto.string_functions = {};
  411
+
  412
+// chunk(size)     negative size chunks from end
  413
+proto.string_functions.chunk = function(string, args) {
  414
+    var size = args[0];
  415
+    var list = new Array();
  416
+    if (! size)
  417
+        size = 1;
  418
+    if (size < 0) {
  419
+        size = 0 - size;
  420
+        for (i = string.length - size; i >= 0; i = i - size)
  421
+            list.unshift(string.substr(i, size));
  422
+        if (string.length % size)
  423
+            list.unshift(string.substr(0, string.length % size));
  424
+    }
  425
+    else
  426
+        for (i = 0; i < string.length; i = i + size)
  427
+            list.push(string.substr(i, size));
  428
+    return list;
  429
+}
  430
+
  431
+// defined         is value defined?
  432
+proto.string_functions.defined = function(string) {
  433
+    return 1;
  434
+}
  435
+
  436
+// hash            treat as single-element hash with key value
  437
+proto.string_functions.hash = function(string) {
  438
+    return { 'value': string };
  439
+}
  440
+
  441
+// length          length of string representation
  442
+proto.string_functions.length = function(string) {
  443
+    return string.length;
  444
+}
  445
+
  446
+// list            treat as single-item list
  447
+proto.string_functions.list = function(string) {
  448
+    return [ string ];
  449
+}
  450
+
  451
+// match(re)       get list of matches
  452
+proto.string_functions.match = function(string, args) {
  453
+    var regexp = new RegExp(args[0], 'gm');
  454
+    var list = string.match(regexp);
  455
+    return list;
  456
+}
  457
+
  458
+// repeat(n)       repeated n times
  459
+proto.string_functions.repeat = function(string, args) {
  460
+    var n = args[0] || 1;
  461
+    var output = '';
  462
+    for (var i = 0; i < n; i++) {
  463
+        output += string;
  464
+    }
  465
+    return output;
  466
+}
  467
+
  468
+// replace(re, sub)    replace instances of re with sub
  469
+proto.string_functions.replace = function(string, args) {
  470
+    var regexp = new RegExp(args[0], 'gm');
  471
+    var sub = args[1];
  472
+    if (! sub)
  473
+        sub  = '';
  474
+    var output = string.replace(regexp, sub);
  475
+    return output;
  476
+}
  477
+
  478
+// search(re)      true if value matches re
  479
+proto.string_functions.search = function(string, args) {
  480
+    var regexp = new RegExp(args[0]);
  481
+    return (string.search(regexp) >= 0) ? 1 : 0;
  482
+}
  483
+
  484
+// size            returns 1, as if a single-item list
  485
+proto.string_functions.size = function(string) {
  486
+    return 1;
  487
+}
  488
+
  489
+// split(re)       split string on re
  490
+proto.string_functions.split = function(string, args) {
  491
+    var regexp = new RegExp(args[0]);
  492
+    var list = string.split(regexp);
  493
+    return list;
  494
+}
  495
+
  496
+
  497
+
  498
+proto.list_functions = {};
  499
+
  500
+proto.list_functions.join = function(list, args) {
  501
+    return list.join(args[0]);
  502
+};
  503
+
  504
+proto.list_functions.sort = function(list,key) {
  505
+    if( typeof(key) != 'undefined' && key != "" ) {
  506
+        // we probably have a list of hashes
  507
+        // and need to sort based on hash key
  508
+        return list.sort(
  509
+            function(a,b) {
  510
+                if( a[key] == b[key] ) {
  511
+                    return 0;
  512
+                }
  513
+                else if( a[key] > b[key] ) {
  514
+                    return 1;
  515
+                }
  516
+                else {
  517
+                    return -1;
  518
+                }
  519
+            }
  520
+        );
  521
+    }
  522
+    return list.sort();
  523
+}
  524
+
  525
+proto.list_functions.nsort = function(list) {
  526
+    return list.sort(function(a, b) { return (a-b) });
  527
+}
  528
+
  529
+proto.list_functions.grep = function(list, args) {
  530
+    var regexp = new RegExp(args[0]);
  531
+    var result = [];
  532
+    for (var i = 0; i < list.length; i++) {
  533
+        if (list[i].match(regexp))
  534
+            result.push(list[i]);
  535
+    }
  536
+    return result;
  537
+}
  538
+
  539
+proto.list_functions.unique = function(list) {
  540
+    var result = [];
  541
+    var seen = {};
  542
+    for (var i = 0; i < list.length; i++) {
  543
+        var elem = list[i];
  544
+        if (! seen[elem])
  545
+            result.push(elem);
  546
+        seen[elem] = true;
  547
+    }
  548
+    return result;
  549
+}
  550
+
  551
+proto.list_functions.reverse = function(list) {
  552
+    var result = [];
  553
+    for (var i = list.length - 1; i >= 0; i--) {
  554
+        result.push(list[i]);
  555
+    }
  556
+    return result;
  557
+}
  558
+
  559
+proto.list_functions.merge = function(list, args) {
  560
+    var result = [];
  561
+    var push_all = function(elem) {
  562
+        if (elem instanceof Array) {
  563
+            for (var j = 0; j < elem.length; j++) {
  564
+                result.push(elem[j]);
  565
+            }
  566
+        }
  567
+        else {
  568
+            result.push(elem);
  569
+        }
  570
+    }
  571
+    push_all(list);
  572
+    for (var i = 0; i < args.length; i++) {
  573
+        push_all(args[i]);
  574
+    }
  575
+    return result;
  576
+}
  577
+
  578
+proto.list_functions.slice = function(list, args) {
  579
+    return list.slice(args[0], args[1]);
  580
+}
  581
+
  582
+proto.list_functions.splice = function(list, args) {
  583
+    if (args.length == 1)
  584
+        return list.splice(args[0]);
  585
+    if (args.length == 2)
  586
+        return list.splice(args[0], args[1]);
  587
+    if (args.length == 3)
  588
+        return list.splice(args[0], args[1], args[2]);
  589
+    return null;
  590
+}
  591
+
  592
+proto.list_functions.push = function(list, args) {
  593
+    list.push(args[0]);
  594
+    return list;
  595
+}
  596
+
  597
+proto.list_functions.pop = function(list) {
  598
+    return list.pop();
  599
+}
  600
+
  601
+proto.list_functions.unshift = function(list, args) {