Parent Directory
|
Revision Log
Revision 5971 -
(view)
(download)
(as text)
Original Path: trunk/pg/macros/AppletObjects.pl
| 1 : | gage | 5582 | ################################################################################ |
| 2 : | # WeBWorK Online Homework Delivery System | ||
| 3 : | # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ | ||
| 4 : | gage | 5971 | # $CVSHeader: pg/macros/AppletObjects.pl,v 1.11 2008/05/22 19:17:10 gage Exp $ |
| 5 : | gage | 5582 | # |
| 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 : | gage | 5618 | AppletObjects.pl - Macro-based front end for the Applet.pm module. |
| 20 : | gage | 5582 | |
| 21 : | |||
| 22 : | =head1 DESCRIPTION | ||
| 23 : | |||
| 24 : | This subroutines in this | ||
| 25 : | gage | 5971 | file provide mechanisms to insert Flash applets (and Java applets) |
| 26 : | gage | 5582 | into a WeBWorK problem. |
| 27 : | |||
| 28 : | |||
| 29 : | =head1 SEE ALSO | ||
| 30 : | |||
| 31 : | L<Applets.pm>. | ||
| 32 : | |||
| 33 : | =cut | ||
| 34 : | |||
| 35 : | gage | 5667 | ######################################################################### |
| 36 : | # | ||
| 37 : | # Add basic functionality to the header of the question | ||
| 38 : | # | ||
| 39 : | # don't reload this file | ||
| 40 : | ######################################################################### | ||
| 41 : | gage | 5582 | |
| 42 : | gage | 5667 | sub _AppletObjects_init { |
| 43 : | gage | 5618 | |
| 44 : | |||
| 45 : | main::HEADER_TEXT(<<'END_HEADER_TEXT'); | ||
| 46 : | <script language="javascript">AC_FL_RunContent = 0;</script> | ||
| 47 : | <script src="/webwork2_files/applets/AC_RunActiveContent.js" language="javascript"> | ||
| 48 : | </script> | ||
| 49 : | <script src="/webwork2_files/js/Base64.js" language="javascript"> | ||
| 50 : | </script> | ||
| 51 : | |||
| 52 : | <script language="JavaScript"> | ||
| 53 : | |||
| 54 : | gage | 5660 | ////////////////////////////////////////////////////////// |
| 55 : | // applet lists | ||
| 56 : | ////////////////////////////////////////////////////////// | ||
| 57 : | |||
| 58 : | var applet_initializeAction_list = new Object; // functions for initializing question with an applet | ||
| 59 : | var applet_submitAction_list = new Object; // functions for submitting question with applet | ||
| 60 : | var applet_setState_list = new Object; // functions for setting state (XML) from applets | ||
| 61 : | var applet_getState_list = new Object; // functions for getting state (XML) from applets | ||
| 62 : | var applet_config_list = new Object; // functions for configuring on applets | ||
| 63 : | var applet_checkLoaded_list = new Object; // functions for probing the applet to see if it is loaded | ||
| 64 : | var applet_reportsLoaded_list = new Object; // flag set by applet | ||
| 65 : | var applet_isReady_list = new Object; // flag set by javaScript in checkLoaded | ||
| 66 : | gage | 5618 | |
| 67 : | gage | 5660 | ////////////////////////////////////////////////////////// |
| 68 : | // DEBUGGING tools | ||
| 69 : | ////////////////////////////////////////////////////////// | ||
| 70 : | gage | 5662 | var debug; |
| 71 : | gage | 5660 | var debugText = ""; |
| 72 : | gage | 5662 | function set_debug(num) { // setting debug for any applet sets it for all of them |
| 73 : | if (num) { | ||
| 74 : | debug =1; | ||
| 75 : | } | ||
| 76 : | } | ||
| 77 : | gage | 5660 | function debug_add(str) { |
| 78 : | if (debug) { | ||
| 79 : | debugText = debugText + "\n\n" +str; | ||
| 80 : | } | ||
| 81 : | } | ||
| 82 : | |||
| 83 : | ////////////////////////////////////////////////////////// | ||
| 84 : | // INITIALIZE and SUBMIT actions | ||
| 85 : | ////////////////////////////////////////////////////////// | ||
| 86 : | |||
| 87 : | gage | 5618 | function submitAction() { |
| 88 : | gage | 5677 | |
| 89 : | if (debug) { | ||
| 90 : | debugText = " Begin looping through applet_submitAction_list\n"; | ||
| 91 : | } | ||
| 92 : | gage | 5667 | for (var applet in applet_submitAction_list) { |
| 93 : | gage | 5677 | |
| 94 : | gage | 5667 | applet_submitAction_list[applet](); |
| 95 : | } | ||
| 96 : | gage | 5677 | if (debug) { |
| 97 : | alert(debugText); debugText=""; | ||
| 98 : | }; | ||
| 99 : | gage | 5618 | } |
| 100 : | function initializeAction() { | ||
| 101 : | gage | 5660 | var iMax = 10; |
| 102 : | debugText="start intializeAction() with up to " +iMax + " attempts\n"; | ||
| 103 : | for (var appletName in applet_initializeAction_list) { | ||
| 104 : | safe_applet_initialize(appletName, iMax); | ||
| 105 : | } | ||
| 106 : | gage | 5618 | |
| 107 : | } | ||
| 108 : | gage | 5660 | |
| 109 : | // applet can set isReady flag by calling applet_loaded(appletName, loaded); | ||
| 110 : | function applet_loaded(appletName,loaded) { | ||
| 111 : | applet_reportsLoaded_list[appletName] = loaded; // 0 means not loaded | ||
| 112 : | debug_add("applet reporting that it has been loaded = " + loaded ); | ||
| 113 : | } | ||
| 114 : | gage | 5618 | |
| 115 : | gage | 5660 | // insures that applet is loaded before initializing it |
| 116 : | function safe_applet_initialize(appletName, i) { | ||
| 117 : | debug_add("Iteration " + i + " of safe_applet_initialize with applet " + appletName ); | ||
| 118 : | |||
| 119 : | i--; | ||
| 120 : | var applet_loaded = applet_checkLoaded_list[appletName](); | ||
| 121 : | debug_add("applet is ready = " + applet_loaded ); | ||
| 122 : | |||
| 123 : | if ( 0 < i && !applet_loaded ) { // wait until applet is loaded | ||
| 124 : | debug_add("applet " + appletName + "not ready try again"); | ||
| 125 : | window.setTimeout( "safe_applet_initialize(\"" + appletName + "\"," + i + ")",1); | ||
| 126 : | } else if( 0 < i ){ // now that applet is loaded configure it and initialize it with saved data. | ||
| 127 : | debug_add(" Ready to initialize applet " + appletName + " with " + i + " iterations left. "); | ||
| 128 : | |||
| 129 : | // in-line handler -- configure and initialize | ||
| 130 : | try{ | ||
| 131 : | if (debug && typeof(getApplet(appletName).debug) == "function" ) { | ||
| 132 : | gage | 5667 | getApplet(appletName).debug(1); // turn the applet's debug functions on. |
| 133 : | gage | 5660 | } |
| 134 : | } catch(e) { | ||
| 135 : | alert("Unable to set debug mode for applet " + appletName); | ||
| 136 : | } | ||
| 137 : | try{ | ||
| 138 : | applet_config_list[appletName](); | ||
| 139 : | } catch(e) { | ||
| 140 : | alert("Unable to configure " + appletName + " \n " +e ); | ||
| 141 : | } | ||
| 142 : | try{ | ||
| 143 : | applet_initializeAction_list[appletName](); | ||
| 144 : | } catch(e) { | ||
| 145 : | alert("unable to initialize " + appletName + " \n " +e ); | ||
| 146 : | } | ||
| 147 : | |||
| 148 : | } else { | ||
| 149 : | if (debug) {alert("Error: timed out waiting for applet " +appletName + " to load");} | ||
| 150 : | } | ||
| 151 : | if (debug) {alert(debugText); debugText="";}; | ||
| 152 : | } | ||
| 153 : | |||
| 154 : | /////////////////////////////////////////////////////// | ||
| 155 : | // Utility functions | ||
| 156 : | /////////////////////////////////////////////////////// | ||
| 157 : | gage | 5618 | |
| 158 : | gage | 5660 | |
| 159 : | gage | 5618 | function getApplet(appletName) { |
| 160 : | var isIE = navigator.appName.indexOf("Microsoft") != -1; | ||
| 161 : | var obj = (isIE) ? window[appletName] : window.document[appletName]; | ||
| 162 : | //return window.document[appletName]; | ||
| 163 : | if (obj && (obj.name = appletName)) { | ||
| 164 : | gage | 5660 | return( obj ); |
| 165 : | gage | 5618 | } else { |
| 166 : | gage | 5660 | // alert ("can't find applet " + appletName); |
| 167 : | gage | 5618 | } |
| 168 : | } | ||
| 169 : | gage | 5660 | |
| 170 : | function listQuestionElements() { // list all HTML input and textarea elements in main problem form | ||
| 171 : | var isIE = navigator.appName.indexOf("Microsoft") != -1; | ||
| 172 : | var elementList = (isIE) ? document.getElementsByTagName("input") : document.problemMainForm.getElementsByTagName("input"); | ||
| 173 : | var str=elementList.length +" Question Elements\n type | name = value < id > \n"; | ||
| 174 : | for( var i=0; i< elementList.length; i++) { | ||
| 175 : | str = str + " "+i+" " + elementList[i].type | ||
| 176 : | + " | " + elementList[i].name | ||
| 177 : | + "= " + elementList[i].value + | ||
| 178 : | " <" + elementList[i].id + ">\n"; | ||
| 179 : | } | ||
| 180 : | elementList = (isIE) ? document.getElementsByTagName("textarea") : document.problemMainForm.getElementsByTagName("textarea"); | ||
| 181 : | for( var i=0; i< elementList.length; i++) { | ||
| 182 : | str = str + " "+i+" " + elementList[i].type | ||
| 183 : | + " | " + elementList[i].name | ||
| 184 : | + "= " + elementList[i].value + | ||
| 185 : | " <" + elementList[i].id + ">\n"; | ||
| 186 : | } | ||
| 187 : | alert(str +"\n Place listQuestionElements() at end of document in order to get all form elements!"); | ||
| 188 : | } | ||
| 189 : | |||
| 190 : | function base64Q(str) { | ||
| 191 : | return ( !str.match(/<XML/i) && !str.match(/<?xml/i)); | ||
| 192 : | } | ||
| 193 : | function setEmptyState(appletName){ | ||
| 194 : | var newState = "<xml></xml>"; | ||
| 195 : | applet_setState_list[appletName](newState); | ||
| 196 : | var applet = getApplet(appletName); | ||
| 197 : | getQE(appletName+"_state").value = newState; | ||
| 198 : | getQE("previous_" + appletName + "_state").value = newState | ||
| 199 : | } | ||
| 200 : | |||
| 201 : | function getQE(name1) { // get Question Element in problemMainForm by name | ||
| 202 : | var isIE = navigator.appName.indexOf("Microsoft") != -1; | ||
| 203 : | var obj = (isIE) ? document.getElementById(name1) | ||
| 204 : | :document.problemMainForm[name1]; | ||
| 205 : | // needed for IE -- searches id and name space so it can be unreliable if names are not unique | ||
| 206 : | if (!obj || obj.name != name1) { | ||
| 207 : | alert("Can't find element " + name1); | ||
| 208 : | listQuestionElements(); | ||
| 209 : | } else { | ||
| 210 : | return( obj ); | ||
| 211 : | } | ||
| 212 : | |||
| 213 : | } | ||
| 214 : | function getQuestionElement(name1) { | ||
| 215 : | return getQE(name1); | ||
| 216 : | } | ||
| 217 : | gage | 5618 | |
| 218 : | </script> | ||
| 219 : | |||
| 220 : | END_HEADER_TEXT | ||
| 221 : | |||
| 222 : | gage | 5667 | }; |
| 223 : | gage | 5618 | |
| 224 : | gage | 5582 | =head3 |
| 225 : | FlashApplet | ||
| 226 : | |||
| 227 : | Useage: $applet = FlashApplet(); | ||
| 228 : | |||
| 229 : | =cut | ||
| 230 : | |||
| 231 : | sub FlashApplet { | ||
| 232 : | gage | 5597 | return new FlashApplet(@_); |
| 233 : | gage | 5582 | |
| 234 : | } | ||
| 235 : | |||
| 236 : | gage | 5618 | sub JavaApplet { |
| 237 : | return new JavaApplet(@_); | ||
| 238 : | gage | 5582 | |
| 239 : | gage | 5618 | } |
| 240 : | |||
| 241 : | package Applet; | ||
| 242 : | |||
| 243 : | |||
| 244 : | |||
| 245 : | gage | 5582 | =head2 Methods |
| 246 : | |||
| 247 : | =cut | ||
| 248 : | |||
| 249 : | ## this method is defined in this file | ||
| 250 : | ## because the main subroutines HEADER_TEXT and MODES are | ||
| 251 : | ## not available to the module FlashApplet when that file | ||
| 252 : | ## is compiled (at the time the apache child process is first initialized) | ||
| 253 : | |||
| 254 : | gage | 5618 | =head3 insertAll |
| 255 : | gage | 5582 | |
| 256 : | gage | 5618 | Useage: TEXT( $applet->insertAll() ); |
| 257 : | \{ $applet->insertAll() \} (used within BEGIN_TEXT/END_TEXT blocks) | ||
| 258 : | gage | 5582 | |
| 259 : | =cut | ||
| 260 : | |||
| 261 : | =pod | ||
| 262 : | |||
| 263 : | Inserts applet at this point in the HTML code. (In TeX mode a message "Applet" is written.) This method | ||
| 264 : | also adds the applets header material into the header portion of the HTML page. It effectively inserts | ||
| 265 : | gage | 5618 | the outputs of both C<$applet-E<gt>insertHeader> and C<$applet-E<gt>insertObject> (defined in L<Applet.pm> ) |
| 266 : | in the appropriate places. | ||
| 267 : | gage | 5582 | |
| 268 : | gage | 5618 | Note: This method is defined here rather than in Applet.pl because it |
| 269 : | requires access to the RECORD_FORM_LABEL subroutine | ||
| 270 : | and to the routine accessing the stored values of the answers. These are defined in main::. | ||
| 271 : | |||
| 272 : | gage | 5582 | =cut |
| 273 : | |||
| 274 : | gage | 5618 | sub insertAll { ## inserts both header text and object text |
| 275 : | gage | 5582 | my $self = shift; |
| 276 : | gage | 5618 | my %options = @_; |
| 277 : | $self->debug( (defined($options{debug}) and $options{debug}==1) ? 1 : 0 ); | ||
| 278 : | my $reset_button = $options{reset_button} || 0; | ||
| 279 : | # prepare html code for storing state | ||
| 280 : | my $appletName = $self->appletName; | ||
| 281 : | my $appletStateName = "${appletName}_state"; | ||
| 282 : | my $getState = $self->getStateAlias; | ||
| 283 : | my $setState = $self->setStateAlias; | ||
| 284 : | my $base64_initialState = $self->base64_state; | ||
| 285 : | main::RECORD_FORM_LABEL($appletStateName); #this insures that they'll be saved from one invocation to the next | ||
| 286 : | #main::RECORD_FORM_LABEL("previous_$appletStateName"); | ||
| 287 : | my $answer_value = ''; | ||
| 288 : | $answer_value = ${$main::inputs_ref}{$appletStateName} if defined(${$main::inputs_ref}{$appletStateName}); | ||
| 289 : | |||
| 290 : | if ( defined( $main::rh_sticky_answers->{$appletStateName} ) ) { | ||
| 291 : | $answer_value = shift( @{ $main::rh_sticky_answers->{$appletStateName} }); | ||
| 292 : | $answer_value = '' unless defined($answer_value); | ||
| 293 : | } | ||
| 294 : | $answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3 | ||
| 295 : | $answer_value =~ s/\s+/ /g; ## remove excessive whitespace from student answer | ||
| 296 : | |||
| 297 : | ####### | ||
| 298 : | # insert a hidden variable to hold the applet's state (debug =>1 makes it visible for debugging and provides debugging buttons) | ||
| 299 : | ####### | ||
| 300 : | gage | 5667 | my $base_64_encoded_answer_value = ($answer_value =~/<XML|<?xml/i)? encode_base64($answer_value) : $answer_value; |
| 301 : | gage | 5677 | $base_64_encoded_answer_value =~ s/\r|\n//g; # get rid of line returns |
| 302 : | gage | 5667 | my $decoded_answer_value = ($answer_value =~/<XML|<?xml/i) ? $answer_value : decode_base64($answer_value); |
| 303 : | gage | 5618 | my $debug_input_element = qq!\n<textarea rows="4" cols="80" |
| 304 : | name = "$appletStateName">$decoded_answer_value</textarea><br/> | ||
| 305 : | gage | 5623 | <input type="button" value="$getState" |
| 306 : | onClick="applet_getState_list['$appletName']()" | ||
| 307 : | > | ||
| 308 : | <input type="button" value="$setState" | ||
| 309 : | onClick="var tmp = getQE('$appletStateName').value; | ||
| 310 : | applet_setState_list['$appletName'](tmp);" | ||
| 311 : | > | ||
| 312 : | gage | 5618 | !; |
| 313 : | my $state_input_element = ($self->debug == 1) ? $debug_input_element : | ||
| 314 : | qq!\n<input type="hidden" name = "$appletStateName" value ="$base_64_encoded_answer_value">!; | ||
| 315 : | my $reset_button_str = ($reset_button) ? | ||
| 316 : | gage | 5660 | qq!<br/><input type='button' value='set applet state empty' onClick="setEmptyState('$appletName')"> |
| 317 : | <input type="button" value="reinitialize applet" onClick="getQE('$appletStateName').value='$base64_initialState'"/>! | ||
| 318 : | gage | 5618 | : '' |
| 319 : | ; | ||
| 320 : | # always base64 encode the hidden answer value to prevent problems with quotes. | ||
| 321 : | # | ||
| 322 : | $state_storage_html_code = | ||
| 323 : | $reset_button_str. | ||
| 324 : | $state_input_element. | ||
| 325 : | qq!<input type="hidden" name="previous_$appletStateName" value = "$base_64_encoded_answer_value">!; | ||
| 326 : | ####### | ||
| 327 : | # insert header material | ||
| 328 : | ####### | ||
| 329 : | gage | 5582 | main::HEADER_TEXT($self->insertHeader()); |
| 330 : | gage | 5618 | return main::MODES(TeX=>' {\bf applet } ', HTML=>$self->insertObject.$main::BR.$state_storage_html_code); |
| 331 : | gage | 5582 | } |
| 332 : | |||
| 333 : | =head3 Example problem | ||
| 334 : | |||
| 335 : | |||
| 336 : | =cut | ||
| 337 : | |||
| 338 : | |||
| 339 : | |||
| 340 : | =pod | ||
| 341 : | |||
| 342 : | |||
| 343 : | DOCUMENT(); | ||
| 344 : | |||
| 345 : | # Load whatever macros you need for the problem | ||
| 346 : | loadMacros("PG.pl", | ||
| 347 : | "PGbasicmacros.pl", | ||
| 348 : | "PGchoicemacros.pl", | ||
| 349 : | "PGanswermacros.pl", | ||
| 350 : | "AppletObjects.pl", | ||
| 351 : | "MathObjects.pl", | ||
| 352 : | "source.pl" | ||
| 353 : | ); | ||
| 354 : | |||
| 355 : | ## Do NOT show partial correct answers | ||
| 356 : | $showPartialCorrectAnswers = 0; | ||
| 357 : | |||
| 358 : | |||
| 359 : | |||
| 360 : | ################################### | ||
| 361 : | # Create link to applet | ||
| 362 : | ################################### | ||
| 363 : | |||
| 364 : | $applet = FlashApplet(); | ||
| 365 : | my $appletName = "ExternalInterface"; | ||
| 366 : | $applet->codebase(findAppletCodebase("$appletName.swf")); | ||
| 367 : | $applet->appletName($appletName); | ||
| 368 : | $applet->appletId($appletName); | ||
| 369 : | |||
| 370 : | # findAppletCodebase looks for the applet in a list | ||
| 371 : | # of locations specified in global.conf | ||
| 372 : | |||
| 373 : | ################################### | ||
| 374 : | gage | 5618 | # Add additional javaScript functions to header section of HTML to |
| 375 : | gage | 5593 | # communicate with the "ExternalInterface" applet. |
| 376 : | gage | 5582 | ################################### |
| 377 : | |||
| 378 : | $applet->header(<<'END_HEADER'); | ||
| 379 : | <script type="text/javascript" src="https://devel.webwork.rochester.edu:8002/webwork2_files/js/BrowserSniffer.js"> | ||
| 380 : | </script> | ||
| 381 : | |||
| 382 : | |||
| 383 : | <script language="JavaScript"> | ||
| 384 : | function getBrowser() { | ||
| 385 : | //alert("look for sniffer"); | ||
| 386 : | var sniffer = new BrowserSniffer(); | ||
| 387 : | //alert("found sniffer" +sniffer); | ||
| 388 : | return sniffer; | ||
| 389 : | } | ||
| 390 : | |||
| 391 : | function updateStatus(sMessage) { | ||
| 392 : | gage | 5618 | getQE("playbackStatus").value = sMessage; |
| 393 : | gage | 5582 | } |
| 394 : | |||
| 395 : | function newColor() { | ||
| 396 : | gage | 5618 | |
| 397 : | getApplet("ExternalInterface").updateColor(Math.round(Math.random() * 0xFFFFFF)); | ||
| 398 : | gage | 5582 | } |
| 399 : | |||
| 400 : | </script> | ||
| 401 : | END_HEADER | ||
| 402 : | |||
| 403 : | ################################### | ||
| 404 : | # Configure applet | ||
| 405 : | ################################### | ||
| 406 : | |||
| 407 : | # not used here. Allows for uploading an xml string for the applet | ||
| 408 : | |||
| 409 : | |||
| 410 : | |||
| 411 : | |||
| 412 : | ################################### | ||
| 413 : | # write the text for the problem | ||
| 414 : | ################################### | ||
| 415 : | |||
| 416 : | TEXT(beginproblem()); | ||
| 417 : | |||
| 418 : | |||
| 419 : | |||
| 420 : | BEGIN_TEXT | ||
| 421 : | gage | 5618 | \{ $applet->insertAll() \} |
| 422 : | gage | 5582 | $PAR |
| 423 : | |||
| 424 : | The Flash object operates above this line. The box and button below this line are part of | ||
| 425 : | the WeBWorK problem. They communicate with the Flash object. | ||
| 426 : | $HR | ||
| 427 : | Status <input type="text" id="playbackStatus" value="started" /><br /> | ||
| 428 : | Color <input type="button" value="new color" name="newColorButton" onClick="newColor()" /> | ||
| 429 : | $PAR $HR | ||
| 430 : | This flash applet was created by Barbara Kaskosz. | ||
| 431 : | |||
| 432 : | END_TEXT | ||
| 433 : | |||
| 434 : | ENDDOCUMENT(); | ||
| 435 : | |||
| 436 : | |||
| 437 : | |||
| 438 : | |||
| 439 : | =cut |
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |