The following listing shows jsload.cgi, which is really a stub programit determines which script is being loaded and then calls
JSInclude.pm is where the real action is. JSInclude::load() starts the process by calling include()that's the Perl include, not the JavaScript include. Before and after this, however, it prints a preamble and postamble (respectively). These are normally empty, but if you set the '$verbose' variable at the top, the preamble and postamble will time the entire loading process on the client side and display the total loading time.
The following listing shows JSInclude::include():
package JSInclude;
my $verbose = 0;
sub get_includes( $ )
{
my ($filename) = @_;
my @includes = ();
if (open( SRC, $filename )) {
while (<SRC>) {
if (/^\s*include\s*\(\s*"(.*)"\s*\)\s*;\s*$/) {
my $filename = $1;
push @includes, $filename;
}
}
close( SRC );
}
return \@includes;
}
sub include( $$ );
sub include( $$ )
{
my ($filename,$already) = @_;
if ($already->{$filename}) {
return;
}
$already->{$filename} = 1;
my $includes = get_includes( $filename );
foreach my $include (@$includes) {
include( $include, $already );
}
if (!open( SRC, $filename )) {
print "Can't open '$filename': $!\n";
die;
}
print qq(<script language="javascript">\n);
while (<SRC>) {
print if (!/^\s*include\s*\(\s*"(.*)"\s*\)\s*;\s*$/);
}
print qq(</script>\n);
close( SRC );
}
sub preamble
{
print <<EOF if ($verbose);
<script language="javascript">
var ___tt = new Date().getTime();
</script>
EOF
}
sub postamble
{
print <<EOF if ($verbose);
<script language="javascript">
___tt = new Date().getTime() - ___tt;
alert( "Loading: "+(___tt/1000)+" seconds" );
</script>
EOF
}
sub load( $ )
{
my ($startfile) = @_;
preamble();
include( $startfile, {} );
postamble();
}
1;
This function is responsible for reading the contents of the file, scanning it for include(...) statements, and doing the include itself. The actual scanning is done by the get_includes() function.
Note that the include(...) statement in the JavaScript is never executed; instead, it is replaced (here on the server side) by the contents of the included file. Also note that include() maintains a variable called $already, which is a hash containing the names of all the files that have already been included. Including a file multiple times isn't necessary, and it in fact can lead to infinite loops.
A Cross-Platform include Hack
In the end, the technique described here is as much a hack as any other technique for JavaScript include. However, it relies only on CGI and Perltwo mature and predictable technologies, so you can expect your includes to work across all server and client platforms.
It's also a lot faster.