From 6ca2197818029fad5253edd8f08988ddc66aa359 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 6 Mar 2013 16:31:28 +0100 Subject: [PATCH] ClientJS: um jstree-Funktionen erweitert; client_js.js komplett automatisch erzeugen --- SL/ClientJS.pm | 92 ++++++++++++++++++++++++-- js/client_js.js | 89 ++++++++++++++++--------- scripts/generate_client_js_actions.pl | 52 ++++++++++----- scripts/generate_client_js_actions.tpl | 22 ++++++ 4 files changed, 199 insertions(+), 56 deletions(-) create mode 100644 scripts/generate_client_js_actions.tpl diff --git a/SL/ClientJS.pm b/SL/ClientJS.pm index 4a0080fc5..6f9ce5adb 100644 --- a/SL/ClientJS.pm +++ b/SL/ClientJS.pm @@ -13,6 +13,8 @@ use Rose::Object::MakeMethods::Generic ); my %supported_methods = ( + # ## jQuery basics ## + # Basic effects hide => 1, show => 1, @@ -56,6 +58,31 @@ my %supported_methods = ( # Data storage data => 3, removeData => 2, + + # ## jstree plugin ## pattern: $.jstree._reference($()).() + + # 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($()).(, true) + 'jstree:deselect_node' => 2, + 'jstree:deselect_all' => 1, ); sub AUTOLOAD { @@ -67,8 +94,8 @@ 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; @@ -103,6 +130,12 @@ sub render { return $controller->render(\$self->to_json, { type => 'json' }); } +sub jstree { + my ($self) = @_; + $self->{_prefix} = 'jstree:'; + return $self; +} + 1; __END__ @@ -152,8 +185,17 @@ Now some Perl code: 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 @@ -210,6 +252,13 @@ to the following: $controller->render(\$self->to_json, { type => 'json' }); +=item C + +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 @@ -254,13 +303,38 @@ C, C =back +=head2 JSTREE JQUERY PLUGIN + +The following functions of the C plugin to jQuery are +supported: + +=over 4 + +=item Operations on the whole tree + +C, C + +=item Opening and closing nodes + +C, C, C, C, +C, C, C + +=item Modifying nodes + +C, C, C + +=item Selecting nodes (from the 'ui' jstree plugin) + +C, C, C + +=back + =head1 ADDING SUPPORT FOR ADDITIONAL FUNCTIONS In order not having to maintain two files (this one and C) 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. The steps -are: +C<%supported_methods> definition and generate the file +C accordingly. The steps are: =over 2 @@ -268,13 +342,17 @@ are: key is the function name and the value is the number of expected parameters. -=item 2. Run C +=item 2. Run C. It will +generate C automatically. -=item 3. Edit C 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 is +C. + =head1 BUGS Nothing here yet. diff --git a/js/client_js.js b/js/client_js.js index 3354fe7ca..34b254b5a 100644 --- a/js/client_js.js +++ b/js/client_js.js @@ -1,6 +1,6 @@ // 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. @@ -15,52 +15,79 @@ function eval_json_result(data) { $(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()); } diff --git a/scripts/generate_client_js_actions.pl b/scripts/generate_client_js_actions.pl index b2598b39e..d2219a1d1 100755 --- a/scripts/generate_client_js_actions.pl +++ b/scripts/generate_client_js_actions.pl @@ -5,40 +5,56 @@ use warnings; 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 = '$().()'; +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_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//'action[1]'/eg; + $call =~ s//$function/eg; + $call =~ s//$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"; diff --git a/scripts/generate_client_js_actions.tpl b/scripts/generate_client_js_actions.tpl new file mode 100644 index 000000000..6572e22c3 --- /dev/null +++ b/scripts/generate_client_js_actions.tpl @@ -0,0 +1,22 @@ +// 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()); +} -- 2.20.1