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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5624 - (download) (as text) (annotate)
Wed Mar 26 01:25:52 2008 UTC (11 years, 8 months ago) by gage
File size: 19834 byte(s)
Modified method in which parameters are passed to
flash applets (uses   FlashVars   parameter )

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: pg/lib/Applet.pm,v 1.6 2008/03/25 21:59:06 gage Exp $
    5 #
    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   my $appletName = "LineThruPointsWW";
   27   $applet = new FlashApplet(
   28      # can be replaced by $applet =FlashApplet() when using AppletObjects.pl
   29      codebase   => findAppletCodebase("$appletName.swf"),
   30      appletName => $appletName,
   31      appletId   => $appletName,
   32      submitActionAlias => 'checkAnswer',
   33   );
   34 
   35   ###################################
   36   # Configure applet
   37   ###################################
   38 
   39   #xml data to set up the problem-rac
   40   $applet->state(qq{<XML>
   41   <point xval='$xval_1' yval='$yval_1' />
   42   <point xval='$xval_2' yval='$yval_2' />
   43   </XML>});
   44 
   45 
   46   ###################################
   47   # insert applet header material
   48   ###################################
   49   HEADER_TEXT($applet->insertHeader );
   50 
   51   ###################################
   52   # Text section
   53   #
   54 
   55   ###################################
   56   #insert applet into body
   57   ###################################
   58   TEXT( MODES(TeX=>'object code', HTML=>$applet->insertObject));
   59 
   60 
   61 =head1 DESCRIPTION
   62 
   63 This file provides an object to store in one place
   64 all of the information needed to call an applet.
   65 
   66 The object FlashApplet has defaults for inserting flash applets.
   67 
   68 =over
   69 
   70 =item *
   71 
   72 =item *
   73 
   74 =back
   75 
   76 (not yet completed)
   77 
   78 The module JavaApplet has defaults for inserting java applets.
   79 
   80 The module Applet stores common code for the two types of applet.
   81 
   82 =head1 USAGE
   83 
   84 These modules are activate by listing it in the modules section of global.conf and rebooting the server.
   85 The companion file to this one is macros/AppletObjects.pl
   86 
   87 qw(Applet FlashApplet JavaApplet)
   88 
   89 =cut
   90 
   91 
   92 
   93 package Applet;
   94 
   95 use URI::Escape;
   96 
   97 
   98 
   99 use MIME::Base64 qw( encode_base64 decode_base64);
  100 
  101 
  102 =head2 Default javaScript functions placed in header
  103 
  104 These functions are automatically defined for use for
  105 any javaScript placed in the text of a PG question.
  106 
  107   getApplet(appletName)  -- finds the applet path in the DOM
  108 
  109   submitAction()            -- calls the submit action of the applets
  110 
  111 
  112     initializeAction()        -- calls the initialize action of the applets
  113 
  114     getQE(name)               -- gets an HTML element of the question by name
  115                                  or by id.  Be sure to keep all names and ids
  116                                  unique within a given PG question.
  117 
  118     getQuestionElement(name)  -- long form of getQE(name)
  119 
  120     listQuestionElements()    -- for discovering the names of inputs in the
  121                                  PG question.  An alert dialog will list all
  122                                  of the elements.
  123              Usage: Place this at the END of the question,
  124              just before END_DOCUMENT():
  125 
  126               TEXT(qq!<script> listQuestionElements() </script>!);
  127         ENDDOCUMENT();
  128 
  129     list of  accessor methods  format:  current_value = $self->method(new_value or empty)
  130 
  131     appletId         for simplicity and reliability appletId and appletName are always the same
  132     appletName
  133 
  134     archive      the name of the .jar file containing the applet code
  135     code         the name of the applet code in the .jar archive
  136     codebase     a prefix url used to find the archive and the applet itself
  137 
  138     height       rectangle alloted in the html page for displaying the applet
  139     width
  140 
  141     params       an anonymous array containing name/value pairs
  142                  to configure the applet [name =>'value, ...]
  143 
  144     header       stores the text to be added to the header section of the html page
  145         object       stores the text which places the applet on the html page
  146 
  147     debug        in debug mode several alerts mark progress through the procedure of calling the applet
  148 
  149     config       configuration are those customizable attributes of the applet which don't
  150                  change as it is used.  When stored in hidden answer fields
  151                  it is usually stored in base64 encoded format.
  152     base64_config base64 encode version of the contents of config
  153 
  154     configAlias  (default: config ) names the applet command called with the contents of $self->config
  155                  to configure the applet.  The parameters are passed to the applet in plain text using <xml>
  156                  The outer tags must be   <xml> .....   </xml>
  157     state        state consists of those customizable attributes of the applet which change
  158                  as the applet is used.  It is stored by the calling .pg question so that
  159                  when revisiting the question the applet
  160                  will be restored to the same state it was left in when the question was last
  161                  viewed.
  162 
  163     getStateAlias  (default: getState) alias for command called to read the current state of the applet.
  164                    The state is passed in plain text xml format with outer tags: <xml>....</xml>
  165     setStateAlias  (default: setState) alias for the command called to reset the  state of the applet.
  166                    The state is passed in plain text in xml format with outer tags: <xml>....</xml>
  167 
  168     base64_state   returns the base64 encoded version of the state stored in the applet object.
  169 
  170     initializeActionAlias  -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState
  171         submitActionAlias      -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the
  172                                   .pg question is pressed.
  173 
  174     returnFieldName
  175 
  176 
  177 
  178 
  179 
  180 =cut
  181 
  182 
  183 
  184 
  185 sub new {
  186    my $class = shift;
  187    my $self = {
  188     appletName =>'',
  189     code=>'',
  190     codebase=>'',
  191 #   appletId  =>'',   #always use identical applet Id's and applet Names
  192     params    =>undef,
  193     width     => 550,
  194     height    => 400,
  195     base64_state       =>  '',
  196     base64_config      =>  '',
  197     getStateAlias      =>  'getXML',
  198     setStateAlias      =>  'setState',
  199     configAlias        =>  'config',
  200     initializeActionAlias => 'setXML',
  201     submitActionAlias  =>  'getXML',
  202     returnFieldName    =>  'receivedField',
  203     headerText         =>  DEFAULT_HEADER_TEXT(),
  204     objectText         => '',
  205     debug              => 0,
  206     @_,
  207   };
  208   bless $self, $class;
  209   return $self;
  210 }
  211 
  212 sub  header {
  213   my $self = shift;
  214   if ($_[0] eq "reset") {  # $applet->header('reset');  erases default header text.
  215     $self->{headerText}='';
  216   } else {
  217     $self->{headerText} .= join("",@_);  # $applet->header(new_text); concatenates new_text to existing header.
  218   }
  219     $self->{headerText};
  220 }
  221 sub  object {
  222   my $self = shift;
  223   if ($_[0] eq "reset") {
  224     $self->{objectText}='';
  225   } else {
  226     $self->{objectText} .= join("",@_);
  227   }
  228     $self->{objectText};
  229 }
  230 sub params {
  231   my $self = shift;
  232   if (ref($_[0]) =~/HASH/) {
  233     $self->{params} = shift;
  234   } elsif ( !defined($_[0]) or $_[0] =~ '') {
  235     # do nothing (read)
  236   } else {
  237     warn "You must enter a reference to a hash for the parameter list";
  238   }
  239   $self->{params};
  240 }
  241 
  242 sub initializeActionAlias {
  243   my $self = shift;
  244   $self->{initializeActionAlias} = shift ||$self->{initializeActionAlias}; # replace the current contents if non-empty
  245     $self->{initializeActionAlias};
  246 }
  247 
  248 sub submitActionAlias {
  249   my $self = shift;
  250   $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty
  251     $self->{submitActionAlias};
  252 }
  253 sub getStateAlias {
  254   my $self = shift;
  255   $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty
  256     $self->{getStateAlias};
  257 }
  258 
  259 sub setStateAlias {
  260   my $self = shift;
  261   $self->{setStateAlias} = shift ||$self->{setStateAlias}; # replace the current contents if non-empty
  262     $self->{setStateAlias};
  263 }
  264 sub configAlias {
  265   my $self = shift;
  266   $self->{configAlias} = shift ||$self->{configAlias}; # replace the current contents if non-empty
  267     $self->{configAlias};
  268 }
  269 sub returnFieldName {
  270   my $self = shift;
  271   $self->{returnFieldName} = shift ||$self->{returnFieldName}; # replace the current contents if non-empty
  272     $self->{returnFieldName};
  273 }
  274 sub codebase {
  275   my $self = shift;
  276   $self->{codebase} = shift ||$self->{codebase}; # replace the current codebase if non-empty
  277     $self->{codebase};
  278 }
  279 sub code {
  280   my $self = shift;
  281   $self->{code} = shift ||$self->{code}; # replace the current code if non-empty
  282     $self->{code};
  283 }
  284 sub height {
  285   my $self = shift;
  286   $self->{height} = shift ||$self->{height}; # replace the current height if non-empty
  287     $self->{height};
  288 }
  289 sub width {
  290   my $self = shift;
  291   $self->{width} = shift ||$self->{width}; # replace the current width if non-empty
  292     $self->{width};
  293 }
  294 sub archive {
  295   my $self = shift;
  296   $self->{archive} = shift ||$self->{archive}; # replace the current archive if non-empty
  297     $self->{archive};
  298 }
  299 sub appletName {
  300   my $self = shift;
  301   $self->{appletName} = shift ||$self->{appletName}; # replace the current appletName if non-empty
  302     $self->{appletName};
  303 }
  304 sub debug {
  305   my $self = shift;
  306   my $new_flag = shift;
  307   $self->{debug} = $new_flag if defined($new_flag);
  308   $self->{debug};
  309 }
  310 sub appletId {
  311   appletName(@_);
  312 }
  313 sub state {
  314   my $self = shift;
  315   my $str = shift;
  316   $self->{base64_state} =  encode_base64($str)   ||$self->{base64_state}; # replace the current string if non-empty
  317   $self->{base64_state} =~ s/\n//g;
  318     decode_base64($self->{base64_state});
  319 }
  320 
  321 sub base64_state{
  322   my $self = shift;
  323   $self->{base64_state} = shift ||$self->{base64_state}; # replace the current string if non-empty
  324     $self->{base64_state};
  325 }
  326 sub config {
  327   my $self = shift;
  328   my $str = shift;
  329   $self->{base64_config} =  encode_base64($str)   || $self->{base64_config}; # replace the current string if non-empty
  330   $self->{base64_config} =~ s/\n//g;
  331     decode_base64($self->{base64_config});
  332 }
  333 sub base64_config {
  334   my $self = shift;
  335   $self->{base64_config} = shift ||$self->{base64_config}; # replace the current string if non-empty
  336   $self->{base64_config} =$self->{base64_config};
  337     $self->{base64_config};
  338 }
  339 #FIXME
  340 # need to be able to adjust header material
  341 
  342 sub insertHeader {
  343     my $self = shift;
  344     my $codebase         =  $self->codebase;
  345     my $appletId         =  $self->appletId;
  346     my $appletName       =  $self->appletName;
  347     my $base64_initialState     = $self->base64_state;
  348     my $initializeAction =  $self->initializeActionAlias;
  349     my $submitAction     =  $self->submitActionAlias;
  350     my $setState         =  $self->setStateAlias;
  351     my $getState         =  $self->getStateAlias;
  352     my $config           =  $self->configAlias;
  353     my $base64_config    =  $self->base64_config;
  354     my $debugMode        =  ($self->debug) ? "1": "0";
  355     my $returnFieldName  =  $self->{returnFieldName};
  356 #    my $encodeStateQ    =  ($self->debug)?'' : "state = Base64.encode(state);";              # in debug mode base64 encoding is not used.
  357 #     my $decodeStateQ   =  "if (!state.match(/<XML>*/i) ) {state = Base64.decode(state)}";   # decode if <XML> is not present
  358     my $headerText       =  $self->header();
  359 
  360     $headerText =~ s/(\$\w+)/$1/gee;   # interpolate variables p17 of Cookbook
  361 
  362     return $headerText;
  363 
  364 
  365 }
  366 
  367 sub insertObject {
  368     my $self       = shift;
  369     my $code       = $self->{code};
  370     my $codebase   = $self->{codebase};
  371     my $appletId   = $self->{appletName};
  372     my $appletName = $self->{appletName};
  373     my $archive    = $self->{archive};
  374     my $width      = $self->{width};
  375     my $height     = $self->{height};
  376     my $javaParameters = '';
  377     my $flashParameters = '';
  378     my %param_hash = %{$self->params()};
  379     foreach my $key (keys %param_hash) {
  380       $javaParameters .= qq!<param name ="$key"  value = "$param_hash{$key}">\n!;
  381       $flashParameters .= uri_escape($key).'='.uri_escape($param_hash{$key}).'&';
  382     }
  383     $flashParameters =~ s/\&$//;    # trim last &
  384 
  385 
  386     $objectText = $self->{objectText};
  387     $objectText =~ s/(\$\w+)/$1/gee;
  388     return $objectText;
  389 }
  390 sub initialize  {
  391     my $self = shift;
  392   return q{
  393     <script>
  394       initializeAction();
  395       // this should really be done in the <body> tag
  396     </script>
  397   };
  398 
  399 }
  400 
  401 
  402 use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT';
  403 
  404   <script language="JavaScript">
  405   var debug = $debugMode;
  406   //
  407   //CONFIGURATIONS
  408   //
  409     // configurations are "permanent"
  410     applet_config_list["$appletName"]   = function() {
  411         if (debug) { alert("configure $appletName . $config ( $base64_config )");}
  412       try {
  413           if (debug || !( typeof(getApplet("$appletName").$config)  == "undefined" ) ) {
  414 
  415           getApplet("$appletName").$config(Base64.decode("$base64_config"));
  416         }
  417       } catch(e) {
  418         alert("error executing configuration command $config for $appletName: " + e );
  419       }
  420     }
  421     //
  422     //STATE
  423     //
  424     // state can vary as the applet is manipulated.
  425     applet_setState_list["$appletName"] = function(state) {
  426         state =  state || getQE("$appletName"+"_state").value
  427         if (state.match("\S") ) {  // if state is not all white space
  428         if ( base64Q(state) ) {
  429         state=Base64.decode(state);
  430         }
  431         if (debug) { alert("set state for $appletName to " + state);}
  432         try {
  433         if (debug || !( typeof(getApplet("$appletName").$setState)  =="undefined" ) ) {
  434           getApplet("$appletName").$setState( state );
  435         }
  436         } catch(e) {
  437         alert("Error in setting state of $appletName using command $setState : " + e );
  438         }
  439        }
  440   };
  441   applet_getState_list["$appletName"] = function () {
  442       if (debug) { alert("getState for applet $appletName");}
  443       try {
  444         var applet = getApplet("$appletName");
  445         var state;
  446         if (!( typeof(getApplet("$appletName").$getState)  =="undefined" ) ) {
  447           state  = applet.$getState();               // get state in xml format
  448         }
  449         if (!debug) {state = Base64.encode(state) };   // replace state by encoded version
  450         getQE("$appletName"+"_state").value = state;   //place in state htmlItem (debug: textarea, otherwise hidden)
  451       } catch (e) {
  452         alert("Error in getting state for $appletName " + e );
  453       }
  454     };
  455     //
  456     //INITIALIZE
  457     //
  458     applet_initializeAction_list["$appletName"] = function () {
  459           applet_setState_list["$appletName"]();
  460   };
  461 
  462   applet_submitAction_list["$appletName"] = function () {
  463           applet_getState_list["$appletName"]();
  464       getQE("$returnFieldName").value = getApplet("$appletName").sendData();
  465     };
  466     </script>
  467 
  468 END_HEADER_SCRIPT
  469 
  470 package FlashApplet;
  471 @ISA = qw(Applet);
  472 
  473 
  474 
  475 =pod
  476 
  477 The secret to making this applet work with IE in addition to normal browsers
  478 is the addition of the C(<form></form>) construct just before the object.
  479 
  480 For some reason IE has trouble locating a flash object which is contained
  481 within a form.  Adding this second blank form with the larger problemMainForm
  482 seems to solve the problem.
  483 
  484 This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2)
  485 Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated:
  486 http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js
  487 http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933
  488 
  489     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
  490       <form></form>
  491       <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
  492            id="$appletName" width="500" height="375"
  493            codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
  494          <param name="movie" value="$codebase/$appletName.swf" />
  495          <param name="quality" value="high" />
  496          <param name="bgcolor" value="#869ca7" />
  497          <param name="allowScriptAccess" value="sameDomain" />
  498          <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7"
  499            width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
  500            play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
  501            type="application/x-shockwave-flash"
  502            pluginspage="http://www.macromedia.com/go/getflashplayer">
  503          </embed>
  504 
  505        </object>
  506     END_OBJECT_TEXT
  507 
  508 
  509 =cut
  510 
  511 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
  512   <form></form>
  513   <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
  514              id="$appletName" width="500" height="375"
  515              codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
  516          <param name="movie" value="$codebase/$appletName.swf" />
  517          <param name="quality" value="high" />
  518          <param name="bgcolor" value="#869ca7" />
  519          <param name="allowScriptAccess" value="sameDomain" />
  520          <param name="FlashVars" value="$flashParameters"/>
  521          <embed src="$codebase/$appletName.swf" quality="high" bgcolor="#869ca7"
  522              width="$width" height="$height" name="$appletName" align="middle" id="$appletName"
  523              play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
  524              type="application/x-shockwave-flash"
  525              pluginspage="http://www.macromedia.com/go/getflashplayer"
  526              FlashVars="$flashParameters">
  527          </embed>
  528 
  529      </object>
  530 END_OBJECT_TEXT
  531 
  532 sub new {
  533     my $class = shift;
  534   $class -> SUPER::new( objectText   => DEFAULT_OBJECT_TEXT(),
  535                   @_
  536   );
  537 
  538 }
  539 
  540 
  541 package JavaApplet;
  542 @ISA = qw(Applet);
  543 
  544 
  545 
  546 =pod
  547 
  548 The secret to making this applet work with IE in addition to normal browsers
  549 is the addition of the C(<form></form>) construct just before the object.
  550 
  551 For some reason IE has trouble locating a flash object which is contained
  552 within a form.  Adding this second blank form with the larger problemMainForm
  553 seems to solve the problem.
  554 
  555 This follows method2 of the advice given in url(http://kb.adobe.com/selfservice/viewContent.do?externalId=kb400730&sliceId=2)
  556 Method1 and methods involving SWFObject(Geoff Stearns) and SWFFormFix (Steve Kamerman) have yet to be fully investigated:
  557 http://devel.teratechnologies.net/swfformfix/swfobject_swfformfix_source.js
  558 http://www.teratechnologies.net/stevekamerman/index.php?m=01&y=07&entry=entry070101-033933
  559 
  560     use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
  561       <form></form>
  562      <applet
  563       code     = "$code"
  564       codebase = "$codebase"
  565       archive  = "$archive"
  566       name     = "$appletName"
  567       id       = "$appletName"
  568       width    = "$width"
  569       height   = "$height"
  570       MAYSCRIPT
  571      >
  572       $javaParameters
  573      </applet>
  574     END_OBJECT_TEXT
  575 
  576 =cut
  577 
  578 use constant DEFAULT_OBJECT_TEXT =><<'END_OBJECT_TEXT';
  579   <form></form>
  580  <applet
  581   code     = "$code"
  582   codebase = "$codebase"
  583   archive  = "$archive"
  584   name     = "$appletName"
  585     id       = "$appletName"
  586     width    = "$width"
  587     height   = "$height"
  588     MAYSCRIPT
  589  >
  590   $javaParameters
  591  </applet>
  592 END_OBJECT_TEXT
  593 
  594 sub new {
  595     my $class = shift;
  596   $class -> SUPER::new( objectText   => DEFAULT_OBJECT_TEXT(),
  597                   @_
  598   );
  599 
  600 }
  601 
  602 
  603 
  604 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9