Projekt

Allgemein

Profil

Herunterladen (15,6 KB) Statistiken
| Zweig: | Markierung: | Revision:
package SL::ClientJS;

use strict;

use parent qw(Rose::Object);

use Carp;
use SL::JSON ();

use Rose::Object::MakeMethods::Generic
(
scalar => [ qw() ],
'scalar --get_set_init' => [ qw(controller _actions _error) ],
);

my %supported_methods = (
# ## jQuery basics ##

# Basic effects
hide => 1,
show => 1,
toggle => 1,

# DOM insertion, around
unwrap => 1,
wrap => 2,
wrapAll => 2,
wrapInner => 2,

# DOM insertion, inside
append => 2,
appendTo => 2,
html => 2,
prepend => 2,
prependTo => 2,
text => 2,

# DOM insertion, outside
after => 2,
before => 2,
insertAfter => 2,
insertBefore => 2,

# DOM removal
empty => 1,
remove => 1,

# DOM replacement
replaceAll => 2,
replaceWith => 2,

# General attributes
attr => 3,
prop => 3,
removeAttr => 2,
removeProp => 2,
val => 2,

# Class attribute
addClass => 2,
removeClass => 2,
toggleClass => 2,

# Data storage
data => 3,
removeData => 2,

# Form Events
focus => 1, # kivi.set_focus(<TARGET>)

# Generic Event Handling ## pattern: $(<TARGET>).<FUNCTION>(<ARG1>, kivi.get_function_by_name(<ARG2>))
on => 3,
off => 3,
one => 3,

# ## jQuery UI dialog plugin ## pattern: $(<TARGET>).dialog('<FUNCTION>')

# Opening and closing a popup
'dialog:open' => 1, # kivi.popup_dialog(<TARGET>)
'dialog:close' => 1,

# ## jQuery Form plugin ##
'ajaxForm' => 1, # $(<TARGET>).ajaxForm({ success: eval_json_result })

# ## jstree plugin ## pattern: $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>)

# Operations on the whole tree
'jstree:lock' => 1,
'jstree:unlock' => 1,

# Opening and closing nodes
'jstree:open_node' => 2,
'jstree:open_all' => 2,
'jstree:close_node' => 2,
'jstree:close_all' => 2,
'jstree:toggle_node' => 2,
'jstree:save_opened' => 1,
'jstree:reopen' => 1,

# Modifying nodes
'jstree:create_node' => 4,
'jstree:rename_node' => 3,
'jstree:delete_node' => 2,
'jstree:move_node' => 5,

# Selecting nodes (from the 'ui' plugin to jstree)
'jstree:select_node' => 2, # $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>, true)
'jstree:deselect_node' => 2,
'jstree:deselect_all' => 1,

# ## ckeditor stuff ##
'focus_ckeditor' => 1, # kivi.focus_ckeditor_when_ready(<TARGET>)

# ## other stuff ##
redirect_to => 1, # window.location.href = <TARGET>
save_file => 4, # kivi.save_file(<TARGET>, <ARGS>)

# flash
flash => -2, # kivi.Flash.display_flash.apply({}, action.slice(1, action.length))
clear_flash => 0, # kivi.Flash.clear_flash()
show_flash => 0, # kivi.Flash.show()
hide_flash => 0, # kivi.Flash.hide()

reinit_widgets => 0, # kivi.reinit_widgets()
run => -1, # kivi.run(<TARGET>, <ARGS>)
run_once_for => 3, # kivi.run_once_for(<TARGET>, <ARGS>)

scroll_into_view => 1, # $(<TARGET>)[0].scrollIntoView()

set_cursor_position => 2, # kivi.set_cursor_position(<TARGET>, <ARGS>)
);

my %trim_target_for = map { ($_ => 1) } qw(insertAfter insertBefore appendTo prependTo);

sub AUTOLOAD {
our $AUTOLOAD;

my ($self, @args) = @_;

my $method = $AUTOLOAD;
$method =~ s/.*:://;
return if $method eq 'DESTROY';
return $self->action($method, @args);
}

sub action {
my ($self, $method, @args) = @_;

$method = (delete($self->{_prefix}) || '') . $method;
my $num_args = $supported_methods{$method};

croak "Unsupported jQuery action: $method" unless defined $num_args;

if ($num_args > 0) {
croak "Parameter count mismatch for $method(actual: " . scalar(@args) . " wanted: $num_args)" if scalar(@args) != $num_args;
} else {
$num_args *= -1;
croak "Parameter count mismatch for $method(actual: " . scalar(@args) . " wanted at least: $num_args)" if scalar(@args) < $num_args;
$num_args = scalar @args;
}

foreach my $idx (0..$num_args - 1) {
# Force flattening from SL::Presenter::EscapedText.
$args[$idx] = "" . $args[$idx] if ref($args[$idx]) eq 'SL::Presenter::EscapedText';
}

# Trim leading whitespaces for certain jQuery functions that operate
# on HTML code: $("<p>test</p>").appendTo('#some-id'). jQuery croaks
# on leading whitespaces, e.g. on $(" <p>test</p>").
$args[0] =~ s{^\s+}{} if $trim_target_for{$method};

push @{ $self->_actions }, [ $method, @args ];

return $self;
}

sub action_if {
my ($self, $condition, @args) = @_;

return $condition ? $self->action(@args) : $self;
}

sub init__actions {
return [];
}

sub to_json {
my ($self) = @_;

return SL::JSON::to_json({ error => $self->_error }) if $self->_error;
return SL::JSON::to_json({ eval_actions => $self->_actions });
}

sub to_array {
my ($self) = @_;
return $self->_actions;
}

sub transfer_flash {
my ($self) = @_;
$self->flash(@$_) for SL::Helper::Flash->flash_contents;
}

sub render {
my ($self, $controller) = @_;
$controller ||= $self->controller;
$self->reinit_widgets if $::request->presenter->need_reinit_widgets;

return $self;
}

sub init_controller {
# fallback
require SL::Controller::Base;
SL::Controller::Base->new;
}

1;
__END__

=pod

=encoding utf8

=head1 NAME

SL::ClientJS - Easy programmatic client-side JavaScript generation