X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FClientJS.pm;h=49bde8092cbfbeb1114c5969ad9f93786a51a04f;hb=2d0387d1624b5b7ed6b13b79b9d5a87ce9b6d12b;hp=fcc2c146c18546db465c4dc6dd9c917560e4c2fb;hpb=551d4b787115605d6b12ef199ea52ac6a5a2ad0c;p=kivitendo-erp.git diff --git a/SL/ClientJS.pm b/SL/ClientJS.pm index fcc2c146c..49bde8092 100644 --- a/SL/ClientJS.pm +++ b/SL/ClientJS.pm @@ -9,7 +9,8 @@ use SL::JSON (); use Rose::Object::MakeMethods::Generic ( - 'scalar --get_set_init' => [ qw(_actions _flash _error) ], + scalar => [ qw() ], + 'scalar --get_set_init' => [ qw(controller _actions _flash _flash_detail _no_flash_clear _error) ], ); my %supported_methods = ( @@ -74,11 +75,12 @@ my %supported_methods = ( # ## jQuery UI dialog plugin ## pattern: $().dialog('') - # Closing and removing the popup + # Opening and closing a popup + 'dialog:open' => 1, # kivi.popup_dialog() 'dialog:close' => 1, # ## jQuery Form plugin ## - 'ajaxForm' => 1, # pattern: $().ajaxForm({ success: eval_json_result }) + 'ajaxForm' => 1, # $().ajaxForm({ success: eval_json_result }) # ## jstree plugin ## pattern: $.jstree._reference($()).() @@ -111,13 +113,22 @@ my %supported_methods = ( # ## other stuff ## redirect_to => 1, # window.location.href = + save_file => 4, # kivi.save_file(, ) flash => 2, # kivi.display_flash(, ) + flash_detail => 2, # kivi.display_flash_detail(, ) + clear_flash => 2, # kivi.clear_flash(, ) reinit_widgets => 0, # kivi.reinit_widgets() run => -1, # kivi.run(, ) run_once_for => 3, # kivi.run_once_for(, ) + + scroll_into_view => 1, # $()[0].scrollIntoView() + + set_cursor_position => 2, # kivi.set_cursor_position(, ) ); +my %trim_target_for = map { ($_ => 1) } qw(insertAfter insertBefore appendTo prependTo); + sub AUTOLOAD { our $AUTOLOAD; @@ -135,10 +146,10 @@ sub action { $method = (delete($self->{_prefix}) || '') . $method; my $num_args = $supported_methods{$method}; - croak "Unsupported jQuery action: $method" unless defined $num_args; + croak "Unsupported jQuery action: $method" unless defined $num_args; if ($num_args > 0) { - croak "Parameter count mismatch for $method(actual: " . scalar(@args) . " wanted: $num_args)" if scalar(@args) != $num_args; + 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; @@ -146,11 +157,15 @@ sub action { } foreach my $idx (0..$num_args - 1) { - # Force flattening from SL::Presenter::EscapedText and trim leading whitespace for scalars - $args[$idx] = "" . $args[$idx] if ref($args[$idx]) eq 'SL::Presenter::EscapedText'; - $args[$idx] =~ s/^\s+// if !ref($args[$idx]); + # Force flattening from SL::Presenter::EscapedText. + $args[$idx] = "" . $args[$idx] if ref($args[$idx]) eq 'SL::Presenter::EscapedText'; } + # Trim leading whitespaces for certain jQuery functions that operate + # on HTML code: $("

test

").appendTo('#some-id'). jQuery croaks + # on leading whitespaces, e.g. on $("

test

"). + $args[0] =~ s{^\s+}{} if $trim_target_for{$method}; + push @{ $self->_actions }, [ $method, @args ]; return $self; @@ -170,15 +185,23 @@ sub init__flash { return {}; } +sub init__flash_detail { + return {}; +} + sub init__error { return ''; } +sub init__no_flash_clear { + return ''; +} + sub to_json { my ($self) = @_; - return SL::JSON::to_json({ error => $self->_error }) if $self->_error; - return SL::JSON::to_json({ eval_actions => $self->_actions }); + return SL::JSON::to_json({ error => $self->_error }) if $self->_error; + return SL::JSON::to_json({ no_flash_clear => $self->_no_flash_clear, eval_actions => $self->_actions }); } sub to_array { @@ -188,6 +211,7 @@ sub to_array { sub render { my ($self, $controller) = @_; + $controller ||= $self->controller; $self->reinit_widgets if $::request->presenter->need_reinit_widgets; return $controller->render(\$self->to_json, { type => 'json' }); } @@ -225,6 +249,27 @@ sub flash { return $self; } +sub flash_detail { + my ($self, $type, @messages) = @_; + + my $message = join '
', grep { $_ } @messages; + + if (!$self->_flash_detail->{$type}) { + $self->_flash_detail->{$type} = [ 'flash_detail', $type, $message ]; + push @{ $self->_actions }, $self->_flash_detail->{$type}; + } else { + $self->_flash_detail->{$type}->[-1] .= ' ' . $message; + } + + return $self; +} + +sub no_flash_clear{ + my ($self) = @_; + $self->_no_flash_clear('1'); + return $self; +} + sub error { my ($self, @messages) = @_; @@ -233,6 +278,12 @@ sub error { return $self; } +sub init_controller { + # fallback + require SL::Controller::Base; + SL::Controller::Base->new; +} + 1; __END__ @@ -252,24 +303,19 @@ First some JavaScript code: // In the client generate an AJAX request whose 'success' handler // calls "eval_json_result(data)": var data = { - action: "SomeController/the_action", + action: "SomeController/my_personal_action", id: $('#some_input_field').val() }; $.post("controller.pl", data, eval_json_result); -Now some Perl code: - - # In the controller itself. First, make sure that the "client_js.js" - # is loaded. This must be done when the whole side is loaded, so - # it's not in the action called by the AJAX request shown above. - $::request->layout->use_javascript('client_js.js'); +Now some Controller (perl) code for my personal action: - # Now in that action called via AJAX: - sub action_the_action { + # my personal action + sub action_my_personal_action { my ($self) = @_; # Create a new client-side JS object and do stuff with it! - my $js = SL::ClientJS->new; + my $js = SL::ClientJS->new(controller => $self); # Show some element on the page: $js->show('#usually_hidden'); @@ -295,7 +341,7 @@ Now some Perl code: # Rendering can also be chained, e.g. $js->html('#selector', $html) - ->render($self); + ->render; } =head1 OVERVIEW @@ -304,7 +350,7 @@ This module enables the generation of jQuery-using JavaScript code on the server side. That code is then evaluated in a safe way on the client side. -The workflow is usally that the client creates an AJAX request, the +The workflow is usually that the client creates an AJAX request, the server creates some actions and sends them back, and the client then implements each of these actions. @@ -345,13 +391,16 @@ are the function parameters. Returns the actions gathered so far as a JSON string ready to be sent to the client. -=item C +=item C Renders C<$self> via the controller. Useful for chaining. Equivalent to the following: $controller->render(\$self->to_json, { type => 'json' }); +The controller instance to use can be set during object creation (see +synopsis) or as an argument to C. + =item C Tells C<$self> that the next action is to be called on a jQuery UI @@ -443,6 +492,17 @@ C on the same C<$self> will be merged by type. On the client side the flashes of all types will be cleared after each successful ClientJS call that did not end with C<$js-Eerror(...)>. +This clearing can be switched of by the function C + +=item C + +Display a detailed message C<$message> in the flash of type C<$type>. Multiple calls of +C on the same C<$self> will be merged by type. +So the flash message can be hold short and the visibility of details can toggled by the user. + +=item C + +No automatic clearing of flash after successful ClientJS call =item C @@ -600,7 +660,7 @@ C, C, C =head1 ADDING SUPPORT FOR ADDITIONAL FUNCTIONS -In order not having to maintain two files (this one and +In order to not have to maintain two files (this one and C) there's a script that can parse this file's C<%supported_methods> definition and generate the file C accordingly. The steps are: