Parent Directory
|
Revision Log
cosmetic changes some corrections on typos for setState subroutines.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: pg/lib/Applet.pm,v 1.5 2008/03/16 14:39:39 gage Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 =head1 NAME 18 19 Applet.pl - Provides code for inserting FlashApplets and JavaApplets into webwork problems 20 21 =head1 SYNPOSIS 22 23 ################################### 24 # Create link to applet 25 ################################### 26 my $appletName = "LineThruPointsWW"; 27 $applet = new FlashApplet( 28 # can be replaced by $applet =FlashApplet() when using AppletObjects.pl 29 codebase => findAppletCodebase("$appletName.swf"), 30 appletName => $appletName, 31 appletId => $appletName, 32 submitActionAlias => 'checkAnswer', 33 ); 34 35 ################################### 36 # Configure applet 37 ################################### 38 39 #xml data to set up the problem-rac 40 $applet->state(qq{<XML> 41 <point xval='$xval_1' yval='$yval_1' /> 42 <point xval='$xval_2' yval='$yval_2' /> 43 </XML>}); 44 45 46 ################################### 47 # insert applet header material 48 ################################### 49 HEADER_TEXT($applet->insertHeader ); 50 51 ################################### 52 # Text section 53 # 54 55 ################################### 56 #insert applet into body 57 ################################### 58 TEXT( MODES(TeX=>'object code', HTML=>$applet->insertObject)); 59 60 61 =head1 DESCRIPTION 62 63 This file provides an object to store in one place 64 all of the information needed to call an applet. 65 66 The object FlashApplet has defaults for inserting flash applets. 67 68 =over 69 70 =item * 71 72 =item * 73 74 =back 75 76 (not yet completed) 77 78 The module JavaApplet has defaults for inserting java applets. 79 80 The module Applet stores common code for the two types of applet. 81 82 =head1 USAGE 83 84 These modules are activate by listing it in the modules section of global.conf and rebooting the server. 85 The companion file to this one is macros/AppletObjects.pl 86 87 qw(Applet FlashApplet JavaApplet) 88 89 =cut 90 91 92 93 package Applet; 94 95 96 97 98 99 use MIME::Base64 qw( encode_base64 decode_base64); 100 101 102 =head2 Default javaScript functions placed in header 103 104 These functions are automatically defined for use for 105 any javaScript placed in the text of a PG question. 106 107 getApplet(appletName) -- finds the applet path in the DOM 108 109 submitAction() -- calls the submit action of the applets 110 111 112 initializeAction() -- calls the initialize action of the applets 113 114 getQE(name) -- gets an HTML element of the question by name 115 or by id. Be sure to keep all names and ids 116 unique within a given PG question. 117 118 getQuestionElement(name) -- long form of getQE(name) 119 120 listQuestionElements() -- for discovering the names of inputs in the 121 PG question. An alert dialog will list all 122 of the elements. 123 Usage: Place this at the END of the question, 124 just before END_DOCUMENT(): 125 126 TEXT(qq!<script> listQuestionElements() </script>!); 127 ENDDOCUMENT(); 128 129 list of accessor methods format: current_value = $self->method(new_value or empty) 130 131 appletId for simplicity and reliability appletId and appletName are always the same 132 appletName 133 134 archive the name of the .jar file containing the applet code 135 code the name of the applet code in the .jar archive 136 codebase a prefix url used to find the archive and the applet itself 137 138 height rectangle alloted in the html page for displaying the applet 139 width 140 141 params an anonymous array containing name/value pairs 142 to configure the applet [name =>'value, ...] 143 144 header stores the text to be added to the header section of the html page 145 object stores the text which places the applet on the html page 146 147 debug in debug mode several alerts mark progress through the procedure of calling the applet 148 149 config configuration are those customizable attributes of the applet which don't 150 change as it is used. When stored in hidden answer fields 151 it is usually stored in base64 encoded format. 152 base64_config base64 encode version of the contents of config 153 154 configAlias (default: config ) names the applet command called with the contents of $self->config 155 to configure the applet. The parameters are passed to the applet in plain text using <xml> 156 The outer tags must be <xml> ..... </xml> 157 state state consists of those customizable attributes of the applet which change 158 as the applet is used. It is stored by the calling .pg question so that 159 when revisiting the question the applet 160 will be restored to the same state it was left in when the question was last 161 viewed. 162 163 getStateAlias (default: getState) alias for command called to read the current state of the applet. 164 The state is passed in plain text xml format with outer tags: <xml>....</xml> 165 setStateAlias (default: setState) alias for the command called to reset the state of the applet. 166 The state is passed in plain text in xml format with outer tags: <xml>....</xml> 167 168 base64_state returns the base64 encoded version of the state stored in the applet object. 169 170 initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState 171 submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the 172 .pg question is pressed. 173 174 returnFieldName 175 176 177 178 179 180 =cut 181 182 183 184 185 sub new { 186 my $class = shift; 187 my $self = { 188 appletName =>'', 189 code=>'', 190 codebase=>'', 191 # appletId =>'', #always use identical applet Id's and applet Names 192 params =>undef, 193 width => 550, 194 height => 400, 195 base64_state => '', 196 base64_config => '', 197 getStateAlias => 'getXML', 198 setStateAlias => 'setState', 199 configAlias => 'config', 200 initializeActionAlias => 'setXML', 201 submitActionAlias => 'getXML', 202 returnFieldName => 'receivedField', 203 headerText => DEFAULT_HEADER_TEXT(), 204 objectText => '', 205 debug => 0, 206 @_, 207 }; 208 bless $self, $class; 209 return $self; 210 } 211 212 sub header { 213 my $self = shift; 214 if ($_[0] eq "reset") { # $applet->header('reset'); erases default header text. 215 $self->{headerText}=''; 216 } else { 217 $self->{headerText} .= join("",@_); # $applet->header(new_text); concatenates new_text to existing header. 218 } 219 $self->{headerText}; 220 } 221 sub object { 222 my $self = shift; 223 if ($_[0] eq "reset") { 224 $self->{objectText}=''; 225 } else { 226 $self->{objectText} .= join("",@_); 227 } 228 $self->{objectText}; 229 } 230 sub params { 231 my $self = shift; 232 if (ref($_[0]) =~/HASH/) { 233 $self->{params} = shift; 234 } elsif ( !defined($_[0]) or $_[0] =~ '') { 235 # do nothing (read) 236 } else { 237 warn "You must enter a reference to a hash for the parameter list"; 238 } 239 $self->{params}; 240 } 241 242 sub initializeActionAlias { 243 my $self = shift; 244 $self->{initializeActionAlias} = shift ||$self->{initializeActionAlias}; # replace the current contents if non-empty 245 $self->{initializeActionAlias}; 246 } 247 248 sub submitActionAlias { 249 my $self = shift; 250 $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty 251 $self->{submitActionAlias}; 252 } 253 sub getStateAlias { 254 my $self = shift; 255 $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty 256 $self->{getStateAlias}; 257 } 258 259 sub setStateAlias { 260 my $self = shift; 261 $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty 262 $self->{setStateAlias}; 263 } 264 sub configAlias { 265 my $self = shift; 266 $self->{configAlias} = shift ||$self->{configAlias}; # replace the current contents if non-empty 267 $self->{configAlias}; 268 } 269 sub returnFieldName { 270 my $self = shift; 271 $self->{returnFieldName} = shift ||$self->{returnFieldName}; # replace the current contents if non-empty 272 $self->{returnFieldName}; 273 } 274 sub codebase { 275 my $self = shift; 276 $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty 277 $self->{codebase}; 278 } 279 sub code { 280 my $self = shift; 281 $self->{code} = shift ||$self->{code}; # replace the current code if non-empty 282 $self->{code}; 283 } 284 sub height { 285 my $self = shift; 286 $self->{height} = shift ||$self->{height}; # replace the current height if non-empty 287 $self->{height}; 288 } 289 sub width { 290 my $self = shift; 291 $self->{width} = shift ||$self->{width}; # replace the current width if non-empty 292 $self->{width}; 293 } 294 sub archive { 295 my $self = shift; 296 $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty 297 $self->{archive}; 298 } 299 sub appletName { 300 my $self = shift; 301 $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty 302 $self->{appletName}; 303 } 304 sub debug { 305 my $self = shift; 306 my $new_flag = shift; 307 $self->{debug} = $new_flag if defined($new_flag); 308 $self->{debug}; 309 } 310 sub appletId { 311 appletName(@_); 312 } 313 sub state { 314 my $self = shift; 315 my $str = shift; 316 $self->{base64_state} = encode_base64($str) ||$self->{base64_state}; # replace the current string if non-empty 317 $self->{base64_state} =~ s/\n//g; 318 decode_base64($self->{base64_state}); 319 } 320 321 sub base64_state{ 322 my $self = shift; 323 $self->{base64_state} = shift ||$self->{base64_state}; # replace the current string if non-empty 324 $self->{base64_state}; 325 } 326 sub config { 327 my $self = shift; 328 my $str = shift; 329 $self->{base64_config} = encode_base64($str) || $self->{base64_config}; # replace the current string if non-empty 330 $self->{base64_config} =~ s/\n//g; 331 decode_base64($self->{base64_config}); 332 } 333 sub base64_config { 334 my $self = shift; 335 $self->{base64_config} = shift ||$self->{base64_config}; # replace the current string if non-empty 336 $self->{base64_config} =$self->{base64_config}; 337 $self->{base64_config}; 338 } 339 #FIXME 340 # need to be able to adjust header material 341 342 sub insertHeader { 343 my $self = shift; 344 my $codebase = $self->codebase; 345 my $appletId = $self->appletId; 346 my $appletName = $self->appletName; 347 my $base64_initialState = $self->base64_state; 348 my $initializeAction = $self->initializeActionAlias; 349 my $submitAction = $self->submitActionAlias; 350 my $setState = $self->setStateAlias; 351 my $getState = $self->getStateAlias; 352 my $config = $self->configAlias; 353 my $base64_config = $self->base64_config; 354 my $debugMode = ($self->debug) ? "1": "0"; 355 my $returnFieldName = $self->{returnFieldName}; 356 # my $encodeStateQ = ($self->debug)?'' : "state = Base64.encode(state);"; # in debug mode base64 encoding is not used. 357 # my $decodeStateQ = "if (!state.match(/<XML>*/i) ) {state = Base64.decode(state)}"; # decode if <XML> is not present 358 my $headerText = $self->header(); 359 360 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook 361 362 return $headerText; 363 364 365 } 366 367 sub insertObject { 368 my $self = shift; 369 my $code = $self->{code}; 370 my $codebase = $self->{codebase}; 371 my $appletId = $self->{appletName}; 372 my $appletName = $self->{appletName}; 373 my $archive = $self->{archive}; 374 my $width = $self->{width}; 375 my $height = $self->{height}; 376 my $parameters = ''; 377 my $parameters = ''; 378 my %param_hash = %{$self->params()}; 379 foreach my $key (keys %param_hash) { 380 $parameters .= qq!<param name ="$key" value = "$param_hash{$key}">\n! 381 } 382 383 $objectText = $self->{objectText}; 384 $objectText =~ s/(\$\w+)/$1/gee; 385 return $objectText; 386 } 387 sub initialize { 388 my $self = shift; 389 return q{ 390 <script> 391 initializeAction(); 392 // this should really be done in the <body> tag 393 </script> 394 }; 395 396 } 397 398 399 use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT'; 400 401 <script language="JavaScript"> 402 var debug = $debugMode; 403 // 404 //CONFIGURATIONS 405 // 406 // configurations are "permanent" 407 applet_config_list["$appletName"] = function() { 408 if (debug) { alert("configure $appletName . $config ( $base64_config )");} 409 try { 410 if (debug || !( typeof(getApplet("$appletName").$config) == "undefined" ) ) { 411 412 getApplet("$appletName").$config(Base64.decode("$base64_config")); 413 } 414 } catch(e) { 415 alert("error executing configuration command $config for $appletName: " + e ); 416 } 417 } 418 // 419 //STATE 420 // 421 // state can vary as the applet is manipulated. 422 applet_setState_list["$appletName"] = function(state) { 423 state = state || getQE("$appletName"+"_state").value 424 if (state.match("\S") ) { // if state is not all white space 425 if ( base64Q(state) ) { 426 state=Base64.decode(state); 427 } 428 if (debug) { alert("set state for $appletName to " + state);} 429 try { 430 if (debug || !( typeof(getApplet("$appletName").$setState) =="undefined" ) ) { 431 getApplet("$appletName").$setState( state ); 432 } 433 } catch(e) { 434 alert("Error in setting state of $appletName using command $setState : " + e ); 435 } 436 } 437 }; 438 applet_getState_list["$appletName"] = function () { 439 if (debug) { alert("getState for applet $appletName");} 440 try { 441 var applet = getApplet("$appletName"); 442 var state; 443 if (!( typeof(getApplet("$appletName").$getState) =="undefined" ) ) { 444 state = applet.$getState(); // get state in xml format 445 } 446 if (!debug) {state = Base64.encode(state) }; // replace state by encoded version 447 getQE("$appletName"+"_state").value = state; //place in state htmlItem (debug: textarea, otherwise hidden) 448 } catch (e) { 449 alert("Error in getting state for $appletName " + e ); 450 } 451 }; 452 // 453 //INITIALIZE 454 // 455 applet_initializeAction_list["$appletName"] = function () { 456 applet_setState_list["$appletName"](); 457 }; 458 459 applet_submitAction_list["$appletName"] = function () { 460 applet_getState_list["$appletName"](); 461 getQE("$returnFieldName").value = getApplet("$appletName").sendData(); 462 }; 463 </script> 464 465 END_HEADER_SCRIPT 466 467 package FlashApplet; 468 @ISA = qw(Applet); 469 470 471 472 =pod 473 474 The secret to making this applet work with IE in addition to normal browsers 475 is the addition of the C(<form></form>) construct just before the object. 476 477 For some reason IE has trouble locating a flash object which is contained 478 within a form. Adding this second blank form with the larger problemMainForm 479 seems to solve the problem. 480 481 This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2) 482 Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated: 483 http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js 484 http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933 485 486 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; 487 <form></form> 488 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 489 id="$appletName" width="500" height="375" 490 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> 491 <param name="movie" value="$codebase/$appletName.swf" /> 492 <param name="quality" value="high" /> 493 <param name="bgcolor" value="#869ca7" /> 494 <param name="allowScriptAccess" value="sameDomain" /> 495 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" 496 width="$width" height="$height" name="$appletName" align="middle" id="$appletName" 497 play="true" loop="false" quality="high" allowScriptAccess="sameDomain" 498 type="application/x-shockwave-flash" 499 pluginspage="http://www.macromedia.com/go/getflashplayer"> 500 </embed> 501 502 </object> 503 END_OBJECT_TEXT 504 505 506 =cut 507 508 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; 509 <form></form> 510 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 511 id="$appletName" width="500" height="375" 512 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> 513 <param name="movie" value="$codebase/$appletName.swf" /> 514 <param name="quality" value="high" /> 515 <param name="bgcolor" value="#869ca7" /> 516 <param name="allowScriptAccess" value="sameDomain" /> 517 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" 518 width="$width" height="$height" name="$appletName" align="middle" id="$appletName" 519 play="true" loop="false" quality="high" allowScriptAccess="sameDomain" 520 type="application/x-shockwave-flash" 521 pluginspage="http://www.macromedia.com/go/getflashplayer"> 522 </embed> 523 524 </object> 525 END_OBJECT_TEXT 526 527 sub new { 528 my $class = shift; 529 $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(), 530 @_ 531 ); 532 533 } 534 535 536 package JavaApplet; 537 @ISA = qw(Applet); 538 539 540 541 =pod 542 543 The secret to making this applet work with IE in addition to normal browsers 544 is the addition of the C(<form></form>) construct just before the object. 545 546 For some reason IE has trouble locating a flash object which is contained 547 within a form. Adding this second blank form with the larger problemMainForm 548 seems to solve the problem. 549 550 This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2) 551 Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated: 552 http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js 553 http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933 554 555 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; 556 <form></form> 557 <applet 558 code = "$code" 559 codebase = "$codebase" 560 archive = "$archive" 561 name = "$appletName" 562 id = "$appletName" 563 width = "$width" 564 height = "$height" 565 MAYSCRIPT 566 > 567 $parameters 568 </applet> 569 END_OBJECT_TEXT 570 571 =cut 572 573 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; 574 <form></form> 575 <applet 576 code = "$code" 577 codebase = "$codebase" 578 archive = "$archive" 579 name = "$appletName" 580 id = "$appletName" 581 width = "$width" 582 height = "$height" 583 MAYSCRIPT 584 > 585 $parameters 586 </applet> 587 END_OBJECT_TEXT 588 589 sub new { 590 my $class = shift; 591 $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(), 592 @_ 593 ); 594 595 } 596 597 598 599 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |