Jifty::Web::PageRegion - Defines a page region


Jifty documentation Contained in the Jifty distribution.

Index


Code Index:

NAME

Top

Jifty::Web::PageRegion - Defines a page region

DESCRIPTION

Top

Describes a region of the page which contains a Mason fragment which can be updated via AJAX or via query parameters.

new PARAMHASH

Creates a new page region. The possible arguments in the PARAMHASH are:

name

The (unqualified) name of the region. This is used to generate a unique id -- it should consist of only letters and numbers.

path

The path to the fragment that this page region contains. Defaults to /__jifty/empty, which, as its name implies, is empty.

arguments (optional) (formerly 'defaults')

Specifies an optional set of parameter defaults. These should all be simple scalars, as they might be passed across HTTP if AJAX is used.

See Jifty::Web::Form::Element for a list of the supported parameters.

force_arguments (optional)

Specifies an optional set of parameter values. They will override anything sent by the user or set via AJAX.

force_path (optional)

A fixed path to the fragment that this page region contains. Overrides anything set by the user.

parent (optional)

The parent Jifty::Web::PageRegion that this region is enclosed in.

region_wrapper (optional)

A boolean; whether or not the region, when rendered, will include the HTML region preamble that makes Javascript aware of its presence. Defaults to true.

lazy (optional)

Delays the loading of the fragment until client render-time. Obviously, does not work with downlevel browsers which don't support javascript.

loading_path (optional)

The fragment to display while the client fetches the actual region. Make this lightweight, or you'll be losing most of the benefits of lazy loading!

name [NAME]

Gets or sets the name of the page region.

qualified_name [NAME]

Gets or sets the fully qualified name of the page region. This should be unique on a page. This is usually set by enter, based on the page regions that this region is inside. See qualified_region in Jifty::Web.

default_path [PATH]

Gets or sets the default path of the fragment. This is overridden by path.

path [PATH]

Gets or sets the path that the fragment actually contains. This overrides default_path.

default_argument NAME [VALUE]

Gets or sets the default value of the NAME argument. This is used as a fallback, and also to allow generated links to minimize the amount of state they must transmit.

argument NAME [VALUE]

Gets or sets the actual run-time value of the page region. This usually comes from HTTP parameters. It overrides the default_argument of the same NAME.

arguments [HASHREF]

Sets all arguments at once, or returns all arguments. The latter will also include all default arguments.

enter

Enters the region; this sets the qualified name based on qualified_region in Jifty::Web, and uses that to pull runtime values for the path and arguments from the state_variables in Jifty::Request before overriding them with the "force" versions.

exit

Exits the page region, if it is the most recent one. Normally, you won't need to call this by hand; however, if you are calling enter by hand, you will need to call the corresponding exit.

as_string

Deals with the bulk of the effort to show a page region. Returns a string of the fragment and associated javascript (if any).

render

Calls enter, outputs the results of as_string, and then calls exit. Returns an empty string.

make_body

Outputs the results of the region to the current buffer.

render_as_subrequest

get_element [RULES]

Returns a CSS2 selector which selects only elements under this region which fit the RULES. This method is used by AJAX code to specify where to add new regions.

client_cacheable

Returns the client cacheable state of the regions path. Returns false if the template has not been marked as client cacheable. Otherwise it returns the string "static" or "action" based on the cacheable attribute set on the template.

client_cache_content

Returns the template as JavaScript code.


Jifty documentation Contained in the Jifty distribution.
use warnings;
use strict;
 
package Jifty::Web::PageRegion;

use base qw/Jifty::Object Class::Accessor::Fast/;
__PACKAGE__->mk_accessors(qw(name force_path force_arguments default_path default_arguments qualified_name parent region_wrapper lazy loading_path class));
use Jifty::JSON;
use Encode ();

