);
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.
// 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());
}
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";
--- /dev/null
+// 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());
+}