use strict; use warnings; package Jifty::Plugin::SinglePage; use base 'Jifty::Plugin'; __PACKAGE__->mk_accessors(qw(region_name)); our $NO_SPA; =head1 NAME Jifty::Plugin::SinglePage - Makes your app into a single-page =head1 DESCRIPTION Makes your normal Jifty app into a single-page app through clever use of regions =head1 METHODS =head2 init Registers a before_new trigger to modify links and sets up the special region =cut Jifty->web->add_javascript( 'singlepage/rsh/rsh.js', 'singlepage/spa.js' ); sub init { my $self = shift; return if $self->_pre_init; Jifty::Web::Form::Clickable->add_trigger( before_new => _sp_link($self)); Jifty::Web::Form::Clickable->add_trigger( name => 'before_state_variable', callback => _filter_page_region_vars($self), abortable => 1 ); my %opt = @_; $self->region_name($opt{region_name} || '__page'); } sub _push_onclick { my $self = shift; my $args = shift; $args->{onclick} = [ $args->{onclick} ? $args->{onclick} : () ] unless ref $args->{onclick} eq 'ARRAY'; push @{$args->{onclick}}, @_ if @_; } sub _filter_page_region_vars { my $self = shift; return sub { return if $NO_SPA; my ( $clickable, $key, $value ) = @_; if ($key eq 'region-'.$self->region_name || $key =~ m/^region-\Q$self->{region_name}\E\./) { return 0; } return 1; } } sub _sp_link { my $self = shift; return sub { return if $NO_SPA; return if Jifty->web->temporary_current_user; # for LetMe my ( $clickable, $args ) = @_; my $url = $args->{'url'}; if ( $url && $url !~ m/^#/ && $url !~ m{^https?://} && $url !~ m{^javascript:} ) { my $complete_url = $url.'?'.Jifty->web->query_string(%{$args->{parameters}}); $complete_url =~ s/\?$//; $self->_push_onclick($args, { region => $self->region_name, replace_with => $url, beforeclick => qq{SPA.historyChange('$complete_url', { 'continuation':{}, 'actions':{}, 'fragments':[{'mode':'Replace','args':@{[ Jifty::JSON::objToJson($args->{parameters})]},'region':'__page','path':'$url'}],'action_arguments':{}}, true);}, args => { %{$args->{parameters}}} }); } elsif (exists $args->{submit} && !$args->{onclick}) { if ($args->{_form} && $args->{_form}{submit_to}) { my $to = $args->{_form}{submit_to}; $self->_push_onclick($args, { beforeclick => qq{return SPA._sp_submit_form(this, event, "$to");} }); } else { $self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} }); } $args->{as_button} = 1; } if (my $form = delete $args->{_form}) { $args->{call} = $form->call; } my $onclick = $args->{onclick}; if ( $args->{onclick} ) { $self->_push_onclick($args); # make sure it's array for my $onclick ( @{ $args->{onclick} } ) { next unless UNIVERSAL::isa($onclick, 'HASH'); if ( $onclick->{region} && !ref( $onclick->{region} ) ) { my $region = $self->region_name; $onclick->{region} = $region . '-' . $onclick->{region} unless $onclick->{region} eq $region or $onclick->{region} =~ m/^\Q$region\E-/; } } } } } 1;