Parent Directory
|
Revision Log
Added jsMath support to these files. Basically, HTML_jsMath should do whatever HTML_dpng mode does. I simply included it HTML_jsMath every time HTML_dpng appeared.
1 # This file provided the fundamental macros for the pg language 2 # These macros define the interface between the problems written by 3 # the professor and the processing which occurs in the script 4 # processProblem.pl 5 6 7 BEGIN { 8 be_strict(); 9 } 10 11 sub _PG_init{ 12 13 } 14 15 #package PG; 16 17 18 =head1 NAME 19 20 PG.pl --- located in the courseScripts directory. 21 Defines the Program Generating language at the most basic level. 22 23 =head1 SYNPOSIS 24 25 The basic PG problem structure: 26 27 DOCUMENT(); # should be the first statment in the problem 28 loadMacros(.....); # (optional) load other macro files if needed. 29 # (loadMacros is defined in F<dangerousMacros.pl>) 30 31 HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems. 32 33 # # insert text of problems 34 TEXT("Problem text to be", 35 "displayed. Enter 1 in this blank:", 36 ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long. 37 # It is defined in F<PGbasicmacros.pl> 38 ); 39 40 41 ANS( answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors. 42 43 ENDDOCUMENT() # must be the last statement in the problem 44 45 46 47 =head1 DESCRIPTION 48 49 As described in the synopsis, this file and the macros C<DOCUMENT()> and C<ENDDOCUMENT()> determine 50 the interface between problems written in the PG language and the rest of B<WeBWorK>, in particular 51 the subroutine C<createPGtext(()> in the file F<translate.pl>. 52 53 C<DOCUMENT()> must be the first statement in each problem template. 54 It initializes variables, 55 in particular all of the contents of the 56 environment variable become defined in the problem enviroment. 57 (See 58 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>) 59 60 ENDDOCUMENT() must the last executable statement in any problem template. It returns 61 the rendered problem, answer evaluators and other flags to the rest of B<WeBWorK>, specificially 62 to the routine C<createPGtext()> defined in F<translate.pl> 63 64 65 The C<HEADER_TEXT()>, C<TEXT()>, and C<ANS()> functions load the 66 header text string, the problem text string. 67 and the answer evaulator queue respectively. 68 69 70 =cut 71 72 73 # Private variables for the PG.pl file. 74 75 my ($STRINGforOUTPUT, $STRINGforHEADER_TEXT, @PG_ANSWERS, @PG_UNLABELED_ANSWERS); 76 my %PG_ANSWERS_HASH ; 77 78 # my variables are unreliable if two DOCUMENTS were to be called before and ENDDOCUMENT 79 # there could be conflicts. As I understand the behavior of the Apache child 80 # this cannot occur -- a child finishes with one request before obtaining the next 81 82 # DOCUMENT must come early in every .pg file, before any answers or text are 83 # defined. It initializes the variables. 84 # It can appear only once. 85 86 =head2 DOCUMENT() 87 88 C<DOCUMENT()> must be the first statement in each problem template. It can 89 only be used once in each problem. 90 91 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the 92 variables in the C<%envir> variable which is implicitly passed to the problem. It must 93 be the first statement in any problem template. It 94 also unpacks any answers submitted and places them in the C<@submittedAnswer> list, 95 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and 96 initializes the pseudo random number generator object in C<$PG_random_generator>. 97 98 You can reset the standard number generator using the command: 99 100 $PG_random_generator->srand($new_seed_value); 101 102 (See also C<SRAND> in the L<PGbasicmacros.pl> file.) 103 104 The 105 environment variable contents is defined in 106 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html> 107 108 109 =cut 110 111 sub DOCUMENT { 112 113 $STRINGforOUTPUT =""; 114 $STRINGforHEADER_TEXT =""; 115 @PG_ANSWERS=(); 116 117 @PG_UNLABELED_ANSWERS = (); 118 %PG_ANSWERS_HASH = (); 119 # FIXME: We are initializing these variables into both Safe::Root1 (the cached safe compartment) 120 # and Safe::Root2 (the current one) 121 # There is a good chance they won't be properly updated in one or the other of these compartments. 122 123 # @main::PG_ANSWER_ENTRY_ORDER = (); 124 # $main::ANSWER_PREFIX = 'AnSwEr'; 125 # %main::PG_FLAGS=(); #global flags 126 # $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 127 # $main::showHint = 1 unless defined($main::showHint); 128 # $main::solutionExists =0; 129 # $main::hintExists =0; 130 # %main::gifs_created = (); 131 eval(q! 132 @main::PG_ANSWER_ENTRY_ORDER = (); 133 $main::ANSWER_PREFIX = 'AnSwEr'; 134 %main::PG_FLAGS=(); #global flags 135 $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 136 $main::showHint = 1 unless defined($main::showHint); 137 $main::solutionExists =0; 138 $main::hintExists =0; 139 %main::gifs_created = (); 140 141 !); 142 # warn eval(q! "PG.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!); 143 my $rh_envir = eval(q!\%main::envir!); 144 my %envir = %$rh_envir; 145 #no strict; 146 foreach my $var (keys %envir) { 147 eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! ); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time. 148 # warn eval(q! "var $var is defined ". $main::!.$var); 149 warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@; 150 } 151 #use strict; 152 #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0 153 154 155 156 eval(q! 157 @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 158 $main::PG_original_problemSeed = $main::problemSeed; 159 $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 160 $main::ans_rule_count = 0; # counts questions 161 162 # end unpacking of environment variables. 163 $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX) 164 165 !); 166 # @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 167 # $main::PG_original_problemSeed = $main::problemSeed; 168 # $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 169 # $main::ans_rule_count = 0; # counts questions 170 171 # end unpacking of environment variables. 172 # $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX) 173 174 $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{jsMathURL}.'"></SCRIPT>' . "\n" . 175 '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' . 176 '<B>Warning: the mathematics on this page requires JavaScript.<BR>' . 177 'If your browser supports it, be sure it is enabled.</B>'. 178 '</FONT></CENTER><p></NOSCRIPT>' . 179 $STRINGforOUTPUT if ($main::envir{displayMode} eq 'HTML_jsMath'); 180 181 $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" . 182 '<SCRIPT>mathcolor = "black"</SCRIPT>' . $STRINGforOUTPUT 183 if ($main::envir{displayMode} eq 'HTML_asciimath'); 184 185 } 186 187 sub inc_ans_rule_count { 188 eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main:: 189 } 190 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such 191 # as javaScript code. 192 193 =head2 HEADER_TEXT() 194 195 196 HEADER_TEXT("string1", "string2", "string3"); 197 198 The C<HEADER_TEXT()> 199 function concatenates its arguments and places them in the output 200 header text string. It is used for material which is destined to be placed in 201 the header of the html problem -- such as javaScript code. 202 It can be used more than once in a file. 203 204 205 =cut 206 207 sub HEADER_TEXT { 208 my @in = @_; 209 $STRINGforHEADER_TEXT .= join(" ",@in); 210 } 211 212 # TEXT is the function which defines text which will appear in the problem. 213 # All text must be an argument to this function. Any other statements 214 # are calculations (done in perl) which will not directly appear in the 215 # output. Think of this as the "print" function for the .pg language. 216 # It can be used more than once in a file. 217 218 =head2 TEXT() 219 220 TEXT("string1", "string2", "string3"); 221 222 The C<TEXT()> function concatenates its arguments and places them in the output 223 text string. C<TEXT()> is the function which defines text which will appear in the problem. 224 All text must be an argument to this function. Any other statements 225 are calculations (done in perl) which will not directly appear in the 226 output. Think of this as the "print" function for the .pg language. 227 It can be used more than once in a file. 228 229 =cut 230 231 sub TEXT { 232 my @in = @_; 233 $STRINGforOUTPUT .= join(" ",@in); 234 } 235 236 237 238 =head2 ANS() 239 240 ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...) 241 242 Places the answer evaluators in the unlabeled answer_evaluator queue. They will be paired 243 with unlabeled answer rules (answer entry blanks) in the order entered. This is the standard 244 method for entering answers. 245 246 LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...) 247 248 Places the answer evaluators in the labeled answer_evaluator hash. This allows pairing of 249 labeled answer evaluators and labeled answer rules which may not have been entered in the same 250 order. 251 252 =cut 253 254 sub ANS{ # store answer evaluators which have not been explicitly labeled 255 my @in = @_; 256 while (@in ) { 257 warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to 258 subroutines<BR>") 259 unless ref($in[0]); 260 push(@PG_ANSWERS, shift @in ); 261 } 262 } 263 sub LABELED_ANS { #a better alias for NAMED_ANS 264 &NAMED_ANS; 265 } 266 267 sub NAMED_ANS{ # store answer evaluators which have been explicitly labeled (submitted in a hash) 268 my @in = @_; 269 while (@in ) { 270 my $label = shift @in; 271 $label = eval(q!$main::QUIZ_PREFIX.$label!); 272 my $ans_eval = shift @in; 273 TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B> 274 -- inputs must be references to subroutines<BR>") 275 unless ref($ans_eval); 276 $PG_ANSWERS_HASH{$label}= $ans_eval; 277 } 278 } 279 sub RECORD_ANS_NAME { # this maintains the order in which the answer rules are printed. 280 my $label = shift; 281 eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!); 282 $label; 283 } 284 285 sub NEW_ANS_NAME { # this keeps track of the answers which are entered implicitly, 286 # rather than with a specific label 287 my $number=shift; 288 my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!); 289 my $label = $prefix.$number; 290 push(@PG_UNLABELED_ANSWERS,$label); 291 $label; 292 } 293 sub ANS_NUM_TO_NAME { # This converts a number to an answer label for use in 294 # radio button and check box answers. No new answer 295 # name is recorded. 296 my $number=shift; 297 my $label = eval(q!$main::QUIZ_PREFIX$main::ANSWER_PREFIX$number"!); 298 $label; 299 } 300 301 my $vecnum; 302 303 sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more 304 # it's a bit of hack since we are storing these in the KEPT_EXTRA_ANSWERS queue even if they aren't answers per se. 305 my $label = shift; # the label of the input box or textarea 306 eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes 307 $label; 308 } 309 sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers which are entered implicitly, 310 # rather than with a specific label 311 my $number=shift; 312 $vecnum = 0; 313 my $row = shift; 314 my $col = shift; 315 # my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]"; 316 my $label = "ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"; 317 push(@PG_UNLABELED_ANSWERS,$label); 318 $label; 319 } 320 321 sub NEW_ANS_ARRAY_NAME_EXTENSION { # this keeps track of the answers which are entered implicitly, 322 # rather than with a specific label 323 my $number=shift; 324 my $row = shift; 325 my $col = shift; 326 if( $row == 0 && $col == 0 ){ 327 $vecnum += 1; 328 } 329 #FIXME change made to conform to HTML 4.01 standards. "Name" attributes can only contain 330 # alphanumeric characters, _ : and . 331 # Also need to make corresponding changes in PGmorematrixmacros. grep for ArRaY. 332 #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]"; 333 my $label = "ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"; 334 eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes 335 $label; 336 } 337 338 # ENDDOCUMENT must come at the end of every .pg file. 339 # It exports the resulting text of the problem, the text to be used in HTML header material 340 # (for javaScript), the list of answer evaluators and any other flags. It can appear only once and 341 # it MUST be the last statement in the problem. 342 343 =head2 ENDDOCUMENT() 344 345 ENDDOCUMENT() must the last executable statement in any problem template. It can 346 only appear once. It returns 347 an array consisting of 348 349 A reference to a string containing the rendered text of the problem. 350 A reference to a string containing text to be placed in the header 351 (for javaScript) 352 A reference to the array containing the answer evaluators. 353 (May be changed to a hash soon.) 354 A reference to an associative array (hash) containing various flags. 355 356 The following flags are set by ENDDOCUMENT: 357 (1) showPartialCorrectAnswers -- determines whether students are told which 358 of their answers in a problem are wrong. 359 (2) recordSubmittedAnswers -- determines whether students submitted answers 360 are saved. 361 (3) refreshCachedImages -- determines whether the cached image of the problem 362 in typeset mode is always refreshed (i.e. setting this to 1 means cached 363 images are not used). 364 (4) solutionExits -- indicates the existence of a solution. 365 (5) hintExits -- indicates the existence of a hint. 366 (6) showHintLimit -- determines the number of attempts after which hint(s) will be shown 367 368 (7) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order 369 (a) A problem grader specified by the problem using: 370 install_problem_grader(\&grader); 371 (b) One of the standard problem graders defined in PGanswermacros.pl when set to 372 'std_problem_grader' or 'avg_problem_grader' by the environment variable 373 $PG_environment{PROBLEM_GRADER_TO_USE} 374 (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE} 375 (d) The default &std_problem_grader defined in PGanswermacros.pl 376 377 378 =cut 379 380 sub ENDDOCUMENT { 381 382 my $index=0; 383 foreach my $label (@PG_UNLABELED_ANSWERS) { 384 if ( defined($PG_ANSWERS[$index]) ) { 385 $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index]; 386 #warn "recording answer label = $label"; 387 } else { 388 warn "No answer provided by instructor for answer $label"; 389 } 390 $index++; 391 } 392 393 $STRINGforOUTPUT .="\n"; 394 eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines. 395 $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers; 396 $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers; 397 $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages; 398 $main::PG_FLAGS{'hintExists'} = $main::hintExists; 399 $main::PG_FLAGS{'showHintLimit'} = $main::showHint; 400 $main::PG_FLAGS{'solutionExists'} = $main::solutionExists; 401 $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER; 402 $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME" 403 $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX; 404 # install problem grader 405 if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) { 406 # problem grader defined within problem -- no further action needed 407 } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) { 408 if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader 409 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE}; 410 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) { 411 if (defined(&std_problem_grader) ){ 412 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 413 } # std_problem_grader is the default in any case so don't give a warning. 414 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) { 415 if (defined(&avg_problem_grader) ){ 416 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl 417 } 418 #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning. 419 # warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?"; 420 #} 421 } else { 422 warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader."; 423 } 424 } elsif (defined(&std_problem_grader)) { 425 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 426 } else { 427 # PGtranslator will install its default problem grader 428 } 429 430 warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE' 431 or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader' 432 or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader'; 433 # return results 434 }; 435 436 $STRINGforOUTPUT .= '<SCRIPT> jsMath.ProcessBeforeShowing() </SCRIPT>' 437 if ($main::envir{displayMode} eq 'HTML_jsMath'); 438 439 if ($main::envir{displayMode} eq 'HTML_asciimath') { 440 $STRINGforOUTPUT .= '<SCRIPT> translate() </SCRIPT>'; 441 $STRINGforHEADER_TEXT .= 442 '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" . 443 '</object><?import namespace="mml" implementation="#mathplayer"?>' 444 unless ($STRINGforHEADER_TEXT =~ m/mathplayer/); 445 } 446 447 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!)); 448 } 449 450 451 452 =head2 INITIALIZE_PG() 453 454 This is executed each C<DOCUMENT()> is called. For backward compatibility 455 C<loadMacros> also checks whether the C<macroDirectory> has been defined 456 and if not, it runs C<INITIALIZE_PG()> and issues a warning. 457 458 =cut 459 460 461 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |