The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Catalyst::View::JavaScript::Minifier::XS;

use warnings;
use strict;

use base qw/Catalyst::View/;

our $VERSION = '0.02';

use NEXT;
use Carp qw/croak/;
use JavaScript::Minifier::XS qw/minify/;
use Path::Class::File;
use Data::Dump qw/dump/;
use Catalyst::Exception;
use URI;

=head1 NAME

Catalyst::View::JavaScript::Minifier::XS - Concenate and minify your JavaScript files.

=head1 VERSION

Version 0.02

=head1 SYNOPSIS

    # creating MyApp::View::JavaScript
    ./script/myapp_create.pl view JavaScript JavaScript::Minifier::XS

	# in your controller file, as an action
    sub js : Local {
		my ( $self, $c ) = @_;	
		
		$c->stash->{js} = [qw/script1 script2/]; # loads root/js/script1.js and root/js/script2.js
	
		$c->forward("View::JavaScript");
    }
	
	# in your html template use
	<script type="text/javascript" src="/js"></script>

=head1 DESCRIPTION

Use your minified js files as a separated catalyst request. By default they are read from C<< $c->stash->{js} >> as array or string.

=head1 CONFIG VARIABLES

=over 2

=item stash_variable

sets a different stash variable from the default C<< $c->stash->{js} >>

=item path

sets a different path for your javascript files

default : js

=item subinclude

setting this to true will take your js files (stash variable) from your referer action

	# in your controller 
	sub action : Local {
		my ( $self, $c ) = @_;
		
		$c->stash->{js} = "exclusive"; # loads exclusive.js only when /action is loaded
	}

This could be very dangerous since it's using C<< $c->forward($c->request->headers->referer) >>. It doesn't work with the index action!

default : false

=back

=cut

__PACKAGE__->mk_accessors(qw(stash_variable path subinclude));

sub new {
	my($class, $c, $arguments) = @_;
    my $self = $class->NEXT::new($c);
	my %config = ( stash_variable => 'js', path => 'js', subinclude => 0, %$arguments );
	for my $field ( keys %config ) {
		if ( $self->can($field) ) {
			$self->$field( $config{$field} );
		} else {
			$c->log->debug("Unknown config parameter '$field'");
		}
	}
	return $self;
}

sub process {
	my ($self,$c) = @_;
			
	my $path = $self->path;	
	my $variable = $self->stash_variable;	
	my @files = ();	

	my $original_stash = $c->stash->{$variable};
	
	# setting the return content type
	$c->res->content_type("text/javascript");
	
	# turning stash variable into @files
	if ( $c->stash->{$variable} ) {
		@files = ( ref $c->stash->{$variable} eq 'ARRAY' ? @{ $c->stash->{$variable} } : split /\s+/, $c->stash->{$variable} );	
	}
	
	# No referer we won't show anything
	if ( ! $c->request->headers->referer ) {		
		$c->log->debug("javascripts called from no referer sending blank");		
		$c->res->body( " " );			
		$c->detach();
	}
	
	# If we have subinclude ON then we should run the action and see what it left behind
	if ( $self->subinclude ) {
		my $base = $c->request->base;
		if ( $c->request->headers->referer ) {			
			my $referer = URI->new($c->request->headers->referer);			
			if ( $referer->path ne "/" ) {
				$c->forward("/".$referer->path);
				$c->log->debug("js taken from referer : ".$referer->path);
				if ( $c->stash->{$variable} ne $original_stash ) {
					# adding other files returned from $c->forward to @files ( if any )
					push @files, ( ref $c->stash->{$variable} eq 'ARRAY' ? @{ $c->stash->{$variable} } : split /\s+/, $c->stash->{$variable} );	
				}
			} else {
				# well for now we can't get js files from index, because it's indefinite loop
				$c->log->debug("we can't take js from index, it's too dangerous!");
			}			
		}
	}
	
	my $home = $self->config->{INCLUDE_PATH} || $c->path_to('root');
	
	@files = map {
		my $file = $_;
		$file =~ s/\.js$//;
		Path::Class::File->new( $home, "$path", "$file.js" );		
	} @files;
	
	# combining the files
	my @output = ();
	for my $file ( @files ) {
		$c->log->debug("loading js file ... $file");
		open(IN, "<$file");
		for ( <IN> ) {
			push @output, $_;
		}
		close(IN);
	}

	if ( @output ) {
		# minifying them if any files loaded at all
		$c->res->body( minify(join(" ",@output)) );	
	} else {
		$c->res->body( " " );	
	}
}

=head1 SEE ALSO

L<Catalyst> , L<Catalyst::View>, L<JavaScript::Minifier::XS>

=head1 AUTHOR

Ivan Drinchev C<< <drinchev at gmail.com> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-catalyst-view-JavaScript-minifier-xs at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-JavaScript-Minifier-XS>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 COPYRIGHT & LICENSE

Copyright 2009 Ivan Drinchev, all rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

1; # End of Catalyst::View::JavaScript::Minifier::XS