From: Mike G. v. a. <we...@ma...> - 2008-03-16 14:53:12
|
Log Message: ----------- New version of Applet.pm and AppletObjects.pl that supports both flashnew FlashApplet(); and java applets new JavaApplet(); see pod docs in those two files for more details. Modified Files: -------------- pg/lib: Applet.pm Revision Data ------------- Index: Applet.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Applet.pm,v retrieving revision 1.4 retrieving revision 1.5 diff -Llib/Applet.pm -Llib/Applet.pm -u -r1.4 -r1.5 --- lib/Applet.pm +++ lib/Applet.pm @@ -37,7 +37,7 @@ ################################### #xml data to set up the problem-rac - $applet->xmlString(qq{<XML> + $applet->state(qq{<XML> <point xval='$xval_1' yval='$yval_1' /> <point xval='$xval_2' yval='$yval_2' /> </XML>}); @@ -77,11 +77,14 @@ The module JavaApplet has defaults for inserting java applets. -The module Applet will store common code for the two types of applet. +The module Applet stores common code for the two types of applet. =head1 USAGE -This file is included by listing it in the modules section of global.conf. +These modules are activate by listing it in the modules section of global.conf and rebooting the server. +The companion file to this one is macros/AppletObjects.pl + +qw(Applet FlashApplet JavaApplet) =cut @@ -90,7 +93,7 @@ package Applet; -package FlashApplet; + use MIME::Base64 qw( encode_base64 decode_base64); @@ -101,12 +104,12 @@ These functions are automatically defined for use for any javaScript placed in the text of a PG question. - getFlashMovie(appletName) -- finds the applet path in the DOM + getApplet(appletName) -- finds the applet path in the DOM - submitAction() -- calls the submit action of the applet - -- the submitAction is defined + submitAction() -- calls the submit action of the applets + - initialize() -- calls the initialize action of the applet + initializeAction() -- calls the initialize action of the applets getQE(name) -- gets an HTML element of the question by name or by id. Be sure to keep all names and ids @@ -123,162 +126,86 @@ TEXT(qq!<script> listQuestionElements() </script>!); ENDDOCUMENT(); + list of accessor methods format: current_value = $self->method(new_value or empty) -=cut - + appletId for simplicity and reliability appletId and appletName are always the same + appletName + + archive the name of the .jar file containing the applet code + code the name of the applet code in the .jar archive + codebase a prefix url used to find the archive and the applet itself + + height rectangle alloted in the html page for displaying the applet + width + + params an anonymous array containing name/value pairs + to configure the applet [name =>'value, ...] + + header stores the text to be added to the header section of the html page + object stores the text which places the applet on the html page + + debug in debug mode several alerts mark progress through the procedure of calling the applet + + config configuration are those customizable attributes of the applet which don't + change as it is used. When stored in hidden answer fields + it is usually stored in base64 encoded format. + base64_config base64 encode version of the contents of config + + configAlias (default: config ) names the applet command called with the contents of $self->config + to configure the applet. The parameters are passed to the applet in plain text using <xml> + The outer tags must be <xml> ..... </xml> + state state consists of those customizable attributes of the applet which change + as the applet is used. It is stored by the calling .pg question so that + when revisiting the question the applet + will be restored to the same state it was left in when the question was last + viewed. + + getStateAlias (default: getState) alias for command called to read the current state of the applet. + The state is passed in plain text xml format with outer tags: <xml>....</xml> + setStateAlias (default: setState) alias for the command called to reset the state of the applet. + The state is passed in plain text in xml format with outer tags: <xml>....</xml> + + base64_state returns the base64 encoded version of the state stored in the applet object. + + initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState + submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the + .pg question is pressed. + + returnFieldName + + -use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT'; - <script language="javascript">AC_FL_RunContent = 0;</script> - <script src="http://hosted2.webwork.rochester.edu/webwork2_files/applets/AC_RunActiveContent.js" language="javascript"> - </script> - - - <script language="JavaScript"> - - var flash; - function getFlashMovie(appletName) { - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var obj = (isIE) ? window[appletName] : window.document[appletName]; - //return window.document[appletName]; - if (obj.name = appletName) { - return( obj ); - } else { - alert ("can't find applet " + appletName); - } - } - - function submitAction() { - getQE("$returnFieldName").value = getFlashMovie("$appletId").$submitAction(); - } - function initialize() { - getFlashMovie("$appletId").$initializeAction("$base64_xmlString"); - } - function getQE(name1) { // get Question Element in problemMainForm by name - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var obj = (isIE) ? document.getElementById(name1) - :document.problemMainForm[name1]; - // needed for IE -- searches id and name space so it can be unreliable if names are not unique - if (obj.name = name1 ) { - return( obj ); - } else { - alert("Can't find " + name1); - listQuestionElements(); - } - - } - function getQuestionElement(name1) { - return getQE(name1); - } - function listQuestionElements() { // list all HTML elements in main problem form - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var mainForm = (isIE) ? document.getElementsByTagName("input") : document.getElementsByTagName("input"); - var str=mainForm.length +" Question Elements\n type | name = value < id > \n"; - for( var i=0; i< mainForm.length; i++) { - str = str + " "+i+" " + mainForm[i].type - + " | " + mainForm[i].name - + "= " + mainForm[i].value + - " <" + mainForm[i].id + ">\n"; - } - alert(str +"\n Place listQuestionElements() at end of document in order to get all form elements!"); - } - - - </script> - -END_HEADER_SCRIPT - - -# <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" -# width="550" height="400" id="$appletId" align="middle"> -# <param name="allowScriptAccess" value="sameDomain" /> -# <param name="allowFullScreen" value="false" /> -# <param name="movie" value="$appletName.swf" /> -# <param name="quality" value="high" /> -# <param name="bgcolor" value="#ffffcc" /> -# <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#ffffcc" width="550" height="400" name="$appletName" -# align="middle" id="$appletId", -# align="middle" allowScriptAccess="sameDomain" -# allowFullScreen="false" -# type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> -# <param name="quality" value="high" /><param name="bgcolor" value="#ffffcc" /> -# </object> - -=pod - -The secret to making this applet work with IE in addition to normal browsers -is the addition of the C(<form></form>) construct just before the object. - -For some reason IE has trouble locating a flash object which is contained -within a form. Adding this second blank form with the larger problemMainForm -seems to solve the problem. - -This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2) -Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated: -http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js -http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933 - - use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; - <form></form> - <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - id="$appletName" width="500" height="375" - codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> - <param name="movie" value="$codebase/$appletName.swf" /> - <param name="quality" value="high" /> - <param name="bgcolor" value="#869ca7" /> - <param name="allowScriptAccess" value="sameDomain" /> - <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" - width="550" height="400" name="$appletName" align="middle" id="$appletID" - play="true" loop="false" quality="high" allowScriptAccess="sameDomain" - type="application/x-shockwave-flash" - pluginspage="http://www.macromedia.com/go/getflashplayer"> - </embed> - - </object> - END_OBJECT_TEXT =cut -use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; - <form></form> - <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - id="$appletName" width="500" height="375" - codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> - <param name="movie" value="$codebase/$appletName.swf" /> - <param name="quality" value="high" /> - <param name="bgcolor" value="#869ca7" /> - <param name="allowScriptAccess" value="sameDomain" /> - <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" - width="550" height="400" name="$appletName" align="middle" id="$appletID" - play="true" loop="false" quality="high" allowScriptAccess="sameDomain" - type="application/x-shockwave-flash" - pluginspage="http://www.macromedia.com/go/getflashplayer"> - </embed> - </object> -END_OBJECT_TEXT sub new { my $class = shift; my $self = { - host =>'', - port => '', - path => '', appletName =>'', + code=>'', codebase=>'', - appletId =>'', +# appletId =>'', #always use identical applet Id's and applet Names params =>undef, - base64_xmlString => 'foobar', - initializeActionAlias => 'setupProblem', - submitActionAlias => 'checkAnswer', - returnFieldName => 'receivedField', - headerText => DEFAULT_HEADER_TEXT(), - objectText => DEFAULT_OBJECT_TEXT(), + width => 550, + height => 400, + base64_state => '', + base64_config => '', + getStateAlias => 'getXML', + setStateAlias => 'setState', + configAlias => 'config', + initializeActionAlias => 'setXML', + submitActionAlias => 'getXML', + returnFieldName => 'receivedField', + headerText => DEFAULT_HEADER_TEXT(), + objectText => '', + debug => 0, @_, }; bless $self, $class; - #$self -> _initialize(@_); return $self; } @@ -304,7 +231,7 @@ my $self = shift; if (ref($_[0]) =~/HASH/) { $self->{params} = shift; - } elsif ( $_[0] =~ '') { + } elsif ( !defined($_[0]) or $_[0] =~ '') { # do nothing (read) } else { warn "You must enter a reference to a hash for the parameter list"; @@ -323,6 +250,22 @@ $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty $self->{submitActionAlias}; } +sub getStateAlias { + my $self = shift; + $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty + $self->{getStateAlias}; +} + +sub setStateAlias { + my $self = shift; + $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty + $self->{setStateAlias}; +} +sub configAlias { + my $self = shift; + $self->{configAlias} = shift ||$self->{configAlias}; # replace the current contents if non-empty + $self->{configAlias}; +} sub returnFieldName { my $self = shift; $self->{returnFieldName} = shift ||$self->{returnFieldName}; # replace the current contents if non-empty @@ -333,43 +276,86 @@ $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty $self->{codebase}; } +sub code { + my $self = shift; + $self->{code} = shift ||$self->{code}; # replace the current code if non-empty + $self->{code}; +} +sub height { + my $self = shift; + $self->{height} = shift ||$self->{height}; # replace the current height if non-empty + $self->{height}; +} +sub width { + my $self = shift; + $self->{width} = shift ||$self->{width}; # replace the current width if non-empty + $self->{width}; +} +sub archive { + my $self = shift; + $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty + $self->{archive}; +} sub appletName { my $self = shift; $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty $self->{appletName}; } -sub appletId { +sub debug { my $self = shift; - $self->{appletId} = shift ||$self->{appletId}; # replace the current appletName if non-empty - $self->{appletId}; + my $new_flag = shift; + $self->{debug} = $new_flag if defined($new_flag); + $self->{debug}; +} +sub appletId { + appletName(@_); } -sub xmlString { +sub state { my $self = shift; my $str = shift; - $self->{base64_xmlString} = encode_base64($str) ||$self->{base64_xmlString}; # replace the current string if non-empty - $self->{base64_xmlString} =~ s/\n//g; - decode_base64($self->{base64_xmlString}); + $self->{base64_state} = encode_base64($str) ||$self->{base64_state}; # replace the current string if non-empty + $self->{base64_state} =~ s/\n//g; + decode_base64($self->{base64_state}); } -sub base64_xmlString{ +sub base64_state{ my $self = shift; - $self->{base64_xmlString} = shift ||$self->{base64_xmlString}; # replace the current string if non-empty - $self->{base64_xmlString}; + $self->{base64_state} = shift ||$self->{base64_state}; # replace the current string if non-empty + $self->{base64_state}; +} +sub config { + my $self = shift; + my $str = shift; + $self->{base64_config} = encode_base64($str) || $self->{base64_config}; # replace the current string if non-empty + $self->{base64_config} =~ s/\n//g; + decode_base64($self->{base64_config}); +} +sub base64_config { + my $self = shift; + $self->{base64_config} = shift ||$self->{base64_config}; # replace the current string if non-empty + $self->{base64_config} =$self->{base64_config}; + $self->{base64_config}; } - #FIXME # need to be able to adjust header material sub insertHeader { my $self = shift; - my $codebase = $self->{codebase}; - my $appletId = $self->{appletId}; - my $appletName = $self->{appletName}; - my $base64_xmlString = $self->{base64_xmlString}; - my $initializeAction = $self->{initializeActionAlias}; - my $submitAction = $self->{submitActionAlias}; - my $returnFieldName= $self->{returnFieldName}; - my $headerText = $self->header(); + my $codebase = $self->codebase; + my $appletId = $self->appletId; + my $appletName = $self->appletName; + my $base64_initialState = $self->base64_state; + my $initializeAction = $self->initializeActionAlias; + my $submitAction = $self->submitActionAlias; + my $setState = $self->setStateAlias; + my $getState = $self->getStateAlias; + my $config = $self->configAlias; + my $base64_config = $self->base64_config; + my $debugMode = ($self->debug) ? "1": "0"; + my $returnFieldName = $self->{returnFieldName}; +# my $encodeStateQ = ($self->debug)?'' : "state = Base64.encode(state);"; # in debug mode base64 encoding is not used. +# my $decodeStateQ = "if (!state.match(/<XML>*/i) ) {state = Base64.decode(state)}"; # decode if <XML> is not present + my $headerText = $self->header(); $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook return $headerText; @@ -377,55 +363,232 @@ } - -# <script language="javascript"> -# if (AC_FL_RunContent == 0) { -# alert("This page requires AC_RunActiveContent.js."); -# } else { -# AC_FL_RunContent( -# 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0', -# 'width', '100%', -# 'height', '100%', -# 'src', 'http://$codebase/$appletName', -# 'quality', 'high', -# 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', -# 'align', 'middle', -# 'play', 'true', -# 'loop', 'true', -# 'scale', 'showall', -# 'wmode', 'window', -# 'devicefont', 'false', -# 'id', '$appletId', -# 'bgcolor', '#ffffcc', -# 'name', '$appletName', -# 'menu', 'true', -# 'allowFullScreen', 'false', -# 'allowScriptAccess','sameDomain', -# 'movie', '$appletName', -# 'salign', '' -# ); //end AC code -# } -# </script> sub insertObject { - my $self = shift; - my $codebase = $self->{codebase}; - my $appletId = $self->{appletId}; + my $self = shift; + my $code = $self->{code}; + my $codebase = $self->{codebase}; + my $appletId = $self->{appletName}; my $appletName = $self->{appletName}; - $codebase = findAppletCodebase("$appletName.swf") unless $codebase; + my $archive = $self->{archive}; + my $width = $self->{width}; + my $height = $self->{height}; + my $parameters = ''; + my $parameters = ''; + my %param_hash = %{$self->params()}; + foreach my $key (keys %param_hash) { + $parameters .= qq!<param name ="$key" value = "$param_hash{$key}">\n! + } + $objectText = $self->{objectText}; $objectText =~ s/(\$\w+)/$1/gee; return $objectText; } - sub initialize { my $self = shift; return q{ <script> - initialize(); + initializeAction(); // this should really be done in the <body> tag </script> }; } + +use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT'; + + <script language="JavaScript"> + var debug = $debugMode; + // + //CONFIGURATIONS + // + // configurations are "permanent" + applet_config_list["$appletName"] = function() { + if (debug) { alert("configure $appletName . $config ( $base64_config )");} + try { + if (debug || !( typeof(getApplet("$appletName").$config) == "undefined" ) ) { + + getApplet("$appletName").$config(Base64.decode("$base64_config")); + } + } catch(e) { + alert("error executing configuration command $config for $appletName: " + e ); + } + } + // + //STATE + // + // state can vary as the applet is manipulated. + applet_setState_list["$appletName"] = function(state) { + state = state || getQE("$appletName"+"_state").value || "<xml></xml>"; + if ( base64Q(state) ) { state=Base64.decode(state);} + if (debug) { alert("set state for $appletName to " + state);} + try { + if (debug || !( typeof(getApplet("$appletName").$setState) =="undefined" ) ) { + getApplet("$appletName").$setState( state ); + } + } catch(e) { + alert("Error in setting state of $appletName: " + e ); + } + }; + applet_getState_list["$appletName"] = function () { + if (debug) { alert("getState for applet $appletName");} + try { + var applet = getApplet("$appletName"); + var state; + if (!( typeof(getApplet("$appletName").$getState) =="undefined" ) ) { + state = applet.$getState(); // get state in xml format + } + if (!debug) {state = Base64.encode(state) }; // replace state by encoded version + getQE("$appletName"+"_state").value = state; //place in state htmlItem (debug: textarea, otherwise hidden) + } catch (e) { + alert("Error in getting state for $appletName " + e ); + } + }; + // + //INITIALIZE + // + applet_initializeAction_list["$appletName"] = function () { + applet_setState_list["$appletName"](); + }; + + applet_submitAction_list["$appletName"] = function () { + applet_getState_list["$appletName"](); + getQE("$returnFieldName").value = getApplet("$appletName").sendData(); + }; + </script> + +END_HEADER_SCRIPT + +package FlashApplet; +@ISA = qw(Applet); + + + +=pod + +The secret to making this applet work with IE in addition to normal browsers +is the addition of the C(<form></form>) construct just before the object. + +For some reason IE has trouble locating a flash object which is contained +within a form. Adding this second blank form with the larger problemMainForm +seems to solve the problem. + +This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2) +Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated: +http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js +http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933 + + use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; + <form></form> + <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + id="$appletName" width="500" height="375" + codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> + <param name="movie" value="$codebase/$appletName.swf" /> + <param name="quality" value="high" /> + <param name="bgcolor" value="#869ca7" /> + <param name="allowScriptAccess" value="sameDomain" /> + <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" + width="$width" height="$height" name="$appletName" align="middle" id="$appletName" + play="true" loop="false" quality="high" allowScriptAccess="sameDomain" + type="application/x-shockwave-flash" + pluginspage="http://www.macromedia.com/go/getflashplayer"> + </embed> + + </object> + END_OBJECT_TEXT + + +=cut + +use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; + <form></form> + <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + id="$appletName" width="500" height="375" + codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> + <param name="movie" value="$codebase/$appletName.swf" /> + <param name="quality" value="high" /> + <param name="bgcolor" value="#869ca7" /> + <param name="allowScriptAccess" value="sameDomain" /> + <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7" + width="$width" height="$height" name="$appletName" align="middle" id="$appletName" + play="true" loop="false" quality="high" allowScriptAccess="sameDomain" + type="application/x-shockwave-flash" + pluginspage="http://www.macromedia.com/go/getflashplayer"> + </embed> + + </object> +END_OBJECT_TEXT + +sub new { + my $class = shift; + $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(), + @_ + ); + +} + + +package JavaApplet; +@ISA = qw(Applet); + + + +=pod + +The secret to making this applet work with IE in addition to normal browsers +is the addition of the C(<form></form>) construct just before the object. + +For some reason IE has trouble locating a flash object which is contained +within a form. Adding this second blank form with the larger problemMainForm +seems to solve the problem. + +This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2) +Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated: +http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js +http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933 + + use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; + <form></form> + <applet + code = "$code" + codebase = "$codebase" + archive = "$archive" + name = "$appletName" + id = "$appletName" + width = "$width" + height = "$height" + MAYSCRIPT + > + $parameters + </applet> + END_OBJECT_TEXT + +=cut + +use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT'; + <form></form> + <applet + code = "$code" + codebase = "$codebase" + archive = "$archive" + name = "$appletName" + id = "$appletName" + width = "$width" + height = "$height" + MAYSCRIPT + > + $parameters + </applet> +END_OBJECT_TEXT + +sub new { + my $class = shift; + $class -> SUPER::new( objectText => DEFAULT_OBJECT_TEXT(), + @_ + ); + +} + + + 1; \ No newline at end of file |