sub new {
    my $class = shift;
    my $self = bless {}, $class;

    my %args = (
                name => undef,
                path => "/__jifty/empty",
                defaults => {},
                parent => undef,
                force_arguments => {},
                force_path => undef,
                region_wrapper => 1,
                lazy => 0,
                loading_path => undef,
                @_
               );


    $args{'arguments'} ||= delete $args{'defaults'};

    # Name is required
    if (not defined $args{name}) {
        warn "Name is required for page regions.";
        return;
    }

    # References don't go over HTTP very well
    if (grep {ref $_} values %{$args{arguments}}) {
        warn "Reference '$args{arguments}{$_}' passed as default for '$_' to region '$args{name}'"
          for grep {ref $args{arguments}{$_}} keys %{$args{arguments}};
        return;
    }

    $self->name($args{name});
    $self->qualified_name(Jifty->web->qualified_region($self));
    $self->default_path($args{path});
    $self->default_arguments($args{arguments});
    $self->force_arguments($args{force_arguments});
    $self->force_path($args{force_path});
    $self->arguments({});
    $self->parent($args{parent} || Jifty->web->current_region);
    $self->region_wrapper($args{region_wrapper});
    $self->lazy($args{lazy});
    $self->loading_path($args{loading_path});
    $self->class($args{class});

    # Keep track of the fully qualified name (which should be unique)
    $self->log->warn("Repeated region: " . $self->qualified_name)
        if Jifty->web->get_region( $self->qualified_name );
    Jifty->web->{'regions'}{ $self->qualified_name } = $self;

    return $self;
}

sub path {
    my $self = shift;
    $self->{path} = shift if @_;
    return $self->{path} || $self->default_path;
}

sub default_argument {
    my $self = shift;
    my $name = shift;
    $self->{default_arguments}{$name} = shift if @_;
    return $self->{default_arguments}{$name} || '';
}

sub argument {
    my $self = shift;
    my $name = shift;
    $self->{arguments}{$name} = shift if @_;
    return $self->force_arguments->{$name}||$self->{arguments}{$name} || $self->default_argument($name);
}

sub arguments {
    my $self = shift;
    $self->{arguments} = shift if @_;
    return { %{$self->{default_arguments}}, %{$self->{arguments}}, %{$self->force_arguments}};
}

sub enter {
    my $self = shift;

    # Add ourselves to the region stack
    push @{Jifty->web->{'region_stack'}}, $self;

    # Merge in the settings passed in via state variables
    for my $var (Jifty->web->request->state_variables) {
        my $key = $var->key;
        my $value = $var->value || '';

        if ($key =~ /^region-(.*?)\.(.*)/ and $1 eq $self->qualified_name and $value ne $self->default_argument($2)) {
            $self->argument($2 => $value);
        }
        if ($key =~ /^region-(.*)$/ and $1 eq $self->qualified_name and $value ne $self->default_path) {
            $self->path(URI::Escape::uri_unescape($value));
        }

        # We should always inherit the state variables from the uplevel request.
        Jifty->web->set_variable($key => $value);
    }

    for my $argument (keys %{$self->force_arguments}) {
            $self->argument($argument => $self->force_arguments->{$argument});
    }

    $self->path($self->force_path) if ($self->force_path);
}

sub exit {
    my $self = shift;

    if (Jifty->web->current_region != $self) {
        $self->log->warn("Attempted to exit page region ".$self->qualified_name." when it wasn't the most recent");
    } else {
        pop @{Jifty->web->{'region_stack'}};
    }
}

sub as_string {
    my $self = shift;
    Jifty->handler->buffer->push(private => 1, from => "PageRegion render of ".$self->path);
    $self->make_body;
    return Jifty->handler->buffer->pop;
}

sub render {
    my $self = shift;

    $self->enter;
    $self->make_body;
    $self->exit;

    return '';
}

