|
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;
|
"ClientJS.pm.html#L238" data-txt="238"> |
|
|
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
|
|
|