[system] / trunk / webwork / system / courseScripts / PGbasicmacros.pl Repository:
ViewVC logotype

View of /trunk/webwork/system/courseScripts/PGbasicmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 30 - (download) (as text) (annotate)
Tue Jun 19 22:29:01 2001 UTC (18 years, 8 months ago) by gage
File size: 52195 byte(s)
More refinement of the initialization routines in PGbasicmacros.pl and the
way it is called from dangerousMacros.pl

    1 #!/usr/local/bin/webwork-perl
    2 
    3 ####################################################################
    4 # Copyright @ 1995-1998 University of Rochester
    5 # All Rights Reserved
    6 ####################################################################
    7 
    8 =head1 NAME
    9 
   10     PGbasicmacros.pl --- located in the courseScripts directory
   11 
   12 =head1 SYNPOSIS
   13 
   14 
   15 
   16 =head1 DESCRIPTION
   17 
   18 
   19 
   20 =cut
   21 
   22 # this is equivalent to use strict, but can be used within the Safe compartment.
   23 BEGIN{
   24   be_strict;
   25 }
   26 sub _PGbasicmacros_export {
   27   my @EXPORT = ( '&NAMED_ANS_RULE', '&NAMED_ANS_RULE_OPTION',
   28     '&NAMED_ANS_RULE_EXTENSION', '&ANS_RULE', '&NAMED_ANS_BOX', '&ANS_BOX',
   29     '&NAMED_ANS_RADIO', '&NAMED_ANS_RADIO_OPTION',
   30     '&NAMED_ANS_RADIO_BUTTONS', '&ANS_RADIO', '&ANS_RADIO_OPTION',
   31     '&ANS_RADIO_BUTTONS', '&NAMED_ANS_CHECKBOX',
   32     '&NAMED_ANS_CHECKBOX_OPTION', '&NAMED_ANS_CHECKBOX_BUTTONS',
   33     '&ANS_CHECKBOX', '&ANS_CHECKBOX_OPTION', '&ANS_CHECKBOX_BUTTONS',
   34     '&ans_rule', '&ans_rule_extension', '&ans_radio_buttons',
   35     '&ans_checkbox', '&tex_ans_rule', '&tex_ans_rule_extension', '&ans_box',
   36     '&checkbox', '&NAMED_POP_UP_LIST', '&pop_up_list', '&solution',
   37     '&SOLUTION', '&hint', '&HINT', '&random', '&non_zero_random', '&SRAND',
   38     '&TEX', '&M3', '&MODES', '&_PGbasicmacros_export',
   39     '&_PGbasicmacros_initialize', '&ALPHABET', '&PAR', '&BR', '&LQ', '&RQ',
   40     '&BM', '&EM', '&BDM', '&EDM', '&LTS', '&GTS', '&LTE', '&GTE',
   41     '&BEGIN_ONE_COLUMN', '&END_ONE_COLUMN', '&SOLUTION_HEADING',
   42     '&HINT_HEADING', '&US', '&SPACE', '&BBOLD', '&EBOLD', '&HR', '&LBRACE',
   43     '&RBRACE', '&LB', '&RB', '&DOLLAR', '&PERCENT', '&CARET', '&PI', '&E',
   44     '&ev_substring', '&safe_ev', '&old_safe_ev', '&FEQ', '&math_ev3',
   45     '&display_math_ev3', '&EV2', '&EV3', '&beginproblem',
   46     '&protect_underbar', '&OL', '&htmlLink', '&appletLink', '&spf', '&sspf',
   47     '&lex_sort', '&num_sort', '&begintable', '&endtable', '&row', '&image',
   48     '&images', '&caption', '&captions', '&imageRow', '&display_options2',
   49     '&_PGbasicmacros_init','&test'
   50   );
   51   @EXPORT;
   52 }
   53 my $displayMode=$main::displayMode;
   54 
   55 my ($PAR,
   56   $BR,
   57   $LQ,
   58   $RQ,
   59   $BM,
   60   $EM,
   61   $BDM,
   62   $EDM,
   63   $LTS,
   64   $GTS,
   65   $LTE,
   66   $GTE,
   67   $BEGIN_ONE_COLUMN,
   68   $END_ONE_COLUMN,
   69   $SOL,
   70   $HINT,
   71   $US,
   72   $SPACE,
   73   $BBOLD,
   74   $EBOLD,
   75   $HR,
   76   $LBRACE,
   77   $RBRACE,
   78   $LB,
   79   $RB,
   80   $DOLLAR,
   81   $PERCENT,
   82   $CARET,
   83   $PI,
   84   $E,
   85   @ALPHABET,
   86   );
   87 #_PGbasicmacros_init();
   88 sub _PGbasicmacros_init {
   89     PG_restricted_eval( <<'EOF');
   90   $main::PAR        = PAR();
   91   $main::BR       = BR();
   92   $main::LQ       = LQ();
   93   $main::RQ       = RQ();
   94   $main::BM       = BM();
   95   $main::EM       = EM();
   96   $main::BDM        = BDM();
   97   $main::EDM        = EDM();
   98   $main::LTS        = LTS();
   99   $main::GTS        = GTS();
  100   $main::LTE        = LTE();
  101   $main::GTE        = GTE();
  102   $main::BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN();
  103   $main::END_ONE_COLUMN = END_ONE_COLUMN();
  104   $main::SOL        = SOLUTION_HEADING();
  105   $main::HINT       = HINT_HEADING();
  106   $main::US       = US();
  107   $main::SPACE      = SPACE();
  108   $main::BBOLD      = BBOLD();
  109   $main::EBOLD      = EBOLD();
  110   $main::HR       = HR();
  111   $main::LBRACE     = LBRACE();
  112   $main::RBRACE     = RBRACE();
  113   $main::LB       = LB();
  114   $main::RB       = RB();
  115   $main::DOLLAR     = DOLLAR();
  116   $main::PERCENT      = PERCENT();
  117   $main::CARET      = CARET();
  118   $main::PI       = PI();
  119   $main::E        = E();
  120   @main::ALPHABET     = ('A'..'ZZ');
  121 
  122   $PAR        = PAR();
  123   $BR       = BR();
  124   $LQ       = LQ();
  125   $RQ       = RQ();
  126   $BM       = BM();
  127   $EM       = EM();
  128   $BDM        = BDM();
  129   $EDM        = EDM();
  130   $LTS        = LTS();
  131   $GTS        = GTS();
  132   $LTE        = LTE();
  133   $GTE        = GTE();
  134   $BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN();
  135   $END_ONE_COLUMN = END_ONE_COLUMN();
  136   $SOL        = SOLUTION_HEADING();
  137   $HINT       = HINT_HEADING();
  138   $US       = US();
  139   $SPACE      = SPACE();
  140   $BBOLD      = BBOLD();
  141   $EBOLD      = EBOLD();
  142   $HR       = HR();
  143   $LBRACE     = LBRACE();
  144   $RBRACE     = RBRACE();
  145   $LB       = LB();
  146   $RB       = RB();
  147   $DOLLAR     = DOLLAR();
  148   $PERCENT      = PERCENT();
  149   $CARET      = CARET();
  150   $PI       = PI();
  151   $E        = E();
  152   @ALPHABET     = ('A'..'ZZ');
  153 
  154   $displayMode    =$main::displayMode;
  155 EOF
  156 
  157 }
  158 sub test {
  159   "PI is $PI $BR displayMode is $displayMode";
  160 }
  161 #_PGbasicmacros_init();  # initialize constants
  162 
  163 =head2  Answer blank macros:
  164 
  165 These produce answer blanks of various sizes or pop up lists or radio answer buttons.
  166 The names for the answer blanks are
  167 generated implicitly.
  168 
  169   ans_rule( width )
  170   tex_ans_rule( width )
  171   ans_radio_buttons(value1=>label1, value2,label2 => value3,label3=>...)
  172   pop_up_list(@list)  # list consists of (value => label,  PR => "Product rule",...)
  173 
  174 To indicate the checked position of radio buttons put a '%' in front of the value: C<ans_radio_buttons(1, 'Yes','%2','No')>
  175 will have 'No' checked.  C<tex_ans_rule> works inside math equations in C<HTML_tth> mode.  It does not work in C<Latex2HTML> mode
  176 since this mode produces gif pictures.
  177 
  178 
  179 The following method is defined in F<PG.pl> for entering the answer evaluators corresponding
  180 to answer rules with automatically generated names.  The answer evaluators are matched with the
  181 answer rules in the order in which they appear on the page.
  182 
  183   ANS(ans_evaluator1, ans_evaluator2,...);
  184 
  185 These are more primitive macros which produce answer blanks for specialized cases when complete
  186 control over the matching of answers blanks and answer evaluators is desired.
  187 The names of the answer blanks must be generated manually, and it is best if they do NOT begin
  188 with the default answer prefix (currently AnSwEr).
  189 
  190 
  191   NAMED_ANS_RULE(name, width)
  192   NAMED_ANS_BOX(name, rows, cols)
  193   NAMED_ANS_RADIO(name, value,label,)
  194   NAMED_ANS_RADIO_OPTION(name, value,label)
  195   NAMED_ANS_RADIO_BUTTONS(name,value1,label1,value2,label2,...)
  196   check_box('-name' =>answer5,'-value' =>'statement3','-label' =>'I loved this course!'   )
  197   NAMED_POP_UP_LIST($name, @list) # list consists of (value => tag,  PR => "Product rule",...)
  198 
  199 (Name is the name of the variable, value is the value given to the variable when this option is selected,
  200 and label is the text printed next to the button or check box.    Check box variables can have multiple values.)
  201 
  202 NAMED_ANS_RADIO_BUTTONS creates a sequence of NAMED_ANS_RADIO and NAMED_ANS_RADIO_OPTION  items which
  203 are  output either as an array or, in scalar context, as the array glued together with spaces.  It is
  204 usually easier to use this than to manually construct the radio buttons by hand.  However, sometimes
  205  extra flexibility is desiredin which case:
  206 
  207 When entering radio buttons using the "NAMED" format, you should use NAMED_ANS_RADIO button for the first button
  208 and then use NAMED_ANS_RADIO_OPTION for the remaining buttons.  NAMED_ANS_RADIO requires a matching answer evalutor,
  209 while NAMED_ANS_RADIO_OPTION does not. The name used for NAMED_ANS_RADIO_OPTION should match the name
  210 used for NAMED_ANS_RADIO (and the associated answer evaluator).
  211 
  212 
  213 
  214 The following method is defined in in F<PG.pl> for entering the answer evaluators corresponding
  215 to answer rules with automatically generated names.  The answer evaluators are matched with the
  216 answer rules in the order in which they appear on the page.
  217 
  218       NAMED_ANS(name1 => ans_evaluator1, name2 => ans_evaluator2,...);
  219 
  220 These auxiliary macros are defined in PG.pl
  221 
  222 
  223   NEW_ANS_NAME( number );   # produces a new answer blank name from a number by adding a prefix (AnSwEr)
  224                             # and registers this name as an implicitly labeled answer
  225                             # Its use is paired with each answer evaluator being entered using ANS()
  226 
  227     ANS_NUM_TO_NAME(number);  # adds the prefix (AnSwEr) to the number, but does nothing else.
  228 
  229   RECORD_ANS_NAME( name );  # records the order in which the answer blank  is rendered
  230                             # This is called by all of the constructs above, but must
  231                             # be called explicitly if an input blank is constructed explictly
  232                             # using HTML code.
  233 
  234 These are legacy macros:
  235 
  236   ANS_RULE( number, width );                  # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME(number), width)
  237   ANS_BOX( question_number,height, width );       # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME(number), height, width)
  238   ANS_RADIO( question_number, value,tag );        # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME(number), value,tag)
  239   ANS_RADIO_OPTION( question_number, value,tag );   # equivalent to NAMED_ANS_RADIO_OPTION( ANS_NUM_TO_NAME(number), value,tag)
  240 
  241 
  242 =cut
  243 
  244 
  245 sub NAMED_ANS_RULE {
  246   my($name,$col) = @_;
  247   my $len = 0.07*$col;
  248   my $answer_value = '';
  249   $answer_value = ${$main::inputs_ref}{$name} if    defined(${$main::inputs_ref}{$name});
  250     if ($answer_value =~ /\0/ ) {
  251       my @answers = split("\0", $answer_value);
  252       $answer_value = shift(@answers);  # use up the first answer
  253       $main::rh_sticky_answers{$name}=\@answers;  # store the rest
  254       $answer_value= '' unless defined($answer_value);
  255   }
  256   $name = RECORD_ANS_NAME($name);
  257   MODES(
  258     TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ",
  259     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  260     HTML => "<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"$answer_value\">\n"
  261   );
  262 }
  263 
  264 sub NAMED_ANS_RULE_OPTION {
  265   &NAMED_ANS_RULE_EXTENSION;
  266 }
  267 
  268 sub NAMED_ANS_RULE_EXTENSION {
  269   my($name,$col) = @_;
  270   my $len = 0.07*$col;
  271   my $answer_value = '';
  272   $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name});
  273   if ( defined($main::rh_sticky_answers{$name}) ) {
  274     $answer_value = shift( @{$main::rh_sticky_answers{$name}});
  275     $answer_value = '' unless defined($answer_value);
  276   }
  277   MODES(
  278     TeX => '\\hrulefill\\quad ',
  279     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  280     HTML => qq!<INPUT TYPE=TEXT SIZE=$col NAME = "$name" VALUE = "$answer_value">\n!
  281   );
  282 }
  283 
  284 sub ANS_RULE {
  285   my($number,$col) = @_;
  286   my $name = NEW_ANS_NAME($number);
  287     NAMED_ANS_RULE($name,$col);
  288 }
  289 
  290 
  291 sub  NAMED_ANS_BOX {
  292   my($name,$row,$col) = @_;
  293   $row = 10 unless defined($row);
  294   $col = 80 unless defined($col);
  295   $name = RECORD_ANS_NAME($name);
  296   my $len = 0.07*$col;
  297   my $height = .07*$row;
  298   my $answer_value = '';
  299   $answer_value = $main::inputs_ref->{$name} if defined( $main::inputs_ref->{$name} );
  300   my $out = M3(
  301        qq!\\vskip $height in \\hrulefill\\quad !,
  302        qq!\\begin{rawhtml}<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  303                WRAP="VIRTUAL">$answer_value</TEXTAREA>\\end{rawhtml}!,
  304          qq!<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  305                WRAP="VIRTUAL">$answer_value</TEXTAREA>!
  306          );
  307   $out;
  308 }
  309 
  310 sub  ANS_BOX {
  311   my($number,$row,$col) = @_;
  312   my $name = NEW_ANS_NAME($number);
  313     NAMED_ANS_BOX($name,$row,$col);
  314 }
  315 
  316 sub NAMED_ANS_RADIO {
  317   my $name = shift;
  318   my $value = shift;
  319     my $tag =shift;
  320     $name = RECORD_ANS_NAME($name);
  321     my $checked = '';
  322     if ($value =~/^\%/) {
  323       $value =~ s/^\%//;
  324       $checked = 'CHECKED'
  325     }
  326   if (defined($main::inputs_ref->{$name}) ) {
  327     if ($main::inputs_ref->{$name} eq $value) {
  328       $checked = 'CHECKED'
  329     } else {
  330       $checked = '';
  331     }
  332 
  333     }
  334 
  335   MODES(
  336     TeX => qq!\\item{$tag}\n!,
  337     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  338     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  339   );
  340 
  341 }
  342 
  343 sub NAMED_ANS_RADIO_OPTION {
  344   my $name = shift;
  345   my $value = shift;
  346   my $tag =shift;
  347 
  348 
  349     my $checked = '';
  350     if ($value =~/^\%/) {
  351       $value =~ s/^\%//;
  352       $checked = 'CHECKED'
  353     }
  354   if (defined($main::inputs_ref->{$name}) ) {
  355     if ($main::inputs_ref->{$name} eq $value) {
  356       $checked = 'CHECKED'
  357     } else {
  358       $checked = '';
  359     }
  360 
  361     }
  362 
  363   MODES(
  364     TeX => qq!\\item{$tag}\n!,
  365     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  366     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  367   );
  368 
  369 }
  370 
  371 sub NAMED_ANS_RADIO_BUTTONS {
  372     my $name  =shift;
  373     my $value = shift;
  374     my $tag = shift;
  375 
  376 
  377   my @out = ();
  378   push(@out, NAMED_ANS_RADIO($name, $value,$tag));
  379   my @buttons = @_;
  380   while (@buttons) {
  381     $value = shift @buttons;  $tag = shift @buttons;
  382     push(@out, NAMED_ANS_RADIO_OPTION($name, $value,$tag));
  383   }
  384   (wantarray) ? @out : join(" ",@out);
  385 }
  386 sub ANS_RADIO {
  387   my $number = shift;
  388   my $value = shift;
  389   my $tag =shift;
  390     my $name = NEW_ANS_NAME($number);
  391   NAMED_ANS_RADIO($name,$value,$tag);
  392 }
  393 
  394 sub ANS_RADIO_OPTION {
  395   my $number = shift;
  396   my $value = shift;
  397   my $tag =shift;
  398 
  399 
  400     my $name = ANS_NUM_TO_NAME($number);
  401   NAMED_ANS_RADIO_OPTION($name,$value,$tag);
  402 }
  403 sub ANS_RADIO_BUTTONS {
  404     my $number  =shift;
  405     my $value = shift;
  406     my $tag = shift;
  407 
  408 
  409   my @out = ();
  410   push(@out, ANS_RADIO($number, $value,$tag));
  411   my @buttons = @_;
  412   while (@buttons) {
  413       $value = shift @buttons; $tag = shift @buttons;
  414     push(@out, ANS_RADIO_OPTION($number, $value,$tag));
  415   }
  416   (wantarray) ? @out : join(" ",@out);
  417 }
  418 
  419 sub NAMED_ANS_CHECKBOX {
  420   my $name = shift;
  421   my $value = shift;
  422     my $tag =shift;
  423     $name = RECORD_ANS_NAME($name);
  424 
  425     my $checked = '';
  426     if ($value =~/^\%/) {
  427       $value =~ s/^\%//;
  428       $checked = 'CHECKED'
  429     }
  430 
  431   if (defined($main::inputs_ref->{$name}) ) {
  432     if ($main::inputs_ref->{$name} eq $value) {
  433       $checked = 'CHECKED'
  434     }
  435     else {
  436       $checked = '';
  437     }
  438 
  439     }
  440 
  441   MODES(
  442     TeX => qq!\\item{$tag}\n!,
  443     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  444     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  445   );
  446 
  447 }
  448 
  449 sub NAMED_ANS_CHECKBOX_OPTION {
  450   my $name = shift;
  451   my $value = shift;
  452   my $tag =shift;
  453 
  454     my $checked = '';
  455     if ($value =~/^\%/) {
  456       $value =~ s/^\%//;
  457       $checked = 'CHECKED'
  458     }
  459 
  460   if (defined($main::inputs_ref->{$name}) ) {
  461     if ($main::inputs_ref->{$name} eq $value) {
  462       $checked = 'CHECKED'
  463     }
  464     else {
  465       $checked = '';
  466     }
  467 
  468     }
  469 
  470   MODES(
  471     TeX => qq!\\item{$tag}\n!,
  472     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  473     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  474   );
  475 
  476 }
  477 
  478 sub NAMED_ANS_CHECKBOX_BUTTONS {
  479     my $name  =shift;
  480     my $value = shift;
  481     my $tag = shift;
  482 
  483   my @out = ();
  484   push(@out, NAMED_ANS_CHECKBOX($name, $value,$tag));
  485 
  486   my @buttons = @_;
  487   while (@buttons) {
  488     $value = shift @buttons;  $tag = shift @buttons;
  489     push(@out, NAMED_ANS_CHECKBOX_OPTION($name, $value,$tag));
  490   }
  491 
  492   (wantarray) ? @out : join(" ",@out);
  493 }
  494 
  495 sub ANS_CHECKBOX {
  496   my $number = shift;
  497   my $value = shift;
  498   my $tag =shift;
  499     my $name = NEW_ANS_NAME($number);
  500 
  501   NAMED_ANS_CHECKBOX($name,$value,$tag);
  502 }
  503 
  504 sub ANS_CHECKBOX_OPTION {
  505   my $number = shift;
  506   my $value = shift;
  507   my $tag =shift;
  508     my $name = ANS_NUM_TO_NAME($number);
  509 
  510   NAMED_ANS_CHECKBOX_OPTION($name,$value,$tag);
  511 }
  512 
  513 sub ANS_CHECKBOX_BUTTONS {
  514     my $number  =shift;
  515     my $value = shift;
  516     my $tag = shift;
  517 
  518   my @out = ();
  519   push(@out, ANS_CHECKBOX($number, $value, $tag));
  520 
  521   my @buttons = @_;
  522   while (@buttons) {
  523     $value = shift @buttons;  $tag = shift @buttons;
  524     push(@out, ANS_CHECKBOX_OPTION($number, $value,$tag));
  525   }
  526 
  527   (wantarray) ? @out : join(" ",@out);
  528 }
  529 
  530 sub ans_rule {
  531   my $len = shift;     # gives the optional length of the answer blank
  532   $len    = 20 unless $len ;
  533   my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  534   NAMED_ANS_RULE($name ,$len);
  535 }
  536 sub ans_rule_extension {
  537   my $len = shift;
  538     $len    = 20 unless $len ;
  539   my $name = NEW_ANS_NAME($main::ans_rule_count);  # don't update the answer name
  540   NAMED_ANS_RULE($name ,$len);
  541 }
  542 sub ans_radio_buttons {
  543   my $name  = NEW_ANS_NAME(++$main::ans_rule_count);
  544   my @radio_buttons = NAMED_ANS_RADIO_BUTTONS($name, @_);
  545 
  546   if ($displayMode eq 'TeX') {
  547     $radio_buttons[0] = "\n\\begin{itemize}\n" . $radio_buttons[0];
  548     $radio_buttons[$#radio_buttons] .= "\n\\end{itemize}\n";
  549   }
  550 
  551   (wantarray) ? @radio_buttons: join(" ", @radio_buttons);
  552 }
  553 
  554 #added 6/14/2000 by David Etlinger
  555 sub ans_checkbox {
  556   my $name = NEW_ANS_NAME( ++$main::ans_rule_count );
  557   my @checkboxes = NAMED_ANS_CHECKBOX_BUTTONS( $name, @_ );
  558 
  559   if ($displayMode eq 'TeX') {
  560     $checkboxes[0] = "\n\\begin{itemize}\n" . $checkboxes[0];
  561     $checkboxes[$#checkboxes] .= "\n\\end{itemize}\n";
  562   }
  563 
  564   (wantarray) ? @checkboxes: join(" ", @checkboxes);
  565 }
  566 
  567 
  568 ## define a version of ans_rule which will work inside TeX math mode or display math mode -- at least for tth mode.
  569 ## This is great for displayed fractions.
  570 ## This will not work with latex2HTML mode since it creates gif equations.
  571 
  572 sub tex_ans_rule {
  573   my $len = shift;
  574   $len    = 20 unless $len ;
  575     my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  576     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  577     my $out = MODES(
  578                      'TeX' => $answer_rule,
  579                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  580                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  581                      'HTML'     => $answer_rule
  582                    );
  583 
  584     $out;
  585 }
  586 sub tex_ans_rule_extension {
  587   my $len = shift;
  588   $len    = 20 unless $len ;
  589     my $name = NEW_ANS_NAME($main::ans_rule_count);
  590     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  591     my $out = MODES(
  592                      'TeX' => $answer_rule,
  593                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  594                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  595                      'HTML'     => $answer_rule
  596                    );
  597 
  598     $out;
  599 }
  600 # still needs some cleanup.
  601 sub NAMED_TEX_ANS_RULE {
  602     my $name = shift;
  603   my $len = shift;
  604   $len    = 20 unless $len ;
  605     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  606     my $out = MODES(
  607                      'TeX' => $answer_rule,
  608                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  609                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  610                      'HTML'     => $answer_rule
  611                    );
  612 
  613     $out;
  614 }
  615 sub NAMED_TEX_ANS_RULE_EXTENSION {
  616   my $name = shift;
  617   my $len = shift;
  618   $len    = 20 unless $len ;
  619     my $answer_rule = NAMED_ANS_RULE_EXTENSION($name ,$len);  # we don't want to create three answer rules in different modes.
  620     my $out = MODES(
  621                      'TeX' => $answer_rule,
  622                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  623                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  624                      'HTML'     => $answer_rule
  625                    );
  626 
  627     $out;
  628 }
  629 sub ans_box {
  630   my $row = shift;
  631   my $col =shift;
  632   $row = 5 unless $row;
  633   $col = 80 unless $col;
  634   my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  635   NAMED_ANS_BOX($name ,$row,$col);
  636 }
  637 
  638 #this is legacy code; use ans_checkbox instead
  639 sub checkbox {
  640   my %options = @_;
  641   qq!<INPUT TYPE="checkbox" NAME="$options{'-name'}" VALUE="$options{'-value'}">$options{'-label'}!
  642 }
  643 
  644 
  645 sub NAMED_POP_UP_LIST {
  646     my $name = shift;
  647   my @list = @_;
  648   $name = RECORD_ANS_NAME($name);   # record answer name
  649     my $answer_value = '';
  650   $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name});
  651   my $out = "";
  652   if ($displayMode eq "HTML" or $displayMode eq "HTML_tth") {
  653     $out = qq!<SELECT NAME = "$name" SIZE=1> \n!;
  654     my $i;
  655     foreach ($i=0; $i< @list; $i=$i+2) {
  656       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  657       $out .= qq!<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\n!;
  658     };
  659     $out .= " </SELECT>\n";
  660   } elsif ( $displayMode eq "Latex2HTML") {
  661     $out = qq! \\begin{rawhtml}<SELECT NAME = "$name" SIZE=1> \\end{rawhtml} \n !;
  662     my $i;
  663     foreach ($i=0; $i< @list; $i=$i+2) {
  664       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  665       $out .= qq!\\begin{rawhtml}<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\\end{rawhtml}\n!;
  666     };
  667     $out .= " \\begin{rawhtml}</SELECT>\\end{rawhtml}\n";
  668   } elsif ( $displayMode eq "TeX") {
  669       $out .= "\\fbox{?}";
  670   }
  671 
  672 }
  673 
  674 sub pop_up_list {
  675   my @list = @_;
  676   my $name = NEW_ANS_NAME(++$main::ans_rule_count);  # get new answer name
  677   NAMED_POP_UP_LIST($name, @list);
  678 }
  679 
  680 # end answer blank macros
  681 
  682 =head2 Hints and solutions macros
  683 
  684   solution('text','text2',...);
  685   SOLUTION('text','text2',...);   # equivalent to TEXT(solution(...));
  686 
  687   hint('text', 'text2', ...);
  688   HINT('text', 'text2',...);      # equivalent to TEXT("$BR$HINT" . hint(@_) . "$BR") if hint(@_);
  689 
  690 Solution prints its concatenated input when the check box named 'ShowSol' is set and
  691 the time is after the answer date. (The variable C<$envir{'inputs_ref'}->>C<{'ShowSol'}> is set.)
  692 
  693 PG_FLAGS{'solutionExists'} is set to 1 when a solution is available for viewing.
  694 
  695 Hints are shown only after the number of attempts is greater than $:showHint.
  696 ($:showHint defaults to 1.)
  697 
  698 =cut
  699 
  700 
  701 
  702 #   solution prints its input when $displaySolutionsQ is set.
  703 #   use as TEXT(solution("blah, blah");
  704 #   \$solutionExists
  705 #   is passed to processProblem which displays a "show Solution" button
  706 #   when a solution is available for viewing
  707 
  708 
  709 sub solution {
  710   my @in = @_;
  711   my $out = '';
  712   PG_restricted_eval('$main::solutionExists =1');
  713   if ($envir{'inputs_ref'}->{'ShowSol'}) {
  714     my %inlist;
  715     grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} });
  716     if  ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) {
  717       $out = join(' ',@in);
  718     } elsif ($envir{'answerDate'} < time ){
  719       $out = join(' ',@in);
  720     }
  721   }
  722 
  723     $out;
  724 }
  725 
  726 
  727 
  728 sub SOLUTION {
  729   TEXT( solution(@_)) ;
  730 }
  731 
  732 
  733 
  734 sub hint {
  735     my @in = @_;
  736   my $out = '';
  737 
  738     $main::showHint = 1 unless defined($main::showHint);
  739     $main::numOfAttempts = 0 unless defined($main::numOfAttempts);
  740   if ($displayMode eq 'TeX')   {
  741     $out = '';  # do nothing since hints are not available for download
  742 
  743   } elsif ($main::numOfAttempts >= $main::showHint ) { #numOfAttempts is only defined in interactive mode
  744     if ( ${$main::inputs_ref}{'ShowHint'} ) {
  745         $out = join(' ',@in);     # show hint
  746     } elsif ($displayMode eq 'HTML' or $displayMode eq 'HTML_tth') {
  747       $out =  checkbox(-name=>'ShowHint',
  748               -value=>1,
  749               -label=>"Show Hint",
  750               -override => 1
  751               ) ;
  752     } elsif ($displayMode eq 'Latex2HTML')  {
  753       $out =  '\\begin{rawhtml}' . checkbox(-name=>'ShowHint',
  754                   -value=>1,
  755                   -label=>"Show Hint",
  756                   -override => 1
  757                   ) . '\\end{rawhtml}' ;
  758     } else {
  759       warn "Error: The subroutine hints doesn't know what to do in display mode $displayMode.";
  760       $out = '';
  761     }
  762   } else {
  763     # do nothing since the $numOf Attempts is not yet large enough.
  764     $out = '';
  765   }
  766   $out ;
  767 }
  768 
  769 
  770 
  771 sub HINT {
  772     TEXT("$main::BR$main::HINT" . hint(@_) . "$main::BR") if hint(@_);
  773 }
  774 
  775 
  776 
  777 # End hints and solutions macros
  778 #################################
  779 
  780 # Produces a random number between $begin and $end with increment 1.
  781 # You do not have to worry about integer or floating point types.
  782 
  783 =head2 Pseudo-random number generator
  784 
  785   Usage:
  786   random(0,5,.1)      # produces a random number between 0 and 5 in increments of .1
  787   non_zero_random(0,5,.1) # gives a non-zero random number
  788 
  789   SRAND(seed)     # resets the main random generator -- use very cautiously
  790 
  791 
  792 SRAND(time) will create a different problem everytime it is called.  This makes it difficult
  793 to check the answers :-).
  794 
  795 SRAND($envir{'inputs_ref'}->{'key'} ) will create a different problem for each login session.
  796 This is probably what is desired.
  797 
  798 =cut
  799 
  800 
  801 sub random  {
  802   my ($begin, $end, $incr) = @_;
  803   $main::PG_random_generator->random($begin,$end,$incr);
  804 }
  805 
  806 
  807 sub non_zero_random { ##gives a non-zero random number
  808   my (@arguments)=@_;
  809   my $a=0;
  810   my $i=100; #safety counter
  811   while ($a==0 && ( 0 < $i-- ) ) {
  812     $a=random(@arguments);
  813   }
  814   $a;
  815 }
  816 
  817 sub SRAND { # resets the main random generator -- use cautiously
  818     my $seed = shift;
  819   $main::PG_random_generator -> srand($seed);
  820 }
  821 
  822 # display macros
  823 
  824 =head2 Display Macros
  825 
  826 These macros produce different output depending on the display mode being used to show
  827 the problem on the screen, or whether the problem is being converted to TeX to produce
  828 a hard copy output.
  829 
  830   MODES   ( TeX =>        "Output this in TeX mode",
  831             HTML =>       "output this in HTML mode",
  832             HTML_tth =>   "output this in HTML_tth mode",
  833             Latex2HTML => "output this in Latex2HTML mode",
  834            )
  835 
  836   TEX     (tex_version, html_version) #obsolete
  837 
  838   M3      (tex_version, latex2html_version, html_version) #obsolete
  839 
  840 
  841 
  842 =cut
  843 
  844 
  845 sub TEX {
  846   my ($tex, $html ) = @_;
  847   MODES(TeX => $tex, HTML => $html, HTML_tth => $html);
  848 }
  849 
  850 
  851 sub M3 {
  852   my($tex,$l2h,$html) = @_;
  853   MODES(TeX => $tex, Latex2HTML => $l2h, HTML => $html, HTML_tth => $html);
  854 }
  855 
  856 # This replaces M3.  You can add new modes at will to this one.
  857 
  858 sub MODES {
  859   my %options = @_;
  860   return $options{$displayMode}
  861              if defined( $options{$displayMode} );
  862 
  863   # default searches.
  864   if ($displayMode eq "Latex2HTML") {
  865     return $options{TeX}
  866              if defined( $options{TeX} );
  867       return $options{HTML}
  868              if defined( $options{HTML} );
  869       die " ERROR in using MODES: 'HTML' and 'TeX' options not defined for 'Latex2HTML'";
  870   }
  871 
  872   if ($displayMode eq "HTML_tth") {
  873     return $options{HTML}
  874              if defined( $options{HTML} );
  875       die " ERROR in using MODES: 'HTML' option not defined for HTML_tth";
  876 
  877   }
  878 
  879   # trap undefined errors
  880   die "ERROR in defining MODES:  Can't find |$displayMode| among
  881            available options:" . join(" ", keys(%options) )
  882            . " file " . __FILE__ ." line " . __LINE__."\n\n";
  883 
  884 }
  885 
  886 
  887 # end display macros
  888 
  889 
  890 =head2  Display constants
  891 
  892   @ALPHABET       ALPHABET()      capital letter alphabet -- ALPHABET[0] = 'A'
  893   $PAR        PAR()       paragraph character (\par or <p>)
  894   $BR             BR()        line break character
  895   $LQ         LQ()        left double quote
  896   $RQ         RQ()        right double quote
  897   $BM         BM()        begin math
  898   $EM         EM()        end math
  899   $BDM        BDM()       begin display math
  900   $EDM        EDM()       end display math
  901   $LTS        LTS()       strictly less than
  902   $GTS        GTS()       strictly greater than
  903   $LTE        LTE()       less than or equal
  904   $GTE        GTE()       greater than or equal
  905   $BEGIN_ONE_COLUMN BEGIN_ONE_COLUMN()  begin one-column mode
  906   $END_ONE_COLUMN   END_ONE_COLUMN()  end one-column mode
  907   $SOL        SOLUTION_HEADING()  solution headline
  908   $HINT       HINT_HEADING()    hint headline
  909   $US         US()        underscore character
  910   $SPACE        SPACE()       space character (tex and latex only)
  911   $BBOLD        BBOLD()       begin bold typeface
  912   $EBOLD        EBOLD()       end bold typeface
  913   $HR         HR()        horizontal rule
  914   $LBRACE       LBRACE()      left brace
  915   $LB         LB ()       left brace
  916   $RBRACE       RBRACE()      right brace
  917   $RB         RB ()       right brace
  918   $DOLLAR       DOLLAR()      a dollar sign
  919   $PERCENT      PERCENT()     a percent sign
  920   $CARET        CARET()       a caret sign
  921   $PI         PI()        the number pi
  922   $E          E()         the number e
  923 
  924 =cut
  925 
  926 
  927 
  928 
  929 
  930 # A utility variable.  Notice that "B"=$ALPHABET[1] and
  931 # "ABCD"=@ALPHABET[0..3].
  932 
  933 sub ALPHABET  {
  934   ('A'..'ZZ')[@_];
  935 }
  936 
  937 ###############################################################
  938 # Some constants which are different in tex and in HTML
  939 # The order of arguments is TeX, Latex2HTML, HTML
  940 sub PAR { MODES( TeX => '\\par ',Latex2HTML => '\\par ',HTML => '<P>' ); };
  941 sub BR { MODES( TeX => '\\par\\noindent ',Latex2HTML => '\\par\\noindent ',HTML => '<BR>'); };
  942 sub LQ { MODES( TeX => "``", Latex2HTML =>   '"',  HTML =>  '&quot;' ); };
  943 sub RQ { MODES( TeX => "''", Latex2HTML =>   '"',   HTML =>  '&quot;' ); };
  944 sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML =>  ''); };  # begin math mode
  945 sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); };  # end math mode
  946 sub BDM { MODES(TeX => '\\[', Latex2HTML =>   '\\[', HTML =>   '<P ALIGN=CENTER>'); };  #begin displayMath mode
  947 sub EDM { MODES(TeX => '\\]',  Latex2HTML =>  '\\]', HTML => '</P>'); };              #end displayMath mode
  948 sub LTS { MODES(TeX => ' < ', Latex2HTML => ' \\lt ',  HTML =>   '&lt;'); };
  949 sub GTS {MODES(TeX => ' > ', Latex2HTML => ' \\gt ',  HTML =>    '&gt;'); };
  950 sub LTE { MODES(TeX => ' \\le ', Latex2HTML =>  ' \\le ',  HTML => '&lt;=' ); };
  951 sub GTE { MODES(TeX => ' \\ge ',  Latex2HTML => ' \\ge ',  HTML =>  '&gt;'); };
  952 sub BEGIN_ONE_COLUMN { MODES(TeX => " \\end{multicols}\n",  Latex2HTML => " ", HTML =>   " "); };
  953 sub END_ONE_COLUMN { MODES(TeX =>
  954               " \\begin{multicols}{2}\n\\columnwidth=\\linewidth\n",
  955                             Latex2HTML => ' ', HTML => ' ');
  956 
  957 };
  958 sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf Solution:}',
  959                  Latex2HTML => '\\par {\\bf Solution:}',
  960                HTML =>  '<P><B>Solution:</B>');
  961               };
  962 sub HINT_HEADING { MODES( TeX => "\\par {\\bf Hint:}", Latex2HTML => "\\par {\\bf Hint:}", HTML => "<P><B>Hint:</B>"); };
  963 sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');};  # underscore, e.g. file${US}name
  964 sub SPACE { MODES(TeX => '\\ ',  Latex2HTML => '\\ ', HTML => '&nbsp;');};  # force a space in latex, doesn't force extra space in html
  965 sub BBOLD { MODES(TeX => '{\\bf ',  Latex2HTML => '{\\bf ', HTML => '<B>'); };
  966 sub EBOLD { MODES( TeX => '}', Latex2HTML =>  '}',HTML =>  '</B>'); };
  967 sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml} <HR> \\end{rawhtml}', HTML =>  '<HR>'); };
  968 sub LBRACE { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '\{' , HTML_tth=> '\\lbrace' ); };
  969 sub RBRACE { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '\}' , HTML_tth=> '\\rbrace',); };
  970 sub LB { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '\{' , HTML_tth=> '\\lbrace' ); };
  971 sub RB { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '\}' , HTML_tth=> '\\rbrace',); };
  972 sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '\\$', HTML => '$' ); };
  973 sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); };
  974 sub CARET { MODES( TeX => '\\^', Latex2HTML => '\\^', HTML => '^' ); };
  975 sub PI {4*atan2(1,1);};
  976 sub E {exp(1);};
  977 
  978 ###############################################################
  979 ## Evaluation macros
  980 
  981 
  982 =head2 TEXT macros
  983 
  984   Usage:
  985     TEXT(@text);
  986 
  987 This is the simplest way to print text from a problem.  The strings in the array C<@text> are concatenated
  988 with spaces between them and printed out in the text of the problem.  The text is not processed in any other way.
  989 C<TEXT> is defined in PG.pl.
  990 
  991   Usage:
  992     BEGIN_TEXT
  993       text.....
  994     END_TEXT
  995 
  996 This is the most common way to enter text into the problem.  All of the text between BEGIN_TEXT and END_TEXT
  997 is processed by the C<EV3> macro described below and then printed using the C<TEXT> command.  The two key words
  998 must appear on lines by themselves.  The preprocessing that makes this construction work is done in F<PGtranslator.pm>.
  999 See C<EV3> below for details on the processing.
 1000 
 1001 
 1002 =cut
 1003 
 1004 =head2 Evaluation macros
 1005 
 1006 =head3 EV3
 1007 
 1008         TEXT(EV3("This is a formulat \( \int_0^5 x^2 \, dx \) ");
 1009         TEXT(EV3(@text));
 1010 
 1011     TEXT(EV3(<<'END_TEXT'));
 1012       text stuff...
 1013     END_TEXT
 1014 
 1015 
 1016 The BEGIN_TEXT/END_TEXT construction is translated into the construction above by PGtranslator.pm.  END_TEXT must appear
 1017 on a line by itself and be left justified.  (The << construction is known as a "here document" in UNIX and in PERL.)
 1018 
 1019 The single quotes around END_TEXT mean that no automatic interpolation of variables takes place in the text.
 1020 Using EV3 with strings which have been evaluated by double quotes may lead to unexpected results.
 1021 
 1022 
 1023 The evaluation macro E3 first evaluates perl code inside the braces:  C<\{  code \}>.
 1024 Any perl statment can be put inside the braces.  The
 1025 result of the evaluation (i.e. the last statement evaluated) replaces the C<\{ code \}> construction.
 1026 
 1027 Next interpolation of all variables (e.g. C<$var or @array> ) is performed.
 1028 
 1029 Then mathematical formulas in TeX are evaluated within the
 1030 C<\(  tex math mode \)> and
 1031 C<\[ tex display math mode \] >
 1032 constructions, in that order:
 1033 
 1034 =head3 FEQ
 1035 
 1036   FEQ($string);   # processes and outputs the string
 1037 
 1038 
 1039 The mathematical formulas are run through the macro C<FEQ> (Format EQuations) which performs
 1040 several substitutions (see below).
 1041 In C<HTML_tth> mode the resulting code is processed by tth to obtain an HTML version
 1042 of the formula. (In the future processing by WebEQ may be added here as another option.)
 1043 The Latex2HTML mode does nothing
 1044 at this stage; it creates the entire problem before running it through
 1045 TeX and creating the GIF images of the equations.
 1046 
 1047 The resulting string is output (and usually fed into TEXT to be printed in the problem).
 1048 
 1049   Usage:
 1050 
 1051     $string2 = FEQ($string1);
 1052 
 1053 This is a filter which is used to format equations by C<EV2> and C<EV3>, but can also be used on its own.  It is best
 1054 understood with an example.
 1055 
 1056     $string1 = "${a}x^2 + ${b}x + {$c:%.1f}"; $a = 3;, $b = -2; $c = -7.345;
 1057 
 1058 when interpolated becomes:
 1059 
 1060     $string1 = '3x^2 + -2x + {-7.345:%0.1f}
 1061 
 1062 FEQ first changes the number of decimal places displayed, so that the last term becomes -7.3 Then it removes the
 1063 extraneous plus and minus signs, so that the final result is what you want:
 1064 
 1065     $string2 = '3x^2 - 2x -7.3';
 1066 
 1067 (The %0.1f construction
 1068 is the same formatting convention used by Perl and nearly identical to the one used by the C printf statement. Some common
 1069 usage:  %0.3f 3 decimal places, fixed notation; %0.3e 3 significant figures exponential notation; %0.3g uses either fixed
 1070 or exponential notation depending on the size of the number.)
 1071 
 1072 Two additional legacy formatting constructions are also supported:
 1073 
 1074 C<?{$c:%0.3f} > will give a number with 3 decimal places and a negative
 1075 sign if the number is negative, no sign if the number is positive.
 1076 
 1077 C<!{$c:%0.3f}> determines the sign and prints it
 1078 whether the number is positive or negative.
 1079 
 1080 =head3 EV2
 1081 
 1082     TEXT(EV2(@text));
 1083 
 1084     TEXT(EV2(<<END_OF_TEXT));
 1085       text stuff...
 1086     END_OF_TEXT
 1087 
 1088 This is a precursor to EV3.  In this case the constants are interpolated first, before the evaluation of the \{ ...code...\}
 1089 construct. This can lead to unexpected results.  For example C<\{ join(" ", @text) \}> with C<@text = ("Hello","World);> becomes,
 1090 after interpolation, C<\{ join(" ",Hello World) \}> which then causes an error when evaluated because Hello is a bare word.
 1091 C<EV2> can still be useful if you allow for this, and in particular it works on double quoted strings, which lead to
 1092 unexpected results with C<EV3>. Using single quoted strings with C<EV2> may lead to unexpected results.
 1093 
 1094 The unexpected results have to do with the number of times backslashed constructions have to be escaped. It's quite messy.  For
 1095 more details get a good Perl book and then read the code. :-)
 1096 
 1097 
 1098 
 1099 
 1100 =cut
 1101 
 1102 
 1103 sub ev_substring {
 1104     my $string      = shift;
 1105   my $start_delim = shift;
 1106   my $end_delim   = shift;
 1107   my $actionRef   = shift;
 1108   my ($eval_out,$PG_eval_errors,$PG_full_error_report)=();
 1109     my $out = "";
 1110     while ($string) {
 1111         if ($string =~ /\Q$start_delim\E/s) {
 1112        #print "$start_delim $end_delim evaluating_substring=$string<BR>";
 1113         $string =~ s/^(.*?)\Q$start_delim\E//s;  # get string up to next \{ ---treats string as a single line, ignoring returns
 1114         $out .= $1;
 1115        #print "$start_delim $end_delim substring_out=$out<BR>";
 1116         $string =~ s/^(.*?)\Q$end_delim\E//s;  # get perl code up to \} ---treats string as a single line,  ignoring returns
 1117            #print "$start_delim $end_delim evaluate_string=$1<BR>";
 1118         ($eval_out,$PG_eval_errors,$PG_full_error_report) = &$actionRef($1);
 1119         $eval_out = "$start_delim $eval_out $end_delim" if $PG_full_error_report;
 1120         $out = $out . $eval_out;
 1121        #print "$start_delim $end_delim new substring_out=$out<BR><p><BR>";
 1122         $out .="$main::PAR ERROR $0 in ev_substring, PGbasicmacros.pl:$main::PAR <PRE>  $@ </PRE>$main::PAR" if $@;
 1123         }
 1124       else {
 1125         $out .= $string;  # flush the last part of the string
 1126         last;
 1127         }
 1128 
 1129       }
 1130   $out;
 1131 }
 1132 sub  safe_ev {
 1133     my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev;   # process input by old_safe_ev first
 1134     $out =~s/\\/\\\\/g;   # protect any new backslashes introduced.
 1135   ($out,$PG_eval_errors,$PG_full_error_report)
 1136 }
 1137 
 1138 sub  old_safe_ev {
 1139     my $in = shift;
 1140     my   ($out,$PG_eval_errors,$PG_full_error_report) = PG_restricted_eval("$in;");
 1141     # the addition of the ; seems to provide better error reporting
 1142     if ($PG_eval_errors) {
 1143       my @errorLines = split("\n",$PG_eval_errors);
 1144     #$out = "<PRE>$main::PAR % ERROR in $0:old_safe_ev, PGbasicmacros.pl: $main::PAR % There is an error occuring inside evaluation brackets \\{ ...code... \\} $main::BR % somewhere in an EV2 or EV3 or BEGIN_TEXT block. $main::BR % Code evaluated:$main::BR $in $main::BR % $main::BR % $errorLines[0]\n % $errorLines[1]$main::BR % $main::BR % $main::BR </PRE> ";
 1145     warn " ERROR in old_safe_ev, PGbasicmacros.pl: <PRE>
 1146      ## There is an error occuring inside evaluation brackets \\{ ...code... \\}
 1147      ## somewhere in an EV2 or EV3 or BEGIN_TEXT block.
 1148      ## Code evaluated:
 1149      ## $in
 1150      ##" .join("\n     ", @errorLines). "
 1151      ##</PRE>$main::BR
 1152      ";
 1153      $out ="$main::PAR $main::BBOLD  $in $main::EBOLD $main::PAR";
 1154 
 1155 
 1156   }
 1157 
 1158   ($out,$PG_eval_errors,$PG_full_error_report);
 1159 }
 1160 
 1161 sub FEQ   {    # Format EQuations
 1162   my $in = shift;
 1163    # formatting numbers -- the ?{} and !{} constructions
 1164   $in =~s/\?\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &sspf($1,$2) )}/g;
 1165   $in =~s/\!\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &spf($1,$2) )}/g;
 1166 
 1167   # more formatting numbers -- {number:format} constructions
 1168   $in =~ s/\{(\s*[\+\-\d\.]+[eE]*[\+\-]*\d*):(\%\d*.\d*\w)}/${ \( &spf($1,$2) )}/g;
 1169   $in =~ s/\+\s*\-/ - /g;
 1170   $in =~ s/\-\s*\+/ - /g;
 1171   $in =~ s/\+\s*\+/ + /g;
 1172   $in =~ s/\-\s*\-/ + /g;
 1173   $in;
 1174 }
 1175 
 1176 sub math_ev3 {
 1177   my $in = shift; #print "in=$in<BR>";
 1178   my ($out,$PG_eval_errors,$PG_full_error_report);
 1179   $in = FEQ($in);
 1180   $in =~ s/%/\\%/g;   #  % causes trouble in TeX and HTML_tth it usually (always?) indicates an error, not comment
 1181   return("$main::BM $in $main::EM") unless ($displayMode eq 'HTML_tth');
 1182   $in = "\\(" . $in . "\\)";
 1183   $out = tth($in);
 1184   ($out,$PG_eval_errors,$PG_full_error_report);
 1185 
 1186 }
 1187 
 1188 sub display_math_ev3 {
 1189   my $in = shift; #print "in=$in<BR>";
 1190   my ($out,$PG_eval_errors,$PG_full_error_report);
 1191   $in = FEQ($in);
 1192   $in =~ s/%/\\%/g;
 1193   return("$main::BDM $in $main::EDM") unless $displayMode eq 'HTML_tth' ;
 1194   $in = "\\[" . $in . "\\]";
 1195   $out =tth($in);
 1196   ($out,$PG_eval_errors,$PG_full_error_report);
 1197 }
 1198 
 1199 sub EV2 {
 1200   my $string = join(" ",@_);
 1201   # evaluate code inside of \{  \}  (no nesting allowed)
 1202     $string = ev_substring($string,"\\{","\\}",\&old_safe_ev);
 1203     $string = ev_substring($string,"\\<","\\>",\&old_safe_ev);
 1204   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1205   $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1206   # macros for displaying math
 1207   $string =~ s/\\\(/$main::BM/g;
 1208   $string =~ s/\\\)/$main::EM/g;
 1209   $string =~ s/\\\[/$main::BDM/g;
 1210   $string =~ s/\\\]/$main::EDM/g;
 1211   $string;
 1212 }
 1213 
 1214 sub EV3{
 1215   my $string = join(" ",@_);
 1216   # evaluate code inside of \{  \}  (no nesting allowed)
 1217     $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev);  # handles \{ \} in single quoted strings of PG files
 1218   # interpolate variables
 1219   my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n");
 1220   if ($PG_eval_errors) {
 1221       my @errorLines = split("\n",$PG_eval_errors);
 1222       $string =~ s/</&lt;/g; $string =~ s/>/&gt;/g;
 1223     $evaluated_string = "<PRE>$main::PAR % ERROR in $0:EV3, PGbasicmacros.pl: $main::PAR % There is an error occuring in the following code:$main::BR $string $main::BR % $main::BR % $errorLines[0]\n % $errorLines[1]$main::BR % $main::BR % $main::BR </PRE> ";
 1224     $@="";
 1225   }
 1226   $string = $evaluated_string;
 1227   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1228     $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1229   $string;
 1230 }
 1231 
 1232 =head2 Formatting macros
 1233 
 1234   beginproblem()  # generates text listing number and the point value of
 1235                   # the problem. It will also print the file name containing
 1236                   # the problem for users listed in the PRINT_FILE_NAMES_FOR PG_environment
 1237                   # variable.
 1238   OL(@array)      # formats the array as an Ordered List ( <OL> </OL> ) enumerated by letters.
 1239 
 1240   htmlLink($url, $text)
 1241                   # Places a reference to the URL with the specified text in the problem.
 1242                   # A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help')
 1243                   # where alias finds the full address of the prob1_help.html file in the same directory
 1244                   # as the problem file
 1245   appletLink($url, $parameters)
 1246                   # For example
 1247                   # appletLink(q!  archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip"
 1248                                   code="xFunctionsLauncher.class"  width=100 height=14!,
 1249                   " parameter text goes here")
 1250                   # will link to xFunctions.
 1251 
 1252   low level:
 1253 
 1254   spf($number, $format)   # prints the number with the given format
 1255   sspf($number, $format)  # prints the number with the given format, always including a sign.
 1256   protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX.
 1257 
 1258 =cut
 1259 
 1260 sub beginproblem {
 1261   my $out = "";
 1262     my $TeXFileName = protect_underbar($main::fileName);
 1263     my $l2hFileName = protect_underbar($main::fileName);
 1264   my %inlist;
 1265   my $points ='pts';
 1266   $points = 'pt' if $main::problemValue == 1;
 1267   ##    Prepare header for the problem
 1268   grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} });
 1269   if ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) {
 1270     $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.{\\footnotesize ($main::problemValue $points) $TeXFileName}}\\newline ",
 1271     " \\begin{rawhtml} ($main::problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}",
 1272      "($main::problemValue $points) <B>$main::fileName</B><BR>"
 1273        );
 1274   } else {
 1275     $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.}($main::problemValue $points) ",
 1276     "($main::problemValue $points) ",
 1277      "($main::problemValue $points) "
 1278        );
 1279   }
 1280   $out;
 1281 
 1282 }
 1283 
 1284 # kludge to clean up path names
 1285             ## allow underscore character in set and section names and also allows line breaks at /
 1286 sub protect_underbar {
 1287     my $in = shift;
 1288     if ($displayMode eq 'TeX')  {
 1289 
 1290         $in =~ s|_|\\\_|g;
 1291         $in =~ s|/|\\\-/|g;  # allows an optional hyphenation of the path (in tex)
 1292     }
 1293     $in;
 1294 }
 1295 
 1296 
 1297 # An example of a macro which prints out a list (with letters)
 1298 sub OL {
 1299   my(@array) = @_;
 1300   my $i = 0;
 1301   my  $out=   &M3(
 1302           "\\begin{enumerate}\n",
 1303           " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ",
 1304           "<OL TYPE=\"A\" VALUE=\"1\">\n"
 1305           ) ;
 1306   my $elem;
 1307   foreach $elem (@array) {
 1308     $out .= &M3(
 1309           "\\item[$main::ALPHABET[$i].] $elem\n",
 1310           " \\begin{rawhtml} <LI> \\end{rawhtml} $elem  ",
 1311           "<LI> $elem\n"
 1312           ) ;
 1313     $i++;
 1314   }
 1315   $out .= &M3(
 1316         "\\end{enumerate}\n",
 1317         " \\begin{rawhtml} </OL>\n \\end{rawhtml} ",
 1318         "</OL>\n"
 1319         ) ;
 1320 }
 1321 
 1322 sub htmlLink {
 1323   my $url = shift;
 1324   my $text = shift;
 1325   my $options = shift;
 1326   $options = "" unless defined($options);
 1327   return "${main::BBOLD}[ broken link:  $text ] ${main::EBOLD}" unless defined($url);
 1328   M3( "{\\bf \\underline{$text}  }",
 1329       "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}",
 1330       "<A HREF=\"$url\" $options> $text </A>"
 1331       );
 1332 }
 1333 sub appletLink {
 1334   my $url = shift;
 1335   my $options = shift;
 1336   $options = "" unless defined($options);
 1337   M3( "{\\bf \\underline{APPLET}  }",
 1338       "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}",
 1339       "<APPLET $url> $options </APPLET>"
 1340       );
 1341 }
 1342 sub spf {
 1343   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1344   $format = "%4.3g" unless $format;   # default value for format
 1345   sprintf($format, $number);
 1346   }
 1347 sub sspf {
 1348   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1349   $format = "%4.3g" unless $format;   # default value for format
 1350   my $sign = $number>=0 ? " + " : " - ";
 1351   $number = $number>=0 ? $number : -$number;
 1352   $sign .sprintf($format, $number);
 1353   }
 1354 
 1355 =head2  Sorting and other list macros
 1356 
 1357 
 1358 
 1359   Usage:
 1360   lex_sort(@list);   # outputs list in lexigraphic (alphabetical) order
 1361   num_sort(@list);   # outputs list in numerical order
 1362   uniq( @list);      # outputs a list with no duplicates.  Order is unspecified.
 1363 
 1364   PGsort( \&sort_subroutine, @list);
 1365   # &sort_subroutine defines order. It's output must be -1,0 or 1.
 1366 
 1367 =cut
 1368 
 1369 #  uniq gives unique elements of a list:
 1370  sub uniq {
 1371    my (@in) =@_;
 1372    my %temp = ();
 1373    while (@in) {
 1374           $temp{shift(@in)}++;
 1375       }
 1376    my @out =  keys %temp;  # sort is causing trouble with Safe.??
 1377    @out;
 1378 }
 1379 
 1380 sub lex_sort {
 1381   PGsort sub {$_[0] cmp $_[1]}, @_;
 1382 }
 1383 sub num_sort {
 1384   PGsort sub {$_[0] <=> $_[1]}, @_;
 1385 }
 1386 
 1387 
 1388 =head2 Macros for handling tables
 1389 
 1390   Usage:
 1391   begintable( number_of_columns_in_table)
 1392   row(@dataelements)
 1393   endtable()
 1394 
 1395 Example of useage:
 1396 
 1397   BEGIN_TEXT
 1398     This problem tests calculating new functions from old ones:$BR
 1399     From the table below calculate the quantities asked for:$BR
 1400     \{begintable(scalar(@firstrow)+1)\}
 1401     \{row(" \(x\) ",@firstrow)\}
 1402     \{row(" \(f(x)\) ", @secondrow)\}
 1403     \{row(" \(g(x)\) ", @thirdrow)\}
 1404     \{row(" \(f'(x)\) ", @fourthrow)\}
 1405     \{row(" \(g'(x)\) ", @fifthrow)\}
 1406     \{endtable()\}
 1407 
 1408    (The arrays contain numbers which are placed in the table.)
 1409 
 1410   END_TEXT
 1411 
 1412 =cut
 1413 
 1414 sub begintable {
 1415   my ($number)=shift;   #number of columns in table
 1416   my %options = @_;
 1417   warn "begintable(cols) requires a number indicating the number of columns" unless defined($number);
 1418   my $out = "";
 1419   if ($displayMode eq 'TeX') {
 1420     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1421     }
 1422   elsif ($displayMode eq 'Latex2HTML') {
 1423     $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}";
 1424     }
 1425   elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') {
 1426     $out .= "<TABLE BORDER=1>\n"
 1427   }
 1428   else {
 1429     $out = "Error: PGchoicemacros: begintable: Unknown displayMode: $displayMode.\n";
 1430     }
 1431   $out;
 1432   }
 1433 
 1434 sub endtable {
 1435   my $out = "";
 1436   if ($displayMode eq 'TeX') {
 1437     $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1438     }
 1439   elsif ($displayMode eq 'Latex2HTML') {
 1440     $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}";
 1441     }
 1442   elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') {
 1443     $out .= "</TABLE>\n";
 1444     }
 1445   else {
 1446     $out = "Error: PGchoicemacros: endtable: Unknown displayMode: $displayMode.\n";
 1447     }
 1448   $out;
 1449   }
 1450 
 1451 
 1452 sub row {
 1453   my @elements = @_;
 1454   my $out = "";
 1455   if ($displayMode eq 'TeX') {
 1456     while (@elements) {
 1457       $out .= shift(@elements) . " &";
 1458       }
 1459      chop($out); # remove last &
 1460      $out .= "\\\\ \\hline \n";
 1461      # carriage returns must be added manually for tex
 1462     }
 1463   elsif ($displayMode eq 'Latex2HTML') {
 1464     $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n";
 1465     while (@elements) {
 1466       $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n";
 1467       }
 1468     $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n";
 1469   }
 1470   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1471     $out .= "<TR>\n";
 1472     while (@elements) {
 1473       $out .= "<TD>" . shift(@elements) . "</TD>";
 1474       }
 1475     $out .= "\n</TR>\n";
 1476   }
 1477   else {
 1478     $out = "Error: PGchoicemacros: row: Unknown displayMode: $main::displayMode.\n";
 1479     }
 1480   $out;
 1481 }
 1482 
 1483 =head2 Macros for displaying static images
 1484 
 1485   Usage:
 1486   $string = image($image, width => 100, height => 100, tex_size => 800)
 1487   $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800)
 1488   $string = caption($string);
 1489   $string = imageRow([$image1, $image2 ], [$caption1, $caption2]);
 1490            # produces a complete table with rows of pictures.
 1491 
 1492 
 1493 =cut
 1494 
 1495 #   More advanced macros
 1496 sub image {
 1497   my $image_ref  = shift;
 1498     my @opt = @_;
 1499     unless (scalar(@opt) % 2 == 0 ) {
 1500       warn "ERROR in image macro.  A list of macros must be inclosed in square brackets.";
 1501     }
 1502     my %in_options = @opt;
 1503     my %known_options = ( width   => 100,
 1504                           height  => 100,
 1505                           tex_size  => 800
 1506                          );
 1507 #   # handle options
 1508     my %out_options = %known_options;
 1509   foreach my $opt_name (keys %in_options) {
 1510     if ( exists( $known_options{$opt_name} ) ) {
 1511       $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ;
 1512     } else {
 1513       die "Option $opt_name not defined for image. " .
 1514       "Default options are:<BR> ", display_options2(%known_options);
 1515 
 1516     }
 1517   }
 1518   my $width     = $out_options{width};
 1519   my $height    = $out_options{height};
 1520   my $tex_size  = $out_options{tex_size};
 1521   my $width_ratio = $tex_size*(.001);
 1522   my @image_list = ();
 1523 
 1524   if (ref($image_ref) =~ /ARRAY/ ) {
 1525     @image_list = @{$image_ref};
 1526   } else {
 1527     push(@image_list,$image_ref);
 1528   }
 1529   my @output_list = ();
 1530     while(@image_list) {
 1531 
 1532     my $imageURL = alias(shift @image_list);
 1533     my $out="";
 1534 
 1535 
 1536     if ($main::displayMode eq 'TeX') {
 1537       $out = qq!\\includegraphics[width=$width_ratio\\linewidth]{$imageURL}\n !
 1538       }
 1539     elsif ($main::displayMode eq 'Latex2HTML') {
 1540       $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>\n
 1541       \\end{rawhtml}\n !
 1542       }
 1543     elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1544       $out = qq!<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>
 1545       !
 1546       }
 1547     else {
 1548       $out = "Error: PGchoicemacros: image: Unknown displayMode: $main::displayMode.\n";
 1549       }
 1550     push(@output_list, $out);
 1551   }
 1552   wantarray ? @output_list : $output_list[0] ;
 1553 }
 1554 
 1555 # This is legacy code.
 1556 sub images {
 1557   my @in = @_;
 1558   my @outlist = ();
 1559   while (@in) {
 1560      push(@outlist,&image( shift(@in) ) );
 1561    }
 1562   @outlist;
 1563 }
 1564 
 1565 
 1566 sub caption {
 1567   my ($out) = @_;
 1568   $out = " $out \n" if $main::displayMode eq 'TeX';
 1569   $out = " $out  " if $main::displayMode eq 'HTML';
 1570   $out = " $out  " if $main::displayMode eq 'HTML_tth';
 1571   $out = " $out  " if $main::displayMode eq 'Latex2HTML';
 1572     $out;
 1573 }
 1574 
 1575 sub captions {
 1576   my @in = @_;
 1577   my @outlist = ();
 1578   while (@in) {
 1579      push(@outlist,&caption( shift(@in) ) );
 1580   }
 1581   @outlist;
 1582 }
 1583 
 1584 sub imageRow {
 1585 
 1586   my $pImages = shift;
 1587   my $pCaptions=shift;
 1588   my $out = "";
 1589   my @images = @$pImages;
 1590   my @captions = @$pCaptions;
 1591   my $number = @images;
 1592   # standard options
 1593   my %options = ( 'tex_size' => 200,  # width for fitting 4 across
 1594                   'height' => 100,
 1595                   'width' => 100,
 1596                   @_            # overwrite any default options
 1597                 );
 1598 
 1599   if ($main::displayMode eq 'TeX') {
 1600     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1601     while (@images) {
 1602       $out .= &image( shift(@images),%options ) . '&';
 1603     }
 1604     chop($out);
 1605     $out .= "\\\\ \\hline \n";
 1606     while (@captions) {
 1607       $out .= &caption( shift(@captions) ) . '&';
 1608     }
 1609     chop($out);
 1610     $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1611   } elsif ($main::displayMode eq 'Latex2HTML'){
 1612 
 1613     $out .= "\n\\begin{rawhtml} <TABLE  BORDER=1><TR>\n\\end{rawhtml}\n";
 1614     while (@images) {
 1615       $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options )
 1616               . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ;
 1617     }
 1618 
 1619     $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n";
 1620     while (@captions) {
 1621       $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) )
 1622               . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ;
 1623     }
 1624 
 1625     $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}";
 1626   } elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth'){
 1627     $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER    VALIGN=MIDDLE>\n";
 1628     while (@images) {
 1629       $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>";
 1630     }
 1631     $out .= "</TR>\n<TR>";
 1632     while (@captions) {
 1633       $out .= " <TH>". &caption( shift(@captions) ) ."</TH>";
 1634     }
 1635     $out .= "\n</TR></TABLE></P>\n"
 1636   }
 1637   else {
 1638     $out = "Error: PGchoicemacros: imageRow: Unknown languageMode: $main::displayMode.\n";
 1639     warn $out;
 1640   }
 1641   $out;
 1642 }
 1643 
 1644 
 1645 ###########
 1646 # Auxiliary macros
 1647 
 1648 sub display_options2{
 1649   my %options = @_;
 1650   my $out_string = "";
 1651   foreach my $key (keys %options) {
 1652     $out_string .= " $key => $options{$key},<BR>";
 1653   }
 1654   $out_string;
 1655 }
 1656 
 1657 
 1658 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9