sub make_body {
    my $self = shift;
    my $buffer = Jifty->handler->buffer;

    my %arguments = %{ $self->arguments };

    # undef arguments cause warnings. We hatesses the warnings, we do.
    defined $arguments{$_} or delete $arguments{$_} for keys %arguments;

    # We need to tell the browser this is a region and what its
    # default arguments are as well as the path of the "fragment".  We
    # do this by passing in a snippet of javascript which encodes this
    # information.  We only render this region wrapper if we're asked
    # to (which is true by default)
    if ( $self->region_wrapper ) {
         $buffer->append(qq|<script type="text/javascript">\n|
            . qq|new Region('| . $self->qualified_name . qq|',|
            . Jifty::JSON::encode_json( \%arguments ) . qq|,| 
            . qq|'| . $self->path . qq|',|
            . ( $self->parent ? qq|'| . $self->parent->qualified_name . qq|'| : q|null|)
            . qq|,| . (Jifty->web->form->is_open ? '1' : 'null')
            . qq|);\n|
            . qq|</script>|);
        if ($self->lazy) {
            $buffer->append(qq|<script type="text/javascript">|
              . qq|jQuery(function(){ Jifty.update( { 'fragments': [{'region': '|.$self->qualified_name.qq|', 'mode': 'Replace'}], 'actions': {}}, document.getElementById('region-|.$self->qualified_name.qq|'))})|
              . qq|</script>|);
        }

        my $class = 'jifty-region';
        $class .= ' ' . $self->class if $self->class;
        $buffer->append(qq|<div id="region-| . $self->qualified_name . qq|" class="| . $class . qq|">|);

        if ($self->lazy) {
            if ($self->loading_path) {
                local $self->{path} = $self->loading_path;
                $self->render_as_subrequest(\%arguments);
            }
            $buffer->append(qq|</div>|);
            return;
        }
    }

    $self->render_as_subrequest(\%arguments);
    $buffer->append(qq|</div>|) if ( $self->region_wrapper );
}

sub render_as_subrequest {
    my ($self, $arguments, $enable_actions) = @_;

    # Make a fake request and throw it at the dispatcher
    my $subrequest = Jifty->web->request->clone;
    $subrequest->argument( region => $self );
    # XXX: use ->arguments?
    $subrequest->argument( $_ => $arguments->{$_}) for keys %$arguments;
    $subrequest->template_arguments({});
    $subrequest->path( $self->path );
    $subrequest->top_request( Jifty->web->request->top_request );

    my %args;
    if ($self->path =~ m/\?/) {
        # XXX: this only happens if we are redirect within region AND
        # with continuation, which is already taken care of by the
        # clone.
        my ($path, $arg) = split(/\?/, $self->path, 2);
        $subrequest->path( $path );
        %args = (map { split /=/, $_ } split /&/, $arg);
        if ($args{'J:C'}) {
            $subrequest->continuation($args{'J:C'});
        }
    }
    # Remove all of the actions
    unless ($enable_actions) {
        $_->active(0) for ($subrequest->actions);
    }
    # $subrequest->clear_actions;
    local Jifty->web->{request} = $subrequest;
    if ($args{'J:RETURN'}) {
        my $top = Jifty->web->request->top_request;
        my $cont = Jifty->web->session->get_continuation($args{'J:RETURN'});
        $cont->return;
        # need to set this as subrequest again as it's clobbered by the return
        Jifty->web->request->top_request($top);
    }

    # Call into the dispatcher
    Jifty->handler->dispatcher->handle_request;

    return;
}

sub get_element {
    my $self = shift;
    return "#region-" . $self->qualified_name . ' ' . join(' ', @_);
}

sub client_cacheable {
    my $self = shift;
    my ($jspr) = Jifty->find_plugin('Jifty::Plugin::JSPageRegion') or return;

    return $jspr->client_cacheable($self->path);
}

sub client_cache_content {
    my $self = shift;
    my ($jspr) = Jifty->find_plugin('Jifty::Plugin::JSPageRegion') or return;

    return $jspr->compile_to_js($self->path);
}

1;