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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6453 - (view) (download) (as text)

1 : gage 5574 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 6058 # $CVSHeader$
5 : gage 5574 #
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 : gage 6026 $appletName = "PointGraph";
27 :     $applet = FlashApplet(
28 :     codebase => findAppletCodebase("$appletName.swf"),
29 :     appletName => $appletName,
30 :     appletId => $appletName,
31 :     setStateAlias => 'setXML',
32 :     getStateAlias => 'getXML',
33 :     setConfigAlias => 'config',
34 : gage 6030 answerBoxAlias => 'answerBox',
35 :     submitActionScript => qq{ getQE('answerBox').value = getApplet("$appletName").getAnswer() },
36 : gage 6026 );
37 : gage 5574
38 : gage 6026 ###################################
39 :     # Configure applet
40 :     ###################################
41 : gage 5574
42 : gage 6026 #data to set up the equation
43 : gage 6036 $applet->configuration(qq{<XML expr='(x - $a)^3 + $b/$a * x' />});
44 : gage 6026 # initial points
45 : gage 6036 $applet->intialState(qq{<XML> </XML>});
46 : gage 6026 ###################################
47 :     #insert applet into body
48 :     ###################################
49 :    
50 :     TEXT( MODES(TeX=>'object code', HTML=>$applet->insertAll(
51 : gage 6036 includeAnswerBox => 1
52 :     debug=>0,
53 :     reinitialize_button=>1,
54 : gage 6026 )));
55 :    
56 :    
57 : gage 5574 =head1 DESCRIPTION
58 :    
59 :     This file provides an object to store in one place
60 :     all of the information needed to call an applet.
61 :    
62 :     The object FlashApplet has defaults for inserting flash applets.
63 :    
64 :     =over
65 :    
66 :     =item *
67 :    
68 :     =item *
69 :    
70 :     =back
71 :    
72 :     (not yet completed)
73 :    
74 :     The module JavaApplet has defaults for inserting java applets.
75 :    
76 : gage 5619 The module Applet stores common code for the two types of applet.
77 : gage 5574
78 :     =head1 USAGE
79 :    
80 : gage 5619 These modules are activate by listing it in the modules section of global.conf and rebooting the server.
81 :     The companion file to this one is macros/AppletObjects.pl
82 : gage 5574
83 : gage 5619 qw(Applet FlashApplet JavaApplet)
84 :    
85 : gage 5574 =cut
86 :    
87 :    
88 :    
89 :     package Applet;
90 :    
91 : gage 5624 use URI::Escape;
92 : gage 5574
93 :    
94 :    
95 :     use MIME::Base64 qw( encode_base64 decode_base64);
96 :    
97 : gage 5594
98 :     =head2 Default javaScript functions placed in header
99 :    
100 : gage 5944 =pod
101 :    
102 : gage 5594 These functions are automatically defined for use for
103 :     any javaScript placed in the text of a PG question.
104 :    
105 : gage 6036 getApplet(appletName) -- finds the applet path in the DOM
106 : gage 5594
107 : gage 5944 submitAction() -- calls the submit action of the applets
108 :    
109 : gage 6021 initializeWWquestion() -- calls the initialize action of the applets
110 : gage 5594
111 :     getQE(name) -- gets an HTML element of the question by name
112 :     or by id. Be sure to keep all names and ids
113 :     unique within a given PG question.
114 : gage 5944
115 : gage 5594 getQuestionElement(name) -- long form of getQE(name)
116 : gage 5944
117 : gage 5594 listQuestionElements() -- for discovering the names of inputs in the
118 :     PG question. An alert dialog will list all
119 :     of the elements.
120 : gage 6036
121 :     Usage: Place this at the END of the question, just before END_DOCUMENT():
122 : gage 5594
123 : gage 5944 TEXT(qq!<script> listQuestionElements() </script>!);
124 :     ENDDOCUMENT();
125 :     to obtain a list of all of the HTML elements in the question
126 : gage 6021
127 :     ----------------------------------------------------------------------------
128 :    
129 :    
130 : gage 5944 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 :     );
140 : gage 5594
141 :    
142 : gage 6036 appletId for simplicity and reliability appletId and appletName are always the same
143 : gage 5944 appletName
144 :     archive the name of the .jar file containing the applet code
145 :     code the name of the applet code in the .jar archive
146 :     codebase a prefix url used to find the archive and the applet itself
147 : gage 6036
148 : gage 5944 params an anonymous array containing name/value pairs
149 :     to configure the applet [name =>'value, ...]
150 : gage 6036
151 :     width rectangle alloted in the html page for displaying the applet
152 :     height
153 : gage 5944
154 : gage 6036 bgcolor background color of the applet rectangle
155 :    
156 : gage 5944 header stores the text to be added to the header section of the html page
157 : gage 5619 object stores the text which places the applet on the html page
158 : gage 5594
159 : gage 5944
160 : gage 6030 configuration configuration contains those customizable attributes of the applet which don't
161 : gage 5944 change as it is used. When stored in hidden answer fields
162 :     it is usually stored in base64 encoded format.
163 : gage 6021 initialState the state consists of those customizable attributes of the applet which change
164 :     as the applet is used by the student. It is stored by the calling .pg question so that
165 : gage 6036 when revisiting the question the applet will be restored to the same state it was
166 :     left in when the question was last viewed.
167 :    
168 : gage 5944 getStateAlias (default: getState) alias for command called to read the current state of the applet.
169 :     The state is passed in plain text xml format with outer tags: <xml>....</xml>
170 :     setStateAlias (default: setState) alias for the command called to reset the state of the applet.
171 :     The state is passed in plain text in xml format with outer tags: <xml>....</xml>
172 :    
173 : gage 6036 configAlias (deprecated) -- a synonym for configAlias
174 :    
175 :     getConfigAlias (default: getConfig) -- retrieves the configuration from the applet. This is used
176 :     mainly for debugging. In principal the configuration remains the same for a given instance
177 :     of the applet -- i.e. for the homework question for a single student. The state however
178 :     will change depending on the interactions between the student and the applet.
179 :     setConfigAlias (default: setConfig ) names the applet command called with the contents of $self->config
180 :     to configure the applet. The parameters are passed to the applet in plain text using <xml>
181 :     The outer tags must be <xml> ..... </xml>
182 :    
183 :    
184 :     initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called
185 :     to initialize the applet (some overlap with config/ and setState
186 :     maxInitializationAttempts -- (default: 5) number attempts to test applet to see if it is installed.
187 :     If isActive() exists then the WW question waits until the return value is 1 before
188 :     calling the applet's confguration commands.
189 :     Because some applets have isActive return 0 even when they are ready,
190 :     if isActive() exists but does not return 1 then the applet's configuration commands
191 :     are called after maxInitializationAttempts number of times. If none of the configuration commands
192 :     of the applet can be detected then the WW question gives up after maxInitializationAttempts.
193 :    
194 :     submitActionAlias -- (default: getXML) applet subroutine called when the submit button of the
195 : gage 5619 .pg question is pressed.
196 : gage 6036 submitActionScript -- (default: qq{ getQE('answerBox').value = getApplet("$appletName").getAnswer() },
197 :    
198 : gage 6026 answerBoxAlias -- name of answer box to return answer to: default defaultAnswerBox
199 :     returnFieldName -- (deprecated) synonmym for answerBoxAlias
200 : gage 6036
201 :    
202 :     debugMode (default: 0) for debugMode==1 the answerBox and the box preserving the applet state
203 :     between questions are made visible along with some buttons for manually getting the state of
204 :     the applet and setting the state of the applet.
205 :    
206 :     for debugMode==2, in addition to the answerBox and stateBox there are several alerts
207 :     which mark progress through the procedures of calling the applet. Useful for troubleshooting
208 :     where in the chain of command a communication failure occurs
209 : gage 5594
210 : gage 6036
211 :     Methods:
212 :    
213 :     insertHeader -- inserts text in header section of HTML page
214 :     insertObject -- inserts <object></object> or <applet></applet> tag in body of the HTML page
215 :     insertAll -- (defined in AppletObjects.pl) installs applet by inserting both header text and the object text
216 :     Usage: $applet->insertAll(
217 :     includeAnswerBox => 0,
218 :     debugMode => 0,
219 :     reinitialize_button =>0,
220 :     );
221 : gage 5578
222 : gage 6036
223 : gage 5583 =cut
224 :    
225 : gage 6021 =head4 More details
226 : gage 5574
227 : gage 6021 There are three different "images" of the applet. The first is the java or flash applet itself. The object that actually does the work.
228 :     The second is a perl image of the applet -- henceforth the perlApplet -- which is configured in the .pg file and allows a WeBWorK question
229 :     to communicate with the applet. The third image is a javaScript image of the applet -- henceforth the jsApplet which is a mirror of the perlApplet
230 :     but 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
231 :     the jsApplet as a runtime version of the perlApplet since it can be accessed and modified after the virtual HTML page has been created by
232 :     the PG rendering process.
233 : gage 5574
234 : gage 6021 The perlApplet is initialized by $newApplet = new flashApplet( appletName=>'myApplet',..... ); The jsApplet is automatically defined in
235 :     ww_applet_list["myApplet"] by copying the instance variables of $newApplet to a corresponding javaScript object. So $newApplet->{appletName}
236 :     corresponds to ww_applet_list["myApplet"].appletName. (This paragraph is not yet fully implemented :-().
237 : gage 5574
238 : gage 6021 Currently 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
239 : gage 6026 to a base64 constant and then converted back to text form when it is read by a javaScript subroutine.
240 : gage 6021
241 : gage 6026 The perlApplet has methods that help place the jsApplet code on the HTML page and create the link to the applet itself.
242 :     In particular instance variables such as "setStateAlias", "getStateAlias" connect the WW default of "setState" to subroutine
243 :     name chosen by the applet designer. The aim is to make it easier to connect to applets previously designed to work
244 :     with javaScript in an HTML page or other systems.
245 :    
246 :    
247 :     The jsApplet acts as an intermediary for commands directed at the applet.
248 :     It is not necessary for the minimal operations of
249 :     configuring the applet and maintaining
250 :     state from one viewing of the WW question to address the applet directly.
251 :     The methods such as "setState", "getState", "setConfig" which are part of the jsApplet
252 :     take care of the book keeping details.
253 :     It is also possible to make direct calls to the applet from handcrafted javaScript subroutines,
254 :     but it may be convenient to store these as additional methods in the
255 :     jsApplet.
256 :    
257 : gage 6021 =cut
258 :    
259 : gage 6026 =head4 Detecting that the applet is ready
260 :    
261 :     Timing issues are among the pitfalls awaiting when using flash or java applets in WW questions. It is important that the WW question
262 :     does not issue any commands to the applet until the applet is fully loaded, including the uploading of any additional configuration
263 :     information from XML files. This can be tricky since the timing issues usually don't arise when initiating the applet from an HTML page.
264 :    
265 :     The WW API performs the following actions to determine if the applet is loaded:
266 :    
267 :     check the ww_applet_list[appletName].isReady flag (1== applet is ready)
268 :     -- this caches the readiness information so that it doesn't
269 :     have to be repeated within a given viewing of a WW question
270 :     If this is 1 then the applet is ready.
271 :     determine whether the applet's isActive subroutine is defined AND returns 1 when called.
272 :     -- if the return value is 1 the applet is ready, if it is zero or no response then the applet is NOT ready
273 :     -- If the applet has an isActive() subroutine -- there is no alias for this --
274 :     then it must return 1 as soon as the applet is ready. Otherwise
275 :     the applet will timeout.
276 :     determine whether the applet's setConfig subroutine is defined.
277 :     -- applet.{setConfigAlias}.
278 :     determine whether the applet's setState subroutine is defined.
279 :     determine whether the jsApplets ww_applet_list[appletName].reportsLoaded flag is set to 1
280 :     -- this can be set by the applet if it calls the javaScript function
281 :     "applet_loaded(appletName, loaded_status). The loaded_status is 1 or 0
282 :    
283 :     Logic for determining applet status: if any one of the above checks succeeds (or returns 1) then the applet is
284 :     consdered to be ready UNLESS the isActive() exists and the call returns a 0 or no response. In this case
285 :     the applet is assumed to be loading additional data and is not yet ready.
286 :    
287 :     For this reason if the isActive subroutine
288 :     is defined in the applet it must return a 1 once the applet is prepared to accept additional commands.
289 :     (Since there are some extent flashApplets with non-functioning isActive() subroutines a temporary workaround
290 :     assuems that after C<maxInitializationAttempts> -- 5 by default -- the applet is in fact ready but the
291 :     isActive() subroutine is non functioning. This can give rise to false "readiness" signals if the applet
292 :     takes a long time to load auxiliary files.)
293 :    
294 :     The applet itself can take measures to insure that the setConfig subroutine is prepared to respond immediately once the applet is loaded.
295 :     It can include timers that delay execution of the configuring actions until all of the auxiliary files needed by the applet are loaded.
296 :    
297 :    
298 :     =cut
299 :    
300 :    
301 :    
302 :    
303 : gage 6036 =head4 Instance variables in the javaScript applet ww_applet_list[appletName]
304 : gage 6026
305 : gage 6036 Most of the instance variables in the perl version of the applet are transferred to the javaScript applet
306 :    
307 : gage 6026 =cut
308 :    
309 :    
310 : gage 6036 =head4 Methods defined for the javaScript applet ww_applet_list[appletName]
311 :    
312 : gage 6026 This is not a comprehensive list
313 :    
314 :     setConfig -- transmits the information for configuring the applet
315 :    
316 :     getConfig -- retrieves the configuration information -- this is used mainly for debugging and may not be defined in most applets
317 :    
318 :    
319 :     setState -- sets the current state (1) from the appletName_state HTML element if this contains an <xml>...</xml> string
320 :     -- if the value contains <xml>restart_applet</xml> then set the current state to ww_applet_list[appletName].initialState
321 :     -- if the value is a blank string set the current state to ww_applet_list[appletName].initialState
322 :    
323 :    
324 :     getState -- retrieves the current state and stores in the appletName_state HTML element.
325 :    
326 :    
327 : gage 6036 =cut
328 : gage 6026
329 : gage 6021 =head4 Requirements for applets
330 :    
331 :     The following methods are desirable in an applet that preserves state in a WW question. None of them are required.
332 :    
333 :     setState(str) (default: setXML)
334 :     -- set the current state of the applet from an xml string
335 :     -- should be able to accept an empty string or a string of
336 :     the form <XML>.....</XML> without creating errors
337 :     -- can be designed to receive other forms of input if it is
338 :     coordinated with the WW question.
339 :     getState() (default: getXML)
340 :     -- return the current state of the applet in an xml string.
341 :     -- an empty string or a string of the form <XML>.....</XML>
342 :     are the standard responses.
343 :     -- can be designed to return other strings if it is
344 :     coordinated with the WW question.
345 :     setConfig(str) (default: setConfig)
346 :     -- If the applet allows configuration this configures the applet
347 :     from an xml string
348 :     -- should be able to accept an empty string or a string of the
349 :     form <XML>.....</XML> without creating errors
350 :     -- can be designed to receive other forms of input if it is
351 :     coordinated with the WW question.
352 :     getConfig (default: getConfig)
353 :     -- This returns a string defining the configuration of the
354 :     applet in an xml string
355 :     -- an empty string or a string of the form <XML>.....</XML>
356 :     are the standard responses.
357 :     -- can be designed to return other strings if it is
358 :     coordinated with the WW question.
359 :     -- this method is used for debugging to ensure that
360 :     the configuration was set as expected.
361 :     getAnswer (default: getAnswer)
362 :     -- Returns a string (usually NOT xml) which is the
363 : gage 6026 response that the student is effectvely submitting to answer
364 : gage 6021 the WW question.
365 :    
366 :    
367 :     =cut
368 :    
369 : gage 6036 =head4 Initialization sequence
370 :    
371 :     When the WW question is loaded the C<initializeWWquestion> javaScript subroutine calls each of the applets used in the question asking them
372 :     to initialize themselves.
373 :    
374 :     The applets initialization method is as follows:
375 :    
376 :     -- wait until the applet is loaded and the applet has loaded all of its auxiliary files.
377 :     -- set the debugMode in the applet
378 :     -- call the setConfig method in the javaScript applet -- (configuration parameters are "permanent" for the life of the applet
379 :     -- call the setInitialization method in the javaScript applet -- this often calls the setState method in the applet
380 :    
381 :     =cut
382 :    
383 :    
384 :     =head Submit sequence
385 :    
386 :     When the WW question submit button is pressed the form containing the WW question calles the javaScript "submitAction()" which then asks
387 :     each of the applets on the page to perform its submit action which consists of
388 :    
389 :     -- if the applet is to be reinitialized (appletName_state contains <xml>restart_applet</xml>) then
390 :     the HTML elements appletName_state and previous_appletName_state are set to <xml>restart_applet</xml>
391 :     to be interpreted by the next setState command
392 :     -- Otherwise getState() from the applet and save it to the HTML input element appletName_state
393 :     -- Perform the javaScript commands in .submitActionScript (default: '' )
394 :     a typical submitActionScript looks like getQE(this.answerBox).value = getApplet(appletName).getAnswer() )
395 :    
396 :     =cut
397 :    
398 :    
399 : gage 5574 sub new {
400 :     my $class = shift;
401 :     my $self = {
402 : gage 6036 appletName => '',
403 : gage 6308 appletId => '', #always use identical applet Id's and applet Names
404 : gage 6036 archive => '',
405 :     code => '',
406 :     codebase => '',
407 : gage 5574 params =>undef,
408 : gage 5619 width => 550,
409 :     height => 400,
410 : gage 6009 bgcolor => "#869ca7",
411 : gage 6368 type => '',
412 :     visible => 0,
413 : gage 6030 configuration => '', # configuration defining the applet
414 :     initialState => '', # initial state.
415 : gage 5619 getStateAlias => 'getXML',
416 : gage 5625 setStateAlias => 'setXML',
417 : gage 6023 configAlias => '', # deprecated
418 : gage 6021 getConfigAlias => 'getConfig',
419 :     setConfigAlias => 'setConfig',
420 : gage 5619 initializeActionAlias => 'setXML',
421 : gage 6026 maxInitializationAttempts => 5, # number of attempts to initialize applet
422 : gage 5619 submitActionAlias => 'getXML',
423 : gage 6023 submitActionScript => '', # script executed on submitting the WW question
424 :     answerBoxAlias => 'answerBox',
425 :     answerBox => '', # deprecated
426 :     returnFieldName => '', # deprecated
427 : gage 5619 headerText => DEFAULT_HEADER_TEXT(),
428 :     objectText => '',
429 : gage 6026 debugMode => 0,
430 : gage 6368 selfLoading => 0,
431 : gage 5574 @_,
432 :     };
433 :     bless $self, $class;
434 : gage 6021 $self->initialState('<xml></xml>');
435 : gage 6025 if ($self->{returnFieldName} or $self->{answerBox} ) { # backward compatibility
436 : gage 6023 warn "use answerBoxAlias instead of returnFieldName or answerBox";
437 :     $self->{answerBox}='';
438 :     $self->{returnFieldName}='';
439 :     }
440 : gage 6021 if ($self->{configAlias}) { # backward compatibility
441 :     warn "use setConfigAlias instead of configAlias";
442 :     $self->{configAlias}='';
443 :     }
444 : gage 6030 $self->configuration('<xml></xml>');
445 : gage 5574 return $self;
446 :     }
447 : gage 6036 sub appletId {
448 :     appletName(@_);
449 :     }
450 :     sub appletName {
451 :     my $self = shift;
452 :     $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty
453 :     $self->{appletName};
454 :     }
455 :     sub archive {
456 :     my $self = shift;
457 :     $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty
458 :     $self->{archive};
459 :     }
460 :     sub code {
461 :     my $self = shift;
462 :     $self->{code} = shift ||$self->{code}; # replace the current code if non-empty
463 :     $self->{code};
464 :     }
465 :     sub codebase {
466 :     my $self = shift;
467 :     $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty
468 :     $self->{codebase};
469 :     }
470 :     sub params {
471 :     my $self = shift;
472 :     if (ref($_[0]) =~/HASH/) {
473 :     $self->{params} = shift;
474 :     } elsif ( !defined($_[0]) or $_[0] =~ '') {
475 :     # do nothing (read)
476 :     } else {
477 :     warn "You must enter a reference to a hash for the parameter list";
478 :     }
479 :     $self->{params};
480 :     }
481 : gage 5574
482 : gage 6036 sub width {
483 :     my $self = shift;
484 :     $self->{width} = shift ||$self->{width}; # replace the current width if non-empty
485 :     $self->{width};
486 :     }
487 :     sub height {
488 :     my $self = shift;
489 :     $self->{height} = shift ||$self->{height}; # replace the current height if non-empty
490 :     $self->{height};
491 :     }
492 :     sub bgcolor {
493 :     my $self = shift;
494 :     $self->{bgcolor} = shift ||$self->{bgcolor}; # replace the current background color if non-empty
495 :     $self->{bgcolor};
496 :     }
497 :    
498 : gage 5574 sub header {
499 :     my $self = shift;
500 :     if ($_[0] eq "reset") { # $applet->header('reset'); erases default header text.
501 :     $self->{headerText}='';
502 :     } else {
503 :     $self->{headerText} .= join("",@_); # $applet->header(new_text); concatenates new_text to existing header.
504 :     }
505 :     $self->{headerText};
506 :     }
507 :     sub object {
508 :     my $self = shift;
509 :     if ($_[0] eq "reset") {
510 :     $self->{objectText}='';
511 :     } else {
512 :     $self->{objectText} .= join("",@_);
513 :     }
514 :     $self->{objectText};
515 :     }
516 : gage 6036 sub configuration {
517 : gage 5574 my $self = shift;
518 : gage 6036 my $str = shift;
519 :     $self->{configuration} = $str || $self->{configuration}; # replace the current string if non-empty
520 :     $self->{configuration} =~ s/\n//g;
521 :     $self->{configuration};
522 : gage 5574 }
523 : gage 6036
524 :     sub initialState {
525 : gage 5574 my $self = shift;
526 : gage 6036 my $str = shift;
527 :     $self->{initialState} = $str ||$self->{initialState}; # replace the current string if non-empty
528 :     $self->{initialState};
529 : gage 5574 }
530 :    
531 : gage 5619 sub getStateAlias {
532 :     my $self = shift;
533 :     $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty
534 :     $self->{getStateAlias};
535 :     }
536 :    
537 :     sub setStateAlias {
538 :     my $self = shift;
539 :     $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty
540 :     $self->{setStateAlias};
541 :     }
542 : gage 6036
543 :     sub getConfigAlias {
544 : gage 5619 my $self = shift;
545 : gage 6036 $self->{getConfigAlias} = shift ||$self->{getConfigAlias}; # replace the current contents if non-empty
546 :     $self->{getConfigAlias};
547 : gage 5619 }
548 : gage 6021 sub setConfigAlias {
549 : gage 5574 my $self = shift;
550 : gage 6021 $self->{setConfigAlias} = shift ||$self->{setConfigAlias}; # replace the current contents if non-empty
551 :     $self->{setConfigAlias};
552 : gage 5574 }
553 : gage 6021
554 : gage 6036 sub initializeActionAlias {
555 : gage 6021 my $self = shift;
556 : gage 6036 $self->{initializeActionAlias} = shift ||$self->{initializeActionAlias}; # replace the current contents if non-empty
557 :     $self->{initializeActionAlias};
558 : gage 5994 }
559 : gage 6036 sub maxInitializationAttempts {
560 : gage 5574 my $self = shift;
561 : gage 6036 $self->{maxInitializationAttempts} = shift || $self->{maxInitializationAttempts};
562 :     $self->{maxInitializationAttempts};
563 :     }
564 :     sub submitActionAlias {
565 : gage 5619 my $self = shift;
566 : gage 6036 $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty
567 :     $self->{submitActionAlias};
568 : gage 5619 }
569 : gage 6036 sub submitActionScript {
570 : gage 5619 my $self = shift;
571 : gage 6036 $self->{submitActionScript} = shift ||$self->{submitActionScript}; # replace the current contents if non-empty
572 :     $self->{submitActionScript};
573 : gage 5619 }
574 : gage 6036
575 :     sub answerBoxAlias {
576 : gage 5619 my $self = shift;
577 : gage 6036 $self->{answerBox} = shift ||$self->{answerBox}; # replace the current contents if non-empty
578 :     $self->{answerBox};
579 : gage 5619 }
580 : gage 6036
581 : gage 6026 sub debugMode {
582 : gage 5574 my $self = shift;
583 : gage 5619 my $new_flag = shift;
584 : gage 6026 $self->{debugMode} = $new_flag if defined($new_flag);
585 :     $self->{debugMode};
586 : gage 5574 }
587 : gage 6030
588 : gage 6036
589 : gage 6021 #######################
590 :     # soon to be deprecated?
591 :     #######################
592 : gage 6030
593 :     sub config {
594 :     my $self = shift;
595 :     my $str = shift;
596 :     warn "use $self->configuration instead of $self->config. Internally this string is ascii, not base64 encoded", join(' ', caller());
597 :     # $self->{base64_config} = encode_base64($str) || $self->{base64_config}; # replace the current string if non-empty
598 :     # $self->{base64_config} =~ s/\n//g;
599 :     # decode_base64($self->{base64_config});
600 :     }
601 : gage 6346 sub state { #deprecated
602 : gage 5574 my $self = shift;
603 :     my $str = shift;
604 : gage 6030 warn "use $self->initialState instead of $self->state. Internally this string is ascii, not base64 encoded", join(' ', caller());
605 :     # $self->{base64_state} = encode_base64($str) ||$self->{base64_state}; # replace the current string if non-empty
606 :     # $self->{base64_state} =~ s/\n//g;
607 :     # decode_base64($self->{base64_state});
608 : gage 5574 }
609 : gage 5619 sub base64_state{
610 : gage 5574 my $self = shift;
611 : gage 6030 warn "use $self->InitialState instead of $self->state. Internally this string is ascii, not base64 encoded", join(' ', caller());
612 :    
613 :    
614 : gage 5574 }
615 : gage 6021
616 : gage 5619 sub base64_config {
617 :     my $self = shift;
618 : gage 6030 warn "use $self->configuration instead of $self->config. Internally this string is ascii, not base64 encoded";
619 : gage 5619 }
620 : gage 6021
621 :     sub returnFieldName {
622 :     my $self = shift;
623 :     warn "use answerBoxName instead of returnFieldName";
624 :     }
625 :     sub answerBox {
626 :     my $self = shift;
627 : gage 6026 warn "use answerBoxAlias instead of AnswerBox";
628 : gage 6021 }
629 : gage 6036 sub configAlias {
630 :     my $self = shift;
631 :     warn "use setConfigAlias instead of configAlias";
632 :     }
633 : gage 6021 #########################
634 : gage 5574 #FIXME
635 :     # need to be able to adjust header material
636 :    
637 :     sub insertHeader {
638 :     my $self = shift;
639 : gage 6013
640 : gage 6009 my $codebase = $self->codebase;
641 :     my $appletId = $self->appletId;
642 :     my $appletName = $self->appletName;
643 : gage 6026 my $initializeActionAlias = $self->initializeActionAlias;
644 :     my $submitActionScript = $self->submitActionScript;
645 : gage 6009 my $setStateAlias = $self->setStateAlias;
646 :     my $getStateAlias = $self->getStateAlias;
647 : gage 6021
648 :     my $setConfigAlias = $self->setConfigAlias;
649 :     my $getConfigAlias = $self->getConfigAlias;
650 : gage 6026 my $maxInitializationAttempts = $self->maxInitializationAttempts;
651 :     my $debugMode = ($self->debugMode) ? "1": "0";
652 :     my $answerBoxAlias = $self->{answerBoxAlias};
653 : gage 6346 my $onInit = $self->{onInit}; # function to indicate that applet is loaded (for geogebra: ggbOnInit
654 : gage 6009 my $headerText = $self->header();
655 : gage 6368 my $selfLoading = $self->{selfLoading};
656 : gage 5622
657 : gage 6030 #$submitActionScript =~ s/"/\\"/g; # escape quotes for ActionScript
658 : gage 6009 # other variables should not have quotes.
659 :    
660 :     $submitActionScript =~ s/\n/ /g; # replace returns with spaces -- returns in the wrong spot can cause trouble with javaScript
661 :     $submitActionScript =~ s/\r/ /g; # replace returns with spaces -- returns can cause trouble
662 : gage 6026 my $base64_submitActionScript = encode_base64($submitActionScript);
663 : gage 6030 my $base64_configuration = encode_base64($self->configuration);
664 :     my $base64_initialState = encode_base64($self->initialState);
665 :    
666 : gage 6026 $base64_submitActionScript =~s/\n//g;
667 : gage 6030 $base64_initialState =~s/\n//g; # base64 encoded xml
668 :     $base64_configuration =~s/\n//g; # base64 encoded xml
669 : gage 6009
670 : gage 5574 $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook
671 :    
672 :     return $headerText;
673 :    
674 :    
675 :     }
676 :    
677 : gage 5619
678 : gage 5661 ########################################################
679 :     # HEADER material for one flash or java applet
680 :     ########################################################
681 : gage 5574
682 : gage 5619 use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT';
683 : gage 5994 <script src="/webwork2_files/js/Base64.js" language="javascript">
684 :     </script>
685 : gage 6009 <script src="/webwork2_files/js/ww_applet_support.js" language="javascript">
686 : gage 5994 //upload functions stored in /opt/webwork/webwork2/htdocs/js ...
687 : gage 6021
688 : gage 5994 </script>
689 : gage 5619 <script language="JavaScript">
690 : gage 5661
691 : gage 6308 function getApplet(appletName) {
692 : gage 6316 var isIE = navigator.appName.indexOf("Microsoft") != -1; // ie8 uses this for java and firefox uses it for flash.
693 : gage 6308 var obj = (isIE) ? window[appletName] : window.document[appletName];
694 :     //return window.document[appletName];
695 : gage 6316 if (!obj) { obj = document.getElementById(appletName) }
696 :     if (obj ) { //RECENT FIX to ==
697 : gage 6308 return( obj );
698 :     } else {
699 :     alert ("can't find applet " + appletName);
700 :     }
701 :     }
702 : gage 5994
703 :     //////////////////////////////////////////////////////////
704 :     //TEST code
705 :     //
706 :     //
707 :     //////////////////////////////////////////////////////////
708 :    
709 : gage 6346 ww_applet_list["$appletName"] = new ww_applet("$appletName");
710 : gage 5994
711 :    
712 : gage 6346 ww_applet_list["$appletName"].code = "$code";
713 : gage 6026 ww_applet_list["$appletName"].codebase = "$codebase";
714 :     ww_applet_list["$appletName"].appletID = "$appletID";
715 : gage 6346 ww_applet_list["$appletName"].base64_state = "$base64_initialState";
716 :     ww_applet_list["$appletName"].initialState = Base64.decode("$base64_initialState");
717 :     ww_applet_list["$appletName"].configuration = Base64.decode("$base64_configuration");;
718 : gage 6026 ww_applet_list["$appletName"].getStateAlias = "$getStateAlias";
719 :     ww_applet_list["$appletName"].setStateAlias = "$setStateAlias";
720 : gage 6021 ww_applet_list["$appletName"].setConfigAlias = "$setConfigAlias";
721 :     ww_applet_list["$appletName"].getConfigAlias = "$getConfigAlias";
722 : gage 6026 ww_applet_list["$appletName"].initializeActionAlias = "$initializeActionAlias";
723 : gage 6009 ww_applet_list["$appletName"].submitActionAlias = "$submitActionAlias";
724 : gage 6030 ww_applet_list["$appletName"].submitActionScript = Base64.decode("$base64_submitActionScript");
725 : gage 6346 ww_applet_list["$appletName"].answerBoxAlias = "$answerBoxAlias";
726 : gage 6026 ww_applet_list["$appletName"].maxInitializationAttempts = $maxInitializationAttempts;
727 : gage 6346 ww_applet_list["$appletName"].debugMode = "$debugMode";
728 :     ww_applet_list["$appletName"].onInit = "$onInit";
729 : gage 5994
730 : gage 6346
731 : gage 5619 </script>
732 :    
733 :     END_HEADER_SCRIPT
734 :    
735 : gage 6036
736 :    
737 :     sub insertObject {
738 :     my $self = shift;
739 :     my $code = $self->{code};
740 :     my $codebase = $self->{codebase};
741 :     my $appletId = $self->{appletName};
742 :     my $appletName = $self->{appletName};
743 :     my $archive = $self->{archive};
744 :     my $width = $self->{width};
745 :     my $height = $self->{height};
746 :     my $applet_bgcolor = $self->{bgcolor};
747 : gage 6368 my $selfLoading = $self->{selfLoading};
748 : gage 6036 my $javaParameters = '';
749 :     my $flashParameters = '';
750 : gage 6346 if (PGcore::not_null($self->{parameter_string}) ) {
751 :     $javaParameters = $self->{parameter_string};
752 :     $flashParameters = $self->{parameter_string};
753 :     } else {
754 :     my %param_hash = %{$self->params()};
755 :     foreach my $key (keys %param_hash) {
756 :     $javaParameters .= qq!<param name ="$key" value = "$param_hash{$key}">\n!;
757 :     $flashParameters .= uri_escape($key).'='.uri_escape($param_hash{$key}).'&';
758 :     }
759 :     $flashParameters =~ s/\&$//; # trim last &
760 :     }
761 : gage 6036
762 :    
763 :     $objectText = $self->{objectText};
764 :     $objectText =~ s/(\$\w+)/$1/gee;
765 : gage 6346 $objectText .=qq{<script language="javascript">ww_applet_list["$appletName"].visible = 1;</script>}; # don't submit things if not visible
766 : gage 6036 return $objectText;
767 :     }
768 :    
769 : gage 6308
770 :     ###############################################################################################################
771 :     #
772 :     # FLASH APPLET PACKAGE
773 :     #
774 :     ###############################################################################################################
775 :    
776 : gage 5619 package FlashApplet;
777 :     @ISA = qw(Applet);
778 :    
779 :    
780 : gage 5944 =head2 Insertion HTML code for FlashApplet
781 : gage 5619
782 :     =pod
783 :    
784 :     The secret to making this applet work with IE in addition to normal browsers
785 :     is the addition of the C(<form></form>) construct just before the object.
786 :    
787 :     For some reason IE has trouble locating a flash object which is contained
788 :     within a form. Adding this second blank form with the larger problemMainForm
789 :     seems to solve the problem.
790 :    
791 :     This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2)
792 :     Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated:
793 :     http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js
794 :     http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933
795 :    
796 :     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
797 :     <form></form>
798 :     <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
799 :     id="$appletName" width="500" height="375"
800 :     codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
801 :     <param name="movie" value="$codebase/$appletName.swf" />
802 :     <param name="quality" value="high" />
803 : gage 6009 <param name="bgcolor" value="$applet_bgcolor" />
804 : gage 5619 <param name="allowScriptAccess" value="sameDomain" />
805 : gage 6009 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="$applet_bgcolor"
806 : gage 5619 width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
807 :     play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
808 :     type="application/x-shockwave-flash"
809 :     pluginspage="http://www.macromedia.com/go/getflashplayer">
810 :     </embed>
811 :    
812 :     </object>
813 :     END_OBJECT_TEXT
814 :    
815 :    
816 :     =cut
817 :    
818 :     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
819 : gage 6316
820 : gage 6346
821 : gage 6316 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
822 :     id="$appletName" width="500" height="375"
823 : gage 5619 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
824 :     <param name="movie" value="$codebase/$appletName.swf" />
825 :     <param name="quality" value="high" />
826 : gage 6009 <param name="bgcolor" value="$applet_bgcolor" />
827 : gage 5619 <param name="allowScriptAccess" value="sameDomain" />
828 : gage 5624 <param name="FlashVars" value="$flashParameters"/>
829 : gage 6009 <embed src="$codebase/$appletName.swf" quality="high" bgcolor="$applet_bgcolor"
830 : gage 6316 width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
831 : gage 5619 play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
832 :     type="application/x-shockwave-flash"
833 : gage 5624 pluginspage="http://www.macromedia.com/go/getflashplayer"
834 :     FlashVars="$flashParameters">
835 : gage 5619 </embed>
836 :    
837 :     </object>
838 :     END_OBJECT_TEXT
839 :    
840 :     sub new {
841 :     my $class = shift;
842 :     $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(),
843 : gage 6368 type => 'flash',
844 : gage 5619 @_
845 :     );
846 :    
847 :     }
848 :    
849 : gage 6308 ###############################################################################################################
850 :     #
851 :     # JAVA APPLET PACKAGE
852 :     #
853 :     ###############################################################################################################
854 : gage 5619
855 :     package JavaApplet;
856 :     @ISA = qw(Applet);
857 :    
858 : gage 5944 =head2 Insertion HTML code for JavaApplet
859 : gage 5619
860 :     =pod
861 :    
862 :     The secret to making this applet work with IE in addition to normal browsers
863 :     is the addition of the C(<form></form>) construct just before the object.
864 :    
865 :     For some reason IE has trouble locating a flash object which is contained
866 :     within a form. Adding this second blank form with the larger problemMainForm
867 :     seems to solve the problem.
868 :    
869 :     This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2)
870 :     Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated:
871 :     http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js
872 :     http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933
873 :    
874 :     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
875 :     <applet
876 :     code = "$code"
877 :     codebase = "$codebase"
878 :     archive = "$archive"
879 :     name = "$appletName"
880 :     id = "$appletName"
881 :     width = "$width"
882 :     height = "$height"
883 :     MAYSCRIPT
884 :     >
885 : gage 5624 $javaParameters
886 : gage 5619 </applet>
887 :     END_OBJECT_TEXT
888 :    
889 :     =cut
890 :    
891 : gage 6346
892 :     =pod
893 :    
894 : gage 5619 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
895 : gage 6316
896 : gage 5619 <applet
897 :     code = "$code"
898 :     codebase = "$codebase"
899 :     archive = "$archive"
900 :     name = "$appletName"
901 :     id = "$appletName"
902 :     width = "$width"
903 :     height = "$height"
904 : gage 6009 bgcolor = "$applet_bgcolor"
905 : gage 6346 mayscript = "true";
906 : gage 5619 >
907 : gage 5624 $javaParameters
908 : gage 5667
909 :     Sorry, the Applet could not be started. Please make sure that
910 :     Java 1.4.2 (or later) is installed and activated.
911 :     (<a href="http://java.sun.com/getjava">click here to install Java now</a>)
912 : gage 5619 </applet>
913 :     END_OBJECT_TEXT
914 :    
915 : gage 6346 =cut
916 :    
917 :     # classid = "java:MyApplet.class"
918 :    
919 :    
920 :     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
921 :    
922 :     <applet
923 :     id = "$appletName"
924 :     name = "$appletName"
925 :     code = "$code"
926 :     type = "application/x-java-applet"
927 :     codebase = "$codebase"
928 :     archive = "$archive"
929 :     height = "$height"
930 :     width = "$width"
931 :     bgcolor = "$applet_bgcolor"
932 :     mayscript = "true"
933 :     >
934 :     <PARAM NAME="MAYSCRIPT" VALUE="true">
935 :     $javaParameters
936 :    
937 :     Sorry, the Applet could not be started. Please make sure that
938 :     Java 1.4.2 (or later) is installed and activated.
939 :     (<a href="http://java.sun.com/getjava">click here to install Java now</a>)
940 :     </applet>
941 :     END_OBJECT_TEXT
942 :    
943 :    
944 :    
945 : gage 5619 sub new {
946 :     my $class = shift;
947 :     $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(),
948 : gage 6368 type => 'java',
949 : gage 5619 @_
950 :     );
951 :    
952 :     }
953 :    
954 : gage 6308 ###############################################################################################################
955 :     #
956 :     # CANVAS APPLET PACKAGE
957 :     #
958 :     ###############################################################################################################
959 : gage 5619
960 : gage 6308 package CanvasApplet;
961 :     @ISA = qw(Applet);
962 : gage 5619
963 : gage 6308
964 :     =head2 Insertion HTML code for CanvasApplet
965 :    
966 :     =pod
967 :    
968 :     The secret to making this applet work with IE in addition to normal browsers
969 :     is the addition of the C(<form></form>) construct just before the object.
970 :    
971 :     For some reason IE has trouble locating a flash object which is contained
972 :     within a form. Adding this second blank form with the larger problemMainForm
973 :     seems to solve the problem.
974 :    
975 :     This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2)
976 :     Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated:
977 :     http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js
978 :     http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933
979 :    
980 :     use constant CANVAS_OBJECT_TEXT =><<'END_OBJECT_TEXT';
981 :     <form></form>
982 :     <script> var width = 200; var height = 200;</script>
983 :     <canvas name="cv" id="cv" data-src="http://localhost/webwork2_files/js/sketchgraphhtml5b/SketchGraph.pjs" width="400" height="400"></canvas>
984 :     END_OBJECT_TEXT
985 :    
986 :    
987 :    
988 :     =cut
989 :    
990 :    
991 :     use constant CANVAS_OBJECT_HEADER_TEXT =><<'END_HEADER_SCRIPT';
992 :     <script src="/webwork2_files/js/Base64.js" language="javascript">
993 :     </script>
994 :     <script src="/webwork2_files/js/ww_applet_support.js" language="javascript">
995 :     //upload functions stored in /opt/webwork/webwork2/htdocs/js ...
996 :    
997 :     </script>
998 :     <script language="JavaScript">
999 :    
1000 :    
1001 :    
1002 :     //////////////////////////////////////////////////////////
1003 :     //CANVAS OBJECT HEADER CODE
1004 :     //
1005 :     //////////////////////////////////////////////////////////
1006 :    
1007 :     ww_applet_list["$appletName"] = new ww_applet("$appletName");
1008 :    
1009 :    
1010 : gage 6346 ww_applet_list["$appletName"].code = "$code";
1011 : gage 6308 ww_applet_list["$appletName"].codebase = "$codebase";
1012 :     ww_applet_list["$appletName"].appletID = "$appletID";
1013 :     ww_applet_list["$appletName"].base64_state = "$base64_initializationState";
1014 :     ww_applet_list["$appletName"].initialState = Base64.decode("$base64_initialState");
1015 :     ww_applet_list["$appletName"].configuration = Base64.decode("$base64_configuration");;
1016 :     ww_applet_list["$appletName"].getStateAlias = "$getStateAlias";
1017 :     ww_applet_list["$appletName"].setStateAlias = "$setStateAlias";
1018 :     ww_applet_list["$appletName"].setConfigAlias = "$setConfigAlias";
1019 :     ww_applet_list["$appletName"].getConfigAlias = "$getConfigAlias";
1020 :     ww_applet_list["$appletName"].initializeActionAlias = "$initializeActionAlias";
1021 :     ww_applet_list["$appletName"].submitActionAlias = "$submitActionAlias";
1022 :     ww_applet_list["$appletName"].submitActionScript = Base64.decode("$base64_submitActionScript");
1023 :     ww_applet_list["$appletName"].answerBoxAlias = "$answerBoxAlias";
1024 :     ww_applet_list["$appletName"].maxInitializationAttempts = $maxInitializationAttempts;
1025 :     ww_applet_list["$appletName"].debugMode = "$debugMode";
1026 : gage 6368 ww_applet_list["$appletName"].debugMode = "$selfLoading";
1027 : gage 6308
1028 :     ww_applet_list["$appletName"].reportsLoaded = 1;
1029 :     ww_applet_list["$appletName"].object = $appletName;
1030 :    
1031 :     function getApplet(appletName) {
1032 :     //var isIE = navigator.appName.indexOf("Microsoft") != -1;
1033 :     //var obj = (isIE) ? window[appletName] : window.document[appletName];
1034 :     //return window.document[appletName];
1035 : gage 6316 var obj = ww_applet_list[appletName].object; // define fake applet for this object
1036 : gage 6308 if (obj && (obj.name == appletName)) { //RECENT FIX to ==
1037 :     //alert("getting fake applet " + obj.name);
1038 :     return( obj );
1039 :     } else {
1040 :     alert ("can't find fake applet " + appletName + " in object "+obj.name);
1041 :     }
1042 :     }
1043 :     </script>
1044 :    
1045 :     END_HEADER_SCRIPT
1046 :    
1047 :    
1048 :     #FIXME need to get rid of hardcoded url
1049 :    
1050 :    
1051 :     use constant CANVAS_OBJECT_TEXT =><<'END_OBJECT_TEXT';
1052 : gage 6453 <script language="javascript">ww_applet_list["$appletName"].visible = 1; // don't submit things if not visible
1053 :     </script>
1054 : gage 6308 <canvas name="cv" id="cv" data-src="/webwork2_files/js/sketchgraphhtml5b/SketchGraph.pjs" width="$width" height="$height"></canvas>
1055 :     END_OBJECT_TEXT
1056 :    
1057 :     sub new {
1058 :     my $class = shift;
1059 :     $class -> SUPER::new( objectText => CANVAS_OBJECT_TEXT(),
1060 :     headerText => CANVAS_OBJECT_HEADER_TEXT(),
1061 : gage 6368 type => 'html5canvas',
1062 : gage 6308 @_
1063 :     );
1064 :    
1065 :     }
1066 :    
1067 :    
1068 : gage 5574 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9