| blib/lib/CGI/Ajax.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 35 | 231 | 15.1 |
| branch | 5 | 110 | 4.5 |
| condition | 1 | 33 | 3.0 |
| subroutine | 7 | 22 | 31.8 |
| pod | 4 | 16 | 25.0 |
| total | 52 | 412 | 12.6 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package CGI::Ajax; | ||||||
| 2 | 1 | 1 | 44284 | use strict; | |||
| 1 | 3 | ||||||
| 1 | 39 | ||||||
| 3 | 1 | 1 | 1690 | use Data::Dumper; | |||
| 1 | 20118 | ||||||
| 1 | 87 | ||||||
| 4 | 1 | 1 | 12 | use base qw(Class::Accessor); | |||
| 1 | 3 | ||||||
| 1 | 1351 | ||||||
| 5 | 1 | 1 | 3479 | use overload '""' => 'show_javascript'; # for building web pages, so | |||
| 1 | 2 | ||||||
| 1 | 9 | ||||||
| 6 | # you can just say: print $pjx | ||||||
| 7 | |||||||
| 8 | BEGIN { | ||||||
| 9 | 1 | 1 | 75 | use vars qw ($VERSION @ISA @METHODS); | |||
| 1 | 1 | ||||||
| 1 | 96 | ||||||
| 10 | 1 | 1 | 4 | @METHODS = qw(url_list coderef_list CACHE DEBUG JSDEBUG html | |||
| 11 | js_encode_function cgi_header_extra skip_header fname); | ||||||
| 12 | |||||||
| 13 | 1 | 7 | CGI::Ajax->mk_accessors(@METHODS); | ||||
| 14 | |||||||
| 15 | 1 | 3544 | $VERSION = .707; | ||||
| 16 | } | ||||||
| 17 | |||||||
| 18 | ########################################### main pod documentation begin ## | ||||||
| 19 | |||||||
| 20 | =head1 NAME | ||||||
| 21 | |||||||
| 22 | CGI::Ajax - a perl-specific system for writing Asynchronous web | ||||||
| 23 | applications | ||||||
| 24 | |||||||
| 25 | =head1 SYNOPSIS | ||||||
| 26 | |||||||
| 27 | use strict; | ||||||
| 28 | use CGI; # or any other CGI:: form handler/decoder | ||||||
| 29 | use CGI::Ajax; | ||||||
| 30 | |||||||
| 31 | my $cgi = new CGI; | ||||||
| 32 | my $pjx = new CGI::Ajax( 'exported_func' => \&perl_func ); | ||||||
| 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 = < | ||||||
| 44 | |||||||
| 45 | |||||||
| 46 | Enter something: | ||||||
| 47 | |||||||
| 48 | onkeyup="exported_func( ['val1'], ['resultdiv'] );"> | ||||||
| 49 | |
||||||
| 50 | |||||||
| 51 | |||||||
| 52 | |||||||
| 53 | EOHTML | ||||||
| 54 | return $html; | ||||||
| 55 | } | ||||||
| 56 | |||||||
| 57 | When you use CGI::Ajax within Applications that send their own header information, | ||||||
| 58 | you can skip the header: | ||||||
| 59 | |||||||
| 60 | my $pjx = new CGI::Ajax( | ||||||
| 61 | 'exported_func' => \&perl_func, | ||||||
| 62 | 'skip_header' => 1, | ||||||
| 63 | ); | ||||||
| 64 | $pjx->skip_header(1); | ||||||
| 65 | |||||||
| 66 | print $pjx->build_html( $cgi, \&Show_HTML); | ||||||
| 67 | |||||||
| 68 | I | ||||||
| 69 | directory of the distribution.> | ||||||
| 70 | |||||||
| 71 | =head1 DESCRIPTION | ||||||
| 72 | |||||||
| 73 | CGI::Ajax is an object-oriented module that provides a unique | ||||||
| 74 | mechanism for using perl code asynchronously from javascript- | ||||||
| 75 | enhanced HTML pages. CGI::Ajax unburdens the user from having to | ||||||
| 76 | write extensive javascript, except for associating an exported | ||||||
| 77 | method with a document-defined event (such as onClick, onKeyUp, | ||||||
| 78 | etc). CGI::Ajax also mixes well with HTML containing more complex | ||||||
| 79 | javascript. | ||||||
| 80 | |||||||
| 81 | CGI::Ajax supports methods that return single results or multiple | ||||||
| 82 | results to the web page, and supports returning values to multiple | ||||||
| 83 | DIV elements on the HTML page. | ||||||
| 84 | |||||||
| 85 | Using CGI::Ajax, the URL for the HTTP GET/POST request is | ||||||
| 86 | automatically generated based on HTML layout and events, and the | ||||||
| 87 | page is then dynamically updated with the output from the perl | ||||||
| 88 | function. Additionally, CGI::Ajax supports mapping URL's to a | ||||||
| 89 | CGI::Ajax function name, so you can separate your code processing | ||||||
| 90 | over multiple scripts. | ||||||
| 91 | |||||||
| 92 | Other than using the Class::Accessor module to generate CGI::Ajax' | ||||||
| 93 | accessor methods, CGI::Ajax is completely self-contained - it | ||||||
| 94 | does not require you to install a larger package or a full Content | ||||||
| 95 | Management System, etc. | ||||||
| 96 | |||||||
| 97 | We have added I |
||||||
| 98 | like L |
||||||
| 99 | since we run mod_perl2 only here. CGI::Ajax checks to see if a | ||||||
| 100 | header() method is available to the CGI object, and then uses it. | ||||||
| 101 | If method() isn't available, it creates it's own minimal header. | ||||||
| 102 | |||||||
| 103 | A primary goal of CGI::Ajax is to keep the module streamlined and | ||||||
| 104 | maximally flexible. We are trying to keep the generated javascript | ||||||
| 105 | code to a minimum, but still provide users with a variety of | ||||||
| 106 | methods for deploying CGI::Ajax. And VERY little user javascript. | ||||||
| 107 | |||||||
| 108 | =head1 EXAMPLES | ||||||
| 109 | |||||||
| 110 | The CGI::Ajax module allows a Perl subroutine to be called | ||||||
| 111 | asynchronously, when triggered from a javascript event on the | ||||||
| 112 | HTML page. To do this, the subroutine must be I |
||||||
| 113 | usually done during: | ||||||
| 114 | |||||||
| 115 | my $pjx = new CGI::Ajax( 'JSFUNC' => \&PERLFUNC ); | ||||||
| 116 | |||||||
| 117 | This maps a perl subroutine (PERLFUNC) to an automatically | ||||||
| 118 | generated Javascript function (JSFUNC). Next you setup a trigger this | ||||||
| 119 | function when an event occurs (e.g. "onClick"): | ||||||
| 120 | |||||||
| 121 | onClick="JSFUNC(['source1','source2'], ['dest1','dest2']);" | ||||||
| 122 | |||||||
| 123 | where 'source1', 'dest1', 'source2', 'dest2' are the DIV ids of | ||||||
| 124 | HTML elements in your page... | ||||||
| 125 | |||||||
| 126 | |||||||
| 127 | |||||||
| 128 | |||||||
| 129 | |||||||
| 130 | |||||||
| 131 | L |
||||||
| 132 | Perl subroutine and returns the results to dest1 and dest2. | ||||||
| 133 | |||||||
| 134 | =head2 4 Usage Methods | ||||||
| 135 | |||||||
| 136 | =over 4 | ||||||
| 137 | |||||||
| 138 | =item 1 Standard CGI::Ajax example | ||||||
| 139 | |||||||
| 140 | Start by defining a perl subroutine that you want available from | ||||||
| 141 | javascript. In this case we'll define a subrouting that determines | ||||||
| 142 | whether or not an input is odd, even, or not a number (NaN): | ||||||
| 143 | |||||||
| 144 | use strict; | ||||||
| 145 | use CGI::Ajax; | ||||||
| 146 | use CGI; | ||||||
| 147 | |||||||
| 148 | |||||||
| 149 | sub evenodd_func { | ||||||
| 150 | my $input = shift; | ||||||
| 151 | |||||||
| 152 | # see if input is defined | ||||||
| 153 | if ( not defined $input ) { | ||||||
| 154 | return("input not defined or NaN"); | ||||||
| 155 | } | ||||||
| 156 | |||||||
| 157 | # see if value is a number (*thanks Randall!*) | ||||||
| 158 | if ( $input !~ /\A\d+\z/ ) { | ||||||
| 159 | return("input is NaN"); | ||||||
| 160 | } | ||||||
| 161 | |||||||
| 162 | # got a number, so mod by 2 | ||||||
| 163 | $input % 2 == 0 ? return("EVEN") : return("ODD"); | ||||||
| 164 | } | ||||||
| 165 | |||||||
| 166 | Alternatively, we could have used coderefs to associate an | ||||||
| 167 | exported name... | ||||||
| 168 | |||||||
| 169 | my $evenodd_func = sub { | ||||||
| 170 | # exactly the same as in the above subroutine | ||||||
| 171 | }; | ||||||
| 172 | |||||||
| 173 | Next we define a function to generate the web page - this can | ||||||
| 174 | be done many different ways, and can also be defined as an | ||||||
| 175 | anonymous sub. The only requirement is that the sub send back | ||||||
| 176 | the html of the page. You can do this via a string containing the | ||||||
| 177 | html, or from a coderef that returns the html, or from a function | ||||||
| 178 | (as shown here)... | ||||||
| 179 | |||||||
| 180 | sub Show_HTML { | ||||||
| 181 | my $html = < | ||||||
| 182 | |||||||
| 183 | |
||||||
| 184 | |||||||
| 185 | |||||||
| 186 | Enter a number: | ||||||
| 187 | |||||||
| 188 | OnKeyUp="evenodd( ['val1'], ['resultdiv'] );"> | ||||||
| 189 | |
||||||
| 190 | |
||||||
| 191 | |
||||||
| 192 | |||||||
| 193 | |||||||
| 194 | |||||||
| 195 | EOT | ||||||
| 196 | return $html; | ||||||
| 197 | } | ||||||
| 198 | |||||||
| 199 | The exported Perl subrouting is triggered using the C |
||||||
| 200 | event handler of the input HTML element. The subroutine takes one | ||||||
| 201 | value from the form, the input element B<'val1'>, and returns the | ||||||
| 202 | the result to an HTML div element with an id of B<'resultdiv'>. | ||||||
| 203 | Sending in the input id in an array format is required to support | ||||||
| 204 | multiple inputs, and similarly, to output multiple the results, | ||||||
| 205 | you can use an array for the output divs, but this isn't mandatory - | ||||||
| 206 | as will be explained in the B |
||||||
| 207 | |||||||
| 208 | Now create a CGI object and a CGI::Ajax object, associating a reference | ||||||
| 209 | to our subroutine with the name we want available to javascript. | ||||||
| 210 | |||||||
| 211 | my $cgi = new CGI(); | ||||||
| 212 | my $pjx = new CGI::Ajax( 'evenodd' => \&evenodd_func ); | ||||||
| 213 | |||||||
| 214 | And if we used a coderef, it would look like this... | ||||||
| 215 | |||||||
| 216 | my $pjx = new CGI::Ajax( 'evenodd' => $evenodd_func ); | ||||||
| 217 | |||||||
| 218 | Now we're ready to print the output page; we send in the cgi | ||||||
| 219 | object and the HTML-generating function. | ||||||
| 220 | |||||||
| 221 | print $pjx->build_html($cgi,\&Show_HTML); | ||||||
| 222 | |||||||
| 223 | CGI::Ajax has support for passing in extra HTML header information | ||||||
| 224 | to the CGI object. This can be accomplished by adding a third | ||||||
| 225 | argument to the build_html() call. The argument needs to be a | ||||||
| 226 | hashref containing Key=>value pairs that CGI objects understand: | ||||||
| 227 | |||||||
| 228 | print $pjx->build_html($cgi,\&Show_HTML, | ||||||
| 229 | {-charset=>'UTF-8, -expires=>'-1d'}); | ||||||
| 230 | |||||||
| 231 | See L |
||||||
| 232 | Perl6 CGI) | ||||||
| 233 | |||||||
| 234 | That's it for the CGI::Ajax standard method. Let's look at | ||||||
| 235 | something more advanced. | ||||||
| 236 | |||||||
| 237 | =item 2 Advanced CGI::Ajax example | ||||||
| 238 | |||||||
| 239 | Let's say we wanted to have a perl subroutine process multiple | ||||||
| 240 | values from the HTML page, and similarly return multiple values | ||||||
| 241 | back to distinct divs on the page. This is easy to do, and | ||||||
| 242 | requires no changes to the perl code - you just create it as you | ||||||
| 243 | would any perl subroutine that works with multiple input values | ||||||
| 244 | and returns multiple values. The significant change happens in | ||||||
| 245 | the event handler javascript in the HTML... | ||||||
| 246 | |||||||
| 247 | onClick="exported_func(['input1','input2'],['result1','result2']);" | ||||||
| 248 | |||||||
| 249 | Here we associate our javascript function ("exported_func") with | ||||||
| 250 | two HTML element ids ('input1','input2'), and also send in two | ||||||
| 251 | HTML element ids to place the results in ('result1','result2'). | ||||||
| 252 | |||||||
| 253 | =item 3 Sending Perl Subroutine Output to a Javascript function | ||||||
| 254 | |||||||
| 255 | Occassionally, you might want to have a custom javascript function | ||||||
| 256 | process the returned information from your Perl subroutine. | ||||||
| 257 | This is possible, and the only requierment is that you change | ||||||
| 258 | your event handler code... | ||||||
| 259 | |||||||
| 260 | onClick="exported_func(['input1'],[js_process_func]);" | ||||||
| 261 | |||||||
| 262 | In this scenario, C |
||||||
| 263 | write to take the returned value from your Perl subroutine and | ||||||
| 264 | process the results. I | ||||||
| 265 | quoted -- if it were, then CGI::Ajax would look for a HTML element | ||||||
| 266 | with that id.> Beware that with this usage, B | ||||||
| 267 | for distributing the results to the appropriate place on the | ||||||
| 268 | HTML page>. If the exported Perl subroutine returns, e.g. 2 | ||||||
| 269 | values, then C |
||||||
| 270 | by working through an array, or using the javascript Function | ||||||
| 271 | C |
||||||
| 272 | |||||||
| 273 | function js_process_func() { | ||||||
| 274 | var input1 = arguments[0] | ||||||
| 275 | var input2 = arguments[1]; | ||||||
| 276 | // do something and return results, or set HTML divs using | ||||||
| 277 | // innerHTML | ||||||
| 278 | document.getElementById('outputdiv').innerHTML = input1; | ||||||
| 279 | } | ||||||
| 280 | |||||||
| 281 | =item 4 URL/Outside Script CGI::Ajax example | ||||||
| 282 | |||||||
| 283 | There are times when you may want a different script to | ||||||
| 284 | return content to your page. This could be because you have | ||||||
| 285 | an existing script already written to perform a particular | ||||||
| 286 | task, or you want to distribute a part of your application to another | ||||||
| 287 | script. This can be accomplished in L |
||||||
| 288 | place of a locally-defined Perl subroutine. In this usage, | ||||||
| 289 | you alter you creation of the L |
||||||
| 290 | exported javascript function name to a local URL instead of | ||||||
| 291 | a coderef or a subroutine. | ||||||
| 292 | |||||||
| 293 | my $url = 'scripts/other_script.pl'; | ||||||
| 294 | my $pjx = new CGI::Ajax( 'external' => $url ); | ||||||
| 295 | |||||||
| 296 | This will work as before in terms of how it is called from you | ||||||
| 297 | event handler: | ||||||
| 298 | |||||||
| 299 | onClick="external(['input1','input2'],['resultdiv']);" | ||||||
| 300 | |||||||
| 301 | The other_script.pl will get the values via a CGI object and | ||||||
| 302 | accessing the 'args' key. The values of the B<'args'> key will | ||||||
| 303 | be an array of everything that was sent into the script. | ||||||
| 304 | |||||||
| 305 | my @input = $cgi->params('args'); | ||||||
| 306 | $input[0]; # contains first argument | ||||||
| 307 | $input[1]; # contains second argument, etc... | ||||||
| 308 | |||||||
| 309 | This is good, but what if you need to send in arguments to the | ||||||
| 310 | other script which are directly from the calling Perl script, | ||||||
| 311 | i.e. you want a calling Perl script's variable to be sent, not | ||||||
| 312 | the value from an HTML element on the page? This is possible | ||||||
| 313 | using the following syntax: | ||||||
| 314 | |||||||
| 315 | onClick="exported_func(['args__$input1','args__$input2'], | ||||||
| 316 | ['resultdiv']);" | ||||||
| 317 | |||||||
| 318 | Similary, if the external script required a constant as input | ||||||
| 319 | (e.g. C |
||||||
| 320 | |||||||
| 321 | onClick="exported_func(['args__42'],['resultdiv']);" | ||||||
| 322 | |||||||
| 323 | In both of the above examples, the result from the external | ||||||
| 324 | script would get placed into the I |
||||||
| 325 | (the calling script's) page. | ||||||
| 326 | |||||||
| 327 | If you are sending more than one argument from an external perl | ||||||
| 328 | script back to a javascript function, you will need to split the | ||||||
| 329 | string (AJAX applications communicate in strings only) on something. | ||||||
| 330 | Internally, we use '__pjx__', and this string is checked for. If | ||||||
| 331 | found, L |
||||||
| 332 | don't want to use '__pjx__', you can do it yourself: | ||||||
| 333 | |||||||
| 334 | For example, from your Perl script, you would... | ||||||
| 335 | |||||||
| 336 | return("A|B"); # join with "|" | ||||||
| 337 | |||||||
| 338 | and then in the javascript function you would have something like... | ||||||
| 339 | |||||||
| 340 | process_func() { | ||||||
| 341 | var arr = arguments[0].split("|"); | ||||||
| 342 | // arr[0] eq 'A' | ||||||
| 343 | // arr[1] eq 'B' | ||||||
| 344 | } | ||||||
| 345 | |||||||
| 346 | In order to rename parameters, in case the outside script needs | ||||||
| 347 | specifically-named parameters and not CGI::Ajax' I<'args'> default | ||||||
| 348 | parameter name, change your event handler associated with an HTML | ||||||
| 349 | event like this | ||||||
| 350 | |||||||
| 351 | onClick="exported_func(['myname__$input1','myparam__$input2'], | ||||||
| 352 | ['resultdiv']);" | ||||||
| 353 | |||||||
| 354 | The URL generated would look like this... | ||||||
| 355 | |||||||
| 356 | C |
||||||
| 357 | |||||||
| 358 | You would then retrieve the input in the outside script with this... | ||||||
| 359 | |||||||
| 360 | my $p1 = $cgi->params('myname'); | ||||||
| 361 | my $p1 = $cgi->params('myparam'); | ||||||
| 362 | |||||||
| 363 | Finally, what if we need to get a value from our HTML page and we | ||||||
| 364 | want to send that value to an outside script but the outside script | ||||||
| 365 | requires a named parameter different from I<'args'>? You can | ||||||
| 366 | accomplish this with L |
||||||
| 367 | method (which returns an array, thus the C |
||||||
| 368 | |||||||
| 369 | onClick="exported_func(['myparam__' + getVal('div_id')[0]], | ||||||
| 370 | ['resultdiv']);" | ||||||
| 371 | |||||||
| 372 | This will get the value of our HTML element with and | ||||||
| 373 | I |
||||||
| 374 | I |
||||||
| 375 | called I'; | ||||||
| 624 | 0 | 0 | return $rv; | ||||
| 625 | } | ||||||
| 626 | |||||||
| 627 | ## new | ||||||
| 628 | sub new { | ||||||
| 629 | 1 | 1 | 1 | 14 | my ($class) = shift; | ||
| 630 | 1 | 33 | 11 | my $self = bless( {}, ref($class) || $class ); | |||
| 631 | |||||||
| 632 | # $self->SUPER::new(); | ||||||
| 633 | 1 | 8 | $self->fname("fname");# default parameter for exported function name | ||||
| 634 | 1 | 96 | $self->JSDEBUG(0); # turn javascript debugging off (if on, | ||||
| 635 | # extra info will be added to the web page output | ||||||
| 636 | # if set to 1, then the core js will get | ||||||
| 637 | # compressed, but the user-defined functions will | ||||||
| 638 | # not be compressed. If set to 2 (or anything | ||||||
| 639 | # greater than 1 or 0), then none of the | ||||||
| 640 | # javascript will get compressed. | ||||||
| 641 | # | ||||||
| 642 | 1 | 12 | $self->DEBUG(0); # turn debugging off (if on, check web logs) | ||||
| 643 | 1 | 12 | $self->CACHE(1); # default behavior is to allow cache of content | ||||
| 644 | # which can be explicitly switched off by passing | ||||||
| 645 | # NO_CACHE in the arg list | ||||||
| 646 | |||||||
| 647 | #accessorized attributes | ||||||
| 648 | 1 | 12 | $self->coderef_list( {} ); | ||||
| 649 | 1 | 12 | $self->url_list( {} ); | ||||
| 650 | |||||||
| 651 | #$self->html(""); | ||||||
| 652 | #$self->cgi(); | ||||||
| 653 | #$self->cgi_header_extra(""); # set cgi_header_extra to an empty string | ||||||
| 654 | |||||||
| 655 | # setup a default endcoding; if you need support for international | ||||||
| 656 | # charsets, use 'escape' instead of encodeURIComponent. Due to the | ||||||
| 657 | # number of browser problems users report about scripts with a default of | ||||||
| 658 | # encodeURIComponent, we are setting the default to 'escape' | ||||||
| 659 | 1 | 13 | $self->js_encode_function('escape'); | ||||
| 660 | |||||||
| 661 | 1 | 50 | 14 | if ( @_ < 2 ) { | |||
| 662 | 0 | 0 | die "incorrect usage: must have fn=>code pairs in new\n"; | ||||
| 663 | |||||||
| 664 | } | ||||||
| 665 | |||||||
| 666 | 1 | 11 | while (@_) { | ||||
| 667 | 1 | 4 | my ( $function_name, $code ) = splice( @_, 0, 2 ); | ||||
| 668 | |||||||
| 669 | 1 | 50 | 5 | if( $function_name eq 'skip_header' ){ | |||
| 670 | 0 | 0 | $self->skip_header( $code ); | ||||
| 671 | 0 | 0 | next; | ||||
| 672 | } | ||||||
| 673 | |||||||
| 674 | 1 | 50 | 18 | if ( ref($code) eq "CODE" ) { | |||
| 50 | |||||||
| 675 | 0 | 0 | 0 | if ( $self->DEBUG() ) { | |||
| 676 | 0 | 0 | print STDERR "name = $function_name, code = $code\n"; | ||||
| 677 | } | ||||||
| 678 | |||||||
| 679 | # add the name/code to hash | ||||||
| 680 | 0 | 0 | $self->coderef_list()->{$function_name} = $code; | ||||
| 681 | } | ||||||
| 682 | elsif ( ref($code) ) { | ||||||
| 683 | 0 | 0 | die "Unsuported code block/url\n"; | ||||
| 684 | } | ||||||
| 685 | else { | ||||||
| 686 | 1 | 50 | 4 | if ( $self->DEBUG() ) { | |||
| 687 | 0 | 0 | print STDERR "Setting function $function_name to url $code\n"; | ||||
| 688 | } | ||||||
| 689 | |||||||
| 690 | # if it's a url, it is added here | ||||||
| 691 | 1 | 21 | $self->url_list()->{$function_name} = $code; | ||||
| 692 | } | ||||||
| 693 | } | ||||||
| 694 | 1 | 12 | return ($self); | ||||
| 695 | } | ||||||
| 696 | |||||||
| 697 | ###################################################### | ||||||
| 698 | ## METHODS - private ## | ||||||
| 699 | ###################################################### | ||||||
| 700 | |||||||
| 701 | # sub cgiobj(), cgi() | ||||||
| 702 | # | ||||||
| 703 | # Purpose: accessor method to associate a CGI object with our | ||||||
| 704 | # CGI::Ajax object | ||||||
| 705 | # Arguments: a CGI object | ||||||
| 706 | # Returns: CGI::Ajax objects cgi object | ||||||
| 707 | # Called By: originating cgi script, or build_html() | ||||||
| 708 | # | ||||||
| 709 | sub cgiobj { | ||||||
| 710 | 0 | 0 | 0 | my $self = shift; | |||
| 711 | |||||||
| 712 | # see if any values were sent in... | ||||||
| 713 | 0 | 0 | if (@_) { | ||||
| 714 | 0 | my $cgi = shift; | |||||
| 715 | |||||||
| 716 | # add support for other CGI::* modules This requires that your web server | ||||||
| 717 | # be configured properly. I can't test anything but a mod_perl2 | ||||||
| 718 | # setup, so this prevents me from testing CGI::Lite,CGI::Simple, etc. | ||||||
| 719 | 0 | 0 | 0 | if ( ref($cgi) =~ /CGI.*/ | |||
| 0 | |||||||
| 720 | or ( $cgi->isa('CGI::Application') && $cgi->query =~ /CGI/ ) ) | ||||||
| 721 | { #pmg | ||||||
| 722 | 0 | 0 | if ( $self->DEBUG() ) { | ||||
| 723 | 0 | print STDERR "cgiobj() received a CGI-like object ($cgi)\n"; | |||||
| 724 | } | ||||||
| 725 | 0 | $self->{'cgi'} = $cgi; | |||||
| 726 | } | ||||||
| 727 | else { | ||||||
| 728 | 0 | die | |||||
| 729 | "CGI::Ajax -- Can't set internal CGI object to a non-CGI object ($cgi)\n"; | ||||||
| 730 | } | ||||||
| 731 | } | ||||||
| 732 | |||||||
| 733 | # return the object | ||||||
| 734 | 0 | return ( $self->{'cgi'} ); | |||||
| 735 | } | ||||||
| 736 | |||||||
| 737 | sub cgi { | ||||||
| 738 | 0 | 0 | 0 | my $self = shift; | |||
| 739 | 0 | 0 | if (@_) { | ||||
| 740 | 0 | return ( $self->cgiobj(@_) ); | |||||
| 741 | } | ||||||
| 742 | else { | ||||||
| 743 | 0 | return ( $self->cgiobj() ); | |||||
| 744 | } | ||||||
| 745 | } | ||||||
| 746 | |||||||
| 747 | ## # sub cgi_header_extra | ||||||
| 748 | ## # | ||||||
| 749 | ## # Purpose: accessor method to associate CGI header information | ||||||
| 750 | ## # with the CGI::Ajax object | ||||||
| 751 | ## # Arguments: a hashref with key=>value pairs that get handed off to | ||||||
| 752 | ## # the CGI object's header() method | ||||||
| 753 | ## # Returns: hashref of extra cgi header params | ||||||
| 754 | ## # Called By: originating cgi script, or build_html() | ||||||
| 755 | ## | ||||||
| 756 | ## sub cgi_header_extra { | ||||||
| 757 | ## my $self = shift; | ||||||
| 758 | ## if ( @_ ) { | ||||||
| 759 | ## $self->{'cgi_header_extra'} = shift; | ||||||
| 760 | ## } | ||||||
| 761 | ## return( $self->{'cgi_header_extra'} ); | ||||||
| 762 | ## } | ||||||
| 763 | |||||||
| 764 | # sub create_js_setRequestHeader | ||||||
| 765 | # | ||||||
| 766 | # Purpose: create text of the header for the javascript side, | ||||||
| 767 | # xmlhttprequest call | ||||||
| 768 | # Arguments: none | ||||||
| 769 | # Returns: text of header to pass to xmlhttpreq call so it will | ||||||
| 770 | # match whatever was setup for the main web-page | ||||||
| 771 | # Called By: originating cgi script, or build_html() | ||||||
| 772 | # | ||||||
| 773 | |||||||
| 774 | sub create_js_setRequestHeader { | ||||||
| 775 | 0 | 0 | 0 | my $self = shift; | |||
| 776 | 0 | my $cgi_header_extra = $self->cgi_header_extra(); | |||||
| 777 | 0 | my $js_header_string = q{r.setRequestHeader("}; | |||||
| 778 | |||||||
| 779 | #$js_header_string .= $self->cgi()->header( $cgi_header_extra ); | ||||||
| 780 | 0 | $js_header_string .= $self->getHeader; | |||||
| 781 | 0 | $js_header_string .= q{");}; | |||||
| 782 | |||||||
| 783 | #if ( ref $cgi_header_extra eq "HASH" ) { | ||||||
| 784 | # foreach my $k ( keys(%$cgi_header_extra) ) { | ||||||
| 785 | # $js_header_string .= $self->cgi()->header($cgi_headers) | ||||||
| 786 | # } | ||||||
| 787 | #} else { | ||||||
| 788 | #print STDERR $self->cgi()->header($cgi_headers) ; | ||||||
| 789 | |||||||
| 790 | 0 | 0 | if ( $self->DEBUG() ) { | ||||
| 791 | 0 | print STDERR "js_header_string is (", $js_header_string, ")\n"; | |||||
| 792 | } | ||||||
| 793 | |||||||
| 794 | 0 | return ($js_header_string); | |||||
| 795 | } | ||||||
| 796 | |||||||
| 797 | # sub show_common_js() | ||||||
| 798 | # | ||||||
| 799 | # Purpose: create text of the javascript needed to interface with | ||||||
| 800 | # the perl functions | ||||||
| 801 | # Arguments: none | ||||||
| 802 | # Returns: text of common javascript subroutine, 'do_http_request' | ||||||
| 803 | # Called By: originating cgi script, or build_html() | ||||||
| 804 | # | ||||||
| 805 | |||||||
| 806 | sub show_common_js { | ||||||
| 807 | 0 | 0 | 0 | my $self = shift; | |||
| 808 | 0 | my $fname = $self->fname(); | |||||
| 809 | 0 | my $encodefn = $self->js_encode_function(); | |||||
| 810 | 0 | my $decodefn = $encodefn; | |||||
| 811 | 0 | $decodefn =~ s/^(en)/de/; | |||||
| 812 | 0 | $decodefn =~ s/^(esc)/unesc/; | |||||
| 813 | |||||||
| 814 | #my $request_header_str = $self->create_js_setRequestHeader(); | ||||||
| 815 | 0 | my $request_header_str = ""; | |||||
| 816 | 0 | my $rv = < | |||||
| 817 | var ajax = []; | ||||||
| 818 | var cache; | ||||||
| 819 | |||||||
| 820 | function pjx(args,fname,method) { | ||||||
| 821 | this.target=args[1]; | ||||||
| 822 | this.args=args[0]; | ||||||
| 823 | method=(method)?method:'GET'; | ||||||
| 824 | if(method=='post'){method='POST';} | ||||||
| 825 | this.method = method; | ||||||
| 826 | this.r=ghr(); | ||||||
| 827 | this.url = this.getURL(fname); | ||||||
| 828 | } | ||||||
| 829 | |||||||
| 830 | function formDump(){ | ||||||
| 831 | var all = []; | ||||||
| 832 | var fL = document.forms.length; | ||||||
| 833 | for(var f = 0;f | ||||||
| 834 | var els = document.forms[f].elements; | ||||||
| 835 | for(var e in els){ | ||||||
| 836 | var tmp = (els[e].id != undefined)? els[e].id : els[e].name; | ||||||
| 837 | if(typeof tmp != 'string'){continue;} | ||||||
| 838 | if(tmp){ all[all.length]=tmp} | ||||||
| 839 | } | ||||||
| 840 | } | ||||||
| 841 | return all; | ||||||
| 842 | } | ||||||
| 843 | function getVal(id) { | ||||||
| 844 | if (id.constructor == Function ) { return id(); } | ||||||
| 845 | if (typeof(id)!= 'string') { return id; } | ||||||
| 846 | |||||||
| 847 | var element = document.getElementById(id); | ||||||
| 848 | if( !element ) { | ||||||
| 849 | for( var i=0; i | ||||||
| 850 | element = document.forms[i].elements[id]; | ||||||
| 851 | if( element ) break; | ||||||
| 852 | } | ||||||
| 853 | if( element && !element.type ) element = element[0]; | ||||||
| 854 | } | ||||||
| 855 | if(!element){ | ||||||
| 856 | alert('ERROR: Cant find HTML element with id or name: ' + | ||||||
| 857 | id+'. Check that an element with name or id='+id+' exists'); | ||||||
| 858 | return 0; | ||||||
| 859 | } | ||||||
| 860 | |||||||
| 861 | if(element.type == 'select-one') { | ||||||
| 862 | if(element.selectedIndex == -1) return; | ||||||
| 863 | var item = element[element.selectedIndex]; | ||||||
| 864 | return item.value || item.text; | ||||||
| 865 | } | ||||||
| 866 | if(element.type == 'select-multiple') { | ||||||
| 867 | var ans = []; | ||||||
| 868 | var k =0; | ||||||
| 869 | for (var i=0;i | ||||||
| 870 | if (element[i].selected || element[i].checked ) { | ||||||
| 871 | ans[k++]= element[i].value || element[i].text; | ||||||
| 872 | } | ||||||
| 873 | } | ||||||
| 874 | return ans; | ||||||
| 875 | } | ||||||
| 876 | if(element.type == 'radio' || element.type == 'checkbox'){ | ||||||
| 877 | var ans =[]; | ||||||
| 878 | var elms = document.getElementsByTagName('input'); | ||||||
| 879 | var endk = elms.length ; | ||||||
| 880 | var i =0; | ||||||
| 881 | for(var k=0;k | ||||||
| 882 | if(elms[k].type== element.type && elms[k].checked && (elms[k].id==id||elms[k].name==id)){ | ||||||
| 883 | ans[i++]=elms[k].value; | ||||||
| 884 | } | ||||||
| 885 | } | ||||||
| 886 | return ans; | ||||||
| 887 | } | ||||||
| 888 | if( element.value == undefined ){ | ||||||
| 889 | return element.innerHTML; | ||||||
| 890 | }else{ | ||||||
| 891 | return element.value; | ||||||
| 892 | } | ||||||
| 893 | } | ||||||
| 894 | function fnsplit(arg) { | ||||||
| 895 | var url=""; | ||||||
| 896 | if(arg=='NO_CACHE'){cache = 0; return "";}; | ||||||
| 897 | if((typeof(arg)).toLowerCase() == 'object'){ | ||||||
| 898 | for(var k in arg){ | ||||||
| 899 | url += '&' + k + '=' + arg[k]; | ||||||
| 900 | } | ||||||
| 901 | }else if (arg.indexOf('__') != -1) { | ||||||
| 902 | arga = arg.split(/__/); | ||||||
| 903 | url += '&' + arga[0] +'='+ $encodefn(arga[1]); | ||||||
| 904 | } else { | ||||||
| 905 | var res = getVal(arg) || ''; | ||||||
| 906 | if(res.constructor != Array){ res = [res] } | ||||||
| 907 | else if( res.length == 0 ) { res = [ '' ] } | ||||||
| 908 | for(var i=0;i | ||||||
| 909 | url += '&args=' + $encodefn(res[i]) + '&' + arg + '=' + $encodefn(res[i]); | ||||||
| 910 | } | ||||||
| 911 | } | ||||||
| 912 | return url; | ||||||
| 913 | } | ||||||
| 914 | |||||||
| 915 | pjx.prototype = { | ||||||
| 916 | send2perl : function(){ | ||||||
| 917 | var r = this.r; | ||||||
| 918 | var dt = this.target; | ||||||
| 919 | if (dt==undefined) { return true; } | ||||||
| 920 | this.pjxInitialized(dt); | ||||||
| 921 | var url=this.url; | ||||||
| 922 | var postdata; | ||||||
| 923 | if(this.method=="POST"){ | ||||||
| 924 | var idx=url.indexOf('?'); | ||||||
| 925 | postdata = url.substr(idx+1); | ||||||
| 926 | url = url.substr(0,idx); | ||||||
| 927 | } | ||||||
| 928 | r.open(this.method,url,true); | ||||||
| 929 | $request_header_str; | ||||||
| 930 | if(this.method=="POST"){ | ||||||
| 931 | r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); | ||||||
| 932 | r.send(postdata); | ||||||
| 933 | } | ||||||
| 934 | if(this.method=="GET"){ | ||||||
| 935 | r.send(null); | ||||||
| 936 | } | ||||||
| 937 | r.onreadystatechange = handleReturn; | ||||||
| 938 | }, | ||||||
| 939 | pjxInitialized : function(){}, | ||||||
| 940 | pjxCompleted : function(){}, | ||||||
| 941 | readyState4 : function(){ | ||||||
| 942 | var rsp = $decodefn(this.r.responseText); /* the response from perl */ | ||||||
| 943 | var splitval = '__pjx__'; /* to split text */ | ||||||
| 944 | /* fix IE problems with undef values in an Array getting squashed*/ | ||||||
| 945 | rsp = rsp.replace(splitval+splitval+'g',splitval+" "+splitval); | ||||||
| 946 | var data = rsp.split(splitval); | ||||||
| 947 | dt = this.target; | ||||||
| 948 | if (dt.constructor != Array) { dt=[dt]; } | ||||||
| 949 | if (data.constructor != Array) { data=[data]; } | ||||||
| 950 | if (typeof(dt[0])!='function') { | ||||||
| 951 | for ( var i=0; i | ||||||
| 952 | var div = document.getElementById(dt[i]); | ||||||
| 953 | if (div.type =='text' || div.type=='textarea' || div.type=='hidden' ) { | ||||||
| 954 | div.value=data[i]; | ||||||
| 955 | } else if (div.type =='checkbox') { | ||||||
| 956 | div.checked=data[i]; | ||||||
| 957 | } else { | ||||||
| 958 | div.innerHTML = data[i]; | ||||||
| 959 | } | ||||||
| 960 | } | ||||||
| 961 | } else if (typeof(dt[0])=='function') { | ||||||
| 962 | dt[0].apply(this,data); | ||||||
| 963 | } | ||||||
| 964 | this.pjxCompleted(dt); | ||||||
| 965 | }, | ||||||
| 966 | |||||||
| 967 | getURL : function(fname) { | ||||||
| 968 | var args = this.args; | ||||||
| 969 | var url= '$fname=' + fname; | ||||||
| 970 | for (var i=0;i | ||||||
| 971 | url=url + args[i]; | ||||||
| 972 | } | ||||||
| 973 | return url; | ||||||
| 974 | } | ||||||
| 975 | }; | ||||||
| 976 | |||||||
| 977 | handleReturn = function() { | ||||||
| 978 | for( var k=0; k | ||||||
| 979 | if (ajax[k].r==null) { ajax.splice(k--,1); continue; } | ||||||
| 980 | if ( ajax[k].r.readyState== 4) { | ||||||
| 981 | ajax[k].readyState4(); | ||||||
| 982 | ajax.splice(k--,1); | ||||||
| 983 | continue; | ||||||
| 984 | } | ||||||
| 985 | } | ||||||
| 986 | }; | ||||||
| 987 | |||||||
| 988 | var ghr=getghr(); | ||||||
| 989 | function getghr(){ | ||||||
| 990 | if(typeof XMLHttpRequest != "undefined") | ||||||
| 991 | { | ||||||
| 992 | return function(){return new XMLHttpRequest();} | ||||||
| 993 | } | ||||||
| 994 | var msv= ["Msxml2.XMLHTTP.7.0", "Msxml2.XMLHTTP.6.0", | ||||||
| 995 | "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", | ||||||
| 996 | "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; | ||||||
| 997 | for(var j=0;j<=msv.length;j++){ | ||||||
| 998 | try | ||||||
| 999 | { | ||||||
| 1000 | A = new ActiveXObject(msv[j]); | ||||||
| 1001 | if(A){ | ||||||
| 1002 | return function(){return new ActiveXObject(msv[j]);} | ||||||
| 1003 | } | ||||||
| 1004 | } | ||||||
| 1005 | catch(e) { } | ||||||
| 1006 | } | ||||||
| 1007 | return false; | ||||||
| 1008 | } | ||||||
| 1009 | |||||||
| 1010 | |||||||
| 1011 | function jsdebug(){ | ||||||
| 1012 | var tmp = document.getElementById('pjxdebugrequest').innerHTML = ""; |
||||||
| 1013 | for( var i=0; i < ajax.length; i++ ) { | ||||||
| 1014 | tmp += '' + | ||||||
| 1015 | decodeURI(ajax[i].url) + ' <' + '/a> '; |
||||||
| 1016 | } | ||||||
| 1017 | document.getElementById('pjxdebugrequest').innerHTML = tmp + "<" + "/pre>"; | ||||||
| 1018 | } | ||||||
| 1019 | |||||||
| 1020 | EOT | ||||||
| 1021 | |||||||
| 1022 | 0 | 0 | if ( $self->JSDEBUG() <= 1 ) { | ||||
| 1023 | 0 | $rv = $self->compress_js($rv); | |||||
| 1024 | } | ||||||
| 1025 | |||||||
| 1026 | 0 | return ($rv); | |||||
| 1027 | } | ||||||
| 1028 | |||||||
| 1029 | # sub compress_js() | ||||||
| 1030 | # | ||||||
| 1031 | # Purpose: searches the javascript for newlines and spaces and | ||||||
| 1032 | # removes them (if a newline) or shrinks them to a single (if | ||||||
| 1033 | # space). | ||||||
| 1034 | # Arguments: javascript to compress | ||||||
| 1035 | # Returns: compressed js string | ||||||
| 1036 | # Called By: show_common_js(), | ||||||
| 1037 | # | ||||||
| 1038 | |||||||
| 1039 | sub compress_js { | ||||||
| 1040 | 0 | 0 | 0 | my ( $self, $js ) = @_; | |||
| 1041 | 0 | 0 | return if not defined $js; | ||||
| 1042 | 0 | 0 | return if $js eq ""; | ||||
| 1043 | 0 | $js =~ s/\n//g; # drop newlines | |||||
| 1044 | 0 | $js =~ s/\s+/ /g; # replace 1+ spaces with just one space | |||||
| 1045 | 0 | return $js; | |||||
| 1046 | } | ||||||
| 1047 | |||||||
| 1048 | # sub insert_js_in_head() | ||||||
| 1049 | # | ||||||
| 1050 | # Purpose: searches the html value in the CGI::Ajax object and inserts | ||||||
| 1051 | # the ajax javascript code in the section, | ||||||
| 1052 | # or if no such section exists, then it creates it. If | ||||||
| 1053 | # JSDEBUG is set, then an extra div will be added and the | ||||||
| 1054 | # url will be displayed as a link | ||||||
| 1055 | # Arguments: none | ||||||
| 1056 | # Returns: none | ||||||
| 1057 | # Called By: build_html() | ||||||
| 1058 | # | ||||||
| 1059 | |||||||
| 1060 | sub insert_js_in_head { | ||||||
| 1061 | 0 | 0 | 0 | my $self = shift; | |||
| 1062 | 0 | my $mhtml = $self->html(); | |||||
| 1063 | 0 | my $newhtml; | |||||
| 1064 | my @shtml; | ||||||
| 1065 | 0 | my $js = $self->show_javascript(); | |||||
| 1066 | |||||||
| 1067 | 0 | 0 | if ( $self->JSDEBUG() ) { | ||||
| 1068 | 0 | my $showurl = qq! !; |
|||||
| 1069 | |||||||
| 1070 | # find the terminal |
so we can insert just before it