Revision 6ca21978
Von Moritz Bunkus vor fast 12 Jahren hinzugefügt
SL/ClientJS.pm | ||
---|---|---|
);
|
||
|
||
my %supported_methods = (
|
||
# ## jQuery basics ##
|
||
|
||
# Basic effects
|
||
hide => 1,
|
||
show => 1,
|
||
... | ... | |
# Data storage
|
||
data => 3,
|
||
removeData => 2,
|
||
|
||
# ## 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: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,
|
||
);
|
||
|
||
sub AUTOLOAD {
|
||
... | ... | |
$method =~ s/.*:://;
|
||
return if $method eq 'DESTROY';
|
||
|
||
$method = (delete($self->{_prefix}) || '') . $method;
|
||
my $num_args = $supported_methods{$method};
|
||
$::lxdebug->message(0, "autoload method $method");
|
||
|
||
croak "Unsupported jQuery action: $method" unless defined $num_args;
|
||
croak "Parameter count mismatch for $method(actual: " . scalar(@args) . " wanted: $num_args)" if scalar(@args) != $num_args;
|
||
... | ... | |
return $controller->render(\$self->to_json, { type => 'json' });
|
||
}
|
||
|
||
sub jstree {
|
||
my ($self) = @_;
|
||
$self->{_prefix} = 'jstree:';
|
||
return $self;
|
||
}
|
||
|
||
1;
|
||
__END__
|
||
|
||
... | ... | |
my $html = $self->render('SomeController/the_action', { output => 0 });
|
||
$js->html('#id_with_new_content', $html);
|
||
|
||
# Operations on a jstree: rename a node and select it
|
||
my $text_block = SL::DB::RequirementSpecTextBlock->new(id => 4711)->load;
|
||
$js->jstree->rename_node('#tb-' . $text_block->id, $text_block->title)
|
||
->jstree->select_node('#tb-' . $text_block->id);
|
||
|
||
# Finally render the JSON response:
|
||
$self->render($js);
|
||
|
||
# Rendering can also be chained, e.g.
|
||
$js->html('#selector', $html)
|
||
->render($self);
|
||
}
|
||
|
||
=head1 OVERVIEW
|
||
... | ... | |
|
||
$controller->render(\$self->to_json, { type => 'json' });
|
||
|
||
=item C<jstree>
|
||
|
||
Tells C<$self> that the next action is to be called on a jstree
|
||
instance. For example:
|
||
|
||
$js->jstree->rename_node('tb-' . $text_block->id, $text_block->title);
|
||
|
||
=back
|
||
|
||
=head1 FUNCTIONS EVALUATED ON THE CLIENT SIDE
|
||
... | ... | |
|
||
=back
|
||
|
||
=head2 JSTREE JQUERY PLUGIN
|
||
|
||
The following functions of the C<jstree> plugin to jQuery are
|
||
supported:
|
||
|
||
=over 4
|
||
|
||
=item Operations on the whole tree
|
||
|
||
C<lock>, C<unlock>
|
||
|
||
=item Opening and closing nodes
|
||
|
||
C<open_node>, C<close_node>, C<toggle_node>, C<open_all>,
|
||
C<close_all>, C<save_opened>, C<reopen>
|
||
|
||
=item Modifying nodes
|
||
|
||
C<rename_node>, C<delete_node>, C<move_node>
|
||
|
||
=item Selecting nodes (from the 'ui' jstree plugin)
|
||
|
||
C<select_node>, C<deselect_node>, C<deselect_all>
|
||
|
||
=back
|
||
|
||
=head1 ADDING SUPPORT FOR ADDITIONAL FUNCTIONS
|
||
|
||
In order not having to maintain two files (this one and
|
||
C<js/client_js.js>) there's a script that can parse this file's
|
||
C<%supported_methods> definition and convert it into the appropriate
|
||
code ready for manual insertion into C<js/client_js.js>. The steps
|
||
are:
|
||
C<%supported_methods> definition and generate the file
|
||
C<js/client_js.js> accordingly. The steps are:
|
||
|
||
=over 2
|
||
|
||
... | ... | |
key is the function name and the value is the number of expected
|
||
parameters.
|
||
|
||
=item 2. Run C<scripts/generate_client_js_actions.pl>
|
||
=item 2. Run C<scripts/generate_client_js_actions.pl>. It will
|
||
generate C<js/client_js.js> automatically.
|
||
|
||
=item 3. Edit C<js/client_js.js> and replace the type casing code with
|
||
the output generated in step 2.
|
||
=item 3. Reload the files in your browser (cleaning its cache can also
|
||
help).
|
||
|
||
=back
|
||
|
||
The template file used for generated C<js/client_js.js> is
|
||
C<scripts/generate_client_js_actions.tpl>.
|
||
|
||
=head1 BUGS
|
||
|
||
Nothing here yet.
|
js/client_js.js | ||
---|---|---|
// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE:
|
||
|
||
// Generate the dispatching lines in this script by running
|
||
// This file is generated automatically by the script
|
||
// "scripts/generate_client_js_actions.pl". See the documentation for
|
||
// SL/ClientJS.pm for instructions.
|
||
|
||
... | ... | |
$(data.eval_actions).each(function(idx, action) {
|
||
// console.log("ACTION " + action[0] + " ON " + action[1]);
|
||
|
||
// ## jQuery basics ##
|
||
// Basic effects
|
||
if (action[0] == 'hide') $(action[1]).hide();
|
||
else if (action[0] == 'show') $(action[1]).show();
|
||
else if (action[0] == 'toggle') $(action[1]).toggle();
|
||
if (action[0] == 'hide') $(action[1]).hide();
|
||
else if (action[0] == 'show') $(action[1]).show();
|
||
else if (action[0] == 'toggle') $(action[1]).toggle();
|
||
|
||
// DOM insertion, around
|
||
else if (action[0] == 'unwrap') $(action[1]).unwrap();
|
||
else if (action[0] == 'wrap') $(action[1]).wrap(action[2]);
|
||
else if (action[0] == 'wrapAll') $(action[1]).wrapAll(action[2]);
|
||
else if (action[0] == 'wrapInner') $(action[1]).wrapInner(action[2]);
|
||
else if (action[0] == 'unwrap') $(action[1]).unwrap();
|
||
else if (action[0] == 'wrap') $(action[1]).wrap(action[2]);
|
||
else if (action[0] == 'wrapAll') $(action[1]).wrapAll(action[2]);
|
||
else if (action[0] == 'wrapInner') $(action[1]).wrapInner(action[2]);
|
||
|
||
// DOM insertion, inside
|
||
else if (action[0] == 'append') $(action[1]).append(action[2]);
|
||
else if (action[0] == 'appendTo') $(action[1]).appendTo(action[2]);
|
||
else if (action[0] == 'html') $(action[1]).html(action[2]);
|
||
else if (action[0] == 'prepend') $(action[1]).prepend(action[2]);
|
||
else if (action[0] == 'prependTo') $(action[1]).prependTo(action[2]);
|
||
else if (action[0] == 'text') $(action[1]).text(action[2]);
|
||
else if (action[0] == 'append') $(action[1]).append(action[2]);
|
||
else if (action[0] == 'appendTo') $(action[1]).appendTo(action[2]);
|
||
else if (action[0] == 'html') $(action[1]).html(action[2]);
|
||
else if (action[0] == 'prepend') $(action[1]).prepend(action[2]);
|
||
else if (action[0] == 'prependTo') $(action[1]).prependTo(action[2]);
|
||
else if (action[0] == 'text') $(action[1]).text(action[2]);
|
||
|
||
// DOM insertion, outside
|
||
else if (action[0] == 'after') $(action[1]).after(action[2]);
|
||
else if (action[0] == 'before') $(action[1]).before(action[2]);
|
||
else if (action[0] == 'insertAfter') $(action[1]).insertAfter(action[2]);
|
||
else if (action[0] == 'insertBefore') $(action[1]).insertBefore(action[2]);
|
||
else if (action[0] == 'after') $(action[1]).after(action[2]);
|
||
else if (action[0] == 'before') $(action[1]).before(action[2]);
|
||
else if (action[0] == 'insertAfter') $(action[1]).insertAfter(action[2]);
|
||
else if (action[0] == 'insertBefore') $(action[1]).insertBefore(action[2]);
|
||
|
||
// DOM removal
|
||
else if (action[0] == 'empty') $(action[1]).empty();
|
||
else if (action[0] == 'remove') $(action[1]).remove();
|
||
else if (action[0] == 'empty') $(action[1]).empty();
|
||
else if (action[0] == 'remove') $(action[1]).remove();
|
||
|
||
// DOM replacement
|
||
else if (action[0] == 'replaceAll') $(action[1]).replaceAll(action[2]);
|
||
else if (action[0] == 'replaceWith') $(action[1]).replaceWith(action[2]);
|
||
else if (action[0] == 'replaceAll') $(action[1]).replaceAll(action[2]);
|
||
else if (action[0] == 'replaceWith') $(action[1]).replaceWith(action[2]);
|
||
|
||
// General attributes
|
||
else if (action[0] == 'attr') $(action[1]).attr(action[2], action[3]);
|
||
else if (action[0] == 'prop') $(action[1]).prop(action[2], action[3]);
|
||
else if (action[0] == 'removeAttr') $(action[1]).removeAttr(action[2]);
|
||
else if (action[0] == 'removeProp') $(action[1]).removeProp(action[2]);
|
||
else if (action[0] == 'val') $(action[1]).val(action[2]);
|
||
else if (action[0] == 'attr') $(action[1]).attr(action[2], action[3]);
|
||
else if (action[0] == 'prop') $(action[1]).prop(action[2], action[3]);
|
||
else if (action[0] == 'removeAttr') $(action[1]).removeAttr(action[2]);
|
||
else if (action[0] == 'removeProp') $(action[1]).removeProp(action[2]);
|
||
else if (action[0] == 'val') $(action[1]).val(action[2]);
|
||
|
||
// Data storage
|
||
else if (action[0] == 'data') $(action[1]).data(action[2], action[3]);
|
||
else if (action[0] == 'removeData') $(action[1]).removeData(action[2]);
|
||
else if (action[0] == 'data') $(action[1]).data(action[2], action[3]);
|
||
else if (action[0] == 'removeData') $(action[1]).removeData(action[2]);
|
||
|
||
// ## jstree plugin ##
|
||
|
||
// Operations on the whole tree
|
||
else if (action[0] == 'jstree:lock') $.jstree._reference($(action[1])).lock();
|
||
else if (action[0] == 'jstree:unlock') $.jstree._reference($(action[1])).unlock();
|
||
|
||
// Opening and closing nodes
|
||
else if (action[0] == 'jstree:open_node') $.jstree._reference($(action[1])).open_node(action[2]);
|
||
else if (action[0] == 'jstree:open_all') $.jstree._reference($(action[1])).open_all(action[2]);
|
||
else if (action[0] == 'jstree:close_node') $.jstree._reference($(action[1])).close_node(action[2]);
|
||
else if (action[0] == 'jstree:close_all') $.jstree._reference($(action[1])).close_all(action[2]);
|
||
else if (action[0] == 'jstree:toggle_node') $.jstree._reference($(action[1])).toggle_node(action[2]);
|
||
else if (action[0] == 'jstree:save_opened') $.jstree._reference($(action[1])).save_opened();
|
||
else if (action[0] == 'jstree:reopen') $.jstree._reference($(action[1])).reopen();
|
||
|
||
// Modifying nodes
|
||
else if (action[0] == 'jstree:rename_node') $.jstree._reference($(action[1])).rename_node(action[2], action[3]);
|
||
else if (action[0] == 'jstree:delete_node') $.jstree._reference($(action[1])).delete_node(action[2]);
|
||
else if (action[0] == 'jstree:move_node') $.jstree._reference($(action[1])).move_node(action[2], action[3], action[4], action[5]);
|
||
|
||
// Selecting nodes (from the 'ui' plugin to jstree)
|
||
else if (action[0] == 'jstree:select_node') $.jstree._reference($(action[1])).select_node(action[2], true);
|
||
else if (action[0] == 'jstree:deselect_node') $.jstree._reference($(action[1])).deselect_node(action[2]);
|
||
else if (action[0] == 'jstree:deselect_all') $.jstree._reference($(action[1])).deselect_all();
|
||
|
||
else console.log('Unknown action: ' + action[0]);
|
||
|
||
else console.log("Unknown action: " + action[0]);
|
||
});
|
||
|
||
console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
|
||
// console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
|
||
}
|
scripts/generate_client_js_actions.pl | ||
---|---|---|
|
||
use File::Slurp;
|
||
use List::Util qw(first max);
|
||
use Template;
|
||
|
||
my $file_name = (first { -f } qw(SL/ClientJS.pm ../SL/ClientJS.pm)) || die "ClientJS.pm not found";
|
||
my $rel_dir = (first { -f "${_}/SL/ClientJS.pm" } qw(. ..)) || die "ClientJS.pm not found";
|
||
my @actions;
|
||
|
||
foreach (read_file($file_name)) {
|
||
foreach (read_file("${rel_dir}/SL/ClientJS.pm")) {
|
||
chomp;
|
||
|
||
next unless (m/^my \%supported_methods/ .. m/^\);/);
|
||
|
||
push @actions, [ 'action', $1, $2 ] if m/^\s+([a-zA-Z]+)\s*=>\s*(\d+),$/;
|
||
push @actions, [ 'comment', $1 ] if m/^\s+#\s+(.+)/;
|
||
push @actions, [ 'action', $1, $2, $3 ] if m/^ \s+ '? ([a-zA-Z_:]+) '? \s*=>\s* (\d+) , (?: \s* \# \s+ (.+))? $/x;
|
||
push @actions, [ 'comment', $1, $2 ] if m/^ \s+\# \s+ (.+?) (?: \s* pattern: \s+ (.+))? $/x;
|
||
}
|
||
|
||
my $longest = max map { length($_->[1]) } grep { $_->[0] eq 'action' } @actions;
|
||
my $first = 1;
|
||
my $output;
|
||
my $longest = max map { length($_->[1]) } grep { $_->[0] eq 'action' } @actions;
|
||
my $first = 1;
|
||
my $default_pattern = '$(<TARGET>).<FUNCTION>(<ARGS>)';
|
||
my $pattern = $default_pattern;
|
||
my $output = '';
|
||
|
||
# else if (action[0] == 'hide') $(action[1]).hide();
|
||
foreach my $action (@actions) {
|
||
if ($action->[0] eq 'comment') {
|
||
print "\n" unless $first;
|
||
print " // ", $action->[1], "\n";
|
||
$output .= "\n" unless $first;
|
||
$output .= " // " . $action->[1] . "\n";
|
||
|
||
$pattern = $action->[2] eq '<DEFAULT>' ? $default_pattern : $action->[2] if $action->[2];
|
||
|
||
} else {
|
||
my $args = $action->[2] == 1 ? '' : join(', ', map { "action[$_]" } (2..$action->[2]));
|
||
|
||
printf(' %s if (action[0] == \'%s\')%s $(action[1]).%s(%s);' . "\n",
|
||
$first ? ' ' : 'else',
|
||
$action->[1],
|
||
' ' x ($longest - length($action->[1])),
|
||
$action->[1],
|
||
$args);
|
||
$first = 0;
|
||
$output .= sprintf(' %s if (action[0] == \'%s\')%s ',
|
||
$first ? ' ' : 'else',
|
||
$action->[1],
|
||
' ' x ($longest - length($action->[1])));
|
||
|
||
my $function = $action->[1];
|
||
$function =~ s/.*://;
|
||
|
||
my $call = $action->[3] || $pattern;
|
||
$call =~ s/<TARGET>/'action[1]'/eg;
|
||
$call =~ s/<FUNCTION>/$function/eg;
|
||
$call =~ s/<ARGS>/$args/eg;
|
||
|
||
$output .= $call . ";\n";
|
||
$first = 0;
|
||
}
|
||
}
|
||
|
||
printf "\n else\%sconsole.log('Unknown action: ' + action[0]);\n", ' ' x (4 + 2 + 6 + 3 + 4 + 2 + $longest + 1);
|
||
$output .= sprintf "\n else\%sconsole.log('Unknown action: ' + action[0]);\n", ' ' x (4 + 2 + 6 + 3 + 4 + 2 + $longest + 1);
|
||
|
||
my $template = Template->new({ RELATIVE => 1 });
|
||
$template->process($rel_dir . '/scripts/generate_client_js_actions.tpl', { actions => $output }, $rel_dir . '/js/client_js.js') || die $template->error(), "\n";
|
||
print "js/client_js.js generated automatically.\n";
|
scripts/generate_client_js_actions.tpl | ||
---|---|---|
// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE:
|
||
|
||
// This file is generated automatically by the script
|
||
// "scripts/generate_client_js_actions.pl". See the documentation for
|
||
// SL/ClientJS.pm for instructions.
|
||
|
||
function eval_json_result(data) {
|
||
if (!data)
|
||
return;
|
||
|
||
if ((data.js || '') != '')
|
||
eval(data.js);
|
||
|
||
if (data.eval_actions)
|
||
$(data.eval_actions).each(function(idx, action) {
|
||
// console.log("ACTION " + action[0] + " ON " + action[1]);
|
||
|
||
[% actions %]
|
||
});
|
||
|
||
// console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
|
||
}
|
Auch abrufbar als: Unified diff
ClientJS: um jstree-Funktionen erweitert; client_js.js komplett automatisch erzeugen