[system] / trunk / pg / lib / Applet.pm Repository:
ViewVC logotype

Diff of /trunk/pg/lib/Applet.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 6025 Revision 6026
1################################################################################ 1################################################################################
2# WeBWorK Online Homework Delivery System 2# WeBWorK Online Homework Delivery System
3# Copyright 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 3# Copyright 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: pg/lib/Applet.pm,v 1.19 2009/03/10 20:48:51 gage Exp $ 4# $CVSHeader: pg/lib/Applet.pm,v 1.20 2009/03/10 20:58:46 gage Exp $
5# 5#
6# This program is free software; you can redistribute it and/or modify it under 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 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 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. 9# version, or (b) the "Artistic License" which comes with this package.
21=head1 SYNPOSIS 21=head1 SYNPOSIS
22 22
23 ################################### 23 ###################################
24 # Create link to applet 24 # Create link to applet
25 ################################### 25 ###################################
26 my $appletName = "LineThruPointsWW"; 26 $appletName = "PointGraph";
27 $applet = new FlashApplet( 27$applet = FlashApplet(
28 # can be replaced by $applet =FlashApplet() when using AppletObjects.pl
29 codebase => findAppletCodebase("$appletName.swf"), 28 codebase => findAppletCodebase("$appletName.swf"),
30 appletName => $appletName, 29 appletName => $appletName,
31 appletId => $appletName, 30 appletId => $appletName,
32 submitActionAlias => 'checkAnswer', 31 setStateAlias => 'setXML',
33 ); 32 getStateAlias => 'getXML',
34 33 setConfigAlias => 'config',
34 answerBoxAlias => 'receivedField',
35);
36
35 ################################### 37###################################
36 # Configure applet 38# Configure applet
37 ################################### 39###################################
38 40
39 #xml data to set up the problem-rac 41#data to set up the equation
40 $applet->config(qq{<XML> 42$applet->config(qq{<XML expr='(x - $a)^3 + $b/$a * x' />});
41 <point xval='$xval_1' yval='$yval_1' /> 43# initial points
42 <point xval='$xval_2' yval='$yval_2' /> 44$applet->state(qq{<XML>
43 </XML>}); 45</XML>});
44
45
46 ################################### 46###################################
47 # insert applet header material 47#insert applet into body
48 ################################### 48###################################
49 HEADER_TEXT($applet->insertHeader ); 49
50
51 ###################################
52 # Text section
53 #
54
55 ###################################
56 #insert applet into body
57 ###################################
58 TEXT( MODES(TeX=>'object code', HTML=>$applet->insertObject)); 50TEXT( MODES(TeX=>'object code', HTML=>$applet->insertAll(
51 debug=>0,
52 reset_button=>1,
53 )));
59 54
60 55
61=head1 DESCRIPTION 56=head1 DESCRIPTION
62 57
63This file provides an object to store in one place 58This file provides an object to store in one place
184 base64_state returns the base64 encoded version of the state stored in the applet object. 179 base64_state returns the base64 encoded version of the state stored in the applet object.
185 180
186 initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState 181 initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState
187 submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the 182 submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the
188 .pg question is pressed. 183 .pg question is pressed.
189 answerBox -- name of answer box to return answer to: default defaultAnswerBox 184 answerBoxAlias -- name of answer box to return answer to: default defaultAnswerBox
190 getAnswer -- (formerly sendData) get student answer from applet and place in answerBox 185 getAnswer -- (formerly sendData) get student answer from applet and place in answerBox
191 returnFieldName -- (deprecated) synonmym for answerBox 186 returnFieldName -- (deprecated) synonmym for answerBoxAlias
192 187
193 188
194=cut 189=cut
195 190
196=head4 More details 191=head4 More details
205The perlApplet is initialized by $newApplet = new flashApplet( appletName=>'myApplet',..... ); The jsApplet is automatically defined in 200The perlApplet is initialized by $newApplet = new flashApplet( appletName=>'myApplet',..... ); The jsApplet is automatically defined in
206ww_applet_list["myApplet"] by copying the instance variables of $newApplet to a corresponding javaScript object. So $newApplet->{appletName} 201ww_applet_list["myApplet"] by copying the instance variables of $newApplet to a corresponding javaScript object. So $newApplet->{appletName}
207corresponds to ww_applet_list["myApplet"].appletName. (This paragraph is not yet fully implemented :-(). 202corresponds to ww_applet_list["myApplet"].appletName. (This paragraph is not yet fully implemented :-().
208 203
209Currently all messages read by the applet are xml text. If some of the code needs to be printed in the HTML header than it is converted 204Currently all messages read by the applet are xml text. If some of the code needs to be printed in the HTML header than it is converted
210to a base64 constant and then converted back to text form when it is read by an javaScript subroutine. 205to a base64 constant and then converted back to text form when it is read by a javaScript subroutine.
206
207The perlApplet has methods that help place the jsApplet code on the HTML page and create the link to the applet itself.
208In particular instance variables such as "setStateAlias", "getStateAlias" connect the WW default of "setState" to subroutine
209name chosen by the applet designer. The aim is to make it easier to connect to applets previously designed to work
210with javaScript in an HTML page or other systems.
211
212
213The jsApplet acts as an intermediary for commands directed at the applet.
214It is not necessary for the minimal operations of
215configuring the applet and maintaining
216state from one viewing of the WW question to address the applet directly.
217The methods such as "setState", "getState", "setConfig" which are part of the jsApplet
218take care of the book keeping details.
219It is also possible to make direct calls to the applet from handcrafted javaScript subroutines,
220but it may be convenient to store these as additional methods in the
221jsApplet.
211 222
212=cut 223=cut
224
225=head4 Detecting that the applet is ready
226
227Timing issues are among the pitfalls awaiting when using flash or java applets in WW questions. It is important that the WW question
228does not issue any commands to the applet until the applet is fully loaded, including the uploading of any additional configuration
229information from XML files. This can be tricky since the timing issues usually don't arise when initiating the applet from an HTML page.
230
231The WW API performs the following actions to determine if the applet is loaded:
232
233 check the ww_applet_list[appletName].isReady flag (1== applet is ready)
234 -- this caches the readiness information so that it doesn't
235 have to be repeated within a given viewing of a WW question
236 If this is 1 then the applet is ready.
237 determine whether the applet's isActive subroutine is defined AND returns 1 when called.
238 -- if the return value is 1 the applet is ready, if it is zero or no response then the applet is NOT ready
239 -- If the applet has an isActive() subroutine -- there is no alias for this --
240 then it must return 1 as soon as the applet is ready. Otherwise
241 the applet will timeout.
242 determine whether the applet's setConfig subroutine is defined.
243 -- applet.{setConfigAlias}.
244 determine whether the applet's setState subroutine is defined.
245 determine whether the jsApplets ww_applet_list[appletName].reportsLoaded flag is set to 1
246 -- this can be set by the applet if it calls the javaScript function
247 "applet_loaded(appletName, loaded_status). The loaded_status is 1 or 0
248
249 Logic for determining applet status: if any one of the above checks succeeds (or returns 1) then the applet is
250 consdered to be ready UNLESS the isActive() exists and the call returns a 0 or no response. In this case
251 the applet is assumed to be loading additional data and is not yet ready.
252
253 For this reason if the isActive subroutine
254 is defined in the applet it must return a 1 once the applet is prepared to accept additional commands.
255 (Since there are some extent flashApplets with non-functioning isActive() subroutines a temporary workaround
256 assuems that after C<maxInitializationAttempts> -- 5 by default -- the applet is in fact ready but the
257 isActive() subroutine is non functioning. This can give rise to false "readiness" signals if the applet
258 takes a long time to load auxiliary files.)
259
260The applet itself can take measures to insure that the setConfig subroutine is prepared to respond immediately once the applet is loaded.
261It can include timers that delay execution of the configuring actions until all of the auxiliary files needed by the applet are loaded.
262
263
264=cut
265
266
267=head4 Initialization sequence
268
269When the WW question is loaded the C<initializeWWquestion> javaScript subroutine calls each of the applets used in the question asking them
270to initialize themselves.
271
272The applets initialization method is as follows:
273
274 -- wait until the applet is loaded and the applet has loaded all of its auxiliary files.
275 -- set the debugMode in the applet
276 -- call the setConfig method in the javaScript applet -- (configuration parameters are "permanent" for the life of the applet
277 -- call the setInitialization method in the javaScript applet -- this often calls the setState method in the applet
278
279=cut
280
281=head Methods defined for the javaScript applet ww_applet_list[appletName]
282
283This is not a comprehensive list
284
285 setConfig -- transmits the information for configuring the applet
286
287 getConfig -- retrieves the configuration information -- this is used mainly for debugging and may not be defined in most applets
288
289
290 setState -- sets the current state (1) from the appletName_state HTML element if this contains an <xml>...</xml> string
291 -- if the value contains <xml>restart_applet</xml> then set the current state to ww_applet_list[appletName].initialState
292 -- if the value is a blank string set the current state to ww_applet_list[appletName].initialState
293
294
295 getState -- retrieves the current state and stores in the appletName_state HTML element.
296
297
298
299
300
301
302
303=head Submit sequence
304
305When the WW question submit button is pressed the form containing the WW question calles the javaScript "submitAction()" which then asks
306each of the applets on the page to perform its submit action which consists of
307
308 -- if the applet is to be reinitialized (appletName_state contains <xml>restart_applet</xml>) then
309 the HTML elements appletName_state and previous_appletName_state are set to <xml>restart_applet</xml>
310 to be interpreted by the next setState command
311 -- Otherwise getState() from the applet and save it to the HTML input element appletName_state
312 -- Perform the javaScript commands in .submitActionScript (default: '' )
313 a typical submitActionScript looks like getQE(this.answerBox).value = getApplet(appletName).getAnswer() )
213 314
214=head4 Requirements for applets 315=head4 Requirements for applets
215 316
216The following methods are desirable in an applet that preserves state in a WW question. None of them are required. 317The following methods are desirable in an applet that preserves state in a WW question. None of them are required.
217 318
243 coordinated with the WW question. 344 coordinated with the WW question.
244 -- this method is used for debugging to ensure that 345 -- this method is used for debugging to ensure that
245 the configuration was set as expected. 346 the configuration was set as expected.
246 getAnswer (default: getAnswer) 347 getAnswer (default: getAnswer)
247 -- Returns a string (usually NOT xml) which is the 348 -- Returns a string (usually NOT xml) which is the
248 response that the student is submitting to answer 349 response that the student is effectvely submitting to answer
249 the WW question. 350 the WW question.
250 351
251 352
252=cut 353=cut
253 354
270 setStateAlias => 'setXML', 371 setStateAlias => 'setXML',
271 configAlias => '', # deprecated 372 configAlias => '', # deprecated
272 getConfigAlias => 'getConfig', 373 getConfigAlias => 'getConfig',
273 setConfigAlias => 'setConfig', 374 setConfigAlias => 'setConfig',
274 initializeActionAlias => 'setXML', 375 initializeActionAlias => 'setXML',
376 maxInitializationAttempts => 5, # number of attempts to initialize applet
275 submitActionAlias => 'getXML', 377 submitActionAlias => 'getXML',
276 submitActionScript => '', # script executed on submitting the WW question 378 submitActionScript => '', # script executed on submitting the WW question
277 answerBoxAlias => 'answerBox', 379 answerBoxAlias => 'answerBox',
278 answerBox => '', # deprecated 380 answerBox => '', # deprecated
279 returnFieldName => '', # deprecated 381 returnFieldName => '', # deprecated
280 headerText => DEFAULT_HEADER_TEXT(), 382 headerText => DEFAULT_HEADER_TEXT(),
281 objectText => '', 383 objectText => '',
282 debug => 0, 384 debugMode => 0,
283 @_, 385 @_,
284 }; 386 };
285 bless $self, $class; 387 bless $self, $class;
286 $self->initialState('<xml></xml>'); 388 $self->initialState('<xml></xml>');
287 if ($self->{returnFieldName} or $self->{answerBox} ) { # backward compatibility 389 if ($self->{returnFieldName} or $self->{answerBox} ) { # backward compatibility
408sub appletName { 510sub appletName {
409 my $self = shift; 511 my $self = shift;
410 $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty 512 $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty
411 $self->{appletName}; 513 $self->{appletName};
412} 514}
413sub debug { 515sub debugMode {
414 my $self = shift; 516 my $self = shift;
415 my $new_flag = shift; 517 my $new_flag = shift;
416 $self->{debug} = $new_flag if defined($new_flag); 518 $self->{debugMode} = $new_flag if defined($new_flag);
417 $self->{debug}; 519 $self->{debugMode};
418} 520}
419sub appletId { 521sub appletId {
420 appletName(@_); 522 appletName(@_);
421} 523}
422 524sub maxInitializationAttempts {
525 my $self = shift;
526 $self->{maxInitializationAttempts} = shift || $self->{maxInitializationAttempts};
527 $self->{maxInitializationAttempts};
528}
423sub initialState { 529sub initialState {
424 my $self = shift; 530 my $self = shift;
425 my $str = shift; 531 my $str = shift;
426 $self->{initialState} = $str ||$self->{initialState}; # replace the current string if non-empty 532 $self->{initialState} = $str ||$self->{initialState}; # replace the current string if non-empty
427 $self->{initialState}; 533 $self->{initialState};
461 my $self = shift; 567 my $self = shift;
462 warn "use answerBoxName instead of returnFieldName"; 568 warn "use answerBoxName instead of returnFieldName";
463} 569}
464sub answerBox { 570sub answerBox {
465 my $self = shift; 571 my $self = shift;
466 warn "use answerBoxName instead of AnswerBox"; 572 warn "use answerBoxAlias instead of AnswerBox";
467} 573}
468######################### 574#########################
469#FIXME 575#FIXME
470# need to be able to adjust header material 576# need to be able to adjust header material
471 577
473 my $self = shift; 579 my $self = shift;
474 580
475 my $codebase = $self->codebase; 581 my $codebase = $self->codebase;
476 my $appletId = $self->appletId; 582 my $appletId = $self->appletId;
477 my $appletName = $self->appletName; 583 my $appletName = $self->appletName;
478 my $base64_initialState = $self->base64_state; 584 my $base64_initialState = $self->base64_state;
479 my $initializeAction = $self->initializeActionAlias; 585 my $initializeActionAlias = $self->initializeActionAlias;
480 my $submitActionAlias = $self->submitActionAlias; 586 my $submitActionAlias = $self->submitActionAlias;
481 my $submitActionScript = $self->submitActionScript; 587 my $submitActionScript = $self->submitActionScript;
482 my $setStateAlias = $self->setStateAlias; 588 my $setStateAlias = $self->setStateAlias;
483 my $getStateAlias = $self->getStateAlias; 589 my $getStateAlias = $self->getStateAlias;
484 590
485 my $setConfigAlias = $self->setConfigAlias; 591 my $setConfigAlias = $self->setConfigAlias;
486 my $getConfigAlias = $self->getConfigAlias; 592 my $getConfigAlias = $self->getConfigAlias;
593 my $maxInitializationAttempts = $self->maxInitializationAttempts;
487 my $base64_config = $self->base64_config; 594 my $base64_config = $self->base64_config;
488 my $debugMode = ($self->debug) ? "1": "0"; 595 my $debugMode = ($self->debugMode) ? "1": "0";
489 my $returnFieldName = $self->{returnFieldName};
490 my $answerBox = $self->{answerBox}; 596 my $answerBoxAlias = $self->{answerBoxAlias};
491 my $headerText = $self->header(); 597 my $headerText = $self->header();
492 598
493 599
494 $submitActionScript =~ s/"/\\"/g; # escape quotes for ActionScript 600 $submitActionScript =~ s/"/\\"/g; # escape quotes for ActionScript
495 # other variables should not have quotes. 601 # other variables should not have quotes.
496 602
497 $submitActionScript =~ s/\n/ /g; # replace returns with spaces -- returns in the wrong spot can cause trouble with javaScript 603 $submitActionScript =~ s/\n/ /g; # replace returns with spaces -- returns in the wrong spot can cause trouble with javaScript
498 $submitActionScript =~ s/\r/ /g; # replace returns with spaces -- returns can cause trouble 604 $submitActionScript =~ s/\r/ /g; # replace returns with spaces -- returns can cause trouble
605 my $base64_submitActionScript = encode_base64($submitActionScript);
606 $base64_submitActionScript =~s/\n//g;
499 607
500 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook 608 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook
501 609
502 return $headerText; 610 return $headerText;
503 611
549 //upload functions stored in /opt/webwork/webwork2/htdocs/js ... 657 //upload functions stored in /opt/webwork/webwork2/htdocs/js ...
550 658
551 </script> 659 </script>
552 <script language="JavaScript"> 660 <script language="JavaScript">
553 661
554 // set debug mode for this applet 662
555 set_debug($debugMode);
556 663
557 ////////////////////////////////////////////////////////// 664 //////////////////////////////////////////////////////////
558 //TEST code 665 //TEST code
559 // 666 //
560 // 667 //
562 669
563 ww_applet_list["$appletName"] = new ww_applet("$appletName"); 670 ww_applet_list["$appletName"] = new ww_applet("$appletName");
564 671
565 672
566 ww_applet_list["$appletName"].code = "$code"; 673 ww_applet_list["$appletName"].code = "$code";
567 ww_applet_list["$appletName"].codebase = "$codebase"; 674 ww_applet_list["$appletName"].codebase = "$codebase";
568 ww_applet_list["$appletName"].appletID = "$appletID"; 675 ww_applet_list["$appletName"].appletID = "$appletID";
569 ww_applet_list["$appletName"].base64_state = "$base64_initializationState"; 676 ww_applet_list["$appletName"].base64_state = "$base64_initializationState";
677 ww_applet_list["$appletName"].initialState = Base64.decode("$base64_intialState");
570 ww_applet_list["$appletName"].base64_config = "$base64_config"; 678 ww_applet_list["$appletName"].base64_config = "$base64_config";
571 ww_applet_list["$appletName"].getStateAlias = "$getStateAlias"; 679 ww_applet_list["$appletName"].getStateAlias = "$getStateAlias";
572 ww_applet_list["$appletName"].setStateAlias = "$setStateAlias"; 680 ww_applet_list["$appletName"].setStateAlias = "$setStateAlias";
573 ww_applet_list["$appletName"].setConfigAlias = "$setConfigAlias"; 681 ww_applet_list["$appletName"].setConfigAlias = "$setConfigAlias";
574 ww_applet_list["$appletName"].getConfigAlias = "$getConfigAlias"; 682 ww_applet_list["$appletName"].getConfigAlias = "$getConfigAlias";
575 ww_applet_list["$appletName"].initializeActionAlias = "$initializeAction"; 683 ww_applet_list["$appletName"].initializeActionAlias = "$initializeActionAlias";
576 ww_applet_list["$appletName"].submitActionAlias = "$submitActionAlias"; 684 ww_applet_list["$appletName"].submitActionAlias = "$submitActionAlias";
577 ww_applet_list["$appletName"].submitActionScript = "$submitActionScript"; 685 ww_applet_list["$appletName"].submitActionScript = Base64.decode("$submitActionScript_base64");
578 ww_applet_list["$appletName"].answerBox = "$answerBox"; 686 ww_applet_list["$appletName"].answerBoxAlias = "$answerBoxAlias";
687 ww_applet_list["$appletName"].maxInitializationAttempts = $maxInitializationAttempts;
579 ww_applet_list["$appletName"].debug = "$debugMode"; 688 ww_applet_list["$appletName"].debugMode = "$debugMode";
580 689
581 </script> 690 </script>
582 691
583END_HEADER_SCRIPT 692END_HEADER_SCRIPT
584 693

Legend:
Removed from v.6025  
changed lines
  Added in v.6026

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9