5 use parent qw(Rose::Object);
10 use Rose::Object::MakeMethods::Generic
12 'scalar --get_set_init' => [ qw(_actions) ],
15 my %supported_methods = (
23 # DOM insertion, around
29 # DOM insertion, inside
37 # DOM insertion, outside
62 # ## jstree plugin ## pattern: $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>)
64 # Operations on the whole tree
68 # Opening and closing nodes
69 'jstree:open_node' => 2,
70 'jstree:open_all' => 2,
71 'jstree:close_node' => 2,
72 'jstree:close_all' => 2,
73 'jstree:toggle_node' => 2,
74 'jstree:save_opened' => 1,
78 'jstree:rename_node' => 3,
79 'jstree:delete_node' => 2,
80 'jstree:move_node' => 5,
82 # Selecting nodes (from the 'ui' plugin to jstree)
83 'jstree:select_node' => 2, # $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>, true)
84 'jstree:deselect_node' => 2,
85 'jstree:deselect_all' => 1,
91 my ($self, @args) = @_;
93 my $method = $AUTOLOAD;
95 return if $method eq 'DESTROY';
97 $method = (delete($self->{_prefix}) || '') . $method;
98 my $num_args = $supported_methods{$method};
100 croak "Unsupported jQuery action: $method" unless defined $num_args;
101 croak "Parameter count mismatch for $method(actual: " . scalar(@args) . " wanted: $num_args)" if scalar(@args) != $num_args;
104 # Force flattening from SL::Presenter::EscapedText: "" . $...
105 $args[0] = "" . $args[0];
106 $args[0] =~ s/^\s+//;
109 push @{ $self->_actions }, [ $method, @args ];
120 return SL::JSON::to_json({ eval_actions => $self->_actions });
125 return $self->_actions;
129 my ($self, $controller) = @_;
130 return $controller->render(\$self->to_json, { type => 'json' });
135 $self->{_prefix} = 'jstree:';
148 SL::ClientJS - Easy programmatic client-side JavaScript generation
153 First some JavaScript code:
155 // In the client generate an AJAX request whose 'success' handler
156 // calls "eval_json_response(data)":
158 action: "SomeController/the_action",
159 id: $('#some_input_field').val()
161 $.post("controller.pl", data, eval_json_response);
165 # In the controller itself. First, make sure that the "client_js.js"
166 # is loaded. This must be done when the whole side is loaded, so
167 # it's not in the action called by the AJAX request shown above.
168 $::request->layout->use_javascript('client_js.js');
170 # Now in that action called via AJAX:
171 sub action_the_action {
174 # Create a new client-side JS object and do stuff with it!
175 my $js = SL::ClientJS->new;
177 # Show some element on the page:
178 $js->show('#usually_hidden');
180 # Set to hidden inputs. Yes, calls can be chained!
181 $js->val('#hidden_id', $self->new_id)
182 ->val('#other_type', 'Unicorn');
184 # Replace some HTML code:
185 my $html = $self->render('SomeController/the_action', { output => 0 });
186 $js->html('#id_with_new_content', $html);
188 # Operations on a jstree: rename a node and select it
189 my $text_block = SL::DB::RequirementSpecTextBlock->new(id => 4711)->load;
190 $js->jstree->rename_node('#tb-' . $text_block->id, $text_block->title)
191 ->jstree->select_node('#tb-' . $text_block->id);
193 # Finally render the JSON response:
196 # Rendering can also be chained, e.g.
197 $js->html('#selector', $html)
203 This module enables the generation of jQuery-using JavaScript code on
204 the server side. That code is then evaluated in a safe way on the
207 The workflow is usally that the client creates an AJAX request, the
208 server creates some actions and sends them back, and the client then
209 implements each of these actions.
211 There are three things that need to be done for this to work:
215 =item 1. The "client_js.js" has to be loaded before the AJAX request is started.
217 =item 2. The client code needs to call C<eval_json_response()> with the result returned from the server.
219 =item 3. The server must use this module.
223 The functions called on the client side are mostly jQuery
224 functions. Other functionality may be added later.
226 Note that L<SL::Controller/render> is aware of this module which saves
227 you some boilerplate. The following two calls are equivalent:
229 $controller->render($client_js);
230 $controller->render(\$client_js->to_json, { type => 'json' });
232 =head1 FUNCTIONS NOT PASSED TO THE CLIENT SIDE
238 Returns the actions gathered so far as an array reference. Each
239 element is an array reference containing at least two items: the
240 function's name and what it is called on. Additional array elements
241 are the function parameters.
245 Returns the actions gathered so far as a JSON string ready to be sent
248 =item C<render $controller>
250 Renders C<$self> via the controller. Useful for chaining. Equivalent
253 $controller->render(\$self->to_json, { type => 'json' });
257 Tells C<$self> that the next action is to be called on a jstree
258 instance. For example:
260 $js->jstree->rename_node('tb-' . $text_block->id, $text_block->title);
264 =head1 FUNCTIONS EVALUATED ON THE CLIENT SIDE
266 =head2 JQUERY FUNCTIONS
268 The following jQuery functions are supported:
274 C<hide>, C<show>, C<toggle>
276 =item DOM insertion, around
278 C<unwrap>, C<wrap>, C<wrapAll>, C<wrapInner>
280 =item DOM insertion, inside
282 C<append>, C<appendTo>, C<html>, C<prepend>, C<prependTo>, C<text>
284 =item DOM insertion, outside
286 C<after>, C<before>, C<insertAfter>, C<insertBefore>
292 =item DOM replacement
294 C<replaceAll>, C<replaceWith>
296 =item General attributes
298 C<attr>, C<prop>, C<removeAttr>, C<removeProp>, C<val>
302 C<data>, C<removeData>
306 =head2 JSTREE JQUERY PLUGIN
308 The following functions of the C<jstree> plugin to jQuery are
313 =item Operations on the whole tree
317 =item Opening and closing nodes
319 C<open_node>, C<close_node>, C<toggle_node>, C<open_all>,
320 C<close_all>, C<save_opened>, C<reopen>
322 =item Modifying nodes
324 C<rename_node>, C<delete_node>, C<move_node>
326 =item Selecting nodes (from the 'ui' jstree plugin)
328 C<select_node>, C<deselect_node>, C<deselect_all>
332 =head1 ADDING SUPPORT FOR ADDITIONAL FUNCTIONS
334 In order not having to maintain two files (this one and
335 C<js/client_js.js>) there's a script that can parse this file's
336 C<%supported_methods> definition and generate the file
337 C<js/client_js.js> accordingly. The steps are:
341 =item 1. Add lines in this file to the C<%supported_methods> hash. The
342 key is the function name and the value is the number of expected
345 =item 2. Run C<scripts/generate_client_js_actions.pl>. It will
346 generate C<js/client_js.js> automatically.
348 =item 3. Reload the files in your browser (cleaning its cache can also
353 The template file used for generated C<js/client_js.js> is
354 C<scripts/generate_client_js_actions.tpl>.
362 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>