[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 5667 Revision 6030
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.10 2008/05/05 17:24:31 gage Exp $ 4# $CVSHeader: pg/lib/Applet.pm,v 1.21 2009/03/15 19:25:03 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 => 'answerBox',
35 submitActionScript => qq{ getQE('answerBox').value = getApplet("$appletName").getAnswer() },
36);
37
35 ################################### 38###################################
36 # Configure applet 39# Configure applet
37 ################################### 40###################################
38 41
39 #xml data to set up the problem-rac 42#data to set up the equation
40 $applet->config(qq{<XML> 43$applet->config(qq{<XML expr='(x - $a)^3 + $b/$a * x' />});
41 <point xval='$xval_1' yval='$yval_1' /> 44# initial points
42 <point xval='$xval_2' yval='$yval_2' /> 45$applet->state(qq{<XML>
43 </XML>}); 46</XML>});
44
45
46 ################################### 47###################################
47 # insert applet header material 48#insert applet into body
48 ################################### 49###################################
49 HEADER_TEXT($applet->insertHeader ); 50
50
51 ###################################
52 # Text section
53 #
54
55 ###################################
56 #insert applet into body
57 ###################################
58 TEXT( MODES(TeX=>'object code', HTML=>$applet->insertObject)); 51TEXT( MODES(TeX=>'object code', HTML=>$applet->insertAll(
52 includeAnswerBox => 1
53 debug=>0,
54 reinitialize_button=>1,
55 )));
59 56
60 57
61=head1 DESCRIPTION 58=head1 DESCRIPTION
62 59
63This file provides an object to store in one place 60This file provides an object to store in one place
99use MIME::Base64 qw( encode_base64 decode_base64); 96use MIME::Base64 qw( encode_base64 decode_base64);
100 97
101 98
102=head2 Default javaScript functions placed in header 99=head2 Default javaScript functions placed in header
103 100
101=pod
102
104These functions are automatically defined for use for 103These functions are automatically defined for use for
105any javaScript placed in the text of a PG question. 104any javaScript placed in the text of a PG question.
106 105
107 getApplet(appletName) -- finds the applet path in the DOM 106 getApplet(appletName) -- finds the applet path in the DOM
108 107
109 submitAction() -- calls the submit action of the applets 108 submitAction() -- calls the submit action of the applets
110
111 109
112 initializeAction() -- calls the initialize action of the applets 110 initializeWWquestion() -- calls the initialize action of the applets
113 111
114 getQE(name) -- gets an HTML element of the question by name 112 getQE(name) -- gets an HTML element of the question by name
115 or by id. Be sure to keep all names and ids 113 or by id. Be sure to keep all names and ids
116 unique within a given PG question. 114 unique within a given PG question.
117 115
118 getQuestionElement(name) -- long form of getQE(name) 116 getQuestionElement(name) -- long form of getQE(name)
119 117
120 listQuestionElements() -- for discovering the names of inputs in the 118 listQuestionElements() -- for discovering the names of inputs in the
121 PG question. An alert dialog will list all 119 PG question. An alert dialog will list all
122 of the elements. 120 of the elements.
123 Usage: Place this at the END of the question, 121 Usage: Place this at the END of the question, just before END_DOCUMENT():
124 just before END_DOCUMENT():
125 122
126 TEXT(qq!<script> listQuestionElements() </script>!); 123 TEXT(qq!<script> listQuestionElements() </script>!);
127 ENDDOCUMENT(); 124 ENDDOCUMENT();
125 to obtain a list of all of the HTML elements in the question
126
127 ----------------------------------------------------------------------------
128
129
130 List of accessor methods made available by the FlashApplet class:
131 Usage: $current_value = $applet->method(new_value or empty)
132 These can also be set when creating the class -- for exampe:
133 $applet = new FlashApplet(
134 # can be replaced by $applet =FlashApplet() when using AppletObjects.pl
135 codebase => findAppletCodebase("$appletName.swf"),
136 appletName => $appletName,
137 appletId => $appletName,
138 submitActionAlias => 'checkAnswer',
139 );
128 140
129 list of accessor methods format: current_value = $self->method(new_value or empty)
130 141
131 appletId for simplicity and reliability appletId and appletName are always the same 142 appletId for simplicity and reliability appletId and appletName are always the same
132 appletName 143 appletName
133
134 archive the name of the .jar file containing the applet code 144 archive the name of the .jar file containing the applet code
135 code the name of the applet code in the .jar archive 145 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 146 codebase a prefix url used to find the archive and the applet itself
137 147
138 height rectangle alloted in the html page for displaying the applet 148 height rectangle alloted in the html page for displaying the applet
139 width 149
140
141 params an anonymous array containing name/value pairs 150 params an anonymous array containing name/value pairs
142 to configure the applet [name =>'value, ...] 151 to configure the applet [name =>'value, ...]
143 152
144 header stores the text to be added to the header section of the html page 153 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 154 object stores the text which places the applet on the html page
146 155
147 debug in debug mode several alerts mark progress through the procedure of calling the applet 156 debugMode in debug mode several alerts mark progress through the procedure of calling the applet
148 157
149 config configuration are those customizable attributes of the applet which don't 158 configuration configuration contains those customizable attributes of the applet which don't
150 change as it is used. When stored in hidden answer fields 159 change as it is used. When stored in hidden answer fields
151 it is usually stored in base64 encoded format. 160 it is usually stored in base64 encoded format.
152 base64_config base64 encode version of the contents of config 161
153
154 configAlias (default: config ) names the applet command called with the contents of $self->config 162 configAlias (default: setConfig ) 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> 163 to configure the applet. The parameters are passed to the applet in plain text using <xml>
156 The outer tags must be <xml> ..... </xml> 164 The outer tags must be <xml> ..... </xml>
165 setConfigAlias (default: setConfig) -- a synonym for configAlias
166 getConfigAlias (default: getConfig) -- retrieves the configuration from the applet. This is used
167 mainly for debugging. In principal the configuration remains the same for a given instance
168 of the applet -- i.e. for the homework question for a single student. The state however
169 will change depending on the interactions between the student and the applet.
157 state state consists of those customizable attributes of the applet which change 170 initialState the 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 171 as the applet is used by the student. It is stored by the calling .pg question so that
159 when revisiting the question the applet 172 when revisiting the question the applet will be restored to the same state it was left in when the question was last
160 will be restored to the same state it was left in when the question was last
161 viewed. 173 viewed.
162 174
163 getStateAlias (default: getState) alias for command called to read the current state of the applet. 175 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> 176 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. 177 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> 178 The state is passed in plain text in xml format with outer tags: <xml>....</xml>
167 179
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 180 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 181 submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the
172 .pg question is pressed. 182 .pg question is pressed.
183 answerBoxAlias -- name of answer box to return answer to: default defaultAnswerBox
184 getAnswer -- (formerly sendData) get student answer from applet and place in answerBox
185 returnFieldName -- (deprecated) synonmym for answerBoxAlias
173 186
174 returnFieldName
175
176
177
178
179 187
180=cut 188=cut
181 189
190=head4 More details
182 191
192There are three different "images" of the applet. The first is the java or flash applet itself. The object that actually does the work.
193The second is a perl image of the applet -- henceforth the perlApplet -- which is configured in the .pg file and allows a WeBWorK question
194to communicate with the applet. The third image is a javaScript image of the applet -- henceforth the jsApplet which is a mirror of the perlApplet
195but is available to the javaScript code setup and executed in the virtual HTML page defined by the .pg file of the WeBWorK question. One can think of
196the jsApplet as a runtime version of the perlApplet since it can be accessed and modified after the virtual HTML page has been created by
197the PG rendering process.
183 198
199The perlApplet is initialized by $newApplet = new flashApplet( appletName=>'myApplet',..... ); The jsApplet is automatically defined in
200ww_applet_list["myApplet"] by copying the instance variables of $newApplet to a corresponding javaScript object. So $newApplet->{appletName}
201corresponds to ww_applet_list["myApplet"].appletName. (This paragraph is not yet fully implemented :-().
202
203Currently 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
204to a base64 constant and then converted back to text form when it is read by a javaScript subroutine.
205
206The perlApplet has methods that help place the jsApplet code on the HTML page and create the link to the applet itself.
207In particular instance variables such as "setStateAlias", "getStateAlias" connect the WW default of "setState" to subroutine
208name chosen by the applet designer. The aim is to make it easier to connect to applets previously designed to work
209with javaScript in an HTML page or other systems.
210
211
212The jsApplet acts as an intermediary for commands directed at the applet.
213It is not necessary for the minimal operations of
214configuring the applet and maintaining
215state from one viewing of the WW question to address the applet directly.
216The methods such as "setState", "getState", "setConfig" which are part of the jsApplet
217take care of the book keeping details.
218It is also possible to make direct calls to the applet from handcrafted javaScript subroutines,
219but it may be convenient to store these as additional methods in the
220jsApplet.
221
222=cut
223
224=head4 Detecting that the applet is ready
225
226Timing issues are among the pitfalls awaiting when using flash or java applets in WW questions. It is important that the WW question
227does not issue any commands to the applet until the applet is fully loaded, including the uploading of any additional configuration
228information from XML files. This can be tricky since the timing issues usually don't arise when initiating the applet from an HTML page.
229
230The WW API performs the following actions to determine if the applet is loaded:
231
232 check the ww_applet_list[appletName].isReady flag (1== applet is ready)
233 -- this caches the readiness information so that it doesn't
234 have to be repeated within a given viewing of a WW question
235 If this is 1 then the applet is ready.
236 determine whether the applet's isActive subroutine is defined AND returns 1 when called.
237 -- if the return value is 1 the applet is ready, if it is zero or no response then the applet is NOT ready
238 -- If the applet has an isActive() subroutine -- there is no alias for this --
239 then it must return 1 as soon as the applet is ready. Otherwise
240 the applet will timeout.
241 determine whether the applet's setConfig subroutine is defined.
242 -- applet.{setConfigAlias}.
243 determine whether the applet's setState subroutine is defined.
244 determine whether the jsApplets ww_applet_list[appletName].reportsLoaded flag is set to 1
245 -- this can be set by the applet if it calls the javaScript function
246 "applet_loaded(appletName, loaded_status). The loaded_status is 1 or 0
247
248 Logic for determining applet status: if any one of the above checks succeeds (or returns 1) then the applet is
249 consdered to be ready UNLESS the isActive() exists and the call returns a 0 or no response. In this case
250 the applet is assumed to be loading additional data and is not yet ready.
251
252 For this reason if the isActive subroutine
253 is defined in the applet it must return a 1 once the applet is prepared to accept additional commands.
254 (Since there are some extent flashApplets with non-functioning isActive() subroutines a temporary workaround
255 assuems that after C<maxInitializationAttempts> -- 5 by default -- the applet is in fact ready but the
256 isActive() subroutine is non functioning. This can give rise to false "readiness" signals if the applet
257 takes a long time to load auxiliary files.)
258
259The applet itself can take measures to insure that the setConfig subroutine is prepared to respond immediately once the applet is loaded.
260It can include timers that delay execution of the configuring actions until all of the auxiliary files needed by the applet are loaded.
261
262
263=cut
264
265
266=head4 Initialization sequence
267
268When the WW question is loaded the C<initializeWWquestion> javaScript subroutine calls each of the applets used in the question asking them
269to initialize themselves.
270
271The applets initialization method is as follows:
272
273 -- wait until the applet is loaded and the applet has loaded all of its auxiliary files.
274 -- set the debugMode in the applet
275 -- call the setConfig method in the javaScript applet -- (configuration parameters are "permanent" for the life of the applet
276 -- call the setInitialization method in the javaScript applet -- this often calls the setState method in the applet
277
278=cut
279
280=head Methods defined for the javaScript applet ww_applet_list[appletName]
281
282This is not a comprehensive list
283
284 setConfig -- transmits the information for configuring the applet
285
286 getConfig -- retrieves the configuration information -- this is used mainly for debugging and may not be defined in most applets
287
288
289 setState -- sets the current state (1) from the appletName_state HTML element if this contains an <xml>...</xml> string
290 -- if the value contains <xml>restart_applet</xml> then set the current state to ww_applet_list[appletName].initialState
291 -- if the value is a blank string set the current state to ww_applet_list[appletName].initialState
292
293
294 getState -- retrieves the current state and stores in the appletName_state HTML element.
295
296
297
298
299
300
301
302=head Submit sequence
303
304When the WW question submit button is pressed the form containing the WW question calles the javaScript "submitAction()" which then asks
305each of the applets on the page to perform its submit action which consists of
306
307 -- if the applet is to be reinitialized (appletName_state contains <xml>restart_applet</xml>) then
308 the HTML elements appletName_state and previous_appletName_state are set to <xml>restart_applet</xml>
309 to be interpreted by the next setState command
310 -- Otherwise getState() from the applet and save it to the HTML input element appletName_state
311 -- Perform the javaScript commands in .submitActionScript (default: '' )
312 a typical submitActionScript looks like getQE(this.answerBox).value = getApplet(appletName).getAnswer() )
313
314=head4 Requirements for applets
315
316The following methods are desirable in an applet that preserves state in a WW question. None of them are required.
317
318 setState(str) (default: setXML)
319 -- set the current state of the applet from an xml string
320 -- should be able to accept an empty string or a string of
321 the form <XML>.....</XML> without creating errors
322 -- can be designed to receive other forms of input if it is
323 coordinated with the WW question.
324 getState() (default: getXML)
325 -- return the current state of the applet in an xml string.
326 -- an empty string or a string of the form <XML>.....</XML>
327 are the standard responses.
328 -- can be designed to return other strings if it is
329 coordinated with the WW question.
330 setConfig(str) (default: setConfig)
331 -- If the applet allows configuration this configures the applet
332 from an xml string
333 -- should be able to accept an empty string or a string of the
334 form <XML>.....</XML> without creating errors
335 -- can be designed to receive other forms of input if it is
336 coordinated with the WW question.
337 getConfig (default: getConfig)
338 -- This returns a string defining the configuration of the
339 applet in an xml string
340 -- an empty string or a string of the form <XML>.....</XML>
341 are the standard responses.
342 -- can be designed to return other strings if it is
343 coordinated with the WW question.
344 -- this method is used for debugging to ensure that
345 the configuration was set as expected.
346 getAnswer (default: getAnswer)
347 -- Returns a string (usually NOT xml) which is the
348 response that the student is effectvely submitting to answer
349 the WW question.
350
351
352=cut
184 353
185sub new { 354sub new {
186 my $class = shift; 355 my $class = shift;
187 my $self = { 356 my $self = {
188 appletName =>'', 357 appletName =>'',
190 codebase=>'', 359 codebase=>'',
191# appletId =>'', #always use identical applet Id's and applet Names 360# appletId =>'', #always use identical applet Id's and applet Names
192 params =>undef, 361 params =>undef,
193 width => 550, 362 width => 550,
194 height => 400, 363 height => 400,
364 bgcolor => "#869ca7",
195 base64_state => undef, # this is an state to use for initializing the first occurence of the question. 365# base64_state => undef, # this is a state to use for initializing the first occurence of the question.
196 base64_config => undef, # this is the initial (and final?) configuration 366# base64_config => undef, # this is the initial (and final?) configuration
367 configuration => '', # configuration defining the applet
368 initialState => '', # initial state.
197 getStateAlias => 'getXML', 369 getStateAlias => 'getXML',
198 setStateAlias => 'setXML', 370 setStateAlias => 'setXML',
371 configAlias => '', # deprecated
199 configAlias => 'config', 372 getConfigAlias => 'getConfig',
373 setConfigAlias => 'setConfig',
200 initializeActionAlias => 'setXML', 374 initializeActionAlias => 'setXML',
375 maxInitializationAttempts => 5, # number of attempts to initialize applet
201 submitActionAlias => 'getXML', 376 submitActionAlias => 'getXML',
202 returnFieldName => 'receivedField', 377 submitActionScript => '', # script executed on submitting the WW question
378 answerBoxAlias => 'answerBox',
379 answerBox => '', # deprecated
380 returnFieldName => '', # deprecated
203 headerText => DEFAULT_HEADER_TEXT(), 381 headerText => DEFAULT_HEADER_TEXT(),
204 objectText => '', 382 objectText => '',
205 debug => 0, 383 debugMode => 0,
206 @_, 384 @_,
207 }; 385 };
208 bless $self, $class; 386 bless $self, $class;
209 $self->state('<xml></xml>'); 387 $self->initialState('<xml></xml>');
388 if ($self->{returnFieldName} or $self->{answerBox} ) { # backward compatibility
389 warn "use answerBoxAlias instead of returnFieldName or answerBox";
390 $self->{answerBox}='';
391 $self->{returnFieldName}='';
392 }
393 if ($self->{configAlias}) { # backward compatibility
394 warn "use setConfigAlias instead of configAlias";
395 $self->{configAlias}='';
396 }
210 $self->config('<xml></xml>'); 397 $self->configuration('<xml></xml>');
211 return $self; 398 return $self;
212} 399}
213 400
214sub header { 401sub header {
215 my $self = shift; 402 my $self = shift;
250sub submitActionAlias { 437sub submitActionAlias {
251 my $self = shift; 438 my $self = shift;
252 $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty 439 $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty
253 $self->{submitActionAlias}; 440 $self->{submitActionAlias};
254} 441}
442sub submitActionScript {
443 my $self = shift;
444 $self->{submitActionScript} = shift ||$self->{submitActionScript}; # replace the current contents if non-empty
445 $self->{submitActionScript};
446}
255sub getStateAlias { 447sub getStateAlias {
256 my $self = shift; 448 my $self = shift;
257 $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty 449 $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty
258 $self->{getStateAlias}; 450 $self->{getStateAlias};
259} 451}
263 $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty 455 $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty
264 $self->{setStateAlias}; 456 $self->{setStateAlias};
265} 457}
266sub configAlias { 458sub configAlias {
267 my $self = shift; 459 my $self = shift;
268 $self->{configAlias} = shift ||$self->{configAlias}; # replace the current contents if non-empty 460 $self->{setConfigAlias} = shift ||$self->{setConfigAlias}; # replace the current contents if non-empty
269 $self->{configAlias}; 461 $self->{setConfigAlias};
270} 462}
271sub returnFieldName { 463sub setConfigAlias {
272 my $self = shift; 464 my $self = shift;
273 $self->{returnFieldName} = shift ||$self->{returnFieldName}; # replace the current contents if non-empty 465 $self->{setConfigAlias} = shift ||$self->{setConfigAlias}; # replace the current contents if non-empty
274 $self->{returnFieldName}; 466 $self->{setConfigAlias};
467}
468sub getConfigAlias {
469 my $self = shift;
470 $self->{getConfigAlias} = shift ||$self->{getConfigAlias}; # replace the current contents if non-empty
471 $self->{getConfigAlias};
472}
473
474sub answerBoxName {
475 my $self = shift;
476 $self->{answerBox} = shift ||$self->{answerBox}; # replace the current contents if non-empty
477 $self->{answerBox};
275} 478}
276sub codebase { 479sub codebase {
277 my $self = shift; 480 my $self = shift;
278 $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty 481 $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty
279 $self->{codebase}; 482 $self->{codebase};
291sub width { 494sub width {
292 my $self = shift; 495 my $self = shift;
293 $self->{width} = shift ||$self->{width}; # replace the current width if non-empty 496 $self->{width} = shift ||$self->{width}; # replace the current width if non-empty
294 $self->{width}; 497 $self->{width};
295} 498}
499sub bgcolor {
500 my $self = shift;
501 $self->{bgcolor} = shift ||$self->{bgcolor}; # replace the current background color if non-empty
502 $self->{bgcolor};
503}
296sub archive { 504sub archive {
297 my $self = shift; 505 my $self = shift;
298 $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty 506 $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty
299 $self->{archive}; 507 $self->{archive};
300} 508}
301sub appletName { 509sub appletName {
302 my $self = shift; 510 my $self = shift;
303 $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty 511 $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty
304 $self->{appletName}; 512 $self->{appletName};
305} 513}
306sub debug { 514sub debugMode {
307 my $self = shift; 515 my $self = shift;
308 my $new_flag = shift; 516 my $new_flag = shift;
309 $self->{debug} = $new_flag if defined($new_flag); 517 $self->{debugMode} = $new_flag if defined($new_flag);
310 $self->{debug}; 518 $self->{debugMode};
311} 519}
312sub appletId { 520sub appletId {
313 appletName(@_); 521 appletName(@_);
314} 522}
523sub maxInitializationAttempts {
524 my $self = shift;
525 $self->{maxInitializationAttempts} = shift || $self->{maxInitializationAttempts};
526 $self->{maxInitializationAttempts};
527}
528sub initialState {
529 my $self = shift;
530 my $str = shift;
531 $self->{initialState} = $str ||$self->{initialState}; # replace the current string if non-empty
532 $self->{initialState};
533}
534sub configuration {
535 my $self = shift;
536 my $str = shift;
537 $self->{configuration} = $str || $self->{configuration}; # replace the current string if non-empty
538 $self->{configuration} =~ s/\n//g;
539 $self->{configuration};
540}
541
542#######################
543# soon to be deprecated?
544#######################
545
546sub config {
547 my $self = shift;
548 my $str = shift;
549 warn "use $self->configuration instead of $self->config. Internally this string is ascii, not base64 encoded", join(' ', caller());
550# $self->{base64_config} = encode_base64($str) || $self->{base64_config}; # replace the current string if non-empty
551# $self->{base64_config} =~ s/\n//g;
552# decode_base64($self->{base64_config});
553}
315sub state { 554sub state {
316 my $self = shift; 555 my $self = shift;
317 my $str = shift; 556 my $str = shift;
557 warn "use $self->initialState instead of $self->state. Internally this string is ascii, not base64 encoded", join(' ', caller());
318 $self->{base64_state} = encode_base64($str) ||$self->{base64_state}; # replace the current string if non-empty 558# $self->{base64_state} = encode_base64($str) ||$self->{base64_state}; # replace the current string if non-empty
319 $self->{base64_state} =~ s/\n//g; 559# $self->{base64_state} =~ s/\n//g;
320 decode_base64($self->{base64_state}); 560# decode_base64($self->{base64_state});
321} 561}
322
323sub base64_state{ 562sub base64_state{
324 my $self = shift; 563 my $self = shift;
325 $self->{base64_state} = shift ||$self->{base64_state}; # replace the current string if non-empty 564 warn "use $self->InitialState instead of $self->state. Internally this string is ascii, not base64 encoded", join(' ', caller());
326 $self->{base64_state}; 565
566
327} 567}
328sub config { 568
329 my $self = shift;
330 my $str = shift;
331 $self->{base64_config} = encode_base64($str) || $self->{base64_config}; # replace the current string if non-empty
332 $self->{base64_config} =~ s/\n//g;
333 decode_base64($self->{base64_config});
334}
335sub base64_config { 569sub base64_config {
336 my $self = shift; 570 my $self = shift;
337 $self->{base64_config} = shift ||$self->{base64_config}; # replace the current string if non-empty 571 warn "use $self->configuration instead of $self->config. Internally this string is ascii, not base64 encoded";
338 $self->{base64_config} =$self->{base64_config};
339 $self->{base64_config};
340} 572}
573
574sub returnFieldName {
575 my $self = shift;
576 warn "use answerBoxName instead of returnFieldName";
577}
578sub answerBox {
579 my $self = shift;
580 warn "use answerBoxAlias instead of AnswerBox";
581}
582#########################
341#FIXME 583#FIXME
342# need to be able to adjust header material 584# need to be able to adjust header material
343 585
344sub insertHeader { 586sub insertHeader {
345 my $self = shift; 587 my $self = shift;
588
346 my $codebase = $self->codebase; 589 my $codebase = $self->codebase;
347 my $appletId = $self->appletId; 590 my $appletId = $self->appletId;
348 my $appletName = $self->appletName; 591 my $appletName = $self->appletName;
349 my $base64_initialState = $self->base64_state;
350 my $initializeAction = $self->initializeActionAlias; 592 my $initializeActionAlias = $self->initializeActionAlias;
351 my $submitAction = $self->submitActionAlias; 593 my $submitActionScript = $self->submitActionScript;
352 my $setState = $self->setStateAlias; 594 my $setStateAlias = $self->setStateAlias;
353 my $getState = $self->getStateAlias; 595 my $getStateAlias = $self->getStateAlias;
596
354 my $config = $self->configAlias; 597 my $setConfigAlias = $self->setConfigAlias;
355 my $base64_config = $self->base64_config; 598 my $getConfigAlias = $self->getConfigAlias;
599 my $maxInitializationAttempts = $self->maxInitializationAttempts;
356 my $debugMode = ($self->debug) ? "1": "0"; 600 my $debugMode = ($self->debugMode) ? "1": "0";
357 my $returnFieldName = $self->{returnFieldName}; 601 my $answerBoxAlias = $self->{answerBoxAlias};
358# my $encodeStateQ = ($self->debug)?'' : "state = Base64.encode(state);"; # in debug mode base64 encoding is not used.
359# my $decodeStateQ = "if (!state.match(/<XML>*/i) ) {state = Base64.decode(state)}"; # decode if <XML> is not present
360 my $headerText = $self->header(); 602 my $headerText = $self->header();
603
604
605 #$submitActionScript =~ s/"/\\"/g; # escape quotes for ActionScript
606 # other variables should not have quotes.
607
608 $submitActionScript =~ s/\n/ /g; # replace returns with spaces -- returns in the wrong spot can cause trouble with javaScript
609 $submitActionScript =~ s/\r/ /g; # replace returns with spaces -- returns can cause trouble
610 my $base64_submitActionScript = encode_base64($submitActionScript);
611 my $base64_configuration = encode_base64($self->configuration);
612 my $base64_initialState = encode_base64($self->initialState);
613
614 $base64_submitActionScript =~s/\n//g;
615 $base64_initialState =~s/\n//g; # base64 encoded xml
616 $base64_configuration =~s/\n//g; # base64 encoded xml
361 617
362 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook 618 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook
363 619
364 return $headerText; 620 return $headerText;
365 621
373 my $appletId = $self->{appletName}; 629 my $appletId = $self->{appletName};
374 my $appletName = $self->{appletName}; 630 my $appletName = $self->{appletName};
375 my $archive = $self->{archive}; 631 my $archive = $self->{archive};
376 my $width = $self->{width}; 632 my $width = $self->{width};
377 my $height = $self->{height}; 633 my $height = $self->{height};
634 my $applet_bgcolor = $self->{bgcolor};
378 my $javaParameters = ''; 635 my $javaParameters = '';
379 my $flashParameters = ''; 636 my $flashParameters = '';
380 my %param_hash = %{$self->params()}; 637 my %param_hash = %{$self->params()};
381 foreach my $key (keys %param_hash) { 638 foreach my $key (keys %param_hash) {
382 $javaParameters .= qq!<param name ="$key" value = "$param_hash{$key}">\n!; 639 $javaParameters .= qq!<param name ="$key" value = "$param_hash{$key}">\n!;
387 644
388 $objectText = $self->{objectText}; 645 $objectText = $self->{objectText};
389 $objectText =~ s/(\$\w+)/$1/gee; 646 $objectText =~ s/(\$\w+)/$1/gee;
390 return $objectText; 647 return $objectText;
391} 648}
392sub initialize { 649# sub initialize {
393 my $self = shift; 650# my $self = shift;
394 return q{ 651# return q{
395 <script> 652# <script>
396 initializeAction(); 653# initializeAllApplets();
397 // this should really be done in the <body> tag 654# // this should really be done in the <body> tag
398 </script> 655# </script>
399 }; 656# };
400 657#
401} 658# }
402######################################################## 659########################################################
403# HEADER material for one flash or java applet 660# HEADER material for one flash or java applet
404######################################################## 661########################################################
405 662
406use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT'; 663use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT';
407 664 <script src="/webwork2_files/js/Base64.js" language="javascript">
665 </script>
666 <script src="/webwork2_files/js/ww_applet_support.js" language="javascript">
667 //upload functions stored in /opt/webwork/webwork2/htdocs/js ...
668
669 </script>
408 <script language="JavaScript"> 670 <script language="JavaScript">
409 671
410 // set debug mode for this applet 672
411 set_debug($debugMode); 673
412
413 ////////////////////////////////////////////////////////// 674 //////////////////////////////////////////////////////////
414 //CONFIGURATIONS 675 //TEST code
415 // 676 //
416 // configurations are "permanent" 677 //
417 ////////////////////////////////////////////////////////// 678 //////////////////////////////////////////////////////////
679
680 ww_applet_list["$appletName"] = new ww_applet("$appletName");
418 681
419 applet_config_list["$appletName"] = function() {
420 debug_add("applet_config_list:\n attempt to configure $appletName . $config ( $base64_config ) if config function is defined: "
421 );
422 try {
423 if (( typeof(getApplet("$appletName").$config) == "function" ) ) {
424 debug_add("CONFIGURE $appletName");
425 getApplet("$appletName").$config(Base64.decode("$base64_config"));
426 }
427 } catch(e) {
428 alert("Error executing configuration command $config for $appletName: " + e );
429 }
430 }
431 ////////////////////////////////////////////////////////////
432 //
433 //STATE:
434 // state can vary as the applet is manipulated -- it is reset from the questions _state values
435 //
436 //////////////////////////////////////////////////////////
437 682
438 applet_setState_list["$appletName"] = function(state) { 683 ww_applet_list["$appletName"].code = "$code";
439 debug_add("Begin setState for applet $appletName"); 684 ww_applet_list["$appletName"].codebase = "$codebase";
440 debug_add("Obtain state from $appletName"+"_state"); 685 ww_applet_list["$appletName"].appletID = "$appletID";
441 state = state || getQE("$appletName"+"_state").value; 686 ww_applet_list["$appletName"].base64_state = "$base64_initializationState";
442 if ( base64Q(state) ) { 687 ww_applet_list["$appletName"].initialState = Base64.decode("$base64_initialState");
443 state=Base64.decode(state); 688 ww_applet_list["$appletName"].configuration = Base64.decode("$base64_configuration");;
444 } 689 ww_applet_list["$appletName"].getStateAlias = "$getStateAlias";
445 if (state.match(/<xml/i) || state.match(/<?xml/i) ) { // if state starts with <?xml 690 ww_applet_list["$appletName"].setStateAlias = "$setStateAlias";
446 691 ww_applet_list["$appletName"].setConfigAlias = "$setConfigAlias";
447 debug_add("applet_setState_list: \n set (decoded) state for $appletName to " + 692 ww_applet_list["$appletName"].getConfigAlias = "$getConfigAlias";
448 state +"\nfunction type is " +typeof(getApplet("$appletName").$setState) 693 ww_applet_list["$appletName"].initializeActionAlias = "$initializeActionAlias";
449 ); 694 ww_applet_list["$appletName"].submitActionAlias = "$submitActionAlias";
450 try { 695 ww_applet_list["$appletName"].submitActionScript = Base64.decode("$base64_submitActionScript");
451 if (( typeof(getApplet("$appletName").$setState) =="function" ) ) { 696 ww_applet_list["$appletName"].answerBoxAlias = "$answerBoxAlias";
452 debug_add("setState for $appletName"); 697 ww_applet_list["$appletName"].maxInitializationAttempts = $maxInitializationAttempts;
453 getApplet("$appletName").$setState( state ); 698 ww_applet_list["$appletName"].debugMode = "$debugMode";
454 }
455 } catch(e) {
456 alert("Error in setting state of $appletName using command $setState : " + e );
457 }
458 } else if (debug) {
459 alert("new state was empty string or did not begin with <xml-- state was not reset");
460 }
461 };
462 applet_getState_list["$appletName"] = function () {
463 debug_add("get current state for applet $appletName and store it in $appletName"+"_state");
464 var applet = getApplet("$appletName");
465 try {
466 if (( typeof(applet.$getState) == "function" ) ) { // there may be no state function
467 state = applet.$getState(); // get state in xml format
468 }
469
470 if (!debug) {state = Base64.encode(state) }; // replace state by encoded version unless in debug mode
471 debug_add("state is "+state); // this should still be in plain text
472 getQE("$appletName"+"_state").value = state; //place state in input item (debug: textarea, otherwise: hidden)
473 } catch (e) {
474 alert("Error in getting state for $appletName " + e );
475 }
476 };
477
478 ////////////////////////////////////////////////////////////
479 //
480 //INITIALIZE
481 //
482 ////////////////////////////////////////////////////////////
483
484 699
485 applet_checkLoaded_list["$appletName"] = function() { // this function returns 0 unless:
486 // applet has already been flagged as ready in applet_isReady_list
487 // applet.config is defined (or alias for .config)
488 // applet.setState is defined
489 // applet.isActive is defined
490 // applet reported that it is loaded by calling loadQ()
491 var ready = 0;
492 var applet = getApplet("$appletName");
493 if (!debug && applet_isReady_list["$appletName"]) {return(1)}; // memorize readiness in non-debug mode
494 if ( typeof(applet.$config) == "function") {
495 debug_add( "applet.config is " + typeof(applet.$config) );
496 ready = 1;
497 }
498 if( typeof(applet.$getState) == "function") {
499 debug_add( "applet.getState is " + typeof(applet.$getState) );
500 ready =1;
501 }
502 if (typeof(applet.isActive) == "function" && applet.isActive ) {
503 debug_add( "applet.isActive is " + typeof(applet.isActive) );
504 ready =1;
505 }
506 if (typeof(applet_reportsLoaded_list["$appletName"]) !="undefined" && applet_reportsLoaded_list["$appletName"] != 0 ) {
507 debug_add( "applet reports that it is loaded " + applet_reportsLoaded_list["$appletName"] );
508 ready =1;
509 }
510 applet_isReady_list["$appletName"]= ready;
511 return(ready);
512 }
513
514 applet_initializeAction_list["$appletName"] = function (state) {
515 applet_setState_list["$appletName"](state);
516 };
517
518 applet_submitAction_list["$appletName"] = function () {
519 if (! applet_isReady_list["$appletName"] ) {
520 alert("$appletName is not ready");
521 }
522 applet_getState_list["$appletName"]();
523 //getQE("$returnFieldName").value = getApplet("$appletName").sendData(); //FIXME -- not needed in general?
524 };
525 </script> 700 </script>
526 701
527END_HEADER_SCRIPT 702END_HEADER_SCRIPT
528 703
529package FlashApplet; 704package FlashApplet;
530@ISA = qw(Applet); 705@ISA = qw(Applet);
531 706
532 707
708=head2 Insertion HTML code for FlashApplet
533 709
534=pod 710=pod
535 711
536The secret to making this applet work with IE in addition to normal browsers 712The secret to making this applet work with IE in addition to normal browsers
537is the addition of the C(<form></form>) construct just before the object. 713is the addition of the C(<form></form>) construct just before the object.
550 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 726 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
551 id="$appletName" width="500" height="375" 727 id="$appletName" width="500" height="375"
552 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> 728 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
553 <param name="movie" value="$codebase/$appletName.swf" /> 729 <param name="movie" value="$codebase/$appletName.swf" />
554 <param name="quality" value="high" /> 730 <param name="quality" value="high" />
555 <param name="bgcolor" value="#869ca7" /> 731 <param name="bgcolor" value="$applet_bgcolor" />
556 <param name="allowScriptAccess" value="sameDomain" /> 732 <param name="allowScriptAccess" value="sameDomain" />
557 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" 733 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="$applet_bgcolor"
558 width="$width" height="$height" name="$appletName" align="middle" id="$appletName" 734 width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
559 play="true" loop="false" quality="high" allowScriptAccess="sameDomain" 735 play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
560 type="application/x-shockwave-flash" 736 type="application/x-shockwave-flash"
561 pluginspage="http://www.macromedia.com/go/getflashplayer"> 737 pluginspage="http://www.macromedia.com/go/getflashplayer">
562 </embed> 738 </embed>
572 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 748 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
573 id="$appletName" width="500" height="375" 749 id="$appletName" width="500" height="375"
574 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> 750 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
575 <param name="movie" value="$codebase/$appletName.swf" /> 751 <param name="movie" value="$codebase/$appletName.swf" />
576 <param name="quality" value="high" /> 752 <param name="quality" value="high" />
577 <param name="bgcolor" value="#869ca7" /> 753 <param name="bgcolor" value="$applet_bgcolor" />
578 <param name="allowScriptAccess" value="sameDomain" /> 754 <param name="allowScriptAccess" value="sameDomain" />
579 <param name="FlashVars" value="$flashParameters"/> 755 <param name="FlashVars" value="$flashParameters"/>
580 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" 756 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="$applet_bgcolor"
581 width="$width" height="$height" name="$appletName" align="middle" id="$appletName" 757 width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
582 play="true" loop="false" quality="high" allowScriptAccess="sameDomain" 758 play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
583 type="application/x-shockwave-flash" 759 type="application/x-shockwave-flash"
584 pluginspage="http://www.macromedia.com/go/getflashplayer" 760 pluginspage="http://www.macromedia.com/go/getflashplayer"
585 FlashVars="$flashParameters"> 761 FlashVars="$flashParameters">
598 774
599 775
600package JavaApplet; 776package JavaApplet;
601@ISA = qw(Applet); 777@ISA = qw(Applet);
602 778
603 779=head2 Insertion HTML code for JavaApplet
604 780
605=pod 781=pod
606 782
607The secret to making this applet work with IE in addition to normal browsers 783The secret to making this applet work with IE in addition to normal browsers
608is the addition of the C(<form></form>) construct just before the object. 784is the addition of the C(<form></form>) construct just before the object.
642 archive = "$archive" 818 archive = "$archive"
643 name = "$appletName" 819 name = "$appletName"
644 id = "$appletName" 820 id = "$appletName"
645 width = "$width" 821 width = "$width"
646 height = "$height" 822 height = "$height"
823 bgcolor = "$applet_bgcolor"
647 MAYSCRIPT 824 MAYSCRIPT
648 > 825 >
649 $javaParameters 826 $javaParameters
650 827
651 Sorry, the Applet could not be started. Please make sure that 828 Sorry, the Applet could not be started. Please make sure that

Legend:
Removed from v.5667  
changed lines
  Added in v.6030

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9