You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: Gavin L. v. a. <we...@ma...> - 2008-06-25 14:58:18
|
Log Message: ----------- GatewayQuiz: allow problem preview from problem editor. (Allows input set as Undefined_Set and creates a fake one problem assignment.) Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: GatewayQuiz.pm Revision Data ------------- Index: GatewayQuiz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm,v retrieving revision 1.52 retrieving revision 1.53 diff -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -u -r1.52 -r1.53 --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -34,9 +34,10 @@ use WeBWorK::PG::ImageGenerator; use WeBWorK::PG::IO; use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers - ref2string makeTempDirectory sortByName before after between - formatDateTime); + ref2string makeTempDirectory path_is_subdir sortByName before after + between formatDateTime); use WeBWorK::DB::Utils qw(global2user user2global); +use WeBWorK::Utils::Tasks qw(fake_set fake_set_version fake_problem); use WeBWorK::Debug; use WeBWorK::ContentGenerator::Instructor qw(assignSetVersionToUser); use PGrandom; @@ -159,6 +160,9 @@ $tmplSet, $submitAnswers) = @_; my $authz = $self->r->authz; +# easy first case: never record answers for undefined sets + return 0 if ( $Set->set_id eq "Undefined_Set" ); + my $timeNow = ( defined($self->{timeNow}) ) ? $self->{timeNow} : time(); # get the sag time after the due date in which we'll still grade the test my $grace = $self->{ce}->{gatewayGracePeriod}; @@ -520,80 +524,167 @@ # assigned the set, below ################################### -# gateway content generator tests +# gateway set and problem collection ################################### +# we need the template (user) set, the merged set-version, and a +# problem from the set to be able to test whether we're creating a +# new set version. assemble these + my ( $tmplSet, $set, $Problem ) = ( 0, 0, 0 ); + +# if the set comes in as "Undefined_Set", then we're trying/editing a +# single problem in a set, and so create a fake set with which to work +# if the user has the authorization to do that. + if ( $setName eq "Undefined_Set" ) { + + # make sure these are defined + $requestedVersion = 1; + $self->{assignment_type} = 'gateway'; + + if ( ! $authz->hasPermissions($userName, + "modify_problem_sets") ) { + $self->{invalidSet} = "You do not have the " . + "authorization level required to view/" . + "edit undefined sets."; + + # define these so that we can drop through + # to report the error in body() + $tmplSet = fake_set( $db ); + $set = fake_set_version( $db ); + $Problem = fake_problem( $db ); + } else { + # in this case we're creating a fake set from the input, so + # the input must include a source file. + if ( ! $r->param("sourceFilePath") ) { + $self->{invalidSet} = "An Undefined_Set " . + "was requested, but no source " . + "file for the contained problem " . + "was provided."; + + # define these so that we can drop through + # to report the error in body() + $tmplSet = fake_set( $db ); + $set = fake_set_version( $db ); + $Problem = fake_problem( $db ); + + } else { + my $sourceFPath = $r->param("sourceFilePath"); + die("sourceFilePath is unsafe!") unless + path_is_subdir($sourceFPath, + $ce->{courseDirs}->{templates}, + 1); + + $tmplSet = fake_set( $db ); + $set = fake_set_version( $db ); + $Problem = fake_problem( $db ); + + $tmplSet->assignment_type( "gateway" ); + $tmplSet->attempts_per_version( 0 ); + $tmplSet->time_interval( 0 ); + $tmplSet->versions_per_interval(1); + $tmplSet->version_time_limit( 0 ); + $tmplSet->version_creation_time( time() ); + $tmplSet->problem_randorder( 0 ); + $tmplSet->problems_per_page( 1 ); + $tmplSet->hide_score('N'); + $tmplSet->hide_score_by_problem('N'); + $tmplSet->hide_work('N'); + $tmplSet->time_limit_cap('0'); + $tmplSet->restrict_ip('No'); + + $set->assignment_type( "gateway" ); + $set->time_interval( 0 ); + $set->versions_per_interval(1); + $set->version_time_limit( 0 ); + $set->version_creation_time( time() ); + $set->time_limit_cap('0'); + + $Problem->problem_id(1); + $Problem->source_file($sourceFPath); + $Problem->user_id($effectiveUserName); + $Problem->value(1); + $Problem->problem_seed( $r->param("problemSeed") ) if ( $r->param("problemSeed") ); + } + } + } else { + # get template set: the non-versioned set that's assigned to the user # if this fails/failed in authz->checkSet, then $self->{invalidSet} is # set - my $tmplSet = $db->getMergedSet( $effectiveUserName, $setName ); + $tmplSet = $db->getMergedSet( $effectiveUserName, $setName ); # now we know that we're in a gateway test, save the assignment test # for the processing of proctor keys for graded proctored tests; # if we failed to get the set from the database, we store a fake # value here to be able to continue - $self->{'assignment_type'} = $tmplSet->assignment_type() || 'gateway'; + $self->{'assignment_type'} = $tmplSet->assignment_type() || + 'gateway'; # next, get the latest (current) version of the set if we don't have a # requested version number - my @allVersionIds = $db->listSetVersions($effectiveUserName, $setName); - my $latestVersion = ( @allVersionIds ? $allVersionIds[-1] : 0 ); + my @allVersionIds = $db->listSetVersions($effectiveUserName, + $setName); + my $latestVersion = (@allVersionIds ? $allVersionIds[-1] : 0); # double check that any requested version makes sense - $requestedVersion = $latestVersion - if ( $requestedVersion !~ /^\d+$/ || - $requestedVersion > $latestVersion || - $requestedVersion < 0 ); - - die("No requested version when returning to problem?!") - if ( ($r->param("previewAnswers") || $r->param("checkAnswers") || - $r->param("submitAnswers") || $r->param("newPage")) - && ! $requestedVersion ); + $requestedVersion = $latestVersion + if ( $requestedVersion !~ /^\d+$/ || + $requestedVersion > $latestVersion || + $requestedVersion < 0 ); + + die("No requested version when returning to problem?!") + if ( ( $r->param("previewAnswers") || + $r->param("checkAnswers") || + $r->param("submitAnswers") || + $r->param("newPage") ) && ! $requestedVersion ); # to test for a proctored test, we need the set version, not the # template, to allow a finished proctored test to be checked as an # unproctored test. so we get the versioned set here - my $set; - if ( $requestedVersion ) { + if ( $requestedVersion ) { # if a specific set version was requested, it was stored in the $authz # object when we did the set check - $set = $db->getMergedSetVersion($effectiveUserName, $setName, - $requestedVersion); - } elsif ( $latestVersion ) { + $set = $db->getMergedSetVersion($effectiveUserName, + $setName, + $requestedVersion); + } elsif ( $latestVersion ) { # otherwise, if there's a current version, which we take to be the # latest version taken, we use that - $set = $db->getMergedSetVersion($effectiveUserName, $setName, - $latestVersion); - } else { + $set = $db->getMergedSetVersion($effectiveUserName, + $setName, + $latestVersion); + } else { # and if neither of those work, get a dummy set so that we have # something to work with - my $userSetClass = $ce->{dbLayout}->{set_version}->{record}; + my $userSetClass = $ce->{dbLayout}->{set_version}->{record}; # FIXME RETURN TO: should this be global2version? - $set = global2user($userSetClass, $db->getGlobalSet($setName)); - die "set $setName not found." unless $set; - $set->user_id($effectiveUserName); - $set->psvn('000'); - $set->set_id("$setName"); # redundant? - $set->version_id(0); + $set = global2user($userSetClass, + $db->getGlobalSet($setName)); + die "set $setName not found." unless $set; + $set->user_id($effectiveUserName); + $set->psvn('000'); + $set->set_id("$setName"); # redundant? + $set->version_id(0); + } } - my $setVersionNumber = $set->version_id(); + my $setVersionNumber = ($set) ? $set->version_id() : 0; ################################# # assemble gateway parameters ################################# # we get the open/close dates for the gateway from the template set. - # note $isOpen/Closed give the open/close dates for the gateway + # note $isOpen/Closed give the open/close dates for the gateway # as a whole (that is, the merged user|global set). because the # set could be bad (if $self->{invalidSet}), we check ->open_date # before actually testing the date - my $isOpen = $tmplSet->open_date && + my $isOpen = $tmplSet && $tmplSet->open_date && ( after($tmplSet->open_date()) || $authz->hasPermissions($userName, "view_unopened_sets") ); - # FIXME for $isClosed, "record_answers_after_due_date" isn't quite + # FIXME for $isClosed, "record_answers_after_due_date" isn't quite # the right description, but it seems reasonable - my $isClosed = $tmplSet->due_date && + my $isClosed = $tmplSet && $tmplSet->due_date && ( after($tmplSet->due_date()) && ! $authz->hasPermissions($userName, "record_answers_after_due_date") ); @@ -604,15 +695,20 @@ # problems will have the same number of attempts. This means that # if the set doesn't have any problems we're up a creek, so check # for that here and bail if it's the case - my @setPNum = $db->listUserProblems($EffectiveUser->user_id, $setName); + my @setPNum = $setName eq "Undefined_Set" ? ( 1 ) : + $db->listUserProblems($EffectiveUser->user_id, $setName); die("Set $setName contains no problems.") if ( ! @setPNum ); - # the Problem here can be undefined, if the set hasn't been versioned - # to the user yet--this gets fixed when we assign the setVersion - my $Problem = $setVersionNumber ? - $db->getMergedProblemVersion($EffectiveUser->user_id, $setName, - $setVersionNumber, $setPNum[0]) : - undef; + # if we assigned a fake problem above, $Problem is already defined. + # otherwise, we get the Problem, or define it to be undefined if + # the set hasn't been versioned to the user yet--this gets fixed + # when we assign the setVersion + if ( ! $Problem ) { + $Problem = $setVersionNumber ? + $db->getMergedProblemVersion($EffectiveUser->user_id, + $setName, $setVersionNumber, $setPNum[0]) : + undef; + } # note that having $maxAttemptsPerVersion set to an infinite/0 value is # nonsensical; if we did that, why have versions? @@ -621,8 +717,8 @@ my $versionsPerInterval = $tmplSet->versions_per_interval(); my $timeLimit = $tmplSet->version_time_limit(); - # what happens if someone didn't set one of these? I think this can - # happen if we're handed a malformed set, where the values in the + # what happens if someone didn't set one of these? I think this can + # happen if we're handed a malformed set, where the values in the # database are null. $timeInterval = 0 if (! defined($timeInterval) || $timeInterval eq ''); $versionsPerInterval = 0 if (! defined($versionsPerInterval) || @@ -642,21 +738,23 @@ $Problem->max_attempts() : -1; # finding the number of versions per time interval is a little harder. - # we interpret the time interval as a rolling interval: that is, - # if we allow two sets per day, that's two sets in any 24 hour + # we interpret the time interval as a rolling interval: that is, + # if we allow two sets per day, that's two sets in any 24 hour # period. this is probably not what we really want, but it's - # more extensible to a limitation like "one version per hour", - # and we can set it to two sets per 12 hours for most "2ce daily" + # more extensible to a limitation like "one version per hour", + # and we can set it to two sets per 12 hours for most "2ce daily" # type applications my $timeNow = time(); my $grace = $ce->{gatewayGracePeriod}; - my $currentNumVersions = 0; # this is the number of versions in the + my $currentNumVersions = 0; # this is the number of versions in the # time interval my $totalNumVersions = 0; - # we don't need to check this if $self->{invalidSet} is already set - if ( $setVersionNumber && ! $self->{invalidSet} ) { + # we don't need to check this if $self->{invalidSet} is already set, + # or if we're working with an Undefined_Set + if ( $setVersionNumber && ! $self->{invalidSet} && + $setName ne "Undefined_Set" ) { my @setVersionIDs = $db->listSetVersions($effectiveUserName, $setName); my @setVersions = $db->getSetVersions(map {[$effectiveUserName, $setName,, $_]} @setVersionIDs); foreach ( @setVersions ) { @@ -832,7 +930,7 @@ "record_set_version_answers_when_acting_as_student") ) ) { if ( between($set->open_date(), - $set->due_date() + $grace, + $set->due_date() + $grace, $timeNow) ) { $versionIsOpen = 1; } else { @@ -857,7 +955,7 @@ $self->{set} = $set; $self->{problem} = $Problem; $self->{requestedVersion} = $requestedVersion; - + $self->{userName} = $userName; $self->{effectiveUserName} = $effectiveUserName; $self->{user} = $User; @@ -869,7 +967,7 @@ $self->{versionIsOpen} = $versionIsOpen; $self->{timeNow} = $timeNow; - + #################################### # form processing #################################### @@ -900,7 +998,7 @@ my $previewAnswers = $r->param("previewAnswers"); my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars }; - + $self->{displayMode} = $displayMode; $self->{redisplay} = $redisplay; $self->{submitAnswers} = $submitAnswers; @@ -910,6 +1008,7 @@ # now that we've set all the necessary variables quit out if the set or # problem is invalid + return if $self->{invalidSet} || $self->{invalidProblem}; # [End lifted section] ############################################### @@ -917,9 +1016,10 @@ #################################### # permissions #################################### - + # bail without doing anything if the set isn't yet open for this user - return unless $self->{isOpen}; + return unless $self->{isOpen} || + $authz->hasPermissions($userName,"view_unopened_sets"); # what does the user want to do? my %want = @@ -991,9 +1091,14 @@ # set up problem numbering and multipage variables #################################### - my @problemNumbers = $db->listProblemVersions($effectiveUserName, - $setName, - $setVersionNumber); + my @problemNumbers; + if ( $setName eq "Undefined_Set" ) { + @problemNumbers = ( 1 ); + } else { + @problemNumbers = $db->listProblemVersions($effectiveUserName, + $setName, + $setVersionNumber); + } # to speed up processing of long (multi-page) tests, we want to only # translate those problems that are being submitted or are currently @@ -1059,7 +1164,13 @@ $self->{errors} = [ ]; # process the problems as needed - my @mergedProblems = $db->getAllMergedProblemVersions($effectiveUserName, $setName, $setVersionNumber); + my @mergedProblems; + if ( $setName eq "Undefined_Set" ) { + @mergedProblems = ( $Problem ); + } else { + @mergedProblems = $db->getAllMergedProblemVersions($effectiveUserName, $setName, $setVersionNumber); + } + foreach my $problemNumber (sort {$a<=>$b } @problemNumbers) { # pIndex numbers from zero @@ -1086,9 +1197,9 @@ # this is the actual translation of each problem. errors are # stored in @{$self->{errors}} in each case if ( (grep /^$pIndex$/, @probsToDisplay) || $submitAnswers ) { - $pg = $self->getProblemHTML($self->{effectiveUser}, - $setName,$setVersionNumber, - $formFields, $ProblemN); + $pg = $self->getProblemHTML($self->{effectiveUser}, + $set, $formFields, + $ProblemN); } push(@pg_results, $pg); } @@ -1209,7 +1320,7 @@ CGI::p($self->{invalidSet}), $newlink); } - + my $tmplSet = $self->{tmplSet}; my $set = $self->{set}; my $Problem = $self->{problem}; @@ -1268,6 +1379,7 @@ #################################### # save results to database as appropriate #################################### + if ( $submitAnswers || ( ($previewAnswers || $newPage) && $can{recordAnswers} ) ) { # if we're submitting answers, we have to save the problems @@ -1779,7 +1891,7 @@ } print CGI::end_div(); - if ( $canShowWork ) { + if ( $canShowWork && $set->set_id ne "Undefined_Set" ) { print "The test (which is number $versionNumber) may " . "no longer be submitted for a grade"; print "" . (($can{showScore}) ? ", but you may still " . @@ -2044,6 +2156,21 @@ CGI::b("all") . " problems, not just those " . "on this page.") : " ") ); + ## save the source file, etc., if we're trying an undefined + ## set + # print( CGI::hidden( + # -name => 'sourceFilePath', + # -value => $self->{problem}->{source_file} + # )) if defined($self->{problem}->{source_file}); + print( CGI::hidden( + -name => 'sourceFilePath', + -value => $r->param("sourceFilePath") + )) if defined($r->param("sourceFilePath")); + print( CGI::hidden( + -name => 'problemSeed', + -value => $r->param("problemSeed") + )) if defined($r->param("problemSeed")); + print CGI::endform(); } @@ -2101,13 +2228,12 @@ ############################################################################ sub getProblemHTML { - my ( $self, $EffectiveUser, $setName, $setVersionNumber, $formFields, + my ( $self, $EffectiveUser, $set, $formFields, $mergedProblem, $pgFile ) = @_; -# in: $EffectiveUser is the effective user we're working as, $setName -# the set name, $setVersionNumber the version number, %$formFields -# the form fields from the input form that we need to worry about -# putting into the HTML we're generating, and $mergedProblem and -# $pgFile are what we'd expect. +# in: $EffectiveUser is the effective user we're working as, $set is the +# merged set version, %$formFields the form fields from the input form +# that we need to worry about putting into the HTML we're generating, +# and $mergedProblem and $pgFile are what we'd expect. # $pgFile is optional # out: the translated problem is returned @@ -2115,21 +2241,9 @@ my $ce = $r->ce; my $db = $r->db; my $key = $r->param('key'); - -# this isn't good because it doesn't include the sticky answers that we -# might want. so off with its head! -## my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars }; - + my $setName = $set->set_id; + my $setVersionNumber = $set->version_id; my $permissionLevel = $self->{permissionLevel}; - my $set = $db->getMergedSetVersion( $EffectiveUser->user_id, - $setName, $setVersionNumber ); - -# should this ever happen? I think we should have die()ed way earlier than -# this if the set doesn't exist, but it can't hurt to try and die() here -# too - die "set $setName,v$setVersionNumber for effectiveUser " . - $EffectiveUser->user_id . " not found." unless $set; - my $psvn = $set->psvn(); if ( defined($mergedProblem) && $mergedProblem->problem_id ) { @@ -2138,8 +2252,8 @@ } elsif ($pgFile) { $mergedProblem = WeBWorK::DB::Record::ProblemVersion->new( - set_id => $set->set_id, - version_id => $set->version_id, + set_id => $setName, + version_id => $setVersionNumber, problem_id => 0, login_id => $EffectiveUser->user_id, source_file => $pgFile, @@ -2176,7 +2290,7 @@ ); # FIXME is problem_id the correct thing in the following two stanzas? -# FIXME the original version had "problem number", which is what we want. +# FIXME the original version had "problem number", which is what we want. # FIXME I think problem_id will work, too if ($pg->{warnings} ne "") { push @{$self->{warnings}}, { @@ -2185,7 +2299,7 @@ message => $pg->{warnings}, }; } - + if ($pg->{flags}->{error_flag}) { push @{$self->{errors}}, { set => "$setName,v$setVersionNumber", |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 14:55:35
|
Log Message: ----------- forward port from rel-2-4-patches changed wording on registration text Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.75 retrieving revision 1.76 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.75 -r1.76 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -2444,8 +2444,9 @@ !, CGI::p("If you are using your WeBWorK server for courses please help us out by registering your server."), CGI::p("We are often asked how many institutions are using WeBWorK and how many students are using - WeBWorK Since WeBWorK is open source and can be freely downloaded from http://www.openwebwork.org - and http://webwork.maa.org it is frequently difficult for us to give a reasonable answer to this + WeBWorK Since WeBWorK is open source and can be freely downloaded from ". + CGI::a({href=>'http://www.openwebwork.org'},'http://www.openwebwork.org' ). " and ". + CGI::a({href=> 'http://webwork.maa.org'},'http://www.openwebwork.org'). ", it is frequently difficult for us to give a reasonable answer to this question."), CGI::p("You can help by registering your current version of WeBWorK -- click the button, answer a few questions (the ones you can answer easily) and send the email. It takes less than two minutes. Thank you!. -- The WeBWorK Team"), @@ -2495,7 +2496,7 @@ 'click here'), q! to open your email application. There are a few questions, some of which have already been filled in for your installation. Fill in the other questions which you can answer easily and send - the email to gage\@math.rochester.edu + the email to ga...@ma... ! ); @@ -2503,8 +2504,8 @@ print "\n",CGI::p({style=>"text-align: left; width:60%"},q!Once you have emailed your registration information you can hide the "registration" banner for successive visits by clicking - the button below.!) - ; + the button below. It writes an empty file (!.CGI::code('registered_versionNumber').q!) to the directory !.CGI::code('..../courses/admin') + ); print "</center>"; print CGI::start_form(-method=>"POST", -action=>$self->r->uri); |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 14:53:31
|
Log Message: ----------- Changed wording of the registration text Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.64.2.2.2.1 retrieving revision 1.64.2.2.2.2 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.64.2.2.2.1 -r1.64.2.2.2.2 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -2444,8 +2444,9 @@ !, CGI::p("If you are using your WeBWorK server for courses please help us out by registering your server."), CGI::p("We are often asked how many institutions are using WeBWorK and how many students are using - WeBWorK Since WeBWorK is open source and can be freely downloaded from http://www.openwebwork.org - and http://webwork.maa.org it is frequently difficult for us to give a reasonable answer to this + WeBWorK Since WeBWorK is open source and can be freely downloaded from ". + CGI::a({href=>'http://www.openwebwork.org'},'http://www.openwebwork.org' ). " and ". + CGI::a({href=> 'http://webwork.maa.org'},'http://www.openwebwork.org'). ", it is frequently difficult for us to give a reasonable answer to this question."), CGI::p("You can help by registering your current version of WeBWorK -- click the button, answer a few questions (the ones you can answer easily) and send the email. It takes less than two minutes. Thank you!. -- The WeBWorK Team"), @@ -2495,7 +2496,7 @@ 'click here'), q! to open your email application. There are a few questions, some of which have already been filled in for your installation. Fill in the other questions which you can answer easily and send - the email to gage\@math.rochester.edu + the email to ga...@ma... ! ); @@ -2503,8 +2504,8 @@ print "\n",CGI::p({style=>"text-align: left; width:60%"},q!Once you have emailed your registration information you can hide the "registration" banner for successive visits by clicking - the button below.!) - ; + the button below. It writes an empty file (!.CGI::code('registered_versionNumber').q!) to the directory !.CGI::code('..../courses/admin') + ); print "</center>"; print CGI::start_form(-method=>"POST", -action=>$self->r->uri); |
From: Arnie P. v. a. <we...@ma...> - 2008-06-25 13:37:05
|
Log Message: ----------- Backport to patches distribution Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/courses.dist/modelCourse/templates: demoCourse.lst Revision Data ------------- Index: demoCourse.lst =================================================================== RCS file: /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/demoCourse.lst,v retrieving revision 1.1.4.1 retrieving revision 1.1.4.1.2.1 diff -Lcourses.dist/modelCourse/templates/demoCourse.lst -Lcourses.dist/modelCourse/templates/demoCourse.lst -u -r1.1.4.1 -r1.1.4.1.2.1 --- courses.dist/modelCourse/templates/demoCourse.lst +++ courses.dist/modelCourse/templates/demoCourse.lst @@ -1,9 +1,9 @@ -000-00-000a ,PRACTICE1 ,JANE ,C , , , , ,practice1 -000-00-000b ,PRACTICE2 ,JANE ,C , , , , ,practice2 -000-00-000c ,PRACTICE3 ,JANE ,C , , , , ,practice3 -000-00-000d ,PRACTICE4 ,JANE ,C , , , , ,practice4 -000-00-000e ,PRACTICE5 ,JANE ,C , , , , ,practice5 -000-00-000f ,PRACTICE6 ,JANE ,C , , , , ,practice6 -000-00-000g ,PRACTICE7 ,JANE ,C , , , , ,practice7 -000-00-000h ,PRACTICE8 ,JANE ,C , , , , ,practice8 -000-00-000i ,PRACTICE9 ,JANE ,C , , , , ,practice9 +practice1 ,PRACTICE1 ,JANE ,C , , , , ,practice1, ,-5 +practice2 ,PRACTICE2 ,JANE ,C , , , , ,practice2, ,-5 +practice3 ,PRACTICE3 ,JANE ,C , , , , ,practice3, ,-5 +practice4 ,PRACTICE4 ,JANE ,C , , , , ,practice4, ,-5 +practice5 ,PRACTICE5 ,JANE ,C , , , , ,practice5, ,-5 +practice6 ,PRACTICE6 ,JANE ,C , , , , ,practice6, ,-5 +practice7 ,PRACTICE7 ,JANE ,C , , , , ,practice7, ,-5 +practice8 ,PRACTICE8 ,JANE ,C , , , , ,practice8, ,-5 +practice9 ,PRACTICE9 ,JANE ,C , , , , ,practice9, ,-5 |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 12:36:15
|
Log Message: ----------- Adding mathobjects examples to setMAAtutorial Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/courses.dist/modelCourse/templates/setMAAtutorial: hello_mo.pg standardexample_mo.pg Revision Data ------------- --- /dev/null +++ courses.dist/modelCourse/templates/setMAAtutorial/standardexample_mo.pg @@ -0,0 +1,101 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", # calls in some extra macros -- in this case Title() used for formatting only. + "source.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +Context("Numeric"); + +TEXT(beginproblem()); + +Title("Standard Example"); +#################################################### +# +# Set up section +# + +Context()->strings-> add(hello=>{}, goodbye=>{}); + +###################### +#Setup a question requiring a word as an answer +###################### + +$str = 'world'; #alternative strings for use in the question +#$str = "Dolly"; + +$hello = String("Hello"); # correct answer + +###################### +# Setup a question requiring a numberical answer +###################### + +$a = Real(3); +$b = Real(5); +#$a=Real(random(1,9,1)); #uncomment these lines and comment the previous ones +#$b=Real(random(2,9,1)) ; #to generate a "random" or algorithmic version of the problem + +$sum = $a + $b; # sum is automatically a Real object + +###################### +# Setup a question requiring a function or formula as an answer -- here is where +# the power of MathObjects starts to shine +###################### + +$f = Formula("x^$b"); +$fp = $f->D; # the derivative of $f. + +#################################################### +# +# Text section +# + +Context()->texStrings; +BEGIN_TEXT +Complete the sentence: $BR +\{ $hello->ans_rule \} $str; +$PAR + +Enter the sum of these two numbers: $BR + \($a + $b = \) \{$sum->ans_rule(10) \} +$PAR + +Enter the derivative of \[ $f \] $BR +\(f '(x) = \) \{ $f->ans_rule(40) \} +$PAR +END_TEXT +Context()->normalStrings; + +#################################################### +# +# Answer section +# + +ANS($hello->cmp); +ANS($sum->cmp); +ANS($fp->cmp); + + + + +#################################################### +# +# Hint section +# Hints should be used cautiously +# + + +#################################################### +# +# Solution section +#Solutions are not required but they are appreciated. +# + + +ENDDOCUMENT(); + \ No newline at end of file --- /dev/null +++ courses.dist/modelCourse/templates/setMAAtutorial/hello_mo.pg @@ -0,0 +1,69 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", +# "source.pl", +# "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +Context()->strings->add(hello =>{},goodbye=>{}); # allows only these strings to be entered without + # alerting student to a mistake +$ans = String("Hello"); # generates the correct answer + +TEXT(beginproblem()); + +#################################################### +# +# Set up section +# + + +#################################################### +# +# Text section +# + +Context()->texStrings; +BEGIN_TEXT +Complete the sentence: $PAR +\{$ans->ans_rule() \} world! + +END_TEXT +Context()->normalStrings; + +#################################################### +# +# Answer section +# + +ANS($ans->cmp); + +#################################################### +# +# Solution section +# +#################################################### +# +# Hint section +# + +HINT(EV3(<<'END_HINT')); +$HINT +Try Hello or goodbye. +END_HINT + +SOLUTION(EV3(<<'END_SOLUTION')); +$SOL +Good-bye world is too pessimistic! :-) +$PAR + Solutions are not required but they are +appreciated. + +END_SOLUTION + + +ENDDOCUMENT(); + \ No newline at end of file |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 12:12:52
|
Log Message: ----------- Adding the contents of modelCourse templates to rel-2-4-5 Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/courses.dist/modelCourse/templates: course_info.txt set0.def Added Files: ----------- webwork2/courses.dist/modelCourse/templates: setHeader.pg setOrientation.def setOrientation.pl webwork2/courses.dist/modelCourse/templates/setDemo: nsc2s10p2.pg.bak prob6b.html srw1_9_4.pg.bak srw1_9_4.pg.gage.tmp Revision Data ------------- --- /dev/null +++ courses.dist/modelCourse/templates/setOrientation.pl @@ -0,0 +1,21 @@ +setNumber=Orientation +openDate = 6/26/04 at 11:30am +dueDate = 4/4/05 at 12:20pm +answerDate = 4/5/05 at 12:00pm +screenHeaderFile = unionLibrary/parserOrientation/setHeader.pg +problemList = +unionLibrary/parserOrientation/prob01.pg, 1 +unionLibrary/parserOrientation/prob02.pg, 1 +unionLibrary/parserOrientation/prob03.pg, 1 +unionLibrary/parserOrientation/prob04.pg, 1 +unionLibrary/parserOrientation/prob05/prob05.pg, 1 +unionLibrary/parserOrientation/prob06.pg, 1 +unionLibrary/parserOrientation/prob07.pg, 1 +unionLibrary/parserOrientation/prob08.pg, 1 +unionLibrary/parserOrientation/prob09.pg, 1 +unionLibrary/parserOrientation/prob10.pg, 1 +unionLibrary/parserOrientation/prob11.pg, 1 +unionLibrary/parserOrientation/prob12.pg, 1 +unionLibrary/parserOrientation/prob13.pg, 1 +unionLibrary/parserOrientation/prob14/prob14.pg, 1 +unionLibrary/parserOrientation/prob15.pg, 1 Index: course_info.txt =================================================================== RCS file: /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/course_info.txt,v retrieving revision 1.1 retrieving revision 1.1.6.1 diff -Lcourses.dist/modelCourse/templates/course_info.txt -Lcourses.dist/modelCourse/templates/course_info.txt -u -r1.1 -r1.1.6.1 --- courses.dist/modelCourse/templates/course_info.txt +++ courses.dist/modelCourse/templates/course_info.txt @@ -1 +1,3 @@ +this information is written in the file: +[coursesDirectory]/courseName/templates/course_info.txt --- /dev/null +++ courses.dist/modelCourse/templates/setOrientation.def @@ -0,0 +1,21 @@ +setNumber=Orientation +openDate = 6/26/04 at 11:30am +dueDate = 4/4/05 at 12:20pm +answerDate = 4/5/05 at 12:00pm +screenHeaderFile = unionLibrary/parserOrientation/setHeader.pg +problemList = +unionLibrary/parserOrientation/prob01.pg, 1 +unionLibrary/parserOrientation/prob02.pg, 1 +unionLibrary/parserOrientation/prob03.pg, 1 +unionLibrary/parserOrientation/prob04.pg, 1 +unionLibrary/parserOrientation/prob05/prob05.pg, 1 +unionLibrary/parserOrientation/prob06.pg, 1 +unionLibrary/parserOrientation/prob07.pg, 1 +unionLibrary/parserOrientation/prob08.pg, 1 +unionLibrary/parserOrientation/prob09.pg, 1 +unionLibrary/parserOrientation/prob10.pg, 1 +unionLibrary/parserOrientation/prob11.pg, 1 +unionLibrary/parserOrientation/prob12.pg, 1 +unionLibrary/parserOrientation/prob13.pg, 1 +unionLibrary/parserOrientation/prob14/prob14.pg, 1 +unionLibrary/parserOrientation/prob15.pg, 1 Index: set0.def =================================================================== RCS file: /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/set0.def,v retrieving revision 1.1 retrieving revision 1.1.6.1 diff -Lcourses.dist/modelCourse/templates/set0.def -Lcourses.dist/modelCourse/templates/set0.def -u -r1.1 -r1.1.6.1 --- courses.dist/modelCourse/templates/set0.def +++ courses.dist/modelCourse/templates/set0.def @@ -4,10 +4,10 @@ answerDate = 1/21/09 at 6:00am screenHeaderFile = setHeader.pg problemList = -set0/prob1.pg, 1 -set0/prob1a.pg, 1 -set0/prob1b.pg, 1 -set0/prob2.pg, 1 -set0/prob3.pg, 1 -set0/prob4/prob4.pg, 1 -set0/prob5.pg, 1 +rochesterLibrary/set0/prob1.pg, 1 +rochesterLibrary/set0/prob1a.pg, 1 +rochesterLibrary/set0/prob1b.pg, 1 +rochesterLibrary/set0/prob2.pg, 1 +rochesterLibrary/set0/prob3.pg, 1 +rochesterLibrary/set0/prob4/prob4.pg, 1 +rochesterLibrary/set0/prob5.pg, 1 --- /dev/null +++ courses.dist/modelCourse/templates/setHeader.pg @@ -0,0 +1,88 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/courses.dist/modelCourse/templates/setHeader.pg,v 1.1.2.1 2008/06/25 11:55:31 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +DOCUMENT(); + +loadMacros( + "PG.pl", + "PGbasicmacros.pl", + +); + +TEXT($BEGIN_ONE_COLUMN); + +TEXT(MODES(TeX =>EV3(<<'EOT'),HTML=>"",Latex2HTML=>"")); +\noindent {\large \bf $studentName} +\hfill +\noindent {\large \bf MTH 161 $sectionNumber Fall 2003} +\par + +EOT + +BEGIN_TEXT + +$BBOLD WeBWorK assignment number $setNumber is due : $formattedDueDate. $EBOLD + +$PAR + +$PAR +The +(* home page *) +\{ +#htmlLink(qq!http://www.math.rochester.edu/courses/161/home/!,"home page") +\} +for the course contains the syllabus, grading policy and other information. +$PAR +END_TEXT + +################## +# EDIT BELOW HERE +################## +BEGIN_TEXT +$HR +$PAR +This file is /conf/snippets/setHeader.pg you can use it as +a model for creating files which introduce each problem set. +$PAR +$HR +END_TEXT +################## +# EDIT ABOVE HERE +################## +BEGIN_TEXT +The primary purpose of WeBWorK is to let you know that you are getting the correct answer or to alert +you if you are making some kind of mistake. Usually you can attempt a problem as many times as you want before +the due date. However, if you are having trouble figuring out your error, you should +consult the book, or ask a fellow student, one of the TA's or +your professor for help. Don't spend a lot of time guessing -- it's not very efficient or effective. +$PAR +Give 4 or 5 significant digits for (floating point) numerical answers. +For most problems when entering numerical answers, you can if you wish +enter elementary expressions such as \( 2\wedge3 \) instead of 8, \( sin(3*pi/2) \)instead +of -1, \( e\wedge (ln(2)) \) instead of 2, +\( (2+tan(3))*(4-sin(5))\wedge6-7/8 \) instead of 27620.3413, etc. + Here's the +\{ htmlLink(qq!http://webwork.math.rochester.edu/docs/docs/pglanguage/availableFunctions.html!,"list of the functions") \} + which WeBWorK understands. +$PAR +You can use the Feedback button on each problem +page to send e-mail to the professors. + + +$END_ONE_COLUMN +END_TEXT + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file --- /dev/null +++ courses.dist/modelCourse/templates/setDemo/srw1_9_4.pg.bak @@ -0,0 +1,59 @@ +##DESCRIPTION +## find distance between two points, find coordinates of the midpoint of +## a line segment connecting them +##ENDDESCRIPTION + +##KEYWORDS('algebra', 'coordinate geometry', 'distance', 'midpoint') + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl" +); + +TEXT(&beginproblem); + +$showPartialCorrectAnswers = 1; + +#install_problem_grader(~~&std_problem_grader); ##uncomment to use std grader +#install_problem_grader(~~&custom_problem_grader); ##uncomment to use custom grader +$x1 = random(1,5,1); +$y1 = random(-5,-1,1); +$x2 = random(-10,-3,1); +$y2 = random(-9,-2,1); +$len1 = sqrt(($x1-$x2)**2 + ($y1-$y2)**2); +$midx = ($x1+$x2)/2; +$midy = ($y1+$y2)/2; + +BEGIN_TEXT +Consider the two points \( ($x1 ,$y1 )\) and \( ($x2 ,$y2 )\). +The distance between them is:$BR +\{ans_rule(30) \} +$BR +END_TEXT + +$ans = $len1; +&ANS(num_cmp($ans)); + +BEGIN_TEXT +The x co-ordinate of the midpoint of the line +segment that joins them is:\{ans_rule(20) \} +$BR +END_TEXT +$ans = $midx; +&ANS(num_cmp($ans)); + +BEGIN_TEXT +The y co-ordinate of the midpoint of the line segment that joins them is: +\{ans_rule(20) \} +$BR +END_TEXT +$ans = $midy; +&ANS(num_cmp($ans)); + +ENDDOCUMENT(); # This should be the last executable line in the problem. + + --- /dev/null +++ courses.dist/modelCourse/templates/setDemo/prob6b.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> + <TITLE>Source for problem 6</TITLE> + <META NAME="generator" CONTENT="BBEdit 5.0"> +</HEAD> +<BODY BGCOLOR="#FFFFFF"> +<PRE> +DOCUMENT(); + +loadMacros("PG.pl", + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + ); + + + + + +# allow the student to change the seed for this problem. +$newProblemSeed = ( defined( ${$inputs_ref}{'newProblemSeed'} ) )? ${$inputs_ref}{'newProblemSeed'} : $problemSeed; +$PG_random_generator->srand($newProblemSeed); + +$p = random(2,9,1); # multiplier +$p2 = ( $p % 2 == 0) ? 2*$p : $p; + +TEXT(beginproblem()); + +# The link to the java applet is hard wired to use the java applet +# served from the University of Rochester WeBWorK machine. +# It is possible to set this up so that the java applet is served +# from any machine +# For details use the Feedback button to contact the authors of WeBWorK + +BEGIN_TEXT +This problem requires a browser capable of running Java. +$PAR +To see a different version of the problem change +the problem seed and press the 'Submit Answer' button below.$PAR Problem Seed: +\{ M3( +qq! Change the problem seed to change the problem:$problemSeed!, +qq! Change the problem seed to change the problem: + \begin{rawhtml} + <INPUT NAME="newProblemSeed" VALUE = "$newProblemSeed" SIZE = "10"> + \end{rawhtml}!, +qq! <INPUT NAME="newProblemSeed" VALUE = "$newProblemSeed" SIZE = "10">! +) +\} +$PAR +This problem illustrates how you can Java applets in a WeBWorK example. +$PAR +This polar coordinate grapher was designed at constructed at Mathematics Department +at The Johns Hopkins University +$PAR +WeBWorK can use existing $BBOLD JavaScript$EBOLD and $BBOLD Java $EBOLD code to augment its capabilities. +$HR +END_TEXT +TEXT(TEX("The Johns Hopkins University Mathematics Department's polar graph plotting applet goes here", +qq{ +<APPLET CODE="PolarApplet/PolarApplet.class" WIDTH="250" HEIGHT="350" +CODEBASE="http://xena.mat.jhu.edu/vander/stable/"> +<PARAM NAME="tmin" VALUE="0"> +<PARAM NAME="tmax" VALUE="2*pi"> +<PARAM NAME="showcartesian" VALUE="no"> +<PARAM NAME="showinterval" VALUE="YES"> +</APPLET> +}, + +)); + +BEGIN_TEXT +$PAR +For what value of \( k \) does the graph of \( r = \cos(kt) \) look like a rose with $p2 petals? +$BR +\(k = \) \{ ans_rule(20) \} ; + +$PAR +You can view the \{ htmlLink(alias("prob6b.html"),"source", q!TARGET="source"!)\} for this problem. +or consult the \{ htmlLink("${webworkDocsURL}techdescription/pglanguage/index.html","documentation") \} for more details on the PG language. + +END_TEXT + +ANS(num_cmp($p) ); + +ENDDOCUMENT(); + +</PRE> +</BODY> +</HTML> --- /dev/null +++ courses.dist/modelCourse/templates/setDemo/srw1_9_4.pg.gage.tmp @@ -0,0 +1,111 @@ +##DESCRIPTION +## find distance between two points, find coordinates of the midpoint of +## a line segment connecting them +##ENDDESCRIPTION + +##KEYWORDS('algebra', 'coordinate geometry', 'distance', 'midpoint') + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl", +"PGauxiliaryFunctions.pl" +); + +TEXT(&beginproblem); + +$showPartialCorrectAnswers = 1; + +#install_problem_grader(~~&std_problem_grader); ##uncomment to use std grader +#install_problem_grader(~~&custom_problem_grader); ##uncomment to use custom grader + + +$x1 = random(1,5,1); +$y1 = random(-5,-1,1); +$x2 = random(-10,-3,1); +$y2 = random(-9,-2,1); +$len1 = sqrt(($x1-$x2)**2 + ($y1-$y2)**2); +$midx = ($x1+$x2)/2; +$midy = ($y1+$y2)/2; + +BEGIN_TEXT +Consider the two points \( ($x1 ,$y1 )\) and \( ($x2 ,$y2 )\). +The distance between them is:$BR +\{ans_rule(30) \} +$BR +END_TEXT + +$ans = $len1; +&ANS(std_num_cmp($ans)); + +BEGIN_TEXT +The x co-ordinate of the midpoint of the line +segment that joins them is:\{ans_rule(20) \} +$BR +END_TEXT +$ans = $midx; +&ANS(std_num_cmp($ans)); + +BEGIN_TEXT +The y co-ordinate of the midpoint of the line segment that joins them is: +\{ans_rule(20) \} +$BR +END_TEXT +$ans = $midy; +&ANS(std_num_cmp($ans)); + + +sub custom_problem_grader { + my $rh_evaluated_answers = shift; + my $rh_problem_state = shift; + my %form_options = @_; + my %evaluated_answers = %{$rh_evaluated_answers}; + # The hash $rh_evaluated_answers typically contains: + # 'answer1' => 34, 'answer2'=> 'Mozart', etc. + + # By default the old problem state is simply passed back out again. + my %problem_state = %$rh_problem_state; + + + # %form_options might include + # The user login name + # The permission level of the user + # The studentLogin name for this psvn. + # Whether the form is asking for a refresh or is submitting a new answer. + + # initial setup of the answer + my $total=0; + my %problem_result = ( score => 0, + errors => '', + type => 'custom_problem_grader', + msg => 'Part 1 is worth 50% and parts 2 and 3 are worth 25% each.', + ); + + # Return unless answers have been submitted + unless ($form_options{answers_submitted} == 1) { + return(~~%problem_result,~~%problem_state); + } + # Answers have been submitted -- process them. + + $total += .5*($evaluated_answers{'AnSwEr1'}->{score}); + $total += .25*($evaluated_answers{'AnSwEr2'}->{score}); + $total += .25*($evaluated_answers{'AnSwEr3'}->{score}); + + + $problem_result{score} = $total; + # increase recorded score if the current score is greater. + $problem_state{recorded_score} = $problem_result{score} if $problem_result{score} > $problem_state{recorded_score}; + + + $problem_state{num_of_correct_ans}++ if $total == 1; + $problem_state{num_of_incorrect_ans}++ if $total < 1 ; + (~~%problem_result, ~~%problem_state); + +} + + +ENDDOCUMENT(); # This should be the last executable line in the problem. + --- /dev/null +++ courses.dist/modelCourse/templates/setDemo/nsc2s10p2.pg.bak @@ -0,0 +1,143 @@ +################################################################## +##########Date:: 9-1-101, 19:17:23################ + + + +#DESCRIPTION +# Identify the graphs of the function and the derivative +#ENDDESCRIPTION + +#KEYWORDS('derivatives', 'graphs') +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl", + "PGgraphmacros.pl" +); + +TEXT(beginproblem()); +$showPartialCorrectAnswers = 0; + +$a=random(0, 6.3, .1); +$b=random(1.1, 1.5, .1); + +$dom = 4; +@slice = NchooseK(3,3); + +@colors = ("blue", "red", "green"); +@sc = @colors[@slice]; #scrambled colors +@sa = ('A','B','C')[@slice]; + + +$f = "sin($a+$b*cos(x)) for x in <-$dom,$dom> using color:$sc[0] and weight:2"; +$fp = "cos($a+$b*cos(x))*(-$b)*sin(x) for x in <-$dom,$dom> using color=$sc[1] and weight:2"; +$fpp = " -sin($a+$b*cos(x))*$b*$b*sin(x)*sin(x) + cos($a+$b*cos(x))*(-$b)*cos(x) for x in <-$dom,$dom> using color=$sc[2] and weight=2"; + +$graph = init_graph(-4,-4,4,4,'axes'=>[0,0],'grid'=>[8,8]); + +($fRef,$fpRef,$fppRef) = plot_functions( $graph, + $f,$fp,$fpp + ); + +# create labels + +$label_point=-0.75; +$label_f = new Label ( $label_point,&{$fRef->rule}($label_point),$sa[0],"$sc[0]",'left') ; + # NOTE: $fRef->rule is a reference to the subroutine which calculates the + # function. It was defined in the output of plot_functions. It is used here + # to calculate the y value of the label corresponding to the function, + # and below to find the y values for the labels corresponding to the + # first and second derivatives. + +$label_fp = new Label ( $label_point,&{$fpRef->rule}($label_point),$sa[1],"$sc[1]",'left') ; +$label_fpp = new Label ( $label_point,&{$fppRef->rule}($label_point),$sa[2],"$sc[2]",'left'); + +# insert the labels into the graph + +$graph->lb($label_f,$label_fp,$label_fpp); + +BEGIN_TEXT +\{ image(insertGraph($graph))\}$BR +Identify the graphs A (blue), B( red) and C (green) as the graphs of a function and its +derivatives:$PAR +\{ans_rule(4)\} is the graph of the function $PAR +\{ans_rule(4)\} is the graph of the function's first derivative $PAR +\{ans_rule(4)\} is the graph of the function's second derivative $PAR +END_TEXT +ANS(std_str_cmp_list( @sa ) ); + +ENDDOCUMENT(); # This should be the last executable line in the problem. +################################################################## +##########Date:: 9-1-101, 19:18:0################ + + +#DESCRIPTION +# Identify the graphs of the function and the derivative +#ENDDESCRIPTION + +#KEYWORDS('derivatives', 'graphs') +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl", + "PGgraphmacros.pl" +); + +TEXT(beginproblem()); +$showPartialCorrectAnswers = 0; + +$a=random(0, 6.3, .1); +$b=random(1.1, 1.5, .1); + +$dom = 4; +@slice = NchooseK(3,3); + +@colors = ("blue", "red", "orange"); +@sc = @colors[@slice]; #scrambled colors +@sa = ('A','B','C')[@slice]; + + +$f = "sin($a+$b*cos(x)) for x in <-$dom,$dom> using color:$sc[0] and weight:2"; +$fp = "cos($a+$b*cos(x))*(-$b)*sin(x) for x in <-$dom,$dom> using color=$sc[1] and weight:2"; +$fpp = " -sin($a+$b*cos(x))*$b*$b*sin(x)*sin(x) + cos($a+$b*cos(x))*(-$b)*cos(x) for x in <-$dom,$dom> using color=$sc[2] and weight=2"; + +$graph = init_graph(-4,-4,4,4,'axes'=>[0,0],'grid'=>[8,8]); + +($fRef,$fpRef,$fppRef) = plot_functions( $graph, + $f,$fp,$fpp + ); + +# create labels + +$label_point=-0.75; +$label_f = new Label ( $label_point,&{$fRef->rule}($label_point),$sa[0],"$sc[0]",'left') ; + # NOTE: $fRef->rule is a reference to the subroutine which calculates the + # function. It was defined in the output of plot_functions. It is used here + # to calculate the y value of the label corresponding to the function, + # and below to find the y values for the labels corresponding to the + # first and second derivatives. + +$label_fp = new Label ( $label_point,&{$fpRef->rule}($label_point),$sa[1],"$sc[1]",'left') ; +$label_fpp = new Label ( $label_point,&{$fppRef->rule}($label_point),$sa[2],"$sc[2]",'left'); + +# insert the labels into the graph + +$graph->lb($label_f,$label_fp,$label_fpp); + +BEGIN_TEXT +\{ image(insertGraph($graph))\}$BR +Identify the graphs A (blue), B( red) and C (green) as the graphs of a function and its +derivatives:$PAR +\{ans_rule(4)\} is the graph of the function $PAR +\{ans_rule(4)\} is the graph of the function's first derivative $PAR +\{ans_rule(4)\} is the graph of the function's second derivative $PAR +END_TEXT +ANS(std_str_cmp_list( @sa ) ); + +ENDDOCUMENT(); # This should be the last executable line in the problem. |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 12:08:28
|
Update of /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/email In directory devel.webwork.rochester.edu:/tmp/cvs-serv46834/email Log Message: Directory /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/email added to the repository --> Using per-directory sticky tag `rel-2-4-patches' |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 12:08:28
|
Update of /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/macros In directory devel.webwork.rochester.edu:/tmp/cvs-serv46834/macros Log Message: Directory /webwork/cvs/system/webwork2/courses.dist/modelCourse/templates/macros added to the repository --> Using per-directory sticky tag `rel-2-4-patches' |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 04:06:03
|
Log Message: ----------- forward port of changes to rel-2-4-5 Modified Files: -------------- webwork2/conf: global.conf.dist webwork.apache-config.dist webwork2/conf/templates/union: system.template webwork2/conf/templates/ur: system.template Revision Data ------------- Index: webwork.apache-config.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/webwork.apache-config.dist,v retrieving revision 1.23 retrieving revision 1.24 diff -Lconf/webwork.apache-config.dist -Lconf/webwork.apache-config.dist -u -r1.23 -r1.24 --- conf/webwork.apache-config.dist +++ conf/webwork.apache-config.dist @@ -109,7 +109,8 @@ $ENV{WEBWORK_ROOT} = $webwork_dir; -#$PerlConfig .= <<EOF; +$PerlConfig .= <<EOF; + # ##### Sam's WeBWorK::Request-based XML-RPC testbed ##### # # # #PerlModule WeBWorK::RPC @@ -182,7 +183,8 @@ # Order Allow,Deny # Allow from All # </Location> -#EOF + +EOF </Perl> Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.206 retrieving revision 1.207 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.206 -r1.207 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -42,7 +42,7 @@ # URL and path to courses directory. $webwork_courses_url = "/webwork2_course_files"; -#$webwork_courses_dir = "/opt/webwork/courses"; +$webwork_courses_dir = "/opt/webwork/courses"; #(a typical place to put the course directory ################################################################################ # Paths to external programs @@ -242,7 +242,11 @@ $webworkURLs{bugReporter} = "http://bugs.webwork.rochester.edu/"; # Location of CSS -$webworkURLs{stylesheet} = "$webworkURLs{htdocs}/css/${defaultTheme}.css"; +# $webworkURLs{stylesheet} = "$webworkURLs{htdocs}/css/${defaultTheme}.css"; +# this is never used -- changing the theme from the config panel +# doesn't appear to reset the theme in time? +# It's better to refer directly to the .css file in the system.template +# <link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/math.css"/> # Location of jsMath script, used for the jsMath display mode. $webworkURLs{jsMath} = "$webworkURLs{htdocs}/jsMath/jsMath-ww.js"; @@ -909,7 +913,8 @@ [qw(Parser Value)], [qw(Parser::Legacy)], # [qw(SaveFile)], -# [qw(Applet FlashApplet)], + [qw(Chromatic)], + [qw(Applet FlashApplet)], ]; ##### Answer evaluatior defaults Index: system.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/union/system.template,v retrieving revision 1.1 retrieving revision 1.2 diff -Lconf/templates/union/system.template -Lconf/templates/union/system.template -u -r1.1 -r1.2 --- conf/templates/union/system.template +++ conf/templates/union/system.template @@ -24,7 +24,7 @@ <head> <title><!--#path style="text" text=" : " textonly="1"--></title> <!--#head--> -<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> +<link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/union.css"/> </head> <body> <table width="100%" cellpadding="10" cellspacing="0" border="0"> Index: system.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/ur/system.template,v retrieving revision 1.4 retrieving revision 1.5 diff -Lconf/templates/ur/system.template -Lconf/templates/ur/system.template -u -r1.4 -r1.5 --- conf/templates/ur/system.template +++ conf/templates/ur/system.template @@ -25,7 +25,7 @@ <head> <title><!--#path style="text" text=" : " textonly="1"--></title> <!--#head--> -<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> +<link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/ur.css"/> </head> <body> <table width="100%" cellpadding="10" cellspacing="0" border="0"> |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 04:05:17
|
Log Message: ----------- added Chromatic module (commented out) Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.189.2.7.2.3 retrieving revision 1.189.2.7.2.4 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.189.2.7.2.3 -r1.189.2.7.2.4 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -913,6 +913,7 @@ [qw(Parser Value)], [qw(Parser::Legacy)], # [qw(SaveFile)], +# [qw(Chromatic)], [qw(Applet FlashApplet)], ]; |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 03:53:24
|
Log Message: ----------- Changed the way the stylesheet is referred to from system.template Link directly to the location for the corresponding .css file Commented out the webworkURLs->{stylesheet}= ... $defaultTheme.css value in global.conf since the theme can be overridden in simple.conf by the Config page but by then it's too late to change the stylesheet page. Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/conf: global.conf.dist webwork2/conf/templates/union: system.template webwork2/conf/templates/ur: system.template Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.189.2.7.2.2 retrieving revision 1.189.2.7.2.3 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.189.2.7.2.2 -r1.189.2.7.2.3 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -242,7 +242,11 @@ $webworkURLs{bugReporter} = "http://bugs.webwork.rochester.edu/"; # Location of CSS -$webworkURLs{stylesheet} = "$webworkURLs{htdocs}/css/${defaultTheme}.css"; +# $webworkURLs{stylesheet} = "$webworkURLs{htdocs}/css/${defaultTheme}.css"; +# this is never used -- changing the theme from the config panel +# doesn't appear to reset the theme in time? +# It's better to refer directly to the .css file in the system.template +# <link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/math.css"/> # Location of jsMath script, used for the jsMath display mode. $webworkURLs{jsMath} = "$webworkURLs{htdocs}/jsMath/jsMath-ww.js"; Index: system.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/union/system.template,v retrieving revision 1.1.2.1 retrieving revision 1.1.2.2 diff -Lconf/templates/union/system.template -Lconf/templates/union/system.template -u -r1.1.2.1 -r1.1.2.2 --- conf/templates/union/system.template +++ conf/templates/union/system.template @@ -24,7 +24,7 @@ <head> <title><!--#path style="text" text=" : " textonly="1"--></title> <!--#head--> -<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> +<link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/union.css"/> </head> <body> <table width="100%" cellpadding="10" cellspacing="0" border="0"> Index: system.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/ur/system.template,v retrieving revision 1.3.4.1 retrieving revision 1.3.4.1.2.1 diff -Lconf/templates/ur/system.template -Lconf/templates/ur/system.template -u -r1.3.4.1 -r1.3.4.1.2.1 --- conf/templates/ur/system.template +++ conf/templates/ur/system.template @@ -25,7 +25,7 @@ <head> <title><!--#path style="text" text=" : " textonly="1"--></title> <!--#head--> -<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> +<link rel="stylesheet" type="text/css" href="<!--#url type="webwork" name="htdocs"-->/css/ur.css"/> </head> <body> <table width="100%" cellpadding="10" cellspacing="0" border="0"> |
From: Mike G. v. a. <we...@ma...> - 2008-06-25 00:01:41
|
Log Message: ----------- changed formatting to prevent errors when some of the stanzas are uncommented. Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/conf: webwork.apache-config.dist Revision Data ------------- Index: webwork.apache-config.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/webwork.apache-config.dist,v retrieving revision 1.16.2.4.2.2 retrieving revision 1.16.2.4.2.3 diff -Lconf/webwork.apache-config.dist -Lconf/webwork.apache-config.dist -u -r1.16.2.4.2.2 -r1.16.2.4.2.3 --- conf/webwork.apache-config.dist +++ conf/webwork.apache-config.dist @@ -109,7 +109,8 @@ $ENV{WEBWORK_ROOT} = $webwork_dir; -#$PerlConfig .= <<EOF; +$PerlConfig .= <<EOF; + # ##### Sam's WeBWorK::Request-based XML-RPC testbed ##### # # # #PerlModule WeBWorK::RPC @@ -182,7 +183,8 @@ # Order Allow,Deny # Allow from All # </Location> -#EOF + +EOF </Perl> |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:26:22
|
Log Message: ----------- Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2: README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.17.4.3.2.2 retrieving revision 1.17.4.3.2.3 diff -LREADME -LREADME -u -r1.17.4.3.2.2 -r1.17.4.3.2.3 --- README +++ README @@ -2,6 +2,7 @@ Online Homework Delivery System Version 2.4.5 Branch: rel-2-4-patches + http://webwork.maa.org/wiki/Release_notes_for_WeBWorK_2.4.5 Copyright 2000-2007, The WeBWorK Project All rights reserved. |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:25:09
|
Log Message: ----------- update version in README file Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2: README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.17.4.3.2.1 retrieving revision 1.17.4.3.2.2 diff -LREADME -LREADME -u -r1.17.4.3.2.1 -r1.17.4.3.2.2 --- README +++ README @@ -1,6 +1,7 @@ WeBWorK Online Homework Delivery System - Version 2.4.5 + Version 2.4.5 + Branch: rel-2-4-patches Copyright 2000-2007, The WeBWorK Project All rights reserved. |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:21:43
|
Log Message: ----------- replacing .txt files with .pod files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/doc/parser/docs: ParserAnswerCheckers.pod UsingParser.pod Removed Files: ------------- webwork2/doc/parser/docs: ParserAnswerCheckers.txt UsingParser.txt Revision Data ------------- --- /dev/null +++ doc/parser/docs/UsingParser.pod @@ -0,0 +1,393 @@ +=head1 USING MATHOBJECTS + +To use MathObjects in your own problems, you need to load the +"MathObjects.pl" macro file: + + loadMacros("Parser.pl"); + +which defines the commands you need to interact with MathObjects. +Once you have done that, you can call the MathObjects functions to create +formulas for you. The main call is Formula(), which takes a string and +returns a parsed version of the string. For example: + + $f = Formula("x^2 + 3x + 1"); + +will set $f to a reference to the parsed version of the formula. + +=head2 Working With Formulas + +A formula has a number of methods that you can call. These include: + +=over + +=item $f->eval(x=>5) + +Evaluate the formula when x is 5. +If $f has more variables than that, then +you must provide additional values, as in +$f->eval(x=>3,y=>1/2); + +=item $f->reduce + +Tries to remove redundent items from your +formula. For example, Formula("1x+0") returns "x". +Reduce tries to factor out negatives and do +some other adjustments as well. (There still +needs to be more work done on this. What it does +is correct, but not always smart, and there need +to be many more situations covered.) All the +reduction rules can be individually enabled +or disabled using the Context()->reduction->set() +method, but the documentation for the various +rules is not yet ready. + +=item $f->substitute(x=>5) + +Replace x by the value 5 throughout (you may want +to reduce the result afterword, as this is not +done automatically). Note that you can replace a +variable by another formula, if you wish. To make +this easier, substitute will apply Formula() to +any string values automatically. E.g., + Formula("x-1")->substitute(x=>"y") +returns "y-1" as a formula. + +=item $f->string + +returns a string representation of the formula +(should be equivalent to the original, though not +necessarily equal to it). + +=item $f->TeX + +returns a LaTeX representation of the formula. +You can use this in BEGIN_TEXT...END_TEXT blocks +as follows: + + BEGIN_TEXT + Suppose \(f(x) = \{$f->TeX}\). ... + END_TEXT + +=item $f->perl + +returns a representation of the formula that could +be evaluated by perl's eval() function. + +=item $f->perlFunction + +returns a perl code block that can be called to +evaluate the function. For example: + + $f = Formula('x^2 + 3')->perlFunction; + $y = &$f(5); + +will assign the value 28 to $y. +You can also pass a function name to perlFunction +to get a named function to call: + + Formula('x^2 + 3')->perlFunction('f'); + $y = f(5); + +If the formula involves more than one variable, +then the paramaters should be given in +alphabetical order. + + Formula('x^2 + y')->perlFunction('f'); + $z = f(5,3); # $z is 28. + +Alternatively, you can tell the order for the +parameters: + + Formula('x^2 + y')->perlFunction('f',['y','x']); + $z = f(5,3); $ now $z is 14. + +=back + +=head2 Combining Formulas + +There is a second way to create formulas. Once you have a formula, you can +create additional formulas simply by using perls' built-in operations and +functions, which have been overloaded to handle formulas. For example, + + $x = Formula('x'); + $f = 3*x**2 + 2*$x - 1; + +makes $f be a formula, and is equivalent to having done + + $f = Formula("3x^2 + 2x - 1"); + +This can be very convenient, but also has some pitfalls. First, you +need to include '*' for multiplication, since perl doesn't do implied +multiplication, and you must remember to use '**' not '^'. (If you use '^' +on a formula, the parser will remind you to use '**'.) Second, the +precedences of the operators in perl are fixed, and so changes you make to +the precedence table for the parser are not reflected in formulas produced +in this way. (The reason '^' is not overloaded to do exponentiation is +that the precedence of '^' is wrong for that in perl, and can't be +changed.) As long as you leave the default precedences, however, things +should work as you expect. + +Note that the standard functions, like sin, cos, etc, are overloaded to +generate appropriate formulas when their values are formulas. For example, + + $x = Formula('x'); + $f = cos(3*$x + 1); + +produces the same result as $f = Formula("cos(3x+1)"); and you can then go +on to output its TeX form, etc. + +=head2 Special Syntax + +This parser has support for some things that are missing from the current +one, like absolute values. You can say |1+x| rather than abs(1+x) +(though both are allowed), and even |1 - |x|| works. + +Also, you can use sin^2(x) (or even sin^2 x) to get (sin(x))^2. + +Finally, you can use sin^-1(x) to get arcsin(x). + +There is an experimental set of operator precedences that make it possible +to write sin 2x + 3 and get sin(2x) + 3. See examples/7-precedence.pg +for some details. + +=head2 The Formula Types + +The parser understands a wide range of data types, including real and +complex numbers, points, vectors, matrices, arbitrary lists, intervals, +unions of intervals, and predefined words. Each has a syntax for use +within formulas, as described below: + + numbers the usual form: 153, 233.5, -2.456E-3, etc. + + complex a + b i where a and b are numbers: 1+i, -5i, 6-7i, etc. + + infinitites the words 'infinity' or '-infinity' (or several + equivalents). + + point (a,b,c) where a, b and c are real or complex numbers. + any number of coordinates are allowed. Eg, (1,2), + (1,0,0,0), (-1,2,-3). Points are promoted to vectors + automatically, when necessary. + + vector <a,b,c> or a i + b j + c k (when used in vector context). + As with points, vectors can have any number of + coordinates. For example, <1,0,0>, <-1,3>, <x,1-x>, etc. + + matrix [[a11,...,a1n],...[am1,...amn]], i.e., use [..] around + each row, and around the matrix itself. The elements + are separated by commas (not spaces). e.g, + [[1,2],[3,4]] (a 2x2 matrix) + [1,2] (a 1x2 matrix, really a vector) + [[1],[2]] (a 2x1 matrix, ie. column vector) + Points and vectors are promoted to matrices when + appropriate. Vectors are converted to column vectors + when needed for matrix-vector multiplication. Matrices + can be 3-dimensional or higher by repeated nesting of + matrices. (In this way, a 2-dimensional matrix is really + thought of as a vector of vectors, and n-dimensional + ones as vectors of (n-1)-dimensional ones.) + + list (a,b,c) where a,b,c are arbitrary elements. + For example, (1+i, -3, <1,2,3>, Infinity). + The empty list () is allowed, and the parentheses are + optional if there is only one list. (This makes it + possible to make list-based answer checkers that + really know where the separations occur.) + + interval (a,b), (a,b], [a,b), [a,b], or [a,a] where a and b are + numbers or appropriate forms of infinity. + For example, (-INF,3], [4,4], [2,INF), (-INF,INF). + + union represented by 'U'. For example [-1,0) U (0,1]. + + string special predefined strings like NONE and DNE. + +These forms are what are used in the strings passed to Formula(). +If you want to create versions of these in perl, there are several +ways to do it. One way is to use the Compute() command, which takes a +string parses it and then evaluates the result (it is equivalent to +Formula(...)->eval). If the formula produces a vector, the result +will be a Vector constant that you can use in perl formulas by hand. + +For example: + + $v = Compute("<1,1,0> >< <-1,4,-2>"); + +would compute the dot product of the two vectors and assign the +resulting vector object to $v. + +Another way to generate constants of the various types is to use the +following routines. If their inputs are constant, they produce a +constant of the appropriate type. If an input is a formula, they +produce corresponding formula objects. + + Real(a) create a real number with "fuzzy" + comparisons (so that 1.0000001 == Real(1) is true). + + Complex(a,b) create a complex number a + b i + + Infinity creates the +infinity object + -(Infinity) creates -infinity + + Point(x1,...xn) or Point([x1,...,xn]) produces (x1,...,xn) + + Vector(x1,...,xn) or Vector([x1,...,xn]) produces <x1,...,xn> + + Matrix([a11,...,a1m],...,[am1,...,amn]) or + Matrix([[a11,...,a1m],...,[am1,...,amn]]) produces an n x m matrix + + List(a,...,b) produces a list with the given elements + + Interval('(',a,b,']') produces (a,b], (the other endpoints work as + expected. Use 'INF' and '-INF' for infinities.) + + Union(I1,...,In) takes the union of the n intervals. (where I1 to In + are intervals.) + + String(word) Produces a string object for the given word (if it + is a known word). This is mostly to be able to + call the ->cmp and ->TeX methods. + +For example, + + $a = random(-5,5,1) + $V = Vector($a,1-$a,$a**2+1); + +produces a vector with some random coordinates. + +Objects of these types also have TeX, string and perl methods, so you can +use: + + Vector(1,2,3)->TeX + +to produce a TeX version of the vector, just as you can with formulas. + +There are several "constant" functions that generate common constant +values. These include pi, i, j, k and Infininty. you can use these +in perl expressions as though they were their actual values: + + $z = $a + $b * i; + $v = $a*i + $b*j + $c*k; + $I = Infinity; + +Note that because of a peculiarity of perl, you need to use -(pi) +or - pi (with a space) rather than -pi, and similary for the other +functions. Without this, you will get an error message about an +ambiguity being resolved. (This is not a problem if you process your +expressions through the parser itself, only if you are writing +expressions in perl directly. Note that since student answers are +processed by the parser, not perl directly, they can write -pi without +problems.) + +Another useful command is Compute(), which evaluates a formula and +returns its value. This is one way to create point or vector-valued +constants, but there is an easier way discussed below. + +=head2 Specifying the Context + +You may have noticed that "i" was used in two different ways in the +examples above. In the first example, it was treated as a complex +number and the second as a coordinate unit vector. To control which +interpretation is used, you specify a parser "context". + +The context controls what operations and functions are defined in the +parser, what variables and constants to allow, how to interpret +various paretheses, and so on. Changing the context can completely +change the way a formula is interpreted. + +There are several predefined contexts: Numeric, Complex, Vector, +Interval and Full. (You can also define your own contexts, but that +will be described elsewhere.) To select a context, use the Context() +function, e.g. + + Context("Numeric"); + +selects the numeric context, where i, j and k have no special meaning, +points and vectors can't be used, and the only predefined variable is +'x'. + +On the other hand, Context("Vector") makes i, j and k represent the +unit coordinate vectors, and defines variables 'x', 'y' and 'z'. + +Context("Interval") is like numeric context, but it also defines the +parentheses so that they will form intervals (rather than points or +lists). + +Once you have selected a context, you can modify it to suit the +particular needs of your problem. The command + + $context = Context(); + +gets you a reference to the current context object (you can also use +something like + + $context = Context("Numeric"); + +to set the context and get its reference at the same time). Once you +have this reference, you can call the Context methods to change values +in the context. These are discussed in more detail in the +documentation of the Context object [not yet written], but some of the +more common actions are described here. + +To add a variable, use, for example, + + $context->variables->add(y=>'Real'); + +To delete any existing variables and replace them with new ones, use + + $context->variables->are(t=>'Real'); + +To remove a variable, use + + $context->variables->remove('t'); + +To get the names of the defind variables, use + + @names = $context->variables->names; + + +Similarly, you can add a named constant via + + $context->constants->add(M=>1/log(10)); + +and can change, remove or list the constants via methods like those +used for variables above. The command + + $M = $constant->constants->get('M'); + +will return the value of the consant M. (See the +pg/lib/Value/Context/Data.pm file for more information on the methods +you can call for the various types of context data.) + +To add new predefined words (like 'NONE' and 'DNE'), use something +like + + $constant->strings->add(TRUE=>{},FALSE=>{}); + +Note that strings are case-sensitive, so you might want to add + + $constant->strings->add( + true => {alias=>'TRUE'}, + false => {alias=>'FALSE'}, + ); + +so that either "TRUE" or "true" will be interpreted as TRUE. + +There are a number of values stored in the context that control things +like the tolerance used when comparing numbers, and so on. You +control these via commands like: + + $context->flags->set(tolerance=>.00001); + +For example, + + $context->flags->set(ijk=>1); + +will cause the output of all vectors to be written in ijk format +rather than <...> format. + +Finally, you can add or modify the operators and functions that are +available in the parser via calls to $context->operators and +$context->functions. See the files in webwork2/docs/parser/extensions +for examples of how to do this. + --- doc/parser/docs/UsingParser.txt +++ /dev/null @@ -1,383 +0,0 @@ -USING THE PARSER: - -To use the new parser in your own problems, you need to load the -"Parser.pl" macro file: - - loadMacros("Parser.pl"); - -which defines the commands you need to interact with the parser. -Once you have done that, you can call the Parser functions to create -formulas for you. The main call is Formula(), which takes a string and -returns a parsed version of the string. For example: - - $f = Formula("x^2 + 3x + 1"); - -will set $f to a reference to the parsed version of the formula. - - -WORKING WITH FORMULAS: - -A formula has a number of methods that you can call. These include: - - $f->eval(x=>5) Evaluate the formula when x is 5. - If $f has more variables than that, then - you must provide additional values, as in - $f->eval(x=>3,y=>1/2); - - $f->reduce Tries to remove redundent items from your - formula. For example, Formula("1x+0") returns "x". - Reduce tries to factor out negatives and do - some other adjustments as well. (There still - needs to be more work done on this. What it does - is correct, but not always smart, and there need - to be many more situations covered.) All the - reduction rules can be individually enabled - or disabled using the Context()->reduction->set() - method, but the documentation for the various - rules is not yet ready. - - $f->substitute(x=>5) Replace x by the value 5 throughout (you may want - to reduce the result afterword, as this is not - done automatically). Note that you can replace a - variable by another formula, if you wish. To make - this easier, substitute will apply Formula() to - any string values automatically. E.g., - Formula("x-1")->substitute(x=>"y") - returns "y-1" as a formula. - - $f->string returns a string representation of the formula - (should be equivalent to the original, though not - necessarily equal to it). - - $f->TeX returns a LaTeX representation of the formula. - You can use this in BEGIN_TEXT...END_TEXT blocks - as follows: - - BEGIN_TEXT - Suppose \(f(x) = \{$f->TeX}\). ... - END_TEXT - - $f->perl returns a representation of the formula that could - be evaluated by perl's eval() function. - - $f->perlFunction returns a perl code block that can be called to - evaluate the function. For example: - - $f = Formula('x^2 + 3')->perlFunction; - $y = &$f(5); - - will assign the value 28 to $y. - You can also pass a function name to perlFunction - to get a named function to call: - - Formula('x^2 + 3')->perlFunction('f'); - $y = f(5); - - If the formula involves more than one variable, - then the paramaters should be given in - alphabetical order. - - Formula('x^2 + y')->perlFunction('f'); - $z = f(5,3); # $z is 28. - - Alternatively, you can tell the order for the - parameters: - - Formula('x^2 + y')->perlFunction('f',['y','x']); - $z = f(5,3); $ now $z is 14. - - -COMBINING FORMULAS: - -There is a second way to create formulas. Once you have a formula, you can -create additional formulas simply by using perls' built-in operations and -functions, which have been overloaded to handle formulas. For example, - - $x = Formula('x'); - $f = 3*x**2 + 2*$x - 1; - -makes $f be a formula, and is equivalent to having done - - $f = Formula("3x^2 + 2x - 1"); - -This can be very convenient, but also has some pitfalls. First, you -need to include '*' for multiplication, since perl doesn't do implied -multiplication, and you must remember to use '**' not '^'. (If you use '^' -on a formula, the parser will remind you to use '**'.) Second, the -precedences of the operators in perl are fixed, and so changes you make to -the precedence table for the parser are not reflected in formulas produced -in this way. (The reason '^' is not overloaded to do exponentiation is -that the precedence of '^' is wrong for that in perl, and can't be -changed.) As long as you leave the default precedences, however, things -should work as you expect. - -Note that the standard functions, like sin, cos, etc, are overloaded to -generate appropriate formulas when their values are formulas. For example, - - $x = Formula('x'); - $f = cos(3*$x + 1); - -produces the same result as $f = Formula("cos(3x+1)"); and you can then go -on to output its TeX form, etc. - - -SPECIAL SYNTAX: - -This parser has support for some things that are missing from the current -one, like absolute values. You can say |1+x| rather than abs(1+x) -(though both are allowed), and even |1 - |x|| works. - -Also, you can use sin^2(x) (or even sin^2 x) to get (sin(x))^2. - -Finally, you can use sin^-1(x) to get arcsin(x). - -There is an experimental set of operator precedences that make it possible -to write sin 2x + 3 and get sin(2x) + 3. See examples/7-precedence.pg -for some details. - - -THE FORMULA TYPES: - -The parser understands a wide range of data types, including real and -complex numbers, points, vectors, matrices, arbitrary lists, intervals, -unions of intervals, and predefined words. Each has a syntax for use -within formulas, as described below: - - numbers the usual form: 153, 233.5, -2.456E-3, etc. - - complex a + b i where a and b are numbers: 1+i, -5i, 6-7i, etc. - - infinitites the words 'infinity' or '-infinity' (or several - equivalents). - - point (a,b,c) where a, b and c are real or complex numbers. - any number of coordinates are allowed. Eg, (1,2), - (1,0,0,0), (-1,2,-3). Points are promoted to vectors - automatically, when necessary. - - vector <a,b,c> or a i + b j + c k (when used in vector context). - As with points, vectors can have any number of - coordinates. For example, <1,0,0>, <-1,3>, <x,1-x>, etc. - - matrix [[a11,...,a1n],...[am1,...amn]], i.e., use [..] around - each row, and around the matrix itself. The elements - are separated by commas (not spaces). e.g, - [[1,2],[3,4]] (a 2x2 matrix) - [1,2] (a 1x2 matrix, really a vector) - [[1],[2]] (a 2x1 matrix, ie. column vector) - Points and vectors are promoted to matrices when - appropriate. Vectors are converted to column vectors - when needed for matrix-vector multiplication. Matrices - can be 3-dimensional or higher by repeated nesting of - matrices. (In this way, a 2-dimensional matrix is really - thought of as a vector of vectors, and n-dimensional - ones as vectors of (n-1)-dimensional ones.) - - list (a,b,c) where a,b,c are arbitrary elements. - For example, (1+i, -3, <1,2,3>, Infinity). - The empty list () is allowed, and the parentheses are - optional if there is only one list. (This makes it - possible to make list-based answer checkers that - really know where the separations occur.) - - interval (a,b), (a,b], [a,b), [a,b], or [a,a] where a and b are - numbers or appropriate forms of infinity. - For example, (-INF,3], [4,4], [2,INF), (-INF,INF). - - union represented by 'U'. For example [-1,0) U (0,1]. - - string special predefined strings like NONE and DNE. - -These forms are what are used in the strings passed to Formula(). -If you want to create versions of these in perl, there are several -ways to do it. One way is to use the Compute() command, which takes a -string parses it and then evaluates the result (it is equivalent to -Formula(...)->eval). If the formula produces a vector, the result -will be a Vector constant that you can use in perl formulas by hand. - -For example: - - $v = Compute("<1,1,0> >< <-1,4,-2>"); - -would compute the dot product of the two vectors and assign the -resulting vector object to $v. - -Another way to generate constants of the various types is to use the -following routines. If their inputs are constant, they produce a -constant of the appropriate type. If an input is a formula, they -produce corresponding formula objects. - - Real(a) create a real number with "fuzzy" - comparisons (so that 1.0000001 == Real(1) is true). - - Complex(a,b) create a complex number a + b i - - Infinity creates the +infinity object - -(Infinity) creates -infinity - - Point(x1,...xn) or Point([x1,...,xn]) produces (x1,...,xn) - - Vector(x1,...,xn) or Vector([x1,...,xn]) produces <x1,...,xn> - - Matrix([a11,...,a1m],...,[am1,...,amn]) or - Matrix([[a11,...,a1m],...,[am1,...,amn]]) produces an n x m matrix - - List(a,...,b) produces a list with the given elements - - Interval('(',a,b,']') produces (a,b], (the other endpoints work as - expected. Use 'INF' and '-INF' for infinities.) - - Union(I1,...,In) takes the union of the n intervals. (where I1 to In - are intervals.) - - String(word) Produces a string object for the given word (if it - is a known word). This is mostly to be able to - call the ->cmp and ->TeX methods. - -For example, - - $a = random(-5,5,1) - $V = Vector($a,1-$a,$a**2+1); - -produces a vector with some random coordinates. - -Objects of these types also have TeX, string and perl methods, so you can -use: - - Vector(1,2,3)->TeX - -to produce a TeX version of the vector, just as you can with formulas. - -There are several "constant" functions that generate common constant -values. These include pi, i, j, k and Infininty. you can use these -in perl expressions as though they were their actual values: - - $z = $a + $b * i; - $v = $a*i + $b*j + $c*k; - $I = Infinity; - -Note that because of a peculiarity of perl, you need to use -(pi) -or - pi (with a space) rather than -pi, and similary for the other -functions. Without this, you will get an error message about an -ambiguity being resolved. (This is not a problem if you process your -expressions through the parser itself, only if you are writing -expressions in perl directly. Note that since student answers are -processed by the parser, not perl directly, they can write -pi without -problems.) - -Another useful command is Compute(), which evaluates a formula and -returns its value. This is one way to create point or vector-valued -constants, but there is an easier way discussed below. - - -SPECIFYING THE CONTEXT - -You may have noticed that "i" was used in two different ways in the -examples above. In the first example, it was treated as a complex -number and the second as a coordinate unit vector. To control which -interpretation is used, you specify a parser "context". - -The context controls what operations and functions are defined in the -parser, what variables and constants to allow, how to interpret -various paretheses, and so on. Changing the context can completely -change the way a formula is interpreted. - -There are several predefined contexts: Numeric, Complex, Vector, -Interval and Full. (You can also define your own contexts, but that -will be described elsewhere.) To select a context, use the Context() -function, e.g. - - Context("Numeric"); - -selects the numeric context, where i, j and k have no special meaning, -points and vectors can't be used, and the only predefined variable is -'x'. - -On the other hand, Context("Vector") makes i, j and k represent the -unit coordinate vectors, and defines variables 'x', 'y' and 'z'. - -Context("Interval") is like numeric context, but it also defines the -parentheses so that they will form intervals (rather than points or -lists). - -Once you have selected a context, you can modify it to suit the -particular needs of your problem. The command - - $context = Context(); - -gets you a reference to the current context object (you can also use -something like - - $context = Context("Numeric"); - -to set the context and get its reference at the same time). Once you -have this reference, you can call the Context methods to change values -in the context. These are discussed in more detail in the -documentation of the Context object [not yet written], but some of the -more common actions are described here. - -To add a variable, use, for example, - - $context->variables->add(y=>'Real'); - -To delete any existing variables and replace them with new ones, use - - $context->variables->are(t=>'Real'); - -To remove a variable, use - - $context->variables->remove('t'); - -To get the names of the defind variables, use - - @names = $context->variables->names; - - -Similarly, you can add a named constant via - - $context->constants->add(M=>1/log(10)); - -and can change, remove or list the constants via methods like those -used for variables above. The command - - $M = $constant->constants->get('M'); - -will return the value of the consant M. (See the -pg/lib/Value/Context/Data.pm file for more information on the methods -you can call for the various types of context data.) - -To add new predefined words (like 'NONE' and 'DNE'), use something -like - - $constant->strings->add(TRUE=>{},FALSE=>{}); - -Note that strings are case-sensitive, so you might want to add - - $constant->strings->add( - true => {alias=>'TRUE'}, - false => {alias=>'FALSE'}, - ); - -so that either "TRUE" or "true" will be interpreted as TRUE. - -There are a number of values stored in the context that control things -like the tolerance used when comparing numbers, and so on. You -control these via commands like: - - $context->flags->set(tolerance=>.00001); - -For example, - - $context->flags->set(ijk=>1); - -will cause the output of all vectors to be written in ijk format -rather than <...> format. - -Finally, you can add or modify the operators and functions that are -available in the parser via calls to $context->operators and -$context->functions. See the files in webwork2/docs/parser/extensions -for examples of how to do this. - - - - --- /dev/null +++ doc/parser/docs/ParserAnswerCheckers.pod @@ -0,0 +1,423 @@ +=head1 MathObjects-based Answer Checkers + +MathObjects is designed to be used in two ways. First, you can use +it within your perl code when writing problems as a means of making it +easier to handle formulas, and in particular, to genarate be able to +use a single object to produce numeric values, TeX output and answer +strings. This avoids having to type a function three different ways +(which makes maintaining a problem much harder). Since MathObjects +also included vector and complex arthimatic, it is easier to work with +these types of values as well. + +The second reason for MathObjects is to use it to process student +input. This is accomplished through special answer checkers that are +part of the Parser package (rather than the traditional WeBWorK answer +checkers). Checkers are available for all the types of values that +the parser can produce (numbers, complex numbers, infinities, points, +vectors, intervals, unions, formulas, lists of numbers, lists of +points, lists of intervals, lists of formulas returning numbers, lists +of formulas returning points, and so on). + +To use one of these checkers, simply call the ->cmp method of the +object that represents the correct answer. For example: + + $n = Real(sqrt(2)); + ANS($n->cmp); + +will produce an answer checker that matches the square root of two. +Similarly, + + ANS(Vector(1,2,3)->cmp); + +matches the vector <1,2,3> (or any computation that produces it, e.g., +i+2j+3k, or <4,4,4>-<3,2,1>), while + + ANS(Interval("(-inf,3]")->cmp); + +matches the given interval. Other examples include: + + ANS(Infinity->cmp); + ANS(String('NONE')->cmp); + ANS(Union("(-inf,$a) U ($a,inf)")->cmp); + +and so on. + +Formulas are handled in the same way: + + ANS(Formula("x+1")->cmp); + + $a = random(-5,5,1); $b = random(-5,5,1); $x = random(-5,5,1); + $f = Formula("x^2 + $a x + $b")->reduce; + ANS($f->cmp); + ANS($f->eval(x=>$x)->cmp); + + $x = Formula('x'); + ANS((1+$a*$x)->cmp); + + Context("Vector")->variables->are(t=>'Real'); + $v = Formula("<t,t^2,t^3>"); $t = random(-5,5,1); + ANS($v->cmp); + ANS($v->eval(t=>$t)->cmp); + +and so on. + +Lists of items can be checked as easily: + + ANS(List(1,-1,0)->cmp); + ANS(List(Point($a,$b),Point($a,-$b))->cmp); + ANS(List(Vector(1,0,0),Vector(0,1,1))->cmp); + ANS(Compute("(-inf,2),(4,5)")->cmp); # easy way to get list of intervals + ANS(Formula("x, x+1, x^2-1")->cmp); + ANS(Formula("<x,2x>,<x,-2x>,<0,x>")->cmp); + ANS(List('NONE')->cmp); + +and so on. The last example may seem strange, as you could have used +ANS(String('NONE')->cmp), but there is a reason for using this type +of construction. You might be asking for one or more numbers (or +points, or whatever) or the word 'NONE' of there are no numbers (or +points). If you used String('NONE')->cmp, the student would get an +error message about a type mismatch if he entered a list of numbers, +but with List('NONE')->cmp, he will get appropriate error messages for +the wrong entries in the list. + +It is often appropriate to use the list checker in this way even when +the correct answer is a single value, if the student might type a list +of answers. + +On the other hand, using the list checker has its disadvantages. For +example, if you use + + ANS(Interval("(-inf,3]")->cmp); + +and the student enters (-inf,3), she will get a message indicating +that the type of interval is incorrect, while that would not be the +case if + + ANS(List(Interval("(-inf,3]"))->cmp); + +were used. (This is because the student doesn't know how many +intervals there are, so saying that the type of interval is wrong +would inform her that there is only one.) + +The rule of thumb is: the individual checkers can give more detailed +information about what is wrong with the student's answer; the list +checker allows a wider range of answers to be given without giving +away how many answers there are. If the student knows there's only +one, use the individual checker; if there may or may not be more than +one, use the list checker. + +Note that you can form lists of formulas as well. The following all +produce the same answer checker: + + ANS(List(Formula("x+1"),Formula("x-1"))->cmp); + + ANS(Formula("x+1,x-1")->cmp); # easier + + $f = Formula("x+1"); $g = Formula("x-1"); + ANS(List($f,$g)->cmp); + + $x = Formula('x'); + ANS(List($x+1,$x-1)->cmp); + +See the files in webwork2/doc/parser/problems for more +examples of using the parser's answer checkers. + +=head2 Controlling the Details of the Answer Checkers + +The action of the answer checkers can be modified by passing flags to +the cmp() method. For example: + + ANS(Real(pi)->cmp(showTypeWarnings=>0)); + +will prevent the answer checker from reporting errors due to the +student entering in the wrong type of answer (say a vector rather than +a number). + +=head3 Flags common to all answer checkers + +There are a number of flags common to all the checkers: + +=over + +=item S<C<< showTypeWarnings=>1 or 0 >>> + +show/don't show messages about student +answers not being of the right type. +(default: 1) + +=item S<C<< showEqualErrors=>1 or 0 >>> + +show/don't show messages produced by +trying to compare the professor and +student values for equality, e.g., +conversion errors between types. +(default: 1) + +=item S<C<< ignoreStrings=>1 or 0 >>> + +show/don't show type mismatch errors +produced by strings (so that 'NONE' will +not cause a type mismatch in a checker +looking for a list of numbers, for example). +(default: 1) + +=back + +In addition to these, the individual types have their own flags: + +=head3 Flags for Real()->cmp + +=over + +=item S<C<< ignoreInfinity=>1 or 0 >>> + +Don't report type mismatches if the +student enters an infinity. +(default: 1) + +=back + +=head3 Flags for String()->cmp + +=over + +=item S<C<< typeMatch=>value >>> + +Specifies the type of object that +the student should be allowed to enter +(in addition the string). +(default: 'Value::Real') + +=back + +=head3 Flags for Point()->cmp + +=over + +=item S<C<< showDimensionHints=>1 or 0 >>> + +show/don't show messages about the +wrong number of coordinates. +(default: 1) + +=item S<C<< showCoordinateHints=>1 or 0 >>> + +show/don't show message about +which coordinates are right. +(default: 1) + +=back + +=head3 Flags for Vector()->cmp + +=over + +=item S<C<< showDimensionHints=>1 or 0 >>> + +show/don't show messages about the +wrong number of coordinates. +(default: 1) + +=item S<C<< showCoordinateHints=>1 or 0 >>> + +show/don't show message about +which coordinates are right. +(default: 1) + +=item S<C<< promotePoints=>1 or 0 >>> + +do/don't allow the student to +enter a point rather than a vector. +(default: 1) + +=item S<C<< parallel=>1 or 0 >>> + +Mark the answer as correct if it +is parallel to the professor's answer. +Note that a value of 1 forces +showCoordinateHints to be 0. +(default: 0) + +=item S<C<< sameDirection=>1 or 0 >>> + +During a parallel check, mark the +answer as correct only if it is in +the same (not the opposite) +direction as the professor's answer. +(default: 0) + +=back + +=head3 Flags for Matrix()->cmp + +=over + +=item S<C<< showDimensionHints=>1 or 0 >>> + +show/don't show messages about the +wrong number of coordinates. +(default: 1) + +=back + +The default for showEqualErrors is set to 0 for Matrices, since +these errors usually are dimension errors, and that is handled +separately (and after the equality check). + +=head3 Flags for Interval()->cmp + +=over + +=item S<C<< showEndpointHints=>1 or 0 >>> + +do/don't show messages about which +endpoints are correct. +(default: 1) + +=item S<C<< showEndTypeHints=>1 or 0 >>> + +do/don't show messages about +whether the open/closed status of +the enpoints are correct (only +shown when the endpoints themselves +are correct). +(default: 1) + +=back + +=head3 Flags for Union()->cmp and List()->cmp + +all the flags from the Real()->cmp, plus: + +=over + +=item S<C<< showHints=>1 or 0 >>> + +do/don't show messages about which +entries are incorrect. +(default: $showPartialCorrectAnswers) + +=item S<C<< showLengthHints=>1 or 0 >>> + +do/don't show messages about having the +correct number of entries (only shown +when all the student answers are +correct but there are more needed, or +all the correct answsers are among the +ones given, but some extras were given). +(default: $showPartialCorrectAnswers) + +=item S<C<< partialCredit=>1 or 0 >>> + +do/don't give partial credit for when +some answers are right, but not all. +(default: $showPartialCorrectAnswers) +(currently the default is 0 since WW +can't handle partial credit properly). + +=item S<C<< ordered=>1 or 0 >>> + +give credit only if the student answers +are in the same order as the +professor's answers. +(default: 0) + +=item S<C<< entry_type=>'a (name)' >>> + +The string to use in error messages +about type mismatches. +(default: dynamically determined from list) + +=item S<C<< list_type=>'a (name)' >>> + +The string to use in error messages +about numbers of entries in the list. +(default: dynamically determined from list) + +=item S<C<< typeMatch=>value >>> + +Specifies the type of object that +the student should be allowed to enter +in the list (determines what +constitutes a type mismatch error). +(default: dynamically determined from list) + +=item S<C<< requireParenMatch=>1 or 0 >>> + +Do/don't require the parentheses in the +student's answer to match those in the +professor's answer exactly. +(default: 1) + +=item S<C<< removeParens=>1 or 0 >>> + +Do/don't remove the parentheses from the +professor's list as part of the correct +answer string. This is so that if you +use List() to create the list (which +doesn't allow you to control the parens +directly), you can still get a list +with no parentheses. +(default: 0 for List() and 1 for Formula()) + +=back + +=head3 Flags for Formula()->cmp + +The flags for formulas are dependent on the type of the result of +the formula. If the result is a list or union, it gets the flags +for that type above, otherwise it gets that flags of the Real +type above. + +More flags need to be added in order to allow more control over the +answer checkers to give the full flexibility of the traditional +WeBWorK answer checkers. Note that some things, like whether trig +functions are allowed in the answer, are controlled through the +Context() rather than the answer checker itself. For example, + + Context()->functions->undefine('sin','cos','tan'); + +would remove those three functions from use. (One would need to remove +cot, sec, csc, arcsin, asin, etc., to do this properly; there could be +a function call to do this.) + +Similarly, which arithmetic operations are available is controlled +through Context()->operations. + +The tolerances used in comparing numbers are part of the Context as +well. You can set these via: + + Context()->flags->set( + tolerance => .0001, # the relative or absolute tolerance + tolType => 'relative', # or 'absolute' + zeroLevel => 1E-14, # when to use zeroLevelTol + zeroLevelTol => 1E-12, # smaller than this matches zero + # when one of the two is less + # than zeroLevel + limits => [-2,2], # limits for variables in formulas + num_points => 5, # the number of test points + ); + +[These need to be handled better.] + +Note that for testing formulas, you can override the limits and +num_points settings by setting these fields of the formula itself: + + $f = Formula("sqrt(x-10)"); + $f->{limits} = [10,12]; + + $f = Formula("log(xy)"); + $f->{limits} = [[.1,2],[.1,2]]; # x and y limits + +You can also specify the test points explicitly: + + $f = Formula("sqrt(x-10)"); + $f->{test_points} = [[11],[11.5],[12]]; + + $f = Formula("log(xy)"); + $f->{test_points} = [[.1,.1],[.1,.5],[.1,.75], + [.5,.1],[.5,.5],[.5,.75]]; + +[There still needs to be a means of handling the tolerances similarly, +and through the ->cmp() call itself.] + --- doc/parser/docs/ParserAnswerCheckers.txt +++ /dev/null @@ -1,348 +0,0 @@ -PARSER-BASED ANSWER CHECKERS - -The new parser is designed to be used in two ways. First, you can use -it within your perl code when writing problems as a means of making it -easier to handle formulas, and in particular, to genarate be able to -use a single object to produce numeric values, TeX output and answer -strings. This avoids having to type a function three different ways -(which makes maintaining a problem much harder). Since the parser -also included vector and complex arthimatic, it is easier to work with -these types of values as well. - -The second reason for the parser is to use it to process student -input. This is accomplished through special answer checkers that are -part of the Parser package (rather than the traditional WeBWorK answer -checkers). Checkers are available for all the types of values that -the parser can produce (numbers, complex numbers, infinities, points, -vectors, intervals, unions, formulas, lists of numbers, lists of -points, lists of intervals, lists of formulas returning numbers, lists -of formulas returning points, and so on). - -To use one of these checkers, simply call the ->cmp method of the -object that represents the correct answer. For example: - - $n = Real(sqrt(2)); - ANS($n->cmp); - -will produce an answer checker that matches the square root of two. -Similarly, - - ANS(Vector(1,2,3)->cmp); - -matches the vector <1,2,3> (or any computation that produces it, e.g., -i+2j+3k, or <4,4,4>-<3,2,1>), while - - ANS(Interval("(-inf,3]")->cmp); - -matches the given interval. Other examples include: - - ANS(Infinity->cmp); - ANS(String('NONE')->cmp); - ANS(Union("(-inf,$a) U ($a,inf)")->cmp); - -and so on. - -Formulas are handled in the same way: - - ANS(Formula("x+1")->cmp); - - $a = random(-5,5,1); $b = random(-5,5,1); $x = random(-5,5,1); - $f = Formula("x^2 + $a x + $b")->reduce; - ANS($f->cmp); - ANS($f->eval(x=>$x)->cmp); - - $x = Formula('x'); - ANS((1+$a*$x)->cmp); - - Context("Vector")->variables->are(t=>'Real'); - $v = Formula("<t,t^2,t^3>"); $t = random(-5,5,1); - ANS($v->cmp); - ANS($v->eval(t=>$t)->cmp); - -and so on. - -Lists of items can be checked as easily: - - ANS(List(1,-1,0)->cmp); - ANS(List(Point($a,$b),Point($a,-$b))->cmp); - ANS(List(Vector(1,0,0),Vector(0,1,1))->cmp); - ANS(Compute("(-inf,2),(4,5)")->cmp); # easy way to get list of intervals - ANS(Formula("x, x+1, x^2-1")->cmp); - ANS(Formula("<x,2x>,<x,-2x>,<0,x>")->cmp); - ANS(List('NONE')->cmp); - -and so on. The last example may seem strange, as you could have used -ANS(String('NONE')->cmp), but there is a reason for using this type -of construction. You might be asking for one or more numbers (or -points, or whatever) or the word 'NONE' of there are no numbers (or -points). If you used String('NONE')->cmp, the student would get an -error message about a type mismatch if he entered a list of numbers, -but with List('NONE')->cmp, he will get appropriate error messages for -the wrong entries in the list. - -It is often appropriate to use the list checker in this way even when -the correct answer is a single value, if the student might type a list -of answers. - -On the other hand, using the list checker has its disadvantages. For -example, if you use - - ANS(Interval("(-inf,3]")->cmp); - -and the student enters (-inf,3), she will get a message indicating -that the type of interval is incorrect, while that would not be the -case if - - ANS(List(Interval("(-inf,3]"))->cmp); - -were used. (This is because the student doesn't know how many -intervals there are, so saying that the type of interval is wrong -would inform her that there is only one.) - -The rule of thumb is: the individual checkers can give more detailed -information about what is wrong with the student's answer; the list -checker allows a wider range of answers to be given without giving -away how many answers there are. If the student knows there's only -one, use the individual checker; if there may or may not be more than -one, use the list checker. - -Note that you can form lists of formulas as well. The following all -produce the same answer checker: - - ANS(List(Formula("x+1"),Formula("x-1"))->cmp); - - ANS(Formula("x+1,x-1")->cmp); # easier - - $f = Formula("x+1"); $g = Formula("x-1"); - ANS(List($f,$g)->cmp); - - $x = Formula('x'); - ANS(List($x+1,$x-1)->cmp); - -See the files in webwork2/doc/parser/problems for more -examples of using the parser's answer checkers. - - -CONTROLLING THE DETAILS OF THE ANSWER CHECKERS: - -The action of the answer checkers can be modified by passing flags to -the cmp() method. For example: - - ANS(Real(pi)->cmp(showTypeWarnings=>0)); - -will prevent the answer checker from reporting errors due to the -student entering in the wrong type of answer (say a vector rather than -a number). - -There are a number of flags common to all the checkers: - - showTypeWarnings=>1 or 0 show/don't show messages about student - answers not being of the right type. - (default: 1) - - showEqualErrors=>1 or 0 show/don't show messages produced by - trying to compare the professor and - student values for equality, e.g., - conversion errors between types. - (default: 1) - - ignoreStrings=>1 or 0 show/don't show type mismatch errors - produced by strings (so that 'NONE' will - not cause a type mismatch in a checker - looking for a list of numbers, for example). - (default: 1) - -In addition to these, the individual types have their own flags: - - Real()->cmp: - - ignoreInfinity=>1 or 0 Don't report type mismatches if the - student enters an infinity. - (default: 1) - - String()->cmp: - - typeMatch=>value Specifies the type of object that - the student should be allowed to enter - (in addition the string). - (default: 'Value::Real') - - - Point()->cmp: - - showDimensionHints=>1 or 0 show/don't show messages about the - wrong number of coordinates. - (default: 1) - - showCoordinateHints=>1 or 0 show/don't show message about - which coordinates are right. - (default: 1) - - - Vector()->cmp: - - showDimensionHints=>1 or 0 show/don't show messages about the - wrong number of coordinates. - (default: 1) - - showCoordinateHints=>1 or 0 show/don't show message about - which coordinates are right. - (default: 1) - - promotePoints=>1 or 0 do/don't allow the student to - enter a point rather than a vector. - (default: 1) - - parallel=>1 or 0 Mark the answer as correct if it - is parallel to the professor's answer. - Note that a value of 1 forces - showCoordinateHints to be 0. - (default: 0) - - sameDirection=>1 or 0 During a parallel check, mark the - answer as correct only if it is in - the same (not the opposite) - direction as the professor's answer. - (default: 0) - - Matrix()->cmp: - - showDimensionHints=>1 or 0 show/don't show messages about the - wrong number of coordinates. - (default: 1) - - The default for showEqualErrors is set to 0 for Matrices, since - these errors usually are dimension errors, and that is handled - separately (and after the equality check). - - - Interval()->cmp: - - showEndpointHints=>1 or 0 do/don't show messages about which - endpoints are correct. - (default: 1) - - showEndTypeHints=>1 or 0 do/don't show messages about - whether the open/closed status of - the enpoints are correct (only - shown when the endpoints themselves - are correct). - (default: 1) - - - Union()->cmp and - List()->cmp: - - all the flags from the Real()->cmp, plus: - - showHints=>1 or 0 do/don't show messages about which - entries are incorrect. - (default: $showPartialCorrectAnswers) - - showLengthHints=>1 or 0 do/don't show messages about having the - correct number of entries (only shown - when all the student answers are - correct but there are more needed, or - all the correct answsers are among the - ones given, but some extras were given). - (default: $showPartialCorrectAnswers) - - partialCredit=>1 or 0 do/don't give partial credit for when - some answers are right, but not all. - (default: $showPartialCorrectAnswers) - (currently the default is 0 since WW - can't handle partial credit properly). - - ordered=>1 or 0 give credit only if the student answers - are in the same order as the - professor's answers. - (default: 0) - - entry_type=>'a (name)' The string to use in error messages - about type mismatches. - (default: dynamically determined from list) - - list_type=>'a (name)' The string to use in error messages - about numbers of entries in the list. - (default: dynamically determined from list) - - typeMatch=>value Specifies the type of object that - the student should be allowed to enter - in the list (determines what - constitutes a type mismatch error). - (default: dynamically determined from list) - - requireParenMatch=>1 or 0 - Do/don't require the parentheses in the - student's answer to match those in the - professor's answer exactly. - (default: 1) - - removeParens=>1 or 0 Do/don't remove the parentheses from the - professor's list as part of the correct - answer string. This is so that if you - use List() to create the list (which - doesn't allow you to control the parens - directly), you can still get a list - with no parentheses. - (default: 0 for List() and 1 for Formula()) - - Formula()->cmp: - - The flags for formulas are dependent on the type of the result of - the formula. If the result is a list or union, it gets the flags - for that type above, otherwise it gets that flags of the Real - type above. - - -More flags need to be added in order to allow more control over the -answer checkers to give the full flexibility of the traditional -WeBWorK answer checkers. Note that some things, like whether trig -functions are allowed in the answer, are controlled through the -Context() rather than the answer checker itself. For example, - - Context()->functions->undefine('sin','cos','tan'); - -would remove those three functions from use. (One would need to remove -cot, sec, csc, arcsin, asin, etc., to do this properly; there could be -a function call to do this.) - -Similarly, which arithmetic operations are available is controlled -through Context()->operations. - -The tolerances used in comparing numbers are part of the Context as -well. You can set these via: - - Context()->flags->set( - tolerance => .0001, # the relative or absolute tolerance - tolType => 'relative', # or 'absolute' - zeroLevel => 1E-14, # when to use zeroLevelTol - zeroLevelTol => 1E-12, # smaller than this matches zero - # when one of the two is less - # than zeroLevel - limits => [-2,2], # limits for variables in formulas - num_points => 5, # the number of test points - ); - -[These need to be handled better.] - -Note that for testing formulas, you can override the limits and -num_points settings by setting these fields of the formula itself: - - $f = Formula("sqrt(x-10)"); - $f->{limits} = [10,12]; - - $f = Formula("log(xy)"); - $f->{limits} = [[.1,2],[.1,2]]; # x and y limits - -You can also specify the test points explicitly: - - $f = Formula("sqrt(x-10)"); - $f->{test_points} = [[11],[11.5],[12]]; - - $f = Formula("log(xy)"); - $f->{test_points} = [[.1,.1],[.1,.5],[.1,.75], - [.5,.1],[.5,.5],[.5,.75]]; - -[There still needs to be a means of handling the tolerances similarly, -and through the ->cmp() call itself.] |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:20:07
|
Log Message: ----------- modifications to webwork.apache-config.dist Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/conf: webwork.apache-config.dist Revision Data ------------- Index: webwork.apache-config.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/webwork.apache-config.dist,v retrieving revision 1.16.2.4.2.1 retrieving revision 1.16.2.4.2.2 diff -Lconf/webwork.apache-config.dist -Lconf/webwork.apache-config.dist -u -r1.16.2.4.2.1 -r1.16.2.4.2.2 --- conf/webwork.apache-config.dist +++ conf/webwork.apache-config.dist @@ -162,6 +162,7 @@ # #</Location> # # PerlModule WebworkSOAP + # #WEBWORK SOAP CONFIGURATION # <Location /webwork2_rpc> # PerlHandler Apache::SOAP @@ -171,6 +172,7 @@ # Order Allow,Deny # Allow from All # </Location> + # #WEBWORK SOAP WSDL HANDLER :: TO BE REPLACED WITH A FILE FOR PRODUCTION SERVERS # <Location /webwork2_wsdl> # PerlSetVar dispatch_to "WebworkSOAP::WSDL" |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:19:32
|
Log Message: ----------- adding missing files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/bin: pg-append-textbook-tags pg-find-tags pg-pull Revision Data ------------- --- /dev/null +++ bin/pg-find-tags @@ -0,0 +1,110 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/pg-find-tags,v 1.2.2.1 2008/06/24 23:02:16 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +# +# Contributed by W.H. Freeman; Bedford, Freeman, and Worth Publishing Group. +################################################################################ + +use strict; +use warnings; + +use Data::Dumper;# $Data::Dumper::Indent = 0; +use File::Find; +use Getopt::Long; +use IO::Handle; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::NPL qw/gen_find_tags/; + +sub main { + my ($pattern, @paths) = @_; + my $oldfh = select(STDERR); $|=1; select(STDOUT); $|=1; select($oldfh); + my $wanted = gen_find_tags($pattern, \&report); + find({ wanted=>$wanted, no_chdir=>1, }, @paths); +} + +sub report { + my ($name, $tags) = @_; + print "$name\n"; +} + +my %o; +GetOptions(\%o, + "DESCRIPTION=s", + "KEYWORDS=s", + "DBsubject=s", + "DBchapter=s", + "DBsection=s", + "Date=s", + "Institution=s", + "Author=s", + "title=s", + "edition=s", + "author=s", + "chapter=s", + "section=s", + "problem=s", +); +main(\%o, @ARGV); + +__END__ + +=head1 NAME + +pg-find-tags - Search for PG files that contain the specified metadata tags. + +=head1 SYNOPSIS + + pg-find-tags ~/MyLibrary /ww/OtherLibrary --author=Rogawski --edition=1 + +=head1 DESCRIPTION + +Recusively searches the paths given for PG files containing all of the specified +tags. Output is the path to each matching file. Legal tags are as follows: + +B<Global fields:> + + --DESCRIPTION=STRING + --KEYWORDS=STRING + --DBsubject=STRING + --DBchapter=STRING + --DBsection=STRING + --Date=STRING + --Institution=STRING + --Author=STRING + +B<Text-specific fields:> + + --title=STRING + --edition=STRING + --author=STRING + --chapter=STRING + --section=STRING + --problem=STRING + +If multiple text-specific fields are given, then all must match for a single +textbook. + +=head1 LIMITATIONS + +Doesn't support full boolean searches, and it probably should. Can only match on +full strings, so you can't match on a single keyword, for example. + +=cut --- /dev/null +++ bin/pg-append-textbook-tags @@ -0,0 +1,115 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/pg-append-textbook-tags,v 1.2.2.1 2008/06/24 23:02:16 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +# +# Contributed by W.H. Freeman; Bedford, Freeman, and Worth Publishing Group. +################################################################################ + +use strict; +use warnings; + +use IO::File; +use Data::Dumper;# $Data::Dumper::Indent = 0; +use Getopt::Long; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::NPL qw/read_tags format_tags/; + +sub main { + my ($new_tags, @files) = @_; + $new_tags = { textbooks=>[$new_tags] }; + foreach my $file (@files) { + add_tags_to_file($new_tags, $file); + } +} + +sub add_tags_to_file { + my ($new_tags, $file) = @_; + + my $pgfile = new IO::File($file, '+<:utf8') or do { + warn "Failed to open file $file for editing: $!\n"; + warn "New tags will not be written to this file.\n"; + return; + }; + + my $old_tags = {}; + read_tags($pgfile, $old_tags, 1); # 1==extra_editing_info + my $pos = $old_tags->{_pos}; + my $rest = $old_tags->{_rest}; + my $maxtextbook = $old_tags->{_maxtextbook}; + print "pos=$pos maxtextbook=$maxtextbook\n"; + + my @tagstrings = format_tags($new_tags, $maxtextbook+1); + + seek $pgfile, $pos, 0; + foreach my $string (@tagstrings) { + $string =~ s/^/## /gm; + print $pgfile "$string\n"; + } + print $pgfile $rest; +} + +my %o; +GetOptions(\%o, + "title=s", + "edition=s", + "author=s", + "chapter=s", + "section=s", + "problem=s", +); +main(\%o, @ARGV); + +__END__ + +=head1 NAME + +pg-append-text-tags -- Add textbook tags to a PG file. + +=head1 SYNOPSIS + + pg-append-text-tags file1.pg file2.pg --author=Rogawski \ + --title='Calculus: Early Transcendentals' --edition=1 \ + --chapter=3 --section=1 --problem=11,13 + +=head1 DESCRIPTION + +This script appends metadata tags for a new textbook to one or more PG files. +Tags are given as switches on the command line: + + --title=STRING + --edition=STRING + --author=STRING + --chapter=STRING + --section=STRING + --problem=STRING + +More than one problem can be specified for B<--problem> by passing a +comma-separated list. + +=head1 LIMITATIONS + +Only adds tags for a new textbook, can't rewrite existing tags, can't remove +tags. + +At some point I will write Tie::PGFile, which will allow direct editing of tags +just by modifying a hash, and that will fix all of these problems. :) + +=cut --- /dev/null +++ bin/pg-pull @@ -0,0 +1,284 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/pg-pull,v 1.4.2.1 2008/06/24 23:02:16 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +# +# Contributed by W.H. Freeman; Bedford, Freeman, and Worth Publishing Group. +################################################################################ + +# Assemble a new problem library consisting of the specified PG files and any required +# auxiliary files (i.e. images). +# +# Auxiliary files must be specified in the (unofficial) UsesAuxiliaryFiles(...) tag. +# It is assumed that auxiliary files don't depend on further auxiliary files. +# This will be true <100% of the time. +# +# Also, right now this script always uses the FIRST textbook entry in generating +# the new file's path and name. Eventually, a --match-text switch will make the +# script useful for files with multiple text tags. + +use strict; +use warnings; + +use Data::Dumper; +use File::Path; +use File::Spec; +use File::Copy; +use Getopt::Long; +use IO::File; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::NPL qw/read_textbooks read_tags/; + +my %o; +my %textbooks; +my %seen_paths; + +sub main { + my (@files) = @_; + my $oldfh = select(STDERR); $|=1; select(STDOUT); $|=1; select($oldfh); + + if (defined $o{help}) { + print usage(); + exit; + } + + my $dest_lib = $o{'dest-lib'}; + # using the last argument as dest-lib is too confusing + #$dest_lib = pop @files unless defined $dest_lib; + die "dest-lib not specified.\n" . usage() unless defined $dest_lib; + + die "no files specified (perhaps you meant to use --stdin?)\n" . usage() + unless @files or $o{stdin}; + + if ($o{'orig-paths'} and not defined $o{'src-lib'}) { + die "--src-lib must be specified with --orig-paths.\n", usage(); + } + + if (defined $o{'src-lib'} and not $o{'orig-paths'}) { + warn "ignoring --src-lib since --orig-paths was not specified.\n"; + } + + if ($o{'orig-paths'}) { + if (defined $o{textbooks}) { + warn "ignoring --textbooks since --orig-paths was specified.\n"; + } + } else { + if (defined $o{textbooks}) { + get_texts($o{textbooks}); + } else { + warn "No Textbooks file specified -- directories will not be named.\n"; + } + } + + my $getfile; + if ($o{stdin}) { + $getfile = sub { if (defined ($_=<STDIN>)) { chomp; $_ } else { () } }; + } else { + my $i = 0; + $getfile = sub { defined $files[$i] ? $files[$i++] : () }; + } + + while (defined (my $file = &$getfile)) { + #print "file=$file\n"; + process_file($file); + } +} + +sub get_texts { + my $textbooks = shift; + my @textbooks; + + open my $fh, '<', $textbooks or die "$textbooks: $!\n"; + read_textbooks($fh, \@textbooks); + close $fh; + + foreach my $textbook (@textbooks) { + $textbooks{$textbook->{'_author'}}{$textbook->{'_title'}}{$textbook->{'_edition'}} = $textbook; + } +} + +sub process_file { + my $file = shift; + + # ignoring volume here because we don't care about w32 + (undef, my ($dir, $name)) = File::Spec->splitpath($file); + #print " dir=$dir\n"; + #print " name=$name\n"; + + my %tags; + read_tags($file, \%tags); + #print Dumper(\%tags); + + my ($target_dir_rel, $target_name); + if ($o{'orig-paths'}) { + $target_dir_rel = File::Spec->abs2rel($dir, $o{'src-lib'}); + $target_name = $name; + } else { + ($target_dir_rel, $target_name) = tags_to_path(\%tags, '.pg', $file); + } + + if ($o{'check-names'} and $name ne $target_name) { + warn "$file: name will be changed to $target_name\n"; + } + + my $target_dir = File::Spec->catdir($o{'dest-lib'}, $target_dir_rel); + + my @files = ($target_name); + push @files, @{$tags{UsesAuxiliaryFiles}} if defined $tags{UsesAuxiliaryFiles}; + + #print "mkpath $target_dir\n" if $o{pretend} or $o{verbose}; + mkpath($target_dir) unless $o{pretend}; + + foreach my $curr (@files) { + my $src = File::Spec->catpath(undef, $dir, $curr); + my $dest = File::Spec->catpath(undef, $target_dir, $curr); + print "copy $src $dest\n" if $o{pretend} or $o{verbose}; + copy($src, $dest) unless $o{pretend}; + } +} + +sub tags_to_path { + my ($tags, $ext, $file) = @_; + + # FIXME here is where we'd put in textbook matching + my $text = $tags->{textbooks}[0]; + unless (defined $text) { + warn "$file: no textbook tags\n"; + return; + } + my %text = %$text; + + if (not defined $text{author} + or not defined $text{title} + or not defined $text{edition} + or not defined $text{chapter} + or not defined $text{section} + or not defined $text{problem} + or @{$text{problem}} == 0) { + warn "$file: incomplete textbook tags\n"; + return; + } + + my $chapter_name = $text{chapter}; + my $chapsec_name = "$text{chapter}.$text{section}"; + + if (defined $o{textbooks}) { + my $text_names = $textbooks{$text{author}}{$text{title}}{$text{edition}}; + if (defined $text_names) { + my %text_names = %$text_names; + + if (defined $text_names{$text{chapter}}) { + $chapter_name .= sissy_filename(" $text_names{$text{chapter}}") + } else { + warn "$file: no chapter name for $text{chapter}"; + } + + if (defined $text_names{"$text{chapter}.$text{section}"}) { + $chapsec_name .= sissy_filename(" $text_names{qq|$text{chapter}.$text{section}|}"); + } else { + warn "$file: no section name for $text{chapter}.$text{section}"; + } + } else { + warn "$file: can't find text $text{author}/$text{title}/$text{edition} in Textbooks file -- directories will be unnamed\n"; + } + } + + my $ex_name = "$text{chapter}.$text{section}.$text{problem}[0]"; + $ex_name .= '+' if @{$text{problem}} > 1; + + #print " chapter_name=$chapter_name\n"; + #print " chapsec_name=$chapsec_name\n"; + #print " ex_name=$ex_name\n"; + + # make sure path hasn't been seen before + my $dir = File::Spec->catdir($chapter_name, $chapsec_name); + my $partA = $ex_name; + $partA .= $o{suffix} if defined $o{suffix}; + my $partB = $ext; + my $uniq = unique_name($dir, $partA, $partB); + + #print " partA=$partA\n"; + #print " uniq=$uniq\n"; + #print " partB=$partB\n"; + + return $dir, "$partA$uniq$partB"; +} + +sub unique_name { + my ($dir, $partA, $partB) = @_; + my $whole = File::Spec->catpath(undef, $dir, "$partA$partB"); + my $uniq = ''; + if (exists $seen_paths{$whole}) { + my $i = 2; + do { + $uniq = "~$i"; + $whole = File::Spec->catpath(undef, $dir, "$partA$uniq$partB"); + } while (exists $seen_paths{$whole}); + } + $seen_paths{$whole} = (); + return $uniq; +} + +#sub find_macro_file { +# my ($name, $ref_by) = @_; +# my (undef,$ref_by_dir,undef) = File::Spec->splitpath($ref_by); +# my $ref_by_dir_rel = File::Spec->abs2rel($ref_by_dir, $o{'src-lib'}); +# my @dirs = File::Spec->splitdir($ref_by_dir_rel); +# while (1) { +# my $dir = File::Spec->catdir(@dirs); +# my $file = File::Spec->catfile(${'src-lib'}, $dir, $name); +# return $file if -f $file; +# pop @dirs; +# } +#} + +sub sissy_filename { + my $string = shift; + $string =~ s/:/-/g; + $string =~ s/[<>\"\/\/|?*]/_/g; + $string =~ s/\s+/_/g; + return $string; +} + +sub usage { + return "USAGE:\n" + . "$0 [OPTIONS] --dest-lib=PATH [--textbooks=PATH] [--suffix=STRING] files...\n" + . "$0 [OPTIONS] --dest-lib=PATH --orig-paths --src-lib=PATH files...\n" + . "Options:\n" + . "\t--stdin\n" + . "\t--pretend\n" + . "\t--verbose\n" + . "\t--check-names\n" + ; +} + +GetOptions(\%o, + 'textbooks=s', + 'dest-lib=s', + 'orig-paths', + 'src-lib=s', + 'stdin', + 'suffix=s', + 'pretend', + 'verbose', + 'check-names', + 'help', +); +main(@ARGV); |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:18:55
|
Log Message: ----------- adding missing files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/lib/WeBWorK: NPL.pm Revision Data ------------- --- /dev/null +++ lib/WeBWorK/NPL.pm @@ -0,0 +1,625 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/NPL.pm,v 1.2.2.1 2008/06/24 23:01:40 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +# +# Contributed by W.H. Freeman; Bedford, Freeman, and Worth Publishing Group. +################################################################################ + +package WeBWorK::NPL; +use base 'Exporter'; + +=head1 NAME + +WeBWorK::NPL - Parse formats used by the National Problem Library. + +=head1 SYNOPSIS + + use WeBWorK::NPL qw/read_textbooks read_tags format_tags gen_find_tags/; + + open TEXTS, "<", "Textbooks"; + my $textbooks = []; + read_textbooks(\*TEXTS, $textbooks); + + open PGFILE, "<", "file.pg"; + my $tags = {}; + read_tags(\*PGFILE, $tags); + + foreach my $string (format_tags($tags)) { + $string =~ s/^/## /gm; + print "TAG: $string\n"; + } + + use File::Find; + my $process = sub { print "Found: $_[0]\n" }; + my $wanted = gen_find_tags({author=>'Rogawski'}, $process); + find({wanted=>$wanted}, @ARGV); + +=head1 DESCRIPTION + +This package contains parsing routines for the various data formats associated +with the National Problem Library. + +=cut + +use strict; +use warnings; +use Data::Dumper; + +our @EXPORT_OK = qw( + read_textbooks + read_tags + format_tags + gen_find_tags +); + +our @global_fields = qw(DESCRIPTION KEYWORDS DBsubject DBchapter DBsection Date +Institution Author UsesAuxiliaryFiles); +our @textbook_fields = qw(title edition author chapter section problem); + +our %tag2field = ( TitleText => "title", EditionText => "edition", +AuthorText => "author", Section => "section", Problem => "problem", ); +our %field2tag = reverse %tag2field; + +=head1 FUNCTIONS + +=head2 read_textbooks + + read_textbooks($fh, $arrayref) + +Reads a Textbooks file opened for reading on $fh and appends its contents to +$arrayref. Each item appended to $arrayref is a reference to a hash containing +the following keys: + + _title The title of the textbook + _edition The edition of the textbook + _author The author of the textbook + 1 The name of chapter 1 + 1.1 The name of section 1.1 + 1.2 The name of section 1.2 + ... + 2 The name of chapter 2 + 2.1 The name of section 2.1 + ... + +Since the number of sections in a textbook is typically small, it is not terribly +inefficient to pull chapters or sections out: + + @chapters = grep { /^\d+$/ } keys %textbook; + @sections = grep { /^\d+\.\d+$/ } keys %textbook; + +=cut + +sub read_textbooks { + my ($fh, $result) = @_; + + my %curr_textbook; + + while (<$fh>) { + s/#.*$//g; + next unless /\S/; + s/^\s*//; + s/\s*$//; + + if (/^(TitleText|EditionText|AuthorText)\(\s*'(.*?)'\s*\)/) { + my $field = $tag2field{$1}; + my $value = $2; + if (exists $curr_textbook{"_$field"}) { + # repeated tag -- this is a new textbook + push @$result, {%curr_textbook}; + %curr_textbook = (); + } + $curr_textbook{"_$field"} = $value; + } elsif (/^(\d+)(?:\.(\d+))?\s*>>>\s*(.*)$/) { + my $chapter = $1; + my $section = $2; + my $name = $3; + if (defined $section and length $section > 0) { + $curr_textbook{"$chapter.$section"} = $name; + } else { + $curr_textbook{$chapter} = $name; + } + } + } + push @$result, {%curr_textbook}; +} + +=head2 read_tags + + read_tags($fh, $hashref, $extra_editing_info); + +Reads the NPL tags from a PG file opened for reading on $fh and stores the tags +in %$hashref. The following keys may be added to %$hashref: + + DESCRIPTION + KEYWORDS + DBsubject + DBchapter + DBsection + Date + Institution + Author + UsesAuxiliaryFiles (experimental, subject to change) + textbooks (arrayref) + +The value for the C<textbooks> key will be a reference to an array of textbook +hashes containing the textbook tags from the source file. In each textbook hash, +entries with empty values (e.g. C<TitleText1('')>) will be omitted. This is to +deal with the large number of empty-valued tags in the NPL. The keys of each +textbook hash will be among: + + title + edition + author + chapter + section + problem (arrayref) + +The value for the C<problem> key will be a reference to an array of problem +numbers. + +If $extra_editing_info is true, special hash items _pos, _rest, and _maxtextbook +will also be added to %$hashref. + +_pos will contain the position of the first byte of the next line after the last +tag in the file. _rest will contain the bytes of the "rest" of the file, after +all tags, starting at _pos. _maxtextbook will contain the highest number used to +identify a textbook in the file. (e.g. If TitleText1 and TitleText3 appear in +the file, there will only be two items in the textbooks array, but _maxtextbook +will be 3.) + +This is useful for appending tags to a file which contains existing tags, where +the new tags should appear immediately after the existing tags: + + open PGFILE, "+<", "file.pg"; + my $tags = {}; + read_tags(\*PGFILE, $tags, 1); + my $pos = $tags{_pos}; + my $rest = $tags{_rest}; + seek PGFILE, $pos, 0; + print PGFILE "## SomeNewTag('foo','bar')\n"; + print PGFILE $rest; + close PGFILE; + +=cut + +sub read_tags { + my ($file, $result, $extra_editing_info) = @_; + + my $fh; + if (ref $file) { + $fh = $file; + } elsif (defined $file and not ref $file) { + $fh = new IO::File($file, 'r'); + } + + my $pos; + my $rest = ''; + my $maxtextbook; + while (<$fh>) { + #if (0) { + if (/^(.*?\#.*?)(\s*)DESCRIPTION/) { + my $prefix = $1; + my $whitespace = $2; + my $description = ''; + while (<$fh>) { + if (/\#.*ENDDESCRIPTION/) { + chomp $description; + $result->{DESCRIPTION} = $description if length $description > 0; + last; + } else { + # handle prefix and whitespace separately so that we can still + # chop the prefix off even if people are being careless about + # whitespace. :P + s/^$prefix//; + s/^$whitespace//; + $description .= $_; + } + } + if ($extra_editing_info) { + $pos = tell $fh; + $rest = ''; + } + } elsif (/\#.*KEYWORDS\((.*)\)/) { + my $keywords = $1; + push @{$result->{KEYWORDS}}, parse_keywords($keywords); + if ($extra_editing_info) { + $pos = tell $fh; + $rest = ''; + } + } elsif (/\#.*(DBsubject|DBchapter|DBsection|Date|Institution|Author)\(\s*(.*?)\s*\)/) { + my $field = $1; + my $value = $2; + my ($parsed_value, $parse_errors) = parse_normal_value($field, $value); + if (@$parse_errors) { + warn "error while parsing value \"$value\" in field $field:\n" + . join('', @$parse_errors) + . "value may be incomplete. use with caution.\n" + . "(line $. of file $file)\n"; + } + $result->{$field} = $parsed_value; + if ($extra_editing_info) { + $pos = tell $fh; + $rest = ''; + } + } elsif (/\#.*(TitleText|EditionText|AuthorText|Section|Problem)(\d+)\(\s*'(.*?)'\s*\)/) { + my $field = $tag2field{$1}; + my $num = $2; + my $value = $3; + next unless $value =~ /\S/; + $value = [ parse_problems($value) ] if $field eq "problem"; + if ($field eq "section") { + my ($ch, $sec) = split /\./, $value; + $result->{textbooks}[$num]{chapter} = $ch; + $result->{textbooks}[$num]{section} = $sec if defined $sec and length $sec > 0; + } else { + $result->{textbooks}[$num]{$field} = $value; + } + if ($extra_editing_info) { + $pos = tell $fh; + $rest = ''; + $maxtextbook = $num if not defined $maxtextbook or $num > $maxtextbook; + } + } elsif (/\#.*(UsesAuxiliaryFiles)\(\s*(.*?)\s*\)/) { + my $field = $1; + my $value = $2; + my ($parsed_value, $parse_errors) = parse_normal_list($field, $value); + if (@$parse_errors) { + warn "error while parsing list value \"$value\" in field $field:\n" + . join('', @$parse_errors) + . "value may be incomplete. use with caution.\n" + . "(line $. of file $file)\n"; + } + $result->{$field} = $parsed_value; + if ($extra_editing_info) { + $pos = tell $fh; + $rest = ''; + } + } else { + if ($extra_editing_info) { + $rest .= $_; + } + } + } + + # remove holes in textbook numbering + @{$result->{textbooks}} = grep { defined } @{$result->{textbooks}}; + delete $result->{textbooks} unless @{$result->{textbooks}}; + + if ($extra_editing_info) { + $result->{_pos} = $pos; + $result->{_rest} = $rest; + $result->{_maxtextbook} = $maxtextbook; + } +} + +sub parse_normal_list { + my ($name, $string) = @_; + + use constant NRM=>0; + use constant STR=>1; + use constant ESC=>2; + use constant STP=>3; + my $state = NRM; + my @errors; + my @items; + my $curr_item = ''; + my $next_item = 0; + foreach my $i (0 .. length($string)-1) { + my $c = substr($string,$i,1); + #print "i=$i c=$c state=$state curr_item=$curr_item next_item=$next_item\n"; + # state changes + if ($state == NRM) { + if ($c eq "'") { + $state = STR; + } elsif ($c eq ',' or $c eq ' ') { + # do nothing -- closequote already consumed curr_item + } else { + push @errors, + "illegal char '$c' in state NRM while parsing value for $name.\n" + . " $string\n" + . ' ' . ' 'x$i . "^\n"; + $next_item = 1; + $state = STP; + } + } elsif ($state == STR) { + if ($c eq "'") { + $state = NRM; + $next_item = 1; + } elsif ($c eq '\\') { + $state = ESC; + } else { + $curr_item .= $c; + } + } elsif ($state == ESC) { + $curr_item .= $c; + $state = STR; + } elsif ($state == STP) { + last; + } else { + die "unexpected state $state while parsing value for $name.\n"; + } + #print "i=$i c=$c state=$state curr_item=$curr_item next_item=$next_item\n"; + # actions + if ($next_item) { + push @items, $curr_item; + $curr_item = ''; + $next_item = 0; + #print "stored item to list\n"; + } + } + + return \@items, \@errors; +} + +sub parse_normal_value { + my ($name, $string) = @_; + my ($items, $errors) = parse_normal_list($name, $string); + push @$errors, "only one item allowed in value for $name.\n" if @$items > 1; + return shift @$items, $errors; +} + +# this now works for keywords is embedded spaces (which are later stripped out +# by kwtidy) but now it doesn't work for values with double quotes or no quotes! +sub parse_keywords { + my $string = shift; + my ($items, $errors) = parse_normal_list('KEYWORDS', $string); + if (@$errors) { + warn "errors while parsing KEYWORDS list:\n@$errors\n" + . "Partially-parsed KEYWORDS: @$items\n" + . "Resorting to old-style KEYWORDS parsing...\n"; + @$items = split /(?:,|\s)+/, $string; + warn "Old-style parse result: ", join('|', @$items), "\n"; + } + return map { kwtidy($_) } @$items; +} + +sub kwtidy { + my $keyword = shift; + $keyword =~ s/\W//g; + $keyword =~ s/_//g; + return lc $keyword; +} + +sub parse_problems { + my $string = shift; + $string =~ s/\D/ /g; + return grep { /\S/ } split /\s+/, $string; +} + +=head2 format_tags + + format_tags($tags, $mintextbook); + +Given a reference to a hash of tags, return a list of strings representing said +tags. The strings do not begin with the standard NPL comment prefix ("## ") or +end with newlines. These must be added by the caller if the strings are to be +inserted into a PG source file. + +One complication is the DESCRIPTION field, which contains embedded newlines. If +a DESCRIPTION tag occurs in %$tags, it will be formatted with embedded newlines +but without a trailing newline. For example, after this code executes, + + $tags = { DESCRIPTION => "line one\nline two\nline three" }; + ($desc) = format_tags($tags); + +$desc will contain the string: + + "DESCRIPTION\nline one\nline two\nline three\nENDDESCRIPTION" + +To account for this when writing to a PG file, you could use: + + foreach my $string (format_tags($tags)) { + $string =~ s/^/## /gm; + print PGFILE "$string\n"; + } + +=cut + +sub format_tags { + my ($tags, $mintextbook) = @_; + $mintextbook ||= 1; + my @result; + my @ordered_fields = grep { exists $tags->{$_} } @global_fields, "textbooks"; + foreach my $field (@ordered_fields) { + my $value = $tags->{$field}; + if ($field eq "DESCRIPTION") { + push @result, format_description($value); + } elsif ($field eq "textbooks") { + push @result, format_textbooks($value, $mintextbook); + } else { + push @result, format_tag($field, $value); + } + } + return @result; +} + +sub format_tag { + my ($field, $value, $n) = @_; + my $tag = $field2tag{$field} || $field; + + # problems are always listed in a single string in the tag. + if ($field eq "problem") { + $value = format_problems($value); + } + + # if we have an arrayref, we represent it as multiple strings in one tag. + if (ref $value) { + $value = join(',', map { "'$_'" } @$value); + } elsif (defined $value) { + $value = "'$value'"; + } else { + warn "value is not defined for field $field!\n"; + $value = "''"; + } + + if (defined $n) { + return "$tag$n($value)"; + } else { + return "$tag($value)"; + } +} + +sub format_description { + my $value = shift; + return "DESCRIPTION\n$value\nENDDESCRIPTION"; +} + +sub format_textbooks { + my ($textbook, $n) = @_; + my @textbooks = @$textbook; + my @result; + foreach my $textbook (@textbooks) { + push @result, format_textbook($textbook, $n); + $n++; + } + return @result; +} + +sub format_textbook { + my ($textbook, $n) = @_; + + # combine chapter/section into single section tag + my $chapter = $textbook->{chapter}; + my $section = $textbook->{section}; + if (defined $chapter or defined $section) { + $section = ".$section" if defined $section; + $section = "$chapter$section" if defined $chapter; + delete $textbook->{chapter}; + $textbook->{section} = $section; + } + + my @result; + my @ordered_fields = grep { exists $textbook->{$_} } @textbook_fields; + foreach my $field (@ordered_fields) { + my $value = $textbook->{$field}; + push @result, format_tag($field, $value, $n); + } + return @result; +} + +sub format_problems { + my $first = shift; + my @problems; + if (ref $first) { + @problems = @$first; + } else { + @problems = ($first, @_); + } + + return join(',', @problems); +} + +=head2 gen_find_tags + + gen_find_tags($pattern, $action, $extra_editing_info); + +Generates an anonymous subroutine suitable for passing the the find() function +of the File::Find module. The no_chdir=>1 option must be passed to find() for +the generated subroutine to operate properly. + +$pattern is a reference to a hash describing the fields that must match. $action +is a reference to a subroutine that will be called if all fields match. +$extra_editing_info is passed to read_tags(). + +Legal fields for $pattern are as follows: + +B<Global fields:> DESCRIPTION, KEYWORDS, DBsubject, DBchapter, DBsection, Date, +Institution, Author. (The experimental UsesAuxiliaryFiles field may be supported +in the future.) + +B<Text-specific fields:> title, edition, author, chapter, section, problem. + +If multiple text-specific keys are given, then all must match for a single +textbook. + +$action is called as follows: + + $action->($path, $tags, $text_index) + +There $path the path to the matching file, $tags a reference to the tag hash for +the matching file, and $text_index the index into the @{$tags->{textbooks}} array +if $pattern included textbook-specific tags. + +=cut + +sub gen_find_tags { + my ($pattern, $action, $extra_editing_info) = @_; + return sub { + return unless /\.pg$/ and -f $File::Find::name; + + my $name = $File::Find::name; + #my $relpath = $name; + #$relpath =~ s/^$src\///; + + my %tags; + + open my $fh, "<", $name or do { + warn "skipping $name: $!\n"; + return; + }; + read_tags($fh, \%tags, $extra_editing_info); + close $fh; + + my (%global_pattern, %textbook_pattern); + foreach my $field (@global_fields) { + $global_pattern{$field} = $pattern->{$field} if exists $pattern->{$field}; + } + foreach my $field (@textbook_fields) { + $textbook_pattern{$field} = $pattern->{$field} if exists $pattern->{$field}; + } + + if (%global_pattern) { + return unless match_global(\%tags, \%global_pattern); + } + my $text_index; + if (%textbook_pattern) { + $text_index = match_textbook(\%tags, \%textbook_pattern); + return unless $text_index >= 0; + } + + $action->($name, \%tags, $text_index); + }; +} + +sub match_global { + my ($tags, $matches) = @_; + foreach my $field (keys %$matches) { + return 0 unless $tags->{$field} eq $matches->{$field}; + } + return 1; +} + +sub match_textbook { + my ($tags, $matches) = @_; + return -1 unless defined $tags->{textbooks}; + my @textbooks = @{$tags->{textbooks}}; + + #textbook: foreach my $textbook (@{$tags->{textbooks}}) { + textbook: foreach my $i (0 .. $#{$tags->{textbooks}}) { + my $textbook = $tags->{textbooks}[$i]; + foreach my $field (keys %$matches) { + next if $field !~ /^(title|edition|author|chapter|section|problem)$/; + next textbook unless $textbook->{$field} eq $matches->{$field}; + } + #warn "matched text i=$i: ", Dumper($textbook); + return $i; + } + return -1; +} + +=back + +=cut + +1; |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:18:18
|
Log Message: ----------- adding favicon icon Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/htdocs: favicon.ico Revision Data ------------- |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:17:50
|
Log Message: ----------- adding image files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/htdocs/images: navNextGrey.gif navPrevGrey.gif Revision Data ------------- |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:16:43
|
Log Message: ----------- fix formatting Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/lib/WeBWorK/DB: Record.pm Revision Data ------------- Index: Record.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Record.pm,v retrieving revision 1.12.4.1.2.1 retrieving revision 1.12.4.1.2.2 diff -Llib/WeBWorK/DB/Record.pm -Llib/WeBWorK/DB/Record.pm -u -r1.12.4.1.2.1 -r1.12.4.1.2.2 --- lib/WeBWorK/DB/Record.pm +++ lib/WeBWorK/DB/Record.pm @@ -1,3 +1,5 @@ +################################################################################ +# WeBWorK Online Homework Delivery System # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:15:52
|
Log Message: ----------- Adding missing files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/lib/WeBWorK/ContentGenerator: ProblemRenderer.pm Revision Data ------------- --- /dev/null +++ lib/WeBWorK/ContentGenerator/ProblemRenderer.pm @@ -0,0 +1,85 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/ProblemRenderer.pm,v 1.1.2.1 2008/06/24 22:58:37 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +package WeBWorK::ContentGenerator::ProblemRenderer; +use base qw(WeBWorK::ContentGenerator); + +=head1 NAME + +WeBWorK::ContentGenerator::ProblemRenderer - render a problem with a minimal +amount of UI garbage. + +=cut + +use strict; +use warnings; +use WeBWorK::CGI; +use WeBWorK::Utils::Tasks qw(renderProblems); + +sub pre_header_initialize { + my ($self) = @_; + my $r = $self->r; + + my $pg = $r->param('pg'); + my $file = $r->param('file'); + my $seed = $r->param('seed'); + my $mode = $r->param('mode'); + my $hint = $r->param('hint'); + my $sol = $r->param('sol'); + + die "must specify either a PG problems (param 'pg') or a path to a PG file (param 'file') and not both" + unless defined $pg and length $pg xor defined $file and length $file; + + my $problem = $self->get_problem($pg, $file); + my @options = (r=>$r, problem_list=>[\$pg]); + + #push @options, (problem_seed=>$seed) if defined $seed; + #push @options, (displayMode=>$mode) if defined $mode; + #push @options, (showHints=>$hint) if defined $hint; + #push @options, (showSolutions=>$sol) if defined $sol; + + ($self->{result}) = renderProblems(@options); +} + +sub get_problem { + my ($self, $pg, $file) = @_; + + if (defined $pg) { + return \$pg; + } else { + return $file; + } +} + +use Data::Dumper; +sub content { + my ($self) = @_; + my $result = $self->{result}; + my $dump = Dumper($result); + + print <<EOF; +<html> +<head> +<title>Yuck!</title> +</head> +<body> +<pre>$dump</pre> +</body> +</html> +EOF +} + +1; |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:12:55
|
Log Message: ----------- Adding missing files Tags: ---- rel-2-4-patches Added Files: ----------- webwork2/lib/WebworkSOAP: WSDL.pm Revision Data ------------- --- /dev/null +++ lib/WebworkSOAP/WSDL.pm @@ -0,0 +1,28 @@ +package WebworkSOAP::WSDL; + +use lib '/opt/webwork/webwork2/lib'; +use Pod::WSDL; +use WebworkSOAP; + +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); +use constant RPC_URL => 'http://localhost/webwork2_rpc'; + +sub handler($) { + my ($r) = @_; + my $pod = new Pod::WSDL( + source => 'WebworkSOAP', + location => RPC_URL, + pretty => 1, + withDocumentation => 0 + ); + #$r->content_type('application/wsdl+xml'); + if (MP2) { + #$r->send_http_header; + } else { + $r->send_http_header; + } + print($pod->WSDL); + return 0; +} + +1; |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 23:11:24
|
Log Message: ----------- forward port of changes in rel-2-4-5 Modified Files: -------------- webwork2: README webwork2/bin: remove_stale_images webwork2/conf: webwork.apache-config.dist webwork2/conf/templates/math: system.template webwork2/htdocs/helpFiles: InstructorScoring.html instructor_links.html Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.18 retrieving revision 1.19 diff -LREADME -LREADME -u -r1.18 -r1.19 --- README +++ README @@ -1,6 +1,7 @@ WeBWorK Online Homework Delivery System - Version 2.x + Version 2.4.x Copyright 2000-2007, The WeBWorK Project All rights reserved. + Index: remove_stale_images =================================================================== RCS file: /webwork/cvs/system/webwork2/bin/remove_stale_images,v retrieving revision 1.6 retrieving revision 1.7 diff -Lbin/remove_stale_images -Lbin/remove_stale_images -u -r1.6 -r1.7 --- bin/remove_stale_images +++ bin/remove_stale_images @@ -284,3 +284,4 @@ +1; \ No newline at end of file Index: webwork.apache-config.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/webwork.apache-config.dist,v retrieving revision 1.22 retrieving revision 1.23 diff -Lconf/webwork.apache-config.dist -Lconf/webwork.apache-config.dist -u -r1.22 -r1.23 --- conf/webwork.apache-config.dist +++ conf/webwork.apache-config.dist @@ -20,8 +20,14 @@ # # Include /path/to/webwork.apache-config # -# Customize the variable $webwork_dir below to match the location of your -# WeBWorK installation. +# Customize the variables below to match your WeBWorK installation. + +# Uncomment the ScriptAliasMatch to allow access to show-source.cgi +# This allows the "show source" button to work for demonstration "courses" +# See for example Davide Cervone's Knoxville lectures on math objects + +#ScriptAliasMatch /webwork2_course_files/([^/]*)/show-source.cgi/(.*) /opt/webwork/courses/$1/html/show-source.cgi/$2 + <Perl> @@ -94,16 +100,67 @@ $Location{$webwork_htdocs_url} = { SetHandler => "none" }; } -$ENV{WEBWORK_ROOT} = $webwork_dir; - -$PerlConfig .= <<EOF; +# The following stanzas can be uncommented to enable various experimental +# WeBWorK web services. These are still in testing and have not been audited +# for security. -########## SOAP installation ########## -# +# uncomment the line below if you use the XMLRPC, RQP, or SOAP installations below -# uncomment these three stanzas to use WeBWorK with Moodle +$ENV{WEBWORK_ROOT} = $webwork_dir; +#$PerlConfig .= <<EOF; +# ##### Sam's WeBWorK::Request-based XML-RPC testbed ##### +# # +# #PerlModule WeBWorK::RPC +# #<Location /webwork2_rpc> +# # SetHandler perl-script +# # PerlHandler Apache::XMLRPC::Lite +# # PerlSetVar dispatch_to "WeBWorK::RPC WeBWorK::RPC::CourseManagement" +# #</Location> +# +# ########## XMLRPC installation ########## +# # +# #PerlModule WebworkWebservice +# #<Location /mod_xmlrpc> +# # SetHandler perl-script +# # PerlHandler Apache::XMLRPC::Lite +# # PerlSetVar dispatch_to "WebworkXMLRPC" +# # PerlSetVar options "compress_threshold => 10000" +# # Order Allow,Deny +# # Allow from All +# #</Location> +# +# ########## RQP installation ########## +# # +# #PerlModule RQP +# ##<Location /rqp> +# ## SetHandler perl-script +# ## PerlHandler Apache::SOAP +# ## PerlSetVar dispatch_to "RQP" +# ## PerlSetVar options "compress_threshold => 10000" +# ## Order Allow,Deny +# ## Allow from All +# ##</Location> +# #<Location /rqp> +# # SetHandler perl-script +# # PerlHandler MySOAP +# # Order Allow,Deny +# # Allow from All +# #</Location> +# +# ########## SOAP installation ########## +# # +# #PerlModule WebworkWebservice +# #<Location /mod_soap> +# # SetHandler perl-script +# # PerlHandler Apache::SOAP +# # PerlSetVar dispatch_to "WebworkXMLRPC" +# # PerlSetVar options "compress_threshold => 10000" +# # Order Allow,Deny +# # Allow from All +# #</Location> +# # PerlModule WebworkSOAP # #WEBWORK SOAP CONFIGURATION @@ -125,10 +182,7 @@ # Order Allow,Deny # Allow from All # </Location> - -# end of WeBWorK -- Moodle stanzas - -EOF +#EOF </Perl> Index: system.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/math/system.template,v retrieving revision 1.8 retrieving revision 1.9 diff -Lconf/templates/math/system.template -Lconf/templates/math/system.template -u -r1.8 -r1.9 --- conf/templates/math/system.template +++ conf/templates/math/system.template @@ -29,19 +29,13 @@ <div id="masthead"> <div id="loginstatus"> <!--#loginstatus--> - <!--#if can="nav"--> - <div id="Nav"> - <!--#nav style="images" imageprefix="/webwork2_files/images/nav" imagesuffix=".gif" separator=" "--> - </div> - <!--#endif--> - </div> <div id="logo"> <img src="<!--#url type="webwork" name="htdocs"-->/images/webwork_rectangle.png" alt="WeBWorK" height="51" width="267" /> </div> </div> <hr class="for-broken-browsers"/> -<div id="big-wrapper" > +<div id="big-wrapper"> <div id="breadcrumbs"> <!--#path style="text" text=" → "--> </div> @@ -61,7 +55,14 @@ <!-- styles could be different for different pages so they are not set here --> <!--#info--> <!--#endif--> - <!--#if can="body"--> + + <!--#if can="nav"--> + <div class="Nav"> + <!--#nav style="images" imageprefix="/webwork2_files/images/nav" imagesuffix=".gif" separator=" "--> + </div> + <!--#endif--> + + <!--#if can="body"--> <!--#if warnings="1"--> <div class="Body" style="background-color:#ffcccc"> <p style="font-size:larger"> Index: instructor_links.html =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/helpFiles/instructor_links.html,v retrieving revision 1.6 retrieving revision 1.7 diff -Lhtdocs/helpFiles/instructor_links.html -Lhtdocs/helpFiles/instructor_links.html -u -r1.6 -r1.7 --- htdocs/helpFiles/instructor_links.html +++ htdocs/helpFiles/instructor_links.html @@ -30,6 +30,7 @@ Click the icon <a href="instructor_links.html"><img src="/webwork2_files/images/question_mark.png"></a> for page and item specific help. +<hr/> <dl> <dt>Instructor Tools</dt> <dd>Quick access to many instructor tools, including Index: InstructorScoring.html =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/helpFiles/InstructorScoring.html,v retrieving revision 1.3 retrieving revision 1.4 diff -Lhtdocs/helpFiles/InstructorScoring.html -Lhtdocs/helpFiles/InstructorScoring.html -u -r1.3 -r1.4 --- htdocs/helpFiles/InstructorScoring.html +++ htdocs/helpFiles/InstructorScoring.html @@ -21,8 +21,76 @@ </head> <body><br> -<p>The files created by this page contain all the scoring data in a comma delimited file. This file can easily be opened by a -spreadsheet program. To download the file after it's been created click on the link below, or go to the file transfer page -where scoring files may also be downloaded.</p> + +<p> +WeBWorK does not have a full featured scoring ability -- we leave that to your +favorite spread sheet application. +</p> + +<p> +What WeBWorK does have is good support for summarizing the scores on WeBWorK +homework sets and exporting them in a form (.csv) which any spreadsheet can use. +WeBWorK reports all of the homework grades, +creates a column which totals these grades and leaves it at that. +</p> + +<p> +Click on the sets you want scored (usually all of them :-) ) +choose the name of the export file you want to +use (by default: courseName_totals.csv) and then click the +"score selected sets" button. +</p> + +<p> +If you want a form that is easy to read on the web (for a quick look at grades) +click the "pad fields" option. This adds spaces the fields which makes the +columns easy to read when in text form but it can confuse +some spread sheet applications since the extra spaces violate the csv standard +(although Excel handles them with no problem). If you want a reliable .csv file +for use in any spreadsheet application unclick the "pad fields" option. +You can download the .csv file immediately by clicking on the link, or you +can download it using the "File Manager" from the scoring directory. +</p> + +<p> +I have no clue what "record scores for single sets" does -- sorry. +</p> + +<p> +The index is a number assigned on the basis of the number of incorrect attempts +(roughly equivalent to 1/the number of attempts) which seems to correlate with +the relative difficulty the student had with the problem. +</p> + +<p> +We would all like to be able to merge the spreadsheet with the webwork grades +and a spread sheet with the excel grades automatically. Unfortunately this +application has not yet been written for WeBWorK and for that matter it doesn't +appear to be an ability that Excel has either. The closes I have been able +to find is third party software for excel (e.g. http://www.synkronizer.com/e/tutorial_merging.html) +</p> + +<p> +To use the Email and spreadsheet merge feature, upload your spreadsheet with +calculated grades to the scoring directory using the File Manager link. +</p> + +<p> +Do NOT use the file name courseName_totals.csv, since you might accidentally +overwrite that if you again export your WeBWorK homework scores (actually the +earlier file is moved to courseName_totals_bak1.csv -- so you can recover +using the File Manager -- but it's still a pain). +</p> + +<p> +If you upload your file on the web with the name: <code>report_grades_data.csv</code> +and also create an email message with the name <code>report_grade.msg</code> with the +approriate <code>$COL</code> variables then not only can you email the message +with the embedded grades to the students, but files with those exact names are +automatically appended to the "Grades" page seen by the students. +</p> + + + </body> </html> |
From: Mike G. v. a. <we...@ma...> - 2008-06-24 22:10:12
|
Log Message: ----------- updating jsMath to 3.5 again Tags: ---- rel-2-4-patches Modified Files: -------------- webwork2/htdocs/jsMath/extensions: bbox.js boldsymbol.js font.js newcommand.js webwork2/htdocs/jsMath/plugins: CHMmode.js autoload.js noImageFonts.js tex2math.js webwork2/htdocs/jsMath/test: index-images.html sample.html webwork2/htdocs/jsMath/uncompressed: jsMath-fallback-mac.js jsMath.js Added Files: ----------- webwork2/htdocs/jsMath/extensions: AMSmath.js autobold.js verb.js Revision Data ------------- --- /dev/null +++ htdocs/jsMath/extensions/autobold.js @@ -0,0 +1,51 @@ +/* + * extensions/autobold.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file causes jsMath to use \boldsymbol{...} around mathematics + * that appears within <B>...</B> tags or has font-weight:bold applied + * via CSS rule. You can activate it by calling + * + * jsMath.Extension.Require('autobold'); + * + * once jsMath.js has been loaded, or by adding "extensions/autobold.js" + * to the loadFiles array in jsMath/easy/load.js. + * + * Note that you will need to install the cmmib10 and cmbsy10 fonts + * that are available from the jsMath extra font page at + * + * http://www.math.union.edu/locate/jsMath/download/extra-fonts/ + * + * to make this work in image mode. Note that there is no unicode + * fallback for these fonts at the moment. + * + * --------------------------------------------------------------------- + * + * Copyright 2008 by Davide P. Cervone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************************************************/ + +jsMath.Extension.Require("boldsymbol"); + +jsMath.Translate.OldParse = jsMath.Translate.Parse; +jsMath.Translate.Parse = function (style,text,noCache) { + if (jsMath.BBoxFor('</SPAN></SPAN>MMMMMMMMMM<SPAN><SPAN>').w > + jsMath.BBoxFor('</SPAN></SPAN><SPAN STYLE="font-weight:normal">MMMMMMMMMM</SPAN><SPAN><SPAN>').w) { + text = '\\boldsymbol{' + text + '}'; + } + return jsMath.Translate.OldParse(style,text,noCache); +} Index: boldsymbol.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/extensions/boldsymbol.js,v retrieving revision 1.1 retrieving revision 1.1.8.1 diff -Lhtdocs/jsMath/extensions/boldsymbol.js -Lhtdocs/jsMath/extensions/boldsymbol.js -u -r1.1 -r1.1.8.1 --- htdocs/jsMath/extensions/boldsymbol.js +++ htdocs/jsMath/extensions/boldsymbol.js @@ -25,7 +25,7 @@ * * --------------------------------------------------------------------- * - * Copyright 2006 by Davide P. Cervone + * Copyright 2006-2007 by Davide P. Cervone * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,12 +50,13 @@ * Implement \boldsymbol{...} */ BoldSymbol: function (name) { - var fam = jsMath.TeX.fam + var fam = jsMath.TeX.fam; var restart = 0; var oldfam = [fam[0],fam[1],fam[2]]; fam[0] = "cmbx10"; fam[1] = "cmmib10"; fam[2] = "cmbsy10"; - var box = this.ProcessArg(this.cmd+name); + try{var box = this.ProcessArg(this.cmd+name)} + catch (e) {restart = (e == "restart")} fam[0] = oldfam[0]; fam[1] = oldfam[1]; fam[2] = oldfam[2]; - if (this.error) return; + if (this.error) return; if (restart) {throw "restart"} this.mlist.Add(jsMath.mItem.Atom('ord',box)); } Index: bbox.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/extensions/bbox.js,v retrieving revision 1.2 retrieving revision 1.2.4.1 diff -Lhtdocs/jsMath/extensions/bbox.js -Lhtdocs/jsMath/extensions/bbox.js -u -r1.2 -r1.2.4.1 --- htdocs/jsMath/extensions/bbox.js +++ htdocs/jsMath/extensions/bbox.js @@ -58,16 +58,58 @@ }); +jsMath.Add(jsMath.mList.prototype.Atomize,{ + /* + * Creates the box HTML + */ + bbox: function (style,size,mitem,prev,mlist) { + var box = jsMath.Box.Set(mitem.nuc2,style,size,1).Remeasured(); + delete mitem.nuc2; + /* + * If the box has super- or subscripts, move them + * to the contained item if is in a big operator + * (does anything else need this?) + */ + if (mitem.sup || mitem.sub) { + if (mitem.nuc.type == 'mlist' && mitem.nuc.mlist.Length() == 1) { + var atom = mitem.nuc.mlist.Last(); + if (atom.atom && atom.type == 'op' && !atom.sup && !atom.sub) { + if (mitem.sup) {atom.sup = mitem.sup; delete mitem.sup} + if (mitem.sub) {atom.sub = mitem.sub; delete mitem.sub} + } + } + } + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + var nuc = mitem.nuc; nuc.Styled(); var pad = mitem.pad; + if (pad) {box.w += 2*pad; box.h += pad; box.d += pad; nuc.w += pad} + if (jsMath.Browser.msieCenterBugFix) + {nuc.html = '<span style="position:relative">'+nuc.html+'</span>'} + nuc.html = + jsMath.HTML.BBox(box.w,box.h,box.d,mitem.color,mitem.style) + + jsMath.HTML.Spacer(pad-box.w) + + nuc.html; + if (pad && nuc.w < box.w) { + nuc.html += jsMath.HTML.Spacer(box.w-nuc.w); + nuc.w = box.w; + } + nuc.h = Math.max(nuc.h,box.h); nuc.d = Math.max(nuc.d,box.d); + nuc.bh = Math.max(nuc.bh,box.h); nuc.bd = Math.max(nuc.bd,box.d); + mitem.type = 'ord'; + } +}); + jsMath.Package(jsMath.Parser,{ macros: {bbox: 'BBox'}, - + /* * Implement \bbox[...]{...} */ BBox: function (name) { var extra = this.GetBrackets(this.cmd+name); if (this.error) return; - var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + var arg = this.GetArgument(this.cmd+name); if (this.error) return; + var nuc = this.Process(arg); if (this.error) return; + var nuc2 = this.Process(arg); // need a second copy since Box.Set changes the list var color; var pad = 0; var style = ''; if (extra != '') { var parts = extra.split(/,/); @@ -78,15 +120,9 @@ else {color = parts[i]} } } - var box = jsMath.Box.Set(arg,this.mlist.data.style,this.mlist.data.size,1).Remeasured(); - var frame = jsMath.HTML.BBox(box.w+2*pad,box.h+pad,box.d+pad,color,style); - if (jsMath.Browser.msieCenterBugFix) - {box.html = '<span style="position:relative">'+box.html+'</span>'} - box.html = frame + jsMath.HTML.Spacer(-box.w-pad) + box.html; - if (pad) {box.html += jsMath.HTML.Spacer(pad)} - box.w += 2*pad; box.h += pad; box.d += pad; - box.bh = Math.max(box.bh,box.h); box.bd = Math.max(box.bd,box.d); - this.mlist.Add(jsMath.mItem.Atom('ord',box)); + this.mlist.Add(new jsMath.mItem('bbox',{ + nuc: nuc, nuc2: nuc2, atom: 1, pad: pad, color: color, style: style + })); } - + }); --- /dev/null +++ htdocs/jsMath/extensions/AMSmath.js @@ -0,0 +1,293 @@ +/* + * extensions/AMSmath.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file defines most of the macros and environments from + * the amsmath LaTeX package. You can activate it by calling + * + * jsMath.Extension.Require('AMSmath'); + * + * once jsMath.js has been loaded, or by adding "extensions/AMSmath.js" + * to the loadFiles array in jsMath/easy/load.js. + * + * You may wish to load AMSsymbols.js as well, but note that it + * requires the extra msam10 and msb10 fonts that you will have + * to install on your server first. + * + * --------------------------------------------------------------------- + * + * Copyright 2007 by Davide P. Cervone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************************************************/ + +jsMath.Extension.Require("moreArrows"); + +jsMath.Package(jsMath.Parser,{ + macros: { + intI: ['Macro','\\mathchoice{\\!}{}{}{}\\!\\!\\int'], + iint: ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI}'], + iiint: ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI\\intI}'], + iiiint: ['Macro','\\!\\!\\!\\mathop{\\,\\,\\,\\int\\intI\\intI\\intI}'], + idotsint: ['Macro','\\!\\!\\mathop{\\,\\,\\int\\cdots\\int}'], + + dddot: ['Macro','\\mathop{#1}\\limits^{\\textstyle ...}',1], + ddddot: ['Macro','\\mathop{#1}\\limits^{\\textstyle ....}',1], + + sideset: ['Macro','\\mathop{\\rlap{\\phantom{#3}}}#1\\!{#3}#2',3], + stackrel: ['Macro','\\mathrel{\\mathop{#2}\\limits^{#1}}',2], + + boxed: ['Macro','\\fbox{$\\displaystyle{#1}$}',1], + + tag: 'HandleTag', + notag: ['Macro',''], + + substack: ['Macro','\\begin{subarray}{c}#1\\end{subarray}',1], + + varliminf: ['Macro','\\mathop{\\underline{\\raise1.5pt{\\rule{0pt}{.6em}{0pt}\\smash{\\lower1.5pt{\\rm lim}}}}}'], + varlimsup: ['Macro','\\mathop{\\overline{\\rule{0pt}{.6em}{0pt}\\smash{\\rm lim}}}'], + varinjlim: ['Macro','\\mathop{\\underrightarrow{\\rm lim}}'], + varprojlim: ['Macro','\\mathop{\\underleftarrow{\\rm lim}}'], + + DeclareMathOperator: 'HandleDeclareOp', + operatorname: 'HandleOperatorName', + + genfrac: 'Genfrac', + frac: ['Genfrac',"","","",""], + tfrac: ['Genfrac',"","","","1"], + dfrac: ['Genfrac',"","","","0"], + binom: ['Genfrac',"(",")","0pt",""], + tbinom: ['Genfrac',"(",")","0pt","1"], + dbinom: ['Genfrac',"(",")","0pt","0"], + + cfrac: 'CFrac', + + shoveleft: ['HandleShove','left'], + shoveright: ['HandleShove','right'] + }, + + environments: { + align: ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'], + 'align*': ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'], + aligned: ['Array',null,null,'rlrlrlrlrlrl',[5/18,2,5/18,2,5/18,2,5/18,2,5/18,2,5/18],1,'D'], + multline: 'Multline', + 'multline*': 'Multline', + split: ['Array',null,null,'rl',[5/18],1,'D'], + gather: ['Array',null,null,'c',null,1,'D'], + 'gather*': ['Array',null,null,'c',null,1,'D'], + gathered: ['Array',null,null,'c',null,1,'D'], + subarray: ['Array',null,null,null,[0,0,0,0],1,'S',0,.25], + smallmatrix: ['Array',null,null,'cccccccccc',[1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3,1/3],1,'S',0] + }, + + delimiter: { + '\\lvert': [4,2,0x6A,3,0x0C], + '\\rvert': [5,2,0x6A,3,0x0C], + '\\lVert': [4,2,0x6B,3,0x0D], + '\\rVert': [5,2,0x6B,3,0x0D] + }, + + /* + * Ignore the tag for now + */ + HandleTag: function (name) { + var arg = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return; + if (arg == "*") this.GetArgument(this.cmd+name); + }, + + /* + * Handle \DeclareMathOperator + */ + HandleDeclareOp: function (name) { + var limits = ""; + var cs = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return; + if (cs == "*") { + limits = "\\limits"; + cs = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return; + } + if (cs.charAt(0) == "\\") {cs = cs.substr(1)} + var op = this.GetArgument(this.cmd+name); if (this.error) return; + op = op.replace(/\*/g,'\\char{cmr10}{0x2A}').replace(/-/g,'\\char{cmr10}{0x2D}'); + jsMath.Parser.prototype.macros[cs] = ['Macro','\\mathop{\\rm '+op+'}'+limits]; + }, + + HandleOperatorName: function (name) { + var limits = ""; + var op = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return; + if (op == "*") { + limits = "\\limits"; + op = this.trimSpaces(this.GetArgument(this.cmd+name)); if (this.error) return; + } + op = op.replace(/\*/g,'\\char{cmr10}{0x2A}').replace(/-/g,'\\char{cmr10}{0x2D}'); + this.string = '\\mathop{\\rm '+op+'}'+limits+this.string.slice(this.i); + this.i = 0; + }, + + /* + * Record presence of \shoveleft and \shoveright + */ + HandleShove: function (name,data) { + if (this.mlist.data.entry == null) {this.mlist.data.entry = {}} + this.mlist.data.entry.shove = data[0]; + }, + + /* + * Handle \cfrac + */ + CFrac: function (name) { + var lr = this.GetBrackets(this.cmd+name); if (this.error) return; + var num = this.GetArgument(this.cmd+name); if (this.error) return; + var den = this.GetArgument(this.cmd+name); if (this.error) return; + + num = this.Process('\\strut\\textstyle{'+num+'}'); if (this.error) return; + den = this.Process('\\strut\\textstyle{'+den+'}'); if (this.error) return; + var data = this.mlist.data; + var TeX = jsMath.Typeset.TeX(data.style,data.size); + + if (lr != "") { + if (lr != 'l' && lr != 'r') {this.Error("Illegal alignment specified in "+this.cmd+name); return} + num = jsMath.Box.Set(num,data.style,data.size); + den = jsMath.Box.Set(den,data.style,data.size); + if (num.w > den.w) { + if (lr == 'l') {den.html += jsMath.HTML.Spacer(num.w-den.w)} + else {den.html = jsMath.HTML.Spacer(num.w-den.w) + den.html} + den.w = num.w; + } else if (num.w < den.w) { + if (lr == 'l') {num.html += jsMath.HTML.Spacer(den.w-num.w)} + else {num.html = jsMath.HTML.Spacer(den.w-num.w) + num.html} + num.w = den.w; + } + } + + this.mlist.Add(jsMath.mItem.Fraction(name,num,den,TeX.default_rule_thickness)); + }, + + /* + * Implement AMS generalized fraction + */ + Genfrac: function (name,data) { + var left = data[0]; var right = data[1]; + var thickness = data[2]; var style = data[3]; + + if (left != null) {left = this.delimiter[left]} else + {left = this.GetDelimiterArg(this.cmd+name); if (this.error) return} + if (right != null) {right = this.delimiter[right]} else + {right = this.GetDelimiterArg(this.cmd+name); if (this.error) return} + if (thickness == null) {thickness = this.GetArgument(this.cmd+name); if (this.error) return} + if (style == null) {style = this.GetArgument(this.cmd+name); if (this.error) return} + + var num = this.ProcessArg(this.cmd+name); if (this.error) return; + var den = this.ProcessArg(this.cmd+name); if (this.error) return; + + if (left == "") {left = null}; if (right == "") {right = null} + if (thickness == "") { + var TeX =jsMath.Typeset.TeX(this.mlist.data.style,this.mlist.data.size); + thickness = TeX.default_rule_thickness; + } else { + thickness = this.ParseDimen(thickness,this.cmd+name,0,0); + } + + var frac = jsMath.mItem.Fraction(name,num,den,thickness,left,right); + + if (style != "") { + style = (["D","T","S","SS"])[style]; + if (style == null) {this.Error("Bad math style for "+this.cmd+name); return} + var mlist = new jsMath.mList([new jsMath.mItem('style',{style:style}),frac]); + this.mlist.Add(jsMath.mItem.Atom('inner',{type:'mlist',mlist: mlist})); + } else { + this.mlist.Add(frac); + } + }, + + /* + * Implements the multline environment + */ + Multline: function (name,delim) { + var data = this.mlist.data; + var width = this.GetBrackets(this.cmd+'begin{'+name+'}'); if (this.error) return; + var arg = this.GetEnd(name); if (this.error) return; + + var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,'D'); + parse.matrix = name; parse.row = []; parse.table = []; parse.rspacing = []; + parse.Parse(); if (parse.error) {this.Error(parse); return} + parse.HandleRow(name,1); // be sure the last row is recorded + + // + // check rows for extra columns and maximum width + // + var i; var row; var W = 0; + for (i = 0; i < parse.table.length; i++) { + row = parse.table[i]; + if (row.length > 1) { + this.Error("Rows can contain only one equation in '"+name+"' environment"); + return; + } + if (row[0].w > W) {W = row[0].w} + } + + // + // Determine width of display + // + if (width == "") {width = W+2} else { + width = this.ParseDimen(width,name,0,0); + if (width < W) {width = W} + } + + // + // Shove the top and bottom lines + // + if (parse.table.length > 1) { + parse.table[0][0].entry.shove = 'left'; + row = parse.table[parse.table.length-1]; + if (!row[0].entry.shove) {row[0].entry.shove = 'right'} + } + // + // Adjust widths of shoved lines + // + for (i = 0; i < parse.table.length; i++) { + row = parse.table[i][0]; + if (row.entry.shove && row.w < width) { + switch (row.entry.shove) { + case 'left': + row.html += jsMath.HTML.Spacer(width-row.w); + break; + + case 'right': + row.html = jsMath.HTML.Spacer(width-row.w)+row.html; + break; + } + row.w = width; + } + } + + // + // Do the layout + // + var box = jsMath.Box.Layout(data.size,parse.table); + this.mlist.Add(jsMath.mItem.Atom('ord',box)); + }, + + /* + * Get a delimiter or empty argument + */ + GetDelimiterArg: function (name) { + var c = this.trimSpaces(this.GetArgument(name)); if (this.error) return null; + if (c == "") return null; + if (this.delimiter[c]) return this.delimiter[c]; + this.Error("Missing or unrecognized delimiter for "+name); + return null; + } +}); \ No newline at end of file Index: newcommand.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/extensions/newcommand.js,v retrieving revision 1.1.6.1 retrieving revision 1.1.6.1.2.1 diff -Lhtdocs/jsMath/extensions/newcommand.js -Lhtdocs/jsMath/extensions/newcommand.js -u -r1.1.6.1 -r1.1.6.1.2.1 --- htdocs/jsMath/extensions/newcommand.js +++ htdocs/jsMath/extensions/newcommand.js @@ -44,7 +44,7 @@ var def = this.GetArgument(this.cmd+name); if (this.error) return; if (n == '') {n = null} if (cs.charAt(0) == this.cmd) {cs = cs.substr(1)} - if (!cs.match(/^(.|[a-z]+)$/)) {this.Error("Illegal control sequence name for "+this.cmd+name); return} + if (!cs.match(/^(.|[a-z]+)$/i)) {this.Error("Illegal control sequence name for "+this.cmd+name); return} if (n != null && !n.match(/^[0-9]+$/)) {this.Error("Illegal number of parameters specified in "+this.cmd+name); return} jsMath.Parser.prototype.macros[cs] = ['Macro',def,n]; }, Index: font.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/extensions/font.js,v retrieving revision 1.1 retrieving revision 1.1.8.1 diff -Lhtdocs/jsMath/extensions/font.js -Lhtdocs/jsMath/extensions/font.js -u -r1.1 -r1.1.8.1 --- htdocs/jsMath/extensions/font.js +++ htdocs/jsMath/extensions/font.js @@ -1,6 +1,7 @@ jsMath.Package(jsMath.Parser,{ macros: {font: 'Font'}, + fontCS: {}, /* * Get a CS name or give an error @@ -23,10 +24,11 @@ var font = this.string.slice(this.i).match(/^[a-z]+[0-9]+/i); if (font) { this.i += (new String(font)).length; - if (jsMath.TeX.famName[font]) { + if (jsMath.TeX.famName[font] != null) { this.macros[cs] = ['HandleFont',jsMath.TeX.famName[font]]; } else { - this.macros[cs] = ['Extension',jsMath.Font.URL(font)]; + this.macros[cs] = ['Extension',jsMath.Font.URL(font),"fontCS"]; + this.fontCS[cs] = 1; // so Extension has something to delete } } else {this.Error("Missing font name")} } else {this.Error("Missing font definition")} --- /dev/null +++ htdocs/jsMath/extensions/verb.js @@ -0,0 +1,58 @@ +/* + * extensions/verb.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file implements the \verb macro. You can activate it + * by calling + * + * jsMath.Extension.Macro('verb'); + * + * which will cause the extension to be loaded only when it is + * needed, or you can force it to be loaded via + * + * jsMath.Extension.Require('verb'); + * + * once jsMath.js has been loaded, or by adding "extensions/verb.js" + * to the loadFiles array in the easy/load.js file. + * + * --------------------------------------------------------------------- + * + * Copyright 2008 by Davide P. Cervone + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************************************************/ + +jsMath.Package(jsMath.Parser,{ + + macros: {verb: 'Verb'}, + + /* + * Implement \verb|...| + */ + Verb: function (name) { + var c = this.GetNext(); var start = ++this.i; + if (c == "" ) {this.Error(this.cmd+name+" requires an argument"); return} + while (this.i < this.string.length && this.string.charAt(this.i) != c) {this.i++} + if (this.i == this.string.length) + {this.Error("Can't find closing delimiter for "+this.cmd+name); return} + var text = this.string.slice(start,this.i); this.i++; + text = text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + text = '<span style="font-family:monospace">'+text+'</span>'; + var box = jsMath.Box.Text(text,'normal','T',this.mlist.data.size).Styled(); + box.h = box.bh+box.bd -jsMath.d; box.d = jsMath.d; + this.mlist.Add(jsMath.mItem.Typeset(box)); + } +}); Index: noImageFonts.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/plugins/noImageFonts.js,v retrieving revision 1.4 retrieving revision 1.4.6.1 diff -Lhtdocs/jsMath/plugins/noImageFonts.js -Lhtdocs/jsMath/plugins/noImageFonts.js -u -r1.4 -r1.4.6.1 --- htdocs/jsMath/plugins/noImageFonts.js +++ htdocs/jsMath/plugins/noImageFonts.js @@ -24,4 +24,12 @@ */ if (!window.jsMath) {window.jsMath = {}} -window.jsMath.noImgFonts = 1; +jsMath.noImgFonts = 1; + +if (!jsMath.Font) {jsMath.Font = {}} +if (!jsMath.Font.extra_message) { + jsMath.Font.extra_message = + 'Extra TeX fonts not found: <b><span id="jsMath_ExtraFonts"></span></b><br/>' + + 'Using unicode fonts instead. This may be slow and might not print well.<br/>\n' + + 'Use the jsMath control panel to get additional information.'; +} \ No newline at end of file Index: autoload.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/plugins/autoload.js,v retrieving revision 1.7 retrieving revision 1.7.4.1 diff -Lhtdocs/jsMath/plugins/autoload.js -Lhtdocs/jsMath/plugins/autoload.js -u -r1.7 -r1.7.4.1 --- htdocs/jsMath/plugins/autoload.js +++ htdocs/jsMath/plugins/autoload.js @@ -95,12 +95,34 @@ request: null, // XMLHttpRequest object (if we can get it) iframe: null, // the hidden iframe (if not) + operaXMLHttpRequestBug: (window.opera != null), // is Opera browser /* * Get XMLHttpRequest object, if possible, and look up the URL root + * (MSIE can't use xmlReuest to load local files, so avoid that) */ Init: function () { - if (window.XMLHttpRequest) {try {this.request = new XMLHttpRequest} catch (err) {}} + this.Root(); + if (window.XMLHttpRequest) { + try {this.request = new XMLHttpRequest} catch (err) {} + // MSIE and FireFox3 can't use xmlRequest on local files, + // but we don't have jsMath.browser yet to tell, so use this check + if (this.request && window.location.protocol == "file:") { + try { + this.request.open("GET",jsMath.Autoload.root+"plugins/autoload.js",false); + this.request.send(null); + } catch (err) { + this.request = null; + // Firefox3 has window.postMessage for inter-window communication. + // It can be used to handle the new file:// security model, + // so set up the listener. + if (window.postMessage) { + this.mustPost = 1; + window.addEventListener("message",jsMath.Autoload.Post.Listener,false); + } + } + } + } if (!this.request && window.ActiveXObject) { var xml = ["MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP","Microsoft.XMLHTTP"]; @@ -108,14 +130,13 @@ try {this.request = new ActiveXObject(xml[i])} catch (err) {} } } - this.Root(); }, /* * Load an external JavaScript file */ Load: function (url) { - if (this.request) { + if (this.request && !(this.operaXMLHttpRequestBug && url == 'jsMath.js')) { setTimeout(function () {jsMath.Autoload.Script.xmlLoad(url)},1); } else { this.startLoad(url); @@ -167,12 +188,16 @@ * the issue, but that's the only time I see it). */ setURL: function () { - var url = jsMath.Autoload.root+"jsMath-autoload.html"; - var doc = this.iframe.contentDocument; - if (!doc && this.iframe.contentWindow) {doc = this.iframe.contentWindow.document} - if (navigator.vendor == "Apple Computer, Inc." && - document.location.protocol == 'file:') {doc = null} - if (doc) {doc.location.replace(url)} else {this.iframe.src = url} + if (this.mustPost) { + this.iframe.src = jsMath.Autoload.Post.startLoad(this.url,this.iframe); + } else { + var url = jsMath.Autoload.root+"jsMath-autoload.html"; + var doc = this.iframe.contentDocument; + if (!doc && this.iframe.contentWindow) {doc = this.iframe.contentWindow.document} + if (navigator.vendor == "Apple Computer, Inc." && + document.location.protocol == 'file:') {doc = null} + if (doc) {doc.location.replace(url)} else {this.iframe.src = url} + } }, /* @@ -202,7 +227,7 @@ if (script) { for (var i = 0; i < script.length; i++) { var src = script[i].src; - if (src && src.match('(^|/)plugins/autoload.js$')) { + if (src && src.match('(^|/|\\\\)plugins/autoload.js$')) { jsMath.Autoload.root = src.replace(/plugins\/autoload.js$/,''); break; } @@ -212,6 +237,44 @@ }, + /* + * Handle window.postMessage() events in Firefox3 + */ + Post: { + window: null, // iframe we are listening to + + Listener: function (event) { + if (event.source != jsMath.Autoload.Post.window) return; + var domain = event.origin; var ddomain = document.domain + if (domain == null || domain == "") {domain = "localhost"} + if (ddomain == null || ddomain == "") {ddomain = "localhost"} + if (domain != ddomain || event.data.substr(0,6) != "jsMAL:") return; + var type = event.data.substr(6,3).replace(/ /g,''); + var message = event.data.substr(10); + if (jsMath.Autoload.Post.Commands[type]) (jsMath.Autoload.Post.Commands[type])(message); + // cancel event? + }, + + /* + * Commands that can be performed by the listener + */ + Commands: { + SCR: function (message) {window.eval(message)}, + ERR: function (message) {jsMath.Autoload.Script.endLoad()}, + END: function (message) {jsMath.Autoload.Script.endLoad()} + }, + + startLoad: function (url,iframe) { + this.window = iframe.contentWindow; + return jsMath.Autoload.root+"jsMath-loader-post.html?autoload="+url; + }, + + endLoad: function () { + this.window = null; + } + }, + + /**************************************************************/ /* @@ -312,8 +375,10 @@ * and then do any pending commands. */ LoadJsMath: function () { + if (this.loading) return; if (jsMath.loaded) {this.afterLoad(); return} if (this.root) { + this.loading = 1; this.setMessage('Loading jsMath...'); this.Script.AfterLoad = this.afterLoad; this.Script.Load('jsMath.js'); @@ -322,20 +387,21 @@ } }, afterLoad: function () { - if (jsMath.tex2math.window) {jsMath.tex2math.window.jsMath = jsMath} + jsMath.Autoload.loading = 0; // // Handle MSIE bug where jsMath.window both is and is not the actual window // + if (jsMath.tex2math.window) {jsMath.tex2math.window.jsMath = jsMath} if (jsMath.browser == 'MSIE') {window.onscroll = jsMath.window.onscroll}; var fonts = jsMath.Autoload.loadFonts; if (fonts) { if (typeof(fonts) != 'object') {fonts = [fonts]} - for (var i in fonts) {jsMath.Font.Load(fonts[i])} + for (var i = 0; i < fonts.length; i++) {jsMath.Font.Load(fonts[i])} } var files = jsMath.Autoload.loadFiles; if (files) { if (typeof(files) != 'object') {files = [files]} - for (var i in files) {jsMath.Setup.Script(files[i])} + for (var i = 0; i < files.length; i++) {jsMath.Setup.Script(files[i])} } jsMath.Synchronize(function () {jsMath.Autoload.Script.RunStack()}); jsMath.Autoload.setMessage(); @@ -350,7 +416,7 @@ if (!document.body.hasChildNodes) {document.body.appendChild(this.div)} else {document.body.insertBefore(this.div,document.body.firstChild)} var style = { - position:'absolute', bottom:'1px', left:'2px', + position:'fixed', bottom:'1px', left:'2px', backgroundColor:'#E6E6E6', border:'solid 1px #959595', margin:'0px', padding:'1px 8px', zIndex:102, color:'black', fontSize:'75%', width:'auto' @@ -391,4 +457,4 @@ jsMath.Autoload.Script.Init(); jsMath.Autoload.InitStubs(); -if (document.body) {jsMath.Autoload.Check()} +if (document.body && !jsMath.Autoload.delayCheck) {jsMath.Autoload.Check()} Index: CHMmode.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/plugins/CHMmode.js,v retrieving revision 1.3 retrieving revision 1.3.4.1 diff -Lhtdocs/jsMath/plugins/CHMmode.js -Lhtdocs/jsMath/plugins/CHMmode.js -u -r1.3 -r1.3.4.1 --- htdocs/jsMath/plugins/CHMmode.js +++ htdocs/jsMath/plugins/CHMmode.js @@ -45,7 +45,7 @@ * to load the controls file, so fake it using XMLHttpRequest. * Load the data into a DIV instead of an IFRAME, and make sure * that the styles are correct for it. Change the GetPanel() - * call to get the hide the other panel and open the cirrect one. + * call to hide the other panel and open the correct one. */ jsMath.Controls.Init = function () { Index: tex2math.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/plugins/tex2math.js,v retrieving revision 1.11 retrieving revision 1.11.4.1 diff -Lhtdocs/jsMath/plugins/tex2math.js -Lhtdocs/jsMath/plugins/tex2math.js -u -r1.11 -r1.11.4.1 --- htdocs/jsMath/plugins/tex2math.js +++ htdocs/jsMath/plugins/tex2math.js @@ -10,7 +10,7 @@ * * --------------------------------------------------------------------- * - * Copyright 2004-2006 by Davide P. Cervone + * Copyright 2004-2007 by Davide P. Cervone * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ this.Convert(element,{ processSingleDollars: 1, processDoubleDollars: 1, processSlashParens: 1, processSlashBrackets: 1, + processLaTeXenvironments: 0, custom: 0, fixEscapedDollars: 1 }); }, @@ -52,6 +53,7 @@ this.Convert(element,{ processSingleDollars: 0, processDoubleDollars: 1, processSlashParens: 1, processSlashBrackets: 1, + processLaTeXenvironments: 0, custom: 0, fixEscapedDollars: 0 }); }, @@ -60,6 +62,7 @@ this.Convert(element,{ processSingleDollars: 0, processDoubleDollars: 0, processSlashParens: 1, processSlashBrackets: 1, + processLaTeXenvironments: 1, custom: 0, fixEscapedDollars: 0 }); }, @@ -133,7 +136,7 @@ } if (this.processDoubleDollars || this.processSingleDollars || this.processSlashParens || this.processSlashBrackets || - this.custom) this.ScanElement(element); + this.processLaTeXenvironments || this.custom) this.ScanElement(element); } }, @@ -194,8 +197,8 @@ stdProcessMatch: function (match,index,element) { if (match == this.search.end) { this.search.close = element; - this.search.clength = match.length; this.search.cpos = this.pattern.lastIndex; + this.search.clength = (match.substr(0,4) == '\\end' ? 0 : match.length); element = this.EncloseMath(element); } else { switch (match) { @@ -235,6 +238,14 @@ this.pattern.lastIndex--; } break; + + default: + if (match.substr(0,6) == '\\begin' && this.search.end == null && + this.processLaTeXenvironments) { + this.ScanMark('div',element,'\\end'+match.substr(6)); + this.search.olength = 0; + } + break; } } return element; @@ -357,9 +368,7 @@ if (this.inited || !jsMath.browser) return; /* * MSIE can't handle the DIV's properly, so we need to do it by - * hand. Look up the style for typeset math to see if the user - * has changed it, and get whether it is centered or indented - * so we can mirror that using a SPAN + * hand. Use an extra SPAN that uses CSS to act like a DIV. */ if (jsMath.browser == 'MSIE' && jsMath.platform == 'pc') {this.createMathTag = this.MSIEcreateMathTag} @@ -384,6 +393,6 @@ if (jsMath.Controls.cookie.tex2math == null) {jsMath.Controls.cookie.tex2math = 1} if (jsMath.tex2math.allowDisableTag == null) {jsMath.tex2math.allowDisableTag = 1} jsMath.tex2math.TestPatterns(); -jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$)/g); +jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$|\\(begin|end)\{[^}]+\})/g); } Index: index-images.html =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/test/index-images.html,v retrieving revision 1.1.6.1 retrieving revision 1.1.6.2 diff -Lhtdocs/jsMath/test/index-images.html -Lhtdocs/jsMath/test/index-images.html -u -r1.1.6.1 -r1.1.6.2 --- htdocs/jsMath/test/index-images.html +++ htdocs/jsMath/test/index-images.html @@ -68,7 +68,13 @@ installed the <A HREF="http://www.math.union.edu/locate/jsMath/download/jsMath.html">jsMath image fonts</A> file correctly. Follow the instructions on the linked -page. </BLOCKQUOTE> +page. +<p> + +Once you have jsMath working, go on to the <A HREF="sample.html">sample +page</A> to see if <CODE>easy/load.js</CODE> is configured properly. + +</BLOCKQUOTE> <SCRIPT> jsMath.Controls.cookie.autofont = 0; Index: sample.html =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/test/sample.html,v retrieving revision 1.1 retrieving revision 1.1.6.1 diff -Lhtdocs/jsMath/test/sample.html -Lhtdocs/jsMath/test/sample.html -u -r1.1 -r1.1.6.1 --- htdocs/jsMath/test/sample.html +++ htdocs/jsMath/test/sample.html @@ -5,7 +5,7 @@ <!-- | ----------------------------------------------------------- | The following line loads and initializes jsMath. - | You will need to edit the load.js file to set the + | You may need to edit the load.js file to set the | root URL for your site before this file will | display properly | ----------------------------------------------------------- @@ -36,7 +36,7 @@ This is a sample file showing you how to use jsMath to display mathematics in your web pages. Be sure you have followed the <A HREF="http://www.math.union.edu/locate/jsMath/authors/installation.html">installation -instructions</A> before loading this file. Also, you will need to edit the +instructions</A> before loading this file. Also, you may need to edit the <CODE>jsMath/easy/load.js</CODE> file to set the root URL for where jsMath can be found on your web site. The rest of this document gives examples of how to enter mathematics in your pages. Depending on the settings in @@ -93,18 +93,18 @@ paragraph is wrapped in a DIV tag with <CODE>CLASS="tex2math_ignore"</CODE>, and so no math delimiters will be processed: $f\colon X\to Y$, \(x^2 \gt 5\), $$1\over 1+x^2$$ and -\[\martrix{a& b\cr c& d}.\] +\[\matrix{a& b\cr c& d}.\] Note that this includes the processing of escaped dollars (\$) and -to custom delimiters ([math]a \mapsto a^2[/math]) as well. +custom delimiters ([math]a \mapsto a^2[/math]) as well. This makes it possible to produce examples of how to enter mathematics on your site, for instance. </DIV> <P> -Note also that jsMath will automatically ignore the text within +JsMath will automatically ignore the text within <CODE>PRE</CODE> tags, so you can easily enter examples that way as well: <PRE> $f\colon X\to Y$, \(x^2 \gt 5\), - $$1\over 1+x^2$$ and \[\martrix{a& b\cr c& d}.\] + $$1\over 1+x^2$$ and \[\matrix{a& b\cr c& d}.\] </PRE> <P> Index: jsMath.js =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/jsMath/uncompressed/jsMath.js,v retrieving revision 1.10.2.1 retrieving revision 1.10.2.1.2.1 diff -Lhtdocs/jsMath/uncompressed/jsMath.js -Lhtdocs/jsMath/uncompressed/jsMath.js -u -r1.10.2.1 -r1.10.2.1.2.1 --- htdocs/jsMath/uncompressed/jsMath.js +++ htdocs/jsMath/uncompressed/jsMath.js @@ -67,7 +67,7 @@ window.jsMath = { - version: "3.4c", // change this if you edit the file, but don't edit this file + version: "3.5", // change this if you edit the file, but don't edit this file document: document, // the document loading jsMath window: window, // the window of the of loading document @@ -84,10 +84,10 @@ styles: { '.math': 'font-family:serif; font-style:normal; font-weight:normal', - '.typeset': 'font-family:serif; font-style:normal; font-weight:normal; line-height:normal', + '.typeset': 'font-family:serif; font-style:normal; font-weight:normal; line-height:normal;', 'div.typeset': 'text-align:center; margin:1em 0px;', 'span.typeset': 'text-align:left', - '.typeset span': 'text-align:left; border:0px; margin:0px; padding:0px', + '.typeset span': 'text-align:left; border:0px; margin:0px; padding:0px;', '.typeset .normal': 'font-family:serif; font-style:normal; font-weight:normal', @@ -213,8 +213,8 @@ if (!cache[this.em][s]) { var bbox = this.BBoxFor(s); if (s.match(/<i>|class=\"(icm|italic|igreek|iaccent)/i)) { - bbox.w = this.BBoxFor(s+jsMath.Browser.italicString).w - - jsMath.Browser.italicCorrection; + bbox.w = bbox.Mw = this.BBoxFor(s+jsMath.Browser.italicString).w + - jsMath.Browser.italicCorrection; } cache[this.em][s] = {w: bbox.w/this.em, h: bbox.h/this.em}; } @@ -236,11 +236,7 @@ jsMath.Setup.inited = 1; } } - this.em = this.BBoxFor('<span style="'+jsMath.Browser.block+';width:13em;height:1em"></span>').w/13; - if (this.em == 0) { - // handle older browsers - this.em = this.BBoxFor('<img src="'+jsMath.blank+'" style="width:13em;height:1em"/>').w/13; - } + this.em = this.CurrentEm(); var cache = jsMath.Global.cache.B; if (!cache[this.em]) { cache[this.em] = {}; @@ -253,7 +249,6 @@ var bb = cache[this.em].bb; var h = bb.h; var d = cache[this.em].d this.h = (h-d)/this.em; this.d = d/this.em; this.hd = this.h + this.d; - this.xWidth = bb.w; // used to tell if scale has changed this.Setup.TeXfonts(); @@ -274,11 +269,20 @@ }, /* - * Get the xWidth size and if it has changed, reinitialize the sizes + * Get the x size and if it has changed, reinitialize the sizes */ ReInit: function () { - var w = this.BBoxFor('x').w; - if (w != this.xWidth) {this.Init()} + if (this.em != this.CurrentEm()) {this.Init()} + }, + + /* + * Find the em size in effect at the current text location + */ + CurrentEm: function () { + var em = this.BBoxFor('<span style="'+jsMath.Browser.block+';width:13em;height:1em"></span>').w/13; + if (em > 0) {return em} + // handle older browsers + return this.BBoxFor('<img src="'+jsMath.blank+'" style="width:13em;height:1em"/>').w/13; }, /* @@ -422,7 +426,26 @@ */ Init: function () { if (!(jsMath.Controls.cookie.asynch && jsMath.Controls.cookie.progress)) { - if (window.XMLHttpRequest) {try {this.request = new XMLHttpRequest} catch (err) {}} + if (window.XMLHttpRequest) { + try {this.request = new XMLHttpRequest} catch (err) {} + // MSIE and FireFox3 can't use xmlRequest on local files, + // but we don't have jsMath.browser yet to tell, so use this check + if (this.request && jsMath.root.match(/^file:\/\//)) { + try { + this.request.open("GET",jsMath.root+"jsMath.js",false); + this.request.send(null); + } catch (err) { + this.request = null; + // Firefox3 has window.postMessage for inter-window communication. + // It can be used to handle the new file:// security model, + // so set up the listener. + if (window.postMessage) { + this.mustPost = 1; + jsMath.window.addEventListener("message",jsMath.Post.Listener,false); + } + } + } + } if (!this.request && window.ActiveXObject) { var xml = ["MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP","Microsoft.XMLHTTP"]; @@ -468,7 +491,7 @@ throw "jsMath can't load the file '"+url+"'\n" + "Message: "+err.message; } - if (this.request.status && this.request.status >= 400) { + if (this.request.status != null && (this.request.status >= 400 || this.request.status < 0)) { // Do we need to deal with redirected links? this.blocking = 0; if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""} @@ -495,7 +518,6 @@ cancelTimeout: 30*1000, // delay for canceling load (30 sec) - iframe: null, // the hidden iframe blocking: 0, // true when an asynchronous action is being performed cancelTimer: null, // timer to cancel load if it takes too long needsBody: 0, // true if loading files requires BODY to be present @@ -516,7 +538,7 @@ * If nothing is being loaded, do the pending commands. */ Push: function (object,method,data) { -// this.debug('Pushing: '+method+' at '+this.queue.length); +// this.debug('Pushing: '+method+' at '+this.queue.length); // debug this.queue[this.queue.length] = [object,method,data]; if (!(this.blocking || (this.needsBody && !jsMath.document.body))) this.Process(); }, @@ -526,16 +548,31 @@ */ Process: function () { while (this.queue.length && !this.blocking) { - var call = this.queue[0]; var tmpQueue = this.queue.slice(1); this.queue = []; + var call = this.queue[0]; this.queue = this.queue.slice(1); + var savedQueue = this.SaveQueue(); var object = call[0]; var method = call[1]; var data = call[2]; -// this.debug('Calling: '+method+' ['+tmpQueue.length+']'); +// this.debug('Calling: '+method+' ['+savedQueue.length+']'); // debug if (object) {object[method](data)} else if (method) {method(data)} -// this.debug('Done: '+method+' ['+this.queue.length+' + '+tmpQueue.length+']'); - this.queue = this.queue.concat(tmpQueue); +// this.debug('Done: '+method+' ['+this.queue.length+' + '+savedQueue.length+'] ('+this.blocking+')'); // debug + this.RestoreQueue(savedQueue); } }, /* + * Allows pushes to occur at the FRONT of the queue + * (so a command acts as a single unit, including anything + * that it pushes on to the command stack) + */ + SaveQueue: function () { + var queue = this.queue; + this.queue = []; + return queue; + }, + RestoreQueue: function (queue) { + this.queue = this.queue.concat(queue); + }, + + /* * Handle loading of scripts that run asynchronously */ delayedLoad: function (url) { @@ -543,26 +580,28 @@ this.Push(this,'startLoad',url); }, startLoad: function (url) { - this.iframe = jsMath.document.createElement('iframe'); - this.iframe.style.visibility = 'hidden'; - this.iframe.style.position = 'absolute'; - this.iframe.style.width = '0px'; - this.iframe.style.height = '0px'; + var iframe = jsMath.document.createElement('iframe'); + iframe.style.visibility = 'hidden'; + iframe.style.position = 'absolute'; + iframe.style.width = '0px'; + iframe.style.height = '0px'; if (jsMath.document.body.firstChild) { - jsMath.document.body.insertBefore(this.iframe,jsMath.document.body.firstChild); + jsMath.document.body.insertBefore(iframe,jsMath.document.body.firstChild); } else { - jsMath.document.body.appendChild(this.iframe); + jsMath.document.body.appendChild(iframe); } this.blocking = 1; this.url = url; - if (!url.match(/\.js$/)) {this.iframe.src = url} - else {this.iframe.src = jsMath.root+"jsMath-loader.html"} if (url.substr(0,jsMath.root.length) == jsMath.root) {url = url.substr(jsMath.root.length)} jsMath.Message.Set("Loading "+url); this.cancelTimer = setTimeout('jsMath.Script.cancelLoad()',this.cancelTimeout); + if (this.mustPost) {iframe.src = jsMath.Post.startLoad(url,iframe)} + else if (url.match(/\.js$/)) {iframe.src = jsMath.root+"jsMath-loader.html"} + else {iframe.src = this.url} }, endLoad: function (action) { - if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null;} + if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null} + jsMath.Post.endLoad(); jsMath.Message.Clear(); if (action != 'cancel') {this.blocking = 0; this.Process()} }, @@ -579,10 +618,12 @@ /* * If the loading takes too long, cancel it and end the load. */ - cancelLoad: function () { - this.cancelTimer = null; - jsMath.Message.Set("Can't load file"); - this.endLoad("cancel"); + cancelLoad: function (message,delay) { + if (this.cancelTimer) {clearTimeout(this.cancelTimer); this.cancelTimer = null} + if (message == null) {message = "Can't load file"} + if (delay == null) {delay = 2000} + jsMath.Message.Set(message); + setTimeout('jsMath.Script.endLoad("cancel")',delay); }, /* @@ -622,16 +663,57 @@ data[k] = d.join(''); } window.eval(data.join('')); - }//, + } /* * for debugging the event queue */ -// debug: function (message) { -// if (jsMath.document.body && jsMath.window.debug) {jsMath.window.debug(message)} -// else {alert(message)} -// } + /* + * ,debug: function (message) { + * if (jsMath.document.body && jsMath.window.debug) {jsMath.window.debug(message)} + * else {alert(message)} + * } + */ + +}; +/***************************************************************************/ + +/* + * Handle window.postMessage() events in Firefox3 + */ + +jsMath.Post = { + window: null, // iframe we are listening to + + Listener: function (event) { + if (event.source != jsMath.Post.window) return; + var domain = event.origin; var ddomain = document.domain + if (domain == null || domain == "") {domain = "localhost"} + if (ddomain == null || ddomain == "") {ddomain = "localhost"} + if (domain != ddomain || !event.data.substr(0,6).match(/jsM(CP|LD):/)) return; + var type = event.data.substr(6,3).replace(/ /g,''); + var message = event.data.substr(10); + if (jsMath.Post.Commands[type]) (jsMath.Post.Commands[type])(message); + // cancel event? + }, + + /* + * Commands that can be performed by the listener + */ + Commands: { + SCR: function (message) {jsMath.window.eval(message)}, + ERR: function (message) {jsMath.Script.cancelLoad(message,3000)}, + BGN: function (message) {jsMath.Script.Start()}, + END: function (message) {jsMath.Script.End(); jsMath.Script.endLoad()} + }, + + startLoad: function (url,iframe) { + this.window = iframe.contentWindow; + if (!url.match(/\.js$/)) {return jsMath.root+url} + return jsMath.root+"jsMath-loader-post.html?"+url; + }, + endLoad: function () {this.window = null} }; /***************************************************************************/ @@ -788,22 +870,27 @@ if (script) { for (var i = 0; i < script.length; i++) { var src = script[i].src; - if (src && src.match('(^|/)jsMath.js$')) { + if (src && src.match('(^|/|\\\\)jsMath.js$')) { jsMath.root = src.replace(/jsMath.js$/,''); - i = script.length; + break; } } } } + if (jsMath.root.charAt(0) == '\\') {jsMath.root = jsMath.root.replace(/\\/g,'/')} if (jsMath.root.charAt(0) == '/') { - jsMath.root = jsMath.document.location.protocol + '//' - + jsMath.document.location.host + jsMath.root; + if (jsMath.root.charAt(1) != '/') { + if (jsMath.document.location.port) + {jsMath.root = ':' + jsMath.document.location.port + jsMath.root} + jsMath.root = '//' + jsMath.document.location.host + jsMath.root; + } + jsMath.root = jsMath.document.location.protocol + jsMath.root; } else if (!jsMath.root.match(/^[a-z]+:/i)) { - src = new String(jsMath.document.location); + var src = new String(jsMath.document.location); + var pattern = new RegExp('/[^/]*/\\.\\./') jsMath.root = src.replace(new RegExp('[^/]*$'),'') + jsMath.root; - while (jsMath.root.match('/[^/]*/\\.\\./')) { - jsMath.root = jsMath.root.replace(new RegExp('/[^/]*/\\.\\./'),'/'); - } + while (jsMath.root.match(pattern)) + {jsMath.root = jsMath.root.replace(pattern,'/')} } jsMath.Img.root = jsMath.root + "fonts/"; jsMath.blank = jsMath.root + "blank.gif"; @@ -904,11 +991,11 @@ * Compute font parameters for various sizes */ Sizes: function () { - jsMath.TeXparams = []; - for (var j=0; j < jsMath.sizes.length; j++) {jsMath.TeXparams[j] = {}} - for (var i in jsMath.TeX) { + jsMath.TeXparams = []; var i; var j; + for (j=0; j < jsMath.sizes.length; j++) {jsMath.TeXparams[j] = {}} + for (i in jsMath.TeX) { if (typeof(jsMath.TeX[i]) != 'object') { - for (var j=0; j < jsMath.sizes.length; j++) { + for (j=0; j < jsMath.sizes.length; j++) { jsMath.TeXparams[j][i] = jsMath.sizes[j]*jsMath.TeX[i]/100; } } @@ -948,7 +1035,7 @@ */ Body: function () { if (this.inited) return; - + this.inited = -1; jsMath.Setup.Hidden(); this.inited = -2; @@ -1167,15 +1254,22 @@ if (jsMath.platform == 'pc') { this.IE7 = (window.XMLHttpRequest != null); this.quirks = (jsMath.document.compatMode == "BackCompat"); + this.msieStandard6 = !this.quirks && !this.IE7; this.allowAbsoluteDelim = 1; this.separateSkips = 1; this.buttonCheck = 1; this.msieBlankBug = 1; + this.msieAccentBug = 1; this.msieRelativeClipBug = 1; this.msieDivWidthBug = 1; this.msiePositionFixedBug = 1; this.msieIntegralBug = 1; this.waitForImages = 1; this.msieAlphaBug = !this.IE7; this.alphaPrintBug = !this.IE7; this.msieCenterBugFix = 'position:relative; '; this.msieInlineBlockFix = ' display:inline-block;'; - if (!this.IE7) {this.msieSpaceFix = '<span style="display:inline-block"></span>'} + this.msieTeXfontBaselineBug = !this.quirks; + this.msieBorderBug = this.blankWidthBug = 1; // force these, since IE7 doesn't register it + this.msieSpaceFix = '<span style="display:inline-block"></span>'; jsMath.Macro('joinrel','\\mathrel{\\kern-5mu}'), + jsMath.Parser.prototype.mathchardef.mapstocharOrig = jsMath.Parser.prototype.mathchardef.mapstochar; + delete jsMath.Parser.prototype.mathchardef.mapstochar; + jsMath.Macro('mapstochar','\\rlap{\\mapstocharOrig\\,}\\kern1mu'), jsMath.styles['.typeset .arial'] = "font-family: 'Arial unicode MS'"; if (!this.IE7 || this.quirks) { // MSIE doesn't implement fixed positioning, so use absolute @@ -1196,6 +1290,8 @@ jsMath.styles['.typeset .spacer'].replace(/display:inline-block/,''); // MSIE can't insert DIV's into text nodes, so tex2math must use SPAN's to fake DIV's jsMath.styles['.tex2math_div'] = jsMath.styles['div.typeset'] + '; width: 100%; display: inline-block'; + // Reduce occurrance of zoom bug in IE7 + jsMath.styles['.typeset'] += '; letter-spacing:0'; // MSIE will rescale images if the DPIs differ if (screen.deviceXDPI && screen.logicalXDPI && screen.deviceXDPI != screen.logicalXDPI) { @@ -1231,7 +1327,7 @@ jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}'); if (navigator.vendor == 'Firefox') { this.version = navigator.vendorSub; - } else if (navigator.userAgent.match(' Firefox/([0-9.]+)( |$)')) { + } else if (navigator.userAgent.match(' Firefox/([0-9.]+)([a-z ]|$)')) { this.version = RegExp.$1; } } @@ -1378,8 +1474,11 @@ CheckTeX: function () { var wh = jsMath.BBoxFor('<span style="font-family: '+jsMath.Font.testFont+', serif">'+jsMath.TeX.cmex10[1].c+'</span>'); jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10',null,null,'jsMath-')); - if (jsMath.nofonts && (jsMath.platform != "mac" || - jsMath.browser != 'Mozilla' || !jsMath.Browser.VersionAtLeast(1.5))) { + if (!jsMath.nofonts) return; + if (jsMath.browser != 'Mozilla' || + (jsMath.platform == "mac" && + (!jsMath.Browser.VersionAtLeast(1.5) || jsMath.Browser.VersionAtLeast(3.0))) || + (jsMath.platform != "mac" && !jsMath.Browser.VersionAtLeast(3.0))) { wh = jsMath.BBoxFor('<span style="font-family: cmex10, serif">'+jsMath.TeX.cmex10[1].c+'</span>'); jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10')); if (!jsMath.nofonts) {jsMath.Setup.Script("jsMath-BaKoMa-fonts.js")} @@ -1538,7 +1637,12 @@ return; } // Image fonts - var font = {}; font[fontname] = ['all']; + var font = {}; + if (cookie.font == 'symbol' && data.symbol != null) { + font[fontname] = data.symbol(fontname,fontfam,data); + } else { + font[fontname] = ['all']; + } jsMath.Img.SetFont(font); jsMath.Img.LoadFont(fontname); if (jsMath.initialized) { @@ -2405,7 +2509,6 @@ */ UpdateFonts: function () { var change = this.update; if (!this.loaded) return; - var best = this[jsMath.Img.fonts[this.best]]; for (var font in change) { for (var i = 0; i < change[font].length; i++) { var c = change[font][i]; @@ -2525,8 +2628,12 @@ } if (w == 0) { if (jsMath.Browser.blankWidthBug) { - style += 'width:1px;'; - backspace = '<span class="spacer" style="margin-right:-1px"></span>' + if (jsMath.Browser.quirks) { + style += 'width:1px;'; + backspace = '<span class="spacer" style="margin-right:-1px"></span>' + } else if (!isRule) { + style += 'width:1px;margin-right:-1px;'; + } } } else {style += 'width:'+this.Em(w)+';'} if (d == null) {d = 0} @@ -2579,11 +2686,26 @@ * also doesn't combine vertical and horizontal spacing well. * Here the x and y positioning are done in separate <SPAN> tags */ - PlaceSeparateSkips: function (html,x,y) { + PlaceSeparateSkips: function (html,x,y,mw,Mw,w) { if (Math.abs(x) < .0001) {x = 0} if (Math.abs(y) < .0001) {y = 0} - if (y) {html = '<span style="position: relative; top:'+this.Em(-y)+';' - + '">' + html + '</span>'} + if (y) { + var lw = 0; var rw = 0; var width = ""; + if (mw != null) { + rw = Mw - w; lw = mw; + width = ' width:'+this.Em(Mw-mw)+';'; + } + html = + this.Spacer(lw-rw) + + '<span style="position: relative; ' + + 'top:'+this.Em(-y)+';' + + 'left:'+this.Em(rw)+';' + + width + '">' + + this.Spacer(-lw) + + html + + this.Spacer(rw) + + '</span>' + } if (x) {html = this.Spacer(x) + html} return html; }, @@ -2591,16 +2713,24 @@ /* * Place a SPAN with absolute coordinates */ - PlaceAbsolute: function (html,x,y) { + PlaceAbsolute: function (html,x,y,mw,Mw,w) { if (Math.abs(x) < .0001) {x = 0} if (Math.abs(y) < .0001) {y = 0} - html = '<span style="position:absolute; left:'+this.Em(x)+'; ' - + 'top:'+this.Em(y)+';">' + html + ' </span>'; - // space normalizes line height in script styles + var leftSpace = ""; var rightSpace = ""; + if (jsMath.Browser.msieRelativeClipBug && mw != null) { + leftSpace = this.Spacer(-mw); x += mw; + rightSpace = this.Spacer(Mw-w); + } + html = + '<span style="position:absolute; left:'+this.Em(x)+'; ' + + 'top:'+this.Em(y)+';">' + + leftSpace + html + rightSpace + + ' ' + // space normalizes line height in script styles + '</span>'; return html; }, - Absolute: function(html,w,h,d,y,H) { + Absolute: function(html,w,h,d,y) { if (y != "none") { if (Math.abs(y) < .0001) {y = 0} html = '<span style="position:absolute; ' @@ -2613,22 +2743,9 @@ if (jsMath.Browser.msieAbsoluteBug) { // for MSIE (Mac) html = '<span style="position:relative;">' + html + '</span>'; } - if (jsMath.Browser.spanHeightVaries) { - var style = ''; - style = jsMath.Browser.msieInlineBlockFix - + ' width:'+jsMath.HTML.Em(w)+';'; - if (jsMath.Browser.quirks) { - style += ' height:'+jsMath.HTML.Em(H)+';' - } else { - style += ' height: 0px;' - + ' vertical-align:'+jsMath.HTML.Em(H)+';' - } - html = '<span style="position:relative;' + style + '">' - + html - + '</span>'; - } else { - html = '<span style="position:relative">' + html + '</span>'; - } + html = '<span style="position:relative;' + + jsMath.Browser.msieInlineBlockFix + + '">' + html + '</span>'; return html; } @@ -2645,7 +2762,7 @@ if (d == null) {d = jsMath.d} this.type = 'typeset'; this.w = w; this.h = h; this.d = d; this.bh = h; this.bd = d; - this.... [truncated message content] |