From a82f3befb2c673b0d1af747033a284ef2434c061 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Mon, 12 Aug 2013 11:23:06 +0200 Subject: [PATCH] ClientJS: neue Funktionen "run()", "run_once_for()"; Dokumentation --- SL/ClientJS.pm | 80 ++++++++++++++++++++++++--- js/client_js.js | 9 ++- js/kivi.js | 46 +++++++++++++++ scripts/generate_client_js_actions.pl | 6 +- 4 files changed, 127 insertions(+), 14 deletions(-) diff --git a/SL/ClientJS.pm b/SL/ClientJS.pm index 204e3203b..ff4aeb8e2 100644 --- a/SL/ClientJS.pm +++ b/SL/ClientJS.pm @@ -13,9 +13,6 @@ use Rose::Object::MakeMethods::Generic ); my %supported_methods = ( - # ## Non-jQuery methods ## - flash => 2, # kivi.display_flash(, ) - # ## jQuery basics ## # Basic effects @@ -112,7 +109,10 @@ my %supported_methods = ( # ## other stuff ## redirect_to => 1, # window.location.href = + flash => 2, # kivi.display_flash(, ) reinit_widgets => 0, # kivi.reinit_widgets() + run => -1, # kivi.run(, ) + run_once_for => 3, # kivi.run_once_for(, ) ); sub AUTOLOAD { @@ -133,7 +133,14 @@ sub action { my $num_args = $supported_methods{$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; + + 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 and trim leading whitespace for scalars @@ -447,6 +454,26 @@ L. =back +=head2 KIVITENDO FUNCTIONS + +The following functions from the C namespace are supported: + +=over 4 + +=item Displaying stuff + +C (don't call directly, use L instead) + +=item Running functions + +C, C + +=item Widgets + +C + +=back + =head2 JQUERY FUNCTIONS The following jQuery functions are supported: @@ -481,6 +508,10 @@ C, C C, C, C, C, C +=item Class attributes + +C, C, C + =item Data storage C, C @@ -500,13 +531,45 @@ already exist when the handler is added. =back -=head2 JSTREE JQUERY PLUGIN +=head2 JQUERY POPUP DIALOG PLUGIN -The following functions of the C plugin to jQuery are +Supported functions of the C plugin to jQuery. They are +invoked by first calling C in the ClientJS instance and then +the function itself: + + $js->dialog->close(...); + +=over 4 + +=item Closing and removing the popup + +C + +=back + +=head2 AJAXFORM JQUERY PLUGIN + +The following functions of the C plugin to jQuery are supported: =over 4 +=item All functions by the generic accessor function: + +C + +=back + +=head2 JSTREE JQUERY PLUGIN + +Supported functions of the C plugin to jQuery. They are +invoked by first calling C in the ClientJS instance and then +the function itself: + + $js->jstree->open_node(...); + +=over 4 + =item Operations on the whole tree C, C @@ -537,7 +600,10 @@ C accordingly. The steps are: =item 1. Add lines in this file to the C<%supported_methods> hash. The key is the function name and the value is the number of expected -parameters. +parameters. The value can be negative to indicate that the function +takes at least the absolute of this value as parameters and optionally +more. In such a case the CARGSE> format expands to an actual +array (and the individual elements if the value is positive>. =item 2. Run C. It will generate C automatically. diff --git a/js/client_js.js b/js/client_js.js index 4697fa63b..f39b19289 100644 --- a/js/client_js.js +++ b/js/client_js.js @@ -29,13 +29,9 @@ ns.eval_json_result = function(data) { $(data.eval_actions).each(function(idx, action) { // console.log("ACTION " + action[0] + " ON " + action[1]); - // ## Non-jQuery methods ## - if (action[0] == 'flash') kivi.display_flash(action[1], action[2]); - // ## jQuery basics ## - // Basic effects - else if (action[0] == 'hide') $(action[1]).hide(); + if (action[0] == 'hide') $(action[1]).hide(); else if (action[0] == 'show') $(action[1]).show(); else if (action[0] == 'toggle') $(action[1]).toggle(); @@ -127,7 +123,10 @@ ns.eval_json_result = function(data) { // ## other stuff ## else if (action[0] == 'redirect_to') window.location.href = action[1]; + else if (action[0] == 'flash') kivi.display_flash(action[1], action[2]); else if (action[0] == 'reinit_widgets') kivi.reinit_widgets(); + else if (action[0] == 'run') kivi.run(action[1], action.slice(2, action.length)); + else if (action[0] == 'run_once_for') kivi.run_once_for(action[1], action[2], action[3]); else console.log('Unknown action: ' + action[0]); diff --git a/js/kivi.js b/js/kivi.js index d84f28d7a..a39820ae1 100644 --- a/js/kivi.js +++ b/js/kivi.js @@ -99,6 +99,52 @@ namespace("kivi", function(ns) { return true; }; + + // Run code only once for each matched element + // + // This allows running the function 'code' exactly once for each + // element that matches 'selector'. This is achieved by storing the + // state with jQuery's 'data' function. The 'identification' is + // required for differentiating unambiguously so that different code + // functions can still be run on the same elements. + // + // 'code' can be either a function or the name of one. It must + // resolve to a function that receives the jQueryfied element as its + // sole argument. + // + // Returns nothing. + ns.run_once_for = function(selector, identification, code) { + var attr_name = 'data-run-once-for-' + identification.toLowerCase().replace(/[^a-z]+/g, '-'); + var fn = typeof code === 'function' ? code : ns.get_function_by_name(code); + if (!fn) { + console.error('kivi.run_once_for(..., "' + code + '"): No function by that name found'); + return; + } + + $(selector).filter(function() { return $(this).data(attr_name) != true; }).each(function(idx, elt) { + var $elt = $(elt); + $elt.data(attr_name, true); + fn($elt); + }); + }; + + // Run a function by its name passing it some arguments + // + // This is a function useful mainly for the ClientJS functionality. + // It finds a function by its name and then executes it on an empty + // object passing the elements in 'args' (an array) as the function + // parameters retuning its result. + // + // Logs an error to the console and returns 'undefined' if the + // function cannot be found. + ns.run = function(function_name, args) { + var fn = ns.get_function_by_name(function_name); + if (fn) + return fn.apply({}, args); + + console.error('kivi.run("' + function_name + '"): No function by that name found'); + return undefined; + }; }); kivi = namespace('kivi'); diff --git a/scripts/generate_client_js_actions.pl b/scripts/generate_client_js_actions.pl index 448a42c51..a51c669c7 100755 --- a/scripts/generate_client_js_actions.pl +++ b/scripts/generate_client_js_actions.pl @@ -15,7 +15,7 @@ foreach (read_file("${rel_dir}/SL/ClientJS.pm")) { next unless (m/^my \%supported_methods/ .. m/^\);/); - push @actions, [ 'action', $1, $2, $3 ] if m/^ \s+ '? ([a-zA-Z_:]+) '? \s*=>\s* (\d+) , (?: \s* \# \s+ (.+))? $/x; + 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; } @@ -33,7 +33,9 @@ foreach my $action (@actions) { $pattern = $action->[2] eq '' ? $default_pattern : $action->[2] if $action->[2]; } else { - my $args = $action->[2] == 1 ? '' : join(', ', map { "action[$_]" } (2..$action->[2])); + my $args = $action->[2] == 1 ? '' + : $action->[2] < 0 ? 'action.slice(2, action.length)' + : join(', ', map { "action[$_]" } (2..$action->[2])); $output .= sprintf(' %s if (action[0] == \'%s\')%s ', $first ? ' ' : 'else', -- 2.20.1