);
my %supported_methods = (
- # ## Non-jQuery methods ##
- flash => 2, # kivi.display_flash(<TARGET>, <ARGS>)
-
# ## jQuery basics ##
# Basic effects
# ## other stuff ##
redirect_to => 1, # window.location.href = <TARGET>
+ flash => 2, # kivi.display_flash(<TARGET>, <ARGS>)
reinit_widgets => 0, # kivi.reinit_widgets()
+ run => -1, # kivi.run(<TARGET>, <ARGS>)
+ run_once_for => 3, # kivi.run_once_for(<TARGET>, <ARGS>)
);
sub AUTOLOAD {
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
=back
+=head2 KIVITENDO FUNCTIONS
+
+The following functions from the C<kivi> namespace are supported:
+
+=over 4
+
+=item Displaying stuff
+
+C<flash> (don't call directly, use L</flash> instead)
+
+=item Running functions
+
+C<run>, C<run_once_for>
+
+=item Widgets
+
+C<reinit_widgets>
+
+=back
+
=head2 JQUERY FUNCTIONS
The following jQuery functions are supported:
C<attr>, C<prop>, C<removeAttr>, C<removeProp>, C<val>
+=item Class attributes
+
+C<addClass>, C<removeClass>, C<toggleClass>
+
=item Data storage
C<data>, C<removeData>
=back
-=head2 JSTREE JQUERY PLUGIN
+=head2 JQUERY POPUP DIALOG PLUGIN
-The following functions of the C<jstree> plugin to jQuery are
+Supported functions of the C<popup dialog> plugin to jQuery. They are
+invoked by first calling C<dialog> in the ClientJS instance and then
+the function itself:
+
+ $js->dialog->close(...);
+
+=over 4
+
+=item Closing and removing the popup
+
+C<close>
+
+=back
+
+=head2 AJAXFORM JQUERY PLUGIN
+
+The following functions of the C<ajaxForm> plugin to jQuery are
supported:
=over 4
+=item All functions by the generic accessor function:
+
+C<ajaxForm>
+
+=back
+
+=head2 JSTREE JQUERY PLUGIN
+
+Supported functions of the C<jstree> plugin to jQuery. They are
+invoked by first calling C<jstree> in the ClientJS instance and then
+the function itself:
+
+ $js->jstree->open_node(...);
+
+=over 4
+
=item Operations on the whole tree
C<lock>, C<unlock>
=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 C<E<lt>ARGSE<gt>> format expands to an actual
+array (and the individual elements if the value is positive>.
=item 2. Run C<scripts/generate_client_js_actions.pl>. It will
generate C<js/client_js.js> automatically.
$(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();
// ## 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]);
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');
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;
}
$pattern = $action->[2] eq '<DEFAULT>' ? $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',