Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision a82451ee

Von Sven Schöling vor etwa 13 Jahren hinzugefügt

  • ID a82451eefa47668fc17b22e7365e390483ff3296
  • Vorgänger a871ab4c
  • Nachfolger f99ef184

CGI::Ajax entfernt.

Unterschiede anzeigen:

SL/Form.pm
40 40
use Data::Dumper;
41 41

  
42 42
use CGI;
43
use CGI::Ajax;
44 43
use Cwd;
45 44
use Encode;
46 45
use File::Copy;
SL/InstallationCheck.pm
13 13
  { name => "Archive::Zip",    version => '1.16',  url => "http://search.cpan.org/~adamk/",     debian => 'libarchive-zip-perl' },
14 14
  { name => "Class::Accessor", version => '0.30',  url => "http://search.cpan.org/~kasei/",     debian => 'libclass-accessor-perl' },
15 15
  { name => "Config::Std",                         url => "http://search.cpan.org/~dconway/",   debian => 'libconfig-std-perl' },
16
  { name => "CGI::Ajax",       version => '0.697', url => "http://search.cpan.org/~bct/" }, # no debian package, ours contains bugfixes
17 16
  { name => "DateTime",                            url => "http://search.cpan.org/~drolsky/",   debian => 'libdatetime-perl' },
18 17
  { name => "DBI",             version => '1.50',  url => "http://search.cpan.org/~timb/",      debian => 'libdbi-perl' },
19 18
  { name => "DBD::Pg",         version => '1.49',  url => "http://search.cpan.org/~dbdpg/",     debian => 'libdbd-pg' },
bin/mozilla/invoice_io.pl
34 34
#######################################################################
35 35

  
36 36
use CGI;
37
use CGI::Ajax;
38 37
use List::Util qw(max);
39 38

  
40 39
use SL::Common;
bin/mozilla/io.pl
38 38

  
39 39
use Carp;
40 40
use CGI;
41
use CGI::Ajax;
42 41
use List::Util qw(min max first);
43 42

  
44 43
use SL::CVar;
doc/INSTALL.html
215 215
<li>parent
216 216
<li>Archive::Zip
217 217
<li>Class::Accessor
218
<li>CGI::Ajax
219 218
<li>Config::Std
220 219
<li>DateTime
221 220
<li>DBI
......
248 247
aus dem Paket entfernt werden. Es wird empfohlen diese Module zusammen mit den
249 248
anderen als Bibliotheken zu installieren.
250 249

  
251
   <p><code>CGI::Ajax</code> ist nach wie vor in einer modifizierten Version mitgeliefert
252
und braucht nicht nachinstalliert werden.
253

  
254 250
   <p>Die zu installierenden Pakete können in den verschiedenen Distributionen unterschiedlich heißen.
255 251

  
256 252
   <p>Für Debian oder Ubuntu benötigen Sie diese Pakete:
doc/INSTALL.texi
140 140
@item
141 141
Class::Accessor
142 142
@item
143
CGI::Ajax
144
@item
145 143
Config::Std
146 144
@item
147 145
DateTime
......
190 188
aus dem Paket entfernt werden. Es wird empfohlen diese Module zusammen mit den
191 189
anderen als Bibliotheken zu installieren.
192 190

  
193
@code{CGI::Ajax} ist nach wie vor in einer modifizierten Version mitgeliefert
194
und braucht nicht nachinstalliert werden.
195

  
196 191
Die zu installierenden Pakete können in den verschiedenen Distributionen unterschiedlich heißen.
197 192

  
198 193
Für Debian oder Ubuntu benötigen Sie diese Pakete:
doc/INSTALL.txt
133 133

  
134 134
   * Class::Accessor
135 135

  
136
   * CGI::Ajax
137

  
138 136
   * Config::Std
139 137

  
140 138
   * DateTime
