The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Mojolicious::Plugin::CSSLoader;

# ABSTRACT: move css loading to the end of the document

use strict;
use warnings;

use parent 'Mojolicious::Plugin';

our $VERSION = 0.07;

sub register {
    my ($self, $app, $config) = @_;

    my $base  = $config->{base}  || '';
    my $media = $config->{media} || '';

    if ( $base and substr( $base, -1 ) ne '/' ) {
        $base .= '/';
    }

    $app->helper( css_load => sub {
        my $c = shift;

        if ( $_[1]->{check} ) {
            my $asset = $c->app->static->file(
                $_[1]->{no_base} ? $_[0] : "$base$_[0]"
            );

            return '' if !$asset;
        }

        push @{ $c->stash->{__CSSLOADERFILES__} }, [ @_ ];
    } );

    $app->hook( after_render => sub {
        my ($c, $content, $format) = @_;

        return if $format ne 'html';
        return if !$c->stash->{__CSSLOADERFILES__};

        my $load_css =
            join "\n", 
                map{
                    my ($file,$config) = @{ $_ };
                    my $local_base  = $config->{no_base} ? '' : $base;

                    $local_base  = $c->url_for( $local_base ) if $local_base;

                    my $local_media = "";

                    $local_media = ' media="' . $media . '"'           if $media;
                    $local_media = ' media="' . $config->{media} . '"' if $config->{media};

                    my $ie_start = '';
                    my $ie_end   = '';

                    if ( exists $config->{ie} ) {
                        my $start_extra = '';
                        my $end_extra   = '';
                        my $cmp         = '';
                        my $version     = '';

                        if ( !ref $config->{ie} && !$config->{ie} ) {
                            $start_extra = '<!-->';
                            $end_extra   = '<!--';
                            $cmp         = '!';
                        }
                        elsif( ref $config->{ie} ) {
                            my %map = qw/> gt >= gte < lt <= lte/;
                            (my $key, $version) = %{ $config->{ie} };
                            $cmp = $map{$key} || '';
                            $cmp .= ' ' if $cmp;
                        }

                        $ie_start = sprintf "<!-- [if %sIE %s]>%s",
                            $cmp,
                            $version,
                            $start_extra;

                        $ie_end = sprintf "%s<![endif]-->", $end_extra;
                    }

                    $config->{no_file} ? 
                         qq~$ie_start<style type="text/css">$file</style>$ie_end~ :
                         qq~$ie_start<link rel="stylesheet" href="$local_base$file"$local_media/>$ie_end~;
                }
                @{ $c->stash->{__CSSLOADERFILES__} || [] };

        return if !$load_css;

        ${$content} =~ s!(</head>)!$load_css$1! or ${$content} = $load_css . ${$content};
    });
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Mojolicious::Plugin::CSSLoader - move css loading to the end of the document

=head1 VERSION

version 0.07

=head1 SYNOPSIS

In your C<startup>:

    sub startup {
        my $self = shift;
  
        # do some Mojolicious stuff
        $self->plugin( 'CSSLoader' );

        # more Mojolicious stuff
    }

In your template:

    <% css_load('css_file.css') %>

=head1 HELPERS

This plugin adds a helper method to your web application:

=head2 css_load

This method requires at least one parameter: The path to the JavaScript file to load.
An optional second parameter is the configuration. You can switch off the I<base> for
this CSS file this way:

  # <link rel="stylesheet" href="$base/css_file.css"/>
  <% css_load('css_file.css') %>
  
  # <link rel="stylesheet" href="http://domain/css_file.css"/>
  <% css_load('http://domain/css_file.css', {no_base => 1});
  
  # load css file only in Internet Explorer
  # <!-- [if IE]> <link rel="stylesheet" href="http://domain/css_file.css"/> <![endif] -->
  <% css_load('css_file.css', {ie => 1});
  
  # load css file except in Internet Explorer
  # <!-- [if !IE]><!--> <link rel="stylesheet" href="http://domain/css_file.css"/> <!--<![endif] -->
  <% css_load('css_file.css', {ie => 0});
  
  # load css file in Internet Explorer greater version 7
  # <!-- [if gt IE 7]> <link rel="stylesheet" href="http://domain/css_file.css"/> <![endif] -->
  <% css_load('css_file.css', {ie => { '>' => 7 } });
  
  # allowed ie settings: >, >=, <, <=, ==

=head1 HOOKS

When you use this module, a hook for I<after_render> is installed. That hook inserts
the C<< <link> >> tag at the end of the C<< <head> >> part or at the start of the
document.

=head1 METHODS

=head2 register

Called when registering the plugin. On creation, the plugin accepts a hashref to configure the plugin.

    # load plugin, alerts are dismissable by default
    $self->plugin( 'CSSLoader' );

=head3 Configuration

    $self->plugin( 'CSSLoader' => {
        base  => 'http://domain/css',  # base for all CSS files
        media => 'screen',             # media setting (default: none)
    });

=head1 NOTES

This plugin uses the I<stash> key C<__CSSLOADERFILES__>, so you should avoid using
this stash key for your own purposes.

=head1 AUTHOR

Renee Baecker <reneeb@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2015 by Renee Baecker.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut