[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 28 - (download) (as text) (annotate)
Tue Jun 19 20:56:08 2001 UTC (11 years, 11 months ago) by gage
File size: 52393 byte(s)
Fixed problem in dangerousMacros.pl defining i so that it can be used
as a complex number.

Fixed htmlLink so that it gives a friendly error message about broken links
when fed an undefined url.

    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 ($main::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 ($main::displayMode eq "HTML" or $main::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 ( $main::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 ( $main::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("$main::BR$main::HINT" . hint(@_) . "$main::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 $main::showHint.
  696 ($main::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 ($main::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 ($main::displayMode eq 'HTML' or $main::displayMode eq 'HTML_tth') {
  747       $out =  checkbox(-name=>'ShowHint',
  748               -value=>1,
  749               -label=>"Show Hint",
  750               -override => 1
  751               ) ;
  752     } elsif ($main::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 $main::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{$main::displayMode}
  861              if defined( $options{$main::displayMode} );
  862 
  863   # default searches.
  864   if ($main::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 ($main::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 # A utility variable.  Notice that "B"=$ALPHABET[1] and
  929 # "ABCD"=@ALPHABET[0..3].
  930 
  931 sub ALPHABET  {
  932   ('A'..'ZZ')[@_];
  933 }
  934 
  935 ###############################################################
  936 # Some constants which are different in tex and in HTML
  937 # The order of arguments is TeX, Latex2HTML, HTML
  938 sub PAR { MODES( TeX => '\\par ',Latex2HTML => '\\par ',HTML => '<P>' ); };
  939 sub BR { MODES( TeX => '\\par\\noindent ',Latex2HTML => '\\par\\noindent ',HTML => '<BR>'); };
  940 sub LQ { MODES( TeX => "``", Latex2HTML =>   '"',  HTML =>  '&quot;' ); };
  941 sub RQ { MODES( TeX => "''", Latex2HTML =>   '"',   HTML =>  '&quot;' ); };
  942 sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML =>  ''); };  # begin math mode
  943 sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); };  # end math mode
  944 sub BDM { MODES(TeX => '\\[', Latex2HTML =>   '\\[', HTML =>   '<P ALIGN=CENTER>'); };  #begin displayMath mode
  945 sub EDM { MODES(TeX => '\\]',  Latex2HTML =>  '\\]', HTML => '</P>'); };              #end displayMath mode
  946 sub LTS { MODES(TeX => ' < ', Latex2HTML => ' \\lt ',  HTML =>   '&lt;'); };
  947 sub GTS {MODES(TeX => ' > ', Latex2HTML => ' \\gt ',  HTML =>    '&gt;'); };
  948 sub LTE { MODES(TeX => ' \\le ', Latex2HTML =>  ' \\le ',  HTML => '&lt;=' ); };
  949 sub GTE { MODES(TeX => ' \\ge ',  Latex2HTML => ' \\ge ',  HTML =>  '&gt;'); };
  950 sub BEGIN_ONE_COLUMN { MODES(TeX => " \\end{multicols}\n",  Latex2HTML => " ", HTML =>   " "); };
  951 sub END_ONE_COLUMN { MODES(TeX =>
  952               " \\begin{multicols}{2}\n\\columnwidth=\\linewidth\n",
  953                             Latex2HTML => ' ', HTML => ' ');
  954 
  955 };
  956 sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf Solution:}',
  957                  Latex2HTML => '\\par {\\bf Solution:}',
  958                HTML =>  '<P><B>Solution:</B>');
  959               };
  960 sub HINT_HEADING { MODES( TeX => "\\par {\\bf Hint:}", Latex2HTML => "\\par {\\bf Hint:}", HTML => "<P><B>Hint:</B>"); };
  961 sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');};  # underscore, e.g. file${US}name
  962 sub SPACE { MODES(TeX => '\\ ',  Latex2HTML => '\\ ', HTML => '&nbsp;');};  # force a space in latex, doesn't force extra space in html
  963 sub BBOLD { MODES(TeX => '{\\bf ',  Latex2HTML => '{\\bf ', HTML => '<B>'); };
  964 sub EBOLD { MODES( TeX => '}', Latex2HTML =>  '}',HTML =>  '</B>'); };
  965 sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml} <HR> \\end{rawhtml}', HTML =>  '<HR>'); };
  966 sub LBRACE { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '\{' , HTML_tth=> '\\lbrace' ); };
  967 sub RBRACE { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '\}' , HTML_tth=> '\\rbrace',); };
  968 sub LB { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '\{' , HTML_tth=> '\\lbrace' ); };
  969 sub RB { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '\}' , HTML_tth=> '\\rbrace',); };
  970 sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '\\$', HTML => '$' ); };
  971 sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); };
  972 sub CARET { MODES( TeX => '\\^', Latex2HTML => '\\^', HTML => '^' ); };
  973 sub PI {4*atan2(1,1);};
  974 sub E {exp(1);};
  975 
  976 ###############################################################
  977 ## Evaluation macros
  978 
  979 
  980 =head2 TEXT macros
  981 
  982   Usage:
  983     TEXT(@text);
  984 
  985 This is the simplest way to print text from a problem.  The strings in the array C<@text> are concatenated
  986 with spaces between them and printed out in the text of the problem.  The text is not processed in any other way.
  987 C<TEXT> is defined in PG.pl.
  988 
  989   Usage:
  990     BEGIN_TEXT
  991       text.....
  992     END_TEXT
  993 
  994 This is the most common way to enter text into the problem.  All of the text between BEGIN_TEXT and END_TEXT
  995 is processed by the C<EV3> macro described below and then printed using the C<TEXT> command.  The two key words
  996 must appear on lines by themselves.  The preprocessing that makes this construction work is done in F<PGtranslator.pm>.
  997 See C<EV3> below for details on the processing.
  998 
  999 
 1000 =cut
 1001 
 1002 =head2 Evaluation macros
 1003 
 1004 =head3 EV3
 1005 
 1006         TEXT(EV3("This is a formulat \( \int_0^5 x^2 \, dx \) ");
 1007         TEXT(EV3(@text));
 1008 
 1009     TEXT(EV3(<<'END_TEXT'));
 1010       text stuff...
 1011     END_TEXT
 1012 
 1013 
 1014 The BEGIN_TEXT/END_TEXT construction is translated into the construction above by PGtranslator.pm.  END_TEXT must appear
 1015 on a line by itself and be left justified.  (The << construction is known as a "here document" in UNIX and in PERL.)
 1016 
 1017 The single quotes around END_TEXT mean that no automatic interpolation of variables takes place in the text.
 1018 Using EV3 with strings which have been evaluated by double quotes may lead to unexpected results.
 1019 
 1020 
 1021 The evaluation macro E3 first evaluates perl code inside the braces:  C<\{  code \}>.
 1022 Any perl statment can be put inside the braces.  The
 1023 result of the evaluation (i.e. the last statement evaluated) replaces the C<\{ code \}> construction.
 1024 
 1025 Next interpolation of all variables (e.g. C<$var or @array> ) is performed.
 1026 
 1027 Then mathematical formulas in TeX are evaluated within the
 1028 C<\(  tex math mode \)> and
 1029 C<\[ tex display math mode \] >
 1030 constructions, in that order:
 1031 
 1032 =head3 FEQ
 1033 
 1034   FEQ($string);   # processes and outputs the string
 1035 
 1036 
 1037 The mathematical formulas are run through the macro C<FEQ> (Format EQuations) which performs
 1038 several substitutions (see below).
 1039 In C<HTML_tth> mode the resulting code is processed by tth to obtain an HTML version
 1040 of the formula. (In the future processing by WebEQ may be added here as another option.)
 1041 The Latex2HTML mode does nothing
 1042 at this stage; it creates the entire problem before running it through
 1043 TeX and creating the GIF images of the equations.
 1044 
 1045 The resulting string is output (and usually fed into TEXT to be printed in the problem).
 1046 
 1047   Usage:
 1048 
 1049     $string2 = FEQ($string1);
 1050 
 1051 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
 1052 understood with an example.
 1053 
 1054     $string1 = "${a}x^2 + ${b}x + {$c:%.1f}"; $a = 3;, $b = -2; $c = -7.345;
 1055 
 1056 when interpolated becomes:
 1057 
 1058     $string1 = '3x^2 + -2x + {-7.345:%0.1f}
 1059 
 1060 FEQ first changes the number of decimal places displayed, so that the last term becomes -7.3 Then it removes the
 1061 extraneous plus and minus signs, so that the final result is what you want:
 1062 
 1063     $string2 = '3x^2 - 2x -7.3';
 1064 
 1065 (The %0.1f construction
 1066 is the same formatting convention used by Perl and nearly identical to the one used by the C printf statement. Some common
 1067 usage:  %0.3f 3 decimal places, fixed notation; %0.3e 3 significant figures exponential notation; %0.3g uses either fixed
 1068 or exponential notation depending on the size of the number.)
 1069 
 1070 Two additional legacy formatting constructions are also supported:
 1071 
 1072 C<?{$c:%0.3f} > will give a number with 3 decimal places and a negative
 1073 sign if the number is negative, no sign if the number is positive.
 1074 
 1075 C<!{$c:%0.3f}> determines the sign and prints it
 1076 whether the number is positive or negative.
 1077 
 1078 =head3 EV2
 1079 
 1080     TEXT(EV2(@text));
 1081 
 1082     TEXT(EV2(<<END_OF_TEXT));
 1083       text stuff...
 1084     END_OF_TEXT
 1085 
 1086 This is a precursor to EV3.  In this case the constants are interpolated first, before the evaluation of the \{ ...code...\}
 1087 construct. This can lead to unexpected results.  For example C<\{ join(" ", @text) \}> with C<@text = ("Hello","World);> becomes,
 1088 after interpolation, C<\{ join(" ",Hello World) \}> which then causes an error when evaluated because Hello is a bare word.
 1089 C<EV2> can still be useful if you allow for this, and in particular it works on double quoted strings, which lead to
 1090 unexpected results with C<EV3>. Using single quoted strings with C<EV2> may lead to unexpected results.
 1091 
 1092 The unexpected results have to do with the number of times backslashed constructions have to be escaped. It's quite messy.  For
 1093 more details get a good Perl book and then read the code. :-)
 1094 
 1095 
 1096 
 1097 
 1098 =cut
 1099 
 1100 
 1101 sub ev_substring {
 1102     my $string      = shift;
 1103   my $start_delim = shift;
 1104   my $end_delim   = shift;
 1105   my $actionRef   = shift;
 1106   my ($eval_out,$PG_eval_errors,$PG_full_error_report)=();
 1107     my $out = "";
 1108     while ($string) {
 1109         if ($string =~ /\Q$start_delim\E/s) {
 1110        #print "$start_delim $end_delim evaluating_substring=$string<BR>";
 1111         $string =~ s/^(.*?)\Q$start_delim\E//s;  # get string up to next \{ ---treats string as a single line, ignoring returns
 1112         $out .= $1;
 1113        #print "$start_delim $end_delim substring_out=$out<BR>";
 1114         $string =~ s/^(.*?)\Q$end_delim\E//s;  # get perl code up to \} ---treats string as a single line,  ignoring returns
 1115            #print "$start_delim $end_delim evaluate_string=$1<BR>";
 1116         ($eval_out,$PG_eval_errors,$PG_full_error_report) = &$actionRef($1);
 1117         $eval_out = "$start_delim $eval_out $end_delim" if $PG_full_error_report;
 1118         $out = $out . $eval_out;
 1119        #print "$start_delim $end_delim new substring_out=$out<BR><p><BR>";
 1120         $out .="$main::PAR ERROR $0 in ev_substring, PGbasicmacros.pl:$main::PAR <PRE>  $@ </PRE>$main::PAR" if $@;
 1121         }
 1122       else {
 1123         $out .= $string;  # flush the last part of the string
 1124         last;
 1125         }
 1126 
 1127       }
 1128   $out;
 1129 }
 1130 sub  safe_ev {
 1131     my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev;   # process input by old_safe_ev first
 1132     $out =~s/\\/\\\\/g;   # protect any new backslashes introduced.
 1133   ($out,$PG_eval_errors,$PG_full_error_report)
 1134 }
 1135 
 1136 sub  old_safe_ev {
 1137     my $in = shift;
 1138     my   ($out,$PG_eval_errors,$PG_full_error_report) = PG_restricted_eval("$in;");
 1139     # the addition of the ; seems to provide better error reporting
 1140     if ($PG_eval_errors) {
 1141       my @errorLines = split("\n",$PG_eval_errors);
 1142     #$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> ";
 1143     warn " ERROR in old_safe_ev, PGbasicmacros.pl: <PRE>
 1144      ## There is an error occuring inside evaluation brackets \\{ ...code... \\}
 1145      ## somewhere in an EV2 or EV3 or BEGIN_TEXT block.
 1146      ## Code evaluated:
 1147      ## $in
 1148      ##" .join("\n     ", @errorLines). "
 1149      ##</PRE>$main::BR
 1150      ";
 1151      $out ="$main::PAR $main::BBOLD  $in $main::EBOLD $main::PAR";
 1152 
 1153 
 1154   }
 1155 
 1156   ($out,$PG_eval_errors,$PG_full_error_report);
 1157 }
 1158 
 1159 sub FEQ   {    # Format EQuations
 1160   my $in = shift;
 1161    # formatting numbers -- the ?{} and !{} constructions
 1162   $in =~s/\?\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &sspf($1,$2) )}/g;
 1163   $in =~s/\!\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &spf($1,$2) )}/g;
 1164 
 1165   # more formatting numbers -- {number:format} constructions
 1166   $in =~ s/\{(\s*[\+\-\d\.]+[eE]*[\+\-]*\d*):(\%\d*.\d*\w)}/${ \( &spf($1,$2) )}/g;
 1167   $in =~ s/\+\s*\-/ - /g;
 1168   $in =~ s/\-\s*\+/ - /g;
 1169   $in =~ s/\+\s*\+/ + /g;
 1170   $in =~ s/\-\s*\-/ + /g;
 1171   $in;
 1172 }
 1173 
 1174 sub math_ev3 {
 1175   my $in = shift; #print "in=$in<BR>";
 1176   my ($out,$PG_eval_errors,$PG_full_error_report);
 1177   $in = FEQ($in);
 1178   $in =~ s/%/\\%/g;   #  % causes trouble in TeX and HTML_tth it usually (always?) indicates an error, not comment
 1179   return("$main::BM $in $main::EM") unless ($main::displayMode eq 'HTML_tth');
 1180   $in = "\\(" . $in . "\\)";
 1181   $out = tth($in);
 1182   ($out,$PG_eval_errors,$PG_full_error_report);
 1183 
 1184 }
 1185 
 1186 sub display_math_ev3 {
 1187   my $in = shift; #print "in=$in<BR>";
 1188   my ($out,$PG_eval_errors,$PG_full_error_report);
 1189   $in = FEQ($in);
 1190   $in =~ s/%/\\%/g;
 1191   return("$main::BDM $in $main::EDM") unless $main::displayMode eq 'HTML_tth' ;
 1192   $in = "\\[" . $in . "\\]";
 1193   $out =tth($in);
 1194   ($out,$PG_eval_errors,$PG_full_error_report);
 1195 }
 1196 
 1197 sub EV2 {
 1198   my $string = join(" ",@_);
 1199   # evaluate code inside of \{  \}  (no nesting allowed)
 1200     $string = ev_substring($string,"\\{","\\}",\&old_safe_ev);
 1201     $string = ev_substring($string,"\\<","\\>",\&old_safe_ev);
 1202   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1203   $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1204   # macros for displaying math
 1205   $string =~ s/\\\(/$main::BM/g;
 1206   $string =~ s/\\\)/$main::EM/g;
 1207   $string =~ s/\\\[/$main::BDM/g;
 1208   $string =~ s/\\\]/$main::EDM/g;
 1209   $string;
 1210 }
 1211 
 1212 sub EV3{
 1213   my $string = join(" ",@_);
 1214   # evaluate code inside of \{  \}  (no nesting allowed)
 1215     $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev);  # handles \{ \} in single quoted strings of PG files
 1216   # interpolate variables
 1217   my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n");
 1218   if ($PG_eval_errors) {
 1219       my @errorLines = split("\n",$PG_eval_errors);
 1220       $string =~ s/</&lt;/g; $string =~ s/>/&gt;/g;
 1221     $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> ";
 1222     $@="";
 1223   }
 1224   $string = $evaluated_string;
 1225   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1226     $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1227   $string;
 1228 }
 1229 
 1230 =head2 Formatting macros
 1231 
 1232   beginproblem()  # generates text listing number and the point value of
 1233                   # the problem. It will also print the file name containing
 1234                   # the problem for users listed in the PRINT_FILE_NAMES_FOR PG_environment
 1235                   # variable.
 1236   OL(@array)      # formats the array as an Ordered List ( <OL> </OL> ) enumerated by letters.
 1237 
 1238   htmlLink($url, $text)
 1239                   # Places a reference to the URL with the specified text in the problem.
 1240                   # A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help')
 1241                   # where alias finds the full address of the prob1_help.html file in the same directory
 1242                   # as the problem file
 1243   appletLink($url, $parameters)
 1244                   # For example
 1245                   # appletLink(q!  archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip"
 1246                                   code="xFunctionsLauncher.class"  width=100 height=14!,
 1247                   " parameter text goes here")
 1248                   # will link to xFunctions.
 1249 
 1250   low level:
 1251 
 1252   spf($number, $format)   # prints the number with the given format
 1253   sspf($number, $format)  # prints the number with the given format, always including a sign.
 1254   protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX.
 1255 
 1256 =cut
 1257 
 1258 sub beginproblem {
 1259   my $out = "";
 1260     my $TeXFileName = protect_underbar($main::fileName);
 1261     my $l2hFileName = protect_underbar($main::fileName);
 1262   my %inlist;
 1263   my $points ='pts';
 1264   $points = 'pt' if $main::problemValue == 1;
 1265   ##    Prepare header for the problem
 1266   grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} });
 1267   if ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) {
 1268     $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.{\\footnotesize ($main::problemValue $points) $TeXFileName}}\\newline ",
 1269     " \\begin{rawhtml} ($main::problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}",
 1270      "($main::problemValue $points) <B>$main::fileName</B><BR>"
 1271        );
 1272   } else {
 1273     $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.}($main::problemValue $points) ",
 1274     "($main::problemValue $points) ",
 1275      "($main::problemValue $points) "
 1276        );
 1277   }
 1278   $out;
 1279 
 1280 }
 1281 
 1282 # kludge to clean up path names
 1283             ## allow underscore character in set and section names and also allows line breaks at /
 1284 sub protect_underbar {
 1285     my $in = shift;
 1286     if ($main::displayMode eq 'TeX')  {
 1287 
 1288         $in =~ s|_|\\\_|g;
 1289         $in =~ s|/|\\\-/|g;  # allows an optional hyphenation of the path (in tex)
 1290     }
 1291     $in;
 1292 }
 1293 
 1294 
 1295 # An example of a macro which prints out a list (with letters)
 1296 sub OL {
 1297   my(@array) = @_;
 1298   my $i = 0;
 1299   my  $out=   &M3(
 1300           "\\begin{enumerate}\n",
 1301           " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ",
 1302           "<OL TYPE=\"A\" VALUE=\"1\">\n"
 1303           ) ;
 1304   my $elem;
 1305   foreach $elem (@array) {
 1306     $out .= &M3(
 1307           "\\item[$main::ALPHABET[$i].] $elem\n",
 1308           " \\begin{rawhtml} <LI> \\end{rawhtml} $elem  ",
 1309           "<LI> $elem\n"
 1310           ) ;
 1311     $i++;
 1312   }
 1313   $out .= &M3(
 1314         "\\end{enumerate}\n",
 1315         " \\begin{rawhtml} </OL>\n \\end{rawhtml} ",
 1316         "</OL>\n"
 1317         ) ;
 1318 }
 1319 
 1320 sub htmlLink {
 1321   my $url = shift;
 1322   my $text = shift;
 1323   my $options = shift;
 1324   $options = "" unless defined($options);
 1325   return "${main::BBOLD}[ broken link:  $text ] ${main::EBOLD}" unless defined($url);
 1326   M3( "{\\bf \\underline{$text}  }",
 1327       "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}",
 1328       "<A HREF=\"$url\" $options> $text </A>"
 1329       );
 1330 }
 1331 sub appletLink {
 1332   my $url = shift;
 1333   my $options = shift;
 1334   $options = "" unless defined($options);
 1335   M3( "{\\bf \\underline{APPLET}  }",
 1336       "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}",
 1337       "<APPLET $url> $options </APPLET>"
 1338       );
 1339 }
 1340 sub spf {
 1341   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1342   $format = "%4.3g" unless $format;   # default value for format
 1343   sprintf($format, $number);
 1344   }
 1345 sub sspf {
 1346   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1347   $format = "%4.3g" unless $format;   # default value for format
 1348   my $sign = $number>=0 ? " + " : " - ";
 1349   $number = $number>=0 ? $number : -$number;
 1350   $sign .sprintf($format, $number);
 1351   }
 1352 
 1353 =head2  Sorting and other list macros
 1354 
 1355 
 1356 
 1357   Usage:
 1358   lex_sort(@list);   # outputs list in lexigraphic (alphabetical) order
 1359   num_sort(@list);   # outputs list in numerical order
 1360   uniq( @list);      # outputs a list with no duplicates.  Order is unspecified.
 1361 
 1362   PGsort( \&sort_subroutine, @list);
 1363   # &sort_subroutine defines order. It's output must be -1,0 or 1.
 1364 
 1365 =cut
 1366 
 1367 #  uniq gives unique elements of a list:
 1368  sub uniq {
 1369    my (@in) =@_;
 1370    my %temp = ();
 1371    while (@in) {
 1372           $temp{shift(@in)}++;
 1373       }
 1374    my @out =  keys %temp;  # sort is causing trouble with Safe.??
 1375    @out;
 1376 }
 1377 
 1378 sub lex_sort {
 1379   PGsort sub {$_[0] cmp $_[1]}, @_;
 1380 }
 1381 sub num_sort {
 1382   PGsort sub {$_[0] <=> $_[1]}, @_;
 1383 }
 1384 
 1385 
 1386 =head2 Macros for handling tables
 1387 
 1388   Usage:
 1389   begintable( number_of_columns_in_table)
 1390   row(@dataelements)
 1391   endtable()
 1392 
 1393 Example of useage:
 1394 
 1395   BEGIN_TEXT
 1396     This problem tests calculating new functions from old ones:$BR
 1397     From the table below calculate the quantities asked for:$BR
 1398     \{begintable(scalar(@firstrow)+1)\}
 1399     \{row(" \(x\) ",@firstrow)\}
 1400     \{row(" \(f(x)\) ", @secondrow)\}
 1401     \{row(" \(g(x)\) ", @thirdrow)\}
 1402     \{row(" \(f'(x)\) ", @fourthrow)\}
 1403     \{row(" \(g'(x)\) ", @fifthrow)\}
 1404     \{endtable()\}
 1405 
 1406    (The arrays contain numbers which are placed in the table.)
 1407 
 1408   END_TEXT
 1409 
 1410 =cut
 1411 
 1412 sub begintable {
 1413   my ($number)=shift;   #number of columns in table
 1414   my %options = @_;
 1415   warn "begintable(cols) requires a number indicating the number of columns" unless defined($number);
 1416   my $out = "";
 1417   if ($main::displayMode eq 'TeX') {
 1418     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1419     }
 1420   elsif ($main::displayMode eq 'Latex2HTML') {
 1421     $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}";
 1422     }
 1423   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1424     $out .= "<TABLE BORDER=1>\n"
 1425   }
 1426   else {
 1427     $out = "Error: PGchoicemacros: begintable: Unknown displayMode: $main::displayMode.\n";
 1428     }
 1429   $out;
 1430   }
 1431 
 1432 sub endtable {
 1433   my $out = "";
 1434   if ($main::displayMode eq 'TeX') {
 1435     $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1436     }
 1437   elsif ($main::displayMode eq 'Latex2HTML') {
 1438     $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}";
 1439     }
 1440   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1441     $out .= "</TABLE>\n";
 1442     }
 1443   else {
 1444     $out = "Error: PGchoicemacros: endtable: Unknown displayMode: $main::displayMode.\n";
 1445     }
 1446   $out;
 1447   }
 1448 
 1449 
 1450 sub row {
 1451   my @elements = @_;
 1452   my $out = "";
 1453   if ($main::displayMode eq 'TeX') {
 1454     while (@elements) {
 1455       $out .= shift(@elements) . " &";
 1456       }
 1457      chop($out); # remove last &
 1458      $out .= "\\\\ \\hline \n";
 1459      # carriage returns must be added manually for tex
 1460     }
 1461   elsif ($main::displayMode eq 'Latex2HTML') {
 1462     $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n";
 1463     while (@elements) {
 1464       $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n";
 1465       }
 1466     $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n";
 1467   }
 1468   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1469     $out .= "<TR>\n";
 1470     while (@elements) {
 1471       $out .= "<TD>" . shift(@elements) . "</TD>";
 1472       }
 1473     $out .= "\n</TR>\n";
 1474   }
 1475   else {
 1476     $out = "Error: PGchoicemacros: row: Unknown displayMode: $main::displayMode.\n";
 1477     }
 1478   $out;
 1479 }
 1480 
 1481 =head2 Macros for displaying static images
 1482 
 1483   Usage:
 1484   $string = image($image, width => 100, height => 100, tex_size => 800)
 1485   $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800)
 1486   $string = caption($string);
 1487   $string = imageRow([$image1, $image2 ], [$caption1, $caption2]);
 1488            # produces a complete table with rows of pictures.
 1489 
 1490 
 1491 =cut
 1492 
 1493 #   More advanced macros
 1494 sub image {
 1495   my $image_ref  = shift;
 1496     my @opt = @_;
 1497     unless (scalar(@opt) % 2 == 0 ) {
 1498       warn "ERROR in image macro.  A list of macros must be inclosed in square brackets.";
 1499     }
 1500     my %in_options = @opt;
 1501     my %known_options = ( width   => 100,
 1502                           height  => 100,
 1503                           tex_size  => 800
 1504                          );
 1505 #   # handle options
 1506     my %out_options = %known_options;
 1507   foreach my $opt_name (keys %in_options) {
 1508     if ( exists( $known_options{$opt_name} ) ) {
 1509       $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ;
 1510     } else {
 1511       die "Option $opt_name not defined for image. " .
 1512       "Default options are:<BR> ", display_options2(%known_options);
 1513 
 1514     }
 1515   }
 1516   my $width     = $out_options{width};
 1517   my $height    = $out_options{height};
 1518   my $tex_size  = $out_options{tex_size};
 1519   my $width_ratio = $tex_size*(.001);
 1520   my @image_list = ();
 1521 
 1522   if (ref($image_ref) =~ /ARRAY/ ) {
 1523     @image_list = @{$image_ref};
 1524   } else {
 1525     push(@image_list,$image_ref);
 1526   }
 1527   my @output_list = ();
 1528     while(@image_list) {
 1529 
 1530     my $imageURL = alias(shift @image_list);
 1531     my $out="";
 1532 
 1533 
 1534     if ($main::displayMode eq 'TeX') {
 1535       $out = qq!\\includegraphics[width=$width_ratio\\linewidth]{$imageURL}\n !
 1536       }
 1537     elsif ($main::displayMode eq 'Latex2HTML') {
 1538       $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>\n
 1539       \\end{rawhtml}\n !
 1540       }
 1541     elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1542       $out = qq!<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>
 1543       !
 1544       }
 1545     else {
 1546       $out = "Error: PGchoicemacros: image: Unknown displayMode: $main::displayMode.\n";
 1547       }
 1548     push(@output_list, $out);
 1549   }
 1550   wantarray ? @output_list : $output_list[0] ;
 1551 }
 1552 
 1553 # This is legacy code.
 1554 sub images {
 1555   my @in = @_;
 1556   my @outlist = ();
 1557   while (@in) {
 1558      push(@outlist,&image( shift(@in) ) );
 1559    }
 1560   @outlist;
 1561 }
 1562 
 1563 
 1564 sub caption {
 1565   my ($out) = @_;
 1566   $out = " $out \n" if $main::displayMode eq 'TeX';
 1567   $out = " $out  " if $main::displayMode eq 'HTML';
 1568   $out = " $out  " if $main::displayMode eq 'HTML_tth';
 1569   $out = " $out  " if $main::displayMode eq 'Latex2HTML';
 1570     $out;
 1571 }
 1572 
 1573 sub captions {
 1574   my @in = @_;
 1575   my @outlist = ();
 1576   while (@in) {
 1577      push(@outlist,&caption( shift(@in) ) );
 1578   }
 1579   @outlist;
 1580 }
 1581 
 1582 sub imageRow {
 1583 
 1584   my $pImages = shift;
 1585   my $pCaptions=shift;
 1586   my $out = "";
 1587   my @images = @$pImages;
 1588   my @captions = @$pCaptions;
 1589   my $number = @images;
 1590   # standard options
 1591   my %options = ( 'tex_size' => 200,  # width for fitting 4 across
 1592                   'height' => 100,
 1593                   'width' => 100,
 1594                   @_            # overwrite any default options
 1595                 );
 1596 
 1597   if ($main::displayMode eq 'TeX') {
 1598     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1599     while (@images) {
 1600       $out .= &image( shift(@images),%options ) . '&';
 1601     }
 1602     chop($out);
 1603     $out .= "\\\\ \\hline \n";
 1604     while (@captions) {
 1605       $out .= &caption( shift(@captions) ) . '&';
 1606     }
 1607     chop($out);
 1608     $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1609   } elsif ($main::displayMode eq 'Latex2HTML'){
 1610 
 1611     $out .= "\n\\begin{rawhtml} <TABLE  BORDER=1><TR>\n\\end{rawhtml}\n";
 1612     while (@images) {
 1613       $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options )
 1614               . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ;
 1615     }
 1616 
 1617     $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n";
 1618     while (@captions) {
 1619       $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) )
 1620               . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ;
 1621     }
 1622 
 1623     $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}";
 1624   } elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth'){
 1625     $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER    VALIGN=MIDDLE>\n";
 1626     while (@images) {
 1627       $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>";
 1628     }
 1629     $out .= "</TR>\n<TR>";
 1630     while (@captions) {
 1631       $out .= " <TH>". &caption( shift(@captions) ) ."</TH>";
 1632     }
 1633     $out .= "\n</TR></TABLE></P>\n"
 1634   }
 1635   else {
 1636     $out = "Error: PGchoicemacros: imageRow: Unknown languageMode: $main::displayMode.\n";
 1637     warn $out;
 1638   }
 1639   $out;
 1640 }
 1641 
 1642 
 1643 ###########
 1644 # Auxiliary macros
 1645 
 1646 sub display_options2{
 1647   my %options = @_;
 1648   my $out_string = "";
 1649   foreach my $key (keys %options) {
 1650     $out_string .= " $key => $options{$key},<BR>";
 1651   }
 1652   $out_string;
 1653 }
 1654 
 1655 
 1656 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9