......
183 181
empfohlen diese Module zusammen mit den anderen als Bibliotheken zu
184 182
installieren.
185 183

  
186
   `CGI::Ajax' ist nach wie vor in einer modifizierten Version
187
mitgeliefert und braucht nicht nachinstalliert werden.
188

  
189 184
   Die zu installierenden Pakete können in den verschiedenen
190 185
Distributionen unterschiedlich heißen.
191 186

  
modules/override/CGI/.htaccess
1
Order Allow,Deny
2
Deny from all
modules/override/CGI/Ajax.pm
1
package CGI::Ajax;
2
use strict;
3
use Data::Dumper;
4
use base qw(Class::Accessor);
5
use overload '""' => 'show_javascript'; # for building web pages, so
6
                                        # you can just say: print $pjx
7
BEGIN {
8
	use vars qw ($VERSION @ISA @METHODS);
9
	@METHODS = qw(url_list coderef_list DEBUG JSDEBUG html
10
								js_encode_function cgi_header_extra);
11

  
12
	CGI::Ajax->mk_accessors( @METHODS );
13

  
14
	$VERSION     = .697;
15
}
16

  
17
########################################### main pod documentation begin ##
18

  
19
=head1 NAME
20

  
21
CGI::Ajax - a perl-specific system for writing Asynchronous web
22
applications
23

  
24
=head1 SYNOPSIS
25

  
26
  use strict;
27
  use CGI;      # or any other CGI:: form handler/decoder
28
  use CGI::Ajax;
29

  
30
  my $cgi = new CGI;
31
  my $pjx = new CGI::Ajax( 'exported_func' => \&perl_func );
32

  
33
  print $pjx->build_html( $cgi, \&Show_HTML);
34

  
35
  sub perl_func {
36
    my $input = shift;
37
    # do something with $input
38
    my $output = $input . " was the input!";
39
    return( $output );
40
  }
41

  
42
  sub Show_HTML {
43
    my $html = <<EOHTML;
44
    <HTML>
45
    <BODY>
46
      Enter something: 
47
        <input type="text" name="val1" id="val1"
48
         onkeyup="exported_func( ['val1'], ['resultdiv'] );">
49
      <br>
50
      <div id="resultdiv"></div>
51
    </BODY>
52
    </HTML>
53
  EOHTML
54
    return $html;
55
  }
56

  
57
I<There are several fully-functional examples in the 'scripts/'
58
directory of the distribution.>
59

  
60
=head1 DESCRIPTION
61

  
62
CGI::Ajax is an object-oriented module that provides a unique
63
mechanism for using perl code asynchronously from javascript-
64
enhanced HTML pages.  CGI::Ajax unburdens the user from having to
65
write extensive javascript, except for associating an exported
66
method with a document-defined event (such as onClick, onKeyUp,
67
etc).  CGI::Ajax also mixes well with HTML containing more complex
68
javascript.
69

  
70
CGI::Ajax supports methods that return single results or multiple
71
results to the web page, and supports returning values to multiple
72
DIV elements on the HTML page.
73

  
74
Using CGI::Ajax, the URL for the HTTP GET/POST request is
75
automatically generated based on HTML layout and events, and the
76
page is then dynamically updated with the output from the perl
77
function.  Additionally, CGI::Ajax supports mapping URL's to a
78
CGI::Ajax function name, so you can separate your code processing
79
over multiple scripts.
80

  
81
Other than using the Class::Accessor module to generate CGI::Ajax'
82
accessor methods, CGI::Ajax is completely self-contained - it
83
does not require you to install a larger package or a full Content
84
Management System, etc.
85

  
86
We have added I<support> for other CGI handler/decoder modules,
87
like L<CGI::Simple> or L<CGI::Minimal>, but we can't test these
88
since we run mod_perl2 only here.  CGI::Ajax checks to see if a
89
header() method is available to the CGI object, and then uses it.
90
If method() isn't available, it creates it's own minimal header.
91

  
92
A primary goal of CGI::Ajax is to keep the module streamlined and
93
maximally flexible.  We are trying to keep the generated javascript
94
code to a minimum, but still provide users with a variety of
95
methods for deploying CGI::Ajax. And VERY little user javascript.
96

  
97
=head1 EXAMPLES
98

  
99
The CGI::Ajax module allows a Perl subroutine to be called
100
asynchronously, when triggered from a javascript event on the
101
HTML page.  To do this, the subroutine must be I<registered>,
102
usually done during:
103

  
104
  my $pjx = new CGI::Ajax( 'JSFUNC' => \&PERLFUNC );
105

  
106
This maps a perl subroutine (PERLFUNC) to an automatically
107
generated Javascript function (JSFUNC).  Next you setup a trigger this
108
function when an event occurs (e.g. "onClick"):
109

  
110
  onClick="JSFUNC(['source1','source2'], ['dest1','dest2']);"
111

  
112
where 'source1', 'dest1', 'source2', 'dest2' are the DIV ids of
113
HTML elements in your page...
114

  
115
  <input type=text id=source1>
116
  <input type=text id=source2>
117
  <div id=dest1></div>
118
  <div id=dest2></div>
119

  
120
L<CGI::Ajax> sends the values from source1 and source2 to your
121
Perl subroutine and returns the results to dest1 and dest2.
122

  
123
=head2 4 Usage Methods
124

  
125
=over 4
126

  
127
=item 1 Standard CGI::Ajax example
128

  
129
Start by defining a perl subroutine that you want available from
130
javascript.  In this case we'll define a subrouting that determines
131
whether or not an input is odd, even, or not a number (NaN):
132

  
133
  use strict;
134
  use CGI::Ajax;
135
  use CGI;
136

  
137

  
138
  sub evenodd_func {
139
    my $input = shift;
140

  
141
    # see if input is defined
142
    if ( not defined $input ) {
143
      return("input not defined or NaN");
144
    }
145

  
146
    # see if value is a number (*thanks Randall!*)
147
    if ( $input !~ /\A\d+\z/ ) {
148
      return("input is NaN");
149
    }
150

  
151
    # got a number, so mod by 2
152
    $input % 2 == 0 ? return("EVEN") : return("ODD");
153
  }
154

  
155
Alternatively, we could have used coderefs to associate an
156
exported name...
157

  
158
  my $evenodd_func = sub {
159
    # exactly the same as in the above subroutine
160
  };
161

  
162
Next we define a function to generate the web page - this can
163
be done many different ways, and can also be defined as an
164
anonymous sub.  The only requirement is that the sub send back
165
the html of the page.  You can do this via a string containing the
166
html, or from a coderef that returns the html, or from a function
167
(as shown here)...
168

  
169
  sub Show_HTML {
170
    my $html = <<EOT;
171
  <HTML>
172
  <HEAD><title>CGI::Ajax Example</title>
173
  </HEAD>
174
  <BODY>
175
    Enter a number:&nbsp;
176
    <input type="text" name="somename" id="val1" size="6"
177
       OnKeyUp="evenodd( ['val1'], ['resultdiv'] );">
178
    <br>
179
    <hr>
180
    <div id="resultdiv">
181
    </div>
182
  </BODY>
183
  </HTML>
184
EOT
185
    return $html;
186
  }
187

  
188
The exported Perl subrouting is triggered using the C<OnKeyUp>
189
event handler of the input HTML element.  The subroutine takes one
190
value from the form, the input element B<'val1'>, and returns the
191
the result to an HTML div element with an id of B<'resultdiv'>.
192
Sending in the input id in an array format is required to support
193
multiple inputs, and similarly, to output multiple the results,
194
you can use an array for the output divs, but this isn't mandatory -
195
as will be explained in the B<Advanced> usage.
196

  
197
Now create a CGI object and a CGI::Ajax object, associating a reference
198
to our subroutine with the name we want available to javascript.
199

  
200
  my $cgi = new CGI();
201
  my $pjx = new CGI::Ajax( 'evenodd' => \&evenodd_func );
202

  
203
And if we used a coderef, it would look like this...
204

  
205
  my $pjx = new CGI::Ajax( 'evenodd' => $evenodd_func );
206

  
207
Now we're ready to print the output page; we send in the cgi
208
object and the HTML-generating function.
209

  
210
  print $pjx->build_html($cgi,\&Show_HTML);
211

  
212
CGI::Ajax has support for passing in extra HTML header information
213
to the CGI object.  This can be accomplished by adding a third
214
argument to the build_html() call.  The argument needs to be a
215
hashref containing Key=>value pairs that CGI objects understand:
216

  
217
  print $pjx->build_html($cgi,\&Show_HTML,
218
    {-charset=>'UTF-8, -expires=>'-1d'});
219

  
220
See L<CGI> for more header() method options.
221

  
222
That's it for the CGI::Ajax standard method.  Let's look at
223
something more advanced.
224

  
225
=item 2 Advanced CGI::Ajax example
226

  
227
Let's say we wanted to have a perl subroutine process multiple
228
values from the HTML page, and similarly return multiple values
229
back to distinct divs on the page.  This is easy to do, and
230
requires no changes to the perl code - you just create it as you
231
would any perl subroutine that works with multiple input values
232
and returns multiple values.  The significant change happens in
233
the event handler javascript in the HTML...
234

  
235
  onClick="exported_func(['input1','input2'],['result1','result2']);"
236

  
237
Here we associate our javascript function ("exported_func") with
238
two HTML element ids ('input1','input2'), and also send in two
239
HTML element ids to place the results in ('result1','result2').
240

  
241
=item 3 Sending Perl Subroutine Output to a Javascript function
242

  
243
Occassionally, you might want to have a custom javascript function
244
process the returned information from your Perl subroutine.
245
This is possible, and the only requierment is that you change
246
your event handler code...
247

  
248
  onClick="exported_func(['input1'],[js_process_func]);"
249

  
250
In this scenario, C<js_process_func> is a javascript function you
251
write to take the returned value from your Perl subroutine and
252
process the results.  I<Note that a javascript function is not
253
quoted -- if it were, then CGI::Ajax would look for a HTML element
254
with that id.>  Beware that with this usage, B<you are responsible
255
for distributing the results to the appropriate place on the
256
HTML page>.  If the exported Perl subroutine returns, e.g. 2
257
values, then C<js_process_func> would need to process the input
258
by working through an array, or using the javascript Function
259
C<arguments> object.
260

  
261
  function js_process_func() {
262
    var input1 = arguments[0]
263
    var input2 = arguments[1];
264
    // do something and return results, or set HTML divs using
265
    // innerHTML
266
    document.getElementById('outputdiv').innerHTML = input1;
267
  }
268

  
269
=item 4 URL/Outside Script CGI::Ajax example
270

  
271
There are times when you may want a different script to
272
return content to your page.  This could be because you have
273
an existing script already written to perform a particular
274
task, or you want to distribute a part of your application to another
275
script.  This can be accomplished in L<CGI::Ajax> by using a URL in
276
place of a locally-defined Perl subroutine.  In this usage,
277
you alter you creation of the L<CGI::Ajax> object to link an
278
exported javascript function name to a local URL instead of
279
a coderef or a subroutine.
280

  
281
  my $url = 'scripts/other_script.pl';
282
  my $pjx = new CGI::Ajax( 'external' => $url );
283

  
284
This will work as before in terms of how it is called from you
285
event handler:
286

  
287
  onClick="external(['input1','input2'],['resultdiv']);"
288

  
289
The other_script.pl will get the values via a CGI object and
290
accessing the 'args' key.  The values of the B<'args'> key will
291
be an array of everything that was sent into the script.
292

  
293
  my @input = $cgi->params('args');
294
  $input[0]; # contains first argument
295
  $input[1]; # contains second argument, etc...
296

  
297
This is good, but what if you need to send in arguments to the
298
other script which are directly from the calling Perl script,
299
i.e. you want a calling Perl script's variable to be sent, not
300
the value from an HTML element on the page?  This is possible
301
using the following syntax:
302

  
303
  onClick="exported_func(['args__$input1','args__$input2'],
304
                         ['resultdiv']);"
305

  
306
Similary, if the external script required a constant as input
307
(e.g.  C<script.pl?args=42>, you would use this syntax:
308

  
309
  onClick="exported_func(['args__42'],['resultdiv']);"
310

  
311
In both of the above examples, the result from the external
312
script would get placed into the I<resultdiv> element on our
313
(the calling script's) page.
314

  
315
If you are sending more than one argument from an external perl
316
script back to a javascript function, you will need to split the
317
string (AJAX applications communicate in strings only) on something.
318
Internally, we use '__pjx__', and this string is checked for.  If
319
found, L<CGI::Ajax> will automatically split it.  However, if you
320
don't want to use '__pjx__', you can do it yourself:
321

  
322
For example, from your Perl script, you would...
323

  
324
	return("A|B"); # join with "|"
325

  
326
and then in the javascript function you would have something like...
327

  
328
	process_func() {
329
		var arr = arguments[0].split("|");
330
		// arr[0] eq 'A'
331
		// arr[1] eq 'B'
332
	}
333

  
334
In order to rename parameters, in case the outside script needs
335
specifically-named parameters and not CGI::Ajax' I<'args'> default
336
parameter name, change your event handler associated with an HTML
337
event like this
338

  
339
  onClick="exported_func(['myname__$input1','myparam__$input2'],
340
                         ['resultdiv']);"
341

  
342
The URL generated would look like this...
343

  
344
C<script.pl?myname=input1&myparam=input2>
345

  
346
You would then retrieve the input in the outside script with this...
347

  
348
  my $p1 = $cgi->params('myname');
349
  my $p1 = $cgi->params('myparam');
350

  
351
Finally, what if we need to get a value from our HTML page and we
352
want to send that value to an outside script but the outside script
353
requires a named parameter different from I<'args'>?  You can
354
accomplish this with L<CGI::Ajax> using the getVal() javascript
355
method (which returns an array, thus the C<getVal()[0]> notation):
356

  
357
  onClick="exported_func(['myparam__' + getVal('div_id')[0]],
358
                         ['resultdiv']);"
359

  
360
This will get the value of our HTML element with and
361
I<id> of I<div_id>, and submit it to the url attached to
362
I<myparam__>.  So if our exported handler referred to a URI
363
called I<script/scr.pl>, and the element on our HTML page called
364
I<div_id> contained the number '42', then the URL would look
365
like this C<script/scr.pl?myparam=42>.  The result from this
366
outside URL would get placed back into our HTML page in the
367
element I<resultdiv>.  See the example script that comes with
368
the distribution called I<pjx_url.pl> and its associated outside
369
script I<convert_degrees.pl> for a working example.
370

  
371
B<N.B.> These examples show the use of outside scripts which
372
are other perl scripts - I<but you are not limited to Perl>!
373
The outside script could just as easily have been PHP or any other
374
CGI script, as long as the return from the other script is just
375
the result, and not addition HTML code (like FORM elements, etc).
376

  
377
=back
378

  
379
=head2 GET versus POST
380

  
381
Note that all the examples so far have used the following syntax:
382

  
383
  onClick="exported_func(['input1'],['result1']);"
384

  
385
There is an optional third argument to a L<CGI::Ajax> exported
386
function that allows change the submit method.  The above event could
387
also have been coded like this...
388

  
389
  onClick="exported_func(['input1'],['result1'], 'GET');"
390

  
391
By default, L<CGI::Ajax> sends a I<'GET'> request.  If you need it,
392
for example your URL is getting way too long, you can easily switch
393
to a I<'POST'> request with this syntax...
394

  
395
  onClick="exported_func(['input1'],['result1'], 'POST');"
396

  
397
I<('POST' and 'post' are supported)>
398

  
399
=head2 Page Caching
400

  
401
We have implemented a method to prevent page cacheing from undermining
402
the AJAX methods in a page.  If you send in an input argument to a
403
L<CGI::Ajax>-exported function called 'NO_CACHE', the a special
404
parameter will get attached to the end or your url with a random
405
number in it.  This will prevent a browser from caching your request.
406

  
407
  onClick="exported_func(['input1','NO_CACHE'],['result1']);"
408

  
409
The extra param is called pjxrand, and won't interfere with the order
410
of processing for the rest of your parameters.
411

  
412
=head1 METHODS
413

  
414
=cut
415

  
416
################################### main pod documentation end ##
417

  
418
######################################################
419
## METHODS - public                                 ##
420
######################################################
421

  
422
=over 4
423

  
424
=item build_html()
425

  
426
    Purpose: Associates a cgi obj ($cgi) with pjx object, inserts
427
             javascript into <HEAD></HEAD> element and constructs
428
             the page, or part of the page.  AJAX applications
429
             are designed to update only the section of the
430
             page that needs it - the whole page doesn't have
431
             to be redrawn.  L<CGI::Ajax> applications use the
432
             build_html() method to take care of this: if the CGI
433
             parameter C<fname> exists, then the return from the
434
             L<CGI::Ajax>-exported function is sent to the page.
435
             Otherwise, the entire page is sent, since without
436
             an C<fname> param, this has to be the first time
437
             the page is being built.
438

  
439
  Arguments: The CGI object, and either a coderef, or a string
440
             containing html.  Optionally, you can send in a third
441
             parameter containing information that will get passed
442
             directly to the CGI object header() call.
443
    Returns: html or updated html (including the header)
444
  Called By: originating cgi script
445

  
446
=cut
447
sub build_html {
448
  my ( $self, $cgi, $html_source, $cgi_header_extra ) = @_;
449

  
450
  if ( ref( $cgi ) =~ /CGI.*/ ) {
451
    if ( $self->DEBUG() ) {
452
      print STDERR "CGI::Ajax->build_html: CGI* object was received\n";
453
    }
454
    $self->cgi( $cgi ); # associate the cgi obj with the CGI::Ajax object
455
  }
456

  
457
  if ( defined $cgi_header_extra ) {
458
    if ( $self->DEBUG() ) {
459
      print STDERR "CGI::Ajax->build_html: got extra cgi header info\n";
460
      if ( ref($cgi_header_extra) eq "HASH" ) {
461
        foreach my $k ( keys %$cgi_header_extra ) {
462
          print STDERR "\t$k => ", $cgi_header_extra->{$k}, "\n";
463
        }
464
      } else {
465
        print STDERR "\t$cgi_header_extra\n";
466
      }
467
    }
468
    $self->cgi_header_extra( $cgi_header_extra ); 
469
  }
470

  
471
  #check if "fname" was defined in the CGI object
472
  if ( defined $self->cgi()->param("fname") ) {
473
    # it was, so just return the html from the handled request
474
    return ( $self->handle_request() );
475
  }
476
  else {
477
    # start with the minimum, a http header line and any extra cgi
478
    # header params sent in
479
    my $html = "";
480
    if ( $self->cgi()->can('header') ) {
481
      #$html .= $self->cgi()->header();
482
      $html .= $self->cgi()->header( $self->cgi_header_extra() );
483
    }
484
    else {
485
      # don't have an object with a "header()" method, so just create
486
      # a mimimal one
487
      $html .= "Content-Type: text/html;";
488
      $html .= $self->cgi_header_extra();
489
      $html .= "\n\n";
490
    }
491

  
492
    # check if the user sent in a coderef for generating the html,
493
    # or the actual html
494
    if ( ref($html_source) eq "CODE" ) {
495
      if ( $self->DEBUG() ) {
496
        print STDERR "CGI::Ajax->build_html: html_source is a CODEREF\n";
497
      }
498
      eval { $html .= &$html_source };
499
      if ($@) {
500
        # there was a problem evaluating the html-generating function
501
        # that was sent in, so generate an error page
502
        if ( $self->cgi()->can('header') ) {
503
          $html = $self->cgi()->header( $self->cgi_header_extra() );
504
        }
505
        else {
506
          # don't have an object with a "header()" method, so just create
507
          # a mimimal one
508
          $html = "Content-Type: text/html;";
509
          $html .= $self->cgi_header_extra();
510
          $html .= "\n\n";
511
        }
512
        $html .= qq!<html><head><title></title></head><body><h2>Problems</h2> with
513
          the html-generating function sent to CGI::Ajax
514
          object</body></html>!;
515
        return $html;
516
      }
517
      $self->html($html);    # no problems, so set html
518
    }
519
    else {
520
      # user must have sent in raw html, so add it
521
      if ( $self->DEBUG() ) {
522
        print STDERR "CGI::Ajax->build_html: html_source is HTML\n";
523
      }
524
      $self->html( $html . $html_source );
525
    }
526

  
527
    # now modify the html to insert the javascript
528
    $self->insert_js_in_head();
529
  }
530
  return $self->html();
531
}
532

  
533
=item show_javascript()
534

  
535
    Purpose: builds the text of all the javascript that needs to be
536
             inserted into the calling scripts html <head> section
537
  Arguments:
538
    Returns: javascript text
539
  Called By: originating web script
540
       Note: This method is also overridden so when you just print
541
             a CGI::Ajax object it will output all the javascript needed
542
             for the web page.
543

  
544
=cut
545

  
546
sub show_javascript {
547
  my ($self) = @_;
548
  my $rv = $self->show_common_js();    # show the common js
549

  
550
  # build the js for each perl function you want exported to js
551
  foreach my $func ( keys %{ $self->coderef_list() }, keys %{ $self->url_list() } ) {
552
    $rv .= $self->make_function($func);
553
  }
554
  # wrap up the return in a CDATA structure for XML compatibility
555
  # (thanks Thos Davis)
556
  $rv = "\n" . '//<![CDATA[' . "\n" . $rv . "\n" . '//]]>' . "\n";
557
  $rv = '<script type="text/javascript">' . $rv . '</script>';
558
  return $rv;
559
}
560

  
561
## new
562
sub new {
563
  my ($class) = shift;
564
  my $self = bless ({}, ref ($class) || $class);
565
#  $self->SUPER::new();
566
  $self->JSDEBUG(0); # turn javascript debugging off (if on,
567
                     # extra info will be added to the web page output
568
                     # if set to 1, then the core js will get
569
                     # compressed, but the user-defined functions will
570
                     # not be compressed.  If set to 2 (or anything
571
                     # greater than 1 or 0), then none of the
572
                     # javascript will get compressed.
573
                     #
574
  $self->DEBUG(0);   # turn debugging off (if on, check web logs)
575

  
576
  #accessorized attributes
577
  $self->coderef_list({});
578
  $self->url_list({});
579
  #$self->html("");
580
  #$self->cgi();
581
  #$self->cgi_header_extra(""); # set cgi_header_extra to an empty string
582

  
583
  # setup a default endcoding; if you need support for international
584
	# charsets, use 'escape' instead of encodeURIComponent.  Due to the
585
	# number of browser problems users report about scripts with a default of
586
	# encodeURIComponent, we are setting the default to 'escape'
587
  $self->js_encode_function('escape');
588

  
589
  if ( @_ < 2 ) {
590
    die "incorrect usage: must have fn=>code pairs in new\n";
591
  }
592

  
593
  while ( @_ ) {
594
    my($function_name,$code) = splice( @_, 0, 2 );
595
    if ( ref( $code ) eq "CODE" ) {
596
      if ( $self->DEBUG() ) {
597
        print STDERR "name = $function_name, code = $code\n";
598
      }
599
      # add the name/code to hash
600
      $self->coderef_list()->{ $function_name } = $code;
601
    } elsif ( ref($code) ) {
602
      die "Unsuported code block/url\n";
603
    } else {
604
      if ( $self->DEBUG() ) {
605
        print STDERR "Setting function $function_name to url $code\n";
606
      }
607
      # if it's a url, it is added here
608
      $self->url_list()->{ $function_name } = $code;
609
    }
610
  }
611
  return ($self);
612
}
613

  
614
######################################################
615
## METHODS - private                                ##
616
######################################################
617

  
618
# sub cgiobj(), cgi()
619
#
620
#    Purpose: accessor method to associate a CGI object with our
621
#             CGI::Ajax object
622
#  Arguments: a CGI object
623
#    Returns: CGI::Ajax objects cgi object
624
#  Called By: originating cgi script, or build_html()
625
#
626
sub cgiobj {
627
  my $self = shift;
628
  # see if any values were sent in...
629
  if ( @_ ) {
630
    my $cgi = shift;
631
    # add support for other CGI::* modules This requires that your web server
632
    # be configured properly.  I can't test anything but a mod_perl2
633
    # setup, so this prevents me from testing CGI::Lite,CGI::Simple, etc.
634
    if ( ref($cgi) =~ /CGI.*/ ) {
635
      if ( $self->DEBUG() ) {
636
				print STDERR "cgiobj() received a CGI-like object ($cgi)\n";
637
      }
638
      $self->{'cgi'} = $cgi;
639
    } else {
640
      die "CGI::Ajax -- Can't set internal CGI object to a non-CGI object ($cgi)\n";
641
    }
642
  }
643
  # return the object
644
  return( $self->{'cgi'} );
645
}
646

  
647
sub cgi {
648
  my $self = shift;
649
  if ( @_ ) {
650
    return( $self->cgiobj( @_ ) );
651
  } else {
652
    return( $self->cgiobj() );
653
  }
654
}
655

  
656
## # sub cgi_header_extra
657
## #
658
## #    Purpose: accessor method to associate CGI header information
659
## #             with the CGI::Ajax object
660
## #  Arguments: a hashref with key=>value pairs that get handed off to
661
## #             the CGI object's header() method
662
## #    Returns: hashref of extra cgi header params
663
## #  Called By: originating cgi script, or build_html()
664
## 
665
## sub cgi_header_extra {
666
##   my $self = shift;
667
##   if ( @_ ) {
668
##     $self->{'cgi_header_extra'} = shift;
669
##   }
670
##   return( $self->{'cgi_header_extra'} );
671
## }
672

  
673
# sub create_js_setRequestHeader
674
#
675
#    Purpose: create text of the header for the javascript side,
676
#             xmlhttprequest call
677
#  Arguments: none
678
#    Returns: text of header to pass to xmlhttpreq call so it will
679
#             match whatever was setup for the main web-page
680
#  Called By: originating cgi script, or build_html()
681
#
682

  
683
sub create_js_setRequestHeader {
684
  my $self = shift;
685
  my $cgi_header_extra = $self->cgi_header_extra();
686
  my $js_header_string = q{r.setRequestHeader("};
687
	#$js_header_string .= $self->cgi()->header( $cgi_header_extra );
688
	$js_header_string .= $self->cgi()->header();
689
  $js_header_string .= q{");};
690
	#if ( ref $cgi_header_extra eq "HASH" ) {
691
	#	foreach my $k ( keys(%$cgi_header_extra) ) {
692
	#		$js_header_string .= $self->cgi()->header($cgi_headers) 
693
	#	}
694
	#} else {
695
  #print STDERR  $self->cgi()->header($cgi_headers) ;
696
  
697
	if ( $self->DEBUG() ) {
698
		print STDERR "js_header_string is (", $js_header_string, ")\n";
699
	}
700

  
701
  return($js_header_string);
702
}
703

  
704
# sub show_common_js()
705
#
706
#    Purpose: create text of the javascript needed to interface with
707
#             the perl functions
708
#  Arguments: none
709
#    Returns: text of common javascript subroutine, 'do_http_request'
710
#  Called By: originating cgi script, or build_html()
711
#
712

  
713
sub show_common_js {
714
  my $self = shift;
715
  my $encodefn = $self->js_encode_function();
716
  my $decodefn = $encodefn;
717
  $decodefn =~ s/^(en)/de/;
718
  $decodefn =~ s/^(esc)/unesc/;
719
  #my $request_header_str = $self->create_js_setRequestHeader();
720
  my $request_header_str = "";
721
  my $rv = <<EOT;
722
var ajax = [];
723
function pjx(args,fname,method) {
724
  this.target=args[1];
725
  this.args=args[0];
726
  method=(method)?method:'GET';
727
  if(method=='post'){method='POST';}
728
  this.method = method;
729
  this.r=ghr();
730
  this.url = this.getURL(fname);
731
}
732

  
733
function formDump(){
734
  var all = [];
735
  var fL = document.forms.length;
736
  for(var f = 0;f<fL;f++){
737
    var els = document.forms[f].elements;
738
    for(var e in els){
739
      var tmp = (els[e].id != undefined)? els[e].id : els[e].name;
740
      if(typeof tmp != 'string'){continue;}
741
      if(tmp){ all[all.length]=tmp}
742
    }
743
  }
744
  return all;
745
}
746
function getVal(id) {
747
  if (id.constructor == Function ) { return id(); }
748
  if (typeof(id)!= 'string') { return id; }
749
  var element = document.getElementById(id) || document.forms[0].elements[id];
750
  if(!element){
751
     alert('ERROR: Cant find HTML element with id or name: ' +
752
     id+'. Check that an element with name or id='+id+' exists');
753
     return 0;
754
  }
755
   if(element.type == 'select-one') { 
756
      if(element.selectedIndex == -1) return;
757
      var item = element[element.selectedIndex]; 
758
      return  item.value || item.text
759
   } 
760
  if (element.type == 'select-multiple') {
761
  var ans = [];
762
  var k =0;
763
    for (var i=0;i<element.length;i++) {
764
      if (element[i].selected || element[i].checked ) {
765
        ans[k++]= element[i].value || element[i].text;
766
      }
767
    }
768
    return ans;
769
  }
770
    
771
  if(element.type == 'radio' || element.type == 'checkbox'){
772
    var ans =[];
773
    var elms = document.getElementsByTagName('input');
774
    var endk = elms.length;
775
    var i =0;
776
    for(var k=0;k<endk;k++){
777
      if(elms[k].type== element.type && elms[k].checked && elms[k].id==id){
778
        ans[i++]=elms[k].value;
779
      }
780
    }
781
    return ans;
782
  }
783
  if( element.value == undefined ){
784
    return element.innerHTML;
785
  }else{
786
    return element.value;
787
  }
788
}
789
function fnsplit(arg) {
790
  var url="";
791
  if(arg=='NO_CACHE'){return '&pjxrand='+Math.random()}
792
  if((typeof(arg)).toLowerCase() == 'object'){
793
      for(var k in arg){
794
         url += '&' + k + '=' + arg[k];
795
      }
796
  }else if (arg.indexOf('__') != -1) {
797
    arga = arg.split(/__/);
798
    url += '&' + arga[0] +'='+ $encodefn(arga[1]);
799
  } else {
800
    var res = getVal(arg) || '';
801
    if(res.constructor != Array){ res = [res] }
802
    for(var i=0;i<res.length;i++) {
803
      url += '&args=' + $encodefn(res[i]) + '&' + arg + '=' + $encodefn(res[i]);
804
    }
805
  }
806
  return url;
807
}
808

  
809
pjx.prototype =  {
810
  send2perl : function(){
811
    var r = this.r;
812
    var dt = this.target;
813
    this.pjxInitialized(dt);
814
    var url=this.url;
815
    var postdata;
816
    if(this.method=="POST"){
817
      var idx=url.indexOf('?');
818
      postdata = url.substr(idx+1);
819
      url = url.substr(0,idx);
820
    }
821
    r.open(this.method,url,true);
822
    $request_header_str;
823
    if(this.method=="POST"){
824
      r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
825
      r.send(postdata);
826
    }
827
    if(this.method=="GET"){
828
      r.send(null);
829
    }
830
    r.onreadystatechange = handleReturn;
831
 },
832
 pjxInitialized : function(){},
833
 pjxCompleted : function(){},
834
 readyState4 : function(){
835
    var rsp = $decodefn(this.r.responseText);  /* the response from perl */
836
    var splitval = '__pjx__';  /* to split text */
837
    var data = rsp.split(splitval);  
838
    dt = this.target;
839
    if (dt.constructor != Array) { dt=[dt]; }
840
    if (data.constructor != Array) { data=[data]; }
841
    if (typeof(dt[0])=='function') {
842
       dt[0].apply(this,data);
843
    } else {
844
      for ( var i=0; i<dt.length; i++ ) {
845
        if (typeof(dt[i])=='function') {
846
          dt[i].apply(this,[data[i]]);
847
        } else {
848
          var div = document.getElementById(dt[i]);
849
          if (div.type =='text' || div.type=='textarea' || div.type=='hidden' ) {
850
            div.value=data[i];
851
          } else{
852
            div.innerHTML = data[i];
853
          }
854
        }
855
      }
856
    }
857
    this.pjxCompleted(dt);
858
 },
859

  
860
  getURL : function(fname) {
861
      var args = this.args;
862
      var url= 'fname=' + fname;
863
      for (var i=0;i<args.length;i++) {
864
        url=url + args[i];
865
      }
866
      return url;
867
  }
868
};
869

  
870
handleReturn = function() {
871
  for( var k=0; k<ajax.length; k++ ) {
872
    if (ajax[k].r==null) { ajax.splice(k--,1); continue; }
873
    if ( ajax[k].r.readyState== 4) { 
874
      ajax[k].readyState4();
875
      ajax.splice(k--,1);
876
      continue;
877
    }
878
  }
879
};
880

  
881
var ghr=getghr();
882
function getghr(){
883
    if(typeof XMLHttpRequest != "undefined")
884
    {
885
        return function(){return new XMLHttpRequest();}
886
    }
887
    var msv= ["Msxml2.XMLHTTP.7.0", "Msxml2.XMLHTTP.6.0",
888
    "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0",
889
    "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
890
    for(var j=0;j<=msv.length;j++){
891
        try
892
        {
893
            A = new ActiveXObject(msv[j]);
894
            if(A){ 
895
              return function(){return new ActiveXObject(msv[j]);}
896
            }
897
        }
898
        catch(e) { }
899
     }
900
     return false;
901
}
902

  
903

  
904
function jsdebug(){
905
    var tmp = document.getElementById('pjxdebugrequest').innerHTML = "<br><pre>";
906
    for( var i=0; i < ajax.length; i++ ) {
907
      tmp += '<a href= '+ ajax[i].url +' target=_blank>' +
908
      decodeURI(ajax[i].url) + ' </a><br>';
909
    }
910
    document.getElementById('pjxdebugrequest').innerHTML = tmp + "</pre>";
911
}
912

  
913
EOT
914

  
915
  if ( $self->JSDEBUG() <= 1 ) {
916
    $rv = $self->compress_js($rv);
917
  }
918

  
919
  return($rv);
920
}
921

  
922
# sub compress_js()
923
#
924
#    Purpose: searches the javascript for newlines and spaces and
925
#             removes them (if a newline) or shrinks them to a single (if
926
#             space).
927
#  Arguments: javascript to compress
928
#    Returns: compressed js string
929
#  Called By: show_common_js(),
930
#
931

  
932
sub compress_js {
933
  my($self,$js) = @_;
934
  return if not defined $js;
935
  return if $js eq "";
936
  $js =~ s/\n//g;   # drop newlines
937
  $js =~ s/\s+/ /g; # replace 1+ spaces with just one space
938
  return $js;
939
}
940

  
941

  
942
# sub insert_js_in_head()
943
#
944
#    Purpose: searches the html value in the CGI::Ajax object and inserts
945
#             the ajax javascript code in the <script></script> section,
946
#             or if no such section exists, then it creates it.  If
947
#             JSDEBUG is set, then an extra div will be added and the
948
#             url wil be desplayed as a link
949
#  Arguments: none
950
#    Returns: none
951
#  Called By: build_html()
952
#
953

  
954
sub insert_js_in_head{
955
  my $self = shift;
956
  my $mhtml = $self->html();
957
  my $newhtml;
958
  my @shtml;
959
  my $js = $self->show_javascript();
960

  
961
  if ( $self->JSDEBUG() ) {
962
    my $showurl=qq!<br/><div id='pjxdebugrequest'></div><br/>!;
963
    # find the terminal </body> so we can insert just before it
964
    my @splith = $mhtml =~ /(.*)(<\s*\/\s*body[^>]*>?)(.*)/is;
965
    $mhtml = $splith[0].$showurl.$splith[1].$splith[2];
966
  }
967

  
968
  # see if we can match on <head>
969
  @shtml= $mhtml =~ /(.*)(<\s*head[^>]*>?)(.*)/is;
970
  if ( @shtml ) {
971
    # yes, there's already a <head></head>, so let's insert inside it,
972
    # at the beginning
973
    $newhtml = $shtml[0].$shtml[1].$js.$shtml[2];
974
  } elsif( @shtml= $mhtml =~ /(.*)(<\s*html[^>]*>?)(.*)/is){
975
    # there's no <head>, so look for the <html> tag, and insert out
976
    # javascript inside that tag
977
    $newhtml = $shtml[0].$shtml[1].$js.$shtml[2];
978
  } else {
979
    $newhtml .= "<html><head>";
980
    $newhtml .= $js;
981
    $newhtml .= "</head><body>";
982
    $newhtml .= "No head/html tags, nowhere to insert.  Returning javascript anyway<br>";
983
    $newhtml .= "</body></html>";
984
  }
985
  $self->html($newhtml);
986
  return;
987
}
988

  
989
# sub handle_request()
990
#
991
#    Purpose: makes sure a fname function name was set in the CGI
992
#             object, and then tries to eval the function with
993
#             parameters sent in on args
994
#  Arguments: none
995
#    Returns: the result of the perl subroutine, as text; if multiple
996
#             arguments are sent back from the defined, exported perl
997
#             method, then join then with a connector (__pjx__).
998
#  Called By: build_html()
999
#
1000

  
1001
sub handle_request {
1002
  my ($self) = shift;
1003

  
1004
  my $result; # $result takes the output of the function, if it's an
1005
              # array split on __pjx__
1006
  my @other = (); # array for catching extra parameters
1007

  
1008
  # we need to access "fname" in the form from the web page, so make
1009
  # sure there is a CGI object defined
1010
  return undef unless defined $self->cgi();
1011

  
1012
  my $rv = "";
1013
  if ( $self->cgi()->can('header') ) {
1014
    $rv = $self->cgi()->header( $self->cgi_header_extra() );
1015
  } else {
1016
    # don't have an object with a "header()" method, so just create
1017
    # a mimimal one
1018
    $rv = "Content-Type: text/html;";
1019
    # TODO: 
1020
    $rv .= $self->cgi_header_extra();
1021
    $rv .= "\n\n";
1022
  }
1023

  
1024
  # get the name of the function
1025
  my $func_name = $self->cgi()->param("fname");
1026

  
1027
  # check if the function name was created
1028
  if ( defined $self->coderef_list()->{$func_name} ) {
1029
    my $code = $self->coderef_list()->{$func_name};
1030

  
1031
    # eval the code from the coderef, and append the output to $rv
1032
    if ( ref($code) eq "CODE" ) {
1033
      eval { ($result, @other) = $code->( $self->cgi()->param("args") ) };
1034

  
1035
      if ($@) {
1036
        # see if the eval caused and error and report it
1037
        # Should we be more severe and die?
1038
        if ( $self->DEBUG() ) {
1039
          print STDERR "Problem with code: $@\n";
1040
        }
1041
      }
1042

  
1043
      if( @other ) {
1044
          $rv .= join( "__pjx__", ($result, @other) );
1045
          if ( $self->DEBUG() ) {
1046
            print STDERR "rv = $rv\n";
1047
          }
1048
      } else {
1049
        if ( defined $result ) {
1050
          $rv .= $result;
1051
        }
1052
      }
1053

  
1054
    } # end if ref = CODE
1055
  } else {
1056
    # # problems with the URL, return a CGI rrror
1057
    print STDERR "POSSIBLE SECURITY INCIDENT! Browser from ", $self->cgi()->remote_addr();
1058
    print STDERR "\trequested URL: ", $self->cgi()->url();
1059
    print STDERR "\tfname request: ", $self->cgi()->param('fname');
1060
    print STDERR " -- returning Bad Request status 400\n";
1061
    if ( $self->cgi()->can('header') ) {
1062
      return($self->cgi()->header( -status=>'400' ));
1063
    } else {
1064
      # don't have an object with a "header()" method, so just create
1065
      # a mimimal one with 400 error
1066
      $rv = "Status: 400\nContent-Type: text/html;\n\n";
1067
    }
1068
  }
1069
  return $rv;
1070
}
1071

  
1072

  
1073
# sub make_function()
1074
#
1075
#    Purpose: creates the javascript wrapper for the underlying perl
1076
#             subroutine
1077
#  Arguments: CGI object from web form, and the name of the perl
1078
#             function to export to javascript, or a url if the
1079
#             function name refers to another cgi script
1080
#    Returns: text of the javascript-wrapped perl subroutine
1081
#  Called By: show_javascript; called once for each registered perl
1082
#             subroutine
1083
#
1084

  
1085
sub make_function {
1086
  my ($self, $func_name ) = @_;
1087
  return("") if not defined $func_name;
1088
  return("") if $func_name eq "";
1089
  my $rv = "";
1090
  my $script = $0 || $ENV{SCRIPT_FILENAME};
1091
  $script =~ s/.*[\/|\\](.+)$/$1/;
1092
  my $outside_url = $self->url_list()->{ $func_name };
1093
  my $url = defined $outside_url ? $outside_url : $script;
1094
  if ($url =~ /\?/) { $url.='&'; } else {$url.='?'}
1095
  $url = "'$url'";
1096
  my $jsdebug = "";
1097
  if ( $self->JSDEBUG()) {
1098
    $jsdebug = "jsdebug()";
1099
  }
1100

  
1101
  #create the javascript text
1102
  $rv .= <<EOT;
1103
function $func_name() {
1104
  var args = $func_name.arguments;
1105
  for( var i=0; i<args[0].length;i++ ) {
1106
    args[0][i] = fnsplit(args[0][i]);
1107
  }
1108
  var l = ajax.length;
1109
  ajax[l]= new pjx(args,"$func_name",args[2]);
1110
  ajax[l].url = $url + ajax[l].url;
1111
  ajax[l].send2perl();
1112
  $jsdebug;
1113
}
1114
EOT
1115

  
1116
  if ( not $self->JSDEBUG() ) {
1117
    $rv = $self->compress_js($rv);
1118
  }
1119
  return $rv;
1120
}
1121

  
1122
=item register()
1123

  
1124
    Purpose: adds a function name and a code ref to the global coderef
1125
             hash, after the original object was created
1126
  Arguments: function name, code reference
1127
    Returns: none
1128
  Called By: originating web script
1129

  
1130
=cut
1131

  
1132
sub register {
1133
  my ( $self, $fn, $coderef ) = @_;
1134
  # coderef_list() is a Class::Accessor function
1135
  # url_list() is a Class::Accessor function
1136
  if ( ref( $coderef ) eq "CODE" ) {
1137
    $self->coderef_list()->{$fn} = $coderef;
1138
  } elsif ( ref($coderef) ) {
1139
    die "Unsupported code/url type - error\n";
1140
  } else {
1141
    $self->url_list()->{$fn} = $coderef;
1142
  }
1143
}
1144

  
1145
=item JSDEBUG()
1146

  
1147
    Purpose: Show the AJAX URL that is being generated, and stop
1148
             compression of the generated javascript, both of which can aid
1149
             during debugging.  If set to 1, then the core js will get
1150
             compressed, but the user-defined functions will not be
1151
             compressed.  If set to 2 (or anything greater than 1 or 0), 
1152
             then none of the javascript will get compressed.
1153

  
1154
  Arguments: JSDEBUG(0); # turn javascript debugging off
1155
             JSDEBUG(1); # turn javascript debugging on, some javascript compression
1156
             JSDEBUG(2); # turn javascript debugging on, no javascript compresstion
1157
    Returns: prints a link to the url that is being generated automatically by
1158
             the Ajax object. this is VERY useful for seeing what
1159
             CGI::Ajax is doing. Following the link, will show a page
1160
             with the output that the page is generating.
1161
             
1162
  Called By: $pjx->JSDEBUG(1) # where $pjx is a CGI::Ajax object;
1163

  
1164
=item DEBUG()
1165

  
1166
    Purpose: Show debugging information in web server logs
1167
  Arguments: DEBUG(0); # turn debugging off (default)
1168
             DEBUG(1); # turn debugging on
1169
    Returns: prints debugging information to the web server logs using
1170
             STDERR
1171
  Called By: $pjx->DEBUG(1) # where $pjx is a CGI::Ajax object;
1172

  
1173
=back
1174

  
1175
=head1 BUGS
1176

  
1177
Follow any bugs at our homepage....
1178

  
1179
  http://www.perljax.us
1180

  
1181
=head1 SUPPORT
1182

  
1183
Check out the news/discussion/bugs lists at our homepage:
1184

  
1185
  http://www.perljax.us
1186

  
1187
=head1 AUTHORS
1188

  
1189
  Brian C. Thomas     Brent Pedersen
1190
  CPAN ID: BCT
1191
  bct.x42@gmail.com   bpederse@gmail.com
1192

  
1193
=head1 A NOTE ABOUT THE MODULE NAME
1194

  
1195
This module was initiated using the name "Perljax", but then
1196
registered with CPAN under the WWW group "CGI::", and so became
1197
"CGI::Perljax".  Upon further deliberation, we decided to change it's
1198
name to L<CGI::Ajax>.
1199

  
1200
=head1 COPYRIGHT
1201

  
1202
This program is free software; you can redistribute
1203
it and/or modify it under the same terms as Perl itself.
1204

  
1205
The full text of the license can be found in the
1206
LICENSE file included with this module.
1207

  
1208
=head1 SEE ALSO
1209

  
1210
L<Data::Javascript>
1211
L<CGI>
1212
L<Class::Accessor>
1213

  
1214
=cut
1215

  
1216
1;
1217
__END__

Auch abrufbar als: Unified diff