diff --git a/DATA/uploads/README b/DATA/uploads/README new file mode 100644 index 0000000000..dccd572a96 --- /dev/null +++ b/DATA/uploads/README @@ -0,0 +1,3 @@ +$CVSHeader$ + +This directory is used as temporary storage for file uploads. diff --git a/LICENSE b/LICENSE index f3a1a61036..1ea40576cc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,9 @@ WeBWorK Online Homework Delivery System - Version 2.0 + Version 2.5.0+ - Copyright 2000-2003, The WeBWorK Project + Copyright 2000-2011, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify diff --git a/README b/README index 0d0dc492e7..58b27413e9 100644 --- a/README +++ b/README @@ -1,15 +1,10 @@ - - WeBWorK + WeBWorK Online Homework Delivery System - Version 2.0 + Version 2.5.0 + Branch: system/tags/rel-2-5-0/webwork2 - Copyright 2000-2003, The WeBWorK Project + http://webwork.maa.org/wiki/Release_notes_for_WeBWorK_2.5.0 + Copyright 2000-2011, The WeBWorK Project + http://webwork.maa.org All rights reserved. -Some important information should go here. For example: - - DESCRIPTION - REQUIREMENTS - INSTALLATION - CONFIGURATION - USE diff --git a/bin/NPL-update b/bin/NPL-update new file mode 100755 index 0000000000..19f6f44946 --- /dev/null +++ b/bin/NPL-update @@ -0,0 +1,765 @@ +#!/usr/bin/perl + +# This is the script formerly known as loadDB2. It is used to update +# the database when it comes to the National Problem Library (NPL). +# This should be run after doing a cvs checkout or update for the NPL +# files. + +# In order for this script to work: +# 1) The NPL downloaded to your machine (the .pg files) +# 2) The environment variable WEBWORK_ROOT needs to be +# correctly defined (as with other scripts here). +# 3) Configuration for the NPL in global.conf needs to be +# done (basically just setting the path to the NPL files. + +#use strict; +use File::Find; +use File::Basename; +use Cwd; +use DBI; + + #(maximum varchar length is 255 for mysql version < 5.0.3. + #You can increase path length to 4096 for mysql > 5.0.3) + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +### Data for creating the database tables + +@create_tables = ( +['NPL-DBsubject', ' + DBsubject_id int(15) NOT NULL auto_increment, + name varchar(127) NOT NULL, + KEY DBsubject (name), + PRIMARY KEY (DBsubject_id) +'], +['NPL-DBchapter', ' + DBchapter_id int(15) NOT NULL auto_increment, + name varchar(127) NOT NULL, + DBsubject_id int(15) DEFAULT 0 NOT NULL, + KEY DBchapter (name), + KEY (DBsubject_id), + PRIMARY KEY (DBchapter_id) +'], +['NPL-DBsection', ' + DBsection_id int(15) NOT NULL auto_increment, + name varchar(255) NOT NULL, + DBchapter_id int(15) DEFAULT 0 NOT NULL, + KEY DBsection (name), + KEY (DBchapter_id), + PRIMARY KEY (DBsection_id) +'], +['NPL-author', ' + author_id int (15) NOT NULL auto_increment, + institution tinyblob, + lastname varchar (100) NOT NULL, + firstname varchar (100) NOT NULL, + email varchar (255), + KEY author (lastname, firstname), + PRIMARY KEY (author_id) +'], +['NPL-path', ' + path_id int(15) NOT NULL auto_increment, + path varchar(255) NOT NULL, + machine varchar(127), + user varchar(127), + KEY (path), + PRIMARY KEY (path_id) +'], +['NPL-pgfile', ' + pgfile_id int(15) NOT NULL auto_increment, + DBsection_id int(15) NOT NULL, + author_id int(15), + institution tinyblob, + path_id int(15) NOT NULL, + filename varchar(255) NOT NULL, + PRIMARY KEY (pgfile_id) +'], +['NPL-keyword', ' + keyword_id int(15) NOT NULL auto_increment, + keyword varchar(65) NOT NULL, + KEY (keyword), + PRIMARY KEY (keyword_id) +'], +['NPL-pgfile-keyword', ' + pgfile_id int(15) DEFAULT 0 NOT NULL, + keyword_id int(15) DEFAULT 0 NOT NULL, + KEY pgfile_keyword (keyword_id, pgfile_id), + KEY pgfile (pgfile_id) +'], +['NPL-textbook', ' + textbook_id int (15) NOT NULL auto_increment, + title varchar (255) NOT NULL, + edition int (3) DEFAULT 0 NOT NULL, + author varchar (63) NOT NULL, + publisher varchar (127), + isbn char (15), + pubdate varchar (27), + PRIMARY KEY (textbook_id) +'], +['NPL-chapter', ' + chapter_id int (15) NOT NULL auto_increment, + textbook_id int (15), + number int(3), + name varchar(127) NOT NULL, + page int(4), + KEY (textbook_id, name), + KEY (number), + PRIMARY KEY (chapter_id) +'], +['NPL-section', ' + section_id int(15) NOT NULL auto_increment, + chapter_id int (15), + number int(3), + name varchar(127) NOT NULL, + page int(4), + KEY (chapter_id, name), + KEY (number), + PRIMARY KEY section (section_id) +'], +['NPL-problem', ' + problem_id int(15) NOT NULL auto_increment, + section_id int(15), + number int(4) NOT NULL, + page int(4), + #KEY (page, number), + KEY (section_id), + PRIMARY KEY (problem_id) +'], +['NPL-pgfile-problem', ' + pgfile_id int(15) DEFAULT 0 NOT NULL, + problem_id int(15) DEFAULT 0 NOT NULL, + PRIMARY KEY (pgfile_id, problem_id) +']); + +### End of database data + +# Get database connection + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; + +my $ce = new WeBWorK::CourseEnvironment({webwork_dir=>$ENV{WEBWORK_ROOT}}); +my $dbh = DBI->connect( + $ce->{database_dsn}, + $ce->{database_username}, + $ce->{database_password}, + { + PrintError => 0, + RaiseError => 1, + }, +); + +my $passwd = $ce->{database_password}; +my $user = $ce->{database_username}; +my $libraryRoot = $ce->{problemLibrary}->{root}; +my $verbose = 0; +my $cnt2 = 0; + +$| = 1; # autoflush output + +sub dbug { + my $msg = shift; + my $insignificance = shift || 2; + print $msg if($verbose>=$insignificance); +} + +## Resetting the database tables. +# First take care of tables which are no longer used + +$dbh->do("DROP TABLE IF EXISTS `NPL-institution`"); +$dbh->do("DROP TABLE IF EXISTS `NPL-pgfile-institution`"); + +for my $tableinfo (@create_tables) { + my $tabname = $tableinfo->[0]; + my $tabinit = $tableinfo->[1]; + my $query = "DROP TABLE IF EXISTS `$tabname`"; + $dbh->do($query); + $query = "CREATE TABLE `$tabname` ( $tabinit )"; + $dbh->do($query); +} + +print "Mysql database reinitialized.\n"; + +# From pgfile +## DBchapter('Limits and Derivatives') +## DBsection('Calculating Limits using the Limit Laws') +## Date('6/3/2002') +## Author('Tangan Gao') +## Institution('csulb') +## TitleText1('Calculus Early Transcendentals') +## EditionText1('4') +## AuthorText1('Stewart') +## Section1('2.3') +## Problem1('7') +# +# The database structure is in the file create_tables2.sql and its ER-graph is +# the file wwdb_er_graph.pdf. + +my ($name,$pgfile,$pgpath); + +#### First read in textbook information + +if(open(IN, "$libraryRoot/Textbooks")) { + print "Reading in textbook data from Textbooks in the library $libraryRoot.\n"; + my %textinfo = ( TitleText => '', EditionText =>'', AuthorText=>''); + my $bookid = undef; + while (my $line = ) { + $line =~ s|#*$||; + if($line =~ /^\s*(.*?)\s*>>>\s*(.*?)\s*$/) { # Should have chapter or section information + my $chapsec = $1; + my $title = $2; + if($chapsec=~ /(\d+)\.(\d+)/) { # We have a section + if(defined($bookid)) { + my $query = "SELECT chapter_id FROM `NPL-chapter` WHERE textbook_id = \"$bookid\" AND number = \"$1\""; + my $chapid = $dbh->selectrow_array($query); + if(defined($chapid)) { + $query = "SELECT section_id FROM `NPL-section` WHERE chapter_id = \"$chapid\" AND name = \"$title\""; + my $sectid = $dbh->selectrow_array($query); + if (!defined($sectid)) { + $dbh->do("INSERT INTO `NPL-section` + VALUES( + \"\", + \"$chapid\", + \"$2\", + \"$title\", + \"\" + )" + ); + dbug "INSERT INTO section VALUES(\"\", \"$chapid\", \"$2\", \"$title\", \"\" )\n"; + } + } else { + print "Cannot enter section $chapsec because textbook information is missing the chapter entry\n"; + } + } else { + print "Cannot enter section $chapsec because textbook information is incomplete\n"; + } + } else { # We have a chapter entry + if(defined($bookid)) { + my $query = "SELECT chapter_id FROM `NPL-chapter` WHERE textbook_id = \"$bookid\" AND number = \"$chapsec\""; + my $chapid = $dbh->selectrow_array($query); + if (!defined($chapid)) { + $dbh->do("INSERT INTO `NPL-chapter` + VALUES( + \"\", + \"$bookid\", + \"".$chapsec."\", + \"$title\", + \"\" + )" + ); + $chapid = $dbh->selectrow_array($query); + + # Add dummy section entry for problems tagged to the chapter + # without a section + $query = "SELECT section_id FROM `NPL-section` WHERE chapter_id = \"$chapid\" AND number = -1"; + my $sectid = $dbh->selectrow_array($query); + if (!defined($sectid)) { + $dbh->do("INSERT INTO `NPL-section` + VALUES( + \"\", + \"$chapid\", + \"-1\", + \"\", + \"\" + )" + ); + dbug "INSERT INTO section VALUES(\"\", \"$chapid\", \"-1\", \"\", \"\" )\n"; + } + } + } else { + print "Cannot enter chapter $chapsec because textbook information is incomplete\n"; + } + } + } elsif($line =~ /^\s*(TitleText|EditionText|AuthorText)\(\s*'(.*?)'\s*\)/) { + # Textbook information, maybe new + my $type = $1; + if(defined($textinfo{$type})) { # signals new text + %textinfo = ( TitleText => undef, + EditionText =>undef, + AuthorText=> undef); + $textinfo{$type} = $2; + $bookid = undef; + } else { + $textinfo{$type} = $2; + if(defined($textinfo{TitleText}) and + defined($textinfo{AuthorText}) and + defined($textinfo{EditionText})) { + my $query = "SELECT textbook_id FROM `NPL-textbook` WHERE title = \"$textinfo{TitleText}\" AND edition = \"$textinfo{EditionText}\" AND author=\"$textinfo{AuthorText}\""; + $bookid = $dbh->selectrow_array($query); + if (!defined($bookid)) { + $dbh->do("INSERT INTO `NPL-textbook` + VALUES( + \"\", + \"$textinfo{TitleText}\", + \"$textinfo{EditionText}\", + \"$textinfo{AuthorText}\", + \"\", + \"\", + \"\" + )" + ); + dbug "INSERT INTO textbook VALUES( \"\", \"$textinfo{TitleText}\", \"$textinfo{EditionText}\", \"$textinfo{AuthorText}\", \"\", \"\", \"\" )\n"; + $bookid = $dbh->selectrow_array($query); + } + } + } + } + } + close(IN); +} else{ + print "Textbooks file was not found in library $libraryRoot. If the path to the problem library doesn't seem + correct, make modifications in webwork2/conf/global.conf (\$problemLibrary{root}). If that is correct then + updating from cvs should download the Textbooks file.\n"; +} + +print "Converting data from tagged pgfiles into mysql.\n"; +print "Number of files processed:\n"; + +#### Now search for tagged problems +#recursive search for all pg files + +find({ wanted => \&pgfiles, follow_fast=> 1}, $libraryRoot); + +sub kwtidy { + my $s = shift; + $s =~ s/\W//g; + $s =~ s/_//g; + $s = lc($s); + return($s); +} + +sub keywordcleaner { + my $string = shift; + my @spl1 = split /,/, $string; + my @spl2 = map(kwtidy($_), @spl1); + return(@spl2); +} + +# Save on passing these values around +my %textinfo; + +# Initialize, if needed more text-info information; +sub maybenewtext { + my $textno = shift; + return if defined($textinfo{$textno}); + # So, not defined yet + $textinfo{$textno} = { title => '', author =>'', edition =>'', + section => '', chapter =>'', problems => [] }; +} + +# process each file returned by the find command. +sub pgfiles { + my $name = $File::Find::name; + my ($subject, $chapter, $section, $date, $institution, $author, $text); + my ($edition, $textauthor, $textsection, $textproblem, $tagged); + %textinfo=(); + my @textproblems = (-1); + if ($name =~ /pg$/) { + $pgfile = basename($name); + $pgpath = dirname($name); + $cnt2++; + printf("%6d", $cnt2) if(($cnt2 % 100) == 0); + print "\n" if(($cnt2 % 1000) == 0); + $pgpath =~ s|^$libraryRoot/||; + open(IN,"$name") or die "can not open $name: $!"; + $tagged = 0; + while () { + SWITCH: { + if (/\bKEYWORDS\((.*)\)/i) { + @keyword = keywordcleaner($1); + last SWITCH; + } + if (/\bDBsubject\s*\(\s*'?(.*?)'?\s*\)/) { + $subject = $1; + $subject =~ s/'/\'/g; + last SWITCH; + } + if (/\bDBchapter\s*\(\s*'?(.*?)'?\s*\)/) { + $chapter = $1; + $chapter =~ s/'/\'/g; + $tagged = 1; + last SWITCH; + } + if (/\bDBsection\s*\(\s*'?(.*?)'?\s*\)/) { + $section = $1; + $section =~ s/'/\'/g; + last SWITCH; + } + if (/\bDate\s*\(\s*'(.*?)'\s*\)/) { + $date = $1; + $date =~ s/'/\'/g; + last SWITCH; + } + if (/\bInstitution\s*\(\s*'(.*?)'\s*\)/) { + $institution = $1; + $institution =~ s/'/\'/g; + last SWITCH; + } + if (/\bAuthor\(\s*'?(.*?)'?\s*\)/) { + $author = $1; + $author =~ s/'/\'/g; + last SWITCH; + } + if (/\bTitleText(\d+)\(\s*'?(.*?)'?\s*\)/) { + $textno = $1; + $text = $2; + $text =~ s/'/\'/g; + if ($text =~ /\S/) { + maybenewtext($textno); + $textinfo{$textno}->{title} = $text; + } + last SWITCH; + } + if (/\bEditionText(\d+)\(\s*'?(.*?)'?\s*\)/) { + $textno = $1; + $edition = $2; + $edition =~ s/'/\'/g; + if ($edition =~ /\S/) { + maybenewtext($textno); + $textinfo{$textno}->{edition} = $edition; + } + last SWITCH; + } + if (/\bAuthorText(\d+)\(\s*'?(.*?)'?\s*\)/) { + $textno = $1; + $textauthor = $2; + $textauthor =~ s/'/\'/g; + if ($textauthor =~ /\S/) { + maybenewtext($textno); + $textinfo{$textno}->{author} = $textauthor; + } + last SWITCH; + } + if (/\bSection(\d+)\(\s*'?(.*?)'?\s*\)/) { + $textno = $1; + $textsection = $2; + $textsection =~ s/'/\'/g; + if ($textsection =~ /\S/) { + maybenewtext($textno); + if ($textsection =~ /(\d*?)\.(\d*)/) { + $textinfo{$textno}->{chapter} = $1; + $textinfo{$textno}->{section} = $2; + } else { + $textinfo{$textno}->{chapter} = $textsection; + $textinfo{$textno}->{section} = -1; + } + + } + last SWITCH; + } + if (/\bProblem(\d+)\(\s*(.*?)\s*\)/) { + $textno = $1; + $textproblem = $2; + $textproblem =~ s/\D/ /g; + @textproblems = split /\s+/, $textproblem; + @textproblems = grep { $_ =~ /\S/ } @textproblems; + if (scalar(@textproblems) or defined($textinfo{$textno})) { + @textproblems = (-1) unless(scalar(@textproblems)); + maybenewtext($textno); + $textinfo{$textno}->{problems} = \@textproblems; + } + #print "$textproblem\n" if ($textproblem !~ /^[\s\d]+$/); + # $textproblem =~ s/'/\'/g; + last SWITCH; + } + } + } #end of SWITCH and while + if ($tagged and $chapter eq 'ZZZ-Inserted Text') { + $tagged=0; + } + if ($tagged) { + # + # kludge to fix the omission of a subject field + unless($subject) { + if ($text =~ /precalculus/i) { + $subject = "Precalculus"; + } elsif ($text =~ /calculus/i) { + $subject = "Calculus"; + } elsif ($text =~ /linear/i) { + $subject = "Linear Algebra"; + } elsif ($text =~ /algebra/i) { + $subject = "Algebra"; + } elsif ($text =~ /statistic/i) { + $subject = "Statistics"; + } elsif ($text =~ /financial/i) { + $subject = "Financial Mathematics"; + } else { + $subject = "Misc"; + } + } + # From the pgfile we just looked at, + ## DBchapter('Limits and Derivatives') in $chapter + ## DBsection('Calculating Limits using the Limit Laws') in $section + ## Date('6/3/2002') in $date + ## Author('Tangan Gao') in $author + ## Institution('csulb') in $institution + ## TitleText1('Calculus Early Transcendentals') in $text + ## EditionText1('4') in $edition + ## AuthorText1('Stewart') in $textauthor + ## Section1('2.3') in $textsection + ## Problem1('7') in $textproblem + # + # The database structure is in the file create_tables2.sql and its ER-graph + # is the file wwdb_er_graph.pdf. Insert, in order, into the tables + # in that file. + # + + #selectrow_array returns first field of first row in scalar context or undef + # undef for failure also, $dbh->{RaiseError} = 1 should catch that case. + # + # DBsubject table + # + $query = "SELECT DBsubject_id FROM `NPL-DBsubject` WHERE name = \"$subject\""; + my $DBsubject_id = $dbh->selectrow_array($query); + if (!defined($DBsubject_id)) { + $dbh->do( + "INSERT INTO `NPL-DBsubject` + VALUES( + \"\", + \"$subject\" + )" + ); + dbug "INSERT INTO DBsubject VALUES(\"\",\"$subject\")\n"; + $DBsubject_id = $dbh->selectrow_array($query); + } + + # DBchapter table + # + $query = "SELECT DBchapter_id FROM `NPL-DBchapter` WHERE name = \"$chapter\" and DBsubject_id = $DBsubject_id"; + my $DBchapter_id = $dbh->selectrow_array($query); + if (!defined($DBchapter_id)) { + $dbh->do("INSERT INTO `NPL-DBchapter` + VALUES( + \"\", + \"$chapter\", + \"$DBsubject_id\" + )" + ); + dbug "INSERT INTO DBchapter VALUES( \"\", \"$chapter\", \"$DBsubject_id\)\n"; + $DBchapter_id = $dbh->selectrow_array($query); + } + + # DBsection table + # + $query = "SELECT DBsection_id FROM `NPL-DBsection` WHERE name = \"$section\" AND DBchapter_id = $DBchapter_id"; + my $DBsection_id = $dbh->selectrow_array($query); + if (!defined($DBsection_id)) { + $dbh->do("INSERT INTO `NPL-DBsection` + VALUES( + \"\", + \"$section\", + \"$DBchapter_id\" + )" + ); + dbug "INSERT INTO DBsection VALUES( \"\", \"$section\", \"$DBchapter_id\", \"$DBsubject_id\" )\n"; + $DBsection_id = $dbh->selectrow_array($query); + } + + # author table + # + $author =~ /(.*?)\s(\w+)\s*$/; + my $firstname = $1; + my $lastname = $2; + #remove leading and trailing spaces from firstname, which includes any middle name too. + $firstname =~ s/^\s*//; + $firstname =~ s/\s*$//; + $query = "SELECT author_id FROM `NPL-author` WHERE lastname = \"$lastname\" AND firstname=\"$firstname\""; + my $author_id = $dbh->selectrow_array($query); + if (!defined($author_id)) { + $dbh->do("INSERT INTO `NPL-author` + VALUES( + \"\", + \"$institution\", + \"$lastname\", + \"$firstname\", + \"\" + )" + ); + dbug "INSERT INTO author VALUES( \"\", \"$institution\", \"$lastname\", \"$firstname\", \"\" )\n"; + $author_id = $dbh->selectrow_array($query); + } + + # path table + # + $query = "SELECT path_id FROM `NPL-path` WHERE path = \"$pgpath\""; + my $path_id = $dbh->selectrow_array($query); + if (!defined($path_id)) { + $dbh->do("INSERT INTO `NPL-path` + VALUES( + \"\", + \"$pgpath\", + \"\", + \"\" + )" + ); + dbug "INSERT INTO path VALUES( \"\", \"$path\", \"\", \"\" )\n"; + $path_id = $dbh->selectrow_array($query); + } + + # pgfile table + # + my $pgfile_id; + $dbh->do("INSERT INTO `NPL-pgfile` + VALUES( + \"\", + \"$DBsection_id\", + \"$author_id\", + \"$institution\", + \"$path_id\", + \"$pgfile\" + )" + ); + dbug "INSERT INTO pgfile VALUES( \"\", \"$DBsection_id\", \"$author_id\", \"$institution\", \"$path_id\", \"$pgfile\", \"\" )\n"; + $query = "SELECT pgfile_id FROM `NPL-pgfile` WHERE filename = \"$pgfile\" and path_id=$path_id"; + $pgfile_id = $dbh->selectrow_array($query); + + # keyword table, and problem_keyword many-many table + # + foreach my $keyword (@keyword) { + $keyword =~ s/[\'\"]//g; + $query = "SELECT keyword_id FROM `NPL-keyword` WHERE keyword = \"$keyword\""; + my $keyword_id = $dbh->selectrow_array($query); + if (!defined($keyword_id)) { + $dbh->do("INSERT INTO `NPL-keyword` + VALUES( + \"\", + \"$keyword\" + )" + ); + dbug "INSERT INTO keyword VALUES( \"\", \"$keyword\")\n"; + $keyword_id = $dbh->selectrow_array($query); + } + + $query = "SELECT pgfile_id FROM `NPL-pgfile-keyword` WHERE keyword_id = \"$keyword_id\" and pgfile_id=\"$pgfile_id\""; + my $ok = $dbh->selectrow_array($query); + if (!defined($ok)) { + $dbh->do("INSERT INTO `NPL-pgfile-keyword` + VALUES( + \"$pgfile_id\", + \"$keyword_id\" + )" + ); + dbug "INSERT INTO pgfile_keyword VALUES( \"$pgfile_id\", \"$keyword_id\" )\n"; + } + } #end foreach keyword + + # Textbook section + # problem table contains textbook problems + # + for my $textno (keys %textinfo) { + my $texthashref = $textinfo{$textno}; + + # textbook table + # + $text = $texthashref->{title}; + $edition = $texthashref->{edition}; + $textauthor = $texthashref->{author}; + next unless($text and $textauthor); + my $chapnum = $texthashref->{chapter}; + my $secnum = $texthashref->{section}; + $query = "SELECT textbook_id FROM `NPL-textbook` WHERE title = \"$text\" AND edition = \"$edition\" AND author=\"$textauthor\""; + my $textbook_id = $dbh->selectrow_array($query); + if (!defined($textbook_id)) { + $dbh->do("INSERT INTO `NPL-textbook` + VALUES( + \"\", + \"$text\", + \"$edition\", + \"$textauthor\", + \"\", + \"\", + \"\" + )" + ); + dbug "INSERT INTO textbook VALUES( \"\", \"$text\", \"$edition\", \"$textauthor\", \"\", \"\", \"\" )\n"; + dbug "\nLate add into NPL-textbook \"$text\", \"$edition\", \"$textauthor\"\n", 1; + $textbook_id = $dbh->selectrow_array($query); + } + + # chapter weak table of textbook + # + $query = "SELECT chapter_id FROM `NPL-chapter` WHERE textbook_id = \"$textbook_id\" AND number = \"$chapnum\""; + my $chapter_id = $dbh->selectrow_array($query); + if (!defined($chapter_id)) { + $dbh->do("INSERT INTO `NPL-chapter` + VALUES( + \"\", + \"$textbook_id\", + \"".$chapnum."\", + \"$chapter\", + \"\" + )" + ); + dbug "\nLate add into NPL-textchapter \"$text\", \"$edition\", \"$textauthor\", $chapnum $chapter from $name\n", 1; + dbug "INSERT INTO chapter VALUES(\"\", \"$textbook_id\", \"".$chapnum."\", \"$chapter\", \"\" )\n"; + $chapter_id = $dbh->selectrow_array($query); + } + + # section weak table of textbook + # + $section = '' if ($secnum < 0); + $query = "SELECT section_id FROM `NPL-section` WHERE chapter_id = \"$chapter_id\" AND number = \"$secnum\""; + my $section_id = $dbh->selectrow_array($query); + if (!defined($section_id)) { + $dbh->do("INSERT INTO `NPL-section` + VALUES( + \"\", + \"$chapter_id\", + \"$secnum\", + \"$section\", + \"\" + )" + ); + dbug "INSERT INTO section VALUES(\"\", \"$textbook_id\", \"$secnum\", \"$section\", \"\" )\n"; + dbug "\nLate add into NPL-textsection \"$text\", \"$edition\", \"$textauthor\", $secnum $section from $name\n", 1; + $section_id = $dbh->selectrow_array($query); + } + + @textproblems = @{$texthashref->{problems}}; + for my $tp (@textproblems) { + $query = "SELECT problem_id FROM `NPL-problem` WHERE section_id = \"$section_id\" AND number = \"$tp\""; + my $problem_id = $dbh->selectrow_array($query); + if (!defined($problem_id)) { + $dbh->do("INSERT INTO `NPL-problem` + VALUES( + \"\", + \"$section_id\", + \"$tp\", + \"\" + )" + ); + dbug "INSERT INTO problem VALUES( \"\", \"$section_id\", \"$tp\", \"\" )\n"; + $problem_id = $dbh->selectrow_array($query); + } + + # pgfile_problem table associates pgfiles with textbook problems + # + $query = "SELECT problem_id FROM `NPL-pgfile-problem` WHERE problem_id = \"$problem_id\" AND pgfile_id = \"$pgfile_id\""; + my $pg_problem_id = $dbh->selectrow_array($query); + if (!defined($pg_problem_id)) { + $dbh->do("INSERT INTO `NPL-pgfile-problem` + VALUES( + \"$pgfile_id\", + \"$problem_id\" + )" + ); + dbug "INSERT INTO pgfile_problem VALUES( \"$pgfile_id\", \"$problem_id\" )\n"; + } + } + + #reset tag vars, they may not match the next text/file + $date =""; $textauthor=""; $textsection=""; + $chapter=""; $section=""; + } + } + close(IN) or die "can not close: $!"; + } +} + + +$dbh->disconnect; + +print "\nDone.\n"; diff --git a/bin/PSH.pm b/bin/PSH.pm deleted file mode 100644 index 15d703f392..0000000000 --- a/bin/PSH.pm +++ /dev/null @@ -1,329 +0,0 @@ -package PSH; - -use vars '$it'; - -$PSH::VERSION = '0.7'; - -#use strict; ##use only for testing !!!!!!!! - -sub welcome { - print STDOUT "Welcome to psh $PSH::VERSION by Jenda\@Krynicky.cz\nRunning under Perl $]\n\n"; -} - -$PSH::allowsystem = 1; -%PSH::specials = (); - -eval {require 'PSH.config'}; - print STDERR "Error in psh.config : $@\n" if ($@ and $@ !~ /^Can't locate PSH.config in \@INC/i); -$@=''; - -sub Exec { - my $line = shift; - if ($PSH::allowsystem) { - if ($line =~ s/>\s*$//) { - ${$PSH::package.'::it'}= `$line`; - } else { - $line =~ /^(.*?)(?:\s(.*))?$/; - my $cmd; - if (defined ($cmd = $PSH::alias{lc $1})) { - ${$PSH::package.'::it'}=system( $cmd.' '.$2 ); - } else { - ${$PSH::package.'::it'}=system( $line ); - } - } - } else { - print STDOUT "Disallowed by the script!\n"; - } -} - -sub specials { - return if @_ % 2; # I need even number of parameters - my ($char,$fun); - while (defined($char = shift)) { - $fun = shift; - if ($fun) { - $PSH::specials{$char} = $fun; - } else { - delete $PSH::specials{$char}; - } - } - $PSH::specials = join('|', map {"\Q$_\E"} keys %PSH::specials); -} - -$PSH::specials{'!'} = \&PSH::Exec; - -sub prompt { - my $prompt = shift || 'perl'; - my $eval = shift; - $PSH::specials = join('|', map {"\Q$_\E"} keys %PSH::specials); # just for sure - local $it=''; - my $command=''; - local ($PSH::package, $PSH::filename, $PSH::ln) = caller; - ${$PSH::package.'::it'}=''; -# print "called from $PSH::package\n"; - print STDOUT "$prompt\$ "; - - my $line; - while (defined ($line = )) { - if (!$command and $line =~ /^$/) { - print STDOUT "$prompt\$ "; - } elsif (!$command and $PSH::specials and $line =~ /^\s*($PSH::specials)\s*/ and $PSH::specials{$1}) { - $line =~ s/^\s*($PSH::specials)\s*(.*)$/$2/o; - ${$PSH::package.'::it'}= &{$PSH::specials{$1}}($line); - print STDOUT "\n$prompt\$ "; - } elsif ($line =~ /^\?$/) { - PSH::help(); - print STDOUT "\n$prompt\$ "; - - } elsif (!$command and $line =~ /^<<(.*)$/) { - my $eoc = $1; - print STDOUT "$prompt($eoc)\$ "; - while (defined ($line = )) { - last if $line =~ /^\Q$eoc\E\s*$/; - $command .=$line; - print STDOUT "$prompt($eoc)\$ "; - } - if ($eval) { - ${$PSH::package.'::it'} = &$eval($command); - } else { - ${$PSH::package.'::it'} = eval "package $PSH::package;\n".$command; - } - $command = ''; - print STDOUT "\nERROR: $@\n" if $@; - print STDOUT "\n$prompt\$ "; - } elsif ($line =~ s/;$//) { - if ($eval) { - ${$PSH::package.'::it'} = &$eval($command.$line); - } else { - ${$PSH::package.'::it'} = eval "package $PSH::package;\n".$command.$line; - } - $command = ''; - print STDOUT "\nERROR: $@\n" if $@; - print STDOUT "\n$prompt\$ "; - } else { - $command .= $line; - print STDOUT "$prompt> "; - } - } - return ${$PSH::package.'::it'}; -} - -sub PSH::help { - print STDOUT <<"*END*"; -Commands starting by ! are passed to the command prompt. -If the line ends by >, the output of the command is redirected to -variable \$it. If you want to catch both STDOUT and STDERR use this: - - perl\$ ! command 2>&1 > - -All other commands are suposed to be a perl code. - -The code to be evaluated may be entered in two ways -or use something like heredoc - -If the first line in a new command starts with <<, the rest of the line -is considered as the heredoc delimiter. As long as you do not enter a -line containing only those characters, the lines are only appended into -a variable. As soon as you close the heredoc, the code is evaluated. - -Otherwise the code you enter is evaluated as soon as you enter a line -finished by a semicolon. - -The value of the last command may be found in \$it. - -You may exit this "shell" by either "exit;" or CTRL+Z. -Please keep in mind that "exit;" will close the whole script, while -CTRL+Z will only close the prompt and the script will continue runing! - -Therefore you should use "exit;" with caution. - -psh $PSH::VERSION by Jenda\@Krynicky.cz -*END* -} - -"I am an excellent programmer"; # A required file must return a true value ;-) - -__END__ - -=head1 NAME - -PSH - perl shell - -Version 0.7 - -=head1 SYNOPSIS - - use PSH; - ... - PSH::prompt; - -=head1 DESCRIPTION - -This module provides a "perl command prompt" facility for your program. -You may do some processing and then simply call PSH::prompt to allow -the user to finish the task if something went wrong by calling the functions -of your program. - -I use it for example at the end of the Golem (peoplemeter data processing software) -import script. Sometimes I get not only the new data, but also some -repairs of old ones and sometimes some stage of import fails. -This perl prompt at the end of the script allows me to fix such problems "by hand". - -=head2 Usage - -This module provides two functions, PSH::prompt and PSH::welcome. -The first prints the "perl$" prompt, waits for user interaction and executes the entered -commands. The user then closes the prompt by pressing CTRL-D (Unix/Mac) or CTRL-Z (Windoze). - -All commands are processed in the same package from which PSH::prompt was -called. You may access all global or local() variables, but of course not -my() variables. - -The call to PSH::prompt returns the value of the last executed statement. - - -Since version 0.4 you may pass two parameters to PSH::prompt : - - PSH::prompt [$prompttext, [ \&evalsub ] ] - -The first sets the prompt used by the module, the second sets the function used -to evaluate the code you entered. Default is - - PSH::prompt 'perl', \&eval; - -The second function prints out the version info. - -=head2 Prompt - -Commands starting by ! are passed to the command prompt, -If the line ends by >, the output of the command is redirected to -variable $it. If you want to catch both STDOUT and STDERR use this: - - perl$ ! command 2>&1 > - -All other commands are supposed to be a perl code. - -The code to be evaluated may be entered in two ways -or use something like heredoc - -If the first line in a new command starts with <<, the rest of the line -is considered as the heredoc delimiter. As long as you do not enter a -line containing only those characters, the lines are only appended into -a variable. As soon as you close the heredoc, the code is evaluated. - -Otherwise the code you enter is evaluated as soon as you enter a line -finished by a semicolon. - -The value of the last command may be found in $it. - -You may exit this "shell" by either "exit;" or CTRL+Z. -Please keep in mind that "exit;" will close the whole script, while -CTRL+Z will only close the prompt and the script will continue running! - -Therefore you should use "exit;" with caution. - -=head2 PSH.config - -In the same directory as PSH.pm may be also file PSH.config. -This file will be "required" whenever you use PSH. You may add some -function definitions and variables there. - -Please keep in mind that this file is required in PSH package so -the variables and functions you define therein are in this package by default! - -Also keep in mind that this file is require()d! -The last statement in this file MUST return a true value!!! -And there must be some command in the file! At least - - 1; - -You should not do any changes to PSH.pm cause it would -be quite hard to upgrade then. If possible, do the necessary personalization -through PSH.config. If you find something that would be useful for other people, -or something you cannot do from within PSH.config, contact me. -I'm always open to suggestions and additions :-) - -=head2 Options and settings - - $PSH::allowsystem = should the prompt allow executing system - commands through "! command" ? Default = yes. - - %PSH::alias = a hash of aliases for commands. - Every time you enter a line starting with an exclamation mark, - the first word is looked up in this hash and if a match is found, - this word is replaced by the value from the hash. - All keys in this hash should be lowercase, the match is case-insensitive. - - You will probably want to populate this hash according to macros in - your preferred shell or OS. On my pages you may find examples for - reading doskey macros and applications registered to Windoze. - - %PSH::specials = a hash of specials - This hash allows you to install additional special characters - similar to "!". If PSH sees a special character (a key from - this hash), it calls the specified function for that character - (the value). Actually it doesn't have to be a character :-) - - Default : $PSH::specials{'!'} = \&PSH::Exec; - - You should not modify this hash directly, you'd better use function - PSH::specials : - - PSH::specials '^' => \&foo; - PSH::specials '!' => undef; - - Otherwise the change may be ignored ! - -=head2 Example - - use PSH; - END {PSH::prompt unless $OK} - $do->some('processing) or die "Error : $do->{error}!\n"; - some(more->commands) or die "Error : some went wrong!\n"; - $OK=1; - __END__ - -This will allow the user to do some by-hand cleansing if an error occures. - - use PSH; - PSH::prompt 'hello', sub {print $_[0]}; - -=head2 Ussage example - - perl$ print 45+6; - 51 - perl$ print 12 - perl> + 15; - 27 - perl$ sub Foo { - perl> print "Foo called\n"; - - ERROR: Missing right bracket at (eval 3) line 5, at end of line - syntax error at (eval 3) line 5, at EOF - - perl$ sub Foo { - perl> print "Foo called\n"; # - perl> }; - - perl$ Foo; - Foo called - - perl$ < - -=head2 AUTHOR - -Jenda@Krynicky.cz - -=cut diff --git a/bin/addcourse b/bin/addcourse index a3c4876d91..89cddcc3b9 100755 --- a/bin/addcourse +++ b/bin/addcourse @@ -1,8 +1,8 @@ #!/usr/bin/env perl ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/addcourse,v 1.20 2006/12/09 03:29:56 sh002i 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 @@ -21,154 +21,260 @@ addcourse - add a course =head1 SYNOPSIS - addcourse [B<--users>=I [B<--professors>=I[,I]...] ] - [B<--templates>=I] I + addcourse [options] COURSEID =head1 DESCRIPTION -Add a course to the courses directory. The required directories will be -created. Optionally, a database can be populated with users and the -F directory can be populated with the contents of another directory. -Also, one or more users can be granted professor privileges. +Add a course to the courses directory. The required directories will be created. +Optionally, a database can be populated with users. Also, one or more users can +be granted professor privileges. =head1 OPTIONS =over +=item B<--db-layout>=I + +The specified database layout will be used in place of the default specified in +F. + =item B<--users>=I The users listed in the comma-separated text file I will be added to the -user list of the new course. +user list of the new course. The format of this file is the same as user lists +exported from WeBWorK. =item B<--professors>=I[,I]... Each I, if it is present in the new course's user list, will be granted professor privileges (i.e. a permission level of 10). Requires B<--users>. -=item B<--templates>=I +=item B<--templates-from>=I -The contents of the directory I will be copied to the F -directory of the new course. +If specified, the contents of the specified course's templates directory are +used to populate the new course's templates directory. =item I The name of the course to create. +=back + =cut +BEGIN { + # hide arguments (there could be passwords there!) + $0 = "$0"; +} + use strict; use warnings; -use FindBin; use Getopt::Long; -use lib "$FindBin::Bin/../lib"; + + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; + my $webwork_dir = $ENV{WEBWORK_ROOT}; + print "addcourse: WeBWorK root directory set to $webwork_dir\n"; + + # link to WeBWorK code libraries + eval "use lib '$webwork_dir/lib'"; die $@ if $@; + eval "use WeBWorK::CourseEnvironment"; die $@ if $@; + + +} + +our $webwork_dir = $ENV{WEBWORK_ROOT}; + + +BEGIN { $main::VERSION = "2.4"; } #hack to set version + + + + +# link to WeBWorK code libraries +eval "use lib '$webwork_dir/lib'"; die $@ if $@; +eval "use WeBWorK::CourseEnvironment"; die $@ if $@; + +################################################################# +# The folloinwg code reads global.conf and extracts the remaining configuration +# variables. There is no need to modify it. +################################################################# + +# link to WeBWorK code libraries +eval "use lib '$webwork_dir/lib'"; die $@ if $@; +eval "use WeBWorK::CourseEnvironment"; die $@ if $@; + +# grab course environment (by reading webwork2/conf/global.conf) +my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_dir }); + +# set important configuration variables (from global.conf) + +my $webwork_url = $ce->{webwork_url}; # e.g. /webwork2 +my $pg_dir = $ce->{pg_dir}; # e.g. /opt/webwork/pg + +################################################################# +# report server setup when child process starts up -- for example when restarting the server +################################################################# +print "addcourse: WeBWorK server is starting\n"; +print "addcourse: WeBWorK root directory set to $webwork_dir in webwork2/conf/webwork.apache2-config\n"; +print "addcourse: The following locations and urls are set in webwork2/conf/global.conf\n"; +print "addcourse: PG root directory set to $pg_dir\n"; +print "addcourse: WeBWorK server userID is ", $ce->{server_userID}, "\n"; +print "addcourse: WeBWorK server groupID is ", $ce->{server_groupID}, "\n"; +print "addcourse: The webwork url on this site is ", $ce->{server_root_url},"$webwork_url\n"; + + +# link to PG code libraries +eval "use lib '$pg_dir/lib'"; die $@ if $@; + +eval q{ use WeBWorK::CourseEnvironment; use WeBWorK::DB; -use WeBWorK::Utils qw/readFile cryptPassword/; +use WeBWorK::File::Classlist; +use WeBWorK::Utils qw(runtime_use readFile cryptPassword); +use WeBWorK::Utils::CourseManagement qw(addCourse deleteCourse listCourses); +}; die $@ if $@; sub usage { - print STDERR "$0 [--users=FILE [--professors=USERID[,USERID]...] ]\n"; - print STDERR "[--templates=DIR] COURSEID\n"; + print STDERR "usage: $0 [options] COURSEID\n"; + print STDERR "Options:\n"; + print STDERR " [--db-layout=LAYOUT]\n"; + print STDERR " [--users=FILE [--professors=USERID[,USERID]...] ]\n"; exit; } +sub usage_error { + print STDERR "$0: @_\n"; + usage(); +} + +my $dbLayout = ""; +my $sql_host = ""; +my $sql_port = ""; +my $sql_user = ""; +my $sql_pass = ""; +my $sql_db = ""; +my $sql_wwhost = ""; +my $globalUserID = ""; my $users = ""; my @professors = (); -my $templates = ""; +my $templates_from = ""; + +##### get command-line options ##### GetOptions( + "db-layout=s" => \$dbLayout, + "sql-host=s" => \$sql_host, + "sql-port=s" => \$sql_port, + "sql-user=s" => \$sql_user, + "sql-pass=s" => \$sql_pass, + "sql-db=s" => \$sql_db, + "sql-wwhost=s" => \$sql_wwhost, + "global-user=s" => \$globalUserID, "users=s" => \$users, "professors=s" => \@professors, - "templates=s" => \$templates, + "templates-from=s" => \$templates_from, ); my %professors = map { $_ => 1 } map { split /,/ } @professors; my $courseID = shift; -#print "users=$users\n"; -#print "professors=@professors\n"; -#print "templates=$templates\n"; -#print "courseID=$courseID\n"; +##### perform sanity checks ##### -unless ($ENV{WEBWORK_ROOT}) { - die "WEBWORK_ROOT not found in environment.\n"; +usage_error("must specify COURSEID.") unless $courseID; + +# bring up a minimal course environment +$ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $courseID +}); + +if ($dbLayout) { + die "Database layout $dbLayout does not exist in the course environment.", + " (It must be defined in global.conf.)\n" + unless exists $ce->{dbLayouts}->{$dbLayout}; +} else { + # use default value + $dbLayout = $ce->{dbLayoutName}; } -unless ($courseID) { - print STDERR "$0: must specify COURSEID.\n"; - usage(); -}; +usage_error("can't specify --professors without also specifying --users.") + if @professors and not $users; -if (@professors and not $users) { - print STDERR "$0: can't specify --professors without also specifying --users.\n"; - usage(); -} +##### set up parameters to pass to addCourse() ##### -# bring up a minimal course environment -my $ce = WeBWorK::CourseEnvironment->new($ENV{WEBWORK_ROOT}, "FAKE_URL_ROOT", - "FAKE_PG_ROOT", $courseID); - -# collect some data -my $coursesDir = $ce->{webworkDirs}->{courses}; -my $courseDir = "$coursesDir/$courseID"; - -# create course directory -#print "mkdir $courseDir\n"; -#mkdir $courseDir or die "Failed to create course directory: $!\n"; - -# populate it with some subdirectories -my @subDirs = sort values %{ $ce->{courseDirs} }; -foreach my $subDir (@subDirs) { - print "mkdir $subDir\n"; - mkdir "$subDir" - or die "Failed to create course directory $subDir: $!\n"; -} +my %courseOptions = ( dbLayoutName => $dbLayout ); + +# this is kinda left over from when we had 'gdbm' and 'sql' database layouts +# below this line, we would grab values from getopt and put them in this hash +# but for now the hash can remain empty +my %dbOptions; +my @users; if ($users) { - # import users - much of this code is burgled from UserList.pm + # this is a hack to create records without bringing up a DB object + #my $db = WeBWorK::DB->new($ce->{dbLayouts}->{$dbLayout}); + my $userClass = $ce->{dbLayouts}->{$dbLayout}->{user}->{record}; + my $passwordClass = $ce->{dbLayouts}->{$dbLayout}->{password}->{record}; + my $permissionClass = $ce->{dbLayouts}->{$dbLayout}->{permission}->{record}; + + runtime_use($userClass); + runtime_use($passwordClass); + runtime_use($permissionClass); - my $db = WeBWorK::DB->new($ce); - my @contents = split /\n/, readFile($users); + # Default status is enrolled -- fetch abbreviation for enrolled + my $default_status_abbrev = $ce->{statuses}->{Enrolled}->{abbrevs}->[0]; - foreach my $string (@contents) { - $string =~ s/^\s+//; - $string =~ s/\s+$//; - my ( - $student_id, $last_name, $first_name, $status, $comment, - $section, $recitation, $email_address, $user_id - ) = split /\s*,\s*/, $string; + # default permission level + my $default_permission_level = $ce->{default_permission_level}; + + my @classlist = parse_classlist($users); + foreach my $record (@classlist) { + my %record = %$record; + my $user_id = $record{user_id}; - my $User = $db->newUser; - $User->user_id($user_id); - $User->first_name($first_name); - $User->last_name($last_name); - $User->email_address($email_address); - $User->student_id($student_id); - $User->status($status); - $User->section($section); - $User->recitation($recitation); - $User->comment($comment); + # set default status is status field is "empty" + $record{status} = $default_status_abbrev + unless defined $record{status} and $record{status} ne ""; - my $PermissionLevel = $db->newPermissionLevel; - $PermissionLevel->user_id($user_id); - if (exists $professors{$user_id}) { - $PermissionLevel->permission(10); - } else { - $PermissionLevel->permission(0); + # set password from student ID if password field is "empty" + if (not defined $record{password} or $record{password} eq "") { + if (defined $record{student_id} and $record{student_id} ne "") { + # crypt the student ID and use that + $record{password} = cryptPassword($record{student_id}); + } else { + # an empty password field in the database disables password login + $record{password} = ""; + } } - my $Password = $db->newPassword; - $Password->user_id($user_id); - $Password->password(cryptPassword($student_id)); + # set default permission level if pedefault_permission_levelrmission level is "empty" + if (not defined $record{status} and $record{status} ne "") { + if (exists $professors{$user_id}) { + $record{permission} = $ce->{userRoles}{professor}; # FIXME hardcoded role name is bad + } else { + $record{permission} = $default_permission_level; + } + } - $db->addUser($User); - $db->addPermissionLevel($PermissionLevel); - $db->addPassword($Password); + my $User = $userClass->new(%record); + my $PermissionLevel = $permissionClass->new(user_id => $user_id, permission => $record{permission}); + my $Password = $passwordClass->new(user_id => $user_id, password => $record{password}); - if (exists $professors{$user_id}) { - print "add professor $user_id\n"; + if (exists $record{permission}) { + $PermissionLevel->permission($record{permission}); delete $professors{$user_id}; - } else { - print "add user $user_id\n"; + } elsif (exists $professors{$user_id}) { + $PermissionLevel->permission(10); + delete $professors{$user_id}; + } + + if (exists $record{password}) { + $Password->password($record{password}); } + + push @users, [ $User, $Password, $PermissionLevel ]; } if (my @ids = keys %professors) { @@ -176,27 +282,29 @@ if ($users) { } } -if ($templates) { - unless (-d "$courseDir/templates") { - warn "$courseDir/templates: not found, creating:\n"; - print "mkdir $courseDir/templates\n"; - mkdir "$courseDir/templates" - or die "Failed to mkdir $courseDir/templates: $!\n"; - } - print "copy $templates/* -> $courseDir/templates\n"; - system "/bin/cp -r $templates/* $courseDir/templates/" - and die "Failed to copy $templates/* to $courseDir/templates: $!\n"; +my %optional_arguments; +if ($templates_from ne "") { + $optional_arguments{templatesFrom} = $templates_from; } -=head1 BUGS - -Databases are created using the database layout specified in the global -configuration file (F), which requires users to temporarily modify -their system's configuration in order to create a course with a nonstandard -database layout. +##### call addCourse(), handle errors ##### + +eval { + addCourse( + courseID => $courseID, + ce => $ce, + courseOptions => \%courseOptions, + dbOptions => \%dbOptions, + users => \@users, + %optional_arguments, + ); +}; -Also, some database drivers are unable to create storage for their data. The -GDBM backend can do this, but the SQL backend cannot (currently). +if ($@) { + my $error = $@; + print STDERR "$error\n"; + exit; +} =head1 AUTHOR diff --git a/bin/check_latex.tex b/bin/check_latex.tex new file mode 100644 index 0000000000..e3c3e04f12 --- /dev/null +++ b/bin/check_latex.tex @@ -0,0 +1,76 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% WeBWorK Online Homework Delivery System +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/bin/check_latex.tex,v 1.2 2004/10/06 21:09:33 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. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\documentclass[10pt,dvips]{amsart} +\usepackage{amsmath,amsfonts,amssymb,multicol} +\usepackage[pdftex]{graphicx} +\usepackage[active,textmath,displaymath]{preview} % needed for dvipng +\usepackage{epstopdf} % needed for eps graphics in CAPA +\usepackage{epsf} +\usepackage{epsfig} % needed for eps graphics in CAPA +\usepackage{pslatex} + + +\pagestyle{plain} +\textheight 9in +\oddsidemargin = -0.42in +\evensidemargin = -0.42in +\textwidth= 7.28in +\columnsep = .25in +\columnseprule = .4pt +\def\endline{\bigskip\hrule width \hsize height 0.8pt } +\newcommand{\lt}{<} +\newcommand{\gt}{>} +\newcommand{\less}{<} +\newcommand{\grt}{>} + +% BEGIN capa tex macros + +\newcommand{\capa}{{\sl C\kern-.10em\raise-.00ex\hbox{\rm A}\kern-.22em% +{\sl P}\kern-.14em\kern-.01em{\rm A}}} + +\newenvironment{choicelist} +{\begin{list}{} + {\setlength{\rightmargin}{0in}\setlength{\leftmargin}{0.13in} + \setlength{\topsep}{0.05in}\setlength{\itemsep}{0.022in} + \setlength{\parsep}{0in}\setlength{\belowdisplayskip}{0.04in} + \setlength{\abovedisplayskip}{0.05in} + \setlength{\abovedisplayshortskip}{-0.04in} + \setlength{\belowdisplayshortskip}{0.04in}} + } +{\end{list}} + +% END capa tex macros + +\begin{document} +\voffset=-0.8in +\newpage +\setcounter{page}{1} +\begin{multicols}{2} +\columnwidth=\linewidth + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Testing the LaTeX set up for WeBWorK. +%{\includegraphics[width = 3.5 in]{prob03av8.pdf}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\end{multicols} +\vfill +\end{document} diff --git a/bin/check_modules.pl b/bin/check_modules.pl new file mode 100755 index 0000000000..6dc265a2e6 --- /dev/null +++ b/bin/check_modules.pl @@ -0,0 +1,167 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @applicationsList = qw( + mkdir + mv + mysql + tar + gzip + latex + pdflatex + dvipng + tth + giftopnm + ppmtopgm + pnmtops + pnmtopng + pngtopnm +); + +my @apache1ModulesList = qw( + Apache + Apache::Constants + Apache::Cookie + Apache::Log + Apache::Request +); + +my @apache2ModulesList = qw( + Apache2::Request + Apache2::Cookie + Apache2::ServerRec + Apache2::ServerUtil +); + +my @modulesList = qw( + Benchmark + Carp + CGI + Data::Dumper + Data::UUID + Date::Format + Date::Parse + DateTime + DBD::mysql + DBI + Digest::MD5 + Email::Address + Errno + Exception::Class + File::Copy + File::Find + File::Path + File::Spec + File::stat + File::Temp + GD + Getopt::Long + Getopt::Std + HTML::Entities + HTML::Tagset + IO::File + Iterator + Iterator::Util + Locale::Maketext::Lexicon + Locale::Maketext::Simple + Mail::Sender + MIME::Base64 + Net::IP + Net::LDAPS + Net::SMTP + Opcode + PadWalker + PHP::Serialization + Pod::Usage + Pod::WSDL + Safe + Scalar::Util + SOAP::Lite + Socket + SQL::Abstract + String::ShellQuote + Text::Wrap + Tie::IxHash + Time::HiRes + Time::Zone + URI::Escape + UUID::Tiny + XML::Parser + XML::Parser::EasyTree + XML::Writer + XMLRPC::Lite +); + +# modules used by disabled code +# RQP::Render (RQP) +# SOAP::Lite (PG::Remote) + +my $apache_version = shift @ARGV; +unless (defined $apache_version and $apache_version =~ /^apache[12]$/) { + warn "invalid apache version specified -- assuming apache2\n"; + warn "usage: $0 { apache1 | apache2 }\n"; + sleep 1; + $apache_version = "apache2"; +} + +if ($apache_version eq "apache1") { + push @modulesList, @apache1ModulesList; +} elsif ($apache_version eq "apache2") { + push @modulesList, @apache2ModulesList; +} + +my @PATH = split(/:/, $ENV{PATH}); +check_apps(@applicationsList); + +check_modules(@modulesList); + +sub check_apps { + my @applicationsList = @_; + print "\nChecking your \$PATH for executables required by WeBWorK...\n"; +# print "\$PATH=", shift @PATH, "\n"; # this throws away the first item -- usually /bin + print "\$PATH="; + print join ("\n", map(" $_", @PATH)), "\n\n"; + + foreach my $app (@applicationsList) { + my $found = which($app); + if ($found) { + print " $app found at $found\n"; + } else { + print "** $app not found in \$PATH\n"; + } + } +} + +sub which { + my $app = shift; + foreach my $path (@PATH) { + return "$path/$app" if -e "$path/$app"; + } +} + +sub check_modules { + my @modulesList = @_; + + print "\nChecking your \@INC for modules required by WeBWorK...\n"; + my @inc = @INC; + print "\@INC="; + print join ("\n", map(" $_", @inc)), "\n\n"; + + foreach my $module (@modulesList) { + eval "use $module"; + if ($@) { + my $file = $module; + $file =~ s|::|/|g; + $file .= ".pm"; + if ($@ =~ /Can't locate $file in \@INC/) { + print "** $module not found in \@INC\n"; + } else { + print "** $module found, but failed to load: $@"; + } + } else { + print " $module found and loaded\n"; + } + } +} diff --git a/bin/convert-functions.pl b/bin/convert-functions.pl new file mode 100755 index 0000000000..f2ad857525 --- /dev/null +++ b/bin/convert-functions.pl @@ -0,0 +1,262 @@ +#! /usr/bin/perl + +# +# Usage: convert-functions [-t | --test] [-q | --quiet] filename [filename ...] +# +# If the filename is '-', act as a filter (read from stdin and write to stdout), +# otherwise, each file is read and modified. +# +# If --test is specified, then no output is written, but you just see what changes +# would have been made. +# +# If --quiet is specified, the conversions are not printed out +# (but the file names still are) +# + +# +# The functions to be converted and their parameter lists. +# +# The hash key is the original function name, and the value is an array of two or three items: +# The first is the name of the routine it is being mapped to +# The second is an array that tells how to map the original functions parameters to the +# new functions hash list (see below) +# The third is an optional hash of parameters that are always passed to the new routine +# +# The values in the array of arguments have several special interpretations: +# A value listed as "undef" will be passed to the new routine as a plain argument (not as +# part of the hash). +# An entry of the form name[n] (where n is a number) will be put in position n of an array +# reference whose key is name in the hash passed to the new routine. (E.g., "limits[0]" +# puts the argument in that position of the original argument list into the first entry +# of limits=>[n,m] in the hash.) The default value for the array hash is given by +# the variable $default{name}, e.g. $default{limits} = ['$funcLLimitDefault','$funcULimitDefault']. +# An entry of '@' means make the rest of the parameters into an array reference and pass them +# as the first parameter to the new routine. +# +# Any extra parameters from the original routine are passed verbatim to the new one. +# + +%function = ( + std_num_cmp => ['num_cmp',[undef,'relTol','format','zeroLevel','zeroLevelTol']], + std_num_cmp_abs => ['num_cmp',[undef,'tol','format'],{tolType=>'absolute'}], + std_num_cmp_list => ['num_cmp',['relTol','format','@']], + std_num_cmp_abs_list => ['num_cmp',['tol','format','@'],{tolType=>'absolute'}], + + arith_num_cmp => ['num_cmp',[undef,'relTol','format','zeroLevel','zeroLevelTol'],{mode=>'arith'}], + arith_num_cmp_abs => ['num_cmp',[undef,'tol','format'],{mode=>'arith',tolType=>'absolute'}], + arith_num_cmp_list => ['num_cmp',['relTol','format','@'],{mode=>'arith'}], + arith_num_cmp_abs_list => ['num_cmp',['tol','format','@'],{mode=>'arith',tolType=>'absolute'}], + + strict_num_cmp => ['num_cmp',[undef,'relTol','format','zeroLevel','zeroLevelTol'],{mode=>'strict'}], + strict_num_cmp_abs => ['num_cmp',[undef,'tol','format'],{mode=>'strict',tolType=>'absolute'}], + strict_num_cmp_list => ['num_cmp',['relTol','format','@'],{mode=>'strict'}], + strict_num_cmp_abs_list => ['num_cmp',['tol','format','@'],{mode=>'strict',tolType=>'absolute'}], + + frac_num_cmp => ['num_cmp',[undef,'relTol','format','zeroLevel','zeroLevelTol'],{mode=>'frac'}], + frac_num_cmp_abs => ['num_cmp',[undef,'tol','format'],{mode=>'frac',tolType=>'absolute'}], + frac_num_cmp_list => ['num_cmp',['relTol','format','@'],{mode=>'frac'}], + frac_num_cmp_abs_list => ['num_cmp',['tol','format','@'],{mode=>'frac',tolType=>'absolute'}], + + std_num_str_cmp => + ['num_cmp',[undef,'strings','relTol','format','zeroLevel','zeroLevelTol']], + + function_cmp => + ['fun_cmp',[undef,'vars','limits[0]','limits[1]','relTol','numPoints','zeroLevel','zeroLevelTol']], + + function_cmp_up_to_constant => + ['fun_cmp',[undef,'vars','limits[0]','limits[1]','relTol','numPoints','maxConstantOfIntegration', + 'zeroLevel','zeroLevelTol'],{mode=>'antider'}], + + function_cmp_abs => + [fun_cmp,[undef,'vars','limits[0]','limits[1]','tol','numPoints'],{tolType=>'absolute'}], + + function_cmp_up_to_constant_abs => + [fun_cmp,[undef,'vars','limits[0]','limits[1]','tol','numPoints','maxConstantOfIntegration'], + {mode=>'antider',tolType=>'absolute'}], + + multivar_function_cmp => ['fun_cmp',[undef,'vars']], + + std_str_cmp => ['str_cmp',[]], + std_str_cmp_list => ['str_cmp',['@']], + std_cs_str_cmp => ['str_cmp',[],{filters=>['trim_whitespace','compress_whitespace']}], + std_cs_str_cmp_list => ['str_cmp',['@'],{filters=>['trim_whitespace','compress_whitespace']}], + strict_str_cmp => ['str_cmp',[],{filters=>['trim_whitespace']}], + strict_str_cmp_list => ['str_cmp',['@'],{filters=>['trim_whitespace']}], + unordered_str_cmp => ['str_cmp',[],{filters=>['remove_whitespace','ignore_order','ignore_case']}], + unordered_str_cmp_list => ['str_cmp',['@'],{filters=>['remove_whitespace','ignore_order','ignore_case']}], + unordered_cs_str_cmp => ['str_cmp',[],{filters=>['remove_whitespace','ignore_order']}], + unordered_cs_str_cmp_list => ['str_cmp',['@'],{filters=>['remove_whitespace','ignore_order']}], + ordered_str_cmp => ['str_cmp',[],{filters=>['remove_whitespace','ignore_case']}], + ordered_str_cmp_list => ['str_cmp',['@'],{filters=>['remove_whitespace','ignore_case']}], + ordered_cs_str_cmp => ['str_cmp',[],{filters=>['remove_whitespace']}], + ordered_cs_str_cmp_list => ['str_cmp',['@'],{filters=>['remove_whitespace']}], + +); + +#numerical_compare_with_units() needs to be handled by hand -- but there are very few uses. + +$default{limits} = ['$funcLLimitDefault','$funcULimitDefault']; + +# +# Make a patter from all the names (we sort be length of the names, to +# make sure prefixes appear later in the list). +# +$pattern = join("|",sort byName keys(%function)); + +sub byName { + return $a <=> $b if length($a) == length($b); + return length($b) <=> length($a); +} + +# +# Remove leading and trailing spaces +# +sub trim { + my $s = shift; + $s =~ s/(^\s+|\s+$)//g; + return $s; +} + +# +# Remove leading comment lines +# +sub trimComments { + my $s = shift; + $s =~ s/^(\s*#.*?(\n|$))*//; + return $s; +} + + +# +# Command-line options and internal state parameters +# +$testing = 0; # true if not writing output files +$quiet = 0; # true if not printing changed function calls +$changed = 0; # true if we have changes a function in the current file + +# +# Read the contents of a file, and search through it for the functions +# above. Then modify the argument lists to use hashes rather than +# direct parameter lists. +# +sub Process { + my @lines; + if ($file eq "-") { + @lines = <>; + open(PGFILE,">&STDOUT"); # redirect this to STDOUT + } elsif ($file eq "--test" || $file eq "-t") { + $testing = 1; return; + } elsif ($file eq "--quiet" || $file eq "-q") { + $quiet = 1; return; + } else { + print stderr "\n" if $changed; + print stderr "Converting: $file\n"; + open(PGFILE,$file) || warn "Can't read '$file': $!"; + @lines = ; close(PGFILE); + open(PGFILE,$testing? ">/dev/null": ">$file"); + } + $changed = 0; + + my $file = join("",@lines); + $file =~ s/\&beginproblem(\(\))?/beginproblem()/gm; # remove unneeded ampersands + $file =~ s/\&ANS\(/ANS\(/gm; # remove unneeded ampersands + $file =~ s/ANS\( */ANS\(/gm; # remove unneeded spaces + my @parts = split(/($pattern)/o,$file); +# +# Because of the parentheses around the pattern above, split returns the pattern +# as well as the stuff it separates. So @parts contains stuff, first function, +# args and more stuff, next function, etc. +# + print PGFILE shift(@parts); + while (my $f = shift(@parts)) { + my ($args,$rest) = GetArgs(shift(@parts)); + unless ($args) {print $f,$rest; next}; # skip it if doesn't look like an actual call + print PGFILE HandleFunction($f,$function{$f},$args),$rest; + } +} + +# +# Convert the list of arguments to appropriate hash values +# (taking into account the interpretations given above +# for the special entries in the list). +# Don't include empty parameters (I don't think this should be a problem). +# Return the modified function call with the new name and hash +# +sub HandleFunction { + my $original = shift; my $f = shift; my $args = shift; + my @names = @{$f->[1]}; my @args = @{$args}; + my ($name,$value); + # + # Get the fixed options needed for this function + # + my %options = %{$f->[2] || {}}; + foreach my $id (keys(%options)) { + if (ref($options{$id}) eq 'ARRAY') { + $options{$id} = '["'.join('","',@{$options{$id}}).'"]'; + } else { + $options{$id} = '"'.$options{$id}.'"'; + } + } + # + # Process the list of arguments supplied by the user + # (treating special cases properly) + # + my @options = (); my @params = (); + while (my ($name,$value) = (shift(@names),shift(@args))) { + last unless defined $value; + unless ($name) {push(@params,$value); next} + if ($name eq '@') {push(@params,'['.join(',',$value,@args).']'); @args = (); last} + if ($name =~ s/\[(\d+)\]$//) { + $options{$name} = $default{$name} unless defined $options{$name}; + $options{$name}[$1] = $value; next; + } + $options{$name} = $value unless $value eq '""' || $value eq "''"; + } + # + # Add the hash values to the new argument list + # + while (($name,$value) = each %options) { + $value = '['.join(',',@{$options{$name}}).']' if ref($value) eq 'ARRAY'; + push(@options,"$name=>$value"); + } + # + # Create the new function and display it + # + my $F = $f->[0].'('.join(', ',@params,@options,@args).')'; + unless ($quiet) { + print stderr " $original(",join(',',@{$args}),") -> $F\n"; + $changed = 1; + } + return $F; +} + +# +# Get the argument list for the function, respecting quotation marks, +# nested parentheses, and so on. Remove comments that might be +# nested within a multi-line function call. +# +sub GetArgs { + my $text = shift; + my @args = (); my $parenCount = 0; my $arg = ""; + return (undef,$text) unless $text =~ s/^\s*\(//; # remove leading spaces and opening paren + $text = trimComments($text); + while ($text =~ s/^((?:"(?:\\.|[^\"])*"|'(?:\\.|[^\'])*'|\\.|[^\\])*?)([(){}\[\],\n])//) { + if ($2 eq '(' || $2 eq '[' || $2 eq '{') {$parenCount++; $arg .= $1.$2; next} + if ($2 eq ')' && $parenCount == 0) {$arg .= $1; push(@args,trim($arg)); last} + if ($2 eq ')' || $2 eq ']' || $2 eq '}') {$parenCount--; $arg .= $1.$2; next} + if ($2 eq "\n") {$arg .= $1; $text = trimComments($text); next} + if ($parenCount == 0) { + push(@args,trim($arg.$1)); $arg = ""; + $text = trimComments($text); + } else {$arg .= $1.$2} + } + $text =~ s/^ +//; # remove unneeded leading spaces + return(\@args,$text); +} + +# +# Process each file +# +push(@ARGV,"-") if (scalar(@ARGV) == 0); +foreach $file (@ARGV) {print Process($file)} +print stderr "\n"; diff --git a/bin/convert_fun_in_dir.sh b/bin/convert_fun_in_dir.sh new file mode 100755 index 0000000000..98a00bc751 --- /dev/null +++ b/bin/convert_fun_in_dir.sh @@ -0,0 +1 @@ +find . -name "*.pg" -exec convert-functions.pl {} ';' diff --git a/bin/delcourse b/bin/delcourse new file mode 100755 index 0000000000..6c5a18a3d2 --- /dev/null +++ b/bin/delcourse @@ -0,0 +1,183 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/delcourse,v 1.4 2006/01/25 23:13:45 sh002i 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. +################################################################################ + +=head1 NAME + +delcourse - delete a course + +=head1 SYNOPSIS + + delcourse [options] COURSEID + +=head1 DESCRIPTION + +Delete a course, including its database and course directory. + +=head1 OPTIONS + +=over + +=item I + +The name of the course to delete. + +=back + +If the course's database layout is sql, the following options are valid: + +=over + +=item B<--sql-host>=I + +Specifies the hostname of the SQL server on the course database resides. If not +specified, the default for your RDBMS will be used. + +=item B<--sql-port>=I + +Specifies the port of the SQL server on the course database resides. If not +specified, the default for your RDBMS will be used. + +=item B<--sql-user>=I + +Specifies the username to use when connecting to the SQL server to delete the +course database. This user must have CREATE, DELETE, FILE, INSERT, SELECT, and +UPDATE privileges, WITH GRANT OPTION. + +=item B<--sql-pass>=I + +Specifies the password to use when connecting to the SQL server. + +=item B<--sql-db>=I + +Specifies the name of the database to delete. (This is usually +"webwork_COURSENAME", but can be overridden by changing the database layout in +F.) + +=back + +=cut + +BEGIN { + # hide arguments (there could be passwords there!) + $0 = "$0"; +} + +use strict; +use warnings; +use Getopt::Long; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; + + die "PG_ROOT not found in environment.\n" + unless exists $ENV{PG_ROOT}; + + # This prevents some spurious warning. + $main::VERSION = "2.4"; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use lib "$ENV{PG_ROOT}/lib"; + +use WeBWorK::CourseEnvironment; +use WeBWorK::DB; +use WeBWorK::Utils qw(runtime_use readFile cryptPassword); +use WeBWorK::Utils::CourseManagement qw(addCourse deleteCourse listCourses); + +sub usage { + print STDERR "usage: $0 [options] COURSEID\n"; + print STDERR "Options:\n"; + print STDERR " for \"sql\" database layout:\n"; + print STDERR " [--sql-host=HOST] [--sql-port=port]\n"; + print STDERR " --sql-user=USER --sql-pass=PASS\n"; + print STDERR " --sql-db=DBNAME\n"; + exit; +} + +sub usage_error { + print STDERR "$0: @_\n"; + usage(); +} + +my $sql_host = ""; +my $sql_port = ""; +my $sql_user = ""; +my $sql_pass = ""; +my $sql_db = ""; + +##### get command-line options ##### + +GetOptions( + "sql-host=s" => \$sql_host, + "sql-port=s" => \$sql_port, + "sql-user=s" => \$sql_user, + "sql-pass=s" => \$sql_pass, + "sql-db=s" => \$sql_db, +); +my $courseID = shift; + +##### perform sanity checks ##### + +usage_error("must specify COURSEID.") unless $courseID; + +# bring up a minimal course environment +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $courseID, +}); + +my $dbLayout = $ce->{dbLayoutName}; + +if ($dbLayout eq "sql") { + usage_error("must specify --sql-user.") unless $sql_user; + usage_error("must specify --sql-pass.") unless $sql_pass; + usage_error("must specify --sql-db.") unless $sql_db; +} + +##### set up parameters to pass to deleteCourse() ##### + +my %dbOptions; +if ($dbLayout eq "sql") { + $dbOptions{host} = $sql_host if $sql_host ne ""; + $dbOptions{port} = $sql_port if $sql_port ne ""; + $dbOptions{username} = $sql_user; + $dbOptions{password} = $sql_pass; + $dbOptions{database} = $sql_db; +} + +##### call deleteCourse(), handle errors ##### + +eval { + deleteCourse( + courseID => $courseID, + ce => $ce, + dbOptions => \%dbOptions, + ); +}; + +if ($@) { + my $error = $@; + print STDERR "$error\n"; + exit; +} + +=head1 AUTHOR + +Written by Sam Hathaway, hathaway at users.sourceforge.net. + +=cut diff --git a/bin/gif2eps b/bin/gif2eps deleted file mode 100755 index 44102f5a1e..0000000000 --- a/bin/gif2eps +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -# This script wraps system-dependant calls to several netpbm utilities. -# In order to use this script, set the NETPBM variable below to the -# directory which contains giftopnm, ppmtopgm, and pnmtops. - -NETPBM=/usr/local/bin - -# If you wish to set the paths to each utility separately, i.e. if they -# are in different locations, do so below: - -GIFTOPNM=$NETPBM/giftopnm; -PPMTOPGM=$NETPBM/ppmtopgm; -PNMTOPS=$NETPBM/pnmtops; - -# There should be no need for customization beyond this point. - -umask 022 -cat $1 | $GIFTOPNM | $PPMTOPGM | $PNMTOPS -noturn 2>/dev/null > $2 - -# CODER NOTE: -# We used to use `pnmdepth 1' instead of ppmtopgm as it creates a -# monochrome image (1 bit) rather than a greyscale image (8 bits). -# However, this was causing improper display of some sort in PDFs. diff --git a/bin/hash2sql b/bin/hash2sql deleted file mode 100755 index e2484cbcb7..0000000000 --- a/bin/hash2sql +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -=head1 NAME - -hash2sql - copies data from a course using a hash-based database (without global tables) -to a course using an sql-based database (with global tables). - -=cut - -use strict; -use warnings; -use FindBin; -use lib "$FindBin::Bin/../lib"; -use WeBWorK::CourseEnvironment; -use WeBWorK::DB; -use WeBWorK::DB::Utils qw(findDefaults); - -sub main(@) { - my ($hashCourse, $sqlCourse) = @_; - - unless ($ENV{WEBWORK_ROOT}) { - die "WEBWORK_ROOT not found in environment.\n"; - } - - unless ($hashCourse and $sqlCourse) { - die "usage: $0 hashCourse sqlCourse\n"; - } - - my $hashCE = WeBWorK::CourseEnvironment->new($ENV{WEBWORK_ROOT}, "", "", $hashCourse); - my $hashDB = WeBWorK::DB->new($hashCE); - - my $sqlCE = WeBWorK::CourseEnvironment->new($ENV{WEBWORK_ROOT}, "", "", $sqlCourse); - my $sqlDB = WeBWorK::DB->new($sqlCE); - - # get names of global record classes - my $globalSetClass = $sqlCE->{dbLayout}->{set}->{record}; - my $globalProblemClass = $sqlCE->{dbLayout}->{problem}->{record}; - - # get non-key, global field names - my @setFields = getNonKeyFields($sqlCE->{dbLayout}->{set}->{record}); - my @problemFields = getNonKeyFields($sqlCE->{dbLayout}->{problem}->{record}); - - # hash to store all sets - my %allSets; - - # populate user, password, permission, and key tables - print "\n---------- adding users: ----------\n\n"; - - foreach my $userID ($hashDB->listUsers()) { - print "adding user $userID:"; - $sqlDB->addUser($hashDB->getUser($userID)); - - my $Password = $hashDB->getPassword($userID); - if ($Password) { - print " +password"; - $sqlDB->addPassword($Password); - } else { - print " -password"; - } - - my $PermissionLevel = $hashDB->getPermissionLevel($userID); - if ($PermissionLevel) { - print " +permissionLevel"; - $sqlDB->addPermissionLevel($PermissionLevel); - } else { - print " -permissionLevel"; - } - - my $Key = $hashDB->getKey($userID); - if ($Key) { - print " +key"; - $sqlDB->addKey($Key) if $Key; - } else { - print " -key"; - } - - print "\n"; - - # also get the names of all sets - foreach my $setID ($hashDB->listUserSets($userID)) { - $allSets{$setID}++; - } - } - - print "\n---------- found these sets: ----------\n\n"; - - print join(" ", keys %allSets), "\n"; - - print "\n---------- determining set defaults: ----------\n\n"; - - foreach my $setID (keys %allSets) { - print "processing set $setID.\n"; - # get a consensus view for each set - my @setUserIDs = $hashDB->listSetUsers($setID); - my @UserSets = map { $hashDB->getUserSet($_, $setID) } @setUserIDs; - my $GlobalSet = findDefaults($globalSetClass, @UserSets); - print "adding global set record.\n"; - $sqlDB->addGlobalSet($GlobalSet); - - # remove default values from each user set - # and store into sql db. - foreach my $UserSet (@UserSets) { - foreach my $field (@setFields) { - if ($UserSet->$field() eq $GlobalSet->$field()) { - $UserSet->$field(undef); - } - } - print "adding user set for user ", $UserSet->user_id, ".\n"; - $sqlDB->addUserSet($UserSet); - } - - # get all problems in this set - my %allProblems; - foreach my $userID ($hashDB->listSetUsers($setID)) { - foreach my $problemID ($hashDB->listUserProblems($userID, $setID)) { - $allProblems{$problemID}++; - } - } - - print "\n----- found these problems in set $setID: ----\n\n"; - - print join(" ", keys %allProblems), "\n"; - - print "\n----- determining defaults for problems in set $setID: -----\n\n"; - - # get a consensus for each problem in this set - foreach my $problemID (keys %allProblems) { - print "determining defaults for problem $problemID.\n"; - my @problemUserIDs = $hashDB->listProblemUsers($setID, $problemID); - my @UserProblems; - print "getting user problem for user:"; - foreach (@problemUserIDs) { - print " $_"; - my $UserProblem = $hashDB->getUserProblem($_, $setID, $problemID); - unless (defined $UserProblem) { - print "(UNDEFINED!)"; - next; - } - push @UserProblems, $UserProblem; - } - print "\n"; - my $GlobalProblem = findDefaults($globalProblemClass, @UserProblems); - print "adding global problem record.\n"; - $sqlDB->addGlobalProblem($GlobalProblem); - - # remove defaults from each user problem - foreach my $UserProblem (@UserProblems) { - foreach my $field (@problemFields) { - if ($UserProblem->$field() eq $GlobalProblem->$field()) { - $UserProblem->$field(undef); - } - } - print "adding user problem for user ", $UserProblem->user_id, ".\n"; - $sqlDB->addUserProblem($UserProblem); - } - } - } -} - -sub getNonKeyFields($) { - my ($Record) = @_; - - my %fields = map { $_ => {} } $Record->FIELDS(); - delete $fields{$_} foreach $Record->KEYFIELDS(); - return keys %fields; -} - -main(@ARGV); diff --git a/bin/integrity_check.pl b/bin/integrity_check.pl new file mode 100644 index 0000000000..69bf341075 --- /dev/null +++ b/bin/integrity_check.pl @@ -0,0 +1,58 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwdb_upgrade,v 1.17 2007/08/13 22:59:50 sh002i 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. +################################################################################ + +use strict; +use warnings; +use Getopt::Std; +use Data::Dumper; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils::CourseIntegrityCheck; +use WeBWorK; + +our ($opt_v); +getopts("v"); + +if ($opt_v) { + $WeBWorK::Debug::Enabled = 1; +} else { + $WeBWorK::Debug::Enabled = 0; +} + + +my $courseName = "tmp_course"; + +my $ce = new WeBWorK::CourseEnvironment( + {webwork_dir=>$ENV{WEBWORK_ROOT}, + courseName=> $courseName + }); + + +print "ce ready $ce"; + +my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck($ce); + +my $return = $CIchecker->checkCourseDirectories(); + +print "result $return"; +1; \ No newline at end of file diff --git a/bin/mkhtmldocs b/bin/mkhtmldocs deleted file mode 100755 index 5ad1998376..0000000000 --- a/bin/mkhtmldocs +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -=head1 NAME - -mkhtmldocs - use pod2html to generate HTML documentation - -=cut - -use strict; -use warnings; -use FindBin; - -# set this to the URL of the htdocs/doc directory. -use constant DOC_BASE => "/webwork2_files/doc"; -# FIXME: this should probably be read from global.conf. - -sub main(@) { - my $force = @_ && $_[0] eq "-f"; - - my $lib = "$FindBin::Bin/../lib"; - my $htdocs = "$FindBin::Bin/../htdocs"; - my $doc = "$htdocs/doc"; - my $htmlroot = DOC_BASE; - - my @modules = `find $lib -name "*.pm"`; - foreach my $module (@modules) { - chomp $module; - - my $docfile = $module; - $docfile =~ s/^$lib/$doc/; - $docfile =~ s/\.pm$/.html/; - - next if not $force and -e $docfile and (stat $docfile)[9] >= (stat $module)[9]; - - my ($docdir) = $docfile =~ m|^(.*)/|; - unless (-e $docdir) { - print "creating missing directory $docdir\n"; - system "mkdir -p $docdir" - and die "mkdir failed: $!\n"; - } - - print "generating documentation for module $module\n"; - system "pod2html --htmlroot=$htmlroot --podroot=$lib --infile=$module --outfile=$docfile" - and die "pod2html failed: $!\n"; - } -} - -main(@ARGV); diff --git a/bin/newpassword b/bin/newpassword new file mode 100755 index 0000000000..60c40818db --- /dev/null +++ b/bin/newpassword @@ -0,0 +1,103 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/newpassword,v 1.3 2006/01/25 23:13:45 sh002i 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. +################################################################################ + +=head1 NAME + +newpassword - change a users password + +=head1 SYNOPSIS + + newpassword COURSEID USERID NEWPASSWORD + +=head1 DESCRIPTION + +Change the password for a user. + +=head1 ARGUMENTS + +=over + +=item I + +The name of the course. + +=item I + +The login name of the user you will change the password for. + +=item I + +The new password. + +=back + +=cut + +BEGIN { + # hide arguments (there could be passwords there!) + $0 = "$0"; +} + +use strict; +use warnings; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::DB; +use WeBWorK::Utils qw(runtime_use readFile cryptPassword); + +if((scalar(@ARGV) != 3)) { + print "\nSyntax is: newpassword CourseID User NewPassword"; + print "\n (e.g. newpassword MAT_123 jjones abracadabra\n\n"; + exit(); +} + +##### get command-line options ##### + +my $courseID = shift; +my $user = shift; +my $newP = shift; + +##### main function ##### + +sub dopasswd { + my ($db, $user, $newpass) = @_; + my $passwordRecord = eval {$db->getPassword($user)}; + die "Can't get password for user |$user| $@" if $@ or not defined($passwordRecord); + my $cryptedPassword = cryptPassword($newP); + $passwordRecord->password($cryptedPassword); + eval {$db->putPassword($passwordRecord) }; + if ($@) { + die "Errors $@ "; + } +} + +# bring up a minimal course environment +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $courseID +}); + +my $db = new WeBWorK::DB($ce->{dbLayout}); + +dopasswd($db, $user, $newP); +print "Changed password for $user in $courseID\n"; diff --git a/bin/timing b/bin/old_scripts/timing similarity index 91% rename from bin/timing rename to bin/old_scripts/timing index 30e403a180..b44e004f55 100755 --- a/bin/timing +++ b/bin/old_scripts/timing @@ -1,8 +1,8 @@ #!/usr/bin/env perl ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/timing,v 1.5 2006/01/25 23:13:45 sh002i 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 diff --git a/bin/old_scripts/ww-update-config b/bin/old_scripts/ww-update-config new file mode 100755 index 0000000000..d1d82f9646 --- /dev/null +++ b/bin/old_scripts/ww-update-config @@ -0,0 +1,43 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $cvs_header_line = '\$' . 'CVSHeader'; + +foreach my $arg (@ARGV) { + my ($conf_file, $dist_file); + + if ($arg =~ /^(.*)\.dist$/) { + $conf_file = $1; + $dist_file = $arg; + } else { + $conf_file = $arg; + $dist_file = "$arg.dist"; + } + + my $conf_version = cvs_version($conf_file) + or die "couldn't find CVS version in $conf_file\n"; + my $dist_version = cvs_version($dist_file) + or die "couldn't find CVS version in $dist_file\n"; + + if ($conf_version eq $dist_version) { + print "$conf_file is up-to-date at version $conf_version.\n"; + next; + } + + #print "conf_version=$conf_version dist_version=$dist_version\n"; + system "cvs diff -r '$conf_version' -r '$dist_version' '$dist_file'" + . "| patch '$conf_file'"; +} + +sub cvs_version { + my ($file) = @_; + open my $fh, "<", $file or die "couldn't open $file for reading: $!\n"; + my $line; + while (my $line = <$fh>) { + if ($line =~ /$cvs_header_line.*?(1(?:\.\d+)+)/) { + return $1; + } + } +} diff --git a/bin/old_scripts/wwaddindexing b/bin/old_scripts/wwaddindexing new file mode 100755 index 0000000000..a258d7edfa --- /dev/null +++ b/bin/old_scripts/wwaddindexing @@ -0,0 +1,137 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwaddindexing,v 1.3 2006/01/25 23:13:45 sh002i 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. +################################################################################ + +=head1 NAME + +wwaddindexing - add indices to an existing sql_single course. + +=head1 SYNOPSIS + + wwaddindexing COURSEID + +=head1 DESCRIPTION + +Adds indices to the course named COURSEID. The course must use the sql_single +database layout. + +=cut + +BEGIN { + # hide arguments (there could be passwords there!) + $0 = "$0"; +} + +use strict; +use warnings; +use DBI; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::DB; +use WeBWorK::Utils qw/runtime_use/; +use WeBWorK::Utils::CourseManagement qw/dbLayoutSQLSources/; + +sub usage { + print STDERR "usage: $0 COURSEID \n"; + exit; +} + +sub usage_error { + print STDERR "$0: @_\n"; + usage(); +} + +# get command-line options +my ($courseID) = @ARGV; + +# perform sanity check +usage_error("must specify COURSEID.") unless $courseID and $courseID ne ""; + +# bring up a minimal course environment +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $courseID, +}); + +# make sure the course actually uses the 'sql_single' layout +usage_error("$courseID: does not use 'sql_single' database layout.") + unless $ce->{dbLayoutName} eq "sql_single"; + +# get database layout source data +my %sources = dbLayoutSQLSources($ce->{dbLayout}); + +foreach my $source (keys %sources) { + my %source = %{$sources{$source}}; + my @tables = @{$source{tables}}; + my $username = $source{username}; + my $password = $source{password}; + + my $dbh = DBI->connect($source, $username, $password); + + foreach my $table (@tables) { + # this stuff straight out of sql_single.pm + my %table = %{ $ce->{dbLayout}{$table} }; + my %params = %{ $table{params} }; + + my $source = $table{source}; + my $tableOverride = $params{tableOverride}; + my $recordClass = $table{record}; + + runtime_use($recordClass); + my @fields = $recordClass->FIELDS; + my @keyfields = $recordClass->KEYFIELDS; + + if (exists $params{fieldOverride}) { + my %fieldOverride = %{ $params{fieldOverride} }; + foreach my $field (@fields) { + $field = $fieldOverride{$field} if exists $fieldOverride{$field}; + } + } + + my @fieldList; + foreach my $start (0 .. $#keyfields) { + my $line = "ADD INDEX ( "; + $line .= join(", ", map { "`$_`(16)" } @keyfields[$start .. $#keyfields]); + $line .= " )"; + push @fieldList, $line; + } + my $fieldString = join(", ", @fieldList); + + my $tableName = $tableOverride || $table; + my $stmt = "ALTER TABLE `$tableName` $fieldString;"; + + unless ($dbh->do($stmt)) { + die "An error occured while trying to modify the course database.\n", + "It is possible that the course database is in an inconsistent state.\n", + "The DBI error message was:\n\n", + $dbh->errstr, "\n"; + } + } + + $dbh->disconnect; +} + +=head1 AUTHOR + +Written by Sam Hathaway, hathaway at users.sourceforge.net. + +=cut diff --git a/bin/old_scripts/wwdb_addgw b/bin/old_scripts/wwdb_addgw new file mode 100755 index 0000000000..939bbf7c94 --- /dev/null +++ b/bin/old_scripts/wwdb_addgw @@ -0,0 +1,395 @@ +#!/usr/bin/perl -w +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwdb_addgw,v 1.3 2006/01/25 23:13:45 sh002i 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. +################################################################################ +# +# wwdb_addgw +# update webwork database tables to add fields for the gateway module +# +# by Gavin LaRose +# +=head1 NAME + +wwdb_addgw - convert SQL databases for WeBWorK 2 to add gateway fields. + +=head1 SYNOPSIS + + wwdb_addgw [-h] [sql|sql_single] + +=head1 DESCRIPTION + +Adds fields to the set and set_user tables in the WeBWorK mysql databases +that are required for the gateway module. The script prompts for which +courses to modify. Adding gateway database fields to existing courses +should have no effect on those courses, even if they are running under a +non-gateway aware version of the WeBWorK system. + +If C<-h> is provided, the script hides the mysql admin password. + +C or C gives the default WeBWorK database format. If +omitted, the script assumes sql_single and prompts to be sure. + +=cut + +use strict; +use DBI; + +# this is necessary on some systems +system("stty erase "); + +my $source = 'DBI:mysql'; + +# fields to add to the set and set_user tables +my %addFields = ( 'assignment_type' => 'text', + 'attempts_per_version' => 'integer', + 'time_interval' => 'integer', + 'versions_per_interval' => 'integer', + 'version_time_limit' => 'integer', + 'version_creation_time' => 'bigint', + 'problem_randorder' => 'integer', + 'version_last_attempt_time' => 'bigint', ); + +# process input data +my $hidepw = 0; +my $dbtype = 'sql_single'; +while ( $_ = shift(@ARGV) ) { + if ( /^-h$/ ) { + $hidepw = 1; + } elsif ( /^-/ ) { + die("Unknown input flag $_.\nUsage: wwdb_addgw [-h] sql|sql_single\n"); + } else { + if ( $_ eq 'sql' || $_ eq 'sql_single' ) { + $dbtype = $_; + } else { + die("Unknown argument $_.\nUsage: wwdb_addgw [-h] " . + "sql|sql_single\n"); + } + } +} + +printHdr( $dbtype ); + +# get database information +my ( $admin, $adminpw ); +( $admin, $adminpw, $dbtype ) = getDBInfo( $hidepw, $dbtype ); + +# connect to database, if we're in sql_single mode; this lets us easily +# get a list of courses to work with. in sql mode, it's harder b/c I can't +# get DBI->data_sources('mysql') to work on my system, so we prompt for +# those separately. if we're in sql single mode, $dbh is a place holder, +# because we have to do the database connects in the subroutines to connect +# to each different database +my $dbh = ''; +if ( $dbtype eq 'sql_single' ) { + $dbh = DBI->connect("$source:webwork", $admin, $adminpw) or + die( $DBI::errstr ); +} + +# get courses list +my @courses = getCourses( $dbtype, $dbh ); + +# now $course{coursename} = format (sql or sql_single) + +# do update +my ( $doneRef, $skipRef ) = updateCourses( $dbtype, $dbh, \@courses, + $admin, $adminpw ); +$dbh->disconnect() if ( $dbh ); + +# all done +confirmUpdate( $dbtype, $doneRef, $skipRef ); + +# end of main +#------------------------------------------------------------------------------- +# subroutines + +sub printHdr { + print < "; + my $admin = ; + chomp( $admin ); + $admin = 'root' if ( ! $admin ); + + print "mySQL login password for $admin > "; + system("stty -echo") if ( $hide ); + my $passwd = ; + if ( $hide ) { system("stty echo"); print "\n"; } + chomp( $passwd ); + die("Error: no password provided\n") if ( ! $passwd ); + + print "WeBWorK database type (sql or sql_single) [$type] > "; + my $dbtype = ; + chomp( $dbtype ); + $dbtype = $type if ( ! $dbtype ); + + return( $admin, $passwd, $dbtype ); +} + +sub getCourses { + my ( $dbtype, $dbh ) = @_; + + my %courses = (); + +# get a course list + if ( $dbtype eq 'sql' ) { + print "courses to update (enter comma separated) > "; + my $crslist = ; + chomp($crslist); + my @crslist = split(/,\s*/, $crslist); + die("Error: no courses specified\n") if ( ! @crslist ); + foreach ( @crslist ) { $courses{$_} = 1; } + + } else { + my $cmd = 'show tables'; + my $st = $dbh->prepare( $cmd ) or die( $dbh->errstr() ); + $st->execute() or die( $st->errstr() ); + my $rowRef = $st->fetchall_arrayref(); + foreach my $r ( @$rowRef ) { + $_ = $r->[0]; + #my ($crs, $tbl) = ( /^([^_]+)_(.*)$/ ); # this fails on courses with underscores in their names + my ($crs) = (/^(.*)_key$/); # match the key table + $courses{$crs} = 1 if ( defined( $crs ) ); + } + die("Error: found now sql_single WeBWorK courses\n") if ( ! %courses ); + } + +# confirm this is correct + print "\nList of courses to update:\n"; + my %nummap = orderedList( %courses ); + printclist( sort keys( %courses ) ); + print "Enter # to edit name, d# to delete from update list, or [cr] to " . + "continue.\n > "; + my $resp = ; + chomp($resp); + while ( $resp ) { + if ( $resp =~ /^\d+$/ ) { + print " old course name $nummap{$resp}; new > "; + delete( $courses{$nummap{$resp}} ); + my $newname = ; + chomp($newname); + $courses{ $newname } = 1; + } elsif ( $resp =~ /^d(\d+)$/ ) { + $resp = $1; + delete( $courses{$nummap{$resp}} ); + } else { + print "unrecognized response: $resp.\n"; + } + %nummap = orderedList( %courses ); + print "Current list of courses to update:\n"; + printclist( sort keys( %courses ) ); + print "Enter #, d# or [cr] > "; + chomp( $resp = ); + } + + my @courses = sort( keys %courses ); + if ( @courses ) { + return @courses; + } else { + die("Error: no courses left to update.\n"); + } +} + +sub orderedList { + my %hash = @_; + my $i=1; + my %nummap = (); + foreach ( sort( keys( %hash ) ) ) { + $nummap{ $i } = $_; + $i++; + } + return %nummap; +} + +sub printclist { + my @list = @_; + +# assumes a 75 column screen + + my $i = 1; + if ( @list <= 3 ) { + foreach ( @list ) { print " $i. $_\n"; $i++ } + } else { + while ( @list >= $i ) { + printf(" %2d. %-19s", $i, $list[$i-1]); + printf(" %2d. %-19s", ($i+1), $list[$i]) if ( @list >= ($i+1) ); + printf(" %2d. %-19s", ($i+2), $list[$i+1]) if ( @list >= ($i+2) ); + print "\n"; + $i+=3; + } + } + return 1; +} + +sub updateCourses { + my ( $dbtype, $dbh, $crsRef, $admin, $adminpw ) = @_; + + my @done = (); + my @skipped = (); + +# give some sense of progress + select STDOUT; $| = 1; # unbuffer output + print "doing update for $dbtype databases.\n"; + +# list of added fields to check for classes that don't need updating + my @newFields = keys( %addFields ); + + foreach my $crs ( @$crsRef ) { + print "updating $crs.\n"; + my $colRef; + + if ( $dbtype eq 'sql' ) { + # we need to get a database handle first + $dbh = DBI->connect("$source:webwork_$crs", $admin, $adminpw) or + die( $DBI::errstr ); + + # now get a list of columns from the set table to check to see if + # we need an update + my $cmd = "show columns from set_not_a_keyword"; + my $st = $dbh->prepare( $cmd ) or die( $dbh->errstr() ); + $st->execute(); + $colRef = $st->fetchall_arrayref(); + + } else { + # for sql_single we already have a database handle; get the set table + # columns and proceed + my $cmd = "show columns from `${crs}_set`"; + print "$cmd\n"; + my $st = $dbh->prepare( $cmd ) or die( $dbh->errstr() ); + $st->execute(); + $colRef = $st->fetchall_arrayref(); + } + + # now, do we have the columns we need already? + my $doneAlready = 0; + foreach my $cols ( @$colRef ) { + if ( inList( $cols->[0], @newFields ) ) { + $doneAlready = 1; + last; + } + } + if ( $doneAlready ) { + push( @skipped, $crs ); + next; + } else { + + # do update for course + my ( $cmd1, $cmd2 ); + if ( $dbtype eq 'sql' ) { + $cmd1 = 'alter table set_not_a_keyword add column'; + $cmd2 = 'alter table set_user add column'; + } else { + $cmd1 = "alter table `${crs}_set` add column"; + $cmd2 = "alter table `${crs}_set_user` add column"; + } + + foreach my $f ( keys %addFields ) { + print "$cmd1 $f $addFields{$f}\n"; + my $st = $dbh->prepare( "$cmd1 $f $addFields{$f}" ) or + die( $dbh->errstr() ); + $st->execute() or die( $st->errstr() ); + } + + foreach my $f ( keys %addFields ) { + print "$cmd2 $f $addFields{$f}\n"; + my $st = $dbh->prepare( "$cmd2 $f $addFields{$f}" ) or + die( $dbh->errstr() ); + $st->execute() or die( $st->errstr() ); + } + + push( @done, $crs ); + } + # if we're doing sql databases, disconnect from this courses' database + $dbh->disconnect() if ( $dbtype eq 'sql' ); + + } # end loop through courses + print "\n"; + + return( \@done, \@skipped ); +} + +sub inList { + my $v = shift(); + foreach ( @_ ) { return 1 if ( $v eq $_ ); } + return 0; +} + +sub confirmUpdate { + my ( $dbtype, $doneRef, $skipRef ) = @_; + + my $s1 = "updated $dbtype courses: "; + my $s2 = "courses not needing updates were skipped: "; + my $l1 = length($s1); + my $l2 = length($s2); + + my $crsList= (@$doneRef) ? join(', ', @$doneRef) : ''; + my $skpList= (@$skipRef) ? join(', ', @$skipRef) : ''; + my $crsString = ( $crsList ) ? + $s1 . hangIndent( $l1, 75, $l1, "$crsList.") . "\n" : ''; + my $skpString = ( $skpList ) ? + $s2 . hangIndent( $l1, 75, $l2, "$skpList." ) : ''; + + print <= $width ) { + $htext .= $line . "\n$ldr"; + $line = "$_ "; + $indent = $hang; + } else { + $line .= "$_ "; + } + } + $htext .= $line if ( $line ); + } + $htext =~ s/\n$ldr$//; + return $htext; +} + +# end of script +#------------------------------------------------------------------------------- diff --git a/bin/old_scripts/wwdb_check b/bin/old_scripts/wwdb_check new file mode 100755 index 0000000000..a624523058 --- /dev/null +++ b/bin/old_scripts/wwdb_check @@ -0,0 +1,1023 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwdb_check,v 1.7 2006/08/14 17:23:56 sh002i 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. +################################################################################ + +=head1 NAME + +wwdb_check - check the schema of an existing WeBWorK database + +=head1 SYNOPSIS + + wwdb_check [-nv] [ COURSE ... ] + +=head1 DESCRIPTION + +Scans an existing WeBWorK database to verify that its structure is correct for +version 0 of the database structure. Version 0 refers to the last version before +automatic database upgrading was added to WeBWorK. This utility should be run +once after upgrading webwork from version 2.2.x to version 2.3.0. + +Once any inconsistencies are fixed using this utility, F should be +run to affect automatic database upgrades to the database version appropriate +for the current version of WeBWorK. + +If no courses are listed on the command line, all courses are checked. Checks +for the following: + +=over + +=item * + +Make sure that the appropriate tables exist for each course. + +=item * + +Make sure that the proper columns exist in each table. + +=item * + +Verify that the proper column type is in use for each column. + +=back + +=head1 OPTIONS + +=over + +=item -n + +Don't offer to fix problems, just report them. + +=item -v + +Verbose output. + +=back + +=cut + +use strict; +use warnings; +use Getopt::Std; +use DBI; +use Data::Dumper; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils qw/runtime_use/; +use WeBWorK::Utils::CourseManagement qw/listCourses/; + +our ($opt_n, $opt_v); +getopts("nv"); + +my $noop = sub {}; + +if ($opt_n) { + *maybe_add_table = $noop; + *maybe_add_field = $noop; + *maybe_change_field = $noop; +} else { + *maybe_add_table = \&ask_add_table; + *maybe_add_field = \&ask_add_field; + *maybe_change_field = \&ask_change_field; +} + +if ($opt_v) { + $| = 1; + *verbose = sub { print STDERR @_ }; +} else { + *verbose = $noop; +} + +use constant DB_VERSION => 0; + +# a random coursename we can grab back out later +#my @chars = ('A'..'Z','a'..'z','0'..'9'); +#my $random_courseID = join("", map { $chars[rand(@chars)] } 1..16); +# fixed courseID for "version zero table data" +my $random_courseID = "6SC36NukknC3IT3M"; + +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $random_courseID, +}); + +my $dbh = DBI->connect( + $ce->{database_dsn}, + $ce->{database_username}, + $ce->{database_password}, + { + PrintError => 0, + RaiseError => 1, + }, +); + +=for comment + + %ww_table_data = ( + $table => { + sql_name => "SQL name for this field, probably contains $random_courseID", + field_order => [ ... ], + keyfield_order => [ ... ], + fields => { + $field => { + sql_name => "SQL name for this field, possibly overridden", + sql_type => "type for this field, from SQL_TYPES in record class", + is_keyfield => "boolean, whether or not this field is a keyfield", + }, + ... + }, + }, + ... + ); + +=cut + +# get table data for the current version of webwork +#my %ww_table_data = get_ww_table_data(); +#$Data::Dumper::Indent = 1; +#print Dumper(\%ww_table_data); +#exit; +# get static table data for version zero of the database +my %ww_table_data = get_version_zero_ww_table_data(); + +my %sql_tables = get_sql_tables(); + +if (exists $sql_tables{dbupgrade}) { + print "A 'dbupgrade' table exists in this database. This suggests that this database may already be upgraded beyond db_version 0. If this is the case, running this utility is not necessary. This utility is only needed to make sure that databases are set up correctly to enter into the automatic upgrade regimen.\n"; + exit unless ask_permission("Go ahead with table checks?", 0); + delete $sql_tables{dbupgrade}; +} + +my @ww_courses = @ARGV; +@ww_courses = listCourses($ce) if not @ww_courses; + +foreach my $ww_course_name (@ww_courses) { + my $ce2 = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $ww_course_name, + }); + + my @diffs = compare_dbLayouts($ce, $ce2); + if (@diffs) { + print "\nThe database layout for course '$ww_course_name' differs from the generic database layout in global.conf. Here's how:\n\n"; + print map("* $_\n", @diffs), "\n"; + next unless ask_permission("Check course '$ww_course_name'?", 0); + } + + print "\nChecking tables for course '$ww_course_name'\n"; + + foreach my $ww_table_name (keys %ww_table_data) { + if ($ce2->{dbLayout}{$ww_table_name}{params}{non_native}) { + verbose("skipping table $ww_table_name for course $ww_course_name -- not a native table.\n"); + } else { + check_table($ww_course_name, $ww_table_name); + } + } +} + +my $qualifier = @ARGV ? " selected" : ""; +print "\nDone checking course tables.\n"; +print "The following tables exist in the database but are not associated with any$qualifier course:\n\n"; +print join("\n", sort keys %sql_tables), "\n\n"; + +exit; + +################################################################################ + +sub get_ww_table_data { + my %result; + + foreach my $table (keys %{$ce->{dbLayout}}) { + my $record_class = $ce->{dbLayout}{$table}{record}; + runtime_use $record_class; + + my @fields = $record_class->FIELDS; + my @types = $record_class->SQL_TYPES; + my @keyfields = $record_class->KEYFIELDS; + my %keyfields; @keyfields{@keyfields} = (); + + my %field_data; + + foreach my $i (0..$#fields) { + my $field = $fields[$i]; + my $field_sql = $ce->{dbLayout}{$table}{params}{fieldOverride}{$field}; + $field_data{$field}{sql_name} = $field_sql || $field; + + my $type = $types[$i]; + $field_data{$field}{sql_type} = $type; + + $field_data{$field}{is_keyfield} = exists $keyfields{$field}; + } + + $result{$table}{fields} = \%field_data; + $result{$table}{field_order} = \@fields; + $result{$table}{keyfield_order} = \@keyfields; + + my $table_sql = $ce->{dbLayout}{$table}{params}{tableOverride}; + $result{$table}{sql_name} = $table_sql || $table; + } + + return %result; +} + +sub get_sql_tables { + my $sql_tables_ref = $dbh->selectcol_arrayref("SHOW TABLES"); + my %sql_tables; @sql_tables{@$sql_tables_ref} = (); + + return %sql_tables; +} + +################################################################################ + +sub check_table { + my ($ww_course_name, $ww_table_name) = @_; + my $sql_table_name = get_sql_table_name($ww_table_data{$ww_table_name}{sql_name}, $ww_course_name); + + verbose("\nChecking '$ww_table_name' table (SQL table '$sql_table_name')\n"); + + if (exists $sql_tables{$sql_table_name}) { + check_fields($ww_course_name, $ww_table_name, $sql_table_name); + delete $sql_tables{$sql_table_name}; + } else { + print "$sql_table_name: table missing\n"; + my $ww_table_rec = $ww_table_data{$ww_table_name}; + if (maybe_add_table($ww_course_name, $ww_table_name)) { + check_fields($ww_course_name, $ww_table_name, $sql_table_name); + delete $sql_tables{$sql_table_name}; + } + } +} + +sub ask_add_table { + my ($ww_course_name, $ww_table_name) = @_; + my $ww_table_rec = $ww_table_data{$ww_table_name}; + my $sql_table_name = get_sql_table_name($ww_table_rec->{sql_name}, $ww_course_name); + + my $stmt = create_table_stmt($ww_table_rec, $sql_table_name); + + print "\nI can add this table to the database with the following SQL statement:\n"; + print "$stmt\n\n"; + print "If this is an upgraded installation, it is possible that '$ww_course_name' is an old GDBM course. If this is the case, you should probably not add this table, as it won't be used.\n"; + return 0 unless ask_permission("Add table '$sql_table_name'?"); + + return unless do_handle_error($dbh, $stmt); + print "Added table '$sql_table_name'.\n\n"; + + return 1; +} + +sub create_table_stmt { + my ($ww_table_rec, $sql_table_name) = @_; + + #print Dumper($ww_table_rec); + + my @field_list; + + # generate a column specification for each field + my @fields = @{$ww_table_rec->{field_order}}; + foreach my $field (@fields) { + my $ww_field_rec = $ww_table_rec->{fields}{$field}; + my $sql_field_name = $ww_field_rec->{sql_name}; + my $sql_field_type = $ww_field_rec->{sql_type}; + + push @field_list, "`$sql_field_name` $sql_field_type"; + } + + # generate an INDEX specification for each all possible sets of keyfields (i.e. 0+1+2, 1+2, 2) + my @keyfields = @{$ww_table_rec->{keyfield_order}}; + foreach my $start (0 .. $#keyfields) { + my @index_components; + + foreach my $component (@keyfields[$start .. $#keyfields]) { + my $ww_field_rec = $ww_table_rec->{fields}{$component}; + my $sql_field_name = $ww_field_rec->{sql_name}; + my $sql_field_type = $ww_field_rec->{sql_type}; + my $length_specifier = ($sql_field_type =~ /int/i) ? "" : "(16)"; + push @index_components, "`$sql_field_name`$length_specifier"; + } + + my $index_string = join(", ", @index_components); + push @field_list, "INDEX ( $index_string )"; + } + + my $field_string = join(", ", @field_list); + my $create_stmt = "CREATE TABLE `$sql_table_name` ( $field_string )"; + + return $create_stmt; +} + +################################################################################ + +sub check_fields { + my ($ww_course_name, $ww_table_name, $sql_table_name) = @_; + + my $describe_data = $dbh->selectall_hashref("DESCRIBE `$sql_table_name`", 1); + + foreach my $ww_field_name (@{$ww_table_data{$ww_table_name}{field_order}}) { + my $ww_field_rec = $ww_table_data{$ww_table_name}{fields}{$ww_field_name}; + my $sql_field_name = $ww_field_rec->{sql_name}; + my $sql_field_rec = $describe_data->{$sql_field_name}; + + verbose("Checking '$ww_field_name' field (SQL field '$sql_table_name.$sql_field_name')\n"); + + #print "$sql_table_name.$sql_field_name:\n"; + #print Dumper($ww_field_rec); + #print Dumper($sql_field_rec); + + if (defined $sql_field_rec) { + my ($sql_base_type) = $sql_field_rec->{Type} =~ /^([^(]*)/; + #print $sql_field_rec->{Type}, " => $sql_base_type\n"; + + my $needs_fixing = 0; + if ($ww_field_name eq "psvn") { + + unless ("int" eq lc($sql_base_type)) { + $needs_fixing = 1; + print "$sql_table_name.$sql_field_name: type should be 'int' but appears to be '", + lc($sql_base_type), "'\n"; + } + + unless (lc($sql_field_rec->{Extra}) =~ /\bauto_increment\b/) { + $needs_fixing = 1; + print "$sql_table_name.$sql_field_name: extra should contain 'auto_increment' but appears to be '", + lc($sql_field_rec->{Extra}), "'\n"; + } + + # FIXME instead of checking this, figure out how to use "SHOW INDEXES FROM `$sql_table_name`" + #unless ("pri" eq lc($sql_field_rec->{Key})) { + # $needs_fixing = 1; + # print "$sql_table_name.$sql_field_name: key should be 'pri' but appears to be '", + # lc($sql_field_rec->{Key}), "'\n"; + #} + + } else { + + unless (lc($ww_field_rec->{sql_type}) eq lc($sql_base_type)) { + $needs_fixing = 1; + print "$sql_table_name.$sql_field_name: type should be '", lc($ww_field_rec->{sql_type}), + "' but appears to be '", lc($sql_base_type), "'\n"; + } + + # FIXME instead of checking this, figure out how to use "SHOW INDEXES FROM `$sql_table_name`" + #unless ( $ww_field_rec->{is_keyfield} == (lc($sql_field_rec->{Key}) eq "mul") ) { + # $needs_fixing = 1; + # print "$sql_table_name.$sql_field_name: key should be '", + # ($ww_field_rec->{is_keyfield} ? "mul" : ""), "' but appears to be '", + # lc($sql_field_rec->{Key}), "'\n"; + #} + } + + $needs_fixing and maybe_change_field($ww_course_name, $ww_table_name, $ww_field_name, $sql_base_type); + + } else { + print "$sql_table_name.$sql_field_name: field missing\n"; + maybe_add_field($ww_course_name, $ww_table_name, $ww_field_name); + } + } +} + +sub ask_add_field { + my ($ww_course_name, $ww_table_name, $ww_field_name) = @_; + my $ww_table_rec = $ww_table_data{$ww_table_name}; + my $sql_table_name = get_sql_table_name($ww_table_rec->{sql_name}, $ww_course_name); + my $sql_field_name = $ww_table_rec->{fields}{$ww_field_name}{sql_name}; + + my $stmt = add_field_stmt($ww_table_rec, $ww_field_name, $sql_table_name); + + print "\nI can add this field to the database with the following SQL statement:\n"; + print "$stmt\n\n"; + return 0 unless ask_permission("Add field '$sql_table_name.$sql_field_name'?"); + + return unless do_handle_error($dbh, $stmt); + print "Added field '$sql_field_name'.\n\n"; + + return 0; +} + +sub add_field_stmt { + my ($ww_table_rec, $ww_field_name, $sql_table_name) = @_; + my $sql_field_name = $ww_table_rec->{fields}{$ww_field_name}{sql_name}; + my $sql_field_type = $ww_table_rec->{fields}{$ww_field_name}{sql_type}; + my $location_modifier = get_location_modifier($ww_table_rec, $ww_field_name); + + return "ALTER TABLE `$sql_table_name` ADD COLUMN `$sql_field_name` $sql_field_type $location_modifier"; +} + +sub get_location_modifier { + my ($ww_table_rec, $ww_field_name) = @_; + + my $field_index = -1; + + for (my $i = 0; $i < @{$ww_table_rec->{field_order}}; $i++) { + if ($ww_table_rec->{field_order}[$i] eq $ww_field_name) { + $field_index = $i; + last; + } + } + + if ($field_index < 0) { + die "field '$ww_field_name' not found in field_order (shouldn't happen!)"; + } elsif ($field_index > 0) { + my $ww_prev_field_name = $ww_table_rec->{field_order}[$field_index-1]; + my $sql_prev_field_name = $ww_table_rec->{fields}{$ww_prev_field_name}{sql_name}; + return "AFTER `$sql_prev_field_name`"; + } else { + return "FIRST"; + } +} + +sub ask_change_field { + my ($ww_course_name, $ww_table_name, $ww_field_name, $sql_curr_base_type) = @_; + my $ww_table_rec = $ww_table_data{$ww_table_name}; + my $sql_table_name = get_sql_table_name($ww_table_rec->{sql_name}, $ww_course_name); + my $sql_field_name = $ww_table_rec->{fields}{$ww_field_name}{sql_name}; + + my @stmts = change_field_stmts($ww_table_rec, $ww_field_name, $sql_table_name, $sql_curr_base_type); + + my $pl = @stmts == 1 ? "" : "s"; + print "\nI can change this field with the following SQL statement$pl:\n"; + print map("$_\n", @stmts), "\n"; + return 0 unless ask_permission("Change field '$sql_table_name.$sql_field_name'?"); + + foreach my $stmt (@stmts) { + return unless do_handle_error($dbh, $stmt); + } + print "Changed field '$sql_field_name'.\n\n"; + + return 0; +} + +sub change_field_stmts { + my ($ww_table_rec, $ww_field_name, $sql_table_name, $sql_curr_base_type) = @_; + my $sql_field_name = $ww_table_rec->{fields}{$ww_field_name}{sql_name}; + my $sql_field_type = $ww_table_rec->{fields}{$ww_field_name}{sql_type}; + + if ($sql_curr_base_type =~ /text/i and $sql_field_type =~ /int/i) { + return ( + "ALTER TABLE `$sql_table_name` CHANGE COLUMN `$sql_field_name` `$sql_field_name` VARCHAR(255)", + "ALTER TABLE `$sql_table_name` CHANGE COLUMN `$sql_field_name` `$sql_field_name` $sql_field_type", + ); + } else { + return "ALTER TABLE `$sql_table_name` CHANGE COLUMN `$sql_field_name` `$sql_field_name` $sql_field_type"; + } +} + +################################################################################ + +sub get_sql_table_name { + my ($template, $course_name) = @_; + + $template =~ s/$random_courseID/$course_name/g; + return $template; +} + +sub ask_permission { + my ($prompt, $default) = @_; + + $default = 1 if not defined $default; + my $options = $default ? "[Y/n]" : "[y/N]"; + + while (1) { + print "$prompt $options "; + my $resp = ; + chomp $resp; + return $default if $resp eq ""; + return 1 if lc $resp eq "y"; + return 0 if lc $resp eq "n"; + $prompt = 'Please enter "y" or "n".'; + } +} + +# no error => returns true +# error, user says continue => returns false +# error, user says don't continue => returns undef +# error, user says exit => exits +sub do_handle_error { + my ($dbh, $stmt) = @_; + + eval { $dbh->do($stmt) }; + if ($@) { + print "SQL statment failed. Here is the error message: $@\n"; + return ask_permission("Continue?", 1); + } else { + return 1; + } +} + +sub compare_dbLayouts { + my ($ce1, $ce2) = @_; + + my $dbLayout1 = $ce1->{dbLayoutName}; + my $dbLayout2 = $ce2->{dbLayoutName}; + #warn "Generic: '$dbLayout1' this course: '$dbLayout2'.\n"; + + # simplisic check for now + if ($dbLayout1 ne $dbLayout2) { + return "\$dbLayoutName differs. Generic: '$dbLayout1' this course: '$dbLayout2'. (If you've created" + . " a modified version of the '$dbLayout1' database layout for use with this course, it's probably" + . " OK to check this course anyway. Just be sure that any fixes this program proposes are" + . " appropriate given your modifications.)"; + } + + return (); +} + +################################################################################ + +sub get_version_zero_ww_table_data { + return ( + 'problem_user' => { + 'fields' => { + 'problem_seed' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'problem_seed' + }, + 'status' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'status' + }, + 'max_attempts' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'max_attempts' + }, + 'value' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'value' + }, + 'last_answer' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'last_answer' + }, + 'source_file' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'source_file' + }, + 'set_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'set_id' + }, + 'problem_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'INT', + 'sql_name' => 'problem_id' + }, + 'num_incorrect' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'num_incorrect' + }, + 'num_correct' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'num_correct' + }, + 'attempted' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'attempted' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + } + }, + 'keyfield_order' => [ + 'user_id', + 'set_id', + 'problem_id' + ], + 'field_order' => [ + 'user_id', + 'set_id', + 'problem_id', + 'source_file', + 'value', + 'max_attempts', + 'problem_seed', + 'status', + 'attempted', + 'last_answer', + 'num_correct', + 'num_incorrect' + ], + 'sql_name' => '6SC36NukknC3IT3M_problem_user' + }, + 'permission' => { + 'fields' => { + 'permission' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'permission' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + } + }, + 'keyfield_order' => [ + 'user_id' + ], + 'field_order' => [ + 'user_id', + 'permission' + ], + 'sql_name' => '6SC36NukknC3IT3M_permission' + }, + 'key' => { + 'fields' => { + 'timestamp' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'timestamp' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + }, + 'key' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'key_not_a_keyword' + } + }, + 'keyfield_order' => [ + 'user_id' + ], + 'field_order' => [ + 'user_id', + 'key', + 'timestamp' + ], + 'sql_name' => '6SC36NukknC3IT3M_key' + }, + 'password' => { + 'fields' => { + 'password' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'password' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + } + }, + 'keyfield_order' => [ + 'user_id' + ], + 'field_order' => [ + 'user_id', + 'password' + ], + 'sql_name' => '6SC36NukknC3IT3M_password' + }, + 'problem' => { + 'fields' => { + 'problem_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'INT', + 'sql_name' => 'problem_id' + }, + 'max_attempts' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'max_attempts' + }, + 'value' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'value' + }, + 'source_file' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'source_file' + }, + 'set_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'set_id' + } + }, + 'keyfield_order' => [ + 'set_id', + 'problem_id' + ], + 'field_order' => [ + 'set_id', + 'problem_id', + 'source_file', + 'value', + 'max_attempts' + ], + 'sql_name' => '6SC36NukknC3IT3M_problem' + }, + 'user' => { + 'fields' => { + 'email_address' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'email_address' + }, + 'student_id' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'student_id' + }, + 'comment' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'comment' + }, + 'status' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'status' + }, + 'recitation' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'recitation' + }, + 'section' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'section' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + }, + 'last_name' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'last_name' + }, + 'first_name' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'first_name' + } + }, + 'keyfield_order' => [ + 'user_id' + ], + 'field_order' => [ + 'user_id', + 'first_name', + 'last_name', + 'email_address', + 'student_id', + 'status', + 'section', + 'recitation', + 'comment' + ], + 'sql_name' => '6SC36NukknC3IT3M_user' + }, + 'set_user' => { + 'fields' => { + 'version_time_limit' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'version_time_limit' + }, + 'set_header' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'set_header' + }, + 'psvn' => { + 'is_keyfield' => '', + 'sql_type' => 'INT NOT NULL PRIMARY KEY AUTO_INCREMENT', + 'sql_name' => 'psvn' + }, + 'hardcopy_header' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'hardcopy_header' + }, + 'version_creation_time' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'version_creation_time' + }, + 'open_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'open_date' + }, + 'problem_randorder' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'problem_randorder' + }, + 'versions_per_interval' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'versions_per_interval' + }, + 'version_last_attempt_time' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'version_last_attempt_time' + }, + 'time_interval' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'time_interval' + }, + 'set_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'set_id' + }, + 'visible' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'visible' + }, + 'assignment_type' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'assignment_type' + }, + 'due_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'due_date' + }, + 'answer_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'answer_date' + }, + 'user_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'user_id' + }, + 'attempts_per_version' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'attempts_per_version' + } + }, + 'keyfield_order' => [ + 'user_id', + 'set_id' + ], + 'field_order' => [ + 'user_id', + 'set_id', + 'psvn', + 'set_header', + 'hardcopy_header', + 'open_date', + 'due_date', + 'answer_date', + 'visible', + 'assignment_type', + 'attempts_per_version', + 'time_interval', + 'versions_per_interval', + 'version_time_limit', + 'version_creation_time', + 'problem_randorder', + 'version_last_attempt_time' + ], + 'sql_name' => '6SC36NukknC3IT3M_set_user' + }, + 'set' => { + 'fields' => { + 'version_last_attempt_time' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'version_last_attempt_time' + }, + 'version_time_limit' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'version_time_limit' + }, + 'versions_per_interval' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'versions_per_interval' + }, + 'time_interval' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'time_interval' + }, + 'set_header' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'set_header' + }, + 'set_id' => { + 'is_keyfield' => 1, + 'sql_type' => 'BLOB', + 'sql_name' => 'set_id' + }, + 'hardcopy_header' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'hardcopy_header' + }, + 'visible' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'visible' + }, + 'version_creation_time' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'version_creation_time' + }, + 'due_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'due_date' + }, + 'assignment_type' => { + 'is_keyfield' => '', + 'sql_type' => 'TEXT', + 'sql_name' => 'assignment_type' + }, + 'open_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'open_date' + }, + 'answer_date' => { + 'is_keyfield' => '', + 'sql_type' => 'BIGINT', + 'sql_name' => 'answer_date' + }, + 'attempts_per_version' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'attempts_per_version' + }, + 'problem_randorder' => { + 'is_keyfield' => '', + 'sql_type' => 'INT', + 'sql_name' => 'problem_randorder' + } + }, + 'keyfield_order' => [ + 'set_id' + ], + 'field_order' => [ + 'set_id', + 'set_header', + 'hardcopy_header', + 'open_date', + 'due_date', + 'answer_date', + 'visible', + 'assignment_type', + 'attempts_per_version', + 'time_interval', + 'versions_per_interval', + 'version_time_limit', + 'version_creation_time', + 'problem_randorder', + 'version_last_attempt_time' + ], + 'sql_name' => '6SC36NukknC3IT3M_set' + } + ); +} diff --git a/bin/old_scripts/wwdb_init b/bin/old_scripts/wwdb_init new file mode 100755 index 0000000000..955aa1fab0 --- /dev/null +++ b/bin/old_scripts/wwdb_init @@ -0,0 +1,198 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright ? 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# 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. +################################################################################ + +use strict; +use warnings; +use Getopt::Std; +use DBI; +use Data::Dumper; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils qw/runtime_use/; + + +our ($opt_v); +getopts("v"); + +if ($opt_v) { + $| = 1; + *verbose = sub { print STDERR @_ }; +} else { + *verbose = sub {}; +} + +# global variables, hah hah. +my ($dbh, %sql_tables); + +################################################################################ + +my $i = -1; +our @DB_VERSIONS; + +$DB_VERSIONS[++$i]{desc} = "is the initial version of database, identical to database structure in WeBWorK 2.2.x."; + +$DB_VERSIONS[++$i]{desc} = "adds dbupgrade table to facilitate automatic database upgrades."; +$DB_VERSIONS[ $i]{global_code} = sub { + $dbh->do("CREATE TABLE `dbupgrade` (`name` VARCHAR(255) NOT NULL PRIMARY KEY, `value` TEXT)"); + $dbh->do("INSERT INTO `dbupgrade` (`name`, `value`) VALUES (?, ?)", {}, "db_version", 1); + $sql_tables{dbupgrade} = (); +}; + + +$DB_VERSIONS[++$i]{desc} = "adds depths table to keep track of dvipng depth information."; +$DB_VERSIONS[ $i]{global_code} = sub { + $dbh->do("CREATE TABLE depths (md5 CHAR(33) NOT NULL, depth SMALLINT, PRIMARY KEY (md5))"); + $sql_tables{depths} = (); +}; + +$DB_VERSIONS[++$i]{desc} = "adds locations, location_addresses, set_locations and set_locations_user tables to database, and add restrict_ip to set and set_user."; +$DB_VERSIONS[ $i]{global_code} = sub { + $dbh->do("CREATE TABLE locations (location_id TINYBLOB NOT NULL, description TEXT, PRIMARY KEY (location_id(1000)))"); + $dbh->do("CREATE TABLE location_addresses (location_id TINYBLOB NOT NULL, ip_mask TINYBLOB NOT NULL, PRIMARY KEY (location_id(500),ip_mask(500)))"); +}; + +our $THIS_DB_VERSION = $i; + +################################################################################ + +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, +}); + +$dbh = DBI->connect( + $ce->{database_dsn}, + $ce->{database_username}, + $ce->{database_password}, + { + PrintError => 0, + RaiseError => 1, + }, +); + +{ + verbose("Obtaining dbupgrade lock...\n"); + my ($lock_status) = $dbh->selectrow_array("SELECT GET_LOCK('dbupgrade', 10)"); + if (not defined $lock_status) { + print "Couldn't obtain lock because an error occurred.\n"; + exit 2; + } + if ($lock_status) { + verbose("Got lock.\n"); + } else { + print "Timed out while waiting for lock.\n"; + exit 2; + } +} + +%sql_tables = get_sql_tables(); + +my $db_version = 0; + + +verbose("Initial db_version is $db_version\n"); + +if ($db_version > $THIS_DB_VERSION) { + print "db_version is $db_version, but the current database version is only $THIS_DB_VERSION. This database was probably used with a newer version of WeBWorK.\n"; + exit; +} + +while ($db_version < $THIS_DB_VERSION) { + $db_version++; + unless (upgrade_to_version($db_version)) { + print "\nUpgrading from version ".($db_version-1)." to $db_version failed.\n\n"; + unless (ask_permission("Ignore this error and go on to the next version?", 0)) { + exit 3; + } + } + set_db_version($db_version); +} + +print "\nDatabase is up-to-date at version $db_version.\n"; + +END { + verbose("Releasing dbupgrade lock...\n"); + my ($lock_status) = $dbh->selectrow_array("SELECT RELEASE_LOCK('dbupgrade')"); + if (not defined $lock_status) { + print "Couldn't release lock because the lock does not exist.\n"; + exit 2; + } + if ($lock_status) { + verbose("Released lock.\n"); + } else { + print "Couldn't release lock because the lock is not held by this thread.\n"; + exit 2; + } +} + +################################################################################ + +sub get_sql_tables { + my $sql_tables_ref = $dbh->selectcol_arrayref("SHOW TABLES"); + my %sql_tables; @sql_tables{@$sql_tables_ref} = (); + + return %sql_tables; +} + +sub set_db_version { + my $vers = shift; + $dbh->do("UPDATE `dbupgrade` SET `value`=? WHERE `name`='db_version'", {}, $vers); +} + +sub upgrade_to_version { + my $vers = shift; + my %info = %{$DB_VERSIONS[$vers]}; + + print "\nUpgrading database from version " . ($vers-1) . " to $vers...\n"; + my $desc = $info{desc} || "has no description."; + print "(Version $vers $desc)\n"; + + if (exists $info{global_code}) { + eval { $info{global_code}->() }; + if ($@) { + print "\nAn error occured while running the system upgrade code for version $vers:\n"; + print "$@"; + return 0 unless ask_permission("Ignore this error and keep going?", 0); + } + } + print "Done.\n"; + return 1; +} + +################################################################################ + +sub ask_permission { + my ($prompt, $default) = @_; + + $default = 1 if not defined $default; + my $options = $default ? "[Y/n]" : "[y/N]"; + + while (1) { + print "$prompt $options "; + my $resp = ; + chomp $resp; + return $default if $resp eq ""; + return 1 if lc $resp eq "y"; + return 0 if lc $resp eq "n"; + $prompt = 'Please enter "y" or "n".'; + } +} \ No newline at end of file diff --git a/bin/old_scripts/wwdb_upgrade b/bin/old_scripts/wwdb_upgrade new file mode 100755 index 0000000000..9a4b6ac859 --- /dev/null +++ b/bin/old_scripts/wwdb_upgrade @@ -0,0 +1,49 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwdb_upgrade,v 1.16 2007/07/22 05:24:20 sh002i 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. +################################################################################ + +use strict; +use warnings; +use Getopt::Std; +use Data::Dumper; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils::DBUpgrade; + +our ($opt_v); +getopts("v"); + +if ($opt_v) { + $WeBWorK::Debug::Enabled = 1; +} else { + $WeBWorK::Debug::Enabled = 0; +} + +my $ce = new WeBWorK::CourseEnvironment({webwork_dir=>$ENV{WEBWORK_ROOT}}); + +my $upgrader = new WeBWorK::Utils::DBUpgrade( + ce => $ce, + verbose_sub => sub { print STDERR @_ }, +); + +$upgrader->do_upgrade; + diff --git a/bin/pg-append-textbook-tags b/bin/pg-append-textbook-tags new file mode 100755 index 0000000000..009ab087c9 --- /dev/null +++ b/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.1 2007/10/17 16:56:16 sh002i 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 diff --git a/bin/pg-find-tags b/bin/pg-find-tags new file mode 100755 index 0000000000..1cd81486bc --- /dev/null +++ b/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.1 2007/10/17 16:56:16 sh002i 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 + + --DESCRIPTION=STRING + --KEYWORDS=STRING + --DBsubject=STRING + --DBchapter=STRING + --DBsection=STRING + --Date=STRING + --Institution=STRING + --Author=STRING + +B + + --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 diff --git a/bin/pg-pull b/bin/pg-pull new file mode 100755 index 0000000000..143d3a3fe2 --- /dev/null +++ b/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.3 2007/10/22 20:32:38 sh002i 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 ($_=)) { 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); diff --git a/bin/png2eps b/bin/png2eps deleted file mode 100755 index ed1a3ebeac..0000000000 --- a/bin/png2eps +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -# This script wraps system-dependant calls to several netpbm utilities. -# In order to use this script, set the NETPBM variable below to the -# directory which contains giftopnm, ppmtopgm, and pnmtops. - -NETPBM=/usr/local/bin - -# If you wish to set the paths to each utility separately, i.e. if they -# are in different locations, do so below: - -PNGTOPNM=$NETPBM/pngtopnm; -PPMTOPGM=$NETPBM/ppmtopgm; -PNMTOPS=$NETPBM/pnmtops; - -# There should be no need for customization beyond this point. - -umask 022 -cat $1 | $PNGTOPNM | $PPMTOPGM | $PNMTOPS -noturn 2>/dev/null > $2 - -# CODER NOTE: -# We used to use `pnmdepth 1' instead of ppmtopgm as it creates a -# monochrome image (1 bit) rather than a greyscale image (8 bits). -# However, this was causing improper display of some sort in PDFs. diff --git a/bin/readURClassList.pl b/bin/readURClassList.pl new file mode 100755 index 0000000000..d354022212 --- /dev/null +++ b/bin/readURClassList.pl @@ -0,0 +1,250 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/readURClassList.pl,v 1.2.2.1 2007/08/13 22:53:39 sh0$ +# +# 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. +################################################################################ + +## readURClassList +## +## This is a specific routine for reading class lists which come from the registrar's +## office at the Univ of Rochester and producing a classlist file usable by WeBWorK + +## It can be used as a sample script for creating similar scripts for othert schools. + +## IT IS ASSUMED THAT THE REGISTRAR'S LIST IS DELIMITED WITH SEMICOLONS (;) + +## Takes three parameters. + +## First, the full file name of the Registrar's class list file with the header +## material stripped off. The format for this file is +## 46246;26661017; ;EASTON, KEVIN LAWRENCE ;01;09;OPT;BS ;400 ; ;keaston@mail.rochester.edu +## 46246;26611026; ;ECKEL, GRETCHEN ANNA ;01;09;BIO;BA ;400 ; ;geckel@mail.rochester.edu +## 46246;26672985; ;FLYER, JOHANNA GABRIELLE ;01;09;UNC;BA ;400 ; ;jflyer@mail.rochester.edu +## 46246;23932114; ;GALVAN, NESTOR YAMIL ;01;08;ECE;BS ;400 ; ;ngalvan@mail.rochester.edu +## 46246;26603063; ;GARCIA, HENRY ALEXANDER ;01;09;BBC;BS ;400 ; ;hgarcia@mail.rochester.edu + +## Second, the full file name of the output WeBWorK classlist file + +## Third, the name of the section, e.g. Pizer or "Pizer MWF" or "" (blank). +## For example classlist files from multiple sections can be concatonated into +## one large classlist file for a whole multisection course +## +## NOTE: Be very careful. The registrar's file may get corrupted by e-mail. + + +require 5.000; + + +$0 =~ s|.*/||; +if (@ARGV != 3) { + print "\n usage: $0 registrar's-list outputfile sectionName\n + e.g. readURClassList.pl ClassRoster.txt mth140A.lst 'Pizer MWF9'\n\n" ; + exit(0); +} + +my ($infile, $outfile, $section) = @ARGV; + +open(REGLIST, "$infile") || die "can't open $infile: $!\n"; +open(OURLIST, ">$outfile") + || die "can't write $outfile: $!\n"; + +while () { + chomp; + next unless($_=~/\w/); ## skip blank lines + s/;$/; /; ## make last field non empty + my @regArray=split(/;/); ## get fields from registrar's file + + foreach (@regArray) { ## clean 'em up! + ($_) = m/^\s*(.*?)\s*$/; ## (remove leading and trailing spaces) + } + + ## extract the relevant fields + + my($crn, $id, $grade, $name, $school, $gradyear, + $major, $degree, $hours, $status, $login ) + = @regArray; + + ## Hack. The login comes as a complete email address. Remove the @ and following sysbols. + + $login =~ s/@.*//; + + + ## massage the data a bit + + my($lname, $fname) = ($name =~ /^(.*),\s*(.*)$/); + if ($login =~/\w/) {$email = "$login".'@mail.rochester.edu';} + else + { + $email= " "; + $login = $id; + } + $status = 'C' unless (defined $status and $status =~/\w/); + ## dump it in our classArray format + ## our format is: $id, $lname, $fname, $status, 'comment ', $dept, $course, $section, + ## $hours, $crn, $year, $semester, $school, $gradyear, $major, $degree, $email, $login + + ## At the U of R 'comment' is blank + ## At present only $id, $lname, $fname, $status, $email, $section, $recitation and $login are used by WeBWorK + + my @classArray=($id, $lname, $fname, $status, ' ', $section, ' ',$email, $login); + + ## and print that sucker! + + print OURLIST join(',', @classArray) , "\n"; +} + close(OURLIST); + + ## arrange the columns nicely + + &columnPrint("$outfile","$outfile"); + + + +sub columnPrint { + +# Takes two parameters. The first is the filename of the +# delimited input file. The second is the name of the +# output file (these names may be the same). The permissions +# and group of the output file will be the same as the +# input file + +# Takes any delimited (with \$DELIM delimiters) file and adds +# extra space if necessary to the fields so that all columns line up. +# The widest field in any column will contain exactly 2 spaces at the +# end of the (non space characters 0f the) field. For example +# ",a very long field entry ," at one extreme and ", ," at the other +# + my($inFileName,$outFileName)=@_; + my($line); + + my ($permission, $gid) = (stat($inFileName))[2,5]; + $permission = ($permission & 0777); ##get rid of file type stuff + + open(INFILE,"$inFileName") or wwerror("$0","can't open $inFileName for reading"); + my @inFile=; + close(INFILE); + + &createFile($outFileName, $permission, $gid); + + my @outFile = &columnArrayArrange(@inFile); + + open(OUTFILE,">$outFileName") or wwerror("$0","can't open $outFileName for writing"); + foreach $line (@outFile) {print OUTFILE $line;} + close(OUTFILE); +} + +sub columnArrayArrange { + +## takes as a parameter a delimited array +## (such as you would get by reading in a delimited file) +## where each element is a line from a delimited file. + +# Outputs an array which adds +# extra space if necessary to the fields so that all columns line up. +# The widest field in any column will contain exactly 1 spaces at the +# end of the (non space characters of the) field. For example +# ",a very long field entry ," at one extreme and ", ," at the other + + my @inFile=@_; + my($i,$tempFileName,$datString,$line); + my @outFile =(); + my(@fieldLength,@datArray); + $i=1; + + @fieldLength=&getFieldLengths(\@inFile); + foreach $line (@inFile) { ## read through file array and get field lengths + unless ($line =~ /\S/) {next;} ## skip blank lines + chomp $line; + @datArray=&getRecord($line); + for ($i=0; $i <=$#datArray; $i++) { + $datArray[$i].=(" " x ($fieldLength[$i]+1-length("$datArray[$i]"))); + } + $datString=join(',',@datArray); + push @outFile , "$datString\n"; + } + @outFile; +} + +sub createFile { + my ($fileName, $permission, $numgid) = @_; + + open(TEMPCREATEFILE, ">$fileName") || + warn " Can't open $fileName"; + my @stat = stat TEMPCREATEFILE; + close(TEMPCREATEFILE); + + ## if the owner of the file is running this script (e.g. when the file is first created) + ## set the permissions and group correctly + if ($< == $stat[4]) { + my $tmp = chmod($permission,$fileName) or + warn "Can't do chmod($permission, $fileName)"; + chown(-1,$numgid,$fileName) or + warn "Can't do chown($numgid, $fileName)"; + } +} + +sub getFieldLengths { + + ## takes as a parameter the reference to a delimited array + ## (such as you would get by reading in a delimited file) + ## where each element is a line from a delimited file. + ## returns an array which holds + ## the maximum field lengths in the file. + + my ($datFileArray_ref)=@_; + my($i); + my(@datArray,@fieldLength,@datFileArray, $line); + @fieldLength=(); + @datFileArray=@$datFileArray_ref; + + foreach $line (@datFileArray) { ## read through file and get field lengths + unless ($line =~ /\S/) {next;} ## skip blank lines + chomp $line; + @datArray=&getRecord($line); + for ($i=0; $i <=$#datArray; $i++) { + $fieldLength[$i] = 0 unless defined $fieldLength[$i]; + $fieldLength[$i]=&max(length("$datArray[$i]"),$fieldLength[$i]); + } + } + return (@fieldLength); +} + + + +sub getRecord + + # Takes a delimited line as a parameter and returns an + # array. Note that all white space is removed. If the + # last field is empty, the last element of the returned + # array is also empty (unlike what the perl split command + # would return). E.G. @lineArray=&getRecord(\$delimitedLine). + { + my $DELIM = ','; + my($line) = $_[0]; + my(@lineArray); + $line.='A'; # add 'A' to end of line so that + # last field is never empty + @lineArray = split(/\s*${DELIM}\s*/,$line); + $lineArray[$#lineArray] =~s/\s*A$//; # remove spaces and the 'A' from last element + $lineArray[0] =~s/^\s*//; # remove white space from first element + @lineArray; + } +sub max { ## find the max element of array + my $out = $_[0]; + my $num; + foreach $num (@_) { + if ((defined $num) and ($num > $out)) {$out = $num;} + } + $out; +} diff --git a/bin/remove_stale_images b/bin/remove_stale_images new file mode 100755 index 0000000000..bb2aac50f5 --- /dev/null +++ b/bin/remove_stale_images @@ -0,0 +1,287 @@ +#!/usr/bin/env perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/remove_stale_images,v 1.7 2008/06/24 22:54:03 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. +################################################################################ + +=head1 NAME + +remove_stale_images - remove old dvipng images + +=head1 SYNOPSIS + + remove_stale_images ARGUMENTS + +=head1 DESCRIPTION + +Remove old dvipng images. + +=head1 ARGUMENTS + +=over + +Arguments are optional. + + --help prints the usage message + --delete delete selected images + --remove same as --delete + --report just report information about image dates + + --days=E final date for images considered is E days ago + E can be a decimal. E defaults to 7 days ago + for deleting, and now for reporting + --access-dates use last-accessed date, the default + --modify-dates use last-modified date (probably the creation date) + + --from=D start deleting D days ago; D can be a decimal + defaulting at the beginning of time + + +You can also just specify E at the end of the command line. + +To get a status report on all of your images, use + + remove_stale_images + +To remove all which have not been accessed in the past 10 days, use + + remove_stale_images --delete --days=10 + +=cut + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use File::Find; +use DBI; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils qw(runtime_use readFile); + +use constant ACCESSED => 8; +use constant MODIFIED => 9; + +my $now = time(); +my $which = ACCESSED; + +##### global variables to hold information from the find + +my $num_removed = 0; +my %kept = (); +my %days=(); +my %week=(); +my %depths=(); +my $grandtotal=0; +my $depthConnection; + +##### get command-line options ##### + +my $start = $now; # way too big, but definitely before the beginning of the epoch +my $end = -1; +my $help = 0; +my $deloption = 0; +my $reportoption = 0; +my $modifydate = 0; +my $accessdate = 0; + +GetOptions('from=s' => \$start, + 'days=s' => \$end, + 'delete|remove' => \$deloption, + 'report' => \$reportoption, + 'modify-dates' => \$modifydate, + 'access-dates' => \$accessdate, + 'help' => \$help) or pod2usage(1); + +pod2usage(1) if $help; + +$reportoption = 1 if(not $deloption); + +if($modifydate and $accessdate) { + print "You cannot specify using both access dates and modify dates\n"; + pod2usage(1); +} + +## now fix up type +my $type = $accessdate ? ACCESSED : MODIFIED; + +if(scalar(@ARGV)>0) { + if(scalar(@ARGV)>1) { + print "Too many arguments given\n"; + pod2usage(1); + } + $end = $ARGV[0]; +} + +$end = 7 if($end == -1 and $deloption); + +if($start<= $end) { + print "The start time must be greater than the end length\n"; + exit(); +} +## Fix up the start and end times + +$start = $now - 24*60*60*$start; +$end = $now - 24*60*60*$end; + +##### "wanted" function for find ##### + +sub wanted { + if(-f $File::Find::name and $File::Find::name =~ /\.png$/) { + my @stat = stat(_); + + if($deloption) { + my $fullmd5 = $File::Find::name; + if(length($_) > (4+32)) { # this is an old style path + $fullmd5 = $_; + $fullmd5 =~ s/\.png$//; + } else { + $fullmd5 =~ s|.*/([^/]+)/([^/]+)$|$1$2|; + $fullmd5 =~ s/\.png$//; + } + if($stat[$type]<$start or $stat[$type]>$end) { + $kept{$fullmd5} = ''; + if($depthConnection) { # hold dvipng depths + my $fetchdepth = $depthConnection->selectall_arrayref(" + SELECT depth FROM depths WHERE md5=\"$fullmd5\""); + my $fetchdepthval = $fetchdepth->[0]->[0]; + $depths{$fullmd5} = $fetchdepthval if($fetchdepthval); + } + } else { + my $result = unlink($File::Find::name); + if($result) { + $num_removed++; + return(); + } else { + # If you don't have permissions to delete a file, you will probably + # mess up the permissions on the equation cache + die "Tried, but could not remove $File::Find::name\n"; + } + } + } + + if($reportoption) { + return() if(not $deloption and ($stat[$type]<$start or $stat[$type]>$end)); + my $lapse = $now - $stat[$type]; + my $val = int(($lapse)/(60*60*24)); + $val = " ".$val if($val<10); + $val = " ".$val if($val<100); + if(defined($days{$val})) {$days{$val} += 1;} else {$days{$val} = 1;} + $val = int($val/7); + if(defined($week{$val})) {$week{$val} += 1;} else {$week{$val} = 1;} + $grandtotal++; + } + + } +} + +##### reporting function ##### + +sub count_report { + my $j; + print "Days\n"; + for $j (sort {$a <=> $b} keys(%days)) { + print "$j $days{$j}\n"; + } + + print "\nWeeks\n"; + for $j (sort {$a <=> $b} keys(%week)) { + print " $j $week{$j}\n"; + } + + print "\nTotal: $grandtotal\n"; +} + +##### main function ##### + + +# bring up a minimal course environment +my $ce = WeBWorK::CourseEnvironment->new({ webwork_dir => $ENV{WEBWORK_ROOT} }); + +my $dirHead = $ce->{webworkDirs}->{equationCache}; +my $cachePath = $ce->{webworkFiles}->{equationCacheDB}; +my $tmpdir = $ce->{webworkDirs}->{tmp}; +my $tmpfile = "$tmpdir/equationcache.tmp"; + +# Prepare to handle depths database table +my $alignType = $ce->{pg}->{displayModeOptions}->{images}->{dvipng_align}; +if($alignType eq 'mysql' and $deloption) { + my $dbinfo = $ce->{pg}->{displayModeOptions}->{images}->{dvipng_depth_db}; + $depthConnection = DBI->connect_cached( + $dbinfo->{dbsource}, + $dbinfo->{user}, + $dbinfo->{passwd}, + { PrintError => 0, RaiseError => 1 }, + ); + print "Could not make database connection for dvipng image depths.\n" unless defined $depthConnection; +} + +find({wanted => \&wanted, follow_fast => 1}, $dirHead); + +print "Removed $num_removed images.\n\n" if($deloption); +count_report() if($reportoption); + + +# For depth database, empty it and insert only values for the images +# we kept. +my $ent; +if($depthConnection) { # clean out database and put back in good values + $depthConnection->do("TRUNCATE depths"); + for my $ent (keys %depths) { + $depthConnection->do("INSERT INTO `depths` VALUES( + \"$ent\", \"$depths{$ent}\")"); + } +} + +## The rest is updating the equation cache if we deleted images and there is a cache +exit() unless($deloption and $num_removed); +exit() unless ($cachePath); +print "Updating the equation cache\n"; +my ($perms, $uid, $groupID) = (stat $cachePath)[2,4,5]; #Get values from current cache file +my $cachevalues = readFile($cachePath); +my @cachelines = split "\n", $cachevalues; +for $ent (@cachelines) { + chomp($ent); + my $entmd5 = $ent; + $entmd5 =~ s/^(\S+)\s+(\S+)\s+.*$/$1$2/; + if(defined($kept{$entmd5})) { + $kept{$entmd5} = $ent; + } +} + +#print "Temp file $tmpfile\n"; +#print "Cache file had group $groupID and perms $perms\n"; +open(OUTF, ">$tmpfile") or die "Could not write to temp file $tmpfile"; +for $ent (keys %kept) { + if($kept{$ent}) { + print OUTF $kept{$ent}."\n"; + } else {print "could not find $ent\n";} +} +close(OUTF); +rename($tmpfile, $cachePath); +# hopefully, either we are superuser and this works, or we are +# the web server in which case it wasn't needed +chmod $perms, $cachePath; +chown $uid, $groupID, $cachePath; + + + + +1; diff --git a/bin/timing_log_check.pl b/bin/timing_log_check.pl new file mode 100755 index 0000000000..2340876ec9 --- /dev/null +++ b/bin/timing_log_check.pl @@ -0,0 +1,108 @@ +#!/usr/bin/perl + +# This script reads the timing log and outputs timing information including +# the number and per cent of accesses taking various amount of time. +# It can give you a rough idea of how well your server id performing. +# If you have a large timing log, it can tale awhile for the script to complete processing. + +# To run the script, cd to the WeBWorK logs directory (usually /opt/webwork/webwork2/logs) +# and enter the command: timing_log_check.pl + +# Note that this assumes perl is locates in /usr/bin/perl (check with the command "which perl") +# and /opt/webwork/webwork2/bin/ is in your path. + + open(PGFILE,'timing.log') || warn "Can't read timing.log: $!"; + my @lines = ; close(PGFILE); + + my $under0point1sec = 0; + my $under0point2sec = 0; + my $under0point5sec = 0; + my $under1sec = 0; + my $under2sec = 0; + my $under3sec = 0; + my $under4sec = 0; + my $under5sec = 0; + my $under10sec = 0; + my $over10sec = 0; + my $nonvalid = 0; + my $line; + my $count = 0; + my $time = 0; + + foreach $line (@lines) { + $count++; + $line =~ /runTime = (\d+\.\d+) sec/; + $time = $1; + if ($time < 0.1){ + $under0point1sec++; + } + elsif ($time < 0.2){ + $under0point2sec++; + } + elsif ($time < 0.5){ + $under0point5sec++; + } + elsif ($time < 1.0){ + $under1sec++; + } + elsif ($time < 2.0){ + $under2sec++; + } + elsif ($time < 3.0){ + $under3sec++; + } + elsif ($time < 4.0){ + $under4sec++; + } + elsif ($time < 5.0){ + $under5sec++; + } + elsif ($time < 10.0){ + $under10sec++; + } + elsif ($time >= 10.0){ + $over10sec++; + } + else { + $nonvalid++; + } + } + my $percent_under0point1sec = 0; + my $percent_under0point2sec = 0; + my $percent_under0point5sec = 0; + my $percent_under1sec = 0; + my $percent_under2sec = 0; + my $percent_under3sec = 0; + my $percent_under4sec = 0; + my $percent_under5sec = 0; + my $percent_under10sec = 0; + my $percent_over10sec = 0; + my $percent_nonvalid = 0; + + $percent_under0point1sec = (int($under0point1sec/$count*1000 +.5))/10; + $percent_under0point2sec = (int($under0point2sec/$count*1000 +.5))/10; + $percent_under0point5sec = (int($under0point5sec/$count*1000 +.5))/10; + $percent_under1sec = (int($under1sec/$count*1000 +.5))/10; + $percent_under2sec = (int($under2sec/$count*1000 +.5))/10; + $percent_under3sec = (int($under3sec/$count*1000 +.5))/10; + $percent_under4sec = (int($under4sec/$count*1000 +.5))/10; + $percent_under5sec = (int($under5sec/$count*1000 +.5))/10; + $percent_under10sec = (int($under10sec/$count*1000 +.5))/10; + $percent_over10sec = (int($over10sec/$count*1000 +.5))/10; + $percent_nonvalid = (int($nonvalid/$count*1000 +.5))/10; + + + print "count = $count\n"; + print "under 0.1 seconds = $under0point1sec: ${percent_under0point1sec}%\n"; + print "between 0.1 and 0.2 seconds = $under0point2sec: ${percent_under0point2sec}%\n"; + print "between 0.2 and 0.5 seconds = $under0point5sec: ${percent_under0point5sec}%\n"; + print "between 0.5 and 1.0 seconds = $under1sec: ${percent_under1sec}%\n"; + print "between 1.0 and 2.0 seconds = $under2sec: ${percent_under2sec}%\n"; + print "between 2.0 and 3.0 seconds = $under3sec: ${percent_under3sec}%\n"; + print "between 3.0 and 4.0 seconds = $under4sec: ${percent_under4sec}%\n"; + print "between 4.0 and 5.0 seconds = $under5sec: ${percent_under5sec}%\n"; + print "between 5.0 and 10.0 seconds = $under10sec: ${percent_under10sec}%\n"; + print "over 10.0 seconds = $over10sec: ${percent_over10sec}%\n"; + print "non valid response = $nonvalid: ${percent_nonvalid}%\n"; + +exit(0); \ No newline at end of file diff --git a/bin/wwapache2ctl.dist b/bin/wwapache2ctl.dist new file mode 100755 index 0000000000..53b561ff32 --- /dev/null +++ b/bin/wwapache2ctl.dist @@ -0,0 +1,110 @@ +#!/bin/sh +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwapache2ctl.dist,v 1.3 2007/08/13 22:59:50 sh002i 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. +################################################################################ + +# This is a wrapper for the apachectl utility suitable for use when doing +# development on the WeBWorK 2 system. This setup allows each developer to run +# an independent Apache server under their own UID, using their own working copy +# of the WeBWorK code. + +################################################################################ +# Configuration +################################################################################ + +# The path to the Apache binary +HTTPD=/usr/local/apache2/bin/httpd + +# The path to your personal webwork2 directory +WEBWORKROOT=$HOME/webwork2 + +# If you're sharing files with systems being run by other users, uncomment this +#umask 2 + +# if you want to use SSL, uncomment this line +#SSL=-DSSL + +# Change these only if you need to customize the locations +PID=$WEBWORKROOT/logs/httpd2.pid +CONFIG_GLOBAL=$WEBWORKROOT/conf/global.conf +CONFIG_DATABASE=$WEBWORKROOT/conf/database.conf +CONFIG_DEVEL=$WEBWORKROOT/conf/devel.apache2-config +CONFIG_DEVEL_SITE=$WEBWORKROOT/conf/devel-site.apache2-config +CONFIG_WEBWORK=$WEBWORKROOT/conf/webwork.apache2-config +CONFIG_THISFILE=$WEBWORKROOT/bin/wwapache2ctl + +################################################################################ +# Implementation +################################################################################ + +usage() { + echo "$0 { start | stop | restart | graceful | configtest }" +} + +checkpid() { + if [ ! -e $PID ]; then + echo "$PID: not found. (Perhaps the server is not running?)" + exit + fi +} + +checknopid() { + if [ -e $PID ]; then + echo "$PID: exists. (Perhaps the server is already running?)" + exit + fi +} + +checkconfs() { + for conf in "$@"; do + checkconf "$conf" + done +} + +checkconf() { + if [ $1.dist -nt $1 ]; then + echo "WARNING: "`basename $1.dist`" is newer than "`basename $1`": UPDATE "`basename $1`"!" + fi +} + +case $1 in + start) + checknopid + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + mkdir -pv "$WEBWORKROOT"/run # hack for httpd.mm + "$HTTPD" $SSL -d "$WEBWORKROOT" -f "$CONFIG_DEVEL" + ;; + stop) + checkpid + kill -TERM `cat "$PID"` + rm -f "$PID" + ;; + restart) + checkpid + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + kill -HUP `cat "$PID"` + ;; + graceful) + checkpid + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + kill -USR1 `cat "$PID"` + ;; + configtest) + "$HTTPD" $SSL -d "$WEBWORKROOT" -f "$CONFIG_DEVEL" -t + ;; + *) + usage + ;; +esac diff --git a/bin/wwapachectl.dist b/bin/wwapachectl.dist index a955a35906..9175ba6a8d 100755 --- a/bin/wwapachectl.dist +++ b/bin/wwapachectl.dist @@ -1,8 +1,8 @@ #!/bin/sh ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwapachectl.dist,v 1.17 2006/05/31 18:25:18 sh002i 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 @@ -15,12 +15,39 @@ # Artistic License for more details. ################################################################################ -# Set the following values to correspond to your local configuration +# This is a wrapper for the apachectl utility suitable for use when doing +# development on the WeBWorK 2 system. This setup allows each developer to run +# an independent Apache server under their own UID, using their own working copy +# of the WeBWorK code. + +################################################################################ +# Configuration +################################################################################ + +# The path to the Apache binary HTTPD=/usr/local/sbin/httpd -WEBWORKROOT=/home/yourname/webwork-modperl -CONFIG=$WEBWORKROOT/conf/httpd-wwmp.conf +# The path to your personal webwork2 directory +WEBWORKROOT=$HOME/webwork2 + +# If you're sharing files with systems being run by other users, uncomment this +#umask 2 + +# if you want to use SSL, uncomment this line +#SSL=-DSSL + +# Change these only if you need to customize the locations PID=$WEBWORKROOT/logs/httpd.pid +CONFIG_GLOBAL=$WEBWORKROOT/conf/global.conf +CONFIG_DATABASE=$WEBWORKROOT/conf/database.conf +CONFIG_DEVEL=$WEBWORKROOT/conf/devel.apache-config +CONFIG_DEVEL_SITE=$WEBWORKROOT/conf/devel-site.apache-config +CONFIG_WEBWORK=$WEBWORKROOT/conf/webwork.apache-config +CONFIG_THISFILE=$WEBWORKROOT/bin/wwapachectl + +################################################################################ +# Implementation +################################################################################ usage() { echo "$0 { start | stop | restart | graceful | configtest }" @@ -40,25 +67,41 @@ checknopid() { fi } +checkconfs() { + for conf in "$@"; do + checkconf "$conf" + done +} + +checkconf() { + if [ $1.dist -nt $1 ]; then + echo "WARNING: "`basename $1.dist`" is newer than "`basename $1`": UPDATE "`basename $1`"!" + fi +} + case $1 in start) checknopid - "${HTTPD}" -f "${CONFIG}" + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + mkdir -pv "$WEBWORKROOT"/run # hack for httpd.mm + "$HTTPD" -f "$CONFIG_DEVEL" -d "$WEBWORKROOT" "$SSL" ;; stop) checkpid - kill -TERM `cat "${PID}"` + kill -TERM `cat "$PID"` ;; restart) checkpid - kill -HUP `cat "${PID}"` + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + kill -HUP `cat "$PID"` ;; graceful) checkpid - kill -USR1 `cat "${PID}"` + checkconfs "$CONFIG_GLOBAL" "$CONFIG_DATABASE" "$CONFIG_DEVEL" "$CONFIG_DEVEL_SITE" "$CONFIG_WEBWORK" "$CONFIG_THISFILE" + kill -USR1 `cat "$PID"` ;; configtest) - "${HTTPD}" -t -f "${CONFIG}" + "$HTTPD" -t -f "$CONFIG_DEVEL" -d "$WEBWORKROOT" "$SSL" ;; *) usage diff --git a/bin/wwdb b/bin/wwdb index 45485f318c..834fadf86d 100755 --- a/bin/wwdb +++ b/bin/wwdb @@ -1,8 +1,8 @@ -#!/usr/bin/env perl +#!/usr/bin/perl ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwdb,v 1.13 2006/01/25 23:13:45 sh002i 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 @@ -17,109 +17,111 @@ =head1 NAME -wwdb - command-line interface to the WeBWorK databases (WWDBv2). +wwdb - export and import webwork databases. + +=head1 SYNOPSIS + + wwdb [-f] course { import | export } file [table ...] + +=head1 DESCRIPTION + +Exports data from a course database to an XML file, or imports data from an XML +file to a course database. Optionally restrict which tables are imported or +exported and specify a duplicate policy. + +=head1 OPTIONS + +=over + +=item -f + +Overwite duplicate records. + +=item course + +Course to use for import or export. + +=item { import | export } + +Specify action -- export or import data. + +=item file + +XML file to write to (in the case of export) or read from (in the case of +import). + +=item [table ...] + +If specified, only the listed tables will be imported or exported. + +=back =cut use strict; use warnings; -use FindBin; -use lib "$FindBin::Bin/../lib"; -use WeBWorK::ContentGenerator; # for cook_args +use Getopt::Std; + +BEGIN { + die "WEBWORK_ROOT not found in environment.\n" + unless exists $ENV{WEBWORK_ROOT}; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; use WeBWorK::CourseEnvironment; use WeBWorK::DB; +use WeBWorK::Utils::DBImportExport qw/listTables dbExport dbImport/; -# Command line syntax -# -# wwdb course command [ argument ... ] -# -# Commands correspond to the functions of WeBWorK::DB. Arguments are either -# strings or records. To represent records, use the following syntax: -# -# {id="sh002i" first_name="Sam" last_name="Hathaway"} -# -# You'll have to protect arguments like this from your shell by enclosing them -# in single quotes. -# -# The special string \undef represents an undefined value. You'll have to -# protect this from your shell as well. Use \\undef or '\undef'. - -sub main(@) { - my ($course, $command, @arguments) = @_; - - unless ($course and $command) { - die "usage: $0 course command [arguments ...]\n"; - } - - my $ce = WeBWorK::CourseEnvironment->new($ENV{WEBWORK_ROOT}, "", "", $course); - my $db = WeBWorK::DB->new($ce); - - if ($command eq "dumpDB") { - print $db->$command("set_user"); - exit; - } - - die "$command: unsupported command.\n" - unless $db->can($command); - - my ($verb, $noun) = $command =~ m/^(list|add|get|put|delete)(.*?)s?$/; - - my $type = "WeBWorK::DB::Record::"; - if ($noun =~ m/^Global(User)?(Set|Problem)$/) { - $type .= "$1$2"; - } else { - $type .= $noun; - } - - foreach (@arguments) { - if (m/^\\undef$/) { - $_ = undef; - } elsif (m/^{(.*)}$/) { - $_ = string2record($type, $1); - } - } - - my @result = $db->$command(@arguments); - - if ($verb eq "list") { - print join("\n", @result), "\n"; - } elsif ($verb eq "add") { - print "result: $result[0]\n"; - } elsif ($verb eq "get") { - if (defined $result[0]) { - print "{", record2string($result[0]), "}\n"; - } else { - print join("/", @arguments), ": record not found\n"; - } - } elsif ($verb eq "put") { - print "result: $result[0]\n"; - } elsif ($verb eq "delete") { - print "result: $result[0]\n"; - } +sub usage { + print STDERR "usage: $0 [-f] course { import | export } file [table ...]\n"; + print STDERR "tables: ", join(" ", listTables()), "\n"; + exit 1; } -sub string2record($$) { - my ($type, $string) = @_; - my %hash = WeBWorK::ContentGenerator::cook_args($string); - my $record = $type->new(%hash); - return $record; -} +our $opt_f; +getopts("f"); + +my ($course, $command, $file, @tables) = @ARGV; + +usage() unless $course and $command and $file; + +my $ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $course, +}); -sub record2string($) { - my ($record) = @_; - my $string; - foreach my $key ($record->FIELDS()) { - my $value = $record->$key(); - if (defined $value) { - $value =~ s/"/\\"/g; - $value = "\"$value\""; - } else { - $value = "NULL"; - } - $string .= "$key=$value "; +my $db = WeBWorK::DB->new($ce->{dbLayout}); + +my @errors; + +if ($command eq "export") { + my $fh; + if ($file eq "-") { + $fh = *STDOUT; + } else { + open $fh, ">", $file or die "failed to open file '$file' for writing: $!\n"; } - chop $string; - return $string; + @errors = dbExport( + db => $db, + xml => $fh, + tables => \@tables, + ); + close $fh; +} elsif ($command eq "import") { + my $conflict = ($opt_f ? "replace" : "skip"); + open my $fh, "<", $file or die "failed to open file '$file' for writing: $!\n"; + @errors = dbImport( + db => $db, + xml => $fh, + tables => \@tables, + conflict => $conflict, + ); + close $fh; +} else { + die "$command: unrecognized command.\n"; } -main(@ARGV); +if (@errors) { + warn "The following errors occured:\n", map { "* $_\n" } @errors; + exit 1; +} diff --git a/bin/wwsh b/bin/wwsh index aa1352625f..2afdcf2159 100755 --- a/bin/wwsh +++ b/bin/wwsh @@ -1,8 +1,8 @@ -#!/usr/bin/env perl +#!/usr/bin/perl -d ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/bin/wwsh,v 1.10 2006/05/31 01:07:25 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 @@ -21,45 +21,38 @@ wwdb - command-line interface to the WeBWorK libraries. =cut -use strict; -use warnings; -use FindBin; -use lib "$FindBin::Bin"; -use PSH; -use lib "$FindBin::Bin/../lib"; -use WeBWorK::CourseEnvironment; -use WeBWorK::DB; +use Data::Dumper; -sub main(@) { - my ($course, $cmd) = @_; - +BEGIN { + DB::parse_options("NonStop=1"); unless ($ENV{WEBWORK_ROOT}) { die "WEBWORK_ROOT not found in environment.\n"; } - - unless ($course) { - die "usage: $0 course\n"; - } - - our $ce = WeBWorK::CourseEnvironment->new($ENV{WEBWORK_ROOT}, "", "", $course); - our $db = WeBWorK::DB->new($ce); - (undef) = $db; # placate warnings - - if ($cmd) { - no warnings; - no strict; - eval $cmd; - die $@ if $@; - use strict; - use warnings; - } else { - print <<'EOF'; +} + +use lib "$ENV{WEBWORK_ROOT}/lib"; +use WeBWorK::CourseEnvironment; +use WeBWorK::DB; + +our $ce; +our $db; + +my $courseID = shift @ARGV; +unless ($courseID) { + die "usage: $0 courseID\n"; +} + +$ce = WeBWorK::CourseEnvironment->new({ + webwork_dir => $ENV{WEBWORK_ROOT}, + courseName => $courseID, +}); +$db = WeBWorK::DB->new($ce->{dbLayout}); + +print <<'EOF'; wwsh - The WeBWorK Shell Available objects: $ce (WeBWorK::CourseEnvironment) $db (WeBWorK::DB) +Available modules: Data::Dumper EOF - PSH::prompt(); - } -} -main(@ARGV); +DB::parse_options("NonStop=0"); diff --git a/clients/README b/clients/README new file mode 100644 index 0000000000..9c490b7bdc --- /dev/null +++ b/clients/README @@ -0,0 +1,73 @@ +2010.05.11 + +At this point renderProblem.pl is the most up to date and works with BBedit to +to send a local file to an xmlserver (mod_xmlrpc) to be rendered. In addition the returned' +file's question form action url is pointed to the same server with the path ending in xml2html. +This allows one to enter answers and test whether they work. + +There were changes to webwork2/lib/WebworkWebservice.pm to make it work with Apache2. +There were also changes to webwork2/lib/WeBWorK/URLpath.pm and a new +module webwork2/lib/WeBWorK/ContentGenerator/xmlViaHTTP.pm + +The webwork_xmplrp_client and webwork_soap_client.pl have not been updated, but may still work. + +to do: + •add facilities for inspecting libraries via xmlrpc and html2xml links + •figure out how to automate the configuration of these xmlrpc files + Apache2::ServerUtil should do this?? but I can't figure out how. + •I suspect that auxiliary html files and applets don't work yet. + + I'm pretty sure that there is no security checking to insure that only appropriate + people have access to the xmlrpc webservice. +############################################################################################## + + +These test clients will process these two commands: + +./webwork_xmlrpc_client.pl renderProblem input.txt >foo.txt +./webwork_xmlrpc_client.pl listLibraries +or + +./webwork_soap_client.pl renderProblem input.txt >foo.txt +./webwork_soap_client.pl listLibraries + + +You may need to adjust some hardwired parameters in the scripts so that +you are pointed at the correct servers. :-) + +The scripts hello_world_xmlrpc.pl and hello_world_soap.pl take no arguments +and can be used to test the connection with the chosen servers. + +The scripts renderProblem.pl and renderProblem_rawoutput.pl are versions of the +xmlrpc_xmlrp_client which can be used as filters. The were designed to be used with +bbedit (Mac editor) to send a PG problem file of to a webservice to be rendered. + +The renderProblem_rawoutput version decodes the xml response and prints it as perl structures +through "less". + +The renderProblem.pl pipes the HTML part of the response through safari (other browsers could +be used) for viewing. This is usually what is wanted for testing. Currently the error recovery +is minimal, so error messages sent from the server may totally confuse the client (which +will probably report a bad result type). The resulting web page is live and pointed +to a responder at webhost.math.rochester.edu. When that server is working you can +actually submit an answer and have it evaluated. It's a very old, tired PC running +Frontier as a server, so be patient with it. :-) + + +checkProblem.pl will take a problem source, send it to the webserver for checking +and record the result in a log file. It is very similar to renderProblem.pl. + +check_problems_in_dir.sh will apply checkProblem.pl to all .pg problems found +underneath the current directory in which the command is executed. I've used +it to check all the problems in a directory for warning errors. There are more +subtle checks that could be made, such as checking to see that correct +answers work. + +Typically one downloads a problem library from the SVN +and then uses check_problems_in_dir.sh to walk through it. By modifying +checkProblems and webwork_xmlrpc_inc.pl one could have the HTML version +of each problem dumped to a text file, or by changing the DISPLAYMODE to +"tex" instead of image one could dump the TeX version of each file. + + +-- Mike Gage 1/1/2007 diff --git a/clients/bad_input.txt b/clients/bad_input.txt new file mode 100644 index 0000000000..bf7d8d8d1e --- /dev/null +++ b/clients/bad_input.txt @@ -0,0 +1,108 @@ +##DESCRIPTION +## A very simple first problem +##ENDDESCRIPTION +##KEYWORDS('algebra') +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; +$a = random(-10,-1,1); +$b = random(1,11,1); +$c = random(1,11,1); +$d = random(1,11,1); + + +=123; + +#warn "foobar"; + +#DEBUG_MESSAGE("this is a debuggin message."); + +#WARN_MESSAGE("this is a warning message."); + +BEGIN_TEXT +$PAR +displayMode is $displayMode $BR +$PAR +This problem demonstrates how you enter numerical answers into WeBWorK. $PAR +Evaluate the expression \(3($a )($b -$c -2($d ))\): + + \{ ans_rule(10) \} + +$BR +END_TEXT +$ans = 3*($a)*($b-$c-2*($d)); + +&ANS(strict_num_cmp($ans)); + +BEGIN_TEXT + +In the case above you need to enter a number, since we're testing whether you can multiply +out these numbers. (You can use a calculator if you want.) +$PAR +For most problems, you will be able to get WeBWorK to +do some of the work for you. For example +$BR +Calculate ($a) * ($b): \{ ans_rule()\} +$BR +END_TEXT +$ans = $a*$b; + +&ANS(std_num_cmp($ans)); + +BEGIN_TEXT +The asterisk is what most computers use to denote multiplication and you can use this with WeBWorK. +But WeBWorK will also allow use to use a space to denote multiplication. +You can either \($a * $b\) or \{$a*$b\} or even \($a \ $b\). All will work. Try them. +$PAR +Now try calculating the sine of 45 degrees ( that's sine of pi over 4 in radians +and numerically sin(pi/4) equals \{1/sqrt(2)\} or, more precisely, \(1/\sqrt{2} \) ). +You can enter this as sin(pi/4) , as +sin(3.1415926/4), as 1/sqrt(2), as 2**(-.5), etc. This is because WeBWorK knows about +functions like sin and sqrt (square root). (Note: exponents +can be indicated by either a "caret" or **). Try it.$BR \( \sin(\pi/4) = \) \{ ans_rule(20) \}$PAR + Here's the +\{ +htmlLink(qq!http://webwork.math.rochester.edu/webwork_system_html/docs/docs/pglanguage/availablefunctions.html!,"list +of the functions") \} + which WeBWorK understands. WeBWorK ALWAYS uses radian mode for trig functions. + $PAR +END_TEXT + +&ANS( std_num_cmp(sin(3.1415926/4)) ); +BEGIN_TEXT +You can also use juxtaposition to denote multiplication. E.g. enter \( 2\sin(3\pi/2) \). +You can enter this as 2*sin(3*pi/2) or more simply as 2sin(3pi/2). Try it: $BR +\{ ans_rule(20) \}$PAR + +END_TEXT + +$pi = 4*atan(1); +&ANS( std_num_cmp(2*sin(3*$pi/2)) ); + +BEGIN_TEXT +Sometimes you need to use ( )'s to make your meaning clear. E.g. 1/2+3 is 3.5, but 1/(2+3) is .2 Why? +Try entering both and use the ${LQ}Preview${RQ} button below to see the difference. In addition to +( )'s, you can also use [ ]'s and $LB ${RB}'s. $BR +\{ ans_rule(20) \}$PAR +END_TEXT + +&ANS( std_num_cmp(.2)); + +BEGIN_TEXT +You can always try to enter answers and let WeBWorK do the calculating. +WeBWorK will tell you if the problem requires a strict numerical answer. +The way we use WeBWorK in this class there is no penalty for getting an answer wrong. What counts +is that you get the answer right eventually (before the due date). For complicated answers, +you should use the ${LQ}Preview${RQ} button to check for syntax errors and also to check that the answer +you enter is really what you think it is. +END_TEXT + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/clients/checkProblem.pl b/clients/checkProblem.pl new file mode 100755 index 0000000000..5a60b6cee6 --- /dev/null +++ b/clients/checkProblem.pl @@ -0,0 +1,577 @@ +#!/usr/bin/perl -w + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/clients/renderProblem.pl,v 1.4 2010/05/11 15:44:05 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. +################################################################################ + +=head1 NAME + +webwork2/clients/renderProblem.pl + +This script will take a file and send it to a WeBWorK daemon webservice +to have it rendered. The result is split into the basic HTML rendering +and evaluation of answers and then passed to a browser for printing. + +The formatting allows the browser presentation to be interactive with the +daemon running the script webwork2/lib/renderViaXMLRPC.pm + +Rembember to configure the local output file and display command !!!!!!!! + +=cut + +use strict; +use warnings; + + + +################################################## +# configuration section for client +################################################## + +# Use address to WeBWorK code library where WebworkClient.pm is located. +use lib '/opt/webwork/webwork2/lib'; +#use Crypt::SSLeay; # needed for https +use WebworkClient; + + +############################################# +# Configure +############################################# + + ############################################################ +# configure the local output file and display command !!!!!!!! + ############################################################ + +use constant LOG_FILE => '/opt/webwork/libraries/t/bad_problems.txt'; + +use constant DISPLAYMODE => 'images'; # jsMath is another possibilities. + + # Path to a temporary file for storing the output of renderProblem.pl +# use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; + + # Command line for displaying the temporary file in a browser. + #use constant DISPLAY_COMMAND => 'open -a firefox '; #browser opens tempoutputfile above + # use constant DISPLAY_COMMAND => "open -a 'Google Chrome' "; + use constant DISPLAY_COMMAND => " less "; # display tempoutputfile with less + ############################################################ + + my $use_site; + $use_site = 'test_webwork'; # select a rendering site + #$use_site = 'local'; # select a rendering site + #$use_site = 'rochester_test'; # select a rendering site + + + ############################################################ + +# To configure the target webwork server +# two URLs are required +# 1. $XML_URL http://test.webwork.maa.org/mod_xmlrpc +# points to the Webservice.pm and Webservice/RenderProblem modules +# Is used by the client to send the original XML request to the webservice +# +# 2. $FORM_ACTION_URL http:http://test.webwork.maa.org/webwork2/html2xml +# points to the renderViaXMLRPC.pm module. +# +# This url is placed as form action url when the rendered HTML from the original +# request is returned to the client from Webservice/RenderProblem. The client +# reorganizes the XML it receives into an HTML page (with a WeBWorK form) and +# pipes it through a local browser. +# +# The browser uses this url to resubmit the problem (with answers) via the standard +# HTML webform used by WeBWorK to the renderViaXMLRPC.pm handler. +# +# This renderViaXMLRPC.pm handler acts as an intermediary between the browser +# and the webservice. It interprets the HTML form sent by the browser, +# rewrites the form data in XML format, submits it to the WebworkWebservice.pm +# which processes it and sends the the resulting HTML back to renderViaXMLRPC.pm +# which in turn passes it back to the browser. +# 3. The second time a problem is submitted renderViaXMLRPC.pm receives the WeBWorK form +# submitted directly by the browser. +# The renderViaXMLRPC.pm translates the WeBWorK form, has it processes by the webservice +# and returns the result to the browser. +# The The client renderProblem.pl script is no longer involved. +# 4. Summary: renderProblem.pl is only involved in the first round trip +# of the submitted problem. After that the communication is between the browser and +# renderViaXMLRPC using HTML forms and between renderViaXMLRPC and the WebworkWebservice.pm +# module using XML_RPC. +# 5. The XML_PASSWORD is defined on the site. In future versions a more secure password method +# may be implemented. This is sufficient to keep out robots. +# 6. The course "daemon_course" must be a course that has been created on the server or an error will +# result. A different name can be used but the course must exist on the server. + + +our ( $XML_URL,$FORM_ACTION_URL, $XML_PASSWORD, $XML_COURSE); +if ($use_site eq 'local') { +# the rest can work!! + $XML_URL = 'http://localhost:80'; + $FORM_ACTION_URL = 'http://localhost:80/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; +} elsif ($use_site eq 'rochester_test') { + + $XML_URL = 'http://128.151.231.2'; + $FORM_ACTION_URL = 'http://128.151.231.2/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} elsif ($use_site eq 'test_webwork') { + + $XML_URL = 'https://test.webwork.maa.org'; + $FORM_ACTION_URL = 'https://test.webwork.maa.org/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} + +################################################## +# END configuration section for client +################################################## + + + + +our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf + + +################################################## +# end configuration section +################################################## + + + +our $xmlrpc_client = new WebworkClient; + +################################################## +# input/output section +################################################## + + +$xmlrpc_client->url($XML_URL); +$xmlrpc_client->{form_action_url}= $FORM_ACTION_URL; +$xmlrpc_client->{displayMode} = DISPLAYMODE(); +$xmlrpc_client->{user} = 'xmluser'; +$xmlrpc_client->{password} = $XML_PASSWORD; +$xmlrpc_client->{course} = $XML_COURSE; + +our $source = ''; +our $output; +our $return_string; +if (@ARGV) { + local(*FH); + open(FH, ">>".LOG_FILE()) || die "Can't open log file ". LOG_FILE(); + $source = (defined $ARGV[0]) ? `cat $ARGV[0]` : '' ; + $xmlrpc_client->encodeSource($source); + if ( $xmlrpc_client->xmlrpcCall('renderProblem') ) { + $output = $xmlrpc_client->{output}; + if (defined($output->{flags}->{error_flag}) and $output->{flags}->{error_flag} ) { + $return_string = "0\t $ARGV[0] has errors\n"; + } elsif (defined($output->{errors}) and $output->{errors} ){ + $return_string = "0\t $ARGV[0] has syntax errors\n"; + } else { + # + if (defined($output->{flags}->{DEBUG_messages}) ) { + my @debug_messages = @{$output->{flags}->{DEBUG_messages}}; + $return_string .= (pop @debug_messages ) ||'' ; #avoid error if array was empty + if (@debug_messages) { + $return_string .= join(" ", @debug_messages); + } else { + $return_string = ""; + } + } + if (defined($output->{flags}->{WARNING_messages}) ) { + my @warning_messages = @{$output->{flags}->{WARNING_messages}}; + $return_string .= (pop @warning_messages)||''; #avoid error if array was empty + $@=undef; + if (@warning_messages) { + $return_string .= join(" ", @warning_messages); + } else { + $return_string = ""; + } + } + $return_string = "0\t ".$return_string."\n" if $return_string; # add a 0 if there was an warning or debug message. + } + unless ($return_string) { + $return_string = "1\t $ARGV[0] is ok\n"; + } + } else { + + $return_string = "0\t $ARGV[0] has undetermined errors -- could not be read perhaps?\n"; + } + print FH $return_string; + close(FH); +} else { + print "0 $ARGV[0] something went wrong -- could not render file\n"; + print STDERR "Useage: ./checkProblem.pl [file_name]\n"; + print STDERR "For example: ./checkProblem.pl input.txt\n"; + print STDERR "Output is sent to the log file: ",LOG_FILE(); + +} + + +################################################## +# XMLRPC client -- +# the code below is identical between renderProblem.pl and renderViaXMLRPC.pm???? +# and has been included in WebworkClient.pm +################################################## + +# package WeBWorK::ContentGenerator::renderViaXMLRPC_client; +# +# use Crypt::SSLeay; # needed for https +# use XMLRPC::Lite; +# use MIME::Base64 qw( encode_base64 decode_base64); +# +# use constant TRANSPORT_METHOD => 'XMLRPC::Lite'; +# use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +# use constant REQUEST_URI => 'mod_xmlrpc'; +# +# sub new { +# my $self = { +# output => '', +# encodedSource => '', +# url => '', +# password => '', +# course => '', +# displayMode => '', +# inputs_ref => { AnSwEr0001 => '', +# AnSwEr0002 => '', +# AnSwEr0003 => '', +# }, +# }; +# +# bless $self; +# } +# +# +# our $result; +# +# ################################################## +# # Utilities -- +# # this code is identical between renderProblem.pl and renderViaXMLRPC.pm +# ################################################## +# +# sub xmlrpcCall { +# my $self = shift; +# my $command = shift; +# $command = 'listLibraries' unless $command; +# +# my $requestResult = TRANSPORT_METHOD +# -> proxy($self->{url}.'/'.REQUEST_URI); +# +# my $input = $self->setInputTable(); +# local( $result); +# # use eval to catch errors +# eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) }; +# if ($@) { +# print STDERR "There were a lot of errors for $command\n" ; +# print STDERR "Errors: \n $@\n End Errors\n" ; +# return 0 #failure +# } +# +# unless (ref($result) and $result->fault) { +# my $rh_result = $result->result(); +# #print STDERR pretty_print_rh($rh_result); +# $self->{output} = $rh_result; #$self->formatRenderedProblem($rh_result); +# return 1; # success +# +# } else { +# $self->{output} = 'Error from server: '. join( ",\n ", +# $result->faultcode, +# $result->faultstring); +# return 0; #failure +# } +# } +# +# sub encodeSource { +# my $self = shift; +# my $source = shift; +# $self->{encodedSource} =encode_base64($source); +# } +# sub url { +# my $self = shift; +# my $new_url = shift; +# $self->{url} = $new_url if defined($new_url) and $new_url =~ /\S/; +# $self->{url}; +# } +# sub pretty_print { # provides html output -- NOT a method +# my $r_input = shift; +# my $level = shift; +# $level = 4 unless defined($level); +# $level--; +# return '' unless $level > 0; # only print three levels of hashes (safety feature) +# my $out = ''; +# if ( not ref($r_input) ) { +# $out = $r_input if defined $r_input; # not a reference +# $out =~ s/"; +# +# +# foreach my $key ( sort ( keys %$r_input )) { +# $out .= " $key=> ".pretty_print($r_input->{$key}) . ""; +# } +# $out .=""; +# } elsif (ref($r_input) eq 'ARRAY' ) { +# my @array = @$r_input; +# $out .= "( " ; +# while (@array) { +# $out .= pretty_print(shift @array, $level) . " , "; +# } +# $out .= " )"; +# } elsif (ref($r_input) eq 'CODE') { +# $out = "$r_input"; +# } else { +# $out = $r_input; +# $out =~ s/ $self->{password}, +# set => 'set0', +# library_name => 'Library', +# command => 'all', +# }; +# +# $out; +# } +# sub setInputTable { +# my $self = shift; +# my $out = { +# pw => $self->{password}, +# library_name => 'Library', +# command => 'renderProblem', +# answer_form_submitted => 1, +# course => $self->{course}, +# extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr +# ExprWithImplicitExpand AnswerEvaluator +# AnswerEvaluatorMaker +# )], +# mode => $self->{displayMode}, +# modules_to_evaluate => [ qw( +# Exporter +# DynaLoader +# GD +# WWPlot +# Fun +# Circle +# Label +# PGrandom +# Units +# Hermite +# List +# Match +# Multiple +# Select +# AlgParser +# AnswerHash +# Fraction +# VectorField +# Complex1 +# Complex +# MatrixReal1 Matrix +# Distributions +# Regression +# +# )], +# envir => $self->environment(), +# problem_state => { +# +# num_of_correct_ans => 0, +# num_of_incorrect_ans => 4, +# recorded_score => 1.0, +# }, +# source => $self->{encodedSource}, #base64 encoded +# +# +# +# }; +# +# $out; +# } +# +# sub environment { +# my $self = shift; +# my $envir = { +# answerDate => '4014438528', +# CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', +# CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', +# CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', +# CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', +# cgiDirectory=>'Not defined', +# cgiURL => 'Not defined', +# classDirectory=> 'Not defined', +# courseName=>'Not defined', +# courseScriptsDirectory=>'not defined', +# displayMode=>$self->{displayMode}, +# dueDate=> '4014438528', +# effectivePermissionLevel => 10, +# externalGif2EpsPath=>'not defined', +# externalPng2EpsPath=>'not defined', +# externalTTHPath=>'/usr/local/bin/tth', +# fileName=>'set0/prob1a.pg', +# formattedAnswerDate=>'6/19/00', +# formattedDueDate=>'6/19/00', +# formattedOpenDate=>'6/19/00', +# functAbsTolDefault=> 0.0000001, +# functLLimitDefault=>0, +# functMaxConstantOfIntegration=> 1000000000000.0, +# functNumOfPoints=> 5, +# functRelPercentTolDefault=> 0.000001, +# functULimitDefault=>1, +# functVarDefault=> 'x', +# functZeroLevelDefault=> 0.000001, +# functZeroLevelTolDefault=>0.000001, +# htmlDirectory =>'not defined', +# htmlURL =>'not defined', +# inputs_ref => $self->{inputs_ref}, +# macroDirectory=>'not defined', +# numAbsTolDefault=>0.0000001, +# numFormatDefault=>'%0.13g', +# numOfAttempts=> 0, +# numRelPercentTolDefault => 0.0001, +# numZeroLevelDefault =>0.000001, +# numZeroLevelTolDefault =>0.000001, +# openDate=> '3014438528', +# permissionLevel =>10, +# PRINT_FILE_NAMES_FOR => [ 'gage'], +# probFileName => 'set0/prob1a.pg', +# problemSeed => 1234, +# problemValue =>1, +# probNum => 13, +# psvn => 54321, +# psvn=> 54321, +# questionNumber => 1, +# scriptDirectory => 'Not defined', +# sectionName => 'Gage', +# sectionNumber => 1, +# sessionKey=> 'Not defined', +# setNumber =>'not defined', +# studentLogin =>'gage', +# studentName => 'Mike Gage', +# tempDirectory => 'not defined', +# templateDirectory=>'not defined', +# tempURL=>'not defined', +# webworkDocsURL => 'not defined', +# +# showHints => 1, # extra options -- usually passed from the input form +# showSolutions => 1, +# +# }; +# $envir; +# }; +# +# sub formatAnswerRow { +# my $self = shift; +# my $rh_answer = shift; +# my $problemNumber = shift; +# my $answerString = $rh_answer->{original_student_ans}||' '; +# my $correctAnswer = $rh_answer->{correct_ans}||''; +# my $ans_message = $rh_answer->{ans_message}||''; +# my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect'; +# my $row = qq{ +# +# +# Prob: $problemNumber +# +# +# $answerString +# +# +# $score +# +# +# Correct answer is $correctAnswer +# +# +# $ans_message +# +# \n +# }; +# $row; +# } +# +# sub formatRenderedProblem { +# my $self = shift; +# my $rh_result = $self->{output}; # wrap problem in formats +# my $problemText = decode_base64($rh_result->{text}); +# my $rh_answers = $rh_result->{answers}; +# my $encodedSource = $self->{encodedSource}||'foobar'; +# my $warnings = ''; +# if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){ +# $warnings = "
+#

WARNINGS

".decode_base64($rh_result->{WARNINGS})."

"; +# } +# #warn "keys: ", join(" | ", sort keys %{$rh_result }); +# my $debug_messages = $rh_result->{flags}->{DEBUG_messages} || []; +# $debug_messages = join("
\n", @{ $debug_messages } ); +# my $internal_debug_messages = $rh_result->{internal_debug_messages} || []; +# $internal_debug_messages = join("
\n", @{ $internal_debug_messages } ); +# # collect answers +# my $answerTemplate = q{
ANSWERS }; +# my $problemNumber = 1; +# foreach my $key (sort keys %{$rh_answers}) { +# $answerTemplate .= $self->formatAnswerRow($rh_answers->{$key}, $problemNumber++); +# } +# $answerTemplate .= q{

}; +# +# my $FULL_URL = $self->url; +# my $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; +# my $problemTemplate = < +# +# +# WeBWorK Editor using host $HOSTNAME +# +# +# $answerTemplate +#
+# $problemText +# +# +# +# +# +#

+#
+#
+#

Warning section

+# $warnings +#

+# Debug message section +#

+# $debug_messages +#

+# internal errors +#

+# $internal_debug_messages +# +# +# +# +# ENDPROBLEMTEMPLATE +# +# +# +# $problemTemplate; +# } +# + +1; diff --git a/clients/check_problems_in_dir.sh b/clients/check_problems_in_dir.sh new file mode 100755 index 0000000000..3be228fd9b --- /dev/null +++ b/clients/check_problems_in_dir.sh @@ -0,0 +1 @@ +find . -name "*.pg" -exec /opt/webwork/webwork2/clients/checkProblem.pl {} ';' diff --git a/clients/hello_world_soap_client.pl b/clients/hello_world_soap_client.pl new file mode 100755 index 0000000000..3b669c673b --- /dev/null +++ b/clients/hello_world_soap_client.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + + +use SOAP::Lite; +my $soap = SOAP::Lite +#-> uri('http://math.webwork.rochester.edu/WebworkXMLRPC') +#-> proxy('https://math.webwork.rochester.edu/mod_soap/WebworkWebservice'); +-> uri('http://localhost/WebworkXMLRPC') +-> proxy('http://localhost/mod_soap/WebworkWebservice'); + +#-> uri('https://devel.webwork.rochester.edu:8002/WebworkXMLRPC') +#-> proxy('https://devel.webwork.rochester.edu:8002/mod_soap/WebworkWebservice'); + + +my $result = $soap->hi(); + +unless ($result->fault) { + print $result->result(); +} else { + print join ', ', + $result->faultcode, + $result->faultstring; +} + + diff --git a/clients/hello_world_xmlrpc_client.pl b/clients/hello_world_xmlrpc_client.pl new file mode 100755 index 0000000000..200f34dbd8 --- /dev/null +++ b/clients/hello_world_xmlrpc_client.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +# +use XMLRPC::Lite; + my $soap = XMLRPC::Lite + # -> proxy('https://math.webwork.rochester.edu/mod_xmlrpc/'); + #-> proxy('https://devel.webwork.rochester.edu:8002/mod_xmlrpc/'); + -> proxy('http://localhost/mod_xmlrpc/'); + + + my $result = $soap->call("WebworkXMLRPC.hi"); + + + unless ($result->fault) { + print $result->result(),"\n"; + } else { + print join ', ', + $result->faultcode, + $result->faultstring; + } diff --git a/clients/input.txt b/clients/input.txt new file mode 100644 index 0000000000..592526419b --- /dev/null +++ b/clients/input.txt @@ -0,0 +1,99 @@ +##DESCRIPTION +## A very simple first problem +##ENDDESCRIPTION +##KEYWORDS('algebra') +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; +$a = random(-10,-1,1); +$b = random(1,11,1); +$c = random(1,11,1); +$d = random(1,11,1); + +BEGIN_TEXT +$PAR +displayMode is $displayMode $BR +$PAR +This problem demonstrates how you enter numerical answers into WeBWorK. $PAR +Evaluate the expression \(3($a )($b -$c -2($d ))\): + + \{ ans_rule(10) \} + +$BR +END_TEXT +$ans = 3*($a)*($b-$c-2*($d)); + +&ANS(strict_num_cmp($ans)); + +BEGIN_TEXT + +In the case above you need to enter a number, since we're testing whether you can multiply +out these numbers. (You can use a calculator if you want.) +$PAR +For most problems, you will be able to get WeBWorK to +do some of the work for you. For example +$BR +Calculate ($a) * ($b): \{ ans_rule()\} +$BR +END_TEXT +$ans = $a*$b; + +&ANS(std_num_cmp($ans)); + +BEGIN_TEXT +The asterisk is what most computers use to denote multiplication and you can use this with WeBWorK. +But WeBWorK will also allow use to use a space to denote multiplication. +You can either \($a * $b\) or \{$a*$b\} or even \($a \ $b\). All will work. Try them. +$PAR +Now try calculating the sine of 45 degrees ( that's sine of pi over 4 in radians +and numerically sin(pi/4) equals \{1/sqrt(2)\} or, more precisely, \(1/\sqrt{2} \) ). +You can enter this as sin(pi/4) , as +sin(3.1415926/4), as 1/sqrt(2), as 2**(-.5), etc. This is because WeBWorK knows about +functions like sin and sqrt (square root). (Note: exponents +can be indicated by either a "caret" or **). Try it.$BR \( \sin(\pi/4) = \) \{ ans_rule(20) \}$PAR + Here's the +\{ +htmlLink(qq!http://webwork.math.rochester.edu/webwork_system_html/docs/docs/pglanguage/availablefunctions.html!,"list +of the functions") \} + which WeBWorK understands. WeBWorK ALWAYS uses radian mode for trig functions. + $PAR +END_TEXT + +&ANS( std_num_cmp(sin(3.1415926/4)) ); +BEGIN_TEXT +You can also use juxtaposition to denote multiplication. E.g. enter \( 2\sin(3\pi/2) \). +You can enter this as 2*sin(3*pi/2) or more simply as 2sin(3pi/2). Try it: $BR +\{ ans_rule(20) \}$PAR + +END_TEXT + +$pi = 4*atan(1); +&ANS( std_num_cmp(2*sin(3*$pi/2)) ); + +BEGIN_TEXT +Sometimes you need to use ( )'s to make your meaning clear. E.g. 1/2+3 is 3.5, but 1/(2+3) is .2 Why? +Try entering both and use the ${LQ}Preview${RQ} button below to see the difference. In addition to +( )'s, you can also use [ ]'s and $LB ${RB}'s. $BR +\{ ans_rule(20) \}$PAR +END_TEXT + +&ANS( std_num_cmp(.2)); + +BEGIN_TEXT +You can always try to enter answers and let WeBWorK do the calculating. +WeBWorK will tell you if the problem requires a strict numerical answer. +The way we use WeBWorK in this class there is no penalty for getting an answer wrong. What counts +is that you get the answer right eventually (before the due date). For complicated answers, +you should use the ${LQ}Preview${RQ} button to check for syntax errors and also to check that the answer +you enter is really what you think it is. +END_TEXT + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/clients/renderProblem.pl b/clients/renderProblem.pl new file mode 100755 index 0000000000..09d7f7ff47 --- /dev/null +++ b/clients/renderProblem.pl @@ -0,0 +1,552 @@ +#!/usr/bin/perl -w + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/clients/renderProblem.pl,v 1.4 2010/05/11 15:44:05 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. +################################################################################ + +=head1 NAME + +webwork2/clients/renderProblem.pl + +This script will take a file and send it to a WeBWorK daemon webservice +to have it rendered. The result is split into the basic HTML rendering +and evaluation of answers and then passed to a browser for printing. + +The formatting allows the browser presentation to be interactive with the +daemon running the script webwork2/lib/renderViaXMLRPC.pm + +Rembember to configure the local output file and display command !!!!!!!! + +=cut + +use strict; +use warnings; + + + +################################################## +# configuration section for client +################################################## + +# Use address to WeBWorK code library where WebworkClient.pm is located. +use lib '/opt/webwork/webwork2/lib'; +#use Crypt::SSLeay; # needed for https +use WebworkClient; + + +############################################# +# Configure +############################################# + + ############################################################ +# configure the local output file and display command !!!!!!!! + ############################################################ + + # Path to a temporary file for storing the output of renderProblem.pl + use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; + + # Command line for displaying the temporary file in a browser. + use constant DISPLAY_COMMAND => 'open -a firefox '; #browser opens tempoutputfile above + # use constant DISPLAY_COMMAND => "open -a 'Google Chrome' "; + + ############################################################ + + my $use_site; + $use_site = 'test_webwork'; # select a rendering site + #$use_site = 'local'; # select a rendering site + #$use_site = 'rochester_test'; # select a rendering site + + + ############################################################ + +# To configure the target webwork server +# two URLs are required +# 1. $XML_URL http://test.webwork.maa.org/mod_xmlrpc +# points to the Webservice.pm and Webservice/RenderProblem modules +# Is used by the client to send the original XML request to the webservice +# +# 2. $FORM_ACTION_URL http:http://test.webwork.maa.org/webwork2/html2xml +# points to the renderViaXMLRPC.pm module. +# +# This url is placed as form action url when the rendered HTML from the original +# request is returned to the client from Webservice/RenderProblem. The client +# reorganizes the XML it receives into an HTML page (with a WeBWorK form) and +# pipes it through a local browser. +# +# The browser uses this url to resubmit the problem (with answers) via the standard +# HTML webform used by WeBWorK to the renderViaXMLRPC.pm handler. +# +# This renderViaXMLRPC.pm handler acts as an intermediary between the browser +# and the webservice. It interprets the HTML form sent by the browser, +# rewrites the form data in XML format, submits it to the WebworkWebservice.pm +# which processes it and sends the the resulting HTML back to renderViaXMLRPC.pm +# which in turn passes it back to the browser. +# 3. The second time a problem is submitted renderViaXMLRPC.pm receives the WeBWorK form +# submitted directly by the browser. +# The renderViaXMLRPC.pm translates the WeBWorK form, has it processes by the webservice +# and returns the result to the browser. +# The The client renderProblem.pl script is no longer involved. +# 4. Summary: renderProblem.pl is only involved in the first round trip +# of the submitted problem. After that the communication is between the browser and +# renderViaXMLRPC using HTML forms and between renderViaXMLRPC and the WebworkWebservice.pm +# module using XML_RPC. +# 5. The XML_PASSWORD is defined on the site. In future versions a more secure password method +# may be implemented. This is sufficient to keep out robots. +# 6. The course "daemon_course" must be a course that has been created on the server or an error will +# result. A different name can be used but the course must exist on the server. + + +our ( $XML_URL,$FORM_ACTION_URL, $XML_PASSWORD, $XML_COURSE); +if ($use_site eq 'local') { +# the rest can work!! + $XML_URL = 'http://localhost:80'; + $FORM_ACTION_URL = 'http://localhost:80/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; +} elsif ($use_site eq 'rochester_test') { + + $XML_URL = 'http://128.151.231.2'; + $FORM_ACTION_URL = 'http://128.151.231.2/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} elsif ($use_site eq 'test_webwork') { + + $XML_URL = 'https://test.webwork.maa.org'; + $FORM_ACTION_URL = 'https://test.webwork.maa.org/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} + +################################################## +# END configuration section for client +################################################## + + + +use constant DISPLAYMODE => 'images'; # jsMath is another possibilities. + + +our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf + + +################################################## +# end configuration section +################################################## + + + +our $xmlrpc_client = new WebworkClient; + +################################################## +# input/output section +################################################## + + +our $source; +our $rh_result; +# filter mode main code + +undef $/; +$source = <>; #slurp input +$/ =1; +$xmlrpc_client->encodeSource($source); +$xmlrpc_client->url($XML_URL); +$xmlrpc_client->{form_action_url}= $FORM_ACTION_URL; +$xmlrpc_client->{displayMode} = DISPLAYMODE(); +$xmlrpc_client->{user} = 'xmluser'; +$xmlrpc_client->{password} = $XML_PASSWORD; +$xmlrpc_client->{course} = $XML_COURSE; + +#xmlrpcCall('renderProblem'); +our $output; +if ( $xmlrpc_client->xmlrpcCall('renderProblem') ) { + + $output = $xmlrpc_client->formatRenderedProblem; +} else { + $output = $xmlrpc_client->{output}; # error report +} + + + + +local(*FH); +open(FH, '>'.TEMPOUTPUTFILE) or die "Can't open file ".TEMPOUTPUTFILE()." for writing"; +print FH $output; +close(FH); + +system(DISPLAY_COMMAND().TEMPOUTPUTFILE()); + +################################################## +# end input/output section +################################################## + + +################################################## +# XMLRPC client -- +# the code below is identical between renderProblem.pl and renderViaXMLRPC.pm???? +# and has been included in WebworkClient.pm +################################################## + +# package WeBWorK::ContentGenerator::renderViaXMLRPC_client; +# +# use Crypt::SSLeay; # needed for https +# use XMLRPC::Lite; +# use MIME::Base64 qw( encode_base64 decode_base64); +# +# use constant TRANSPORT_METHOD => 'XMLRPC::Lite'; +# use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +# use constant REQUEST_URI => 'mod_xmlrpc'; +# +# sub new { +# my $self = { +# output => '', +# encodedSource => '', +# url => '', +# password => '', +# course => '', +# displayMode => '', +# inputs_ref => { AnSwEr0001 => '', +# AnSwEr0002 => '', +# AnSwEr0003 => '', +# }, +# }; +# +# bless $self; +# } +# +# +# our $result; +# +# ################################################## +# # Utilities -- +# # this code is identical between renderProblem.pl and renderViaXMLRPC.pm +# ################################################## +# +# sub xmlrpcCall { +# my $self = shift; +# my $command = shift; +# $command = 'listLibraries' unless $command; +# +# my $requestResult = TRANSPORT_METHOD +# -> proxy($self->{url}.'/'.REQUEST_URI); +# +# my $input = $self->setInputTable(); +# local( $result); +# # use eval to catch errors +# eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) }; +# if ($@) { +# print STDERR "There were a lot of errors for $command\n" ; +# print STDERR "Errors: \n $@\n End Errors\n" ; +# return 0 #failure +# } +# +# unless (ref($result) and $result->fault) { +# my $rh_result = $result->result(); +# #print STDERR pretty_print_rh($rh_result); +# $self->{output} = $rh_result; #$self->formatRenderedProblem($rh_result); +# return 1; # success +# +# } else { +# $self->{output} = 'Error from server: '. join( ",\n ", +# $result->faultcode, +# $result->faultstring); +# return 0; #failure +# } +# } +# +# sub encodeSource { +# my $self = shift; +# my $source = shift; +# $self->{encodedSource} =encode_base64($source); +# } +# sub url { +# my $self = shift; +# my $new_url = shift; +# $self->{url} = $new_url if defined($new_url) and $new_url =~ /\S/; +# $self->{url}; +# } +# sub pretty_print { # provides html output -- NOT a method +# my $r_input = shift; +# my $level = shift; +# $level = 4 unless defined($level); +# $level--; +# return '' unless $level > 0; # only print three levels of hashes (safety feature) +# my $out = ''; +# if ( not ref($r_input) ) { +# $out = $r_input if defined $r_input; # not a reference +# $out =~ s/"; +# +# +# foreach my $key ( sort ( keys %$r_input )) { +# $out .= " $key=> ".pretty_print($r_input->{$key}) . ""; +# } +# $out .=""; +# } elsif (ref($r_input) eq 'ARRAY' ) { +# my @array = @$r_input; +# $out .= "( " ; +# while (@array) { +# $out .= pretty_print(shift @array, $level) . " , "; +# } +# $out .= " )"; +# } elsif (ref($r_input) eq 'CODE') { +# $out = "$r_input"; +# } else { +# $out = $r_input; +# $out =~ s/ $self->{password}, +# set => 'set0', +# library_name => 'Library', +# command => 'all', +# }; +# +# $out; +# } +# sub setInputTable { +# my $self = shift; +# my $out = { +# pw => $self->{password}, +# library_name => 'Library', +# command => 'renderProblem', +# answer_form_submitted => 1, +# course => $self->{course}, +# extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr +# ExprWithImplicitExpand AnswerEvaluator +# AnswerEvaluatorMaker +# )], +# mode => $self->{displayMode}, +# modules_to_evaluate => [ qw( +# Exporter +# DynaLoader +# GD +# WWPlot +# Fun +# Circle +# Label +# PGrandom +# Units +# Hermite +# List +# Match +# Multiple +# Select +# AlgParser +# AnswerHash +# Fraction +# VectorField +# Complex1 +# Complex +# MatrixReal1 Matrix +# Distributions +# Regression +# +# )], +# envir => $self->environment(), +# problem_state => { +# +# num_of_correct_ans => 0, +# num_of_incorrect_ans => 4, +# recorded_score => 1.0, +# }, +# source => $self->{encodedSource}, #base64 encoded +# +# +# +# }; +# +# $out; +# } +# +# sub environment { +# my $self = shift; +# my $envir = { +# answerDate => '4014438528', +# CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', +# CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', +# CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', +# CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', +# cgiDirectory=>'Not defined', +# cgiURL => 'Not defined', +# classDirectory=> 'Not defined', +# courseName=>'Not defined', +# courseScriptsDirectory=>'not defined', +# displayMode=>$self->{displayMode}, +# dueDate=> '4014438528', +# effectivePermissionLevel => 10, +# externalGif2EpsPath=>'not defined', +# externalPng2EpsPath=>'not defined', +# externalTTHPath=>'/usr/local/bin/tth', +# fileName=>'set0/prob1a.pg', +# formattedAnswerDate=>'6/19/00', +# formattedDueDate=>'6/19/00', +# formattedOpenDate=>'6/19/00', +# functAbsTolDefault=> 0.0000001, +# functLLimitDefault=>0, +# functMaxConstantOfIntegration=> 1000000000000.0, +# functNumOfPoints=> 5, +# functRelPercentTolDefault=> 0.000001, +# functULimitDefault=>1, +# functVarDefault=> 'x', +# functZeroLevelDefault=> 0.000001, +# functZeroLevelTolDefault=>0.000001, +# htmlDirectory =>'not defined', +# htmlURL =>'not defined', +# inputs_ref => $self->{inputs_ref}, +# macroDirectory=>'not defined', +# numAbsTolDefault=>0.0000001, +# numFormatDefault=>'%0.13g', +# numOfAttempts=> 0, +# numRelPercentTolDefault => 0.0001, +# numZeroLevelDefault =>0.000001, +# numZeroLevelTolDefault =>0.000001, +# openDate=> '3014438528', +# permissionLevel =>10, +# PRINT_FILE_NAMES_FOR => [ 'gage'], +# probFileName => 'set0/prob1a.pg', +# problemSeed => 1234, +# problemValue =>1, +# probNum => 13, +# psvn => 54321, +# psvn=> 54321, +# questionNumber => 1, +# scriptDirectory => 'Not defined', +# sectionName => 'Gage', +# sectionNumber => 1, +# sessionKey=> 'Not defined', +# setNumber =>'not defined', +# studentLogin =>'gage', +# studentName => 'Mike Gage', +# tempDirectory => 'not defined', +# templateDirectory=>'not defined', +# tempURL=>'not defined', +# webworkDocsURL => 'not defined', +# +# showHints => 1, # extra options -- usually passed from the input form +# showSolutions => 1, +# +# }; +# $envir; +# }; +# +# sub formatAnswerRow { +# my $self = shift; +# my $rh_answer = shift; +# my $problemNumber = shift; +# my $answerString = $rh_answer->{original_student_ans}||' '; +# my $correctAnswer = $rh_answer->{correct_ans}||''; +# my $ans_message = $rh_answer->{ans_message}||''; +# my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect'; +# my $row = qq{ +# +# +# Prob: $problemNumber +# +# +# $answerString +# +# +# $score +# +# +# Correct answer is $correctAnswer +# +# +# $ans_message +# +# \n +# }; +# $row; +# } +# +# sub formatRenderedProblem { +# my $self = shift; +# my $rh_result = $self->{output}; # wrap problem in formats +# my $problemText = decode_base64($rh_result->{text}); +# my $rh_answers = $rh_result->{answers}; +# my $encodedSource = $self->{encodedSource}||'foobar'; +# my $warnings = ''; +# if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){ +# $warnings = "
+#

WARNINGS

".decode_base64($rh_result->{WARNINGS})."

"; +# } +# #warn "keys: ", join(" | ", sort keys %{$rh_result }); +# my $debug_messages = $rh_result->{flags}->{DEBUG_messages} || []; +# $debug_messages = join("
\n", @{ $debug_messages } ); +# my $internal_debug_messages = $rh_result->{internal_debug_messages} || []; +# $internal_debug_messages = join("
\n", @{ $internal_debug_messages } ); +# # collect answers +# my $answerTemplate = q{
ANSWERS }; +# my $problemNumber = 1; +# foreach my $key (sort keys %{$rh_answers}) { +# $answerTemplate .= $self->formatAnswerRow($rh_answers->{$key}, $problemNumber++); +# } +# $answerTemplate .= q{

}; +# +# my $FULL_URL = $self->url; +# my $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; +# my $problemTemplate = < +# +# +# WeBWorK Editor using host $HOSTNAME +# +# +# $answerTemplate +#
+# $problemText +# +# +# +# +# +#

+#
+#
+#

Warning section

+# $warnings +#

+# Debug message section +#

+# $debug_messages +#

+# internal errors +#

+# $internal_debug_messages +# +# +# +# +# ENDPROBLEMTEMPLATE +# +# +# +# $problemTemplate; +# } +# + +1; diff --git a/clients/renderProblem_rawoutput.pl b/clients/renderProblem_rawoutput.pl new file mode 100755 index 0000000000..a081b6e7d3 --- /dev/null +++ b/clients/renderProblem_rawoutput.pl @@ -0,0 +1,592 @@ +#!/usr/bin/perl -w + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/clients/renderProblem.pl,v 1.4 2010/05/11 15:44:05 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. +################################################################################ + +=head1 NAME + +webwork2/clients/renderProblem.pl + +This script will take a file and send it to a WeBWorK daemon webservice +to have it rendered. The result is split into the basic HTML rendering +and evaluation of answers and then passed to a browser for printing. + +The formatting allows the browser presentation to be interactive with the +daemon running the script webwork2/lib/renderViaXMLRPC.pm + +Rembember to configure the local output file and display command !!!!!!!! + +=cut + +use strict; +use warnings; + + + +################################################## +# configuration section for client +################################################## + +# Use address to WeBWorK code library where WebworkClient.pm is located. +use lib '/opt/webwork/webwork2/lib'; +#use Crypt::SSLeay; # needed for https +use WebworkClient; + + +############################################# +# Configure +############################################# + + ############################################################ +# configure the local output file and display command !!!!!!!! + ############################################################ + + # Path to a temporary file for storing the output of renderProblem.pl + use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; + + # Command line for displaying the temporary file in a browser. + #use constant DISPLAY_COMMAND => 'open -a firefox '; #browser opens tempoutputfile above + # use constant DISPLAY_COMMAND => "open -a 'Google Chrome' "; + use constant DISPLAY_COMMAND => " less "; # display tempoutputfile with less + ############################################################ + + my $use_site; + $use_site = 'test_webwork'; # select a rendering site + #$use_site = 'local'; # select a rendering site + #$use_site = 'rochester_test'; # select a rendering site + + + ############################################################ + +# To configure the target webwork server +# two URLs are required +# 1. $XML_URL http://test.webwork.maa.org/mod_xmlrpc +# points to the Webservice.pm and Webservice/RenderProblem modules +# Is used by the client to send the original XML request to the webservice +# +# 2. $FORM_ACTION_URL http:http://test.webwork.maa.org/webwork2/html2xml +# points to the renderViaXMLRPC.pm module. +# +# This url is placed as form action url when the rendered HTML from the original +# request is returned to the client from Webservice/RenderProblem. The client +# reorganizes the XML it receives into an HTML page (with a WeBWorK form) and +# pipes it through a local browser. +# +# The browser uses this url to resubmit the problem (with answers) via the standard +# HTML webform used by WeBWorK to the renderViaXMLRPC.pm handler. +# +# This renderViaXMLRPC.pm handler acts as an intermediary between the browser +# and the webservice. It interprets the HTML form sent by the browser, +# rewrites the form data in XML format, submits it to the WebworkWebservice.pm +# which processes it and sends the the resulting HTML back to renderViaXMLRPC.pm +# which in turn passes it back to the browser. +# 3. The second time a problem is submitted renderViaXMLRPC.pm receives the WeBWorK form +# submitted directly by the browser. +# The renderViaXMLRPC.pm translates the WeBWorK form, has it processes by the webservice +# and returns the result to the browser. +# The The client renderProblem.pl script is no longer involved. +# 4. Summary: renderProblem.pl is only involved in the first round trip +# of the submitted problem. After that the communication is between the browser and +# renderViaXMLRPC using HTML forms and between renderViaXMLRPC and the WebworkWebservice.pm +# module using XML_RPC. +# 5. The XML_PASSWORD is defined on the site. In future versions a more secure password method +# may be implemented. This is sufficient to keep out robots. +# 6. The course "daemon_course" must be a course that has been created on the server or an error will +# result. A different name can be used but the course must exist on the server. + + +our ( $XML_URL,$FORM_ACTION_URL, $XML_PASSWORD, $XML_COURSE); +if ($use_site eq 'local') { +# the rest can work!! + $XML_URL = 'http://localhost:80'; + $FORM_ACTION_URL = 'http://localhost:80/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; +} elsif ($use_site eq 'rochester_test') { + + $XML_URL = 'http://128.151.231.2'; + $FORM_ACTION_URL = 'http://128.151.231.2/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} elsif ($use_site eq 'test_webwork') { + + $XML_URL = 'https://test.webwork.maa.org'; + $FORM_ACTION_URL = 'https://test.webwork.maa.org/webwork2/html2xml'; + $XML_PASSWORD = 'xmlwebwork'; + $XML_COURSE = 'daemon_course'; + +} + +################################################## +# END configuration section for client +################################################## + + + +use constant DISPLAYMODE => 'images'; # jsMath is another possibilities. + + +our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf + + +################################################## +# end configuration section +################################################## + + + +our $xmlrpc_client = new WebworkClient; + +################################################## +# input/output section +################################################## + + +our $source; +our $rh_result; +# filter mode main code + +undef $/; +$source = <>; #slurp input +$/ =1; +$xmlrpc_client->encodeSource($source); +$xmlrpc_client->url($XML_URL); +$xmlrpc_client->{form_action_url}= $FORM_ACTION_URL; +$xmlrpc_client->{displayMode} = DISPLAYMODE(); +$xmlrpc_client->{user} = 'xmluser'; +$xmlrpc_client->{password} = $XML_PASSWORD; +$xmlrpc_client->{course} = $XML_COURSE; + +#xmlrpcCall('renderProblem'); +our $output; +if ( $xmlrpc_client->xmlrpcCall('renderProblem') ) { + $output = "1\n"; + $output = pretty_print( $xmlrpc_client->{output} ); +} else { + $output = "0\n"; + $output = $xmlrpc_client->{output}; # error report +} + + + +local(*FH); +open(FH, '>'.TEMPOUTPUTFILE) or die "Can't open file ".TEMPOUTPUTFILE()." for writing"; +print FH $output; +close(FH); + +system(DISPLAY_COMMAND().TEMPOUTPUTFILE()); + +################################################## +# end input/output section +################################################## + +sub pretty_print { + shift if UNIVERSAL::isa($_[0] => __PACKAGE__); + my $rh = shift; + my $indent = shift || 0; + my $out = ""; + my $type = ref($rh); + + if (defined($type) and $type) { + $out .= " type = $type; "; + } elsif (! defined($rh )) { + $out .= " type = UNDEFINED; "; + } + return $out." " unless defined($rh); + + if ( ref($rh) =~/HASH/ or "$rh" =~/HASH/ ) { + $out .= "{\n"; + $indent++; + foreach my $key (sort keys %{$rh}) { + $out .= " "x$indent."$key => " . pretty_print( $rh->{$key}, $indent ) . "\n"; + } + $indent--; + $out .= "\n"." "x$indent."}\n"; + + } elsif (ref($rh) =~ /ARRAY/ or "$rh" =~/ARRAY/) { + $out .= " ( "; + foreach my $elem ( @{$rh} ) { + $out .= pretty_print($elem, $indent); + + } + $out .= " ) \n"; + } elsif ( ref($rh) =~ /SCALAR/ ) { + $out .= "scalar reference ". ${$rh}; + } elsif ( ref($rh) =~/Base64/ ) { + $out .= "base64 reference " .$$rh; + } else { + $out .= $rh; + } + + return $out." "; +} + +################################################## +# XMLRPC client -- +# the code below is identical between renderProblem.pl and renderViaXMLRPC.pm???? +# and has been included in WebworkClient.pm +################################################## + +# package WeBWorK::ContentGenerator::renderViaXMLRPC_client; +# +# use Crypt::SSLeay; # needed for https +# use XMLRPC::Lite; +# use MIME::Base64 qw( encode_base64 decode_base64); +# +# use constant TRANSPORT_METHOD => 'XMLRPC::Lite'; +# use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +# use constant REQUEST_URI => 'mod_xmlrpc'; +# +# sub new { +# my $self = { +# output => '', +# encodedSource => '', +# url => '', +# password => '', +# course => '', +# displayMode => '', +# inputs_ref => { AnSwEr0001 => '', +# AnSwEr0002 => '', +# AnSwEr0003 => '', +# }, +# }; +# +# bless $self; +# } +# +# +# our $result; +# +# ################################################## +# # Utilities -- +# # this code is identical between renderProblem.pl and renderViaXMLRPC.pm +# ################################################## +# +# sub xmlrpcCall { +# my $self = shift; +# my $command = shift; +# $command = 'listLibraries' unless $command; +# +# my $requestResult = TRANSPORT_METHOD +# -> proxy($self->{url}.'/'.REQUEST_URI); +# +# my $input = $self->setInputTable(); +# local( $result); +# # use eval to catch errors +# eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) }; +# if ($@) { +# print STDERR "There were a lot of errors for $command\n" ; +# print STDERR "Errors: \n $@\n End Errors\n" ; +# return 0 #failure +# } +# +# unless (ref($result) and $result->fault) { +# my $rh_result = $result->result(); +# #print STDERR pretty_print_rh($rh_result); +# $self->{output} = $rh_result; #$self->formatRenderedProblem($rh_result); +# return 1; # success +# +# } else { +# $self->{output} = 'Error from server: '. join( ",\n ", +# $result->faultcode, +# $result->faultstring); +# return 0; #failure +# } +# } +# +# sub encodeSource { +# my $self = shift; +# my $source = shift; +# $self->{encodedSource} =encode_base64($source); +# } +# sub url { +# my $self = shift; +# my $new_url = shift; +# $self->{url} = $new_url if defined($new_url) and $new_url =~ /\S/; +# $self->{url}; +# } +# sub pretty_print { # provides html output -- NOT a method +# my $r_input = shift; +# my $level = shift; +# $level = 4 unless defined($level); +# $level--; +# return '' unless $level > 0; # only print three levels of hashes (safety feature) +# my $out = ''; +# if ( not ref($r_input) ) { +# $out = $r_input if defined $r_input; # not a reference +# $out =~ s/"; +# +# +# foreach my $key ( sort ( keys %$r_input )) { +# $out .= " $key=> ".pretty_print($r_input->{$key}) . ""; +# } +# $out .=""; +# } elsif (ref($r_input) eq 'ARRAY' ) { +# my @array = @$r_input; +# $out .= "( " ; +# while (@array) { +# $out .= pretty_print(shift @array, $level) . " , "; +# } +# $out .= " )"; +# } elsif (ref($r_input) eq 'CODE') { +# $out = "$r_input"; +# } else { +# $out = $r_input; +# $out =~ s/ $self->{password}, +# set => 'set0', +# library_name => 'Library', +# command => 'all', +# }; +# +# $out; +# } +# sub setInputTable { +# my $self = shift; +# my $out = { +# pw => $self->{password}, +# library_name => 'Library', +# command => 'renderProblem', +# answer_form_submitted => 1, +# course => $self->{course}, +# extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr +# ExprWithImplicitExpand AnswerEvaluator +# AnswerEvaluatorMaker +# )], +# mode => $self->{displayMode}, +# modules_to_evaluate => [ qw( +# Exporter +# DynaLoader +# GD +# WWPlot +# Fun +# Circle +# Label +# PGrandom +# Units +# Hermite +# List +# Match +# Multiple +# Select +# AlgParser +# AnswerHash +# Fraction +# VectorField +# Complex1 +# Complex +# MatrixReal1 Matrix +# Distributions +# Regression +# +# )], +# envir => $self->environment(), +# problem_state => { +# +# num_of_correct_ans => 0, +# num_of_incorrect_ans => 4, +# recorded_score => 1.0, +# }, +# source => $self->{encodedSource}, #base64 encoded +# +# +# +# }; +# +# $out; +# } +# +# sub environment { +# my $self = shift; +# my $envir = { +# answerDate => '4014438528', +# CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', +# CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', +# CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', +# CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', +# cgiDirectory=>'Not defined', +# cgiURL => 'Not defined', +# classDirectory=> 'Not defined', +# courseName=>'Not defined', +# courseScriptsDirectory=>'not defined', +# displayMode=>$self->{displayMode}, +# dueDate=> '4014438528', +# effectivePermissionLevel => 10, +# externalGif2EpsPath=>'not defined', +# externalPng2EpsPath=>'not defined', +# externalTTHPath=>'/usr/local/bin/tth', +# fileName=>'set0/prob1a.pg', +# formattedAnswerDate=>'6/19/00', +# formattedDueDate=>'6/19/00', +# formattedOpenDate=>'6/19/00', +# functAbsTolDefault=> 0.0000001, +# functLLimitDefault=>0, +# functMaxConstantOfIntegration=> 1000000000000.0, +# functNumOfPoints=> 5, +# functRelPercentTolDefault=> 0.000001, +# functULimitDefault=>1, +# functVarDefault=> 'x', +# functZeroLevelDefault=> 0.000001, +# functZeroLevelTolDefault=>0.000001, +# htmlDirectory =>'not defined', +# htmlURL =>'not defined', +# inputs_ref => $self->{inputs_ref}, +# macroDirectory=>'not defined', +# numAbsTolDefault=>0.0000001, +# numFormatDefault=>'%0.13g', +# numOfAttempts=> 0, +# numRelPercentTolDefault => 0.0001, +# numZeroLevelDefault =>0.000001, +# numZeroLevelTolDefault =>0.000001, +# openDate=> '3014438528', +# permissionLevel =>10, +# PRINT_FILE_NAMES_FOR => [ 'gage'], +# probFileName => 'set0/prob1a.pg', +# problemSeed => 1234, +# problemValue =>1, +# probNum => 13, +# psvn => 54321, +# psvn=> 54321, +# questionNumber => 1, +# scriptDirectory => 'Not defined', +# sectionName => 'Gage', +# sectionNumber => 1, +# sessionKey=> 'Not defined', +# setNumber =>'not defined', +# studentLogin =>'gage', +# studentName => 'Mike Gage', +# tempDirectory => 'not defined', +# templateDirectory=>'not defined', +# tempURL=>'not defined', +# webworkDocsURL => 'not defined', +# +# showHints => 1, # extra options -- usually passed from the input form +# showSolutions => 1, +# +# }; +# $envir; +# }; +# +# sub formatAnswerRow { +# my $self = shift; +# my $rh_answer = shift; +# my $problemNumber = shift; +# my $answerString = $rh_answer->{original_student_ans}||' '; +# my $correctAnswer = $rh_answer->{correct_ans}||''; +# my $ans_message = $rh_answer->{ans_message}||''; +# my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect'; +# my $row = qq{ +# +# +# Prob: $problemNumber +# +# +# $answerString +# +# +# $score +# +# +# Correct answer is $correctAnswer +# +# +# $ans_message +# +# \n +# }; +# $row; +# } +# +# sub formatRenderedProblem { +# my $self = shift; +# my $rh_result = $self->{output}; # wrap problem in formats +# my $problemText = decode_base64($rh_result->{text}); +# my $rh_answers = $rh_result->{answers}; +# my $encodedSource = $self->{encodedSource}||'foobar'; +# my $warnings = ''; +# if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){ +# $warnings = "
+#

WARNINGS

".decode_base64($rh_result->{WARNINGS})."

"; +# } +# #warn "keys: ", join(" | ", sort keys %{$rh_result }); +# my $debug_messages = $rh_result->{flags}->{DEBUG_messages} || []; +# $debug_messages = join("
\n", @{ $debug_messages } ); +# my $internal_debug_messages = $rh_result->{internal_debug_messages} || []; +# $internal_debug_messages = join("
\n", @{ $internal_debug_messages } ); +# # collect answers +# my $answerTemplate = q{
ANSWERS }; +# my $problemNumber = 1; +# foreach my $key (sort keys %{$rh_answers}) { +# $answerTemplate .= $self->formatAnswerRow($rh_answers->{$key}, $problemNumber++); +# } +# $answerTemplate .= q{

}; +# +# my $FULL_URL = $self->url; +# my $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; +# my $problemTemplate = < +# +# +# WeBWorK Editor using host $HOSTNAME +# +# +# $answerTemplate +#
+# $problemText +# +# +# +# +# +#

+#
+#
+#

Warning section

+# $warnings +#

+# Debug message section +#

+# $debug_messages +#

+# internal errors +#

+# $internal_debug_messages +# +# +# +# +# ENDPROBLEMTEMPLATE +# +# +# +# $problemTemplate; +# } +# + +1; diff --git a/clients/webwork_soap_client.pl b/clients/webwork_soap_client.pl new file mode 100755 index 0000000000..5f0bc7a1cd --- /dev/null +++ b/clients/webwork_soap_client.pl @@ -0,0 +1,278 @@ +#!/usr/bin/perl -w + +use SOAP::Lite; + +# configuration section +use constant HOSTURL => 'localhost'; +use constant HOSTPORT => 80; +use constant TRANSPORT_METHOD => 'SOAP::Lite'; +use constant REQUEST_CLASS =>'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +use constant REQUEST_URI =>'mod_soap'; + +my @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf + +# $pg{displayModes} = [ +# "plainText", # display raw TeX for math expressions +# "formattedText", # format math expressions using TtH +# "images", # display math expressions as images generated by dvipng +# "jsMath", # render TeX math expressions on the client side using jsMath +# "asciimath", # render TeX math expressions on the client side using ASCIIMathML +# ]; +use constant DISPLAYMODE => 'images'; + +# end configuration section +use MIME::Base64 qw( encode_base64 decode_base64); + + +print STDERR "inputs are ", join (" | ", @ARGV), "\n"; +our $source; + +if (@ARGV) { + my $command = $ARGV[0]; + + warn "executing WebworkXMLRPC.$command"; + $source = (defined $ARGV[1]) ? `cat $ARGV[1]` : '' ; + xmlrpcCall($command); + + +} else { + + print STDERR "Useage: ./webwork_soap_client.pl command inputs\n"; + print STDERR "For example: ./webwork_soap_client renderProblem input.txt\n"; + print STDERR "For example: ./webwork_soap_client listLibraries \n"; + print STDERR "Commands are: ", join(" ", @COMMANDS), "\n"; + +} + + + +sub xmlrpcCall { + my $command = shift; + $command = 'listLibraries' unless $command; + + my $requestResult = TRANSPORT_METHOD + ->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS) + -> proxy('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_URI); + + my $test = [3,4,5,6]; + my $input = setInputTable(); + print "displayMode=",$input->{envir}->{displayMode},"\n"; + local( $result); + # use eval to catch errors + eval { $result = $requestResult->call("$command",$input) }; + print STDERR "There were a lot of errors\n" if $@; + print "Errors: \n $@\n End Errors\n" if $@; + + print "result is|", ref($result),"|"; + + unless (ref($result) and $result->fault) { + + if (ref($result->result())=~/HASH/ and defined($result->result()->{text}) ) { + $result->result()->{text} = decode_base64($result->result()->{text}); + } + print pretty_print_rh($result->result()),"\n"; #$result->result() + } else { + print 'oops ', join ', ', + $result->faultcode, + $result->faultstring; + } +} + +sub source { + encode_base64($source); +} +sub pretty_print_rh { + shift if UNIVERSAL::isa($_[0] => __PACKAGE__); + my $rh = shift; + my $indent = shift || 0; + my $out = ""; + my $type = ref($rh); + + if (defined($type) and $type) { + $out .= " type = $type; "; + } elsif (! defined($rh )) { + $out .= " type = UNDEFINED; "; + } + return $out." " unless defined($rh); + + if ( ref($rh) =~/HASH/ or "$rh" =~/HASH/ ) { + $out .= "{\n"; + $indent++; + foreach my $key (sort keys %{$rh}) { + $out .= " "x$indent."$key => " . pretty_print_rh( $rh->{$key}, $indent ) . "\n"; + } + $indent--; + $out .= "\n"." "x$indent."}\n"; + + } elsif (ref($rh) =~ /ARRAY/ or "$rh" =~/ARRAY/) { + $out .= " ( "; + foreach my $elem ( @{$rh} ) { + $out .= pretty_print_rh($elem, $indent); + + } + $out .= " ) \n"; + } elsif ( ref($rh) =~ /SCALAR/ ) { + $out .= "scalar reference ". ${$rh}; + } elsif ( ref($rh) =~/Base64/ ) { + $out .= "base64 reference " .$$rh; + } else { + $out .= $rh; + } + + return $out." "; +} + +sub setInputTable_for_listLib { + $out = { + #password => 'geometry', + pw => 'geometry', + set => 'set0', + library_name => 'rochesterLibrary', + command => 'all', + }; + + $out; +} +sub setInputTable { + $out = { + #password => 'geometry', + pw => 'geometry', + set => 'set0', + library_name => 'rochesterLibrary', + command => 'all', + answer_form_submitted => 1, + course => 'daemon_course', + extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr + ExprWithImplicitExpand AnswerEvaluator + AnswerEvaluatorMaker + )], + mode => 'HTML_dpng', + modules_to_evaluate => [ qw( +Exporter + +DynaLoader + + +GD +WWPlot +Fun +Circle +Label + + +PGrandom +Units +Hermite + +List + + +Match +Multiple +Select + + +AlgParser + +AnswerHash + + +Fraction +VectorField + + +Complex1 +Complex + + +MatrixReal1 Matrix + + +Distributions + +Regression + + )], + envir => environment(), + problem_state => { + + num_of_correct_ans => 2, + num_of_incorrect_ans => 4, + recorded_score => 1.0, + }, + source => source(), #base64 encoded + + + + }; + + $out; +} + +sub environment { + my $envir = { + answerDate => '4014438528', + CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', + CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', + CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', + CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', + cgiDirectory=>'Not defined', + cgiURL => 'Not defined', + classDirectory=> 'Not defined', + courseName=>'Not defined', + courseScriptsDirectory=>'/ww/webwork/system/courseScripts/', + displayMode=>DISPLAYMODE, + dueDate=> '4014438528', + externalGif2EpsPath=>'not defined', + externalPng2EpsPath=>'not defined', + externalTTHPath=>'/usr/local/bin/tth', + fileName=>'set0/prob1a.pg', + formattedAnswerDate=>'6/19/00', + formattedDueDate=>'6/19/00', + formattedOpenDate=>'6/19/00', + functAbsTolDefault=> 0.0000001, + functLLimitDefault=>0, + functMaxConstantOfIntegration=> 1000000000000.0, + functNumOfPoints=> 5, + functRelPercentTolDefault=> 0.000001, + functULimitDefault=>1, + functVarDefault=> 'x', + functZeroLevelDefault=> 0.000001, + functZeroLevelTolDefault=>0.000001, + htmlDirectory =>'/ww/webwork/courses/gage_course/html/', + htmlURL =>'http://webwork-db.math.rochester.edu/gage_course/', + inputs_ref => { + AnSwEr1 => '', + AnSwEr2 => '', + AnSwEr3 => '', + }, + macroDirectory=>'/ww/webwork/courses/gage_course/templates/macros/', + numAbsTolDefault=>0.0000001, + numFormatDefault=>'%0.13g', + numOfAttempts=> 0, + numRelPercentTolDefault => 0.0001, + numZeroLevelDefault =>0.000001, + numZeroLevelTolDefault =>0.000001, + openDate=> '3014438528', + PRINT_FILE_NAMES_FOR => [ 'gage'], + probFileName => 'set0/prob1a.pg', + problemSeed => 1234, + problemValue =>1, + probNum => 13, + psvn => 54321, + psvn=> 54321, + questionNumber => 1, + scriptDirectory => 'Not defined', + sectionName => 'Gage', + sectionNumber => 1, + sessionKey=> 'Not defined', + setNumber =>'MAAtutorial', + studentLogin =>'gage', + studentName => 'Mike Gage', + tempDirectory => '/ww/htdocs/tmp/gage_course/', + templateDirectory=>'/ww/webwork/courses/gage_course/templates/', + tempURL=>'http://webwork-db.math.rochester.edu/tmp/gage_course/', + webworkDocsURL => 'http://webwork.math.rochester.edu/webwork_gage_system_html', + }; + $envir; +}; \ No newline at end of file diff --git a/clients/webwork_xmlrpc_client.pl b/clients/webwork_xmlrpc_client.pl new file mode 100755 index 0000000000..dea4d2b0a5 --- /dev/null +++ b/clients/webwork_xmlrpc_client.pl @@ -0,0 +1,366 @@ +#!/usr/bin/perl -w + +=pod + +This script will take a command and an input +file. + +It will list available libraries, list the contents of libraries +or render the input file. + +All of this is done by contacting the webservice. + + + +=cut + +use feature ":5.10"; +use XMLRPC::Lite; +use MIME::Base64 qw( encode_base64 decode_base64); + +# configuration section +use constant PROTOCOL => 'http'; # or 'http'; +use constant HOSTURL => 'localhost'; +use constant HOSTPORT => '80'; # or 80 +use constant TRANSPORT_METHOD => 'XMLRPC::Lite'; +use constant REQUEST_CLASS =>'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +use constant REQUEST_URI =>'mod_xmlrpc'; +use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; +use constant COURSENAME => 'gage_course'; + +my $credential_path = ".ww_credentials"; +eval{require $credential_path}; +if ($@ ) { +print STDERR < "my login name for the webwork course", + password => "my password ", + courseID => "the name of the webwork course", +) +--------------------------------------------------------- +EOF +die; +} + + +# print "credentials: ", join(" | ", %credentials), "\n"; + +my @COMMANDS = qw( listLibraries renderProblem listLib readFile tex2pdf ); + +# $pg{displayModes} = [ +# "plainText", # display raw TeX for math expressions +# "formattedText", # format math expressions using TtH +# "images", # display math expressions as images generated by dvipng +# "jsMath", # render TeX math expressions on the client side using jsMath +# "asciimath", # render TeX math expressions on the client side using ASCIIMathML +# ]; +use constant DISPLAYMODE => 'images'; + + +# end configuration section + +our $courseName = $credentials{courseID}; + +print STDERR "inputs are ", join (" | ", @ARGV), "\n"; +our $source; + +if (@ARGV) { + my $command = $ARGV[0]; + my $result; + print "executing WebworkXMLRPC.$command \n\n-----------------------\n\n"; + given($command) { + when ('renderProblem') { if ( defined $ARGV[1]) { + if (-r $ARGV[1] ) { + $source = `cat $ARGV[1]`; + xmlrpcCall($command); + } else { + print STDERR "Can't read source file $ARGV[1]\n"; + } + } else { + print STDERR "Useage: ./webwork_xmlrpc_client.pl command \n"; + } + } + when ('listLibraries') {$result = xmlrpcCall($command); + if (defined($result) ) { + print STDOUT "The libraries available in course $courseName are:\n\t ", join("\n\t ", @$result), "\n"; + } else { + print STDOUT "No libraries available for course $courseName\n"; + } + } + when ('listLib') {listLib( @ARGV );} + when ('readFile') {print STDERR "Command $command not yet implemented\n"} + when ('tex2pdf') {print STDERR "Command $command not yet implemented\n"} + } + + +} else { + + print STDERR "Useage: ./webwork_xmlrpc_client.pl command [file_name]\n"; + print STDERR "For example: ./webwork_xmlrpc_client.pl renderProblem \n"; + print STDERR "Commands are: ", join(" ", @COMMANDS), "\n"; + +} + + + +sub xmlrpcCall { + my $command = shift; + my $input = shift||{}; + $command = 'listLibraries' unless defined $command; + my $std_input = standard_input(); + $input = {%$std_input, %$input}; # concatenate and override standard_input + + # print "new input is ", pretty_print_rh($input); + + my $requestResult = TRANSPORT_METHOD + #->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS) + -> proxy(PROTOCOL.'://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_URI); + + # my $test = [3,4,5,6]; + + # print "displayMode=",$input->{envir}->{displayMode},"\n"; + local( $result); + # use eval to catch errors + eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) }; + print STDERR "There were a lot of errors\n" if $@; + print "Errors: \n $@\n End Errors\n" if $@; + + print "result is <|", ref($result),"|>\n"; + + unless (ref($result) and $result->fault) { + + if (ref($result->result())=~/HASH/ and defined($result->result()->{text}) ) { + $result->result()->{text} = decode_base64($result->result()->{text}); + } + print pretty_print_rh($result->result()),"\n"; #$result->result() + return $result->result(); + } else { + print 'oops ', join ', ', + $result->faultcode, + $result->faultstring; + return undef; + } +} + +sub source { + return "" unless $source; + return encode_base64($source); +} +sub listLib { + my @ARGS = @_; + #print "args for listLib are ", join(" ", @ARGS), "\n"; + given($ARGS[1]) { + when ("all") { $input = { pw => 'xmluser', + password => $credentials{password}, + user => $credentials{userID}, + courseID => $credentials{courseID}, + command => 'all', + }; + xmlrpcCall("listLib", $input); + } + when ('dirOnly') { $input = { pw => 'xmluser', + password => $credentials{password}, + user => $credentials{userID}, + courseID => $credentials{courseID}, + command => 'dirOnly', + }; + xmlrpcCall("listLib", $input); + } + when('files') { if ($ARGS[2] ) { + my $dirPath = $ARGS[2]; + $input = { pw => 'xmluser', + password => $credentials{password}, + user => $credentials{userID}, + courseID => $credentials{courseID}, + command => 'files', + dirPath => $dirPath, + }; + xmlrpcCall("listLib", $input); + } else { + print STDERR "Usage: webwork_xmlrpc_client listLib files \n" + } + + } + + + + + default {print "The possible arguments for listLib are:". + "\n\t all -- print all paths". + "\n\t dirOnly -- print only directories". + "\n\t files -- print .pg files in the given directory \n" + } + } +} +sub pretty_print_rh { + shift if UNIVERSAL::isa($_[0] => __PACKAGE__); + my $rh = shift; + my $indent = shift || 0; + my $out = ""; + my $type = ref($rh); + + if (defined($type) and $type) { + $out .= " type = $type; "; + } elsif (! defined($rh )) { + $out .= " type = UNDEFINED; "; + } + return $out." " unless defined($rh); + + if ( ref($rh) =~/HASH/ or "$rh" =~/HASH/ ) { + $out .= "{\n"; + $indent++; + foreach my $key (sort keys %{$rh}) { + $out .= " "x$indent."$key => " . pretty_print_rh( $rh->{$key}, $indent ) . "\n"; + } + $indent--; + $out .= "\n"." "x$indent."}\n"; + + } elsif (ref($rh) =~ /ARRAY/ or "$rh" =~/ARRAY/) { + $out .= " ( "; + foreach my $elem ( @{$rh} ) { + $out .= pretty_print_rh($elem, $indent); + + } + $out .= " ) \n"; + } elsif ( ref($rh) =~ /SCALAR/ ) { + $out .= "scalar reference ". ${$rh}; + } elsif ( ref($rh) =~/Base64/ ) { + $out .= "base64 reference " .$$rh; + } else { + $out .= $rh; + } + + return $out." "; +} + + +sub standard_input { + $out = { + pw => 'xmluser', + password => $credentials{password}, + userID => $credentials{userID}, + set => 'set0', + library_name => 'Library', + command => 'all', + answer_form_submitted => 1, + courseID => $credentials{courseID},, + extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr + ExprWithImplicitExpand AnswerEvaluator + AnswerEvaluatorMaker + )], + mode => DISPLAYMODE(), + modules_to_evaluate => [ qw( +Exporter +DynaLoader +GD +WWPlot +Fun +Circle +Label +PGrandom +Units +Hermite +List +Match +Multiple +Select +AlgParser +AnswerHash +Fraction +VectorField +Complex1 +Complex +MatrixReal1 Matrix +Distributions +Regression + + )], + envir => environment(), + problem_state => { + + num_of_correct_ans => 2, + num_of_incorrect_ans => 4, + recorded_score => 1.0, + }, + source => source(), #base64 encoded + + + + }; + + $out; +} + +sub environment { + my $envir = { + answerDate => '4014438528', + CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', + CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', + CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', + CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', + cgiDirectory=>'Not defined', + cgiURL => 'Not defined', + classDirectory=> 'Not defined', + courseName=>'Not defined', + courseScriptsDirectory=>'/ww/webwork/system/courseScripts/', + displayMode=>DISPLAYMODE, + dueDate=> '4014438528', + externalGif2EpsPath=>'not defined', + externalPng2EpsPath=>'not defined', + externalTTHPath=>'/usr/local/bin/tth', + fileName=>'set0/prob1a.pg', + formattedAnswerDate=>'6/19/00', + formattedDueDate=>'6/19/00', + formattedOpenDate=>'6/19/00', + functAbsTolDefault=> 0.0000001, + functLLimitDefault=>0, + functMaxConstantOfIntegration=> 1000000000000.0, + functNumOfPoints=> 5, + functRelPercentTolDefault=> 0.000001, + functULimitDefault=>1, + functVarDefault=> 'x', + functZeroLevelDefault=> 0.000001, + functZeroLevelTolDefault=>0.000001, + htmlDirectory =>'/ww/webwork/courses/gage_course/html/', + htmlURL =>'http://webwork-db.math.rochester.edu/gage_course/', + inputs_ref => { + AnSwEr1 => '', + AnSwEr2 => '', + AnSwEr3 => '', + }, + macroDirectory=>'/ww/webwork/courses/gage_course/templates/macros/', + numAbsTolDefault=>0.0000001, + numFormatDefault=>'%0.13g', + numOfAttempts=> 0, + numRelPercentTolDefault => 0.0001, + numZeroLevelDefault =>0.000001, + numZeroLevelTolDefault =>0.000001, + openDate=> '3014438528', + PRINT_FILE_NAMES_FOR => [ 'gage'], + probFileName => 'set0/prob1a.pg', + problemSeed => 1234, + problemValue =>1, + probNum => 13, + psvn => 54321, + psvn=> 54321, + questionNumber => 1, + scriptDirectory => 'Not defined', + sectionName => 'Gage', + sectionNumber => 1, + sessionKey=> 'Not defined', + setNumber =>'MAAtutorial', + studentLogin =>'gage', + studentName => 'Mike Gage', + tempDirectory => '/ww/htdocs/tmp/gage_course/', + templateDirectory=>'/ww/webwork/courses/gage_course/templates/', + tempURL=>'http://webwork-db.math.rochester.edu/tmp/gage_course/', + webworkDocsURL => 'http://webwork.math.rochester.edu/webwork_gage_system_html', + }; + $envir; +}; diff --git a/conf/database.conf.dist b/conf/database.conf.dist new file mode 100644 index 0000000000..5a4d19a95c --- /dev/null +++ b/conf/database.conf.dist @@ -0,0 +1,312 @@ +#!perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/database.conf.dist,v 1.38 2007/08/13 22:59:51 sh002i 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. +################################################################################ + +=head1 NAME + +database.conf - define standard database layouts + +=head1 SYNOPSIS + +In global.conf: + + include "conf/database.conf"; + *dbLayout = $dbLayouts{layoutName}; + +=head1 DESCRIPTION + +This file contains definitions for the commonly-used database layouts. Database +layouts consist of all the information necessary to describe how to access data +used by WeBWorK. For more information on the format of a database layout, +consult the documentation for the WeBWorK::DB module. + +A database layout is selected from the list of possible layouts by adding a +line like the one below to the F or F file. + + $dbLayoutName = "layoutName"; + *dbLayout = $dbLayouts{$dbLayoutName}; + +=cut + +%dbLayouts = (); # layouts are added to this hash below + +=head2 THE SQL_SINGLE DATABASE LAYOUT + +The C layout is similar to the C layout, excpet that it uses a +single database for all courses. This is accomplised by prefixing each table +name with the name of the course. The names and passwords of these accounts are +given as parameters to each table in the layout. + + username the username to use when connecting to the database + password the password to use when connecting to the database + +Be default, username is "webworkRead" and password is "". It is not recommended +that you use only a non-empty password to secure database access. Most RDBMSs +allow IP-based authorization as well. As the system administrator, IT IS YOUR +RESPONSIBILITY TO SECURE DATABASE ACCESS. + +Don't confuse the account information above with the accounts of the users of a +course. This is a system-wide account which allow WeBWorK to talk to the +database server. + +Other parameters that can be given are as follows: + + tableOverride an alternate name to use when referrring to the table (used + when a table name is a resereved word) + fieldOverride a hash mapping WeBWorK field names to alternate names to use + when referring to those fields (used when one or more field + names are reserved words) + debug if true, SQL statments are printed before being executed + +=cut + +# params common to all tables +my %sqlParams = ( + username => $database_username, + password => $database_password, + debug => $database_debug, + # kinda hacky, but needed for table dumping + mysql_path => $externalPrograms{mysql}, + mysqldump_path => $externalPrograms{mysqldump}, +); + +$dbLayouts{sql_single} = { + locations => { + record => "WeBWorK::DB::Record::Locations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + non_native => 1, + }, + }, + location_addresses => { + record => "WeBWorK::DB::Record::LocationAddresses", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + non_native => 1, + }, + }, + depths => { + record => "WeBWorK::DB::Record::Depths", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + non_native => 1, + }, + }, + password => { + record => "WeBWorK::DB::Record::Password", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_password", + }, + }, + permission => { + record => "WeBWorK::DB::Record::PermissionLevel", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_permission", + }, + }, + key => { + record => "WeBWorK::DB::Record::Key", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_key", + fieldOverride => { key => "key_not_a_keyword" }, + }, + }, + user => { + record => "WeBWorK::DB::Record::User", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_user", + }, + }, + set => { + record => "WeBWorK::DB::Record::Set", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_set", + fieldOverride => { visible => "published" }, # for compatibility -- visible was originally called published + }, + }, + set_user => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_set_user", + fieldOverride => { visible => "published" }, # for compatibility -- visible was originally called published + }, + }, + set_merged => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + depend => [qw/set_user set/], + params => { %sqlParams, + non_native => 1, + merge => [qw/set_user set/], + }, + }, + set_version => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + non_native => 1, + tableOverride => "${courseName}_set_user", + fieldOverride => { visible => "published" }, # for compatibility -- visible was originally called published + + }, + }, + set_version_merged => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + depend => [qw/set_version set_user set/], + params => { %sqlParams, + non_native => 1, + merge => [qw/set_version set_user set/], + }, + }, + set_locations => { + record => "WeBWorK::DB::Record::SetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_set_locations" + }, + }, + set_locations_user => { + record => "WeBWorK::DB::Record::UserSetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_set_locations_user" + }, + }, + problem => { + record => "WeBWorK::DB::Record::Problem", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_problem" + }, + }, + problem_user => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_problem_user" + }, + }, + problem_merged => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + depend => [qw/problem_user problem/], + params => { %sqlParams, + non_native => 1, + merge => [qw/problem_user problem/], + }, + }, + problem_version => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + non_native => 1, + tableOverride => "${courseName}_problem_user", + }, + }, + problem_version_merged => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + depend => [qw/problem_version problem_user problem/], + params => { %sqlParams, + non_native => 1, + merge => [qw/problem_version problem_user problem/], + }, + }, + setting => { + record => "WeBWorK::DB::Record::Setting", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $database_dsn, + params => { %sqlParams, + tableOverride => "${courseName}_setting" + }, + }, +}; + + +=head1 DATABASE LAYOUT METADATA + +=over + +=item @dbLayout_order + +Database layouts listed in this array will be displayed first, in the order +specified, wherever database layouts are listed. (For example, in the "Add +Course" tool.) Other layouts are listed after these. + +=cut + +@dbLayout_order = qw/sql_single sql_moodle/; + +=item %dbLayout_descr + +Hash mapping database layout names to textual descriptions. + +=cut + +%dbLayout_descr = ( + sql_single => "Uses a single SQL database to record WeBWorK data for all courses using this layout. This is the recommended layout for new courses.", +# sql_moodle => "Simiar to sql_single, but uses a Moodle database for user, password, and permission information. This layout should be used for courses used with wwmoodle.", +); + +=back + +=cut diff --git a/conf/httpd-wwmp-header.conf.dist b/conf/devel-site.apache-config.dist similarity index 65% rename from conf/httpd-wwmp-header.conf.dist rename to conf/devel-site.apache-config.dist index 98fb7941b3..a48bd07574 100644 --- a/conf/httpd-wwmp-header.conf.dist +++ b/conf/devel-site.apache-config.dist @@ -1,7 +1,7 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/devel-site.apache-config.dist,v 1.7 2006/09/01 14:16:36 sh002i 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 @@ -14,21 +14,23 @@ # Artistic License for more details. ################################################################################ -# This file contains apache configuration directives that should be suitable for -# any WeBWorK 2 development installation. http-wwmp.conf.dist expects to find -# this file at /path/to/webwork-modperl/conf/httpd-wwmp-header.conf. Simply -# copying this file to that location should suffice. +# This file contains the site-specific (but user-independent) directives used +# in the configuration of Apache servers for WeBWorK development. See the file +# devel.apache-config for more information. +# +# Configure this file to match your main Apache configuration file, usually +# apache.conf or httpd.conf. This file ships configured use on the host +# devel.webwork.rochester.edu. -### Section 1: Global Environment -ServerType standalone -ServerRoot /usr/local +################################################################################ +# Section 1: Global Environment +################################################################################ +ServerType standalone +#ServerRoot /usr/local ResourceConfig /dev/null AccessConfig /dev/null - Timeout 300 - -# Whether or not to allow persistent connections KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 15 @@ -36,39 +38,45 @@ KeepAliveTimeout 15 # Dynamic Shared Object (DSO) Support # Note: The order in which modules are loaded is important. Don't change # the order below without expert advice. -LoadModule mmap_static_module libexec/apache/mod_mmap_static.so -LoadModule vhost_alias_module libexec/apache/mod_vhost_alias.so -LoadModule env_module libexec/apache/mod_env.so -LoadModule config_log_module libexec/apache/mod_log_config.so -LoadModule mime_magic_module libexec/apache/mod_mime_magic.so -LoadModule mime_module libexec/apache/mod_mime.so -LoadModule negotiation_module libexec/apache/mod_negotiation.so -LoadModule status_module libexec/apache/mod_status.so -LoadModule info_module libexec/apache/mod_info.so -LoadModule includes_module libexec/apache/mod_include.so -LoadModule autoindex_module libexec/apache/mod_autoindex.so -LoadModule dir_module libexec/apache/mod_dir.so -LoadModule cgi_module libexec/apache/mod_cgi.so -LoadModule asis_module libexec/apache/mod_asis.so -LoadModule imap_module libexec/apache/mod_imap.so -LoadModule action_module libexec/apache/mod_actions.so -LoadModule speling_module libexec/apache/mod_speling.so -LoadModule userdir_module libexec/apache/mod_userdir.so -LoadModule alias_module libexec/apache/mod_alias.so -LoadModule rewrite_module libexec/apache/mod_rewrite.so -LoadModule access_module libexec/apache/mod_access.so -LoadModule auth_module libexec/apache/mod_auth.so -LoadModule anon_auth_module libexec/apache/mod_auth_anon.so -LoadModule db_auth_module libexec/apache/mod_auth_db.so -LoadModule digest_module libexec/apache/mod_digest.so -LoadModule proxy_module libexec/apache/libproxy.so -LoadModule cern_meta_module libexec/apache/mod_cern_meta.so -LoadModule expires_module libexec/apache/mod_expires.so -LoadModule headers_module libexec/apache/mod_headers.so -LoadModule usertrack_module libexec/apache/mod_usertrack.so -LoadModule unique_id_module libexec/apache/mod_unique_id.so -LoadModule setenvif_module libexec/apache/mod_setenvif.so -LoadModule perl_module libexec/apache/libperl.so +LoadModule mmap_static_module /usr/local/libexec/apache/mod_mmap_static.so +LoadModule vhost_alias_module /usr/local/libexec/apache/mod_vhost_alias.so +LoadModule env_module /usr/local/libexec/apache/mod_env.so +LoadModule define_module /usr/local/libexec/apache/mod_define.so +LoadModule config_log_module /usr/local/libexec/apache/mod_log_config.so +LoadModule mime_magic_module /usr/local/libexec/apache/mod_mime_magic.so +LoadModule mime_module /usr/local/libexec/apache/mod_mime.so +LoadModule negotiation_module /usr/local/libexec/apache/mod_negotiation.so +LoadModule status_module /usr/local/libexec/apache/mod_status.so +LoadModule info_module /usr/local/libexec/apache/mod_info.so +LoadModule includes_module /usr/local/libexec/apache/mod_include.so +LoadModule autoindex_module /usr/local/libexec/apache/mod_autoindex.so +LoadModule dir_module /usr/local/libexec/apache/mod_dir.so +LoadModule cgi_module /usr/local/libexec/apache/mod_cgi.so +LoadModule asis_module /usr/local/libexec/apache/mod_asis.so +LoadModule imap_module /usr/local/libexec/apache/mod_imap.so +LoadModule action_module /usr/local/libexec/apache/mod_actions.so +LoadModule speling_module /usr/local/libexec/apache/mod_speling.so +LoadModule userdir_module /usr/local/libexec/apache/mod_userdir.so +LoadModule alias_module /usr/local/libexec/apache/mod_alias.so +LoadModule rewrite_module /usr/local/libexec/apache/mod_rewrite.so +LoadModule access_module /usr/local/libexec/apache/mod_access.so +LoadModule auth_module /usr/local/libexec/apache/mod_auth.so +LoadModule anon_auth_module /usr/local/libexec/apache/mod_auth_anon.so +LoadModule db_auth_module /usr/local/libexec/apache/mod_auth_db.so +LoadModule digest_module /usr/local/libexec/apache/mod_digest.so +LoadModule proxy_module /usr/local/libexec/apache/libproxy.so +LoadModule cern_meta_module /usr/local/libexec/apache/mod_cern_meta.so +LoadModule expires_module /usr/local/libexec/apache/mod_expires.so +LoadModule headers_module /usr/local/libexec/apache/mod_headers.so +LoadModule usertrack_module /usr/local/libexec/apache/mod_usertrack.so +LoadModule log_forensic_module /usr/local/libexec/apache/mod_log_forensic.so +LoadModule unique_id_module /usr/local/libexec/apache/mod_unique_id.so +LoadModule setenvif_module /usr/local/libexec/apache/mod_setenvif.so + +LoadModule ssl_module /usr/local/libexec/apache/libssl.so + +LoadModule perl_module /usr/local/libexec/apache/libperl.so +LoadModule php4_module /usr/local/libexec/apache/libphp4.so # Reconstruction of the complete module list from all available modules # (static and shared ones) to achieve correct module execution order. @@ -77,6 +85,7 @@ ClearModuleList AddModule mod_mmap_static.c AddModule mod_vhost_alias.c AddModule mod_env.c +AddModule mod_define.c AddModule mod_log_config.c AddModule mod_mime_magic.c AddModule mod_mime.c @@ -104,25 +113,32 @@ AddModule mod_cern_meta.c AddModule mod_expires.c AddModule mod_headers.c AddModule mod_usertrack.c +AddModule mod_log_forensic.c AddModule mod_unique_id.c AddModule mod_so.c AddModule mod_setenvif.c + +AddModule mod_ssl.c + AddModule mod_perl.c +AddModule mod_php4.c ExtendedStatus On -### Section 2: 'Main' server configuration -ServerName webwork-dev.math.rochester.edu +################################################################################ +# Section 2: 'Main' server configuration +################################################################################ + +ServerName devel.webwork.rochester.edu +UseCanonicalName Off Options FollowSymLinks AllowOverride None -# # DirectoryIndex: Name of the file or files to use as a pre-written HTML # directory index. Separate multiple entries with spaces. -# @@ -151,8 +167,6 @@ AccessFileName .htaccess Satisfy All -UseCanonicalName Off - TypesConfig /usr/local/etc/apache/mime.types @@ -195,34 +209,28 @@ ServerSignature On Allow from all - # The same rules about trailing "/" apply to ScriptAlias directives as to - # Alias. - ScriptAlias /cgi-bin/ "/usr/local/www/cgi-bin/" - - - AllowOverride None - Options None - Order allow,deny - Allow from all - +# # The same rules about trailing "/" apply to ScriptAlias directives as to +# # Alias. +# ScriptAlias /cgi-bin/ "/usr/local/www/cgi-bin/" +# +# +# AllowOverride None +# Options None +# Order allow,deny +# Allow from all +# -# Redirect old-URI new-URL - # Directives controlling the display of server-generated directory listings. - # # FancyIndexing is whether you want fancy directory indexing or standard - # IndexOptions FancyIndexing - # # AddIcon* directives tell the server which icon to show for different # files or filename extensions. These are only displayed for # FancyIndexed directories. - # AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip AddIconByType (TXT,/icons/text.gif) text/* @@ -267,9 +275,7 @@ ServerSignature On IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t -# # Document types. -# # @@ -368,16 +374,43 @@ ServerSignature On BrowserMatch "JDK/1\.0" force-response-1.0 -# -# SetHandler server-status -# Order deny,allow -# Deny from all -# Allow from .math.rochester.edu -# - -# -# SetHandler server-info -# Order deny,allow -# Deny from all -# Allow from .math.rochester.edu -# + + SetHandler server-status + Order allow,deny + Allow from all + + + + SetHandler server-info + Order allow,deny + Allow from all + + + + SetHandler perl-script + PerlHandler Apache::Status + PerlSetVar StatusOptionsAll On + Order allow,deny + Allow from all + + + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + + + SSLPassPhraseDialog builtin + + SSLSessionCache dbm:run/ssl_scache + SSLSessionCacheTimeout 300 + + SSLMutex file:run/ssl_mutex + + SSLRandomSeed startup builtin + SSLRandomSeed connect builtin + + #sam# label this as an apache1 log + SSLLog logs/httpd-ssl.log + SSLLogLevel info + diff --git a/conf/devel-site.apache2-config.dist b/conf/devel-site.apache2-config.dist new file mode 100644 index 0000000000..c800554ea1 --- /dev/null +++ b/conf/devel-site.apache2-config.dist @@ -0,0 +1,1133 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# $CVSHeader: webwork2/conf/devel-site.apache2-config.dist,v 1.3 2006/08/03 16:13:48 sh002i Exp $ +################################################################################ + +# This file contains the site-specific (but user-independent) directives used +# in the configuration of Apache servers for WeBWorK development. See the file +# devel.apache-config for more information. +# +# Configure this file to match your main Apache configuration file, usually +# apache.conf or httpd.conf. This file is based on the slightly-modified +# httpd.conf as installed on devel.webwork.rochester.edu. + +# +# Based upon the NCSA server configuration files originally by Rob McCool. +# +# This is the main Apache server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information about +# the directives. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# The configuration directives are grouped into three basic sections: +# 1. Directives that control the operation of the Apache server process as a +# whole (the 'global environment'). +# 2. Directives that define the parameters of the 'main' or 'default' server, +# which responds to requests that aren't handled by a virtual host. +# These directives also provide default values for the settings +# of all virtual hosts. +# 3. Settings for virtual hosts, which allow Web requests to be sent to +# different IP addresses or hostnames and have them handled by the +# same Apache server process. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so "logs/foo.log" +# with ServerRoot set to "/usr/local/apache2" will be interpreted by the +# server as "/usr/local/apache2/logs/foo.log". +# + +### Section 1: Global Environment +# +# The directives in this section affect the overall operation of Apache, +# such as the number of concurrent requests it can handle or where it +# can find its configuration files. +# + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# NOTE! If you intend to place this on an NFS (or otherwise network) +# mounted filesystem then please read the LockFile documentation (available +# at ); +# you will save yourself a lot of trouble. +# +# Do NOT add a slash at the end of the directory path. +# +#ww# passed in when httpd is started (-d) +#ServerRoot "/usr/local/apache2" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# + + +#LockFile logs/accept.lock + + + +# +# ScoreBoardFile: File used to store internal server process information. +# If unspecified (the default), the scoreboard will be stored in an +# anonymous shared memory segment, and will be unavailable to third-party +# applications. +# If specified, ensure that no two invocations of Apache share the same +# scoreboard file. The scoreboard file MUST BE STORED ON A LOCAL DISK. +# + + +#ScoreBoardFile logs/apache_runtime_status + + + + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# + +#ww# don't clobber apache1 pidfile +#PidFile logs/httpd.pid +PidFile logs/httpd2.pid + + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout 300 + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive On + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests 100 + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout 15 + +## +## Server-Pool Size Regulation (MPM specific) +## + +# prefork MPM +# StartServers: number of server processes to start +# MinSpareServers: minimum number of server processes which are kept spare +# MaxSpareServers: maximum number of server processes which are kept spare +# MaxClients: maximum number of server processes allowed to start +# MaxRequestsPerChild: maximum number of requests a server process serves + +StartServers 5 +MinSpareServers 5 +MaxSpareServers 10 +MaxClients 150 +MaxRequestsPerChild 0 + + +# worker MPM +# StartServers: initial number of server processes to start +# MaxClients: maximum number of simultaneous client connections +# MinSpareThreads: minimum number of worker threads which are kept spare +# MaxSpareThreads: maximum number of worker threads which are kept spare +# ThreadsPerChild: constant number of worker threads in each server process +# MaxRequestsPerChild: maximum number of requests a server process serves + +StartServers 2 +MaxClients 150 +MinSpareThreads 25 +MaxSpareThreads 75 +ThreadsPerChild 25 +MaxRequestsPerChild 0 + + +# perchild MPM +# NumServers: constant number of server processes +# StartThreads: initial number of worker threads in each server process +# MinSpareThreads: minimum number of worker threads which are kept spare +# MaxSpareThreads: maximum number of worker threads which are kept spare +# MaxThreadsPerChild: maximum number of worker threads in each server process +# MaxRequestsPerChild: maximum number of connections per server process + +NumServers 5 +StartThreads 5 +MinSpareThreads 5 +MaxSpareThreads 10 +MaxThreadsPerChild 20 +MaxRequestsPerChild 0 + + +# WinNT MPM +# ThreadsPerChild: constant number of worker threads in the server process +# MaxRequestsPerChild: maximum number of requests a server process serves + +ThreadsPerChild 250 +MaxRequestsPerChild 0 + + +# BeOS MPM +# StartThreads: how many threads do we initially spawn? +# MaxClients: max number of threads we can have (1 thread == 1 client) +# MaxRequestsPerThread: maximum number of requests each thread will process + +StartThreads 10 +MaxClients 50 +MaxRequestsPerThread 10000 + + +# NetWare MPM +# ThreadStackSize: Stack size allocated for each worker thread +# StartThreads: Number of worker threads launched at server startup +# MinSpareThreads: Minimum number of idle threads, to handle request spikes +# MaxSpareThreads: Maximum number of idle threads +# MaxThreads: Maximum number of worker threads alive at the same time +# MaxRequestsPerChild: Maximum number of requests a thread serves. It is +# recommended that the default value of 0 be set for this +# directive on NetWare. This will allow the thread to +# continue to service requests indefinitely. + +ThreadStackSize 65536 +StartThreads 250 +MinSpareThreads 25 +MaxSpareThreads 250 +MaxThreads 1000 +MaxRequestsPerChild 0 +MaxMemFree 100 + + +# OS/2 MPM +# StartServers: Number of server processes to maintain +# MinSpareThreads: Minimum number of idle threads per process, +# to handle request spikes +# MaxSpareThreads: Maximum number of idle threads per process +# MaxRequestsPerChild: Maximum number of connections per server process + +StartServers 2 +MinSpareThreads 5 +MaxSpareThreads 10 +MaxRequestsPerChild 0 + + +# +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, instead of the default. See also the +# directive. +# +# Change this to Listen on specific IP addresses as shown below to +# prevent Apache from glomming onto all bound IP addresses (0.0.0.0) +# +#Listen 12.34.56.78:80 +#ww# set in devel.apache2-config +#Listen 0.0.0.0:80 +#Listen [::]:80 + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +#ww# prepend /usr/local/apache2 to each path here +#ww# some unneeded modules commented out +#ww#LoadModule auth_anon_module /usr/local/apache2/modules/mod_auth_anon.so +#ww#LoadModule auth_dbm_module /usr/local/apache2/modules/mod_auth_dbm.so +#ww#LoadModule auth_digest_module /usr/local/apache2/modules/mod_auth_digest.so +LoadModule file_cache_module /usr/local/apache2/modules/mod_file_cache.so +LoadModule cache_module /usr/local/apache2/modules/mod_cache.so +LoadModule disk_cache_module /usr/local/apache2/modules/mod_disk_cache.so +LoadModule ext_filter_module /usr/local/apache2/modules/mod_ext_filter.so +LoadModule include_module /usr/local/apache2/modules/mod_include.so +LoadModule deflate_module /usr/local/apache2/modules/mod_deflate.so +LoadModule mime_magic_module /usr/local/apache2/modules/mod_mime_magic.so +LoadModule cern_meta_module /usr/local/apache2/modules/mod_cern_meta.so +LoadModule expires_module /usr/local/apache2/modules/mod_expires.so +LoadModule headers_module /usr/local/apache2/modules/mod_headers.so +LoadModule usertrack_module /usr/local/apache2/modules/mod_usertrack.so +LoadModule unique_id_module /usr/local/apache2/modules/mod_unique_id.so +#ww#LoadModule proxy_module /usr/local/apache2/modules/mod_proxy.so +#ww#LoadModule proxy_connect_module /usr/local/apache2/modules/mod_proxy_connect.so +#ww#LoadModule proxy_ftp_module /usr/local/apache2/modules/mod_proxy_ftp.so +#ww#LoadModule proxy_http_module /usr/local/apache2/modules/mod_proxy_http.so + +LoadModule ssl_module /usr/local/apache2/modules/mod_ssl.so + +#ww#LoadModule dav_module /usr/local/apache2/modules/mod_dav.so +LoadModule asis_module /usr/local/apache2/modules/mod_asis.so +LoadModule info_module /usr/local/apache2/modules/mod_info.so +#ww#LoadModule suexec_module /usr/local/apache2/modules/mod_suexec.so +#ww#LoadModule cgi_module /usr/local/apache2/modules/mod_cgi.so +#ww#LoadModule cgid_module /usr/local/apache2/modules/mod_cgid.so +#ww#LoadModule dav_fs_module /usr/local/apache2/modules/mod_dav_fs.so +LoadModule vhost_alias_module /usr/local/apache2/modules/mod_vhost_alias.so +LoadModule imap_module /usr/local/apache2/modules/mod_imap.so +LoadModule actions_module /usr/local/apache2/modules/mod_actions.so +LoadModule speling_module /usr/local/apache2/modules/mod_speling.so +LoadModule userdir_module /usr/local/apache2/modules/mod_userdir.so +LoadModule rewrite_module /usr/local/apache2/modules/mod_rewrite.so +LoadModule perl_module /usr/local/apache2/modules/mod_perl.so +LoadModule apreq_module /usr/local/apache2/modules/mod_apreq2.so + +# +# ExtendedStatus controls whether Apache will generate "full" status +# information (ExtendedStatus On) or just basic information (ExtendedStatus +# Off) when the "server-status" handler is called. The default is Off. +# +#ExtendedStatus On + +### Section 2: 'Main' server configuration +# +# The directives in this section set up the values used by the 'main' +# server, which responds to any requests that aren't handled by a +# definition. These values also provide defaults for +# any containers you may define later in the file. +# +# All of these directives may appear inside containers, +# in which case these default settings will be overridden for the +# virtual host being defined. +# + + + +# +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. +# +# User/Group: The name (or #number) of the user/group to run httpd as. +# . On SCO (ODT 3) use "User nouser" and "Group nogroup". +# . On HPUX you may not be able to use shared memory as nobody, and the +# suggested workaround is to create a user www and use that user. +# NOTE that some kernels refuse to setgid(Group) or semctl(IPC_SET) +# when the value of (unsigned)Group is above 60000; +# don't use Group #-1 on these systems! +# +#ww# set in devel.apache2-config +#User nobody +#Group #-1 + + + +# +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. This address appears on some server-generated pages, such +# as error documents. e.g. admin@your-domain.com +# +#ww# set in devel.apache2-config +#ServerAdmin you@example.com + +# +# ServerName gives the name and port that the server uses to identify itself. +# This can often be determined automatically, but we recommend you specify +# it explicitly to prevent problems during startup. +# +# If this is not set to valid DNS name for your host, server-generated +# redirections will not work. See also the UseCanonicalName directive. +# +# If your host doesn't have a registered DNS name, enter its IP address here. +# You will have to access it by its address anyway, and this will make +# redirections work in a sensible way. +# +#ServerName www.example.com:80 + +# +# UseCanonicalName: Determines how Apache constructs self-referencing +# URLs and the SERVER_NAME and SERVER_PORT variables. +# When set "Off", Apache will use the Hostname and Port supplied +# by the client. When set "On", Apache will use the value of the +# ServerName directive. +# +UseCanonicalName Off + +# +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. +# +DocumentRoot "/usr/local/apache2/htdocs" + +# +# Each directory to which Apache has access can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). +# +# First, we configure the "default" to be a very restrictive set of +# features. +# + + Options FollowSymLinks + AllowOverride None + + +# +# Note that from this point forward you must specifically allow +# particular features to be enabled - so if something's not working as +# you might expect, make sure that you have specifically enabled it +# below. +# + +# +# This should be changed to whatever you set DocumentRoot to. +# + + +# +# Possible values for the Options directive are "None", "All", +# or any combination of: +# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews +# +# Note that "MultiViews" must be named *explicitly* --- "Options All" +# doesn't give it to you. +# +# The Options directive is both complicated and important. Please see +# http://httpd.apache.org/docs/2.0/mod/core.html#options +# for more information. +# + Options Indexes FollowSymLinks + +# +# AllowOverride controls what directives may be placed in .htaccess files. +# It can be "All", "None", or any combination of the keywords: +# Options FileInfo AuthConfig Limit Indexes +# + AllowOverride None + +# +# Controls who can get stuff from this server. +# + Order allow,deny + Allow from all + + + +# +# UserDir: The name of the directory that is appended onto a user's home +# directory if a ~user request is received. +# +#ww# don't need userdirs +#UserDir public_html + +# +# Control access to UserDir directories. The following is an example +# for a site where these directories are restricted to read-only. +# +# +# AllowOverride FileInfo AuthConfig Limit Indexes +# Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec +# +# Order allow,deny +# Allow from all +# +# +# Order deny,allow +# Deny from all +# +# + +# +# DirectoryIndex: sets the file that Apache will serve if a directory +# is requested. +# +# The index.html.var file (a type-map) is used to deliver content- +# negotiated documents. The MultiViews Option can be used for the +# same purpose, but it is much slower. +# +DirectoryIndex index.html index.html.var + +# +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Order allow,deny + Deny from all + + +# +# TypesConfig describes where the mime.types file (or equivalent) is +# to be found. +# +#ww# need full path +#TypesConfig conf/mime.types +TypesConfig /usr/local/apache2/conf/mime.types + +# +# DefaultType is the default MIME type the server will use for a document +# if it cannot otherwise determine one, such as from filename extensions. +# If your server contains mostly text or HTML documents, "text/plain" is +# a good value. If most of your content is binary, such as applications +# or images, you may want to use "application/octet-stream" instead to +# keep browsers from trying to display binary files as though they are +# text. +# +DefaultType text/plain + +# +# The mod_mime_magic module allows the server to use various hints from the +# contents of the file itself to determine its type. The MIMEMagicFile +# directive tells the module where the hint definitions are located. +# + + #ww# need full path + #MIMEMagicFile conf/magic + MIMEMagicFile /usr/local/apache2/conf/magic + + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# +# EnableMMAP: Control whether memory-mapping is used to deliver +# files (assuming that the underlying OS supports it). +# The default is on; turn this off if you serve from NFS-mounted +# filesystems. On some systems, turning it off (regardless of +# filesystem) can improve performance; for details, please see +# http://httpd.apache.org/docs/2.0/mod/core.html#enablemmap +# +#EnableMMAP off + +# +# EnableSendfile: Control whether the sendfile kernel support is +# used to deliver files (assuming that the OS supports it). +# The default is on; turn this off if you serve from NFS-mounted +# filesystems. Please see +# http://httpd.apache.org/docs/2.0/mod/core.html#enablesendfile +# +#EnableSendfile off + +# +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +#sam# name this specifically for apache2 +#ErrorLog logs/error_log +ErrorLog logs/httpd2-error.log + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn +#LogLevel debug + +# +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +# +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +# You need to enable mod_logio.c to use %I and %O +#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + +# +# The location and format of the access logfile (Common Logfile Format). +# If you do not define any access logfiles within a +# container, they will be logged here. Contrariwise, if you *do* +# define per- access logfiles, transactions will be +# logged therein and *not* in this file. +# +#ww# use combined format instead (below) +#CustomLog logs/access_log common + +# +# If you would like to have agent and referer logfiles, uncomment the +# following directives. +# +#CustomLog logs/referer_log referer +#CustomLog logs/agent_log agent + +# +# If you prefer a single logfile with access, agent, and referer information +# (Combined Logfile Format) you can use the following directive. +# +#ww# use combined format instead +#ww# name this specifically for apache2 +#CustomLog logs/access_log combined +CustomLog logs/httpd2-access.log combined + +# +# ServerTokens +# This directive configures what you return as the Server HTTP response +# Header. The default is 'Full' which sends information about the OS-Type +# and compiled in modules. +# Set to one of: Full | OS | Minor | Minimal | Major | Prod +# where Full conveys the most information, and Prod the least. +# +ServerTokens Full + +# +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (internal error documents, FTP directory +# listings, mod_status and mod_info output etc., but not CGI generated +# documents or custom error documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +# +ServerSignature On + +# +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname +# +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example, only "/icons/". If the fakename is slash-terminated, then the +# realname must also be slash terminated, and if the fakename omits the +# trailing slash, the realname must also omit it. +# +# We include the /icons/ alias for FancyIndexed directory listings. If you +# do not use FancyIndexing, you may comment this out. +# +Alias /icons/ "/usr/local/apache2/icons/" + + + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + +# +# This should be changed to the ServerRoot/manual/. The alias provides +# the manual, even if you choose to move your DocumentRoot. You may comment +# this out if you do not care for the documentation. +# +AliasMatch ^/manual(?:/(?:de|en|es|fr|ja|ko|ru))?(/.*)?$ "/usr/local/apache2/manual$1" + + + Options Indexes + AllowOverride None + Order allow,deny + Allow from all + + + SetHandler type-map + + + SetEnvIf Request_URI ^/manual/(de|en|es|fr|ja|ko|ru)/ prefer-language=$1 + RedirectMatch 301 ^/manual(?:/(de|en|es|fr|ja|ko|ru)){2,}(/.*)?$ /manual/$1$2 + + +# +# ScriptAlias: This controls which directories contain server scripts. +# ScriptAliases are essentially the same as Aliases, except that +# documents in the realname directory are treated as applications and +# run by the server when requested rather than as documents sent to the client. +# The same rules about trailing "/" apply to ScriptAlias directives as to +# Alias. +# +#ww# don't need cgi dir +#ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/" + + +# +# Additional to mod_cgid.c settings, mod_cgid has Scriptsock +# for setting UNIX socket for communicating with cgid. +# +#Scriptsock logs/cgisock + + +# +# "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. +# +#ww# don't need cgi dir +# +# AllowOverride None +# Options None +# Order allow,deny +# Allow from all +# + +# +# Redirect allows you to tell clients about documents which used to exist in +# your server's namespace, but do not anymore. This allows you to tell the +# clients where to look for the relocated document. +# Example: +# Redirect permanent /foo http://www.example.com/bar + +# +# Directives controlling the display of server-generated directory listings. +# + +# +# IndexOptions: Controls the appearance of server-generated directory +# listings. +# +IndexOptions FancyIndexing VersionSort + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t + +# +# DefaultLanguage and AddLanguage allows you to specify the language of +# a document. You can then use content negotiation to give a browser a +# file in a language the user can understand. +# +# Specify a default language. This means that all data +# going out without a specific language tag (see below) will +# be marked with this one. You probably do NOT want to set +# this unless you are sure it is correct for all cases. +# +# * It is generally better to not mark a page as +# * being a certain language than marking it with the wrong +# * language! +# +# DefaultLanguage nl +# +# Note 1: The suffix does not have to be the same as the language +# keyword --- those with documents in Polish (whose net-standard +# language code is pl) may wish to use "AddLanguage pl .po" to +# avoid the ambiguity with the common suffix for perl scripts. +# +# Note 2: The example entries below illustrate that in some cases +# the two character 'Language' abbreviation is not identical to +# the two character 'Country' code for its country, +# E.g. 'Danmark/dk' versus 'Danish/da'. +# +# Note 3: In the case of 'ltz' we violate the RFC by using a three char +# specifier. There is 'work in progress' to fix this and get +# the reference data for rfc1766 cleaned up. +# +# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) +# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) +# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) +# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) +# Norwegian (no) - Polish (pl) - Portugese (pt) +# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) +# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) +# +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +# +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# +# Just list the languages in decreasing order of preference. We have +# more or less alphabetized them here. You probably want to change this. +# +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW + +# +# ForceLanguagePriority allows you to serve a result page rather than +# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) +# [in case no accepted languages matched the available variants] +# +ForceLanguagePriority Prefer Fallback + +# +# Commonly used filename extensions to character sets. You probably +# want to avoid clashes with the language extensions, unless you +# are good at carefully testing your setup after each change. +# See http://www.iana.org/assignments/character-sets for the +# official list of charset names and their respective RFCs. +# +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .latin5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .latin6 .arb +AddCharset ISO-8859-7 .iso8859-7 .latin7 .grk +AddCharset ISO-8859-8 .iso8859-8 .latin8 .heb +AddCharset ISO-8859-9 .iso8859-9 .latin9 .trk +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5 .Big5 .big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-8 .utf8 + +# The set below does not map to a specific (iso) standard +# but works on a fairly wide range of browsers. Note that +# capitalization actually matters (it should not, but it +# does for some browsers). +# +# See http://www.iana.org/assignments/character-sets +# for a list of sorts. But browsers support few. +# +AddCharset GB2312 .gb2312 .gb +AddCharset utf-7 .utf7 +AddCharset utf-8 .utf8 +AddCharset big5 .big5 .b5 +AddCharset EUC-TW .euc-tw +AddCharset EUC-JP .euc-jp +AddCharset EUC-KR .euc-kr +AddCharset shift_jis .sjis + +# +# AddType allows you to add to or override the MIME configuration +# file mime.types for specific file types. +# +#AddType application/x-tar .tgz +# +# AddEncoding allows you to have certain browsers uncompress +# information on the fly. Note: Not all browsers support this. +# Despite the name similarity, the following Add* directives have nothing +# to do with the FancyIndexing customization directives above. +# +#AddEncoding x-compress .Z +#AddEncoding x-gzip .gz .tgz +# +# If the AddEncoding directives above are commented-out, then you +# probably should define those extensions to indicate media types: +# +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz + +# +# AddHandler allows you to map certain file extensions to "handlers": +# actions unrelated to filetype. These can be either built into the server +# or added with the Action directive (see below) +# +# To use CGI scripts outside of ScriptAliased directories: +# (You will also need to add "ExecCGI" to the "Options" directive.) +# +#AddHandler cgi-script .cgi + +# +# For files that include their own HTTP headers: +# +#AddHandler send-as-is asis + +# +# For server-parsed imagemap files: +# +#AddHandler imap-file map + +# +# For type maps (negotiated resources): +# (This is enabled by default to allow the Apache "It Worked" page +# to be distributed in multiple languages.) +# +AddHandler type-map var + +# +# Filters allow you to process content before it is sent to the client. +# +# To parse .shtml files for server-side includes (SSI): +# (You will also need to add "Includes" to the "Options" directive.) +# +#AddType text/html .shtml +#AddOutputFilter INCLUDES .shtml + +# +# Action lets you define media types that will execute a script whenever +# a matching file is called. This eliminates the need for repeated URL +# pathnames for oft-used CGI file processors. +# Format: Action media/type /cgi-script/location +# Format: Action handler-name /cgi-script/location +# + +# +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# Putting this all together, we can internationalize error responses. +# +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line: +# +# Alias /error/include/ "/your/include/path/" +# +# which allows you to create your own set of files by starting with the +# /usr/local/apache2/error/include/ files and copying them to /your/include/path/, +# even on a per-VirtualHost basis. The default include files will display +# your Apache version number and your ServerAdmin email address regardless +# of the setting of ServerSignature. +# +# The internationalized error documents require mod_alias, mod_include +# and mod_negotiation. To activate them, uncomment the following 30 lines. + +# Alias /error/ "/usr/local/apache2/error/" +# +# +# AllowOverride None +# Options IncludesNoExec +# AddOutputFilter Includes html +# AddHandler type-map var +# Order allow,deny +# Allow from all +# LanguagePriority en cs de es fr it ja ko nl pl pt-br ro sv tr +# ForceLanguagePriority Prefer Fallback +# +# +# ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +# ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +# ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +# ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +# ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +# ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +# ErrorDocument 410 /error/HTTP_GONE.html.var +# ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +# ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +# ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +# ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +# ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +# ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +# ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +# ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +# ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +# ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + +# +# The following directives modify normal HTTP response behavior to +# handle known problems with browser implementations. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. +# +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[0123]" redirect-carefully +BrowserMatch "^gnome-vfs" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully + +# +# Allow server status reports generated by mod_status, +# with the URL of http://servername/server-status +# Change the ".example.com" to match your domain to enable. +# + + SetHandler server-status + Order deny,allow + Deny from all + Allow from localhost + + +# +# Allow remote server configuration reports, with the URL of +# http://servername/server-info (requires that mod_info.c be loaded). +# Change the ".example.com" to match your domain to enable. +# + + SetHandler server-info + Order deny,allow + Deny from all + Allow from localhost + + +#ww# add mod_perl status handler + + SetHandler perl-script + PerlResponseHandler Apache2::Status + PerlSetVar StatusOptionsAll On + Order deny,allow + Deny from all + Allow from localhost + + +# +# Bring in additional module-specific configurations +# + + #ww# not bothering with this file + #Include conf/devel-site-ssl.apache2-config + + SSLRandomSeed startup builtin + SSLRandomSeed connect builtin + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + SSLPassPhraseDialog builtin + + SSLSessionCache dbm:run/ssl_scache2 + SSLSessionCacheTimeout 300 + SSLMutex file:run/ssl_mutex2 + + SSLEngine On + SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + SSLCertificateFile /usr/local/etc/apache/ssl.crt/server.crt + SSLCertificateKeyFile /usr/local/etc/apache/ssl.key/server.key + + +### Section 3: Virtual Hosts +# +# VirtualHost: If you want to maintain multiple domains/hostnames on your +# machine you can setup VirtualHost containers for them. Most configurations +# use only name-based virtual hosts so the server doesn't need to worry about +# IP addresses. This is indicated by the asterisks in the directives below. +# +# Please see the documentation at +# +# for further details before you try to setup virtual hosts. +# +# You may use the command line option '-S' to verify your virtual host +# configuration. + +# +# Use name-based virtual hosting. +# +#NameVirtualHost *:80 + +# +# VirtualHost example: +# Almost any Apache directive may go into a VirtualHost container. +# The first VirtualHost section is used for requests without a known +# server name. +# +# +# ServerAdmin webmaster@dummy-host.example.com +# DocumentRoot /www/docs/dummy-host.example.com +# ServerName dummy-host.example.com +# ErrorLog logs/dummy-host.example.com-error_log +# CustomLog logs/dummy-host.example.com-access_log common +# diff --git a/conf/devel.apache-config.dist b/conf/devel.apache-config.dist new file mode 100644 index 0000000000..ae9873432c --- /dev/null +++ b/conf/devel.apache-config.dist @@ -0,0 +1,111 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork-modperl/conf/devel.apache-config.dist,v 1.12 2007/08/13 22:59:51 sh002i 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. +################################################################################ + +# This is an Apache configuration file suitable for use when doing development +# on the WeBWorK 2 system. This setup allows each developer to run an +# independent Apache server under their own UID, using their own working copy of +# the WeBWorK code. The configuration is split into three parts: +# +# The first part is a site-specific (but user-indepenent) file named +# devel-site.apache-config. It contains directives that are common to all +# development servers on the same host. The site administrator can chose to +# maintain a single copy of this file and have all developers reference it in a +# central location. +# +# The second part is the stock webwork.apache-config file that is used for +# normal installations. Customize this file, setting the $webwork_url, +# $webwork_dir, $pg_dir, etc. appropriatly for your development server. +# +# The third part is this file. It contains the user-specific directives that are +# specific to each developer's server. + +################################################################################ +# Include site-specific configuration +################################################################################ + +Include /path/to/webwork2/conf/devel-site.apache-config + +################################################################################ +# Include WeBWorK configuration +################################################################################ + +Include /path/to/webwork2/conf/webwork.apache-config + +################################################################################ +# Perform user-specific configuration +################################################################################ + + + +my $user_name = (getpwuid $>)[0]; +my $group_name = (getgrgid $))[0]; +my $host_name = Apache->server->server_hostname; + +# The server will run as the user who starts it. +$User = $user_name; +$Group = $group_name; + +# It will listen on a port equal to the UID of the user who starts it +7000. +# This effectively picks a port between 8000 and 8999 since UID's are >=1000. +$Port = $> + 7000; + +# Email address of server administator. +$ServerAdmin = "$user_name\@$host_name"; + +# Locations of some files. +$LockFile = "$WeBWorK::SeedCE{webwork_dir}/logs/httpd.lock"; +$PidFile = "$WeBWorK::SeedCE{webwork_dir}/logs/httpd.pid"; +$ErrorLog = "$WeBWorK::SeedCE{webwork_dir}/logs/httpd-error.log"; + +# Control the number of child processes and how long they stick around. +$StartServers = 2; +$MinSpareServers = 2; +$MaxSpareServers = 2; +$MaxClients = 5; +$MaxRequestsPerChild = 100; + +# The document root doesn't really matter, but it has to be set, so we set it to +# the htdocs directory. +$DocumentRoot = $WeBWorK::SeedCE{webwork_htdocs_dir}; + +# You may want to add access control to prevent interlopers from monkeying with +# your development system. This is a really good idea, since there could +# potentially be security holes in the code. Create a file named .htpasswd in +# the conf containing accounts for those allowed to access your development +# system: +# +# htpasswd -c .htpasswd user-name + +#$Location{"/"} = { +# AuthType => "Basic", +# AuthName => "\"$user_name's WeBWorK development system\"", # ' requires extra "" +# AuthUserFile => "$WeBWorK::SeedCE{webwork_dir}/conf/.htpasswd", +# Require => "valid-user", +#}; + + + + + SSLEngine On + SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + SSLCertificateFile /usr/local/etc/apache/ssl.crt/server.crt + SSLCertificateKeyFile /usr/local/etc/apache/ssl.key/server.key + + +################################################################################ +# Stick any local additions down here +################################################################################ + diff --git a/conf/devel.apache2-config.dist b/conf/devel.apache2-config.dist new file mode 100644 index 0000000000..6e245c6416 --- /dev/null +++ b/conf/devel.apache2-config.dist @@ -0,0 +1,76 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/devel.apache2-config.dist,v 1.2 2006/08/03 16:14:55 sh002i 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. +################################################################################ + +# This is an Apache configuration file suitable for use when doing development +# on the WeBWorK 2 system. This setup allows each developer to run an +# independent Apache server under their own UID, using their own working copy of +# the WeBWorK code. The configuration is split into three parts: +# +# The first part is a site-specific (but user-indepenent) file named +# devel-site.apache-config. It contains directives that are common to all +# development servers on the same host. The site administrator can chose to +# maintain a single copy of this file and have all developers reference it in a +# central location. +# +# The second part is the stock webwork.apache-config file that is used for +# normal installations. Customize this file, setting the $webwork_url, +# $webwork_dir, $pg_dir, etc. appropriatly for your development server. +# +# The third part is this file. It contains the user-specific directives that are +# specific to each developer's server. + +################################################################################ +# Include site-specific configuration +################################################################################ + +Include /path/to/webwork2/conf/devel-site.apache2-config + +################################################################################ +# Include WeBWorK configuration +################################################################################ + +Include /path/to/webwork2/conf/webwork.apache2-config + +################################################################################ +# Perform user-specific configuration +################################################################################ + + + +use Apache2::ServerUtil; +use Apache2::ServerRec; + +my $user_name = (getpwuid $>)[0]; +my $group_name = (getgrgid $))[0]; +my $host_name = Apache2::ServerUtil->server->server_hostname; + +# The server will run as the user who starts it. +$User = $user_name; +$Group = $group_name; + +# It will listen on a port equal to the UID of the user who starts it +8000. +# This effectively picks a port between 9000 and 9999 since UID's are >=1000. +$Listen = "0.0.0.0:" . ($>+8000); + +# Email address of server administator. +$ServerAdmin = "$user_name\@$host_name"; + + + +################################################################################ +# Stick any local additions down here +################################################################################ + diff --git a/conf/gdbm.conf.dist b/conf/gdbm.conf.dist deleted file mode 100644 index 59422d4454..0000000000 --- a/conf/gdbm.conf.dist +++ /dev/null @@ -1,105 +0,0 @@ -#!perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -=head1 NAME - -gdbm.conf - defines database layout for WeBWorK 1.x compatible GDBM databases. - -=head1 SYNOPSIS - -In global.conf (or course.conf): - - include "conf/gdbm.conf"; - -=head1 DESCRIPTION - -This layout uses the traditional WeBWorK 1.x database format, using the GDBM -file format. Use this layout if you wish to share courses between WeBWorK 1.x -and WeBWorK 2. - -The C parameter given for the C and C tables denotes -the ID of the user that the GlobalTableEmulator will use to store data for the -C and C tables. This is set to "professor" by default, but should -be overridden on a course-by-course basis to the ID of the professor who is most -likely to be involved in creating new problem sets. Sets which have not been -assigned to any users will only be visible to the selected user when logging -into WeBWorK 1.x. - -Use the following code to override these values in each course's course.conf -file: - - $dbLayout{set}->{params}->{globalUserID} = "some_user"; - $dbLayout{problem}->{params}->{globalUserID} = "some_user"; - -=cut - -%dbLayout = ( - password => { - record => "WeBWorK::DB::Record::Password", - schema => "WeBWorK::DB::Schema::Auth1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{auth_DATA}/$courseName\_password_DB", - }, - permission => { - record => "WeBWorK::DB::Record::PermissionLevel", - schema => "WeBWorK::DB::Schema::Auth1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{auth_DATA}/$courseName\_permissions_DB", - }, - key => { - record => "WeBWorK::DB::Record::Key", - schema => "WeBWorK::DB::Schema::Auth1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{auth_DATA}/keys", - }, - user => { - record => "WeBWorK::DB::Record::User", - schema => "WeBWorK::DB::Schema::Classlist1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{DATA}/$courseName\_classlist_DB", - }, - set => { - record => "WeBWorK::DB::Record::Set", - schema => "WeBWorK::DB::Schema::GlobalTableEmulator", - driver => "WeBWorK::DB::Driver::Null", - }, - set_user => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::WW1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{DATA}/$courseName\_webwork_DB", - params => { psvnLength => 5 }, - }, - problem => { - record => "WeBWorK::DB::Record::Problem", - schema => "WeBWorK::DB::Schema::GlobalTableEmulator", - driver => "WeBWorK::DB::Driver::Null", - }, - problem_user => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::WW1Hash", - driver => "WeBWorK::DB::Driver::GDBM", - source => "$courseDirs{DATA}/$courseName\_webwork_DB", - params => { psvnLength => 5 }, - }, -); - -# These lines should be overridden in course.conf -$dbLayout{set}->{params}->{globalUserID} = "professor"; -$dbLayout{problem}->{params}->{globalUserID} = "professor"; - -1; diff --git a/conf/global.conf.dist b/conf/global.conf.dist index 22892a17be..49727da403 100644 --- a/conf/global.conf.dist +++ b/conf/global.conf.dist @@ -1,8 +1,8 @@ #!perl ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/global.conf.dist,v 1.225 2010/05/18 18:03:31 apizer 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 @@ -19,198 +19,777 @@ # requests. Values may be overwritten by the course.conf for a specific course. # All package variables set in this file are added to the course environment. # If you wish to set a variable here but omit it from the course environment, -# use the "my" keyword. The following variables are available to this file: -# -# $webworkRoot directory that contains the WeBWorK distribution -# $webworkURL base URL handled by Apache::WeBWorK -# $pgRoot directory that contains the PG distribution -# $courseName name of the course being used - -################################################################################ -# WeBWorK settings -################################################################################ - -%webworkDirs = ( - root => "$webworkRoot", - DATA => "$webworkRoot/DATA", - uploadCache => "$webworkRoot/DATA/uploads", - bin => "$webworkRoot/bin", - conf => "$webworkRoot/conf", - courses => "$webworkRoot/courses", - htdocs => "$webworkRoot/htdocs", - htdocs_temp => "$webworkRoot/htdocs/tmp", - equationCache => "$webworkRoot/htdocs/tmp/equations", - lib => "$webworkRoot/lib", - logs => "$webworkRoot/logs", - macros => "$pgRoot/macros", - tmp => "$webworkRoot/tmp", -); +# use the "my" keyword. The $webwork_dir variable is set in the WeBWorK Apache +# configuration file (webwork.apache-config) and is available for use here. In +# addition, the $courseName variable holds the name of the current course. + +################################################################################ +# Seed variables +################################################################################ + +# Set these variables to correspond to your configuration and preferences. You +# will need to restart the webserver to reset the variables in this section. + +# URL of WeBWorK handler. If WeBWorK is to be on the web server root, use "". Note +# that using "" may not work so we suggest sticking with "/webwork2". +$webwork_url = "/webwork2"; + + +$server_root_url = ""; # e.g. http://webwork.yourschool.edu +$server_userID = ""; # e.g. www-data +$server_groupID = ""; # e.g. wwdata + + +# In the apache configuration file (often called httpd.conf) you will find +# User wwadmin --- this is the $server_userID -- of course it may be wwhttpd or some other name +# Group wwdata --- this is the $server_groupID -- this will have different names also + +# Root directory of PG. +$pg_dir = "/opt/webwork/pg"; + +# URL and path to htdocs directory. +# Uncomment the second line below when using litetpd +$webwork_htdocs_url = "/webwork2_files"; +$webwork_htdocs_dir = "$webwork_dir/htdocs"; + +# URL and path to courses directory. +$webwork_courses_url = "/webwork2_course_files"; +$webwork_courses_dir = "/opt/webwork/courses"; #(a typical place to put the course directory + +################################################################################ +# Paths to external programs +################################################################################ + +# system utilities +$externalPrograms{mv} = "/bin/mv"; +$externalPrograms{cp} = "/bin/cp"; +$externalPrograms{rm} = "/bin/rm"; +$externalPrograms{mkdir} = "/bin/mkdir"; +$externalPrograms{tar} = "/bin/tar"; +$externalPrograms{gzip} = "/bin/gzip"; + +# equation rendering/hardcopy utiltiies +$externalPrograms{latex} = "/usr/bin/latex"; +$externalPrograms{pdflatex} = "/usr/bin/pdflatex --shell-escape"; +$externalPrograms{dvipng} = "/usr/bin/dvipng"; +$externalPrograms{tth} = "/usr/bin/tth"; + +#################################################### +# NetPBM - basic image manipulation utilities +# Most sites only need to configure $netpbm_prefix. +#################################################### +my $netpbm_prefix = "/usr/bin"; +$externalPrograms{giftopnm} = "$netpbm_prefix/giftopnm"; +$externalPrograms{ppmtopgm} = "$netpbm_prefix/ppmtopgm"; +$externalPrograms{pnmtops} = "$netpbm_prefix/pnmtops"; +$externalPrograms{pnmtopng} = "$netpbm_prefix/pnmtopng"; +$externalPrograms{pngtopnm} = "$netpbm_prefix/pngtopnm"; + +# url checker +$externalPrograms{checkurl} = "/usr/bin/lwp-request -d -mHEAD "; # or "/usr/local/bin/w3c -head " + +# image conversions utiltiies +# the source file is given on stdin, and the output expected on stdout. +$externalPrograms{gif2eps} = "$externalPrograms{giftopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; +$externalPrograms{png2eps} = "$externalPrograms{pngtopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; +$externalPrograms{gif2png} = "$externalPrograms{giftopnm} | $externalPrograms{pnmtopng}"; + +# mysql clients +$externalPrograms{mysql} = "/usr/bin/mysql"; +$externalPrograms{mysqldump} = "/usr/bin/mysqldump"; + +################################################################################ +# Mail settings +################################################################################ + +# Mail sent by the PG system and the mail merge and feedback modules will be +# sent via this SMTP server. +$mail{smtpServer} = 'mail.yourschool.edu'; + +# When connecting to the above server, WeBWorK will send this address in the +# MAIL FROM command. This has nothing to do with the "From" address on the mail +# message. It can really be anything, but some mail servers require it contain +# a valid mail domain, or at least be well-formed. +$mail{smtpSender} = 'webwork@yourserver.yourschool.edu'; + +# Seconds to wait before timing out when connecting to the SMTP server. +$mail{smtpTimeout} = 30; + +# AllowedRecipients defines addresses that the PG system is allowed to send mail +# to. this prevents subtle PG exploits. This should be set in course.conf to the +# addresses of professors of each course. Sending mail from the PG system (i.e. +# questionaires, essay questions) will fail if this is not set somewhere (either +# here or in course.conf). +$mail{allowedRecipients} = [ + #'prof1@yourserver.yourdomain.edu', + #'prof2@yourserver.yourdomain.edu', +]; + +# By default, feeback is sent to all users who have permission to +# receive_feedback. If this list is non-empty, feedback is also sent to the +# addresses specified here. +# +# * If you want to disable feedback altogether, leave this empty and set +# submit_feeback => $nobody in %permissionLevels below. This will cause the +# feedback button to go away as well. +# +# * If you want to send email ONLY to addresses in this list, set +# receive_feedback => $nobody in %permissionLevels below. +# +# It's often useful to set this in the course.conf to change the behavior of +# feedback for a specific course. +# +# Items in this list may be bare addresses, or RFC822 mailboxes, like: +# 'Joe User ' +# The advantage of this form is that the resulting email will include the name +# of the recipient in the "To" field of the email. +# +$mail{feedbackRecipients} = [ + #'prof1@yourserver.yourdomain.edu', + #'prof2@yourserver.yourdomain.edu', +]; + +# Feedback subject line -- the following escape sequences are recognized: +# +# %c = course ID +# %u = user ID +# %s = set ID +# %p = problem ID +# %x = section +# %r = recitation +# %% = literal percent sign +# + +$mail{feedbackSubjectFormat} = "[WWfeedback] course:%c user:%u set:%s prob:%p sec:%x rec:%r"; + +# feedbackVerbosity: +# 0: send only the feedback comment and context link +# 1: as in 0, plus user, set, problem, and PG data +# 2: as in 1, plus the problem environment (debugging data) +$mail{feedbackVerbosity} = 1; + +# Defines the size of the Mail Merge editor window +# FIXME: should this be here? it's UI, not mail +# FIXME: replace this with the auto-size method that TWiki uses +$mail{editor_window_rows} = 15; +$mail{editor_window_columns} = 100; + +################################################### +# Customizing the action of the "Email your instructor" button +################################################### + +# Use this to customize the text of the feedback button. +$feedback_button_name = "Email instructor"; + +# If this value is true, feedback will only be sent to users with the same +# section as the user initiating the feedback. +$feedback_by_section = 0; + +# If the variable below is set to a non-empty value (i.e. in course.conf), WeBWorK's usual +# email feedback mechanism will be replaced with a link to the given URL. +# See also $feedback_button_name, above. + +$courseURLs{feedbackURL} = ""; + +# If the variable below is set to a non-empty value (i.e. in course.conf), +# WeBWorK's usual email feedback mechanism will be replaced with a link to the given URL and +# a POST request with information about the problem including the HTML rendering +# of the problem will be sent to that URL. +# See also $feedback_button_name, above. + +#$courseURLs{feedbackFormURL} = "http://www.mathnerds.com/MathNerds/mmn/SDS/askQuestion.aspx"; #"http://www.tipjar.com/cgi-bin/test"; +$courseURLs{feedbackFormURL} = ""; + +################################################################################ +# Theme +################################################################################ + +$defaultTheme = "math2"; +$defaultThemeTemplate = "system"; + +################################################################################ +# Language +################################################################################ + +$language = "en"; # tr = turkish, en=english + +################################################################################ +# System-wide locations (directories and URLs) +################################################################################ + +# The root directory, set by webwork_root variable in Apache configuration. +$webworkDirs{root} = "$webwork_dir"; + +# Location of system-wide data files. +$webworkDirs{DATA} = "$webworkDirs{root}/DATA"; + +# Used for temporary storage of uploaded files. +$webworkDirs{uploadCache} = "$webworkDirs{DATA}/uploads"; + +# Location of utility programs. +$webworkDirs{bin} = "$webworkDirs{root}/bin"; + +# Location of configuration files, templates, snippets, etc. +$webworkDirs{conf} = "$webworkDirs{root}/conf"; + +# Location of theme templates. +$webworkDirs{templates} = "$webworkDirs{conf}/templates"; + +# Location of course directories. +$webworkDirs{courses} = "$webwork_courses_dir" || "$webworkDirs{root}/courses"; + +# Contains log files. +$webworkDirs{logs} = "$webworkDirs{root}/logs"; + +# Contains non-web-accessible temporary files, such as TeX working directories. +$webworkDirs{tmp} = "$webworkDirs{root}/tmp"; + +# The (absolute) destinations of symbolic links that are OK for the FileManager to follow. +# (any subdirectory of these is a valid target for a symbolic link.) +# For example: +# $webworkDirs{valid_symlinks} = ["$webworkDirs{courses}/modelCourse/templates","/ww2/common/sets"]; +$webworkDirs{valid_symlinks} = []; + +################################################################################ +##### The following locations are web-accessible. +################################################################################ + +# The root URL (usually /webwork2), set by in Apache configuration. +$webworkURLs{root} = "$webwork_url"; + +# Location of system-wide web-accessible files, such as equation images, and +# help files. +$webworkDirs{htdocs} = "$webwork_htdocs_dir" || "$webworkDirs{root}/htdocs"; +$webworkURLs{htdocs} = "$webwork_htdocs_url"; + +# Location of web-accessible temporary files, such as equation images. +$webworkDirs{htdocs_temp} = "$webworkDirs{htdocs}/tmp"; +$webworkURLs{htdocs_temp} = "$webworkURLs{htdocs}/tmp"; + +# Location of cached equation images. +$webworkDirs{equationCache} = "$webworkDirs{htdocs_temp}/equations"; +$webworkURLs{equationCache} = "$webworkURLs{htdocs_temp}/equations"; + +# Contains context-sensitive help files. +$webworkDirs{local_help} = "$webworkDirs{htdocs}/helpFiles"; +$webworkURLs{local_help} = "$webworkURLs{htdocs}/helpFiles"; + +# URL of general WeBWorK documentation. +$webworkURLs{docs} = "http://webwork.maa.org"; + +# URL of WeBWorK Bugzilla database. +$webworkURLs{bugReporter} = "http://bugs.webwork.maa.org/enter_bug.cgi"; + +# Location of 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 +# /css/math.css"/> + +# Location of jsMath script, used for the jsMath display mode. +$webworkURLs{jsMath} = "$webworkURLs{htdocs}/jsMath/jsMath-ww.js"; + +# Location of MathJax script, used for the MathJax display mode. +$webworkURLs{MathJax} = "$webworkURLs{htdocs}/mathjax/MathJax.js?config=TeX-AMS_HTML-full"; + +# Location of Tabber script, used to generate tabbed widgets. +$webworkURLs{tabber} = "$webworkURLs{htdocs}/js/tabber.js"; + +# Location of ASCIIMathML script, used for the asciimath display mode. +$webworkURLs{asciimath} = "$webworkURLs{htdocs}/ASCIIMathML/ASCIIMathML.js"; + +# Location of LaTeXMathML script, used for the LaTeXMathML display mode. +$webworkURLs{LaTeXMathML} = "$webworkURLs{htdocs}/LaTeXMathML/LaTeXMathML.js"; + +################################################################################ +# Defaults for course-specific locations (directories and URLs) +################################################################################ + +# The root directory of the current course. (The ID of the current course is +# available in $courseName.) +$courseDirs{root} = "$webworkDirs{courses}/$courseName"; + +# Location of course-specific data files. +$courseDirs{DATA} = "$courseDirs{root}/DATA"; + +# Location of course HTML files, passed to PG. +$courseDirs{html} = "$courseDirs{root}/html"; +$courseURLs{html} = "$webwork_courses_url/$courseName"; + +# Location of course image files, passed to PG. +$courseDirs{html_images} = "$courseDirs{html}/images"; + +# Location of web-accessible, course-specific temporary files, like static and +# dynamically-generated PG graphics. +$courseDirs{html_temp} = "$courseDirs{html}/tmp"; +$courseURLs{html_temp} = "$courseURLs{html}/tmp"; + +# Location of course-specific logs, like the transaction log. +$courseDirs{logs} = "$courseDirs{root}/logs"; + +# Location of scoring files. +$courseDirs{scoring} = "$courseDirs{root}/scoring"; + +# Location of PG templates and set definition files. +$courseDirs{templates} = "$courseDirs{root}/templates"; + +# Location of course-specific macro files. +$courseDirs{macros} = "$courseDirs{templates}/macros"; + +# Location of mail-merge templates. +$courseDirs{email} = "$courseDirs{templates}/email"; + +# Location of temporary editing files. +$courseDirs{tmpEditFileDir} = "$courseDirs{templates}/tmpEdit"; + + +# mail merge status directory +$courseDirs{mailmerge} = "$courseDirs{DATA}/mailmerge"; + +################################################################################ +# System-wide files +################################################################################ + +# Location of this file. +$webworkFiles{environment} = "$webworkDirs{conf}/global.conf"; + +# Flat-file database used to protect against MD5 hash collisions. TeX equations +# are hashed to determine the name of the image file. There is a tiny chance of +# a collision between two TeX strings. This file allows for that. However, this +# is slow, so most people chose not to worry about it. Set this to "" if you +# don't want to use the equation cache file. +$webworkFiles{equationCacheDB} = ""; # "$webworkDirs{DATA}/equationcache"; + +################################################################################ +# Hardcopy snippets are used in constructing a TeX file for hardcopy output. +# They should contain TeX code unless otherwise noted. +################################################################################ +# The preamble is the first thing in the TeX file. +$webworkFiles{hardcopySnippets}{preamble} = "$webworkDirs{conf}/snippets/hardcopyPreamble.tex"; + +# The setHeader preceeds each set in hardcopy output. It is a PG file. +# This is the default file which is used if a specific files is not selected +$webworkFiles{hardcopySnippets}{setHeader} = "$webworkDirs{conf}/snippets/setHeader.pg"; # hardcopySetHeader.pg", +#$webworkFiles{hardcopySnippets}{setHeader} = "$courseDirs{templates}/ASimpleHardCopyHeaderFile.pg"; # An alternate default header file + +# The problem divider goes between problems. +$webworkFiles{hardcopySnippets}{problemDivider} = "$webworkDirs{conf}/snippets/hardcopyProblemDivider.tex"; + +# The set footer goes after each set. Is is a PG file. +$webworkFiles{hardcopySnippets}{setFooter} = "$webworkDirs{conf}/snippets/hardcopySetFooter.pg"; + +# The set divider goes between sets (in multiset output). +$webworkFiles{hardcopySnippets}{setDivider} = "$webworkDirs{conf}/snippets/hardcopySetDivider.tex"; + +# The user divider does between users (in multiuser output). +$webworkFiles{hardcopySnippets}{userDivider} = "$webworkDirs{conf}/snippets/hardcopyUserDivider.tex"; + +# The postabmle is the last thing in the TeX file. +$webworkFiles{hardcopySnippets}{postamble} = "$webworkDirs{conf}/snippets/hardcopyPostamble.tex"; + +##### Screen snippets are used when displaying problem sets on the screen. + +# The set header is displayed on the problem set page. It is a PG file. +# This is the default file which is used if a specific files is not selected +#$webworkFiles{screenSnippets}{setHeader} = "$webworkDirs{conf}/snippets/setHeader.pg"; # screenSetHeader.pg" +$webworkFiles{screenSnippets}{setHeader} = "$courseDirs{templates}/ASimpleScreenHeaderFile.pg"; # An alternate default header file + +# A PG template for creation of new problems. +$webworkFiles{screenSnippets}{blankProblem} = "$webworkDirs{conf}/snippets/blankProblem2.pg"; # screenSetHeader.pg" + +# A site info "message of the day" file +$webworkFiles{site_info} = "$webworkDirs{htdocs}/site_info.txt"; + +################################################################################ +# Course-specific files +################################################################################ -%webworkFiles = ( - environment => "$webworkDirs{conf}/global.conf", - hardcopySnippets => { - preamble => "$webworkDirs{conf}/snippets/hardcopyPreamble.tex", - setHeader => "$webworkDirs{conf}/snippets/hardcopySetHeader.pg", - problemDivider => "$webworkDirs{conf}/snippets/hardcopyProblemDivider.tex", - setFooter => "$webworkDirs{conf}/snippets/hardcopySetFooter.pg", - setDivider => "$webworkDirs{conf}/snippets/hardcopySetDivider.tex", - userDivider => "$webworkDirs{conf}/snippets/hardcopyUserDivider.tex", - postamble => "$webworkDirs{conf}/snippets/hardcopyPostamble.tex", +# The course configuration file. +$courseFiles{environment} = "$courseDirs{root}/course.conf"; + +# The course simple configuration file (holds web-based configuratoin). +$courseFiles{simpleConfig} = "$courseDirs{root}/simple.conf"; + +# File contents are displayed after login, on the problem sets page. Path given +# here is relative to the templates directory. +$courseFiles{course_info} = "course_info.txt"; + +# File contents are displayed on the login page. Path given here is relative to +# the templates directory. +$courseFiles{login_info} = "login_info.txt"; + +# Additional library buttons can be added to the Library Browser (SetMaker.pm) +# by adding the libraries you want to the following line. For each key=>value +# in the list, if a directory (or link to a directory) with name 'key' appears +# in the templates directory, then a button with name 'value' will be placed at +# the top of the problem browser. (No button will appear if there is no +# directory or link with the given name in the templates directory.) For +# example, +# +# $courseFiles{problibs} = {rochester => "Rochester", asu => "ASU"}; +# +# would add two buttons, one for the Rochester library and one for the ASU +# library, provided templates/rochester and templates/asu exists either as +# subdirectories or links to other directories. The "NPL Directory" button +# activated below gives access to all the directories in the National +# Problem Library. +# +$courseFiles{problibs} = { + Library => "NPL Directory", +# rochesterLibrary => "Rochester", +# unionLibrary =>"Union", +# asuLibrary => "Arizona State", +# dcdsLibrary => "Detroit CDS", +# dartmouthLibrary => "Dartmouth", +# indianaLibrary => "Indiana", +# osuLibrary => "Ohio State", +# capaLibrary => "CAPA", +}; + +################################################################################ +# Status system +################################################################################ + +# This is the default status given to new students and students with invalid +# or missing statuses. +$default_status = "Enrolled"; + +# The first abbreviation in the abbreviations list is the canonical +# abbreviation, and will be used when setting the status value in a user record +# or an exported classlist file. +# +# Results are undefined if more than one status has the same abbreviation. +# +# The four behaviors that are controlled by status are: +# allow_course_access => is this user allowed to log in? +# include_in_assignment => is this user included when assigning as set to "all" users? +# include_in_stats => is this user included in statistical reports? +# include_in_email => is this user included in emails sent to the class? +# include_in_scoring => is this user included in score reports? + +%statuses = ( + Enrolled => { + abbrevs => [qw/ C c current enrolled /], + behaviors => [qw/ allow_course_access include_in_assignment include_in_stats include_in_email include_in_scoring /], }, - screenSnippets => { - setHeader => "$webworkDirs{conf}/snippets/setHeader.pg", + Audit => { + abbrevs => [qw/ A a audit /], + behaviors => [qw/ allow_course_access include_in_assignment include_in_stats include_in_email /], }, - logs => { - timing => "$webworkDirs{logs}/timing.log", + Drop => { + abbrevs => [qw/ D d drop withdraw /], + behaviors => [qw/ /], + }, + Proctor => { + abbrevs => [qw/ P p proctor /], + behaviors => [qw/ /], }, - equationCacheDB => "$webworkDirs{DATA}/equationcache", -); - -%webworkURLs = ( - root => "$webworkURLRoot", - home => "/webwork2_files/index.html", - htdocs => "/webwork2_files", - htdocs_temp => "/webwork2_files/tmp", - equationCache => "/webwork2_files/tmp/equations", - docs => "http://webhost.math.rochester.edu/webworkdocs/docs", - oldProf => "/webwork-old/profLogin.pl", ); ################################################################################ -# Default course-specific settings +# Database options ################################################################################ -my $courseRoot = "$webworkDirs{courses}/$courseName"; -%courseDirs = ( - root => "$courseRoot", - DATA => "$courseRoot/DATA", - auth_DATA => "$courseRoot/DATA/.auth", - html => "$courseRoot/html", - html_images => "$courseRoot/html/images", - html_temp => "$courseRoot/html/tmp", - logs => "$courseRoot/logs", - scoring => "$courseRoot/scoring", - templates => "$courseRoot/templates", - macros => "$courseRoot/templates/macros", - email => "$courseRoot/templates/email", -); +# these variables are used by database.conf. we define them here so that editing +# database.conf isn't necessary. -%courseFiles = ( - environment => "$courseDirs{root}/course.conf", - motd => "$courseDirs{root}/motd.txt", - logs => { - answer_log => "$courseDirs{logs}/answer_log", - }, - course_info => "$courseDirs{root}/course_info.txt", - login_info => "$courseDirs{root}/login_info.txt", -); +# required permissions +# GRANT SELECT ON webwork.* TO webworkRead@localhost IDENTIFIED BY 'passwordRO'; +# GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW'; -# quick hack to fix transaction logging. blah. -$webworkFiles{logs}->{transaction} = "$courseDirs{logs}/transaction.log"; -$webworkFiles{logs}->{pastAnswerList} = "$courseDirs{logs}/past_answers.log"; +$database_dsn = "dbi:mysql:webwork"; +$database_username = "webworkWrite"; +$database_password = ""; +$database_debug = 0; -my $courseURLRoot = "$webworkURLs{htdocs}/courses/$courseName"; -%courseURLs = ( - root => "$courseURLRoot", - html => "$courseURLRoot", - html_temp => "$courseURLRoot/tmp", -); +# Variables for sql_moodle database layout. +$moodle_dsn = "dbi:mysql:moodle"; +$moodle_username = $database_username; +$moodle_password = $database_password; +$moodle_table_prefix = "mdl_"; +$moodle17 = 0; + +# Several database are defined in the file conf/database.conf and stored in the +# hash %dbLayouts. +include "conf/database.conf"; + +# Select the default database layout. This can be overridden in the course.conf +# file of a particular course. The only database layout supported in WW 2.1.4 +# and up is "sql_single". +$dbLayoutName = "sql_single"; + +# This sets the symbol "dbLayout" as an alias for the selected database layout. +*dbLayout = $dbLayouts{$dbLayoutName}; ################################################################################ -# Other site-specific options -################################################################################ - -%mail = ( - smtpServer => "mail.math.rochester.edu", - smtpSender => "webwork\@math.rochester.edu", - # allowedRecipients defines addresses that the PG system is allowed to - # send mail to. this prevents subtle PG exploits. This should be set - # in course.conf to the addresses of professors of each course. Sending - # mail from the PG system (i.e. questionaires, essay questions) will - # fail if this is not set somewhere (either here or in course.conf). - #allowedRecipients => [ - # "yourname\@host.yourdomain.edu", - #], - # if defined, feedbackRecipients overrides the list of recipients for - # feedback email. It's appropriate to set this in the course.conf for - # specific courses, but probably not in global.conf. if not defined, - # mail is sent to all professors and TAs for a given course - #feedbackRecipients => [ - # "prof1\@host.yourdomain.edu", - # "prof2\@host.yourdomain.edu", - #], - # feedbackVerbosity: - # 0: send only the feedback comment and context link - # 1: as in 0, plus user, set, problem, and PG data - # 2: as in 1, plus the problem environment (debugging data) - feedbackVerbosity => 1, - editor_window_rows => 15, - editor_window_columns => 100, -); +# Problem library options +################################################################################ -%externalPrograms = ( - mkdir => "/bin/mkdir", - tth => "/usr/local/bin/tth", - pdflatex => "/usr/local/bin/pdflatex", - latex => "/usr/local/bin/latex", - #dvipng => "/usr/local/bin/dvipng -mode ljfivemp -D600 -Q6 -x1000.5 -bgTransparent", - dvipng => "/usr/local/bin/dvipng", - gif2eps => "$webworkDirs{bin}/gif2eps", - png2eps => "$webworkDirs{bin}/png2eps", - gif2png => "$webworkDirs{bin}/gif2png", -); +# For configuration instructions, see: +# http://webwork.maa.org/wiki/National_Problem_Library +# The directory containing the natinal problem library files. Set to "" if no problem +# library is installed. +$problemLibrary{root} = ""; + +# Problem Library version +# Version 1 is in use. Version 2 will be released soon. +$problemLibrary{version} = "2"; + +# Problem Library SQL database connection information +$problemLibrary_db = { + dbsource => $database_dsn, + user => $database_username, + passwd => $database_password, +}; ################################################################################ -# Frontend options +# Logs ################################################################################ -%templates = ( - system => "$webworkDirs{conf}/templates/ur.template", -); +# FIXME: take logs out of %webworkFiles/%courseFiles and give them their own +# top-level hash. + +# Logs data about how long it takes to process problems. (Do not confuse this +# with the /other/ timing log which can be set by WeBWorK::Timing and is used +# for benchmarking system performance in general. At some point, this timing +# mechanism will be deprecated in favor of the WeBWorK::Timing mechanism.) +$webworkFiles{logs}{timing} = "$webworkDirs{logs}/timing.log"; + +# Logs courses created via the web-based Course Administration module. +$webworkFiles{logs}{hosted_courses} = "$webworkDirs{logs}/hosted_courses.log"; + +# The transaction log contains data from each recorded answer submission. This +# is useful if the database becomes corrupted. +$webworkFiles{logs}{transaction} = "$webworkDirs{logs}/${courseName}_transaction.log"; + +# The answer log stores a history of all users' submitted answers. +$courseFiles{logs}{answer_log} = "$courseDirs{logs}/answer_log"; + +# Log logins. +$courseFiles{logs}{login_log} = "$courseDirs{logs}/login.log"; + +# Log for almost every click. By default it is the empty string, which +# turns this log off. If you want it turned on, we suggest +# "$courseDirs{logs}/activity.log" +# When turned on, this log can get quite large. +$courseFiles{logs}{activity_log} = ''; ################################################################################ -# Database options +# Site defaults (FIXME: what other things could be "site defaults"?) +################################################################################ + +# Set the default timezone of courses on this server. To get a list of valid +# timezones, run: +# +# perl -MDateTime::TimeZone -e 'print join "\n", DateTime::TimeZone::all_names' +# +# To get a list of valid timezone "links" (deprecated names), run: +# +# perl -MDateTime::TimeZone -e 'print join "\n", DateTime::TimeZone::links' +# +# If left blank, the system timezone will be used. This is usually what you +# want. You might want to set this if your server is NOT in the same timezone as +# your school. If just a few courses are in a different timezone, set this in +# course.conf for the affected courses instead. +# +$siteDefaults{timezone} = "America/New_York"; + +# The default_templates_course is used by default to create a new course. +# The contents of the templates directory are copied from this course +# to the new course being created. +$siteDefaults{default_templates_course} ="modelCourse"; + +################################################################################ +# Authentication system ################################################################################ -# Several database layouts are defined in separate environment files. Select the -# one which should be used by all courses by default, and include it. This can -# be overridden by including a difference environment file in the course.conf of -# a particular course. +# FIXME This mechanism is a little awkward and probably should be merged with +# the dblayout selection system somehow. + +# Select the authentication module to use for normal logins. +# +# If this value is a string, the given authentication module will be used +# regardless of the database layout. If it is a hash, the database layout name +# will be looked up in the hash and the resulting value will be used as the +# authentication module. The special hash key "*" is used if no entry for the +# current database layout is found. +# +$authen{user_module} = { + sql_moodle => "WeBWorK::Authen::Moodle", + # sql_ldap => "WeBWorK::Authen::LDAP", + "*" => "WeBWorK::Authen", +}; -# Include sql.conf to specify a database layout for use with an SQL server. -#include "conf/sql.conf"; +# Select the authentication module to use for proctor logins. +# +# A string or a hash is accepted, as above. +# +$authen{proctor_module} = "WeBWorK::Authen::Proctor"; -# Include gdbm.conf to specify a database layout for WeBWorK 1.x compatible GDBM -# databases. Use this layout if you wish to share courses between WeBWorK 1.x -# and WeBWorK 2. -include "conf/gdbm.conf"; +# Options for particular authentication modules -# Please read the documentation in the file that you chose to include, as there -# are layout-specific options that must be configured. +# $authen{moodle_options} = { +# dsn => $moodle_dsn, +# username => $moodle_username, +# password => $moodle_password, +# table_prefix => $moodle_table_prefix, +# moodle17 => $moodle17, +# }; + +$authen{ldap_options} = { + # hosts to attempt to connect to, in order. For example: + # auth.myschool.edu -- uses LDAP scheme and port 389 + # ldap://auth.myschool.edu:666 -- non-standard port + # ldaps://auth.myschool.edu -- uses LDAPS scheme and port 636 + # ldaps://auth.myschool.edu:389 -- SSL on non-SSL port + # Edit the host(s) below: + net_ldap_hosts => [ + "ldaps://auth1.myschool.edu", + "ldaps://auth2.myschool.edu", + ], + # connection options + net_ldap_options => { + timeout => 30, + version => 3, + }, + # base to use when searching for user's DN + # Edit the data below: + net_ldap_base => "ou=people,dc=myschool,dc=edu", + + # Use a Bind account if set to 1 + bindAccount => 0, + + searchDN => "cn=search,DC=youredu,DC=edu", + bindPassword => "password", + + # The LDAP module searches for a DN whose RDN matches the username + # entered by the user. The net_ldap_rdn setting tells the LDAP + # backend what part of your LDAP schema you want to use as the RDN. + # The correct value for net_ldap_rdn will depend on your LDAP setup. + # + # Uncomment this line if you use Active Directory. + #net_ldap_rdn => "sAMAccountName", + # + # Uncomment this line if your schema uses uid as an RDN. + #net_ldap_rdn => "uid", + # + # By default, net_ldap_rdn is set to "sAMAccountName". + + # If failover = "all", then all LDAP failures will be checked + # against the WeBWorK database. If failover = "local", then only + # users who don't exist in LDAP will be checked against the WeBWorK + # database. If failover = 0, then no attempts will be checked + # against the WeBWorK database. failover = 1 is equivalent to + # failover = "all". + failover => "all", +}; ################################################################################ # Authorization system ################################################################################ -# This lets you specify a minimum permission level needed to perform certain -# actions. In the current system, >=10 will allow a professor to perform the -# action, >=5 will allow a TA to, and >=0 will allow a student to perform an -# action (almost never what you want). -my $ta = 5; -my $professor = 10; +# this section lets you define which groups of users can perform which actions. + +# this hash maps a numeric permission level to the name of a role. the number +# assigned to a role is significant -- roles with higher numbers are considered +# "more privileged", and are included when that role is listed for a privilege +# below. +# +%userRoles = ( + guest => -5, + student => 0, + login_proctor => 2, + grade_proctor => 3, + ta => 5, + professor => 10, +); + +# this hash maps operations to the roles that are allowed to perform those +# operations. the role listed and any role with a higher permission level (in +# the %userRoles hash) will be allowed to perform the operation. If the role +# is undefined, no users will be allowed to perform the operation. +# %permissionLevels = ( - become_student => $professor, - access_instructor_tools => $ta, - create_and_delete_problem_sets => $professor, - modify_problem_sets => $professor, - assign_problem_sets => $professor, - modify_student_data => $professor, - score_sets => $professor, - send_mail => $professor, - modify_classlist_files => $professor, - modify_set_def_files => $professor, + login => "guest", + report_bugs => "ta", + submit_feedback => "student", + change_password => "student", + change_email_address => "student", + + proctor_quiz_login => "login_proctor", + proctor_quiz_grade => "grade_proctor", + view_proctored_tests => "student", + view_hidden_work => "ta", + + view_multiple_sets => "ta", + view_unopened_sets => "ta", + view_hidden_sets => "ta", + view_answers => "ta", + view_ip_restricted_sets => "ta", + + become_student => "professor", + access_instructor_tools => "ta", + score_sets => "professor", + send_mail => "professor", + receive_feedback => "ta", + + create_and_delete_problem_sets => "professor", + assign_problem_sets => "professor", + modify_problem_sets => "professor", + modify_student_data => "professor", + modify_classlist_files => "professor", + modify_set_def_files => "professor", + modify_scoring_files => "professor", + modify_problem_template_files => "professor", + manage_course_files => "professor", + + create_and_delete_courses => "professor", + fix_course_databases => "professor", + + ##### Behavior of the interactive problem processor ##### + + show_correct_answers_before_answer_date => "ta", + show_solutions_before_answer_date => "ta", + avoid_recording_answers => "ta", + # Below this level, old answers are never initially shown + can_show_old_answers_by_default => "student", + # at this level, we look at showOldAnswers for default value + # even after the due date + can_always_use_show_old_answers_default => "professor", + check_answers_before_open_date => "ta", + check_answers_after_open_date_with_attempts => "ta", + check_answers_after_open_date_without_attempts => "guest", + check_answers_after_due_date => "guest", + check_answers_after_answer_date => "guest", + create_new_set_version_when_acting_as_student => undef, + print_path_to_problem => "professor", # see "Special" PG environment variables + record_set_version_answers_when_acting_as_student => undef, + record_answers_when_acting_as_student => undef, + # "record_answers_when_acting_as_student" takes precedence + # over the following for professors acting as students: + record_answers_before_open_date => undef, + record_answers_after_open_date_with_attempts => "student", + record_answers_after_open_date_without_attempts => undef, + record_answers_after_due_date => undef, + record_answers_after_answer_date => undef, + dont_log_past_answers => "professor", + # does the user get to see a dump of the problem? + view_problem_debugging_info => "ta", + + ##### Behavior of the Hardcopy Processor ##### + + download_hardcopy_multiuser => "ta", + download_hardcopy_multiset => "ta", + download_hardcopy_view_errors =>"professor", + download_hardcopy_format_pdf => "guest", + download_hardcopy_format_tex => "ta", ); +# This is the default permission level given to new students and students with +# invalid or missing permission levels. +$default_permission_level = $userRoles{student}; + ################################################################################ # Session options ################################################################################ @@ -219,10 +798,10 @@ my $professor = 10; $sessionKeyTimeout = 60*30; # $sessionKeyLength defines the length (in characters) of the session key -$sessionKeyLength = 40; +$sessionKeyLength = 32; # @sessionKeyChars lists the legal session key characters -@sessionKeyChars = ('A'..'Z', 'a'..'z', '0'..'9', '.', '^', '/', '!', '*'); +@sessionKeyChars = ('A'..'Z', 'a'..'z', '0'..'9'); # Practice users are users who's names start with $practiceUser # (you can comment this out to remove practice user support) @@ -234,88 +813,271 @@ $practiceUserPrefix = "practice"; # password. Come to think of it, why do we even have this?! #$debugPracticeUser = "practice666"; +# Option for gateway tests; $gatewayGracePeriod is the time in seconds +# after the official due date during which we'll still grade the test +$gatewayGracePeriod = 120; + ################################################################################ -# PG translation options +# PG subsystem options ################################################################################ -%pg = ( - # options for various renderers - renderers => { - "WeBWorK::PG::Remote" => { - proxy => "http://localhost:21000/RenderD" - } - }, - # currently selected renderer - renderer => "WeBWorK::PG::Local", - #renderer => "WeBWorK::PG::Remote", - # directories used by PG - directories => { - # directories used only by PG - lib => "$pgRoot/lib", - macros => "$pgRoot/macros", - }, - options => { - # default translation options - displayMode => "images", - showOldAnswers => 1, - showCorrectAnswers => 0, - showHints => 0, - showSolutions => 0, - catchWarnings => 0, # there's a global warning catcher now - # default grader - grader => "avg_problem_grader", - }, - # this will be customized in the course.conf file - specialPGEnvironmentVars => { - PRINT_FILE_NAMES_FOR => [ qw(gage apizer voloshin lr003k professor) ], - CAPA_Tools => "$courseDirs{macros}/CAPA_Tools/", - CAPA_MCTools => "$courseDirs{macros}/CAPA_MCTools/", - CAPA_Graphics_URL => "$courseDirs{html}/CAPA_Graphics/", - CAPA_GraphicsDirectory => "$courseDirs{html}CAPA_Graphics/", - }, - # modules lists module names and the packages each contains - modules => [ - [qw(DynaLoader)], - [qw(Exporter)], - [qw(GD)], - - [qw(AlgParser AlgParserWithImplicitExpand Expr ExprWithImplicitExpand)], - [qw(AnswerHash AnswerEvaluator)], - [qw(WWPlot)], # required by Circle (and others) - [qw(Circle)], - [qw(Complex)], - [qw(Complex1)], - [qw(Distributions)], - [qw(Fraction)], - [qw(Fun)], - [qw(Hermite)], - [qw(Label)], - [qw(List)], - [qw(Match)], - [qw(MatrixReal1)], # required by Matrix - [qw(Matrix)], - [qw(Multiple)], - [qw(PGrandom)], - [qw(Regression)], - [qw(Select)], - [qw(Units)], - [qw(VectorField)], - ], - # defaults used by answer evaluators - ansEvalDefaults => { - functAbsTolDefault => .001, - functLLimitDefault => .0000001, - functMaxConstantOfIntegration => 1E8, - functNumOfPoints => 3, - functRelPercentTolDefault => .1, - functULimitDefault => .9999999, - functVarDefault => "x", - functZeroLevelDefault => 1E-14, - functZeroLevelTolDefault => 1E-12, - numAbsTolDefault => .001, - numFormatDefault => "", - numRelPercentTolDefault => .1, - numZeroLevelDefault => 1E-14, - numZeroLevelTolDefault => 1E-12, +# List of enabled display modes. Comment out any modes you don't wish to make +# available for use. +$pg{displayModes} = [ +# "plainText", # display raw TeX for math expressions +# "formattedText", # format math expressions using TtH + "images", # display math expressions as images generated by dvipng + "jsMath", # render TeX math expressions on the client side using jsMath +# "MathJax", # render TeX math expressions on the client side using MathJax --- we strongly recommend people install and use MathJax +# "asciimath", # render TeX math expressions on the client side using ASCIIMathML +# "LaTeXMathML", # render TeX math expressions on the client side using LaTeXMathML +]; + +#### Default settings for the PG translator + +# Default display mode. Should be listed above (uncomment only one). +$pg{options}{displayMode} = "images"; +#$pg{options}{displayMode} = "jsMath"; +#$pg{options}{displayMode} = "MathJax"; + +# The default grader to use, if a problem doesn't specify. +$pg{options}{grader} = "avg_problem_grader"; + +# Fill in answer blanks with the student's last answer by default? +$pg{options}{showOldAnswers} = 1; + +# Show correct answers (when allowed) by default? +$pg{options}{showCorrectAnswers} = 0; + +# Show hints (when allowed) by default? +$pg{options}{showHints} = 0; + +# Show solutions (when allowed) by default? +$pg{options}{showSolutions} = 0; + +# Display the "Entered" column which automatically shows the evaluated student answer, e.g. 1 if student input is sin(pi/2). +# If this is set to 0, e.g. to save space in the response area, the student can still see their evaluated answer by hovering +# the mouse pointer over the typeset version of their answer +$pg{options}{showEvaluatedAnswers} = 1; + +# Catch translation warnings internally by default? (We no longer need to do +# this, since there is a global warnings handler. So this should be off.) +$pg{options}{catchWarnings} = 0; + +# decorations for correct input blanks -- apparently you can't define and name attribute collections in a .css file +$pg{options}{correct_answer} = "{border-width:2;border-style:solid;border-color:#8F8}"; #matches resultsWithOutError class in math2.css + +# decorations for incorrect input blanks +$pg{options}{incorrect_answer} = "{border-width:2;border-style:solid;border-color:#F55}"; #matches resultsWithError class in math2.css + +##### Currently-selected renderer + +# Only the local renderer is supported in this version. +$pg{renderer} = "WeBWorK::PG::Local"; + +# The remote renderer connects to an XML-RPC PG rendering server. +#$pg{renderer} = "WeBWorK::PG::Remote"; + +##### Renderer-dependent options + +# The remote renderer has one option: +$pg{renderers}{"WeBWorK::PG::Remote"} = { + # The "proxy" server to connect to for remote rendering. + proxy => "http://localhost:21000/RenderD", +}; + +##### Settings for various display modes + +# "images" mode has several settings: +$pg{displayModeOptions}{images} = { + # Determines the method used to align images in output. Can be + # "baseline", "absmiddle", or "mysql". + dvipng_align => 'mysql', + + # If mysql is chosen, this information indicates which database contains the + # 'depths' table. Since 2.3.0, the depths table is kept in the main webwork + # database. (If you are upgrading from an earlier version of webwork, and + # used the mysql method in the past, you should move your existing 'depths' + # table to the main database.) + dvipng_depth_db => { + dbsource => $database_dsn, + user => $database_username, + passwd => $database_password, }, -); +}; + +$pg{displayModeOptions}{jsMath} = { + reportMissingFonts => 0, # set to 1 to allow the missing font message + missingFontMessage => undef, # set to an HTML string to replace the missing font message + noImageFonts => 0, # set to 1 if you didn't install the jsMath image fonts + processDoubleClicks => 1, # set to 0 to disable double-click on math to get TeX source +}; + +##### Directories used by PG + +# The root of the PG directory tree (from pg_root in Apache config). +$pg{directories}{root} = "$pg_dir"; +$pg{directories}{lib} = "$pg{directories}{root}/lib"; +$pg{directories}{macros} = "$pg{directories}{root}/macros"; + +# +# The macro file search path. Each directory in this list is seached +# (in this order) by loadMacros() when it looks for a .pl file. +# +$pg{directories}{macrosPath} = [ + ".", # search the problem file's directory + $courseDirs{macros}, + $pg{directories}{macros}, + "$courseDirs{templates}/Library/macros/Union", + "$courseDirs{templates}/Library/macros/Michigan", + "$courseDirs{templates}/Library/macros/CollegeOfIdaho", + "$courseDirs{templates}/Library/macros/FortLewis", + "$courseDirs{templates}/Library/macros/TCNJ", + "$courseDirs{templates}/Library/macros/NAU", + "$courseDirs{templates}/Library/macros/Dartmouth", +]; + +# The applet search path. If a full URL is given, it is used unmodified. If an +# absolute path is given, the URL of the local server is prepended to it. +# +# For example, if an item is "/math/applets", +# and the local server is "https://math.yourschool.edu", +# then the URL "https://math.yourschool.edu/math/applets" will be used. +# + +$pg{directories}{appletPath} = [ # paths to search for applets (requires full url) + "$webworkURLs{htdocs}/applets", + "$webworkURLs{htdocs}/applets/geogebra_stable", + "$courseURLs{html}/applets", + "$webworkURLs{htdocs}/applets/Xgraph", + "$webworkURLs{htdocs}/applets/PointGraph", + "$webworkURLs{htdocs}/applets/Xgraph", + "$webworkURLs{htdocs}/applets/liveJar", + "$webworkURLs{htdocs}/applets/Image_and_Cursor_All", + +]; + +##### "Special" PG environment variables. (Stuff that doesn't fit in anywhere else.) + +# Users for whom to print the file name of the PG file being processed. +$pg{specialPGEnvironmentVars}{PRINT_FILE_NAMES_FOR} = [ "professor", ]; + # ie file paths are printed for 'gage' +$pg{specialPGEnvironmentVars}{PRINT_FILE_NAMES_PERMISSION_LEVEL} = $userRoles{ $permissionLevels{print_path_to_problem} }; + # (file paths are also printed for anyone with this permission or higher) + +# Locations of CAPA resources. (Only necessary if you need to use converted CAPA +# problems.) +$pg{specialPGEnvironmentVars}{CAPA_Tools} = "/opt/webwork/libraries/CAPA/CAPA_Tools/", +$pg{specialPGEnvironmentVars}{CAPA_MCTools} = "/opt/webwork/libraries/CAPA/CAPA_MCTools/", +$pg{specialPGEnvironmentVars}{CAPA_GraphicsDirectory} = "$webworkDirs{htdocs}/CAPA_Graphics/", +$pg{specialPGEnvironmentVars}{CAPA_Graphics_URL} = "$webworkURLs{htdocs}/CAPA_Graphics/", + +# Size in pixels of dynamically-generated images, i.e. graphs. +$pg{specialPGEnvironmentVars}{onTheFlyImageSize} = 400, + +# To disable the Parser-based versions of num_cmp and fun_cmp, and use the +# original versions instead, set this value to 1. +$pg{specialPGEnvironmentVars}{useOldAnswerMacros} = 0; + +# Strings to insert at the start and end of the body of a problem +# (at beginproblem() and ENDDOCUMENT) in various modes. More display modes +# can be added if different behaviours are desired (e.g., HTML_dpng, +# HTML_asciimath, etc.). These parts are not used in the Library browser. + +$pg{specialPGEnvironmentVars}{problemPreamble} = { TeX => '', HTML=> '' }; +$pg{specialPGEnvironmentVars}{problemPostamble} = { TeX => '', HTML=>'' }; + +# To have the problem body indented and boxed, uncomment: + + $pg{specialPGEnvironmentVars}{problemPreamble}{HTML} = '
+
'; + $pg{specialPGEnvironmentVars}{problemPostamble}{HTML} = '
+
'; + +##### PG modules to load + +# The first item of each list is the module to load. The remaining items are +# additional packages to import. + +${pg}{modules} = [ + [qw(DynaLoader)], + [qw(Exporter)], + [qw(GD)], + + [qw(AlgParser AlgParserWithImplicitExpand Expr ExprWithImplicitExpand utf8)], + [qw(AnswerHash AnswerEvaluator)], + [qw(WWPlot)], # required by Circle (and others) + [qw(Circle)], + [qw(Complex)], + [qw(Complex1)], + [qw(Distributions)], + [qw(Fraction)], + [qw(Fun)], + [qw(Hermite)], + [qw(Inequalities::common)], + [qw(Label)], + [qw(LimitedPolynomial)], + [qw(ChoiceList)], + [qw(Match)], + [qw(MatrixReal1)], # required by Matrix + [qw(Matrix)], + [qw(Multiple)], + [qw(PGrandom)], + [qw(Regression)], + [qw(Select)], + [qw(Units)], + [qw(VectorField)], + [qw(Parser Value)], + [qw(Parser::Legacy)], +# [qw(SaveFile)], +# [qw(Chromatic)], # for Northern Arizona graph problems +# # -- follow instructions at libraries/nau_problib/lib/README to install + [qw(Applet FlashApplet JavaApplet CanvasApplet)], + [qw(PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash)], +]; + +##### Problem creation defaults + +# The default weight (also called value) of a problem to use when using the +# Library Browser, Problem Editor or Hmwk Sets Editor to add problems to a set +# or when this value is left blank in an imported set definition file. +$problemDefaults{value} = 1; + +# The default max_attempts for a problem to use when using the +# Library Browser, Problem Editor or Hmwk Sets Editor to add problems to a set +# or when this value is left blank in an imported set definition file. Note that +# setting this to -1 gives students unlimited attempts. +$problemDefaults{max_attempts} = -1; + +##### Answer evaluatior defaults + +$pg{ansEvalDefaults} = { + functAbsTolDefault => .001, + functLLimitDefault => .0000001, + functMaxConstantOfIntegration => 1E8, + functNumOfPoints => 3, + functRelPercentTolDefault => .1, + functULimitDefault => .9999999, + functVarDefault => "x", + functZeroLevelDefault => 1E-14, + functZeroLevelTolDefault => 1E-12, + numAbsTolDefault => .001, + numFormatDefault => "", + numRelPercentTolDefault => .1, + numZeroLevelDefault => 1E-14, + numZeroLevelTolDefault => 1E-12, + useBaseTenLog => 0, + defaultDisplayMatrixStyle => "[s]", # left delimiter, middle line delimiters, right delimiter + reducedScoringPeriod => 0, # Length of Reduced Credit Period (formally Reduced Scoring Period) in minutes + reducedScoringValue => 1, # A number in [0,1]. Students will be informed of the value as a percentage +}; + +################################################################################ +# Compatibility +################################################################################ + +# Define the old names for the various "root" variables. +$webworkRoot = $webworkDirs{root}; +$webworkURLRoot = $webworkURLs{root}; +$pgRoot = $pg{directories}{root}; + diff --git a/conf/httpd-wwmp.conf.dist b/conf/httpd-wwmp.conf.dist deleted file mode 100644 index 7aae2a4b67..0000000000 --- a/conf/httpd-wwmp.conf.dist +++ /dev/null @@ -1,113 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -# The best way to turn this into a working webwork server config file is to do -# a global find/replace on the following strings: -# !WEBWORK_ROOT! -> the root of your personal webwork-modperl tree -# !WEBWORK_USER! -> your user name -# !WEBWORK_PORT! -> the port on which you want to run this server -# (please use 10000 + your UID) -# !PG_ROOT! -> the root of your personal pg tree -# -# After those find/replace operations, you should edit the access control -# statements at the bottom of this file. - -Include !WEBWORK_ROOT!/conf/httpd-wwmp-header.conf - -Port !WEBWORK_PORT! -User !WEBWORK_USER! -Group !WEBWORK_USER! - -ServerAdmin !WEBWORK_USER!@localhost - -LockFile !WEBWORK_ROOT!/logs/httpd.lock -PidFile !WEBWORK_ROOT!/logs/httpd.pid -ErrorLog !WEBWORK_ROOT!/logs/error.log - -# On systems that use it, ScoreBoardFile must be different for different -# invocations of Apache. webwork-dev doens't appear to be one of those -# systems. -#ScoreBoardFile /var/run/httpd.scoreboard - -PerlFreshRestart On - - SetHandler perl-script - PerlHandler Apache::WeBWorK - - PerlSetVar webwork_root !WEBWORK_ROOT! - PerlSetVar pg_root !PG_ROOT! - - use lib '!WEBWORK_ROOT!/lib'; - use lib '!PG_ROOT!/lib'; - - - -# We're limiting the number of children because we'll be running a lot and -# don't want to bog the development box down. -StartServers 2 -MinSpareServers 2 -MaxSpareServers 2 -MaxClients 150 -# How "old" a child is allowed to get. 0 for unlimited requests -# Pick a low number -- you're bound to screw something up, right? -MaxRequestsPerChild 100 - -# This DocumentRoot doesn't actually make a lot of sense. In a -# WeBWorK mod_perl system, there is no static document root, but the -# DocumentRoot does have to exist, and not have a subdirectory -# named "webwork2". It suffices. -DocumentRoot "!WEBWORK_ROOT!/htdocs" - -# This alias, however, is important. -Alias /webwork2_files/ !WEBWORK_ROOT!/htdocs/ - -# This should match the DocumentRoot - - Options Indexes FollowSymLinks MultiViews - AllowOverride None - Order allow,deny - Allow from all - - -# Possible forms of access limitation (edit to taste) - -# The developer maintains a personal htpasswd file -# -# AuthType Basic -# AuthName "!WEBWORK_USER!'s WeBWorK development system" -# AuthUserFile !WEBWORK_ROOT!/conf/htpasswd -# Require valid-user -# - -# There is a site-wide htpasswd file somewhere, with all developers in it -# -# AuthType Basic -# AuthName "!WEBWORK_USER!'s WeBWorK development system" -# AuthUserFile /path/to/site/htpasswd/file -# Require valid-user -# - -# You have mod_pam_auth and would like to authenticate against your /etc/passwd -# file. (NOTE: It is a good idea to use Digest authentication instead of Basic, -# even though this limits your choice of browsers, because of the heightened -# sensitivity of normal system passwords.) -# -# AuthType Digest -# AuthName "!WEBWORK_USER!'s WeBWorK development system" -# AuthPAM_Enabled on -# Require valid-user -## Require group wwdev -# diff --git a/conf/snippets/blankProblem.pg b/conf/snippets/blankProblem.pg new file mode 100644 index 0000000000..092880cc51 --- /dev/null +++ b/conf/snippets/blankProblem.pg @@ -0,0 +1,25 @@ +DOCUMENT(); + +# Load whatever macros you need for the problem +loadMacros("PG.pl", + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl", + "PGgraphmacros.pl", + ); + +## Do NOT show partial correct answers +$showPartialCorrectAnswers = 0; +TEXT(beginproblem()); + +BEGIN_TEXT + +Enter a value for \(\pi\) + +\{ans_rule()\} +END_TEXT + +ANS(num_cmp(3.14159)); + +ENDDOCUMENT(); diff --git a/conf/snippets/blankProblem2.pg b/conf/snippets/blankProblem2.pg new file mode 100644 index 0000000000..0750434b70 --- /dev/null +++ b/conf/snippets/blankProblem2.pg @@ -0,0 +1,72 @@ +##DESCRIPTION +## Algebra problem: true or false for inequality +##ENDDESCRIPTION + +##KEYWORDS('algebra', 'inequality', 'fraction') + +## DBsubject('Algebra') +## DBchapter('Fundamentals') +## DBsection('Real Numbers') +## Date('6/3/2002') +## Author('') +## Institution('') +## TitleText1('Precalculus') +## EditionText1('3') +## AuthorText1('Stewart, Redlin, Watson') +## Section1('1.1') +## Problem1('22') + +######################################################################## + +DOCUMENT(); + +loadMacros( + "PGstandard.pl", # Standard macros for PG language + "MathObjects.pl", + #"source.pl", # allows code to be displayed on certain sites. + #"PGcourse.pl", # Customization file for the course +); + +# Print problem number and point value (weight) for the problem +TEXT(beginproblem()); + +# Show which answers are correct and which ones are incorrect +$showPartialCorrectAnswers = 1; + +############################################################## +# +# Setup +# +# +Context("Numeric"); + +$pi = Real("pi"); + +############################################################## +# +# Text +# +# + +Context()->texStrings; +BEGIN_TEXT + + +Enter a value for \(\pi\) + +\{$pi->ans_rule\} +END_TEXT +Context()->normalStrings; + +############################################################## +# +# Answers +# +# + +ANS($pi->with(tolerance=>.0001)->cmp); +# relative tolerance --3.1412 is incorrect but 3.1413 is correct +# default tolerance is .01 or one percent. + + +ENDDOCUMENT(); diff --git a/conf/snippets/hardcopyPostamble.tex b/conf/snippets/hardcopyPostamble.tex index 8f38ece2d7..7bff985e02 100644 --- a/conf/snippets/hardcopyPostamble.tex +++ b/conf/snippets/hardcopyPostamble.tex @@ -1,7 +1,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % WeBWorK Online Homework Delivery System -% Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -% $CVSHeader$ +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/conf/snippets/hardcopyPostamble.tex,v 1.2 2003/12/09 01:12:29 sh002i 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 diff --git a/conf/snippets/hardcopyPreamble.tex b/conf/snippets/hardcopyPreamble.tex index 72eb2815e7..a6ca721a74 100644 --- a/conf/snippets/hardcopyPreamble.tex +++ b/conf/snippets/hardcopyPreamble.tex @@ -1,7 +1,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % WeBWorK Online Homework Delivery System -% Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -% $CVSHeader$ +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/conf/snippets/hardcopyPreamble.tex,v 1.3 2005/09/17 20:12:01 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 @@ -18,7 +18,9 @@ \documentclass[10pt,dvips]{amsart} \usepackage{amsmath,amsfonts,amssymb,multicol} \usepackage[pdftex]{graphicx} +\usepackage{epstopdf} % allows use of eps files with pdftex \usepackage{epsf} +\usepackage{epsfig} \usepackage{pslatex} \pagestyle{plain} \textheight 9in diff --git a/conf/snippets/hardcopyProblemDivider.tex b/conf/snippets/hardcopyProblemDivider.tex index 1d292a87c5..6d13dbdae4 100644 --- a/conf/snippets/hardcopyProblemDivider.tex +++ b/conf/snippets/hardcopyProblemDivider.tex @@ -1,7 +1,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % WeBWorK Online Homework Delivery System -% Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -% $CVSHeader$ +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/conf/snippets/hardcopyProblemDivider.tex,v 1.3 2004/06/24 21:10:50 dpvc 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 @@ -14,6 +14,8 @@ % Artistic License for more details. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\bigskip +\medskip +\goodbreak \hrule -\bigskip +\nobreak +\smallskip diff --git a/conf/snippets/hardcopySetDivider.tex b/conf/snippets/hardcopySetDivider.tex index 9783036ff5..8315d5777c 100644 --- a/conf/snippets/hardcopySetDivider.tex +++ b/conf/snippets/hardcopySetDivider.tex @@ -1,7 +1,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % WeBWorK Online Homework Delivery System -% Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -% $CVSHeader$ +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/conf/snippets/hardcopySetDivider.tex,v 1.4 2004/07/07 11:35:34 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 @@ -16,8 +16,8 @@ \end{multicols} % close off the columns from the set above -\newpage - +\newpage% +\setcounter{page}{1}% \begin{multicols}{2} \columnwidth=\linewidth % reopen the columns for the following set diff --git a/conf/snippets/hardcopySetFooter.pg b/conf/snippets/hardcopySetFooter.pg index a1bb246f6a..b3cfb96c4f 100644 --- a/conf/snippets/hardcopySetFooter.pg +++ b/conf/snippets/hardcopySetFooter.pg @@ -1,19 +1,3 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - # # hardcopySetFooter.pg - generic hardcopy set footer file # @@ -27,7 +11,7 @@ loadMacros( BEGIN_TEXT $BEGIN_ONE_COLUMN -\noindent {\tiny Generated by the WeBWorK system \copyright WeBWorK Team, Department of Mathematics, University of Rochester} +\noindent {\tiny Generated by \copyright WeBWorK, http://webwork.maa.org, Mathematical Association of America} $END_ONE_COLUMN END_TEXT diff --git a/conf/snippets/hardcopySetHeader.pg b/conf/snippets/hardcopySetHeader.pg index 1729ce19f9..7a357d2d13 100644 --- a/conf/snippets/hardcopySetHeader.pg +++ b/conf/snippets/hardcopySetHeader.pg @@ -1,44 +1,71 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -##Problem set header for WW_Prob_Lib1 Summer 2000 - -DOCUMENT(); # This should be the first executable line in the problem. - -loadMacros("PG.pl", - "PGbasicmacros.pl" -); - +DOCUMENT(); -$dateTime = $formattedDueDate; -$sectionNumber = protect_underbar($sectionNumber); -$setNumber = protect_underbar($setNumber); -$course = protect_underbar(WW_Prob_Lib1); +loadMacros( + "PG.pl", + "PGbasicmacros.pl", +); -BEGIN_TEXT -$BEGIN_ONE_COLUMN +TEXT($BEGIN_ONE_COLUMN); +TEXT(MODES(TeX =>EV3(<<'EOT'),HTML=>"",Latex2HTML=>"")); \noindent {\large \bf $studentName} \hfill -\noindent {\large \bf $course $sectionNumber Summer 2000} +\noindent {\large \bf MTH 161 $sectionNumber Fall 2003} \par -\noindent Sample WeBWorK problems. \hfill WeBWorK assignment $setNumber due $dateTime. + +EOT + +BEGIN_TEXT + +$BBOLD WeBWorK assignment number $setNumber is due : $formattedDueDate. $EBOLD + +$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/hardcopySetHeader.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. - diff --git a/conf/snippets/hardcopyUserDivider.tex b/conf/snippets/hardcopyUserDivider.tex index 806014bda2..c24101aca1 100644 --- a/conf/snippets/hardcopyUserDivider.tex +++ b/conf/snippets/hardcopyUserDivider.tex @@ -1,7 +1,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % WeBWorK Online Homework Delivery System -% Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -% $CVSHeader$ +% Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +% $CVSHeader: webwork2/conf/snippets/hardcopyUserDivider.tex,v 1.3 2004/07/07 11:35:34 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 @@ -16,7 +16,7 @@ \end{multicols} % close off the columns from the set above -\newpage - +\newpage% +\setcounter{page}{1}% \begin{multicols}{2} \columnwidth=\linewidth % reopen the columns for the following set diff --git a/conf/snippets/screenSetHeader.pg b/conf/snippets/screenSetHeader.pg index 2fd0bf7f5e..10ef39eb2a 100644 --- a/conf/snippets/screenSetHeader.pg +++ b/conf/snippets/screenSetHeader.pg @@ -1,19 +1,3 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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(); # Load whatever macros you need for the problem @@ -31,14 +15,18 @@ $showPartialCorrectAnswers = 0; BEGIN_TEXT $BR -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^3 instead of 8, sin(3pi/2) instead of -1, e^(ln(2)) -instead of 2, (2+tan(3))*(4-sin(5))^6-7/8 instead of 27620.3413, etc. Here's -the -\{ htmlLink(qq!http://webwork.math.rochester.edu/webwork_system_html/docs/docs/pglanguage/availablefunctions.html!,"list of the functions") \} -which WeBWorK understands. +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/webwork_system_html/docs/docs/pglanguage/availablefunctions.html!,"list of the functions") \} +which WeBWorK understands. $PAR END_TEXT diff --git a/conf/snippets/setHeader.pg b/conf/snippets/setHeader.pg index 49370453b3..35267ae018 100644 --- a/conf/snippets/setHeader.pg +++ b/conf/snippets/setHeader.pg @@ -1,87 +1,88 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ +# ASimpleCombinedHeaderFile.pg +# This header file can be used for both the screen and hardcopy output + DOCUMENT(); loadMacros( "PG.pl", "PGbasicmacros.pl", - "PGchoicemacros.pl", - "PGanswermacros.pl" + ); TEXT($BEGIN_ONE_COLUMN); -TEXT(MODES(TeX =>EV3(<<'EOT'),HTML=>"",Latex2HTML=>"")); +#################################################### +# +# The item below printed out only when a hardcopy is made. +# +#################################################### + + + +TEXT(MODES(TeX =>EV3(<<'EOT'),HTML=>"")); + \noindent {\large \bf $studentName} \hfill -\noindent {\large \bf MTH 143 $sectionNumber Spring 2003} +{\large \bf {\{protect_underbar($courseName)\}}} +% Uncomment the line below if this course has sections. Note that this is a comment in TeX mode since this is only processed by LaTeX +% {\large \bf { Section: \{protect_underbar($sectionName)\} } } \par +\noindent{\large \bf {Assignment \{protect_underbar($setNumber)\} due $formatedDueDate}} +\par\noindent \bigskip +% Uncomment and edit the line below if this course has a web page. Note that this is a comment in TeX mode. +%See the course web page for information http://yoururl/yourcourse EOT -BEGIN_TEXT +#################################################### +# +# End of hardcopy only output. +# +#################################################### -$BBOLD WeBWorK assignment number $setNumber is due : $formattedDueDate. $EBOLD -$PAR -(This is early Friday morning, so it needs to be done THURSDAY night!) -Remember to get this done early! -$PAR -The \{ htmlLink(qq!http://www.math.rochester.edu/courses/143/home/!,"home page") \} -for the course contains the syllabus, grading policy, and other information. -$PAR -END_TEXT +#################################################### +# +# The items below are printed out only when set is displayed on screen +# +#################################################### +TEXT(MODES(TeX =>"",HTML=>EV3(<<'EOT'))); -################## -# EDIT BELOW HERE -################## -BEGIN_TEXT +$BBOLD WeBWorK Assignment \{ protect_underbar($setNumber) \} is due : $formattedDueDate. $EBOLD $PAR +Here's the list of +\{ htmlLink(qq!http://webwork.maa.org/wiki/Available_Functions!,"functions and symbols") \} + which WeBWorK understands. +$BR +EOT +#################################################### +# Uncomment and edit the lines below if this course has a web page. Note that this is comment in Perl mode. +# IMPORTANT: Make sure the EOT at the bottom is at the beginning of a line with no spaces preceeding it. +#TEXT(MODES(TeX =>"",HTML=>EV3(<<'EOT'))); +#See the course web page for information \{ htmlLink(qq!http://yoururl/yourcourse!,"your course name") \} +#EOT +#################################################### + +#################################################### +# +# End of screen only output. +# +#################################################### + +#################################################### +# +# Anything between the BEGIN_TEXT AND END_TEXT lines +# will be printed in both screen and hardcopy output +# +#################################################### - -$PAR -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. +TEXT($END_ONE_COLUMN); +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/conf/sql.conf.dist b/conf/sql.conf.dist deleted file mode 100644 index 60f716afe9..0000000000 --- a/conf/sql.conf.dist +++ /dev/null @@ -1,120 +0,0 @@ -#!perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# 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. -################################################################################ - -=head1 NAME - -sql.conf - defines database layout for use with an SQL server. - -=head1 SYNOPSIS - -In global.conf (or course.conf): - - include "conf/sql.conf"; - -=head1 DESCRIPTION - -The SQL database layout uses an SQL database server to store the tables of the -WeBWorK database. The SQL driver uses two SQL accounts, a read only and a -read-write account. The read-only account must have C, C, C, and C access. -The names and passwords of these accounts are given as parameters to each table -in the layout. - - usernameRO the name of the read-only account - usernameRW the name of the read-write account - passwordRO the password for the read-only account - passwordRW the password for the read-write account - -Don't confuse these accounts with the accounts of the users of a course. These -are system-wide accounts which allow WeBWorK to talk to the database server. - -FIXME: the way the SQL account information is handled should be changed. It -shouldn't be in the course environment. (We should do the same thing that we -did with the "SECRET" in WeBWorK::Constants. - -=cut - -my %sqlParams = ( - usernameRO => "webworkRead", - passwordRO => "zaqwsxcderfv", - usernameRW => "webworkWrite", - passwordRW => "qwerfdsazxcv", - debug => 0, -); - -%dbLayout = ( - password => { - record => "WeBWorK::DB::Record::Password", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, - permission => { - record => "WeBWorK::DB::Record::PermissionLevel", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, - key => { - record => "WeBWorK::DB::Record::Key", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { - %sqlParams, - tableOverride => "key_not_a_keyword", - fieldOverride => { key => "key_not_a_keyword" }, - }, - }, - user => { - record => "WeBWorK::DB::Record::User", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, - set => { - record => "WeBWorK::DB::Record::Set", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams, tableOverride => "set_not_a_keyword" }, - }, - set_user => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, - problem => { - record => "WeBWorK::DB::Record::Problem", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, - problem_user => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::SQL", - driver => "WeBWorK::DB::Driver::SQL", - source => "dbi:mysql:webwork_$courseName", - params => { %sqlParams }, - }, -); diff --git a/conf/templates/barebones.template b/conf/templates/barebones.template deleted file mode 100644 index f9f67f4bdc..0000000000 --- a/conf/templates/barebones.template +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - WeBWorK - <!--#title--> - - - - - - - - - - - - - - - - - - - -
-
-
- -
- - Report bugs -
- -
-
- - -
- - -
- - - -
- -
- - - -
- colspan="2" - - bgcolor="#ffcccc"> - - - colspan="2" - - > - -
- -
- - - - - -
-

- -

- - -
- -
-
- - diff --git a/conf/templates/classic.template b/conf/templates/classic.template deleted file mode 100644 index 755694e7c1..0000000000 --- a/conf/templates/classic.template +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - WeBWorK - <!--#title--> - - - - - bgcolor="#ffcccc"bgcolor="#efefef"> - - - - - - -
- - -

WeBWorK 2.0

-
- -
-
-

- - - - - - -
- - - -
- -
- - - -

Links

- - - -
- - diff --git a/conf/templates/dgage/gateway.template b/conf/templates/dgage/gateway.template new file mode 100755 index 0000000000..1281f1e645 --- /dev/null +++ b/conf/templates/dgage/gateway.template @@ -0,0 +1,100 @@ + + + + + + + +/css/math.css"/> + +/css/gateway.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ +
+
+
+ +
+ + + + + + + + + + + + +

+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ + +
+
+ + +
+ + + diff --git a/conf/templates/dgage/system.template b/conf/templates/dgage/system.template new file mode 100755 index 0000000000..39460df231 --- /dev/null +++ b/conf/templates/dgage/system.template @@ -0,0 +1,128 @@ + + + + + + + + +/css/dgage.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + + +
+
+ +
+ +
+ + + + + + + + + + +
+ + + + + + + + + + + +
+

+ Warning -- there may be something wrong with this question. Please inform your instructor + including the warning messages below. +

+ +
+ + + +
+ + + + +
+
+ +
+ + + +
+ +
+ + + +
+ + +
+
+ + diff --git a/conf/templates/math/gateway.template b/conf/templates/math/gateway.template new file mode 100644 index 0000000000..aa34b1dab5 --- /dev/null +++ b/conf/templates/math/gateway.template @@ -0,0 +1,100 @@ + + + + + + + +/css/math.css"/> + +/css/gateway.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ +
+
+
+ +
+ + + + + + + + + + + + +

+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ + +
+
+ + +
+ + + diff --git a/conf/templates/math/math.css b/conf/templates/math/math.css new file mode 100644 index 0000000000..ec0c946e31 --- /dev/null +++ b/conf/templates/math/math.css @@ -0,0 +1,504 @@ +/* WeBWorK Online Homework Delivery System + * Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ + * $CVSHeader: webwork2/htdocs/css/math.css,v 1.12 2008/03/04 15:43:44 sh002i 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. + */ + +/* --- Standard elements ---------------------------------------------------- */ + +body { + background: white; + color: black; + margin: .5em .5em 0 .5em; /* MSIE ignores bottom margin; Gecko doesn't */ + padding: 0; + font-family: Times, serif; + min-width: 25em; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + /* FIXME "You have no background-color with your color" */ + color: #225; + background: transparent; +} +h1 { font-size: 170%; } +h2 { font-size: 140%; padding-bottom: 0; margin-bottom: .5ex } +h3 { font-size: 120%; } +h4 { font-size: 110%; } +h5 { font-size: 105%; } +h6 { font-size: 105%; } + +a:link { color: #00e; background-color: inherit; } +a:visited { color: #808; background-color: inherit; } + +/* --- Miscellaneous classes ---------------------------------------------- */ + +/* show only to CSS-ignorant browsers */ +.for-broken-browsers { display: none } + +/* for hiding floats from Netscape 4.x */ +.float-left { float: left; } +.float-right { float: right; } + +/* --- Compound titles (class) ---------------------------------------------- */ + +/* WeBWorK is not using this, but it might be nice to have it around later */ + +/* "Split" title, with main heading left-aligned above a horizontal line, + * and subheading right-aligned below the line. Usage: + * + *

Alumni Newsletter

+ *

Spring '00

+ */ + +/*h1.compound { + border-bottom: 1px solid #559; + text-align: left; + padding: .5ex 0 0 0; + margin: 0; +} +h2.compound { float: right; margin: 0; padding: 0 }*/ + +/* --- Info box (class) ----------------------------------------------------- */ + +/* FIXME as a quick hack, the info() escape outputs a DIV with this class. + * don't let the placement of these styles fool you --
+ * is output by WeBWorK! */ + +/* Common style for a small box to hold supplemental info; typically this + * box will appear in a sidebar. Sample usage: + * + *
+ *

Announcements

+ *
    + *
  • foo
  • + *
+ * Older announcements... + *
+ */ + +.info-box { + border: 1px solid #559; + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + margin-bottom: 1ex; + margin-top: 0; + overflow: hidden; +} +.info-box h2, +.info-box h3, +.info-box h4, +.info-box h5, +.info-box h6 { + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + border-bottom: 1px solid #559; + font-size: 100%; + text-align: center; + background: #88d; + color: white; + margin: 0; + padding: 0 .5em 0 .5em; +} +.info-box h2 a, +.info-box h3 a, +.info-box h4 a, +.info-box h5 a, +.info-box h6 a { + color: #fff; + background: inherit; +} +.info-box ul, +.info-box ol { + margin: 1ex .5em 1ex 0; + padding-left: 3.5em; + font-size: 80%; +} +.info-box dl { + margin: 1ex .5em 1ex 1ex; + padding: 0; + font-size: 80%; +} +.info-box li, +.info-box dt { + margin: 0 0 .5ex 0; + padding: 0; + line-height: 2.2ex; +} +.info-box dt { font-weight: bold } +.info-box dd { + margin: 0 0 .5ex 1em; + padding: 0; + line-height: 2.2ex; +} +.info-box dd p { + margin-top: 0; +} +.info-box a.more { + float: right; + font-size: 80%; + font-style: italic; + margin-bottom: 1ex; + margin-right: .5em; +} + +.Message { +background-color:#ddd; +} +/* --- Fisheye view (id) ---------------------------------------------------- */ + +/* The "fisheye" view: a hierarchical view of the website to show the + * user where they are in the document hierarchy. Provides more "lateral" + * information than breadcrumbs, but not as much as a full sitemap. To + * appear within the #site-navigation div. Inherits many of its attributes + * from class info-box, and overrides some. + */ + +#mini-sitemap, +#fisheye { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + padding: 0 0 1ex 0; +} +#mini-sitemap a, +#fisheye a { + text-decoration: none; +} +#mini-sitemap a:hover, +#fisheye a:hover { + text-decoration: underline; +} +#mini-sitemap li, +#fisheye li { + line-height: 2.5ex; + margin: 0; +} +#mini-sitemap ul, +#fisheye ul { + font-size: 90%; + list-style-type: none; + margin: 0 .1em .3ex .3em; + padding: 0; +} +#mini-sitemap ul ul, +#fisheye ul ul { + font-size: 90%; + margin-left: 0; +} +#mini-sitemap ul li, +#fisheye ul li { + font-weight: bold; +} +#mini-sitemap ul ul ul, +#fisheye ul ul ul { + font-size: 95%; + margin-left: .3em; + padding-left: .5em; + border-left: 1px dotted gray; +} +#mini-sitemap ul ul li a.active, +#fisheye ul ul li a.active { + font-weight: bold; + color: black; + background-color: inherit; +} +#mini-sitemap ul ul ul li, +#fisheye ul ul ul li { + font-weight: normal; +} +#mini-sitemap ul ul ul ul, +#fisheye ul ul ul ul { + font-size: 90%; +} +#mini-sitemap ul ul ul ul ul, +#fisheye ul ul ul ul ul { + font-size: 90%; +} + +/* --- Common layout elements for documents using templates ----------------- */ + +/* The "masthead" of our document that tells users what site they're on. + * It contains a logo-ish title and some basic site navigation. The + * masthead should appear at the top of the page, but we're not positioning + * it absolutely because we can't know its height in advance. Therefore this + * element should be placed at the very top of the of our HTML document. + */ +#masthead { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + font-size: 100%; + margin: 0; + padding: 0; + color: #fff; + border: 1px solid #000; + background-color: #038; + /* [ww] we could have some sort of spider web image here. */ + /*background-image: url("/images/mandel-wide.jpg");*/ + background-repeat: repeat-x; + background-position: top left; +} +#masthead a { + /* FIXME "You have no background-color with your color" */ + color: #fff; + background: transparent; +} +/* [ww] since this is a logo and not text, we need borders that are the same + * width all the way around. after we find a nice background image, we can + * turn these back on. */ +/*#masthead #logo { padding: .5ex .2em .1ex .5em }*/ +#masthead #logo { padding: .5ex } +/* [ww] don't need these -- logo itself an image */ +/*#masthead #logo h1 { + background-image: none; + background-color: transparent; + font-size: 100%; + font-weight: normal; + padding: 0; + margin: 0; + white-space: nowrap; + line-height: 1.9ex; +}*/ +/*#masthead #logo h2 { + background-color: transparent; + background-image: none; + font-weight: bold; + font-size: 210%; + line-height: 1.9ex; + margin: 0; + padding: 0; +}*/ +/* [ww] instead of a search form, we want the loginstatus there */ +/*#masthead form {*/ +#masthead #loginstatus { + float: right; + padding: 0; + margin: 1ex .5em .1ex .1em; + font-size: smaller; +} +#masthead #loginstatus #Nav { + padding: 1ex; +} + +/* "big-wrapper" contains everything other than the masthead. It's merely + * a relatively positioned div that allows us to use absolute positioning + * on elements within it -- and because it's relatively positioned, + * absolutely positioned objects *stay* within it. + */ +#big-wrapper { + position: relative; + top: 1ex; + width: 100%; + min-width: 18em; + margin: 0; + border: 0; +} + +/* + * A simple list of "breadcrumbs" showing a path of links from the root of + * the site's hierarchy to our present location. We are not positioning + * this element absolutely, because we don't know in advance how tall it + * will be, and we might want to place content under it. So when coding + * our HTML document, we'll probably want to include this element right + * before the main content. + */ + +#breadcrumbs { + margin-left: 10.4em; + margin-right: 0; + padding: 0 .4em; + border: 1px solid #559; + background: #88d; + color: #fff; + text-align: left; + font-size: 100%; + font-family: "Trebuchet MS", "Arial", sans-serif; +} +#breadcrumbs a { + font-size: 100%; + white-space: nowrap; + background-color: inherit; + color: #fff; + text-decoration: none; +} +#breadcrumbs a.active { font-weight: bold; } +#breadcrumbs a:hover { text-decoration: underline; } + + +/* For the more CSS-compliant browsers, we'd like to provide site-wide + * navigation links (e.g., a mini site map) to appear in a column along + * the left side of the page, just below the masthead. This column is + * absolutely positioned, so that ideally we should be able to include its + * contents anywhere within the body of our HTML documents. However, we + * probably want to include this data at the END of our documents -- after + * the main content -- so that it doesn't interfere with document flow in + * browsers that don't understand CSS (e.g., lynx) -- or in browsers for + * which we've disabled CSS via some hack (e.g. Netscape Navigator 4.x). + * + * We consider this meta-information to be non-essential; it is NOT part of + * the content, and may not be displayed in some circumstances. + */ +#site-navigation { + position: absolute; + left: 0; + top: 0; + margin: 0; + padding: 0; + width: 9.8em; +} + +/* The primary information content of the document, excluding masthead and + * site navigation. We want to leave a wide left margin to prevent overlap + * with our site map, which will be displayed on the left-hand side of the + * screen. + */ +#content { + margin: .5em 0 0 10.4em; + padding: 0 0 0 0; + font-family: "Times", serif; + /* border-left: 1px dotted #bbf; */ +} +#content h1 { margin: .4ex 0; } /* for crappy MSIE */ + + +#footer { + /* white-space: nowrap; */ + clear: both; + /* border-top: 1px solid #559; */ + margin: 0 .5em .2ex 10.4em; + padding: 0; + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; +} +#copyright { font-size: 75%; margin: 0;} +#last-modified { + clear: both; + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + font-size: 75%; + background-color: inherit; + color: #444; + margin: 1ex 0 0 0; + padding: 0; + border-bottom: 1px solid #559; +} + +/* --- WeBWorK classes ------------------------------------------------------ */ + +/* These classes are emitted by WeBWorK code and should appear in ANY WeBWorK + * template. they need not be implemented the same way in each template though. + * (These are mostly copied from ur.css right atm.) + */ + +/* the info escape emits a DIV with this id. (note that the same DIV has class + * "info-box" which is given above in the "template styles" section. Regardless, + * it is emitted by WW code in ProblemSet.pl (not in system.template) ! */ +#InfoPanel { + font-size: smaller; + float: right; + width: 40%; + overflow: auto; + margin-right: -1px; + background-color: #fff; +} + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/*ul.LinksMenu { list-style: none; margin-left: 0; padding-left: 0; }*/ +/*ul.LinksMenu ul { list-style: none; margin-left: 0.5em; padding-left: 0; }*/ + +/* background styles for success and failure messages */ +div.WarningMessage { background-color: #ffcccc; padding: 3px 3px 3px 3px; } +div.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +div.ResultsWithError { color: #C33; background-color: inherit; } /* red */ +div.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { font-size: small } + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; + /*width: 15ex;*/ /* this was erroniously commented out with "#" */ + padding: 2px 5px 2px 5px; + color: inherit; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { + text-align: left; + padding: 2px 5px 2px 5px; + width: auto; +} +.attemptResultsSummary { font-style: italic; } +.parsehilight { color: inherit; background-color: yellow; } + +/* the problem TEXT itself does in this box */ +/* we used to make this a grey box, but surprise, MSIE is bug-ridden. */ +div.problem { } + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10px; + border-style: solid; + border-width: 3px; + border-color: #DD0000; + color: inherit; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for visible and hidden sets */ +font.visible { font-style: normal; font-weight: normal; color: #000000; background-color: inherit; } /* black */ +font.hidden { font-style: italic; font-weight: normal; color: #aaaaaa; background-color: inherit; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { font-style: italic; color: #F60; background-color: inherit; } + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; background-color: inherit; } +.Enrolled { font-weight: normal; color: black; background-color: inherit; } +.Drop { font-style: italic; color: gray; background-color: inherit; } diff --git a/conf/templates/math/system.template b/conf/templates/math/system.template new file mode 100644 index 0000000000..b193a1e038 --- /dev/null +++ b/conf/templates/math/system.template @@ -0,0 +1,132 @@ + + + + + + + + +/css/math.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + + +
+
+ +
+ +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+

+ Warning -- there may be something wrong with this question. Please inform your instructor + including the warning messages below. +

+ +
+ + + +
+ + + + +
+
+ +
+ + + +
+ +
+ + + +
+ + +
+
+ + diff --git a/conf/templates/math2/gateway.template b/conf/templates/math2/gateway.template new file mode 100644 index 0000000000..665663ea62 --- /dev/null +++ b/conf/templates/math2/gateway.template @@ -0,0 +1,100 @@ + + + + + + + +/css/math.css"/> + +/css/gateway.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ +
+
+
+ +
+ + + + + + + + + + + + +

+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ + +
+
+ + +
+ + + diff --git a/conf/templates/math2/math2.css b/conf/templates/math2/math2.css new file mode 100644 index 0000000000..255417681f --- /dev/null +++ b/conf/templates/math2/math2.css @@ -0,0 +1,727 @@ +/* WeBWorK Online Homework Delivery System + * Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ + * $CVSHeader: webwork2/conf/templates/math2/math2.css,v 1.1.2.1 2008/06/24 17:17:36 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. + */ + +/* --- Standard elements ---------------------------------------------------- */ + +body { + background: white; + color: black; + margin: .5em .5em 0 .5em; /* MSIE ignores bottom margin; Gecko doesn't */ + padding: 0; + font-family: "Times", serif; + min-width: 25em; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + /* FIXME "You have no background-color with your color" */ + color: #225; + background: transparent; +} +h1 { font-size: 170%; } +h2 { font-size: 140%; padding-bottom: 0; margin-bottom: .5ex } +h3 { font-size: 120%; } +h4 { font-size: 110%; } +h5 { font-size: 105%; } +h6 { font-size: 105%; } + +a:link { color: #00e; background-color: inherit; } +a:visited { color: #808; background-color: inherit; } + +/* --- Miscellaneous classes ---------------------------------------------- */ + +/* show only to CSS-ignorant browsers */ +.for-broken-browsers { display: none } + +/* for hiding floats from Netscape 4.x */ +.float-left { float: left; } +.float-right { float: right; } + +/* --- Compound titles (class) ---------------------------------------------- */ + +/* WeBWorK is not using this, but it might be nice to have it around later */ + +/* "Split" title, with main heading left-aligned above a horizontal line, + * and subheading right-aligned below the line. Usage: + * + *

Alumni Newsletter

+ *

Spring '00

+ */ + +/*h1.compound { + border-bottom: 1px solid #559; + text-align: left; + padding: .5ex 0 0 0; + margin: 0; +} +h2.compound { float: right; margin: 0; padding: 0 }*/ + +/* --- Info box (class) ----------------------------------------------------- */ + +/* FIXME as a quick hack, the info() escape outputs a DIV with this class. + * don't let the placement of these styles fool you --
+ * is output by WeBWorK! */ + +/* Common style for a small box to hold supplemental info; typically this + * box will appear in a sidebar. Sample usage: + * + *
+ *

Announcements

+ *
    + *
  • foo
  • + *
+ * Older announcements... + *
+ */ + +.info-box { + border: 1px solid #559; + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + margin-bottom: 1ex; + margin-top: 0; + overflow: hidden; +} +.info-box h2, +.info-box h3, +.info-box h4, +.info-box h5, +.info-box h6 { + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + border-bottom: 1px solid #559; + font-size: 100%; + text-align: center; + background: #88d; + color: white; + margin: 0; + padding: 0 .5em 0 .5em; +} +.info-box h2 a, +.info-box h3 a, +.info-box h4 a, +.info-box h5 a, +.info-box h6 a { + color: #fff; + background: inherit; +} +.info-box ul, +.info-box ol { + margin: 1ex .5em 1ex 0; + padding-left: 3.5em; + font-size: 80%; +} +.info-box dl { + margin: 1ex .5em 1ex 1ex; + padding: 0; + font-size: 80%; +} +.info-box li, +.info-box dt { + margin: 0 0 .5ex 0; + padding: 0; + line-height: 2.2ex; +} +.info-box dt { font-weight: bold } +.info-box dd { + margin: 0 0 .5ex 1em; + padding: 0; + line-height: 2.2ex; +} +.info-box dd p { + margin-top: 0; +} +.info-box a.more { + float: right; + font-size: 80%; + font-style: italic; + margin-bottom: 1ex; + margin-right: .5em; +} + +.Message { +background-color:#ddd; +clear: both; +} +/* --- Fisheye view (id) ---------------------------------------------------- */ + +/* The "fisheye" view: a hierarchical view of the website to show the + * user where they are in the document hierarchy. Provides more "lateral" + * information than breadcrumbs, but not as much as a full sitemap. To + * appear within the #site-navigation div. Inherits many of its attributes + * from class info-box, and overrides some. + */ + +#mini-sitemap, +#fisheye { + font-family: "Trebuchet MS", "Arial", "Helvetica", serif; + padding: 0 0 1ex 0; +} +#mini-sitemap a, +#fisheye a { + text-decoration: none; +} +#mini-sitemap a:hover, +#fisheye a:hover { + text-decoration: underline; +} +#mini-sitemap li, +#fisheye li { + line-height: 2.5ex; + margin: 0; +} +#mini-sitemap ul, +#fisheye ul { + font-size: 90%; + list-style-type: none; + margin: 0 .1em .3ex .3em; + padding: 0; +} +#mini-sitemap ul ul, +#fisheye ul ul { + font-size: 90%; + margin-left: 0; +} +#mini-sitemap ul li, +#fisheye ul li { + font-weight: bold; +} +#mini-sitemap ul ul ul, +#fisheye ul ul ul { + font-size: 95%; + margin-left: .3em; + padding-left: .5em; + border-left: 1px dotted gray; +} +#mini-sitemap ul ul li a.active, +#fisheye ul ul li a.active { + font-weight: bold; + color: black; + background-color: inherit; +} +#mini-sitemap ul ul ul li, +#fisheye ul ul ul li { + font-weight: normal; +} +#mini-sitemap ul ul ul ul, +#fisheye ul ul ul ul { + font-size: 90%; +} +#mini-sitemap ul ul ul ul ul, +#fisheye ul ul ul ul ul { + font-size: 90%; +} + +/* --- Common layout elements for documents using templates ----------------- */ + +/* The "masthead" of our document that tells users what site they're on. + * It contains a logo-ish title and some basic site navigation. The + * masthead should appear at the top of the page, but we're not positioning + * it absolutely because we can't know its height in advance. Therefore this + * element should be placed at the very top of the of our HTML document. + */ +#masthead { + font-family: "Trebuchet MS", "Arial", "Helvetica", serif; + font-size: 100%; + margin: 0; + padding: 0; + color: #fff; + border: 1px solid #000; + background-color: #038; + /* [ww] we could have some sort of spider web image here. */ + /*background-image: url("/images/mandel-wide.jpg");*/ + background-repeat: repeat-x; + background-position: top left; +} +#masthead a { + /* FIXME "You have no background-color with your color" */ + color: #fff; + background: transparent; +} +/* [ww] since this is a logo and not text, we need borders that are the same + * width all the way around. after we find a nice background image, we can + * turn these back on. */ +/*#masthead #logo { padding: .5ex .2em .1ex .5em }*/ +#masthead #logo { padding: 1ex } +/* [ww] don't need these -- logo itself an image */ +/*#masthead #logo h1 { + background-image: none; + background-color: transparent; + font-size: 100%; + font-weight: normal; + padding: 0; + margin: 0; + white-space: nowrap; + line-height: 1.9ex; +}*/ +/*#masthead #logo h2 { + background-color: transparent; + background-image: none; + font-weight: bold; + font-size: 210%; + line-height: 1.9ex; + margin: 0; + padding: 0; +}*/ +/* [ww] instead of a search form, we want the loginstatus there */ +/*#masthead form {*/ +#masthead #loginstatus { + float: right; + padding: 0; + margin: 1ex .5em .1ex .1em; + font-size: smaller; +} +#masthead #loginstatus #Nav { + padding: 1ex; +} + +/* "big-wrapper" contains everything other than the masthead. It's merely + * a relatively positioned div that allows us to use absolute positioning + * on elements within it -- and because it's relatively positioned, + * absolutely positioned objects *stay* within it. + */ +#big-wrapper { + position: relative; + top: 1ex; + width: 100%; + min-width: 18em; + margin: 0; + border: 0; +} + +/* + * A simple list of "breadcrumbs" showing a path of links from the root of + * the site's hierarchy to our present location. We are not positioning + * this element absolutely, because we don't know in advance how tall it + * will be, and we might want to place content under it. So when coding + * our HTML document, we'll probably want to include this element right + * before the main content. + */ + +#breadcrumbs { + margin-left: 10.4em; + margin-right: 0; + padding: 0 .4em; + border: 1px solid #559; + background: #88d; + color: #fff; + text-align: left; + font-size: 100%; + font-family: "Trebuchet MS", "Arial", serif; +} +#breadcrumbs a { + font-size: 100%; + white-space: nowrap; + background-color: inherit; + color: #fff; + text-decoration: none; +} +#breadcrumbs a.active { font-weight: bold; } +#breadcrumbs a:hover { text-decoration: underline; } + + +/* For the more CSS-compliant browsers, we'd like to provide site-wide + * navigation links (e.g., a mini site map) to appear in a column along + * the left side of the page, just below the masthead. This column is + * absolutely positioned, so that ideally we should be able to include its + * contents anywhere within the body of our HTML documents. However, we + * probably want to include this data at the END of our documents -- after + * the main content -- so that it doesn't interfere with document flow in + * browsers that don't understand CSS (e.g., lynx) -- or in browsers for + * which we've disabled CSS via some hack (e.g. Netscape Navigator 4.x). + * + * We consider this meta-information to be non-essential; it is NOT part of + * the content, and may not be displayed in some circumstances. + */ +#site-navigation { + position: absolute; + left: 0; + top: 0; + margin: 0; + padding: 0; + width: 9.8em; +} + +/* The primary information content of the document, excluding masthead and + * site navigation. We want to leave a wide left margin to prevent overlap + * with our site map, which will be displayed on the left-hand side of the + * screen. + */ +#content { + margin: .5em 0 0 10.4em; + padding: 0 0 0 0; + font-family: "Times", serif; + /* border-left: 1px dotted #bbf; */ +} +#content h1 { margin: .4ex 0; } /* for crappy MSIE */ + + +#footer { + /* white-space: nowrap; */ + clear: both; + /* border-top: 1px solid #559; */ + margin: 0 .5em .2ex 10.4em; + padding: 0; + font-family: "Trebuchet MS", "Arial", "Helvetica", serif; +} +#copyright { font-size: 75%; margin: 0;} +#last-modified { + clear: both; + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + font-size: 75%; + background-color: inherit; + color: #444; + margin: 1ex 0 0 0; + padding: 0; + border-bottom: 1px solid #559; +} + +/* --- WeBWorK classes ------------------------------------------------------ */ + +/* These classes are emitted by WeBWorK code and should appear in ANY WeBWorK + * template. they need not be implemented the same way in each template though. + * (These are mostly copied from ur.css right atm.) + */ + +/* the info escape emits a DIV with this id. (note that the same DIV has class + * "info-box" which is given above in the "template styles" section. Regardless, + * it is emitted by WW code in ProblemSet.pl (not in system.template) ! */ +#InfoPanel { + font-size:100%; + float: right; + width: 100%; + max-width:400px; + overflow: auto; + margin-right: -1px; + background-color: #fff; +} +#InfoPanel ol { + font-size:100%; +} + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/*ul.LinksMenu { list-style: none; margin-left: 0; padding-left: 0; }*/ +/*ul.LinksMenu ul { list-style: none; margin-left: 0.5em; padding-left: 0; }*/ + +/* background styles for success and failure messages */ +div.WarningMessage { background-color: #ffcccc; padding: 3px 3px 3px 3px; } +div.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +div.ResultsWithError { color: #C33; background-color: inherit; } /* red */ +div.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +label.WarningMessage { background-color: #FF9494; padding: 3px 3px 3px 3px; } +label.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +label.ResultsWithError { color: #FF9494; background-color: inherit; } /* error red, from http://www.colourlovers.com/color/FF9494/error_red*/ +label.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +span.WarningMessage { background-color: #FF9494; padding: 3px 3px 3px 3px; } +span.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +span.ResultsWithError { color: #C33; background-color: inherit; } /* error red, from http://www.colourlovers.com/color/FF9494/error_red*/ +span.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +span.correct { color: inherit; background-color: #8F8; } /* green */ +span.incorrect { color: #FF9494; background-color: inherit; } /* red */ +input.correct { color:inherit; background-color: #8F8; } /* green */ +input.incorrect { color;inherit; background-color: #FF9494; } /* red */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { font-size: small } + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; + /*width: 15ex;*/ /* this was erroniously commented out with "#" */ + padding: 2px 5px 2px 5px; + color: inherit; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { + text-align: left; + padding: 2px 5px 2px 5px; + width: auto; +} +.attemptResultsSummary { font-style: italic; } +.parsehilight { color: inherit; background-color: yellow; } + +/* the problem TEXT itself goes in this box */ +/* we used to make this a grey box, but surprise, MSIE is bug-ridden. */ +div.problem { } + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10px; + border-style: solid; + border-width: 3px; + border-color: #DD0000; + color: inherit; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for visible and hidden sets */ +font.visible { font-style: normal; font-weight: normal; color: #000000; background-color: inherit; } /* black */ +font.hidden { font-style: italic; font-weight: normal; color: #aaaaaa; background-color: inherit; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { font-style: italic; color: #F60; background-color: inherit; } + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; background-color: inherit; } +.Enrolled { font-weight: normal; color: black; background-color: inherit; } +.Drop { font-style: italic; color: gray; background-color: inherit; } + +/*Styles for the login form*/ + +#login_form input { + margin-top: 7px; +} + +#login_form label{ + margin-right: 2px; +} + +#login_form #pswd_label{ + margin-right: 4px; +} + +#login_form #rememberme{ + margin-left: 75px; +} + +/*Styles for the edit forms, found on the homework sets editor page*/ + +.edit_form input,select{ + margin-top: 5px; +} + +.edit_form label, .edit_form span{ + margin-left: 40px; +} + +.edit_form label.radio_label{ + margin-left: 0px; +} + +.edit_form span#filter_err_msg{ + display: none; +} + +/*Styles for the classlist editor page*/ + +.set_table{ + margin-right: 5px; +} + +.set_table td{ + padding-right: 5px; +} + +.set_table th{ + text-align: center; + padding-right: 5px; + padding-left: 5px; +} + +.set_table label{ + margin-left: 0px; +} + +/*General styles, for elements such as table captions input buttons, columns, etc.*/ + +table caption{ + font-weight: bold; + text-decoration: underline; +} + +.button_input{ + margin-right: 5px; + margin-left: 5px; +} + +.column { + float: left; + width: 50%; + height: 22%; + overflow: hidden; + margin-top: 10px; +} + +.info-wrapper{ + float: right; + min-height: 100px; + width: 40%; + overflow: hidden; + margin-left: 5px; +} + +a.nav_button{ + color: black; + background-color: #DDDDDD; + text-decoration: none; + text-align: center; + font-size: 15px; + padding: 1px 6px 1px 6px; + margin: 2px; + border-style: outset; +} + +a.nav_button:hover{ + background-color: #EEEEEE; + border-color: #00ADEE; +} + +a.nav_button:active{ + border-style: inset; +} + +span.gray_button{ + color: black; + background-color: #DDDDDD; + text-decoration: none; + text-align: center; + font-size: 15px; + padding: 1px 6px 1px 6px; + margin: 2px; + border-style: outset; + opacity: 0.3; +} + +#none{ + /*Please do not do anything with elements with id "none"*/ +} + +/*Styles for the PGProblemEditor Page*/ + +#editor{ + background-color: #FFFFFF; +} + +#editor #source_and_options{ + float: left; + width: 50%; +} + +#editor #problemContents_label{ + text-align: center; + font-weight: bold; + text-decoration: underline; + margin-left: 30%; +} + +#editor #problemContents_id{ + width: 100%; + height: 50%; +} + +#editor .editor_form{ + margin-left: 40px; +} + +#editor #form_div{ + /*float: left;*/ + width: 100%; +} + +/*Styles specifically for the problem viewer*/ + +#problem_viewer_form{ + margin-left: 10px; + float: left; + width: 45%; + height: 100%; + border-left: solid; + border-width: 1px; +} + +#problem_viewer_form>h3{ + margin-left: 37%; +} + +#problem_viewer_form #viewer_span{ + margin-left: 10%; +} + +#problem_viewer_form #reload_button{ + margin-left: 20%; + margin-top: 5%; +} + +#problem_viewer_form #problem_viewer{ + width: 60%; + background-color: #E8E8E8; + margin-left: 20%; + margin-top: 5%; + padding: 1px; +} + +#problem_viewer_form #problem_viewer_content{ + border-style: solid; + padding: 5%; + border-width: 1px; +} + +#problem_viewer_form #viewer_note{ + margin-top: 10%; + margin-left: 10%; + margin-right: 10%; +} + +/*These are styles for the tables of problem sets*/ + +table.problem_set_table th{ + padding-right: 30px; +} + +table.problem_set_table td{ + padding-right: 30px; +} \ No newline at end of file diff --git a/conf/templates/math2/system.template b/conf/templates/math2/system.template new file mode 100644 index 0000000000..8e0250716d --- /dev/null +++ b/conf/templates/math2/system.template @@ -0,0 +1,213 @@ + + + + + + + + +/images/favicon.ico"/> +/css/math2.css"/> + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ + + + + +     + + + + + + + + + + +
+ + + + + + + + +
+ + + + +
+

+ Warning -- there may be something wrong with this question. Please inform your instructor + including the warning messages below. +

+
+ + + +
+ + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + +
+ + +

+ + + + + + +

+ + +
+ +
+ + + + + + +
+ + + +
+ +
+ + + + + +
+ + + + + + +
+ + + + +
+
+ +
+ + + +
+ +
+ + +
+ + + +
+
+ + + diff --git a/conf/templates/math3/gateway.template b/conf/templates/math3/gateway.template new file mode 100644 index 0000000000..36c1af18e5 --- /dev/null +++ b/conf/templates/math3/gateway.template @@ -0,0 +1,100 @@ + + + + + + + +/css/math.css"/> + +/css/gateway.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ +
+
+
+ +
+ + + + + + + + + + + + +

+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ + +
+
+ + +
+ + + diff --git a/conf/templates/math3/math3.css b/conf/templates/math3/math3.css new file mode 100644 index 0000000000..3b14bade45 --- /dev/null +++ b/conf/templates/math3/math3.css @@ -0,0 +1,755 @@ +/* WeBWorK Online Homework Delivery System + * Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ + * $CVSHeader: webwork2/conf/templates/math2/math2.css,v 1.1.2.1 2008/06/24 17:17:36 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. + */ + +/* --- Standard elements ---------------------------------------------------- */ + +body { + background: white; + color: black; + margin: .5em .5em 0 .5em; /* MSIE ignores bottom margin; Gecko doesn't */ + padding: 0; + font-family: "Arial", sans-serif; + min-width: 25em; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + /* FIXME "You have no background-color with your color" */ + color: #225; + background: transparent; +} +h1 { font-size: 170%; } +h2 { font-size: 140%; padding-bottom: 0; margin-bottom: .5ex } +h3 { font-size: 120%; } +h4 { font-size: 110%; } +h5 { font-size: 105%; } +h6 { font-size: 105%; } + +a:link { color: #00e; background-color: inherit; } +a:visited { color: #808; background-color: inherit; } + +/* --- Miscellaneous classes ---------------------------------------------- */ + +/* show only to CSS-ignorant browsers */ +.for-broken-browsers { display: none } + +/* for hiding floats from Netscape 4.x */ +.float-left { float: left; } +.float-right { float: right; } + +/* --- Compound titles (class) ---------------------------------------------- */ + +/* WeBWorK is not using this, but it might be nice to have it around later */ + +/* "Split" title, with main heading left-aligned above a horizontal line, + * and subheading right-aligned below the line. Usage: + * + *

Alumni Newsletter

+ *

Spring '00

+ */ + +/*h1.compound { + border-bottom: 1px solid #559; + text-align: left; + padding: .5ex 0 0 0; + margin: 0; +} +h2.compound { float: right; margin: 0; padding: 0 }*/ + +/* --- Info box (class) ----------------------------------------------------- */ + +/* FIXME as a quick hack, the info() escape outputs a DIV with this class. + * don't let the placement of these styles fool you --
+ * is output by WeBWorK! */ + +/* Common style for a small box to hold supplemental info; typically this + * box will appear in a sidebar. Sample usage: + * + *
+ *

Announcements

+ *
    + *
  • foo
  • + *
+ * Older announcements... + *
+ */ + +.info-box { + border: 1px solid #559; + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + margin-bottom: 1ex; + margin-top: 0; + overflow: hidden; +} +.info-box h2, +.info-box h3, +.info-box h4, +.info-box h5, +.info-box h6 { + /* FIXME: these aren't valid CSS, but they sure look nice :-P */ + border-radius-topright: 1.5ex; border-radius-topleft: 1.5ex; + -moz-border-radius-topright: 1.5ex; -moz-border-radius-topleft: 1.5ex; + border-bottom: 1px solid #559; + font-size: 100%; + text-align: center; + background: #88d; + color: white; + margin: 0; + padding: 0 .5em 0 .5em; +} +.info-box h2 a, +.info-box h3 a, +.info-box h4 a, +.info-box h5 a, +.info-box h6 a { + color: #fff; + background: inherit; +} +.info-box ul, +.info-box ol { + margin: 1ex .5em 1ex 0; + padding-left: 3.5em; + font-size: 80%; +} +.info-box dl { + margin: 1ex .5em 1ex 1ex; + padding: 0; + font-size: 80%; +} +.info-box li, +.info-box dt { + margin: 0 0 .5ex 0; + padding: 0; + line-height: 2.2ex; +} +.info-box dt { font-weight: bold } +.info-box dd { + margin: 0 0 .5ex 1em; + padding: 0; + line-height: 2.2ex; +} +.info-box dd p { + margin-top: 0; +} +.info-box a.more { + float: right; + font-size: 80%; + font-style: italic; + margin-bottom: 1ex; + margin-right: .5em; +} + +.Message { +background-color:#ddd; +clear: both; +} +/* --- Fisheye view (id) ---------------------------------------------------- */ + +/* The "fisheye" view: a hierarchical view of the website to show the + * user where they are in the document hierarchy. Provides more "lateral" + * information than breadcrumbs, but not as much as a full sitemap. To + * appear within the #site-navigation div. Inherits many of its attributes + * from class info-box, and overrides some. + */ + +#mini-sitemap, +#fisheye { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + padding: 0 0 1ex 0; +} +#mini-sitemap a, +#fisheye a { + text-decoration: none; +} +#mini-sitemap a:hover, +#fisheye a:hover { + text-decoration: underline; +} +#mini-sitemap li, +#fisheye li { + line-height: 2.5ex; + margin: 0; +} +#mini-sitemap ul, +#fisheye ul { + font-size: 90%; + list-style-type: none; + margin: 0 .1em .3ex .3em; + padding: 0; +} +#mini-sitemap ul ul, +#fisheye ul ul { + font-size: 90%; + margin-left: 0; +} +#mini-sitemap ul li, +#fisheye ul li { + font-weight: bold; +} +#mini-sitemap ul ul ul, +#fisheye ul ul ul { + font-size: 95%; + margin-left: .3em; + padding-left: .5em; + border-left: 1px dotted gray; +} +#mini-sitemap ul ul li a.active, +#fisheye ul ul li a.active { + font-weight: bold; + color: black; + background-color: inherit; +} +#mini-sitemap ul ul ul li, +#fisheye ul ul ul li { + font-weight: normal; +} +#mini-sitemap ul ul ul ul, +#fisheye ul ul ul ul { + font-size: 90%; +} +#mini-sitemap ul ul ul ul ul, +#fisheye ul ul ul ul ul { + font-size: 90%; +} + +/* --- Common layout elements for documents using templates ----------------- */ + +/* The "masthead" of our document that tells users what site they're on. + * It contains a logo-ish title and some basic site navigation. The + * masthead should appear at the top of the page, but we're not positioning + * it absolutely because we can't know its height in advance. Therefore this + * element should be placed at the very top of the of our HTML document. + */ +#masthead { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + font-size: 100%; + margin: 0; + padding: 0; + color: #fff; + border: 1px solid #000; + background-color: #038; + /* [ww] we could have some sort of spider web image here. */ + /*background-image: url("/images/mandel-wide.jpg");*/ + background-repeat: repeat-x; + background-position: top left; +} +#masthead a { + /* FIXME "You have no background-color with your color" */ + color: #fff; + background: transparent; +} +/* [ww] since this is a logo and not text, we need borders that are the same + * width all the way around. after we find a nice background image, we can + * turn these back on. */ +/*#masthead #logo { padding: .5ex .2em .1ex .5em }*/ +#masthead #logo { padding: 1ex } +/* [ww] don't need these -- logo itself an image */ +/*#masthead #logo h1 { + background-image: none; + background-color: transparent; + font-size: 100%; + font-weight: normal; + padding: 0; + margin: 0; + white-space: nowrap; + line-height: 1.9ex; +}*/ +/*#masthead #logo h2 { + background-color: transparent; + background-image: none; + font-weight: bold; + font-size: 210%; + line-height: 1.9ex; + margin: 0; + padding: 0; +}*/ +/* [ww] instead of a search form, we want the loginstatus there */ +/*#masthead form {*/ + +/*Absolute positioning*/ + +#loginstatus { + color: white; + position: absolute; + top: -75px; + left: 1170px; + width: 150px; + float: right; + text-align: center; + padding: 0; + margin: 1ex .5em .1ex .1em; + font-size: smaller; +} + +/*Relative Positioning*/ + +/*#loginstatus { + color: white; + position: relative; + top: -140px; + left: 470px; + width: 150px; + text-align: center; + padding: 0; + margin: 1ex .5em .1ex .1em; + font-size: smaller; +}*/ + + +#loginstatus a{ + color: white; +} + +#loginstatus #Nav { + padding: 1ex; +} + +/* "big-wrapper" contains everything other than the masthead. It's merely + * a relatively positioned div that allows us to use absolute positioning + * on elements within it -- and because it's relatively positioned, + * absolutely positioned objects *stay* within it. + */ +#big-wrapper { + position: relative; + top: 1ex; + width: 100%; + min-width: 18em; + margin: 0; + border: 0; +} + +/* + * A simple list of "breadcrumbs" showing a path of links from the root of + * the site's hierarchy to our present location. We are not positioning + * this element absolutely, because we don't know in advance how tall it + * will be, and we might want to place content under it. So when coding + * our HTML document, we'll probably want to include this element right + * before the main content. + */ + +#breadcrumbs { + margin-left: 10.4em; + margin-right: 0; + padding: 0 .4em; + border: 1px solid #559; + background: #88d; + color: #fff; + text-align: left; + font-size: 100%; + font-family: "Trebuchet MS", "Arial", sans-serif; +} +#breadcrumbs a { + font-size: 100%; + white-space: nowrap; + background-color: inherit; + color: #fff; + text-decoration: none; +} +#breadcrumbs a.active { font-weight: bold; } +#breadcrumbs a:hover { text-decoration: underline; } + + +/* For the more CSS-compliant browsers, we'd like to provide site-wide + * navigation links (e.g., a mini site map) to appear in a column along + * the left side of the page, just below the masthead. This column is + * absolutely positioned, so that ideally we should be able to include its + * contents anywhere within the body of our HTML documents. However, we + * probably want to include this data at the END of our documents -- after + * the main content -- so that it doesn't interfere with document flow in + * browsers that don't understand CSS (e.g., lynx) -- or in browsers for + * which we've disabled CSS via some hack (e.g. Netscape Navigator 4.x). + * + * We consider this meta-information to be non-essential; it is NOT part of + * the content, and may not be displayed in some circumstances. + */ +#site-navigation { + position: absolute; + left: 0; + top: 0; + margin: 0; + padding: 0; + width: 9.8em; +} + +/* The primary information content of the document, excluding masthead and + * site navigation. We want to leave a wide left margin to prevent overlap + * with our site map, which will be displayed on the left-hand side of the + * screen. + */ +#content { + margin: .5em 0 0 10.4em; + padding: 0 0 0 0; + font-family: "Arial", sans-serif; + /* border-left: 1px dotted #bbf; */ +} +#content h1 { margin: .4ex 0; } /* for crappy MSIE */ + + +#footer { + /* white-space: nowrap; */ + clear: both; + /* border-top: 1px solid #559; */ + margin: 0 .5em .2ex 10.4em; + padding: 0; + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; +} +#copyright { font-size: 75%; margin: 0;} +#last-modified { + clear: both; + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + font-size: 75%; + background-color: inherit; + color: #444; + margin: 1ex 0 0 0; + padding: 0; + border-bottom: 1px solid #559; +} + +/* --- WeBWorK classes ------------------------------------------------------ */ + +/* These classes are emitted by WeBWorK code and should appear in ANY WeBWorK + * template. they need not be implemented the same way in each template though. + * (These are mostly copied from ur.css right atm.) + */ + +/* the info escape emits a DIV with this id. (note that the same DIV has class + * "info-box" which is given above in the "template styles" section. Regardless, + * it is emitted by WW code in ProblemSet.pl (not in system.template) ! */ +#InfoPanel { + font-size:100%; + float: right; + max-width: 400px; + overflow: auto; + /*margin-right: -1px;*/ + background-color: #fff; +} +#InfoPanel ol { + font-size:100%; +} + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/*ul.LinksMenu { list-style: none; margin-left: 0; padding-left: 0; }*/ +/*ul.LinksMenu ul { list-style: none; margin-left: 0.5em; padding-left: 0; }*/ + +/* background styles for success and failure messages */ +div.WarningMessage { background-color: #FF9494; padding: 3px 3px 3px 3px; } +div.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +div.ResultsWithError { color: #FF9494; background-color: inherit; } /* error red, from http://www.colourlovers.com/color/FF9494/error_red*/ +div.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +label.WarningMessage { background-color: #FF9494; padding: 3px 3px 3px 3px; } +label.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +label.ResultsWithError { color: #FF9494; background-color: inherit; } /* error red, from http://www.colourlovers.com/color/FF9494/error_red*/ +label.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +span.WarningMessage { background-color: #FF9494; padding: 3px 3px 3px 3px; } +span.ResultsWithoutError { color: inherit; background-color: #8F8; } /* green */ +span.ResultsWithError { color: #FF9494; background-color: inherit; } /* error red, from http://www.colourlovers.com/color/FF9494/error_red*/ +span.ResultsAlert { color: #F60; background-color: inherit; } /* orange */ +span.correct { color: inherit; background-color: #8F8; } /* green */ +span.incorrect { color: #FF9494; background-color: inherit; } /* red */ +input.correct { color:inherit; background-color: #8F8; } /* green */ +input.incorrect { color;inherit; background-color: #FF9494; } /* red */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { font-size: small } + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; + /*width: 15ex;*/ /* this was erroniously commented out with "#" */ + padding: 2px 5px 2px 5px; + color: inherit; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { + text-align: left; + padding: 2px 5px 2px 5px; + width: auto; +} +.attemptResultsSummary { font-style: italic; } +.parsehilight { color: inherit; background-color: yellow; } + +/* the problem TEXT itself goes in this box */ +/* we used to make this a grey box, but surprise, MSIE is bug-ridden. */ +div.problem { } + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10px; + border-style: solid; + border-width: 3px; + border-color: #DD0000; + color: inherit; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for visible and hidden sets */ +font.visible { font-style: normal; font-weight: normal; color: #000000; background-color: inherit; } /* black */ +font.hidden { font-style: italic; font-weight: normal; color: #aaaaaa; background-color: inherit; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { font-style: italic; color: #F60; background-color: inherit; } + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; background-color: inherit; } +.Enrolled { font-weight: normal; color: black; background-color: inherit; } +.Drop { font-style: italic; color: gray; background-color: inherit; } + +/*Styles for the login form*/ + +#login_form input { + margin-top: 7px; +} + +#login_form label{ + margin-right: 2px; +} + +#login_form #pswd_label{ + margin-right: 4px; +} + +#login_form #rememberme{ + margin-left: 75px; +} + +/*Styles for the edit forms, found on the homework sets editor page*/ + +.edit_form input,select{ + margin-top: 5px; +} + +.edit_form label, .edit_form span{ + margin-left: 40px; +} + +.edit_form label.radio_label{ + margin-left: 0px; +} + +.edit_form span#filter_err_msg{ + display: none; +} + +/*Styles for the classlist editor page*/ + +.set_table{ + margin-right: 5px; +} + +.set_table td{ + padding-right: 5px; +} + +.set_table th{ + text-align: center; + padding-right: 5px; + padding-left: 5px; +} + +.set_table label{ + margin-left: 0px; +} + +/*General styles, for elements such as table captions input buttons, columns, etc.*/ + +table caption{ + font-weight: bold; + text-decoration: underline; +} + +.button_input{ + margin-right: 5px; + margin-left: 5px; +} + +.column { + float: left; + width: 50%; + height: 22%; + overflow: hidden; + margin-top: 10px; +} + +.info-wrapper{ + float: right; + min-height: 100px; + width: 40%; + overflow: hidden; + margin-left: 5px; +} + +a.nav_button{ + color: black; + background-color: #DDDDDD; + text-decoration: none; + text-align: center; + font-size: 15px; + padding: 1px 6px 1px 6px; + margin: 2px; + border-style: outset; +} + +a.nav_button:hover{ + background-color: #EEEEEE; + border-color: #00ADEE; +} + +a.nav_button:active{ + border-style: inset; +} + +span.gray_button{ + color: black; + background-color: #DDDDDD; + text-decoration: none; + text-align: center; + font-size: 15px; + padding: 1px 6px 1px 6px; + margin: 2px; + border-style: outset; + opacity: 0.3; +} + +#none{ + /*Please do not do anything with elements with id "none"*/ +} + +/*Styles for the PGProblemEditor Page*/ + +#editor{ + background-color: #FFFFFF; +} + +#editor #source_and_options{ + float: left; + width: 50%; +} + +#editor #problemContents_label{ + text-align: center; + font-weight: bold; + text-decoration: underline; + margin-left: 30%; +} + +#editor #problemContents_id{ + width: 100%; + height: 50%; +} + +#editor .editor_form{ + margin-left: 40px; +} + +#editor #form_div{ + /*float: left;*/ + width: 100%; +} + +/*Styles specifically for the problem viewer*/ + +#problem_viewer_form{ + margin-left: 10px; + float: left; + width: 45%; + height: 100%; + border-left: solid; + border-width: 1px; +} + +#problem_viewer_form>h3{ + margin-left: 37%; +} + +#problem_viewer_form #viewer_span{ + margin-left: 10%; +} + +#problem_viewer_form #reload_button{ + margin-left: 20%; + margin-top: 5%; +} + +#problem_viewer_form #problem_viewer{ + width: 60%; + background-color: #E8E8E8; + margin-left: 20%; + margin-top: 5%; + padding: 1px; +} + +#problem_viewer_form #problem_viewer_content{ + border-style: solid; + padding: 5%; + border-width: 1px; +} + +#problem_viewer_form #viewer_note{ + margin-top: 10%; + margin-left: 10%; + margin-right: 10%; +} + +/*These are styles for the tables of problem sets*/ + +table.problem_set_table th{ + padding-right: 30px; +} + +table.problem_set_table td{ + padding-right: 30px; +} \ No newline at end of file diff --git a/conf/templates/math3/system.template b/conf/templates/math3/system.template new file mode 100644 index 0000000000..04a73960ff --- /dev/null +++ b/conf/templates/math3/system.template @@ -0,0 +1,213 @@ + + + + + + + + +/images/favicon.ico"/> +/css/math3.css"/> + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+
+ +
+ + + + + +     + + + + + + + + + + +
+ + + + + + + + +
+ + + + +
+

+ Warning -- there may be something wrong with this question. Please inform your instructor + including the warning messages below. +

+
+ + + +
+ + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + +
+ + +

+ + + + + + +

+ + +
+ +
+ + + + + + +
+ + + +
+ +
+ + + + + +
+ + + + + + +
+ + + + +
+
+ +
+ + + +
+ +
+ + +
+ + + +
+
+ + + diff --git a/conf/templates/moodle/system.template b/conf/templates/moodle/system.template new file mode 100644 index 0000000000..9e268ac2c4 --- /dev/null +++ b/conf/templates/moodle/system.template @@ -0,0 +1,122 @@ + + + + + +/css/moodle.css"/> +<!--#path style="text" text=" : " textonly="1"--> + + + + +
+ +
+
+ +
+ +
+
+
+ + +
+ + + + + + + + + + + + + +

+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + + +
+ +
+ + +
+
+
+ +
+
+
+
+ + + diff --git a/conf/templates/system.template b/conf/templates/system.template deleted file mode 100644 index 7d7c8687ae..0000000000 --- a/conf/templates/system.template +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - WeBWorK - <!--#title--> - - - - - - - - - - - - -
- - -

WeBWorK II

- -
-
-
-
-
- -
-

- -
- diff --git a/conf/templates/test/system.template b/conf/templates/test/system.template new file mode 100644 index 0000000000..e3d54e1fd4 --- /dev/null +++ b/conf/templates/test/system.template @@ -0,0 +1,99 @@ + + + + + + +WeBWorK Test Template + + + + + +

help

+
+ +
+ +

links

+
+ +
+ +

siblings

+
+ +
+ +

options

+
+ +
+ +

path

+
+ +
+ +

loginstatus

+
+ +
+ +

nav

+
+ +
+ +

title

+
+ +
+ +

message

+

used for addmessage/addgoodmessae/addbadmessage... displayed at top and bottom of page in ur.template.

+
+ +
+ +

body

+
+ +
+ +

warnings

+
+ +
+ +

info

+
+ +
+ +

timestamp

+
+ +
+ + + diff --git a/conf/templates/union/gateway.template b/conf/templates/union/gateway.template new file mode 100644 index 0000000000..57c9d1b64a --- /dev/null +++ b/conf/templates/union/gateway.template @@ -0,0 +1,279 @@ + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+
+ +
+
+ Updated: +
+ + diff --git a/conf/templates/union/system.template b/conf/templates/union/system.template new file mode 100644 index 0000000000..25e214f11c --- /dev/null +++ b/conf/templates/union/system.template @@ -0,0 +1,163 @@ + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + +/css/union.css"/> + + + + + + + + + + + + + + + + + + + + + + +
+ + WeBWorK +
+ + + +
+ + +
+ + +
+ +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
+ + + + + + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + +
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+
+ +
+
+ Updated: +
+ + diff --git a/conf/templates/union/union.css b/conf/templates/union/union.css new file mode 100644 index 0000000000..6f9512ef53 --- /dev/null +++ b/conf/templates/union/union.css @@ -0,0 +1,190 @@ +/******************************************************************************/ +/* union.css classes: These classes appear in ur.template and are NOT + * emitted by WeBWorK code. They need only appear in this template. */ + +body { margin: 0px; } + +/* left table cell, contains logo and menus */ +td.LeftPanel { background-color: #660033; color: white; white-space: nowrap; width: 8em; zoom: 1; } +td.LeftPanel ul { zoom: 1; margin-left: 0em} +td.LeftPanel ul ul ul li { margin-left: .5em} +td.LeftPanel a:link, +td.LeftPanel a:visited { color: #EEBBBB; } + +div.Logo { } +div.Links { font-size: small; } +div.Links ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Links ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } +div.Siblings { font-size: small; } +div.Siblings ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Siblings ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } +div.Options { font-size: small; } + +/* top table cell, contains login message and path */ +td.TopPanel { background-color: #660033; color: white; height: 1; } +td.TopPanel a:link, +td.TopPanel a:visited { color: #EEBBBB; } + +.Path { } + +.LoginStatus { text-align: right; font-size: small;} + +.TimeLeft { font-size: small; } +.LittleTimeLeft { font-size: small; color: #FF7777; font-weight: bold; margin: 3px} +.TimeLeftReload { font-size: small; color: #EEBBBB; font-style: italic; } +td.Timestamp { text-align: left; font-size: small; font-style: italic; } + +/* main content panel, contains body */ +td.ContentPanel { background-color: white; color: black; } +td.ContentPanel a:link, +td.ContentPanel a:visited { color: blue; } +td.ContentPanel a:active { color: red; } + +td.SetMakerPanel { background-color: #DDDDDD; color: black; width: 50%; padding: 3px; } + +div.Nav { } +div.Title { font-size: 16pt; padding-bottom: 5px; } +div.SubmitError { color: red; font-style: italic; } +div.Message { font-style: italic; } +div.Body { } +div.Warnings { } + +/* contains info */ +td.InfoPanel { background-color: #DDDDDD; color: black; width: 40%; } +td.InfoPanel a:link, +td.InfoPanel a:visited { color: blue; } +td.InfoPanel a:active { color: red; } +div.Info { } + +/******************************************************************************/ +/* WeBWorK classes: These classes are emitted by WeBWorK code and should + * appear in ANY WeBWorK template. */ + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; padding: 5px; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/* ul.LinksMenu { list-style: none; margin: 0 0 0 0.5em; padding: 0; }*/ +/* ul.LinksMenu ul { list-style: none; margin: 0 0 0 0.5em; padding: 0; }*/ + +a.HelpLink { border: 0px; } +a.HelpLink img { vertical-align: -5px; margin: 1px 2px; border: 0px; } + +/* background colors for success and failure messages */ +div.WarningMessage { background-color: #ffcccc; padding: 3px 3px 3px 3px; } +div.ResultsWithoutError { + margin-right: 6px; margin-left: 6px; + border-color: #009900; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + margin-right: 6px; margin-left: 6px; + background-color: #99ffAA; + padding: 3px 3px 3px 10px; +} /* light green */ +div.ResultsWithError { + margin-right: 6px; margin-left: 6px; + border-color: #CC0000; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + background-color: #ffcccc; + padding: 3px 3px 3px 10px; +} /* light red */ +div.ResultsAlert { + margin-right: 6px; margin-left: 6px; + border-color: #CCCC00; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + background-color: #ffffcc; + padding: 3px 3px 3px 10px; +} /* light yellow */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { border: thin groove; background-color: #882848; + line-height: 75%; margin: .5ex; margin-top: 2ex; + padding: 1ex; align: left; } + +.viewChoices { margin: 5px 0px 5px 5px; } +.viewChoices input {margin-top: 0px} + + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ + +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; +# margin: 0px auto 10pt auto; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; + padding: 2px 5px; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { text-align: left; width: auto; } +.attemptResultsSummary { font-style:italic; } + +.parsehilight { background-color:yellow; } + +/* the problem TEXT itself does in this box */ +div.problem { + clear: both; padding: 0 5px; color: black; + border-top-style: solid; border-bottom-style: solid; + border-width: 2px; border-color: #DDBBBB; +} + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10; + border-style: solid; + border-width:3px; + border-color: #DD0000; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for visible and hidden sets */ +font.visible { font-style: normal; font-weight: normal; color: #000000; } /* black */ +font.hidden { font-style: italic; font-weight: normal; color: #aaaaaa; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { + margin-right: 6px; margin-left: 6px; + border-color: #AA6600; border-style: solid; border-width: 1px; + text-align: center; font-style: italic; + background-color: #FFBB88; + padding: 3px 3px 3px 10px; +} + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; } +.Enrolled { font-weight: normal; color: black; } +.Drop { font-style: italic; color: grey; } diff --git a/conf/templates/ur.template b/conf/templates/ur.template deleted file mode 100644 index 1b4d32af8b..0000000000 --- a/conf/templates/ur.template +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - WeBWorK - <!--#title--> - - - - - - - - - - - - - - - - - - - -
-
-
-
- - Report bugs - -
- -
-
- - - -
- - -
- - -
- - - -
- -
- - - - -
- -
- - - -
- colspan="2" - - bgcolor="#ffcccc"> - - - colspan="2" - - bgcolor="#ffffff"> - -
- -
- - - - - -
-

- -

- - -
- -
- -
-
- -
- - diff --git a/conf/templates/ur/gateway.template b/conf/templates/ur/gateway.template new file mode 100644 index 0000000000..2ab3899790 --- /dev/null +++ b/conf/templates/ur/gateway.template @@ -0,0 +1,367 @@ + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+
+ +
+
+ Updated: +
+ + diff --git a/conf/templates/ur/system.template b/conf/templates/ur/system.template new file mode 100644 index 0000000000..a95b2d78e9 --- /dev/null +++ b/conf/templates/ur/system.template @@ -0,0 +1,148 @@ + + + + + + + +<!--#path style="text" text=" : " textonly="1"--> + +/css/ur.css"/> + + + + + + + + + + + + + + + + + + +
+ + WeBWorK + + + + +
+ + + +
+ + + +
+ + +
+ +
+ +
+ +
+ + +
+ + + + + + + + +
+ + + + + + + +
+
+ + + + + + + +
+ +
+ + +
+ +
+ + + +
+ + + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+ + + +
+ Updated: +
+ + + diff --git a/conf/templates/ur/ur.css b/conf/templates/ur/ur.css new file mode 100644 index 0000000000..ce86f16821 --- /dev/null +++ b/conf/templates/ur/ur.css @@ -0,0 +1,162 @@ +/******************************************************************************/ +/* ur.template classes: These classes appear in ur.template and are NOT + * emitted by WeBWorK code. They need only appear in this template. */ + +body { margin: 0px; } + +/* left table cell, contains logo and menus */ +td.LeftPanel { background-color: #003366; color: white; white-space: nowrap; width: 1em; } +td.LeftPanel a:link, +td.LeftPanel a:visited { color: #FF9933; } + +div.Logo { } +div.Links { font-size: small; } +div.Links ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Links ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } + +/* we used to say "height: 10em; overflow: auto" here, but it caused + * problems for mozilla. so we're back to having a looong siblings list + * for the time being. :-( */ +div.Siblings { font-size: small; } +div.Siblings ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Siblings ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } +div.Options { font-size: small; } + +/* top table cell, contains login message and path */ +td.TopPanel { background-color: #003366; color: white; height: 1; } +td.TopPanel a:link, +td.TopPanel a:visited { color: #FF9933; } + +.Path { } + +/*.LoginStatus { text-align: right; font-size: small; position:absolute; top: 0; right: 0; }*/ +.LoginStatus { text-align: right; font-size: small; } +td.Timestamp { text-align: left; font-size: small; font-style: italic; } + +/* (should have ContentPanelError here) */ + +/* main content panel, contains body */ +td.ContentPanel { background-color: white; color: black; } +td.ContentPanel a:link, +td.ContentPanel a:visited { color: blue; } +td.ContentPanel a:active { color: red; } + +div.Nav { } +div.Title { font-size: 16pt; } +div.Message { font-style: italic; } +div.SubmitError { color: red; font-style: italic; } +div.Body { } +div.Warnings { } + +/* (shuld have Message here) */ + +/* contains info */ +/* FIXME: this is erroniously used by SetMaker.pm. */ +td.InfoPanel { background-color: #DDDDDD; color: black; width: 30% } +td.InfoPanel h2 { font-size: 120% } +td.InfoPanel a:link, +td.InfoPanel a:visited { color: blue; } +td.InfoPanel a:active { color: red; } + +div.Info { } + +/******************************************************************************/ +/* WeBWorK classes: These classes are emitted by WeBWorK code and should + * appear in ANY WeBWorK template. */ + +/* the info escape emits a DIV with this class and id. */ +.info-box { } +#InfoPanel { font-size: smaller; } + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/*ul.LinksMenu { list-style: none; margin-left: 0; padding-left: 0; }*/ +/*ul.LinksMenu ul { list-style: none; margin-left: 0.5em; padding-left: 0; }*/ + +/* background styles for success and failure messages */ +div.ResultsWithoutError { background-color: #66ff99 } /* light green */ +div.ResultsWithError { background-color: #ffcccc } /* light red */ +div.ResultsAlert { background-color: yellow } /* yellow */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { border: thin groove; padding: 1ex; margin: 2ex; align: left; } + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; +# width: 15ex; + padding: 2px 5px 2px 5px; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { + text-align: left; + padding: 2px 5px 2px 5px; + width: auto; +} +.attemptResultsSummary { font-style:italic; } +.parsehilight { background-color:yellow; } + +/* the problem TEXT itself goes in this box */ +/* we used to make this a grey box, but surprise, MSIE is bug-ridden. */ +div.problem { } + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10px; + border-style: solid; + border-width: 3px; + border-color: #DD0000; + color: inherit; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for visible and hidden sets */ +font.visible { font-style: normal; font-weight: normal; color: #000000; background-color: inherit; } /* black */ +font.hidden { font-style: italic; font-weight: normal; color: #aaaaaa; background-color: inherit; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { background-color: #ff9900 ; font-style: italic; } + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; background-color: inherit; } +.Enrolled { font-weight: normal; color: black; background-color: inherit; } +.Drop { font-style: italic; color: gray; background-color: inherit; } diff --git a/conf/webwork.apache-config.dist b/conf/webwork.apache-config.dist new file mode 100644 index 0000000000..1440f7094b --- /dev/null +++ b/conf/webwork.apache-config.dist @@ -0,0 +1,197 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/webwork.apache-config.dist,v 1.23 2008/06/24 22:54:04 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. +################################################################################ + +# This file configures Apache to handle requests for WeBWorK. To install WeBWorK +# support in your Apache configuration, add the following line to the end of +# your Apache configuration file (usually apache.conf or httpd.conf): +# +# Include /path/to/webwork.apache-config +# +# 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 + + + + +# Set this variable to the path to your WeBWorK installation. +my $webwork_dir = "/opt/webwork/webwork2"; + +# Cache this value for use by other scripts not necessarily in the Apache2 hierarchy +# Different scripts use different locations :-) + +$ENV{WEBWORK_ROOT} = $webwork_dir; +$WeBWorK::SeedCE{webwork_dir} = $webwork_dir; +$WeBWorK::Constants::WEBWORK_DIRECTORY = $webwork_dir; + +# This code reads global.conf and extracts the remaining configuration +# variables. There is no need to modify it. +eval "use lib '$webwork_dir/lib'"; die $@ if $@; +eval "use WeBWorK::CourseEnvironment"; die $@ if $@; +my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_dir }); +my $webwork_url = $ce->{webwork_url}; +my $pg_dir = $ce->{pg_dir}; +my $webwork_htdocs_url = $ce->{webwork_htdocs_url}; +my $webwork_htdocs_dir = $ce->{webwork_htdocs_dir}; +my $webwork_courses_url = $ce->{webwork_courses_url}; +my $webwork_courses_dir = $ce->{webwork_courses_dir}; +eval "use lib '$pg_dir/lib'"; die $@ if $@; + +require Apache::WeBWorK; # force compilation of pretty much everything + + +# At this point, the following configuration variables should be present for use +# in wiring WeBWorK into Apache: +# +# $webwork_url The base URL handled by Apache::WeBWorK. +# $webwork_dir The path to the base webwork2 directory. +# $pg_dir The path to the base pg directory. +# +# $webwork_htdocs_url The base URL of the WeBWorK htdocs directory. +# $webwork_htdocs_dir The path to the WeBWorK htdocs directory. +# +# $webwork_courses_url The base URL of the WeBWorK courses directory. +# $webwork_courses_dir The path to the WeBWorK courses directory. + +# Define the location that is handled by the Apache::WeBWorK module, and tell +# Perl where to find the libraries Apache::WeBWorK needs to run. +# +$Location{$webwork_url} = { + SetHandler => "perl-script", + PerlHandler => "Apache::WeBWorK", +}; + +# Provide access to system-wide resources. +# +push @Alias, [ $webwork_htdocs_url => $webwork_htdocs_dir ]; +# make sure old alias is available even if we use lighttpd +push @Alias, [ "/webwork2_files" => $webwork_htdocs_dir ]; +$Directory{$webwork_htdocs_dir} = { + Order => "allow,deny", + Allow => "from all", + Options => "FollowSymLinks", + AllowOverride => "none", +}; + +# Provide access to course-specific resources. +# +push @AliasMatch, [ "$webwork_courses_url/([^/]*)/(.*)", "$webwork_courses_dir/\$1/html/\$2" ]; +$Directory{"$webwork_courses_dir/*/html"} = { + Order => "allow,deny", + Allow => "from all", + Options => "FollowSymLinks", + AllowOverride => "none", +}; + +# If WeBWorK is on the root, exempt the static directories from being handled +# by Apache::WeBWorK. +# +if ($webwork_url eq "") { + $Location{$webwork_courses_url} = { SetHandler => "none" }; + $Location{$webwork_htdocs_url} = { SetHandler => "none" }; +} + + +# 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. + +# uncomment the line below if you use the XMLRPC, RQP, or SOAP installations below + +$ENV{WEBWORK_ROOT} = $webwork_dir; + +$PerlConfig .= < +# # SetHandler perl-script +# # PerlHandler Apache::XMLRPC::Lite +# # PerlSetVar dispatch_to "WeBWorK::RPC WeBWorK::RPC::CourseManagement" +# # +# +# ########## XMLRPC installation ########## +# # +# #PerlModule WebworkWebservice +# # +# # SetHandler perl-script +# # PerlHandler Apache::XMLRPC::Lite +# # PerlSetVar dispatch_to "WebworkXMLRPC" +# # PerlSetVar options "compress_threshold => 10000" +# # Order Allow,Deny +# # Allow from All +# # +# +# ########## RQP installation ########## +# # +# #PerlModule RQP +# ## +# ## SetHandler perl-script +# ## PerlHandler Apache::SOAP +# ## PerlSetVar dispatch_to "RQP" +# ## PerlSetVar options "compress_threshold => 10000" +# ## Order Allow,Deny +# ## Allow from All +# ## +# # +# # SetHandler perl-script +# # PerlHandler MySOAP +# # Order Allow,Deny +# # Allow from All +# # +# +# ########## SOAP installation ########## +# # +# #PerlModule WebworkWebservice +# # +# # SetHandler perl-script +# # PerlHandler Apache::SOAP +# # PerlSetVar dispatch_to "WebworkXMLRPC" +# # PerlSetVar options "compress_threshold => 10000" +# # Order Allow,Deny +# # Allow from All +# # +# +# PerlModule WebworkSOAP + +# #WEBWORK SOAP CONFIGURATION +# +# PerlHandler Apache::SOAP +# SetHandler perl-script +# PerlSetVar dispatch_to "WebworkSOAP" +# PerlSetVar options "compress_threshold => 10000" +# Order Allow,Deny +# Allow from All +# + +# #WEBWORK SOAP WSDL HANDLER :: TO BE REPLACED WITH A FILE FOR PRODUCTION SERVERS +# +# PerlSetVar dispatch_to "WebworkSOAP::WSDL" +# PerlSetVar options "compress_threshold => 10000" +# PerlHandler WebworkSOAP::WSDL +# SetHandler perl-script +# Order Allow,Deny +# Allow from All +# + +EOF + + + diff --git a/conf/webwork.apache2-config.dist b/conf/webwork.apache2-config.dist new file mode 100644 index 0000000000..7923823b1e --- /dev/null +++ b/conf/webwork.apache2-config.dist @@ -0,0 +1,217 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2010 The WeBWorK Project, http://openwebwork.sf.net/ +# +# 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. +################################################################################ + +# This file configures Apache to handle requests for WeBWorK. To install WeBWorK +# support in your Apache configuration, add the following line to the end of +# your Apache configuration file (usually apache.conf or httpd.conf): +# +# Include /path/to/webwork.apache2-config +# +# Customize the variable $webwork_dir below to match the location of your +# WeBWorK installation. +# +# ATTENTION APACHE 2.2 USERS: There is a bug in mod_perl 2.0.2 that prevents the +# FollowSymlinksOption from working when specified +# in a section. See below for workaround. + + +# 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 +# It requires that a show-source.cgi script be customized and placed in +# the myCourse/html directory of the course +# The "show source" button is most useful for webwork "courses" used to train new authors +# It is not desirable to expose the code of regular homework questions to students + +ScriptAliasMatch /webwork2_course_files/([^/]*)/show-source.cgi/(.*) /opt/webwork/courses/$1/html/show-source.cgi/$2 + + + +PerlModule mod_perl2 + + + +################################################################# +# Set this variable to the path to your WeBWorK installation. +################################################################# + +my $webwork_dir = "/opt/webwork/webwork2"; +# Cache this value for use by other scripts not necessarily in the Apache2 hierarchy +# Different scripts use different locations :-) + +$ENV{WEBWORK_ROOT} = $webwork_dir; +$WeBWorK::SeedCE{webwork_dir} = $webwork_dir; +$WeBWorK::Constants::WEBWORK_DIRECTORY = $webwork_dir; + + +################################################################# +# The following code reads global.conf and extracts the remaining configuration +# variables. There is no need to modify it. +################################################################# + +# link to WeBWorK code libraries +eval "use lib '$webwork_dir/lib'"; die $@ if $@; +eval "use WeBWorK::CourseEnvironment"; die $@ if $@; + +# grab course environment (by reading webwork2/conf/global.conf) +my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_dir }); + +# set important configuration variables (from global.conf) + +my $webwork_url = $ce->{webwork_url}; # e.g. /webwork2 +my $pg_dir = $ce->{pg_dir}; # e.g. /opt/webwork/pg +my $webwork_htdocs_url = $ce->{webwork_htdocs_url}; # e.g. /webwork2_files +my $webwork_htdocs_dir = $ce->{webwork_htdocs_dir}; # e.g. /opt/webwork/webwork2/htdocs +my $webwork_courses_url = $ce->{webwork_courses_url}; # e.g. /webwork2_course_files +my $webwork_courses_dir = $ce->{webwork_courses_dir}; # e.g. /opt/webwork/courses + + +################################################################# +# report server setup when child process starts up -- for example when restarting the server +################################################################# +print "webwork.apache2-config: WeBWorK server is starting\n"; +print "webwork.apache2-config: WeBWorK root directory set to $webwork_dir in webwork2/conf/webwork.apache2-config\n"; +print "webwork.apache2-config: The following locations and urls are set in webwork2/conf/global.conf\n"; +print "webwork.apache2-config: PG root directory set to $pg_dir\n"; +print "webwork.apache2-config: WeBWorK server userID is ", $ce->{server_userID}, "\n"; +print "webwork.apache2-config: WeBWorK server groupID is ", $ce->{server_groupID}, "\n"; +print "webwork.apache2-config: The webwork url on this site is ", $ce->{server_root_url},"$webwork_url\n"; + + +# link to PG code libraries +eval "use lib '$pg_dir/lib'"; die $@ if $@; + +require Apache::WeBWorK; # force compilation of pretty much everything + + +# At this point, the following configuration variables should be present for use +# in wiring WeBWorK into Apache: +# +# $webwork_url The base URL handled by Apache::WeBWorK. +# $webwork_dir The path to the base webwork2 directory. +# $pg_dir The path to the base pg directory. +# +# $webwork_htdocs_url The base URL of the WeBWorK htdocs directory. +# $webwork_htdocs_dir The path to the WeBWorK htdocs directory. +# +# $webwork_courses_url The base URL of the WeBWorK courses directory. +# $webwork_courses_dir The path to the WeBWorK courses directory. + +# Define the location that is handled by the Apache::WeBWorK module, and tell +# Perl where to find the libraries Apache::WeBWorK needs to run. +# +$Location{$webwork_url} = { + SetHandler => "perl-script", + PerlHandler => "Apache::WeBWorK", +}; + +# Provide access to system-wide resources. +# +push @Alias, [ $webwork_htdocs_url => $webwork_htdocs_dir ]; +$Directory{$webwork_htdocs_dir} = { + Order => "allow,deny", + Allow => "from all", + # APACHE 2.2 USERS: comment out the following line. + Options => "FollowSymLinks", + AllowOverride => "none", +}; + +# Provide access to course-specific resources. +# +push @AliasMatch, [ "$webwork_courses_url/([^/]*)/(.*)", "$webwork_courses_dir/\$1/html/\$2" ]; +$Directory{"$webwork_courses_dir/*/html"} = { + Order => "allow,deny", + Allow => "from all", + # APACHE 2.2 USERS: comment out the following line. + Options => "FollowSymLinks", + AllowOverride => "none", +}; + +# If WeBWorK is on the root, exempt the static directories from being handled +# by Apache::WeBWorK. +# +if ($webwork_url eq "") { + $Location{$webwork_courses_url} = { SetHandler => "none" }; + $Location{$webwork_htdocs_url} = { SetHandler => "none" }; +} + + + +#################################################################### +# WebworkSOAP handlers (for integration with moodle) +#################################################################### + PerlModule WebworkSOAP + + # WEBWORK SOAP CONFIGURATION + + PerlHandler Apache2::SOAP + SetHandler perl-script + PerlSetVar dispatch_to "WebworkSOAP" + PerlSetVar options "compress_threshold => 10000" + Order Allow,Deny + Allow from All + + +#################################################################### +# WebworkSOAP WSDL HANDLER :: TO BE REPLACED WITH A FILE FOR PRODUCTION SERVERS +#################################################################### + + PerlSetVar dispatch_to "WebworkSOAP::WSDL" + PerlSetVar options "compress_threshold => 10000" + PerlHandler WebworkSOAP::WSDL + SetHandler perl-script + Order Allow,Deny + Allow from All + + + +#################################################################### +# WebworkWebservice handlers -- for integration with external editor +#################################################################### + + +########### WebworkWebservice XMLRPC handler ########## + +PerlModule WebworkWebservice + + + SetHandler perl-script + PerlHandler Apache::XMLRPC::Lite + PerlSetVar dispatch_to "WebworkXMLRPC" + PerlSetVar options "compress_threshold => 10000" + Order Allow,Deny + Allow from All + + + + +########## WebworkWebservice SOAP handler ########## + +#PerlModule WebworkWebservice +# +# +# SetHandler perl-script +# PerlHandler Apache::SOAP +# PerlSetVar dispatch_to "WebworkXMLRPC" +# PerlSetVar options "compress_threshold => 10000" +# Order Allow,Deny +# Allow from All +# + +#################################################################### +# WebworkSOAP "bridge2" handler +#################################################################### + +# Include /opt/webwork/wwqs/conf/problemserver.apache-config \ No newline at end of file diff --git a/courses.dist/adminClasslist.lst b/courses.dist/adminClasslist.lst new file mode 100644 index 0000000000..5d92c29c58 --- /dev/null +++ b/courses.dist/adminClasslist.lst @@ -0,0 +1,3 @@ +# $Id: adminClasslist.lst,v 1.2 2009-06-26 00:58:52 gage Exp $ +# Field order: student_id,last_name,first_name,status,comment,section,recitation,email_address,user_id,password,permission +admin,Administrator,,C,,,,,admin,.bxpcera4I/bg,10 diff --git a/courses.dist/defaultClasslist.lst b/courses.dist/defaultClasslist.lst new file mode 100644 index 0000000000..23709b5863 --- /dev/null +++ b/courses.dist/defaultClasslist.lst @@ -0,0 +1,12 @@ +# $Id: defaultClasslist.lst,v 1.1 2006-09-05 16:27:37 sh002i Exp $ +# Field order: student_id,last_name,first_name,status,comment,section,recitation,email_address,user_id,password,permission +practice1,Practice1,,C,,,,,practice1,,-5 +practice2,Practice2,,C,,,,,practice2,,-5 +practice3,Practice3,,C,,,,,practice3,,-5 +practice4,Practice4,,C,,,,,practice4,,-5 +practice5,Practice5,,C,,,,,practice5,,-5 +practice6,Practice6,,C,,,,,practice6,,-5 +practice7,Practice7,,C,,,,,practice7,,-5 +practice8,Practice8,,C,,,,,practice8,,-5 +practice9,Practice9,,C,,,,,practice9,,-5 +professor,Professor,,C,,,,,professor,dmU8ES4L.64VU,10 diff --git a/courses.dist/modelCourse/course.conf b/courses.dist/modelCourse/course.conf new file mode 100644 index 0000000000..1cbba28993 --- /dev/null +++ b/courses.dist/modelCourse/course.conf @@ -0,0 +1,90 @@ +#!perl +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# +# 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. +################################################################################ + +# This file is used to override the global WeBWorK course environment for +# requests to this course. All package variables set in this file are added to +# the course environment. If you wish to set a variable here but omit it from +# the course environment, use the "my" keyword. Commonly changed configuration +# options are noted below. + +# Database Layout (global value typically defined in global.conf) +# +# Several database are defined in the file conf/database.conf and stored in the +# hash %dbLayouts. +# +# The database layout is always set here, since one should be able to change the +# default value in global.conf without disrupting existing courses. +# +# global.conf values: +# $dbLayoutName = 'sql_single'; +# *dbLayout = $dbLayouts{$dbLayoutName}; + +$dbLayoutName = 'sql_single'; +*dbLayout = $dbLayouts{$dbLayoutName}; + +# Global User ID (global value typically defined in database.conf) +# +# The globalUserID parameter given for the set and problem tables denotes the ID +# of the user that the GlobalTableEmulator will use to store data for the set +# and problem tables. +# +# If a course will be used under WeBWorK 1.x, this value should be overridden on +# a course-by-course basis to the ID of the professor who is most likely to be +# involved in creating new problem sets. Sets which have not been assigned will +# only be visible to this user when logging into WeBWorK 1.x. +# +# The global user ID is always set here, since one should be able to change the +# default value in database.conf without disrupting existing courses. +# +# global.conf values: +# $dbLayouts{gdbm}->{set}->{params}->{globalUserID} = 'global_user'; +# $dbLayouts{gdbm}->{problem}->{params}->{globalUserID} = 'global_user'; + +$dbLayouts{gdbm}->{set}->{params}->{globalUserID} = 'global_user'; +$dbLayouts{gdbm}->{problem}->{params}->{globalUserID} = 'global_user'; + +# Allowed Mail Recipients (global value typically not defined) +# +# Defines addresses to which the PG system is allowed to send mail. This should +# probably be set to the addresses of professors of this course. Sending mail +# from the PG system (i.e. questionaires, essay questions) will fail if this is +# not set. +# +# global.conf values: +# $mail{allowedRecipients} = ['gage@math.rochester.edu']; + +$mail{allowedRecipients} = ['gage@math.rochester.edu']; + +# Feedback Mail Recipients (global value typically not defined) +# +# Defines recipients for feedback mail. If not defined, mail is sent to all +# instructors and TAs. +# +# global.conf values: +# $mail{feedbackRecipients} = ['gage@math.rochester.edu']; + + + + +# Users for whom to label problems with the PG file name (global value typically "professor") +# +# For users in this list, PG will display the source file name when rendering a problem. +# +# global.conf values: +# $pg{specialPGEnvironmentVars}{PRINT_FILE_NAMES_FOR} = ['gage', 'apizer', 'sh002i', 'professor']; + +$pg{specialPGEnvironmentVars}{PRINT_FILE_NAMES_FOR} = ['global_user', 'toenail', 'sam', 'apizer', 'gage']; + diff --git a/courses.dist/modelCourse/hide_directory b/courses.dist/modelCourse/hide_directory new file mode 100644 index 0000000000..2ba4632298 --- /dev/null +++ b/courses.dist/modelCourse/hide_directory @@ -0,0 +1,4 @@ + +Place a file named "hide_directory" in a course or other directory +and it will not show up in the courses list on the WeBWorK home page. +It will still appear in the Course Administration listing. diff --git a/courses.dist/modelCourse/templates/ASimpleCombinedHeaderFile.pg b/courses.dist/modelCourse/templates/ASimpleCombinedHeaderFile.pg new file mode 100644 index 0000000000..35267ae018 --- /dev/null +++ b/courses.dist/modelCourse/templates/ASimpleCombinedHeaderFile.pg @@ -0,0 +1,88 @@ +# ASimpleCombinedHeaderFile.pg +# This header file can be used for both the screen and hardcopy output + + +DOCUMENT(); + +loadMacros( + "PG.pl", + "PGbasicmacros.pl", + +); + +TEXT($BEGIN_ONE_COLUMN); + +#################################################### +# +# The item below printed out only when a hardcopy is made. +# +#################################################### + + + +TEXT(MODES(TeX =>EV3(<<'EOT'),HTML=>"")); + +\noindent {\large \bf $studentName} +\hfill +{\large \bf {\{protect_underbar($courseName)\}}} +% Uncomment the line below if this course has sections. Note that this is a comment in TeX mode since this is only processed by LaTeX +% {\large \bf { Section: \{protect_underbar($sectionName)\} } } +\par +\noindent{\large \bf {Assignment \{protect_underbar($setNumber)\} due $formatedDueDate}} +\par\noindent \bigskip +% Uncomment and edit the line below if this course has a web page. Note that this is a comment in TeX mode. +%See the course web page for information http://yoururl/yourcourse + +EOT + +#################################################### +# +# End of hardcopy only output. +# +#################################################### + + +#################################################### +# +# The items below are printed out only when set is displayed on screen +# +#################################################### +TEXT(MODES(TeX =>"",HTML=>EV3(<<'EOT'))); + +$BBOLD WeBWorK Assignment \{ protect_underbar($setNumber) \} is due : $formattedDueDate. $EBOLD +$PAR +Here's the list of +\{ htmlLink(qq!http://webwork.maa.org/wiki/Available_Functions!,"functions and symbols") \} + which WeBWorK understands. +$BR +EOT + +#################################################### +# Uncomment and edit the lines below if this course has a web page. Note that this is comment in Perl mode. +# IMPORTANT: Make sure the EOT at the bottom is at the beginning of a line with no spaces preceeding it. +#TEXT(MODES(TeX =>"",HTML=>EV3(<<'EOT'))); +#See the course web page for information \{ htmlLink(qq!http://yoururl/yourcourse!,"your course name") \} +#EOT +#################################################### + +#################################################### +# +# End of screen only output. +# +#################################################### + +#################################################### +# +# Anything between the BEGIN_TEXT AND END_TEXT lines +# will be printed in both screen and hardcopy output +# +#################################################### + +BEGIN_TEXT + +END_TEXT + + +TEXT($END_ONE_COLUMN); + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/ASimpleHardCopyHeaderFile.pg b/courses.dist/modelCourse/templates/ASimpleHardCopyHeaderFile.pg new file mode 100644 index 0000000000..aae91a7408 --- /dev/null +++ b/courses.dist/modelCourse/templates/ASimpleHardCopyHeaderFile.pg @@ -0,0 +1,36 @@ +# ASimpleHardCopyHeaderFile.pg +# This file can be used as a simple Hard Copy Header file which is processed by LaTeX. +# Do not use it as a Screen Header file which is processed by html + + +DOCUMENT(); + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", + +); + + +BEGIN_TEXT +$BEGIN_ONE_COLUMN +\noindent {\large \bf $studentName} +\hfill +{\large \bf {\{protect_underbar($courseName)\}}} +% Uncomment the line below if this course has sections. Note that this is a comment in TeX mode since this is only processed by LaTeX +% {\large \bf { Section: \{protect_underbar($sectionName)\} } } +\par +\noindent{\large \bf {Assignment \{protect_underbar($setNumber)\} due $formatedDueDate}} +\par\noindent \bigskip +% Uncomment and edit the line below if this course has a web page. Note that this is a comment in TeX mode. +% See the course web page for information http://yoururl/yourcourse + + +$END_ONE_COLUMN +END_TEXT + +ENDDOCUMENT(); + + + + diff --git a/courses.dist/modelCourse/templates/ASimpleScreenHeaderFile.pg b/courses.dist/modelCourse/templates/ASimpleScreenHeaderFile.pg new file mode 100644 index 0000000000..34a2fff901 --- /dev/null +++ b/courses.dist/modelCourse/templates/ASimpleScreenHeaderFile.pg @@ -0,0 +1,27 @@ +##ASimpleScreenHeaderFile.pg + +DOCUMENT(); + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", + +); + +BEGIN_TEXT +$BBOLD WeBWorK Assignment \{ protect_underbar($setNumber) \} is due : $formattedDueDate. $EBOLD +$PAR +Here's the list of +\{ htmlLink(qq!http://webwork.maa.org/wiki/Available_Functions!,"functions and symbols") \} + that WeBWorK understands. +END_TEXT + + +# Uncomment and edit the lines below if this course has a web page. Note that this is comment in Perl mode. +#BEGIN_TEXT +#$PAR +#See the course web page for information \{ htmlLink(qq!http://yoururl/yourcourse!,"your course name") \} +#END_TEXT + +ENDDOCUMENT(); + diff --git a/courses.dist/modelCourse/templates/course_info.txt b/courses.dist/modelCourse/templates/course_info.txt new file mode 100644 index 0000000000..69b888d207 --- /dev/null +++ b/courses.dist/modelCourse/templates/course_info.txt @@ -0,0 +1,3 @@ +this information is written in the file: + +[coursesDirectory]/courseName/templates/course_info.txt diff --git a/courses.dist/modelCourse/templates/demoCourse.lst b/courses.dist/modelCourse/templates/demoCourse.lst new file mode 100644 index 0000000000..87452facf5 --- /dev/null +++ b/courses.dist/modelCourse/templates/demoCourse.lst @@ -0,0 +1,9 @@ +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 diff --git a/courses.dist/modelCourse/templates/email/welcome.msg b/courses.dist/modelCourse/templates/email/welcome.msg new file mode 100644 index 0000000000..3db7f1a075 --- /dev/null +++ b/courses.dist/modelCourse/templates/email/welcome.msg @@ -0,0 +1,23 @@ +## template for a Welcome message to be emailed to class (delete this line) +From: teacher@somewhere.edu (Jan Teacher) +Reply-To: teacher@somewhere.edu +Subject: online homework for Math 123 +Message: + Hi $FN, + +Your course is at + http://somewhere.edu/webwork2/JT_123 + +Your login name is $LOGIN and your initial password is $SID + +You should change your password, once you have logged in, by clicking on the +password/email link in the upper left. After entering your new password, +press the Change User Options button to save your change. + +Some help pages about WeBWorK are at URL + http://webwork.maa.org/wiki/Category:Students + +Have fun. + +Take care, +TJ diff --git a/courses.dist/modelCourse/templates/set0.def b/courses.dist/modelCourse/templates/set0.def new file mode 100644 index 0000000000..4d35f48af9 --- /dev/null +++ b/courses.dist/modelCourse/templates/set0.def @@ -0,0 +1,14 @@ +setNumber=0 +openDate = 1/7/00 at 6:00am +dueDate = 1/20/09 at 6:00am +answerDate = 1/21/09 at 6:00am +paperHeaderFile = set0/paperHeaderFile0.pg +screenHeaderFile = set0/screenHeaderFile0.pg +problemList = +set0/prob1.pg, 0 +set0/prob1a.pg, 0 +set0/prob1b.pg, 0 +set0/prob2.pg, 0 +set0/prob3.pg, 0 +set0/prob4/prob4.pg, 0 +set0/prob5.pg, 0 diff --git a/courses.dist/modelCourse/templates/set0/paperHeaderFile0.pg b/courses.dist/modelCourse/templates/set0/paperHeaderFile0.pg new file mode 100644 index 0000000000..9a353ac92f --- /dev/null +++ b/courses.dist/modelCourse/templates/set0/paperHeaderFile0.pg @@ -0,0 +1,29 @@ +##Problem set header for set 0 + +&DOCUMENT; + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl" +); + +$dateTime = $formatedDueDate; + +TEXT(EV2(< ['c'] ) ); +$a1= random(-9,-1,1); +$b1= random(0,9,1); + +BEGIN_TEXT +$BR $BR +Evaluate \[ F(x) = \int_{$a1}^{$b1} x^{$n1}\cos(x^{$n}) \; dx \] +$BR +\{ans_rule(50)\} +END_TEXT + +$ans = (sin($b1**$n) - sin($a1**$n))/$n; + +&ANS(num_cmp($ans )); + + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setDemo/calc.html b/courses.dist/modelCourse/templates/setDemo/calc.html new file mode 100644 index 0000000000..1056894e8f --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/calc.html @@ -0,0 +1,57 @@ + + +The JavaScript Source: Calculators: Basic + + + + + + +

+ + +I took this calculator straight from the web at javascriptsource.com +

+ +This is a very simple calculator, +all done with javascript. +Just click the numbers and +the operators and use the "=" button to calculate! + +

+
+ + + + + + + +
+ +
+
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ diff --git a/courses.dist/modelCourse/templates/setDemo/josephus.pg b/courses.dist/modelCourse/templates/setDemo/josephus.pg new file mode 100644 index 0000000000..0cfeb57af3 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/josephus.pg @@ -0,0 +1,206 @@ +&DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); +# define function to be evaluated + +HEADER_TEXT(< + +function joeGame(n, k, m){ +this.people = n +this.skip = k +this.keep = m +this.list = new Array(n) +for (var i=0; i n) { + x = Math.floor(( k*(x-n) - 1.0 )/ (k-1.0) ) + } +return(x) +} + +function playGame() { + +var newN = parseInt(document.gameForm.peopleNumField.value) +var newK = parseInt(document.gameForm.skipNumField.value) + +var suff = new String("th") + +var s +var n = newN +var k = newK +var numRows = Math.floor(n/20) +var numLeft = n - 20*numRows + +if (!newN || !newK) { + alert ("Enter some data.") + document.gameForm.peopleNumField.focus() + } +else + { + +currentGame = new joeGame(n, k, 1) + +for (var s=1; s<=currentGame.people; s++){ + currentGame.list[s-1] = joeth(n, k, s) + } + +if ((newK % 10) == 1) { + if ((newK == 1) || (newK > 20)) { + suff = "st" + } + else { + suff="th" + } +} +else { + if ((newK % 10) == 2){ + if ((newK == 2) || (newK > 20)) { + suff = "nd" + } + else { + suff="th" + } + } + else { + if ((newK % 10) == 3){ + if ((newK == 3) || (newK > 20)) { + suff = "rd" } + else { + suff="th" + } + } + } +} + +parent.gameStatus.document.clear() +parent.gameStatus.document.write("

The Josephus Game

") +parent.gameStatus.document.write(" Playing the Josephus Game with " + + document.gameForm.peopleNumField.value + " people, ") +parent.gameStatus.document.write(" eliminating every " + + document.gameForm.skipNumField.value + "" + suff + " person, leaves ") +parent.gameStatus.document.write(" person " + currentGame.list[n-1] + " as the last survivor. ") +parent.gameStatus.document.write("

The entire elimination order is

") + +for (var d=0; d \n Order Eliminated ") + for (var k=1; k<=20; k++) { + parent.gameStatus.document.write(" " + (20*d + k) + " ") + } + parent.gameStatus.document.write(" \n Person Eliminated ") + + for (k=0; k<20; k++) { + parent.gameStatus.document.write(" " + currentGame.list[20*d+k] + " ") + } + parent.gameStatus.document.write(" \n
") + } +if (numLeft > 0) { + parent.gameStatus.document.write(" \n ") + for (var k=1; k<=numLeft; k++) { + parent.gameStatus.document.write(" ") + } + parent.gameStatus.document.write(" \n ") + + for (k=0; k " + currentGame.list[20*d+k] + " ") + } + parent.gameStatus.document.write(" \n
Order Eliminated " + (20*d + k) + "
Person Eliminated

") + } + } + parent.gameStatus.document.close() +} + + +function clearStuff(){ +parent.gameStatus.document.open() +parent.gameStatus.document.clear() +parent.gameStatus.document.location = "gamestart.html" +parent.gameStatus.document.close() +document.gameForm.peopleNumField.focus() +currentGame.people = 10 +currentGame.skip = 0 +currentGame.keep = 0 +for (var i=0; i++; i + + +EOF + + +TEXT(beginproblem()); +TEXT(MODES( +TeX => '', +Latex2HTML => "\begin{rawhtml} \end{rawhtml}", +HTML => "" +)); + +BEGIN_TEXT +$PAR +This problem illustrates how you can embed JavaScript code in a WeBWorK example +to create an interactive homework problem that could never be provided by a text book. +Stolen from Doug Ensley and mathDL. +http://www.mathdl.org/offsite.html?page=http://www.ship.edu/~deensl/mathdl/Joseph.html&content_id=41520 +END_TEXT + +BEGIN_TEXT + +END_TEXT +TEXT(< + + + + + + +
+ + + + + + + + + + + + +
n
k
+
Set the parameters and press Start +to see the elimination order when you start with n people +and eliminate every kth one.
+ +

+EOF +$ans =3; + +ANS(num_cmp($ans,reltol=>1,format =>"%0.14g") ); #We are allowing 1 percent error for the answer. + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/limits.pg b/courses.dist/modelCourse/templates/setDemo/limits.pg new file mode 100644 index 0000000000..61b42252e7 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/limits.pg @@ -0,0 +1,90 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl", + "PGauxiliaryFunctions.pl" +); + +TEXT(&beginproblem); +$showPartialCorrectAnswers = 1; + +$a=random(-3,3,1); +$b=random(-2,3,1); +$c=random(-3,2,1); +$m1=random(-1,1,0.5); +$m2=($b - $a)/2; +$m3=($c - $b - 1)/2; +$m4=random(-1,1,0.5); +@slice = NchooseK(3,3); + +@colors = ("blue", "red", "green"); +@sc = @colors[@slice]; #scrambled colors +@sa = ('A','B','C')[@slice]; + +$f1 = FEQ("${m1}(x+1) + $a for x in [-2,-1) using color:$sc[0] and weight:2"); +$f2 = FEQ("${m2}(x-1) + $b for x in (-1,1) using color=$sc[0] and weight:2"); +$f3 = FEQ("${m3}(x-3) + $c for x in [1,3) using color=$sc[0] and weight=2"); +$f4 = FEQ("1+$a for x in [-1,-1] using color=$sc[0] and weight=2"); +$f5 = FEQ("${m4}(x-3) + $c for x in (3,4] using color=$sc[0] and weight=2"); + +$graph = init_graph(-3,-6,5,6,'axes'=>[0,0],'grid'=>[8,12]); + +($f1Ref,$f2Ref,$f3Ref,$f4Ref,$f5Ref) = plot_functions($graph,$f1,$f2,$f3,$f4,$f5); + +BEGIN_TEXT +Let F be the function below.$PAR +If you are having a hard time seeing the picture clearly, click on the picture. It will expand to a larger picture on its own page so that you can inspect it more clearly.$PAR +END_TEXT + +TEXT(image( insertGraph($graph), height=>200, width=>200 )); + +BEGIN_TEXT +$BR +$BR +Evaluate each of the following expressions. $PAR +Note: Enter 'DNE' if the limit does not exist or is not defined. $PAR + +a) \( \lim_{x \to -1^-} F(x) \) = \{ans_rule(4)\} +$PAR + +b) \( \lim_{x \to -1^+} F(x) \) = \{ans_rule(4)\} +$PAR + +c) \( \lim_{x \to -1} F(x) \) = \{ans_rule(4)\} +$PAR + +d) \( F(-1) \) = \{ans_rule(4)\} +$PAR + +e) \( \lim_{x \to 1^-} F(x) \) = \{ans_rule(4)\} +$PAR + +f) \( \lim_{x \to 1^+} F(x) \) = \{ans_rule(4)\} +$PAR + +g) \( \lim_{x \to 1} F(x) \) = \{ans_rule(4)\} +$PAR + +h) \( \lim_{x \to 3} F(x) \) = \{ans_rule(4)\} +$PAR + +i) \( F(3) \) = \{ans_rule(4)\} +$PAR + +END_TEXT + +$ap1 = 1 + $a; +$bp1 = 1 + $b; + +# limits at -1 +ANS(num_cmp( [ $a, $a, $a, $ap1] , strings => ['DNE'] )) ; +# limits at 1 +ANS(num_cmp( [ $b, $bp1,'DNE'] , strings => ['DNE'] )) ; +# limits at 3 +ANS(num_cmp( [ $c, 'DNE' ] , strings => ['DNE'] )) ; + + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/liteApplet1.pg b/courses.dist/modelCourse/templates/setDemo/liteApplet1.pg new file mode 100644 index 0000000000..7a8cc4c075 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/liteApplet1.pg @@ -0,0 +1,68 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +$showPartialCorrectAnswers = 1; +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 is a lite applet designed by Frank Wattenberg. +$BR +\{htmlLink( '/webwork2_course_files/demoCourse/live_map_instructions.html ', +'Instructions for using the map',' target="intro" ' )\} +$HR +END_TEXT +$appletText = +appletLink( +q! archive="/courses/system_html/applets/Image_and_Cursor_All/Image_and_Cursor.jar" +code="Image_and_Cursor" width = 500 height = 458 +!, +q!Your browser does not support Java, so nothing is displayed. + + + + + + + +! +); +sub dist { + my $ra_pt1 = shift; + my $ra_pt2 =shift; + my $conversion = 300 /(145 - 72); # number of km per pixel + return $conversion* sqrt( ($ra_pt1->[0] - $ra_pt2->[0])**2 + ($ra_pt1->[1] - $ra_pt2->[1])**2); +} + +$kandahar = [132,101]; +$kabul = [209,185]; +$mazur_e_sharif = [170, 243]; +$shindand = [46, 155]; + +$questions = EV3( +"$PAR How far is it from Kandahar to Kabul? " , ans_rule(30), +" $PAR How far is it from Kabul to Mazar-e-Sharif? ", ans_rule(30), +" $PAR How far is it from Kandahar to Shindand? " , ans_rule(30), +); +#TEXT( +#begintable(2), +#row( $appletText, $questions), +#endtable() +#); +TEXT($appletText, $questions); +ANS(num_cmp(dist($kandahar,$kabul), reltol => 3, units=>'km')); +ANS(num_cmp(dist($kabul, $mazur_e_sharif), reltol => 3, units=>'km')); +ANS(num_cmp(dist($kandahar,$shindand), reltol => 3, units=>'km')); + + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/liteApplet2.pg b/courses.dist/modelCourse/templates/setDemo/liteApplet2.pg new file mode 100644 index 0000000000..6f251df90e --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/liteApplet2.pg @@ -0,0 +1,74 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +$showPartialCorrectAnswers = 1; +TEXT(beginproblem()); + + +BEGIN_TEXT +This is a lite applet designed by Frank Wattenberg. +$BR +\{htmlLink( '/webwork2_course_files/demoCourse/live_map_instructions.html ', +'Instructions for using the map',' target="intro" ' )\} +$HR +END_TEXT +TEXT( +appletLink( +q! archive="/courses/system_html/applets/Image_and_Cursor_All/Image_and_Cursor.jar" +code="Image_and_Cursor" width = 500 height = 458 +!, +q!Your browser does not support Java, so nothing is displayed. + + + + + + + +! +), +); +sub dist { + my $ra_pt1 = shift; + my $ra_pt2 =shift; + $conversion = 300 /(145 - 72); # number of km per pixel + return $conversion * sqrt( ($ra_pt1->[0] - $ra_pt2->[0])**2 + ($ra_pt1->[1] - $ra_pt2->[1])**2); +} +@cities = ( + { name => 'Kandahar', location => [132,101] }, + { name => 'Kabul', location => [209,185] }, + { name => 'Mazur e Sharif', location => [170, 243] }, + { name => 'Shindand', location => [46, 155] }, + { name => 'Zaranj', location => [39, 93] } +); +@index = NchooseK(scalar(@cities), 3 ); +sub cityName { + my $index = shift ; + $cities[$index -1]->{name}; +} +sub cityLoc { + my $index = shift; + $cities[$index-1]->{location}; +} + +$conversion = 300 /(145 - 72); # number of km per pixel +BEGIN_TEXT +$PAR +How far is it from \{cityName($index[1])\} to \{cityName($index[2])\}? \{ans_rule(30)\} +$PAR +How far is it from \{cityName($index[1])\} to \{cityName($index[3])\}? \{ans_rule(30)\} +$PAR +How far is it from \{cityName($index[2])\} to \{cityName($index[3])\}? \{ans_rule(30)\} +END_TEXT + +ANS(num_cmp(dist(cityLoc($index[1]),cityLoc($index[2])), reltol=>3, units=>'km')); +ANS(num_cmp(dist(cityLoc($index[2]), cityLoc($index[2])), reltol=>3, units=>'km')); +ANS(num_cmp(dist(cityLoc($index[2]),cityLoc($index[2])), reltol=>3, units=>'km')); + + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/nsc2s10p2.pg b/courses.dist/modelCourse/templates/setDemo/nsc2s10p2.pg new file mode 100644 index 0000000000..93bbc266e5 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/nsc2s10p2.pg @@ -0,0 +1,67 @@ +#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", "black"); +@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. diff --git a/courses.dist/modelCourse/templates/setDemo/paperHeaderFile1.pg b/courses.dist/modelCourse/templates/setDemo/paperHeaderFile1.pg new file mode 100644 index 0000000000..eb04aff8e2 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/paperHeaderFile1.pg @@ -0,0 +1,31 @@ +##Problem set header for set 0, Spring 1999 + +&DOCUMENT; + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl" +); + +$dateTime = $formatedDueDate; + +TEXT(EV2(<' ) \} +END_OF_TEXT + +## + +TEXT("$BR$BR",ans_rule(30),"$BR"); +ANS( CAPA_ans( $v0 , 'format' => "%0.2e" , 'sig' => '3 PLUS 13', 'reltol' => 1 , 'wgt' => $prob_val , 'tries' => $prob_try , 'unit' => 'm/s' ) ); +ENDDOCUMENT(); +##################### + +###Error: $smallg not defined in this file +###Error: $degrad not defined in this file +###Error: $degrad not defined in this file +###Error: $degrad not defined in this file +###Error: $deg_u not defined in this file +###Error: $m_u not defined in this file +###Error: $m_u not defined in this file +###Error: $prob_val not defined in this file +###Error: $prob_try not defined in this file + +##################### + + +################################################# +## Processing time = 0 secs ( 0.56 usr 0.00 sys = 0.56 cpu) +################################################# diff --git a/courses.dist/modelCourse/templates/setDemo/prob5.pg b/courses.dist/modelCourse/templates/setDemo/prob5.pg new file mode 100644 index 0000000000..7cf8acb646 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/prob5.pg @@ -0,0 +1,138 @@ +&DOCUMENT(); +loadMacros("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); +BEGIN_TEXT +To see a different version of the problem change +the problem seed and press the 'Submit Answer' button below.$PAR Problem Seed: +\{ MODES( +TeX => qq! Change the problem seed to change the problem:$problemSeed!, +Latex2HTML => qq! Change the problem seed to change the problem: + \begin{rawhtml} + + \end{rawhtml}!, +HTML => qq! ! +) +\} +END_TEXT +######################################################### +# define function to be evaluated +$a= random(1,3,1); +$b= random(-4,4,.1); +$c = random(-4,4,1); +$x0=random(-2,2,1); +$function = FEQ(" ${a}x^2+${b}x +$c "); +sub fp { # define a subroutine to calculate the derivative + my $x = shift; + 2*$a*$x+$b; +} +$ans = fp($x0); +HEADER_TEXT(< + + + + + + +EOF + + +TEXT(beginproblem()); +TEXT(MODES( +TeX => '', +Latex2HTML => "\begin{rawhtml} \end{rawhtml}", +HTML => "" +)); + +BEGIN_TEXT +$PAR +This problem illustrates how you can embed JavaScript code in a WeBWorK example +to create an interactive homework problem that could never be provided by a text book. +$PAR +WeBWorK can use existing $BBOLD JavaScript$EBOLD and $BBOLD Java $EBOLD code to augment its capabilities. +$HR +$PAR +By typing any value x into the left hand window and pressing the --f--\(>\) button +you can determine the value of f(x). +$PAR +Using this 'oracle' function, calculate the derivative of \( f \) at x=$x0. +$PAR +\(f'($x0) =\) \{ans_rule(20) \} You can use a +\{htmlLink(alias('calc.html'), "calculator" ,q!TARGET = "calculator"!) \} + +$PAR +END_TEXT + +$javaScript =< + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + +ENDOFSCRIPT + +TEXT(M3( + " \fbox{ The java Script calculator was displayed here + }", + "\begin{rawhtml} $javaScript \end{rawhtml}", + $javaScript + )); + + + +ANS(std_num_cmp($ans,1,"%0.14g") ); #We are allowing 1 percent error for the answer. + + +&ENDDOCUMENT; + diff --git a/courses.dist/modelCourse/templates/setDemo/prob6b.pg b/courses.dist/modelCourse/templates/setDemo/prob6b.pg new file mode 100644 index 0000000000..74a2fd86f4 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/prob6b.pg @@ -0,0 +1,88 @@ +DOCUMENT(); + +loadMacros("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); +BEGIN_TEXT +To see a different version of the problem change +the problem seed and press the 'Submit Answer' button below.$PAR Problem Seed: +\{ MODES( +TeX => qq! Change the problem seed to change the problem:$problemSeed!, +Latex2HTML => qq! Change the problem seed to change the problem: + \begin{rawhtml} + + \end{rawhtml}!, +HTML => qq! ! +) +\} +END_TEXT +#########################################################>>> +$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 +This problem illustrates how you can Java applets in a WeBWorK example. +$PAR +This polar coordinate grapher was constructed at the Mathematics Department +of The Johns Hopkins University and the applet is being served from their computer. +$PAR +WeBWorK can use existing $BBOLD JavaScript$EBOLD and $BBOLD Java $EBOLD code to +augment its capabilities. +$HR +END_TEXT +TEXT(MODES( +TeX => "\fbox{The Johns Hopkins University Mathematics Department's + polar graph plotting applet goes here}", +HTML => qq{ + + + + + + +}, +Latex2HTML => qq!\begin{rawhtml} + + + + + + \end{rawhtml} +! +)); + +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 + +END_TEXT + +ANS(num_cmp($p) ); + +ENDDOCUMENT(); + diff --git a/courses.dist/modelCourse/templates/setDemo/s2_2_1.pg b/courses.dist/modelCourse/templates/setDemo/s2_2_1.pg new file mode 100644 index 0000000000..eee730ee7d --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/s2_2_1.pg @@ -0,0 +1,56 @@ +##DESCRIPTION +##KEYWORDS('derivatives') +## Find a derivative of a polynomial, evaluate it at a point +##ENDDESCRIPTION + +DOCUMENT(); # This should be the first executable line in the problem. +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl" +); + +TEXT(&beginproblem); +$showPartialCorrectAnswers = 1; + +$a1 = random(2,7,1); +$b1 = random(2,12,1); +$c1 = random(1,40,1); +$x1 = random(1,5,1); +$deriv1 = 2*$a1*$x1-$b1; +$funct1 = "2*$a1*x-$b1"; + +BEGIN_TEXT +If \( f(x) = $a1 x^2 - $b1 x -$c1 \), find \( f'( x ) \). +$BR $BR \{ans_rule(48) \} +$BR +END_TEXT + +$ans = $funct1; +&ANS(fun_cmp($ans)); + +$a1_2 = 2*$a1; +&SOLUTION(EV3(<<'EOT')); +$SOL $BR +In general the derivative of \( x^n \) is \( nx^{n-1} \). Using this (and the +basic sum and constant multiple rules) we find the derivative of $BR +\(${a1}x^2 - ${b1}x -$c1 \quad \) is $BR \( ${a1_2}x - $b1 \).$BR $BR +EOT + +BEGIN_TEXT +Find \( f'( $x1 ) \). +$BR $BR \{ans_rule(48) \} +$BR $BR +END_TEXT + +$ans = $deriv1; +&ANS(num_cmp($ans)); + +&SOLUTION(EV3(<<'EOT')); +$SOL $BR +To find the derivative we just have to evaluate \( f'( x ) \) at +\( x = $x1) \), i.e. \( ${a1_2}\cdot$x1 - $b1 \) or $ans. +EOT + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setDemo/sample_myown_ans.pg b/courses.dist/modelCourse/templates/setDemo/sample_myown_ans.pg new file mode 100644 index 0000000000..8153a8f3e9 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/sample_myown_ans.pg @@ -0,0 +1,86 @@ +DOCUMENT(); + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" +); + +TEXT(&beginproblem); +$showPartialCorrectAnswers = 1; + +BEGIN_TEXT + This problem demonstrates how you can write your own procedure to check answers. + The procedure is embedded right in the problem. If you wanted to use it for several + problems, you could put it in a file similar to "PGanswermacros.pl" and load it into + the problem. + + This problem asks you to enter a palindrome, a word, number, or phrase that is the same + when read backwards or forward. For example, madam or Hannah. For us a standard + palindrome will ignore spaces and case, but a strict palindrome will not. So e.g. Hannah + is a standard but not a strict palindrome. We will write a test for both types. $BR $BR + Enter a standard palindrome such as "Hannah", "1234321", or "Mom". $BR + This uses std${US}palindrome${US}test $BR + \{ans_rule(60) \} +END_TEXT + + +$std_palindrome_test = sub { + my $in = shift @_; + my $normalizedCorrectAnswer = "There are many correct answers, e.g. Hannah"; + $in =~ s|~~s+||g; # remove all spaces + ## use ~~ inplace of perl's backslash in problems + $in = uc $in; # Make letters uppercase + ## use ~~ inplace of perl's backslash in problems + my $reverse = reverse $in; + my $correctQ = ($in eq $reverse) ? 1: 0; + my $ansMsg = ''; + unless ($in =~ m|~~S|) { + $correctQ = 0; + $ansMsg = 'An empty string is not accepted as a palindrome'; + } + my $rh_answer = new AnswerHash( score => $correctQ, + correct_ans => $normalizedCorrectAnswer, + student_ans => $in, + ans_message => $ansMsg, + type => 'custom' + ); + $rh_answer; + +}; + +ANS($std_palindrome_test); + + +BEGIN_TEXT; +$PAR +Now enter a strict palindrome such as "1234321", or "mom". $BR +This uses strict${US}palindrome${US}test $BR +\{ans_rule(60) \} +END_TEXT + +$strict_palindrome_test = sub { + my $in = shift @_; + my $normalizedCorrectAnswer = "There are many correct answers, e.g. HannaH"; + $in =~ s/~~s*$//; # remove trailing whitespace ## use ~~ inplace of perl's backslash in problems + $in =~ s/^~~s*//; # remove initial spaces ## use ~~ inplace of perl's backslash in problems + my $reverse = reverse $in; + my $correctQ = ($in eq $reverse) ? 1: 0; + my $ansMsg = ''; + unless ($in =~ m|~~S|) { + $correctQ = 0; + $ansMsg = 'An empty string is not accepted as a palindrome'; + } + + my $rh_answer = {score => $correctQ, + correct_ans => $normalizedCorrectAnswer, + student_ans => $in, + ans_message => $ansMsg, + type => 'custom' + }; + $rh_answer; +}; + +ANS($strict_palindrome_test); + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/sample_units_ans.pg b/courses.dist/modelCourse/templates/setDemo/sample_units_ans.pg new file mode 100644 index 0000000000..4cc2074e80 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/sample_units_ans.pg @@ -0,0 +1,41 @@ +DOCUMENT(); +loadMacros( "PGbasicmacros.pl", + "PGauxiliaryFunctions.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +TEXT(beginproblem()); + +$showPartialCorrectAnswers = 1; +$showHint =0; + +$fx = random( 2.1, 6.0 , 0.1) ; +$fy = random( 3.1, 8.0 , 0.1) ; +$ansxy = sqrt($fx * $fx + $fy * $fy); +$anscm = $ansxy*100; + +BEGIN_TEXT +This problem demonstrates how WeBWorK handles +numerical answers involving units. WeBWorK can handle all units that +are used in elementary physics courses. +See \{ htmlLink("http://webwork.maa.org/wiki/Units","answers with units") \} +for more details. $PAR + +Two perpendicular sides of a triangle are $fx m and +$fy m long respectively. +What is the length of the third side of the triangle? $BR$BR +You can answer this in terms of m's, cm's, km's, in's, ft, etc. but you must enter the units. $BR$BR +Check "Show Hint" and then "Submit Answer" if you don't remember the Pythagorean theorem. +END_TEXT + +HINT(EV3(<<'EOT')); +Remembering the Pythagorean theorem \( A^2 +B^2 = C^2 \), you can enter +sqrt(${fx}${CARET}2 + ${fy}${CARET}2) m or \{spf($ansxy, "%0.2f" )\} m or \{spf($anscm, "%0.2f" )\} cm or ... +EOT + +BEGIN_TEXT +\{ans_rule(40) \} +END_TEXT +ANS(num_cmp("$ansxy", units => 'm')); +ENDDOCUMENT() diff --git a/courses.dist/modelCourse/templates/setDemo/screenHeaderFile1.pg b/courses.dist/modelCourse/templates/setDemo/screenHeaderFile1.pg new file mode 100644 index 0000000000..6df115aab1 --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/screenHeaderFile1.pg @@ -0,0 +1,77 @@ +##Screen set header for set 0, Fall 1998 + +&DOCUMENT; + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl" +); + + + +BEGIN_TEXT +This is a demonstration set designed to illustrate the range of types of questions which can be asked using WeBWorK rather than to illustrate a typical calculus problem set. + +$PAR +$BBOLD 1. Simple numerical problem. $EBOLD A simple problem requiring a numerical answer. It illustrates how one can allow WeBWorK to calculate answers from formulas (e.g. an answer such as sqrt(3^2 +4^2) can be entered instead of the answer 5.). It also shows +an example of feedback on the correctness of each answer, rather than grading the entire problem. +$PAR +$BBOLD 2. Graphs and limits. $EBOLD The graph in this example is constructed on the fly. From the graph a student is supposed to determine the values and limits of the function at various points. The immediate feedback on this problem is particularly useful, since students often make unconcious mistakes. +$PAR +$BBOLD 3. Derivatives. $EBOLD An example of checking answers which are formulas, rather than numbers. +$PAR +$BBOLD 4. Anti-derivatives. $EBOLD This example will accept any anti-derivative, adjusting for the fact that the answer is only defined up to a constant. +$PAR +$BBOLD 5. Answers with units. $EBOLD Try entering the answer to this question in meters (m) and also centimeters (cm). +$PAR +$BBOLD 6. A physics example. $EBOLD Includes a static picture. +$PAR +$BBOLD 7. More graphics. $EBOLD An example of on-the-fly graphics. Select the graph of f, it's derivative and it's second derivatives. +$PAR +$BBOLD 8. JavaScript example. $EBOLD I'm particularly fond of this example. The computer provides an "oracle" function: give it a number \(x\) and it will provide you with the value \(f(x)\) of the function at \(x\). Using this, calculate the value of the derivative of \(f\) at 2. (i.e. \(f'(2)\) ). Students are forced to use the Newton quotient, since there are no formulas to work with. I don't think this problem could be asked as written homework. +$PAR +$BBOLD 9. Java example. $EBOLD This gives an example of incorporating a java applet which can be used experimentally to determine answers for WeBWorK questions. This example is of historical interest since it comes from the first site after Rochester, Johns Hopkins University, to use WeBWorK. It currently gives an example of what happens when a WeBWorK problem called an applet residing on a server that no longer exists. +$PAR +$BBOLD 10. Palindrome. $EBOLD To answer this problem enter any palindrome. This problem illustrates the power of the "answer-evaluator" model. For each problem the problem designer writes a function which accepts a student's answer and produces a 0 or 1 (for incorrect or correct). Usually this is done by comparing with an answer given by the problem designer, but in this case the function checks if the answer is the same forward and backward. +$PAR +END_TEXT + + + +BEGIN_TEXT +$HR + +Use this box to give information about this problem +set. Typical information might include some of these facts: +$PAR +WeBWorK assignment number $setNumber is due on : $formatedDueDate. + + +$PAR +The primary purpose of WeBWorK is to let you know if you are getting the right answer or to alert +you if you get the wrong answer. 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 + +You can use the Feedback button on each problem +page to send e-mail to the professors. +$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^3 instead of 8, sin(3*pi/2) instead +of -1, e^(ln(2)) instead of 2, +(2+tan(3))*(4-sin(5))^6-7/8 instead of 27620.3413, etc. +$PAR + Here's the +\{ htmlLink(qq!http://webwork.maa.org/wiki/Available_Functions!,"list of the functions") \} + which WeBWorK understands. + +Along with the \{htmlLink(qq!http://webwork.maa.org/wiki/Units!, "list of units")\} which WeBWorK understands. This can be useful in +physics problems. +END_TEXT + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setDemo/srw1_9_4.pg b/courses.dist/modelCourse/templates/setDemo/srw1_9_4.pg new file mode 100644 index 0000000000..03f9e5a2fa --- /dev/null +++ b/courses.dist/modelCourse/templates/setDemo/srw1_9_4.pg @@ -0,0 +1,112 @@ +##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. + + diff --git a/courses.dist/modelCourse/templates/setMAAtutorial.def b/courses.dist/modelCourse/templates/setMAAtutorial.def new file mode 100644 index 0000000000..37b52ee624 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial.def @@ -0,0 +1,23 @@ +openDate = 1/10/97 at 6:00am +dueDate = 1/1/05 at 2:00am +answerDate = 1/1/05 at 2:00am +paperHeaderFile = setMAAtutorial/MAAtutorialSetHeader.pg +screenHeaderFile = setMAAtutorial/MAAtutorialSetHeader.pg +problemList = +setMAAtutorial/hello.pg, 1 +setMAAtutorial/standardexample.pg, 1 +setMAAtutorial/simplemultiplechoiceexample.pg, 1 +setMAAtutorial/multiplechoiceexample.pg, 1 +setMAAtutorial/matchinglistexample.pg, 1 +setMAAtutorial/truefalseexample.pg, 1 +setMAAtutorial/popuplistexample.pg, 1 +setMAAtutorial/ontheflygraphicsexample1.pg, 1 +setMAAtutorial/ontheflygraphicsexample2.pg, 1 +setMAAtutorial/staticgraphicsexample/staticgraphicsexample.pg, 1 +setMAAtutorial/hermitegraphexample.pg, 1 +setMAAtutorial/htmllinksexample/htmllinksexample.pg, 1 +setMAAtutorial/javascriptexample1.pg, 1 +setMAAtutorial/javascriptexample2.pg, 1 +setMAAtutorial/vectorfieldexample.pg, 1 +setMAAtutorial/conditionalquestionexample.pg, 1 +setMAAtutorial/javaappletexample.pg, 1 diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/MAAtutorialSetHeader.pg b/courses.dist/modelCourse/templates/setMAAtutorial/MAAtutorialSetHeader.pg new file mode 100644 index 0000000000..610ded5be0 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/MAAtutorialSetHeader.pg @@ -0,0 +1,102 @@ +DOCUMENT(); + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" +); + +TEXT($BEGIN_ONE_COLUMN); + +TEXT(MODES(TeX =>EV3(<<'EOT'), HTML=>"", Latex2HTML=>"" )); +\noindent {\large \bf $studentName} +\hfill +\noindent {\large \bf MAA Minicourse New Orleans January 2001} +\par +\noindent WeBWorK assignment number \{ protect_underbar($setNumber) \} due $formattedDueDate;. +\hrule +EOT + + +################## +# EDIT BELOW HERE +################## + +BEGIN_TEXT +$BR +$BR +Welcome to the MAA short course on $BBOLD WeBWorK $EBOLD. +$PAR +Here is a synopsis of the tutorial examples presented in this set. They have been designed for learning the PG language, and are not necessarily the best questions to use for mathematics instruction. +$PAR +$BBOLD 1. Hello world example: $EBOLD Illustrates the basic structure of a PG problem. +$PAR +$BBOLD 2. Standard example: $EBOLD This covers what you need to know to ask the majority of the questions you would want to ask in a calculus course. Problems with text answers, numerical answers and answers involving expressions are covered. +$PAR +$BBOLD 3. Simple multiple choice example: $EBOLD Uses lists(arrays) to implement a multiple choice question. +$PAR +$BBOLD 4. Multiple choice example: $EBOLD Uses the multiple choice object to implement a multiple choice question. +$PAR +$BBOLD 5. Matching list example: $EBOLD +$PAR +$BBOLD 6. True/false example: $EBOLD +$PAR +$BBOLD 7. Pop-up true/false example: $EBOLD Answers are chosen from a pop-up list. +$PAR +$BBOLD 8. On-the-fly graphics example 1: $EBOLD The graphs are regenerated each time you press the submit button +$PAR +$BBOLD 9. On-the-fly-graphics example 2: $EBOLD -- Adds some randomization to the first example. +$PAR +$BBOLD 10. Static graphics example: $EBOLD Presents graphs created on a separate application (e.g. Mathematica) and saved. +$PAR +$BBOLD 11. Hermite graph example: $EBOLD A particularly useful way of generating predictable graphs by specifying the value and first derivative of a function at each point. Piecewise linear graphs are also included in this example. +$PAR +$BBOLD 12. HTML links example: $EBOLD Shows how to link other web resources to your WeBWorK problem. +$PAR +$BBOLD 13. JavaScript example 1: $EBOLD An example which takes advantage of this interactive media! This one requires students to calculate the derivative of a function from the definition. +$PAR +$BBOLD 14. JavaScript example 2: $EBOLD A variant of the previous example that generates the example function as a cubic spline so that students can't read the javaScript code to find out the answer. +$PAR +$BBOLD 15. Vector field example $EBOLD Generates vector field graphs on-the-fly. +$PAR +$BBOLD 16. Conditional question example: $EBOLD Illustrates how you can create a problem which first asks an easy question, and once that has been answered correctly, follows up with a more involved question on the same material. +$PAR +$BBOLD 17 Java applet example: $EBOLD A preliminary example of how to include Java applets in WeBWorK problems. +$HR +END_TEXT + +################## +# EDIT ABOVE HERE +################## +BEGIN_TEXT +The primary purpose of WeBWorK is to let you know if you are getting the right answer or to alert +you if you get the wrong answer. 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. +The computer has NO CLUE about WHY your answer is wrong. Computers are good at checking, +but for help go to a human. + +$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. +$PAR + Here's the +\{ htmlLink(qq!http://webwork.maa.org/wiki/Available_Functions!,"list of the functions") \} + which WeBWorK understands. + +Along with the \{htmlLink(qq!http://webwork.maa.org/wiki/Units!, "list of units")\} which WeBWorK understands. This can be useful in +physics problems. +$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. + diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/conditionalquestionexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/conditionalquestionexample.pg new file mode 100644 index 0000000000..affe1f9138 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/conditionalquestionexample.pg @@ -0,0 +1,83 @@ +DOCUMENT(); +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" +); +TEXT(beginproblem(), $BR,$BBOLD, "Conditional questions example", $EBOLD, $BR,$BR); +$showPartialCorrectAnswers = 1; + +$a1 = random(3,25,1); +$b1 = random(2,27,1); +$x1 = random(-11,11,1); +$a2 = $a1+5; + +BEGIN_TEXT +If \( f(x) = $a1 x + $b1 \), find \( f'( $x1 ) \). +$BR $BR \{NAMED_ANS_RULE('first_answer',10) \} +$BR +END_TEXT + + + +$ans_eval1 = num_cmp($a1); +NAMED_ANS(first_answer => $ans_eval1); + +# Using named answers allows for more control. Any unique label can be +# used for an answer. +# (see http://webwork.math.rochester.edu/docs/docs/pglanguage/pgreference/managinganswers.html +# for more details on answer evaluator formats and on naming answers +# so that you can refer to them later. Look also at the pod documentation in +# PG.pl and PGbasicmacros.pl which you can also reach at +# http://webwork.math.rochester.edu/docs/techdescription/pglanguage/index.html) + +# Check to see that the first answer was answered correctly. If it was then we +# will ask further questions. +$first_Answer = $inputs_ref->{first_answer}; # We need to know what the answer + # was named. +$rh_ans_hash = $ans_eval1->evaluate($first_Answer); + +# warn pretty_print($rh_ans_hash); # this is useful technique for finding errors. + # When uncommented it prints out the contents of + # the ans_hash for debugging + +# The output of each answer evaluator consists of a single %ans_hash with (at +# least) these entries: +# $ans_hash{score} -- a number between 0 and 1 +# $ans_hash{correct_ans} -- The correct answer, as supplied by the instructor +# $ans_hash{student_ans} -- This is the student's answer +# $ans_hash{ans_message} -- Any error message, or hint provided by +# the answer evaluator. +# $ans_hash{type} -- A string indicating the type of answer evaluator. +# -- Some examples: +# 'number_with_units' +# 'function' +# 'frac_number' +# 'arith_number' +# For more details see +# http://webwork.math.rochester.edu/docs/docs/pglanguage/pgreference/answerhashdataype.html + +# If they get the first answer right, then we'll ask a second part to the +# question ... +if (1 == $rh_ans_hash->{score} ) { + + # WATCH OUT!!: BEGIN_TEXT and END_TEXT have to be on lines by + # themselves and left justified!!! This means you can't indent + # this section as you might want to. The placement of BEGIN_TEXT + # and END_TEXT is one of the very few formatting requirements in + # the PG language. + +BEGIN_TEXT + $PAR Right! Now + try the second part of the problem: $PAR $HR + If \( f(x) = $a2 x + \{$b1+5\} \), find \( f'( x) \). + $BR $BR \{ NAMED_ANS_RULE('SecondAnSwEr',10) \} + $BR +END_TEXT + +$ans_eval2 = num_cmp($a2); + + NAMED_ANS(SecondAnSwEr => $ans_eval2); + +} +ENDDOCUMENT(); diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/hello.pg b/courses.dist/modelCourse/templates/setMAAtutorial/hello.pg new file mode 100644 index 0000000000..a27388830d --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/hello.pg @@ -0,0 +1,18 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + ); + +BEGIN_TEXT +Complete the sentence: $PAR +\{ ans_rule(20) \} world! +END_TEXT + +ANS( str_cmp( "Hello" ) ); # here is the answer, a string. + + + + +ENDDOCUMENT(); + \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/hermitegraphexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/hermitegraphexample.pg new file mode 100644 index 0000000000..0556dec368 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/hermitegraphexample.pg @@ -0,0 +1,126 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGnumericalmacros.pl", + "PGgraphmacros.pl" +); +TEXT($BEGIN_ONE_COLUMN); +TEXT(beginproblem(), $BR,$BBOLD, "Hermite polynomial graph example", $EBOLD, $BR,$BR); +$showPartialAnswers = 1; + +$graph = init_graph(-5,-5,5,5,'axes'=>[0,0],'grid'=>[10,10]); + +my (@x_values1, @y_values1); +foreach $i (0..10) { + $x_values1[$i] =$i-5; + $y_values1[$i] = random(-4,4,1); +} + +# creates a reference to a perl subroutine for the piecewise linear function +# passing through the defined points +$fun_rule = plot_list(~~@x_values1, ~~@y_values1); + +#new function is to be plotted in graph +$f1=new Fun($fun_rule, $graph); +$f1->color('black'); + +$trans = non_zero_random(-2,2,1); +# add a new function to the graph which is a translate of the first +$fun_rule2 = sub{ my $x = shift; &$fun_rule($x-$trans) }; +$f2 = new Fun($fun_rule2, $graph); +$f2->color('orange'); + +$graph->stamps(open_circle(-1,&$fun_rule(-1),'black') ); +# indicates open interval at the left endpoint +$graph->stamps(closed_circle(4,&$fun_rule(4), 'black') ); +# and a closed interval at the right endpoint +# Be careful about getting the stamps properly located on the translated +# function below: +$graph->stamps(open_circle(-1 + $trans, &$fun_rule(-1),'orange') ); +# indicates open interval at the left endpoint +$graph->stamps(closed_circle(4 +$trans, &$fun_rule(4), 'orange') ); +# and a closed interval at the right endpoint + +$graph2 = init_graph(-4,-4,4,4,'axes'=>[0,0],'grid'=>[8,8]); +$b1= random(-3.5,3.5,.5); +$b2= random(-3.5,3.5,.5); +$b3= random(-3.5,3.5,.5); +@x_val3 = (-4,-3,-2,-1, 0, 1, 2, 3, 4 ); +@y_val3 = ( 0, 1, 2, 0,$b1, $b2, $b3, 1, 2 ); +@yp_val3= ( .1, 1, 0,-2, 0, 1, 2, -3, 1 ); +$hermite = new Hermite( + ~~@x_val3, # x values + ~~@y_val3, # y values + ~~@yp_val3 # y prime values + ); +$spline_rule = $hermite->rf_f; +$f3 = new Fun($spline_rule, $graph2); +$f3->color('green'); +$graph2->stamps(closed_circle(-4, &$spline_rule(-4), 'green') ) ; +$graph2->stamps(closed_circle( 4, &$spline_rule( 4), 'green') ) ; + +# Insert the graphs and the text. +BEGIN_TEXT + +$PAR +We have developed other ways to specify graphs which are to be created 'on the fly'. +All of these new methods consist of adding macro packages to WeBWorK. Since they +do not require the core of WeBWorK to be changed, these enhancements can be added by +anyone using WeBWorK. +$PAR + These two piecewise linear graphs were created by specifying the points at the nodes. + $BR Click on the graph to view a larger image. +$PAR +\{ image(insertGraph($graph),tex_size => 300, width=> 300, height=> 300 ) \} +$HR +If the black function is written as \(f(x)\), then the orange function +would be written as \( f( \) \{ ans_rule \} \( ) \). +\{ANS(function_cmp("x-$trans")),'' \} +END_TEXT +# $PAR +# The numerical calculations were all written in Perl using +# numerical routines adapted from the Numerical Analysis book by Burden and Faires. +# $BR +# We are also working on a macro which will automatically +# identify the maximum, minimum and inflection points of an arbitary hermite +# cubic spline from its specifying values. This will allow automatic generation +# of problems in which the maximum, minimum and inflection points are to be +# deduced from a graph. +# +# Get the internal local maximums +@critical_points = keys %{$hermite->rh_critical_points}; +@critical_points = num_sort( @critical_points); +@minimum_points = (); +foreach my $x (@critical_points) { + push(@minimum_points, $x) if &{$hermite->rf_fpp}($x) >0 ; +} +# TEXT(pretty_print(~~@minimum_points)); # (for debugging purposes) +$answer_string = ""; +foreach my $x (@minimum_points) { + $answer_string .= EV2(' \{ ans_rule(10) \} '); +} + +BEGIN_TEXT +$HR +This graph was created using a hermite spline by specifying points at + +\{ begintable(1+scalar( @x_val3 ) ) \} +\{ row('x', @x_val3)\} +\{ row('y', @y_val3) \} +\{ row('yp',@yp_val3) \} +\{endtable() \} + +$PAR +\{ begintable(2) \} +\{row( image(insertGraph($graph2), tex_size => 300,width=>300, height=> 300), + "List the internal local minimum points $BR in increasing order: $BR $answer_string" + ) \} +\{ endtable() \} + +$PAR +END_TEXT +ANS(num_cmp([ @minimum_points ], tol => .3)); + +TEXT($END_ONE_COLUMN); +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-24438.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-24438.gif new file mode 100644 index 0000000000..df1f85cbd2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-24438.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-31126.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-31126.gif new file mode 100644 index 0000000000..b0ad26bb6e Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-31126.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-34859.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-34859.gif new file mode 100644 index 0000000000..702784afdd Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-34859.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-42639.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-42639.gif new file mode 100644 index 0000000000..dfebffd690 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-42639.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-76239.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-76239.gif new file mode 100644 index 0000000000..741fdd8452 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-76239.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-89540.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-89540.gif new file mode 100644 index 0000000000..aa9a75d45a Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-89540.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-91734.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-91734.gif new file mode 100644 index 0000000000..cd5aae4ebd Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-91734.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-96355.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-96355.gif new file mode 100644 index 0000000000..23ac97828b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1-96355.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1.gif new file mode 100644 index 0000000000..073055392b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/1.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-42653.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-42653.gif new file mode 100644 index 0000000000..709cd644e2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-42653.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-49261.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-49261.gif new file mode 100644 index 0000000000..3767c0de47 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-49261.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-54427.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-54427.gif new file mode 100644 index 0000000000..11c71d8010 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-54427.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-62384.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-62384.gif new file mode 100644 index 0000000000..eae5ade6ae Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-62384.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-64591.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-64591.gif new file mode 100644 index 0000000000..4bd408e32f Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-64591.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-70190.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-70190.gif new file mode 100644 index 0000000000..3e646a41ac Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-70190.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-81779.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-81779.gif new file mode 100644 index 0000000000..944f3efcf6 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-81779.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-92879.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-92879.gif new file mode 100644 index 0000000000..11506db3bc Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2-92879.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2.gif new file mode 100644 index 0000000000..b85a6d3ad3 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/2.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14197.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14197.gif new file mode 100644 index 0000000000..c28c1b68ee Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14197.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14538.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14538.gif new file mode 100644 index 0000000000..c57d2fbe84 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-14538.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-37616.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-37616.gif new file mode 100644 index 0000000000..a9c2db85c3 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-37616.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-46739.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-46739.gif new file mode 100644 index 0000000000..4244a8ca61 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-46739.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-52898.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-52898.gif new file mode 100644 index 0000000000..afe8cd9d75 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-52898.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-68458.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-68458.gif new file mode 100644 index 0000000000..8690563535 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-68458.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-89262.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-89262.gif new file mode 100644 index 0000000000..5c954beec1 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-89262.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-99389.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-99389.gif new file mode 100644 index 0000000000..f948cc3e83 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3-99389.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3.gif b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3.gif new file mode 100644 index 0000000000..5f8f80e7d8 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/3.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/htmllinksexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/htmllinksexample.pg new file mode 100644 index 0000000000..708fb789a6 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/htmllinksexample/htmllinksexample.pg @@ -0,0 +1,80 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" +); +$showPartialCorrectAnswers = 0; + +TEXT(beginproblem(), $BR,$BBOLD, "HTML links example", $EBOLD, $BR,$BR); + +BEGIN_TEXT +This example shows how to link to resources outside the problem itself. +$PAR +Linking to other web pages over the internet is easy. For example, +you can get more information about the buffon needle problem and how it is used by ants to find new nest sites by linking to + \{ htmlLink("http://www.maa.org/mathland/mathtrek_5_15_00.html", + "Ivars Peterson's column on the MAA site") \}. +$PAR +END_TEXT + +# You can write the HTML code yourself, but +# that will look funny when the problem is printed in +# hard copy, so it is probably better to use the +# htmlLink('url','text') function which +# will create something readable when the problem is printed. + +BEGIN_TEXT +All of the files in the html directory of your WeBWorK course site can be read +by anyone with a web browser and the URL (the address of the file). This is a good +place to put files that are referenced by more than one problem in your WeBWorK course. +$PAR +Here is the link to +the +\{ htmlLink(alias("${htmlDirectory}calc.html"), + 'to the calculator page', + qq!target="ww_calculator" + ONCLICK="window.open( this.href, this.target, + 'width=250,height=350,scrollbars=no,resizable=off' + )" +!) \} +stored in the top level of the +html directory of the tutorialCourse. +$PAR +END_TEXT + +# To link to files on your own computer use the alias function whose +# job it is to find the file in question. +# You need to do this access indirectly, because WeBWorK is set up to +# restrict access to most files -- (you don't want everyone reading +# the source text of the WeBWorK problems, they could reconstruct the answer.) +# +# Note that you need double quotes around "${htmlDirectory}calc.html" so that +# the string in $htmlDirectory will be +# concatenated with calc.html to form a string describing +# the DIRECTORY in which the file is to be found. Alias converts +# the directory to a URL + +BEGIN_TEXT +Finally there are files, such as picture files, which are +stored with the problem itself in the same directory. + $BR \{ image("2-70190.gif", width=>200, height=>200) \} + +END_TEXT + +# Image automatically uses alias +# to search for files. + +BEGIN_TEXT +$PAR +And the table below has three more graphs which are stored +in the directory containing the current problem. $PAR +END_TEXT + +TEXT( + begintable(3), + row( image( [ ( '1-24438.gif', '2-49261.gif', '3-37616.gif') ], + tex_size=>200, width=>200, height=>200 )), + endtable() +); + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/javaappletexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/javaappletexample.pg new file mode 100644 index 0000000000..fee0411cbc --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/javaappletexample.pg @@ -0,0 +1,95 @@ +DOCUMENT(); + +loadMacros("PG.pl", + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + ); + +TEXT(beginproblem(), $BR,$BBOLD, "Java applet example", $EBOLD, $BR,$BR); +# define function to be evaluated +$a= random(1,3,1); +$b= random(-4,4,.1); +$c = random(-4,4,1); +$x0=random(-2,2,1); +$function = FEQ(" ${a}x^2+${b}x +$c "); # This function will be redefined for javaScript as well. +sub fp { # define a subroutine to calculate the derivative + my $x = shift; + 2*$a*$x+$b; +} +$ans = fp($x0); + +BEGIN_TEXT +$PAR +This problem illustrates how you can embed Java applet code in a WeBWorK example +to create an interactive homework problem that could never be provided by a text book. +$PAR +WeBWorK can use existing $BBOLD javaScript$EBOLD and $BBOLD Java $EBOLD +code to augment its capabilities. +$HR + +END_TEXT +$javaApplet = < + + + + + + + +

+mathbean applet from David Ecks +
+EOF +# only print out the java applet code when viewing on the screen +TEXT(MODES( + TeX => " \fbox{ The java applet was displayed here + }", + HTML => $javaApplet, +)); + +$a1= random(-3,3,.5); +$a2= random(-3,3,.5); +$a3= random(-3,3,.5); +$b1 = ($a1/2)**2; # remember to use ** for exponentiation when + # calculating in pure Perl! +$b2= ($a2 / 2)**2; +$b3 = ($a3 / 2)**2; + +ANS( num_cmp( $b1, reltol => 10, format=>'%0.2g')); +ANS( num_cmp( $b2, reltol => 10, format=>'%0.2g')); +ANS( num_cmp( $b3, reltol => 10, format=>'%0.2g')); + +BEGIN_TEXT + +$PAR +The graph above represents the function +\[f(x) = x^2 + a x +b \] +where \( a \) and \( b \) are parameters. $PAR + +For each value of \( a \) find the value of \( b \) which +makes the graph just touch the x-axis. +$BR +if a= $a1 then \{ ans_rule(10) \}$BR +if a= $a2 then \{ ans_rule(10) \}$BR +if a= $a3 then \{ ans_rule(10) \} $PAR + +Does this relationship between a and b specify b as a function of a? + \{ ans_rule(4) \} (Yes or No)$BR + +Does this relationship between a and b specify a as a function of b? + \{ ans_rule(4) \} (Yes or No)$BR + +Write a formula for calculating this value of \( b \) from \( a \).$BR +b = \{ ans_rule(40) \} + +END_TEXT +ANS(str_cmp('Yes') ); +ANS(str_cmp('No') ); +ANS(function_cmp( '(a/2)^2', 'a') ); + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample1.pg b/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample1.pg new file mode 100644 index 0000000000..1f76031c1b --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample1.pg @@ -0,0 +1,146 @@ +DOCUMENT(); + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); +TEXT(beginproblem(), $BR,$BBOLD, "JavaScript Example 1", $EBOLD, $BR,$BR); +# define function to be evaluated +$a= random(1,3,1); +$b= random(-4,4,.1); +$c = random(-4,4,1); +$x0=random(-2,2,1); + +# function = ${a}x^2+${b}x +${c} +# This is just to provide the correct answer. +# This function will be defined for javaScript below. +sub fp { # define a perl subroutine to calculate the derivative + my $x = shift; + 2*$a*$x+$b; +} +$ans = fp($x0); + +## This text will be placed in the header section of the HTML page +## not in the body where TEXT output is placed. +## Not processing is done. + +HEADER_TEXT(< + + + +EOF + +TEXT(beginproblem()); +TEXT(MODES( TeX => "", + Latex2HTML => "\begin{rawhtml} + ~~n\end{rawhtml} + ", + HTML_tth => "~~n", + HTML => "~~n" +)); + +$functionArrow = MODES( + TeX => "\(- f\rightarrow\)", + Latex2HTML => "\(- f\rightarrow \) ", + HTML_tth => "-- f -- >   ", + HTML => '-- f -- >   ' +); + +# The following string contains a combination of HTML and javaScript +# which displays the input table for the javaScript calculator + +$javaScript =< + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + +ENDOFSCRIPT + + + +BEGIN_TEXT + +Find the derivative of the function f(x). The windows below will tell +you the value of f for any input x. (I call this an "oracle function", since +if you ask, it will tell.) +$PAR +\(f '( $x0 ) \) = \{ans_rule(50 ) \} +$PAR +You may want to use a +\{ htmlLink(alias("${htmlDirectory}calc.html"), + 'calculator', + qq! TARGET = "ww_calculator" + ONCLICK="window.open( this.href,this.target, + 'width=200, height=350, scrollbars=no, resizable=off' + )" +!) \} + +to find the result. + You can also enter numerical expressions and have + WeBWorK do the calculations for you. +END_TEXT + +# Here is where we actually print the javaScript, or alternatives for printed output. + +TEXT(MODES( + TeX => " \fbox{ The java Script calculator was displayed here + }", + HTML => $javaScript, + )); + +ANS(num_cmp($ans,reltol => 1) ); #We are allowing 1 percent error for the answer. + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample2.pg b/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample2.pg new file mode 100644 index 0000000000..898f77e842 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/javascriptexample2.pg @@ -0,0 +1,154 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGnumericalmacros.pl", # needed for the javaScript spline code + ); +TEXT(beginproblem(), $BR,$BBOLD, "JavaScript Example 2", $EBOLD, $BR,$BR); + +# define function to be evaluated +$a= random(1,3,1); +$b= random(-4,4,.1); +$c = random(-4,4,1); +$x0=random(-2,2,1); + +# function = ${a}x^2+${b}x +${c}sin(x) +# This is just to provide the correct answer. +# This function will be defined for javaScript below. +sub fp { # define a perl subroutine to calculate the derivative + my $x = shift; + 2*$a*$x+$b; +} +$ans = fp($x0); + +# approximate the function by a cubic spline +sub fun{ + my $x = shift; + ${a}*$x**2+$b*$x +$c; +} +@x = (); +@y = (); +for ( $x1 = -3; $x1<3; $x1 = $x1+.1) { + push(@x, $x1); + push(@y, fun($x1) ); +} +#warn join(" ", @x) ; # test the calculation of the data points +#warn join(" ", @y) ; +$javascript= javaScript_cubic_spline(~~@x, ~~@y, name =>'func'); + +#$javascript =~s/'func') ); + + +TEXT(beginproblem()); +TEXT(MODES( TeX => "", + Latex2HTML => "\begin{rawhtml} + ~~n\end{rawhtml} + ", + HTML_tth => "~~n", + HTML => "~~n" +)); + +$functionArrow = MODES( + TeX => "\(- f\rightarrow\)", + Latex2HTML => "\(- f\rightarrow \) ", + HTML_tth => "-- f -- >   ", + HTML => '-- f -- >   ' +); + +# The following string contains a combination of HTML and javaScript +# which displays the input table for the javaScript calculator + +$javaScript =< + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + +ENDOFSCRIPT + +BEGIN_TEXT +Find the derivative of the function f(x). The windows below will tell +you the value of f for any input x. (I call this an "oracle function", since +if you ask, it will tell.) +$PAR +\(f'( $x0 ) \) = \{ans_rule(50 ) \} +$PAR +You may want to use a +\{ htmlLink(alias("${htmlDirectory}calc.html"), + 'calculator', + qq! TARGET = "ww_calculator" + ONCLICK="window.open( this.href,this.target, + 'width=200, height=350, scrollbars=no, resizable=off' + )" +!) \} + +to find the result. You can also enter numerical expressions and +have WeBWorK do the calculations for you. +END_TEXT + +# Here is where we actually print the javaScript, or alternatives for printed output. +TEXT(MODES( + TeX => " \fbox{ The java Script calculator was displayed here + }", + Latex2HTML => "\begin{rawhtml} $javaScript \end{rawhtml}", + HTML_tth => $javaScript, + HTML => $javaScript, + )); + + + + +ANS(num_cmp($ans,reltol => 1) ); #We are allowing 1 percent error for the answer. + + +ENDDOCUMENT(); diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet1.pg b/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet1.pg new file mode 100644 index 0000000000..7a8cc4c075 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet1.pg @@ -0,0 +1,68 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +$showPartialCorrectAnswers = 1; +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 is a lite applet designed by Frank Wattenberg. +$BR +\{htmlLink( '/webwork2_course_files/demoCourse/live_map_instructions.html ', +'Instructions for using the map',' target="intro" ' )\} +$HR +END_TEXT +$appletText = +appletLink( +q! archive="/courses/system_html/applets/Image_and_Cursor_All/Image_and_Cursor.jar" +code="Image_and_Cursor" width = 500 height = 458 +!, +q!Your browser does not support Java, so nothing is displayed. + + + + + + + +! +); +sub dist { + my $ra_pt1 = shift; + my $ra_pt2 =shift; + my $conversion = 300 /(145 - 72); # number of km per pixel + return $conversion* sqrt( ($ra_pt1->[0] - $ra_pt2->[0])**2 + ($ra_pt1->[1] - $ra_pt2->[1])**2); +} + +$kandahar = [132,101]; +$kabul = [209,185]; +$mazur_e_sharif = [170, 243]; +$shindand = [46, 155]; + +$questions = EV3( +"$PAR How far is it from Kandahar to Kabul? " , ans_rule(30), +" $PAR How far is it from Kabul to Mazar-e-Sharif? ", ans_rule(30), +" $PAR How far is it from Kandahar to Shindand? " , ans_rule(30), +); +#TEXT( +#begintable(2), +#row( $appletText, $questions), +#endtable() +#); +TEXT($appletText, $questions); +ANS(num_cmp(dist($kandahar,$kabul), reltol => 3, units=>'km')); +ANS(num_cmp(dist($kabul, $mazur_e_sharif), reltol => 3, units=>'km')); +ANS(num_cmp(dist($kandahar,$shindand), reltol => 3, units=>'km')); + + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet2.pg b/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet2.pg new file mode 100644 index 0000000000..6f251df90e --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/liteApplet2.pg @@ -0,0 +1,74 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +$showPartialCorrectAnswers = 1; +TEXT(beginproblem()); + + +BEGIN_TEXT +This is a lite applet designed by Frank Wattenberg. +$BR +\{htmlLink( '/webwork2_course_files/demoCourse/live_map_instructions.html ', +'Instructions for using the map',' target="intro" ' )\} +$HR +END_TEXT +TEXT( +appletLink( +q! archive="/courses/system_html/applets/Image_and_Cursor_All/Image_and_Cursor.jar" +code="Image_and_Cursor" width = 500 height = 458 +!, +q!Your browser does not support Java, so nothing is displayed. + + + + + + + +! +), +); +sub dist { + my $ra_pt1 = shift; + my $ra_pt2 =shift; + $conversion = 300 /(145 - 72); # number of km per pixel + return $conversion * sqrt( ($ra_pt1->[0] - $ra_pt2->[0])**2 + ($ra_pt1->[1] - $ra_pt2->[1])**2); +} +@cities = ( + { name => 'Kandahar', location => [132,101] }, + { name => 'Kabul', location => [209,185] }, + { name => 'Mazur e Sharif', location => [170, 243] }, + { name => 'Shindand', location => [46, 155] }, + { name => 'Zaranj', location => [39, 93] } +); +@index = NchooseK(scalar(@cities), 3 ); +sub cityName { + my $index = shift ; + $cities[$index -1]->{name}; +} +sub cityLoc { + my $index = shift; + $cities[$index-1]->{location}; +} + +$conversion = 300 /(145 - 72); # number of km per pixel +BEGIN_TEXT +$PAR +How far is it from \{cityName($index[1])\} to \{cityName($index[2])\}? \{ans_rule(30)\} +$PAR +How far is it from \{cityName($index[1])\} to \{cityName($index[3])\}? \{ans_rule(30)\} +$PAR +How far is it from \{cityName($index[2])\} to \{cityName($index[3])\}? \{ans_rule(30)\} +END_TEXT + +ANS(num_cmp(dist(cityLoc($index[1]),cityLoc($index[2])), reltol=>3, units=>'km')); +ANS(num_cmp(dist(cityLoc($index[2]), cityLoc($index[2])), reltol=>3, units=>'km')); +ANS(num_cmp(dist(cityLoc($index[2]),cityLoc($index[2])), reltol=>3, units=>'km')); + + + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/matchinglistexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/matchinglistexample.pg new file mode 100644 index 0000000000..6d85a7eddc --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/matchinglistexample.pg @@ -0,0 +1,129 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + ); + + +TEXT(beginproblem(), $BR,$BBOLD, "Matching list example", $EBOLD, $BR,$BR); + + +# Since this is a matching question, we do not usually wish to tell students +# which parts of the matching question have been answered correctly and which +# areincorrect. That is too easy. To accomplish this we set the following +# flag to zero. +$showPartialCorrectAnswers = 0; + +# Make a new match list +$ml = new_match_list(); +# enter questions and matching answers +$ml -> qa ( + "\( \sin(x) \)", # Notice the use of the LateX construction + "\( \cos(x) \)", # for math mode: \\( ... \\) and the use of TeX + "\( \cos(x) \)", # symbols such as \\sin and \\tan. + "\( -\sin(x) \)", + "\( \tan(x) \)", + "\( \sec^2(x) \)", # Remember that in these strings we are + # only specifying typography,via TeX, + "\( x^{20} \)", #not any calculational rules. + "\( 20x^{19} \)", + "\( \sin(2x) \)", + "\( 2\cos(2x) \)", + "\( \sin(3x) \)", + "\( 3\cos(3x) \)" +); + + +# Calculate coefficients for another question +$b=random(2,5); +$exp= random(2,5); +$coeff=$b*$exp; +$new_exp = $exp-1; + +# Store the question and answers in the match list object. +$ml -> qa ( + '\( ${b}x^$exp \)', + '\( ${coeff}x^{$new_exp} \)', +); + +# Add another example +$b2=random(2,5); +$exp2= random(2,5); +$coeff2=$b2*$exp; +$new_exp2 = $exp-1; +$ml -> qa ( + "\( ${b2}x^$exp2 \)", + "\( ${coeff2}x^{$new_exp2} \)", +); + +# Choose four of the question and answer pairs at random. +$ml ->choose(4); +# Using choose(8) would choose all eight questions, +# but the order of the questions and answers would be +# scrambled. + +# The following code is needed to make the enumeration work right within tables +# when LaTeX output is being used. +# It is an example of the powerful tools of TeX and perl which are available +# for each PG problem author. +# Once we figure out the best way to protect enumerated lists automatically +# we will include it in the tables macro. Meantime, it is better to have +# have to do it by hand, rather than to have the wrong thing done automatically. + +$BSPACING = MODES( TeX => '\hbox to .5\linewidth {\hspace{0.5cm}\vbox {', + HTML =>' ', + Latex2HTML => ' ' +); +$ESPACING = MODES(TeX => '}}', HTML =>'', Latex2HTML => ''); +sub protect_enumerated_lists { + my @in = @_; + my @out = (); + foreach my $item (@in) { + push(@out, $BSPACING . $item . $ESPACING); + } + @out; +} +# End of code for protecting enumerated lists in TeX. + +# Now print the text using $ml->print_q for +# the questions and $ml->print_a to print the answers. + +BEGIN_TEXT +$PAR + +Place the letter of the derivative next to each function listed below: $BR +\{ $ml -> print_q \} +$PAR +\{$ml -> print_a \} +$PAR +END_TEXT + +ANS( str_cmp( $ml->ra_correct_ans ) ) ; +# insist that the first two questions (labeled 0 and 1) are always included +$ml ->choose([0,1],1); +BEGIN_TEXT +Let's print the questions again, but insist that the +first two questions (about sin and cos) always be included. +Here is a second way to format this question, using tables: +$PAR +\{begintable(2)\} +\{row(protect_enumerated_lists( $ml->print_q, $ml -> print_a) )\} +\{endtable()\} +$PAR +And below is yet another way to enter a table of questions and answers: +$PAR +END_TEXT +ANS( str_cmp( $ml->ra_correct_ans ) ) ; +# Finally add a last answer +$ml ->makeLast("The derivative is not provided"); +BEGIN_TEXT + \{ begintable(2) \} + \{ row( protect_enumerated_lists($ml->print_q, $ml ->print_a))\} + \{endtable()\} +END_TEXT +# Enter the correct answers to be checked against the answers to the students. +ANS( str_cmp( $ml->ra_correct_ans ) ) ; + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/multiplechoiceexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/multiplechoiceexample.pg new file mode 100644 index 0000000000..d1144f0740 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/multiplechoiceexample.pg @@ -0,0 +1,45 @@ +DOCUMENT(); # This should be the first executable line in the problem. +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + +); +TEXT(beginproblem(), $BR,$BBOLD, "Multiple choice example", $EBOLD, $BR,$BR); + +$showPartialCorrectAnswers = 0; +# Make a new multiple choice object. +$mc = new_multiple_choice(); +# $mc now "contains" the multiple choice object. + +# Insert some questions and matching answers in the q/a list +$mc -> qa (# Notice that the first string is the question + "What is the derivative of tan(x)?", + # The second string is the correct answer + "\( \sec^2(x) \)", +); +$mc ->extra( + "\( -\cot(x) \)", + "\( \tan(x) \)", + # Use double quotes " ... " to enter a string + "\( \cosh(x) \)", + "\( \sin(x) \)", + "\( \cos^3(x) \)", + "\( \text{sech}(x) \)" + # Remember that in these strings we are only specifying typography, + # via TeX, not any calculational rules. +); +# Print the question using $mc->print_q +# Use $mc->print_a to print the list of possible answers. +# These need to be done inside BEGIN_TEXT/END_TEXT to make sure that the +# equations inside the questions and answers are processed properly. + +BEGIN_TEXT + +\{$mc -> print_q \} +$PAR +\{$mc -> print_a\} +END_TEXT +# Enter the correct answers to be checked against the answers to the students. +ANS( str_cmp( $mc->correct_ans ) ) ; + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample1.pg b/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample1.pg new file mode 100644 index 0000000000..be9bda7efa --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample1.pg @@ -0,0 +1,117 @@ +DOCUMENT(); +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl" +); + +TEXT(beginproblem(), $BR,$BBOLD, "On-the-fly Graphics Example1", $EBOLD, $BR,$BR); +$showPartialCorrectAnswers = 0; + +# First we define a graph with x and y in the range -4 to 4, axes (strong lines) +# defined at the point [0,0] and +# with 8 gridlines horizontally and 8 grid lines veritically. +# $graph is a graph object (or more appropriately, a pointer to a graph object). + +# We will define a function and it's first and second derivatives defined +# on the domain [-4,4] +$dom = 4; +$graph = init_graph(-$dom,-$dom,$dom,$dom,'axes'=>[0,0],'grid'=>[8,8]); + +# Here are the basic colors -- we'll mix them up in the next example +@colors = ("blue", "red", "green"); #orange, yellow, +@scrambled_colors = @colors; +@labels = ('A', 'B', 'C'); +@scrambled_labels = @labels; + +$a=random(0, 6.3, .1); +$b=random(1.1, 1.5, .1); +# now define the functions too be graphed +# defining strings need to be on one line (\n is not handled correctly) +# The three variables $f, $fp, and $fpp contain strings +# with the correct syntax to be inputs into the plot_function +# macro. The FEQ macro (Format EQuation) cleans up the writing of the function. +# Otherwise we would need to worry about the signs of $a, $b and so forth. +# For example if $b were negative, then after interpolation +# $a+$b might look like 3+-5. FEQ replaces the +- pair by -, which is what you want. + +# The first string (for $f) should be read as: "The function is calculated +# using sin($a+$b*cos(x)) +# and is defined for all x in the +# interval -$dom to +$dom. Draw the function using the first color +# in the permuted color list @scrambled_colors +# and using a weight (width) of two pixels." + +$f = FEQ( + "sin($a+$b*cos(x)) for x in <-$dom,$dom> using color:$scrambled_colors[0] and weight:2" +); +$fp = FEQ( + "cos($a+${b}*cos(x))*(-$b)*sin(x) for x in <-$dom,$dom> using color=$scrambled_colors[1] and weight:2" +); +# The multiplication signs are not actually needed, although they are allowed. + $fpp = FEQ("-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=$scrambled_colors[2] and weight=2" +); + + + +# Install the functions into the graph object. +# Plot_functions converts the string to a subroutine which performs the +# necessary calculations and +# asks the graph object to plot the functions. + +($fRef,$fpRef,$fppRef) = plot_functions( $graph, + $f,$fp,$fpp + ); + +# The output of plot_functions is a list of pointers to functions which +# contain the appropriate data and methods. +# So $fpRef->rule points to the method which will calculate the value +# of the function. +# &{$fpRef->rule}(3) calculates the value of the function at 3. + +# create labels for each function +# The 'left' tag determines the justification of the label to the defining point. + + +$label_point=-0.75; +$label_f = new Label ( $label_point,&{$fRef->rule}($label_point), + $scrambled_labels[0], $scrambled_colors[0],'left'); + # NOTE: $fRef->ruleis 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), + $scrambled_labels[1],$scrambled_colors[1],'left'); +# Place the second letter in the permuted letter list at the point +# (-.75, fp(-.75)) using the second color in the permuted color list. + +$label_fpp = new Label ( $label_point,&{$fppRef->rule}($label_point), + $scrambled_labels[2],$scrambled_colors[2],'left'); + +# insert the labels into the graph +$graph->lb($label_f,$label_fp,$label_fpp); + +# make sure that the browser will fetch +# the new picture when it is created by changing the name of the +# graph each time the problem seed is changed. This helps prevent caching problems +# on browsers. + + $graph->gifName($graph->gifName()."-$newProblemSeed"); +# Begin writing the problem. +# This inserts the graph and then asks three questions: + +BEGIN_TEXT +\{ image(insertGraph($graph)) \} $PAR +Identify the graphs A (blue), B( red) and C (green) as the graphs +of a function and its +derivatives (click on the graph to see an enlarged image):$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(str_cmp( [@scrambled_labels] ) ); + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample2.pg b/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample2.pg new file mode 100644 index 0000000000..063ae5804d --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/ontheflygraphicsexample2.pg @@ -0,0 +1,124 @@ +DOCUMENT(); + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl" +); + +TEXT(beginproblem(), $BR,$BBOLD, "On-the-fly Graphics Example2", $EBOLD, $BR,$BR); + +# First we define a graph with x and y in the range -4 to 4, axes (strong lines) +# defined at the point [0,0] and +# with 8 gridlines horizontally and 8 grid lines veritically. +# $graph is a graph object (or more appropriately, a pointer to a graph object). + +# We will define a function and it's first and second derivatives +# defined on the domain [-4,4] + +$dom = 4; +$graph = init_graph(-$dom,-$dom,$dom,$dom,'axes'=>[0,0],'grid'=>[8,8]); + +# We need to scramble the colors and the labels -- otherwise every student +# will have the function is A, the derivative is B, etc. +# and the colors won't be scrambled either. + +#This provides a permutation of the numbers (0,1,2); + +@slice = NchooseK(3,3); + +# Here are the basic colors + +@colors = ("blue", "red", "green"); #orange, yellow, + +# This lists the colors in the order defined by the list in @slice +# It effectively applies the same permutation to the list of colors +# and to the list of labels. A will always be blue, B red, and C green +#and applies the same permutation to the list of labels + +@scrambled_colors = @colors[@slice]; +@labels = ('A', 'B', 'C'); +@scrambled_labels = @labels[@slice]; + +# The rest of this example is the same as ontheflygraphicsexample1 + +# function definitions need to be on one line +$a=random(0, 6.3, .1); +$b=random(1.1, 1.5, .1); + +# The three variables $f, $fp, and $fpp contain strings +# with the correct syntax to be inputs into the plot_function +# macro. The FEQ macro (Format EQuation) cleans up the writing of the function. +# Otherwise we would need to worry about the signs of $a, $b and so forth. +# For example if $b were negative, then after interpolation +# $a+$b might look like 3+-5. FEQ replaces the +- pair by -, which is what you want. + +# The first string (for $f) should be read as: "The function is calculated +# using sin($a+$b*cos(x)) +# and is defined for all x in the +# interval -$dom to +$dom. Draw the function using the first color +# in the permuted color list @scrambled_colors +# and using a weight (width) of two pixels." + +$f = FEQ("sin($a+$b*cos(x)) for x in <-$dom,$dom> using color:$scrambled_colors[0] and weight:2"); +$fp = FEQ("cos($a+${b}*cos(x))*(-$b)*sin(x) for x in <-$dom,$dom> using color=$scrambled_colors[1] and weight:2"); +$fpp = FEQ("-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=$scrambled_colors[2] and weight=2"); + + +# Install the functions into the graph object. +# Plot_functions converts the string to a subroutine which performs the +# necessary calculations and +# asks the graph object to plot the functions. + +($fRef,$fpRef,$fppRef) = plot_functions( $graph, + $f,$fp,$fpp + ); +# The output of plot_functions is a list of pointers to functions which +# contain the appropriate data and methods. +# So $fpRef->rule points to the method which will calculate the value +# of the function. +# &{$fpRef->rule}(3) calculates the value of the function at 3. + +# create labels for each function +# The 'left' tag determines the justification of the label to the defining point. + + +$label_point=-0.75; +$label_f = new Label ( $label_point,&{$fRef->rule}($label_point), + $scrambled_labels[0],$scrambled_colors[0],'left') ; + # NOTE: $fRef->ruleis 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), + $scrambled_labels[1],$scrambled_colors[1],'left') ; +# Place the second letter in the permuted letter list at the point +# (-.75, fp(-.75)) using the second color in the permuted color list. + +$label_fpp = new Label ( $label_point,&{$fppRef->rule}($label_point),$scrambled_labels[2],$scrambled_colors[2],'left'); + +# insert the labels into the graph +$graph->lb($label_f,$label_fp,$label_fpp); + +# make sure that the browser will fetch +# the new picture when it is created by changing the name of the +# graph each time the problem seed is changed. +$graph->gifName($graph->gifName()."-$newProblemSeed"); + +# Begin writing the problem. +# This inserts the graph and then asks three questions: + +BEGIN_TEXT +\{ image(insertGraph($graph),width => 200, height => 200) \} $PAR +Identify the graphs A (blue), B( red) and C (green) as the graphs of a function and its +derivatives (click on the graph to see an enlarged image):$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(str_cmp( [@scrambled_labels] ) ); + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/paperHeader.pg b/courses.dist/modelCourse/templates/setMAAtutorial/paperHeader.pg new file mode 100644 index 0000000000..d26d1abcd3 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/paperHeader.pg @@ -0,0 +1,22 @@ +## Paper set header for setSampleGraders + +DOCUMENT(); + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl" +); + + +BEGIN_TEXT +$BEGIN_ONE_COLUMN +This set shows how to write simple WeBWorK problems and introduces you to the most common +constructions. +$END_ONE_COLUMN + +END_TEXT + + +ENDDOCUMENT(); diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/popuplistexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/popuplistexample.pg new file mode 100644 index 0000000000..eed7511d1d --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/popuplistexample.pg @@ -0,0 +1,65 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); + +TEXT(beginproblem(), $BR,$BBOLD, "True False Pop-up Example", $EBOLD, $BR,$BR); +$showPartialCorrectAnswers = 0; + +# Make a new select list +$tf = new_select_list(); +# $tf now "contains" the select list object. + +# change the printing mechanism of the object to +# use pop-up list instead of an answer rule. +$tf->rf_print_q(~~&pop_up_list_print_q); + +# What should the pop-up list contain, and what string should it +# submit for an answer when selected? +# These are specified in the statment below. +# To enter T as an answer choose the list element "True" +# To enter F as an answer choose the list element "False" +# The first choice is a blank to make the students do SOMETHING!!! +$tf -> ra_pop_up_list( [ No_answer => "  ?", T => "True", F => "False"] ); +# Note how the list is constructed [ answer => list element text, answer => list element text ] + +# Insert some questions and their answers. + +$tf -> qa ( # each entry has to end with a comma +"All continuous functions are differentiable.", +"F", +"All differentiable functions are continuous.", +"T", +"All polynomials are differentiable.", +"T", +"All functions with positive derivatives are increasing.", +"T", +"All compact sets are closed", +"T", +"All closed sets are compact", +"F", +"All increasing functions have positive deriviatives", +"F", +"All differentiable strictly increasing functions have non-negative derivatives + at every point", +"T", +); + +# Choose two of the question and answer pairs at random. +$tf ->choose(4); # Using choose(3) would choose all three + # questions, but the order of the questions + # and answers would be scrambled. + +# Now print the text using $ml->print_q for the questions. +BEGIN_TEXT +$PAR +Indicate whether each statement is true or false. $BR +\{ $tf-> print_q \} +$PAR +END_TEXT +# Enter the correct answers to be checked against the answers to the students. +ANS( str_cmp( $tf->ra_correct_ans ) ) ; + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/prob3.pg b/courses.dist/modelCourse/templates/setMAAtutorial/prob3.pg new file mode 100644 index 0000000000..7c1671c93c --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/prob3.pg @@ -0,0 +1,162 @@ +# +# +#
+# Description
+# The first example using match lists
+# EndDescription
+
+
+DOCUMENT();        # This should be the first executable line in the problem.
+
+loadMacros("PGbasicmacros.pl",
+           "PGchoicemacros.pl",
+           "PGanswermacros.pl",
+           "PGgraphmacros.pl",
+           "PGnumericalmacros.pl"
+           );
+
+# TEXT( ... , ... , )
+# Is the simplest way of printing text, each string in the input is immediately printed.
+# It does not do any of the simplifying and evaluating tricks performed by the BEGIN_TEXT/END_TEXT construction.
+
+# beginproblem() is a macro which outputs the string 
+# which contains the number
+# of points the problem is worth.
+
+# Putting these two ideas together prints the standard opening line of a problem.
+# We will use this construction routinely at the beginning of all subsequent problems.
+TEXT(beginproblem());
+
+# Since this is a matching questions, we do not usually wish to tell students which
+# parts of the matching question have been answered correctly and which are
+# incorrect.  That is too easy.  To accomplish this we set the following flag to zero.
+$showPartialCorrectAnswers = 0;
+
+
+#####################################################################
+# This section allows you to manipulate the problem seed while working on the problem
+# thus seeing different versions of the problem. Skip the details of how this works
+# for now.
+
+# allow the student to change the seed for this problem.
+$newProblemSeed = ( defined( ${$inputs_ref}{'newProblemSeed'} ) )?  ${$inputs_ref}{'newProblemSeed'} : $problemSeed;
+$PG_random_generator->srand($newProblemSeed);
+
+BEGIN_TEXT     
+
+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}
+    
+    \end{rawhtml}!,
+qq! !
+)
+\}
+
+$HR  
+END_TEXT
+#####################################################################
+
+
+# Make a new match list
+$ml = new_match_list();
+
+# $ml now "contains" the match list object.  (Actually $ml is a scalar variable which contains a pointer to 
+# the match list object, but you can think of the match list object as being shoe horned into the variable $ml.
+# You need to remember that $ml contains (a pointer to) an object, and not ordinary data such as a number or string.
+
+# Some people use the convention $o_ml to remind them that the variable contains an object, but for short problems
+# that is probably not necessary.
+
+# An object contains both data (in this case the list of questions and answers) and subroutines (called methods)
+# for manipulating that data.
+
+
+# Insert some  questions and matching answers in the q/a list by calling on the objects qa method.
+# using the construction $ml ->qa(..list of alternating questions and matching answers ...).
+# Think of this as asking the object $ml to store the  matching questions 
+# and answers given in the argument to the method qa.
+
+$ml -> qa (
+"\( \sin(x) \)",        # Notice the use of the LateX construction for math mode: \\( ...  \\)
+"\( \cos(x) \)",		# and the use of TeX symbols such as \\sin and \\tan
+"\( \cos(x) \)",        # Use " ... " to enter a string
+"\( -\sin(x) \)",
+"\( \tan(x) \)",
+"\( \sec^2(x) \)"       # Remember that in these strings we are only specifying typography, 
+ 						# via TeX, not any calculational rules.
+);
+
+#
+# Calculate coefficients for another question
+$b=random(2,5);
+$exp= random(2,5);
+$coeff=$b*$exp;
+$new_exp = $exp-1;
+
+# Store the question and answers in the match list object. 
+$ml -> qa (
+"\( ${b}x^$exp \)",
+"\( ${coeff}x^{$new_exp} \)",
+);
+
+# Add another example
+$b2=random(2,5);
+$exp2= random(2,5);
+$coeff2=$b2*$exp;
+$new_exp2 = $exp-1;
+$ml -> qa (
+"\( ${b2}x^$exp2 \)",
+"\( ${coeff2}x^{$new_exp2} \)",
+);
+
+
+# Choose two of the question and answer pairs at random.
+$ml ->choose(2);  # Using choose(3) would choose all three questions, but the order of the questions and answers would be 
+                  # scrambled.
+
+
+# Now print the text using $ml->print_q for the questions and $ml->print_a to print the answers.
+
+BEGIN_TEXT
+$PAR
+
+Match the functions and their derivatives: $BR
+
+\{ $ml -> print_q \}
+
+$PAR
+
+\{$ml -> print_a \}
+END_TEXT
+
+# Enter the correct answers to be checked against the answers to the students.
+
+ANS( str_cmp( $ml->ra_correct_ans )   ) ;
+
+# That's it.
+
+#########################################################  
+
+BEGIN_TEXT
+
+ +You can view the +\{ htmlLink(alias("${htmlDirectory}/links/set$setNumber/prob3.html"),"source", q!TARGET="source"!)\} +for this problem. +END_TEXT + +TEXT( +"$PAR Return to ", htmlLink($$inputs_ref{returnPage},$$inputs_ref{returnPage}), +) if exists($$inputs_ref{returnPage}); +######################################################### + + + +ENDDOCUMENT(); # This should be the last executable line in the problem. +#
+# +# diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/prob4.pg b/courses.dist/modelCourse/templates/setMAAtutorial/prob4.pg new file mode 100644 index 0000000000..7d52a89951 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/prob4.pg @@ -0,0 +1,95 @@ +#
+#Description
+# Testing knowledge of differentiation rules
+#EndDescription
+
+DOCUMENT();        # This should be the first executable line in the problem.
+
+loadMacros("PGbasicmacros.pl",
+           "PGchoicemacros.pl",
+           "PGanswermacros.pl",
+           "PGgraphmacros.pl",
+           "PGnumericalmacros.pl"
+           );
+ 
+TEXT(&beginproblem);
+$showPartialCorrectAnswers = 0;
+
+
+
+# allow the student to change the seed for this problem.
+$newProblemSeed = ( defined( ${$inputs_ref}{'newProblemSeed'} ) )?  ${$inputs_ref}{'newProblemSeed'} : $problemSeed;
+$PG_random_generator->srand($newProblemSeed);
+BEGIN_TEXT
+
+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}
+    
+    \end{rawhtml}!,
+qq! !
+)
+\}
+
+$HR  
+END_TEXT
+
+########################################################################
+# Make a new select list
+$ml = new_select_list();
+#$ml -> rf_print_q(~~&my_print_q);
+# New versions using the macros in PGchoicemacros.pl
+$ml->rf_print_q(~~&pop_up_list_print_q);
+$ml -> ra_pop_up_list([ No_answer => "  ?",SR => "Sum Rule",PR => "Product Rule",CR => "Chain rule",QR => "Quotient rule" ] );
+
+
+$ml -> qa (
+"\( (f(x) + g(x) )' = f'(x) + g'(x) \)",
+"SR",
+"\( ( f(x)g(x) )' = f'(x)g(x) + f(x)g'(x) \)",
+"PR",
+"\( ( f(g(x)) )' = f'(g(x))g'(x) \) ",
+"CR",
+"\( \frac{d}{dx} \sin(\cos(x)) = - \cos(\cos(x))\sin(x) \)",
+"CR",
+"\( (f(x) - g(x) )' = f'(x) - g'(x) \)",
+"SR",
+);
+
+$ml ->choose(5);
+
+#coda
+
+
+
+
+BEGIN_TEXT
+ $PAR
+
+For each example below, list the label of the  differentiation rule used in that example: $BR
+
+\{ $ml -> print_q \}
+
+$PAR
+You can view the 
+\{ htmlLink(alias("${htmlDirectory}links/setDerivativeRules/prob2.html"), "source",q!TARGET="source"!) \}
+for this problem.
+or consult the 
+\{ htmlLink("/webwork_system_html/docs/techdescription/pglanguage/index.html","documentation") \}  for  more details on the PG language.
+
+END_TEXT
+
+install_problem_grader(~~&std_problem_grader);
+
+ANS( str_cmp( $ml->ra_correct_ans )   ) ;
+
+BEGIN_TEXT
+$PAR
+There are only a few examples in this problem.  A production verison
+would need more examples to choose from.
+END_TEXT
+ENDDOCUMENT();        # This should be the last executable line in the problem.
+#
diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/screenHeader.pg b/courses.dist/modelCourse/templates/setMAAtutorial/screenHeader.pg new file mode 100644 index 0000000000..bb7de4e6a7 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/screenHeader.pg @@ -0,0 +1,21 @@ +DOCUMENT(); +loadMacros("PG.pl", + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" + ); + + +$usd = '$'; +BEGIN_TEXT + +This set shows how to write simple WeBWorK problems and introduces you to the most common +constructions. + + + +END_TEXT +TEXT("Return to ", htmlLink('http://cartan.math.rochester.edu/WeBWorKdiscussion/discuss/msgReader$14', +"discussion page"), "for more information or to make comments."); +ENDDOCUMENT(); + diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/simple_drawing.pg b/courses.dist/modelCourse/templates/setMAAtutorial/simple_drawing.pg new file mode 100644 index 0000000000..f4d86c4e7a --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/simple_drawing.pg @@ -0,0 +1,40 @@ +##DESCRIPTION +## A very simple drawing problem +##ENDDESCRIPTION + +##KEYWORDS('algebra') + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( +"PG.pl", +"PGbasicmacros.pl", +"PGchoicemacros.pl", +"PGanswermacros.pl", +"PGgraphmacros.pl", +"PGauxiliaryFunctions.pl" +); + + +$graph = init_graph(-5,-5,5,5,ticks=>[4,4],axes=>[0,0],pixels=>[400,400]); + +$graph->moveTo(-2,1); +$graph->lineTo(2,2,'blue'); +$graph->lineTo(-1,2,'red'); +$graph->lineTo(-2,1,'green'); +$graph->fillRegion([0,1.7,'yellow']); +BEGIN_TEXT +\{image(insertGraph($graph),width=>400,height=>400)\} + + +END_TEXT + +# At the moment there is no easy way to change the weight of the lines being drawn. To do so one would want +# to incorporate some of the code in Fun.pm into WWPlot.pm itself. The code involves gdBrushed. +# Since GD has +# gone through many revisions since the WWPlot.pm code was written it may now be possible to write some of +# this code more efficiently. + + + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/simplemultiplechoiceexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/simplemultiplechoiceexample.pg new file mode 100644 index 0000000000..f62e09bb28 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/simplemultiplechoiceexample.pg @@ -0,0 +1,33 @@ +DOCUMENT(); # This should be the first executable line in the problem. +loadMacros( + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", +); +TEXT(beginproblem(), $BR,$BBOLD, "Multiple choice example", $EBOLD, $BR,$BR); + + +$showPartialCorrectAnswers = 0; +$question = "What is the derivative of tan(x)?"; +# An example of a list or array variable. It begins with @. +@answer_list = ( "\( \sec^2(x) \)", # correct + "\( -\cot(x) \)", + "\( \tan(x) \)", + "\( \cosh(x) \)", + "\( \sin(x) \)", +); +# These commands permute the order of the answers. +#@permutation = NchooseK(5,5); # random permutation of the five answers +@permutation = (1,0,2,3,4); # example of fixed permutation +@permuted_answer_list = @answer_list[@permutation]; +@inverted_alphabet = @ALPHABET[invert( @permutation )]; # needed to check the answers + +# Use the macro OL to print an Ordered List of the answerslabeled with letters. +BEGIN_TEXT +$BR $question +$PAR \{ OL( @permuted_answer_list ) \} +$PAR Enter the letter corresponding to the correct answer: \{ ans_rule(10) \} +END_TEXT +ANS( str_cmp( $inverted_alphabet[0] ) ) ; + +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/standardexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/standardexample.pg new file mode 100644 index 0000000000..29ea3ecfca --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/standardexample.pg @@ -0,0 +1,47 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGauxiliaryFunctions.pl" +); + +TEXT(beginproblem(), $BR,$BBOLD, "Standard Example", $EBOLD, $BR,$BR); + +# A question requiring a string answer. +$str = 'world'; +#$str = "Dolly"; +BEGIN_TEXT +Complete the sentence: $BR +\{ ans_rule(20) \} $str; +$PAR +END_TEXT + +ANS( str_cmp( "Hello") ); + +# A question requiring a numerical answer. +#define the variables +$a = 3; +$b = 5; +#$a=random(1,9,1); +#$b=random(2,9,1); + +BEGIN_TEXT +Enter the sum of these two numbers: $BR + \($a + $b = \) \{ans_rule(10) \} +$PAR +END_TEXT + +$sum = $a + $b; +ANS( num_cmp( $sum ) ); + +# A question requiring an expression as an answwer +BEGIN_TEXT +Enter the derivative of \[ f(x) = x^{$b} \] $BR +\(f '(x) = \) \{ ans_rule(30) \} +$PAR +END_TEXT +$new_exponent = $b-1; +$ans2 = "$b*x^($new_exponent)"; +ANS( fun_cmp( $ans2 ) ); +ENDDOCUMENT(); + \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.gif new file mode 100644 index 0000000000..df1f85cbd2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.png new file mode 100644 index 0000000000..580193f1ec Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-24438.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.gif new file mode 100644 index 0000000000..b0ad26bb6e Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.png new file mode 100644 index 0000000000..4a6286ab5c Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-31126.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.gif new file mode 100644 index 0000000000..702784afdd Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.png new file mode 100644 index 0000000000..664c466d85 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-34859.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.gif new file mode 100644 index 0000000000..dfebffd690 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.png new file mode 100644 index 0000000000..1180e668cf Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-42639.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.gif new file mode 100644 index 0000000000..741fdd8452 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.png new file mode 100644 index 0000000000..ad13276793 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-76239.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.gif new file mode 100644 index 0000000000..aa9a75d45a Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.png new file mode 100644 index 0000000000..5ee4d64c9d Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-89540.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.gif new file mode 100644 index 0000000000..cd5aae4ebd Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.png new file mode 100644 index 0000000000..82a533be4b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-91734.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.gif new file mode 100644 index 0000000000..23ac97828b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.png new file mode 100644 index 0000000000..53203cf0e7 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1-96355.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.gif new file mode 100644 index 0000000000..073055392b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.png new file mode 100644 index 0000000000..f8df18ec73 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/1.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.gif new file mode 100644 index 0000000000..709cd644e2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.png new file mode 100644 index 0000000000..11a642e8e2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-42653.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.gif new file mode 100644 index 0000000000..3767c0de47 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.png new file mode 100644 index 0000000000..273247474d Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-49261.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.gif new file mode 100644 index 0000000000..11c71d8010 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.png new file mode 100644 index 0000000000..538e6f5a62 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-54427.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-62384.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-62384.gif new file mode 100644 index 0000000000..538e6f5a62 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-62384.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-62384.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-62384.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.gif new file mode 100644 index 0000000000..4bd408e32f Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.png new file mode 100644 index 0000000000..9d3cfa05c1 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-64591.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-68458.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-68458.png new file mode 100644 index 0000000000..4f821ea915 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-68458.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-68458.pnm b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-68458.pnm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.gif new file mode 100644 index 0000000000..3e646a41ac Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.png new file mode 100644 index 0000000000..cc82c39ef1 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-70190.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.gif new file mode 100644 index 0000000000..944f3efcf6 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.png new file mode 100644 index 0000000000..b72c870a2b Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-81779.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.gif new file mode 100644 index 0000000000..11506db3bc Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.png new file mode 100644 index 0000000000..febe0715b8 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2-92879.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.gif new file mode 100644 index 0000000000..b85a6d3ad3 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.png new file mode 100644 index 0000000000..0a68a46210 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/2.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.gif new file mode 100644 index 0000000000..c28c1b68ee Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.png new file mode 100644 index 0000000000..65fb892896 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14197.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.gif new file mode 100644 index 0000000000..c57d2fbe84 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.png new file mode 100644 index 0000000000..99e6748686 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-14538.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.gif new file mode 100644 index 0000000000..a9c2db85c3 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.png new file mode 100644 index 0000000000..212e96cfe2 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-37616.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.gif new file mode 100644 index 0000000000..4244a8ca61 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.png new file mode 100644 index 0000000000..43d18b86ec Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-46739.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.gif new file mode 100644 index 0000000000..afe8cd9d75 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.png new file mode 100644 index 0000000000..93a35d4e35 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-52898.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.gif new file mode 100644 index 0000000000..8690563535 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.png new file mode 100644 index 0000000000..4f821ea915 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-68458.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.gif new file mode 100644 index 0000000000..5c954beec1 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.png new file mode 100644 index 0000000000..37c3e8418e Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-89262.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.gif new file mode 100644 index 0000000000..f948cc3e83 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.png new file mode 100644 index 0000000000..51f7c7e1b3 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3-99389.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.gif b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.gif new file mode 100644 index 0000000000..5f8f80e7d8 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.gif differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.png b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.png new file mode 100644 index 0000000000..c16fb935e9 Binary files /dev/null and b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/3.png differ diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/staticgraphicsexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/staticgraphicsexample.pg new file mode 100644 index 0000000000..97f15b6b25 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/staticgraphicsexample.pg @@ -0,0 +1,115 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl" +); +TEXT($BEGIN_ONE_COLUMN); +TEXT(beginproblem(), $BR,$BBOLD, "Static graphics Example", $EBOLD, $BR,$BR); + +$showPartialCorrectAnswers = 0; +# Define which of the three sets of pictures to use + +# The pictures are labeled 1.png, 2.png and 3.png and +# stored in the same directory as staticgraphicsexample.png +# These are the corresponding transformed pictures. +# Be careful with the labeling, since the URL's could give the +# correct answers away. +# (In this example the middle integer tells you +# the correct position.) + +$pictID[1] = [ +"1-31126.png", # "\( F(x+3)\)", +"1-76239.png", # "\(F(x-3) \)" , +"1-96355.png", # "\( -F(-x)\)", +"1-24438.png", # "\( F(-x) \)", +"1-89540.png", # "\( 5F(x) \)", +"1-42639.png", # "\( F(3x) \)" , +"1-91734.png", # "\( F(x/3) \)", +"1-34859.png", # "\( F(x^2) \)", +]; +$pictID[2] = [ +"2-70190.png", # ditto +"2-49261.png", +"2-62384.png", +"2-54427.png", +"2-64591.png", +"2-42653.png", +"2-81779.png", +"2-92879.png", +]; +$pictID[3] = [ +"3-14197.png", +"3-89262.png", +"3-99389.png", +"3-68458.png", +"3-14538.png", +"3-37616.png", +"3-46739.png", +"3-52898.png", +]; +$ml = new_match_list(); + +$pictSet=random(1,3,1); # Choose one of the three picture sets +$pictSet=1; +$pictSetname = $pictSet.".png"; +$ml->qa ( +"\( F(x+3)\) ", +image($pictID[$pictSet][0],tex_size=>200), +"\(F(x-3) \)" , +image($pictID[$pictSet][1],tex_size=>200), +"\( -F(-x)\) ", +image($pictID[$pictSet][2],tex_size=>200), +"\( F(-x) \)", +image($pictID[$pictSet][3],tex_size=>200), +"\( 5F(x) \)", +image($pictID[$pictSet][4],tex_size=>200), +"\( F(3x) \)" , +image($pictID[$pictSet][5],tex_size=>200), +"\( F(x/3) \)", +image($pictID[$pictSet][6],tex_size=>200), +"\( F(x^2) \)", +image($pictID[$pictSet][7],tex_size=>200), +); + +$ml->choose(4); +sub format_graphs { + my $self = shift; + my @in = @_; + my $out = ""; + while(@in) { + $out .= shift(@in). "#" ; + } + $out; # The output has to be a string in order to conform to the + # specs for the match list object, but I've put some + # markers in (#) so that + # I can break the string up into a list for use + # as an input into row. +} + +# We need to change the output, since the normal +# output routine will put the pictures one above another. +$ml->rf_print_a(~~&format_graphs); + +BEGIN_TEXT +This is a graph of the function \( F(x) \): +($BBOLD Click on image for a larger view $EBOLD) +$PAR +\{ image($pictSetname, tex_size => 200) \} +$PAR +Enter the letter of the graph below which corresponds to the transformation +of the function. +\{ $ml -> print_q \} +END_TEXT + +# Place the output into a table +TEXT( + begintable(4), + row( split("#",$ml->print_a() ) ), + row('A', 'B', 'C', 'D' ), + endtable(), +); + +ANS( str_cmp( $ml ->ra_correct_ans() ) ) ; + +TEXT($END_ONE_COLUMN); +ENDDOCUMENT(); \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/tmp b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/tmp new file mode 100644 index 0000000000..3090a9405f --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/staticgraphicsexample/tmp @@ -0,0 +1,3 @@ +#!/usr/bin/csh + +cat $1 | giftopnm | pnmtopng >$2 diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/truefalseexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/truefalseexample.pg new file mode 100644 index 0000000000..36e59f0f87 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/truefalseexample.pg @@ -0,0 +1,63 @@ +DOCUMENT(); +loadMacros("PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + +); +TEXT(beginproblem(), $BR,$BBOLD, "True False Example", $EBOLD, $BR,$BR); + +# Since this is a true questions, we do not usually wish to tell students which +# parts of the matching question have been answered correctly and which are +# incorrect. That is too easy. To accomplish this we set the following flag to +# zero. +$showPartialCorrectAnswers = 0; + +# True false questions are a special case of a "select list" +# Make a new select list +$tf = new_select_list(); +# $tf now "contains" the select list object. +# Insert some questions and whether or not they are true. + +$tf -> qa ( # each entry has to end with a comma +"All continuous functions are differentiable.", +"F", +"All differentiable functions are continuous.", +"T", +"All polynomials are differentiable.", +"T", +"All functions with positive derivatives are increasing.", +"T", +"All compact sets are closed", +"T", +"All closed sets are compact", +"F", +"All increasing functions have positive deriviatives", +"F", +"All differentiable strictly increasing functions have non-negative derivatives + at every point", +"T", +); + +# Choose four of the question and answer pairs at random. +$tf ->choose(4); + +# Now print the text using $ml->print_q for the questions +# and $ml->print_a to print the answers. + +BEGIN_TEXT +$PAR + +Enter T or F depending on whether the statement is true or false. +(You must enter T or F -- True and False will not work.)$BR + +\{ $tf-> print_q \} + +$PAR + +END_TEXT + +# Enter the correct answers to be checked against the answers to the students. + +ANS( str_cmp( $tf->ra_correct_ans ) ) ; + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file diff --git a/courses.dist/modelCourse/templates/setMAAtutorial/vectorfieldexample.pg b/courses.dist/modelCourse/templates/setMAAtutorial/vectorfieldexample.pg new file mode 100644 index 0000000000..50214bd5b5 --- /dev/null +++ b/courses.dist/modelCourse/templates/setMAAtutorial/vectorfieldexample.pg @@ -0,0 +1,94 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros("PG.pl", + "PGbasicmacros.pl", + "PGchoicemacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl", +); + +TEXT(beginproblem()); # standard preamble to each problem. + +# Since this is a true questions, we do not usually wish to tell students which +# parts of the matching question have been answered correctly and which are +# incorrect. That is too easy. To accomplish this we set the following flag to zero. +$showPartialCorrectAnswers = 0; + + +# Make a new select list + +$tf = new_match_list(); + +$numberOfQuestions = 4; +$tf -> qa ( +"\(y'= 2y + x^2e^{2x} \)", +sub{my ($x,$y) = @_; 2*$y+($x**2)*exp(2*$x); }, +"\( y'= -2 + x - y \)", +sub{my ($x,$y) = @_; -2 + $x - $y;}, +"\(y'= e^{-x} + 2y\)", +sub{my ($x,$y) = @_; exp(-$x) + 2*$y;}, +"\(y'= 2\sin(x) + 1 + y\)", +sub{my ($x,$y) = @_; 2*sin($x) + 1 + $y;}, +"\(y'= -\frac{2x+y)}{(2y)} \)", +sub{my ($x,$y) = @_; ($y==0)? -2*$x/0.001 : -(2*$x+$y)/(2*$y);}, +"\(y'= y + 2\)", +sub{my ($x,$y) = @_; $y + 2 ;}, +); + +$tf ->choose($numberOfQuestions); +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +Match the following equations with their direction field. +Clicking on each picture will give you an +enlarged view. While you can probably solve this problem by guessing, +it is useful to try to predict characteristics of the direction field +and then match them to the picture. +$PAR +Here are some handy characteristics to start with -- +you will develop more as you practice. +$PAR + +\{OL( + "Set y equal to zero and look at how the derivative behaves along the x axis.", + "Do the same for the y axis by setting x equal to 0", + "Consider the curve in the plane defined by setting y'=0 + -- this should correspond to the points in the picture where the + slope is zero.", + "Setting y' equal to a constant other than zero gives the curve of points + where the slope is that + constant. These are called isoclines, and can be used to construct the + direction field picture by hand." +)\} + + + \{ $tf->print_q \} + +END_TEXT +$dx_rule = sub{my ($x,$y) = @_; 1; }; +$dy_rule = sub{my ($x,$y) = @_; $y; }; +# prepare graphs: +@dy_rules = @{ $tf->{selected_a} }; + +for my $i (0..$numberOfQuestions-1) { + $graph[$i] = init_graph(-4,-4,4,4,'axes'=>[0,0],'grid'=>[8,8]); + $vectorfield[$i] = new VectorField($dx_rule, $dy_rules[$i], $graph[$i]); + $vectorfield[$i]->dot_radius(2); + $graphURL[$i] = insertGraph($graph[$i]); +} +#### +BEGIN_TEXT +$PAR + \{ imageRow( [@graphURL[0..$numberOfQuestions/2-1]], + [@ALPHABET[0..$numberOfQuestions/2-1]], height => 200, + width => 200,tex_size=>300 ) \} + \{ imageRow( [@graphURL[$numberOfQuestions/2..$numberOfQuestions-1]], + [@ALPHABET[$numberOfQuestions/2..$numberOfQuestions-1]], + height => 200, width => 200,tex_size=>300 ) \} + +$END_ONE_COLUMN +END_TEXT + +ANS( str_cmp( $tf->ra_correct_ans ) ) ; + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation.def b/courses.dist/modelCourse/templates/setOrientation.def new file mode 100644 index 0000000000..fec0acb974 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation.def @@ -0,0 +1,23 @@ +setNumber=Orientation +openDate = 6/26/04 at 11:30am +dueDate = 4/4/15 at 12:20pm +answerDate = 4/5/15 at 12:00pm +paperHeaderFile = setOrientation/setHeader.pg +screenHeaderFile = setOrientation/setHeader.pg + +problemList = +setOrientation/prob01.pg, 1 +setOrientation/prob02.pg, 1 +setOrientation/prob03.pg, 1 +setOrientation/prob04.pg, 1 +setOrientation/prob05/prob05.pg, 1 +setOrientation/prob06.pg, 1 +setOrientation/prob07.pg, 1 +setOrientation/prob08.pg, 1 +setOrientation/prob09.pg, 1 +setOrientation/prob10.pg, 1 +setOrientation/prob11.pg, 1 +setOrientation/prob12.pg, 1 +setOrientation/prob13.pg, 1 +setOrientation/prob14/prob14.pg, 1 +setOrientation/prob15.pg, 1 diff --git a/courses.dist/modelCourse/templates/setOrientation/course_info.txt b/courses.dist/modelCourse/templates/setOrientation/course_info.txt new file mode 100644 index 0000000000..f955c346c8 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/course_info.txt @@ -0,0 +1,22 @@ +You have logged into WeBWorK! + +

+ +If you haven't already done so, you should change your password to +something other than your student ID. To do this, click on the +Password/Email link in the list at the far left, and follow the +directions on that page. + +

+ +Once you have changed your password, click on the "Orientation" link +in the white area at the left. This will take you to the the first +homework assignment, which will help you to learn how to use WeBWorK +effectively. + +

+ +If you wish to get a printed copy of one of your assignments, you can +do so from this page by pressing the "Download Hardcopy" button. You +will be taken to a page where you can select what information to +include in the set, and which set to download. diff --git a/courses.dist/modelCourse/templates/setOrientation/login_info.txt b/courses.dist/modelCourse/templates/setOrientation/login_info.txt new file mode 100644 index 0000000000..3ff81bcd37 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/login_info.txt @@ -0,0 +1,20 @@ +Welcome to WeBWorK! + +

+ +Your username is the same as the one used for your Union College +email, and your initial password is your Union College Student ID +number (you will change that after you log in). + +

+ +Log in and follow the instructions that will appear in this panel once +you have done so. For the first assignment, do not log in as a +guest user or using a friend's account. If you do, you will not +receive credit for doing the asignment! + +

+ +On future assignments, you can use the guest accounts to get +additional practice problems that are similar to the ones in your +homework set, but with different numbers. diff --git a/courses.dist/modelCourse/templates/setOrientation/options_info.txt b/courses.dist/modelCourse/templates/setOrientation/options_info.txt new file mode 100644 index 0000000000..f6aa8eadf3 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/options_info.txt @@ -0,0 +1,31 @@ +Change Your Password + +

+ +If you haven't already changed your password, you should do so now. +To do this, type your OLD password in the top box at the left and +your NEW password in the two lower boxes at the left, +then press the "Change User Options" button. + +

+ +Your password should be at least 6 characters long, and should include +something other than just letters. Don't make it something that is +easily guessed, and don't make it the same as the password for your +e-mail account! + +

+ +If you prefer to receive your e-mail at a location other than your +Union College account, you should change your e-mail address in the box +at the left. Be aware, however, that your professor (and others on +the Union campus) may still send mail to your Union College address, +so you should check that account regularly anyway. Note that you can +have your Union college e-mail forwarded to another address +automatically if you wish. + +

+ +Finally, when you have made the changes that you want to make, select +the "Homework Sets" link at the top of the red panel at the far left +to get back to the list of homework sets. diff --git a/courses.dist/modelCourse/templates/setOrientation/parserOrientation.pl b/courses.dist/modelCourse/templates/setOrientation/parserOrientation.pl new file mode 100644 index 0000000000..99abee9ff4 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/parserOrientation.pl @@ -0,0 +1,229 @@ +#!/usr/local/bin/perl + +###################################################################### +# +# Macros used by the orientation problem set +# +###################################################################### + + +#loadMacros("PGcourse.pl"); + +# +# Special use of CARET to have it work in non-math mode +# +$CARET = MODES( + TeX => '\hbox{\texttt{\char94}}', + Latex2HTML => '^', + HTML => '^' +); + +# +# Functions to display student input and computer output +# (written as functions so that we change the style without +# recoding the problems themselves). +# +sub student { + my $message = shift; + MODES( + TeX => '\leavevmode\hbox{\texttt{'.$message.'}}', + Latex2HTML => + $bHTML.''.$eHTML.$message.$bHTML.''.$eHTML, + HTML => ''.$message.'' + ); +} + +sub computer { + my $message = shift; + MODES( + TeX => '\hbox{\texttt{'.$message.'}}', + Latex2HTML => + $bHTML.''.$eHTML.$message.$bHTML.''.$eHTML, + HTML => ''.$message.'' + ); +} + +# +# This prints things we need to fill in yet in red +# +sub moreWork { + my $message = shift; + MODES( + TeX => '{\sl ' . $message . '}', + Latex2HTML => $bHTML . '' . $eHTML . + $message . $bHTML . '' . $eHTML, + HTML => '' . $message . '' + ); +} + +# +# Temporary macros to mark comments in areas that we are +# working on. +# +$BCOMMENT = MODES( + TeX => '{\footnotesize\it', + Latex2HTML => $bHTML.'

'.$eHTML, + HTML => '
' +); + +$ECOMMENT = MODES( + TeX => '}', + Latex2HTML => $bHTML.'
'.$eHTML, + HTML => '
' +); + +# $BCOMMENT = MODES( +# TeX => '\iffalse', +# Latex2HTML => $bHTML.''.$eHTML, +# HTML => ' -->' +# ); + + +# +# Hack to get better spacing in HTML_tth math mode but without +# messing up the spacing in other modes. +# +$SP = MODES( + TeX => ' ', Latex2HTML => ' ', + HTML => ' ', HTML_tth => '\ ', + HTML_jsMath => ' ', HTML_dpng => ' ', +); + + +# +# Special table macros for questions that have +# displayed math expressions equal to an answer rule, +# with an accompanying explanation on a separate line. +# + +sub BeginExamples { + return "" if ($displayMode eq "TeX"); + BeginTable(@_); +} + +sub EndExamples { + return "" if ($displayMode eq "TeX"); + EndTable(); +} + +@ExampleDefaults = (ans_rule_len => 40, ans_rule_height => 1); + +sub BeginExample { + my $math = shift; + my $ans = shift; + my %options = (@ExampleDefaults, @_); + my ($cols,$rows) = ($options{ans_rule_len},$options{ans_rule_height}); + my $rule; + + if ($rows == 1) {$rule = ans_rule($cols)} + else {$rule = ans_box($rows,$cols)} + ANS($ans); + + # + # HTML_tth puts an unwanted
at the beginning, + # and uses a centered table. Remove the
and + # align the table to the right. + # + if ($displayMode eq "HTML_tth") { + $math = trimString(EV2('\['.$math.'\]')); + $math =~ s!
!!; + $math =~ s!table align="center"!table align="right"!; + } elsif ($displayMode eq "HTML") { + $math = '\('.$math.'\)' + } elsif ($displayMode =~ m/^HTML/) { + $math = '\(\displaystyle '.$math.'\)' + } + + MODES( + TeX => "\n".'\['.$math.'=\hbox to 8em{'.$rule.'}\]', + Latex2HTML => $bHTML.''.$eHTML. + '\(\displaystyle '.$math.'\)'.$bHTML.' =  '. + ''.$eHTML.$rule.$bHTML.''. + ''.$eHTML, + HTML => + ''.$math.' =  '. + ''.$rule.'' + ); +} + +sub EndExample { + MODES( + TeX => "\n", + Latex2HTML => $bHTML.'

'.$eHTML, + HTML => '

' + ); +} + +sub ExampleRule { + MODES( + TeX => '\par', + Latex2HTML => $bHTML.'
'.$eHTML, + HTML => '
' + ); +} + +# +# Produce a TeX version and an answer checker for the formula +# +sub DisplayQA {my $f = shift; return (DMATH($f->TeX),$f->cmp)} +sub QA {my $f = shift; return ($f->TeX,$f->cmp)} + +################################################## +# +# Insert an image of an equation (but use the equation +# in TeX mode). +# + +sub MathIMG { + my ($img,$text,$tex) = @_; + my $useTeX = MODES(TeX => 1, Latex2HTML => 0, HTML => 0, HTML_tth => 0, HTML_dpng => 1); + return '\('.$tex.'\)' if $useTeX; + $img = alias($img); + return qq{$text}; +} + + +################################################## +# +# A simple grader that always returns a score of 1. +# This is used in the tutorial to give students +# credit for reading a problem (even if it doesn't +# ask any questions). +# +sub forgiving_grader { + my $rh_evaluated_answers = shift; + my $rh_problem_state = shift; + my %form_options = @_; + my %evaluated_answers = %{$rh_evaluated_answers}; + my %problem_state = %{$rh_problem_state}; + + my %problem_result = ( + score => 1, # always return 1 + errors => '', + type => 'forgiving_grader', + msg => '', + ); + + return(\%problem_result,\%problem_state) + if (!$form_options{answers_submitted}); + + $problem_state{recorded_score} = $problem_result{score}; + $problem_state{num_of_correct_ans}++; + + (\%problem_result, \%problem_state); +} + +################################################## +# +# Syntactic sugar to avoid ugly ~~& construct in PG. +# +sub install_forgiving_grader {install_problem_grader(\&forgiving_grader)} + + +1; diff --git a/courses.dist/modelCourse/templates/setOrientation/prob01.pg b/courses.dist/modelCourse/templates/setOrientation/prob01.pg new file mode 100644 index 0000000000..739d2eec5d --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob01.pg @@ -0,0 +1,82 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +TEXT(beginproblem()); + +Title("Understanding $WW Problem Pages"); + +############################################## +BEGIN_TEXT + +The $WW screen is divided into several areas, each used for a +different purpose. You will need to understand these +in order to use $WW effectively. +$PAR + +At the upper left are the navigation buttons that allow you to move +from problem to problem. The ${LQ}Next$RQ and ${LQ}Previous$RQ +buttons, naturally, send you to the next and previous problems. The +${LQ}Prob. List$RQ button takes you back to the opening +page for the homework set (the one that lists all the problems and +gives the instructions for the homework set). +$PAR + +The area below the navigation buttons is where $WW tells you about +your score for the current problem. When you have submitted your +answers, this is where you will be given information about what +answers you got right and wrong. (It will be shown on a grey +backgound to help make it stand out; you will see this later.) This +area also shows you how many points a problem is worth. +$PAR + +The main part of the page is the text of the problem you are trying to +answer, including blank boxes for you to enter your answers. (There +aren't any such boxes on this page, because you are not being asked +any questions here, but usually there will be one or more answer blanks +on a page.) +$PAR + +Below the problem text is a message area where you may be informed +about how partial credit is handled in multi-part problems. Other +information also may appear there, such as a message indicating that +the due date is passed, or that answers are available. +$PAR + +In the red panel at the left, instead of the list of homework sets, you +now have a list of the problems within this assignment. You can go to +any problem just by clicking on it. The checkboxes at the bottom of +the panel are discussed in the next problem. +$PAR + +In the center of the top red panel is an indication of how much time +you have left to work on the problems for this set. Note that this is +computed at the moment you downloaded the problem page; if you use the +${LQ}Back$RQ and ${LQ}Forward$RQ buttons on your browser, this does +not reload the page, so the times will not be updated. Reload the +page using your browser's Reload or Refresh buttons to get the current +time. +$PAR + +The buttons at the bottom of the screen, including the ${LQ}Submit +Answers$RQ button, are discussed in the next problem. At this point, +you can get credit for Problem 1 by pressing the ${LQ}Submit +Answers$RQ button at the bottom of the page (even though there was no +answer to submit), and then pressing the ${LQ}Next$RQ button at the +top of the screen to go on to the next problem. + +END_TEXT + + +install_forgiving_grader(); +$showPartialCorrectAnswers = 1; + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob02.pg b/courses.dist/modelCourse/templates/setOrientation/prob02.pg new file mode 100644 index 0000000000..9cee044cbc --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob02.pg @@ -0,0 +1,108 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +TEXT(beginproblem()); + +Title("Controlling $WW"); + +############################################## + +BEGIN_TEXT + +The buttons at the bottom of the screen are what cause $WW to process +your answers. Nothing that you type will have any effect until you +press one of these buttons. +$PAR + +The ${LQ}Submit Answers$RQ button causes $WW to check your answers and +report your score for the problem. You can continue to work on a +problem until you get it right, so don't be afraid to submit your +answer even if you have only finished parts of the problem or are +not sure of the correctness of your answer. If the due date is +passed and the answers are available, you can click the ${LQ}Show +Correct Answers$RQ button before pressing ${LQ}Submit$RQ. +If you do, the correct answer(s) will be displayed in the answer area +at the top of the screen along with the answers you have provided. +$PAR + +If you have typed in a complicated answer, or are being told your +answer is incorrect when you think it's right, you may want to use the +${LQ}Preview Answer$RQ button. This will ask $WW to display at the +top of the page its interpretation of what you have entered. This +can be used to help spot errors in your typing, and verify that $WW +understands your answer the way you intend it to. (This is discussed +further in a later problem.) +$PAR + +At the bottom of the red panel at the left is a box that allows you to +change how the problem is displayed. The equations within the problem +can be represented in three different ways: + +\{BeginParList("UL")\} + +$ITEM +${LQ}images${RQ} mode is the default choice (what $WW uses unless you tell +it otherwise). It produces the accurate mathematical notation, +with the disadvantages of being slightly slower, and not printing well +if you want to print out a single problem. +$ITEMSEP + +$ITEM +${LQ}formatted-text${RQ} mode produces results quickly, but more +complicated mathematics may be difficult to read in this +mode. For example, some fractions and integrals are not easy to read +in formatted-text mode. +$ITEMSEP + +$ITEM +${LQ}jsMath${RQ} mode uses a JavaScript program and Cascading Style +Sheets (CSS) to display the mathematics. This produces good results, +and is particularly nice if you need to change the size of the fonts +in your browser or wish to print out a single problem. For best +results, however, you should download and install some fonts that +jsMath uses; there is a link to the fonts on the jsMath control panel, +which you can activate using the small jsMath button that will appear +at the lower right of the screen when you activate this mode. The +first problem you view using jsMath may take a little longer to +appear, as the JavaScript program is being loaded into your browser, +but things should run faster after that. + +\{EndParList("UL")\} +$PAR + +Choose whichever mode is most comfortable for you. You can always +select a different mode if a particular problem needs it. Here is a +sample of some simple mathematics, \(x^2 + 3\), and a more complicated +expression, \(\frac{x(1-x)}{2x + 1}\). Try changing the display mode +by clicking the ${LQ}formatted-text$RQ or ${LQ}jsMath${RQ} radio +button and then pressing the ${LQ}Apply Options${RQ} button to cause +the page to be reformatted. Then change the display mode to back to +${LQ}images$RQ mode for the rest of the homework set. +$PAR + +The ${LQ}Show saved answers${RQ} checkbox tells whether you +want $WW to fill in the answer blanks with your previous answers or +not. (If you like, you can test this out on the next problem, since +there are no answer blanks in this one.) +$PAR + +You are now ready to learn how to enter answers into $WW. Press the +${LQ}Submit Answers${RQ} button to get credit for this problem, and +then press the ${LQ}Next$RQ button at the top of the page to go on to +the next one. + +END_TEXT + +install_forgiving_grader(); +$showPartialCorrectAnswers = 1; + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob03.pg b/courses.dist/modelCourse/templates/setOrientation/prob03.pg new file mode 100644 index 0000000000..fef926ba5d --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob03.pg @@ -0,0 +1,141 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "contextLimitedNumeric.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Typing in Your Answers"); + +############################################## + +BEGIN_TEXT + +Here are the standard symbols that $WW, along with most other +computer software, uses for arithmetic operations: +$PAR + +\{ + BeginTable(). + Row([$BBOLD.'Symbol'.$EBOLD, + $BBOLD.'Meaning'.$EBOLD, + $BBOLD.'Example'.$EBOLD]). + TableLine(). + Row([computer("+"),'Addition',computer("3+4 = 7")],align=>"CENTER"). + Row([computer("-"),'Subtraction',computer("3-4 = -1")],align=>"CENTER"). + Row([computer("*"),'Multiplication',computer("3*4 = 12")],align=>"CENTER"). + Row([computer("/"),'Division',computer("3/4 = .75")],align=>"CENTER"). + Row([computer($CARET)." or ".computer("**"),'Exponentiation', + computer("3${CARET}4 = 81")." or ". + computer("3**4 = 81")],align=>"CENTER"). + TableLine(). + EndTable() +\} +$PAR + +END_TEXT + +################################################## + +$a = non_zero_random(-5,5,1); +$b = non_zero_random(-5,5,1); +$c = non_zero_random(-3,3,1) * 2; +$d = non_zero_random(-5,5,1); + +BEGIN_TEXT + +Sometimes $WW will insist that you calculate the value of an +expression as a single number before you enter it. For example, +calculate the value of \($c($a - $b) - ($c - $d)\) and enter it in +the following blank. +(Here you have to enter a single integer; the question is testing +whether you can do the operations correctly.) + +$PAR +$BBLOCKQUOTE +\($c($a - $b) - ($c - $d)\) = \{ans_rule(10)\} +$EBLOCKQUOTE +$PAR +END_TEXT + +Context("LimitedNumeric"); +$ans = $c*($a - $b) - ($c - $d); +ANS(Real($ans)->cmp); + +################################################## + +BEGIN_TEXT + +Most often you will not have to simplify your answer, but can let +$WW do this for you. The following blanks are all expecting +the value 16. Try entering it several different ways, such as +\{student "7+9"\}, \{student "18-2"\}, \{student "8*2"\}, +\{student "32/2"\}, and \{student "4${CARET}2"\}. Note: pressing +the ${LQ}Tab$RQ key on your keyboard will move you from one answer +box to the next. + +$PAR +$BBLOCKQUOTE +16 = \{ans_rule(8)\} or +\{ans_rule(8)\} or +\{ans_rule(8)\} or +\{ans_rule(8)\} or +\{ans_rule(8)\} +$EBLOCKQUOTE +$PAR + +END_TEXT + +Context("Numeric"); + +ANS( + Real(16)->cmp, + Real(16)->cmp, + Real(16)->cmp, + Real(16)->cmp, + Real(16)->cmp, +); + +################################################## + +BEGIN_TEXT + +$WW also understands that quantities written next to each other are +supposed to be multiplied. For example, you can enter \{student +"(9)(7)"\} instead of \{student "63"\}. Most often this is used when +one quantity is a number and the other a variable or function. For +instance, \{computer "2x"\} means \{computer "2*x"\}, while \{computer +"3sin(5x)"\} means \{computer "3*sin(5*x)"\}. The following blank is +expecting the value 100; try entering it as +\{student("4(30-5)")\}. + +$PAR +$BBLOCKQUOTE +100 = \{ans_rule(10)\} +$EBLOCKQUOTE +$PAR +END_TEXT + +ANS(Real(100)->cmp); + +################################################## + +BEGIN_TEXT + +${BITALIC}When you are ready, don't forget to press the ${LQ}Submit Answers${RQ} +button to ask $WW to check your work. Once you get the answers +correct, press ${LQ}Next${RQ} to go on.${EITALIC} + +END_TEXT + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob04.pg b/courses.dist/modelCourse/templates/setOrientation/prob04.pg new file mode 100644 index 0000000000..09a051ade6 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob04.pg @@ -0,0 +1,95 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "MathObjects.pl", + "PGunion.pl", + "alignedChoice.pl", + "contextLimitedNumeric.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Rules of Precedence"); + +############################################## + +BEGIN_TEXT + +The rules of precedence determine the order in which the mathematical +operations are performed by $WW. It is essential for you to understand +these so that you know how $WW interprets what you type in. If there +are no parentheses and no functions (such as \{computer "sin"\} or +\{computer "log"\}), then $WW computes the value of your answer by +performing exponentiation first, followed by multiplication and division +(from left to right), and finally addition and subtraction +(from left to right). +$PAR + +If there are expressions within parentheses, those expressions are +simplified first. We'll talk about functions (and give a more +complete list of rules) in a later problem. + +$PAR +Examples: +\{BeginList("UL")\} +$ITEM +\{student "4*3/6 = 12/6 = 2"\} (multiplications and divisions are done +from left to right), and \{student "2*7 = 14"\}, so +\{student "4*3/6-2*7+10 = 2 - 14 + 10 = -2"\}. +$ITEM +\{student "12/3/2 = 4/2 = 2"\} (multiplications and divisions are done +from left to right). +$ITEM +\{student "12/(3/2) = 12/1.5 = 8"\} +(expressions inside parentheses are calculated before anything else). +$ITEM +\{student "2*4${CARET}2 = 2*16 = 32"\} (exponentiation is done before multiplication), +so \{student "2*4${CARET}2 - 3*4 = 2*16 - 3*4 = 32 - 12 = 20"\}. +\{EndList("UL")\} +$PAR + +To practice these rules, completely simplify the following +expressions. Because the point of this problem is for you to do the +numerical calculations correctly, $WW will only accept sufficiently +accurate decimal numbers as the answers to these problems. +It will not simplify any expressions, including fractions. +$PAR + +END_TEXT + +$a = random(1,6,1); +$b = random(2,6,1); +$c = random(3,6,1); +$d = random(2,25,1); +$al = new_aligned_list(equals => 1); + +$al->qa( + computer("$a+$b*$c"), Real($a+($b*$c))->cmp, +# computer("($a+$b)*$c"), Real(($a+$b)*$c)->cmp, +# computer("($a+$b)/$c"), Real(($a+$b)/$c)->cmp, +# computer("$a+$b/$c"), Real($a+($b/$c))->cmp, + computer("$a/$b*$c"), Real(($a/$b)*$c)->cmp, +# computer("$a/($b*$c)"), Real($a/($b*$c))->cmp, +# computer("$a/$b/$c"), Real(($a/$b)/$c)->cmp, + computer("3*$b-$a/5*$c+$d"), Real((3*$b)-(($a/5)*$c)+$d)->cmp, + computer("2${CARET}$b+1"), Real((2**$b)+1)->cmp, + computer("2${CARET}($b+1)"), Real(2**($b+1))->cmp, +); + +BEGIN_TEXT +$BBLOCKQUOTE +\{$al->print_q\} +$EBLOCKQUOTE + +END_TEXT + +ANS($al->correct_ans); + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-a.gif b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-a.gif new file mode 100644 index 0000000000..d32ee11b3b Binary files /dev/null and b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-a.gif differ diff --git a/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-b.gif b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-b.gif new file mode 100644 index 0000000000..b074b10281 Binary files /dev/null and b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-b.gif differ diff --git a/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-c.gif b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-c.gif new file mode 100644 index 0000000000..71062c6f06 Binary files /dev/null and b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05-c.gif differ diff --git a/courses.dist/modelCourse/templates/setOrientation/prob05/prob05.pg b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05.pg new file mode 100644 index 0000000000..3e9f0f9fb2 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob05/prob05.pg @@ -0,0 +1,131 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "MathObjects.pl", + "PGunion.pl", + "alignedChoice.pl", + "../parserOrientation.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem); + +Title("Common Errors to Avoid"); + +############################################## + +BEGIN_TEXT + +Many of the answers you enter into $WW will be expressions +that involve variables. Here are some important things to know. + +$PAR + +\{BeginParList("UL")\} + +$ITEM +It matters what letter you use. For example, if you are asked for a +function using the variable \(x\), then it won't work to enter the +function with the variable \(t\). Also, $WW considers upper- and +lower-case letters to be different, so don't use the capital letter +\{student "X"\} in place of the lower-case letter \{student "x"\}. +The following blank is expecting the +function \(x^3\), which you would enter as \{student "x${CARET}3"\} or +\{student "x**3"\}. Instead, try entering \{student "t${CARET}3"\} and +submitting your answer. + +$PAR +$BBLOCKQUOTE +\{ans_rule(10)\} +$EBLOCKQUOTE +$PAR + +You should get an error message informing you that \{computer "t"\} +is not defined in this context. This tells you that $WW did not receive the +correct variable and doesn't know how to check your answer. Now enter +\{student "x${CARET}3"\} and resubmit to get credit for this part of +the problem. + +END_TEXT + +ANS(Formula("x^3")->cmp); + +################################################## + +$IMGA = MathIMG("prob05-a.gif","1/x+1","1/x+1"); +$IMGB = MathIMG("prob05-b.gif","1/(x+1)","\frac{1}{x+1}"); +$IMGC = MathIMG("prob05-c.gif","(1/x)+1","\frac{1}{x} + 1"); + +BEGIN_TEXT + +$ITEM +$WW requires that you be precise in how you think about and present +your answer. We have just seen that you need to be careful about the +variables that you use. You must be equally careful about how the +rules of precedence apply to your answers. Often, this involves using +parentheses appropriately. + +$PAR + +For example, you might write $IMGA on your paper when you meant $IMGB, +but that is actually incorrect. The expression $IMGA means $IMGC, +according to the rules of precedence. $WW will force you to be exact +in what you are thinking and in what you are writing, because it must +interpret your answers according to the standard rules. If you want +to enter something that means $IMGB, you must write \{student +"1/(x+1)"\}. This also is true in written work, so making a habit of +being precise about this will improve your written mathematics as well +as your ability to enter answers quickly and correctly in $WW. + +$PAR +END_TEXT + +################################################## + +BEGIN_TEXT + +\{EndParList("UL")\} + +$PAR +$HR +$PAR + +Now enter the following functions: +$PAR +END_TEXT + +$al = new_aligned_list( + equals => 1, ans_rule_len => 30, + tex_spacing => "5pt", spacing => 10 +); + +Context("Numeric")->variables->are(t=>'Real'); $t = Formula("t"); +Context("Numeric")->variables->are(y=>'Real'); $y = Formula("y"); +Context("Numeric")->variables->are(x=>'Real'); $x = Formula("x"); + +$al->qa( + DisplayQA($t/(2*$t+6)), +# DisplayQA(2*$y*($y**2-$y+1)), +# DisplayQA(1/$x**2 - 3*(1/$x)), + DisplayQA(1/(2*($x-5))), + DisplayQA((2*$x-3)**4), +); + +BEGIN_TEXT + +$BBLOCKQUOTE +\{$al->print_q\} +$EBLOCKQUOTE + +END_TEXT + +ANS($al->correct_ans); + + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob06.pg b/courses.dist/modelCourse/templates/setOrientation/prob06.pg new file mode 100644 index 0000000000..586ad35b35 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob06.pg @@ -0,0 +1,129 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +$showPartialCorrectAnswers = 1; + +Context("Numeric")->variables->are(y=>'Real'); $y = Formula('y'); +Context("Numeric")->variables->are(x=>'Real'); $x = Formula('x'); +Context()->flags->set(limits=>[0,2]); + +TEXT(beginproblem()); +Title("Using Parentheses Effectively"); + +############################################## + +BEGIN_TEXT + +One of the hardest parts about using parentheses is making sure that +they match up correctly. Here are a couple of hints to help you with +this: + +$PAR +END_TEXT + +$BRACES = HTML('{}','\char123\char125'); + +BEGIN_TEXT + +\{BeginParList("UL")\} + +$ITEM +Several types of parentheses are allowed: \{student "()"\}, +\{student "[]"\}, and \{student $BRACES\}. When you need to nest +parentheses inside other parentheses, try using a different type for +each so that you can see more easily which ones match up. +$ITEMSEP + +$ITEM +When you type a left parenthesis, type the corresponding right +parenthesis at the same time, then position your cursor between them and +type the expression that goes inside. This can save you a +lot of time hunting for mismatched parentheses. +$ITEMSEP + +$ITEM +When you have a complicated answer, type a template for +the structure of your result first. For example, suppose that you are +planning to enter the fraction +\[\frac{2x^2-5}{(x+1)(3x^{3x} - 22)}.\] +A good way to start would be to type in \{student "()/[()*()]"\}. +This shows a template of one number divided by the product of two +other numbers. (Note that \{student "()/()*()"\} would not be a good +way to start; do you see why?) Now when you fill in the expressions, you +will be sure your parentheses balance correctly. +$PAR + +Although $WW understands that numbers written next to each other are +meant to be multiplied (so you do not have to use \{student "*"\} to +indicate multiplication if you don't want to), it is often useful for +you to include the \{student "*"\} anyway, as it helps you keep track +of the structure of your answer. +$PAR + +$ITEM +To see how $WW is interpreting what you type, enter your answer and +then click the ${LQ}Preview Answers$RQ button, which is next to the +${LQ}Submit Answers$RQ button below. $WW will show you what it thinks +you entered (the preview appears in your answer area at the top of the +page). Previewing your answer does not submit it for credit; that only +happens when you press the ${LQ}Submit Answers$RQ button. +$ITEMSEP + +$ITEM +When division or exponentiation are involved, it is a good idea to +use parentheses even in simple situations, rather than relying on the +order of operations. For example, 1/2x and (1/2)x both mean the same +thing (first divide 1 by 2, then multiply the result by x), but the +second makes it easier to see what is going on. Likewise, use +parentheses to clarify expressions involving exponentiation. Type +\{student "(e${CARET}x)${CARET}2"\} if you mean \((e^x)^2\), and type +\{student "e${CARET}(x${CARET}2)"\} if you mean \(e^{(x^2)}\). + +\{EndParList("UL")\} + +$PAR +$HR +$PAR + +Now enter the following functions: + +$BBLOCKQUOTE + +\{@ExampleDefaults = (ans_rule_len => 50, ans_rule_height => 1); + BeginExamples\} + +\{BeginExample(QA(($x**(2*$x-1))/(($x**2-$x)*(3*$x+5))))\} +Start with the template \{student "[x${CARET}()]/[()*()]"\}. +\{EndExample\} +\{ExampleRule\} + +\{BeginExample(QA((($y+3)*($y**3+$y+1))/((2*$y**2-2)*(5*$y+4))))\} +Start by putting in an appropriate template. This means that you +should begin by looking at the function and thinking about how many +pieces are used to construct it and how those pieces are related. +Once you have entered your answer, try using the ${LQ}Preview$RQ button +to see how $WW is interpreting your answer. +\{EndExample\} +\{ExampleRule\} + +\{BeginExample(QA((($x+1)/($x-2))**4))\} +Start by putting in an appropriate template. +\{EndExample\} + +\{EndExamples\} + +$EBLOCKQUOTE + +END_TEXT + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob07.pg b/courses.dist/modelCourse/templates/setOrientation/prob07.pg new file mode 100644 index 0000000000..32bffa2fb8 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob07.pg @@ -0,0 +1,137 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "MathObjects.pl", + "PGunion.pl", + "alignedChoice.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Constants and Functions in $WW"); + +############################################## + +BEGIN_TEXT + +$WW knows the value of \(\pi\), which you can enter as \{student +"pi"\}, and the value of \(e\) (the base of the natural logarithm, +\(e\approx 2.71828\)), which you can enter simply as the letter +\{student "e"\}. +$PAR + +$WW also understands many standard functions. Here +is a partial list. Notice that all the function names start with a lower-case +letter. Capitalizing the function will lead to an error message. + +\{BeginParList("UL")\} + +$ITEM +$WW knows about \{student "sin(x)"\}, \{student "cos(x)"\}, \{student +"tan(x)"\}, \{student "arcsin(x)"\}, \{student "arccos(x)"\}, +\{student "arctan(x)"\} and the other trigonometric functions and their +inverses. $WW ${BITALIC}always$EITALIC uses radian mode for these +functions. +$PAR + +$WW will evaluate trigonometric functions for you in many situations. +For example, the following blank is expecting the value \(-1\). +Remember that \(\cos(\pi) = -1\), so enter \{student "cos(pi)"\} +and submit it. + +$PAR +$BBLOCKQUOTE +\{ans_rule(10)\} \(= -1\) +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS(Real(-1)->cmp); + +################################################## + +BEGIN_TEXT + +$ITEM +The square root \(\sqrt x\) is represented by the function \{student +"sqrt(x)"\} or by \{student "x${CARET}(1/2)"\}. +$ITEMSEP + +$ITEM +The function \{student "log(x)"\} means the ${BITALIC}natural$EITALIC +logarithm of \(x\) (the logarithm with base \(e\)), not the common +logarithm (the logarithm with base \(10\), sometimes written +\(\log_{10}\)). You can also write \{student "ln(x)"\} for the +natural logarithm of \(x\), so \{student "log(x)"\} and \{student "ln(x)"\} +mean the same thing. Use \{student "log10(x)"\} for the base 10 +logarithm of \(x\). +$ITEMSEP + +$ITEM +The exponential function with base \(e\) can be entered as +\{student "e${CARET}x"\} or \{student "exp(x)"\}. The second notation +is convenient if you have a long, complicated exponent. +$ITEMSEP + +$ITEM +The absolute value function, \(|x|\), should be entered as +\{student "|x|"\} or \{student "abs(x)"\}. +$ITEMSEP + +$ITEM +The inverse sine function, \(\sin${CARET}{-1}(x)\), is written +\{student "arcsin(x)"\} or \{student "asin(x)"\} or \{student "sin${CARET}(-1)(x)"\} +in $WW. Note that this is ${BITALIC}not$EITALIC the same as +\{student "(sin(x))${CARET}(-1)"\}, which means \(\frac{1}{\sin(x)}\). +The other inverse functions are handled similarly. + +\{EndParList("UL")\} + +$PAR +$HR +$PAR + +Now enter the following functions: +$PAR +END_TEXT + +$al = new_aligned_list( + equals => 1, + ans_rule_len => 40, + tex_spacing => "5pt", + spacing => 10, +); + +Context("Numeric")->variables->are(t=>'Real'); $t = Formula('t'); +Context("Numeric")->variables->are(y=>'Real'); $y = Formula('y'); +Context("Numeric")->variables->are(x=>'Real'); $x = Formula('x'); +Context()->flags->set(limits=>[-2,10]); + +$al->qa( +# DisplayQA(sqrt($y**2+1)), +# DisplayQA(sin(3*$x+1)), + DisplayQA(1/tan($x)), + DisplayQA(asin($t+1)->with(limits=>[-2,0])), + DisplayQA((sin($x)-cos($x))/sqrt(2*$x-7)) +); + +BEGIN_TEXT + +$BBLOCKQUOTE +\{$al->print_q\} +$EBLOCKQUOTE + +END_TEXT + +ANS($al->correct_ans); + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob08.pg b/courses.dist/modelCourse/templates/setOrientation/prob08.pg new file mode 100644 index 0000000000..4534a5c325 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob08.pg @@ -0,0 +1,158 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +Context("Numeric")->variables->are(y=>'Real'); $y = Formula('y'); +Context("Numeric")->variables->are(x=>'Real'); $x = Formula('x'); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Rules of Precedence (Again)"); + +############################################## + +$Explanation = "${BITALIC}Explanation${EITALIC}"; +$Moral = "${BITALIC}Moral${EITALIC}"; + +BEGIN_TEXT + +At this point, we can give the complete rules of precedence for +how $WW computes the value of a mathematical formula. The operations +are handled in the following order: +$PAR + +\{BeginList\} +$ITEM Evaluate expressions within parentheses. +$ITEM Evaluate functions such as \{student "sin(x)"\}, +\{student "cos(x)"\}, \{student "log(x)"\}, \{student "sqrt(x)"\}. +$ITEM Perform exponentiation (from right to left). +$ITEM Perform multiplication and division, (from left to right). +$ITEM Perform addition and subtraction, (from left to right). +\{EndList\} +$PAR + +This can get a little subtle, so be careful. The following are some +typical traps for $WW users. +$PAR + +\{BeginParList("UL")\} + +$ITEM +$WW interprets \{student "sin 2x"\} to mean \((\sin${SP}2)*x\) +$PAR + +$Explanation: Rule 2 tells you that $WW does evaluation of functions +(like \{student "sin"\}) before multiplication. Thus $WW first +computes \(\sin${SP}2\), and then multiplies the result by \(x\). +$PAR + +$Moral: You must type \{student "sin(2x)"\} for the sine of \(2x\), +even though we often write it as \(\sin${SP}2x\). +Get in the habit of using parentheses for all your trigonometric +functions. +$PAR + +Now enter the following function: +$PAR +$BBLOCKQUOTE +The cosine of \(5x\) is entered as \{ans_rule(15)\}. +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS(cos(5*$x)->cmp); + +BEGIN_TEXT + +$ITEM +$WW interprets \{student "cos t${CARET}3"\} to mean \((\cos${SP}t)^3\) +$PAR + +$Explanation: Rule 2 tells you that $WW does evaluation of functions +(like \{student "cos"\}) before exponentiation. Thus $WW first +computes \(\cos${SP}t\) and then raises the result to the power 3. +$PAR + +$Moral: You must type in \{student "cos(t${CARET}3)"\} if you mean the +cosine of \(t^3\), even though we sometimes write it as \(\cos${SP}t^3\). +$PAR + +Now enter the following function: +$PAR +$BBLOCKQUOTE +The tangent of \(y^4\) is entered as \{ans_rule(15)\}. +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS(tan($y**4)->cmp); + +BEGIN_TEXT + +$ITEM +In mathematics, we often write \(\sin^2${SP}x\) to mean \((\sin x)^2\). +$WW will let you write \{student "sin${CARET}2(x)"\} for this, though +it is probably better to type \{student "(sin(x))${CARET}2"\} instead, +as this makes your intention clearer. Note that a power of \(-1\), as +in \{student "sin${CARET}(-1)(x)"\}, is a special case; it indicates the +${BITALIC}inverse${EITALIC} function \{student "arcsin(x)"\} rather +than a power. +$PAR + +Now enter the following function: +$PAR +$BBLOCKQUOTE +\(\sin^2${SP}x + \cos^3${SP}x\) = \{ans_rule(30)\} +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS((sin($x)**2 + cos($x)**3)->cmp); + +BEGIN_TEXT + +$ITEM +\{student "e${CARET}3x"\} means \((e^3) x\) and not \(e^{(3x)}\) $PAR +$PAR + +$Explanation: Rule 3 says that $WW does exponentiation before multiplication. +Thus $WW first computes \{student "e${CARET}3"\}, with the result +\(e^3\), and then multiplies the result by \(x\). +$PAR + +$Moral: Always put parentheses around an exponent. +Type \{student "e${CARET}(3x)"\} if you want \(e^{3x}\). +$PAR + +Now enter the following function: +$PAR +$BBLOCKQUOTE +\(2^{4x^3}\) = \{ans_rule(30)\} +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS((2**(4*($x**3)))->cmp); + +BEGIN_TEXT + +\{EndParList("UL")\} + + +END_TEXT + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob09.pg b/courses.dist/modelCourse/templates/setOrientation/prob09.pg new file mode 100644 index 0000000000..08c31ab5ce --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob09.pg @@ -0,0 +1,113 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Non-Numeric Answers"); + +############################################## + +BEGIN_TEXT + +Sometimes you will be asked to enter answers that are not numbers. +For example, if you are asked to determine a limit, the answer may be +that the limit does not exist, so you might have to type \{student +"DNE"\} to indicate this (the problem should tell you what word to +use). Note that upper- and lower-case letters are not the same to +$WW, so you will need to enter the answer exactly as indicated in the +problem. (Well written problems will allow the answer to be +entered either way.) +$PAR + +$BBLOCKQUOTE +Please enter ${LQ}\{student "DNE"\}${RQ} here: \{ans_rule(10)\}. +$EBLOCKQUOTE + +END_TEXT + +ANS(String('DNE')->cmp); + +################################################## + +BEGIN_TEXT + +Other problems may require you to enter \(\infty\), which you do using +the word ${LQ}\{student "INFINITY"\}${RQ} (in upper- or lower-case) or +${LQ}\{student "INF"\}${RQ} for short. The problem should remind you +of how to do this. Note that most operations are not defined on +infinity, so you can't add or multiply something by infinity. You +can, however, indicate \(-\infty\) by ${LQ}\{student "-INFINITY"\}${RQ}, +or ${LQ}\{student "-INF"\}${RQ}. +$PAR + +$BBLOCKQUOTE +Try entering \(-\infty\) here: \{ans_rule(10)\}. +$EBLOCKQUOTE + +END_TEXT + +ANS((-(Infinity))->cmp); + +################################################## + +Context("Interval"); + +$a = random(-5,5,1); +$I = Compute("(-infinity,$a)"); + +BEGIN_TEXT + +One common place where you use \(\infty\) is as an endpoint of +an interval. $WW allows you to enter intervals using standard +interval notation, including infinite endpoints. For example, +\{student "[-2,5)"\} represents an interval that is closed on the +left and open on the right, while \{student "[2,inf)"\} is an interval +that extends infinitely to the right. +$PAR + +$BBLOCKQUOTE +Write the interval of points that are less than \($a\): \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT + +ANS($I->cmp); + +################################################## + +Context("Interval"); + +$a = random(-8,-2,1); +$b = random($a+1,$a+5,1); +$c = random($b+1,$b+5,1); +$I = Compute("[$a,$b) U ($b,$c)"); + +BEGIN_TEXT + +Several intervals can be combined into one region using the ${LQ}set +union${RQ} operation, \(\cup\), which is represented as ${LQ}\{student +"U"\}${RQ} in $WW. For example, \{student "[-2,0] U (8,inf)"\} +represents the points from \(-2\) to \(0\) together with everything +bigger than 8. +$PAR + +$BBLOCKQUOTE +Write the set of points from \($a\) to \($c\) but excluding \($b\) and \($c\) +as a union of intervals: \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT + +ANS($I->cmp); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob10.pg b/courses.dist/modelCourse/templates/setOrientation/prob10.pg new file mode 100644 index 0000000000..2b1d392e58 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob10.pg @@ -0,0 +1,130 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", + "parserVectorUtils.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Points and Vectors"); + +############################################## + +Context("Vector"); + +$p0 = non_zero_point2D(); +$p1 = $p0 + 2*non_zero_point2D(2,2,1); + +Context()->texStrings; +BEGIN_TEXT + +Some problems will ask you to enter an answer that is a point rather +than a number. You enter points in $WW just as you would expect: by +separating the coordinates by commas and enclosing them all in +parentheses. So \{student "(2,-3)"\} represents the point in the +plane that has an \(x\)-coordinate of \(2\) and \(y\)-coordinate of +\(-3\). +$PAR + +$BBLOCKQUOTE +What point is halfway between \($p0\) and \($p1\)? \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT +Context()->normalStrings; + +ANS((($p0+$p1)/2)->cmp); + +################################################## + +$P = non_zero_point3D(); + +$LANGLE = HTML('<',"\char60 "); +$RANGLE = HTML('>',"\char62 "); + +Context()->flags->set(ijk=>1); +Context()->texStrings; +BEGIN_TEXT + +Other problems require you to provide a vector as your answer. $WW +allows you to enter vectors either as a list of coordinates enclosed +in angle braces, \{student $LANGLE\} and \{student $RANGLE\}, or as a +sum of multiples of the coordinate unit vectors, \(\{i\}\), \(\{j\}\) +and \(\{k\}\), which you enter as \{student "i"\}, \{student "j"\} and +\{student "k"\}. For example, \{student "${LANGLE}1,3,-2${RANGLE}"\} +represents the same vector as \{student "i+3j-2k"\}. +$PAR + +$BBLOCKQUOTE +What vector points from the origin to the point \($P\)? \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT +Context()->normalStrings; +Context()->flags->set(ijk=>0); + +ANS(Vector($P)->cmp); + +################################################## + +$v0 = non_zero_vector3D(); +$v1 = non_zero_vector3D(); + +$SPACING = HTML('  '); +$BNOBR = HTML(''); +$ENOBR = HTML(''); + +Context()->texStrings; +BEGIN_TEXT + +Just as you can enter a number by giving an equation that reduces to it, +$WW allows you to enter points and vectors by giving equations for the +individual coordinates, or by using a vector-valued equation that +reduces to your answer. For example, +$PAR +$BCENTER +$BNOBR\{student "${LANGLE}1-(-3),2-sqrt(4),6/2${RANGLE}"\}$ENOBR +${SPACING} and ${SPACING} +$BNOBR\{student "[1-(-3)]i + [2-sqrt(4)]j + (6/2)k"\}$ENOBR +$ECENTER +$PAR +both represent the vector \(\{Vector(4,0,3)\}\), while +$BNOBR\{student "${LANGLE}1,0,-1${RANGLE} + ${LANGLE}2,-2,3${RANGLE}"\}$ENOBR +could be used to answer a question that asks for the vector \(\{Vector(3,-2,2)\}\). +$PAR + +$BBLOCKQUOTE +Write \(\{$v0+$v1\}\) as a sum of two vectors: \{ans_rule(30)\}. +$EBLOCKQUOTE + +END_TEXT +Context()->normalStrings; + +# +# Check that the result actually IS a sum (or difference). +# +sub checkAdd { + my $ans = shift; + if ($ans->{score} == 1 && !$ans->{isPreview}) { + my $item = $ans->{student_formula}->{tree}; + $ans->{correct_value}->cmp_Error + ($ans,"Your answer is not a sum of vectors") + unless $item->class eq 'BOP' && + ($item->{bop} eq '+' || $item->{bop} eq '-'); + } + return $ans; +} + +my $check = ($v0+$v1)->cmp; +$check->install_post_filter(~~&checkAdd); +ANS($check); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob11.pg b/courses.dist/modelCourse/templates/setOrientation/prob11.pg new file mode 100644 index 0000000000..ca5a2227c5 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob11.pg @@ -0,0 +1,70 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + +$showPartialCorrectAnswers = 1; + +TEXT(beginproblem()); +Title("Multiple Answers in One Blank"); + +############################################## + +Context("Numeric"); + +$a = random(1,5,1); +$f = Formula("1/(x^2-$a^2)")->reduce; + +Context()->texStrings; +BEGIN_TEXT + +You may sometimes be asked to provide more than one answer in a single +answer blank. For example, you may need to enter all the values where +a function is not defined. In this case, you should separate your +answers by commas. Such an answer is called a +${BITALIC}list${EITALIC} in $WW. Note that you need not enter +multiple answers for a list; a single number is a legal answer (there +might only be one point where the function is undefined, for +instance). +$PAR + +$BBLOCKQUOTE +The function \(\displaystyle f(x)=$f\) is not defined at these \(x\) values: \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT +Context()->normalStrings; + +ANS(List($a,-$a)->cmp); + +################################################## + +$a = random(1,5,1); +$f = Formula("1/(x^2+$a^2)")->reduce; + +Context()->texStrings; +BEGIN_TEXT + +When you are asked for a list of numbers, another possible answer is +that there are ${BITALIC}no${EITALIC} numbers that satisfy the +requirements. In that case, you should enter ${LQ}\{student +"NONE"\}${RQ} as your answer. +$PAR + +$BBLOCKQUOTE +The function \(\displaystyle f(x)=$f\) is not defined at these \(x\) values: \{ans_rule(20)\}. +$EBLOCKQUOTE + +END_TEXT +Context()->normalStrings; + +ANS(String('NONE')->cmp); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob12.pg b/courses.dist/modelCourse/templates/setOrientation/prob12.pg new file mode 100644 index 0000000000..117d7992e3 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob12.pg @@ -0,0 +1,69 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "PGunion.pl", + "choiceUtils.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + +TEXT(beginproblem()); +Title("True/False Questions in $WW"); + +############################################## + +$a = random(1,5,1); +$b = random(6,10,1); +$c = random(-10,-1,1); +$d = random(-10,-1,1); +$e = random(1,10,1); + +$sl = new_select_list(); +$sl->{rf_print_q} = ~~&alt_print_q; +$sl->{separation} = 5; + +$sl->qa( + "\(-$a $LT -$b\)", "F", + "\($c $LE $c\)", "T", + "\($d $LT $d\)", "F", + "\(\pi $GE 3.2\)", "F", + "\($e-1 $LE $e\)", "T" +); + +$sl->choose(4); + +################################################## + +BEGIN_TEXT + +Enter a \{student "T"\} or an \{student "F"\} in each +answer space below to indicate whether the corresponding +statement is true or false. +$PAR + +$BBLOCKQUOTE +\{$sl->print_q\} +$EBLOCKQUOTE +$PAR + +END_TEXT + +ANS(str_cmp($sl->ra_correct_ans)); +install_problem_grader(~~&std_problem_grader); +$showPartialCorrectAnswers = 0; + +BEGIN_TEXT + +In most multipart problems, if one or more of your answers is wrong, +then $WW tells you which ones they are. For True/False or +multiple-choice questions, however, $WW usually only tells you whether +${BITALIC}all$EITALIC the answers are correct. It won't tell you +which ones are right or wrong. + +END_TEXT + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob13.pg b/courses.dist/modelCourse/templates/setOrientation/prob13.pg new file mode 100644 index 0000000000..7c43672d71 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob13.pg @@ -0,0 +1,67 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "PGunion.pl", + "parserOrientation.pl", + "choiceUtils.pl", + "PGcourse.pl", +); + +TEXT(beginproblem()); +Title("Matching Lists in $WW"); + +############################################## + +$a = random(-10,10,1); +$b = random(1,3,1); + +$ml = new_match_list(); +$ml->rf_print_q(~~&alt_print_q); +$ml->rf_print_a(~~&alt_print_a); +$ml->{separation} = 5; + +$ml->qa( + "\(x\) is less than \($a\)", "\(x $LT $a\)", + "\(x\) is any real number", "\(-\infty $LT x $LT \infty\)", + "\(x\) is greater than \($a\)", "\($a $LT x\)", + "\(x\) is less than or equal to \($a\)", "\(x $LE $a\)", + "\(x\) is greater than or equal to \($a\)", "\(x $GE $a\)", + "The distance from \(x\) to \($a\) is at most $b", + "\(|x - $a| $LE $b\)", + "The distance from \(x\) to \($a\) is more than $b", + "\(|x - $a| $GT $b\)" +); + +$ml->choose(5); + +################################################## + +BEGIN_TEXT + +Match the statements defined below with the letters labeling their +equivalent expressions. +$PAR + +\{ColumnMatchTable($ml,indent => 30)\} +$PAR + +END_TEXT + +ANS(str_cmp($ml->ra_correct_ans)); +install_problem_grader(~~&std_problem_grader); +$showPartialCorrectAnswers = 0; + +BEGIN_TEXT + +Usually with matching problems like this, +$WW only tells you whether ${BITALIC}all$EITALIC +your answers are correct or not. If they are not all +correct, $WW will not tell you which ones are right +and which are wrong. +$PAR + +END_TEXT + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob14/prob14-hint.html b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14-hint.html new file mode 100644 index 0000000000..9ad160d2da --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14-hint.html @@ -0,0 +1,35 @@ + +WeBWork Set 0 Problem 11 Hint + + + +
+ + +
+ +
+ +

Graphs in WeBWorK

+ +Often in WeBWorK, the graphs are displayed as small thumbnail images. +These can be difficult to read, so in these cases, WeBWorK provides +you with a link to a larger copy of the graph. You can click on the +small version of the image to get the larger one. For example, click +on the diagram below to enlarge it. It will be displayed in a +separate window; close that window when you are done looking at the +larger graph. +

+ +

+ +
+

+ +After you are done, press the "Back" button to go back to the problem page. + +

+
+ + + diff --git a/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.gif b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.gif new file mode 100644 index 0000000000..16461e6334 Binary files /dev/null and b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.gif differ diff --git a/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.pg b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.pg new file mode 100644 index 0000000000..0e4402efe7 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob14/prob14.pg @@ -0,0 +1,116 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGchoicemacros.pl", + "PGgraphmacros.pl", + "PGunion.pl", + "imageChoice.pl", + "../parserOrientation.pl", + "PGcourse.pl" +); + +# +# You need to change this to point to where you have stored the hint +# and graphic files. +# +$htmlWebworkURL = "http://omega.math.union.edu/webwork2_files/local"; +$hintURL = "${htmlWebworkURL}/parserOrientation/prob14-hint.html"; + +TEXT(beginproblem); + +Title("Matching Graphs in $WW"); + +############################################## + +$ml = new_image_match_list(link => 0, border => 0); +$ml->{separation} = 3; + +@Goptions = (-6,-6,6,6, axes => [0,0], grid => [6,6], size => [150,150]); +$G1 = init_graph(@Goptions); +$G2 = init_graph(@Goptions); +$G3 = init_graph(@Goptions); +$G4 = init_graph(@Goptions); + +$a1 = random(-6,2,.1); $b1 = random($a1+1,6,.1); $m1 = ($b1-$a1)/12; +$a2 = random(-2,6,.1); $b2 = random($a2-1,-6,.1); $m2 = ($b2-$a2)/12; +$a3 = non_zero_random(.5,5,.1)*non_zero_random(-1,1,1); +$a4 = non_zero_random(.5,5,.1)*non_zero_random(-1,1,1); + +$plotoptions = "using color:red and weight=2"; +plot_functions($G1,"$m1(x+6)+$a1 for x in <-5.8,5.8> $plotoptions"); +plot_functions($G2,"$m2(x+6)+$a2 for x in <-5.8,5.8> $plotoptions"); +plot_functions($G3,"$a3 for x in <-5.8,5.8> $plotoptions"); +plot_functions($G4,"10000(x-$a4) for x in <-5.8,5.8> $plotoptions"); + +$ml->qa( + "The line is the graph of an increasing function", $G1, + "The line is the graph of a decreasing function", $G2, + "The line is the graph of a constant function", $G3, + "The line is not the graph of a function", $G4 +); + +$ml->choose(4); + +#BEGIN_TEXT +# +#The simplest functions are the ${BITALIC}linear$EITALIC ones --- +#the functions whose graphs are straight lines. They are important +#because many functions locally look like straight lines. (Looking +#like a line ${BITALIC}locally$EITALIC means that if we zoom in on the +#function and look at it at a very powerful magnification, it will look +#like a straight line.) +#$PAR + +BEGIN_TEXT + +Enter the letter of the graph that corresponds to each statement: +$PAR + +$BCENTER +$PAR +\{$ml->print_q\} +$PAR +$ECENTER + +\{$ml->print_a\} +$PAR + +END_TEXT + +ANS(str_cmp($ml->ra_correct_ans)); +install_problem_grader(~~&std_problem_grader); +$showPartialCorrectAnswers = 0; + +################################################## + +BEGIN_TEXT + +As with the previous matching problems, you will not be told which of +your answers are correct when you submit your answers to this problem. +$WW will only tell you if ${BITALIC}all${EITALIC} your answers are +correct or not. +$PAR + +Some $WW problems display a link to additional information or a +\{htmlLink($hintURL,"hint")\}. Follow this link for a hint about +graphs in $WW. +$PAR + +END_TEXT + +#Occasionally, a problem includes a hint that will not be available +#immediately. Once you have submitted incorrect answers a certain +#number of times (determined by the problem), you will see a ${LQ}Show +#Hint$RQ button above the submit buttons at the bottom of the screen. +#Check the box and press ${LQ}Submit$RQ in order to get the hint. For +#this problem, the hint will be available after one wrong answer. +# +#END_TEXT +# +#$showHint = 1; +#HINT("$HINT Usually the hints are more helpful than this."); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/prob15.pg b/courses.dist/modelCourse/templates/setOrientation/prob15.pg new file mode 100644 index 0000000000..fe1f2268b7 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/prob15.pg @@ -0,0 +1,90 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGunion.pl", + "parserOrientation.pl", + "PGcourse.pl", +); + + +TEXT(beginproblem()); + +Title("When You're Stuck..."); + +############################################## + +BEGIN_TEXT +The goal of the $WW software is to help you learn mathematics by giving +you immediate feedback on the correctness of your answer to a problem. +It is not designed to be a tutorial or to replace humans in +explaining the material to you. As with any learning tool, it is +up to you to make efficient and effective use of the software. + +Here are some things you can try when you are stuck on a problem. +$PAR + +\{BeginParList("UL")\} +$ITEM +Reread the problem carefully to see if there are any instructions +that you did not notice. +$ITEMSEP + +$ITEM +Check carefully for directions on the Problem List page. +You can get to this page by pressing the ${LQ}Prob. List$RQ button at the +top of any problem page. +$ITEMSEP + +$ITEM +Look in the textbook for similar problems or relevant methods. +$ITEMSEP + +$ITEM +Talk to your instructor during office hours. +$ITEMSEP + +$ITEM +Ask a fellow student for help. +$ITEMSEP + +$ITEM +Use the Calculus Help Center. (Be sure to take a printout of +the problem with you. The tutors will need the ${BITALIC}exact$EITALIC +wording of the problem.) +$ITEMSEP + +$ITEM +Use the ${LQ}Email instructor$RQ button at the bottom of the problem page +to send e-mail to your instructor. Include in your message the details of +what you have tried so far. If you are having a software problem, +include details about the error messages you are getting. + +\{EndParList("UL")\} + +$PAR + +When you are truly stuck on a $WW problem, you should turn to other +sources (humans or books) for help, because it is not in your best +interest to guess repeatedly instead of thinking about what you might +be doing wrong. +$PAR + +To get credit for this problem, you must click the ${LQ}Submit +Answers$RQ button. Then you can use the ${LQ}Prob. List$RQ button at +the top of the page to return to the problem list page. You will see +that the problems you have done have been labeled as correct or +incorrect, and you can go back and do problems you skipped or couldn't +get right the first time. Once you have done a problem correctly, it +is ${BITALIC}always$EITALIC listed as correct even if you go back and +do it incorrectly later. This means you can use WeBWorK to review +course material without any danger of changing your grade. $PAR + +END_TEXT + +install_forgiving_grader(); +$showPartialCorrectAnswers = 1; + +############################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/courses.dist/modelCourse/templates/setOrientation/setHeader.pg b/courses.dist/modelCourse/templates/setOrientation/setHeader.pg new file mode 100644 index 0000000000..b9d3389043 --- /dev/null +++ b/courses.dist/modelCourse/templates/setOrientation/setHeader.pg @@ -0,0 +1,118 @@ +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGstandard.pl", + "PGunion.pl", + "PGcourse.pl", +); + +$WW = "WeBWorK"; + +if ($displayMode eq 'TeX') { + +TEXT($BEGIN_ONE_COLUMN, + '\noindent{\large\bf '.$studentName.'}\hfill{\large\bf '.$course.'}', + '\par\noindent', +" +This set of $WW problems is designed to orient you to the +$WW system and to help you learn how to communicate with the +software. You will be learning about how to understand what +you see on the screen and about how to enter your answers when you do +the problems. You will practice entering numerical and functional +expressions and look at ways to find and correct errors in your +entries. + +", + "WeBWorK assignment $setNumber is due on $formattedDueDate.", + $END_ONE_COLUMN +); + +} else { + +BEGIN_TEXT + +${BBOLD}Orientation to WeBWorK${EBOLD} +$PAR + +This set of $WW problems is designed to orient you to the $WW system +and to help you learn how to communicate with the software. You will +be learning about how to understand what you see on the screen and +about how to enter your answers when you do the problems. You will +practice entering numerical and functional expressions and look at +ways to find and correct errors in your entries. +$PAR + +Start by examining the features of this page. The panels at the +left and top help you to navigate to the different pages available in +WeBWorK. +$PAR +On the left, in the ${LQ}Main Menu${RQ} panel, you have already +seen the ${LQ}Homework Sets${RQ} page, +which lists all the homework assignments (with due dates, etc.) that have +been assigned to you. Links allow you to go to any assignment +you want to work on. +$PAR +The ${LQ}Password/Email${RQ} page lets you change your password or +email address. The ${LQ}Grades${RQ} page +shows your scores on the various assignments (but there is nothing +much to show at this point). +$PAR + +Links to your homework assignments are also listed in the ${LQ}Sets${RQ} +panel so you can quickly switch to another assignment. +$PAR + +The ${LQ}Display Options${RQ} panel gives you control over how you want +math equations displayed. For example with ${LQ}MathJax${RQ} you can +control the size of equations, have access to accessibility options, etc. +$PAR + +\{ +#The ${LQ}Report Bugs${RQ} button is for reporting bugs in the WeBWorK +#System itself to the developers WeBWorK. It is unlikely that +#you will need to use that yourself. If you are having difficulty with +#WeBWorK, you should contact your professor using the ${LQ}Email +#Instructor${RQ} button instead. This should be available on nearly +#every page, so you always have a quick way to reach your professor. +#$PAR +\} + +The data in the panel at the top of the page tells you where you +are in WeBWorK's hierarchy of pages, but you can ignore that for the +most part. There is an additional logout button at the far right, +however, that you will need to use when you are done using $WW (don't +press it yet). +$PAR + +\{ +#The yellow question mark icon is a ${LQ}Help${RQ} +#button for system documentation, but there is not much of that +#available at the moment. +#$PAR +\} + +The main information on this page is located in the large panel +to the left. It shows the status of the problems in this assignment, +and your score on the set so far. Since you haven't yet tried any of +the problems, your score is zero, but after you work on some of them, +the number of attempts and the score will reflect the work you have +done. +$PAR +Near the top is a link that allows you to download a pdf ${LQ}hardcopy${RQ} +version of your +whole assignment which you can print out. This allows you to work on +the assignment without having to be connected to $WW. +$PAR +Each problem number is a link to that problem in the homework set. +You can click on one to get to any specific problem. To begin the +orientation assignment, click on the link for Problem 1. When you are +done working, be sure to dismiss your connection to the server by +clicking on ${LQ}Log Out${RQ}, so that no one else can gain access to +your account. +$PAR + +END_TEXT + +} + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/Copying b/doc/Copying index 43cd72c3e0..d6400a2c8a 100644 --- a/doc/Copying +++ b/doc/Copying @@ -1,37 +1,41 @@ + GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 + Version 2, June 1991 - Copyright (C) 1989 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - For example, if you distribute copies of a such a program, whether + For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. +source code. And you must show them these terms so they know their +rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, @@ -44,120 +48,208 @@ want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software + interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes @@ -167,47 +259,48 @@ of promoting the sharing and reuse of software generally. NO WARRANTY - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it +possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. - Copyright (C) 19yy + Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -215,34 +308,37 @@ the exclusion of warranty; and each file should have at least the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: +necessary. Here is a sample; alter the names: - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice -That's all there is to it! +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/doc/devel/converted-cgs b/doc/devel/converted-cgs new file mode 100644 index 0000000000..89d98aebcf --- /dev/null +++ b/doc/devel/converted-cgs @@ -0,0 +1,81 @@ +4 => 3 + GENERATE PATHS WITH URLPath INSTEAD OF HARD CODING +(this has to be done before we can change the virtual heirarchy.) + lib/WeBWorK/ContentGenerator.pm + lib/WeBWorK/ContentGenerator/EquationDisplay.pm + lib/WeBWorK/ContentGenerator/Error.pm + lib/WeBWorK/ContentGenerator/Feedback.pm + lib/WeBWorK/ContentGenerator/Grades.pm + lib/WeBWorK/ContentGenerator/Home.pm + lib/WeBWorK/ContentGenerator/Instructor.pm + lib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm + lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm + lib/WeBWorK/ContentGenerator/Instructor/FileXfer.pm + lib/WeBWorK/ContentGenerator/Instructor/Index.pm + lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm + lib/WeBWorK/ContentGenerator/Login.pm + lib/WeBWorK/ContentGenerator/Logout.pm + lib/WeBWorK/ContentGenerator/Options.pm + lib/WeBWorK/ContentGenerator/Problem.pm + lib/WeBWorK/ContentGenerator/ProblemSet.pm + lib/WeBWorK/ContentGenerator/ProblemSets.pm + + lib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm + lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm + lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm + lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm + lib/WeBWorK/ContentGenerator/Instructor/ScoringDownload.pm + lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm + lib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm + lib/WeBWorK/ContentGenerator/Instructor/ShowAnswers.pm + lib/WeBWorK/ContentGenerator/Instructor/Stats.pm + lib/WeBWorK/ContentGenerator/Instructor/UserList.pm + +3 => 2 + GET PATH DATA FROM URLPath INSTEAD OF FROM $self->r +(this has to be done before we can take advantage of path/param munging.) + lib/WeBWorK/ContentGenerator/GatewayQuiz.pm + - delaying path generation changes until major cleanup + lib/WeBWorK/ContentGenerator/Hardcopy.pm + - delaying path generation changes until major cleanup + + + + + + + + + + + + lib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm + +2 => 1 + GET $ce, $db, $authz FROM $self->r INSTEAD OF FROM $self +(this has to be done before we can remove ce/db/authz from $self.) + + +1 => REMOVE DEPENDANCY ON DATA FROM @_ (get from URLPath instead) +(this has to be done before things will work.) + + + + + +0 => NONE OF THE ABOVE DONE + +----- Code that needs cleaning ----- + +- lots of code needs to be factored out of Problem and GatewayQuiz: + - problem logic (recording answers, checking permissions, etc.) + - display idioms (attemptResults, etc.) +- Hardcopy: + - move actual PDF generation into a Utils::* module + - clean up error handling +- code can be factored out of Grades, Stats, and SendMail + - a widget for displaying the "student progress" chart + - util code for doing mail merge from scoring files (whatever it does) +- Instructor needs work -- there's a lot of cut-n-paste going on +- factor info box formatting code out of Login, ProblemSets, ProblemSet + and into WeBWorK::HTML::InfoBox +- some modules should probably go under Utils: + - Compatability.pm + - Timing.pm diff --git a/doc/parser/README b/doc/parser/README new file mode 100644 index 0000000000..8c62267d3f --- /dev/null +++ b/doc/parser/README @@ -0,0 +1,145 @@ +OVERVIEW: + +This directory contains the documentation for a new +mathematical-expression parser written in perl. It was developed for +use with the WeBWorK on-line homework system, but it can be used in +any perl program. + +The goal was to process vector-valued expressions, but the parser was +designed to be extensible, so that you could add your own functions, +operators, and data types. It is still a work in progress, but should +provide a framework for building more sophisticated expression handling. + +Currenlty, the parser understands: + + - real and complex numbers, + - points, vectors, and matrices (with real or complex entries) + - arbitrary lists of elements + - intervals and unions of intervals + - predefined strings like 'infinity' + +Some other useful features are that you can write sin^2 x for (sin(x))^2 +and sin^-1 x for arcsin(x), and so on. + +Most of the documentation still needs to be written, but you can get some +ideas from the samples in the problems and extensions directories, and by +reading the files in the docs directory. + + +INSTALLATION: + +The parser should already be installed as part of the WeBWorK 2.1 +distribution, so you should not need to install it separately. If you +don't seem to have it installed, then it can be obtained from the +Union CVS repository at + + http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS + +The README file in that directory contains the installation instructions. + + +SAMPLE FILES: + +Sample problems are given in the problems and extensions directories. Move +these to the templates directory of a course where you want to test the +Parser, and move the contents of the macros directory to that course's +macros directory. + +Now try looking at these problems using the Library Browser. Edit the +source to see how they work, and to read the comments within the code +itself. + + +EXAMPLE FILES: + +The 'problems' directory contains several examples that show how to use +Parser within your problem files. + + sample01.pg + Uses the parser to make a string into a formula that you can + evaluate and print in TeX form + + sample02.pg + Shows how to create formulas using perl's usual mathematical + expressions rather than character strings. + + sample03.pg + Shows how to use the parser's differentiation abilities. + + sample04.pg and sample05.pg + Use the parser in conjunction with the graphics macros to generate + function graphs on the fly. These also show how to create a + perl function to evaluate an expression. + + sample06.pg + Shows some simple use of vectors in a problem. + + sample07.pg + Example if using the build-in Real object and its answer + checker + + sample08.pg + Uses complex numbers and the built-in checker + + sample09.pg and sample10.pg + Demonstrates points and vectors and their answer checkers + + sample11.pg and sample12.pg + Shows the answer checkers for intervals and unions. + + sample13.pg, sample14.pg, sample15.pg + Demonstrate various list checkers, including a check for the + word 'NONE', which is a predefined string. + + sample16.pg, sample17.pg, sample18.pg + These show the multi-variable function checker in use (for + functions of the form R->R, R^2->R and R->R^3). + + sample19.pg + Uses the function checker to implement a "constant" that can + be used in formulas. + + sample20.pg + Shows how to use the parser's substitution abilities. + + sample21.pg + Checks for a list of points. + + sample22.pg + Shows how to provide named constants that the student can + use in his answer. + +The 'examples' directory contains samples that show how to extend the +parser to include your own functions, operators, and so on. There are also +some samples of how to call the methods available for Formula objects +generated by the parser, and what some error messages look like. + + 1-function.pg + Adds a single-variable function to the parsers list of functions. + + 2-function.pg + Adds a two-variable function to the parser. + + 3-operator.pg + Adds a binary operator to the parser. (Unary operators are similar.) + + 4-list.pg + Adds a new "list type" object. In this case, it's really an + operation [n,r] that returns n choose r. + + 5-list.pg + Add a new "equality" operator that you can use to handle answers + like "x+y=0". + + 6-precedence.pg + Shows an experimental precedence setting that can be used to make + sin 2x return sin(2x) rather than (sin(2))x. + + 7-context.pg + Shows how to switch contexts (in this case, to complex and to vector + contexts), and how this affects the parsing. + + 8-answer.pg + Implements a simple vector-valued answer checker using the + parser's computation and comparison ability. + diff --git a/doc/parser/docs/ParserAnswerCheckers.pod b/doc/parser/docs/ParserAnswerCheckers.pod new file mode 100644 index 0000000000..10d3f7ad69 --- /dev/null +++ b/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 = 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(",,<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 S1 or 0 >>> + +show/don't show messages about student +answers not being of the right type. +(default: 1) + +=item S1 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 S1 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 S1 or 0 >>> + +Don't report type mismatches if the +student enters an infinity. +(default: 1) + +=back + +=head3 Flags for String()->cmp + +=over + +=item Svalue >>> + +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 S1 or 0 >>> + +show/don't show messages about the +wrong number of coordinates. +(default: 1) + +=item S1 or 0 >>> + +show/don't show message about +which coordinates are right. +(default: 1) + +=back + +=head3 Flags for Vector()->cmp + +=over + +=item S1 or 0 >>> + +show/don't show messages about the +wrong number of coordinates. +(default: 1) + +=item S1 or 0 >>> + +show/don't show message about +which coordinates are right. +(default: 1) + +=item S1 or 0 >>> + +do/don't allow the student to +enter a point rather than a vector. +(default: 1) + +=item S1 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 S1 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 S1 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 S1 or 0 >>> + +do/don't show messages about which +endpoints are correct. +(default: 1) + +=item S1 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 S1 or 0 >>> + +do/don't show messages about which +entries are incorrect. +(default: $showPartialCorrectAnswers) + +=item S1 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 S1 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 S1 or 0 >>> + +give credit only if the student answers +are in the same order as the +professor's answers. +(default: 0) + +=item S'a (name)' >>> + +The string to use in error messages +about type mismatches. +(default: dynamically determined from list) + +=item S'a (name)' >>> + +The string to use in error messages +about numbers of entries in the list. +(default: dynamically determined from list) + +=item Svalue >>> + +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 S1 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 S1 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.] + diff --git a/doc/parser/docs/UsingParser.pod b/doc/parser/docs/UsingParser.pod new file mode 100644 index 0000000000..4ece7c5b90 --- /dev/null +++ b/doc/parser/docs/UsingParser.pod @@ -0,0 +1,401 @@ +=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 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>, , 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 + + 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 = $context->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 + + $context->strings->add(TRUE=>{},FALSE=>{}); + +Strings are case-insensitive, unless you say otherwise. To mark a +string as being case-senstive, use + + $context->strings->add(TRUE => {caseSensitive=>1}); + +You may want to privide several forms for the same word; to do so, +make the additional words into aliases: + + $context->strings->add( + T => {alias=>'TRUE'}, + F => {alias=>'FALSE'}, + ); + +so that either "TRUE" or "T" will be interpreted as TRUE, and +similarly for "FALSE" and "F"; + +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. + diff --git a/doc/parser/extensions/1-function.pg b/doc/parser/extensions/1-function.pg new file mode 100644 index 0000000000..7e42c75e9f --- /dev/null +++ b/doc/parser/extensions/1-function.pg @@ -0,0 +1,83 @@ +########################################################## +# +# Example showing how to add a new single-variable function to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); + +############################################# +# +# Create a 'log2' function to the Parser for log base 2 +# + +package MyFunction1; +our @ISA = qw(Parser::Function::numeric); # this is what makes it R -> R + +sub log2 { + shift; my $x = shift; + return CORE::log($x)/CORE::log(2); +} + +package main; + +# +# Make it work on formulas as well as numbers +# +sub log2 {Parser::Function->call('log2',@_)} + +# +# Add the new functions into the Parser +# + +Context()->functions->add( + log2 => {class => 'MyFunction1', TeX => '\log_2'}, # fancier TeX output +); + +$x = Formula('x'); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new function to the Parser: ${BTT}log2(x)${ETT}. +(Edit the code to see how this is done.) +$PAR +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +\{ParserTable( + 'Formula("log2(x)")', + 'log2(8)', + 'log2($x+1)', + 'Formula("log2(x)")->eval(x=>16)', + '(log2($x))->eval(x=>16)', + 'Formula("log2()")', + 'Formula("log2(1,x)")', + 'log2()', + 'log2(1,3)', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/2-function.pg b/doc/parser/extensions/2-function.pg new file mode 100644 index 0000000000..bcde6c059c --- /dev/null +++ b/doc/parser/extensions/2-function.pg @@ -0,0 +1,83 @@ +########################################################## +# +# Example showing how to add a new two-variable function to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); + +############################################# +# +# Create a "Combinations" function +# + +package MyFunction2; +our @ISA = qw(Parser::Function::numeric2); # this is what makes it R^2 -> R + +sub C { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +package main; + +# +# Make it work on formulas as well as numbers +# +sub C {Parser::Function->call('C',@_)} + +# +# Add the new functions into the Parser +# + +Context()->functions->add(C => {class => 'MyFunction2'}); + +$x = Formula('x'); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new function to the Parser: ${BTT}C(n,r)${ETT}. +(Edit the code to see how this is done). +$PAR +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +\{ParserTable( + 'Formula("C(x,3)")', + 'C(6,2)', + 'C($x,3)', + 'Formula("C(x,3)")->eval(x=>6)', + '(C($x,2))->eval(x=>6)', + 'Formula("C(x)")', + 'Formula("C(1,2,3)")', + 'C(1)', + 'C(1,2,3)', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/3-operator.pg b/doc/parser/extensions/3-operator.pg new file mode 100644 index 0000000000..7cbdfa50c6 --- /dev/null +++ b/doc/parser/extensions/3-operator.pg @@ -0,0 +1,113 @@ +########################################################## +# +# Example showing how to add new operators to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own binary operator +# + +package MyOperator; +our @ISA = qw(Parser::BOP); # subclass of Binary OPerator + +# +# Check that the operand types are numbers. +# +sub _check { + my $self = shift; my $name = $self->{bop}; + return if $self->checkNumbers(); + $self->Error("Operands of '$name' must be Numbers"); +} + +# +# Compute the value of n choose r. +# +sub _eval { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +# +# Non-standard TeX output +# +sub TeX { + my $self = shift; + return '{'.$self->{lop}->TeX.' \choose '.$self->{rop}->TeX.'}'; +} + +# +# Non-standard perl output +# +sub perl { + my $self = shift; + return '(MyOperator->_eval('.$self->{lop}->perl.','.$self->{rop}->perl.'))'; +} + +package main; + +########################################################## +# +# Add the operator into the current context +# + +$prec = Context()->operators->get('+')->{precedence} - .25; + +Context()->operators->add( + '#' => { + class => 'MyOperator', + precedence => $prec, # just below addition + associativity => 'left', # computed left to right + type => 'bin', # binary operator + string => ' # ', # output string for it + TeX => '\mathop{\#}', # TeX version (overridden above, but just an example) + } +); + + +$CHOOSE = MODES(TeX => '\#', HTML => '#'); + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new operator to the Parser: ${BTT}n $CHOOSE r${ETT}, +which returns \(n\choose r\). +$PAR + +\{ParserTable( + 'Formula("x # y")', + 'Formula("x+1 # 5")', + 'Formula("x # 5")->eval(x=>7)', + 'Formula("(x#5)+(x#4)")', + 'Formula("x#5+x#4")', + 'Formula("x # y")', + 'Formula("x # y")->substitute(x=>5)', + 'Formula("x # y")->eval(x=>5,y=>2)', + 'Formula("x # y")->perlFunction(~~'C~~'); C(5,2)', + 'Formula("1 # ")', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/4-list.pg b/doc/parser/extensions/4-list.pg new file mode 100644 index 0000000000..1056685ea7 --- /dev/null +++ b/doc/parser/extensions/4-list.pg @@ -0,0 +1,106 @@ +########################################################## +# +# Example showing how to add a new list-type object +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own [n,r] notation for n choose r +# + +package MyChoose; +our @ISA = qw(Parser::List); # subclass of List + +# +# Check that two numbers are given +# +sub _check { + my $self = shift; + $self->{type}{list} = 0; # our result is a single number, not really a list + $self->Error("You need two numbers within '[' and ']'") + if ($self->{type}{length} < 2); + $self->Error("Only two numbers can appear within '[' and ']'") + if ($self->{type}{length} > 2); + my ($n,$r) = @{$self->{coords}}; + $self->Error("The arguments for '[n,r]' must be numbers") + unless ($n->type eq 'Number' && $r->type eq 'Number'); + $self->{type} = $Value::Type{number}; +} + +# +# Compute n choose r +# +sub _eval { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +# +# Non-standard TeX output +# +sub TeX { + my $self = shift; + return '{'.$self->{coords}[0]->TeX.' \choose '.$self->{coords}[1]->TeX.'}'; +} + +# +# Non-standard perl output +# +sub perl { + my $self = shift; + return '(MyChoose->_eval('.$self->{coords}[0]->perl.','.$self->{coords}[1]->perl.'))'; +} + + +package main; + +########################################################## +# +# Add the new list to the context +# + +Context()->lists->add(Choose => {class => 'MyChoose'}); +Context()->parens->replace('[' => {close => ']', type => 'Choose'}); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new list to the Parser: ${BTT}[n,r]${ETT}, +which returns \(n\choose r\). +$PAR + +\{ParserTable( + 'Formula("[x,3]")', + 'Formula("[5,3]")', + 'Formula("[x,3]")->eval(x=>5)', + '$C = Formula("[x,y]"); $C->substitute(x=>5)', + 'Formula("[x,y]")->perlFunction("C"); C(5,3)', + 'Formula("[x,y,3]")', + 'Formula("[x]")', + 'Formula("[x,[y,2]]")', + 'Formula("[x,<1,2>]")', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/5-operator.pg b/doc/parser/extensions/5-operator.pg new file mode 100644 index 0000000000..0ccd57b9aa --- /dev/null +++ b/doc/parser/extensions/5-operator.pg @@ -0,0 +1,89 @@ +########################################################## +# +# Example of how to implement equalities in the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own operator for equality +# + +package Equality; +our @ISA = qw(Parser::BOP); # subclass of Binary OPerator + +# +# Check that the operand types are numbers. +# +sub _check { + my $self = shift; my $name = $self->{bop}; + $self->Error("Only one equality is allowed in an equation") + if ($self->{lop}->class eq 'Equality' || $self->{rop}->class eq 'Equality') ; + $self->Error("Operands of '$name' must be Numbers") unless $self->checkNumbers(); + $self->{type} = Value::Type('Equality',1); # Make it not a number, to get errors with other operations. +} + +# +# Determine if the two sides are equal +# +sub _eval {return ($_[1] == $_[2])? 1: 0} + +package main; + +# +# Add the operator into the current context +# + +$prec = Context()->operators->get(',')->{precedence} + .25; + +Context()->operators->add( + '=' => { + class => 'Equality', + precedence => $prec, # just above comma + associativity => 'left', # computed left to right + type => 'bin', # binary operator + string => '=', # output string for it + perl => '==', # perl string + } +); + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new operator to the Parser: ${BTT} a += b${ETT}, for equality. +$PAR + +\{ParserTable( + 'Formula("x + y = 0")', + 'Formula("x + y = 0")->{tree}->class', + 'Formula("x + y = 0")->{tree}{lop}', + 'Formula("x + y = 0")->{tree}{rop}', + 'Formula("x + y = 0")->eval(x=>2,y=>3)', + 'Formula("x + y = 0")->eval(x=>2,y=>-2)', + 'Formula("x + y = 0 = z")', + 'Formula("(x + y = 0) + 5")', + 'Formula("x + y = 0, 3x-y = 4")', # you CAN get a list of equalities + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/6-precedence.pg b/doc/parser/extensions/6-precedence.pg new file mode 100644 index 0000000000..a3e1f24155 --- /dev/null +++ b/doc/parser/extensions/6-precedence.pg @@ -0,0 +1,84 @@ +########################################################## +# +# Example of the non-standard precedences as a possible alternative +# that makes it possible to write "sin 2x" and get "sin(2x)" +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Use standard precedences for multiplication +# + +Context()->usePrecedence("Standard"); + +$standard = ParserTable( + 'Formula("sin 2xy/3")', + 'Formula("sin 2x y/3")', + 'Formula("sin 2x y / 3")', + 'Formula("sin 2x+5")', + 'Formula("sin x(x+1)")', + 'Formula("sin x (x+1)")', + 'Formula("1/2xy")', + 'Formula("1/2 xy")', + 'Formula("1/2x y")', + 'Formula("sin^2 x")', + 'Formula("sin^(-1) x")', + 'Formula("x^2x")', +); + +Context()->usePrecedence("Non-Standard"); + +$nonstandard = ParserTable( + 'Formula("sin 2xy/3")', + 'Formula("sin 2x y/3")', + 'Formula("sin 2x y / 3")', + 'Formula("sin 2x+5")', + 'Formula("sin x(x+1)")', + 'Formula("sin x (x+1)")', + 'Formula("1/2xy")', + 'Formula("1/2 xy")', + 'Formula("1/2x y")', + 'Formula("sin^2 x")', + 'Formula("sin^(-1) x")', + 'Formula("x^2x")', +); + + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we compare the standard and non-standard precedences for +multiplication. +$PAR + +\{Title("The Non-Standard precedences:")\} +$PAR +$nonstandard +$PAR$BR + +\{Title("The Standard precedences:")\} +$PAR +$standard + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/7-context.pg b/doc/parser/extensions/7-context.pg new file mode 100644 index 0000000000..a43ae859c1 --- /dev/null +++ b/doc/parser/extensions/7-context.pg @@ -0,0 +1,86 @@ +########################################################## +# +# Example showing how to switch different contexts +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we compare formulas in complex and vector contexts. +Note the difference between how ${BTT}i${ETT} is treated in the two +contexts. Note that 'Number' comprises both real and complex numbers. +$PAR + +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +END_TEXT + +$x = Formula('x'); + +########################################################## +# +# Use Complex context +# + +Context('Complex'); + +BEGIN_TEXT +\{Title("The Complex context:")\} +$PAR +\{ParserTable( + 'i', + 'Formula("1+3i")', + 'Formula("x+3i")', + '1 + 3*i', + '$x + 3*i', + '$z = tan(2*i)', + 'Formula("sinh(zi)")', + 'Formula("3i+4j-k")', + 'Formula("3i+4j-k")->eval', + '3*i + 4*j - k', +)\} +$PAR$BR +END_TEXT + + +########################################################## +# +# Use Vector context +# + +Context('Vector'); + +BEGIN_TEXT +\{Title("The Vector context:")\} +$PAR +\{ParserTable( + 'i', + 'Formula("1+3i")', + 'Formula("x+3i")', + '1 + 3*i', + '$x + 3*i', + '$z = tan(2*i)', + 'Formula("sinh(zi)")', + 'Formula("3i+4j-k")', + 'Formula("3i+4j-k")->eval', + '3*i + 4*j - k', +)\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/extensions/8-answer.pg b/doc/parser/extensions/8-answer.pg new file mode 100644 index 0000000000..6f76804e82 --- /dev/null +++ b/doc/parser/extensions/8-answer.pg @@ -0,0 +1,112 @@ +########################################################## +# +# Example showing an answer checker that uses the parser +# to evaluate the student (and professor's) answers. +# +# This is now obsolete, as the paser's ->cmp method +# can be used to produce an answer checker for any +# of the parser types. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Use Vector context +# + +Context('Vector'); + +########################################################## +# +# Make the answer checker +# +sub vector_cmp { + my $v = shift; + die "vector_cmp requires a vector argument" unless defined $v; + my $v = Vector($v); # covert to vector if it isn't already + my $ans = new AnswerEvaluator; + $ans->ans_hash(type => "vector",correct_ans => $v->string, vector=>$v); + $ans->install_evaluator(~~&vector_cmp_check); + return $ans; +} + +sub vector_cmp_check { + my $ans = shift; my $v = $ans->{vector}, + $ans->score(0); # assume failure + my $f = Parser::Formula($ans->{student_ans}); + my $V = Parser::Evaluate($f); + if (defined $V) { + $V = Formula($V) unless Value::isValue($V); # make sure we can call Value methods + $ans->{preview_latex_string} = $f->TeX; + $ans->{preview_text_string} = $f->string; + $ans->{student_ans} = $V->string; + if ($V->type eq 'Vector') { + $ans->score(1) if ($V == $v); # Let the overloaded == do the check + } else { + $ans->{ans_message} = $ans->{error_message} = + "Your answer doesn't seem to be a Vector (it looks like ".Value::showClass($V).")" + unless $inputs_ref->{previewAnswers}; + } + } else { + # + # Student answer evaluation failed. + # Report the error, with formatting, if possible. + # + my $context = Context(); + my $message = $context->{error}{message}; + if ($context->{error}{pos}) { + my $string = $context->{error}{string}; + my ($s,$e) = @{$context->{error}{pos}}; + $message =~ s/; see.*//; # remove the position from the message + $ans->{student_ans} = protectHTML(substr($string,0,$s)) . + '' . + protectHTML(substr($string,$s,$e-$s)) . + '' . + protectHTML(substr($string,$e)); + } + $ans->{ans_message} = $ans->{error_message} = $message; + } + return $ans; +} + +########################################################## +# +# The problem text +# + +$V = Vector(1,2,3); + +Context()->flags->set(ijk=>0); +Context()->constants->add(a=>1,b=>1,c=>1); + +$ABC = Formula(""); + +BEGIN_TEXT +Enter the vector \(\{$V->TeX\}\) in any way you like: \{ans_rule(20)\}. +$PAR +You can use either \(\{$ABC->TeX\}\) or \(\{$ABC->ijk\}\) notation,$BR +and can perform vector operations to produce your answer. +$PAR +${BBOLD}Note:${EBOLD} This problem is obsolete. +END_TEXT + +########################################################### +# +# The answer +# + +ANS(vector_cmp($V)); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/macros/Differentiation.pl b/doc/parser/macros/Differentiation.pl new file mode 100644 index 0000000000..b614caeeb9 --- /dev/null +++ b/doc/parser/macros/Differentiation.pl @@ -0,0 +1,20 @@ +# +# Example of how to add new functionality to the Parser. +# +# Here we load new methods for the Parser object classes. Note, however, +# that these are PERSISTANT when used with webwork2 (mod_perl), and so we +# need to take care not to load them more than once. We look for the +# variable $Parser::Differentiation::loaded, which is defined in the +# differentiation package, in order to tell. +# +# DifferentiationDefs.pl is really just a copy of the +# Parser::Differentiation.pm file, and you really could just preload the +# latter instead by uncommenting the 'use Parser::Differentiation' line at +# the bottom of Parser.pm. (This file is really just a sample). The way +# it's done here will load it the first time it gets used, then will keep +# it around, so not much overhead even this way. +# + +loadMacros("DifferentiationDefs.pl") unless $Parser::Differentiation::loaded; + +1; diff --git a/doc/parser/macros/DifferentiationDefs.pl b/doc/parser/macros/DifferentiationDefs.pl new file mode 100644 index 0000000000..32c37b91aa --- /dev/null +++ b/doc/parser/macros/DifferentiationDefs.pl @@ -0,0 +1,635 @@ +# +# Extend differentiation to multiple variables +# Check differentiation for complex functions +# Do derivatives for norm and unit. +# +# Make shortcuts for getting numbers 1, 2, and sqrt, etc. +# + +################################################## +# +# Differentiate the formula in terms of the given variable +# +sub Parser::D { + my $self = shift; my $x = shift; + if (!defined($x)) { + my @vars = keys(%{$self->{variables}}); + my $n = scalar(@vars); + if ($n == 0) { + return $self->new('0') if $self->{isNumber}; + $x = 'x'; + } else { + $self->Error("You must specify a variable to differentiate by") unless $n ==1; + $x = $vars[0]; + } + } else { + return $self->new('0') unless defined $self->{variables}{$x}; + } + return $self->new($self->{tree}->D($x)); +} + +sub Item::D { + my $self = shift; + my $type = ref($self); $type =~ s/.*:://; + $self->Error("Differentiation for '$type' is not implemented",$self->{ref}); +} + + +######################################################################### + +sub Parser::BOP::comma::D {Item::D(shift)} +sub Parser::BOP::union::D {Item::D(shift)} + +sub Parser::BOP::add::D { + my $self = shift; my $x = shift; + $self = Parser::BOP->new( + $self->{equation},$self->{bop}, + $self->{lop}->D($x),$self->{rop}->D($x) + ); + return $self->reduce; +} + + +sub Parser::BOP::subtract::D { + my $self = shift; my $x = shift; + $self = Parser::BOP->new( + $self->{equation},$self->{bop}, + $self->{lop}->D($x),$self->{rop}->D($x) + ); + return $self->reduce; +} + +sub Parser::BOP::multiply::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + $self = + Parser::BOP->new($equation,'+', + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->D($x),$self->{rop}->copy($equation)), + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->copy($equation),$self->{rop}->D($x)) + ); + return $self->reduce; +} + +sub Parser::BOP::divide::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + $self = + Parser::BOP->new($equation,$self->{bop}, + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'*', + $self->{lop}->D($x),$self->{rop}->copy($equation)), + Parser::BOP->new($equation,'*', + $self->{lop}->copy($equation),$self->{rop}->D($x)) + ), + Parser::BOP->new($equation,'^', + $self->{rop},Parser::Number->new($equation,2) + ) + ); + return $self->reduce; +} + +sub Parser::BOP::power::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + my $vars = $self->{rop}->getVariables; + if (defined($vars->{$x})) { + $vars = $self->{lop}->getVariables; + if (defined($vars->{$x})) { + $self = + Parser::Function->new($equation,'exp', + [Parser::BOP->new($equation,'*',$self->{rop}->copy($equation), + Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0))]); + return $self->D($x); + } + $self = Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0), + Parser::BOP->new($equation,'*', + $self->copy($equation),$self->{rop}->D($x)) + ); + } else { + $self = + Parser::BOP->new($equation,'*', + Parser::BOP->new($equation,'*', + $self->{rop}->copy($equation), + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->copy($equation), + Parser::BOP->new($equation,'-', + $self->{rop}->copy($equation), + Parser::Number->new($equation,1) + ) + ) + ), + $self->{lop}->D($x) + ); + } + return $self->reduce; +} + +sub Parser::BOP::cross::D {Item::D(shift)} +sub Parser::BOP::dot::D {Item::D(shift)} +sub Parser::BOP::underscore::D {Item::D(shift)} + +######################################################################### + +sub Parser::UOP::plus::D { + my $self = shift; my $x = shift; + return $self->{op}->D($x) +} + +sub Parser::UOP::minus::D { + my $self = shift; my $x = shift; + $self = Parser::UOP->new($self->{equation},'u-',$self->{op}->D($x)); + return $self->reduce; +} + +sub Parser::UOP::factorial::D {Item::D(shift)} + +######################################################################### + +sub Parser::Function::D { + my $self = shift; + $self->Error("Differentiation of '$self->{name}' not implemented",$self->{ref}); +} + +sub Parser::Function::D_chain { + my $self = shift; my $x = $self->{params}[0]; + my $name = "D_" . $self->{name}; + $self = Parser::BOP->new($self->{equation},'*',$self->$name($x->copy),$x->D(shift)); + return $self->reduce; +} + +############################# + +sub Parser::Function::trig::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::trig::D_sin { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'cos',[$x]); +} + +sub Parser::Function::trig::D_cos { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::Function->new($equation,'sin',[$x]) + ); +} + +sub Parser::Function::trig::D_tan { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'sec',[$x]), + Parser::Number->new($equation,2) + ); +} + +sub Parser::Function::trig::D_cot { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'csc',[$x]), + Parser::Number->new($equation,2) + ) + ); +} + +sub Parser::Function::trig::D_sec { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'sec',[$x]), + Parser::Function->new($equation,'tan',[$x]) + ); +} + +sub Parser::Function::trig::D_csc { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'csc',[$x]), + Parser::Function->new($equation,'cot',[$x]) + ) + ); +} + +sub Parser::Function::trig::D_asin { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x,Parser::Number->new($equation,2) + ) + )] + ) + ); +} + +sub Parser::Function::trig::D_acos { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x,Parser::Number->new($equation,2) + ) + )] + ) + ) + ); +} + +sub Parser::Function::trig::D_atan { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::trig::D_acot { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ) + ); +} + +sub Parser::Function::trig::D_asec { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ) + ); +} + +sub Parser::Function::trig::D_acsc { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ) + ) + ); +} + + +############################# + +sub Parser::Function::hyperbolic::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::hyperbolic::D_sinh { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'cosh',[$x]); +} + +sub Parser::Function::hyperbolic::D_cosh { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'sinh',[$x]); +} + +sub Parser::Function::hyperbolic::D_tanh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'sech',[$x]), + Parser::Number->new($equation,2) + ); +} + +sub Parser::Function::hyperbolic::D_coth { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'csch',[$x]), + Parser::Number->new($equation,2) + ) + ); +} + +sub Parser::Function::hyperbolic::D_sech { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'sech',[$x]), + Parser::Function->new($equation,'tanh',[$x]) + ) + ); +} + +sub Parser::Function::hyperbolic::D_csch { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'csch',[$x]), + Parser::Function->new($equation,'coth',[$x]) + ) + ); +} + +sub Parser::Function::hyperbolic::D_asinh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ); +} + +sub Parser::Function::hyperbolic::D_acosh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ); +} + +sub Parser::Function::hyperbolic::D_atanh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_acoth { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_asech { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + $x, + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_acsch { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ) + ) + ); +} + + +############################# + +sub Parser::Function::numeric::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::numeric::D_log { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return Parser::BOP->new($equation,'/',Parser::Number->new($equation,1),$x); +} + +sub Parser::Function::numeric::D_log10 { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Number->new($equation,CORE::log(10)), $x + ) + ); +} + +sub Parser::Function::numeric::D_exp { + my $self = shift; my $x = shift; + return $self->copy(); +} + +sub Parser::Function::numeric::D_sqrt { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Number->new($equation,2), + $self->copy + ) + ); +} + +sub Parser::Function::numeric::D_abs {Parser::Function::D(@_)} +sub Parser::Function::numeric::D_int {Parser::Function::D(@_)} +sub Parser::Function::numeric::D_sgn {Parser::Function::D(@_)} + +######################################################################### + +sub Parser::List::D { + my $self = shift; my $x = shift; + $self = $self->copy($self->{equation}); + foreach my $f (@{$self->{coords}}) {$f = $f->D($x)} + return $self->reduce; +} + + +sub Parser::List::Interval::D { + my $self = shift; + $self->Error("Can't differentiate intervals",$self->{ref}); +} + +sub Parser::List::AbsoluteValue::D { + my $self = shift; + $self->Error("Can't differentiate absolute values",$self->{ref}); +} + + +######################################################################### + +sub Parser::Number::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Complex::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Constant::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Value::D { + my $self = shift; my $x = shift; my $equation = $self->{equation}; + return Parser::Value->new($equation,$self->{value}->D($x,$equation)); +} + +sub Value::D { + my $self = shift; my $x = shift; my $equation = shift; + return 0 if $self->isComplex; + my @coords = @{$self->{data}}; + foreach my $n (@coords) + {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x,$equation)->data}} + return $self->new([@coords]); +} + +sub Value::List::D { + my $self = shift; my $x = shift; my $equation = shift; + my @coords = @{$self->{data}}; + foreach my $n (@coords) + {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x)}} + return $self->new([@coords]); +} + +sub Value::Interval::D { + shift; shift; my $self = shift; + $self->Error("Can't differentiate intervals",$self->{ref}); +} + +sub Value::Union::D { + shift; shift; my $self = shift; + $self->Error("Can't differentiate unions",$self->{ref}); +} + +######################################################################### + +sub Parser::Variable::D { + my $self = shift; my $x = shift; + my $d = ($self->{name} eq $x)? 1: 0; + return Parser::Number->new($self->{equation},$d); +} + +######################################################################### + +sub Parser::String::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +package Parser::Differentiation; +our $loaded = 1; + +######################################################################### + +1; diff --git a/doc/parser/macros/parserTables.pl b/doc/parser/macros/parserTables.pl new file mode 100644 index 0000000000..bd382392f6 --- /dev/null +++ b/doc/parser/macros/parserTables.pl @@ -0,0 +1,87 @@ +loadMacros("parserUtils.pl"); + +############################################# +# +# For Parser example tables: +# + +$BTT = MODES(TeX=>'{\tt ', Latex2HTML => $bHTML.''.$eHTML, HTML => ''); +$ETT = MODES(TeX=>'}', Latex2HTML => $bHTML.''.$eHTML, HTML => ''); + +$BC = MODES( + TeX=>'{\small\it ', + Latex2HTML => $bHTML.''.$eHTML, + HTML => '' +); +$EC = MODES( + TeX=>'}', + Latex2HTML => $bHTML.''.$eHTML, + HTML => '' +); + +$LT = MODES(TeX => "<", Latex2HTML => "<", HTML => '<'); +$GT = MODES(TeX => ">", Latex2HTML => ">", HTML => '>'); + +$TEX = MODES(TeX => '{\TeX}', HTML => 'TeX', HTML_dpng => '\(\bf\TeX\)'); + +@rowOptions = ( + indent => 0, + separation => 0, + align => 'LEFT" NOWRAP="1', # alignment hack to get NOWRAP +); + +sub ParserRow { + my $f = shift; my $t = ''; + Context()->clearError; + my ($s,$err) = PG_restricted_eval($f); + if (defined $s) { + my $ss = $s; + if (ref($s) && \&{$s->string}) { + $t = '\('.$s->TeX.'\)'; + $s = $s->string; + } elsif ($s !~ m/^[a-z]+$/i) { + $t = '\('.Formula($s)->TeX.'\)'; + $s = Formula($s)->string; + } + $s =~ s//$GT/g; + if (ref($ss) && \&{$ss->class}) { + if ($ss->class eq 'Formula') { + $s .= ' '.$BC.'(Formula returning '.$ss->showType.')'.$EC; + } else { + $s .= ' '.$BC.'('.$ss->class.' object)'.$EC; + } + } + } else { + $s = $BC. (Context()->{error}{message} || $err) . $EC; + $t = ''; + } + $f =~ s//$GT/g; + if ($displayMode eq 'TeX') { + $f =~ s/\^/\\char`\\^/g; $s =~ s/\^/\\char`\\^/g; + $f =~ s/#/\\#/g; $s =~ s/#/\\#/g; + } + my $row = Row([$BTT.$f.$ETT,$BTT.$s.$ETT,$t],@rowOptions); + $row =~ s/\$/\${DOLLAR}/g; + return $row; +} + +sub ParserTable { + my $table = + BeginTable(border=>1, padding=>20). + Row([$BBOLD."Perl Code".$EBOLD, + $BBOLD."Result".$EBOLD, + $BBOLD.$TEX.' version'.$EBOLD],@rowOptions); + foreach my $f (@_) {$table .= ParserRow($f)} + $table .= EndTable(); + return $table; +} + +sub Title { + my $title = shift; + + MODES( + TeX => "\\par\\centerline{\\bf $title}\\par\\nobreak\n", + Latex2HTML => $bHTML.'

'.$title.'

'.$eHTML, + HTML => '

'.$title.'

' + ); +} diff --git a/doc/parser/macros/parserUtils.pl b/doc/parser/macros/parserUtils.pl new file mode 100644 index 0000000000..4db7dd5c4d --- /dev/null +++ b/doc/parser/macros/parserUtils.pl @@ -0,0 +1,61 @@ +loadMacros( + "unionImage.pl", + "unionTables.pl", +); + +$bHTML = '\begin{rawhtml}'; +$eHTML = '\end{rawhtml}'; + +# HTML(htmlcode) +# HTML(htmlcode,texcode) +# +# Insert $html in HTML mode or \begin{rawhtml}$html\end{rawhtml} in +# Latex2HTML mode. In TeX mode, insert nothing for the first form, and +# $tex for the second form. +# +sub HTML { + my ($html,$tex) = @_; + return('') unless (defined($html) && $html ne ''); + $tex = '' unless (defined($tex)); + MODES(TeX => $tex, Latex2HTML => $bHTML.$html.$eHTML, HTML => $html); +} + +# +# Begin and end mode +# +$BTT = HTML('','\texttt{'); +$ETT = HTML('','}'); + +# +# Begin and end mode +# +$BSMALL = HTML('','{\small '); +$ESMALL = HTML('','}'); + +# +# Block quotes +# +$BBLOCKQUOTE = HTML('
','\hskip3em '); +$EBLOCKQUOTE = HTML('
'); + +# +# Smart-quotes in TeX mode, regular quotes in HTML mode +# +$LQ = MODES(TeX => '``', Latex2HTML => '"', HTML => '"'); +$RQ = MODES(TeX => "''", Latex2HTML => '"', HTML => '"'); + +# +# make sure all characters are displayed +# +sub protectHTML { + my $string = shift; + $string =~ s/&/\&/g; + $string =~ s//\>/g; + $string; +} + +sub _parserUtils_init {} + +1; + diff --git a/doc/parser/macros/unionImage.pl b/doc/parser/macros/unionImage.pl new file mode 100644 index 0000000000..31162b76a3 --- /dev/null +++ b/doc/parser/macros/unionImage.pl @@ -0,0 +1,75 @@ +###################################################################### +# +# A routine to make including images easier to control +# +# Usage: Image(name,options) +# +# where name is the name of an image file or a reference to a +# graphics object (or a reference to a pair of one of these), +# and options are taken from among the following: +# +# size => [w,h] the size of the image in the HTML page +# (default is [150,150]) +# +# tex_size => r the size to use in TeX mode (as a percentage +# of the line width times 10). E.g., 500 is +# half the width, etc. (default is 200.) +# +# link => 0 or 1 whether to include a link to the original +# image (default is 0, unless there are +# two images given) +# +# border => 0 or 1 size of image border in HTML mode +# (defaults to 2 or 1 depending on whether +# there is a link or not) +# +# align => placement vertical alignment for image in HTML mode +# (default is "BOTTOM") +# +# tex_center => 0 or 1 whether to center the image horizontally +# in TeX mode (default is 0) +# +# The image name can be one of a number of different things. It can be +# the name of an image file, or an alias to one produce by the alias() +# command. It can be a graphics object reference created by init_graph(). +# Or it can be a pair of these (in square brackets). The first is the +# image for the HTML file, and the second is the image that it will be +# linked to. +# +# Examples: Image("graph.gif", size => [200,200]); +# Image(["graph.gif","graph-large.gif"]); +# +# The alias() and insertGraph() functions will be called automatically +# when needed. +# +sub Image { + my $image = shift; my $ilink; + my %options = ( + size => [150,150], tex_size => 200, + link => 0, align => "BOTTOM", tex_center => 0, @_); + my ($w,$h) = @{$options{size}}; + my ($ratio,$link) = ($options{tex_size}*(.001),$options{link}); + my ($border,$align) = ($options{border},$options{align}); + my ($tcenter) = $options{tex_center}; + my $HTML; my $TeX; + ($image,$ilink) = @{$image} if (ref($image) eq "ARRAY"); + $image = alias(insertGraph($image)) if (ref($image) eq "WWPlot"); + $image = alias($image) unless ($image =~ m!^/!i); + if ($ilink) { + $ilink = alias(insertGraph($ilink)) if (ref($ilink) eq "WWPlot"); + $ilink = alias($ilink) unless ($ilink =~ m!^/!i); + } else {$ilink = $image} + $border = (($link || $ilink ne $image)? 2: 1) unless defined($border); + $HTML = ''; + $HTML = ''.$HTML.'' if $link or $ilink ne $image; + $TeX = '\includegraphics[width='.$ratio.'\linewidth]{'.$image.'}'; + $TeX = '\centerline{'.$TeX.'}' if $tcenter; + MODES( + TeX => $TeX."\n", + Latex2HTML => $bHTML.$HTML.$eHTML, + HTML => $HTML + ); +} + +1; diff --git a/doc/parser/macros/unionTables.pl b/doc/parser/macros/unionTables.pl new file mode 100644 index 0000000000..8db8c8f46e --- /dev/null +++ b/doc/parser/macros/unionTables.pl @@ -0,0 +1,253 @@ +###################################################################### +## +## Functions for creating tables of various kinds +## +## ColumnTable() Creates a two-column display in HTML, +## but only one column in TeX. +## +## ColumnMatchTable() Does a side-by-side match table +## +## BeginTable() Begin a borderless HTML table +## Row() Create a row in the table +## AlignedRow() Create a row with alignment in each column +## TableSpace() Insert extra vertical space in the table +## EndTable() End the table +## + +###################################################################### +# +# Make a two-column table in HTML and Latex2HTML modes +# +# Usage: ColumnTable(col1,col2,[options]) +# +# Options can be taken from: +# +# indent => n the width to indent the first column +# (default is 0) +# +# separation => n the width of the separating gutter +# (default is 50) +# +# valign => type set the vertical alignment +# (default is "MIDDLE") +# +sub ColumnTable { + my $col1 = shift; my $col2 = shift; + my %options = (indent => 0, separation => 50, valign => "MIDDLE", @_); + my ($ind,$sep) = ($options{"indent"},$options{"separation"}); + my $valign = $options{"valign"}; + + my ($bhtml,$ehtml) = ('\begin{rawhtml}','\end{rawhtml}'); + ($bhtml,$ehtml) = ('','') unless ($displayMode eq "Latex2HTML"); + + my $HTMLtable = qq { + $bhtml +
 $ehtml + $col1 + $bhtml $ehtml + $col2 + $bhtml
$ehtml + }; + + MODES( + TeX => '\par\medskip\hbox{\qquad\vtop{'. + '\advance\hsize by -3em '.$col1.'}}'. + '\medskip\hbox{\qquad\vtop{'. + '\advance\hsize by -3em '.$col2.'}}\medskip', + Latex2HTML => $HTMLtable, + HTML => $HTMLtable + ); +} + +# +# Use columns for a match-list output +# +# Usage: ColumnMatchTable($ml,options) +# +# where $ml is a math list reference and options are those +# allowed for ColumnTable above. +# +sub ColumnMatchTable { + my $ml = shift; + + ColumnTable($ml->print_q,$ml->print_a,@_); +} + + +# +# Command for tables with no borders. +# +# Usage: BeginTable(options); +# +# Options are taken from: +# +# border => n value for BORDER attribute (default 0) +# spacing => n value for CELLSPACING attribute (default 0) +# padding => n value for CELLPADDING attribute (default 0) +# tex_spacing => dimen value for spacing between columns in TeX +# (e.g, tex_spacing => 2em) (default 1em) +# tex_border => dimen value for left- and right border in TeX (0pt) +# center => 0 or 1 center table or not (default 1) +# +sub BeginTable { + my %options = (border => 0, padding => 0, spacing => 0, center => 1, + tex_spacing => "1em", tex_border => "0pt", @_); + my ($bd,$pd,$sp) = ($options{border},$options{padding},$options{spacing}); + my ($tsp,$tbd) = ($options{tex_spacing},$options{tex_border}); + my ($center,$tcenter) = (' ALIGN="CENTER"','\centerline'); + ($center,$tcenter) = ('','') if (!$options{center}); + my $table = + qq{}; + + MODES( + TeX => '\par\medskip'.$tcenter.'{\kern '.$tbd. + '\vbox{\halign{#\hfil&&\kern '.$tsp.' #\hfil', + Latex2HTML => $bHTML.$table.$eHTML."\n", + HTML => $table."\n" + ); +} + +# +# Usage: EndTable(options) +# +# where options are taken from: +# +# tex_border => dimen extra vertical space in TeX mode (default 0pt) +# +sub EndTable { + my %options = (tex_border => "0pt", @_); + my $tbd = $options{tex_border}; + MODES( + TeX => '\cr}}\kern '.$tbd.'}\medskip'."\n", + Latex2HTML => $bHTML.'
'.$eHTML."\n", + HTML => ''."\n" + ); +} + +# +# Creates a row in the table +# +# Usage: Row([item1,item2,...],options); +# +# Each item appears as a separate entry in the table. +# +# Options control how the row is displayed: +# +# indent => num Specifies size of blank column on the left +# (default: indent => 0) +# +# separation => num Specifies separation of columns +# (default: spearation => 30) +# +# align => "type" Specifies alignment of initial column +# (default: align => "LEFT") +# +# valign => "type" Specified vertical alignment of row +# (default: valign => "MIDDLE") +# +sub Row { + my $rowref = shift; my @row = @{$rowref}; + my %options = ( + indent => 0, separation => 30, + align => "LEFT", valign => "MIDDLE", + @_ + ); + my ($cind,$csep) = ($options{indent},$options{separation}); + my ($align,$valign) = ($options{align},$options{valign}); + my $sep = ' '; $sep = '' if ($csep < 1); + my $ind = ' '; $ind = '' if ($cind < 1); + my $fill = ''; + $fill = '\hfil' if (uc($align) eq "CENTER"); + $fill = '\hfill' if (uc($align) eq "RIGHT"); + + MODES( + TeX => "\\cr\n". $fill . join('& ',@row), + Latex2HTML => + $bHTML."$ind".$eHTML . + join($bHTML."$sep".$eHTML,@row) . + $bHTML.''.$eHTML."\n", + HTML => "$ind" . + join("$sep",@row) . ''."\n" + ); +} + +# +# AlignedRow([item1,item2,...],options); +# +# Options control how the row is displayed: +# +# indent => num Specifies size of blank column on the left +# (default: indent => 0) +# +# separation => num Specifies separation of columns +# (default: spearation => 30) +# +# align => "type" Specifies alignment of all columns +# (default: align => "CENTER") +# +# valign => "type" Specified vertical alignment of row +# (default: valign => "MIDDLE") +# +sub AlignedRow { + my $rowref = shift; my @row = @{$rowref}; + my %options = ( + indent => 0, separation => 30, + align => "CENTER", valign => "MIDDLE", + @_ + ); + my ($cind,$csep) = ($options{indent},$options{separation}); + my ($align,$valign) = ($options{align},$options{valign}); + my $sep = ' '; $sep = '' if ($csep < 1); + my $ind = ' '; $ind = '' if ($cind < 1); + my $fill = ''; + $fill = '\hfil ' if (uc($align) eq "CENTER"); + $fill = '\hfill ' if (uc($align) eq "RIGHT"); + + MODES( + TeX => "\\cr\n". $fill . join('&'.$fill,@row), + Latex2HTML => + $bHTML."$ind".$eHTML . + join($bHTML."$sep".$eHTML,@row) . + $bHTML.''.$eHTML."\n", + HTML => "$ind" . + join("$sep",@row) . ''."\n" + ); +} + +# +# Add extra space between rows of a table +# +# Usage: TableSpace(pixels,points) +# +# where pixels is the number of pixels of space in HTML mode and +# points is the number of points to use in TeX mode. +# +sub TableSpace { + my $rsep = shift; + my $tsep = shift; + $rsep = $tsep if (defined($tsep) && $main::displayMode eq "TeX"); + return "" if ($rsep < 1); + MODES( + TeX => '\vadjust{\kern '.$rsep.'pt}' . "\n", + Latex2HTML => + $bHTML.''.$eHTML."\n", + HTML => ''."\n", + ); +} + +# +# A horizontal rule within a table. (Could have been a variable, +# but all the other table commands are subroutines, so kept it +# one to be consistent.) +# +sub TableLine { + MODES( + TeX => '\vadjust{\kern2pt\hrule\kern2pt}', + Latex2HTML => $bHTML. + '
'. + $eHTML."\n", + HTML =>'
'."\n" + ); +} + +1; diff --git a/doc/parser/problems/sample01.pg b/doc/parser/problems/sample01.pg new file mode 100644 index 0000000000..492f8fdb67 --- /dev/null +++ b/doc/parser/problems/sample01.pg @@ -0,0 +1,66 @@ +########################################################### +# +# Example showing how to use the Parser to make +# a formula that you can evaluate and print in TeX form. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); + +# +# Define some functions +# +$a = non_zero_random(-8,8,1); +$b = random(1,8,1); + +@f = ( + "1 + $a*x + $b x^2", + "$a / (1 + $b x)", + "$a x^3 + $b", + "($a - x) / ($b + x^2)" +); + +# +# Pick one at random +# +$k = random(0,$#f,1); +$f = Formula($f[$k])->reduce; + +# +# Where to evaluate it +# +$x = random(-5,5,1); + +########################################################### +# +# The problem text +# +BEGIN_TEXT + +If \(\displaystyle f(x) = \{$f->TeX\}\) then \(f($x)=\) \{ans_rule(10)\}. + +END_TEXT + +########################################################### +# +# The answer +# +ANS(num_cmp($f->eval(x=>$x))); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file diff --git a/doc/parser/problems/sample02.pg b/doc/parser/problems/sample02.pg new file mode 100644 index 0000000000..c0cc938862 --- /dev/null +++ b/doc/parser/problems/sample02.pg @@ -0,0 +1,59 @@ +########################################################### +# +# Example showing how you can use perl expressions (not +# just character strings) to generate formulas. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); +$x = Formula('x'); # used to construct formulas below. + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = ($a*$x**2 + $b*$x + $c) -> reduce; +$df = (2*$a*$x + $b) -> reduce; + +$x = random(-8,8,1); + +########################################################### +# +# The problem text +# +BEGIN_TEXT + +Suppose \(f(x) = \{$f->TeX\}\). +$PAR +Then \(f'(x)=\) \{ans_rule(20)\},$BR +and \(f'($x)=\) \{ans_rule(20)\}. + +END_TEXT + +########################################################### +# +# The answers +# +ANS(fun_cmp($df->string)); +ANS(num_cmp($df->eval(x=>$x))); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample03.pg b/doc/parser/problems/sample03.pg new file mode 100644 index 0000000000..15fe482fe8 --- /dev/null +++ b/doc/parser/problems/sample03.pg @@ -0,0 +1,63 @@ +########################################################### +# +# Example showing how to use the Parser's differentiation +# capabilities. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "Differentiation.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); +$x = Formula('x'); # used to construct formulas below. + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = ($a*$x**2 + $b*$x + $c) -> reduce; +$df = $f->D('x'); + +$x = random(-8,8,1); + +########################################################### +# +# The problem text +# +BEGIN_TEXT + +Suppose \(f(x) = \{$f->TeX\}\). +$PAR +Then \(f'(x)=\) \{ans_rule(20)\},$BR +and \(f'($x)=\) \{ans_rule(20)\}. +$PAR +(Same as previous problem, but using the formal differentiation package. +Note that automatic differentiation does not always produce the simples form.) + +END_TEXT + +########################################################### +# +# The answers +# +ANS(fun_cmp($df->string)); +ANS(num_cmp($df->eval(x=>$x))); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample04.pg b/doc/parser/problems/sample04.pg new file mode 100644 index 0000000000..e325f1df9c --- /dev/null +++ b/doc/parser/problems/sample04.pg @@ -0,0 +1,115 @@ +################################################################ +# +# Example showing how to use the Parser to create functions you +# can call from perl, to substitute values into a formula, and to +# convert a formula to a form that can be used in graphics generated +# on the fly. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PG.pl", + "PGbasicmacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(&beginproblem); + +############################################## +# +# The setup +# + +Context('Vector'); +Context()->variables->add(a => 'Real', b => 'Real'); +$a = non_zero_random(-4,-1,1); +$b = non_zero_random(-3,3,1); + +# +# The function to plot +# +$f = Formula("ax^2 + by"); # the function to display + +# +# Traces to show +# +$x = non_zero_random(-1,1,1); +$y = non_zero_random(-1,1,1); + +# +# Graph domain and size +# +($xm,$xM) = (-2,2); +($ym,$yM) = (-2,2); +($zm,$zM) = (-5,5); +$size = [200,300]; +$tex_size = 350; + +############################################## + +# +# The plot defaults +# +@Goptions = ( + $ym,$zm,$yM,$zM, # dimensions of graph + axes => [0,0], grid => [$yM-$ym,$zM-$zm], # number of grid lines + size => $size # pixel dimension +); +@imageoptions = (size=>$size, tex_size=>$tex_size); + +$xdomain = "x in <$xm,$xM>"; +#$ydomain = "y in <$ym,$yM>"; # plot_functions only handles variable x +$ydomain = "x in <$ym,$yM>"; +$plotoptions = "using color:red and weight:2"; + +# +# Make the traces +# +$fx = $f->substitute(x=>$x, a=>$a, b=>$b, y=>'x')->reduce; # must have variable x +$Gx = init_graph(@Goptions); +plot_functions($Gx,"$fx for $ydomain $plotoptions"); +$Xtrace = Image($Gx,@imageoptions); + +$fy = $f->substitute(y=>$y, a=>$a, b=>$b)->reduce; +$Gy = init_graph(@Goptions); +plot_functions($Gy,"$fy for $xdomain $plotoptions"); +$Ytrace = Image($Gy,@imageoptions); + +# +# Make the table of images +# +@rowopts = (indent=>0, separation=>30); +$Images = + BeginTable(). + AlignedRow([$Xtrace,$Ytrace], @rowopts). + AlignedRow(["Trace for \(x=$x\)","Trace for \(y=$y\)"], @rowopts). + EndTable(); + +############################################## + +BEGIN_TEXT + +The graphs below are traces for a function \(f(x,y)\) at \(x=$x\) and +\(y=$y\). +$PAR + +$Images +$PAR + +If \(f(x,y) = \{$f->TeX\}\) then +\(a\) = \{ans_rule(6)\} and \(b\) = \{ans_rule(6)\}. + +END_TEXT + +################################################## + +ANS(std_num_cmp($a)); +ANS(std_num_cmp($b)); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample05.pg b/doc/parser/problems/sample05.pg new file mode 100644 index 0000000000..ba69a452c4 --- /dev/null +++ b/doc/parser/problems/sample05.pg @@ -0,0 +1,139 @@ +################################################################ +# +# A more complex example showing how to use the Parser to create +# functions you can call from perl, to substitute values into a +# formula, and to convert a formula to a form that can be used in +# graphics generated on the fly. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "PGgraphmacros.pl", + "PGauxiliaryFunctions.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(&beginproblem); + +############################################## +# +# The setup +# + +Context('Vector'); +Context()->variables->add(a => 'Real', b => 'Real'); + +$c = non_zero_random(-1,1,1); +$a = $c*random(2,5,1)/2; +$b = -$c*random(2,5,1)/2; + +# +# The function to plot +# +$f = Formula("a x^2 y + b x y^2"); +$f->substitute(a=>$a,b=>$b)->perlFunction('f'); + +# +# Traces to show +# +$x1 = non_zero_random(-2,2,1); $x1 /= 2 if (abs($b) >= 2 && abs($x1) == 2); +$x2 = non_zero_random(-2,2,1); $x2 /= 2 if (abs($a) >= 2 && abs($x2) == 2); + +$x = max(.5,min(3,round(-2*$b*$x2/$a)/2)); +$y = max(.5,min(3,round(-2*$a*$x1/$b)/2)); + +# +# Points to show +# +$xv = round(-$b*$y/$a/2); $xv = 1 if ($xv == 0); +$fxv = f($xv,$y); if (abs($fxv) < .75) {$xv = -$xv; $fxv = f($xv,$y)} + +$yv = round(-$a*$x/$b/2); $yv = -1 if ($yv == 0); +$fyv = f($x,$yv); if (abs($fyv) < .75) {$yv = -$yv; $fyv = f($x,$yv)} + +$M = int(max(abs($fxv),abs($fyv),4))+1; +# +# Graph size +# +($xm,$xM) = (-3,3); +($ym,$yM) = (-3,3); +($zm,$zM) = (-$M,$M); +$size = [200,250]; +$tex_size = 350; + +############################################## + +# +# The plot defaults +# +@Goptions = ( + $ym,$zm,$yM,$zM, # dimensions of graph + axes => [0,0], grid => [$yM-$ym,$zM-$zm], # number of grid lines + size => $size # pixel dimension +); +@imageoptions = (size=>$size, tex_size=>$tex_size); + +$plotoptions = "using color:red and weight:2"; + +# +# Make the traces +# +$fx = $f->substitute(x => x, a => $a, b => $b, y => 'x')->reduce; +$Gx = init_graph(@Goptions); +plot_functions($Gx, + "$fx for x in <$ym,$yv] $plotoptions", + "$fx for x in <$yv,$yM> $plotoptions", +); +$Xtrace = Image($Gx,@imageoptions); + +$fy = $f->substitute(y => $y, a => $a, b => $b)->reduce; +$Gy = init_graph(@Goptions); +plot_functions($Gy, + "$fy for x in <$xm,$xv] $plotoptions", + "$fy for x in <$xv,$xM> $plotoptions", +); +$Ytrace = Image($Gy,@imageoptions); + +Context()->texStrings; + +# +# Make the table of images +# +@rowopts = (indent=>0, separation=>30); +$Images = + BeginTable(). + AlignedRow([$Xtrace,$Ytrace], @rowopts). + AlignedRow(["Trace for \(x=$x\) has","Trace for \(y=$y\) has"], @rowopts). + AlignedRow(["a point at \(($yv,$fyv)\).","a point at \(($xv,$fxv)\)."], @rowopts). + EndTable(); + +############################################## + +BEGIN_TEXT + +The graphs below are traces for a function \(f(x,y)\) at \(x=$x\) and +\(y=$y\). +$PAR + +$Images +$PAR + +If \(f(x,y) = \{$f->TeX\}\) then +\(a\) = \{ans_rule(6)\} and \(b\) = \{ans_rule(6)\}. + +END_TEXT + +Context()->normalStrings; + +################################################## + +ANS(std_num_cmp($a)); +ANS(std_num_cmp($b)); + +################################################## + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample06.pg b/doc/parser/problems/sample06.pg new file mode 100644 index 0000000000..a15ea0b2c1 --- /dev/null +++ b/doc/parser/problems/sample06.pg @@ -0,0 +1,55 @@ +########################################################### +# +# Example showing how to use the Parser to make +# a formula that you can evaluate and print in TeX form. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Vector'); + +# +# Define a vector +# +$a = non_zero_random(-8,8,1); +$b = non_zero_random(-8,8,1); +$c = non_zero_random(-8,8,1); + +$V = $a*i + $b*j + $c*k; # equivalently: $V = Vector($a,$b,$c); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +The length of the vector \($V\) is \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS(num_cmp(norm($V)->value)); # easier: ANS($V->cmp) +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample07.pg b/doc/parser/problems/sample07.pg new file mode 100644 index 0000000000..cacfc7d25e --- /dev/null +++ b/doc/parser/problems/sample07.pg @@ -0,0 +1,51 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context('Numeric'); + +$a = Real(random(2,6,1)); +$b = Real(random($a+1,$a+8,1)); + +$c = sqrt($a**2 + $b**2); # still a Real object + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose the legs of a triangle are of length \($a\) and \($b\).$BR +Then the hypoteneuse is of length \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings(); + +########################################################### +# +# The answer +# + +ANS($c->cmp); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample08.pg b/doc/parser/problems/sample08.pg new file mode 100644 index 0000000000..42e7f332ec --- /dev/null +++ b/doc/parser/problems/sample08.pg @@ -0,0 +1,51 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context('Complex'); + +$z = random(-5,5,1) + non_zero_random(-5,5,1)*i; + +$f = Formula('z^2 + 2z - 1'); +$fz = $f->eval(z => $z); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(z) = $f\).$BR +Then \(f($z)\) = \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($fz->cmp); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample09.pg b/doc/parser/problems/sample09.pg new file mode 100644 index 0000000000..dbf228f5c2 --- /dev/null +++ b/doc/parser/problems/sample09.pg @@ -0,0 +1,51 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context('Vector'); + +$P1 = Point(-2,4,2); +$P2 = Point(2,-3,1); + +$M = ($P1+$P2)/2; + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +The midpoint of the line segment from \($P1\) to \($P2\) +is \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($M->cmp); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample10.pg b/doc/parser/problems/sample10.pg new file mode 100644 index 0000000000..b63e9e9d92 --- /dev/null +++ b/doc/parser/problems/sample10.pg @@ -0,0 +1,56 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context('Vector'); + +$P1 = Point(1,random(-3,3,1),random(-3,3,1)); +$P2 = Point(random(-3,3,1),4,random(-3,3,1)); + +$V = Vector($P2-$P1); + +Context()->flags->set(ijk=>0); +Context()->constants->add(a=>1,b=>1,c=>1); + +$ABC = Formula(""); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT +The vector from \($P1\) to \($P2\) is \{ans_rule(20)\}. +$PAR +You can use either \($ABC\) or \(\{$ABC->ijk\}\) notation,$BR +and can perform vector operations to produce your answer. +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($V->cmp(promotePoints=>1)); # allow answers to be points or vectors + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample11.pg b/doc/parser/problems/sample11.pg new file mode 100644 index 0000000000..1e1c8514a0 --- /dev/null +++ b/doc/parser/problems/sample11.pg @@ -0,0 +1,50 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context('Interval'); + +$p1 = random(-5,2,1); +$p2 = random($p1+1,$p1+7,1); + +$f = Formula("x^2 - ($p1+$p2) x + $p1*$p2")->reduce; +$I = Interval("($p1,$p2)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT +The function \(f(x) = $f\) is negative for values of \(x\) in the interval +\{ans_rule(20)\}. +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($I->cmp); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample12.pg b/doc/parser/problems/sample12.pg new file mode 100644 index 0000000000..db981a1a7d --- /dev/null +++ b/doc/parser/problems/sample12.pg @@ -0,0 +1,62 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Interval"); + +$a = non_zero_random(-5,5,1); +$f = Formula("(x^2+1)/(x-$a)")->reduce; +$R = Union("(-inf,$a) U ($a,inf)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined on the region \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +Several intervals can be combined using the +set union symbol, ${LQ}${BTT}U${ETT}${RQ}.$BR +Use ${LQ}${BTT}infinity${ETT}${RQ} for ${LQ}\(\infty\)${RQ} and +${LQ}${BTT}-infinity${ETT}${RQ} for ${LQ}\(-\infty\)${RQ}. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($R->cmp); +$showPartialCorrectAnswers=1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample13.pg b/doc/parser/problems/sample13.pg new file mode 100644 index 0000000000..53d7e09671 --- /dev/null +++ b/doc/parser/problems/sample13.pg @@ -0,0 +1,61 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Interval"); + +$a = non_zero_random(-5,5,1); +$f = Formula("(x^2+1)/(x-$a)")->reduce; +$R = Compute("(-inf,$a),($a,inf)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined on the intervals \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one interval, separate them by commas.$BR +Use ${LQ}${BTT}infinity${ETT}${RQ} for ${LQ}\(\infty\)${RQ} and +${LQ}${BTT}-infinity${ETT}${RQ} for ${LQ}\(-\infty\)${RQ}. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($R->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample14.pg b/doc/parser/problems/sample14.pg new file mode 100644 index 0000000000..fbe0f9dd42 --- /dev/null +++ b/doc/parser/problems/sample14.pg @@ -0,0 +1,59 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Numeric"); + +$a = random(1,5,1); +$f = Formula("(x^2+1)/(x^2-$a^2)")->reduce; + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined for all \(x\) except for \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one value, separate them by commas.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if there are no such values. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS(List($a,-$a)->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample15.pg b/doc/parser/problems/sample15.pg new file mode 100644 index 0000000000..2bb48e6140 --- /dev/null +++ b/doc/parser/problems/sample15.pg @@ -0,0 +1,59 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Numeric"); + +$a = random(1,5,1); +$f = Formula("(x^2-$a)/(x^2+$a)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined for all \(x\) except for \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one value, separate them by commas.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if there are no such values. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS(List("NONE")->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample16.pg b/doc/parser/problems/sample16.pg new file mode 100644 index 0000000000..f81d89d65e --- /dev/null +++ b/doc/parser/problems/sample16.pg @@ -0,0 +1,63 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "Differentiation.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Numeric'); +$x = Formula('x'); # used to construct formulas below. + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = ($a*$x**2 + $b*$x + $c) -> reduce; +$df = $f->D('x'); + +$x = random(-8,8,1); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x) = $f\). +$PAR +Then \(f'(x)=\) \{ans_rule(20)\},$BR +and \(f'($x)=\) \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($df->cmp); +ANS($df->eval(x=>$x)->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample17.pg b/doc/parser/problems/sample17.pg new file mode 100644 index 0000000000..710f801a6c --- /dev/null +++ b/doc/parser/problems/sample17.pg @@ -0,0 +1,64 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "Differentiation.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Numeric')->variables->add(y=>'Real'); +$x = Formula('x'); # used to construct formulas below. +$y = Formula('y'); + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = ($a*$x**2 + $b*$x*$y + $c*$y**2) -> reduce; +$fx = $f->D('x'); +$fy = $f->D('y'); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x,y) = $f\). +$PAR +Then \(f_x(x,y) =\) \{ans_rule(30)\},$BR +and \(f_y(x,y) =\) \{ans_rule(30)\}. + + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($fx->cmp); +ANS($fy->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample18.pg b/doc/parser/problems/sample18.pg new file mode 100644 index 0000000000..1321ea31f6 --- /dev/null +++ b/doc/parser/problems/sample18.pg @@ -0,0 +1,63 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "Differentiation.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Vector')->variables->are(t=>'Real'); + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = Formula("") -> reduce; +$df = $f->D('t'); + +$t = random(-5,5,1); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(t) = $f\). +$PAR +Then \(f'(t) =\) \{ans_rule(20)\},$BR +and \(f'($t) =\) \{ans_rule(20)\}. + + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($df->cmp); +ANS($df->eval(t=>$t)->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample19.pg b/doc/parser/problems/sample19.pg new file mode 100644 index 0000000000..3b29af6e59 --- /dev/null +++ b/doc/parser/problems/sample19.pg @@ -0,0 +1,67 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Interval')->variables->add(a=>'Real'); +$x = Formula('x'); $a = Formula('a'); + +$f = log($x-$a); +$I = Formula("(-infinity,a]"); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x) = $f\). +$PAR +Then \(f\) is undefined for \(x\) in the interval(s) +\{ans_rule(20)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one interval, separate them by commas.$BR +Use ${LQ}${BTT}infinity${ETT}${RQ} for ${LQ}\(\infty\)${RQ} and +${LQ}${BTT}-infinity${ETT}${RQ} for ${LQ}\(-\infty\)${RQ}.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if the function is always defined. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS(List($I)->cmp( + list_type => 'a list of intervals', # override these names to avoid + entry_type => "an interval", # 'formula returning ...' messages +)); +Context()->variables->remove('x'); # error if 'x' is used in answer + +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample20.pg b/doc/parser/problems/sample20.pg new file mode 100644 index 0000000000..bfe6b74f6b --- /dev/null +++ b/doc/parser/problems/sample20.pg @@ -0,0 +1,62 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Numeric')->variables->are( + x=>'Real',y=>'Real', + s=>'Real',t=>'Real' +); +$x = Formula('x'); $y = Formula('y'); + +$a = random(1,5,1); +$b = random(-5,5,1); +$c = random(-5,5,1); + +$f = ($a*$x**2 + $b*$x*$y + $c*$y**2) -> reduce; + +$x = random(-5,5,1); +$y = random(-5,5,1); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x) = $f\). +$PAR +Then \(f($x,$y)\) = \{ans_rule(20)\},$BR +and \(f(s+t,s-t)\) = \{ans_rule(30)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($f->eval(x=>$x,y=>$y)->cmp); +ANS($f->substitute(x=>'s+t',y=>'s-t')->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample21.pg b/doc/parser/problems/sample21.pg new file mode 100644 index 0000000000..0a7d71addf --- /dev/null +++ b/doc/parser/problems/sample21.pg @@ -0,0 +1,64 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Vector')->variables->are(x=>'Real',y=>'Real'); +$x = Formula('x'); $y = Formula('y'); + +$a = random(1,16,1); +$b = non_zero_random(-5,5,1); + +$f = ($x**2 + $a*$y**2 + $b*$x**2*$y) -> reduce; + +$x = sqrt(2*$a)/$b; $y = -1/$b; + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x,y) = $f\). +$PAR +Then \(f\) has critical points at the following +points: \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one point, separate them by commas.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if there are none. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS(List(Point(0,0),Point($x,$y),Point(-$x,$y))->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. diff --git a/doc/parser/problems/sample22.pg b/doc/parser/problems/sample22.pg new file mode 100644 index 0000000000..2074914724 --- /dev/null +++ b/doc/parser/problems/sample22.pg @@ -0,0 +1,60 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +$context = Context('Vector'); +$context->variables->are(t=>'Real'); +$context->constants->add( + p0 => Point(pi,sqrt(2),3/exp(1)), + v => Vector(exp(1),log(10),-(pi**2)), +); +$context->constants->set(v => {TeX => '\boldsymbol{v}'}); # make it print nicer + +$L = Formula("p0+tv"); +$v = Formula('v'); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(p_0\) is a point and \($v\) a vector in \(n\)-space. +$PAR +Then the vector-parametric form for the line through \(p_0\) in the +direction of \(v\) is$PAR +${BBLOCKQUOTE} +\(L(t)\) = \{ans_rule(30)\}. +${EBLOCKQUOTE} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($L->cmp); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. \ No newline at end of file diff --git a/doc/webwork2.0pr1-errata b/doc/webwork2.0pr1-errata deleted file mode 100644 index 1b7980141e..0000000000 --- a/doc/webwork2.0pr1-errata +++ /dev/null @@ -1,10 +0,0 @@ -The location of the documentation about what to add to your Apache config is -non-obvious. It is located in lib/Apache/WeBWorK.pm. - -The Apache config documentation is missing the line - use lib '/opt/webwork/pglib'; -which should immediately follow the line - use lib '/opt/webwork/lib'; - -Line 86 of the file lib/WeBWorK/ContentGenerator/ProblemSet.pm should read - my $effectiveUser = $cldb->getUser($r->param("effectiveUser")); diff --git a/htdocs/ASCIIMathML/ASCIIMathML.js b/htdocs/ASCIIMathML/ASCIIMathML.js new file mode 100644 index 0000000000..a35f58f92c --- /dev/null +++ b/htdocs/ASCIIMathML/ASCIIMathML.js @@ -0,0 +1,673 @@ +/* +ASCIIMathML.js +============== +This file contains JavaScript functions to convert ASCII math notation +to Presentation MathML. The conversion is done while the XHTML page +loads, and should work with Internet Explorer 6 + MathPlayer +(http://www.dessci.com/en/products/mathplayer/) and Mozilla/Netscape 7+. +This is a convenient and inexpensive solution for authoring MathML. + +Version 1.3 Apr 6 2004, (c) Peter Jipsen http://www.chapman.edu/~jipsen +Latest version at http://www.chapman.edu/~jipsen/mathml/ASCIIMathML.js +If you use it on a webpage, please send the URL to jipsen@chapman.edu + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +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 the GNU +General Public License (at http://www.gnu.org/copyleft/gpl.html) +for more details. +*/ + +mathcolor = "Red"; // change it to Black or any other preferred color +displaystyle = true; // puts limits above and below large operators +separatetokens = false;//if true, letter tokens must be separated by nonletters +doubleblankmathdelimiter = false; // if true, x+1 is equal to `x+1` + // for IE this works only in +if (document.getElementById==null) + alert("This webpage requires a recent browser such as\ +\nMozilla/Netscape 7+ or Internet Explorer 6+MathPlayer") + +isIE = document.createElementNS==null; + +// character lists for Mozilla/Netscape fonts +cal = [0xEF35,0x212C,0xEF36,0xEF37,0x2130,0x2131,0xEF38,0x210B,0x2110,0xEF39,0xEF3A,0x2112,0x2133,0xEF3B,0xEF3C,0xEF3D,0xEF3E,0x211B,0xEF3F,0xEF40,0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xEF46]; +frk = [0xEF5D,0xEF5E,0x212D,0xEF5F,0xEF60,0xEF61,0xEF62,0x210C,0x2111,0xEF63,0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0x211C,0xEF6B,0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0x2128]; +bbb = [0xEF8C,0xEF8D,0x2102,0xEF8E,0xEF8F,0xEF90,0xEF91,0x210D,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0x2115,0xEF97,0x2119,0x211A,0x211D,0xEF98,0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0x2124]; + +symbols = [ +//some greek symbols +{input:"alpha", tag:"mi", output:"\u03B1"}, +{input:"beta", tag:"mi", output:"\u03B2"}, +{input:"chi", tag:"mi", output:"\u03C7"}, +{input:"delta", tag:"mi", output:"\u03B4"}, +{input:"Delta", tag:"mo", output:"\u0394"}, +{input:"epsi", tag:"mi", output:"\u03B5", tex:"epsilon"}, +{input:"varepsilon", tag:"mi", output:"\u025B"}, +{input:"eta", tag:"mi", output:"\u03B7"}, +{input:"gamma", tag:"mi", output:"\u03B3"}, +{input:"Gamma", tag:"mo", output:"\u0393"}, +{input:"iota", tag:"mi", output:"\u03B9"}, +{input:"kappa", tag:"mi", output:"\u03BA"}, +{input:"lambda", tag:"mi", output:"\u03BB"}, +{input:"Lambda", tag:"mo", output:"\u039B"}, +{input:"mu", tag:"mi", output:"\u03BC"}, +{input:"nu", tag:"mi", output:"\u03BD"}, +{input:"omega", tag:"mi", output:"\u03C9"}, +{input:"Omega", tag:"mo", output:"\u03A9"}, +{input:"phi", tag:"mi", output:"\u03C6"}, +{input:"varphi", tag:"mi", output:"\u03D5"}, +{input:"Phi", tag:"mo", output:"\u03A6"}, +{input:"pi", tag:"mi", output:"\u03C0"}, +{input:"Pi", tag:"mo", output:"\u03A0"}, +{input:"psi", tag:"mi", output:"\u03C8"}, +{input:"rho", tag:"mi", output:"\u03C1"}, +{input:"sigma", tag:"mi", output:"\u03C3"}, +{input:"Sigma", tag:"mo", output:"\u03A3"}, +{input:"tau", tag:"mi", output:"\u03C4"}, +{input:"theta", tag:"mi", output:"\u03B8"}, +{input:"vartheta", tag:"mi", output:"\u03D1"}, +{input:"Theta", tag:"mo", output:"\u0398"}, +{input:"upsilon", tag:"mi", output:"\u03C5"}, +{input:"xi", tag:"mi", output:"\u03BE"}, +{input:"Xi", tag:"mo", output:"\u039E"}, +{input:"zeta", tag:"mi", output:"\u03B6"}, + +//binary operation symbols +{input:"*", tag:"mo", output:"\u22C5", tex:"cdot"}, +{input:"**", tag:"mo", output:"\u22C6", tex:"star"}, +{input:"//", tag:"mo", output:"/"}, +{input:"\\\\", tag:"mo", output:"\\", tex:"backslash"}, +{input:"xx", tag:"mo", output:"\u00D7", tex:"times"}, +{input:"-:", tag:"mo", output:"\u00F7", tex:"divide"}, +{input:"@", tag:"mo", output:"\u2218", tex:"circ"}, +{input:"o+", tag:"mo", output:"\u2295", tex:"oplus"}, +{input:"ox", tag:"mo", output:"\u2297", tex:"otimes"}, +{input:"o.", tag:"mo", output:"\u2299", tex:"odot"}, +{input:"sum", tag:"mo", output:"\u2211", underover:"true"}, +{input:"prod", tag:"mo", output:"\u220F", underover:"true"}, +{input:"^^", tag:"mo", output:"\u2227", tex:"wedge"}, +{input:"^^^", tag:"mo", output:"\u22C0", tex:"bigwedge", underover:"true"}, +{input:"vv", tag:"mo", output:"\u2228", tex:"vee"}, +{input:"vvv", tag:"mo", output:"\u22C1", tex:"bigvee", underover:"true"}, +{input:"nn", tag:"mo", output:"\u2229", tex:"cap"}, +{input:"nnn", tag:"mo", output:"\u22C2", tex:"bigcap", underover:"true"}, +{input:"uu", tag:"mo", output:"\u222A", tex:"cup"}, +{input:"uuu", tag:"mo", output:"\u22C3", tex:"bigcup", underover:"true"}, + +//binary relation symbols +{input:"!=", tag:"mo", output:"\u2260", tex:"ne"}, +{input:"lt", tag:"mo", output:"<"}, +{input:"<=", tag:"mo", output:"\u2264", tex:"le"}, +{input:"lt=", tag:"mo", output:"\u2264", tex:"leq"}, +{input:">=", tag:"mo", output:"\u2265", tex:"ge"}, +{input:"geq", tag:"mo", output:"\u2265"}, +{input:"-<", tag:"mo", output:"\u227A", tex:"prec"}, +{input:"-lt", tag:"mo", output:"\u227A"}, +{input:">-", tag:"mo", output:"\u227B", tex:"succ"}, +{input:"in", tag:"mo", output:"\u2208"}, +{input:"!in", tag:"mo", output:"\u2209", tex:"notin"}, +{input:"sub", tag:"mo", output:"\u2282", tex:"subset"}, +{input:"sup", tag:"mo", output:"\u2283", tex:"supset"}, +{input:"sube", tag:"mo", output:"\u2286", tex:"subseteq"}, +{input:"supe", tag:"mo", output:"\u2287", tex:"supseteq"}, +{input:"-=", tag:"mo", output:"\u2261", tex:"equiv"}, +{input:"~=", tag:"mo", output:"\u2245", tex:"cong"}, +{input:"~~", tag:"mo", output:"\u2248", tex:"approx"}, +{input:"prop", tag:"mo", output:"\u221D", tex:"propto"}, + +//logical symbols +{input:"and", tag:"mtext", output:"and", space:"1ex"}, +{input:"or", tag:"mtext", output:"or", space:"1ex"}, +{input:"not", tag:"mo", output:"\u00AC", tex:"neg"}, +{input:"=>", tag:"mo", output:"\u21D2", tex:"implies"}, +{input:"if", tag:"mo", output:"if", space:"1ex"}, +{input:"<=>", tag:"mo", output:"\u21D4", tex:"iff"}, +{input:"AA", tag:"mo", output:"\u2200", tex:"forall"}, +{input:"EE", tag:"mo", output:"\u2203", tex:"exists"}, +{input:"_|_", tag:"mo", output:"\u22A5", tex:"bot"}, +{input:"TT", tag:"mo", output:"\u22A4", tex:"top"}, +{input:"|-", tag:"mo", output:"\u22A2", tex:"vdash"}, +{input:"|=", tag:"mo", output:"\u22A8", tex:"models"}, + +//grouping brackets +{input:"(", tag:"mo", output:"(", leftBracket:true}, +{input:")", tag:"mo", output:")", rightBracket:true}, +{input:"[", tag:"mo", output:"[", leftBracket:true}, +{input:"]", tag:"mo", output:"]", rightBracket:true}, +{input:"{", tag:"mo", output:"{", leftBracket:true}, +{input:"}", tag:"mo", output:"}", rightBracket:true}, +{input:"(:", tag:"mo", output:"\u2329", leftBracket:true, tex:"langle"}, +{input:":)", tag:"mo", output:"\u232A", rightBracket:true, tex:"rangle"}, +{input:"{:", tag:"mo", output:"{:", leftBracket:true, invisible:true}, +{input:":}", tag:"mo", output:":}", rightBracket:true, invisible:true}, + +//miscellaneous symbols +{input:"int", tag:"mo", output:"\u222B"}, +{input:"oint", tag:"mo", output:"\u222E"}, +{input:"del", tag:"mo", output:"\u2202", tex:"partial"}, +{input:"grad", tag:"mo", output:"\u2207", tex:"nabla"}, +{input:"+-", tag:"mo", output:"\u00B1", tex:"pm"}, +{input:"O/", tag:"mo", output:"\u2205", tex:"emptyset"}, +{input:"oo", tag:"mo", output:"\u221E", tex:"infty"}, +{input:"aleph", tag:"mo", output:"\u2135"}, +{input:"...", tag:"mo", output:"...", tex:"ldots"}, +{input:"\\ ", tag:"mo", output:"\u00A0"}, +{input:"quad", tag:"mo", output:"\u00A0\u00A0"}, +{input:"qquad", tag:"mo", output:"\u00A0\u00A0\u00A0\u00A0"}, +{input:"cdots", tag:"mo", output:"\u22EF"}, +{input:"diamond", tag:"mo", output:"\u22C4"}, +{input:"square", tag:"mo", output:"\u25A1"}, +{input:"|_", tag:"mo", output:"\u230A", tex:"lfloor"}, +{input:"_|", tag:"mo", output:"\u230B", tex:"rfloor"}, +{input:"|~", tag:"mo", output:"\u2308", tex:"lceiling"}, +{input:"~|", tag:"mo", output:"\u2309", tex:"rceiling"}, +{input:"CC", tag:"mo", output:"\u2102"}, +{input:"NN", tag:"mo", output:"\u2115"}, +{input:"QQ", tag:"mo", output:"\u211A"}, +{input:"RR", tag:"mo", output:"\u211D"}, +{input:"ZZ", tag:"mo", output:"\u2124"}, + +//standard functions +{input:"lim", tag:"mo", output:"lim", underover:"true"}, +{input:"sin", tag:"mo", output:"sin"}, +{input:"cos", tag:"mo", output:"cos"}, +{input:"tan", tag:"mo", output:"tan"}, +{input:"sinh", tag:"mo", output:"sinh"}, +{input:"cosh", tag:"mo", output:"cosh"}, +{input:"tanh", tag:"mo", output:"tanh"}, +{input:"cot", tag:"mo", output:"cot"}, +{input:"sec", tag:"mo", output:"sec"}, +{input:"csc", tag:"mo", output:"csc"}, +{input:"log", tag:"mo", output:"log"}, +{input:"ln", tag:"mo", output:"ln"}, +{input:"det", tag:"mo", output:"det"}, +{input:"dim", tag:"mo", output:"dim"}, +{input:"mod", tag:"mo", output:"mod"}, +{input:"gcd", tag:"mo", output:"gcd"}, +{input:"lcm", tag:"mo", output:"lcm"}, +{input:"min", tag:"mo", output:"min", underover:"true"}, +{input:"max", tag:"mo", output:"max", underover:"true"}, + +//arrows +{input:"uarr", tag:"mo", output:"\u2191", tex:"uparrow"}, +{input:"darr", tag:"mo", output:"\u2193", tex:"downarrow"}, +{input:"rarr", tag:"mo", output:"\u2192", tex:"rightarrow"}, +{input:"->", tag:"mo", output:"\u2192", tex:"to"}, +{input:"larr", tag:"mo", output:"\u2190", tex:"leftarrow"}, +{input:"harr", tag:"mo", output:"\u2194", tex:"leftrightarrow"}, +{input:"rArr", tag:"mo", output:"\u21D2", tex:"Rightarrow"}, +{input:"lArr", tag:"mo", output:"\u21D0", tex:"Leftarrow"}, +{input:"hArr", tag:"mo", output:"\u21D4", tex:"Leftrightarrow"}, + +//commands with argument +sqrt = {input:"sqrt", tag:"msqrt", output:"sqrt", unary:true}, +root = {input:"root", tag:"mroot", output:"root", binary:true}, +frac = {input:"frac", tag:"mfrac", output:"/", binary:true}, +div = {input:"/", tag:"mfrac", output:"/", infix:true}, +sub = {input:"_", tag:"msub", output:"_", infix:true}, +sup = {input:"^", tag:"msup", output:"^", infix:true}, +hat = {input:"hat", tag:"mover", output:"\u005E", unary:true, acc:true}, +bar = {input:"bar", tag:"mover", output:"\u00AF", unary:true, acc:true}, +vec = {input:"vec", tag:"mover", output:"\u2192", unary:true, acc:true}, +dot = {input:"dot", tag:"mover", output:".", unary:true, acc:true}, +ddot = {input:"ddot", tag:"mover", output:"..", unary:true, acc:true}, +ul = {input:"ul", tag:"munder", output:"\u0332", unary:true, acc:true}, +text = {input:"text", tag:"mtext", output:"text", unary:true}, +mbox = {input:"mbox", tag:"mtext", output:"mbox", unary:true}, +quote = {input:"\"", tag:"mtext", output:"mbox", unary:true}, +{input:"bb", tag:"mstyle", atname:"fontweight", atval:"bold", output:"bb", unary:true}, +{input:"mathbf", tag:"mstyle", atname:"fontweight", atval:"bold", output:"mathbf", unary:true}, +{input:"sf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"sf", unary:true}, +{input:"mathsf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"mathsf", unary:true}, +{input:"bbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"bbb", unary:true, codes:bbb}, +{input:"mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"mathbb", unary:true, codes:bbb}, +{input:"cc", tag:"mstyle", atname:"mathvariant", atval:"script", output:"cc", unary:true, codes:cal}, +{input:"mathcal", tag:"mstyle", atname:"mathvariant", atval:"script", output:"mathcal", unary:true, codes:cal}, +{input:"tt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"tt", unary:true}, +{input:"mathtt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"mathtt", unary:true}, +{input:"fr", tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"fr", unary:true, codes:frk}, +{input:"mathfrak", tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"mathfrak", unary:true, codes:frk} +]; + +function compareNames(s1,s2) { + if (s1.input > s2.input) return 1 + else return -1; +} + +names = []; //list of input symbols + +function initSymbols() { + var texsymbols = []; + for (var i=0; i=n where str appears or would be inserted +// assumes arr is sorted + if (n==0) { + var h,m; + n = -1; + h = arr.length; + while (n+1> 1; + if (arr[m]=str +} + +var separated = true; + +function getSymbol(str) { +//return maximal initial substring of str that appears in names +//return null if there is none + var k = 0; //new pos + var j = 0; //old pos + var mk; //match pos + var st; + var tagst; + var match = ""; + var more = true; + for (var i=1; i<=str.length && more; i++) { + st = str.slice(0,i); //initial substring of length i + j = k; + k = position(names, st, j); + if (k=names[k]; + } + if (match!="") + if (separatetokens) { + i = match.length; + if ("a">str.charAt(0) || str.charAt(0)>"z" || + "a">str.charAt(i-1) || str.charAt(i-1)>"z") { + separated = true; + return symbols[mk]; + } + st = str.charAt(i); + separated = separated && ("a">st || st>"z"); + if (separated) return symbols[mk]; + } else return symbols[mk]; +// if str[0] is a digit or - return maxsubstring of digits.digits + k = 1; + st = str.slice(0,1); + var pos = true; + var integ = true; + if (st == "-") { + pos = false; + st = str.slice(k,k+1); + k++; + } + while ("0"<=st && st<="9" && k<=str.length) { + st = str.slice(k,k+1); + k++; + } + if (st == ".") { + integ = false; + st = str.slice(k,k+1); + k++; + while ("0"<=st && st<="9" && k<=str.length) { + st = str.slice(k,k+1); + k++; + } + } + if ((pos && integ && k>1) || ((pos || integ) && k>2) || k>3) { + st = str.slice(0,k-1); + tagst = "mn"; + separated = true; + } else { + k = 2; + st = str.slice(0,1); //take 1 character + separated = ("A">st || st>"Z") && ("a">st || st>"z"); + tagst = (separated?"mo":"mi"); + separated = separated || str.charAt(1)<"a" || str.charAt(1)>"z"; + } + return {input:str.slice(0,k-1), tag:tagst, output:st}; +} + +function removeBrackets(node) { + if (node.nodeName=="mrow") { + var st = node.firstChild.firstChild.nodeValue; + if (st=="(" || st=="[" || st=="{") node.removeChild(node.firstChild); + } + if (node.nodeName=="mrow") { + var st = node.lastChild.firstChild.nodeValue; + if (st==")" || st=="]" || st=="}") node.removeChild(node.lastChild); + } +} + +/*Parsing ASCII math expressions with the following grammar +c ::= [A-z] | numbers | greek letters | other constant symbols +u ::= "sqrt" | "text" | "bb" | other unary symbols for font commands +b ::= "frac" | "root" binary symbols +i ::= _ | ^ | / infix symbols: subscript, superscript, division +l ::= ( | [ | { | (: | {: left brackets +r ::= ) | ] | } | :) | :} right brackets +S ::= c | lEr | uS' | bS'S' simple expression: S' is S with brackets removed +E ::= SE | SiS' | S_S'^S' expression +Each terminal symbol is translated into a corresponding mathml node.*/ + +function parseSexpr(str) { //parses str and returns [node,tailstr] + var symbol; + var node; + var result; + str = removeCharsAndBlanks(str,0); + symbol = getSymbol(str); //either a token or a bracket or empty + if (symbol == null || symbol.rightBracket) + return [null,str]; + if (symbol.leftBracket) { //read (expr+) + str = removeCharsAndBlanks(str,symbol.input.length); + result = parseExpr(str); + if (symbol.invisible) node = createMmlNode("mrow",result[0]); + else { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + node = createMmlNode("mrow",node); + node.appendChild(result[0]); + } + return [node,result[1]]; + } + else if (symbol.unary) { + if (symbol == text || symbol == mbox || symbol == quote) { + if (symbol!=quote) str = removeCharsAndBlanks(str,symbol.input.length); + if (str.charAt(0)=="{") var i=str.indexOf("}"); + else if (str.charAt(0)=="(") var i=str.indexOf(")"); + else if (str.charAt(0)=="[") var i=str.indexOf("]"); + else if (symbol==quote) var i=str.slice(1).indexOf("\"")+1; + else var i = 0; + if (i==-1) i = str.length; + var st = str.slice(1,i); + var newFrag = document.createDocumentFragment(); + if (st.charAt(0) == " ") { + node = myCreateElementMathML("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + } + newFrag.appendChild( + createMmlNode(symbol.tag,document.createTextNode(st))); + if (st.charAt(st.length-1) == " ") { + node = myCreateElementMathML("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + } + str = removeCharsAndBlanks(str,i+1); + return [createMmlNode("mrow",newFrag),str]; + } else { + str = removeCharsAndBlanks(str,symbol.input.length); + result = parseSexpr(str); + if (result[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str]; + removeBrackets(result[0]); + if (symbol == sqrt) { // sqrt + return [createMmlNode(symbol.tag,result[0]),result[1]]; + } else if (symbol.acc) { // accent + node = createMmlNode(symbol.tag,result[0]); + node.appendChild(createMmlNode("mo",document.createTextNode(symbol.output))); + return [node,result[1]]; + } else { // font change command + if (!isIE && symbol.codes!=null) { + for (var i=0; i64 && st.charCodeAt(j)<91) newst = newst + + String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]); + else newst = newst + st.charAt(j); + if (result[0].nodeName=="mi") + result[0]=myCreateElementMathML("mo"). + appendChild(document.createTextNode(newst)); + else result[0].replaceChild(myCreateElementMathML("mo"). + appendChild(document.createTextNode(newst)),result[0].childNodes[i]); + } + } + node = createMmlNode(symbol.tag,result[0]); + node.setAttribute(symbol.atname,symbol.atval); + return [node,result[1]]; + } + } + } + else { + str = removeCharsAndBlanks(str,symbol.input.length); + if (symbol.binary) { + var newFrag = document.createDocumentFragment(); + result = parseSexpr(str); + if (result[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str]; + removeBrackets(result[0]); + var result2 = parseSexpr(result[1]); + if (result2[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str]; + removeBrackets(result2[0]); + if (symbol==root) newFrag.appendChild(result2[0]); + newFrag.appendChild(result[0]); + if (symbol==frac) newFrag.appendChild(result2[0]); + return [createMmlNode(symbol.tag,newFrag),result2[1]]; + } + else if (symbol.infix) + return [createMmlNode("mo",document.createTextNode(symbol.output)),str]; + else if (symbol.space!=undefined) { + var newFrag = document.createDocumentFragment(); + node = myCreateElementMathML("mspace"); + node.setAttribute("width",symbol.space); + newFrag.appendChild(node); + newFrag.appendChild( + createMmlNode(symbol.tag,document.createTextNode(symbol.output))); + node = myCreateElementMathML("mspace"); + node.setAttribute("width",symbol.space); + newFrag.appendChild(node); + return [createMmlNode("mrow",newFrag),str]; + }else return [createMmlNode(symbol.tag, //its a constant + document.createTextNode(symbol.output)),str]; + } +} + +function parseExpr(str) { + var symbol,sym1,sym2; + var node; + var result; + var newFrag = document.createDocumentFragment(); + var nodeList = []; + do { + str = removeCharsAndBlanks(str,0); + sym1 = getSymbol(str); + result = parseSexpr(str); + node = result[0]; + str = result[1]; + symbol = getSymbol(str); + if (symbol.infix) { + str = removeCharsAndBlanks(str,symbol.input.length); + result = parseSexpr(str); + removeBrackets(result[0]); + str = result[1]; + if (symbol == div) removeBrackets(node); + if (symbol == sub) { + sym2 = getSymbol(str); + if (sym2 == sup) { + str = removeCharsAndBlanks(str,sym2.input.length); + var res2 = parseSexpr(str); + removeBrackets(res2[0]); + str = res2[1]; + node = createMmlNode((sym1.underover?"munderover":"msubsup"),node); + node.appendChild(result[0]); + node.appendChild(res2[0]); + node = createMmlNode("mrow",node); + } else { + node = createMmlNode((sym1.underover?"munder":"msub"),node); + node.appendChild(result[0]); + } + } else { + node = createMmlNode(symbol.tag,node); + node.appendChild(result[0]); + } + newFrag.appendChild(node); + } + else if (node!=undefined) newFrag.appendChild(node); + } while (!symbol.rightBracket && !(symbol==null) && symbol.output!=""); + if (symbol.rightBracket) { + var len = newFrag.childNodes.length; + if (len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 && + newFrag.childNodes[len-2].nodeName == "mo" && + newFrag.childNodes[len-2].firstChild.nodeValue == ",") { //matrix + var right = newFrag.childNodes[len-1].lastChild.firstChild.nodeValue; + if (right==")" || right=="]") { + var left = newFrag.childNodes[len-1].firstChild.firstChild.nodeValue; + if (left=="(" && right==")" && symbol.output != "}" || + left=="[" && right=="]") { + var pos = []; // positions of commas + var matrix = true; + var m = newFrag.childNodes.length; + for (var i=0; matrix && i1) matrix = pos[i].length == pos[i-2].length; + } + if (matrix) { + var table = document.createDocumentFragment(); + for (var i=0; i(-,-,...,-,-) + var n = node.childNodes.length; + var k = 0; + node.removeChild(node.firstChild); //remove ( + for (var j=1; j2) { + newFrag.removeChild(newFrag.firstChild); //remove ) + newFrag.removeChild(newFrag.firstChild); //remove , + } + table.appendChild(createMmlNode("mtr",row)); + } + node = createMmlNode("mtable",table); + if (symbol.invisible) node.setAttribute("columnalign","left"); + newFrag.replaceChild(node,newFrag.firstChild); + } + }} + } + str = removeCharsAndBlanks(str,symbol.input.length); + if (!symbol.invisible) { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + newFrag.appendChild(node); + } + } + return [newFrag,str]; +} + +function parseMath(str) { + var node = myCreateElementMathML("mstyle"); + node.setAttribute("mathcolor",mathcolor); + if (displaystyle) node.setAttribute("displaystyle","true"); + node.appendChild(parseExpr(str)[0]); + return createMmlNode("math",node); +} + +function strarr2docFrag(arr, linebreaks) { + var newFrag=document.createDocumentFragment(); + var expr = false; + for (var i=0; i1 || mtch) + n.parentNode.replaceChild(strarr2docFrag(arr,n.nodeType==8),n); + } + } else if (n.nodeName!="math") for (var i=0; i +This is a convenient and inexpensive solution for authoring MathML. + +Version 1.4.7 Dec 15, 2005, (c) Peter Jipsen http://www.chapman.edu/~jipsen +Latest version at http://www.chapman.edu/~jipsen/mathml/ASCIIMathML.js +For changes see http://www.chapman.edu/~jipsen/mathml/asciimathchanges.txt +If you use it on a webpage, please send the URL to jipsen@chapman.edu + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +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 the GNU +General Public License (at http://www.gnu.org/copyleft/gpl.html) +for more details. + +LaTeXMathML.js (ctd) +============== + +The instructions for use are the same as for the original +ASCIIMathML.js, except that of course the line you add to your +file should be + +Or use absolute path names if the file is not in the same folder +as your (X)HTML page. +*/ + +var checkForMathML = true; // check if browser can display MathML +var notifyIfNoMathML = true; // display note if no MathML capability +var alertIfNoMathML = false; // show alert box if no MathML capability +// was "red": +var mathcolor = ""; // change it to "" (to inherit) or any other color +// was "serif": +var mathfontfamily = ""; // change to "" to inherit (works in IE) + // or another family (e.g. "arial") +var showasciiformulaonhover = true; // helps students learn ASCIIMath +/* +// Commented out by DRW -- not now used -- see DELIMITERS (twice) near the end +var displaystyle = false; // puts limits above and below large operators +var decimalsign = "."; // change to "," if you like, beware of `(1,2)`! +var AMdelimiter1 = "`", AMescape1 = "\\\\`"; // can use other characters +var AMdelimiter2 = "$", AMescape2 = "\\\\\\$", AMdelimiter2regexp = "\\$"; +var doubleblankmathdelimiter = false; // if true, x+1 is equal to `x+1` + // for IE this works only in +//var separatetokens;// has been removed (email me if this is a problem) +*/ + +if (!window.LaTeXMathML_loaded) { // DPVC +window.LaTeXMathML_loaded = 1; // DPVC + +var isIE = document.createElementNS==null; + +if (document.getElementById==null) + alert("This webpage requires a recent browser such as\ +\nMozilla/Netscape 7+ or Internet Explorer 6+MathPlayer") + +// all further global variables start with "AM" + +function AMcreateElementXHTML(t) { + if (isIE) return document.createElement(t); + else return document.createElementNS("http://www.w3.org/1999/xhtml",t); +} + +function AMnoMathMLNote() { + var nd = AMcreateElementXHTML("h3"); + nd.setAttribute("align","center") + nd.appendChild(AMcreateElementXHTML("p")); + nd.appendChild(document.createTextNode("To view the ")); + var an = AMcreateElementXHTML("a"); + an.appendChild(document.createTextNode("LaTeXMathML")); + an.setAttribute("href","http://www.maths.nott.ac.uk/personal/drw/lm.html"); + nd.appendChild(an); + nd.appendChild(document.createTextNode(" notation use Internet Explorer 6+")); + an = AMcreateElementXHTML("a"); + an.appendChild(document.createTextNode("MathPlayer")); + an.setAttribute("href","http://www.dessci.com/en/products/mathplayer/download.htm"); + nd.appendChild(an); + nd.appendChild(document.createTextNode(" or Netscape/Mozilla/Firefox")); + nd.appendChild(AMcreateElementXHTML("p")); + return nd; +} + +function AMisMathMLavailable() { + if (navigator.appName.slice(0,8)=="Netscape") + if (navigator.appVersion.slice(0,1)>="5") return null; + else return AMnoMathMLNote(); + else if (navigator.appName.slice(0,9)=="Microsoft") + try { + var ActiveX = new ActiveXObject("MathPlayer.Factory.1"); + return null; + } catch (e) { + return AMnoMathMLNote(); + } + else return AMnoMathMLNote(); +} + +// character lists for Mozilla/Netscape fonts +var AMcal = [0xEF35,0x212C,0xEF36,0xEF37,0x2130,0x2131,0xEF38,0x210B,0x2110,0xEF39,0xEF3A,0x2112,0x2133,0xEF3B,0xEF3C,0xEF3D,0xEF3E,0x211B,0xEF3F,0xEF40,0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xEF46]; +var AMfrk = [0xEF5D,0xEF5E,0x212D,0xEF5F,0xEF60,0xEF61,0xEF62,0x210C,0x2111,0xEF63,0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0x211C,0xEF6B,0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0x2128]; +var AMbbb = [0xEF8C,0xEF8D,0x2102,0xEF8E,0xEF8F,0xEF90,0xEF91,0x210D,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0x2115,0xEF97,0x2119,0x211A,0x211D,0xEF98,0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0x2124]; + +var CONST = 0, UNARY = 1, BINARY = 2, INFIX = 3, LEFTBRACKET = 4, + RIGHTBRACKET = 5, SPACE = 6, UNDEROVER = 7, DEFINITION = 8, + TEXT = 9, BIG = 10, LONG = 11, STRETCHY = 12, MATRIX = 13; // token types + +var AMsqrt = {input:"\\sqrt", tag:"msqrt", output:"sqrt", ttype:UNARY}, + AMroot = {input:"\\root", tag:"mroot", output:"root", ttype:BINARY}, + AMfrac = {input:"\\frac", tag:"mfrac", output:"/", ttype:BINARY}, + AMover = {input:"\\stackrel", tag:"mover", output:"stackrel", ttype:BINARY}, + AMatop = {input:"\\atop", tag:"mfrac", output:"", ttype:INFIX}, + AMchoose = {input:"\\choose", tag:"mfrac", output:"", ttype:INFIX}, + AMsub = {input:"_", tag:"msub", output:"_", ttype:INFIX}, + AMsup = {input:"^", tag:"msup", output:"^", ttype:INFIX}, + AMtext = {input:"\\mathrm", tag:"mtext", output:"text", ttype:TEXT}, + AMmbox = {input:"\\mbox", tag:"mtext", output:"mbox", ttype:TEXT}; + +// Commented out by DRW to prevent 1/2 turning into a 2-line fraction +// AMdiv = {input:"/", tag:"mfrac", output:"/", ttype:INFIX}, +// Commented out by DRW so that " prints literally in equations +// AMquote = {input:"\"", tag:"mtext", output:"mbox", ttype:TEXT}; + +var AMsymbols = [ +//Greek letters +{input:"\\alpha", tag:"mi", output:"\u03B1", ttype:CONST}, +{input:"\\beta", tag:"mi", output:"\u03B2", ttype:CONST}, +{input:"\\gamma", tag:"mi", output:"\u03B3", ttype:CONST}, +{input:"\\delta", tag:"mi", output:"\u03B4", ttype:CONST}, +{input:"\\epsilon", tag:"mi", output:"\u03B5", ttype:CONST}, +{input:"\\varepsilon", tag:"mi", output:"\u025B", ttype:CONST}, +{input:"\\zeta", tag:"mi", output:"\u03B6", ttype:CONST}, +{input:"\\eta", tag:"mi", output:"\u03B7", ttype:CONST}, +{input:"\\theta", tag:"mi", output:"\u03B8", ttype:CONST}, +{input:"\\vartheta", tag:"mi", output:"\u03D1", ttype:CONST}, +{input:"\\iota", tag:"mi", output:"\u03B9", ttype:CONST}, +{input:"\\kappa", tag:"mi", output:"\u03BA", ttype:CONST}, +{input:"\\lambda", tag:"mi", output:"\u03BB", ttype:CONST}, +{input:"\\mu", tag:"mi", output:"\u03BC", ttype:CONST}, +{input:"\\nu", tag:"mi", output:"\u03BD", ttype:CONST}, +{input:"\\xi", tag:"mi", output:"\u03BE", ttype:CONST}, +{input:"\\pi", tag:"mi", output:"\u03C0", ttype:CONST}, +{input:"\\varpi", tag:"mi", output:"\u03D6", ttype:CONST}, +{input:"\\rho", tag:"mi", output:"\u03C1", ttype:CONST}, +{input:"\\varrho", tag:"mi", output:"\u03F1", ttype:CONST}, +{input:"\\varsigma", tag:"mi", output:"\u03C2", ttype:CONST}, +{input:"\\sigma", tag:"mi", output:"\u03C3", ttype:CONST}, +{input:"\\tau", tag:"mi", output:"\u03C4", ttype:CONST}, +{input:"\\upsilon", tag:"mi", output:"\u03C5", ttype:CONST}, +{input:"\\phi", tag:"mi", output:"\u03C6", ttype:CONST}, +{input:"\\varphi", tag:"mi", output:"\u03D5", ttype:CONST}, +{input:"\\chi", tag:"mi", output:"\u03C7", ttype:CONST}, +{input:"\\psi", tag:"mi", output:"\u03C8", ttype:CONST}, +{input:"\\omega", tag:"mi", output:"\u03C9", ttype:CONST}, +{input:"\\Gamma", tag:"mo", output:"\u0393", ttype:CONST}, +{input:"\\Delta", tag:"mo", output:"\u0394", ttype:CONST}, +{input:"\\Theta", tag:"mo", output:"\u0398", ttype:CONST}, +{input:"\\Lambda", tag:"mo", output:"\u039B", ttype:CONST}, +{input:"\\Xi", tag:"mo", output:"\u039E", ttype:CONST}, +{input:"\\Pi", tag:"mo", output:"\u03A0", ttype:CONST}, +{input:"\\Sigma", tag:"mo", output:"\u03A3", ttype:CONST}, +{input:"\\Upsilon", tag:"mo", output:"\u03A5", ttype:CONST}, +{input:"\\Phi", tag:"mo", output:"\u03A6", ttype:CONST}, +{input:"\\Psi", tag:"mo", output:"\u03A8", ttype:CONST}, +{input:"\\Omega", tag:"mo", output:"\u03A9", ttype:CONST}, + +//fractions +{input:"\\frac12", tag:"mo", output:"\u00BD", ttype:CONST}, +{input:"\\frac14", tag:"mo", output:"\u00BC", ttype:CONST}, +{input:"\\frac34", tag:"mo", output:"\u00BE", ttype:CONST}, +{input:"\\frac13", tag:"mo", output:"\u2153", ttype:CONST}, +{input:"\\frac23", tag:"mo", output:"\u2154", ttype:CONST}, +{input:"\\frac15", tag:"mo", output:"\u2155", ttype:CONST}, +{input:"\\frac25", tag:"mo", output:"\u2156", ttype:CONST}, +{input:"\\frac35", tag:"mo", output:"\u2157", ttype:CONST}, +{input:"\\frac45", tag:"mo", output:"\u2158", ttype:CONST}, +{input:"\\frac16", tag:"mo", output:"\u2159", ttype:CONST}, +{input:"\\frac56", tag:"mo", output:"\u215A", ttype:CONST}, +{input:"\\frac18", tag:"mo", output:"\u215B", ttype:CONST}, +{input:"\\frac38", tag:"mo", output:"\u215C", ttype:CONST}, +{input:"\\frac58", tag:"mo", output:"\u215D", ttype:CONST}, +{input:"\\frac78", tag:"mo", output:"\u215E", ttype:CONST}, + +//binary operation symbols +{input:"\\pm", tag:"mo", output:"\u00B1", ttype:CONST}, +{input:"\\mp", tag:"mo", output:"\u2213", ttype:CONST}, +{input:"\\triangleleft",tag:"mo", output:"\u22B2", ttype:CONST}, +{input:"\\triangleright",tag:"mo",output:"\u22B3", ttype:CONST}, +{input:"\\cdot", tag:"mo", output:"\u22C5", ttype:CONST}, +{input:"\\star", tag:"mo", output:"\u22C6", ttype:CONST}, +{input:"\\ast", tag:"mo", output:"\u002A", ttype:CONST}, +{input:"\\times", tag:"mo", output:"\u00D7", ttype:CONST}, +{input:"\\div", tag:"mo", output:"\u00F7", ttype:CONST}, +{input:"\\circ", tag:"mo", output:"\u2218", ttype:CONST}, +//{input:"\\bullet", tag:"mo", output:"\u2219", ttype:CONST}, +{input:"\\bullet", tag:"mo", output:"\u2022", ttype:CONST}, +{input:"\\oplus", tag:"mo", output:"\u2295", ttype:CONST}, +{input:"\\ominus", tag:"mo", output:"\u2296", ttype:CONST}, +{input:"\\otimes", tag:"mo", output:"\u2297", ttype:CONST}, +{input:"\\bigcirc", tag:"mo", output:"\u25CB", ttype:CONST}, +{input:"\\oslash", tag:"mo", output:"\u2298", ttype:CONST}, +{input:"\\odot", tag:"mo", output:"\u2299", ttype:CONST}, +{input:"\\land", tag:"mo", output:"\u2227", ttype:CONST}, +{input:"\\wedge", tag:"mo", output:"\u2227", ttype:CONST}, +{input:"\\lor", tag:"mo", output:"\u2228", ttype:CONST}, +{input:"\\vee", tag:"mo", output:"\u2228", ttype:CONST}, +{input:"\\cap", tag:"mo", output:"\u2229", ttype:CONST}, +{input:"\\cup", tag:"mo", output:"\u222A", ttype:CONST}, +{input:"\\sqcap", tag:"mo", output:"\u2293", ttype:CONST}, +{input:"\\sqcup", tag:"mo", output:"\u2294", ttype:CONST}, +{input:"\\uplus", tag:"mo", output:"\u228E", ttype:CONST}, +{input:"\\amalg", tag:"mo", output:"\u2210", ttype:CONST}, +{input:"\\bigtriangleup",tag:"mo",output:"\u25B3", ttype:CONST}, +{input:"\\bigtriangledown",tag:"mo",output:"\u25BD", ttype:CONST}, +{input:"\\dag", tag:"mo", output:"\u2020", ttype:CONST}, +{input:"\\dagger", tag:"mo", output:"\u2020", ttype:CONST}, +{input:"\\ddag", tag:"mo", output:"\u2021", ttype:CONST}, +{input:"\\ddagger", tag:"mo", output:"\u2021", ttype:CONST}, +{input:"\\lhd", tag:"mo", output:"\u22B2", ttype:CONST}, +{input:"\\rhd", tag:"mo", output:"\u22B3", ttype:CONST}, +{input:"\\unlhd", tag:"mo", output:"\u22B4", ttype:CONST}, +{input:"\\unrhd", tag:"mo", output:"\u22B5", ttype:CONST}, + + +//BIG Operators +{input:"\\sum", tag:"mo", output:"\u2211", ttype:UNDEROVER}, +{input:"\\prod", tag:"mo", output:"\u220F", ttype:UNDEROVER}, +{input:"\\bigcap", tag:"mo", output:"\u22C2", ttype:UNDEROVER}, +{input:"\\bigcup", tag:"mo", output:"\u22C3", ttype:UNDEROVER}, +{input:"\\bigwedge", tag:"mo", output:"\u22C0", ttype:UNDEROVER}, +{input:"\\bigvee", tag:"mo", output:"\u22C1", ttype:UNDEROVER}, +{input:"\\bigsqcap", tag:"mo", output:"\u2A05", ttype:UNDEROVER}, +{input:"\\bigsqcup", tag:"mo", output:"\u2A06", ttype:UNDEROVER}, +{input:"\\coprod", tag:"mo", output:"\u2210", ttype:UNDEROVER}, +{input:"\\bigoplus", tag:"mo", output:"\u2A01", ttype:UNDEROVER}, +{input:"\\bigotimes", tag:"mo", output:"\u2A02", ttype:UNDEROVER}, +{input:"\\bigodot", tag:"mo", output:"\u2A00", ttype:UNDEROVER}, +{input:"\\biguplus", tag:"mo", output:"\u2A04", ttype:UNDEROVER}, +{input:"\\int", tag:"mo", output:"\u222B", ttype:CONST}, +{input:"\\oint", tag:"mo", output:"\u222E", ttype:CONST}, + +//binary relation symbols +{input:":=", tag:"mo", output:":=", ttype:CONST}, +{input:"\\colon", tag:"mo", output:":", ttype:CONST}, // DPVC +{input:"\\$", tag:"mo", output:"$", ttype:CONST}, // DPVC +{input:"\\lt", tag:"mo", output:"<", ttype:CONST}, +{input:"\\gt", tag:"mo", output:">", ttype:CONST}, +{input:"\\ne", tag:"mo", output:"\u2260", ttype:CONST}, +{input:"\\neq", tag:"mo", output:"\u2260", ttype:CONST}, +{input:"\\le", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\leq", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\leqslant", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\ge", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\geq", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\geqslant", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\equiv", tag:"mo", output:"\u2261", ttype:CONST}, +{input:"\\ll", tag:"mo", output:"\u226A", ttype:CONST}, +{input:"\\gg", tag:"mo", output:"\u226B", ttype:CONST}, +{input:"\\doteq", tag:"mo", output:"\u2250", ttype:CONST}, +{input:"\\prec", tag:"mo", output:"\u227A", ttype:CONST}, +{input:"\\succ", tag:"mo", output:"\u227B", ttype:CONST}, +{input:"\\preceq", tag:"mo", output:"\u227C", ttype:CONST}, +{input:"\\succeq", tag:"mo", output:"\u227D", ttype:CONST}, +{input:"\\subset", tag:"mo", output:"\u2282", ttype:CONST}, +{input:"\\supset", tag:"mo", output:"\u2283", ttype:CONST}, +{input:"\\subseteq", tag:"mo", output:"\u2286", ttype:CONST}, +{input:"\\supseteq", tag:"mo", output:"\u2287", ttype:CONST}, +{input:"\\sqsubset", tag:"mo", output:"\u228F", ttype:CONST}, +{input:"\\sqsupset", tag:"mo", output:"\u2290", ttype:CONST}, +{input:"\\sqsubseteq", tag:"mo", output:"\u2291", ttype:CONST}, +{input:"\\sqsupseteq", tag:"mo", output:"\u2292", ttype:CONST}, +{input:"\\sim", tag:"mo", output:"\u223C", ttype:CONST}, +{input:"\\simeq", tag:"mo", output:"\u2243", ttype:CONST}, +{input:"\\approx", tag:"mo", output:"\u2248", ttype:CONST}, +{input:"\\cong", tag:"mo", output:"\u2245", ttype:CONST}, +{input:"\\Join", tag:"mo", output:"\u22C8", ttype:CONST}, +{input:"\\bowtie", tag:"mo", output:"\u22C8", ttype:CONST}, +{input:"\\in", tag:"mo", output:"\u2208", ttype:CONST}, +{input:"\\ni", tag:"mo", output:"\u220B", ttype:CONST}, +{input:"\\owns", tag:"mo", output:"\u220B", ttype:CONST}, +{input:"\\propto", tag:"mo", output:"\u221D", ttype:CONST}, +{input:"\\vdash", tag:"mo", output:"\u22A2", ttype:CONST}, +{input:"\\dashv", tag:"mo", output:"\u22A3", ttype:CONST}, +{input:"\\models", tag:"mo", output:"\u22A8", ttype:CONST}, +{input:"\\perp", tag:"mo", output:"\u22A5", ttype:CONST}, +{input:"\\smile", tag:"mo", output:"\u2323", ttype:CONST}, +{input:"\\frown", tag:"mo", output:"\u2322", ttype:CONST}, +{input:"\\asymp", tag:"mo", output:"\u224D", ttype:CONST}, +{input:"\\notin", tag:"mo", output:"\u2209", ttype:CONST}, + +//matrices +{input:"\\begin{eqnarray}", output:"X", ttype:MATRIX, invisible:true}, +{input:"\\begin{array}", output:"X", ttype:MATRIX, invisible:true}, +{input:"\\\\", output:"}&{", ttype:DEFINITION}, +{input:"\\end{eqnarray}", output:"}}", ttype:DEFINITION}, +{input:"\\end{array}", output:"}}", ttype:DEFINITION}, + +//grouping and literal brackets -- ieval is for IE +{input:"\\big", tag:"mo", output:"X", atval:"1.2", ieval:"2.2", ttype:BIG}, +{input:"\\Big", tag:"mo", output:"X", atval:"1.6", ieval:"2.6", ttype:BIG}, +{input:"\\bigg", tag:"mo", output:"X", atval:"2.2", ieval:"3.2", ttype:BIG}, +{input:"\\Bigg", tag:"mo", output:"X", atval:"2.9", ieval:"3.9", ttype:BIG}, +{input:"\\left", tag:"mo", output:"X", ttype:LEFTBRACKET}, +{input:"\\right", tag:"mo", output:"X", ttype:RIGHTBRACKET}, +{input:"{", output:"{", ttype:LEFTBRACKET, invisible:true}, +{input:"}", output:"}", ttype:RIGHTBRACKET, invisible:true}, + +{input:"(", tag:"mo", output:"(", atval:"1", ttype:STRETCHY}, +{input:"[", tag:"mo", output:"[", atval:"1", ttype:STRETCHY}, +{input:"\\lbrack", tag:"mo", output:"[", atval:"1", ttype:STRETCHY}, +{input:"\\{", tag:"mo", output:"{", atval:"1", ttype:STRETCHY}, +{input:"\\lbrace", tag:"mo", output:"{", atval:"1", ttype:STRETCHY}, +{input:"\\langle", tag:"mo", output:"\u2329", atval:"1", ttype:STRETCHY}, +{input:"\\lfloor", tag:"mo", output:"\u230A", atval:"1", ttype:STRETCHY}, +{input:"\\lceil", tag:"mo", output:"\u2308", atval:"1", ttype:STRETCHY}, + +// rtag:"mi" causes space to be inserted before a following sin, cos, etc. +// (see function AMparseExpr() ) +{input:")", tag:"mo",output:")", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"]", tag:"mo",output:"]", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rbrack",tag:"mo",output:"]", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\}", tag:"mo",output:"}", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rbrace",tag:"mo",output:"}", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rangle",tag:"mo",output:"\u232A", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rfloor",tag:"mo",output:"\u230B", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rceil", tag:"mo",output:"\u2309", rtag:"mi",atval:"1",ttype:STRETCHY}, + +// "|", "\\|", "\\vert" and "\\Vert" modified later: lspace = rspace = 0em +{input:"|", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\|", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"\\vert", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\Vert", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"\\mid", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\parallel", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"/", tag:"mo", output:"/", atval:"1.01", ttype:STRETCHY}, +{input:"\\backslash", tag:"mo", output:"\u2216", atval:"1", ttype:STRETCHY}, +{input:"\\setminus", tag:"mo", output:"\\", ttype:CONST}, + +//miscellaneous symbols +{input:"\\!", tag:"mspace", atname:"width", atval:"-0.167em", ttype:SPACE}, +{input:"\\,", tag:"mspace", atname:"width", atval:"0.167em", ttype:SPACE}, +{input:"\\>", tag:"mspace", atname:"width", atval:"0.222em", ttype:SPACE}, +{input:"\\:", tag:"mspace", atname:"width", atval:"0.222em", ttype:SPACE}, +{input:"\\;", tag:"mspace", atname:"width", atval:"0.278em", ttype:SPACE}, +{input:"~", tag:"mspace", atname:"width", atval:"0.333em", ttype:SPACE}, +{input:"\\quad", tag:"mspace", atname:"width", atval:"1em", ttype:SPACE}, +{input:"\\qquad", tag:"mspace", atname:"width", atval:"2em", ttype:SPACE}, +//{input:"{}", tag:"mo", output:"\u200B", ttype:CONST}, // zero-width +{input:"\\prime", tag:"mo", output:"\u2032", ttype:CONST}, +{input:"'", tag:"mo", output:"\u02B9", ttype:CONST}, +{input:"''", tag:"mo", output:"\u02BA", ttype:CONST}, +{input:"'''", tag:"mo", output:"\u2034", ttype:CONST}, +{input:"''''", tag:"mo", output:"\u2057", ttype:CONST}, +{input:"\\ldots", tag:"mo", output:"\u2026", ttype:CONST}, +{input:"\\cdots", tag:"mo", output:"\u22EF", ttype:CONST}, +{input:"\\vdots", tag:"mo", output:"\u22EE", ttype:CONST}, +{input:"\\ddots", tag:"mo", output:"\u22F1", ttype:CONST}, +{input:"\\forall", tag:"mo", output:"\u2200", ttype:CONST}, +{input:"\\exists", tag:"mo", output:"\u2203", ttype:CONST}, +{input:"\\Re", tag:"mo", output:"\u211C", ttype:CONST}, +{input:"\\Im", tag:"mo", output:"\u2111", ttype:CONST}, +{input:"\\aleph", tag:"mo", output:"\u2135", ttype:CONST}, +{input:"\\hbar", tag:"mo", output:"\u210F", ttype:CONST}, +{input:"\\ell", tag:"mo", output:"\u2113", ttype:CONST}, +{input:"\\wp", tag:"mo", output:"\u2118", ttype:CONST}, +{input:"\\emptyset", tag:"mo", output:"\u2205", ttype:CONST}, +{input:"\\infty", tag:"mo", output:"\u221E", ttype:CONST}, +{input:"\\surd", tag:"mo", output:"\\sqrt{}", ttype:DEFINITION}, +{input:"\\partial", tag:"mo", output:"\u2202", ttype:CONST}, +{input:"\\nabla", tag:"mo", output:"\u2207", ttype:CONST}, +{input:"\\triangle", tag:"mo", output:"\u25B3", ttype:CONST}, +{input:"\\therefore", tag:"mo", output:"\u2234", ttype:CONST}, +{input:"\\angle", tag:"mo", output:"\u2220", ttype:CONST}, +//{input:"\\\\ ", tag:"mo", output:"\u00A0", ttype:CONST}, +{input:"\\diamond", tag:"mo", output:"\u22C4", ttype:CONST}, +//{input:"\\Diamond", tag:"mo", output:"\u25CA", ttype:CONST}, +{input:"\\Diamond", tag:"mo", output:"\u25C7", ttype:CONST}, +{input:"\\neg", tag:"mo", output:"\u00AC", ttype:CONST}, +{input:"\\lnot", tag:"mo", output:"\u00AC", ttype:CONST}, +{input:"\\bot", tag:"mo", output:"\u22A5", ttype:CONST}, +{input:"\\top", tag:"mo", output:"\u22A4", ttype:CONST}, +{input:"\\square", tag:"mo", output:"\u25AB", ttype:CONST}, +{input:"\\Box", tag:"mo", output:"\u25A1", ttype:CONST}, +{input:"\\wr", tag:"mo", output:"\u2240", ttype:CONST}, + +//standard functions +//Note UNDEROVER *must* have tag:"mo" to work properly +{input:"\\arccos", tag:"mi", output:"arccos", ttype:UNARY, func:true}, +{input:"\\arcsin", tag:"mi", output:"arcsin", ttype:UNARY, func:true}, +{input:"\\arctan", tag:"mi", output:"arctan", ttype:UNARY, func:true}, +{input:"\\arg", tag:"mi", output:"arg", ttype:UNARY, func:true}, +{input:"\\cos", tag:"mi", output:"cos", ttype:UNARY, func:true}, +{input:"\\cosh", tag:"mi", output:"cosh", ttype:UNARY, func:true}, +{input:"\\cot", tag:"mi", output:"cot", ttype:UNARY, func:true}, +{input:"\\coth", tag:"mi", output:"coth", ttype:UNARY, func:true}, +{input:"\\csc", tag:"mi", output:"csc", ttype:UNARY, func:true}, +{input:"\\deg", tag:"mi", output:"deg", ttype:UNARY, func:true}, +{input:"\\det", tag:"mi", output:"det", ttype:UNARY, func:true}, +{input:"\\dim", tag:"mi", output:"dim", ttype:UNARY, func:true}, //CONST? +{input:"\\exp", tag:"mi", output:"exp", ttype:UNARY, func:true}, +{input:"\\gcd", tag:"mi", output:"gcd", ttype:UNARY, func:true}, //CONST? +{input:"\\hom", tag:"mi", output:"hom", ttype:UNARY, func:true}, +{input:"\\inf", tag:"mo", output:"inf", ttype:UNDEROVER}, +{input:"\\ker", tag:"mi", output:"ker", ttype:UNARY, func:true}, +{input:"\\lg", tag:"mi", output:"lg", ttype:UNARY, func:true}, +{input:"\\lim", tag:"mo", output:"lim", ttype:UNDEROVER}, +{input:"\\liminf", tag:"mo", output:"liminf", ttype:UNDEROVER}, +{input:"\\limsup", tag:"mo", output:"limsup", ttype:UNDEROVER}, +{input:"\\ln", tag:"mi", output:"ln", ttype:UNARY, func:true}, +{input:"\\log", tag:"mi", output:"log", ttype:UNARY, func:true}, +{input:"\\max", tag:"mo", output:"max", ttype:UNDEROVER}, +{input:"\\min", tag:"mo", output:"min", ttype:UNDEROVER}, +{input:"\\Pr", tag:"mi", output:"Pr", ttype:UNARY, func:true}, +{input:"\\sec", tag:"mi", output:"sec", ttype:UNARY, func:true}, +{input:"\\sin", tag:"mi", output:"sin", ttype:UNARY, func:true}, +{input:"\\sinh", tag:"mi", output:"sinh", ttype:UNARY, func:true}, +{input:"\\sup", tag:"mo", output:"sup", ttype:UNDEROVER}, +{input:"\\tan", tag:"mi", output:"tan", ttype:UNARY, func:true}, +{input:"\\tanh", tag:"mi", output:"tanh", ttype:UNARY, func:true}, + +//arrows +{input:"\\gets", tag:"mo", output:"\u2190", ttype:CONST}, +{input:"\\leftarrow", tag:"mo", output:"\u2190", ttype:CONST}, +{input:"\\to", tag:"mo", output:"\u2192", ttype:CONST}, +{input:"\\rightarrow", tag:"mo", output:"\u2192", ttype:CONST}, +{input:"\\leftrightarrow", tag:"mo", output:"\u2194", ttype:CONST}, +{input:"\\uparrow", tag:"mo", output:"\u2191", ttype:CONST}, +{input:"\\downarrow", tag:"mo", output:"\u2193", ttype:CONST}, +{input:"\\updownarrow", tag:"mo", output:"\u2195", ttype:CONST}, +{input:"\\Leftarrow", tag:"mo", output:"\u21D0", ttype:CONST}, +{input:"\\Rightarrow", tag:"mo", output:"\u21D2", ttype:CONST}, +{input:"\\Leftrightarrow", tag:"mo", output:"\u21D4", ttype:CONST}, +{input:"\\iff", tag:"mo", output:"~\\Longleftrightarrow~", ttype:DEFINITION}, +{input:"\\Uparrow", tag:"mo", output:"\u21D1", ttype:CONST}, +{input:"\\Downarrow", tag:"mo", output:"\u21D3", ttype:CONST}, +{input:"\\Updownarrow", tag:"mo", output:"\u21D5", ttype:CONST}, +{input:"\\mapsto", tag:"mo", output:"\u21A6", ttype:CONST}, +{input:"\\longleftarrow", tag:"mo", output:"\u2190", ttype:LONG}, +{input:"\\longrightarrow", tag:"mo", output:"\u2192", ttype:LONG}, +{input:"\\longleftrightarrow", tag:"mo", output:"\u2194", ttype:LONG}, +{input:"\\Longleftarrow", tag:"mo", output:"\u21D0", ttype:LONG}, +{input:"\\Longrightarrow", tag:"mo", output:"\u21D2", ttype:LONG}, +{input:"\\Longleftrightarrow", tag:"mo", output:"\u21D4", ttype:LONG}, +{input:"\\longmapsto", tag:"mo", output:"\u21A6", ttype:CONST}, + // disaster if LONG + +//commands with argument +AMsqrt, AMroot, AMfrac, AMover, AMsub, AMsup, AMtext, AMmbox, AMatop, AMchoose, +//AMdiv, AMquote, + +//diacritical marks +{input:"\\acute", tag:"mover", output:"\u00B4", ttype:UNARY, acc:true}, +//{input:"\\acute", tag:"mover", output:"\u0317", ttype:UNARY, acc:true}, +//{input:"\\acute", tag:"mover", output:"\u0301", ttype:UNARY, acc:true}, +//{input:"\\grave", tag:"mover", output:"\u0300", ttype:UNARY, acc:true}, +//{input:"\\grave", tag:"mover", output:"\u0316", ttype:UNARY, acc:true}, +{input:"\\grave", tag:"mover", output:"\u0060", ttype:UNARY, acc:true}, +{input:"\\breve", tag:"mover", output:"\u02D8", ttype:UNARY, acc:true}, +{input:"\\check", tag:"mover", output:"\u02C7", ttype:UNARY, acc:true}, +{input:"\\dot", tag:"mover", output:".", ttype:UNARY, acc:true}, +{input:"\\ddot", tag:"mover", output:"..", ttype:UNARY, acc:true}, +//{input:"\\ddot", tag:"mover", output:"\u00A8", ttype:UNARY, acc:true}, +{input:"\\mathring", tag:"mover", output:"\u00B0", ttype:UNARY, acc:true}, +{input:"\\vec", tag:"mover", output:"\u20D7", ttype:UNARY, acc:true}, +{input:"\\overrightarrow",tag:"mover",output:"\u20D7", ttype:UNARY, acc:true}, +{input:"\\overleftarrow",tag:"mover", output:"\u20D6", ttype:UNARY, acc:true}, +{input:"\\hat", tag:"mover", output:"\u005E", ttype:UNARY, acc:true}, +{input:"\\widehat", tag:"mover", output:"\u0302", ttype:UNARY, acc:true}, +{input:"\\tilde", tag:"mover", output:"~", ttype:UNARY, acc:true}, +//{input:"\\tilde", tag:"mover", output:"\u0303", ttype:UNARY, acc:true}, +{input:"\\widetilde", tag:"mover", output:"\u02DC", ttype:UNARY, acc:true}, +{input:"\\bar", tag:"mover", output:"\u203E", ttype:UNARY, acc:true}, +{input:"\\overbrace", tag:"mover", output:"\u23B4", ttype:UNARY, acc:true}, +{input:"\\overline", tag:"mover", output:"\u00AF", ttype:UNARY, acc:true}, +{input:"\\underbrace", tag:"munder", output:"\u23B5", ttype:UNARY, acc:true}, +{input:"\\underline", tag:"munder", output:"\u00AF", ttype:UNARY, acc:true}, +//{input:"underline", tag:"munder", output:"\u0332", ttype:UNARY, acc:true}, + +//typestyles and fonts +{input:"\\displaystyle",tag:"mstyle",atname:"displaystyle",atval:"true", ttype:UNARY}, +{input:"\\textstyle",tag:"mstyle",atname:"displaystyle",atval:"false", ttype:UNARY}, +{input:"\\scriptstyle",tag:"mstyle",atname:"scriptlevel",atval:"1", ttype:UNARY}, +{input:"\\scriptscriptstyle",tag:"mstyle",atname:"scriptlevel",atval:"2", ttype:UNARY}, +{input:"\\textrm", tag:"mstyle", output:"\\mathrm", ttype: DEFINITION}, +{input:"\\mathbf", tag:"mstyle", atname:"mathvariant", atval:"bold", ttype:UNARY}, +{input:"\\textbf", tag:"mstyle", atname:"mathvariant", atval:"bold", ttype:UNARY}, +{input:"\\mathit", tag:"mstyle", atname:"mathvariant", atval:"italic", ttype:UNARY}, +{input:"\\textit", tag:"mstyle", atname:"mathvariant", atval:"italic", ttype:UNARY}, +{input:"\\mathtt", tag:"mstyle", atname:"mathvariant", atval:"monospace", ttype:UNARY}, +{input:"\\texttt", tag:"mstyle", atname:"mathvariant", atval:"monospace", ttype:UNARY}, +{input:"\\mathsf", tag:"mstyle", atname:"mathvariant", atval:"sans-serif", ttype:UNARY}, +{input:"\\mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", ttype:UNARY, codes:AMbbb}, +{input:"\\mathcal",tag:"mstyle", atname:"mathvariant", atval:"script", ttype:UNARY, codes:AMcal}, +{input:"\\mathfrak",tag:"mstyle",atname:"mathvariant", atval:"fraktur",ttype:UNARY, codes:AMfrk}, +{input:"\\rm", tag:"mstyle", output:"\\mathrm", ttype: DEFINITION}, // DPVC +{input:"\\bf", tag:"mstyle", atname:"mathvariant", atval:"bold", ttype:UNARY}, // DPVC +{input:"\\it", tag:"mstyle", atname:"mathvariant", atval:"italic", ttype:UNARY} // DPVC +]; + +function compareNames(s1,s2) { + if (s1.input > s2.input) return 1 + else return -1; +} + +var AMnames = []; //list of input symbols + +function AMinitSymbols() { + AMsymbols.sort(compareNames); + for (i=0; i=n where str appears or would be inserted +// assumes arr is sorted + if (n==0) { + var h,m; + n = -1; + h = arr.length; + while (n+1> 1; + if (arr[m]=str +} + +function AMgetSymbol(str) { +//return maximal initial substring of str that appears in names +//return null if there is none + var k = 0; //new pos + var j = 0; //old pos + var mk; //match pos + var st; + var tagst; + var match = ""; + var more = true; + for (var i=1; i<=str.length && more; i++) { + st = str.slice(0,i); //initial substring of length i + j = k; + k = AMposition(AMnames, st, j); + if (k=AMnames[k]; + } + AMpreviousSymbol=AMcurrentSymbol; + if (match!=""){ + AMcurrentSymbol=AMsymbols[mk].ttype; + return AMsymbols[mk]; + } + AMcurrentSymbol=CONST; + k = 1; + st = str.slice(0,1); //take 1 character + if ("0"<=st && st<="9") tagst = "mn"; + else tagst = (("A">st || st>"Z") && ("a">st || st>"z")?"mo":"mi"); +/* +// Commented out by DRW (not fully understood, but probably to do with +// use of "/" as an INFIX version of "\\frac", which we don't want): +//} +//if (st=="-" && AMpreviousSymbol==INFIX) { +// AMcurrentSymbol = INFIX; //trick "/" into recognizing "-" on second parse +// return {input:st, tag:tagst, output:st, ttype:UNARY, func:true}; +//} +*/ + return {input:st, tag:tagst, output:st, ttype:CONST}; +} + + +/*Parsing ASCII math expressions with the following grammar +v ::= [A-Za-z] | greek letters | numbers | other constant symbols +u ::= sqrt | text | bb | other unary symbols for font commands +b ::= frac | root | stackrel binary symbols +l ::= { | \left left brackets +r ::= } | \right right brackets +S ::= v | lEr | uS | bSS Simple expression +I ::= S_S | S^S | S_S^S | S Intermediate expression +E ::= IE | I/I Expression +Each terminal symbol is translated into a corresponding mathml node.*/ + +var AMpreviousSymbol,AMcurrentSymbol; + +function AMparseSexpr(str) { //parses str and returns [node,tailstr,(node)tag] + var symbol, node, result, result2, i, st,// rightvert = false, + newFrag = document.createDocumentFragment(); + str = AMremoveCharsAndBlanks(str,0); + symbol = AMgetSymbol(str); //either a token or a bracket or empty + if (symbol == null || symbol.ttype == RIGHTBRACKET) + return [null,str,null]; + if (symbol.ttype == DEFINITION) { + str = symbol.output+AMremoveCharsAndBlanks(str,symbol.input.length); + symbol = AMgetSymbol(str); + if (symbol == null || symbol.ttype == RIGHTBRACKET) + return [null,str,null]; + } + str = AMremoveCharsAndBlanks(str,symbol.input.length); + switch (symbol.ttype) { + case SPACE: + node = AMcreateElementMathML(symbol.tag); + node.setAttribute(symbol.atname,symbol.atval); + return [node,str,symbol.tag]; + case UNDEROVER: + if (isIE) { + if (symbol.input.substr(0,4) == "\\big") { // botch for missing symbols + str = "\\"+symbol.input.substr(4)+str; // make \bigcup = \cup etc. + symbol = AMgetSymbol(str); + symbol.ttype = UNDEROVER; + str = AMremoveCharsAndBlanks(str,symbol.input.length); + } + } + return [AMcreateMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str,symbol.tag]; + case CONST: + var output = symbol.output; + if (isIE) { + if (symbol.input == "'") + output = "\u2032"; + else if (symbol.input == "''") + output = "\u2033"; + else if (symbol.input == "'''") + output = "\u2033\u2032"; + else if (symbol.input == "''''") + output = "\u2033\u2033"; + else if (symbol.input == "\\square") + output = "\u25A1"; // same as \Box + else if (symbol.input.substr(0,5) == "\\frac") { + // botch for missing fractions + var denom = symbol.input.substr(6,1); + if (denom == "5" || denom == "6") { + str = symbol.input.replace(/\\frac/,"\\frac ")+str; + return [node,str,symbol.tag]; + } + } + } + node = AMcreateMmlNode(symbol.tag,document.createTextNode(output)); + return [node,str,symbol.tag]; + case LONG: // added by DRW + node = AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)); + node.setAttribute("minsize","1.5"); + node.setAttribute("maxsize","1.5"); + node = AMcreateMmlNode("mover",node); + node.appendChild(AMcreateElementMathML("mspace")); + return [node,str,symbol.tag]; + case STRETCHY: // added by DRW + if (isIE && symbol.input == "\\backslash") + symbol.output = "\\"; // doesn't expand, but then nor does "\u2216" + node = AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)); + if (symbol.input == "|" || symbol.input == "\\vert" || + symbol.input == "\\|" || symbol.input == "\\Vert") { + node.setAttribute("lspace","0em"); + node.setAttribute("rspace","0em"); + } + node.setAttribute("maxsize",symbol.atval); // don't allow to stretch here + if (symbol.rtag != null) + return [node,str,symbol.rtag]; + else + return [node,str,symbol.tag]; + case BIG: // added by DRW + var atval = symbol.atval; + if (isIE) + atval = symbol.ieval; + symbol = AMgetSymbol(str); + if (symbol == null) + return [null,str,null]; + str = AMremoveCharsAndBlanks(str,symbol.input.length); + node = AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)); + if (isIE) { // to get brackets to expand + var space = AMcreateElementMathML("mspace"); + space.setAttribute("height",atval+"ex"); + node = AMcreateMmlNode("mrow",node); + node.appendChild(space); + } else { // ignored in IE + node.setAttribute("minsize",atval); + node.setAttribute("maxsize",atval); + } + return [node,str,symbol.tag]; + case LEFTBRACKET: //read (expr+) + if (symbol.input == "\\left") { // left what? + symbol = AMgetSymbol(str); + if (symbol != null) { + if (symbol.input == ".") + symbol.invisible = true; + str = AMremoveCharsAndBlanks(str,symbol.input.length); + } + } + result = AMparseExpr(str,true,false); + if (symbol==null || + (typeof symbol.invisible == "boolean" && symbol.invisible)) + node = AMcreateMmlNode("mrow",result[0]); + else { + node = AMcreateMmlNode("mo",document.createTextNode(symbol.output)); + node = AMcreateMmlNode("mrow",node); + node.appendChild(result[0]); + } + return [node,result[1],result[2]]; + case MATRIX: //read (expr+) + if (symbol.input == "\\begin{array}") { + var mask = ""; + symbol = AMgetSymbol(str); + str = AMremoveCharsAndBlanks(str,0); + if (symbol == null) + mask = "l"; + else { + str = AMremoveCharsAndBlanks(str,symbol.input.length); + if (symbol.input != "{") + mask = "l"; + else do { + symbol = AMgetSymbol(str); + if (symbol != null) { + str = AMremoveCharsAndBlanks(str,symbol.input.length); + if (symbol.input != "}") + mask = mask+symbol.input; + } + } while (symbol != null && symbol.input != "" && symbol.input != "}"); + } + result = AMparseExpr("{"+str,true,true); +// if (result[0]==null) return [AMcreateMmlNode("mo", +// document.createTextNode(symbol.input)),str]; + node = AMcreateMmlNode("mtable",result[0]); + mask = mask.replace(/l/g,"left "); + mask = mask.replace(/r/g,"right "); + mask = mask.replace(/c/g,"center "); + node.setAttribute("columnalign",mask); + node.setAttribute("displaystyle","false"); + if (isIE) + return [node,result[1],null]; +// trying to get a *little* bit of space around the array +// (IE already includes it) + var lspace = AMcreateElementMathML("mspace"); + lspace.setAttribute("width","0.167em"); + var rspace = AMcreateElementMathML("mspace"); + rspace.setAttribute("width","0.167em"); + var node1 = AMcreateMmlNode("mrow",lspace); + node1.appendChild(node); + node1.appendChild(rspace); + return [node1,result[1],null]; + } else { // eqnarray + result = AMparseExpr("{"+str,true,true); + node = AMcreateMmlNode("mtable",result[0]); + if (isIE) + node.setAttribute("columnspacing","0.25em"); // best in practice? + else + node.setAttribute("columnspacing","0.167em"); // correct (but ignored?) + node.setAttribute("columnalign","right center left"); + node.setAttribute("displaystyle","true"); + node = AMcreateMmlNode("mrow",node); + return [node,result[1],null]; + } + case TEXT: + if (str.charAt(0)=="{") i=str.indexOf("}"); + else i = 0; + if (i==-1) + i = str.length; + st = str.slice(1,i); + if (st.charAt(0) == " ") { + node = AMcreateElementMathML("mspace"); + node.setAttribute("width","0.33em"); // was 1ex + newFrag.appendChild(node); + } + newFrag.appendChild( + AMcreateMmlNode(symbol.tag,document.createTextNode(st))); + if (st.charAt(st.length-1) == " ") { + node = AMcreateElementMathML("mspace"); + node.setAttribute("width","0.33em"); // was 1ex + newFrag.appendChild(node); + } + str = AMremoveCharsAndBlanks(str,i+1); + return [AMcreateMmlNode("mrow",newFrag),str,null]; + case UNARY: + result = AMparseSexpr(str); + if (result[0]==null) return [AMcreateMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str]; + if (typeof symbol.func == "boolean" && symbol.func) { // functions hack + st = str.charAt(0); +// if (st=="^" || st=="_" || st=="/" || st=="|" || st==",") { + if (st=="^" || st=="_" || st==",") { + return [AMcreateMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str,symbol.tag]; + } else { + node = AMcreateMmlNode("mrow", + AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output))); + if (isIE) { + var space = AMcreateElementMathML("mspace"); + space.setAttribute("width","0.167em"); + node.appendChild(space); + } + node.appendChild(result[0]); + return [node,result[1],symbol.tag]; + } + } + if (symbol.input == "\\sqrt") { // sqrt + if (isIE) { // set minsize, for \surd + var space = AMcreateElementMathML("mspace"); + space.setAttribute("height","1.2ex"); + space.setAttribute("width","0em"); // probably no effect + node = AMcreateMmlNode(symbol.tag,result[0]) +// node.setAttribute("minsize","1"); // ignored +// node = AMcreateMmlNode("mrow",node); // hopefully unnecessary + node.appendChild(space); + return [node,result[1],symbol.tag]; + } else + return [AMcreateMmlNode(symbol.tag,result[0]),result[1],symbol.tag]; + } else if (typeof symbol.acc == "boolean" && symbol.acc) { // accent + node = AMcreateMmlNode(symbol.tag,result[0]); + var output = symbol.output; + if (isIE) { + if (symbol.input == "\\hat") + output = "\u0302"; + else if (symbol.input == "\\widehat") + output = "\u005E"; + else if (symbol.input == "\\bar") + output = "\u00AF"; + else if (symbol.input == "\\grave") + output = "\u0300"; + else if (symbol.input == "\\tilde") + output = "\u0303"; + } + var node1 = AMcreateMmlNode("mo",document.createTextNode(output)); + if (symbol.input == "\\vec" || symbol.input == "\\check") + // don't allow to stretch + node1.setAttribute("maxsize","1.2"); + // why doesn't "1" work? \vec nearly disappears in firefox + if (isIE && symbol.input == "\\bar") + node1.setAttribute("maxsize","0.5"); + if (symbol.input == "\\underbrace" || symbol.input == "\\underline") + node1.setAttribute("accentunder","true"); + else + node1.setAttribute("accent","true"); + node.appendChild(node1); + if (symbol.input == "\\overbrace" || symbol.input == "\\underbrace") + node.ttype = UNDEROVER; + return [node,result[1],symbol.tag]; + } else { // font change or displaystyle command + if (!isIE && typeof symbol.codes != "undefined") { + for (i=0; i64 && st.charCodeAt(j)<91) newst = newst + + String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]); + else newst = newst + st.charAt(j); + if (result[0].nodeName=="mi") + result[0]=AMcreateElementMathML("mo"). + appendChild(document.createTextNode(newst)); + else result[0].replaceChild(AMcreateElementMathML("mo"). + appendChild(document.createTextNode(newst)),result[0].childNodes[i]); + } + } + node = AMcreateMmlNode(symbol.tag,result[0]); + node.setAttribute(symbol.atname,symbol.atval); + if (symbol.input == "\\scriptstyle" || + symbol.input == "\\scriptscriptstyle") + node.setAttribute("displaystyle","false"); + return [node,result[1],symbol.tag]; + } + case BINARY: + result = AMparseSexpr(str); + if (result[0]==null) return [AMcreateMmlNode("mo", + document.createTextNode(symbol.input)),str,null]; + result2 = AMparseSexpr(result[1]); + if (result2[0]==null) return [AMcreateMmlNode("mo", + document.createTextNode(symbol.input)),str,null]; + if (symbol.input=="\\root" || symbol.input=="\\stackrel") + newFrag.appendChild(result2[0]); + newFrag.appendChild(result[0]); + if (symbol.input=="\\frac") newFrag.appendChild(result2[0]); + return [AMcreateMmlNode(symbol.tag,newFrag),result2[1],symbol.tag]; + case INFIX: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + return [AMcreateMmlNode("mo",document.createTextNode(symbol.output)), + str,symbol.tag]; + default: + return [AMcreateMmlNode(symbol.tag, //its a constant + document.createTextNode(symbol.output)),str,symbol.tag]; + } +} + +function AMparseIexpr(str) { + var symbol, sym1, sym2, node, result, tag, underover; + str = AMremoveCharsAndBlanks(str,0); + sym1 = AMgetSymbol(str); + result = AMparseSexpr(str); + node = result[0]; + str = result[1]; + tag = result[2]; + symbol = AMgetSymbol(str); + if (symbol.ttype == INFIX) { + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseSexpr(str); + if (result[0] == null) // show box in place of missing argument + result[0] = AMcreateMmlNode("mo",document.createTextNode("\u25A1")); + str = result[1]; + tag = result[2]; + if (symbol.input == "_" || symbol.input == "^") { + sym2 = AMgetSymbol(str); + tag = null; // no space between x^2 and a following sin, cos, etc. +// This is for \underbrace and \overbrace + underover = ((sym1.ttype == UNDEROVER) || (node.ttype == UNDEROVER)); +// underover = (sym1.ttype == UNDEROVER); + if (symbol.input == "_" && sym2.input == "^") { + str = AMremoveCharsAndBlanks(str,sym2.input.length); + var res2 = AMparseSexpr(str); + str = res2[1]; + tag = res2[2]; // leave space between x_1^2 and a following sin etc. + node = AMcreateMmlNode((underover?"munderover":"msubsup"),node); + node.appendChild(result[0]); + node.appendChild(res2[0]); + } else if (symbol.input == "_") { + node = AMcreateMmlNode((underover?"munder":"msub"),node); + node.appendChild(result[0]); + } else { + node = AMcreateMmlNode((underover?"mover":"msup"),node); + node.appendChild(result[0]); + } + node = AMcreateMmlNode("mrow",node); // so sum does not stretch + } else { + node = AMcreateMmlNode(symbol.tag,node); + if (symbol.input == "\\atop" || symbol.input == "\\choose") + node.setAttribute("linethickness","0ex"); + node.appendChild(result[0]); + if (symbol.input == "\\choose") + node = AMcreateMmlNode("mfenced",node); + } + } + return [node,str,tag]; +} + +function AMparseExpr(str,rightbracket,matrix) { + var symbol, node, result, i, tag, + newFrag = document.createDocumentFragment(); + do { + str = AMremoveCharsAndBlanks(str,0); + result = AMparseIexpr(str); + node = result[0]; + str = result[1]; + tag = result[2]; + symbol = AMgetSymbol(str); + if (node!=undefined) { + if ((tag == "mn" || tag == "mi") && symbol!=null && + typeof symbol.func == "boolean" && symbol.func) { + // Add space before \sin in 2\sin x or x\sin x + var space = AMcreateElementMathML("mspace"); + space.setAttribute("width","0.167em"); + node = AMcreateMmlNode("mrow",node); + node.appendChild(space); + } + newFrag.appendChild(node); + } + } while ((symbol.ttype != RIGHTBRACKET) + && symbol!=null && symbol.output!=""); + tag = null; + if (symbol.ttype == RIGHTBRACKET) { + if (symbol.input == "\\right") { // right what? + str = AMremoveCharsAndBlanks(str,symbol.input.length); + symbol = AMgetSymbol(str); + if (symbol != null && symbol.input == ".") + symbol.invisible = true; + if (symbol != null) + tag = symbol.rtag; + } + if (symbol!=null) + str = AMremoveCharsAndBlanks(str,symbol.input.length); // ready to return + var len = newFrag.childNodes.length; + if (matrix && + len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 && + newFrag.childNodes[len-2].nodeName == "mo" && + newFrag.childNodes[len-2].firstChild.nodeValue == "&") { //matrix + var pos = []; // positions of ampersands + var m = newFrag.childNodes.length; + for (i=0; matrix && i -&-&...&-&- + n = node.childNodes.length; + k = 0; + for (j=0; j2) { + newFrag.removeChild(newFrag.firstChild); //remove + newFrag.removeChild(newFrag.firstChild); //remove & + } + table.appendChild(AMcreateMmlNode("mtr",row)); + } + return [table,str]; + } + if (typeof symbol.invisible != "boolean" || !symbol.invisible) { + node = AMcreateMmlNode("mo",document.createTextNode(symbol.output)); + newFrag.appendChild(node); + } + } + return [newFrag,str,tag]; +} + +function AMparseMath(str) { + var result, node = AMcreateElementMathML("mstyle"); + if (mathcolor != "") node.setAttribute("mathcolor",mathcolor); + if (mathfontfamily != "") node.setAttribute("fontfamily",mathfontfamily); + node.appendChild(AMparseExpr(str.replace(/^\s+/g,""),false,false)[0]); + node = AMcreateMmlNode("math",node); + if (showasciiformulaonhover) //fixed by djhsu so newline + node.setAttribute("title",str.replace(/\s+/g," "));//does not show in Gecko + if (mathfontfamily != "" && (isIE || mathfontfamily != "serif")) { + var fnode = AMcreateElementXHTML("font"); + fnode.setAttribute("face",mathfontfamily); + fnode.appendChild(node); + return fnode; + } + return node; +} + +function AMstrarr2docFrag(arr, linebreaks) { + var newFrag=document.createDocumentFragment(); + var expr = false; + for (var i=0; i1 || mtch) { + if (checkForMathML) { + checkForMathML = false; + var nd = AMisMathMLavailable(); + AMnoMathML = nd != null; + if (AMnoMathML && notifyIfNoMathML) + if (alertIfNoMathML) + alert("To view the ASCIIMathML notation use Internet Explorer 6 +\nMathPlayer (free from www.dessci.com)\n\ + or Firefox/Mozilla/Netscape"); + else AMbody.insertBefore(nd,AMbody.childNodes[0]); + } + if (!AMnoMathML) { + frg = AMstrarr2docFrag(arr,n.nodeType==8); + var len = frg.childNodes.length; + n.parentNode.replaceChild(frg,n); + return len-1; + } else return 0; + } + } + } else return 0; + } else if (n.nodeName!="math") { + for (i=0; i"); + document.write(""); +} + +// GO1.1 Generic onload by Brothercake +// http://www.brothercake.com/ +//onload function (replaces the onload="translate()" in the tag) +function generic() +{ + translate(); +}; +//setup onload function +if(typeof window.addEventListener != 'undefined') +{ + //.. gecko, safari, konqueror and standard + window.addEventListener('load', generic, false); +} +else if(typeof document.addEventListener != 'undefined') +{ + //.. opera 7 + document.addEventListener('load', generic, false); +} +else if(typeof window.attachEvent != 'undefined') +{ + //.. win/ie + window.attachEvent('onload', generic); +} +//** remove this condition to degrade older browsers +else +{ + //.. mac/ie5 and anything else that gets this far + //if there's an existing onload function + if(typeof window.onload == 'function') + { + //store it + var existing = onload; + //add new onload handler + window.onload = function() + { + //call existing onload function + existing(); + //call generic onload function + generic(); + }; + } + else + { + //setup onload function + window.onload = generic; + } +} + +} // DPVC diff --git a/htdocs/applets/AC_RunActiveContent.js b/htdocs/applets/AC_RunActiveContent.js new file mode 100755 index 0000000000..30cddb9dbb --- /dev/null +++ b/htdocs/applets/AC_RunActiveContent.js @@ -0,0 +1,292 @@ +//v1.7 +// Flash Player Version Detection +// Detect Client Browser type +// Copyright 2005-2007 Adobe Systems Incorporated. All rights reserved. +var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; +var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; +var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; + +function ControlVersion() +{ + var version; + var axo; + var e; + + // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry + + try { + // version will be set for 7.X or greater players + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); + version = axo.GetVariable("$version"); + } catch (e) { + } + + if (!version) + { + try { + // version will be set for 6.X players only + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); + + // installed player is some revision of 6.0 + // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, + // so we have to be careful. + + // default to the first public version + version = "WIN 6,0,21,0"; + + // throws if AllowScripAccess does not exist (introduced in 6.0r47) + axo.AllowScriptAccess = "always"; + + // safe to call for 6.0r47 or greater + version = axo.GetVariable("$version"); + + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 4.X or 5.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = axo.GetVariable("$version"); + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 3.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = "WIN 3,0,18,0"; + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 2.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); + version = "WIN 2,0,0,11"; + } catch (e) { + version = -1; + } + } + + return version; +} + +// JavaScript helper required to detect Flash Player PlugIn version information +function GetSwfVer(){ + // NS/Opera version >= 3 check for Flash plugin in plugin array + var flashVer = -1; + + if (navigator.plugins != null && navigator.plugins.length > 0) { + if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { + var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; + var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; + var descArray = flashDescription.split(" "); + var tempArrayMajor = descArray[2].split("."); + var versionMajor = tempArrayMajor[0]; + var versionMinor = tempArrayMajor[1]; + var versionRevision = descArray[3]; + if (versionRevision == "") { + versionRevision = descArray[4]; + } + if (versionRevision[0] == "d") { + versionRevision = versionRevision.substring(1); + } else if (versionRevision[0] == "r") { + versionRevision = versionRevision.substring(1); + if (versionRevision.indexOf("d") > 0) { + versionRevision = versionRevision.substring(0, versionRevision.indexOf("d")); + } + } + var flashVer = versionMajor + "." + versionMinor + "." + versionRevision; + } + } + // MSN/WebTV 2.6 supports Flash 4 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; + // WebTV 2.5 supports Flash 3 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; + // older WebTV supports Flash 2 + else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; + else if ( isIE && isWin && !isOpera ) { + flashVer = ControlVersion(); + } + return flashVer; +} + +// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available +function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) +{ + versionStr = GetSwfVer(); + if (versionStr == -1 ) { + return false; + } else if (versionStr != 0) { + if(isIE && isWin && !isOpera) { + // Given "WIN 2,0,0,11" + tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"] + tempString = tempArray[1]; // "2,0,0,11" + versionArray = tempString.split(","); // ['2', '0', '0', '11'] + } else { + versionArray = versionStr.split("."); + } + var versionMajor = versionArray[0]; + var versionMinor = versionArray[1]; + var versionRevision = versionArray[2]; + + // is the major.revision >= requested major.revision AND the minor version >= requested minor + if (versionMajor > parseFloat(reqMajorVer)) { + return true; + } else if (versionMajor == parseFloat(reqMajorVer)) { + if (versionMinor > parseFloat(reqMinorVer)) + return true; + else if (versionMinor == parseFloat(reqMinorVer)) { + if (versionRevision >= parseFloat(reqRevision)) + return true; + } + } + return false; + } +} + +function AC_AddExtension(src, ext) +{ + if (src.indexOf('?') != -1) + return src.replace(/\?/, ext+'?'); + else + return src + ext; +} + +function AC_Generateobj(objAttrs, params, embedAttrs) +{ + var str = ''; + if (isIE && isWin && !isOpera) + { + str += ' '; + } + str += ''; + } + else + { + str += ' 0) + { + polar_color = getColor("polar_color", "255255255"); + polar_origin_x = getInteger("polar_origin_x", 0, image_width - 1, image_width/2); + polar_origin_y = getInteger("polar_origin_y", 0, image_height - 1, image_height/2); + } + + grid_sw = getInteger("grid_sw", 0, 1, 0); + grid_x_start = getInteger("grid_x_start", 0, image_width - 1, 0); + grid_dx = getInteger("grid_dx", 2, image_width - 1, 10); + grid_y_start = getInteger("grid_y_start", 0, image_height - 1, 0); + grid_dy = getInteger("grid_dy", 1, image_height - 1, 10); + grid_color = getColor("grid_color", "000000000"); + grid_width = getInteger("grid_width", 0, image_width, image_width); + grid_height = getInteger("grid_height", 0, image_height, image_height); + grid_fill_sw = getInteger("grid_fill_sw", 0, 1, 0); + } + + public void paint(Graphics g) + { + super.paint(g); + drawcursors(); + g.drawImage(bI, 0, 0, this); + } + + public void update(Graphics g) + { + paint(g); + } + + public void drawcursors() + { + int work, work_x, work_y; + int center_x, center_y; + int real_y; + String angle_string; + + bG.setColor(back_color); + bG.fillRect(0, 0, applet_width, applet_height); + bG.drawImage(backdrop, margin_x, margin_y, this); + + if(grid_sw == 1) + { + bG.setColor(grid_color); + for (int ix = grid_x_start; ix <= grid_x_start + grid_width - 1; ix = ix + grid_dx) + { + bG.drawLine(margin_x + ix, margin_y + image_height - 1 - grid_y_start, + margin_x + ix, margin_y + image_height - grid_y_start - grid_height); + } + for (int iy = grid_y_start; iy <= grid_y_start + grid_height - 1; iy = iy + grid_dy) + { + bG.drawLine(margin_x + grid_x_start, margin_y + image_height - 1 - iy, + margin_x + grid_x_start + grid_width - 1, margin_y + image_height - 1 - iy); + } + } + + if (polar_sw > 0) + { + + // Draw polar grid + + bG.setColor(polar_color); + + // Compute center for diagonal polar grid lines + + center_x = margin_x + polar_origin_x; + center_y = margin_y + image_height - 1 - polar_origin_y; + + // Draw horizontal and vertical polar grid lines + + bG.drawLine(margin_x, center_y, margin_x + image_width - 1, center_y); + bG.drawLine(center_x, margin_y, center_x, margin_y + image_height - 1); + + // Draw 45 degree polar grid line + + work = java.lang.Math.min(image_width - 1 - polar_origin_x, polar_origin_y) + 1; + bG.drawLine(center_x, center_y, center_x + work, center_y + work); + + // Draw -45 degree polar grid line + + work = java.lang.Math.min(image_width - polar_origin_x, image_height - polar_origin_y) + 1; + bG.drawLine(center_x, center_y, center_x + work, center_y - work); + + // Draw 135 degree polar grid line + + work = java.lang.Math.min(polar_origin_x, polar_origin_y) + 1; + bG.drawLine(center_x, center_y, center_x - work, center_y + work); + + // Draw -135 degree polar grid line + + work = java.lang.Math.min(polar_origin_x, image_height - 1 - polar_origin_y); + bG.drawLine(center_x, center_y, center_x - work, center_y - work); + } + + // Draw control buttons + + if (record_sw == 0) + { + if (display_sw > 0) + { + record_button.draw_off(bG); + if (save_index > 0) + { + clear_button.draw_off(bG); + if (display_sw == 1) + list_button.draw_off(bG); + } + else + { + clear_button.draw_on(bG); + if (display_sw == 1) + list_button.draw_on(bG); + } + } + } + else + { + if (display_sw > 0) + { + record_button.draw_on(bG); + if (display_sw == 1) + list_button.draw_off(bG); + if (save_index > 0) + { + clear_button.draw_off(bG); + } + else + { + clear_button.draw_on(bG); + } + } + } + + // Draw marks at recorded points + + if ((mark_sw > 0) & (grid_fill_sw == 0)) + { + bG.setColor(mark_color); + for (int i = 0; i < save_index; i = i + 1) + { + work_x = margin_x + save_x[i]; // x- and y-coordinates of marked point + work_y = margin_y + image_height - save_y[i] - 1; + + if (mark_sw == 1) // Mark by open box + { + bG.drawLine(work_x - mark_radius, work_y + mark_radius, work_x + mark_radius, work_y + mark_radius); + bG.drawLine(work_x - mark_radius, work_y - mark_radius, work_x + mark_radius, work_y - mark_radius); + bG.drawLine(work_x - mark_radius, work_y - mark_radius, work_x - mark_radius, work_y + mark_radius); + bG.drawLine(work_x + mark_radius, work_y - mark_radius, work_x + mark_radius, work_y + mark_radius); + } + + if (mark_sw == 2) // Mark by solid box + { + for (int j = -mark_radius; j <= mark_radius; j = j + 1) + bG.drawLine(work_x - mark_radius, work_y + j, work_x + mark_radius, work_y + j); + } + + if (mark_sw == 3) // Mark by open diamond + { + bG.drawLine(work_x - mark_radius, work_y, work_x, work_y + mark_radius); + bG.drawLine(work_x, work_y + mark_radius, work_x + mark_radius, work_y); + bG.drawLine(work_x + mark_radius, work_y, work_x, work_y - mark_radius); + bG.drawLine(work_x, work_y - mark_radius, work_x - mark_radius, work_y); + } + + if (mark_sw == 4) // Mark by solid diamond (default) + { + for (int j = 0; j <= mark_radius; j = j + 1) + { + bG.drawLine(work_x - mark_radius + j, work_y + j, work_x + mark_radius - j, work_y + j); + bG.drawLine(work_x - mark_radius + j, work_y - j, work_x + mark_radius - j, work_y - j); + } + } + } + } + + // Draw lines connecting marked points + + if ((mark_sw > 0) & (mark_connect == 1) & (grid_fill_sw == 0)) + { + bG.setColor(mark_color); + x_start = margin_x + save_x[0]; + y_start = margin_y + image_height - save_y[0] - 1; + for (int i = 1; i < save_index; i = i + 1) + { + work_x = margin_x + save_x[i]; + work_y = margin_y + image_height - save_y[i] - 1; + bG.drawLine(x_start, y_start, work_x, work_y); + x_start = work_x; + y_start = work_y; + } + } + + if ((grid_fill_sw == 1) & (grid_sw == 1)) + { + bG.setColor(mark_color); + for (int i = 0; i < save_index; i = i + 1) + { + work_x = (int) java.lang.Math.floor((save_x[i] - grid_x_start + 100 * grid_dx)/grid_dx) - 100; + work_x = grid_x_start + grid_dx * work_x; + work_y = (int) java.lang.Math.floor((save_y[i] - grid_y_start + 100 * grid_dy)/grid_dy) - 100; + work_y = grid_y_start + grid_dy * work_y + grid_dy; + bG.fillRect(margin_x + work_x + 1, margin_y + image_height - work_y, grid_dx - 1, grid_dy - 1); + } + } + + if (curve_sw == 1) // Draw curve specified as y(x) + { + curve_t = curve_a; + x.set_value(curve_a); + bG.setColor(curve_color); + x_start = (int) java.lang.Math.round(curve_a); + y_start = (int) java.lang.Math.round(y_of_x.value()); + for (int i = 1; i <= curve_n; i = i + 1) + { + curve_t = curve_t + curve_dt; + x.set_value(curve_t); + x_end = (int) java.lang.Math.round(curve_t); + y_end = (int) java.lang.Math.round(y_of_x.value()); + for (int j = -curve_thickness; j <= curve_thickness; j = j + 1) + { + for (int k = -curve_thickness; k <= curve_thickness; k = k + 1) + { + bG.drawLine(margin_x + x_start + j, + margin_y + image_height - 1 - y_start + k, + margin_x + x_end + j, + margin_y + image_height - 1 - y_end + k); + } + } + x_start = x_end; + y_start = y_end; + } + } + + if (curve_sw == 2) // Draw curve specified as x(t), y(t) + { + curve_t = curve_a; + t.set_value(curve_a); + bG.setColor(curve_color); + x_start = (int) java.lang.Math.round(x_of_t.value()); + y_start = (int) java.lang.Math.round(y_of_t.value()); + for (int i = 1; i <= curve_n; i = i + 1) + { + curve_t = curve_t + curve_dt; + t.set_value(curve_t); + x_end = (int) java.lang.Math.round(x_of_t.value()); + y_end = (int) java.lang.Math.round(y_of_t.value()); + for (int j = -curve_thickness; j <= curve_thickness; j = j + 1) + { + for (int k = -curve_thickness; k <= curve_thickness; k = k + 1) + { + bG.drawLine(margin_x + x_start + j, + margin_y + image_height - 1 - y_start + k, + margin_x + x_end + j, + margin_y + image_height - 1 - y_end + k); + } + } + x_start = x_end; + y_start = y_end; + } + } + + if ((curve_sw == 3) & (polar_sw > 0)) + { + center_x = margin_x + polar_origin_x; + center_y = margin_y + image_height - 1 - polar_origin_y; + + curve_t = curve_a; + t.set_value(curve_t); + theta.set_value(curve_t); + bG.setColor(curve_color); + x_start = (int) java.lang.Math.round( + center_x + java.lang.Math.cos(curve_t) * r_of_theta.value()); + y_start = (int) java.lang.Math.round( + center_y - java.lang.Math.sin(curve_t) * r_of_theta.value()); + for (int i = 1; i <= curve_n; i = i + 1) + { + curve_t = curve_t + curve_dt; + t.set_value(curve_t); + theta.set_value(curve_t); + x_end = (int) java.lang.Math.round( + center_x + java.lang.Math.cos(curve_t) * r_of_theta.value()); + y_end = (int) java.lang.Math.round( + center_y - java.lang.Math.sin(curve_t) * r_of_theta.value()); + for (int j = -curve_thickness; j <= curve_thickness; j = j + 1) + { + for (int k = -curve_thickness; k <= curve_thickness; k = k + 1) + bG.drawLine(x_start + j, y_start + k, x_end + j, y_end + k); + } + x_start = x_end; + y_start = y_end; + } + } + + // Draw cursors + + bG.setColor(cursor_color); + bG.drawLine(margin_x + cursor_x, margin_y, margin_x + cursor_x, margin_y + image_height - 1); + bG.drawLine(margin_x, margin_y + cursor_y, margin_x + image_width - 1, margin_y + cursor_y); + + // Draw controls to move cursor one pixel at a time + + bG.setColor(controls_color); + for (int i = 0; i <= cursor_size; i = i + 1) + { + if (cursor_pad_sw == 0) + { + bG.drawLine(mid_x - i, margin_y - 10 - cursor_size + i, mid_x + i, margin_y - 10 - cursor_size + i); + bG.drawLine(mid_x - i, margin_y + 10 + image_height + cursor_size - i, + mid_x + i, margin_y + 10 + image_height + cursor_size - i); + bG.drawLine(margin_x - 10 - cursor_size + i, mid_y - i, margin_x - 10 - cursor_size + i, mid_y + i); + bG.drawLine(margin_x + image_width + 10 + cursor_size - i, mid_y - i, + margin_x + image_width + 10 + cursor_size - i, mid_y + i); + } + else + { + bG.drawLine(cursor_pad_x - i, 5 + cursor_pad_y + 10 + cursor_size - i, + cursor_pad_x + i, 5 + cursor_pad_y + 10 + cursor_size - i); + bG.drawLine(cursor_pad_x - i, cursor_pad_y - 10 - cursor_size + i - 5, + cursor_pad_x + i, cursor_pad_y - 10 - cursor_size + i - 5); + bG.drawLine(5 + cursor_pad_x + 10 + cursor_size - i, cursor_pad_y - i, + 5 + cursor_pad_x + 10 + cursor_size - i, cursor_pad_y + i); + bG.drawLine(cursor_pad_x - 10 - cursor_size + i - 5, cursor_pad_y - i, + cursor_pad_x - 10 - cursor_size + i - 5, cursor_pad_y + i); + } + } + + // Print cursor coordinates + + bG.drawString("x: " + String.valueOf(cursor_x), coordinates_left, coordinates_top + font_size); + bG.drawString("y: " + String.valueOf(image_height - cursor_y - 1), coordinates_left, + coordinates_top + 2 * font_size + 5); + if (grid_fill_sw == 1) + bG.drawString("count: " + String.valueOf(save_index), coordinates_left, coordinates_top + 3 * font_size + 10); + if (polar_sw > 0) + { + real_y = image_height - cursor_y - 1; + distance = java.lang.Math.sqrt((cursor_x - polar_origin_x) * (cursor_x - polar_origin_x) + + (real_y - polar_origin_y) * (real_y - polar_origin_y)); + bG.drawString("r: " + formatdouble(distance, 7, 2), + coordinates_left, coordinates_top + 3 * font_size + 10); + if (cursor_x == polar_origin_x) + { + if (polar_sw == 1) + { + if (real_y >= polar_origin_y) + angle_string = " 1.5708"; + else + angle_string = " 4.7124"; + if (real_y == polar_origin_y) + angle_string = " undefined"; + } + else + { + if (real_y >= polar_origin_y) + angle_string = " 90.00"; + else + angle_string = " 270.00"; + if (real_y == polar_origin_y) + angle_string = " undefined"; + } + } + else + { + angle = java.lang.Math.atan(1.0 * (real_y - polar_origin_y)/(cursor_x - polar_origin_x)); + if (cursor_x < polar_origin_x) + angle = Math.PI + angle; + if (angle < 0) + angle = angle + 2 * Math.PI; + if (polar_sw == 2) + { + angle = angle * 360 / (2 * Math.PI); + angle_string = formatdouble(angle, 10, 2); + } + else + angle_string = formatdouble(angle, 7, 4); + } + bG.drawString("theta: " + angle_string, coordinates_left, coordinates_top + 4 * font_size + 15); + } + } + + public boolean mouseDown(Event evt, int mx, int my) + { + int x_start, y_start, x_end, y_end; + int real_y; + double work; + + if ((mx >= margin_x) & (mx <= margin_x + image_width - 1) & + (my >= margin_y) & (my <= margin_y + image_height - 1)) + { + cursor_x = mx - margin_x; + cursor_y = my - margin_y; + record_sw = 0; + repaint(); + return true; + } + + if (cursor_pad_sw == 0) + { + if ((mx >= margin_x - 10 - cursor_size) & (mx <= margin_x - 10) & + (my >= mid_y - cursor_size) & (my <= mid_y + cursor_size)) + { + cursor_x = java.lang.Math.max(0, cursor_x - 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= margin_x + image_width + 10) & (mx <= margin_x + image_width + 10 + cursor_size) & + (my >= mid_y - cursor_size) & (my <= mid_y + cursor_size)) + { + cursor_x= java.lang.Math.min(image_width - 1, cursor_x + 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= mid_x - cursor_size) & (mx <= mid_x + cursor_size) & + (my >= margin_y - 10 - cursor_size) & (my <= margin_y - 10)) + { + cursor_y = java.lang.Math.max(0, cursor_y - 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= mid_x - cursor_size) & (mx <= mid_x + cursor_size) & + (my >= margin_y + image_height + 10) & (my <= margin_y + image_height + 10 + cursor_size)) + { + cursor_y = java.lang.Math.min(image_height - 1, cursor_y + 1); + record_sw = 0; + repaint(); + return true; + } + } + else + { + if ((mx >= cursor_pad_x - 5 - 2 * cursor_size) & (mx <= cursor_pad_x - 5 - cursor_size) & + (my >= cursor_pad_y - cursor_size - 5) & (my <= cursor_pad_y + cursor_size + 5)) + { + cursor_x = java.lang.Math.max(0, cursor_x - 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= cursor_pad_x + 5 + cursor_size) & (mx <= cursor_pad_x + 5 + 2 * cursor_size) & + (my >= cursor_pad_y - cursor_size - 5) & (my <= cursor_pad_y + cursor_size + 5)) + { + cursor_x= java.lang.Math.min(image_width - 1, cursor_x + 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= cursor_pad_x - 5 - cursor_size) & (mx <= cursor_pad_x + 5 + cursor_size) & + (my >= cursor_pad_y - 5 - 2 * cursor_size) & (my <= cursor_pad_y - 5 - cursor_size)) + { + cursor_y = java.lang.Math.max(0, cursor_y - 1); + record_sw = 0; + repaint(); + return true; + } + if ((mx >= cursor_pad_x - 5 - cursor_size) & (mx <= cursor_pad_x + 5 + cursor_size) & + (my >= cursor_pad_y + 5 + cursor_size) & (my <= cursor_pad_y + 5 + 2 * cursor_size)) + { + cursor_y = java.lang.Math.min(image_height - 1, cursor_y + 1); + record_sw = 0; + repaint(); + return true; + } + + } + + if (record_button.test(mx, my)) + { + if (record_sw == 0) + { + if (save_index == 1000) + save_index = 0; + real_y = image_height - cursor_y - 1; + save_x[save_index] = cursor_x; + save_y[save_index] = real_y; + distance = java.lang.Math.sqrt((cursor_x - polar_origin_x) * (cursor_x - polar_origin_x) + + (real_y - polar_origin_y) * (real_y - polar_origin_y)); + save_distance[save_index] = formatdouble(distance, 10, 2); + if (cursor_x == polar_origin_x) + { + if (polar_sw == 1) + { + if (real_y >= polar_origin_y) + save_angle[save_index] = " 1.5708"; + else + save_angle[save_index] = " 4.7124"; + if (real_y == polar_origin_y) + save_angle[save_index] = " undefined"; + } + else + { + if (real_y >= polar_origin_y) + save_angle[save_index] = " 90.00"; + else + save_angle[save_index] = " 270.00"; + if (real_y == polar_origin_y) + save_angle[save_index] = " undefined"; + } + } + else + { + angle = java.lang.Math.atan(1.0 * (real_y - polar_origin_y)/(cursor_x - polar_origin_x)); + if (cursor_x < polar_origin_x) + angle = Math.PI + angle; + if (angle < 0) + angle = angle + 2 * Math.PI; + if (polar_sw == 2) + { + angle = angle * 360 / (2 * Math.PI); + save_angle[save_index] = formatdouble(angle, 10, 2); + } + else + save_angle[save_index] = formatdouble(angle, 10, 4); + } + save_index = save_index + 1; + record_sw = 1; + repaint(); + return true; + } + } + if (clear_button.test(mx, my)) + { + save_index = 0; + record_sw = 0; + repaint(); + return true; + } + if (list_button.test(mx, my) & (display_sw == 1)) + { + list_window = new DataDisplayFrame("List of marked points"); + list_window.hide(); + list_window.clearText(); + list_window.addText("The way in which you copy information from this window into\n"); + list_window.addText("another window depends on the browser and operating system you are\n"); + list_window.addText("using. One of the following methods should work.\n\n"); + list_window.addText("* Highlight the information to be copied in this window.\n"); + list_window.addText(" Then copy it by pressing command-c. Next click in the area\n"); + list_window.addText(" into which it is to be pasted and press command-v.\n\n"); + list_window.addText("* Highlight the information to be copied in this window.\n"); + list_window.addText(" Then copy it by pressing control-c. Next click in the area\n"); + list_window.addText(" into which it is to be pasted and press control-v.\n\n"); + list_window.addText("* Highlight the information to be copied in this window.\n"); + list_window.addText(" then drag it into the window into which it is to be pasted.\n\n"); + + if (cas_sw == 0) // Format for Maple + { + list_window.addText("n := "); + list_window.addText(String.valueOf(save_index)); + list_window.addText(";\n\n"); + list_window.addText("x := ["); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(String.valueOf(save_x[i])); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/ 10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("];\n\n"); + list_window.addText("y := ["); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(String.valueOf(save_y[i])); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/ 10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("];\n\n"); + if (polar_sw > 0) + { + list_window.addText("r := ["); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(save_distance[i]); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("];\n\n"); + list_window.addText("t := ["); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(save_angle[i]); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("];\n\n"); + } + } + else + if (cas_sw == 1) // Format for Mathematica + { + list_window.addText("n := "); + list_window.addText(String.valueOf(save_index)); + list_window.addText("\n\n"); + list_window.addText("x := {"); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(String.valueOf(save_x[i])); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/ 10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("}\n\n"); + list_window.addText("y := {"); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(String.valueOf(save_y[i])); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/ 10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("}\n\n"); + if (polar_sw > 0) + { + list_window.addText("r := {"); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(save_distance[i]); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("}\n\n"); + list_window.addText("t := {"); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(save_angle[i]); + if (i < save_index - 1) + { + list_window.addText(", "); + if (i == 10 * (i/10) + 9) + list_window.addText("\n "); + } + } + list_window.addText("}\n\n"); + } + } + if (cas_sw == 2) // Format for Excel + { + if (polar_sw == 0) + list_window.addText("x-coordinate \t y-coordinate\n\n"); + if (polar_sw == 1) + list_window.addText("x-coordinate \t y-coordinate \t distance \t angle (radians)\n\n"); + if (polar_sw == 2) + list_window.addText("x-coordinate \t y-coordinate \t distance \t angle (degrees)\n\n"); + for (int i = 0; i < save_index; i = i + 1) + { + list_window.addText(leftfill(6, String.valueOf(save_x[i]))); + list_window.addText("\t "); + list_window.addText(leftfill(6, String.valueOf(save_y[i]))); + if (polar_sw > 0) + list_window.addText("\t" + save_distance[i] + "\t" + save_angle[i]); + list_window.addText("\n"); + } + } + list_window.pack(); + list_window.show(); + } + return true; + } + + public void tell_error(String message) + { + message_window = new DataDisplayFrame("Expression Error"); + message_window.hide(); + message_window.clearText(); + message_window.addText("An error has been detected in an algebraic expression.\n\n"); + message_window.addText(message); + message_window.addText("\n\n"); + message_window.addText(recovery_message_line_1); + message_window.addText("\n"); + message_window.addText(recovery_message_line_2); + message_window.addText("\n"); + message_window.pack(); + message_window.show(); + return; + } + + // + // Used to right justify output + // + + public String leftfill(int n, String str) + { + int k; + String body; + body = str; + k = body.length(); + body = " "; + body = body.substring(0, n - k) + str; + return body; + } +} + +// +// The following is used to display information in a form that +// can be imported into another program by cut-and-paste +// + +class DataDisplayFrame extends Frame +{ + final static Font textfont = new Font("Courier", Font.PLAIN, 12); + + TextArea txt = new TextArea(); + + public DataDisplayFrame (String title) + { + super(title); + txt.setEditable(true); + txt.setFont(textfont); + add(txt); + } + + public void addText(String text) + { + txt.setText(txt.getText() + text); + } + + public void clearText() + { + txt.setText(""); + } + + public boolean handleEvent(Event evt) + { + if (evt.id == Event.WINDOW_DESTROY) + dispose(); + return super.handleEvent(evt); + } +} + +// +// The following were written by Frank Wattenberg +// + +class VCRButton +{ + // + // On-off button + // Click to turn on + // + + public int left; + public int top; + public int width; + public int height; + public String label; + public Color button_color, back_color, shadow_color; + + public VCRButton(int left, int top, int width, int height, + Color button_color, Color back_color, Color shadow_color, String label) + { + this.left = left; + this.top = top; + this.width = width; + this.height = height; + this.button_color = button_color; + this.back_color = back_color; + this.shadow_color = shadow_color; + this.label = label; + } + + public void draw_on(Graphics g) + { + g.setColor(back_color); + g.fillRect(left, top, width, height); + g.setColor(button_color); + g.fillRect(left + 3, top + 3, width - 3, height - 3); + g.setColor(shadow_color); + g.drawString(label, left + width/2 - g.getFontMetrics().stringWidth(label)/2, top + height + height/2); + } + + public void draw_off(Graphics g) + { + g.setColor(back_color); + g.fillRect(left, top, width, height); + g.setColor(shadow_color); + g.fillRect(left + 3, top + 3, width - 3, height - 3); + g.setColor(button_color); + g.fillRect(left, top, width - 3, height - 3); + g.setColor(shadow_color); + g.drawString(label, left + width/2 - g.getFontMetrics().stringWidth(label)/2, top + height + height/2); + } + + public boolean test(int mx, int my) + { + if ((mx >= left) & (mx <= left + width) & + (my >= top) & (my <= top + height)) + return true; + else + return false; + } +} + +// +// The following was written by Darius Bacon +// + +abstract class Expr { + + /** @return the value given the current variable values */ + public abstract double value(); + + /** Binary operator: addition */ public static final int ADD = 0; + /** Binary operator: subtraction */ public static final int SUB = 1; + /** Binary operator: multiplication */ public static final int MUL = 2; + /** Binary operator: division */ public static final int DIV = 3; + /** Binary operator: exponentiation */ public static final int POW = 4; + /** Binary operator: arctangent */ public static final int ATAN2 = 5; + /** Binary operator: maximum */ public static final int MAX = 6; + /** Binary operator: minimum */ public static final int MIN = 7; + /** Binary operator: less than */ public static final int LT = 8; + /** Binary operator: less or equal */ public static final int LE = 9; + /** Binary operator: equality */ public static final int EQ = 10; + /** Binary operator: inequality */ public static final int NE = 11; + /** Binary operator: greater or equal*/ public static final int GE = 12; + /** Binary operator: greater than */ public static final int GT = 13; + /** Binary operator: logical and */ public static final int AND = 14; + /** Binary operator: logical or */ public static final int OR = 15; + + /** Unary operator: absolute value*/ public static final int ABS = 100; + /** Unary operator: arccosine */ public static final int ACOS = 101; + /** Unary operator: arcsine */ public static final int ASIN = 102; + /** Unary operator: arctangent*/ public static final int ATAN = 103; + /** Unary operator: ceiling */ public static final int CEIL = 104; + /** Unary operator: cosine */ public static final int COS = 105; + /** Unary operator: e to the x*/ public static final int EXP = 106; + /** Unary operator: floor */ public static final int FLOOR = 107; + /** Unary operator: natural log*/ public static final int LOG = 108; + /** Unary operator: negation */ public static final int NEG = 109; + /** Unary operator: rounding */ public static final int ROUND = 110; + /** Unary operator: sine */ public static final int SIN = 111; + /** Unary operator: square root */ public static final int SQRT = 112; + /** Unary operator: tangent */ public static final int TAN = 113; + + /** Make a literal expression. + * @param v the constant value of the expression + * @return an expression whose value is always v */ + public static Expr makeLiteral(double v) { + return new LiteralExpr(v); + } + /** Make an expression that applies a unary operator to an operand. + * @param rator a code for a unary operator + * @param rand operand + * @return an expression meaning rator(rand) + */ + public static Expr makeApp1(int rator, Expr rand) { + Expr app = new UnaryExpr(rator, rand); + return rand instanceof LiteralExpr + ? new LiteralExpr(app.value()) + : app; + } + /** Make an expression that applies a binary operator to two operands. + * @param rator a code for a binary operator + * @param rand0 left operand + * @param rand1 right operand + * @return an expression meaning rator(rand0, rand1) + */ + public static Expr makeApp2(int rator, Expr rand0, Expr rand1) { + Expr app = new BinaryExpr(rator, rand0, rand1); + return rand0 instanceof LiteralExpr && rand1 instanceof LiteralExpr + ? new LiteralExpr(app.value()) + : app; + } + /** Make a conditional expression. + * @param test `if' part + * @param consequent `then' part + * @param alternative `else' part + * @return an expression meaning `if test, then consequent, else + * alternative' + */ + public static Expr makeIfThenElse(Expr test, + Expr consequent, + Expr alternative) { + Expr cond = new ConditionalExpr(test, consequent, alternative); + if (test instanceof LiteralExpr && + consequent instanceof LiteralExpr && + alternative instanceof LiteralExpr) + return new LiteralExpr(cond.value()); + else + return cond; + } +} + +// These classes are all private to this module so that I can get rid +// of them later. For applets you want to use as few classes as +// possible to avoid http connections at load time; it'd be profitable +// to replace all these subtypes with bytecodes for a stack machine, +// or perhaps a type that's the union of all of them (see class Node +// in java/demo/SpreadSheet/SpreadSheet.java). + +class LiteralExpr extends Expr { + double v; + LiteralExpr(double v) { this.v = v; } + public double value() { return v; } +} + +class UnaryExpr extends Expr { + int rator; + Expr rand; + + UnaryExpr(int rator, Expr rand) { + this.rator = rator; + this.rand = rand; + } + + public double value() { + double arg = rand.value(); + switch (rator) { + case ABS: return Math.abs(arg); + case ACOS: return Math.acos(arg); + case ASIN: return Math.asin(arg); + case ATAN: return Math.atan(arg); + case CEIL: return Math.ceil(arg); + case COS: return Math.cos(arg); + case EXP: return Math.exp(arg); + case FLOOR: return Math.floor(arg); + case LOG: return Math.log(arg); + case NEG: return -arg; + case ROUND: return Math.round(arg); + case SIN: return Math.sin(arg); + case SQRT: return Math.sqrt(arg); + case TAN: return Math.tan(arg); + default: throw new RuntimeException("BUG: bad rator"); + } + } +} + +class BinaryExpr extends Expr { + int rator; + Expr rand0, rand1; + + BinaryExpr(int rator, Expr rand0, Expr rand1) { + this.rator = rator; + this.rand0 = rand0; + this.rand1 = rand1; + } + public double value() { + double arg0 = rand0.value(); + double arg1 = rand1.value(); + switch (rator) { + case ADD: return arg0 + arg1; + case SUB: return arg0 - arg1; + case MUL: return arg0 * arg1; + case DIV: return arg0 / arg1; // division by 0 has IEEE 754 behavior + case POW: return Math.pow(arg0, arg1); + case ATAN2: return Math.atan2(arg0, arg1); + case MAX: return arg0 < arg1 ? arg1 : arg0; + case MIN: return arg0 < arg1 ? arg0 : arg1; + case LT: return arg0 < arg1 ? 1.0 : 0.0; + case LE: return arg0 <= arg1 ? 1.0 : 0.0; + case EQ: return arg0 == arg1 ? 1.0 : 0.0; + case NE: return arg0 != arg1 ? 1.0 : 0.0; + case GE: return arg0 >= arg1 ? 1.0 : 0.0; + case GT: return arg0 > arg1 ? 1.0 : 0.0; + case AND: return arg0 != 0 && arg1 != 0 ? 1.0 : 0.0; + case OR: return arg0 != 0 || arg1 != 0 ? 1.0 : 0.0; + default: throw new RuntimeException("BUG: bad rator"); + } + } +} + +class ConditionalExpr extends Expr { + Expr test, consequent, alternative; + + ConditionalExpr(Expr test, Expr consequent, Expr alternative) { + this.test = test; + this.consequent = consequent; + this.alternative = alternative; + } + + public double value() { + return test.value() != 0 ? consequent.value() : alternative.value(); + } +} + +// Operator-precedence parser. +// Copyright 1996 by Darius Bacon; see the file COPYING. + +/** + Parses strings representing mathematical formulas with variables. + The following operators, in descending order of precedence, are + defined: + +
    +
  • ^ (raise to a power) +
  • * / +
  • Unary minus (-x) +
  • + - +
  • < <= = <> >= > +
  • and +
  • or +
+ + ^ associates right-to-left; other operators associate left-to-right. + +

These functions are defined: + abs, acos, asin, atan, + ceil, cos, exp, floor, + log, round, sin, sqrt, + tan. Each requires one argument enclosed in parentheses. + +

There are also binary functions: atan2, min, max; and a ternary + conditional function: if(test, then, else). + +

Whitespace outside identifiers is ignored. + +

Examples: +

    +
  • 42 +
  • 2-3 +
  • cos(x^2) + sin(x^2) +
      */ + +class Parser { + + // Built-in constants + static private Variable pi = Variable.make("pi"); + static { + pi.setValue(Math.PI); + } + + /** Return the expression denoted by the input string. + * + * @param input the unparsed expression + * @exception SyntaxException if the input is unparsable */ + static public Expr parse(String input) throws SyntaxException { + return new Parser().parseString(input); + } + + /** Set of Variable's that are allowed to appear in input expressions. + * If null, any Variable is allowed. */ + private Hashtable allowedVariables = null; + + public void allow(Variable variable) { + if (null == allowedVariables) { + allowedVariables = new Hashtable(); + allowedVariables.put(pi, pi); + } + allowedVariables.put(variable, variable); + } + + Scanner tokens = null; + private Token token = null; + + /** Return the expression denoted by the input string. + * + * @param input the unparsed expression + * @exception SyntaxException if the input is unparsable */ + public Expr parseString(String input) throws SyntaxException { + String operatorChars = "+-*/^<>=,"; + tokens = new Scanner(input, operatorChars); + return reparse(); + } + + private Expr reparse() throws SyntaxException { + tokens.index = -1; + nextToken(); + Expr expr = parseExpr(0); + if (token.ttype != Token.TT_EOF) + throw error("Incomplete expression", + SyntaxException.INCOMPLETE, null); + return expr; + } + + private void nextToken() { + token = tokens.nextToken(); + } + + private Expr parseExpr(int precedence) throws SyntaxException { + Expr expr = parseFactor(); + loop: + for (;;) { + int l, r, rator; + + // The operator precedence table. + // l = left precedence, r = right precedence, rator = operator. + // Higher precedence values mean tighter binding of arguments. + // To associate left-to-right, let r = l+1; + // to associate right-to-left, let r = l. + + switch (token.ttype) { + + case '<': l = 20; r = 21; rator = Expr.LT; break; + case Token.TT_LE: l = 20; r = 21; rator = Expr.LE; break; + case '=': l = 20; r = 21; rator = Expr.EQ; break; + case Token.TT_NE: l = 20; r = 21; rator = Expr.NE; break; + case Token.TT_GE: l = 20; r = 21; rator = Expr.GE; break; + case '>': l = 20; r = 21; rator = Expr.GT; break; + + case '+': l = 30; r = 31; rator = Expr.ADD; break; + case '-': l = 30; r = 31; rator = Expr.SUB; break; + + case '/': l = 40; r = 41; rator = Expr.DIV; break; + case '*': l = 40; r = 41; rator = Expr.MUL; break; + + case '^': l = 50; r = 50; rator = Expr.POW; break; + + default: + if (token.ttype == Token.TT_WORD && token.sval.equals("and")) { + l = 5; r = 6; rator = Expr.AND; break; + } + if (token.ttype == Token.TT_WORD && token.sval.equals("or")) { + l = 10; r = 11; rator = Expr.OR; break; + } + break loop; + } + + if (l < precedence) + break loop; + + nextToken(); + expr = Expr.makeApp2(rator, expr, parseExpr(r)); + } + return expr; + } + + static private final String[] procs1 = { + "abs", "acos", "asin", "atan", + "ceil", "cos", "exp", "floor", + "log", "round", "sin", "sqrt", + "tan" + }; + static private final int[] rators1 = { + Expr.ABS, Expr.ACOS, Expr.ASIN, Expr.ATAN, + Expr.CEIL, Expr.COS, Expr.EXP, Expr.FLOOR, + Expr.LOG, Expr.ROUND, Expr.SIN, Expr.SQRT, + Expr.TAN + }; + + static private final String[] procs2 = { + "atan2", "max", "min" + }; + static private final int[] rators2 = { + Expr.ATAN2, Expr.MAX, Expr.MIN + }; + + private boolean atStartOfFactor() { + return token.ttype == Token.TT_NUMBER + || token.ttype == Token.TT_WORD + || token.ttype == '(' + || token.ttype == '-'; + } + + private Expr parseFactor() throws SyntaxException { + switch (token.ttype) { + case Token.TT_NUMBER: { + Expr lit = Expr.makeLiteral(token.nval); + nextToken(); + return lit; + } + case Token.TT_WORD: { + for (int i = 0; i < procs1.length; ++i) + if (procs1[i].equals(token.sval)) { + nextToken(); + expect('('); + Expr rand = parseExpr(0); + expect(')'); + return Expr.makeApp1(rators1[i], rand); + } + + for (int i = 0; i < procs2.length; ++i) + if (procs2[i].equals(token.sval)) { + nextToken(); + expect('('); + Expr rand1 = parseExpr(0); + expect(','); + Expr rand2 = parseExpr(0); + expect(')'); + return Expr.makeApp2(rators2[i], rand1, rand2); + } + + if (token.sval.equals("if")) { + nextToken(); + expect('('); + Expr test = parseExpr(0); + expect(','); + Expr consequent = parseExpr(0); + expect(','); + Expr alternative = parseExpr(0); + expect(')'); + return Expr.makeIfThenElse(test, consequent, alternative); + } + + Expr var = Variable.make(token.sval); + if (null != allowedVariables && null == allowedVariables.get(var)) + throw error("Unknown variable", + SyntaxException.UNKNOWN_VARIABLE, null); + nextToken(); + return var; + } + case '(': { + nextToken(); + Expr enclosed = parseExpr(0); + expect(')'); + return enclosed; + } + case '-': + nextToken(); + return Expr.makeApp1(Expr.NEG, parseExpr(35)); + case Token.TT_EOF: + throw error("Expected a factor", + SyntaxException.PREMATURE_EOF, null); + default: + throw error("Expected a factor", + SyntaxException.BAD_FACTOR, null); + } + } + + private SyntaxException error(String complaint, + int reason, + String expected) { + return new SyntaxException(complaint, this, reason, expected); + } + + private void expect(int ttype) throws SyntaxException { + if (token.ttype != ttype) + throw error("'" + (char)ttype + "' expected", + SyntaxException.EXPECTED, "" + (char)ttype); + nextToken(); + } + + + // Error correction + + boolean tryCorrections() { + return tryInsertions() || tryDeletions() || trySubstitutions(); + } + + private boolean tryInsertions() { + Vector v = tokens.tokens; + for (int i = tokens.index; 0 <= i; --i) { + Token t; + if (i < v.size()) { + t = (Token) v.elementAt(i); + } else { + String s = tokens.getInput(); + t = new Token(Token.TT_EOF, 0, s, s.length(), s.length()); + } + Token[] candidates = possibleInsertions(t); + for (int j = 0; j < candidates.length; ++j) { + v.insertElementAt(candidates[j], i); + try { + reparse(); + return true; + } catch (SyntaxException se) { + v.removeElementAt(i); + } + } + } + return false; + } + + private boolean tryDeletions() { + Vector v = tokens.tokens; + for (int i = tokens.index; 0 <= i; --i) { + if (v.size() <= i) + continue; + Object t = v.elementAt(i); + v.remove(i); + try { + reparse(); + return true; + } catch (SyntaxException se) { + v.insertElementAt(t, i); + } + } + return false; + } + + private boolean trySubstitutions() { + Vector v = tokens.tokens; + for (int i = tokens.index; 0 <= i; --i) { + if (v.size() <= i) + continue; + Token t = (Token) v.elementAt(i); + Token[] candidates = possibleSubstitutions(t); + for (int j = 0; j < candidates.length; ++j) { + v.setElementAt(candidates[j], i); + try { + reparse(); + return true; + } catch (SyntaxException se) { } + } + v.setElementAt(t, i); + } + return false; + } + + private Token[] possibleInsertions(Token t) { + String ops = tokens.getOperatorChars(); + Token[] ts = + new Token[ops.length() + 6 + procs1.length + procs2.length]; + int i = 0; + + Token one = new Token(Token.TT_NUMBER, 1, "1", t); + ts[i++] = one; + + for (int j = 0; j < ops.length(); ++j) + ts[i++] = new Token(ops.charAt(j), 0, "" + ops.charAt(j), t); + + ts[i++] = new Token(Token.TT_WORD, 0, "x", t); + + for (int k = 0; k < procs1.length; ++k) + ts[i++] = new Token(Token.TT_WORD, 0, procs1[k], t); + + for (int m = 0; m < procs2.length; ++m) + ts[i++] = new Token(Token.TT_WORD, 0, procs2[m], t); + + ts[i++] = new Token(Token.TT_LE, 0, "<=", t); + ts[i++] = new Token(Token.TT_NE, 0, "<>", t); + ts[i++] = new Token(Token.TT_GE, 0, ">=", t); + ts[i++] = new Token(Token.TT_WORD, 0, "if", t); + + return ts; + } + + private Token[] possibleSubstitutions(Token t) { + return possibleInsertions(t); + } +} + +class Scanner { + + private String s; + private String operatorChars; + + Vector tokens = new Vector(); + int index = -1; + + public Scanner(String string, String operatorChars) { + this.s = string; + this.operatorChars = operatorChars + "()"; + + int i = 0; + do { + i = scanToken(i); + } while (i < s.length()); + } + + public String getInput() { + return s; + } + + public String getOperatorChars() { + return operatorChars; + } + + // The tokens may have been diddled, so this can be different from + // getInput(). + public String toString() { + StringBuffer sb = new StringBuffer(); + int whitespace = 0; + for (int i = 0; i < tokens.size(); ++i) { + Token t = (Token) tokens.elementAt(i); + + int spaces = (whitespace != 0 ? whitespace : t.leadingWhitespace); + if (i == 0) + spaces = 0; + else if (spaces == 0 && !joinable((Token) tokens.elementAt(i-1), t)) + spaces = 1; + for (int j = spaces; 0 < j; --j) + sb.append(" "); + + sb.append(t.sval); + whitespace = t.trailingWhitespace; + } + return sb.toString(); + } + + private boolean joinable(Token s, Token t) { + return !(isAlphanumeric(s) && isAlphanumeric(t)); + } + + private boolean isAlphanumeric(Token t) { + return t.ttype == Token.TT_WORD || t.ttype == Token.TT_NUMBER; + } + + public boolean isEmpty() { + return tokens.size() == 0; + } + + public boolean atStart() { + return index <= 0; + } + + public boolean atEnd() { + return tokens.size() <= index; + } + + public Token nextToken() { + ++index; + return getCurrentToken(); + } + + public Token getCurrentToken() { + if (atEnd()) + return new Token(Token.TT_EOF, 0, s, s.length(), s.length()); + return (Token) tokens.elementAt(index); + } + + private int scanToken(int i) { + while (i < s.length() && Character.isWhitespace(s.charAt(i))) + ++i; + + if (i == s.length()) { + return i; + } else if (0 <= operatorChars.indexOf(s.charAt(i))) { + if (i+1 < s.length()) { + String pair = s.substring(i, i+2); + int ttype = 0; + if (pair.equals("<=")) + ttype = Token.TT_LE; + else if (pair.equals(">=")) + ttype = Token.TT_GE; + else if (pair.equals("<>")) + ttype = Token.TT_NE; + if (0 != ttype) { + tokens.addElement(new Token(ttype, 0, s, i, i+2)); + return i+2; + } + } + tokens.addElement(new Token(s.charAt(i), 0, s, i, i+1)); + return i+1; + } else if (Character.isLetter(s.charAt(i))) { + return scanSymbol(i); + } else if (Character.isDigit(s.charAt(i)) || '.' == s.charAt(i)) { + return scanNumber(i); + } else { + tokens.addElement(makeErrorToken("Unknown lexeme", i, i+1)); + return i+1; + } + } + + private int scanSymbol(int i) { + int from = i; + while (i < s.length() + && (Character.isLetter(s.charAt(i)) + || Character.isDigit(s.charAt(i)))) + ++i; + tokens.addElement(new Token(Token.TT_WORD, 0, s, from, i)); + return i; + } + + private int scanNumber(int i) { + int from = i; + + // We include letters in our purview because otherwise we'd + // accept a word following with no intervening space. + for (; i < s.length(); ++i) + if ('.' != s.charAt(i) + && !Character.isDigit(s.charAt(i)) + && !Character.isLetter(s.charAt(i))) + break; + + String text = s.substring(from, i); + double nval; + try { + nval = Double.valueOf(text).doubleValue(); + } catch (NumberFormatException nfe) { + tokens.addElement(makeErrorToken("Not a number", from, i)); + return i; + } + + tokens.addElement(new Token(Token.TT_NUMBER, nval, s, from, i)); + return i; + } + + private Token makeErrorToken(String complaint, int from, int i) { + // TODO: incorporate the complaint somehow + return new Token(Token.TT_ERROR, 0, s, from, i); + } +} + +/** + * An exception indicating a problem in parsing an expression. It can + * produce a short, cryptic error message (with getMessage()) or a + * long, hopefully helpful one (with explain()). + */ + +class SyntaxException extends Exception { + + /** An error code meaning the input string had stuff left over. */ + public static final int INCOMPLETE = 0; + + /** An error code meaning the parser ran into a non-value token + (like "/") at a point it was expecting a value (like "42" or + "x^2"). */ + public static final int BAD_FACTOR = 1; + + /** An error code meaning the parser hit the end of its input + before it had parsed a full expression. */ + public static final int PREMATURE_EOF = 2; + + /** An error code meaning the parser hit an unexpected token at a + point it expected to see some particular other token. */ + public static final int EXPECTED = 3; + + /** An error code meaning the expression includes a variable not + on the `approved' list. */ + public static final int UNKNOWN_VARIABLE = 4; + + /** Make a new instance. + * @param complaint short error message + * @param parser the parser that hit this snag + * @param reason one of the error codes defined in this class + * @param expected if nonnull, the token the parser expected to + * see (in place of the erroneous token it did see) + */ + public SyntaxException(String complaint, + Parser parser, + int reason, + String expected) { + super(complaint); + this.reason = reason; + this.parser = parser; + this.scanner = parser.tokens; + this.expected = expected; + } + + /** Give a long, hopefully helpful error message. + * @returns the message */ + public String explain() { + StringBuffer sb = new StringBuffer(); + + sb.append("I don't understand your formula "); + quotify(sb, scanner.getInput()); + sb.append(".\n\n"); + + explainWhere(sb); + explainWhy(sb); + explainWhat(sb); + + return sb.toString(); + } + + private Parser parser; + private Scanner scanner; + + private int reason; + private String expected; + + private String fixedInput = ""; + + private void explainWhere(StringBuffer sb) { + if (scanner.isEmpty()) { + sb.append("It's empty!\n"); + } else if (scanner.atStart()) { + sb.append("It starts with "); + quotify(sb, theToken()); + if (isLegalToken()) + sb.append(", which can never be the start of a formula.\n"); + else + sb.append(", which is a meaningless symbol to me.\n"); + } else { + sb.append("I got as far as "); + quotify(sb, asFarAs()); + sb.append(" and then "); + if (scanner.atEnd()) { + sb.append("reached the end unexpectedly.\n"); + } else { + sb.append("saw "); + quotify(sb, theToken()); + if (isLegalToken()) + sb.append(".\n"); + else + sb.append(", which is a meaningless symbol to me.\n"); + } + } + } + + private void explainWhy(StringBuffer sb) { + switch (reason) { + case INCOMPLETE: + if (isLegalToken()) + sb.append("The first part makes sense, but I don't see " + + "how the rest connects to it.\n"); + break; + case BAD_FACTOR: + case PREMATURE_EOF: + sb.append("I expected a value"); + if (!scanner.atStart()) sb.append(" to follow"); + sb.append(", instead.\n"); + break; + case EXPECTED: + sb.append("I expected "); + quotify(sb, expected); + sb.append(" at that point, instead.\n"); + break; + case UNKNOWN_VARIABLE: + sb.append("That variable has no value.\n"); + break; + default: + throw new Error("Can't happen"); + } + } + + private void explainWhat(StringBuffer sb) { + fixedInput = tryToFix(); + if (null != fixedInput) { + sb.append("An example of a formula I can parse is "); + quotify(sb, fixedInput); + sb.append(".\n"); + } + } + + private String tryToFix() { + return (parser.tryCorrections() ? scanner.toString() : null); + } + + private void quotify(StringBuffer sb, String s) { + sb.append('"'); + sb.append(s); + sb.append('"'); + } + + private String asFarAs() { + Token t = scanner.getCurrentToken(); + int point = t.location - t.leadingWhitespace; + return scanner.getInput().substring(0, point); + } + + private String theToken() { + return scanner.getCurrentToken().sval; + } + + private boolean isLegalToken() { + Token t = scanner.getCurrentToken(); + return t.ttype != Token.TT_EOF + && t.ttype != Token.TT_ERROR; + } +} + +class Token { + public static final int TT_ERROR = -1; + public static final int TT_EOF = -2; + public static final int TT_NUMBER = -3; + public static final int TT_WORD = -4; + public static final int TT_LE = -5; + public static final int TT_NE = -6; + public static final int TT_GE = -7; + + public Token(int ttype, double nval, String input, int start, int end) { + this.ttype = ttype; + this.sval = input.substring(start, end); + this.nval = nval; + this.location = start; + + int count = 0; + for (int i = start-1; 0 <= i; --i) { + if (!Character.isWhitespace(input.charAt(i))) + break; + ++count; + } + this.leadingWhitespace = count; + + count = 0; + for (int i = end; i < input.length(); ++i) { + if (!Character.isWhitespace(input.charAt(i))) + break; + ++count; + } + this.trailingWhitespace = count; + } + + Token(int ttype, double nval, String sval, Token token) { + this.ttype = ttype; + this.sval = sval; + this.nval = nval; + this.location = token.location; + this.leadingWhitespace = token.leadingWhitespace; + this.trailingWhitespace = token.trailingWhitespace; + } + + public final int ttype; + public final String sval; + public final double nval; + + public final int location; + + public final int leadingWhitespace, trailingWhitespace; +} + +/** + * A variable is a simple expression with a name (like "x") and a + * changeable value. + */ +class Variable extends Expr { + private static Hashtable variables = new Hashtable(); + + /** + * Return the variable named `name'. There can be only one + * variable with the same name returned by this method; that is, + * make(s1) == make(s2) if and only if s1.equals(s2). + * @param name the variable's name + * @return the variable; create it initialized to 0 if it doesn't + * yet exist */ + static public synchronized Variable make(String name) { + Variable result = (Variable) variables.get(name); + if (result == null) + variables.put(name, result = new Variable(name)); + return result; + } + + private String name; + private double val; + + /** + * Create a new variable. + * @param name the variable's name + */ + public Variable(String name) { + this.name = name; val = 0; + } + + public String toString() { return name; } + + /** Get the value. + * @return the current value */ + public double value() { + return val; + } + /** Set the value. + * @param value the new value */ + public void setValue(double value) { + val = value; + } + + public void set_value(double value) { + val = value; + } +} + + + diff --git a/htdocs/applets/Image_and_Cursor_All/Image_and_Cursor_All.html b/htdocs/applets/Image_and_Cursor_All/Image_and_Cursor_All.html new file mode 100644 index 0000000000..b2c841fa1d --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/Image_and_Cursor_All.html @@ -0,0 +1,22 @@ + + + + Image and Cursor Java Applets + + + +

      Image and Cursor Applets

      + + + + diff --git a/htdocs/applets/Image_and_Cursor_All/Literal.class b/htdocs/applets/Image_and_Cursor_All/Literal.class new file mode 100644 index 0000000000..d1262e2ad5 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Literal.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/LiteralExpr.class b/htdocs/applets/Image_and_Cursor_All/LiteralExpr.class new file mode 100644 index 0000000000..c798bb2b80 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/LiteralExpr.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/MiscFunctions.class b/htdocs/applets/Image_and_Cursor_All/MiscFunctions.class new file mode 100644 index 0000000000..d688266b66 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/MiscFunctions.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Parser.class b/htdocs/applets/Image_and_Cursor_All/Parser.class new file mode 100644 index 0000000000..9c53796441 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Parser.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Scanner.class b/htdocs/applets/Image_and_Cursor_All/Scanner.class new file mode 100644 index 0000000000..934b8e463e Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Scanner.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/SyntaxException.class b/htdocs/applets/Image_and_Cursor_All/SyntaxException.class new file mode 100644 index 0000000000..5451b0386d Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/SyntaxException.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Syntax_error.class b/htdocs/applets/Image_and_Cursor_All/Syntax_error.class new file mode 100644 index 0000000000..3d7c14ac7a Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Syntax_error.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Token.class b/htdocs/applets/Image_and_Cursor_All/Token.class new file mode 100644 index 0000000000..c0294a0678 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Token.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/UnaryExpr.class b/htdocs/applets/Image_and_Cursor_All/UnaryExpr.class new file mode 100644 index 0000000000..7009ec1009 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/UnaryExpr.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/VCRButton.class b/htdocs/applets/Image_and_Cursor_All/VCRButton.class new file mode 100644 index 0000000000..3a29ab58e7 Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/VCRButton.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Var_ref.class b/htdocs/applets/Image_and_Cursor_All/Var_ref.class new file mode 100644 index 0000000000..1b12d21bce Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Var_ref.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/Variable.class b/htdocs/applets/Image_and_Cursor_All/Variable.class new file mode 100644 index 0000000000..3572a86dac Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/Variable.class differ diff --git a/htdocs/applets/Image_and_Cursor_All/afghanistan_borders.html b/htdocs/applets/Image_and_Cursor_All/afghanistan_borders.html new file mode 100644 index 0000000000..eaece7cfeb --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/afghanistan_borders.html @@ -0,0 +1,63 @@ + + +The Border between Afghanistan and Iran + + +

      The Border between Afghanistan and Iran

      + +
      +

      The live map below shows Afghanistan. Your mission is to use this map to estimate the length of the border between Afghanistan and Iran

      + +
      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + + + +
      +
      Using this Live Map

      + +
        + +
      • Click anyplace on the map to move the cursor to that point. + +

        + +
      • Notice the readout at the upper right gives the location of the cursor measured in pixels from the lower left corner of the map. + +

        + +
      • You can fine tune the location of the cursor by clicking on the four arrows at the left, right, top, and bottom of the map. Each click moves the cursor one pixel in the direction indicated by the arrow. + +

        + +
      • Use the scale printed at the bottom of the map to determine how to convert distances measured on the map in pixels to distances on the ground measured in miles or kilometers. + +
      • Click the blue button, labeled "Mark point," to record the coordinates of a point. Click it each time that you have moved the cursor to a new point whose coordinates you want to record. + +
      • Click the red button, labeled "Show points," to display the coordinates of the points whose coordinates you have recorded. A new window will appear with the information that you have recorded. + +
      • Click the black button, labeled "Clear points," to clear the list of recorded points. + +
      • In this new window select the coordinates of the points in the usual way using your mouse and then copy them by pressing command-C in the MacOS or CTRL-C in Windows. + +
      • Open your favorite spreadsheet and use paste to enter the coordinates that you just copied. + +
      • Use this spreadsheet to do the arithmetic needed to determine the length of the border between Afghanistan and Iran. + +
      +
      + + + + + + diff --git a/htdocs/applets/Image_and_Cursor_All/afghanistan_borders_alt.html b/htdocs/applets/Image_and_Cursor_All/afghanistan_borders_alt.html new file mode 100644 index 0000000000..73e4fd9243 --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/afghanistan_borders_alt.html @@ -0,0 +1,63 @@ + + +The Border between Afghanistan and Iran + + +

      The Border between Afghanistan and Iran

      + +
      +

      The live map below shows Afghanistan. Your mission is to use this map to estimate the length of the border between Afghanistan and Iran

      + +
      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + + + +
      +
      Using this Live Map

      + +
        + +
      • Click anyplace on the map to move the cursor to that point. + +

        + +
      • Notice the readout at the upper right gives the location of the cursor measured in pixels from the lower left corner of the map. + +

        + +
      • You can fine tune the location of the cursor by clicking on the four arrows at the left, right, top, and bottom of the map. Each click moves the cursor one pixel in the direction indicated by the arrow. + +

        + +
      • Use the scale printed at the bottom of the map to determine how to convert distances measured on the map in pixels to distances on the ground measured in miles or kilometers. + +
      • Click the blue button, labeled "Mark point," to record the coordinates of a point. Click it each time that you have moved the cursor to a new point whose coordinates you want to record. + +
      • Click the red button, labeled "Show points," to display the coordinates of the points whose coordinates you have recorded. A new window will appear with the information that you have recorded. + +
      • Click the black button, labeled "Clear points," to clear the list of recorded points. + +
      • In this new window select the coordinates of the points in the usual way using your mouse and then copy them by pressing command-C in the MacOS or CTRL-C in Windows. + +
      • Open your favorite spreadsheet and use paste to enter the coordinates that you just copied. + +
      • Use this spreadsheet to do the arithmetic needed to determine the length of the border between Afghanistan and Iran. + +
      +
      + + + + + + diff --git a/htdocs/applets/Image_and_Cursor_All/afghanistan_distances.html b/htdocs/applets/Image_and_Cursor_All/afghanistan_distances.html new file mode 100644 index 0000000000..62e1590cd1 --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/afghanistan_distances.html @@ -0,0 +1,53 @@ + + +Distances in Afghanistan + + +

      Distances in Afghanistan

      + +

      The live map below shows Afghanistan. Your mission is to use this map to determine the distance (as the crow flies) between various points in Afghanistan.

      + +
      +
      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + +
      +
      Using this Live Map

      + +

       

      +
        + +
      • Click anyplace on the map to move the cursor to that point. + +

         

        + +
      • Notice the readout at the upper right gives the location of the cursor measured in pixels from the lower left corner of the map. + +

         

        + +
      • You can fine tune the location of the cursor by clicking on the four arrows at the left, right, top, and bottom of the map. Each click moves the cursor one pixel in the direction indicated by the arrow. + +

         

        + +
      • Use the scale printed at the bottom of the map to determine how to convert distances measured on the map in pixels to distances on the ground measured in miles or kilometers. +
      +
      + +

      Question 1: How far (in miles) is it from Kandahar to Kabul?

      + +

      Question 2: How far (in miles) is it from Kabul to Mazar-e-Sharif?

      + +

      Question 3: How far (in miles) is it from Kandahar to Shindand?

      + + + + diff --git a/htdocs/applets/Image_and_Cursor_All/afghanistan_distances_alt.html b/htdocs/applets/Image_and_Cursor_All/afghanistan_distances_alt.html new file mode 100644 index 0000000000..b8c7e31431 --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/afghanistan_distances_alt.html @@ -0,0 +1,53 @@ + + +Distances in Afghanistan + + +

      Distances in Afghanistan

      + +

      The live map below shows Afghanistan. Your mission is to use this map to determine the distance (as the crow flies) between various points in Afghanistan.

      + +
      +
      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + +
      +
      Using this Live Map

      + +

       

      +
        + +
      • Click anyplace on the map to move the cursor to that point. + +

         

        + +
      • Notice the readout at the upper right gives the location of the cursor measured in pixels from the lower left corner of the map. + +

         

        + +
      • You can fine tune the location of the cursor by clicking on the four arrows at the left, right, top, and bottom of the map. Each click moves the cursor one pixel in the direction indicated by the arrow. + +

         

        + +
      • Use the scale printed at the bottom of the map to determine how to convert distances measured on the map in pixels to distances on the ground measured in miles or kilometers. +
      +
      + +

      Question 1: How far (in miles) is it from Kandahar to Kabul?

      + +

      Question 2: How far (in miles) is it from Kabul to Mazar-e-Sharif?

      + +

      Question 3: How far (in miles) is it from Kandahar to Shindand?

      + + + + diff --git a/htdocs/applets/Image_and_Cursor_All/curve_template.html b/htdocs/applets/Image_and_Cursor_All/curve_template.html new file mode 100644 index 0000000000..a137c0030a --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/curve_template.html @@ -0,0 +1,7 @@ +The Curve and the Water Fountain

      The Curve and the Water Fountain

      + +

      + + +

      + \ No newline at end of file diff --git a/htdocs/applets/Image_and_Cursor_All/curve_template_alt.html b/htdocs/applets/Image_and_Cursor_All/curve_template_alt.html new file mode 100644 index 0000000000..32d0970084 --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/curve_template_alt.html @@ -0,0 +1,7 @@ +The Curve and the Water Fountain

      The Curve and the Water Fountain

      + +

      + + +

      + \ No newline at end of file diff --git a/htdocs/applets/Image_and_Cursor_All/water_fountain.html b/htdocs/applets/Image_and_Cursor_All/water_fountain.html new file mode 100644 index 0000000000..af2094647f --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/water_fountain.html @@ -0,0 +1,102 @@ + + + + +Describe the Squirt + + + +

      Describe the Squirt

      + +

      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + +

      + +

      The photograph above shows water squirting from a water fountain. Your job is to find a function y = f(x) describing the curve formed by the squirting water. As part of describing the function f(x) you will need to specify its domain -- that is, the lowest and highest values of the variable x.

      + +

      You can click on the photograph above to find the coordinates of the point at which you clicked. You can fine tune the selected point by clicking at the arrows at the four sides of the photograph. Each click on an arrow moves the cursors one pixel in the direction indicated by the arrow.

      + +

      +You must enter three items in the form below to describe the curve formed by the squirting water. The first two items specify the domain of the function f(x) and, thus, the part of the photograph above containing the curve formed by the squirting water. The third item is an algebraic expression -- like x^3 - x + 12, for example -- that defines the function f(x) that describes the shape of the curve. WARNING: Do not type something like f(x) = x^3 - x - 12. Just type the algebraic expression, something like x^3 - x - 12.

      + +

      After you have entered the three items in the form below, click the "Try it!!" button to see the curve superimposed on the photograph.

      +

      + +
      +

      + + + + + + + + + + + +
      Low end of the domain
      High end of the domain
      The function f(x) +

      + +

      + +

      +
      + +

      After you have found a function that you are reasonably happy with that describes the curve formed by the squirting water, discuss why it does not do as good a job as you might have hoped.

      + + + \ No newline at end of file diff --git a/htdocs/applets/Image_and_Cursor_All/water_fountain_1.gif b/htdocs/applets/Image_and_Cursor_All/water_fountain_1.gif new file mode 100644 index 0000000000..e49ce8dfed Binary files /dev/null and b/htdocs/applets/Image_and_Cursor_All/water_fountain_1.gif differ diff --git a/htdocs/applets/Image_and_Cursor_All/water_fountain_alt.html b/htdocs/applets/Image_and_Cursor_All/water_fountain_alt.html new file mode 100644 index 0000000000..7d4c0e86d4 --- /dev/null +++ b/htdocs/applets/Image_and_Cursor_All/water_fountain_alt.html @@ -0,0 +1,102 @@ + + + + +Describe the Squirt + + + +

      Describe the Squirt

      + +

      + +Your browser does not support Java, so nothing is displayed. + + + + + + + + +

      + +

      The photograph above shows water squirting from a water fountain. Your job is to find a function y = f(x) describing the curve formed by the squirting water. As part of describing the function f(x) you will need to specify its domain -- that is, the lowest and highest values of the variable x.

      + +

      You can click on the photograph above to find the coordinates of the point at which you clicked. You can fine tune the selected point by clicking at the arrows at the four sides of the photograph. Each click on an arrow moves the cursors one pixel in the direction indicated by the arrow.

      + +

      +You must enter three items in the form below to describe the curve formed by the squirting water. The first two items specify the domain of the function f(x) and, thus, the part of the photograph above containing the curve formed by the squirting water. The third item is an algebraic expression -- like x^3 - x + 12, for example -- that defines the function f(x) that describes the shape of the curve. WARNING: Do not type something like f(x) = x^3 - x - 12. Just type the algebraic expression, something like x^3 - x - 12.

      + +

      After you have entered the three items in the form below, click the "Try it!!" button to see the curve superimposed on the photograph.

      +

      + +
      +

      + + + + + + + + + + + +
      Low end of the domain
      High end of the domain
      The function f(x) +

      + +

      + +

      +
      + +

      After you have found a function that you are reasonably happy with that describes the curve formed by the squirting water, discuss why it does not do as good a job as you might have hoped.

      + + + \ No newline at end of file diff --git a/htdocs/applets/PointGraph/MultiPointGraph.html b/htdocs/applets/PointGraph/MultiPointGraph.html new file mode 100644 index 0000000000..faaa38119b --- /dev/null +++ b/htdocs/applets/PointGraph/MultiPointGraph.html @@ -0,0 +1,58 @@ + + + + + + + + +

      PointGraph Flash Applet

      + +This should show a blank canvas with a red dot. +

      + + + + + + + + + + +

      + +

      MultiPointGraph Flash Applet

      + +This should show only a blank canvas. +

      + + + + + + + + + + +

      + + + + diff --git a/htdocs/applets/PointGraph/MultiPointGraph.swf b/htdocs/applets/PointGraph/MultiPointGraph.swf new file mode 100644 index 0000000000..16d7f325b5 Binary files /dev/null and b/htdocs/applets/PointGraph/MultiPointGraph.swf differ diff --git a/htdocs/applets/PointGraph/PointGraph.save b/htdocs/applets/PointGraph/PointGraph.save new file mode 100644 index 0000000000..bb0f73e166 Binary files /dev/null and b/htdocs/applets/PointGraph/PointGraph.save differ diff --git a/htdocs/applets/PointGraph/PointGraph.swf b/htdocs/applets/PointGraph/PointGraph.swf new file mode 100644 index 0000000000..bb0f73e166 Binary files /dev/null and b/htdocs/applets/PointGraph/PointGraph.swf differ diff --git a/htdocs/applets/README b/htdocs/applets/README new file mode 100644 index 0000000000..12a1fd1ef6 --- /dev/null +++ b/htdocs/applets/README @@ -0,0 +1,68 @@ + +Sources for the applets used in WeBWork demos: + +================================================= + +Java Applets: +================================================= + +xFunctions (from David Eck at Hobart and William Smith College) +http://math.hws.edu/xFunctions/ +Doesn't connect directly to WeBWorK but can be useful for students + +================================================== + +geogebra (version 3.2.40+) +This applet is updated frequently so you will not to get fresh versions at; +http://www.geogebra.org/cms/en/installers (offline installers) or +http://www.geogebra.org/cms/en/download +Information generaly about geogebra is at: http://www.geogebra.org/cms/ + +================================================== + + +Image_and_Cursor -- liteApplet from Frank Wattenberg (West Point) +http://mathdl.maa.org/images/upload_library/4/vol2/liteapplets/contents.html + +================================================== + +SketchGraph, SliderGraph, GummyGraph -- David Eck +Designed specifically to interact with webwork. +Source code for these applets is in the webwork2/htdocs/applets/source subdirectory +Uses components from this repository: +http://math.hws.edu/javamath/ +================================================= + +FlashApplets +================================================= + + +MutiPointGraph.swf, PointGraph.swf -- flash applets from Doug Ensley (Shippensburg U.) and Mike Gage (UR) +Designed for webwork. Based on other mathematics applets illustrated at: +http://www.flashandmath.com/mathlets/index.html +and http://www.flashandmath.com/index.html for tutorials on using flash. +See also http://webwork.maa.org/wiki/Simple_example for a description of the actionScript code for this flash applet + +================================================= + + +Canvas Applets +-- a version of SketchGraph in html5 and javaScript by David Gage +-- the code is in webwork2/js/sketchgraphhtml5b +-- html5 and javaScript is a relatively new technology but it is likely to become important + particularly for mobile devices such as the iPad +-- javaScript and actionScript have become fairly similar + +================================================= + +The existing applets are still mostly at the proof of concept stage. Find useful +questions to build around them or create your own applets which are more flexible or +more useful. + + +Examples of the use of thee applets are at: + +https://test.webwork.maa.org/webwork2/2010JMM_demo/ +https://test.webwork.maa.org/webwork2/2010geogebra_at_ithaca/ +https://courses.webwork.maa.org/webwork2/wikiExamples/ + diff --git a/htdocs/applets/WeBWorKAppletTest.html b/htdocs/applets/WeBWorKAppletTest.html new file mode 100644 index 0000000000..3794a1a177 --- /dev/null +++ b/htdocs/applets/WeBWorKAppletTest.html @@ -0,0 +1,54 @@ + + + + + + +WeBWorKAppletTest.html + + + + +

      WeBWorK Applet Test page

      + +This page is used to test the availability of the default Java, WeBWorK and HTML5 applets +available through WeBWorK. Use this to test whether your browser can properly interpret +these java applets. + +

      Java Applets

      + + +

      Geogebra Java Applet

      +
        +
      • Geogebra Applet webstart
      • +

        + This references the latest version of Geogebra available at http://www.geogebra.org/. Unfortunately this cannot be + used in WeBWorK questions that interact with geogebra. The security rules for the javaScript communication require that the java applet (geogebra) and + the calling page (the WeBWorK question) come from the same domain. (You can check the version number under the "Help" menu in the geogebra window.) +

        +
      • Geogebra Java Applet
      • +

        + This references the version of Geogebra available on thei WeBWorK website. (You can check the version number under the "Help" menu in the geogebra window.) + This is the version of Geogebra referenced from within most webwork questions. +

        +
      + +

      Flash Applets

      +PointGraph and MultiPointGraph Flash Applets +
      + +

      +Sources for the applets used in WeBWork demos: +README +

      + + + + + diff --git a/htdocs/applets/Xgraph/GummyGraph.class b/htdocs/applets/Xgraph/GummyGraph.class new file mode 100644 index 0000000000..8cdf647b22 Binary files /dev/null and b/htdocs/applets/Xgraph/GummyGraph.class differ diff --git a/htdocs/applets/Xgraph/GummyGraph.html b/htdocs/applets/Xgraph/GummyGraph.html new file mode 100644 index 0000000000..0a4184ca04 --- /dev/null +++ b/htdocs/applets/Xgraph/GummyGraph.html @@ -0,0 +1,20 @@ + + + + Gummy Graph + + + +

      GummyGraph

      + + + + +

      +Written by David Eck + + + + diff --git a/htdocs/applets/Xgraph/SketchGraph.html b/htdocs/applets/Xgraph/SketchGraph.html new file mode 100755 index 0000000000..1911e3ff8a --- /dev/null +++ b/htdocs/applets/Xgraph/SketchGraph.html @@ -0,0 +1,85 @@ + + + + +TrivialApplet + + + + + + + +


      + + + + + +
      + + + +
      + + + +

      + +Y-values: + + + + + +

      + +     + + + + + + + +

      + +
      + + + +

      + + + +
      + + + + + + + diff --git a/htdocs/applets/Xgraph/SketchGraph.jar b/htdocs/applets/Xgraph/SketchGraph.jar new file mode 100644 index 0000000000..53fba62f86 Binary files /dev/null and b/htdocs/applets/Xgraph/SketchGraph.jar differ diff --git a/htdocs/applets/Xgraph/SliderGraph.html b/htdocs/applets/Xgraph/SliderGraph.html new file mode 100644 index 0000000000..880e6593ce --- /dev/null +++ b/htdocs/applets/Xgraph/SliderGraph.html @@ -0,0 +1,156 @@ + + +Sample JCM Applet: SliderGraph + + + + +
      + +
      [ +prev | +main | +next +]
      + + + +
      +

      Slider Graph

      +
      +
      + +

      This applet that draws the graph of a function that can include
      +references to the "constants" a, b, and c. The values of these
      +constants can be adjusted using the sliders at the bottom of the
      +applet. The applet responds to the mouse actions click, shift-click,
      +click-and-drag, and right-click-and-drag. I've added another
      +buttons to the limit control panel. The "Equalize Axes" button
      +adjusts the x- and y-limits so that the scales on the two axes
      +are the same. +

      + +

      + +

      The following source code shows how the applet is built from
      +JCM components. Most of the comments are for features that were
      +not already covered in previous examples. +

      + +
      + +
      +
      +import java.awt.*;
      +import edu.hws.jcm.data.*;
      +import edu.hws.jcm.draw.*;
      +import edu.hws.jcm.awt.*;
      +
      +public class SliderGraph extends java.applet.Applet {
      +
      +   private DisplayCanvas canvas;
      +   
      +   public void stop() {
      +      canvas.releaseResources();
      +   }
      +   
      +   JCMPanel makeSliderPanel(VariableSlider v) {
      +         // A small utility routing that makes a JCMPanel that contains
      +         // a VariableSlider and a DisplayLabel that shows the value
      +         // of the variable associated with that slider.
      +      JCMPanel p = new JCMPanel();
      +      p.add(v, BorderLayout.CENTER);
      +      p.add( new DisplayLabel(v.getName() + " = #", new Value[] { v } ), BorderLayout.EAST);
      +      return p;
      +   }
      +   
      +
      +   public void init() {
      +   
      +      Parser parser = new Parser();
      +      Variable x = new Variable("x");
      +      parser.add(x);
      +      
      +        // Create the three VariableSliders.  In this case, the sliders have
      +        //   names.  There is also a Variable associated with each slider, 
      +        //   which has the same name.  This variable is added to the parser
      +        //   which is passed as the fourth parameter to the constructor, making
      +        //   it possible to use "a", "b", and "c" in expressions parsed by the
      +        //   parser.  Adjusting the value on a slider changes the value of the
      +        //   associated variable, and therefore changes the value of any
      +        //   expression that refers to that variable.  The second and third
      +        //   parameters to the constructor give the minimum and maximum Values
      +        //   on the slider.  Passing "null,null" uses the defaults, namely
      +        //   new Constant(-5) and new Constant(5).
      +      VariableSlider a = new VariableSlider("a",null,null,parser);
      +      VariableSlider b = new VariableSlider("b",null,null,parser);
      +      VariableSlider c = new VariableSlider("c",null,null,parser);
      +
      +      canvas = new DisplayCanvas();
      +      canvas.setHandleMouseZooms(true);
      +      canvas.add(new Panner());
      +      
      +      LimitControlPanel limits =
      +           new LimitControlPanel( LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE
      +                                    | LimitControlPanel.EQUALIZE,  false);
      +      limits.addCoords(canvas);
      +      
      +      ExpressionInput input = new ExpressionInput("a*x^2 + b*x + c", parser);
      +      Graph1D graph = new Graph1D(input.getFunction(x));
      +      
      +      ComputeButton button = new ComputeButton("Graph it!");
      +      
      +      canvas.add(new Axes());
      +      canvas.add(graph);
      +      canvas.add(new DrawBorder(Color.darkGray, 2));
      +      
      +      JCMPanel main = new JCMPanel();  // Build interface out of JCMPanels!
      +      main.setInsetGap(3);
      +      main.add(canvas, BorderLayout.CENTER);
      +      main.add(limits, BorderLayout.EAST);
      +      JCMPanel bot = new JCMPanel(5,1);
      +      main.add(bot, BorderLayout.SOUTH);
      +      
      +      bot.add(new Label("Enter a function f(x), which can use the constants a, b, and c:"));
      +      JCMPanel inputPanel = new JCMPanel();
      +      bot.add(inputPanel);      
      +      inputPanel.add(input, BorderLayout.CENTER);
      +      inputPanel.add(button, BorderLayout.EAST);
      +      
      +      bot.add( makeSliderPanel(a) );  // Create and add the sliders.
      +      bot.add( makeSliderPanel(b) );
      +      bot.add( makeSliderPanel(c) );
      +      
      +      Controller controller = main.getController();  // Set up error reporting.
      +      controller.setErrorReporter(canvas);
      +      limits.setErrorReporter(canvas);
      +
      +      main.gatherInputs();  // Set up main panel to respond to changes in input objects.
      +                            // This works since the interface is built of JCMPanels.  For
      +                            // the same reason, I don't have to add the objects the the
      +                            // controller.
      +                             
      +      button.setOnUserAction(controller);  // Set controller to respond to button.
      +
      +      setBackground(Color.lightGray);
      +      setLayout(new BorderLayout());
      +      add(main,BorderLayout.CENTER);
      +
      +   } // end init()
      +   
      +} // end class SliderGraph
      +
      +
      +
      + +
      +
      [ +prev | +main | +next +]
      + +
      + + diff --git a/htdocs/applets/Xgraph/jcm1.0-config2.jar b/htdocs/applets/Xgraph/jcm1.0-config2.jar new file mode 100644 index 0000000000..da5a76a8d6 Binary files /dev/null and b/htdocs/applets/Xgraph/jcm1.0-config2.jar differ diff --git a/htdocs/applets/geogebra_stable/README b/htdocs/applets/geogebra_stable/README new file mode 100644 index 0000000000..3045b99b71 --- /dev/null +++ b/htdocs/applets/geogebra_stable/README @@ -0,0 +1,15 @@ +Updating the geogebra_stable directory + +Currently at + +Version 3.2.46.0 December 17, 2010 + +(a little behind webstart version) + +To upgrade go to + +http://www.geogebra.org/cms/en/installers + +grab the installer APPROPRIATE FOR YOUR OPERATING SYSTEM + +unpack it and install it in geogebra_stable \ No newline at end of file diff --git a/htdocs/applets/geogebra_stable/geogebra.html b/htdocs/applets/geogebra_stable/geogebra.html new file mode 100644 index 0000000000..ed2b2cf317 --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra.html @@ -0,0 +1,25 @@ + + +

      Geogebra applet

      +
      + + + + + + + + + + + Sorry, the GeoGebra Applet could not be started. Please make sure that Java 1.4.2 (or later) is installed and activated.(click here to install Java now) + +
      + + + + + diff --git a/htdocs/applets/geogebra_stable/geogebra.jar b/htdocs/applets/geogebra_stable/geogebra.jar new file mode 100644 index 0000000000..d0586fc83e Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra.xml b/htdocs/applets/geogebra_stable/geogebra.xml new file mode 100644 index 0000000000..e613d7595c --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra.xml @@ -0,0 +1,27 @@ + + + + GeoGebra worksheet + Pracovní list GeoGebry + GeoGebra-Arbeitsblatt + Archivo GeoGebra + Feuille de travail GeoGebra + जीओ-जेबà¥à¤°à¤¾ कारà¥à¤¯-पतà¥à¤° + Foglio di lavoro GeoGebra + Planilha GeoGebra + + + + + GeoGebra tool + Nástroj GeoGebry + GeoGebra-Werkzeug + Herramienta GeoGebra + Outil GeoGebra + जीओ-जेबà¥à¤°à¤¾ साधन + Strumento GeoGebra + Ferramenta GeoGebra + + + + \ No newline at end of file diff --git a/htdocs/applets/geogebra_stable/geogebra/properties/error.properties b/htdocs/applets/geogebra_stable/geogebra/properties/error.properties new file mode 100755 index 0000000000..572d113010 --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra/properties/error.properties @@ -0,0 +1,129 @@ +AngleMustBeConstant=Every angle has to be constant. + +AppletWindowClosing=Please close the browser window to exit GeoGebra. +# not used +AssignmentDependentToFree=Free objects may not be overwritten by dependent objects. +# not used +AssignmentToDependent=Dependent objects may not be overwritten. + +AssignmentToFixed=Fixed objects may not be changed. + +ChangeDependent=Dependent objects may not be changed. + +CircularDefinition=Circular definition + +CoordinatesMustBeConstant=Coordinates have to be constant. + +DivisorMustBeConstant=Every divisor has to be constant. + +Error=Error + +ExponentMustBeConstant=Every exponent has to be constant. + +ExponentMustBeInteger=Every exponent has to be an integer. + +FileFormatNewer=Please upgrade to a newer version of GeoGebra.\nThis file format is not supported. + +FileFormatUnknown=Unknown file format + +FileNotFound=File not found + +FunctionExpected=Function expected + +HelpNotFound=Could not find online help. + +IllegalAddition=Illegal addition + +IllegalArgument=Illegal argument + +IllegalArgumentNumber=Illegal number of arguments + +IllegalAssignment=Illegal assignment + +IllegalDivision=Illegal division + +IllegalExponent=Illegal exponent + +IllegalMultiplication=Illegal multiplication + +IllegalSubtraction=Illegal subtraction + +IllegalVariable=Illegal variable + +IncompleteEquation=Incomplete equation:\nPlease enter both sides of the equation. + +InvalidEquation=Invalid equation:\nPlease enter a linear or quadratic equation in x and y. + +InvalidFunction=Invalid function:\nPlease enter an explicit function in x. + +InvalidInput=Invalid input + +isNoAngle=is not an angle + +isNoConic=is not a conic + +isNoFunction=is not a function + +isNoLength=is not a length + +isNoLine=is not a line + +isNoNumber=is not a number + +isNoPoint=is not a point + +isNoText=is not a text + +isNoVector=is not a vector + +LengthMustBeConstant=Every length has to be constant. + +LoadFileFailed=Opening file failed. + +NameUsed=This label is already in use. + +NumberExpected=Number expected + +RenameFailed=Rename failed + +ReplaceFailed=Redefinition failed + +SaveFileFailed=Saving file failed + +UndefinedVariable=Undefined variable + +UnknownCommand=Unknown command + +UnsupportedLAF=The chosen look and feel is not available on your computer. + +URLnotFound=Opening URL failed + +VectorExpected=Point or vector expected + +#V3.0 begin +IllegalBoolean=Illegal Boolean operation + +IllegalComparison=Illegal comparison + +Tool.CreationFailed=Tool could not be created. + +Tool.OutputNotDependent=Output object does not depend on input objects. + +Tool.InputNotNeeded=Input object is not needed. + +Tool.DeleteUsed=Tool is used in construction and cannot be deleted. + +Tool.CommandNameTaken=Command name is already used by another tool. + +#V3.0 end + +#V3.1 start +PasteImageFailed=Sorry - couldn't paste bitmap from the clipboard. + +IllegalComplexMultiplication=Illegal complex multiplication + +ExportJarMissing=Sorry, the geogebra_export.jar file is missing or corrupt. +#V3.1 end +# new keys below + +CAS.GeneralErrorMessage = Sorry, the input is not acceptable. \ No newline at end of file diff --git a/htdocs/applets/geogebra_stable/geogebra/properties/javaui.properties b/htdocs/applets/geogebra_stable/geogebra/properties/javaui.properties new file mode 100755 index 0000000000..e23f6f14bc --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra/properties/javaui.properties @@ -0,0 +1,57 @@ +FileChooser.acceptAllFileFilterText=All Files +FileChooser.cancelButtonText=Cancel +FileChooser.cancelButtonToolTipText=Cancel +FileChooser.deleteFileButtonText=Delete +FileChooser.deleteFileButtonToolTipText=Delete selected file +FileChooser.detailsViewActionLabelText=Details +FileChooser.detailsViewButtonAccessibleName=Details +FileChooser.detailsViewButtonToolTipText=Details +FileChooser.directoryDescriptionText=Directory +FileChooser.directoryOpenButtonText=Open +FileChooser.directoryOpenButtonToolTipText=Open selected file +FileChooser.directorysavebuttonText=Save +FileChooser.directorysavebuttonToolTipText=Save directory +FileChooser.enterFilenameLabelText=File name: +FileChooser.fileAttrHeaderText=Attributes +FileChooser.FileChooserDetailsText=Details +FileChooser.fileDateHeaderText=Date +FileChooser.fileDateHeaderText=Modified +FileChooser.fileNameHeaderText=Name +FileChooser.fileNameLabelText=File name: +FileChooser.fileSizeHeaderText=Size +FileChooser.filesLabelText=Files: +FileChooser.filesOfTypeLabelText=Files of type: +FileChooser.fileTypeHeaderText=Type +FileChooser.filterLabelText=Filter: +FileChooser.foldersLabelText=Folder: +FileChooser.helpButtonText=Help +FileChooser.helpButtonToolTipText=Help +FileChooser.homeFolderAccessibleName=Home +FileChooser.homeFolderToolTipText=Home +FileChooser.listViewActionLabelText=List +FileChooser.listViewButtonAccessibleName=List +FileChooser.listViewButtonToolTipText=List +FileChooser.lookInLabelText=Look In: +FileChooser.newFolderActionLabelText=New Folder +FileChooser.newFolderButtonText=New Folder +FileChooser.newFolderButtonToolTipText=Create new folder +FileChooser.newFolderDialogText=New Folder +FileChooser.newFolderToolTipText=Create New Folder +FileChooser.openButtonText=Open +FileChooser.openButtonToolTipText=Open +FileChooser.openDialogTitleText=Open +FileChooser.pathLabelText=Path: +FileChooser.refreshActionLabelText=Refresh +FileChooser.renameFileButtonText=Rename +FileChooser.renameFileButtonToolTipText=Rename +FileChooser.renameFileDialogText=Rename +FileChooser.saveButtonText=Save +FileChooser.saveButtonToolTipText=Save +FileChooser.saveDialogTitleText=Save +FileChooser.saveInLabelText=Save in: +FileChooser.sortMenuLabelText=Arrange Icons By +FileChooser.updateButtonText=Update +FileChooser.updateButtonToolTipText=Update +FileChooser.upFolderAccessibleName=Up +FileChooser.upFolderToolTipText=Up +FileChooser.viewMenuLabelText=View \ No newline at end of file diff --git a/htdocs/applets/geogebra_stable/geogebra/properties/menu.properties b/htdocs/applets/geogebra_stable/geogebra/properties/menu.properties new file mode 100755 index 0000000000..4d8b6d0538 --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra/properties/menu.properties @@ -0,0 +1,606 @@ +About=About + +AngleUnit=Angle Unit + +AngularBisector=Angle Bisector + +Axes=Axes + +Circle2=Circle with Center through Point + +Circle3=Circle through Three Points + +Close=Close + +Conic5=Conic through Five Points + +Conic=Conic + +ConicMenu=Conic + +DecimalPlaces=Decimal Places + +Degree=Degree + +Delete=Delete Object + +DrawingPadToClipboard=Graphics View to Clipboard + +Edit=Edit + +English=English + +Exit=Exit + +Export=Export + +FastHelp=Fast Help + +File=File + +Files=Files + +FontSize=Font Size + +Geometry=Geometry + +German=Deutsch + +GraphicsQuality=Graphics + +Grid=Grid + +Help=Help + +HighQuality=High Quality + +InputField=Input Bar + +Intersect=Intersect Two Objects + +Join=Line through Two Points + +Labels=Show or Hide Labels + +Landscape=Landscape + +Language=Language + +License=License + +LineBisector=Perpendicular Bisector + +LineMenu=Line + +Load=Open + +LookAndFeel=Look and Feel + +LowQuality=Low Quality + +Metal=Metal + +Mode=Mode + +Motif=Motif + +Move=Move + +New=New + +off=Off + +on=On + +Options=Options + +Orthogonal=Perpendicular Line + +Parallel=Parallel Line + +Point=New Point + +PointCapturing=Point Capturing + +PointMenu=Point + +Polygon=Polygon + +Portrait=Portrait + +Print=Print + +PrintPreview=Print Preview + +Radiant=Radians + +Ray=Ray through Two Points + +Redo=Redo + +Refresh=Refresh Views + +Relation=Relation between Two Objects + +Rename=Rename + +Save=Save + +SaveAs=Save As + +SaveCurrentFileQuestion=Do you want to save the current file? + +Segment=Segment between Two Points + +Tangent=Tangents + +#V2.5 changed +Text=Insert Text + +TranslateView=Move Drawing Pad + +Undo=Undo + +Vector=Vector between Two Points + +VectorMenu=Vector + +View=View + +Windows=Windows + + +#V2.5 begin +CircleArc3=Circular Arc with Center between Two Points + +CircleSector3=Circular Sector with Center between Two Points + +CircumcircleArc3=Circumcircular Arc through Three Points + +CircumcircleSector3=Circumcircular Sector through Three Points + +Semicircle=Semicircle through Two Points + +Midpoint=Midpoint or Center + +PointStyle=Point Style + +Zoom=Zoom + +Slider=Slider + +NewWindow=New Window + +Image=Insert Image + +ShowHideObject=Show / Hide Object + +ShowHideLabel=Show / Hide Label + +Window=Window + +CopyVisualStyle=Copy Visual Style + +TranslateByVector=Translate Object by Vector + +RotateByAngle=Rotate Object around Point by Angle + +MirrorAtPoint=Reflect Object about Point + +MirrorAtLine=Reflect Object about Line + +DilateFromPoint=Dilate Object from Point by Factor + +CirclePointRadius=Circle with Center and Radius + +Angle=Angle + +VectorFromPoint=Vector from Point + +Distance=Distance or Length + +MoveRotate=Rotate around Point + +ZoomIn=Zoom In + +ZoomOut=Zoom Out + +PolarDiameter=Polar or Diameter Line + +SegmentFixed=Segment with Given Length from Point + +AngleFixed=Angle with Given Size + +CloseAll=Close All +#V2.5 end + +#V2.5.1 begin +Locus=Locus +#V2.5.1 end + +#V2.6 begin +CmdList=Command List +#V2.6 end + +#V3.0 begin +RightAngleStyle=Right Angle Style + +Continuity=Continuity + +Tool=Tool + +Tools=Tools + +Toolbar=Toolbar + +Toolbar.Customize=Customize Toolbar + +Toolbar.ResetDefault=Restore Default Toolbar + +Tool.CreateNew=Create New Tool + +Tool.Manage=Manage Tools + +Tool.SelectObjects=Select objects in construction or choose from list + +Tool.CreationSuccess=New tool created successfully! + +Tool.DeleteQuestion=Do you really want to delete the selected tools? + +ToolName=Tool name + +ToolHelp=Tool help + +CommandName=Command name + +OutputObjects=Output Objects + +InputObjects=Input Objects + +NameIcon=Name & Icon + +Icon=Icon + +ShowInToolBar=Show in Toolbar + +Select=Select Object +Select.Help=Click on object to select it + +Move.Help=Drag or select objects (Esc) + +InputField.Help=Select object to copy its name to input bar + +Point.Help=Click on the drawing pad or on line, function, or curve + +Join.Help=Select two points + +Segment.Help=Select two points + +SegmentFixed.Help=Select point and enter segment length + +Ray.Help=Select starting point, then point on ray + +Polygon.Help=Select all vertices, then click first vertex again + +Parallel.Help=Select point and parallel line + +Orthogonal.Help=Select point and perpendicular line + +Intersect.Help=Select two objects or click directly on intersection + +LineBisector.Help=Select two points or one segment + +AngularBisector.Help=Select three points or two lines + +Tangent.Help=Select point or line, then circle, conic, or function + +PolarDiameter.Help=Select point or line, then circle or conic + +Circle2.Help=Select center point, then point on circle + +Circle3.Help=Select three points on circle + +Conic5.Help=Select five points on conic + +Relation.Help=Select two objects + +TranslateView.Help=Drag the drawing pad or one axis (Shift + Drag) + +ShowHideObject.Help=Select objects to hide, then switch to another tool + +ShowHideLabel.Help=Select object + +CopyVisualStyle.Help=Select one object, then click on others + +Delete.Help=Select object + +Vector.Help=Select starting point, then end point + +Text.Help=Click on the drawing pad or on point to specify position + +Image.Help=Click on the drawing pad or on point to specify position + +Midpoint.Help=Select two points, one segment, circle, or conic + +Semicircle.Help=Select two end points + +CircleArc3.Help=Select center and two points + +CircleSector3.Help=Select center and two points + +CircumcircleArc3.Help=Select three points on arc + +CircumcircleSector3.Help=Select three points on sector + +Slider.Help=Click on the drawing pad to specify position + +MirrorAtPoint.Help=Select object to reflect, then center point + +MirrorAtLine.Help=Select object to reflect, then line of reflection + +TranslateByVector.Help=Select object to translate, then vector + +RotateByAngle.Help=Select object to rotate, then center point, and enter angle + +DilateFromPoint.Help=Select object to dilate, then center point, and enter factor + +CirclePointRadius.Help=Select center point and enter radius + +Angle.Help=Select three points or two lines + +AngleFixed.Help=Select leg point, then vertex, and enter size + +VectorFromPoint.Help=Select starting point and vector + +Distance.Help=Select two points, segment, polygon, or circle + +MoveRotate.Help=Select rotation center point, then drag object + +ZoomIn.Help=Click on the drawing pad to zoom in (Mouse Wheel) + +ZoomOut.Help=Click on the drawing pad to zoom out (Mouse Wheel) + +Locus.Help=Select locus point, then point on object + +Area=Area +Area.Help=Select polygon, circle, or conic + +Slope=Slope +Slope.Help=Select line + +RegularPolygon=Regular Polygon +RegularPolygon.Help=Select two points and enter number of vertices + +Trace=Trace On / Off +Trace.Help=Select object to show / hide its trace + +Fix=Fix On / Off +Fix.Help=Select object to fix / free it + +ShowCheckBox=Check Box to Show / Hide Objects +ShowCheckBox.Help=Click on the drawing pad to specify position + +General=General + +Advanced=Advanced + +UserInterface=User Interface + +Functionality=Functionality + +EnableRightClick=Enable right click features + +ShowResetIcon=Show icon to reset construction + +ShowMenuBar=Show menubar + +ShowToolBarHelp=Show toolbar help + +ShowToolBar=Show toolbar + +ShowInputField=Show input bar + +Separator=Separator + +Labeling=Labeling + +Labeling.automatic=Automatic + +Labeling.on=All New Objects + +Labeling.off=No New Objects + +Labeling.pointsOnly=New Points Only + +Labeling.propertiesDefault=Use Properties Default + +Settings=Settings + +Settings.Save=Save Settings + +Settings.ResetDefault=Restore Default Settings + +Copy=Copy + +Paste=Paste + +Cut=Cut + +SelectAll=Select All + +Default.Set=Set as Default + +Default.Restore=Restore Default + +Properties.Basic=Basic + +Properties.Algebra=Algebra + +Properties.Style=Style + +Properties.Position=Position + +Preview=Preview + +ExpandAll=Expand All + +CollapseAll=Collapse All + +Button.Caption=Caption + +Conditions=Conditions + +Condition.ShowObject=Condition to Show Object +#V3.0 end + +#V3.1 start +Clipboard=Clipboard + +SelectCurrentLayer=Select Current Layer + +Compasses=Compass +Compasses.Help=Select segment or two points for radius, then center point + +MirrorAtCircle=Reflect Point about Circle +MirrorAtCircle.Help=Select point to reflect, then circle + +#RGB=RGB +#Swatches=Swatches +DynamicColors=Dynamic Colors + +Red=Red + +Green=Green + +Blue=Blue + +Ellipse3=Ellipse +Ellipse3.Help=Select two foci and point on ellipse + +Hyperbola3=Hyperbola +Hyperbola3.Help=Select two foci and point on hyperbola + +Parabola=Parabola +Parabola.Help=Select point and directrix + +PasteDataFromClipboard=Paste Data from Clipboard + +DoYouWantToSaveYourChanges=Do you want to save your changes? + +DontSave=Don't Save + +Cancel=Cancel + +CloseFile=Close File + +DontOverwrite=Don't Overwrite + +Overwrite=Overwrite + +DeleteTool=Delete Tool + +DontDeleteTool=Don't Delete Tool + +ClearSelection=Clear Selection + +InsertLeft=Insert Left + +InsertRight=Insert Right + +InsertAbove=Insert Above + +InsertBelow=Insert Below + +CheckboxSize=Checkbox Size + +CheckboxSize.Large=Large + +CheckboxSize.Regular=Regular + +CreateListOfPoints=Create List of Points + +CreateList=Create List + +CreateMatrix=Create Matrix + +ClearColumn=Clear Column + +ClearColumns=Clear Columns + +ClearRow=Clear Row + +ClearRows=Clear Rows + +FitLine=Best Fit Line +FitLine.Help=Select points using selection rectangle, or list of points + +Isometric=Isometric + +Bold=Bold + +Rounding=Rounding + +SelectAncestors=Select Ancestors + +SelectDescendants=Select Descendants + +RecordToSpreadsheet=Record to Spreadsheet +RecordToSpreadsheet.Help=Select object to trace, then change the construction + +CopyToInputBar=Copy to Input Bar + +EnableLabelDrags=Enable dragging of labels + +DeleteObjects=Delete Objects +## new tokens below + +SubstituteDialog=Substitute Dialog + +RecomputeAllViews=Recompute All Objects +#V3.1 end + +# keys for perspectives in menu +IgnoreDocumentPerspective=Ignore Layout of Loaded Documents + +ShowViewTitlebar=Show Title Bar of Views + +SaveCurrentPerspective=Save Current Perspective + +ManagePerspectives=Manage Perspectives + +InputOnTop=Show at the Top + +Perspective.BasicGeometry=Basic Geometry + +Perspective.Geometry=Geometry + +Perspective.AlgebraAndGraphics=Algebra & Graphics + +Perspective.TableAndGraphics=Table & Graphics +# end keys for perspectives in menu + +# keys for tool boxes in menu +MovementTools=Movement Tools + +PointTools=Point Tools + +BasicLineTools=Line Tools + +SpecialLineTools=Special Line Tools + +PolygonTools=Polygon Tools + +CircleArcTools=Circle & Arc Tools + +ConicSectionTools=Conic Section Tools + +MeasurementTools=Measurement Tools + +TransformationTools=Transformation Tools + +SpecialObjectTools=Special Object Tools + +GeneralTools=General Tools + +CustomTools=Custom Tools +# end keys for tool boxes in menu \ No newline at end of file diff --git a/htdocs/applets/geogebra_stable/geogebra/properties/plain.properties b/htdocs/applets/geogebra_stable/geogebra/properties/plain.properties new file mode 100755 index 0000000000..38619a72a5 --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra/properties/plain.properties @@ -0,0 +1,569 @@ +Algebra=Algebra + +AlgebraWindow=Algebra View + +Angle=Angle + +Angles=Angles + +AnimationStep=Increment + +ApplicationName=GeoGebra + +ApplicationURL=http://www.geogebra.org + +Apply=Apply + +Author=Author + +AuxiliaryObject=Auxiliary Object + +AuxiliaryObjects=Auxiliary Objects + +back=Back + +Cancel=Cancel + +cartesian=Cartesian + +CartesianCoords=Cartesian Coordinates + +ChooseColor=Choose a color + +ChooseObject=Choose one object + +Circle=Circle + +CircleEquation=(x - m)\u00B2 + (y - n)\u00B2 = r\u00B2 + +Color=Color + +Command=Command + +Conic=Conic + +ConicLinesEquation=(a x + b y + c) (d x + e y + f) = 0 + +Conics=Conics + +ConstructionProtocol=Construction Protocol + +ConstructionProtocolHelp=

      Using the Construction Protocol


      Keyboard

      Previous construction step
      Next construction step
      HomeStart of construction
      EndEnd of construction
      DelDelete construction step


      Mouse

      Double click rowChoose construction step
      Double click headerStart of construction
      Drag & drop rowMove construction step
      Right click rowContext menu
      + +Coordinates=Coordinates + +CreatedWith=Created with + +Date=Date + +Definition=Definition + +Delete=Delete + +dependent=dependent + +DependentObjects=Dependent Objects + +Directrix=Directrix + +DoubleClickToOpen=Double click opens application window + +DoubleLine=Double line + +DoubleLineEquation=(a x + b y + c)\u00B2 = 0 + +DrawingPad=Drawing Pad + +DynamicWorksheet=Dynamic worksheet + +Edit=Edit + +Ellipse=Ellipse + +EllipseEquation=(x - m)\u00B2 / a\u00B2 + (y - n)\u00B2 / b\u00B2 = 1 + +EmptySet=Empty set + +eps=Encapsulated Postscript + +equal=equal + +Equation=Equation + +ExplicitConicEquation=y = a x\u00b2 + b x + c + +ExplicitLineEquation=y = m x + b + +Filling=Filling + +firstAxisLength=first axis' length + +FixObject=Fix Object + +Format=Format + +forward=forward + +free=free + +FreeObjects=Free Objects + +Function=Function + +GeometricObjects=Geometric objects + +Height=Height + +Hexagon=Hexagon + +Hide=Hide + +Home=Home + +HorizontalSplit=Horizontal Splitting + +html=Webpage + +Hyperbola=Hyperbola + +HyperbolaEquation=(x - m)\u00B2 / a\u00B2 - (y - n)\u00B2 / b\u00B2 = 1 + +ImplicitConicEquation=a x\u00B2 + b xy + c y\u00B2 + d x + e y = f + +ImplicitLineEquation=a x + b y = c + +InputFieldHelp=

      Using the Input Bar

      Enter Execute input
      EscapeClear input bar
      Previous input
      Next input
      F1Help for current command


      Auto-Completion of Commands
      After typing the first two letters of a command
      GeoGebra will complete the command for you.

      Enter Accept proposed command
      Another letterAdapts proposed command
      + +InputLabel=Input + +InputLabelToolTip=Type a command + +InsertPictureOfConstruction=Insert picture of graphics view + +IntersectingLines=Intersecting lines + +jpg=JPG Format + +LaTeXFormula=LaTeX formula + +Length=Length + +Lengths=Lengths + +Line=Line + +Lines=Lines + +LineStyle=Line Style + +Midpoint=Midpoint + +Name=Name + +No.=No. + +Numeric=Number + +NumericObjects=Numeric objects + +Objects=Objects + +Open=Open + +OpenButton=Button to open application window + +OverwriteFile=Do you want to overwrite this file? + +Parabola=Parabola + +ParabolaEquation=y\u00B2 = or x\u00B2 = + +parallel=parallel + +ParallelLines=Parallel lines + +ParametricForm=Parametric Form + +Pentagon=Pentagon + +Picture=Picture + +png=Portable Network Graphics + +emf=Enhanced Metafile + +svg=Scaleable Vector Graphics + +pdf=Portable Document Format + +Point=Point + +PointOn=Point on + +Points=Points + +polar=polar + +PolarCoords=Polar Coordinates + +Polygon=Polygon + +Properties=Object Properties + +Quadrangle=Quadrilateral + +Question=Question + +Radius=Radius + +Ray=Ray + +Redefine=Redefine + +Rename=Rename + +Segment=Segment + +Show=Show + +ShowLabel=Show Label + +ShowObject=Show Object + +ShowTrace=Show Trace + +Size=Size + +StandardObject=Standard Object + +StandardView=Standard View + +StartingPoint=Starting Point + +Text=Text + +TextAfterConstruction=Text below the construction + +TextBeforeConstruction=Text above the construction + +Thickness=Line Thickness + +Title=Title + +TraceOff=Trace Off + +TraceOn=Trace On + +Triangle=Triangle + +undefined=undefined + +unequal=unequal + +Value=Value + +Vector=Vector + +Vectors=Vectors + +Width=Width + +WindowOpened=Window opened + +ZoomIn=Zoom in + +ZoomOut=Zoom out + + +#V2.5 begin +ColorfulConstructionProtocol=Colorful construction protocol + +InsertPictureOfAlgebraAndConstruction=Insert picture of algebra and graphics view + +# the translation of xAxis MUST NOT include +# any spaces, i.e. it has to be ONE SINGLE word +# e.g. wrong: xAxis=axe X +# right: xAxis=axeX +xAxis=xAxis + +# the translation of yAxis MUST NOT include +# any spaces, i.e. it has to be ONE SINGLE word +# e.g. wrong: yAxis=axe Y +# right: yAxis=axeY +yAxis=yAxis + +Arc=Arc + +Sector=Sector + +Semicircle=Semicircle + +Interval=Interval + +Slider=Slider + +min=min + +max=max + +fixed=Fixed + +horizontal=Horizontal + +vertical=Vertical + +BackgroundImage=Background Image + +CornerPoint=Corner + +allowReflexAngle=Allow Reflex Angle + +Image=Image + +AbsoluteScreenLocation=Absolute Position on Screen + +ScaleInCentimeter=Scale in cm + +dilatedByFactor=dilated by factor + +ResolutionInDPI=Resolution in dpi + +Locus=Locus + +#V2.5 final begin +allowOutlyingIntersections=Allow Outlying Intersections +AxisNumbers=Numbers +AxisUnitLabel=Unit +AxisLabel=Label +TickDistance=Distance +BackgroundColor=Background Color +#V2.5 final end +#V2.5 end + +#V2.6 begin +clockwise=clockwise +counterClockwise=counter clockwise + +Breakpoint=Breakpoint +ShowOnlyBreakpoints=Show Only Breakpoints +ConstructionProtocolNavigation=Navigation Bar for Construction Steps +ConstructionProtocolButton=Button to open construction protocol +PlayButton=Play button +Play=Play +Pause=Pause +#V2.6 end + +#V2.7 begin +Bold=Bold +Italic=Italic +#V2.7 end + +#V3.0 begin +resetConstruction=Reset construction +Decoration=Decoration +EmphasizeRightAngle=Emphasize Right Angle +TitleExportPstricks=GeoGebra to PSTricks export +GeneratePstricks=Generate PSTricks code +CopyToClipboard=Copy to Clipboard +XUnits=X units (cm) +YUnits=Y units (cm) +PictureWidth=Picture width +PictureHeight=Picture height +LatexFontSize=Document font size: +DisplayPointSymbol=Display the symbol for points +AxisTicks=Ticks + +Up=Up +Down=Down +Insert=Insert +Remove=Remove +Next=Next +Back=Back +Finish=Finish + +Quadrilateral=Quadrilateral + +Boolean=Boolean Value +FixCheckbox=Fix Checkbox +List=List +#V3.0 end + +#V3.1 begin + +forceReflexAngle=Force Reflex Angle +Layer=Layer +ExportAsWebpage=Export as Webpage +DrawingPadAsPicture=Graphics View as Picture +DynamicWorksheetAsWebpage=Dynamic Worksheet as Webpage +NameAndValue=Name & Value +Caption=Caption +Spreadsheet=Spreadsheet View +off=off +xmin=xmin +xmax=xmax +ymin=ymin +ymax=ymax +Curve=Curve + + +NewNameForA=New name for %0 +AngleOfA=Angle of %0 +AngleBetweenAB=Angle between %0, %1 +AngleBetweenABC=Angle between %0, %1, %2 +AngleBetweenABCofD=Angle between %0, %1, %2 of %3 +AngleBisectorOfAB=Angle bisector of %0, %1 +AngleBisectorOfABC=Angle bisector of %0, %1, %2 +AsymptoteToA=Asymptote to %0 +AxisOfA=Axis of %0 +FirstAxisOfA=First axis of %0 +FirstAxisLengthOfA=First axis' length of %0 +SecondAxisOfA=Second axis of %0 +SecondAxisLengthOfA=Second axis' length of %0 +CenterOfA=Center of %0 +CentroidOfA=Centroid of %0 +CircleWithCenterAandRadiusB=Circle with center %0 and radius %1 +CircleThroughABC=Circle through %0, %1, %2 +CircleThroughAwithCenterB=Circle through %0 with center %1 +ConicThroughABCDE=Conic through %0, %1, %2, %3, %4 +AthDerivativeOfB=%0th derivative of %1 +DerivativeOfA=Derivative of %0 +DiameterOfAConjugateToB=Diameter of %0 conjugate to %1 +ADilatedByFactorBfromC=%0 dilated by factor %1 from %2 +DirectionOfA=Direction of %0 +DirectrixOfA=Directrix of %0 +DistanceOfAandB=Distance of %0 and %1 +EllipseWithFociABandFirstAxisLengthC=Ellipse with foci %0, %1 and first axis' length %2 +EllipseWithFociABPassingThroughC=Ellipse with foci %0, %1 passing through %2 +HyperbolaWithFociABandFirstAxisLengthC=Hyperbola with foci %0, %1 and first axis' length %2 +HyperbolaWithFociABPassingThroughC=Hyperbola with foci %0, %1 passing through %2 +ParabolaWithFocusAandDirectrixB=Parabola with focus %0 and directrix %1 +EccentricityOfA=Eccentricity of %0 +LinearEccentricityOfA=Linear eccentricity of %0 +ExtremumOfA=Extremum of %0 +FocusOfA=Focus of %0 +FunctionAonIntervalBC=Function %0 on interval [%1, %2] +IntegralOfA=Integral of %0 +IntegralOfAfromBtoC=Integral of %0 from %1 to %2 +SegmentABofC=Segment [%0, %1] of %2 +SegmentAB=Segment [%0, %1] +LengthOfA=Length of %0 +LineBisectorAB=Bisector %0, %1 +LineBisectorOfA=Bisector %0 +LineThroughAParallelToB=Line through %0 parallel to %1 +LineThroughAPerpendicularToB=Line through %0 perpendicular to %1 +LineThroughAwithDirectionB=Line through %0 with direction %1 +RayThroughAWithDirectionB=Ray through %0 with direction %1 +MidpointOfAB=Midpoint of %0, %1 +MidpointOfA=Midpoint of %0 +VectorPerpendicularToA=Vector perpendicular to %0 +UnitVectorPerpendicularToA=Unit vector perpendicular to %0 +ParameterOfA=Parameter of %0 +PointOnA=Point on %0 +PolarLineOfARelativeToB=Polar line of %0 relative to %1 +RadiusOfA=Radius of %0 +RootOfAonIntervalBC=Root of %0 on interval [%1, %2] +RootOfAWithInitialValueB=Root of %0 with initial value %1 +IntersectionPointOfAB=Intersection point of %0, %1 +IntersectionPointOfABWithInitialValueC=Intersection point of %0, %1 with initial value %2 +RootOfA=Root of %0 +ARotatedByAngleB=%0 rotated by angle %1 +SemicircleThroughAandB=Semicircle through %0 and %1 +SlopeOfA=Slope of %0 +TangentToAParallelToB=Tangent to %0 parallel to %1 +TangentToAThroughB=Tangent to %0 through %1 +InflectionPointofA=Inflection point of %0 +VertexOfA=Vertex of %0 +TranslationOfAbyB=Translation of %0 by %1 +TranslationOfAtoB=Translation of %0 to %1 +LineThroughAB=Line through %0, %1 +RayThroughAB=Ray through %0, %1 +PointAplusB=Point %0 + %1 +UnitVectorOfA=Unit vector of %0 +AMirroredAtB=%0 mirrored at %1 +# Relation.java +AIntersectsWithB=%0 intersects with %1 +ADoesNotIntersectWithB=%0 does not intersect with %1 +AandBareParallel=%0 and %1 are parallel +AandBarePerpendicular=%0 and %1 are perpendicular +AliesOnB=%0 lies on %1 +AdoesNotLieOnB=%0 does not lie on %1 +AandBareLinearlyDependent=%0 and %1 are linearly dependent +AandBareLinearlyIndependent=%0 and %1 are linearly independent +AandBareEqual=%0 and %1 are equal +AandBareNotEqual=%0 and %1 are not equal +AisaDegenerateBranchOfB=%0 is a degenerate branch of %1 +AisAnAsymptoteToB=%0 is an asymptote to %1 +AintersectsWithBOnce=%0 intersects with %1 once +AisaTangentToB=%0 is a tangent to %1 +AintersectsWithBTwice=%0 intersects with %1 twice +ADoesNotIntersectWithB=%0 does not intersect with %1 +AisNotDefined=%0 is not defined +AhasTheSameLengthAsB=%0 has the same length as %1 +AdoesNothaveTheSameLengthAsB=%0 does not have the same length as %1 +AhasTheSameAreaAsB=%0 has the same area as %1 +AdoesNothaveTheSameAreaAsB=%0 does not have the same area as %1 +AandBcannotBeCompared=%0 and %1 cannot be compared +# Relation.java end + +AliesOnThePerimeterOfB=%0 lies on the perimeter of %1 +AdoesNotLieOnThePerimeterOfB=%0 does not lie on the perimeter of %1 +ExportTextAsShapes=Export Text as Shapes +ShowAllObjects=Show All Objects +DrawingPadAsPSTricks=Graphics View as PSTricks +#TangentToAatBCD=Tangent to %0 at %1%2%3 +#replaced with: +TangentToAatB=Tangent to %0 at %1 + +TitleExportPgf=GeoGebra to PGF Export +GeneratePgf=Generate PGF/TikZ code +DrawingPagAsPGF=Graphics View as PGF/TikZ +ForceGnuplotPgf=Use Gnuplot to plot functions +Default=Default +CellAisNotDefined=Cell %0 is not defined +2x2Matrix=2x2 matrix +3x3Matrix=3x3 matrix +ADecimalPlace=%0 Decimal Place +ADecimalPlaces=%0 Decimal Places +ASignificantFigure=%0 Significant Figure +ASignificantFigures=%0 Significant Figures +ComplexNumber=Complex Number +TraceToSpreadsheet=Trace to Spreadsheet +OK=OK +Speed=Speed +SyntaxErrorAisNotAList=Syntax Error: %0 is not a list +PGFExport.Grayscale=Grayscale +Inequality=Inequality +LinearInequality=Linear inequality +## new keys below: + +Animating=Animation On +Oscillating=Oscillating +Repeat=Repeat +Increasing=Increasing +Decreasing=Decreasing +AnimationSpeed=Speed +Animation=Animation +Substitute=Substitute +SubstituteSimplify= Substitute & Simplify +XMLTagANotFound=Invalid file: XML tag %0 not found +# for the Distance Tool +DistanceAB.LaTeX="\\overline{" + %0 + %1 + "} \\, = \\, " + %2 +#DistanceAB.LaTeX=%0+%1+" \\, = \\, "+%2 +PointSize=Point Size + +# default names for different object types +Name.list=list +Name.polygon=poly +Name.locus=loc +Name.matrix=matrix +Name.text=text +Name.picture=pic + +# TODO: add (for file menu entry) +# DeleteAll=Delete all objects + +#V3.1 end + +ViewOpenExtraWindow=Show View in New Window +ViewCloseExtraWindow=Show View in Main Window +PerspectiveName=Name of Perspective diff --git a/htdocs/applets/geogebra_stable/geogebra_applet_java2javascript.ggb b/htdocs/applets/geogebra_stable/geogebra_applet_java2javascript.ggb new file mode 100755 index 0000000000..9b64468465 Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_applet_java2javascript.ggb differ diff --git a/htdocs/applets/geogebra_stable/geogebra_blank.ggb b/htdocs/applets/geogebra_stable/geogebra_blank.ggb new file mode 100644 index 0000000000..f2dce8e84d Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_blank.ggb differ diff --git a/htdocs/applets/geogebra_stable/geogebra_blank2.ggb b/htdocs/applets/geogebra_stable/geogebra_blank2.ggb new file mode 100644 index 0000000000..50ad41949b Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_blank2.ggb differ diff --git a/htdocs/applets/geogebra_stable/geogebra_blank3.ggb b/htdocs/applets/geogebra_stable/geogebra_blank3.ggb new file mode 100644 index 0000000000..2389271ee5 Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_blank3.ggb differ diff --git a/htdocs/applets/geogebra_stable/geogebra_cas.jar b/htdocs/applets/geogebra_stable/geogebra_cas.jar new file mode 100644 index 0000000000..eee62253a7 Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_cas.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra_export.jar b/htdocs/applets/geogebra_stable/geogebra_export.jar new file mode 100644 index 0000000000..628b3bae31 Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_export.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra_gui.jar b/htdocs/applets/geogebra_stable/geogebra_gui.jar new file mode 100644 index 0000000000..6c031125dc Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_gui.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra_main.jar b/htdocs/applets/geogebra_stable/geogebra_main.jar new file mode 100644 index 0000000000..95529532ad Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_main.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra_properties.jar b/htdocs/applets/geogebra_stable/geogebra_properties.jar new file mode 100644 index 0000000000..2a3a55c0b4 Binary files /dev/null and b/htdocs/applets/geogebra_stable/geogebra_properties.jar differ diff --git a/htdocs/applets/geogebra_stable/geogebra_webstart.html b/htdocs/applets/geogebra_stable/geogebra_webstart.html new file mode 100644 index 0000000000..3e5442ea06 --- /dev/null +++ b/htdocs/applets/geogebra_stable/geogebra_webstart.html @@ -0,0 +1,68 @@ + + + + + + + diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/128x128/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/apps/geogebra.png new file mode 100644 index 0000000000..05b31e392b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..05b31e392b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..05b31e392b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/128x128/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/16x16/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/apps/geogebra.png new file mode 100644 index 0000000000..3754bfb3bd Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..3754bfb3bd Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..3754bfb3bd Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/16x16/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/192x192/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/apps/geogebra.png new file mode 100644 index 0000000000..88d1878da8 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..88d1878da8 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..88d1878da8 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/192x192/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/22x22/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/apps/geogebra.png new file mode 100644 index 0000000000..65c7197d6b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..65c7197d6b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..65c7197d6b Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/22x22/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/24x24/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/apps/geogebra.png new file mode 100644 index 0000000000..d9d45ac611 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..d9d45ac611 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..d9d45ac611 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/24x24/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/256x256/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/apps/geogebra.png new file mode 100644 index 0000000000..d86b5ff494 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..d86b5ff494 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..d86b5ff494 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/256x256/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/32x32/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/apps/geogebra.png new file mode 100644 index 0000000000..110252d6ef Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..110252d6ef Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..110252d6ef Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/32x32/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/36x36/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/apps/geogebra.png new file mode 100644 index 0000000000..31bc739a8f Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..31bc739a8f Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..31bc739a8f Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/36x36/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/48x48/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/apps/geogebra.png new file mode 100644 index 0000000000..49945ddc03 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..49945ddc03 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..49945ddc03 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/48x48/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/64x64/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/apps/geogebra.png new file mode 100644 index 0000000000..8102d45380 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..8102d45380 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..8102d45380 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/64x64/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/72x72/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/apps/geogebra.png new file mode 100644 index 0000000000..be6dc1f389 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..be6dc1f389 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..be6dc1f389 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/72x72/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/96x96/apps/geogebra.png b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/apps/geogebra.png new file mode 100644 index 0000000000..65bb9e259e Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/apps/geogebra.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.file.png b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.file.png new file mode 100644 index 0000000000..65bb9e259e Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.file.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.tool.png b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.tool.png new file mode 100644 index 0000000000..65bb9e259e Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/96x96/mimetypes/application-vnd.geogebra.tool.png differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/scalable/apps/geogebra.svgz b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/apps/geogebra.svgz new file mode 100644 index 0000000000..0b19364dd9 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/apps/geogebra.svgz differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.file.svgz b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.file.svgz new file mode 100644 index 0000000000..0b19364dd9 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.file.svgz differ diff --git a/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.tool.svgz b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.tool.svgz new file mode 100644 index 0000000000..0b19364dd9 Binary files /dev/null and b/htdocs/applets/geogebra_stable/icons/hicolor/scalable/mimetypes/application-vnd.geogebra.tool.svgz differ diff --git a/htdocs/applets/geogebra_stable/quadratictest.ggb b/htdocs/applets/geogebra_stable/quadratictest.ggb new file mode 100644 index 0000000000..ab1773b3ba Binary files /dev/null and b/htdocs/applets/geogebra_stable/quadratictest.ggb differ diff --git a/htdocs/applets/geogebra_stable/transformationstest2.ggb b/htdocs/applets/geogebra_stable/transformationstest2.ggb new file mode 100644 index 0000000000..c6d7513dca Binary files /dev/null and b/htdocs/applets/geogebra_stable/transformationstest2.ggb differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra.jar new file mode 100644 index 0000000000..32ba6f843e Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra.jar differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra_cas.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra_cas.jar new file mode 100644 index 0000000000..c093a06b37 Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra_cas.jar differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra_export.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra_export.jar new file mode 100644 index 0000000000..30eb7fb2f5 Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra_export.jar differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra_gui.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra_gui.jar new file mode 100644 index 0000000000..8f81814d49 Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra_gui.jar differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra_main.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra_main.jar new file mode 100644 index 0000000000..e93b71e2a9 Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra_main.jar differ diff --git a/htdocs/applets/geogebra_stable/unsigned/geogebra_properties.jar b/htdocs/applets/geogebra_stable/unsigned/geogebra_properties.jar new file mode 100644 index 0000000000..fdb46c561e Binary files /dev/null and b/htdocs/applets/geogebra_stable/unsigned/geogebra_properties.jar differ diff --git a/htdocs/applets/geogebra_stable/vectortest.ggb b/htdocs/applets/geogebra_stable/vectortest.ggb new file mode 100644 index 0000000000..3b6ce6d426 Binary files /dev/null and b/htdocs/applets/geogebra_stable/vectortest.ggb differ diff --git a/htdocs/applets/index.html b/htdocs/applets/index.html new file mode 120000 index 0000000000..58750e779f --- /dev/null +++ b/htdocs/applets/index.html @@ -0,0 +1 @@ +WeBWorKAppletTest.html \ No newline at end of file diff --git a/htdocs/applets/liveJar/live.jar b/htdocs/applets/liveJar/live.jar new file mode 100644 index 0000000000..231dee7abf Binary files /dev/null and b/htdocs/applets/liveJar/live.jar differ diff --git a/htdocs/applets/liveJar/liveJar.html b/htdocs/applets/liveJar/liveJar.html new file mode 100755 index 0000000000..eec665e901 --- /dev/null +++ b/htdocs/applets/liveJar/liveJar.html @@ -0,0 +1,8 @@ + + + + + + + diff --git a/htdocs/applets/liveJar/test.m b/htdocs/applets/liveJar/test.m new file mode 100755 index 0000000000..d8414bf6cf --- /dev/null +++ b/htdocs/applets/liveJar/test.m @@ -0,0 +1,2 @@ +Graphics3D[{ (* ...primitives and directives... *) }, + BoxRatios->{1,4,9} (* ...options... *)] diff --git a/htdocs/applets/live_map_instructions.html b/htdocs/applets/live_map_instructions.html new file mode 100644 index 0000000000..79fffb3b5b --- /dev/null +++ b/htdocs/applets/live_map_instructions.html @@ -0,0 +1,27 @@ + + + + Instructions for live map + + + + Using this Live

      +
        +
      • Click anyplace on the map to move the cursor to that point. + +

         

        + +
      • Notice the readout at the upper right gives the location of the cursor measured in pixels from the lower left corner of the map. + +

         

        + +
      • You can fine tune the location of the cursor by clicking on the four arrows at the left, right, top, and bottom of the map. Each click moves the cursor one pixel in the direction indicated by the arrow. + +

         

        + +
      • Use the scale printed at the bottom of the map to determine how to convert distances measured on the map in pixels to distances on the ground measured in miles or kilometers. +
      + + + diff --git a/htdocs/applets/source/GraphApplet.java b/htdocs/applets/source/GraphApplet.java new file mode 100644 index 0000000000..f248ad348d --- /dev/null +++ b/htdocs/applets/source/GraphApplet.java @@ -0,0 +1 @@ +/* Modified June 10, 2000 to add some public methods that are meant to be called from JavaScript. The following applet parameters were also added: Param name Default value Meaning ---------- ------------- ------- parameter, none The name of a variable, other than "x" parameter1, that can be used as a parameter in parameter2, the definition of the function. ... Their values can be set and queried by calling public methods. value, 0 Initial values for the parameters. value1, value2, ... */ /* GraphApplet displays the graph of a mathematical function. It can be configured with the following applet parameters: Param name Default value Meaning ---------- ------------- ------- userInput no If set to yes, there will be an input box where the user can enter the definition of the function. userLimits no If set to yes, there will be a set of inputs which control the x and y limits on the graph. userZoom no If set to yes, the user can zoom in on a point by clicking on it, and zoom out by shift-clicking. function abs(x) ^ x The function to be graphed, or if userInput is set to "yes", the initial function. limits -5 5 -5 5 Four numbers giving the x and y limits on the graph (xmin, xmax, ymin, ymax). Numbers can be separated by spaces or commas. The definition of a function can include: numbers, the variable x, the operators + - * / ^ and ! (where ^ represents exponentiation), and the functions sin, cos, tan, sec, csc, cot, arcsin, arccos, arctan, ln, log2, log10, exp, abs, sqrt, cubert, trunc, round, and ceiling. It can also include a "?:" conditional operator in the style of the C programming language: test ? value1 : value2 where test can include the logical operators: < <= = > >= and or not. (The ": value2" part is optional.) There is also a sum operation: sum(sumationVariable, start, end, expression). For example: sum(i, 0, k, x^i / i!) This applet is part of the mathbeans package, and it must be used in an tag with code="mathbeans.GraphApplet.class" and with mathbeans.jar available as an archive. See the mathbeans web site at http://math.hws.edu/mathbeans/ for more information. David Eck (eck@hws.edu, http://math.hws.edu/eck/) 26 August 1999 */ package mathbeans; import mathbeans.awt.*; import mathbeans.data.*; import mathbeans.draw.*; import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class GraphApplet extends Applet { /* Some public methods for calling from JavaScript */ public void setFunction(String funcDef) { if (functionInput == null) { if (functionLabel != null) functionLabel.setText(funcDef); try { functionParser.getSymbolTable().remove("f"); Function f = functionParser.defineFunction("f","x",funcDef); graph.setFunction(f); mainPanel.compute(); } catch (ParseError e) { graph.setFunction(null); canvas.setErrorMessage("Error in function definition: " + e); } } else { functionInput.setText(funcDef); funcDefString = funcDef; mainPanel.compute(); } } public String getFunction() { if (functionInput != null) { mainPanel.compute(); return functionInput.getText(); } else { return funcDefString; } } public void setLimits(double xmin, double xmax, double ymin, double ymax) { coordRect.setLimits(xmin,xmax,ymin,ymax); } public boolean setVariableValue(String name, double val) { MathObject obj = functionParser.getSymbol(name); if (obj != null && obj instanceof Variable) { ((Variable)obj).setValue(val); mainPanel.compute(); return true; } else { return false; } } public boolean defineNewVariable(String name) { if (name != null && functionParser.getSymbol(name) == null) { functionParser.defineVariable(name); return true; } else { return false; } } public double getVariableValue(String name) { MathObject obj = functionParser.getSymbol(name); if (obj != null && obj instanceof Variable) return ((Variable)obj).getValue(); else return Double.NaN; } // Variables introduced for use in the above routines. // They are set in the init() method. private MathBeanPanel mainPanel; // Main MathBeanPanel for the applet. private CoordinateRect coordRect; // Coordinate rect for the canvas. private ExpressionFunctionInput functionInput; // Input box, if userInput is "yes" private Label functionLabel; // Label that shows definition, if userInput is "no" private String funcDefString; // String that defines function, if userInput is "no" private Parser functionParser; // Parser used to define function. private Graph1D graph; // Graph that shows the function. //---------------------------------------------------- public void init() { /* The init method sets up the interface of the applet. For a "mathbean" * applet, this is all you have to do, since interface components take care * of themselves once they have been created and properly configured. * The entire applet is filled by a "MathBeanPanel", which coordinates * the activities of the mathematical components that it contains. * "panel.setInsetGap(3) leaves a 3-pixel border between the edge of the * panel and the components that it contains. */ MathBeanPanel panel = new MathBeanPanel(); mainPanel = panel; // new, for Javascript panel.setInsetGap(3); setLayout(new BorderLayout()); add(panel,BorderLayout.CENTER); /* A parser knows how to convert a string that represents a mathematical * expression into an internal form that can be used for computation. * Every mathbeans application needs at least one parser. */ Parser parser = new Parser(); functionParser = parser; /* New for JavaScript: Get names of variables from applet params. These * variables can be used in the function. Their values can be set and * tested by calling the above public methods. */ String name = getParameter("parameter"); String val = getParameter("value"); int ct = 0; if (name == null) { ct++; name = getParameter("parameter1"); val = getParameter("value1"); } while (name != null) { Variable v = parser.defineVariable(name); if (val != null) { double[] value = Util.extractNumbers(val); if (value != null && value.length > 0) v.setValue(value[0]); else v.setValue(0); } ct++; name = getParameter("parameter" + ct); val = getParameter("value" + ct); } /* This section defines the function that is to be graphed, either as * as a fixed function or as a user input function. The call to * getParameter("function") gets the value of the applet param with * name="function", if one has been provided in the tag. * If not, the value will be null. In that case, use a default * function. */ Function func; // The function to be graphed. String functionDef = getParameter("function"); if (functionDef == null) functionDef = "abs(x) ^ x"; if ("yes".equalsIgnoreCase(getParameter("userInput"))) { // Function can be entered by user. Use an // ExpressionFunctionInput box for user input. ExpressionFunctionInput in = new ExpressionFunctionInput("f","x",parser,functionDef); MathBeanPanel top = new MathBeanPanel(); // in.withLabel() is a component consisting of the input // box together with a lable "f(x)=" at its left. top.add(in.withLabel(), BorderLayout.CENTER); Button button = new Button("Use New Data"); // The next two lines cause the panel to recompute its contents // -- that is, to graph the new function -- when the user clicks // the button or presses return in the input box. A MathBeanPanel // always responds to events by recomputing everything. button.addActionListener(panel); in.addActionListener(panel); top.add(button, BorderLayout.EAST); // The ExpressionInputBox has an associated Function object, // representing the function defined by the user's input. // This is the function that is to be graphed. The definition // of the function changes whenever the panel recomputes its // contents (in this applet, when the user clicks the button or // presses return in the function input box). func = in.getFunction(); panel.add(top, BorderLayout.NORTH); functionInput = in; // new, for Javascript } else { // The graph will show a fixed function, defined by functionDef. // If there is an error in the function definition, a ParseError // will be thrown. In that case, use the default function. try { func = parser.defineFunction("f","x",functionDef); } catch (ParseError e) { func = null; } if (func == null) { try { functionDef = "abs(x) ^ x"; func = parser.defineFunction("f","x",functionDef); } catch (ParseError e) { // There shouldn't be an error here, unless the programmer makes // an error in the default function. However, ParseErrors must // always be handled in a try..catch. Print an error to the // console for debugging. System.out.println("Unexpected parse error in init(): " + e.getMessage()); } } Label lbl = new Label("f(x) = " + functionDef, Label.CENTER); panel.add(lbl, BorderLayout.NORTH); functionLabel = lbl; // new, for JavaScript funcDefString = functionDef; // new, for JavaScript } /* Get the xy limits specified by the user, if any. If there is no "limits" * param in the tag or if the value of the param is illegal, use * a default value of -5,5,-5,5. The routine Util.extractNumbers(String) * converts a string consisting of a list of numbers into an array of doubles. */ double[] limits = Util.extractNumbers(getParameter("limits")); if (limits == null || limits.length < 4) limits = new double[] { -5,5,-5,5 }; /* Create the canvas and add it to the panel. The call to * panel.setErrorReporter(canvas) means that if an error is * found in user input, an error message will be display on * the canvas. Otherwise, the error would be reported in * a dialog box, which is probably not a good idea in an applet. * A DisplayCanvas can display various graphical * objects, including axes and graphs. These objects are subclasses * of the class "Drawable". In this case the Drawable objects * displayed by the canvas are a set of axes, the graph of func, * and a one-pixel black border around the canvas. The canvas has * a CoordinateRect, which lays down an xy-coordinate system on * the canvas. The axes and graph use this coordinate system. */ canvas = new DisplayCanvas(); // declared below, outside the init() routine panel.setErrorReporter(canvas); CoordinateRect cr = new CoordinateRect(limits[0],limits[1],limits[2],limits[3]); coordRect = cr; // new, for JavaScript canvas.addCoordinateRect(cr); canvas.add(new Axes()); graph = new Graph1D(func); // new, for JavaScript canvas.add(graph); canvas.add(new DrawBorder()); panel.add(canvas, BorderLayout.CENTER); /* If the user is allowed to change xy limits, add a "control panel" * for this on the right edge of the applet. Here, some extra buttons * are added to a standard LimitControlPanel. The "ZoomButtons" * let the user zoom in or out on the graph by clicking a button. * The "RestoreButton" will restore the original limits from when * the applet was first created. */ if ("yes".equalsIgnoreCase(getParameter("userLimits"))) { LimitControlPanel lcp = new LimitControlPanel(canvas.getCoordinateRect(0)); lcp.addZoomButtons(); lcp.addRestoreButton(); panel.add(lcp, BorderLayout.EAST); } /* If "userZoom" is enabled, call canvas.setHandleMouseZooms(true). This * will let the user zoom in on a point on the graph by clicking on the * canvas. The user can zoom out from a point by clicking with the shift * key held down. */ if ("yes".equalsIgnoreCase(getParameter("userZoom"))) canvas.setHandleMouseZooms(true); } // end init() private DisplayCanvas canvas; // The canvas used in the applet public void stop() { // When the applet is stopped, be polite by releasing the // canvas's fairly large amount of allocated memory. canvas.releaseResources(); } } // end class GraphApplet \ No newline at end of file diff --git a/htdocs/applets/source/GummyGraph.java b/htdocs/applets/source/GummyGraph.java new file mode 100644 index 0000000000..7a063157f0 --- /dev/null +++ b/htdocs/applets/source/GummyGraph.java @@ -0,0 +1 @@ + /* Lets user reshape a graph by dragging points up and down. Shift-click to move all points back to the vertical center. Left click to drag a point and drag its neighbors along. Right-click to drag a point without dragging its neighbors. --David Eck, June 2000 (eck@hws.edu, http://math.hws.edu/eck/) */ import java.awt.*; import java.applet.Applet; import java.util.StringTokenizer; public class GummyGraph extends Applet { // Parameters that can be set through appelt params and manipulated // with JavaScript. (See public methods below.) private int points = 11; private double xmin = -5, xmax = 5, ymin = -5, ymax = 5; private double xGrid = 1; private double yGrid = 1; private int gap = 5; private double fallOff = 0.5; private Color borderColor = Color.black; private int borderWidth = 2; private Color gridColor = Color.lightGray; private Color axesColor = new Color(0,0,220); private Color lightAxesColor = new Color(160,180,255); private Color graphColor = Color.magenta; private boolean showPoints = true; private boolean showGrid = true; // Background color is set in init(); change it there. //-------------------- private implementation details --------------------------- private Image OSC = null; private int canvasWidth, canvasHeight; private boolean graphNeedsRedraw; private boolean axesNeedsRedraw; private boolean needsRedraw; //------------------- Public methods for scripting ------------------------------- public int getPointCount() { // Return the number of draggable points on the graph // (which is one plus the number of sub-intervals). return points; } public double getX(int pointNumber) { // Get the x-value of point number pointNumber. // Note: x-values are always evenly spaced. if (pointNumber < 0 || pointNumber >= points) return Double.NaN; return xmin + pointNumber*(xmax-xmin)/(points-1); } public double getY(int pointNumber) { // Return the y-value of point number pointNumber. // X and y values are stored internally as values // between 0 and 1. The actual x and y values // are determined by scaling these internal values // using the current xmin, xmax, ymin, ymax. if (pointNumber < 0 || pointNumber >= points) return Double.NaN; return ymin + yValues[pointNumber]*(ymax - ymin); } public double getDerivative(int pointNumber) { // Get the derivative at point number pointNumber. // This is just computed from the y-values. if (pointNumber < 0 || pointNumber >= points) return Double.NaN; double dx = 1.0/points * (xmax - xmin); if (pointNumber == 0) return (yValues[1] - yValues[0])*(ymax-ymin)/dx; else if (pointNumber == points-1) return (yValues[points-1] - yValues[points-2])*(ymax-ymin)/dx; else return (yValues[pointNumber+1] - yValues[pointNumber-1])*(ymax-ymin)/(2*dx); } public void reset() { // Puts all points back to the vertical center of the applet. for (int i = 0; i < points; i++) { yValues[i] = 0.5; derivatives[i] = 0; } graphNeedsRedraw = true; repaint(); } public void setPointCount(int ct) { // Set the number of points and reset y-values // to the vertical center of the applet. if (ct > 2 && ct <= 500) { points = ct; yValues = new double[points]; derivatives = new double[points]; for (int i = 0; i < points; i++) yValues[i] = 0.5; graphNeedsRedraw = true; repaint(); } } public void setY(int pointNumber, double val) { // Change the y-value of one point. if (pointNumber < 0 || pointNumber >= 0 || val < ymin || val > ymax) return; yValues[pointNumber] = (val-ymin)/(ymax-ymin); graphNeedsRedraw = true; repaint(); } public boolean setPoints(String points) { // The string, points, should be a list of numbers // separated by spaces or commas. These are used as // y-values for the points on the curve, replacing the // current points. Values are clamped to the range // from ymin to ymax. If the contents of the string // are not legal, the current graph is not changed // and the value false is returned. If the contents // of points are legal, true is returned. double[] vals = getDoubles(points); if (vals == null || vals.length < 3 || vals.length > 500) return false; setPointCount(vals.length); // Sets graphNeedsRedraw = true and calls repaint() for (int i = 0; i < vals.length; i++) { if (vals[i] > ymax) yValues[i] = 1; else if (vals[i] < ymin) yValues[i] = 0; else yValues[i] = (vals[i]-ymin)/(ymax-ymin); } makeDerivs(); return true; } public void setLimits(double xmin, double xmax, double ymin, double ymax) { // Change the ranges of x and y on the applet. if (xmax <= xmin || ymax <= ymin) return; this.ymax = ymax; this.ymin = ymin; this.xmax = xmax; this.xmin = xmin; axesNeedsRedraw = true; repaint(); } public void setGap(int g) { // This is space left between the axex and the border // of the applet. Default is 5. if (g >= 0 && g != gap) { gap = g; axesNeedsRedraw = true; graphNeedsRedraw = true; repaint(); } } public void setXGrid(double spacing) { // Set spacing between x grid lines (in the same units // that are used for xmin and xmax, so changing these // will change the number of grid lines). If the // value is zero, no x grid lines are drawn. if (spacing >= 0) { xGrid = spacing; needsRedraw = true; repaint(); } } public void setYGrid(double spacing) { // Set spacing between y grid lines. If the value is // zero, no y grid lines are drawn. if (spacing >= 0) { yGrid = spacing; needsRedraw = true; repaint(); } } public void setFallOff(double factor) { // When a point is dragged with the left mouse button, it can drag // its neighbors along. The displacement of the neighbor is // less than the displacement of the point by the fallOff factor. // For the neighbor of the neighbor, the displacement decreases by // another fallOff factor, and so on. If the value is zero, // then neighbors are not dragged at all. If the number of points // is small, this should be close to zero. For large numbers of // points, it can be close to one. if (factor >= 0 && factor < 1) { fallOff = factor; } } public void setBorderWidth(int b) { // How many pixels is the border drawn around the edge of the // applet. if (b >= 0) { borderWidth = b; needsRedraw = true; repaint(); } } public void setBorderColor(String color) { // Color of border. Color c = getColor(color); if (c != null) { borderColor = c; needsRedraw = true; repaint(); } } public void setGridColor(String color) { // Color of grid lines. Color c = getColor(color); if (c != null) { gridColor = c; needsRedraw = true; repaint(); } } public void setAxesColor(String color) { // Color of axes. Color c = getColor(color); if (c != null) { axesColor= c; needsRedraw = true; repaint(); } } public void setLightAxesColor(String color) { // Color used for axis when the real axis is // outside of the applet. Color c = getColor(color); if (c != null) { lightAxesColor= c; needsRedraw = true; repaint(); } } public void setGraphColor(String color) { // Color of graph. Color c = getColor(color); if (c != null) { graphColor= c; needsRedraw = true; repaint(); } } public void setBackgroundColor(String color) { // Background color for the whole applet. Color c = getColor(color); if (c != null) { setBackground(c); needsRedraw = true; repaint(); } } public void setShowPoints(boolean show) { // Should the points be drawn as little circles? // If you have a lot of points, it might not be // a good idea, since then just about any point on // the graph can be dragged. if (show != showPoints) { showPoints = show; needsRedraw = true; repaint(); } } public void setShowGrid(boolean show) { // Should the grid lines be drawn? if (show != showGrid) { showGrid = show; needsRedraw = true; repaint(); } } //----------------- Init method can set properties from applet params --------------- public void init() { setBackground(Color.white); setBackgroundColor(getParameter("backgroundColor")); setBorderColor(getParameter("borderColor")); setAxesColor(getParameter("axesColor")); setLightAxesColor(getParameter("lightAxesColor")); setGraphColor(getParameter("graphColor")); setGridColor(getParameter("gridColor")); setGap(getInt(getParameter("gap"))); int n = getInt(getParameter("pointCount")); setPointCount( (n > 2 && n <= 500)? n : 11 ); double[] dvals = getDoubles(getParameter("limits")); if (dvals != null && dvals.length == 4) setLimits(dvals[0], dvals[1], dvals[2], dvals[3]); setXGrid(getDouble(getParameter("xGrid"))); setYGrid(getDouble(getParameter("yGrid"))); setFallOff(getDouble(getParameter("fallOff"))); setBorderWidth(getInt(getParameter("borderWidth"))); if ("no".equalsIgnoreCase(getParameter("showPoints"))) showPoints = false; if ("no".equalsIgnoreCase(getParameter("showGrid"))) showGrid = false; setPoints(getParameter("points")); // overrides point count } //----------------------- For getting values from strings ------------------------- private int getInt(String str) { if (str == null || str.trim().length() == 0) return Integer.MIN_VALUE; try { return Integer.parseInt(str); } catch (NumberFormatException e) { return Integer.MIN_VALUE; } } private int[] getInts(String str) { if (str == null) return null; StringTokenizer tk = new StringTokenizer(str," ,\t"); int ct = tk.countTokens(); if (ct == 0) return null; int[] vals = new int[ct]; for (int i = 0; i < ct; i++) { String s = tk.nextToken(); vals[i] = getInt(s); if (vals[i] == Integer.MIN_VALUE) return null; } return null; } private double getDouble(String str) { if (str == null || str.trim().length() == 0) return Double.NaN; try { return (new Double(str)).doubleValue(); } catch (NumberFormatException e) { return Double.NaN; } } private double[] getDoubles(String str) { if (str == null) return null; StringTokenizer tk = new StringTokenizer(str," ,\t"); int ct = tk.countTokens(); if (ct == 0) return null; double[] vals = new double[ct]; for (int i = 0; i < ct; i++) { String s = tk.nextToken(); vals[i] = getDouble(s); if (Double.isNaN(vals[i]) || Double.isInfinite(vals[i])) return null; } return vals; } private static String[] colorNames = { "black", "white", "red", "green", "blue", "yellow", "cyan", "magenta", "pink", "orange", "lightGray", "gray", "darkGray" }; private static Color[] colorList = { Color.black, Color.white, Color.red, Color.green, Color.blue, Color.yellow, Color.cyan, Color.magenta, Color.pink, Color.orange, Color.lightGray, Color.gray, Color.darkGray }; private Color getColor(String str) { if (str == null) return null; str = str.trim(); if (str.length() == 0) return null; if (Character.isDigit(str.charAt(1))) { int[] levels = getInts(str); if (levels == null || levels.length != 3) return null; if (levels[0] < 0 || levels[0] > 255 || levels[1] < 0 || levels[1] > 255 || levels[2] < 0 || levels[2] > 255) return null; return new Color(levels[0],levels[1],levels[2]); } else { for (int i = 0; i < colorNames.length; i++) if (colorNames[i].equalsIgnoreCase(str)) return colorList[i]; return null; } } public void update(Graphics g) { paint(g); } //-------------------------------------------------------------------------- synchronized public void paint(Graphics g) { int width = size().width; int height = size().height; if (OSC == null || width != canvasWidth || height != canvasHeight) { axesNeedsRedraw = true; graphNeedsRedraw = true; canvasWidth = width; canvasHeight = height; OSC = null; try { OSC = createImage(width,height); } catch (OutOfMemoryError e) { } } if (OSC == null) { draw(g); } else { if (axesNeedsRedraw || graphNeedsRedraw || needsRedraw) { Graphics OSG = OSC.getGraphics(); draw(OSG); OSG.dispose(); needsRedraw = false; } g.drawImage(OSC,0,0,this); } } private void draw(Graphics g) { g.setColor(getBackground()); g.fillRect(0,0,canvasWidth,canvasHeight); drawGrid(g); g.setColor(borderColor); for (int i = 0; i < borderWidth; i++) g.drawRect(i, i, canvasWidth - 2*i - 1, canvasHeight - 2*i - 1); drawAxes(g); drawGraph(g); } private void drawGrid(Graphics g) { if (!showGrid) return; g.setColor(gridColor); if (xGrid > 0) { double x = (int)(xmin/xGrid)*xGrid; while (x <= xmax) { int a = (int)(gap + (x-xmin)*(canvasWidth-2*gap)/(xmax - xmin)); g.drawLine(a,0,a,canvasHeight); x += xGrid; } } if (yGrid > 0) { double y = (int)(ymin/yGrid)*yGrid; while (y <= ymax) { int b = (int)(gap + (ymax-y)*(canvasHeight-2*gap)/(ymax - ymin)); g.drawLine(0,b,canvasWidth,b); y += yGrid; } } } //--------------------------- Axes --------------------------------------- private int[] xTicks; private int[] yTicks; private String[] xTickLabels; private String[] yTickLabels; private int[][] xTickLabelPos; private int[][] yTickLabelPos; private int xAxisPixelPosition, yAxisPixelPosition; private Font font; private int ascent, descent, digitWidth; private void drawAxes(Graphics g) { if (axesNeedsRedraw) { font = g.getFont(); FontMetrics fm = g.getFontMetrics(font); setup(fm, xmin, xmax, ymin, ymax, 0, 0, canvasWidth, canvasHeight, gap); axesNeedsRedraw = false; } if (ymax < 0 || ymin > 0) g.setColor(lightAxesColor); else g.setColor(axesColor); g.drawLine(gap, xAxisPixelPosition, canvasWidth - gap - 1, xAxisPixelPosition); for (int i = 0; i < xTicks.length; i++) { int a = (xAxisPixelPosition - 2 < 0) ? xAxisPixelPosition : xAxisPixelPosition - 2; int b = (xAxisPixelPosition + 2 >= canvasHeight)? xAxisPixelPosition : xAxisPixelPosition + 2; g.drawLine(xTicks[i], a, xTicks[i], b); } for (int i = 0; i < xTickLabels.length; i++) g.drawString(xTickLabels[i], xTickLabelPos[i][0], xTickLabelPos[i][1]); if (xmax < 0 || xmin > 0) g.setColor(lightAxesColor); else g.setColor(axesColor); g.drawLine(yAxisPixelPosition, gap, yAxisPixelPosition, canvasHeight - gap - 1); for (int i = 0; i < yTicks.length; i++) { int a = (yAxisPixelPosition - 2 < 0) ? yAxisPixelPosition : yAxisPixelPosition - 2; int b = (yAxisPixelPosition + 2 >= canvasWidth)? yAxisPixelPosition : yAxisPixelPosition + 2; g.drawLine(a, yTicks[i], b, yTicks[i]); } for (int i = 0; i < yTickLabels.length; i++) g.drawString(yTickLabels[i], yTickLabelPos[i][0], yTickLabelPos[i][1]); } void setup(FontMetrics fm, double xmin, double xmax, double ymin, double ymax, int left, int top, int width, int height, int gap) { digitWidth = fm.charWidth('0'); ascent = fm.getAscent(); descent = fm.getDescent(); if (ymax < 0) xAxisPixelPosition = top + gap; else if (ymin > 0) xAxisPixelPosition = top + height - gap - 1; else xAxisPixelPosition = top + gap + (int)((height-2*gap - 1) * ymax / (ymax-ymin)); if (xmax < 0) yAxisPixelPosition = left + width - gap - 1; else if (xmin > 0) yAxisPixelPosition = left + gap; else yAxisPixelPosition = left + gap - (int)((width-2*gap - 1) * xmin / (xmax-xmin)); double start = fudgeStart( ((xmax-xmin)*(yAxisPixelPosition - (left + gap)))/(width - 2*gap) + xmin, 0.05*(xmax-xmin) ); int labelCt = (width - 2*gap) / (8*digitWidth); if (labelCt <= 2) labelCt = 3; else if (labelCt > 20) labelCt = 20; double interval = fudge( (xmax - xmin) / labelCt ); for (double mul = 1.5; mul < 4; mul += 0.5) { if (fm.stringWidth(realToString(interval+start)) + digitWidth > (interval/(xmax-xmin))*(width-2*gap)) // overlapping labels interval = fudge( mul*(xmax - xmin) / labelCt ); else break; } double[] label = new double[50]; labelCt = 0; double x = start + interval; double limit = left + width; while (labelCt < 50 && x < xmax) { if (left + gap + (width-2*gap)*(x-xmin)/(xmax-xmin) + fm.stringWidth(realToString(x))/2 > limit) break; label[labelCt] = x; labelCt++; x += interval; } x = start - interval; limit = left; while (labelCt < 50 && x >= xmin) { if (left + gap + (width-2*gap)*(x-xmin)/(xmax-xmin) - fm.stringWidth(realToString(x))/2 < limit) break; label[labelCt] = x; labelCt++; x -= interval; } xTicks = new int[labelCt]; xTickLabels = new String[labelCt]; xTickLabelPos = new int[labelCt][2]; for (int i = 0; i < labelCt; i++) { xTicks[i] = (int)(left + gap + (width-2*gap)*(label[i]-xmin)/(xmax-xmin)); xTickLabels[i] = realToString(label[i]); xTickLabelPos[i][0] = xTicks[i] - fm.stringWidth(xTickLabels[i])/2; if (xAxisPixelPosition - 4 - ascent >= top) xTickLabelPos[i][1] = xAxisPixelPosition - 4; else xTickLabelPos[i][1] = xAxisPixelPosition + 4 + ascent; } start = fudgeStart( ymax - ((ymax-ymin)*(xAxisPixelPosition - (top + gap)))/(height - 2*gap), 0.05*(ymax-ymin) ); labelCt = (height - 2*gap) / (4*(ascent+descent)); if (labelCt <= 2) labelCt = 3; else if (labelCt > 20) labelCt = 20; interval = fudge( (ymax - ymin) / labelCt ); labelCt = 0; double y = start + interval; limit = top + 8 + gap; while (labelCt < 50 && y <= ymax) { if (top + gap + (height-2*gap)*(ymax-y)/(ymax-ymin) - ascent/2 < limit) break; label[labelCt] = y; labelCt++; y += interval; } y = start - interval; limit = top + height - gap - 8; while (labelCt < 50 && y >= ymin) { if (top + gap + (height-2*gap)*(ymax-y)/(ymax-ymin) + ascent/2 > limit) break; label[labelCt] = y; labelCt++; y -= interval; } yTicks = new int[labelCt]; yTickLabels = new String[labelCt]; yTickLabelPos = new int[labelCt][2]; int w = 0; // max width of tick mark for (int i = 0; i < labelCt; i++) { yTickLabels[i] = realToString(label[i]); int s = fm.stringWidth(yTickLabels[i]); if (s > w) w = s; } for (int i = 0; i < labelCt; i++) { yTicks[i] = (int)(top + gap + (height-2*gap)*(ymax-label[i])/(ymax-ymin)); yTickLabelPos[i][1] = yTicks[i] + ascent/2; if (yAxisPixelPosition - 4 - w < left) yTickLabelPos[i][0] = yAxisPixelPosition + 4; else yTickLabelPos[i][0] = yAxisPixelPosition - 4 - fm.stringWidth(yTickLabels[i]); } } // end setup() double fudge (double x) { // Translated directly from the Pascal version of xFunctions. // Move x to a more "rounded" value; used for labeling axes. int i, digits; double y; if (Math.abs(x) < 0.0005 || Math.abs(x) > 500000) return x; else if (Math.abs(x) < 0.1 || Math.abs(x) > 5000) { y = x; digits = 0; if (Math.abs(y) >= 1) { while (Math.abs(y) >= 8.75) { y = y / 10; digits = digits + 1; } } else { while (Math.abs(y) < 1) { y = y * 10; digits = digits - 1; } } y = Math.round(y * 4) / 4; if (digits > 0) { for (int j = 0; j < digits; j++) y = y * 10; } else if (digits < 0) { for (int j = 0; j < -digits; j++) y = y / 10; } return y; } else if (Math.abs(x) < 0.5) return Math.round(10 * x) / 10.0; else if (Math.abs(x) < 2.5) return Math.round(2 * x) / 2.0; else if (Math.abs(x) < 12) return Math.round(x); else if (Math.abs(x) < 120) return Math.round(x / 10) * 10.0; else if (Math.abs(x) < 1200) return Math.round(x / 100) * 100.0; else return Math.round(x / 1000) * 1000.0; } double fudgeStart(double a, double diff) { // Adapted from the Pascal version of xFunctions. // Tries to find a "rounded value" withint diff of a if (Math.abs(Math.round(a) - a) < diff) return Math.round(a); for (double x = 10; x <= 100000; x *= 10) { double d = Math.round(a*x) / x; if (Math.abs(d - a) < diff) return d; } return a; } public static String realToString(double x) { return realToString(x,8); } public static String realToString(double x, int width) { // Goal is to return a reasonable string representation // of x, using at most width spaces. (If width is // unreasonably big or small, its value is adjusted to // lie in the range 7 to 25.) width = Math.min(25, Math.max(7,width)); if (Double.isNaN(x)) return "undefined"; if (Double.isInfinite(x)) if (x < 0) return "-INF"; else return "INF"; String s = String.valueOf(x); if (Math.rint(x) == x && Math.abs(x) < 5e15 && s.length() <= (width+2)) return String.valueOf( (long)x ); // return string without trailing ".0" if (s.length() <= width) return s; boolean neg = false; if (x < 0) { neg = true; x = -x; width--; s = String.valueOf(x); } long maxForNonExp = 5*(long)Math.pow(10,width-2); if (x >= 0.0005 && x <= maxForNonExp && (s.indexOf('E') == -1 && s.indexOf('e') == -1)) { s = round(s,width); s = trimZeros(s); } else if (x > 1) { // construct exponential form with positive exponent long power = (long)Math.floor(Math.log(x)/Math.log(10)); String exp = "E" + power; int numlength = width - exp.length(); x = x / Math.pow(10,power); s = String.valueOf(x); s = round(s,numlength); s = trimZeros(s); s += exp; } else { // constuct exponential form with negative argument long power = (long)Math.ceil(-Math.log(x)/Math.log(10)); String exp = "E-" + power; int numlength = width - exp.length(); x = x * Math.pow(10,power); s = String.valueOf(x); s = round(s,numlength); s = trimZeros(s); s += exp; } if (neg) return "-" + s; else return s; } private static String trimZeros(String num) { // remove trailing zeros if num contains a decimal point, and // remove the decimal point as well if all following digits are zero if (num.indexOf('.') >= 0 && num.charAt(num.length() - 1) == '0') { int i = num.length() - 1; while (num.charAt(i) == '0') i--; if (num.charAt(i) == '.') num = num.substring(0,i); else num = num.substring(0,i+1); } return num; } private static String round(String num, int length) { // Round off num to the given field width if (num.indexOf('.') < 0) return num; if (num.length() <= length) return num; if (num.charAt(length) >= '5' && num.charAt(length) != '.') { char[] temp = new char[length+1]; int ct = length; boolean rounding = true; for (int i = length-1; i >= 0; i--) { temp[ct] = num.charAt(i); if (rounding && temp[ct] != '.') { if (temp[ct] < '9') { temp[ct]++; rounding = false; } else temp[ct] = '0'; } ct--; } if (rounding) { temp[ct] = '1'; ct--; } // ct is -1 or 0 return new String(temp,ct+1,length-ct); } else return num.substring(0,length); } //--------------------------- Graph -------------------------------------- private double[] yValues; private double[] derivatives; private int[] pixelX; private int[] pixelY; private int[] dotX; private int[] dotY; private void drawGraph(Graphics g) { if (graphNeedsRedraw) { makeGraphCoords(); graphNeedsRedraw = false; } g.setColor(graphColor); for (int i = 1; i < pixelX.length; i++) { g.drawLine(pixelX[i-1], pixelY[i-1], pixelX[i], pixelY[i]); } if (showPoints) { for (int i = 0; i < dotX.length; i++) g.fillOval(dotX[i], dotY[i], 5, 5); } } private void makeGraphCoords() { int pointCt = (canvasWidth-2*gap-2) / 3 + 2; if (pixelX == null || pixelX.length != pointCt) { pixelX = new int[pointCt]; for (int i = 0; i < pointCt - 1; i++) pixelX[i] = gap + i*3; pixelX[pointCt-1] = canvasWidth - gap - 1; pixelY = new int[pointCt]; } double dx = (xmax - xmin) / (canvasWidth - 2*gap - 1); double dy = (ymax - ymin) / (canvasHeight - 2*gap - 1); for (int i = 0; i < pointCt; i++) { double x = xmin + dx*(pixelX[i]-gap); double y = eval(x); pixelY[i] = (int)(gap + (ymax - y)/dy); } if (dotX == null || dotX.length != points) { dotX = new int[points]; dotY = new int[points]; } dx = (xmax - xmin) / (points-1); double factorX = (canvasWidth - 2*gap - 1)/(xmax-xmin); double factorY = (canvasHeight - 2*gap - 1)/(ymax-ymin); for (int i = 0; i < points; i++) { double x = xmin + dx*i; double y = eval(x); dotX[i] = (int)(gap + (x-xmin)*factorX) - 2; dotY[i] = (int)(gap + (ymax-y)*factorY) - 2; } } private double eval(double x) { // NOTE: yvalues and derivatives are stored as if x and y range from 0 to 1!!! double scaledx = (x-xmin)/(xmax-xmin); int interval = (int)((points-1)*scaledx); if (interval >= points-1) return ymin + yValues[points-1]*(ymax-ymin); double temp = 1.0/(points-1); double a = yValues[interval+1]/(temp*temp*temp); double b = derivatives[interval+1]/(temp*temp) - 3*a; double d = -yValues[interval]/(temp*temp*temp); double c = derivatives[interval]/(temp*temp) - 3*d; double t1 = scaledx - interval*temp; double t2 = t1*t1; double t3 = t2*t1; double s1 = scaledx - (interval+1)*temp; double s2 = s1*s1; double s3 = s2*s1; double scaledy = a*t3 + b*t2*s1 + c*t1*s2 + d*s3; return ymin + scaledy*(ymax-ymin); } //--------------------------- Mouse Handling ----------------------------- private boolean dragging; private int dragPoint; private int lastY; private boolean propogate; synchronized public boolean mouseDown(Event evt, int x, int y) { dragging = false; if (evt.shiftDown()) { for (int i = 0; i < points; i++) { yValues[i] = 0.5; derivatives[i] = 0; } graphNeedsRedraw = true; repaint(); return true; } if (dotX == null || graphNeedsRedraw) { // shouldn't happen repaint(); return true; } for (int i = 0; i < points; i++) if (x >= dotX[i] - 1 && x < dotX[i] + 7 && y >= dotY[i] - 1 && y < dotY[i] + 7) { dragging = true; dragPoint = i; lastY = y; propogate = ! evt.metaDown(); break; } return true; } synchronized public boolean mouseUp(Event evt, int x, int y) { dragging = false; return true; } synchronized public boolean mouseDrag(Event evt, int x, int y) { if (!dragging || graphNeedsRedraw) return true; if (y < gap) y = gap; if (y > canvasHeight - gap) y = canvasHeight - gap; if (y - lastY == 0) return true; movePoint(dragPoint, y - lastY); lastY = y; return true; } private void movePoint(int pointNum, int pixelsOffset) { double offset = -1.0/(canvasHeight - 2*gap) * pixelsOffset; double oldy = yValues[pointNum]; yValues[pointNum] += offset; double dx = 1.0 / (points - 1); if (propogate && fallOff > 0) { if (offset > 0) { int pt = pointNum + 1; double factor = fallOff; double prevy = oldy; while (pt < points && yValues[pt] < prevy) { prevy = yValues[pt]; yValues[pt] += offset*factor; factor *= fallOff; pt++; } pt = pointNum - 1; factor = fallOff; prevy = oldy; while (pt >= 0 && yValues[pt] < prevy) { prevy = yValues[pt]; yValues[pt] += offset*factor; factor *= fallOff; pt--; } } else { int pt = pointNum + 1; double factor = fallOff; double prevy = oldy; while (pt < points && yValues[pt] > prevy) { prevy = yValues[pt]; yValues[pt] += offset*factor; factor *= fallOff; pt++; } pt = pointNum - 1; factor = fallOff; prevy = oldy; while (pt >= 0 && yValues[pt] > prevy) { prevy = yValues[pt]; yValues[pt] += offset*factor; factor *= fallOff; pt--; } } } graphNeedsRedraw = true; makeDerivs(); repaint(); } private void makeDerivs() { double dx = 1.0 / (points - 1); derivatives[0] = (yValues[1] - yValues[0])/dx; for (int i = 1; i < points - 1; i++) derivatives[i] = (yValues[i+1] - yValues[i-1])/(2*dx); derivatives[points-1] = (yValues[points-1] - yValues[points-2])/dx; } } // end class GummyGraph \ No newline at end of file diff --git a/htdocs/applets/source/SketchGraph.java b/htdocs/applets/source/SketchGraph.java new file mode 100644 index 0000000000..87944a6163 --- /dev/null +++ b/htdocs/applets/source/SketchGraph.java @@ -0,0 +1 @@ + /* Lets user sketch a graph by moving the mouse. Left-click and drag to sketch the shape of the graph. Shift-click to reset graph. Right-click (Command-click on Mac) to smoothen the graph. --David Eck, June 2000 (eck@hws.edu, http://math.hws.edu/eck/) */ import java.awt.*; import java.applet.Applet; import java.util.StringTokenizer; public class SketchGraph extends Applet { // Parameters that can be set through appelt params and manipulated // with JavaScript. (See public methods below.) private int points; private double xmin = -5, xmax = 5, ymin = -5, ymax = 5; private double xGrid = 1; private double yGrid = 1; private int gap = 7; private Color borderColor = Color.black; private int borderWidth = 2; private Color gridColor = Color.lightGray; private Color axesColor = new Color(0,0,220); private Color lightAxesColor = new Color(160,180,255); private Color graphColor = Color.magenta; private boolean showGrid = true; // Background color is set in init(); change it there. //-------------------- private implementation details --------------------------- private Image OSC = null; private int canvasWidth, canvasHeight; private boolean graphNeedsRedraw; private boolean axesNeedsRedraw; private boolean needsRedraw; //------------------- Public methods for scripting ------------------------------- public int getPointCount() { // Return the number of draggable points on the graph // (which is one plus the number of sub-intervals). return points; } public double getX(int pointNumber) { // Get the x-value of point number pointNumber. // Note: x-values are always evenly spaced. if (pointNumber < 0 || pointNumber >= points) return Double.NaN; return xmin + pointNumber*(xmax-xmin)/(points-1); } public double getY(int pointNumber) { // Return the y-value of point number pointNumber. // X and y values are stored internally as values // between 0 and 1. The actual x and y values // are determined by scaling these internal values // using the current xmin, xmax, ymin, ymax. if (pointNumber < 0 || pointNumber >= points) return Double.NaN; return ymin + yValues[pointNumber]*(ymax - ymin); } public double getDerivative(int pointNumber) { // Get the derivative at point number pointNumber. // This is just computed from the y-values (but not in // the standard way -- it uses a weighted average of // the slope from the point on left and the slope to // the point on the right). if (pointNumber < 0 || pointNumber >= points) return Double.NaN; double dx = 1.0/points * (xmax - xmin); if (pointNumber == 0) return (yValues[1] - yValues[0])*(ymax-ymin)/dx; else if (pointNumber == points-1) return (yValues[points-1] - yValues[points-2])*(ymax-ymin)/dx; else { int i = pointNumber; double left = Math.abs(yValues[i] - yValues[i-1]); double right = Math.abs(yValues[i+1] - yValues[i]); if (left < 1e-20 || right < 1e-20) return 0; else return ((1/right)*(yValues[i+1]-yValues[i]) - (1/left)*(yValues[i]-yValues[i-1]))/(2*dx*((1/right)+(1/left))); } } public void reset() { // Puts all points back to the vertical center of the applet. for (int i = 0; i < points; i++) { yValues[i] = 0.5; derivatives[i] = 0; } graphNeedsRedraw = true; repaint(); } public void smoothen() { // Make the graph smoother by replacine each y-value with a weighted // average of y-values of nearby points. double[] newPoints = new double[points]; for (int i = 0; i < points; i++) { double sum = 0.3 * yValues[i]; sum += 0.2 * (i > 0? yValues[i-1] : yValues[i]); sum += 0.2 * (i < points-1? yValues[i+1] : yValues[i]); sum += 0.1 * (i > 1? yValues[i-2] : yValues[i]); sum += 0.1 * (i < points - 2? yValues[i+2] : yValues[i]); sum += 0.05 * (i > 2? yValues[i-3] : yValues[i]); sum += 0.05 * (i < points - 3? yValues[i+3] : yValues[i]); newPoints[i] = sum; } yValues = newPoints; graphNeedsRedraw = true; repaint(); } public void setPointCount(int ct) { // Set the number of points and reset y-values // to the vertical center of the applet. if (ct > 2 && ct <= 500) { points = ct; yValues = new double[points]; derivatives = new double[points]; for (int i = 0; i < points; i++) yValues[i] = 0.5; graphNeedsRedraw = true; repaint(); } } public void setY(int pointNumber, double val) { // Change the y-value of one point. if (pointNumber < 0 || pointNumber >= 0 || val < ymin || val > ymax) return; yValues[pointNumber] = (val-ymin)/(ymax-ymin); graphNeedsRedraw = true; repaint(); } public boolean setPoints(String points) { // The string, points, should be a list of numbers // separated by spaces or commas. These are used as // y-values for the points on the curve, replacing the // current points. Values are clamped to the range // from ymin to ymax. If the contents of the string // are not legal, the current graph is not changed // and the value false is returned. If the contents // of points are legal, true is returned. double[] vals = getDoubles(points); if (vals == null || vals.length < 3 || vals.length > 500) return false; setPointCount(vals.length); // Sets graphNeedsRedraw = true and calls repaint() for (int i = 0; i < vals.length; i++) { if (vals[i] > ymax) yValues[i] = 1; else if (vals[i] < ymin) yValues[i] = 0; else yValues[i] = (vals[i]-ymin)/(ymax-ymin); } return true; } public void setLimits(double xmin, double xmax, double ymin, double ymax) { // Change the ranges of x and y on the applet. if (xmax <= xmin || ymax <= ymin) return; this.ymax = ymax; this.ymin = ymin; this.xmax = xmax; this.xmin = xmin; axesNeedsRedraw = true; repaint(); } public void setGap(int g) { // This is space left between the axex and the border // of the applet. Default is 5. if (g >= 0 && g != gap) { gap = g; axesNeedsRedraw = true; graphNeedsRedraw = true; repaint(); } } public void setXGrid(double spacing) { // Set spacing between x grid lines (in the same units // that are used for xmin and xmax, so changing these // will change the number of grid lines). If the // value is zero, no x grid lines are drawn. if (spacing >= 0) { xGrid = spacing; needsRedraw = true; repaint(); } } public void setYGrid(double spacing) { // Set spacing between y grid lines. If the value is // zero, no y grid lines are drawn. if (spacing >= 0) { yGrid = spacing; needsRedraw = true; repaint(); } } public void setBorderWidth(int b) { // How many pixels is the border drawn around the edge of the // applet. if (b >= 0) { borderWidth = b; needsRedraw = true; repaint(); } } public void setBorderColor(String color) { // Color of border. Color c = getColor(color); if (c != null) { borderColor = c; needsRedraw = true; repaint(); } } public void setGridColor(String color) { // Color of grid lines. Color c = getColor(color); if (c != null) { gridColor = c; needsRedraw = true; repaint(); } } public void setAxesColor(String color) { // Color of axes. Color c = getColor(color); if (c != null) { axesColor= c; needsRedraw = true; repaint(); } } public void setLightAxesColor(String color) { // Color used for axis when the real axis is // outside of the applet. Color c = getColor(color); if (c != null) { lightAxesColor= c; needsRedraw = true; repaint(); } } public void setGraphColor(String color) { // Color of graph. Color c = getColor(color); if (c != null) { graphColor= c; needsRedraw = true; repaint(); } } public void setBackgroundColor(String color) { // Background color for the whole applet. Color c = getColor(color); if (c != null) { setBackground(c); needsRedraw = true; repaint(); } } public void setShowGrid(boolean show) { // Should the grid lines be drawn? if (show != showGrid) { showGrid = show; needsRedraw = true; repaint(); } } //----------------- Init method can set properties from applet params --------------- public void init() { setBackground(Color.white); setBackgroundColor(getParameter("backgroundColor")); setBorderColor(getParameter("borderColor")); setAxesColor(getParameter("axesColor")); setLightAxesColor(getParameter("lightAxesColor")); setGraphColor(getParameter("graphColor")); setGridColor(getParameter("gridColor")); setGap(getInt(getParameter("gap"))); int n = getInt(getParameter("pointCount")); setPointCount( (n > 2 && n <= 500)? n : 101 ); double[] dvals = getDoubles(getParameter("limits")); if (dvals != null && dvals.length == 4) setLimits(dvals[0], dvals[1], dvals[2], dvals[3]); setXGrid(getDouble(getParameter("xGrid"))); setYGrid(getDouble(getParameter("yGrid"))); setBorderWidth(getInt(getParameter("borderWidth"))); if ("no".equalsIgnoreCase(getParameter("showGrid"))) showGrid = false; setPoints(getParameter("points")); // overrides point count } //----------------------- For getting values from strings ------------------------- private int getInt(String str) { if (str == null || str.trim().length() == 0) return Integer.MIN_VALUE; try { return Integer.parseInt(str); } catch (NumberFormatException e) { return Integer.MIN_VALUE; } } private int[] getInts(String str) { if (str == null) return null; StringTokenizer tk = new StringTokenizer(str," ,\t"); int ct = tk.countTokens(); if (ct == 0) return null; int[] vals = new int[ct]; for (int i = 0; i < ct; i++) { String s = tk.nextToken(); vals[i] = getInt(s); if (vals[i] == Integer.MIN_VALUE) return null; } return null; } private double getDouble(String str) { if (str == null || str.trim().length() == 0) return Double.NaN; try { return (new Double(str)).doubleValue(); } catch (NumberFormatException e) { return Double.NaN; } } private double[] getDoubles(String str) { if (str == null) return null; StringTokenizer tk = new StringTokenizer(str," ,\t"); int ct = tk.countTokens(); if (ct == 0) return null; double[] vals = new double[ct]; for (int i = 0; i < ct; i++) { String s = tk.nextToken(); vals[i] = getDouble(s); if (Double.isNaN(vals[i]) || Double.isInfinite(vals[i])) return null; } return vals; } private static String[] colorNames = { "black", "white", "red", "green", "blue", "yellow", "cyan", "magenta", "pink", "orange", "lightGray", "gray", "darkGray" }; private static Color[] colorList = { Color.black, Color.white, Color.red, Color.green, Color.blue, Color.yellow, Color.cyan, Color.magenta, Color.pink, Color.orange, Color.lightGray, Color.gray, Color.darkGray }; private Color getColor(String str) { if (str == null) return null; str = str.trim(); if (str.length() == 0) return null; if (Character.isDigit(str.charAt(1))) { int[] levels = getInts(str); if (levels == null || levels.length != 3) return null; if (levels[0] < 0 || levels[0] > 255 || levels[1] < 0 || levels[1] > 255 || levels[2] < 0 || levels[2] > 255) return null; return new Color(levels[0],levels[1],levels[2]); } else { for (int i = 0; i < colorNames.length; i++) if (colorNames[i].equalsIgnoreCase(str)) return colorList[i]; return null; } } public void update(Graphics g) { paint(g); } //-------------------------------------------------------------------------- synchronized public void paint(Graphics g) { int width = size().width; int height = size().height; if (OSC == null || width != canvasWidth || height != canvasHeight) { axesNeedsRedraw = true; graphNeedsRedraw = true; canvasWidth = width; canvasHeight = height; OSC = null; try { OSC = createImage(width,height); } catch (OutOfMemoryError e) { } } if (OSC == null) { draw(g); } else { if (axesNeedsRedraw || graphNeedsRedraw || needsRedraw) { Graphics OSG = OSC.getGraphics(); draw(OSG); OSG.dispose(); needsRedraw = false; } g.drawImage(OSC,0,0,this); } } private void draw(Graphics g) { g.setColor(getBackground()); g.fillRect(0,0,canvasWidth,canvasHeight); drawGrid(g); g.setColor(borderColor); for (int i = 0; i < borderWidth; i++) g.drawRect(i, i, canvasWidth - 2*i - 1, canvasHeight - 2*i - 1); drawAxes(g); drawGraph(g); } private void drawGrid(Graphics g) { if (!showGrid) return; g.setColor(gridColor); if (xGrid > 0) { double x = (int)(xmin/xGrid)*xGrid; while (x <= xmax) { int a = (int)(gap + (x-xmin)*(canvasWidth-2*gap)/(xmax - xmin)); g.drawLine(a,0,a,canvasHeight); x += xGrid; } } if (yGrid > 0) { double y = (int)(ymin/yGrid)*yGrid; while (y <= ymax) { int b = (int)(gap + (ymax-y)*(canvasHeight-2*gap)/(ymax - ymin)); g.drawLine(0,b,canvasWidth,b); y += yGrid; } } } //--------------------------- Axes --------------------------------------- private int[] xTicks; private int[] yTicks; private String[] xTickLabels; private String[] yTickLabels; private int[][] xTickLabelPos; private int[][] yTickLabelPos; private int xAxisPixelPosition, yAxisPixelPosition; private Font font; private int ascent, descent, digitWidth; private void drawAxes(Graphics g) { if (axesNeedsRedraw) { font = g.getFont(); FontMetrics fm = g.getFontMetrics(font); setup(fm, xmin, xmax, ymin, ymax, 0, 0, canvasWidth, canvasHeight, gap); axesNeedsRedraw = false; } if (ymax < 0 || ymin > 0) g.setColor(lightAxesColor); else g.setColor(axesColor); g.drawLine(gap, xAxisPixelPosition, canvasWidth - gap - 1, xAxisPixelPosition); for (int i = 0; i < xTicks.length; i++) { int a = (xAxisPixelPosition - 2 < 0) ? xAxisPixelPosition : xAxisPixelPosition - 2; int b = (xAxisPixelPosition + 2 >= canvasHeight)? xAxisPixelPosition : xAxisPixelPosition + 2; g.drawLine(xTicks[i], a, xTicks[i], b); } for (int i = 0; i < xTickLabels.length; i++) g.drawString(xTickLabels[i], xTickLabelPos[i][0], xTickLabelPos[i][1]); if (xmax < 0 || xmin > 0) g.setColor(lightAxesColor); else g.setColor(axesColor); g.drawLine(yAxisPixelPosition, gap, yAxisPixelPosition, canvasHeight - gap - 1); for (int i = 0; i < yTicks.length; i++) { int a = (yAxisPixelPosition - 2 < 0) ? yAxisPixelPosition : yAxisPixelPosition - 2; int b = (yAxisPixelPosition + 2 >= canvasWidth)? yAxisPixelPosition : yAxisPixelPosition + 2; g.drawLine(a, yTicks[i], b, yTicks[i]); } for (int i = 0; i < yTickLabels.length; i++) g.drawString(yTickLabels[i], yTickLabelPos[i][0], yTickLabelPos[i][1]); } void setup(FontMetrics fm, double xmin, double xmax, double ymin, double ymax, int left, int top, int width, int height, int gap) { digitWidth = fm.charWidth('0'); ascent = fm.getAscent(); descent = fm.getDescent(); if (ymax < 0) xAxisPixelPosition = top + gap; else if (ymin > 0) xAxisPixelPosition = top + height - gap - 1; else xAxisPixelPosition = top + gap + (int)((height-2*gap - 1) * ymax / (ymax-ymin)); if (xmax < 0) yAxisPixelPosition = left + width - gap - 1; else if (xmin > 0) yAxisPixelPosition = left + gap; else yAxisPixelPosition = left + gap - (int)((width-2*gap - 1) * xmin / (xmax-xmin)); double start = fudgeStart( ((xmax-xmin)*(yAxisPixelPosition - (left + gap)))/(width - 2*gap) + xmin, 0.05*(xmax-xmin) ); int labelCt = (width - 2*gap) / (8*digitWidth); if (labelCt <= 2) labelCt = 3; else if (labelCt > 20) labelCt = 20; double interval = fudge( (xmax - xmin) / labelCt ); for (double mul = 1.5; mul < 4; mul += 0.5) { if (fm.stringWidth(realToString(interval+start)) + digitWidth > (interval/(xmax-xmin))*(width-2*gap)) // overlapping labels interval = fudge( mul*(xmax - xmin) / labelCt ); else break; } double[] label = new double[50]; labelCt = 0; double x = start + interval; double limit = left + width; while (labelCt < 50 && x < xmax) { if (left + gap + (width-2*gap)*(x-xmin)/(xmax-xmin) + fm.stringWidth(realToString(x))/2 > limit) break; label[labelCt] = x; labelCt++; x += interval; } x = start - interval; limit = left; while (labelCt < 50 && x >= xmin) { if (left + gap + (width-2*gap)*(x-xmin)/(xmax-xmin) - fm.stringWidth(realToString(x))/2 < limit) break; label[labelCt] = x; labelCt++; x -= interval; } xTicks = new int[labelCt]; xTickLabels = new String[labelCt]; xTickLabelPos = new int[labelCt][2]; for (int i = 0; i < labelCt; i++) { xTicks[i] = (int)(left + gap + (width-2*gap)*(label[i]-xmin)/(xmax-xmin)); xTickLabels[i] = realToString(label[i]); xTickLabelPos[i][0] = xTicks[i] - fm.stringWidth(xTickLabels[i])/2; if (xAxisPixelPosition - 4 - ascent >= top) xTickLabelPos[i][1] = xAxisPixelPosition - 4; else xTickLabelPos[i][1] = xAxisPixelPosition + 4 + ascent; } start = fudgeStart( ymax - ((ymax-ymin)*(xAxisPixelPosition - (top + gap)))/(height - 2*gap), 0.05*(ymax-ymin) ); labelCt = (height - 2*gap) / (4*(ascent+descent)); if (labelCt <= 2) labelCt = 3; else if (labelCt > 20) labelCt = 20; interval = fudge( (ymax - ymin) / labelCt ); labelCt = 0; double y = start + interval; limit = top + 8 + gap; while (labelCt < 50 && y <= ymax) { if (top + gap + (height-2*gap)*(ymax-y)/(ymax-ymin) - ascent/2 < limit) break; label[labelCt] = y; labelCt++; y += interval; } y = start - interval; limit = top + height - gap - 8; while (labelCt < 50 && y >= ymin) { if (top + gap + (height-2*gap)*(ymax-y)/(ymax-ymin) + ascent/2 > limit) break; label[labelCt] = y; labelCt++; y -= interval; } yTicks = new int[labelCt]; yTickLabels = new String[labelCt]; yTickLabelPos = new int[labelCt][2]; int w = 0; // max width of tick mark for (int i = 0; i < labelCt; i++) { yTickLabels[i] = realToString(label[i]); int s = fm.stringWidth(yTickLabels[i]); if (s > w) w = s; } for (int i = 0; i < labelCt; i++) { yTicks[i] = (int)(top + gap + (height-2*gap)*(ymax-label[i])/(ymax-ymin)); yTickLabelPos[i][1] = yTicks[i] + ascent/2; if (yAxisPixelPosition - 4 - w < left) yTickLabelPos[i][0] = yAxisPixelPosition + 4; else yTickLabelPos[i][0] = yAxisPixelPosition - 4 - fm.stringWidth(yTickLabels[i]); } } // end setup() double fudge (double x) { // Translated directly from the Pascal version of xFunctions. // Move x to a more "rounded" value; used for labeling axes. int i, digits; double y; if (Math.abs(x) < 0.0005 || Math.abs(x) > 500000) return x; else if (Math.abs(x) < 0.1 || Math.abs(x) > 5000) { y = x; digits = 0; if (Math.abs(y) >= 1) { while (Math.abs(y) >= 8.75) { y = y / 10; digits = digits + 1; } } else { while (Math.abs(y) < 1) { y = y * 10; digits = digits - 1; } } y = Math.round(y * 4) / 4; if (digits > 0) { for (int j = 0; j < digits; j++) y = y * 10; } else if (digits < 0) { for (int j = 0; j < -digits; j++) y = y / 10; } return y; } else if (Math.abs(x) < 0.5) return Math.round(10 * x) / 10.0; else if (Math.abs(x) < 2.5) return Math.round(2 * x) / 2.0; else if (Math.abs(x) < 12) return Math.round(x); else if (Math.abs(x) < 120) return Math.round(x / 10) * 10.0; else if (Math.abs(x) < 1200) return Math.round(x / 100) * 100.0; else return Math.round(x / 1000) * 1000.0; } double fudgeStart(double a, double diff) { // Adapted from the Pascal version of xFunctions. // Tries to find a "rounded value" withint diff of a if (Math.abs(Math.round(a) - a) < diff) return Math.round(a); for (double x = 10; x <= 100000; x *= 10) { double d = Math.round(a*x) / x; if (Math.abs(d - a) < diff) return d; } return a; } public static String realToString(double x) { return realToString(x,8); } public static String realToString(double x, int width) { // Goal is to return a reasonable string representation // of x, using at most width spaces. (If width is // unreasonably big or small, its value is adjusted to // lie in the range 7 to 25.) width = Math.min(25, Math.max(7,width)); if (Double.isNaN(x)) return "undefined"; if (Double.isInfinite(x)) if (x < 0) return "-INF"; else return "INF"; String s = String.valueOf(x); if (Math.rint(x) == x && Math.abs(x) < 5e15 && s.length() <= (width+2)) return String.valueOf( (long)x ); // return string without trailing ".0" if (s.length() <= width) return s; boolean neg = false; if (x < 0) { neg = true; x = -x; width--; s = String.valueOf(x); } long maxForNonExp = 5*(long)Math.pow(10,width-2); if (x >= 0.0005 && x <= maxForNonExp && (s.indexOf('E') == -1 && s.indexOf('e') == -1)) { s = round(s,width); s = trimZeros(s); } else if (x > 1) { // construct exponential form with positive exponent long power = (long)Math.floor(Math.log(x)/Math.log(10)); String exp = "E" + power; int numlength = width - exp.length(); x = x / Math.pow(10,power); s = String.valueOf(x); s = round(s,numlength); s = trimZeros(s); s += exp; } else { // constuct exponential form with negative argument long power = (long)Math.ceil(-Math.log(x)/Math.log(10)); String exp = "E-" + power; int numlength = width - exp.length(); x = x * Math.pow(10,power); s = String.valueOf(x); s = round(s,numlength); s = trimZeros(s); s += exp; } if (neg) return "-" + s; else return s; } private static String trimZeros(String num) { // remove trailing zeros if num contains a decimal point, and // remove the decimal point as well if all following digits are zero if (num.indexOf('.') >= 0 && num.charAt(num.length() - 1) == '0') { int i = num.length() - 1; while (num.charAt(i) == '0') i--; if (num.charAt(i) == '.') num = num.substring(0,i); else num = num.substring(0,i+1); } return num; } private static String round(String num, int length) { // Round off num to the given field width if (num.indexOf('.') < 0) return num; if (num.length() <= length) return num; if (num.charAt(length) >= '5' && num.charAt(length) != '.') { char[] temp = new char[length+1]; int ct = length; boolean rounding = true; for (int i = length-1; i >= 0; i--) { temp[ct] = num.charAt(i); if (rounding && temp[ct] != '.') { if (temp[ct] < '9') { temp[ct]++; rounding = false; } else temp[ct] = '0'; } ct--; } if (rounding) { temp[ct] = '1'; ct--; } // ct is -1 or 0 return new String(temp,ct+1,length-ct); } else return num.substring(0,length); } //--------------------------- Graph -------------------------------------- private double[] yValues; private double[] derivatives; private int[] pixelX; private int[] pixelY; private void drawGraph(Graphics g) { if (graphNeedsRedraw) { makeGraphCoords(); makeDerivs(); graphNeedsRedraw = false; } g.setColor(graphColor); for (int i = 1; i < pixelX.length; i++) { g.drawLine(pixelX[i-1], pixelY[i-1], pixelX[i], pixelY[i]); } } private void makeGraphCoords() { int pointCt = (canvasWidth-2*gap-2) / 3 + 2; if (pixelX == null || pixelX.length != pointCt) { pixelX = new int[pointCt]; for (int i = 0; i < pointCt - 1; i++) pixelX[i] = gap + i*3; pixelX[pointCt-1] = canvasWidth - gap - 1; pixelY = new int[pointCt]; } double dx = (xmax - xmin) / (canvasWidth - 2*gap - 1); double dy = (ymax - ymin) / (canvasHeight - 2*gap - 1); for (int i = 0; i < pointCt; i++) { double x = xmin + dx*(pixelX[i]-gap); double y = eval(x); pixelY[i] = (int)(gap + (ymax - y)/dy); } } private double eval(double x) { // NOTE: yvalues and derivatives are stored as if x and y range from 0 to 1!!! double scaledx = (x-xmin)/(xmax-xmin); int interval = (int)((points-1)*scaledx); if (interval >= points-1) return ymin + yValues[points-1]*(ymax-ymin); double temp = 1.0/(points-1); double a = yValues[interval+1]/(temp*temp*temp); double b = derivatives[interval+1]/(temp*temp) - 3*a; double d = -yValues[interval]/(temp*temp*temp); double c = derivatives[interval]/(temp*temp) - 3*d; double t1 = scaledx - interval*temp; double t2 = t1*t1; double t3 = t2*t1; double s1 = scaledx - (interval+1)*temp; double s2 = s1*s1; double s3 = s2*s1; double scaledy = a*t3 + b*t2*s1 + c*t1*s2 + d*s3; return ymin + scaledy*(ymax-ymin); } //--------------------------- Mouse Handling ----------------------------- private boolean dragging; private int lastPointNum; private double lastY; synchronized public boolean mouseDown(Event evt, int x, int y) { dragging = false; if (evt.shiftDown()) { for (int i = 0; i < points; i++) { yValues[i] = 0.5; derivatives[i] = 0; } graphNeedsRedraw = true; repaint(); return true; } if (evt.metaDown()) { smoothen(); return true; } if (graphNeedsRedraw) { repaint(); return true; } dragging = true; lastY = Double.NaN; jumpPoint(x, y); return true; } synchronized public boolean mouseUp(Event evt, int x, int y) { if (dragging) { jumpPoint(x,y); dragging = false; } return true; } synchronized public boolean mouseDrag(Event evt, int x, int y) { if (dragging && ! graphNeedsRedraw) jumpPoint(x,y); return true; } private void jumpPoint(int x, int y) { y = Math.max(gap, Math.min(canvasHeight-gap-1, y)); int pointNum = (int)(Math.round( ((double)(x-gap)/(canvasWidth-2*gap-1))*(points-1) )); double newYVal = 1.0 - ((double)(y-gap))/(canvasHeight-2*gap-1); // Scaled! if (pointNum >= 0 && pointNum < points) yValues[pointNum] = newYVal; if (Double.isNaN(lastY) || Math.abs(pointNum - lastPointNum) <= 1) { } else if (pointNum > lastPointNum) { double dy = (newYVal - lastY)/(pointNum - lastPointNum); for (int i = lastPointNum + 1; i < pointNum && i < points; i++) { if (i >= 0) yValues[i] = lastY + (i-lastPointNum)*dy; } } else { double dy = (newYVal - lastY)/(pointNum - lastPointNum); for (int i = lastPointNum - 1; i > pointNum && i >= 0; i--) { if (i < points) yValues[i] = lastY + (i-lastPointNum)*dy; } } lastPointNum = pointNum; lastY = newYVal; graphNeedsRedraw = true; repaint(); } private void makeDerivs() { double dx = 1.0 / (points - 1); derivatives[0] = (yValues[1] - yValues[0])/dx; for (int i = 1; i < points - 1; i++) { double left = Math.abs(yValues[i] - yValues[i-1]); double right = Math.abs(yValues[i+1] - yValues[i]); if (left < 1e-20 || right < 1e-20) derivatives[i] = 0; else derivatives[i] = ((1/right)*(yValues[i+1]-yValues[i]) - (1/left)*(yValues[i]-yValues[i-1]))/(2*dx*((1/right)+(1/left))); } derivatives[points-1] = (yValues[points-1] - yValues[points-2])/dx; } /* I changed to the derivatives to the preceding because the following more usual version gives funny "overshoot" when when there is a big jump in the curve between successive points. private void makeDerivs() { double dx = 1.0 / (points - 1); derivatives[0] = (yValues[1] - yValues[0])/dx; for (int i = 1; i < points - 1; i++) derivatives[i] = (yValues[i+1] - yValues[i-1])/(2*dx); derivatives[points-1] = (yValues[points-1] - yValues[points-2])/dx; } */ } // end class GummyGraph \ No newline at end of file diff --git a/htdocs/applets/xFunctions/example_file.txt b/htdocs/applets/xFunctions/example_file.txt new file mode 100755 index 0000000000..f24874375e --- /dev/null +++ b/htdocs/applets/xFunctions/example_file.txt @@ -0,0 +1,23 @@ + +function; -5,5,-5,5; table; Tbl; 3; -5,-2; -3,1; -1,3; 0,0; 2,1; 4,2; 5,1 +function; -5,5,-5,5; graph; B; -5,1,-1, -3,-0.5,1; -3,-0.5,-1, 0,2,0; 0,-2,4, 2.5,1,-1; 2.5,1,-1, 5,0,1 +function; 0,5,-1,4; expression; Split; ++ x^2; x < 1; 2*x - 1; x >= 1 and x < 2; 5 - x; x >= 2 and x < 3; (x-3)^2 + 1 + +multigraph; tan(x) = sin(x) / cos(x); -5,5,-5,5; tan(x); sin(x); cos(x) +animation; Tweening Animation; -8,8,-2,2,0,1,50; sin(x)*k + (1/(x^2+1))*(1-k); false +param; Squiggly Curve; -6,6,-6,6,0,6.3,300; 2*cos(5*t) + 3*cos(9*t); 2*sin(10*t) - 4*sin(2*t) +derivatives; Bezier Curve Derivative; -5,5,-5,5; b(x) +reimann; Close to e; 1,2.718,0,1.5,10; 1/x; 3 +graph3d; Singularity; -2,2,-2,2,-2,2,20; 1/(x^2 + y^2); 2 +g; 3D Graph with Table Function; -5,5, -5,5, -5,10, 32; Tbl(x)*Tbl(y); 4 + +integral curves; Attracting/Repelling; -1.5,1.5,-1.5,1.5,0,1,0.01; x^2 + y^2 - 1; (x-y) * (x+y); 3; true; ++ -1.2,-1.2; -0.8,,-1.2; -0.4,,-1.2; 0,,-1.2; .4,,-1.2; .8,,-1.2; 1.2,-1.2,; ++ -1.2,-0.8; -0.8,-0.8; -0.4,-0.8; 0,-0.8; .4,-0.8; .8,-0.8; 1.2,-0.8; ++ -1.2,-0.4; -0.8,-0.4; -0.4,-0.4; 0,-0.4; .4,-0.4; .8,-0.4; 1.2,-0.4; ++ -1.2,0; -0.8,0; -0.4,0; 0,0; .4,0; .8,0; 1.2,0; ++ -1.2,.4; -0.8,.4; -0.4,.4; 0,.4; .4,.4; .8,.4; 1.2,.4; ++ -1.2,.8; -0.8,.8; -0.4,.8; 0,.8; .4,.8; .8,.8; 1.2,.8; ++ -1.2,1.2; -0.8,1.2; -0.4,1.2; 0,1.2; .4,1.2; .8,1.2; 1.2,1.2 + diff --git a/htdocs/applets/xFunctions/index.html b/htdocs/applets/xFunctions/index.html new file mode 100644 index 0000000000..de1facc7cd --- /dev/null +++ b/htdocs/applets/xFunctions/index.html @@ -0,0 +1,17 @@ + + name="file" value="example_file.txt" + + + diff --git a/htdocs/applets/xFunctions/using_examples.html b/htdocs/applets/xFunctions/using_examples.html new file mode 100755 index 0000000000..f7757c7a4f --- /dev/null +++ b/htdocs/applets/xFunctions/using_examples.html @@ -0,0 +1,446 @@ + + +xFunctions Educational Mathematics Applet -- Using Examples + + + +

      +

      xFunctions: Using Examples

      +
      + +
      + +

      xFunctions is an educational applet +for exploring several aspects of calculus and precalculus mathematics. +Its use is fully described on the main xFunctions Web page. +This page, on the other hand, explains how extra functions and examples can be added to xFunctions. +The examples can be specified as applet parameters in the HTML source code of +a Web page. If xFunctions is run as a standalone applications, it can read +examples from a file that is specified as a command line parameter. +Unfortunately, if you want to create such examples, you have to +encode them by hand. This page explains how to do the encoding.

      + +

      There are two versions of the applet, one that appears right on the Web +page and one that can be launched in a separate window. Examples are used +in the same way with either version of the applet. You can see them in the +"Launcher" version of the applet on the main page. +The xFunctionsLauncher applet on that page is +specified with the following rather long applet tag, which encodes three example +functions and eight examples for the various xFunctions utilities:

      + +
      +   <applet  archive="xFunctions.zip"  code="xFunctionsLauncher.class"  width=200 height=30>
      +      <param  name="1"   value="function; -5,5,-5,5; table; Tbl; 3; -5,-2; -3,1; -1,3; 0,0; 2,1; 4,2; 5,1">
      +      <param  name="2"   value="function; -5,5,-5,5; graph; B; -5,1,-1, -3,-0.5,1; -3,-0.5,-1, 0,2,0; 0,-2,4, 2.5,1,-1; 2.5,1,-1, 5,0,1">
      +      <param  name="3"   value="function; 0,5,-1,4; expression; Split;">
      +      <param  name="4"   value="+  x^2; x < 1; 2*x - 1; x >= 1 and x < 2; 5 - x; x >= 2 and x < 3; (x-3)^2 + 1">
      +      <param  name="5"   value="multigraph; tan(x) = sin(x) / cos(x); -5,5,-5,5; tan(x); sin(x); cos(x)">
      +      <param  name="6"   value="animation; Tweening Animation; -8,8,-2,2,0,1,50; sin(x)*k + (1/(x^2+1))*(1-k); false">
      +      <param  name="7"   value="param; Squiggly Curve; -6,6,-6,6,0,6.3,300; 2*cos(5*t) + 3*cos(9*t); 2*sin(10*t) - 4*sin(2*t)">
      +      <param  name="8"   value="derivatives; Bezier Curve Derivative; -5,5,-5,5; b(x)">
      +      <param  name="9"   value="reimann; Close to e; 1,2.718,0,1.5,10; 1/x; 3">
      +      <param  name="10"  value="graph3d; Singularity; -2,2,-2,2,-2,2,20; 1/(x^2 + y^2); 2">
      +      <param  name="11"  value="g; 3D Graph with Table Function; -5,5, -5,5, -5,10, 32; Tbl(x)*Tbl(y); 4">
      +      <param  name="12"  value="integral curves; Attracting/Repelling; -1.5,1.5,-1.5,1.5,0,1,0.01; x^2 + y^2 - 1; (x-y) * (x+y); 3; true;">
      +      <param  name="13"  value="+   -1.2,-1.2; -0.8,,-1.2; -0.4,,-1.2;  0,,-1.2;  .4,,-1.2;  .8,,-1.2;  1.2,-1.2,;">
      +      <param  name="14"  value="+   -1.2,-0.8; -0.8,-0.8; -0.4,-0.8;  0,-0.8;  .4,-0.8;  .8,-0.8;  1.2,-0.8;">
      +      <param  name="15"  value="+   -1.2,-0.4; -0.8,-0.4; -0.4,-0.4;  0,-0.4;  .4,-0.4;  .8,-0.4;  1.2,-0.4;">
      +      <param  name="16"  value="+   -1.2,0; -0.8,0; -0.4,0;  0,0;  .4,0;  .8,0;  1.2,0;">
      +      <param  name="17"  value="+   -1.2,.4; -0.8,.4; -0.4,.4;  0,.4;  .4,.4;  .8,.4;  1.2,.4;">
      +      <param  name="18"  value="+   -1.2,.8; -0.8,.8; -0.4,.8;  0,.8;  .4,.8;  .8,.8;  1.2,.8;">
      +      <param  name="19"  value="+   -1.2,1.2; -0.8,1.2; -0.4,1.2;  0,1.2;  .4,1.2;  .8,1.2;  1.2,1.2">
      +   </applet>
      +
      +
      + +
      + +

      Applet Params and Files

      + +

      An applet parameter is specified by a <param> tag between the opening <applet> +tag and the closing </applet>. The applet can read these parameters when it runs. +A <param> tag has the form + +

                    <param  name="some name"  value="some value">
      +
      + +

      For the xFuctions applet, the names must be the consecutive numbers 1, 2, 3, 4, ... +You can have as many as you want, but you can't skip any. The applet will stop reading +as soon as it encounters a missing number. (By the way, the applet requests params by +name, and it doesn't matter to the applet what order the params appear in the +<applet> tag. That's why you need the names.)

      + +

      When xFunctions processes these parameters, it will simply process all the param values +in order, as if they were lines in a file. In fact, if you run xFunctions as a standalone +application, you can put the exact same lines in a file and tell xFunctions to read them +when it starts up. (You do this by providing the file name as a command line argument +when you run xFunctions. If you download xFunctions, using one of the downloading links +on the main xFunctions page, you'll find a +README file that explains more about running xFunctions as +an application. You also find a file of examples +which contains the same examples given in the applet tag above.)

      + +

      Now, when xFunctions reads the examples, either from applet params or from a file, +any line beginning with a plus sign (+) is appended to the preceding line. (The plus +sign is discarded.) This allows you to spread long example definitions over several +lines. After lines have been joined in this way, each line defines one example.

      + +

      So, all you need to know is how to compose a line to specify each of the possible types of example. +It's not all that complicated, but the computer is picky about the syntax, and it's easy to +make mistakes. If an example contains an error, xFunctions will ignore the example. (It won't crash.) +To help you find the problem, xFunctions writes messages to standard output as it processes +the example descriptions. If it finds an error, it prints an error message. If you run +xFunctions in a Web browser, you will have to figure out where it actually prints these +messages. Netscape, for example, prints them to a "Java Console." You can open this +console with an entry under the "Communicator" menu (possibly inside a "Tools" submenu +in this menu, depending on what version of the program you are using).

      + +

      (Note: As an alternative to -- or in addition to -- using applet params to specify +examples in an applet, you can put the examples in a file and tell the applet to read +that file. The file should be on the Web server in the same directory with the +source file of the Web page. Then you just have to +give the applet the name of the file as an applet parameter. The name is specified +in a <param> tag whose name is "file" and whose value is the name of the file. +For example: <param name="file" value="example_file.txt">.)

      + +

      Every example specification consists of a series of items separated by +semicolons (;). The first item tells what kind of example it is. This can +be the word "function" to specify that you are defining a new function, or it can be +the name of a utility (Multigraph, Animate,...) to specify that you are giving an +example for one of the utilities. Actually, xFunctions only looks at the first +character, so you can say "f" or "func" or "fried green tomatoes" instead of +"function". Also, you can use either upper or lower case. The rest of the items +in the example depend on which type of example it is. I'll give lists of the +items required for all the different types of examples.

      + +

      + +

      Defining New Functions in Examples

      + +

      It is possible to define new functions as examples. These will be added to the list of +functions that appears on xFunction's main screen. Once they are in that list, they can +be used in the Utilities and in the definitions of other functions -- you can even use them +in later examples. There are three ways to define functions: as one or more expressions, +as a graph, or as a table of xy-points. I'll describe each of these possibilities +separately, but all three types of function specification begin with the same +four items (separated by semicolons): the word "function" (or any string beginning with +an "f"), the xy-limits for the function, the type of function, and the name of the function.

      + +

      The xy-limits consist of four numbers, separated by commas. These are the xmin, xmax, +ymin, and ymax values that will be used in the corresponding input boxes when the function +is graphed on the Main Screen. Note that they are not the domain and range of the function. +They merely specify what region of the xy-plane is displayed on the Main Screen by default. +The type of function is one of the words "expression", "table", or "graph". +Again, xFunctions will actually only look at the first character, so you can abbreviate +these to "e", "t", and "g". The name of the function can be any sequence of +letters and digits, as long as it begins with a letter. You cannot redefine a function +that is already defined. The letters can be either upper or lower case. (When names are +used in expressions, xFunctions doesn't distinguish between upper and lower case. +However, when it places functions in the list on the Main Screen, it will order +upper case letters before lower case letters. You can make the functions that you define +easier to find if you start their names with upper case letters, so that they appear +at the top of the list.)

      + + +

      Expression Functions

      + +

      An expression function is defined by from one to eight expressions. These are the same +expressions that would be entered in the Expression Function Input Screen in xFunctions. +Note that if you have more than one expression, then every even-numbered expression +must be a logical-valued expression (such as "x <= 3") which defines the +domain on which the previous expression is valid. For an expression function example, +the defining expressions are simply listed as separate items in the example -- separated +by semicolons -- after the four standard items. Here is list of the items that +go into an expression function example:

      + +
        +
      1. the word "function" (or anything beginning with f) +
      2. a list of four numbers, separated by commas, giving the xy-limits for the display on the Main Screen +
      3. the word "expression" (or anything beginning with e) +
      4. the name of the function you are defining +
      5. anywhere from one to eight expressions that define the function (separated by more semicolons) +
      + +

      For example, here is how you could define the hyperbolic functions and add them to the function list +one the Main Screen. Once they are there, they can be used just like the built-in functions. +(Note that spaces between items or between numbers in a list are not significant.)

      + +
                   f; -3,3,-3,3; e; sinh; (e^x - e^(-x)) / 2
      +             f; -3,3,-3,3; e; cosh; (e^x + e^(-x)) / 2
      +             f; -3,3,-3,3; e; tanh; sinh(x) / cosh(x)
      +
      + + +

      Table Functions

      + +

      A table function is defined by a list of xy-points that lie on the graph of the function. +You also have to specify how to fill in the graph between points. There are three possible +styles: a step function, a piecewise linear function, or a smooth function. +In an example, the style is specified (in order of increasing "quality") as one of the +numbers 1, 2, or 3. The specification for a table function includes the following items:

      + +
        +
      1. the word "function" (or anything beginning with f) +
      2. a list of four numbers, separated by commas, giving the xy-limits for the display on the Main Screen +
      3. the word "table" (or anything beginning with t) +
      4. the name of the function you are defining +
      5. one of the numbers 1, 2, or 3 to specify whether its a step function, piecewise linear, or smooth +
      6. two or more points, where each point is a pair of numbers separated by a comma. The +x-values of the points must be in strictly increasing order. The points are separated by semicolons. +
      + +

      For example, this defines a piecewise linear function named "W" that looks like a W"

      + +
                    f; -5,5,0,5; t; W; 2; -4,4; -2,0; 0,2; 2,0; 4,4
      +
      + + +

      Graph Functions

      + +

      A graph function is a sequence of Bezier segments. Each segment is defined by +a list of six numbers, separated by commas. The numbers give:

      + +
        +
      • the x-coordinate of the left endpoint +
      • the y-coordinate of the left endpoint +
      • the slope of the graph at the left endpoint +
      • the x-coordinate of the right endpoint +
      • the y-coordinate of the right endpoint +
      • the slope of the graph at the right endpoint +
      + +

      The x-coordinates in a segment must agree with the x-coordinates of its neighbors. +(It is not possible to define graphs with gaps in the domain.) The y-coordinates +don't have to agree, so you can make functions with discontinuities. Even if the +y-coordinates of neighboring segments do agree, the slopes don't have to +agree, so you can make functions with corners.

      + +

      The specification of a graph function consists of the following items: + +

        +
      1. the word "function" (or anything beginning with f) +
      2. a list of four numbers, separated by commas, giving the xy-limits for the display on the Main Screen +
      3. the word "graph" (or anything beginning with g) +
      4. the name of the function you are defining +
      5. specifications for one or more bezier segments, separated by semicolons. Each segment is +specified as a list of six numbers, as described above, separated by commas. +
      + +

      Here is an example that defines a graph function named "Grf" consisting of two bezier segments with a +sharp corner at the point (0,0) and with horizontal tangents at (-2,1) and (2,1)

      + +
                  f; -2,2,0,2; g; Grf; -2,1,0, 0,0,-5; 0,0,5, 2,1,0;
      + + +

      + +

      Examples for the Utilities

      + +

      To define an example for one of the seven utilities, you have to say what goes into +each of the inputs on that utility screen. In all cases, this includes a set of values +for xmin, xmax, ymin, ymax, and possibly for other numerical values. It also includes +one or more expressions to define the functions that the utility will use. There might +be other inputs, such as the checkbox labeled "Loop back and forth" in the Animate Utility +or the radio buttons for controlling the style of graph in the Graph 3D Utility. +I'll describe the exact format you need in order to specify the inputs for each of the +utilities. In all cases, the first item in the example is the name of the utility, +which can be abbreviated to as little as one letter. This is followed by a menu entry, +which is the string that will appear in the pop-up menu in xFunctions to describe this +example to the user. (It has to be short enough to fit into this menu.) +Next comes a list of numbers, separated by commas, that go into the +numerical input boxes in the utility. +This is followed by one or more expressions, separated by semicolons. Any further +inputs are specified after that.

      + + +

      Multigraph Utility

      + +

      An example for the multigraph utility is specified by

      + +
        +
      1. the word "multigraph" (or anything beginning with m) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 4 numbers, separated by commas, specifying xmin, xmax, ymin, and ymax +
      4. anywhere from one to eight expressions, giving the functions of x that are to be +graphed, separated by more semicolons. +
      + +

      For example:

      + +
                      m; An interesting function; -1,2,0,2; abs(x)^x
      +                multigraph; Two halves of a circle; -2,2,-2,2; sqrt(1-x^2); -sqrt(1-x^2)
      +
      + + +

      Animate Utility

      + +

      For the animate utility, the list of numbers has seven entries. In addition to +xmin, xmax, ymin, and ymax, there are kmin and kmax, which give the limits on the parameter +k, and the number of intervals in the +animation. The number of intervals must be between 1 and 1000, inclusive. +The expression to be animated uses the variables x and k. There is a final item +that specifies whether the "Loop back and forth" checkbox should be on. +The value of this item must be specified as "true" or "false". (As usual, you can +abbreviate to one character.) When the user selects an animation example, the +animation will start playing automatically after a second or two. So, an Animate +Utility example consists of:

      + +
        +
      1. the word "animate" (or anything beginning with a) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 7 numbers, separated by commas, specifying xmin, xmax, ymin, ymax, kmin, kmax + and the number of intervals, where the number of intervals is between 1 and 1000 +
      4. an expression that uses the variables x and k +
      5. one of the values "true" or "false" to specify whether the animation loops back and forth +
      + +

      For example:

      + +
                  a; Functions Converging to f(x)=x; 0,1.2,0,1.2,0,50,50; x^k; false
      +
      + + +

      Parametric Curves Utility

      + +

      The Parametric Curves Utility requires seven numerical inputs. The three extra inputs +are the values of tmin and tmax (the range of values for the parameter) and the +number of points that are plotted on a curve. The number of points must be between +2 and 1000, inclusive. You can have up to eight curves. For each two curves, +you need two expressions involving the variable t, one to define +x(t) and one to define y(t). A Parametric Curves example consists of:

      + +
        +
      1. the words "parametric curves" (or anything beginning with p) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 7 numbers, separated by commas, specifying xmin, xmax, ymin, ymax, tmin, tmax, +and the number of points on a curve. The number of points must be between 2 and 1000. +
      4. an even number of expressions, from two to sixteen, +giving the functions of t that define x(t) and y(t) for each curve, separated by more semicolons. +
      + +

      For example (using a + on the second line to break the example into two lines):

      + +
                    p; A Circle and Two Ellipses; -3,3, -3,3, 0,6.29, 200; 2*cos(t); 2*sin(t);
      +              +   2*cos(t); sin(t);   cos(t); 2*sin(t)
      +
      + + +

      Derivatives Utility

      + +

      An example for the derivatives Utility consists of the following items:

      + +
        +
      1. the word "derivatives" (or anything beginning with d) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 4 numbers, separated by commas, specifying xmin, xmax, ymin, and ymax +
      4. an expression giving the function of x +
      + +

      For example:

      + +
                   d; Vertical tangent at x=0?; -2,2,-2,2; abs(x)^x
      +
      + + +

      Riemann Sums Utility

      + +

      The Riemann Sums Utility requires five numerical inputs. The last one is the number of +intervals into which the domain is to be divided. This must be a number between 1 and 512, +inclusive. You also have to specify which summation method will be displayed on the +graph. There are six possible methods, which are specified by the numbers from 1 to 6: +left endpoint rule, right endpoint rule, midpoint rule, inscribed rectangle rule, +circumscribed rectangle rule, and trapezoid rule. So, an example consists of:

      + +
        +
      1. the words "riemann sums" (or anything beginning with r) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 5 numbers, separated by commas, specifying xmin, xmax, ymin, ymax, and +the number of subintervals. The number of subintervals must be between 1 and 512. +
      4. An expression giving the function of x +
      5. A number from 1 to 6 specifying the display method +
      + +

      For example:

      + +
               r; Area Under a Parabola; 0,1,-0.2,1.2,6; x^2; 3
      +
      + + +

      Integral Curves Utility

      + +

      An Integral Curves example requires seven numbers. The fifth and sixth of these +go in the boxes labeled "Start x" and "Start y" in the utility. (These values give +a point where a curve will start when the user presses the New Curve button. +You might not find it useful to provide such a point in an example, but you have +to include it anyway.) The seventh number is the "dt" value that specifies time +between points on the curves. The example must also specify the integration +method as a number 1, 2, or 3 standing for Euler's method, Runge-Kutta Order 2, +or Runge-Kutta Order 4 (in order of increasing "quality"). +Finally, you have to specify whether the checkbox labeled "Extend curves +in both directions" is checked. You do this with one of the values +"true" or "false", which can be abbreviated to just "t" or "f".

      + +

      The Integral Curve utility draws integral curves starting at specified points. +You can, optionally, include a list of points in your example where curves +are to be started. Each point is specified as a pair of numbers, separated by +a comma. If your example includes such points, then the curves will be started +a second or two after the user chooses the example from the menu.

      + +

      An Integral Curve example consists of

      + +
        +
      1. the words "integral curves" (or anything beginning with i) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 7 numbers, separated by commas, specifying xmin, xmax, ymin, ymax, +Start x, Start y, and dt. The value of dt must be greater than zero. +
      4. two expressions using the variables x and y, separated by a semicolon. +These give the values of dx/dt and dy/dt. +
      5. A number 1, 2, or 3 specifying the integration method. +
      6. One of the values "true" or "false" to specify whether curves are to be +drawn both forwards and backwards in time +
      7. Optionally, you can add any number of points. Each point is a pair of +numbers, separated by a comma. The points are separated by semicolons. +
      + +

      Here is an example that does not use extra points. For an example with +points, see the example in the applet tag at the top of this page.

      + +
                  i; Pendulum Phase Space; -4, 4, -4, 4, 0, 0, 0.1; y; sin(x); 3
      +
      + + +

      Graph 3D Utility

      + +

      For the Graph 3D Utility, you need seven numbers, including zmin, zmax, and the +grid size. The grid size is a number between 8 and 64, inclusive, that determines +how many points are plotted on the graph. Then, in addition to a function of x and y, +you need to specify which type of graph will be drawn. There are four types, +which are specified by the numbers 1, 2, 3, and 4 (in order of increasing "quality"): +wireframe model, wireframe with hidden lines removed, shaded model, and shaded model +with wires. So, an example for Graph 3D consists of:

      + +
        +
      1. the words "graph 3d" (or anything beginning with g) +
      2. the menu entry for the example, which can be any (short) string +
      3. a list of 7 numbers, separated by commas, specifying xmin, xmax, ymin, ymax, +zmin, zmax, and the grid size. The grid size must be between 8 and 64. +
      4. an expression giving the function of x and y that is to be graphed +
      5. A number between 1 and 4 specifying the style of graph. +
      + +

      For example:

      + +
                      g; Eggcrate; -4,4,-4,4,-2,2,50; sin(x)*cos(y); 4
      +
      + + + +

      +
      David Eck +(eck@hws.edu), 27 October 1999
      + +
      + + + + diff --git a/htdocs/applets/xFunctions/xFunctions.html b/htdocs/applets/xFunctions/xFunctions.html new file mode 100755 index 0000000000..d1ee109ce4 --- /dev/null +++ b/htdocs/applets/xFunctions/xFunctions.html @@ -0,0 +1,421 @@ + + + + + xFunctions xPresso Educational Mathematics Applet + + + +math dept +

      +[Up] +[Feedback] +


      +

      + +This applet was created by David Eck (see below). It is the web version +of the graphing program which I often use in class. +
      + +
      +

      Quick Instructions

      + +

      When the applet starts up, it is showing a "Main Screen" where you can see the graph of +functions. The available functions are shown in a list on the left. Click on +a function to view its graph. You can define your own functions to add to +this list using the three buttons at the lower left of the Main Screen. +There are three ways to define new functions: using expressions (such as +0.5*x^2+sin(3*x-2)), by giving the graph of the function, or by listing a +table of (x,y) pairs. There is a separate input screen for each of these input methods. +To get back to the Main Screen from one of these input screens, you have +to press a "Done" or "Cancel" button. +The pop-up menu at the top of the applet can be used to go to seven other +screens. Each screen is a separate "utility" that allows you to play with +functions in a different way. When you enter functions into the utility +screens, you can use any new functions that you have defined, as well as the built-in +functions.

      + +

      In the "Launcher Version," you'll find a few extra functions in the list on the Main Screen. +You'll also find some extra entries in the pop-up menu. These extra entries are examples that +will take to one of the utility screens, and load an example in to that screen. +(Information about making examples for xFunctions +can be found here -- but to do that, you need to make your own Web pages.)

      + +

      Now, just go ahead and play!

      + +
      + +

      More Detailed Instructions

      + +

      First some general ideas

      + +
        +
      • Expressions + in xFunctions can include the operations +, -, *, /, and +^, where ^ represents exponentiation (this is, x^3 is x*x*x). You always have +to type multiplication explicitly as *. When you use a function such as +sin or f in an expression, you have to put parentheses around its argument. +That is, you have to say "sin(x^2)" rather than "sin x^2". +You can use the constants e and pi in expressions. +
      • Built-in functions are shown on the main +screen. Some might not be obvious: "abs" is absolute value. "sqrt" +is square root and cubert is the cube root. "ln" is the +natural logarithm and "exp" is exponentiation in the base e (same as e^x). +"round(x)" is the nearest integer to x, "floor(x)" is the integer +just below or equal to x, "ceiling(x)" is the integer just above or +equal to x, and "trunc(x)" in the integral part of x after any decimal +part is thrown away. +
      • Graph limits and other limits can be set by +filling in the text boxes labeled xmin, tmax, and so on. Usually, +if you press return in any text box, the graph will be redrawn, so you +don't have to use the buttons too much. Many screens have buttons for zooming in +and out on the graph. A "Restore" button will restore the original limits on the +graph. An "Equalize" button will reset the xy limits on the graph so that the +scales on the horizontal and vertical axes are equal. (This makes circles look +like circles instead of ellipses, and it lets you estimate slope visually.) +
      • The mouse can be used for zooming on some of the applet's +screens. Click with the right mouse button at a point (or command-click on Macintosh) +to zoom in on that point. Click with the middle mouse button (or option-click or +ALT-click) to zoom out from the point). +
      • Error messages are displayed in the same region of +the screen where graphs are drawn. An error message will go away if you click on it. Or, you +can just fix the problem and the error message will go away the next time a +graph is drawn. +
      • Never trust a computer! Do not assume that +what you see in this program is perfectly accurate. For one thing, there are +probably some bugs -- out and out programming errors. More important, the methods +used in computations are approximations and in some cases guesses. Graphs are +drawing by plotting a lot of points and connecting them with lines. Since a lot +of points are used, generally the results are pretty good, but if the graph +varies rapidly the results can be positively deceptive. +
      + + +

      The Main Screen

      + +

      The main screen is not the most interesting part of +xFunctions! The more interesting "utility screens" are described +below. However, you have to use the main screen if you want to +define new functions to use in the utility screens.

      + +

      The main screen shows a list of available functions on the left. +Click on a function to see its graph. To add to this list, click on one +of the buttons "New Expr.", "New Table", or "New Graph" in the lower left +corner. This will take you to a screen where you can input a function. +These input screens are describe below. If the function that is hilited +in the list is a user-defined function, as opposed to one of the +built-in functions, then the "Edit" button is enabled. If you click the +"Edit" button, it will take you to the appropriate function input screen, +where you can modify the definition of the function.

      + +

      If you click on the graph, a cross-hair will appear marking a point on the +graph. The coordinates of the point will appear under the graph. +There is also an input box under the graph where you can input an +x-value. This gives you much finer control of the x-value +than you can get by clicking on the graph. +Either press return in this box or click the "Set" button, +and the corresponding point will be marked on the graph. If you +click-and-drag on the graph, the cross-hair will move as you drag +the mouse.

      + +

      Right-click on the graph to zoom in on a point. Click with +the middle-mouse button, or hold down the ALT key and left-click, +to zoom out. You can also control the ranges of values displayed on the +x- and y-axes with the controls along the right edge of the screen.

      + +

      The Utilities

      + +

      The main point of xFunctions is its +seven "utilities." Each utility is a screen that allows you to work +with functions in a different way. To go to a utility, select its name from +the pop-up menu at the top of the applet. This pop-up menu is +visible on the Main Screen and on each of the utility screens, so +it is easy to move among these screens. (The pop-up menu is not +available on the function input screens.) On other Web pages that use +xFunctions, there might also be "examples" in this pop-up menu. Selecting one of the +examples will take you to one of the utility screens and load data for some +example into that screen.

      + +

      Below, you'll find brief instructions for each of the seven utilities. +However, much of the interface for the utilities is pretty self-explanatory.

      + +

      Expression Function Input Screen

      + +

      When you click the "New Expr." button on the main screen, you will be +taken to an input screen where you can define a function as an +expression such as "x^2+2*x+1". You can also define split +functions, which have different expressions on different parts of their +domains.

      + +

      To define a function, set the +name of the function in the input box the top of the screen (or just accept the +name that is already provided there). To define a function using just one +expression, just fill in the box labeled "Let y =". +If you want to limit the domain of the function, you can +enter a condition such as "x > -1 and x < 1" in +the box labeled "provided:". The condition can use the relational operators +=, <>, <, >, <=, and >=, and it can use the logical operators +"and", "or", and "not". You can use any expression as part of the condition, +such as "abs(sin(x)) < 0.5". +You can define a function by up to four cases +by filling in more of the boxes labeled "or y=" and "provided". +For example: Let "y = x^2" provided "abs(x) < 1" or "y = x^3" provided +"x >= 1" or "y = 1" provided "x <= -1".

      + +

      Click the "Test" button to see the graph of your function. The limits on the +axes here do not limit the domain of the function. They are only used to determine +what part of the graph will be shown by default on the Main Screen.

      + +

      Click the "Done" button to define the function and go back to the Main Screen. +You must click either the "Done" or "Cancel" button to get out of +the Expression Function Input Screen.

      + +

      Graph Function Input Screen

      + +

      Functions don't have to be defined by expressions! They can also be +defined by graphs and by tables of values. From the Main Screen, click +the "New Graph" button to go to the Graph Function Input Screen, where +you can define a function as a graph. Technically, the graphs used here +are Bezier functions, which are made up of segments. Each segment is +a piece of a cubic polynomial, which is completely determined by the +two points at the ends of the segments and by the slopes at those two +points. It is possible to make graphs that have discontinuities, +corners, and cusps.

      + +

      A new function is just a line segment on the x-axis, with a point at each +end. You can double-click the graph to introduce new points. Each point +has a "handle", shown as a black point nearby. You can click-and-drag the +points and the handles to modify the graph. Other operations are described +in the "Brief Instructions" at the top right of the screen. If you want +fine control over the x-value of a new endpoint, enter the x-value in the +box labeled "at x =" and either press return or click the "Insert New Point" +button. Note that the x and y limits in the lower right do not affect +the shape of the graph, just how the xy-values on the graph are interpreted.

      + +

      Click the "Done" or "Cancel" button to return to the main screen.

      + +

      Table Function Input Screen

      + +

      Use the Graph Function Input Screen if you want to set the general +shape of a graph by hand. If you have a list of xy-points on the +graph, enter them as a table in the Table Function Input Screen. +You get to this screen by pressing the "New Table" button on the Main +Screen.

      + +

      Just enter the pairs of xy-values one-by-one in the boxes labeled +"Input x" and "Input y". (You can press tab after entering the x-value, +and press return after entering the y-value.) The points will be shown +in a list in the upper right corner of the screen. If you click on +a point in the list, it will be copied to the input boxes, so you can +edit it. There is a button for deleting the selected point from the +list.

      + +

      Just knowing some points on the graph doesn't tell you what the whole +function looks like. You have to specify what happens between the points. +You have three choices: A "Step Function" has a constant value between +two consecutive points from your list. For a "Piecewise Linear Function", +the points are connected by line segments. A "Smooth Function" is +continuous and differentiable at all points. (It's actually a Bezier function, +defined by cubic polynomials between the points specified in your list.)

      + +

      A preview of the graph is shown in the lower right (but not until there are +at least two points). The limits on the axes are increased if necessary, and +or can set them yourself.

      + +

      Click the "Done" or "Cancel" button to get back to the Main Screen.

      + +

      Multigraph Utility

      + +

      The Multigraph Utility Screen let's you draw up to eight functions in different +colors on the same set of axes. You can select the number (1 to 8) of the +function to be graphed with a pop-up menu near the lower left of the +screen. Each curve is a different color, shown in a +colored patch next to the pop-up menu. +Enter the function and press return (or click "Graph!") +to graph the function. Right-clicking on the graph will +zoom in on a point. Click with the middle mouse button, or +hold down the ALT key and left-click, to zoom out. +

      + +

      Animate Utility

      + +

      The Animate Utility Screen lets you work with a function whose definition can +contain the parameter k, as well as the variable x. This is really a family +of different functions, one for each value of k. The Animate Utility +plays a "movie" showing a sequence of the graphs for different values of k. +There are input boxes where you can specify the values of k at the start and +end of the movie. You can also specify the number of intervals in the movie. +(The number of frames is the number of intervals plus one.) What happens +when you get to the end of the movie? The Animate Utility can either jump directly back +to the beginning, or it can play the movie backwards. Which it does is controlled +by a checkbox labeled "Loop Back and Forth." +The "Go" button will start the animation. The "Next" button will move the +animation from one frame to the next. You can use the mouse to zoom in out on the graph.

      + + +

      Parametric Curves Utility

      + +

      The Parametric Curves Utility draws parametric curves in the xy-plane. +A parametric curve is a plot of xy-points where the x and y coordinates +are functions of some parameter. In this utility, the parameter is t. +For example, the parametric curve defined by x=cos(t), y=sin(t) for +t between 0 and 6.3 is a circle. (6.3 is about 2*pi.)

      + +

      In this utility, you can enter x and y functions for up to eight +parametric curves. There is a pop-up menu near the bottom left corner +of the screen that you can use to select the curve whose definition you +want to enter/display. Each curve is a different color, shown in a +colored patch next to the pop-up menu. +There are input boxes for specifying the +range of t-values to use for the curves. You can also specify the +number of points on each curve. (Remember that xFunctions draws graphs +by plotting points and connecting them with lines. The results for +discontinuous or rapidly changing functions might not be great. You might +get a better curve by increasing the number of points.)

      + +

      There is a button labeled "Trace". If you click this button, a crosshair +will be moved along the curve whose number is specified in the pop-up menu. +This lets you see how the point on the curve varies as t changes. The crosshair +disappears after the curve has been traced once.

      + +

      Zooming with the mouse will work in this utility. + +

      Derivatives Utility

      + +

      The Derivatives Utility will draw the graphs of a function, the +first derivative of the function, and the second derivative of the +function. The three graphs are displayed in separate panels in +the bottom half of the applet. If you left-click and drag on the +graph of the function, a tangent line to the graph will be displayed. +At the same time, the corresponding point on the graph of the first +derivative will be marked. (You can also click and drag on the +other graphs and see what happens.) Mouse zooming will also work +in this utility.

      + +

      Riemann Sums Utility

      + +

      The Riemann Sum Utility computes Riemann sums for a specified function on +a specified interval. The values of various Riemann sums, using +different rules, are shown at the right. (The trapezoid rule isn't +strictly a Riemann sum, but it's there as a choice anyway.) +The area corresponding to one of the sums is displayed on the graph. Use the pop-up +menu at the lower right to select which area is displayed. If you +click on the graph, you get details about one of the +rectangles or trapezoids in the sum. (Click on the blue information box +to get rid of it.) Note that for the +"~Circumscribed" and "~Inscribed" rules, the maximum and minimum +of the function on a sub-interval is only computed approximately. +(This explains the "~", which stands for "approximately.") Mouse zooming does +not work on this screen.

      + +

      Integral Curves Utility

      + +

      The Integral Curves Utility draws direction fields and integral curves. +An integral curve is the path followed by a point whose velocity +at each point is given by a "vector field". A vector field on the +xy-plane is given by a pair of functions of x and y. For the curve, +these functions specify the derivatives dx/dt and dy/dt at each point. +In the Integral Curves Utility, you can specify the functions dx/dt +and dy/dt, and you can "drop" points onto the xy-plane and see the +curves along which they move. The utility also draws a grid of arrows. +An arrow at a point shows the direction of the integral curve as it +passes through that point. (This is a "direction field" rather than a +"vector field" because it shows only the direction of the curve, +not the speed.)

      + +

      You can start a curve by clicking at a point on the graph. Alternatively, +for finer control, you can enter the x and y values of the starting +point of the curve in the boxes labeled "Start x" and "Start y". Then, +click the "New Curve" button to start a curve at that point. +You can have as many curves as you want. +If you turn on the "Extend curves in both directions" option, +you get to see how the point would move backwards in time as well as +forwards.

      + +

      An integral curve is drawn by looking at the current position of +the point and at the derivative functions at that point (and possibly +at some nearby points for greater accuracy). This is used to project +the point forward "dt" time units into the future. This +is only an approximation! A smaller value of dt will generally give +a better approximation. There is an input box where you can specify +the value of dt. The Integral Curves Utility can use +three different approximations: Euler's Method, Runge-Kutta Order 2, +and Runge-Kutta Order 4. The utility has a set of radio buttons that +you can use to select the method. Runge-Kutta Order 4 is the most accurate +method, and there is really no reason to use the other two unless you +specifically interested in the method rather than in getting +the best possible approximation of the true integral curve.

      + +

      Once a curve has been started, it uses the same method and the same +value of dt forever. This lets you start different curves from the +same point, using different methods or different values of t, to compare them.

      + +

      In this utility, more than in others, you might want to use the +"Equalize" button to equalize the scales on the x and y axes. Otherwise, +the arrow directions, as drawn, can be misleading. You can use the +mouse for zooming in this utility.

      + +

      Graph 3D Utility

      + +

      The Graph 3D Utility draws three-dimensional graphs of functions +of two variables. The function to be graphed is of the form z=f(x,y), +where f(x,y) is given as an expression that can contain variables x and y. +The values of xmin, xmax, ymin, and ymax have a different interpretation +in this utility: They specify a rectangle in the xy-plane that is used +as the domain over which the graph is drawn. You can also enter +zmin and zmax, which specify the range of z-values that you want to see. +The scale on the graph is adjusted so that the specified ranges of x, y, and +z are visible. But since the image on the screen is a two-dimensional +projection of a three dimensional object, there is not an exact correspondence +between the ranges and the boundaries of the rectangle shown on the screen.

      + +

      To produce the graph, a rectangular grid of points in the domain is used. +The function is evaluated at each point, and the resulting xyz-points +are connected with lines. You can enter the grid size -- that is, the +number of points along each side of this grid of points. +The utility can draw graphs in four different +styles: When only the lines between the points are drawn, the result is a +so-called "wire-mesh model." The second style also consists of just lines, +but lines that are hidden by other parts of the graph are not shown. +Instead of drawing the lines, the regions between the lines can be filled in +with color, giving a "shaded model." (The color used is determined by +the orientation of the graph surface, so it looks as if the graph is +illuminated from a certain direction.) In the fourth style, both the +lines and the shading are shown. You can select among the four styles +using a set of radio buttons under the graph. The shaded model +without lines only really looks good on a high color monitor using a +large grid size.

      + +

      When the utility starts up, you will see a projection of the x-, y-, +and z-axes along with a rectangle that frames the domain of the function +in the xy-plane. When you press the "Graph it!" button or press return +in any input box, the graph will be drawn. You get to see the graph +being drawn, from back to front. (This is deliberate -- for understanding +the graph, seeing the drawing process is more effective than just seeing +the final result.)

      + +

      You can rotate the axes and graph using two scroll bars. The +horizontal scroll bar rotates the graph around the z-axis. The +vertical scroll bar tilts the image towards you and away from you.

      + +

      Mouse zooming does not work in the Graph 3D Utility. Do +not expect Graph 3D to work well with most discontinuous functions.

      + +
      +
      David Eck +(eck@hws.edu), 27 October 1999
      + + + + + + +
      +Last updated: Wednesday, April 12, 2000 +
      +http://www.math.rochester.edu/courses/current/MTH163/xFunctions.html +
      This page was built by M.Gage using Frontier
      + + + + diff --git a/htdocs/applets/xFunctions/xFunctions.zip b/htdocs/applets/xFunctions/xFunctions.zip new file mode 100755 index 0000000000..ac071ff240 Binary files /dev/null and b/htdocs/applets/xFunctions/xFunctions.zip differ diff --git a/htdocs/crossdomain.xml b/htdocs/crossdomain.xml new file mode 100644 index 0000000000..a45fa2ff23 --- /dev/null +++ b/htdocs/crossdomain.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/htdocs/css/dgage.css b/htdocs/css/dgage.css new file mode 100644 index 0000000000..ebe5b0f1cc --- /dev/null +++ b/htdocs/css/dgage.css @@ -0,0 +1,239 @@ +/* WeBWorK Online Homework Delivery System + * Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ + * $CVSHeader: webwork2/htdocs/css/math.css,v 1.12 2008/03/04 15:43:44 sh002i 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. + */ + +/* --- Standard elements ---------------------------------------------------- */ + +body { + background: #EFEFEF; + color: black; + margin: 0 .5em 0 .5em; /* MSIE ignores bottom margin; Gecko doesn't */ + padding: 0; + font-family: Times, serif; + min-width: 25em; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", "Arial", "Helvetica", sans-serif; + /* FIXME "You have no background-color with your color" */ + color: #225; + background: transparent; +} +h1 { font-size: 170%; } +h2 { font-size: 140%; padding-bottom: 0; margin-bottom: .5ex } +h3 { font-size: 120%; } +h4 { font-size: 110%; } +h5 { font-size: 105%; } +h6 { font-size: 105%; } + +a:link { color: #00e; background-color: inherit; } +a:visited { color: #808; background-color: inherit; } + +/* --- Miscellaneous classes ---------------------------------------------- */ + +/* show only to CSS-ignorant browsers */ +.for-broken-browsers { display: none } + +/* for hiding floats from Netscape 4.x */ +.float-left { float: left; } +.float-right { float: right; } + +/* CSS Generated by Primer - primercss.com */ + +div#masthead { + width:100%; +} + +div#loginstatus { + float:right; +} + +div#logo { + float:left; +} + +div#site-navigation { + position:absolute; + width:180px; +} + +div#site-navigation ul, div#site-nagivation li{ + padding-left:10px; + list-style:none; +} + +div#fisheye { + +} +/* +div.info-box { + width:100%; + height:3ex; + border-bottom:solid 1px; + margin-bottom:10px; +} + +div.info-box ul li ul{ + height:0; + overflow:hidden; + list-style:none; + margin:0; + padding:4px; +} + +div.info-box ul{ + position:relative; + list-style:none; +} + +div.info-box ul li:hover{ + cursor:pointer; + width:auto; + z-index:100; +} + +div.info-box ul li:hover>ul{ + z-index:100; + background:rgba(125, 125, 125, 0.9); + height:10ex; + overflow:visible; + width:auto; +} + +div.info-box ul li ul li:hover>ul{ + z-index:100; + left:3em; + background:rgba(125, 125, 125, 0.9); + height:auto; + overflow:visible; +} +*/ + +a#Courses { + +} + +a#Homework_Sets { + +} + +a#Password_Email { + +} + +a#Grades { + +} + +a#Instructor_Tools { + +} + +a#Classlist_Editor { + +} + +a#Hmwk_Sets_Editor { + +} + +a#Library_Browser { + +} + +a#Library_Browser_2 { + +} + +a#Statistics { + +} + +a#Student_Progress { + +} + +a#Scoring_Tools { + +} + +a#Email { + +} + +a#File_Manager { + +} + +a#Course_Configuration { + +} + +a.active { + +} + +hr.for-broken-browsers { + +} + + + + + +div#big-wrapper { + margin:0px 1% 0px 190px; + background:white; + padding-left:10px; + padding-right:10px; + min-width:788px; + min-height:100%; + -moz-box-shadow: 1px 0px 4px gray, -1px 0px 4px gray; /* FF3.5+ */ + -webkit-box-shadow: 1px 1px 4px 2px gray; /* Saf3.0+, Chrome */ + box-shadow: 1px 1px 4px 2px gray; /* Opera 10.5, IE 9 */ + +} + +div#breadcrumbs { + +} + +div#content { + +} + +span.Message { + +} + +div.Body { + +} + +div.Message { + +} + +div#footer { + +} + +p#last-modified { + +} + +div#copyright { + +} + diff --git a/htdocs/css/gateway.css b/htdocs/css/gateway.css new file mode 100644 index 0000000000..a3ab347b94 --- /dev/null +++ b/htdocs/css/gateway.css @@ -0,0 +1,113 @@ +/******************/ +/* gateway styles */ +/******************/ + +/* update margins to reflect missing left column */ +#breadcrumbs { margin-left: .5em } +#content { margin-left: .5em } +#footer { margin-left: .5em } + +hr { clear: both; } + +div.gwMessage { background-color:#dddddd; color: black; } +div.gwTiming { + background-color:#ddddff; + color: black; + margin-top: 10px; + margin-bottom: 0px; +} +div.gwWarning { background-color:#ffffdd; color: black; } + +/* Louis Zulli's IE6 non-fixed positioning workaround follows */ +div#gwTimer { + background-color:#ffeeaa; + color: black; + font-family: Times serif; + position: absolute; /* IE<7 sees this */ + width: 15em; + right: 4em; + top: 4em; + border: dashed black 1px; + padding: 3px; + text-align: center; + z-index: 2; +} +html>body #gwTimer { + position: fixed; /* IE 7 and other browsers see this */ +} +div#gwScoreSummary { + background-color:#fcfccc; + color: black; + font-family:Times serif; + z-index: 1; + font-size: 85%; + border: black 3px outset; + width: 15em; + top: 8em; + right: 2em; + padding: 3px; + position: absolute; +} +html>body #gwScoreSummary { + position: fixed; /* IE 7 and other browsers see this */ +} + +span.resultsWithError { background-color: #ffcccc; color: black; } +span.resultsWithoutError { background-color: #66ff99; color: black; } +div.gwCorrect { background-color: #66ff99; color: black; } +div.gwIncorrect { background-color: #ff9999; color: black; } + +div.gwProblem { + clear: both; + background-color: #E0E0E0; + color: black; + padding: 3px; + border: solid black 1px; +} +div.gwSoln { +/* background-color: #e0e0ff; */ + background-color: transparent; + color: black; +/* padding: 2px; */ +/* border: dashed black 1px; */ +} +div.gwSoln b { color: navy; } + +p.gwPreview { + font-size: smaller; + text-align: right; + margin-top: 0px; + margin-bottom: 0px; +} + +table.gwAttemptResults { + border-width: 0px; +} +table.gwAttemptResults td.label { + font-weight: bold; + background-color: transparent; + color: navy; +} +table.gwAttemptResults td.output { + padding: 2px; + border: solid black 1px; + background-color: #eeeeee; +} + +div.gwPrintMe { + width: 25%; + float: right; + margin-top: 2em; + margin-right: 1em; +/* position: relative; + left: 65%; + top: -1em; + z-index: 1; */ +/* margin-left: auto; + margin-right: 2em; + margin-top: 3px; */ + border: dashed black 1px; + padding: 3px; + color: black; + background-color: #e0e0ff; +} diff --git a/htdocs/css/math.css b/htdocs/css/math.css new file mode 120000 index 0000000000..d1f2981277 --- /dev/null +++ b/htdocs/css/math.css @@ -0,0 +1 @@ +../../conf/templates/math/math.css \ No newline at end of file diff --git a/htdocs/css/math2.css b/htdocs/css/math2.css new file mode 120000 index 0000000000..4ed2570f62 --- /dev/null +++ b/htdocs/css/math2.css @@ -0,0 +1 @@ +../../conf/templates/math2/math2.css \ No newline at end of file diff --git a/htdocs/css/math3.css b/htdocs/css/math3.css new file mode 120000 index 0000000000..1c78305408 --- /dev/null +++ b/htdocs/css/math3.css @@ -0,0 +1 @@ +../../conf/templates/math3/math3.css \ No newline at end of file diff --git a/htdocs/css/moodle.css b/htdocs/css/moodle.css new file mode 100644 index 0000000000..c25d94fbcc --- /dev/null +++ b/htdocs/css/moodle.css @@ -0,0 +1,48 @@ +/* + * Start with math.css + */ +@import url(math.css); + +/* + * Additions/changes to math.css for moodle.css + */ + +BODY { + margin: 0px; /* no margin (use #fullPage for that) */ + padding: 0px; +} + +#fullPage { + border: 10px solid white; /* needed to get height correctly */ + margin:0px; + padding:0px; +} + +#masthead { + margin: 0 0 .5em 0; /* add space below masthead */ + /* rather than above big-wrapper */ +} + +#big-wrapper { + top: 0px; /* see above */ + /* (shouldn't it have been margin-top anyway?) */ +} + +#breadcrumbs { + margin-bottom: .5em; /* put space here rather than in content */ +} + +#content { + margin: 0 0 0 10.4em; /* see above */ +} + +#site-navigation { + position: relative; /* can't use absolute and still get the */ + left: auto; /* correct height in #fullPage */ + top: auto; + float: left; +} + +#footer { + margin: 0 .5em .2ex 0; /* don't indent this */ +} diff --git a/htdocs/css/moodle.js b/htdocs/css/moodle.js new file mode 100644 index 0000000000..ea4297c769 --- /dev/null +++ b/htdocs/css/moodle.js @@ -0,0 +1,82 @@ +/* + * Turn off interface elements that are not needed by Moodle + */ +if (parent.ww) { + document.write(''); + /* + * This prevents scrollbars from appearing + * (we won't need them because we resize the IFRAME + * to fit, but some browsers would show them anyway). + * + * This is not part of the CSS style so that it only + * is in effect if JavaScript is enabled. + */ + if (document.body) {document.body.style.overflow = "hidden"} +} + +ww = { + /* + * Look for the "up" image on the Sets page and disable it. + * (If WW taged these with ID's, this would be easier.) + */ + disableUp: function () { + var img = document.getElementsByTagName('img'); + for (var i=0; i < img.length; i++) { + if (img[i].src.match(/navUp\.gif$/)) { + img[i].parentNode.style.display = "none"; + break; + } + } + }, + + /* + * Look for the "Sets" box and disable it. + * (If WW taged these with ID's, this would be easier.) + */ + disableSets: function () { + var h2 = document.getElementsByTagName('h2'); + for (var i=0; i < h2.length; i++) { + if (h2[i].innerHTML == 'Sets') { + h2[i].parentNode.style.display = "none"; + break; + } + } + }, + + /* + * This resets the size of the IFRAME to match the + * size of the DIV with ID="fullPage", which should be the + * whole document. + */ + updateSize: function () { + parent.ww.page.height = ww.page.offsetHeight+20; + parent.ww.page.style.height = ""; + }, + + /* + * We wait for the window to completely update, then + * resize the IFRAME. Safari has a bug where it gets + * the size wrong when the page is reloaded, so we + * resize again after a short delay. Resizing also is + * done if the browser window size changes. + */ + onload: function () { + if (ww.oldLoad) ww.oldLoad(); + ww.updateSize(); + setTimeout('ww.updateSize()',1); // for reloading in Safari + setTimeout('window.onresize=ww.updateSize',100); // let it update first + }, + + /* + * Save the old onload handler and replace it with ours + */ + Init: function () { + if (!parent.ww || !parent.ww.page) return; + ww.page = document.getElementById('fullPage'); + if (!ww.page) return; + ww.oldLoad = window.onload; + window.onload = ww.onload; + ww.disableUp(); + ww.disableSets(); + } +} diff --git a/htdocs/css/setmaker2.css b/htdocs/css/setmaker2.css new file mode 100644 index 0000000000..08bd560fd7 --- /dev/null +++ b/htdocs/css/setmaker2.css @@ -0,0 +1,212 @@ +div#editor-form{ + min-width:768px; + width:100%; +} + +/*Control Pannel Style*/ +div#control_panel{ + float:left; + width:100%; + margin-bottom:5px; + padding-bottom:0px; + padding-top:5px; + border:solid 1px; + /*-moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px;*/ +} + +div#library_control{ + overflow:auto; + height:auto; + float:right; +} +div.control{ + height:auto; + margin:0.1%; +} + +div#myset_control{ + float:left; +} + +div.setSelector{ + height:163px; + margin-bottom:20px; + overflow:auto; +} + +div.setSelectorLib{ + /*height:1px; + overflow:auto;*/ + display:none; +} +/*End Control Panel Style*/ + +/*Problem Lists Style*/ +div#problem_container{ + width:100%; + float:left; +} + +div.mysets{ + float:left; + background:#ffffff; + margin-top:0px; + /*min-width:200px; + width:24%;*/ +} + +div.setmaker_library{ + /*margin-left:305px;*/ + margin-top:0px; + float:right; + background-color:#EEFFFF; + /*min-width:550px; + width:71%;*/ +} + +div.problemList{ + position:relative; + overflow:auto; + min-height:100px; + height:90%; + border:solid 1px; + /*-moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px;*/ + padding:0.1%; + margin:0px; +} +/*add extra space at the bottom +so we can add at end of list*/ +/* div#mysets_problems{ + +} */ + +div#mysets_problems div.myProblem:last-child { + margin-bottom:200px; +} + +/*order is important here*/ +div.problem{ + background:rgba(255,255,255,0.75); + border-bottom:solid 1px; + margin-bottom:5px; + overflow:hidden; +} + +div.used{ + background:rgba(0, 102, 102,0.8); +} + +div.libProblem{ + background:rgba(0,0,204,0.5); +} + +div.removedProblem{ + background:rgba(204,0,0,0.8); +} + +div.ResultsWithError{ + background:rgba(225,225,0,0.9); +} + + +/*End Problem Lists Style*/ + +select#myset_sets{ + background:none; + height:180px; +} + +div#size_slider{ + float:left; + width:1%; + text-align:center; + height:90%; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + margin:0; + margin-top:235px; + padding:0; +} + +div#horizontal_slider{ + height:15px; + text-align:center; + width:100%; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + margin:0; + padding:0; + position:absolute; + background:white; +} + +div#size_slider:hover{ + cursor:col-resize; +} + +div#horizontal_slider:hover{ + cursor:row-resize; +} + +div#size_slider p{ + position:relative; + top:50%; + width:100%; + margin-top:-10px; +} +.shadowed{ + -moz-box-shadow: 1px 1px 4px 2px gray; /* FF3.5+ */ + -webkit-box-shadow: 1px 1px 4px 2px gray; /* Saf3.0+, Chrome */ + box-shadow: 1px 1px 4px 2px gray; /* Opera 10.5, IE 9 */ +} + + +*[draggable=true] { + -moz-user-select:none; + -khtml-user-drag: element; + cursor: move; +} + +div#loading{ + position:fixed; + height:100%; + width:100%; + background-color:white; + background-image:url('/webwork2_files/images/ajax-loader.gif'); + background-repeat:no-repeat; + background-attachment:fixed; + background-position:center; + top:0; + left:0; + z-index:2000; +} + +div#loading img{ +} + +div#create_new_set{ + background:white; + position:absolute; + height:300px; + width:300px; + margin:0 auto; + z-index:200; + display:none; +} +span.js_action_span{ + text-decoration:underline; +} +span.js_action_span:hover{ + cursor:pointer; +} + +div#set_maker_two_box{ + clear:both; + float:left; +} diff --git a/htdocs/css/tabber.css b/htdocs/css/tabber.css new file mode 100644 index 0000000000..23612bd024 --- /dev/null +++ b/htdocs/css/tabber.css @@ -0,0 +1,112 @@ +/* $Id: example.css,v 1.5 2006/03/27 02:44:36 pat Exp $ */ +/* Renamed tabber.css for use in WeBWorK */ +/* By Patrick Fitzgerald pat@barelyfitz.com, documentation can be found at http://www.barelyfitz.com/projects/tabber/ */ + + +/*-------------------------------------------------- + REQUIRED to hide the non-active tab content. + But do not hide them in the print stylesheet! + --------------------------------------------------*/ +.tabberlive .tabbertabhide { + display:none; +} + +/*-------------------------------------------------- + .tabber = before the tabber interface is set up + .tabberlive = after the tabber interface is set up + --------------------------------------------------*/ +.tabber { +} +.tabberlive { + margin-top:1em; +} + +/*-------------------------------------------------- + ul.tabbernav = the tab navigation list + li.tabberactive = the active tab + --------------------------------------------------*/ +ul.tabbernav +{ + margin:0; + padding: 3px 0; + border-bottom: 1px solid #778; + font: bold 12px Verdana, sans-serif; +} + +ul.tabbernav li +{ + list-style: none; + margin: 0; + display: inline; +} + +ul.tabbernav li a +{ + padding: 3px 0.5em; + margin-left: 3px; + border: 1px solid #778; + border-bottom: none; + background: #DDE; + text-decoration: none; +} + +ul.tabbernav li a:link { color: #448; } +ul.tabbernav li a:visited { color: #667; } + +ul.tabbernav li a:hover +{ + color: #000; + background: #AAE; + border-color: #227; +} + +ul.tabbernav li.tabberactive a +{ + background-color: #fff; + border-bottom: 1px solid #fff; +} + +ul.tabbernav li.tabberactive a:hover +{ + color: #000; + background: white; + border-bottom: 1px solid white; +} + +/*-------------------------------------------------- + .tabbertab = the tab content + Add style only after the tabber interface is set up (.tabberlive) + --------------------------------------------------*/ +.tabberlive .tabbertab { + padding:5px; + border:1px solid #aaa; + border-top:0; + + /* If you don't want the tab size changing whenever a tab is changed + you can set a fixed height */ + + /* height:200px; */ + + /* If you set a fix height set overflow to auto and you will get a + scrollbar when necessary */ + + /* overflow:auto; */ +} + +/* If desired, hide the heading since a heading is provided by the tab */ +.tabberlive .tabbertab h2 { + display:none; +} +.tabberlive .tabbertab h3 { + display:none; +} + +/* Example of using an ID to set different styles for the tabs on the page */ +.tabberlive#tab1 { +} +.tabberlive#tab2 { +} +.tabberlive#tab2 .tabbertab { + height:200px; + overflow:auto; +} diff --git a/htdocs/css/union.css b/htdocs/css/union.css new file mode 120000 index 0000000000..2a9896e880 --- /dev/null +++ b/htdocs/css/union.css @@ -0,0 +1 @@ +../../conf/templates/union/union.css \ No newline at end of file diff --git a/htdocs/css/ur.css b/htdocs/css/ur.css new file mode 120000 index 0000000000..ba86bd7c73 --- /dev/null +++ b/htdocs/css/ur.css @@ -0,0 +1 @@ +../../conf/templates/ur/ur.css \ No newline at end of file diff --git a/htdocs/favicon.ico b/htdocs/favicon.ico new file mode 100644 index 0000000000..10893e0d6b Binary files /dev/null and b/htdocs/favicon.ico differ diff --git a/htdocs/helpFiles/Entering-Angles.html b/htdocs/helpFiles/Entering-Angles.html new file mode 100644 index 0000000000..6b9ea27cd6 --- /dev/null +++ b/htdocs/helpFiles/Entering-Angles.html @@ -0,0 +1,47 @@ + + + + +
      +Entering Angles +
      + +
        + +
      • Angles in radians without units are the default: +
        +For an angle of 60 degrees, enter it in radians as   pi/3   or   1.04719..., but not 60
        +By default, trig functions are evaluated in radians, so cos(pi/3) = 1/2, but cos(60) = -0.9524 since it is radians. You must convert degrees to radians before applying a trig function to an angle. +
        +
      • + +
      • Occasionally, units are required on angles: +
        +If asked for units on an angle, enter, for example,
        +pi/6 rad   (including rad)
        +30 deg   (including deg) +
        +
      • + +
      • Examples of constants available: +
        pi,   e = e^1
        +
      • + +
      • Sometimes decimals are not allowed: +
        Sometimes   pi/6   is allowed, but   0.524   is not
        +
      • + +
      • Sometimes trig functions are not allowed: +
        +Sometimes   0.866025403784   is allowed, but   cos(pi/6)   is not +
        +
      • + +
      • Link to a list of all available functions
      • + + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Decimals.html b/htdocs/helpFiles/Entering-Decimals.html new file mode 100644 index 0000000000..9fc1fef7d1 --- /dev/null +++ b/htdocs/helpFiles/Entering-Decimals.html @@ -0,0 +1,33 @@ + + + + +
      +Entering decimals +
      + +
        + +
      • In general, give at least 5 decimal places. +
        Typically, if your answer is correct to 5 decimal places it will be marked correct, although the number of decimal places required may vary from problem to problem. When in doubt, give more decimal places.
        +
      • + +
      • If there is more than one correct answer, enter your answers as a comma separated list. +
        +For example, if your answers are -3/2, 4/3, 2pi, e^3, 5 enter them as +-1.5, 1.3333333, 6.2831853, 20.0855369, 5 +
        + +
      • Sometimes, fractions and certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Equations.html b/htdocs/helpFiles/Entering-Equations.html new file mode 100644 index 0000000000..99f43ed314 --- /dev/null +++ b/htdocs/helpFiles/Entering-Equations.html @@ -0,0 +1,48 @@ + + + + +
      +Entering Equations +
      + +
        + +
      • Equations must have an equals sign and use the correct variable names: +
        +y = 5x+2   will be incorrect if the answer is   w = 5y+2 +
        +
      • + +
      • Examples of valid equations that are equivalent: +
        +32 = 5*x + 2   is the same as   30 = 5x   or   x = 6
        +y = (x-1)^2 + 3   is the same as   y - 3 = (x-1)^2
        +x^2 + xy + y^2 = 13x   is the same as   y*(y+x) = 13x - x^2
        +
        +
      • + +
      • If there is no equation that solves the question: +
        +Enter   NONE   or   DNE   (this may vary from problem to problem) +
        +
      • + +
      • Examples of constants used in equations: +
        pi, e = e^1
        +
      • + +
      • Functions may be used in equations, but may not be applied across the equals sign: +
        +sqrt(x) = sqrt(5)   is valid, but   sqrt(x=5)   is not +
        +
        +Link to a list of all available functions +
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Exponents.html b/htdocs/helpFiles/Entering-Exponents.html new file mode 100644 index 0000000000..c9b2dcd69d --- /dev/null +++ b/htdocs/helpFiles/Entering-Exponents.html @@ -0,0 +1,33 @@ + + + + +
      +Entering exponents +
      + +
        + +
      • Both ^ and ** are used for exponentiation. +
        For example, x^2 and x**2 are the same, as are e^(-x/2) and 1/(e**(x/2))
        +
      • + +
      • Square roots have a named function, but other roots do not and should be entered using fractional exponents. +
        +For example, the square root of 2 can be entered as sqrt(2), 2^(1/2), or 2**(1/2), but the cube root of 2 must be entered as 2^(1/3) or 2**(1/3). The parentheses in 2^(1/3) are required, since 2^1/3 will be interpreted as (2^1)/3 = 2/3. +
        +
      • + +
      • Sometimes, fractional exponents and certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /. When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Formulas.html b/htdocs/helpFiles/Entering-Formulas.html new file mode 100644 index 0000000000..b40362e9cd --- /dev/null +++ b/htdocs/helpFiles/Entering-Formulas.html @@ -0,0 +1,67 @@ + + + + +
      +Entering Formulas +
      + +
        + +
      • Link to a list of all available functions

      • + +
      • Formulas must use the correct variable(s): +
        +For example, a function of time   t   could be   -16t^2 + 12, while   -16x^2 + 12   would be incorrect. +
        +
      • + +
      • Examples of valid formulas: +
        +5*sin((pi*x)/2)   or   5 sin(pi x/2)
        +e^(-x)   or   e**(-x)   or   1/(e^x)
        +abs(5y)   or   |5y|
        +sqrt(9 - z^2)   or   (9 - z^2)^(1/2)
        +24   or   4!   or   4 * 3 * 2 * 1
        +pi   or   4 arctan(1)   or   4 atan(1)   or   4 tan^(-1)(1)
        +
        +
      • + +
      • Examples of constants used in formulas: +
        pi, e = e^1
        +
      • + +
      • Examples of operations used in formulas: +
        Addition +, subtraction -, multiplication *, division /, exponentiation ^ (or **), factorial ! +
        +
      • + +
      • Examples of functions used in formulas: +
        +sqrt(x) = x^(1/2), abs(x) = | x |
        +2^x, e^x, ln(x), log10(x)
        +sin(x), cos(x), tan(x), csc(x), sec(x), cot(x)
        +arcsin(x) = asin(x) = sin^(-1)(x)
        +arccos(x) = acos(x) = cos^(-1)(x)
        +arctan(x) = atan(x) = tan^(-1)(x)
        +
        + +
      • Sometimes formulas must be simplified: +
        +For example,   6x + 5 - 2x   should be simplified to   4x + 5 +
        +
      • + +
      • Sometimes, certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Fractions.html b/htdocs/helpFiles/Entering-Fractions.html new file mode 100644 index 0000000000..2e456b8b12 --- /dev/null +++ b/htdocs/helpFiles/Entering-Fractions.html @@ -0,0 +1,62 @@ + + + + +
      +Entering fractions +
      + +
        + +
      • Examples of fractions, which are of the form a / b for non-decimal numbers a and b that have no common factors, include: +
        +5/2, -1/3, pi/3, 4, sqrt(2)/2 +
        +
      • + +
      • Examples of fractions that can be simplified include: +
        +15/6, (3-4)/3, 2*pi/6, (16/2)/2 +
        +
      • + +
      • Sometimes decimals are not allowed: +
        +Allowed:    5/2, -1/3, pi/3, 4, sqrt(2)/2, 2^(1/2)
        +Not allowed:    2.5, -0.33333, 3.14159/3, 0.707106/2, 2^(0.5) +
        +
      • + +
      • Sometimes a mixed fraction is required: +
        Enter 1 2/3 (for 1 and 2/3) with a space between the 1 and the 2 instead of 5/3
        +
      • + +
      • Sometimes, you must make an integer into a fraction: +
        Enter 4/1 instead of 4
        +
      • + +
      • If there is more than one correct answer, enter your answers as a comma separated list. +
        +For example, if your answers are -3/2, 4/3, 2pi, e^3, 5 enter +3/2, 4/3, 2*pi, e^3, 5 +
        + +
      • If there are no solutions: +
        +Enter   NONE   or   DNE   (this may vary from problem to problem) +
        +
      • + +
      • Sometimes, certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Inequalities.html b/htdocs/helpFiles/Entering-Inequalities.html new file mode 100644 index 0000000000..03fe890589 --- /dev/null +++ b/htdocs/helpFiles/Entering-Inequalities.html @@ -0,0 +1,73 @@ + + + + +
      +Entering inequalities +
      + +
        + +
      • Types of operators: +
        + + + + + + + +
        <    less than
        <=    less than or equal to (  =<   might also work)
        =    equals
        !=    not equal to (uses exclamation point)
        >    greater than
        >=    greater than or equal to (  =>   might also work)
        +
        +
      • + +
      • Special symbols: +
        +infinity   or   inf   means positive infinity
        +-infinity   or   -inf   means negative infinity
        +R   means all real numbers
        +R   is the same as   -inf < x < inf   or   (-inf,inf)
        +{2,4,5}   using curly braces denotes a finite set
        +NONE   or a pair of curly braces   {}   means the empty set
        +U   denotes the union of intervals +
        +
      • + +
      • Entering answers using inequality or interval notation: +
        + + + + + + + + + + + + + + + +
        Inequality
        Notation
        * Interval
        Notation
        Remarks
        x<2(-infinity,2)Use rounded parentheses (   or   ) at infinite endpoints
        x>2(2,infinity) 
        x<=2(-infinity,2] 
        x>=2[2,infinity) 
        0<x<=2(0,2] 
        0<x and x<2(0,2)and   is special
        x<0 or x>2(-inf,0)U(2,inf)or   is special
        U   denotes union
        x=0 or x=2{0,2}finite sets are allowed using curly braces   {a,b,c}
        x<3 or x>3(-inf,3)U(3,inf)
        x != 3
        R-{3}
        set differences are allowed
        +
        +* Some questions may not allow interval notation to be used +
        +
      • + +
      • Tips for entering inequalities and intervals: +
        +If an interval includes an endpoint, use square brackets: [   or   ]

        +If an interval excludes an endpoint or an endpoint is infinite, use rounded parentheses: (   or   )

        +Use curly braces to enclose finite sets and commas to separate elements the set: { -3, pi, 2/5, 0.75 }

        +All sets should be expressed in their simplest form in interval notation with no overlapping intervals. For example,   [2,4]U[3,5]   is not equivalent to   [2,5]

        +If you are asked to find the range of a function y = f(x), your inequality should be in terms of the variable y +
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Intervals.html b/htdocs/helpFiles/Entering-Intervals.html new file mode 100644 index 0000000000..607c0c81c7 --- /dev/null +++ b/htdocs/helpFiles/Entering-Intervals.html @@ -0,0 +1,73 @@ + + +WeBWorK Help: Interval Notation + + + +

      +Using Interval Notation +

      + +
        + +
      • If an endpoint is included, then use [ or ]. +If not, then use ( or ). For example, the interval +from -3 to 7 that includes 7 but not -3 is expressed (-3,7]. + + +
        +
        + +
      • For infinite intervals, use Inf +for (infinity) and/or +-Inf for -∞ (-Infinity). For +example, the infinite interval containing all points greater than or +equal to 6 is expressed [6,Inf). + + +
        +
        + +
      • If the set includes more than one interval, they are joined using the union +symbol U. For example, the set consisting of all points in (-3,7] together with all points in [-8,-5) is expressed [-8,-5)U(-3,7]. + +
        +
        + +
      • If the answer is the empty set, you can specify that by using + braces with nothing inside: { } + +
        + +
        + +
      • You can use R as a shorthand for all real numbers. + So, it is equivalent to entering (-Inf, Inf). + +
        +
        + +
      • You can use set difference notation. So, for all real numbers + except 3, you can use R-{3} or + (-Inf, 3)U(3,Inf) (they are the same). Similarly, + [1,10)-{3,4} is the same as [1,3)U(3,4)U(4,10). + + +
        +
        + + +
      • WeBWorK will not interpret [2,4]U[3,5] as equivalent + to [2,5], unless a problem tells you otherwise. +All sets should be expressed in their simplest interval notation form, with no +overlapping intervals. + +
      + + + + diff --git a/htdocs/helpFiles/Entering-Limits.html b/htdocs/helpFiles/Entering-Limits.html new file mode 100644 index 0000000000..6b3f3ac6ef --- /dev/null +++ b/htdocs/helpFiles/Entering-Limits.html @@ -0,0 +1,59 @@ + + + + +
      +Entering Limits +
      + +
        + +
      • Limits whose values are numbers: +
        +For example, limx → ∞ arctan(x) = π/2, so you would enter   pi/2 +
        +
      • + +
      • Limits whose values are infinite: +
        +Enter   infinity,   inf,   -infinity,   -inf +
        +
      • + +
      • Limits that don't exist: +
        +Enter   DNE   or   NONE   (this may vary from question to question) +
        +
      • + +
      • Limits whose value is a function: +
        +Enter the function using appropriate syntax, for example:
        +sqrt(x) = x^(1/2), abs(x) = | x |
        +2^x, e^x, ln(x), log10(x)
        +sin(x), cos(x), tan(x), csc(x), sec(x), cot(x)
        +arcsin(x) = asin(x) = sin^(-1)(x)
        +arccos(x) = acos(x) = cos^(-1)(x)
        +arctan(x) = atan(x) = tan^(-1)(x)
        +
        +
      • + +
      • Sometimes answers must be simplified: +
        +For example,   6x+5-2x+7   should be simplified to   4x+12 +
        +
      • + +
      • Sometimes, certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Logarithms.html b/htdocs/helpFiles/Entering-Logarithms.html new file mode 100644 index 0000000000..45fac2d0ee --- /dev/null +++ b/htdocs/helpFiles/Entering-Logarithms.html @@ -0,0 +1,55 @@ + + + + +
      +Entering Logarithms +
      + +
        + +
      • Entering natural logarithms:   ln(x)   or   log(x) +
        +The default is that   ln(x)   and   log(x)   are both the natural log function. (Although unlikely, your professor may have changed the default setting for your course.) +
        +
      • + +
      • Entering base 10 logarithms:   log10(x)   or   logten(x) +
        +The default is that   log10(x)   and   logten(x)   are both the base 10 logarithm function. (Although unlikely, your professor may have changed the default setting for your course.) +
        +
      • + +
      • Entering logarithms base b:   ln(x)/ln(b)   or   log10(x)/log10(b) +
        +WeBWorK does not recognize logarithms to other bases, so you must use the change of base formula for logarithms to enter your answer. For example, enter log base 2 of x as +

        +ln(x)/ln(2)    or    log10(x)/log10(2) +
        +
      • + +
      • Put parentheses around the arguments to logs: +
        +ln(2x+8)   and   ln2x+8 = ln(2)*x+8   are very different. +
        +
      • + +
      • Sometimes logarithms must be simplified or expanded: +
        +For example, the required answer may be   ln(6) + ln(x)   or   ln(6x) +
        +
      • + +
      • Sometimes, certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Numbers.html b/htdocs/helpFiles/Entering-Numbers.html new file mode 100644 index 0000000000..42ddd5d68d --- /dev/null +++ b/htdocs/helpFiles/Entering-Numbers.html @@ -0,0 +1,43 @@ + + + + +
      +Entering numbers +
      + +
        + +
      • Examples of (real) numbers include: +
        4, 5/2, -1/3, pi/3, e^3, 3.1415926535, sqrt(2) = 2^(1/2), ln(2), sin(2pi/3)
        +
      • + +
      • If there is more than one correct answer, enter your answers as a comma separated list. +
        +For example, enter   -1.5, 4/3, 2pi, e^3, 5
        +Do not use commas in large numbers: enter   4321   (not   4,321) +
        + +
      • If there are no solutions: +
        +Enter   NONE   or   DNE   (this may vary from problem to problem) +
        +
      • + +
      • If your answer is a decimal, give at least 5 decimal places. +
        Typically, if your answer is correct to 5 decimal places it will be marked correct, although the number of decimal places required may vary from problem to problem. When in doubt, give more decimal places.
        +
      • + +
      • Sometimes, fractions and certain operations are not allowed. +
        Usually, the operations that are not allowed include addition +, subtraction -, multiplication *, division /, and exponentiation ^ (or **). When these operations are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      • Sometimes, certain functions are not allowed. +
        Usually, the functions that are not allowed include square root sqrt( ), absolute value | | (or abs( )), as well as other named functions such as sin( ), ln( ), etc. When these functions are not allowed, it is usually because you are expected to be able to simplify your answer, often without using a calculator.
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Points.html b/htdocs/helpFiles/Entering-Points.html new file mode 100644 index 0000000000..fb2212e633 --- /dev/null +++ b/htdocs/helpFiles/Entering-Points.html @@ -0,0 +1,48 @@ + + + + +
      +Entering Points +
      + +
        + +
      • A point must use parentheses and commas: +
        +(4.5, 3/7)   is a valid point in 2 dimensions
        +(pi,e,2)   is a valid point in 3 dimensions +
        +
      • + +
      • If the answer is more than one point: +
        +Enter your answer as a comma-separated list of points, for example:    +(4,3), (5,10) +
        +
      • + +
      • If there are no solutions: +
        +Enter   NONE   or   DNE   (this may vary from problem to problem) +
        +
      • + +
      • Examples of constants used in points: +
        pi, e = e^1
        +
      • + +
      • Functions may be used in each coordinate of a point, but may not be applied across the parentheses or commas: +
        +(sqrt(2),sqrt(5))   is valid, but   (sqrt(2,5))   is not +
        +
        +Link to a list of all available functions +
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Entering-Syntax.html b/htdocs/helpFiles/Entering-Syntax.html new file mode 100644 index 0000000000..43fee3f495 --- /dev/null +++ b/htdocs/helpFiles/Entering-Syntax.html @@ -0,0 +1,147 @@ + + + + Syntax for entering answers to WeBWorK + + + +

      Syntax for entering answers to WeBWorK

      + + +

      Mathematical Symbols Available In WeBWorK

      + +
      • + Addition +
      • - Subtraction +
      • * Multiplication can also be indicated by a space or juxtaposition, e.g. 2x, 2 x or 2*x, also 2(3+4). +
      • / Division +
      • ^ or ** You can use either ^ or ** for exponentiation, e.g. 3^2 or 3**2 +
      • Parentheses: () - You can also use square brackets, [ ], and braces, { }, for grouping, e.g. [1+2]/[3(4+5)] +
      +

      Syntax for entering expressions

      +
      • Be careful entering expressions just as you would be careful entering expressions in a calculator. + +
      • Use the "Preview Button" to see exactly how your entry +looks. E.g. to tell the difference between 1+2/3*4 and [1+2]/[3*4] +click the "Preview Button". +
      • Sometimes using the * symbol to indicate mutiplication makes +things easier to read. For example (1+2)*(3+4) and (1+2)(3+4) are both +valid. So are 3*4 and 3 4 (3 space 4, not 34) but using a * makes +things clearer. +
      • Use ('s and )'s to make your meaning clear. You can also use ['s and ]'s and {'s and }'s. +
      • Don't enter 2/4+5 (which is 5.5) when you really want 2/(4+5) (which is 2/9). +
      • Don't enter 2/3*4 (which is 8/3) when you really want 2/(3*4) (which is 2/12). +
      • Entering big quotients with square brackets, e.g. [1+2+3+4]/[5+6+7+8], is a good practice. +
      • Be careful when entering functions. It's always good practice +to use parentheses when entering functions. Write sin(t) instead of +sint or sin t even though WeBWorK is smart enough to usually accept sin t or even sint. For example, sin 2t is interpreted as sin(2)t, i.e. (sin(2))*t so be careful. + +
      • You can enter sin^2(t) as a short cut although mathematically +speaking sin^2(t) is shorthand for (sin(t))^2(the square of sin of t). +(You can enter it as sin(t)^2 or even sint^2, but don't try such things +unless you really understand the precedence of operations. The +"sin" operation has highest precedence, so it is performed first, using +the next token (i.e. t) as an argument. Then the result is squared.) +You can always use the Preview button to see a typeset version of what +you entered and check whether what you wrote was what you +meant. :-) +
      • For example 2+3sin^2(4x) will work and is equivalent to +2+3(sin(4x))^2 or 2+3sin(4x)^2. Why does the last expression work? +Because things in parentheses are always done first [ i.e. (4x)], next +all functions, such as sin, are evaluated [giving sin(4x)], next all +exponents are taken [giving sin(4x)^2], next all multiplications and +divisions are performed in order from left to right [giving +3sin(4x)^2], and finally all additions and subtractions are performed +[giving 2+3sin(4x)^2]. +
      • Is -5^2 positive or negative? It's negative. This is because +the square operation is done before the negative sign is applied. Use +(-5)^2 if you want to square negative 5. +
      • When in doubt use parentheses!!! :-) +
      • The complete rules for the precedence of operations, in addition to the above, are +
        • Multiplications and divisions are performed left to right: 2/3*4 = (2/3)*4 = 8/3. + +
        • Additions and subtractions are performed left to right: 1-2+3 = (1-2)+3 = 2. +
        • Exponents are taken right to left: 2^3^4 = 2^(3^4) = 2^81 = a big number. +
        +
      • Use the "Preview Button" to see exactly how your entry +looks. E.g. to tell the difference between 1+2/3*4 and [1+2]/[3*4] +click the "Preview Button". +
      +

      Mathematical Constants Available In WeBWorK

      +
      • pi This gives 3.14159265358979, e.g. cos(pi) is -1 +
      • e This gives 2.71828182845905, e.g. ln(e*2) is 1 + ln(2) +
      + +

      Scientific Notation Available In WeBWorK

      +
      • 2.1E2 is the same as 210 +
      • 2.1E-2 is the same as .021 +
      +

      Mathematical Functions Available In WeBWorK

      +

      Note that sometimes one or more of these functions is disabled for a WeBWorK problem because the +instructor wants you to calculate the answer by some means other than just using the function. +

      +
      • abs( ) The absolute value +
      • cos( ) Note: cos( ) uses radian measure + +
      • sin( ) Note: sin( ) uses radian measure +
      • tan( ) Note: tan( ) uses radian measure +
      • sec( ) Note: sec( ) uses radian measure +
      • cot( ) Note: cot( ) uses radian measure +
      • csc( ) Note: csc( ) uses radian measure +
      • exp( ) The same function as e^x +
      • log( ) This is usually the natural log but your professor may have redined this as log to the base 10 +
      • ln( ) The natural log +
      • logten( ) The log to the base 10 + +
      • arcsin( ) +
      • asin( ) or sin^-1() Another name for arcsin +
      • arccos( ) +
      • acos( ) or cos^-1() Another name for arccos +
      • arctan( ) +
      • atan( ) or tan^-1() Another name for arctan +
      • arccot( ) +
      • acot( ) or cot^-1() Another name for arccot +
      • arcsec( ) + +
      • asec( ) or sec^-1() Another name for arcsec +
      • arccsc( ) +
      • acsc( ) or csc^-1() Another name for arccsc +
      • sinh( ) +
      • cosh( ) +
      • tanh( ) +
      • sech( ) +
      • csch( ) +
      • coth( ) + +
      • arcsinh( ) +
      • asinh( ) or sinh^-1() Another name for arcsinh +
      • arccosh( ) +
      • acosh( ) or cosh^-1()Another name for arccosh +
      • arctanh( ) +
      • atanh( ) or tanh^-1()Another name for arctanh +
      • arcsech( ) +
      • asech( ) or sech^-1()Another name for arcsech +
      • arccsch( ) + +
      • acsch( ) or csch^-1() Another name for arccsch +
      • arccoth( ) +
      • acoth( ) or coth^-1() Another name for arccoth +
      • sqrt( ) +
      • n! (n factorial -- defined for nÕ0  +
      • These functions may not always be available for every problem. +
        • sgn( ) The sign function, either -1, 0, or 1 + +
        • step( ) The step function (0 if x<0 , 1 if xÕ0 ) +
        • fact(n) The factorial function n! (defined only for nonnegative integers) +
        • P(n,k) = n*(n-1)*(n-2)...(n-k+1) the number of ordered sequences of k elements chosen from n elements +
        • C(n,k) = "n choose k" the number of unordered sequences of k elements chosen from n elements +
        +
      + +For more information: + +http://webwork.maa.org/wiki/Available_Functions + + + + diff --git a/htdocs/helpFiles/Entering-Units.html b/htdocs/helpFiles/Entering-Units.html new file mode 100644 index 0000000000..4bd5978637 --- /dev/null +++ b/htdocs/helpFiles/Entering-Units.html @@ -0,0 +1,96 @@ + + + WeBWorK Help: Units + + + + + +

      + +

      + + +

      Units Available in WeBWorK

      +

      +Some WeBWorK problems ask for answers with units. Below is a list of basic units +and how they need to be abbreviated in WeBWorK answers. In some problems, you +may need to combine units (e.g, velocity might be in ft/s for feet per +second). +

      + +
      Unit Abbreviation +
      Time +
      Seconds s + +
      Minutes min +
      Hours hr +
      Days day +
      Years year +
      Milliseconds ms + + +
      Distance +
      Feet ft +
      Inches in +
      Miles mi +
      Meters m + +
      Centimeters cm +
      Millimeters mm +
      Kilometers km +
      Angstroms A +
      Light years light-year + + +
      Mass +
      Grams g +
      Kilograms kg +
      Slugs slug + +
      Volume +
      Liters L + +
      Cubic Centimeters cc +
      Milliliters ml + +
      Force +
      Newtons N +
      Dynes dyne + +
      Pounds lb +
      Tons ton + +
      Work/Energy +
      Joules J +
      kilo Joule kJ + +
      ergs erg +
      foot pounds lbf +
      calories cal +
      kilo calories kcal +
      electron volts eV + +
      kilo Watt hours kWh + +
      Misc +
      Amperes amp +
      Moles mol +
      Degrees Centrigrade degC + +
      Degrees Fahrenheit degF +
      Degrees Kelvin degK +
      Angle degrees deg +
      Angle radians rad + +
      + + +
      + + + diff --git a/htdocs/helpFiles/Entering-Vectors.html b/htdocs/helpFiles/Entering-Vectors.html new file mode 100644 index 0000000000..edb5f7ad34 --- /dev/null +++ b/htdocs/helpFiles/Entering-Vectors.html @@ -0,0 +1,56 @@ + + + + +
      +Entering Vectors +
      + +
        + +
      • Predefined vectors i, j, and k +
        +i   is the same as   <1,0,0>
        +j   is the same as   <0,1,0>
        +k   is the same as   <0,0,1>
        +
        +
      • + +
      • A vector may be entered using angle brackes and commas, or adding multiples of i, j, and k: +
        +<4.5, 3/7>   and   4.5i + 3/7j   are valid vectors in 2 dimensions
        +<pi,e,2>   and   pi i + e j + 2 k   are valid vectors in 3 dimensions +
        +
      • + +
      • If the answer is more than one vector: +
        +Enter your answer as a comma-separated list of vectors, for example:    +<4,3>, <5,10> +
        +
      • + +
      • If there are no solutions: +
        +Enter   NONE   or   DNE   (this may vary from problem to problem) +
        +
      • + +
      • Examples of constants used in vectors: +
        pi, e = e^1
        +
      • + +
      • Functions may be used in each coordinate of a vector, but may not be applied across the parentheses or commas: +
        +<sqrt(2),sqrt(5)>   is valid, but   <sqrt(2,5)>   is not +
        +
        +Link to a list of all available functions +
        +
      • + +
      + + + + \ No newline at end of file diff --git a/htdocs/helpFiles/Grades.html b/htdocs/helpFiles/Grades.html new file mode 100644 index 0000000000..4560895fd9 --- /dev/null +++ b/htdocs/helpFiles/Grades.html @@ -0,0 +1,31 @@ + + + +Student Grades Help Page + + +
      +

      +

      The "Ind" column is : (totalNumberOfCorrectProblems / totalNumberOfProblems)^2/ AvgNumberOfAttemptsPerProblem), which is a more +useful number when it comes to summarizing how well you are doing. +

      +

      In the problems section of the chart the upper letter represents whether or not the problem was answered correctly (C for +correct) and the lower number is the number of times the problem was attempted.

      + + diff --git a/htdocs/helpFiles/InstructorAddUsers.html b/htdocs/helpFiles/InstructorAddUsers.html new file mode 100644 index 0000000000..cd65d30ed0 --- /dev/null +++ b/htdocs/helpFiles/InstructorAddUsers.html @@ -0,0 +1,37 @@ + + + + + +Add User Help Page + + +
      +

      Add User Help Page

      +

      The number of rows can be changed by changing the number in the text box and hitting the "create" button, but be careful, +once some information is entered do not change the number of rows or all the text in the boxes will be lost.

      +

      The only mandatory information is the "login" and the "Student ID", the Student ID will be the student's initial password.

      +

      Any other boxes may be left blank, but it may be useful to fill in section and recitation as they can be used to seperate +scores later on.

      +

      Multiple sets may be given to the user by holding down the shift key while selecting from the list.

      + + + diff --git a/htdocs/helpFiles/InstructorAssigner.html b/htdocs/helpFiles/InstructorAssigner.html new file mode 100644 index 0000000000..8f23707b0c --- /dev/null +++ b/htdocs/helpFiles/InstructorAssigner.html @@ -0,0 +1,27 @@ + + + +Set Assigner Help Page + + +
      +

      Either list can be reordered by changing the criteria and pushing the button. Then multiple sets can be assigned to a user, or +multiple users to a set.

      + + diff --git a/htdocs/helpFiles/InstructorConfig.html b/htdocs/helpFiles/InstructorConfig.html new file mode 100644 index 0000000000..6ff1c68963 --- /dev/null +++ b/htdocs/helpFiles/InstructorConfig.html @@ -0,0 +1,48 @@ + + + + + +Course Configuration Help Page + + +
      +

      +This page allows you to configuration of certain parameters, such as +

        +
      • permission levels,
      • +
      • display modes allowed
      • +
      • default display mode
      • +
      • and email feedback behavior,
      • +
      +on a course by course basis. +

      + +

      +Click on each of the tabs to view the configuration items. The question +mark icon provides access to information about the behavior of the configuration +settings. +

      + + +
      + + diff --git a/htdocs/helpFiles/InstructorFileManager.html b/htdocs/helpFiles/InstructorFileManager.html new file mode 100644 index 0000000000..847a0172fb --- /dev/null +++ b/htdocs/helpFiles/InstructorFileManager.html @@ -0,0 +1,92 @@ + + + + + +File Manager Help Page + + +
      +

      File Manager Help Page

      + +

      +The File Manager module will soon replace the File Transfer module. We'll keep +both available for the time being. +

      +Locations of files: +

      +
      Set definition (".def") files
      +
      These are stored in the templates directory. To view the format for + Set Definition files see + Set Definition specification + or download set0.def and copy that. Set definition files are mainly + useful for transferring set assignments from one course to another. + (For example from a calculus course taught the previous year.)They contain a + list of problems used and the dates and times. + These definitions can be imported into the current course and a set in the current course + can be exported to a set definition files.
      +
      Class list (".lst") files
      +
      The classlist files are stored in the templates directory and provide + a convenient way to enter a large number of students into your + class. To view the format for ClassList files see + ClassList specification + or download demoCourse.lst and use it as a model. + ClassList files can be + prepared using a spreadsheet and then saved as .csv (comma separated + variables) text files.
      +
      Scoring (".csv") files
      +
      The scoring files are stored in the scoring directory and are produced + using the "Scoring" module or the scoring command in the "Instructor Tools" module. + These files can be downloaded, modified in a spread sheet (for example, add midterm scores) + and then uploaded again to the scoring directory to be merged with email messages. (Use + a new file name, other than courseName_totals.csv, when uploading to prevent the + scoring module from overwriting an uploaded file.) + ) +
      +
      Problem template (".pg") files
      +
      These provide the template from which are problems are created and + are located in the template directory. They can be edited directly using + the "edit problem" link on each problem page. The File Manager allows you to + upload or download these files so that you can save them on your desktop.
      +
      html directory
      +
      This directory is accessible from the web. You can use it to store html documents + or image documents that are used by the course. Do not store private information in + this directory or in any subdirectory.These documents can be pointed to from within + problems using the htmlLink macro
      +
      templates/email directory
      +
      This is where email messages are saved. You can upload or download files in + this directory if you wish to save the files for later.
      +
      templates/macros directory
      +
      Macro (".pl") files containing favorite macros for your course can be stored here. Those + being used for many courses should be stored in pg/macros.
      +
      + + +

      + + +

      + +

      + + + + diff --git a/htdocs/helpFiles/InstructorFileTransfer.html b/htdocs/helpFiles/InstructorFileTransfer.html new file mode 100644 index 0000000000..3c8c6c7e83 --- /dev/null +++ b/htdocs/helpFiles/InstructorFileTransfer.html @@ -0,0 +1,50 @@ + + + + + +File Transfer Help Page + + +
      +

      File Transfer Help Page

      + +

      +To view the format for ClassList files see +ClassList specification +or download demoCourse.lst and use it as a model. +ClassList files can be +prepared using a spreadsheet and then saved as .csv (comma separated +variables) text files. +

      + + +

      +To view the format for Set Definition files see +Set Definition specification +or download set0.def and copy that. Set definition files are mainly +useful for transferring course assignments from one course to another. +(For example from a calculus course taught the previous year.) +

      + + + + diff --git a/htdocs/helpFiles/InstructorIndex.html b/htdocs/helpFiles/InstructorIndex.html new file mode 100644 index 0000000000..665e6790d6 --- /dev/null +++ b/htdocs/helpFiles/InstructorIndex.html @@ -0,0 +1,52 @@ + + + + + + +Instructor Tools Help Page + + + +

      Instructor Tools Help Page

      +
      +

      +This page allows quick access to an individual student's homework set, + a homework set for the entire class, or the class roster data for one or more + students. +

      + +

      +It is a collection of short cut techniques which allow you to modify the database with fewer +page clicks than are required when using the "Class List Editor" and "Hmwk Sets Editor" pages. It is often more efficient because the set and the student can both be selected at the same time. +

      + +

      +New users will probably find it easier to use those two pages until they get used to the information required. +

      + +

      +Importing and exporting class lists, and deleting students must be done +from the class list editor. +

      +
      + + diff --git a/htdocs/helpFiles/InstructorPGProblemEditor.html b/htdocs/helpFiles/InstructorPGProblemEditor.html new file mode 100644 index 0000000000..005e2b09bd --- /dev/null +++ b/htdocs/helpFiles/InstructorPGProblemEditor.html @@ -0,0 +1,115 @@ + + + + + + +PG Problem Editor Help Page + + + +

      PG Problem Editor Help Page

      +
      +

      +This page allows one to edit the contents of PG problem files (templates) +as well as set headers and other files. +

      +

      +When using this page two or more windows may be created -- one to edit the text in the +file and one to view the results. This can be a bit confusing. If a window seems to +have completely disappeared make sure that it is not hiding behind one of the other windows. +

      +

      Features:

      +
      + + +
      | Manpages + | + macro list + | + authoring info and help + | + testing lab + | + pod docs + | Report problem bugs + |
      +
      The first five links are to pages that give some information about writing problems: + +
      +
      Manpages
      +
      This links to an (incomplete) list of the most commonly used macros with a description of their parameters and how to use them;
      +
      + macro list
      +
      This link provides a complete list of macros;
      + +
      + authoring info and help
      +
      This link takes you to the authoring section of the WeBWorK wiki;
      +
      + testing lab
      +
      This links to a WeBWorK "problem" which allows you to try out fragments of PG code.
      +
      + pod docs
      +
      This link gives details for many macros. It links to documentation embedded in the macro files themselves;
      +
      Report problem bugs
      +
      If you find an error in a library problem please use the link to notify us so that we can fix it. You will need to register with an email address the first time you use bugzilla, but after that you will not.
      +
      + +
      +
      The large text window
      +
      This is where you edit the text of the problem template. Nothing happens until you pick an action (below) and + click the "Take action!" button.
      +
      + +

      Action items:

      (If an action cannot be executed it will not appear as an action item.) + +
      +
      View using seed "12345" and display mode "image"
      +
      Will bring up a window to show the results of your editing. It does not change the permanent file on the disk. You can view + different versions of the same problem by changing the seed (any number will do). You can also change mannerin which the + mathematics is typset (jsMath and images are the most common modes.)
      +
      Add to set ...
      +
      Add this problem as the last problem of an existing set, either as a problem or as the set header + (the text that appears on the home page of a homework set). You can rearrange the order of the problems later using + the "Hmwk Sets Editor"
      +
      Save as [TMPL]/"path"...
      +
      Makes a new copy of the file you are editing at the location relative to the course's templates ([TMPL]) directory given by "path". + The checkbox allows you to replace the current problem with the new problem in the current homework set. +

      Check the checkbox if you want to edit the current problem without changing the original problem file.

      +

      Leave the checkbox unchecked if you are using the current problem as a model for a brand new problem. You can add the + new file to the homework set from the Library Browser.

      +

      If the original problem can not be edited than the path name + must be changed in order to be allowed to save the problem. (Adding "local/" to the beginning of the original path is the default solution. All locally created and + edited files will then appear in a subdirectory named "local".

      +

      A new problem whose path ends in blankProblem.pg should be given a new name, for example, "myNewProblem.pg")

      +
      Revert
      +
      Reverts to the copy of the file saved on the disk -- all unsaved editing changes will be lost. + This option only appears when you have making + provisional edits using the first option "View..." above
      +
      Take action
      +
      Executes the action item selected above. You can elect to have the results of this action appear in a new window. +
      +
      + +
      + + diff --git a/htdocs/helpFiles/InstructorPreflight.html b/htdocs/helpFiles/InstructorPreflight.html new file mode 100644 index 0000000000..45b983be36 --- /dev/null +++ b/htdocs/helpFiles/InstructorPreflight.html @@ -0,0 +1 @@ +hi diff --git a/htdocs/helpFiles/InstructorProblemSetDetail.html b/htdocs/helpFiles/InstructorProblemSetDetail.html new file mode 100644 index 0000000000..2e37e75be2 --- /dev/null +++ b/htdocs/helpFiles/InstructorProblemSetDetail.html @@ -0,0 +1,130 @@ + + + + + +Set Detail Help Page + + + + +

      Set Detail Help Page

      + + +

      "Modes"

      + +

      The Set Detail page can be viewed in one of three distinct modes: +

        +
      • All students
      • +
      • One specific student
      • +
      • Many students
      • +
      + +This should be quite clear at the top of the page. If one or more of the students selected has not +had this set assigned to them, then those students will be ignored (they are listed). This can be fixed +if needed by clicking on the link x users or the navigation link Set Assigner + +

      Note: There is a subtle difference between editing a set for an entire class and editting it +for every student in the set at once. If you select all the students and edit the set for all +of them, any new students added after those changes will get the old, unchanged information. Unless you +have good reason, it is best to edit the set in general. + +

      + +

      Contents

      + + + + + + +

      Form buttons

      +
      +
      Save Changes
      +
      Occurs twice on the page, at the top and the bottom.
      Any and all changes made to any + part of the set will be saved with one important exception: When editting the set for one + or more students, you can choose to override the default value (defined for the set in + general) by clicking on the checkbox and providing a value (or possibly leaving a blank). + If you do NOT check the checkbox, the override value will be discarded. +

      Example: +
      Weight 1 +
      Weight 1 +
      The first value of 2 will be discarded because the checkbox is not checked. +

      +
      Reset Form
      +
      Discards any and all changes, reloading the page by re-reading the database information + for this set. Also, resets the display modes.

      +
      Refresh Display
      +
      You can maintain a different display mode for the headers and for the problems if you want. +
      "Refresh Display" does not commit any of the changes currently in the form, but it also + does not reset the form so you can safely change the display mode without making any permanent + changes to the set and without losing any work up to that point. +

      Example: +
      Display Mode:  +   + +
      Change display mode to "images" +

      +
      Reorder problems only
      +
      Sometimes it could be necessary to want to change the order of the problems in a set + without actually make any changes to the information about that problem. This can even + be done individually for one or more students. If all you want to is reorder the problems, + this is much faster than "Save Changes". However, any changes made in the form and not yet + saved will be lost. + +
      +
      Add blank problem template
      +
      Adds a new problem essentially blank problem to the homework set when the homework set is saved. + You can modify this to create your own problem, by clicking on the "Edit it" link and + saving the "blankProblem.pg" to some new file, e.g. "myNewProblem.pg". +
      + +

      General Information

      + + +All of the paths for problem files are relative to the templates directory +of the current course. You can access this directory "directly" using the File Manager +page. + + + diff --git a/htdocs/helpFiles/InstructorProblemSetList.html b/htdocs/helpFiles/InstructorProblemSetList.html new file mode 100644 index 0000000000..fdd23f1c32 --- /dev/null +++ b/htdocs/helpFiles/InstructorProblemSetList.html @@ -0,0 +1,85 @@ + + + +Set List Help Page + + +

      After selecting a task with the radio buttons (round buttons on the left), +click the "Take action" button. +

      +
      +
      +
      Show
      +
      Display sets matching a selected criteria. + Useful if there are many sets, such as in a library.
      +
      Sort
      +
      Sorts the lists displayed; by due date, name, etc.
      +
      Edit
      +
      The sets checked below will become available for editing the due dates and set headers.
      +
      Make visible/hidden
      +
      The sets checked below will be visible to students or hidden from students. Useful for preparing sets + ahead of time, but not showing them to students; or for temporarily hiding a set which has a mistake in + it while it is being fixed.
      +
      Import
      +
      Import a set from a set definition file. + (Use "File Transfer" to upload/download set definition files.)
      +
      Export
      +
      The sets checked below will written to set definition files, to be saved for future use, or + to be transferred to another course. The set definition files can be uploaded/downloaded + using "File Transfer". +
      +
      Score
      +
      Student scores for the sets selected below will be calculated. This and other scoring operations + can also be done using the "Scoring Tools" link.
      +
      Create
      +
      Create a new, empty set. This can also be done directly from the "Library Browser".
      +
      Delete
      +
      Delete the sets checked below. Be careful, this cannot be undone.
      +
      +
      +Other actions +
      +
      "Edit Problems" column
      +
      Indicates the number of problems in the set. Clicking on this link allows you to edit the set headers, + change the problem order, specifying the number of allowed attempts and the weight (credit value) of each problem. + You can also inspect and edit the template of the problem and delete the problem entirely. +

      + To add new problems use the "Library Browser". +

      +
      "Edit Assigned Sets" column
      +
      Shows how many instructors and students have been assigned this problem set, out of the total number in the class. + (While testing it is best to assign + the problem only to instructors and TA's; once the set is ready, assign it to the entire class or section.) + Clicking on this link allows you to assign or unassign this set to additional users and to individually edit + the assignment to specific users. For changing dates for individual users there are also + shortcuts from the "Instructor Tools" link. +
      +
      Changing dates
      +
      Dates for problem sets can be edited by clicking the pencil in the "Edit Set Data" column next to the set name. + To change dates for several sets at once, click the check box in the "Select" column and + choose "Edit selected" from the tasks above. +

      + For those upgrading from WW2.0 to WW2.1: The headers have moved -- to edit them click in the "Edit problems" column. +

      + +
      + + + + diff --git a/htdocs/helpFiles/InstructorScoring.html b/htdocs/helpFiles/InstructorScoring.html new file mode 100644 index 0000000000..1c5b158dea --- /dev/null +++ b/htdocs/helpFiles/InstructorScoring.html @@ -0,0 +1,96 @@ + + + +Scoring Tools Help Page + + +
      + +

      +WeBWorK does not have a full featured scoring ability -- we leave that to your +favorite spread sheet application. +

      + +

      +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. +

      + +

      +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. +

      + +

      +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. +

      + +

      +I have no clue what "record scores for single sets" does -- sorry. +

      + +

      +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. +

      + +

      +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) +

      + +

      +To use the Email and spreadsheet merge feature, upload your spreadsheet with +calculated grades to the scoring directory using the File Manager link. +

      + +

      +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). +

      + +

      +If you upload your file on the web with the name: report_grades_data.csv +and also create an email message with the name report_grade.msg with the +approriate $COL 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. +

      + + + + + diff --git a/htdocs/helpFiles/InstructorSendMail.html b/htdocs/helpFiles/InstructorSendMail.html new file mode 100644 index 0000000000..468f7497e8 --- /dev/null +++ b/htdocs/helpFiles/InstructorSendMail.html @@ -0,0 +1,34 @@ + + + +Mail Merge Help Page + + +
      +

      Emails can be sent from this page to the class. Be sure to change all blanks which read "fixme". The email can be sent to +all students, or some subset. To change the way the students are ordered, after selecting the criteria, push the "Change Display +Settings" button.

      +

      Personalized data can be inserted into the message using any of the variables available in the drop down menu titled "list of +insertable macros". Most of the personal data does not need a merge file, but if you wish to include scoring data, you must select +that scoring file as the merge file, and then enter the score into the message by using what ever column the score is in. For example, +if the scores where in the 4 column of s0scr.cvs then I would set the merge file to that file, and then type $COL[4] to have that +be the score.

      +

      The size of the email box can be changed by changing the numbers in the "columns" and "rows" boxes and pressing enter.

      + + diff --git a/htdocs/helpFiles/InstructorSetMaker.html b/htdocs/helpFiles/InstructorSetMaker.html new file mode 100644 index 0000000000..6f63123e75 --- /dev/null +++ b/htdocs/helpFiles/InstructorSetMaker.html @@ -0,0 +1,32 @@ + + + +Problem Set Maker Help Page + + +
      +

      This page is used to create problem sets. The first step is to choose which set your working on. If you need to name a new problem +set fill in the blank next to the "Create a New Set in This Course" button and then press the button. Then choose which set you want to +work on in the drop down menu.

      +

      Use the options in the uppermost box to pick a collection of problems to look at, and then to add problems to the set select them +and then hit update.

      +

      To set the open and close dates, assign the set to users, delete problems from the set, or change their order, click on the +"Edit Taget Set" button.

      + + diff --git a/htdocs/helpFiles/InstructorStats.html b/htdocs/helpFiles/InstructorStats.html new file mode 100644 index 0000000000..01dd3f4a64 --- /dev/null +++ b/htdocs/helpFiles/InstructorStats.html @@ -0,0 +1,30 @@ + + + +Statistics Help Page + + +
      +

      This page is a useful tool for Professors who want to see how their students are doing. Statistics can either be viewed for a single +set or for a single user simply by clicking on the link.

      +

      The "Ind" column is : (totalNumberOfCorrectProblems / totalNumberOfProblems)^2/ AvgNumberOfAttemptsPerProblem), which is a more +useful number when it comes to summarizing how well students are doing.

      +

      The bottom most chart on the set statistics can be resorted by any column by clicking the blue link of that column's name.

      + + diff --git a/htdocs/helpFiles/InstructorUserDetail.html b/htdocs/helpFiles/InstructorUserDetail.html new file mode 100644 index 0000000000..486c75fd7e --- /dev/null +++ b/htdocs/helpFiles/InstructorUserDetail.html @@ -0,0 +1,49 @@ + + + + + + +Instructor Tools Help Page + + + +

      Sets assigned to student Help Page

      +
      +

      A student must be assigned a homework set in order for it to appear on their WeBWorK home page. Check the box in the left column to assign a student to that homework set. Uncheck the box to unassign a student. If a student is unassigned from a homework set, all of the student's data for that homework set is deleted +and cannot be recovered. Use this action cautiously!

      + +

      +Change the due dates for an individual +student on this page. Check the checkbox and enter the new date in order +to override the date. (You can copy the date format from the date in the right hand column which indicates the date when the homework set is due for the whole class.) +

      + +

      +Click on the homework set links to edit change the grades for this individual student on the homework set. (The grade of each problem is called the "status".) +You can also change the number of allowed attempts and further modify the +individual student's homework set.) You will also be able to change a student's seed which determines the individual values used in the individual version of the student's problem and the weight (how much a problem counts towards the grade.) +

      + +
      + + + diff --git a/htdocs/helpFiles/InstructorUserList.html b/htdocs/helpFiles/InstructorUserList.html new file mode 100644 index 0000000000..b4cc28d7ef --- /dev/null +++ b/htdocs/helpFiles/InstructorUserList.html @@ -0,0 +1,107 @@ + + + + + +Class List Editor Help Page + +

      Class List Editor Help Page

      + +
      +

      +From this page you can add new students, to edit the class list data (names, email addresses, recitation, section +permission levels and enrollment status), to change passwords, to export (save) +class lists for back-up or use in another course. You can also delete students from the class roster, but this cannot be undone. +

      +

      +This page gives access to information about the student, independ of the homework sets +assigned to them. +

      +

      +To perform an action first click the radio button next to desired action then click the "Take Action!" button. +

      + + +

      How to:

      +
      +
      Restrict or sort the students displayed
      +
      When the class is very large not all students will be displayed. Using the first action on this page you can show only the students from a given recitation or from a given section, or only students whose login or last name fits a pattern match. The second action will sort the students currently being displayed. You can also sort the displayed students by clicking on the active links at the top of each column.
      + +
      Edit class list data
      +
      You can edit the class list data for a single student by clicking on the pencil icon next to their login name. To edit several students at once click on the "Select" checkbox next to their names, click on the radio button for editing selected users and then click "Take Action!". You can also edit all visible users (those students currently being displayed) or even all users in the course although this last option might take a long time to load for a large class.
      +
      The login name cannot be changed. (It is the primary key for the student's data.) If you make a mistake in the login name at the beginning of the course (before any work has been done) then it is best to simply add a new student with the correct entry and drop the student with the bad login name. (See drop and delete students below.)
      +
      Add a few students to the course.
      +
      Click the "Add x student(s)" radio button and then click "Take action". This will take you to a new page where the data can be entered for one or more students. It is also possible to assign the student to one or more problem sets as they are being entered: simply select the homework sets from the list below the data entry table. Use 'command' or 'control' click to select more than one homework set.
      + +
      Add many students to a course from a class list.
      +
      This is most easily done by importing a class list. The class list can be uploaded from your workstation to the server using the File Manager page. The class list must be a file ending in .lst and must have a specific format. Once the file has been uploaded to the server the file will appear in the import action pop-up list (5th action). demoCourse.lst is available for most courses and adds the "practice users" which activate guest logins to the class list .
      + +
      Add a TA or an instructor (change permission level of user)
      +
      This is done by first entering the user as a student and then changing the permission level of the user. First edit the user by clicking on the pencil next to their name (or using the technique above for several users), then change their permssion level -- an entry blank at the far right of the screen, you may have to scroll to see it. The permission levels are +
        +
      • Guest: -5
      • +
      • Student: 0
      • +
      • Proctor: 2
      • +
      • TA: 5
      • +
      • Instructor: 10
      • +
      + +
      Drop student from the course
      +
      To drop a student or students, select them for editing as described above and then set the pop-up list to enrolled,drop, or audit. Dropped students cannot log in to the course, are not assigned new homework sets and are not sent e-mail. They can be re-enrolled simply by changing their status back to enrolled. No data is lost, any homework sets assigned before they were dropped are restored unchanged.
      +
      Delete a student from a course
      +
      This should be done cautiously. Once a student is deleted from a course their data is lost forever and cannot be recovered. They can be added to the course as a new student, but all of their homework set assignments and homework has been permanently deleted.
      +
      Assign sets to one student
      +
      To assign one or more sets to an individual student click in the column "Assigned Sets" in the student's row. This will take you to a page where you can assign and unassign homework sets and change the due dates for homework on an individual basis.
      +
      Change the due date for one student
      +
      Click on the column "Assigned Sets" in the student's row. This will take you to a page where you can assign and unassign homework sets and change the due dates for homework on an individual basis.
      +
      "Act as" a student
      +
      Clicking on the login name link in a student's row allows you to view the student's version of the homework (rather than your own) so that you can more easily answer student questions about homework problems. (A "acting as xxx" alert will appear in the upper right corner of each window while you are acting as a student.) You can submit the answers (which will NOT be recorded) to check that the computer is grading the problem correctly. You will also be able to view past answers submitted by the student for each problem. To stop acting in the student's role click the "Stop acting" link in the upper right corner of the window.
      +
      Change the grades on a homework set for one student.
      +
      Click first in the "Assigned Sets" column in the student's row. This will take you to a new page where you will click on the link to the homework set where the grade change is to be made. (The grade for each problem is listed as "status" on this third page).
      +
      Extend the number of attempts allowed a student on a given problem
      +
      Click first in the "Assigned Sets" column in the student's row. This will take you to a new page where you will click on the link to the homework set where the grade change is to be made.
      +
      Assign sets to many students
      +
      This is done from the "Hmwrk Sets Editor" or from the "Instructor Tools" page if you wish to assign a homework set to all students or a large group of students (e.g. a section).
      +
      Change dates for a homework set for the whole class.
      +
      This is done from the "Hmwrk Sets Editor" or from the "Instructor Tools" page.
      +
      Change the grading on a homework set for an entire class.
      +
      You might want to do this if you want to give full credit to everyone on a particular problem that was not worded correctly, or wasn't working properly. This is done from the "Hmwk sets editor" page or the "Instructor tools" page.
      +
      Change the number of atttempts allowed on a problem.
      +
      This is done from the "Hmwk sets editor" page or the "Instructor tools" page.
      +
      + +

      +Many of these editing activities can also be done more quickly from the "Instructor tools" page where students and homework sets can be selected simultaneously. The Instructor Tools page is useful for quick editing of one or two students. The initial setup of the class can be done best from this page. +Importing and +exporting class lists can only be done from this page. Deleting students can only be done from this page. +

      +

      Review of column functions:

      +
        +
      • Clicking on any active link at the top of the column sorts the page by that column. You can do lexigraphic sorts: click on "First name" then "Last name" to sort by last name, sorting those with the same last name by their first name.
      • +
      • The login name column links allow you to "act as" a student.
      • +
      • The pencil in the login column allows you to edit that student's data.
      • +
      • The Assigned sets column (x/y) indicates that x sets out of y sets avaiable have been assigned to this student. Click this link to assign or unassign sets to this student, to adjust due dates, or to adjust the grades on a homework set for a student.
      • +
      • Clicking the e-mail address link will bring up your standard email application so that you can send email to the student. This works even if the student has been dropped from the course. To send email to an entire class or to merge grades with the email message use the "Email" page link in the left margin.
      • +
      + +
      + + diff --git a/htdocs/helpFiles/InstructorUsersAssignedToSet.html b/htdocs/helpFiles/InstructorUsersAssignedToSet.html new file mode 100644 index 0000000000..846ad56215 --- /dev/null +++ b/htdocs/helpFiles/InstructorUsersAssignedToSet.html @@ -0,0 +1,27 @@ + + + +Users Assigned to Set Help Page + + +
      +

      A student needs to be assigned a set in order for them to do it. Once a student is unassigned a set, all the data for them is lost +and there's no way to undo it. When unassigning be sure to change the option to "Allow unassign".

      + + diff --git a/htdocs/helpFiles/IntervalNotation.html b/htdocs/helpFiles/IntervalNotation.html new file mode 100644 index 0000000000..e2feb1d51b --- /dev/null +++ b/htdocs/helpFiles/IntervalNotation.html @@ -0,0 +1,68 @@ + + +WeBWorK Help: Interval Notation + + + +

      +Using Interval Notation +

      + +
        + +
      • If an endpoint is included, then use [ or ]. +If not, then use ( or ). For example, the interval +from -3 to 7 that includes 7 but not -3 is expressed (-3,7]. + +
        +
        + +
      • For infinite intervals, use Inf +for (infinity) and/or +-Inf for -∞ (-Infinity). For +example, the infinite interval containing all points greater than or +equal to 6 is expressed [6,Inf). + +
        +
        + +
      • If the set includes more than one interval, they are joined using the union +symbol U. For example, the set consisting of all points in (-3,7] together with all points in [-8,-5) is expressed [-8,-5)U(-3,7]. + +
        +
        + +
      • If the answer is the empty set, you can specify that by using + braces with nothing inside: { } + +
        +
        + +
      • You can use R as a shorthand for all real numbers. + So, it is equivalent to entering (-Inf, Inf). + +
        +
        + +
      • You can use set difference notation. So, for all real numbers + except 3, you can use R-{3} or + (-Inf, 3)U(3,Inf) (they are the same). Similarly, + [1,10)-{3,4} is the same as [1,3)U(3,4)U(4,10). + +
        +
        + + +
      • WeBWorK will not interpret [2,4]U[3,5] as equivalent + to [2,5], unless a problem tells you otherwise. +All sets should be expressed in their simplest interval notation form, with no +overlapping intervals. + +
      + + + + diff --git a/htdocs/helpFiles/Options.html b/htdocs/helpFiles/Options.html new file mode 100644 index 0000000000..a8d4c7cd83 --- /dev/null +++ b/htdocs/helpFiles/Options.html @@ -0,0 +1,28 @@ + + + +User Option Help Page + + +
      +

      The password can be any combination of letters, numbers and special symbols. Students are encouraged to use all three, and stay +away from dictionary words. Also you should not use the same password you use elsewhere, such as on your email account.

      +

      It is important that you enter your email address or you won't recieve any of the important emails your professor sends you.

      + + diff --git a/htdocs/helpFiles/ProblemSets.html b/htdocs/helpFiles/ProblemSets.html new file mode 100644 index 0000000000..274eec5549 --- /dev/null +++ b/htdocs/helpFiles/ProblemSets.html @@ -0,0 +1,45 @@ + + + + + +Course Home Help Page + + +
      +

      Course Home Help Page

      +

      +This is the standard entry point for the course. +

      + +

      +It lists the homework problem sets available for the students. +

      + +

      +The information box on the right displays information is useful for +leaving course wide messages for students. It contains text from the file +templates/course_info.txt. Instructors can edit this file from the +web (click on the "edit" link). +

      + + + diff --git a/htdocs/helpFiles/Problems.html b/htdocs/helpFiles/Problems.html new file mode 100644 index 0000000000..79e48b4c41 --- /dev/null +++ b/htdocs/helpFiles/Problems.html @@ -0,0 +1,29 @@ + + + +Problems Help Page + + +
      +

      If you want to see the problems change the display mode and press "Refresh". The order of problems can be changed by changing the +numbers. After a problem is deleted (by checking delete and then the "Save Problem Changes" button on the bottom of the page) +the number is left empty unless the option above the button is checked. The +weight of a problem is how many points will be awarded for it during scoring.

      + + diff --git a/htdocs/helpFiles/Syntax.html b/htdocs/helpFiles/Syntax.html new file mode 100644 index 0000000000..2673dd759e --- /dev/null +++ b/htdocs/helpFiles/Syntax.html @@ -0,0 +1,136 @@ + + + + Syntax for entering answers to WeBWorK + + + +

      Syntax for entering answers to WeBWorK

      + + +

      Mathematical Symbols Available In WeBWorK

      +
      • + Addition +
      • - Subtraction +
      • * Multiplication can also be indicated by a space or juxtaposition, e.g. 2x, 2 x or 2*x, also 2(3+4). +
      • / Division +
      • ^ or ** You can use either ^ or ** for exponentiation, e.g. 3^2 or 3**2 +
      • Parentheses: () - You can also use square brackets, [ ], and braces, { }, for grouping, e.g. [1+2]/[3(4+5)] +
      +

      Syntax for entering expressions

      +
      • Be careful entering expressions just as you would be careful entering expressions in a calculator. +
      • Use the "Preview Button" to see exactly how your entry +looks. E.g. to tell the difference between 1+2/3*4 and [1+2]/[3*4] +click the "Preview Button". +
      • Sometimes using the * symbol to indicate mutiplication makes +things easier to read. For example (1+2)*(3+4) and (1+2)(3+4) are both +valid. So are 3*4 and 3 4 (3 space 4, not 34) but using a * makes +things clearer. +
      • Use ('s and )'s to make your meaning clear. You can also use ['s and ]'s and {'s and }'s. +
      • Don't enter 2/4+5 (which is 5.5) when you really want 2/(4+5) (which is 2/9). +
      • Don't enter 2/3*4 (which is 8/3) when you really want 2/(3*4) (which is 2/12). +
      • Entering big quotients with square brackets, e.g. [1+2+3+4]/[5+6+7+8], is a good practice. +
      • Be careful when entering functions. It's always good practice +to use parentheses when entering functions. Write sin(t) instead of +sint or sin t even though WeBWorK is smart enough to usually accept sin t or even sint. For example, sin 2t is interpreted as sin(2)t, i.e. (sin(2))*t so be careful. +
      • You can enter sin^2(t) as a short cut although mathematically +speaking sin^2(t) is shorthand for (sin(t))^2(the square of sin of t). +(You can enter it as sin(t)^2 or even sint^2, but don't try such things +unless you really understand the precedence of operations. The +"sin" operation has highest precedence, so it is performed first, using +the next token (i.e. t) as an argument. Then the result is squared.) +You can always use the Preview button to see a typeset version of what +you entered and check whether what you wrote was what you +meant. :-) +
      • For example 2+3sin^2(4x) will work and is equivalent to +2+3(sin(4x))^2 or 2+3sin(4x)^2. Why does the last expression work? +Because things in parentheses are always done first [ i.e. (4x)], next +all functions, such as sin, are evaluated [giving sin(4x)], next all +exponents are taken [giving sin(4x)^2], next all multiplications and +divisions are performed in order from left to right [giving +3sin(4x)^2], and finally all additions and subtractions are performed +[giving 2+3sin(4x)^2]. +
      • Is -5^2 positive or negative? It's negative. This is because +the square operation is done before the negative sign is applied. Use +(-5)^2 if you want to square negative 5. +
      • When in doubt use parentheses!!! :-) +
      • The complete rules for the precedence of operations, in addition to the above, are +
        • Multiplications and divisions are performed left to right: 2/3*4 = (2/3)*4 = 8/3. +
        • Additions and subtractions are performed left to right: 1-2+3 = (1-2)+3 = 2. +
        • Exponents are taken right to left: 2^3^4 = 2^(3^4) = 2^81 = a big number. +
        +
      • Use the "Preview Button" to see exactly how your entry +looks. E.g. to tell the difference between 1+2/3*4 and [1+2]/[3*4] +click the "Preview Button". +
      +

      Mathematical Constants Available In WeBWorK

      +
      • pi This gives 3.14159265358979, e.g. cos(pi) is -1 +
      • e This gives 2.71828182845905, e.g. ln(e*2) is 1 + ln(2) +
      +

      Scientific Notation Available In WeBWorK

      +
      • 2.1E2 is the same as 210 +
      • 2.1E-2 is the same as .021 +
      +

      Mathematical Functions Available In WeBWorK

      +

      Note that sometimes one or more of these functions is disabled for a WeBWorK problem because the +instructor wants you to calculate the answer by some means other than just using the function. +

      +
      • abs( ) The absolute value +
      • cos( ) Note: cos( ) uses radian measure +
      • sin( ) Note: sin( ) uses radian measure +
      • tan( ) Note: tan( ) uses radian measure +
      • sec( ) Note: sec( ) uses radian measure +
      • cot( ) Note: cot( ) uses radian measure +
      • csc( ) Note: csc( ) uses radian measure +
      • exp( ) The same function as e^x +
      • log( ) This is usually the natural log but your professor may have redined this as log to the base 10 +
      • ln( ) The natural log +
      • logten( ) The log to the base 10 +
      • arcsin( ) +
      • asin( ) or sin^-1() Another name for arcsin +
      • arccos( ) +
      • acos( ) or cos^-1() Another name for arccos +
      • arctan( ) +
      • atan( ) or tan^-1() Another name for arctan +
      • arccot( ) +
      • acot( ) or cot^-1() Another name for arccot +
      • arcsec( ) +
      • asec( ) or sec^-1() Another name for arcsec +
      • arccsc( ) +
      • acsc( ) or csc^-1() Another name for arccsc +
      • sinh( ) +
      • cosh( ) +
      • tanh( ) +
      • sech( ) +
      • csch( ) +
      • coth( ) +
      • arcsinh( ) +
      • asinh( ) or sinh^-1() Another name for arcsinh +
      • arccosh( ) +
      • acosh( ) or cosh^-1()Another name for arccosh +
      • arctanh( ) +
      • atanh( ) or tanh^-1()Another name for arctanh +
      • arcsech( ) +
      • asech( ) or sech^-1()Another name for arcsech +
      • arccsch( ) +
      • acsch( ) or csch^-1() Another name for arccsch +
      • arccoth( ) +
      • acoth( ) or coth^-1() Another name for arccoth +
      • sqrt( ) +
      • n! (n factorial -- defined for nÕ0  +
      • These functions may not always be available for every problem. +
        • sgn( ) The sign function, either -1, 0, or 1 +
        • step( ) The step function (0 if x<0 , 1 if xÕ0 ) +
        • fact(n) The factorial function n! (defined only for nonnegative integers) +
        • P(n,k) = n*(n-1)*(n-2)...(n-k+1) the number of ordered sequences of k elements chosen from n elements +
        • C(n,k) = "n choose k" the number of unordered sequences of k elements chosen from n elements +
        +
      + +For more information: + +http://webwork.maa.org/wiki/Available_Functions + + + + diff --git a/htdocs/helpFiles/Units.html b/htdocs/helpFiles/Units.html new file mode 100644 index 0000000000..e81cdb03a8 --- /dev/null +++ b/htdocs/helpFiles/Units.html @@ -0,0 +1,86 @@ + + + WeBWorK Help: Units + + + + + +

      + +

      + +

      Units Available in WeBWorK

      +

      +Some WeBWorK problems ask for answers with units. Below is a list of basic units +and how they need to be abbreviated in WeBWorK answers. In some problems, you +may need to combine units (e.g, velocity might be in ft/s for feet per +second). +

      + +
      Unit Abbreviation +
      Time +
      Seconds s +
      Minutes min +
      Hours hr +
      Days day +
      Years year +
      Milliseconds ms + +
      Distance +
      Feet ft +
      Inches in +
      Miles mi +
      Meters m +
      Centimeters cm +
      Millimeters mm +
      Kilometers km +
      Angstroms A +
      Light years light-year + +
      Mass +
      Grams g +
      Kilograms kg +
      Slugs slug + +
      Volume +
      Liters L +
      Cubic Centimeters cc +
      Milliliters ml + +
      Force +
      Newtons N +
      Dynes dyne +
      Pounds lb +
      Tons ton + +
      Work/Energy +
      Joules J +
      kilo Joule kJ +
      ergs erg +
      foot pounds lbf +
      calories cal +
      kilo calories kcal +
      electron volts eV +
      kilo Watt hours kWh + +
      Misc +
      Amperes amp +
      Moles mol +
      Degrees Centrigrade degC +
      Degrees Fahrenheit degF +
      Degrees Kelvin degK +
      Angle degrees deg +
      Angle radians rad + +
      + + +
      + + + diff --git a/htdocs/helpFiles/instructor_links.html b/htdocs/helpFiles/instructor_links.html new file mode 100644 index 0000000000..7419c50ab7 --- /dev/null +++ b/htdocs/helpFiles/instructor_links.html @@ -0,0 +1,83 @@ + + + + + +Instructor Links Help Page + + + +

      Instructor Links Help Page

      + +Click the icon for +page and item specific help. + +
      +The main page for WeBWorK documentation is the webwork wiki +while specific help for instructors setting up a a course are +in the instructors' section. +
      + +
      +
      Instructor Tools
      +
      Quick access to many instructor tools, including + Reset passwords, + Act as student, + Assign individual sets + and Edit individual due dates. + Beginners may find + it easier to use the "Class List Editor", "Hmwk Sets Editor" + at first.
      +
      Class List Editor
      +
      Edit class roster data. Add students, edit student data, drop + students from class, import students from a classlist, and give + user professor privileges. Access to individual homework sets
      +
      Hmwk Sets Editor
      +
      Edit homework sets for entire class. Change homework set due dates, create new sets from a set + definition file, create new homework sets, make sets visible/invisible, + score homework sets. Assign homework sets to the class.
      +
      Library Browser
      +
      Choose problems from a library and add + them to a homework set.
      +
      Statistics
      +
      View statistics of students' performance on homework either by individual or by set.
      +
      Student Progress
      +
      View details of student perofrmance either by individual or by set.
      +
      Scoring Tools
      +
      Score one or more sets. This can also be done from the "Hmwk + Sets Editor" or from the "Instructor Tools", but the "Scoring Tools" page + allows control over parameters.
      +
      Email
      +
      Send email to students.
      +
      File Transfer
      +
      Upload, download and delete text files, including scoring + spread sheets, set definition files, class list spread + sheets, and "PG" problems.
      +
      Course configuration
      +
      Allows configuration of certain parameters, such as permission levels, + default display mode for equations, and email feedback behavior, on a course by course basis.
      +
      + +Click the icon for +page and item specific help. + + + diff --git a/conf/templates/test.template b/htdocs/helpFiles/no_help.html similarity index 63% rename from conf/templates/test.template rename to htdocs/helpFiles/no_help.html index 85091cb9ca..b5b973e900 100644 --- a/conf/templates/test.template +++ b/htdocs/helpFiles/no_help.html @@ -1,13 +1,11 @@ - - - - - WeBWorK Test Template - - -

      #path

      -

      #quicklinks

      -

      #siblings

      -

      #nav

      -

      #title

      -

      #body

      - + +No Help Page + + + +

      There is no specific help for this item. +Click on 'Help' in the left margin +for more general information. +

      + + + diff --git a/htdocs/images/ajax-loader-small.gif b/htdocs/images/ajax-loader-small.gif new file mode 100644 index 0000000000..d0bce15423 Binary files /dev/null and b/htdocs/images/ajax-loader-small.gif differ diff --git a/htdocs/images/ajax-loader.gif b/htdocs/images/ajax-loader.gif new file mode 100644 index 0000000000..cc70a7a8b3 Binary files /dev/null and b/htdocs/images/ajax-loader.gif differ diff --git a/htdocs/images/dgage/background.gif b/htdocs/images/dgage/background.gif new file mode 100644 index 0000000000..55dc56f4e7 Binary files /dev/null and b/htdocs/images/dgage/background.gif differ diff --git a/htdocs/images/dgage/slide-button-active.gif b/htdocs/images/dgage/slide-button-active.gif new file mode 100644 index 0000000000..1df6d4dda1 Binary files /dev/null and b/htdocs/images/dgage/slide-button-active.gif differ diff --git a/htdocs/images/dgage/slide-button.gif b/htdocs/images/dgage/slide-button.gif new file mode 100644 index 0000000000..65dd171f77 Binary files /dev/null and b/htdocs/images/dgage/slide-button.gif differ diff --git a/htdocs/images/edit.gif b/htdocs/images/edit.gif new file mode 100644 index 0000000000..1da1fec1da Binary files /dev/null and b/htdocs/images/edit.gif differ diff --git a/htdocs/images/favicon.ico b/htdocs/images/favicon.ico new file mode 100644 index 0000000000..10893e0d6b Binary files /dev/null and b/htdocs/images/favicon.ico differ diff --git a/htdocs/images/images_es/navNext.gif b/htdocs/images/images_es/navNext.gif new file mode 100644 index 0000000000..280dedb92b Binary files /dev/null and b/htdocs/images/images_es/navNext.gif differ diff --git a/htdocs/images/images_es/navNextGrey.gif b/htdocs/images/images_es/navNextGrey.gif new file mode 100644 index 0000000000..622b71b44a Binary files /dev/null and b/htdocs/images/images_es/navNextGrey.gif differ diff --git a/htdocs/images/images_es/navPrev.gif b/htdocs/images/images_es/navPrev.gif new file mode 100644 index 0000000000..37651a7e9e Binary files /dev/null and b/htdocs/images/images_es/navPrev.gif differ diff --git a/htdocs/images/images_es/navPrevGrey.gif b/htdocs/images/images_es/navPrevGrey.gif new file mode 100644 index 0000000000..97ce546cb9 Binary files /dev/null and b/htdocs/images/images_es/navPrevGrey.gif differ diff --git a/htdocs/images/images_es/navProbList.gif b/htdocs/images/images_es/navProbList.gif new file mode 100644 index 0000000000..e75ff61840 Binary files /dev/null and b/htdocs/images/images_es/navProbList.gif differ diff --git a/htdocs/images/images_es/navProbListGrey.gif b/htdocs/images/images_es/navProbListGrey.gif new file mode 100644 index 0000000000..e57a12d703 Binary files /dev/null and b/htdocs/images/images_es/navProbListGrey.gif differ diff --git a/htdocs/images/images_es/navUp.gif b/htdocs/images/images_es/navUp.gif new file mode 100644 index 0000000000..c9c7db184a Binary files /dev/null and b/htdocs/images/images_es/navUp.gif differ diff --git a/htdocs/images/images_fr/navNext.gif b/htdocs/images/images_fr/navNext.gif new file mode 100644 index 0000000000..280dedb92b Binary files /dev/null and b/htdocs/images/images_fr/navNext.gif differ diff --git a/htdocs/images/images_fr/navNextGrey.gif b/htdocs/images/images_fr/navNextGrey.gif new file mode 100644 index 0000000000..622b71b44a Binary files /dev/null and b/htdocs/images/images_fr/navNextGrey.gif differ diff --git a/htdocs/images/images_fr/navPrev.gif b/htdocs/images/images_fr/navPrev.gif new file mode 100644 index 0000000000..37651a7e9e Binary files /dev/null and b/htdocs/images/images_fr/navPrev.gif differ diff --git a/htdocs/images/images_fr/navPrevGrey.gif b/htdocs/images/images_fr/navPrevGrey.gif new file mode 100644 index 0000000000..97ce546cb9 Binary files /dev/null and b/htdocs/images/images_fr/navPrevGrey.gif differ diff --git a/htdocs/images/images_fr/navProbList.gif b/htdocs/images/images_fr/navProbList.gif new file mode 100644 index 0000000000..e75ff61840 Binary files /dev/null and b/htdocs/images/images_fr/navProbList.gif differ diff --git a/htdocs/images/images_fr/navProbListGrey.gif b/htdocs/images/images_fr/navProbListGrey.gif new file mode 100644 index 0000000000..e57a12d703 Binary files /dev/null and b/htdocs/images/images_fr/navProbListGrey.gif differ diff --git a/htdocs/images/images_fr/navUp.gif b/htdocs/images/images_fr/navUp.gif new file mode 100644 index 0000000000..c9c7db184a Binary files /dev/null and b/htdocs/images/images_fr/navUp.gif differ diff --git a/htdocs/images/images_tr/navNext.gif b/htdocs/images/images_tr/navNext.gif new file mode 100644 index 0000000000..280dedb92b Binary files /dev/null and b/htdocs/images/images_tr/navNext.gif differ diff --git a/htdocs/images/images_tr/navNextGrey.gif b/htdocs/images/images_tr/navNextGrey.gif new file mode 100644 index 0000000000..622b71b44a Binary files /dev/null and b/htdocs/images/images_tr/navNextGrey.gif differ diff --git a/htdocs/images/images_tr/navPrev.gif b/htdocs/images/images_tr/navPrev.gif new file mode 100644 index 0000000000..37651a7e9e Binary files /dev/null and b/htdocs/images/images_tr/navPrev.gif differ diff --git a/htdocs/images/images_tr/navPrevGrey.gif b/htdocs/images/images_tr/navPrevGrey.gif new file mode 100644 index 0000000000..97ce546cb9 Binary files /dev/null and b/htdocs/images/images_tr/navPrevGrey.gif differ diff --git a/htdocs/images/images_tr/navProbList.gif b/htdocs/images/images_tr/navProbList.gif new file mode 100644 index 0000000000..e75ff61840 Binary files /dev/null and b/htdocs/images/images_tr/navProbList.gif differ diff --git a/htdocs/images/images_tr/navProbListGrey.gif b/htdocs/images/images_tr/navProbListGrey.gif new file mode 100644 index 0000000000..e57a12d703 Binary files /dev/null and b/htdocs/images/images_tr/navProbListGrey.gif differ diff --git a/htdocs/images/images_tr/navUp.gif b/htdocs/images/images_tr/navUp.gif new file mode 100644 index 0000000000..c9c7db184a Binary files /dev/null and b/htdocs/images/images_tr/navUp.gif differ diff --git a/htdocs/images/maa_logo.png b/htdocs/images/maa_logo.png new file mode 100644 index 0000000000..6f1d84c9aa Binary files /dev/null and b/htdocs/images/maa_logo.png differ diff --git a/htdocs/images/navNextGrey.gif b/htdocs/images/navNextGrey.gif new file mode 100644 index 0000000000..c6d131b134 Binary files /dev/null and b/htdocs/images/navNextGrey.gif differ diff --git a/htdocs/images/navPrevGrey.gif b/htdocs/images/navPrevGrey.gif new file mode 100644 index 0000000000..e5411c974d Binary files /dev/null and b/htdocs/images/navPrevGrey.gif differ diff --git a/htdocs/images/navProbListGrey.gif b/htdocs/images/navProbListGrey.gif new file mode 100644 index 0000000000..f46c033ef5 Binary files /dev/null and b/htdocs/images/navProbListGrey.gif differ diff --git a/htdocs/images/question_mark.png b/htdocs/images/question_mark.png new file mode 100644 index 0000000000..ef8e328569 Binary files /dev/null and b/htdocs/images/question_mark.png differ diff --git a/htdocs/images/spiderweb.png b/htdocs/images/spiderweb.png new file mode 100644 index 0000000000..72cf36e145 Binary files /dev/null and b/htdocs/images/spiderweb.png differ diff --git a/htdocs/images/webwork_rectangle.png b/htdocs/images/webwork_rectangle.png new file mode 100644 index 0000000000..9a0e305039 Binary files /dev/null and b/htdocs/images/webwork_rectangle.png differ diff --git a/htdocs/index.html b/htdocs/index.html index 5f336ada94..74d30d3829 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -2,13 +2,15 @@ "http://www.w3.org/TR/html4/loose.dtd"> - WeBWorK2 site - +WeBWorK Placeholder Page -

      -Welcome to WeBWorK at the University of Rochester -

      + +

      WeBWorK Placeholder Page

      + +

      Exploring?

      + +

      This page sits at the top level of the WeBWorK 2 system htdocs directory. You should never see it, unless you're verifying that you installed WeBWorK correctly.

      diff --git a/htdocs/js/Base64.js b/htdocs/js/Base64.js new file mode 100644 index 0000000000..7d9536a4f0 --- /dev/null +++ b/htdocs/js/Base64.js @@ -0,0 +1,143 @@ + +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = Base64._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = Base64._utf8_decode(output); + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} diff --git a/htdocs/js/addOnLoadEvent.js b/htdocs/js/addOnLoadEvent.js new file mode 100644 index 0000000000..e168626cf7 --- /dev/null +++ b/htdocs/js/addOnLoadEvent.js @@ -0,0 +1,19 @@ +/*Here is declared the addOnLoadEvent function, which is used to sequentially add onload event handlers to the page*/ + +/*This method of adding to the onload event listener is taken from tabber.js, which in turn takes from http://simon.incutio.com/archive/2004/05/26/addLoadEvent*/ + +function addOnLoadEvent(f) { + var prevOnload = window.onload; + + if(typeof window.onload != 'function'){ + window.onload = function() { + f(); + } + } + else{ + window.onload = function() { + prevOnload(); + f(); + } + } +} \ No newline at end of file diff --git a/htdocs/js/color.js b/htdocs/js/color.js new file mode 100644 index 0000000000..5c8e5e1d63 --- /dev/null +++ b/htdocs/js/color.js @@ -0,0 +1,22 @@ +/* color.js file, for coloring the input elements with the proper color based on whether they are correct or incorrect + By ghe3*/ + +function color_in() { + var correct_elem = document.getElementsByName('correct_ids'); + var incorrect_elem = document.getElementsByName('incorrect_ids'); + var length_c = correct_elem.length; + var length_i = incorrect_elem.length; + + for (var i = 0; i 1){ + document.getElementById("set_maker_two_box").style.top = -findPos(document.getElementById("set_maker_two_box"))[1]; + } + if (Math.abs(findPos(document.getElementById("set_maker_two_box"))[0]) > 1){ + document.getElementById("set_maker_two_box").style.left = -findPos(document.getElementById("set_maker_two_box"))[0]; + } + } + gridify(false); + hasBeenGridded = false; + gridify(false); + fullscreen = !fullscreen; +} + +function setup(){ + //console.log("app code name:" + navigator.appCodeName); + //console.log("app name: " + navigator.appName); + if(navigator.appCodeName != "Mozilla"){ + alert("Your browser may not be supported"); + } + //set up all draggable items + var dragItems = document.querySelectorAll('[draggable=true]'); + for (var i = 0; i < dragItems.length; i++) { + dragItems[i].addEventListener('dragstart', function (event) { + dragging = true; + event.dataTransfer.effectAllowed = 'all'; + // store the ID of the element, and collect it on the drop later on + event.dataTransfer.setData('Text', this.id); + event.dataTransfer.setData('Move', event.shiftKey); + },false); + } + + var table = document.getElementById('mysets_problems'); + var targetProblems = table.childNodes; + for(var i = 0; i < targetProblems.length; i++){ + targetProblems[i].addEventListener('drop', reorderDrop, false); + targetProblems[i].addEventListener('dragenter', dragEnter, false); + targetProblems[i].addEventListener('dragleave', reorderDragLeave, false); + targetProblems[i].addEventListener('dragover', reorderDragOver, false); + } + + //dynamically size the lists + var libraryBox = document.querySelectorAll('div.setmaker_library'); + var mysetsBox = document.querySelectorAll('div.mysets'); + //fix sizes + var totalWidth = document.getElementById('problem_container').clientWidth; + myStartWidth = (100*(1/4))-0.8; + libStartWidth = (100*(3/4))-0.8; + for(var i = 0; i < libraryBox.length; i++) + libraryBox[i].style.width = ""+libStartWidth+"%"; + for(var i = 0; i < mysetsBox.length; i++) + mysetsBox[i].style.width = ""+myStartWidth+"%"; + + var myset_sets = document.querySelectorAll('select#myset_sets option'); + for(var i = 0; i < myset_sets.length; i++){ + myset_sets[i].addEventListener('drop', singleAddDrop, false); + myset_sets[i].addEventListener('dragenter', singleAddEnter, false); + myset_sets[i].addEventListener('dragleave', singleAddLeave, false); + myset_sets[i].addEventListener('dragover', singleAddOver, false); + myset_sets[i].addEventListener('mouseover', singleMouseOver, false); + myset_sets[i].addEventListener('mouseout', singleMouseOut, false); + } + + myStartHeight = table.clientHeight; + document.body.addEventListener('mousemove', sliderMove , false); + document.getElementById("size_slider").addEventListener('mousedown', sliderDown , false); + document.body.addEventListener('mouseup', sliderUp , false); + var horizontalSlider = document.getElementById("horizontal_slider"); + horizontalSlider.style.width = totalWidth; + horizontalSliderStartTop = findPos(table)[1] - findPos(horizontalSlider)[1] - 16; + horizontalSlider.style.marginTop = horizontalSliderStartTop+"px"; + horizontalSlider.addEventListener('mousedown', horizontalSliderDown, false); + + //important drag and drop stuff + table.addEventListener('drop', drop, false); + table.addEventListener('dragenter', dragEnter, false); + table.addEventListener('dragleave', dragLeave, false); + table.addEventListener('dragover', dragOver, false); + + document.body.addEventListener('drop', removeDrop, false); + document.body.addEventListener('dragenter', dragEnter, false); + document.body.addEventListener('dragleave', dragLeave, false); + document.body.addEventListener('dragover', dragOver, false); + //set up problem counter + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + + var pastGridded = document.getElementById('pastGridded'); + //console.log(pastGridded.value); + if(pastGridded.value == "true"){ + gridify(false); + } + document.getElementById("loading").style.display = "none"; +} + +function redoSetup(problemSet){ + //set up all draggable items + //var dragItems = problemSet.childNodes; + //this may add multiple listeners but they're all doing the same thing so it should be ok + + var dragItems = problemSet.childNodes; + for (var i = 0; i < dragItems.length; i++) { + dragItems[i].addEventListener('dragstart', function (event) { + dragging = true; + event.dataTransfer.effectAllowed = 'all'; + // store the ID of the element, and collect it on the drop later on + event.dataTransfer.setData('Text', this.id); + event.dataTransfer.setData('Move', event.shiftKey); + },false); + } + if(problemSet.id == "mysets_problems"){ + for(var i = 0; i < dragItems.length; i++){ + dragItems[i].addEventListener('drop', reorderDrop, false); + dragItems[i].addEventListener('dragenter', dragEnter, false); + dragItems[i].addEventListener('dragleave', reorderDragLeave, false); + dragItems[i].addEventListener('dragover', reorderDragOver, false); + } + } + //set up problem counter + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + +} + +/****** view problems functions ******/ + +function selectAll(){ + //console.log("test"); + var libraryCheckboxes = document.querySelectorAll(".add_problem_checkbox"); + for(var i = 0; i < libraryCheckboxes.length; i++){ + libraryCheckboxes[i].checked = true; + } +} + +function selectNone(){ + var libraryCheckboxes = document.querySelectorAll(".add_problem_checkbox"); + for(var i = 0; i < libraryCheckboxes.length; i++){ + libraryCheckboxes[i].checked = false; + } +} + +/****** end view problems functions ******/ + +/****** Event listeners ******/ + +/****** automatic scrolling functions ******/ +function scrollMouseMove(event){ + //var mouseX = event.pageX; + var mousePos = getMouseXY(event); + if (mousePos[1] > (document.getElementById('mysets_problems' ).offsetTop+document.getElementById('mysets_problems').clientHeight) - 20){ + scrollIntervals.push(setInterval('scrollMysetsDown()', 50)); + //document.getElementById('mysets_problems').scrollTop = 300; + } else if (mousePos[1] < document.getElementById('mysets_problems' ).offsetTop + 20){ + scrollIntervals.push(setInterval('scrollMysetsUp()', 50)); + } else { + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + } +} + +function scrollMysetsDown(){ + //console.log("scrolling to: "+document.getElementById('mysets_problems').scrollTop); + document.getElementById('mysets_problems').scrollTop+=5; +} + +function scrollMysetsUp(){ + //console.log("scrolling to: "+document.getElementById('mysets_problems').scrollTop); + document.getElementById('mysets_problems').scrollTop-=5; +} + +/****** resize list functions ******/ +//change the precent width of the lists by how much the mouse has moved +function sliderMove(event){ + event.preventDefault(); + if(changingSize){ + changeInX += event.pageX-mouseX; + var totalWidth = document.getElementById('problem_container').clientWidth; + if((libStartWidth-((changeInX/totalWidth)*100))>2 && (myStartWidth+((changeInX/totalWidth)*100)) > 2 ){ + mouseX = event.pageX; + var libraryBox = document.getElementById('setmaker_library_box'); + var mysetsBox = document.getElementById('mysets_problems_box'); + libraryBox.style.width = ""+(libStartWidth-((changeInX/totalWidth)*100))+ "%"; + mysetsBox.style.width = ""+(myStartWidth+((changeInX/totalWidth)*100))+ "%"; + } + } else if(changingHorizontalSize && 163+(changeInY+(event.pageY-mouseY)) > 0){ + changeInY += event.pageY-mouseY; + var controlPanels = document.querySelectorAll("div.setSelector"); + for(var i = 0; i < controlPanels.length; i++){ + controlPanels[i].style.height = (163 + changeInY) + "px"; + document.getElementById("mysets_problems").style.height = (myStartHeight - changeInY)+"px"; + document.getElementById("setmaker_library_problems").style.height = (myStartHeight - changeInY)+"px"; + } + document.getElementById('horizontal_slider').style.marginTop = (horizontalSliderStartTop + changeInY)+"px"; + document.getElementById('size_slider').style.marginTop = (horizontalSliderStartTop +changeInY)+"px"; + document.getElementById('size_slider').style.height = (myStartHeight - changeInY)+"px"; + mouseY = event.pageY; + } +} + +function horizontalSliderDown(event){ + event.preventDefault(); + changingHorizontalSize = true; + mouseY = event.pageY; +} + +//mouse down over slider bar between the lists +function sliderDown(event){ + event.preventDefault(); + changingSize = true; + mouseX = event.pageX; + mouseY = event.pageY; +} + +function sliderUp(event){ + if(changingSize && isGridded){ + gridify(false) + hasBeenGridded = false; + gridify(false); + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); + } + changingSize = false; + changingHorizontalSize = false; +} + +/****** reorder functions ******/ +//reorder problems +function reorderDrop(event){ + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + dragging = false; + if (event.preventDefault) event.preventDefault(); + if (event.stopPropagation) event.stopPropagation(); + var movedProblem = document.getElementById(event.dataTransfer.getData('Text')); + //console.log(event.dataTransfer.getData('Text')); + previewProblemEnd(event); + //get problem dropped on + var targetProblem = event.target; + while(targetProblem && !hasClassName(targetProblem, "problem")){ + targetProblem = targetProblem.parentNode; + } + //dont let it drop on itself; + if(targetProblem.id == movedProblem.id){ + if (event.stopPropagation) event.stopPropagation(); + return false; + } + if((hasClassName(movedProblem, 'myProblem') && !hasClassName(movedProblem, "removedProblem")) || hasClassName(movedProblem, "libProblem")){ + //console.log("were just doing a reorder"); + movedProblem.parentNode.removeChild(movedProblem); + targetProblem.parentNode.insertBefore(movedProblem, targetProblem); + } else { + //cancel remove problem + if(hasClassName(movedProblem, 'removedProblem')){ + //console.log("we're adding a problem back and reordering it"); + //have to disable a remove checkbox here as well + var deleteProbBox = document.getElementById("deleted" + movedProblem.id); + deleteProbBox.checked = false; + deleteProbBox.parentNode.style.display = "inline"; + removeClassName(movedProblem,"removedProblem"); + //uncomment for reorder + movedProblem.addEventListener('drop', reorderDrop, false); + movedProblem.addEventListener('dragenter', dragEnter, false); + movedProblem.addEventListener('dragleave', reorderDragLeave, false); + movedProblem.addEventListener('dragover', reorderDragOver, false); + + movedProblem.parentNode.removeChild(movedProblem); + targetProblem.parentNode.insertBefore(movedProblem, targetProblem); + } + //add library problem to target set + else{ + //console.log("we're adding a new problem"); + addClassName(movedProblem,"libProblem"); + //where the work needs to be done + var toBeMoved = event.dataTransfer.getData('Move') == "true"; + var moveProbBox = document.getElementById("moved" + movedProblem.id); + if(moveProbBox){ + moveProbBox.checked = toBeMoved; + moveProbBox.parentNode.style.display = "none"; + } + //console.log("trial"+movedProblem.id); + var addProbBox = document.getElementById("trial" + movedProblem.id); + //only add problem if not moving + addProbBox.checked = !toBeMoved || !moveProbBox; + //addProbBox.parentNode.style.display = "none"; + + var hideProbBox = document.getElementById("hideme" + movedProblem.id); + //hideProbBox.parentNode.style.display = "none"; + //do the work to copy problem over to target set + //remove if else to turn off copy + if(addProbBox.checked){ + var cloneEl = movedProblem.cloneNode(true); + cloneEl.id = movedProblem.id + "clone"; + cloneEl.removeChild(getChildById("trial" + movedProblem.id, cloneEl).parentNode.parentNode); + cloneEl.removeChild(getChildById("hideme" + movedProblem.id, cloneEl).parentNode.parentNode); + cloneEl.removeChild(getChildById("filetrial" + movedProblem.id, cloneEl)); + if(moveProbBox) + cloneEl.removeChild(getChildById("moved" + movedProblem.id, cloneEl).parentNode.parentNode); + targetProblem.parentNode.insertBefore(cloneEl, targetProblem); + movedProblem.draggable = false; + cloneEl.addEventListener('dragstart', function (event) { + event.dataTransfer.effectAllowed = 'all'; + // store the ID of the element, and collect it on the drop later on + event.dataTransfer.setData('Text', this.id); + },false); + //uncomment for reorder + cloneEl.addEventListener('drop', reorderDrop, false); + cloneEl.addEventListener('dragenter', dragEnter, false); + cloneEl.addEventListener('dragleave', reorderDragLeave, false); + cloneEl.addEventListener('dragover', reorderDragOver, false); + + if(isGridded){ + cloneEl.addEventListener('mouseover', onMouseOver, false); + cloneEl.addEventListener('mousemove', onMouseMove, false); + //libProblems[i].addEventListener('click', previewProblemEnd, false); + cloneEl.addEventListener('mouseout', onMouseOut, false); + } + } + else{ //move + //console.log("we're moving a problem?"); + //uncomment for reorder + movedProblem.addEventListener('drop', reorderDrop, false); + movedProblem.addEventListener('dragenter', dragEnter, false); + movedProblem.addEventListener('dragleave', reorderDragLeave, false); + movedProblem.addEventListener('dragover', reorderDragOver, false); + + movedProblem.parentNode.removeChild(movedProblem); + targetProblem.parentNode.insertBefore(movedProblem, targetProblem); + } + } + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + } + + document.getElementById('isReordered').value = 1; + //propegation issue so call fix grids here..should really do this better + if(isGridded){ + targetProblem.style.borderLeft = "none"; + movedProblem.style.borderLeft = "none"; + } + else { + targetProblem.style.borderTop = "none"; + movedProblem.style.borderTop = "none"; + } + //rename and reorder all problems in the target set + var reorderedProblems = document.getElementById('mysets_problems').childNodes; + for(var i = 0; i < reorderedProblems.length; i++){ + //starts counting at one + var currentCount = i+1; + if(document.getElementById('reorder'+currentCount)){ + document.getElementById('reorder'+currentCount).parentNode.removeChild(document.getElementById('reorder'+currentCount)); + } + var reorderedInput = document.createElement('input'); + reorderedInput.type = "hidden"; + reorderedInput.id = "reorder"+currentCount; + reorderedInput.name = "reorder"+currentCount; + var fileNameToGet = reorderedProblems[i].id.match("clone")?reorderedProblems[i].id.replace("clone", ""):reorderedProblems[i].id; + reorderedInput.value = document.getElementById("filetrial"+fileNameToGet).value; + reorderedInput.override = 1; + reorderedProblems[i].appendChild(reorderedInput); + } + + if(isGridded){ + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); + } + return true; +} + +function reorderDragOver(event) { + scrollMouseMove(event); + if (event.preventDefault) event.preventDefault(); // allows us to drop + event.dataTransfer.effectAllowed = 'all'; + //var table = e.target; + //table.style.background = "green"; + //doesn't work in webkit + //var movedProblem = document.getElementById(event.dataTransfer.getData('Text')); + //get problem dropped on + var targetProblem = event.target; + while(targetProblem && !hasClassName(targetProblem, "problem")){ + targetProblem = targetProblem.parentNode; + } + //don't allow move and reorder at the same time + //if(((movedProblem.hasClassName("myProblem") && !movedProblem.hasClassName("removedProblem")) || movedProblem.hasClassName("libProblem"))&&((targetProblem.hasClassName("myProblem") && !targetProblem.hasClassName("removedProblem")) || targetProblem.hasClassName("libProblem"))){ + //show where it will be dropped + if(isGridded){ + targetProblem.style.borderLeft = "3px solid blue"; + } + else { + targetProblem.style.borderTop = "3px solid blue"; + } + //} +} + + +function reorderDragLeave(event) { + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + if (event.preventDefault) event.preventDefault(); // allows us to drop + //var table = e.target; + //table.style.background = "red"; + //var movedProblem = document.getElementById(event.dataTransfer.getData('Text')); + //get problem dropped on + var targetProblem = event.target; + while(targetProblem && !hasClassName(targetProblem, "problem")){ + targetProblem = targetProblem.parentNode; + } + //don't allow move and reorder at the same time + //if(((movedProblem.hasClassName("myProblem") && !movedProblem.hasClassName("removedProblem")) || movedProblem.hasClassName("libProblem"))&&((targetProblem.hasClassName("myProblem") && !targetProblem.hasClassName("removedProblem")) || targetProblem.hasClassName("libProblem"))){ + //show where it will be dropped + if(isGridded){ + targetProblem.style.borderLeft = "none"; + } + else { + targetProblem.style.borderTop = "none"; + } + //} + +} + +/****** individual add functions ******/ +function singleMouseOver(event) { + //this is the kind of crazy work around that shouldn't be nessisary: + event.target.style.background = "rgba(176, 216, 230, 1)"; + event.target.parentNode.style.background = "white"; +} + +function singleMouseOut(event) { + event.target.style.background = "none"; + event.target.parentNode.style.background = "none"; +} + +function singleAddOver(event) { + if (event.preventDefault) event.preventDefault(); // allows us to drop + event.dataTransfer.effectAllowed = 'all'; + event.target.style.background = "rgba(176, 216, 230, 1)"; + event.target.parentNode.style.background = "white"; + +} + + // to get IE to work +function singleAddEnter(event) { + if (event.preventDefault) event.preventDefault(); // allows us to drop + event.dataTransfer.effectAllowed = 'all'; + event.target.style.background = "rgba(176, 216, 230, 1)"; + event.target.parentNode.style.background = "white"; + //var table = e.target; + //table.style.background = "white"; + +} + +function singleAddLeave(event) { + //if (event.preventDefault) event.preventDefault(); // allows us to drop + event.target.style.background = "none"; + event.target.parentNode.style.background = "none"; +} + +function singleAddDrop(event) { + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + dragging = false; + previewProblemEnd(event); + if (event.preventDefault) event.preventDefault(); + if (event.stopPropagation) event.stopPropagation(); + var el = document.getElementById(event.dataTransfer.getData('Text')); + var currentURL = window.location.href//window.location.protocol + "//" + window.location.host + window.location.pathname; + //var wantedElements = ["myset_sets", "local_sets", "reorder", "user", "filetrial", "isReordered", "mysetfiletrial", "trial", "deleted", "moved", "effectiveUser", "key", "new_set_name", "library_sets"]; + var form = document.createElement('form'); + var currentFormElements = document.getElementById('mainform').elements; + + form.appendChild(currentFormElements['user'].cloneNode(true)); + form.appendChild(currentFormElements['effectiveUser'].cloneNode(true)); + form.appendChild(currentFormElements['key'].cloneNode(true)); + + var myset_sets = document.createElement('input'); + myset_sets.name = "myset_sets"; + myset_sets.value = event.target.value; + form.appendChild(myset_sets); + + form.appendChild(currentFormElements['local_sets'].cloneNode(true)); + + //console.log(parseInt(el.id)); + for(var i = 1; i < parseInt(el.id); i++){ + var addedProblem = document.createElement('input'); + addedProblem.name = "filetrial" + i; + addedProblem.value = document.getElementById("filetrial" + i).value; + form.appendChild(addedProblem); + } + + var addedProblem = document.createElement('input'); + addedProblem.name = "filetrial" + el.id.replace('myset', ''); + addedProblem.value = document.getElementById("filetrial" + el.id).value; + form.appendChild(addedProblem); + + var addedProblemAddBox = document.createElement('input'); + addedProblemAddBox.name = "trial" + el.id.replace('myset', ''); + addedProblemAddBox.value = 1; + form.appendChild(addedProblemAddBox); + + singleAddSubmit(form, currentURL, 'save_changes_spinner'); + event.target.style.background = "none"; + event.target.parentNode.style.background = "none"; +} + +/****** normal drag functions ******/ +function dragOver(event) { + scrollMouseMove(event); + if (event.preventDefault) event.preventDefault(); // allows us to drop + event.dataTransfer.effectAllowed = 'all'; +} + + // to get IE to work +function dragEnter(event) { + if (event.preventDefault) event.preventDefault(); // allows us to drop + event.dataTransfer.effectAllowed = 'all'; + //var table = e.target; + //table.style.background = "white"; + +} + +function dragLeave(event) { + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + //if (event.preventDefault) event.preventDefault(); // allows us to drop +} + +function drop(event) { + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + dragging = false; + //has to be done in both places just in case + previewProblemEnd(event); + if (event.preventDefault) event.preventDefault(); + if (event.stopPropagation) event.stopPropagation(); + var table = document.getElementById('mysets_problems'); + var el = document.getElementById(event.dataTransfer.getData('Text')); + //If I drop on the empty table place at end + if((hasClassName(el,"myProblem") && !hasClassName(el,"removedProblem")) || hasClassName(el,"libProblem")){ + table.removeChild(el); + table.appendChild(el); + } + else { + //cancel remove problem + if(hasClassName(el,'removedProblem')){ + //have to disable a remove checkbox here as well + var deleteProbBox = document.getElementById("deleted" + el.id); + deleteProbBox.checked = false; + deleteProbBox.parentNode.style.display = "inline"; + removeClassName(el, "removedProblem"); + //uncomment for reorder + el.addEventListener('drop', reorderDrop, false); + el.addEventListener('dragenter', dragEnter, false); + el.addEventListener('dragleave', reorderDragLeave, false); + el.addEventListener('dragover', reorderDragOver, false); + + el.parentNode.removeChild(el); + table.appendChild(el); + } + //add library problem to target set + else{ + addClassName(el,"libProblem"); + //where the work needs to be done + var toBeMoved = event.dataTransfer.getData('Move') == "true"; + var moveProbBox = document.getElementById("moved" + el.id); + if(moveProbBox){ + moveProbBox.checked = toBeMoved; + moveProbBox.parentNode.style.display = "none"; + } + var addProbBox = document.getElementById("trial" + el.id); + //only add problem if not moving + addProbBox.checked = !toBeMoved || !moveProbBox; + //addProbBox.parentNode.style.display = "none"; + + var hideProbBox = document.getElementById("hideme" + el.id); + //hideProbBox.parentNode.style.display = "none"; + //do the work to copy problem over to target set + //remove if else to turn off copy + if(addProbBox.checked){ + var cloneEl = el.cloneNode(true); + cloneEl.id = el.id + "clone"; + cloneEl.removeChild(getChildById("trial" + el.id, cloneEl).parentNode.parentNode); + cloneEl.removeChild(getChildById("hideme" + el.id, cloneEl).parentNode.parentNode); + cloneEl.removeChild(getChildById("filetrial" + el.id, cloneEl)); + if(moveProbBox) + cloneEl.removeChild(getChildById("moved" + el.id, cloneEl).parentNode.parentNode); + table.appendChild(cloneEl); + el.draggable = false; + cloneEl.addEventListener('dragstart', function (event) { + event.dataTransfer.effectAllowed = 'all'; + // store the ID of the element, and collect it on the drop later on + event.dataTransfer.setData('Text', this.id); + },false); + //uncomment for reorder + el.addEventListener('drop', reorderDrop, false); + el.addEventListener('dragenter', dragEnter, false); + el.addEventListener('dragleave', reorderDragLeave, false); + el.addEventListener('dragover', reorderDragOver, false); + } + else{ //move + //uncomment for reorder + el.addEventListener('drop', reorderDrop, false); + el.addEventListener('dragenter', dragEnter, false); + el.addEventListener('dragleave', reorderDragLeave, false); + el.addEventListener('dragover', reorderDragOver, false); + + el.parentNode.removeChild(el); + table.appendChild(el); + } + } + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + } + return false; +} + +function removeDrop(e) { + if (e.preventDefault) e.preventDefault(); + //kind of a hack fix later + if(e.target.id == "div_cover"){ + if (e.stopPropagation) e.stopPropagation(); + return false; + } + //has to be done in both places just in case + previewProblemEnd(e); + if (e.stopPropagation) e.stopPropagation(); // stops the browser from redirecting + var el = document.getElementById(e.dataTransfer.getData('Text')); + //cancel remove + if(hasClassName(el,'myProblem')){ + //have to enable a remove checkbox here as well + var deleteProbBox = document.getElementById("deleted" + el.id); + deleteProbBox.checked = true; + deleteProbBox.parentNode.style.display = "none"; + addClassName(el,"removedProblem"); + el.parentNode.removeChild(el); + //uncomment for reorder + el.removeEventListener('drop', reorderDrop, false); + el.removeEventListener('dragenter', dragEnter, false); + el.removeEventListener('dragleave', reorderDragLeave, false); + el.removeEventListener('dragover', reorderDragOver, false); + document.getElementById('setmaker_library_problems').appendChild(el); + } + //cancel add + else if(hasClassName(el,'libProblem')){ + //comment for only one moved problem + var realEl = document.getElementById(el.id.replace("clone", "")); + realEl.draggable = true; + //remove libProblem tag + removeClassName(realEl,'libProblem'); + //only one moved problem + //el.removeClassName('libProblem'); + var addProbBox = document.getElementById("trial" + realEl.id); + addProbBox.checked = false; + addProbBox.parentNode.style.display = "inline"; + var moveProbBox = document.getElementById("moved" + realEl.id); + if(moveProbBox){ + moveProbBox.checked = false; + moveProbBox.parentNode.style.display = "inline"; + } + var hideProbBox = document.getElementById("hideme" + realEl.id); + hideProbBox.parentNode.style.display = "inline"; + el.parentNode.removeChild(el); + + //uncomment for reorder + el.removeEventListener('drop', reorderDrop, false); + el.removeEventListener('dragenter', dragEnter, false); + el.removeEventListener('dragleave', reorderDragLeave, false); + el.removeEventListener('dragover', reorderDragOver, false); + + //only one moved problem: + //document.getElementById('setmaker_library_problems').appendChild(el); + } + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + return false; +} + +/****** end event listeners ******/ + +/****** help function ******/ +function toggleHelp(sender){ + if(sender.value == "false"){ + document.getElementById('help').style.display = "block"; + sender.value = "true"; + } + else{ + document.getElementById('help').style.display = "none"; + sender.value = "false"; + } +} diff --git a/htdocs/js/form_builder.js b/htdocs/js/form_builder.js new file mode 100644 index 0000000000..9802930a5c --- /dev/null +++ b/htdocs/js/form_builder.js @@ -0,0 +1,396 @@ + +/****** submit types ******/ +function viewSet(formID, uploadAddress, spinnerName){ + var weAreUpdating = "view_set"; + var activeSpinner = spinnerName; + //console.log(uploadAddress); + document.getElementById(activeSpinner).style.display = "inline"; + var wantedElements = ["myset_sets", "local_sets", "mydisplayMode", "user", "key", "effectiveUser", "showHints", "showSolutions"]; + upload(document.getElementById(formID), uploadAddress, wantedElements, null, weAreUpdating, activeSpinner); +} + +function viewProblems(inputName ,formID, uploadAddress, spinnerName){ + var weAreUpdating = "view_problems"; + var activeSpinner = spinnerName; + document.getElementById(activeSpinner).style.display = "inline"; + upload(document.getElementById(formID), uploadAddress, null, inputName, weAreUpdating, activeSpinner); +} + +function saveChanges(formID, uploadAddress, spinnerName){ + var weAreUpdating = ""; + var activeSpinner = spinnerName; + document.getElementById(activeSpinner).style.display = "inline"; + var wantedElements = ["myset_sets", "local_sets", "reorder", "user", "filetrial", "isReordered", "mysetfiletrial", "trial", "deleted", "moved", "effectiveUser", "key", "new_set_name", "library_sets"]; + upload(document.getElementById(formID), uploadAddress, wantedElements, null, weAreUpdating, activeSpinner); +} + +function updateLibCategories(formID, uploadAddress, inputName, spinnerName){ + var weAreUpdating = "lib_categories"; + document.getElementById(spinnerName).style.display = "inline"; + var wantedElements =["effectiveUser", "key", "user", "library_is_basic", "library_subjects", "library_chapters", "library_sections", "library_textbook", "library_textchapter", "library_textsection", "library_keywords"]; + upload(document.getElementById(formID), uploadAddress, wantedElements, inputName, weAreUpdating, spinnerName); +} + +function changeLibrary(formID, uploadAddress, sender, spinnerName){ + //console.log(sender.id); + console.log(sender.value); + var weAreUpdating = "libs"; + document.getElementById(spinnerName).style.display = "inline"; + var wantedElements = ["effectiveUser", "key", "user"]; + upload(document.getElementById(formID), uploadAddress, wantedElements, sender.value, weAreUpdating, spinnerName); +} + +function singleAddSubmit(form, uploadAddress, spinnerName){ + var wantedElements = ["myset_sets", "local_sets", "reorder", "user", "filetrial", "isReordered", "mysetfiletrial", "trial", "deleted", "moved", "effectiveUser", "key", "new_set_name", "library_sets"]; + document.getElementById(spinnerName).style.display = "inline"; + upload(form, uploadAddress, wantedElements, null, "", spinnerName); +} + +function rerandomize(){ + +} + +function cleardisplay(){ + +} + +/****** end submit types *****/ + +function upload(form, uploadAddress, wantedElements, inputName, weAreUpdating, activeSpinner) { + //console.log("uploaded to " + uploadAddress+ " started for form: " + formID); + //var form = document.getElementById(formID); + var formElements = form.elements; + //create a progress bar: + + //style is in main.css can be moved here if nessiasry + var xhr = new XMLHttpRequest(); + xhr.addEventListener("progress", updateProgress, false); + xhr.addEventListener("load", function (event) {transferComplete(event, weAreUpdating, activeSpinner);}, false); + xhr.addEventListener("error", transferFailed, false); + xhr.addEventListener("abort", transferCanceled, false); + + xhr.open("POST", uploadAddress, true); + //webkit + if(!(typeof FormData === "undefined")){ + //var xhr = new XMLHttpRequest(); + //console.log("FormData exists"); + //make this more flexable + //check if this works in ff4 + var formData = new FormData(); //new FormData(form);//form.getFormData(); // these work in ff and chrome but safari doesn't play nice yet + //so we build the formdata by hand + for(var i = 0; i < formElements.length; i++){ + //ignore the submits or we'll confuse perl...poor perl + if(formElements[i].type != "submit" && formElements[i].name && (formElements[i].type != "checkbox" || formElements[i].checked)){ + if(stringIsInArray(formElements[i].name, wantedElements)){ + formData.append(formElements[i].name, formElements[i].value); + } + } + } + switch (weAreUpdating) + { + case "view_set": + break; + + case "view_problems": + formData.append(inputName, 1); + break; + + case "libs": + formData.append(inputName, 1); + break; + + case "lib_categories": + formData.append(inputName, 1); + break; + + default: + formData.append("update", "Update Set"); + break; + } + xhr.send(formData); + } + //mozilla untill ff4 + else{ + //console.log("FormData doesn't exist"); + //build top of form + var boundary = '------WebKitFormBoundary' + (new Date).getTime(); + var dashdash = '--'; + var crlf = '\r\n'; + + /* Build RFC2388 string. */ + var builder = ''; + + /* Generate headers. */ + for(var i = 0; i < formElements.length; i++){ + //ignore the submits or we'll confuse perl...poor perl + if(formElements[i].type != "submit" && formElements[i].name && (formElements[i].type != "checkbox" || formElements[i].checked) && stringIsInArray(formElements[i].name, wantedElements)){ + if(formElements[i].name.indexOf("all_past_list") == -1) + //console.log(formElements[i].name); + //build each input + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name='+formElements[i].name; + builder += crlf; + builder += crlf; + builder += formElements[i].value; + builder += crlf; + } + } + switch (weAreUpdating) + { + case "view_set": + break; + + case "view_problems": + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name='+inputName; + builder += crlf; + builder += crlf; + builder += 1; + builder += crlf; + break; + + case "libs": + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name='+inputName; + builder += crlf; + builder += crlf; + builder += 1; + builder += crlf; + break; + + default: + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name='+"update"; + builder += crlf; + builder += crlf; + builder += "Update Set"; + builder += crlf; + break; + } + //build bottom of form + builder += dashdash; + builder += boundary; + builder += dashdash; + builder += crlf; + //send form + //xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary); + xhr.send(builder); + } + //console.log("uploaded finished"); +} + +/****** event listeners ******/ +// progress on transfers from the server to the client (downloads) +function updateProgress(evt) { + if (evt.lengthComputable) { + var percentComplete = evt.loaded / evt.total; + //console.log("Percent complete: "+precentComplete); + } else { + // Unable to compute progress information since the total size is unknown + //console.log("Loading.. "+ evt.loaded + " out of " + evt.total); + } +} + +function transferComplete(evt, weAreUpdating, activeSpinner) { + //console.log("recieved response"); + //console.log(weAreUpdating); + var responseObject = HTMLParser(evt.target.responseText); + //console.log(responseObject); + switch (weAreUpdating) + { + case "view_set": + var newSet = getChildById("mysets_problems", responseObject); + //console.log(newSet); + if (newSet) { + document.getElementById("mysets_problems").innerHTML = newSet.innerHTML; + document.getElementById('problem_counter').innerHTML = document.getElementById('mysets_problems').childNodes.length; + weAreUpdating = ""; + redoSetup(document.getElementById("mysets_problems")); + newSet = null; + gridify(false) + hasBeenGridded = false; + gridify(false); + if(isGridded){ + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); + } + } else { + //console.log(responseObject); + alert("There is an error in the response, see javascript console for details"); + } + break; + + case "view_problems": + var problems = getChildById("setmaker_library_data", responseObject); + //console.log(problems); + if(problems){ + document.getElementById("setmaker_library_data").innerHTML = problems.innerHTML; + weAreUpdating = ""; + redoSetup(document.getElementById("setmaker_library_problems")); + problems = null; + + gridify(false) + hasBeenGridded = false; + gridify(false); + if(isGridded){ + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); + } + } else { + //console.log(responseObject); + alert("There is an error in the response, see javascript console for details"); + } + break; + + case "lib_categories": + //console.log("looking for categories"); + //console.log(getInnerElementId(responseObject, "control_panel")); + var categories = getChildById("library_categories", responseObject); + if(categories){ + document.getElementById("library_categories").innerHTML = categories.innerHTML; + weAreUpdating = ""; + //redoSetup(document.getElementById("setmaker_library_problems")); + categories = null; + } else { + //console.log(responseObject); + alert("There is an error in the response, see javascript console for details"); + } + break; + + case "libs": + //console.log("looking for categories"); + //console.log(getInnerElementId(responseObject, "control_panel")); + var categories = getChildById("library_categories", responseObject); + if(categories){ + document.getElementById("library_categories").innerHTML = categories.innerHTML; + weAreUpdating = ""; + //redoSetup(document.getElementById("setmaker_library_problems")); + categories = null; + } else { + //console.log(responseObject); + alert("There is an error in the response, see javascript console for details"); + } + break; + + default: + //console.log(responseObject); + break; + } + //update messages: + var newMessages = getChildByClass("Message", responseObject); + var currentMessages = document.querySelectorAll(".Message"); + for(var i = 0; i < currentMessages.length; i++){ + currentMessages[i].innerHTML = newMessages.innerHTML; + } + responseObject = null; + if(document.getElementById(activeSpinner)){ + document.getElementById(activeSpinner).style.display = "none"; + } +} + +function transferFailed(evt) { + alert("An error occurred while transferring the file."); +} + +function transferCanceled(evt) { + alert("The transfer has been canceled by the user."); +} +/****** end event listeners ******/ + +/****** Utility functions ******/ +//i worry about uniqueness of id's can the document see the resulting object here, i hope not +//html parser from https://developer.mozilla.org/en/Code_snippets/HTML_to_DOM#Safely_parsing_simple_HTML.c2.a0to_DOM +function HTMLParser(aHTMLString){ + //their version doesn't seem to work + /*var html = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", null), + body = document.createElementNS("http://www.w3.org/1999/xhtml", "body"); + html.documentElement.appendChild(body); + + body.appendChild(Components.classes["@mozilla.org/feed-unescapehtml;1"] + .getService(Components.interfaces.nsIScriptableUnescapeHTML) + .parseFragment(aHTMLString, false, null, body)); + */ + //might not be safe but it works + var body = document.createElement("div"); + body.innerHTML = aHTMLString; + //console.log(body); + return body; +} + +//creates a modal window containing object +function modal(object){ + var modalDiv = document.getElementById('modal-div'); + if(modalDiv){ + document.body.removeChild(modalDiv); + } + if(object){ + var modalDiv = document.createElement('div'); + //nessisary styles are included in main.css but can be moved to js if needed + modalDiv.id= 'modal-div'; + modalDiv.appendChild(object); + document.body.appendChild(modalDiv); + } +} + +function stringIsInArray(string, array){ + //if the array's empty return true (means we don't care) + if(!array){ + return true; + } + for(var i = 0; i < array.length; i++){ + //switch to regex someday + if(string.indexOf(array[i]) != -1){ + return true; + } + } + return false; +} + +//returns the first instance of a myClass +function getChildByClass(myClass, el){ + var children = el.childNodes; + var result = false; + for(var i = 0; i < children.length; i++){ + if(hasClassName(children[i], myClass)){ + //console.log("Found "+children[i].className); + result = children[i]; + break; + } else if(children[i].childNodes.length > 0){ + result = getChildByClass(myClass, children[i]); + if(result){ + break; + } + } + } + return result; +} + +/*function getInnerElementId(parent, id){ + var children = parent.childNodes; + var result = false; + for(var i = 0; i < children.length; i++){ + if(children[i].id == id){ + console.log("Found "+children[i].id); + result = children[i]; + break; + } else if(children[i].childNodes.length > 0){ + result = getInnerElementId(children[i], id); + if(result){ + break; + } + } + } + return result; +}*/ +/****** End utility functions ******/ diff --git a/htdocs/js/gateway.js b/htdocs/js/gateway.js new file mode 100644 index 0000000000..a74b85ab0f --- /dev/null +++ b/htdocs/js/gateway.js @@ -0,0 +1,126 @@ +/*********************************************************** + * + * Javascript for gateway tests. + * + * This file includes the routines allowing navigation + * within gateway tests, manages the timer, and posts + * alerts when test time is winding up. + * + * To install, include + * + * to the head othe the template file being used. + * + * The code here relies heavily on the existence of form elements + * created by GatewayQuiz.pm + * + ***********************************************************/ + +function jumpTo(ref) { // scrolling javascript function + if ( ref ) { + var pn = ref - 1; // we start anchors at 1, not zero + if ( navigator.appName == "Netscape" && + parseFloat(navigator.appVersion) < 5 ) { + var xpos = document.anchors[pn].x; + var ypos = document.anchors[pn].y; + } else { + var xpos = document.anchors[pn].offsetLeft; + var ypos = document.anchors[pn].offsetTop; + } + if ( window.scrollTo == null ) { // cover for anyone + window.scroll(xpos,ypos); // lacking js1.2 + } else { + window.scrollTo(xpos,ypos); + } + } + return false; // prevent link from being followed +} + +// timer for gateway +var theTimer; // variable for the timer +var browserTime; // on load, the time on the client's computer +var serverTime; // on load, the time on the server +var timeDelta; // the difference between those +var serverDueTime; // the time the test is due + +function runtimer() { +// function to start the timer, initializing the time variables + if ( document.getElementById('gwTimer') == null ) { // no timer + return; + } else { + theTimer = document.getElementById('gwTimer'); + var dateNow = new Date(); + browserTime = Math.round(dateNow.getTime()/1000); + serverTime = document.gwTimeData.serverTime.value; + serverDueTime = document.gwTimeData.serverDueTime.value; + timeDelta = browserTime - serverTime; + + var remainingTime = serverDueTime - browserTime + 1.*timeDelta; + + if ( remainingTime >= 0 ) { + theTimer.innerHTML = "Remaining time: " + toMinSec(remainingTime) + " (min:sec)"; + setTimeout("updateTimer();", 1000); + setTimeout("checkAlert();", 1000); + } else { + theTimer.innerHTML = "Remaining time: 0 min"; + } + } +} + +function updateTimer() { +// update the timer + var dateNow = new Date(); + browserTime = Math.round(dateNow.getTime()/1000); + var remainingTime = serverDueTime - browserTime + 1.*timeDelta; + if ( remainingTime >= 0 ) { + theTimer.innerHTML = "Remaining time: " + toMinSec(remainingTime) + " (min:sec)"; + setTimeout("updateTimer();", 1000); + } +} + +function checkAlert() { +// check to see if we should put up a low time alert + var dateNow = new Date(); + browserTime = Math.round(dateNow.getTime()/1000); + var timeRemaining = serverDueTime - browserTime + 1.*timeDelta; + + if ( timeRemaining <= 0 ) { + alert("* You are out of time! *\n" + + "* Press grade now! *"); + } else if ( timeRemaining <= 45 && timeRemaining > 40 ) { + alert("* You have less than 45 seconds left! *\n" + + "* Press Grade very soon! *"); + } else if ( timeRemaining <= 90 && timeRemaining > 85 ) { + alert("* You only have less than 90 sec left to complete *\n" + + "* this assignment. You should finish it very soon! *"); + } + if ( timeRemaining > 0 ) { + setTimeout("checkAlert();", 5000); + } +} + +function toMinSec(t) { +// convert to min:sec + if ( t < 0 ) { // don't deal with negative times + t = 0; + } + mn = Math.floor(t/60); + sc = t - 60*mn; + if ( mn < 10 && mn > -1 ) { + mn = "0" + mn; + } + if ( sc < 10 ) { + sc = "0" + sc; + } + return mn + ":" + sc; +} + +// for some reason putting this as an onload event in the body tag +// isn't working. so start the timer here; the 500ms delay is to be +// sure that the timer has loaded +setTimeout("runtimer()",500); +// this does not work either +// window.onload=runtimer(); + +/* end of gateway javascript routines + ***********************************************************/ diff --git a/htdocs/js/limit-reloads.js b/htdocs/js/limit-reloads.js new file mode 100644 index 0000000000..18a091a88d --- /dev/null +++ b/htdocs/js/limit-reloads.js @@ -0,0 +1,56 @@ +/******************************************************* + * + * This file implements a JavaScript-based hack to prevent + * an autorepeating F5 key from swamping your server with + * requests. it does this by ignoring F5 keydown events + * and reloading the page on an F5 keyup event. It will + * also put up a dialog box when it has spotted repeated + * F5 attempts to ask you to check if something is sitting + * on the F5 button. + * + * This only works in MSIE/PC and Firefox/PC. It does + * not work for Firefox/Mac or Opera (either platform). + * + * Autorepeating the reload button should be considered + * a browser bug, in my opinion, but I can't do much + * about that. + * + * To install it, add the line + * + * + * + * to the head of the system.template file that you are + * using (in a webwork/conf/templates subdirectory). + * + ********************************************************/ + +var wwLimitReloads = { + maxF5count: 5, // warn after this many blocked F5 attempts + F5count: 0, + warned: 0 +} + +document.onkeydown = function (event) { + if (!event) {event = window.event} + if (event.keyCode == 116) { + if (wwLimitReloads.F5count++ == wwLimitReloads.maxF5count && !wwLimitReloads.warned) { + wwLimitReloads.warned = 1; + setTimeout(function () { + alert("You seem to be generating many F5 presses.\n" + + "See if something is holding the F5 key down."); + },10); + } + try {event.keyCode = 8} catch (err) {}; // make MSIE event cancelable + if (event.preventDefault) event.preventDefault(); + if (event.stopPropagation) event.stopPropagation(); + event.cancelBubble = true; + event.returnValue = false; + return false; + } + return true; +} + +document.onkeyup = function (event) { + if (!event) {event = window.event} + if (event.keyCode == 116) {window.location.reload()} +} diff --git a/htdocs/js/problem_grid.js b/htdocs/js/problem_grid.js new file mode 100644 index 0000000000..61f8a5190a --- /dev/null +++ b/htdocs/js/problem_grid.js @@ -0,0 +1,432 @@ +var isGridded = false; +var mysetsHeight, mysetsWidth; +var libraryHeight, libraryWidth; +var previewWidth = 440; +var previewHeight = 120; +var mysetsAcross = 1; +var libAcross = 4; +var padding = 20; //the amount of padding on the lists +var property = false; //our browsers transform property +//stored values while problem is previewed +var previewProblem; +var previewProblemScale; +var tempTop, tempLeft; +//state variables +var viewing = false; +var hasBeenGridded = false; +var timeoutID; +var relativeNode; +var magnification = 1; + +function gridify(e){ + //set up + var mysetsList = document.getElementById("mysets_problems"); + var mysetsProblems = mysetsList.childNodes; + var libraryList = document.getElementById("setmaker_library_problems"); + var libProblems = libraryList.childNodes; + mysetsHeight = mysetsList.clientHeight; + mysetsWidth = mysetsList.clientWidth - padding; + libraryHeight = libraryList.clientHeight; + libraryWidth = libraryList.clientWidth - padding; + property = getTransformProperty(document.getElementById("editor-form")); + //no point if can't transform + if(property){ + if(!isGridded){ + document.getElementById("gridifyButton").innerHTML = "Ungridify?"; + document.getElementById('mysets_problems').addEventListener('drop', styleAddedGridProblem, false); + document.body.addEventListener('drop', styleRemovedGridProblem, false); + + for(var i = 0; i previewProblem.parentNode.clientHeight){ + previewProblem.style.top = previewProblem.parentNode.clientHeight-previewProblem.clientHeight*magnification-10; + } + if((previewProblem.offsetLeft-leftDiff)+(previewProblem.clientWidth*magnification) > previewProblem.parentNode.clientWidth && previewProblem.clientWidth*magnification < previewProblem.parentNode.clientWidth){ + previewProblem.style.left = previewProblem.parentNode.clientWidth-10-previewProblem.clientWidth*magnification; + }*/ + previewProblem.style[property] = "scale("+magnification+")"; + + //get it's position relative to the problem container + var relativePosition = findPos(previewProblem, "problemList"); + relativePosition[0] += leftDiff; + relativePosition[1] += topDiff; + relativePosition[1] -= previewProblem.parentNode.scrollTop; + relativePosition[0] -= previewProblem.parentNode.scrollLeft; + //set a relative node so it can be put back in the correct place + relativeNode = previewProblem.nextSibling != null ? previewProblem.nextSibling : previewProblem.parentNode; + //move the problem out of the box to deal with overflow + previewProblem.parentNode.removeChild(previewProblem); + document.getElementById('problem_container').appendChild(previewProblem); + //set new position + previewProblem.style.left = relativePosition[0]; + previewProblem.style.top = relativePosition[1]; + //set up a cover so that mouse out will work + var divCover; + if(!document.getElementById("div_cover")){ + divCover = document.createElement('div'); + divCover.id = "div_cover"; + divCover.className += "shadowed" + document.getElementById('problem_container').appendChild(divCover); + divCover.style.position = "absolute"; + divCover.style.zIndex = "2000"; + } else { + divCover = document.getElementById("div_cover"); + } + divCover.addEventListener('dragstart', dummyDrag,false); + divCover.addEventListener('mouseout', previewProblemEnd, false); + divCover.style.display = "block"; + divCover.style.height = previewProblem.clientHeight*magnification; + divCover.style.width = previewProblem.clientWidth*magnification; + divCover.style.top = previewProblem.offsetTop-topDiff; + divCover.style.left = previewProblem.offsetLeft-leftDiff; + //divCover.style[property] = "scale("+magnification+")"; + divCover.draggable = previewProblem.draggable; + viewing = !viewing; +} + +function dummyDrag(event){ + dragging = true; + event.dataTransfer.effectAllowed = 'all'; + event.dataTransfer.setDragImage(previewProblem, previewProblem.clientWidth*(1/4), previewProblem.clientHeight*(1/4)); + //console.log("drag start for: "+previewProblem.id); + // store the ID of the element, and collect it on the drop later on + event.dataTransfer.setData('Text', previewProblem.id); + event.dataTransfer.setData('Move', event.shiftKey); + //previewProblemEnd(event); +} + +function previewProblemEnd(event){ + if (event.preventDefault) event.preventDefault(); + if (event.stopPropagation) event.stopPropagation(); + clearTimeout(timeoutID); + if(viewing){ + //document.getElementById('problem_container').removeChild(document.getElementById("div_cover")); + document.getElementById("div_cover").style.display = "none"; + //document.getElementById("div_cover").style.zIndex = "-1000"; + document.getElementById("div_cover").removeEventListener('dragstart', dummyDrag,false); + document.getElementById("div_cover").removeEventListener('mouseout', previewProblemEnd, false); + document.getElementById('problem_container').removeChild(previewProblem); + if(hasClassName(relativeNode, 'problemList')){ + relativeNode.appendChild(previewProblem); + }else{ + relativeNode.parentNode.insertBefore(previewProblem, relativeNode); + } + if(previewProblem.parentNode.id == "setmaker_library_problems"){ + var scale = (libraryWidth/libAcross)/previewWidth; + }else{ + var scale = (mysetsWidth/mysetsAcross)/previewWidth; + } + previewProblem.style.position = "absolute"; + previewProblem.style.top = tempTop; + previewProblem.style.left = tempLeft; + previewProblem.style.zIndex = "0"; + previewProblem.style[property] = previewProblemScale; + removeClassName(previewProblem, "shadowed"); + viewing = !viewing; + //previewProblem = null; + } +} + +//utilities +function styleAddedGridProblem(e){ + previewProblemEnd(e); + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + dragging = false; + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); +} + +function styleRemovedGridProblem(e){ + previewProblemEnd(e); + for(var i = 0; i < scrollIntervals.length; i++){ + clearInterval(scrollIntervals[i]); + } + dragging = false; + hasBeenGridded = false; + fixGrid(); + fixMysetsGrid(); +} + +function getProblem(node){ + if(hasClassName(node, "problem")){ + return node; + } + else{ + return getProblem(node.parentNode); + } +} + +function getMouseXY(e) { + var IE = document.all?true:false; + var tempX, tempY; + if (IE) { // grab the x-y pos.s if browser is IE + tempX = event.clientX + document.body.scrollLeft; + tempY = event.clientY + document.body.scrollTop; + } + else { // grab the x-y pos.s if browser is NS + tempX = e.pageX; + tempY = e.pageY; + } + if (tempX < 0){tempX = 0;} + if (tempY < 0){tempY = 0;} + + return [tempX, tempY]; +} + +function findPos(obj, until) { + var curleft = curtop = 0; + if (obj.offsetParent) { + while (obj) { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + if(hasClassName(obj,until)) + break; + obj = obj.offsetParent + } + } + return [curleft,curtop]; +} + +function findPos(obj) { + var curleft = curtop = 0; + if (obj.offsetParent) { + while (obj) { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + obj = obj.offsetParent + } + } + return [curleft,curtop]; +} + +//from http://www.zachstronaut.com/posts/2009/02/17/animate-css-transforms-firefox-webkit.html +function getTransformProperty(element) { + var properties = ['transform', 'WebkitTransform', 'MozTransform']; + var p; + while (p = properties.shift()) { + if (typeof element.style[p] != 'undefined') { + return p; + } + } + return false; +} diff --git a/htdocs/js/sketchgraphhtml5b/SketchGraph.html b/htdocs/js/sketchgraphhtml5b/SketchGraph.html new file mode 100644 index 0000000000..43212a2c4c --- /dev/null +++ b/htdocs/js/sketchgraphhtml5b/SketchGraph.html @@ -0,0 +1,114 @@ + + +Trivial HTML5 + + + + + + + +
      +

      +Y-values: + + +

      + + + + +

      +
      + +

      + + + diff --git a/htdocs/js/sketchgraphhtml5b/SketchGraph.pjs b/htdocs/js/sketchgraphhtml5b/SketchGraph.pjs new file mode 100644 index 0000000000..ef71b6eae2 --- /dev/null +++ b/htdocs/js/sketchgraphhtml5b/SketchGraph.pjs @@ -0,0 +1,236 @@ +/* + Lets user sketch a graph by moving the mouse. + Origional Java App: + --David Eck, June 2000 (eck@hws.edu, http://math.hws.edu/eck/) + + Rewritten for WebWork in javascript with processingjs for html5 by David Gage 2010 + see processingjs.org for info on processingjs + + Drag on the graph to change it. + The Y-values box will take numbers separated by commas and space them equaly on the graph. + Enjoy!! + */ + + double xGrid; + double yGrid; + + //show a tick mark every ? spaces + int tickEvery = 2 + + //set the border color + color borderColor = color(0,0,0); + //set the color of the grid + color gridColor = color(150,150,150); + //set the color of the axes + color axesColor = color(0,0,220); + //set the alternat color of the axes + color lightAxesColor = color(160,180,255); + //set the color of the graph + color graphColor = color(255, 0, 255); + + //set the border width + int borderWidth = 20; + + var padding = 0; + //------------------- methods ------------------------------- + + void setup(){ + //set the canvas size + + size( canvasWidth+padding, canvasHeight+padding ); + + strokeWeight( 1 ); + + frameRate( 15 ); + + gwidth = width-2*padding; + gheight = height -2*padding; + gheight = height; + xGrid = gwidth/(xmax - xmin); + yGrid = gheight/(ymax - ymin); + + + //starting number of points + points = 101; + yValues = new int[points]; + derivatives = new double[points]; + for(int i = 0; i < points; i++){ + yValues[i] = (abs(ymin)*yGrid)/gheight; + derivatives[i] = 0; + } + + } + + //processings draw loop + + void draw() { + //background color clears the graph every draw + background(color(255,255,255)); + drawGrid(); + + drawAxes(); + drawGraph(); + } + + void drawGrid() { + if (!showGrid) + return; + stroke(gridColor); + if (xGrid > 0) { + int left = 0.5+padding; + int right = left+gwidth; + int a = left; + while (a < right) { + line(a,padding,a,gheight+padding); + a += xGrid; + } + } + //draw the right edge + line(right-1, padding, right-1, gheight); + if (yGrid > 0) { + int top = 0.5+padding; + int bottom = top +gheight; + int b = top; + while (b < bottom) { + line(padding,b,gwidth+padding,b); + b += yGrid; + } + } + //draw the bottom edge + line(padding, bottom-1, gwidth, bottom-1); + } + + //--------------------------- Axes --------------------------------------- + + void drawAxes() { + + if (ymax < 0 || ymin > 0) + stroke(lightAxesColor); + else + stroke(axesColor); + line(padding, yGrid*abs(ymin)+padding, gwidth+padding, yGrid*abs(ymin)+padding); + //draw the x ticks + for(int i = 0; i < (xmax - xmin); i += tickEvery) { + line(xGrid*abs(xmin)+padding+xGrid*i, yGrid*abs(ymin)-2+padding, xGrid*abs(xmin)+padding + xGrid*i, yGrid*abs(ymin)+2+padding); + line(xGrid*abs(xmin)+padding-xGrid*i, yGrid*abs(ymin)-2+padding, xGrid*abs(xmin)+padding - xGrid*i, yGrid*abs(ymin)+padding); + } + if (xmax < 0 || xmin > 0) + stroke(lightAxesColor); + else + stroke(axesColor); + line(xGrid*abs(xmin)+padding, padding, xGrid*abs(xmin)+padding, gheight+padding); + //draw the y ticks + for(int i = 0; i < (xmax - xmin); i += tickEvery) { + line(xGrid*abs(xmin)-2+padding, yGrid*abs(ymin)+padding+yGrid*i, xGrid*abs(xmin)+2+padding, yGrid*abs(ymin)+padding+yGrid*i); + line(xGrid*abs(xmin)-2+padding, yGrid*abs(ymin)+padding-yGrid*i, xGrid*abs(xmin)+2+padding, yGrid*abs(ymin)+padding-yGrid*i); + } + } + //--------------------------- Graph -------------------------------------- + + int[] pixelX; + int[] pixelY; + + void drawGraph() { + makeGraphCoords(); + makeDerivs(); + stroke(graphColor); + //plot the points + for (int i = 1; i < pixelX.length; i++) { + line(pixelX[i-1], pixelY[i-1], pixelX[i], pixelY[i]); + } + } + + void makeGraphCoords() { + int pointCt = (gwidth); + if (pixelX == null || pixelX.length != pointCt) { + pixelX = new int[pointCt]; + pixelY = new int[pointCt]; + for (int i = 0; i < pointCt; i++) + pixelX[i] = i+padding; + pixelX[pointCt-1] = gwidth+padding; + + } + double dx = (xmax - xmin) / (gwidth); + double dy = (ymax - ymin) / (gheight); + for (int i = 0; i < pointCt; i++) { + double x = xmin + dx*(pixelX[i]); + double y = eval(x); + pixelY[i] = (ymax - y)/dy+padding; + } + } + //math from java applet + double eval(double x) { + // NOTE: yvalues and derivatives are stored as if x and y range from 0 to 1!!! + double scaledx = (x-xmin)/(xmax-xmin); + int interval = (int)((points-1)*scaledx); + if (interval >= points-1) + return ymin + yValues[points-1]*(ymax-ymin); + double temp = 1.0/(points-1); + double a = yValues[interval+1]/(temp*temp*temp); + double b = derivatives[interval+1]/(temp*temp) - 3*a; + double d = -yValues[interval]/(temp*temp*temp); + double c = derivatives[interval]/(temp*temp) - 3*d; + double t1 = scaledx - interval*temp; + double t2 = t1*t1; + double t3 = t2*t1; + double s1 = scaledx - (interval+1)*temp; + double s2 = s1*s1; + double s3 = s2*s1; + double scaledy = a*t3 + b*t2*s1 + c*t1*s2 + d*s3; + + return ymin + scaledy*(ymax-ymin); + } + + //--------------------------- Mouse Handling ----------------------------- + + int lastPointNum; + double lastY; + + //couldn't just use mouseDragged because of a bug if dragged off canvas + + + + void mouseDragged(){ + jumpPoint(mouseX, mouseY); + } + + //math from java applet + void jumpPoint(int x, int y) { + y = min(gheight - 1, y); + int pointNum = (int)( ((x)/(gwidth-1))*(points-1) ); + double newYVal = 1.0 - ((y))/(gheight-1); // Scaled! + if (pointNum >= 0 && pointNum < points) + yValues[pointNum] = newYVal; + if (isNaN(lastY) || abs(pointNum - lastPointNum) <= 1 ||abs(pointNum - lastPointNum) >= 20) {} + else if (pointNum > lastPointNum) { + double dy = (newYVal - lastY)/(pointNum - lastPointNum); + for (int i = lastPointNum + 1; i < pointNum && i < points; i++) { + if (i >= 0) + yValues[i] = lastY + (i-lastPointNum)*dy; + } + } + else { + double dy = (newYVal - lastY)/(pointNum - lastPointNum); + for (int i = lastPointNum - 1; i > pointNum && i >= 0; i--) { + if (i < points) + yValues[i] = lastY + (i-lastPointNum)*dy; + } + } + lastPointNum = pointNum; + lastY = newYVal; + } + + //math from java applet + void makeDerivs() { + double dx = 1.0 / (points - 1); + derivatives[0] = (yValues[1] - yValues[0])/dx; + for (int i = 1; i < points - 1; i++) { + double left = abs(yValues[i] - yValues[i-1]); + double right = abs(yValues[i+1] - yValues[i]); + if (left < 1e-20 || right < 1e-20) + derivatives[i] = 0; + else + derivatives[i] = ((1/right)*(yValues[i+1]-yValues[i]) - (1/left)*(yValues[i]-yValues[i-1]))/(2*dx*((1/right)+(1/left))); + } + derivatives[points-1] = (yValues[points-1] - yValues[points-2])/dx; + } \ No newline at end of file diff --git a/htdocs/js/sketchgraphhtml5b/processing-dgfix.js b/htdocs/js/sketchgraphhtml5b/processing-dgfix.js new file mode 100644 index 0000000000..2f45309b3f --- /dev/null +++ b/htdocs/js/sketchgraphhtml5b/processing-dgfix.js @@ -0,0 +1,8231 @@ +/* + + P R O C E S S I N G . J S - 0.9.1 + a port of the Processing visualization language + + License : MIT + Developer : John Resig: http://ejohn.org + Web Site : http://processingjs.org + Java Version : http://processing.org + Github Repo. : http://github.com/jeresig/processing-js + Bug Tracking : http://processing-js.lighthouseapp.com + Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb + Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js + Hyper-Metrix: http://hyper-metrix.com/#Processing + BuildingSky: http://weare.buildingsky.net/pages/processing-js + + */ + +(function() { + + var Processing = this.Processing = function Processing(curElement, aCode) { + + var p = this; + + p.pjs = { + imageCache: { + pending: 0 + } + }; // by default we have an empty imageCache, no more. + + p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables + p.use3DContext = false; // default '2d' canvas context + p.canvas = curElement; + + // Glyph path storage for textFonts + p.glyphTable = {}; + + // Global vars for tracking mouse position + p.pmouseX = 0; + p.pmouseY = 0; + p.mouseX = 0; + p.mouseY = 0; + p.mouseButton = 0; + p.mouseDown = false; + p.mouseScroll = 0; + + // Undefined event handlers to be replaced by user when needed + p.mouseClicked = undefined; + p.mouseDragged = undefined; + p.mouseMoved = undefined; + p.mousePressed = undefined; + p.mouseReleased = undefined; + p.mouseScrolled = undefined; + p.key = undefined; + p.keyPressed = undefined; + p.keyReleased = undefined; + p.keyTyped = undefined; + p.draw = undefined; + p.setup = undefined; + + // The height/width of the canvas + p.width = curElement.width - 0; + p.height = curElement.height - 0; + + // The current animation frame + p.frameCount = 0; + + // Color modes + p.RGB = 1; + p.ARGB = 2; + p.HSB = 3; + p.ALPHA = 4; + p.CMYK = 5; + + // Renderers + p.P2D = 1; + p.JAVA2D = 1; + p.WEBGL = 2; + p.P3D = 2; + p.OPENGL = 2; + p.EPSILON = 0.0001; + p.MAX_FLOAT = 3.4028235e+38; + p.MIN_FLOAT = -3.4028235e+38; + p.MAX_INT = 2147483647; + p.MIN_INT = -2147483648; + p.PI = Math.PI; + p.TWO_PI = 2 * p.PI; + p.HALF_PI = p.PI / 2; + p.THIRD_PI = p.PI / 3; + p.QUARTER_PI = p.PI / 4; + p.DEG_TO_RAD = p.PI / 180; + p.RAD_TO_DEG = 180 / p.PI; + p.WHITESPACE = " \t\n\r\f\u00A0"; + + // Filter/convert types + p.BLUR = 11; + p.GRAY = 12; + p.INVERT = 13; + p.OPAQUE = 14; + p.POSTERIZE = 15; + p.THRESHOLD = 16; + p.ERODE = 17; + p.DILATE = 18; + + // Blend modes + p.REPLACE = 0; + p.BLEND = 1 << 0; + p.ADD = 1 << 1; + p.SUBTRACT = 1 << 2; + p.LIGHTEST = 1 << 3; + p.DARKEST = 1 << 4; + p.DIFFERENCE = 1 << 5; + p.EXCLUSION = 1 << 6; + p.MULTIPLY = 1 << 7; + p.SCREEN = 1 << 8; + p.OVERLAY = 1 << 9; + p.HARD_LIGHT = 1 << 10; + p.SOFT_LIGHT = 1 << 11; + p.DODGE = 1 << 12; + p.BURN = 1 << 13; + + // Color component bit masks + p.ALPHA_MASK = 0xff000000; + p.RED_MASK = 0x00ff0000; + p.GREEN_MASK = 0x0000ff00; + p.BLUE_MASK = 0x000000ff; + + // Projection matrices + p.CUSTOM = 0; + p.ORTHOGRAPHIC = 2; + p.PERSPECTIVE = 3; + + // Shapes + p.POINT = 2; + p.POINTS = 2; + p.LINE = 4; + p.LINES = 4; + p.TRIANGLE = 8; + p.TRIANGLES = 9; + p.TRIANGLE_STRIP = 10; + p.TRIANGLE_FAN = 11; + p.QUAD = 16; + p.QUADS = 16; + p.QUAD_STRIP = 17; + p.POLYGON = 20; + p.PATH = 21; + p.RECT = 30; + p.ELLIPSE = 31; + p.ARC = 32; + p.SPHERE = 40; + p.BOX = 41; + + // Shape closing modes + p.OPEN = 1; + p.CLOSE = 2; + + // Shape drawing modes + p.CORNER = 0; // Draw mode convention to use (x, y) to (width, height) + p.CORNERS = 1; // Draw mode convention to use (x1, y1) to (x2, y2) coordinates + p.RADIUS = 2; // Draw mode from the center, and using the radius + p.CENTER_RADIUS = 2; // Deprecated! Use RADIUS instead + p.CENTER = 3; // Draw from the center, using second pair of values as the diameter + p.DIAMETER = 3; // Synonym for the CENTER constant. Draw from the center + p.CENTER_DIAMETER = 3; // Deprecated! Use DIAMETER instead + + // Text vertical alignment modes + p.BASELINE = 0; // Default vertical alignment for text placement + p.TOP = 101; // Align text to the top + p.BOTTOM = 102; // Align text from the bottom, using the baseline + + // UV Texture coordinate modes + p.NORMAL = 1; + p.NORMALIZE = 1; + p.IMAGE = 2; + + // Text placement modes + p.MODEL = 4; + p.SHAPE = 5; + + // Stroke modes + p.SQUARE = 'butt'; + p.ROUND = 'round'; + p.PROJECT = 'square'; + p.MITER = 'miter'; + p.BEVEL = 'bevel'; + + // Lighting modes + p.AMBIENT = 0; + p.DIRECTIONAL = 1; + //POINT = 2; Shared with Shape constant + p.SPOT = 3; + + // Key constants + + // Both key and keyCode will be equal to these values + p.BACKSPACE = 8; + p.TAB = 9; + p.ENTER = 10; + p.RETURN = 13; + p.ESC = 27; + p.DELETE = 127; + p.CODED = 0xffff; + + // p.key will be CODED and p.keyCode will be this value + p.SHIFT = 16; + p.CONTROL = 17; + p.ALT = 18; + p.UP = 38; + p.RIGHT = 39; + p.DOWN = 40; + p.LEFT = 37; + + var codedKeys = [p.SHIFT, p.CONTROL, p.ALT, p.UP, p.RIGHT, p.DOWN, p.LEFT]; + + // Cursor types + p.ARROW = 'default'; + p.CROSS = 'crosshair'; + p.HAND = 'pointer'; + p.MOVE = 'move'; + p.TEXT = 'text'; + p.WAIT = 'wait'; + p.NOCURSOR = "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto"; + + // Hints + p.DISABLE_OPENGL_2X_SMOOTH = 1; + p.ENABLE_OPENGL_2X_SMOOTH = -1; + p.ENABLE_OPENGL_4X_SMOOTH = 2; + p.ENABLE_NATIVE_FONTS = 3; + p.DISABLE_DEPTH_TEST = 4; + p.ENABLE_DEPTH_TEST = -4; + p.ENABLE_DEPTH_SORT = 5; + p.DISABLE_DEPTH_SORT = -5; + p.DISABLE_OPENGL_ERROR_REPORT = 6; + p.ENABLE_OPENGL_ERROR_REPORT = -6; + p.ENABLE_ACCURATE_TEXTURES = 7; + p.DISABLE_ACCURATE_TEXTURES = -7; + p.HINT_COUNT = 10; + + // PJS defined constants + p.SINCOS_LENGTH = parseInt(360 / 0.5, 10); + p.FRAME_RATE = 0; + p.focused = true; + p.PRECISIONB = 15; // fixed point precision is limited to 15 bits!! + p.PRECISIONF = 1 << p.PRECISIONB; + p.PREC_MAXVAL = p.PRECISIONF - 1; + p.PREC_ALPHA_SHIFT = 24 - p.PRECISIONB; + p.PREC_RED_SHIFT = 16 - p.PRECISIONB; + p.NORMAL_MODE_AUTO = 0; + p.NORMAL_MODE_SHAPE = 1; + p.NORMAL_MODE_VERTEX = 2; + p.MAX_LIGHTS = 8; + + // "Private" variables used to maintain state + var curContext, + online = true, + doFill = true, + fillStyle = "rgba( 255, 255, 255, 1 )", + doStroke = true, + strokeStyle = "rgba( 204, 204, 204, 1 )", + lineWidth = 1, + loopStarted = false, + refreshBackground = function() {}, + doLoop = true, + looping = 0, + curRectMode = p.CORNER, + curEllipseMode = p.CENTER, + normalX = 0, + normalY = 0, + normalZ = 0, + normalMode = p.NORMAL_MODE_AUTO, + inDraw = false, + curBackground = "rgba( 204, 204, 204, 1 )", + curFrameRate = 60, + curCursor = p.ARROW, + oldCursor = curElement.style.cursor, + curMsPerFrame = 1, + curShape = p.POLYGON, + curShapeCount = 0, + curvePoints = [], + curTightness = 0, + curveDetail = 20, + curveInited = false, + colorModeA = 255, + colorModeX = 255, + colorModeY = 255, + colorModeZ = 255, + pathOpen = false, + mousePressed = false, + mouseDragging = false, + keyPressed = false, + curColorMode = p.RGB, + curTint = function() {}, + curTextSize = 12, + curTextFont = "Arial", + getLoaded = false, + start = new Date().getTime(), + timeSinceLastFPS = start, + framesSinceLastFPS = 0, + lastTextPos = [0, 0, 0], + curveBasisMatrix, + curveToBezierMatrix, + curveDrawMatrix, + bezierBasisInverse, + bezierBasisMatrix, + programObject3D, + programObject2D, + boxBuffer, + boxNormBuffer, + boxOutlineBuffer, + sphereBuffer, + lineBuffer, + fillBuffer, + pointBuffer; + + // User can only have MAX_LIGHTS lights + var lightCount = 0; + + //sphere stuff + var sphereDetailV = 0, + sphereDetailU = 0, + sphereX = [], + sphereY = [], + sphereZ = [], + sinLUT = new Array(p.SINCOS_LENGTH), + cosLUT = new Array(p.SINCOS_LENGTH), + sphereVerts, + sphereNorms; + + // Camera defaults and settings + var cam, + cameraInv, + forwardTransform, + reverseTransform, + modelView, + modelViewInv, + userMatrixStack, + inverseCopy, + projection, + manipulatingCamera = false, + frustumMode = false, + cameraFOV = 60 * (Math.PI / 180), + cameraX = curElement.width / 2, + cameraY = curElement.height / 2, + cameraZ = cameraY / Math.tan(cameraFOV / 2), + cameraNear = cameraZ / 10, + cameraFar = cameraZ * 10, + cameraAspect = curElement.width / curElement.height; + + var vertArray = [], + isCurve = false, + isBezier = false, + firstVert = true; + + // Stores states for pushStyle() and popStyle(). + var styleArray = new Array(0); + + // Vertices are specified in a counter-clockwise order + // triangles are in this order: back, front, right, bottom, left, top + var boxVerts = [0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, + -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, + -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]; + + var boxNorms = [0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]; + + var boxOutlineVerts = [0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]; + + // Vertex shader for points and lines + var vertexShaderSource2D = + "attribute vec3 Vertex;" + + "uniform vec4 color;" + + + "uniform mat4 model;" + + "uniform mat4 view;" + + "uniform mat4 projection;" + + + "void main(void) {" + + " gl_FrontColor = color;" + + " gl_Position = projection * view * model * vec4(Vertex, 1.0);" + + "}"; + + var fragmentShaderSource2D = + "void main(void){" + + " gl_FragColor = gl_Color;" + + "}"; + + // Vertex shader for boxes and spheres + var vertexShaderSource3D = + "attribute vec3 Vertex;" + + "attribute vec3 Normal;" + + + "uniform vec4 color;" + + + "uniform bool usingMat;" + + "uniform vec3 specular;" + + "uniform vec3 mat_emissive;" + + "uniform vec3 mat_ambient;" + + "uniform vec3 mat_specular;" + + "uniform float shininess;" + + + "uniform mat4 model;" + + "uniform mat4 view;" + + "uniform mat4 projection;" + + "uniform mat4 normalTransform;" + + + "uniform int lightCount;" + + "uniform vec3 falloff;" + + + "struct Light {" + + " bool dummy;" + + " int type;" + + " vec3 color;" + + " vec3 position;" + + " vec3 direction;" + + " float angle;" + + " vec3 halfVector;" + + " float concentration;" + + "};" + + "uniform Light lights[8];" + + + "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" + + // Get the vector from the light to the vertex + // Get the distance from the current vector to the light position + " float d = length( light.position - ecPos );" + + " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + " totalAmbient += light.color * attenuation;" + + "}" + + + "void DirectionalLight( inout vec3 col, in vec3 ecPos, inout vec3 spec, in vec3 vertNormal, in Light light ) {" + + " float powerfactor = 0.0;" + + " float nDotVP = max(0.0, dot( vertNormal, light.position ));" + + " float nDotVH = max(0.0, dot( vertNormal, normalize( light.position-ecPos )));" + + + " if( nDotVP != 0.0 ){" + + " powerfactor = pow( nDotVH, shininess );" + + " }" + + + " col += light.color * nDotVP;" + + " spec += specular * powerfactor;" + + "}" + + + "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" + + " float powerfactor;" + + + // Get the vector from the light to the vertex + " vec3 VP = light.position - ecPos;" + + + // Get the distance from the current vector to the light position + " float d = length( VP ); " + + + // Normalize the light ray so it can be used in the dot product operation. + " VP = normalize( VP );" + + + " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + + + " float nDotVP = max( 0.0, dot( vertNormal, VP ));" + + " vec3 halfVector = normalize( VP + eye );" + + " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" + + + " if( nDotVP == 0.0) {" + + " powerfactor = 0.0;" + + " }" + + " else{" + + " powerfactor = pow( nDotHV, shininess );" + + " }" + + + " spec += specular * powerfactor * attenuation;" + + " col += light.color * nDotVP * attenuation;" + + "}" + + + /* + */ + "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" + + " float spotAttenuation;" + + " float powerfactor;" + + + // calculate the vector from the current vertex to the light. + " vec3 VP = light.position - ecPos; " + + " vec3 ldir = normalize( light.direction );" + + + // get the distance from the spotlight and the vertex + " float d = length( VP );" + + " VP = normalize( VP );" + + + " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" + + + // dot product of the vector from vertex to light and light direction. + " float spotDot = dot( VP, ldir );" + + + // if the vertex falls inside the cone + " if( spotDot < cos( light.angle ) ) {" + + " spotAttenuation = pow( spotDot, light.concentration );" + + " }" + + " else{" + + " spotAttenuation = 1.0;" + + " }" + + " attenuation *= spotAttenuation;" + + + " float nDotVP = max( 0.0, dot( vertNormal, VP ));" + + " vec3 halfVector = normalize( VP + eye );" + + " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" + + + " if( nDotVP == 0.0 ) {" + + " powerfactor = 0.0;" + + " }" + + " else {" + + " powerfactor = pow( nDotHV, shininess );" + + " }" + + + " spec += specular * powerfactor * attenuation;" + + " col += light.color * nDotVP * attenuation;" + + "}" + + + "void main(void) {" + + " vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" + + " vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" + + " vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" + + + " vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" + + + " vec4 ecPos4 = view * model * vec4(Vertex,1.0);" + + " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" + + " vec3 eye = vec3( 0.0, 0.0, 1.0 );" + + + // If there were no lights this draw call, just use the + // assigned fill color of the shape and the specular value + " if( lightCount == 0 ) {" + + " gl_FrontColor = color + vec4(mat_specular,1.0);" + + " }" + + " else {" + + " for( int i = 0; i < lightCount; i++ ) {" + + " if( lights[i].type == 0 ) {" + + " AmbientLight( finalAmbient, ecPos, lights[i] );" + + " }" + + " else if( lights[i].type == 1 ) {" + + " DirectionalLight( finalDiffuse,ecPos, finalSpecular, norm, lights[i] );" + + " }" + + " else if( lights[i].type == 2 ) {" + + " PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" + + " }" + + " else if( lights[i].type == 3 ) {" + + " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" + + " }" + + " }" + + + " if( usingMat == false ) {" + + " gl_FrontColor = vec4( " + + " vec3(color) * finalAmbient +" + + " vec3(color) * finalDiffuse +" + + " vec3(color) * finalSpecular," + + " color[3] );" + + " }" + + " else{" + + " gl_FrontColor = vec4( " + + " mat_emissive + " + + " (vec3(color) * mat_ambient * finalAmbient) + " + + " (vec3(color) * finalDiffuse) + " + + " (mat_specular * finalSpecular), " + + " color[3] );" + + " }" + + " }" + + " gl_Position = projection * view * model * vec4( Vertex, 1.0 );" + + "}"; + + var fragmentShaderSource3D = + "void main(void){" + + " gl_FragColor = gl_Color;" + + "}"; + + // Wrapper to easily deal with array names changes. + var newWebGLArray = function(data) { + return new WebGLFloatArray(data); + }; + + var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) { + return { + x: x, + y: y, + w: w, + h: h + }; + }; + var imageModeConvert = imageModeCorner; + + var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) { + return { + x: x, + y: y, + w: whAreSizes ? w : w - x, + h: whAreSizes ? h : h - y + }; + }; + + var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) { + return { + x: x - w / 2, + y: y - h / 2, + w: w, + h: h + }; + }; + + var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) { + var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER); + curContext.shaderSource(vertexShaderObject, vetexShaderSource); + curContext.compileShader(vertexShaderObject); + if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) { + throw curContext.getShaderInfoLog(vertexShaderObject); + } + + var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER); + curContext.shaderSource(fragmentShaderObject, fragmentShaderSource); + curContext.compileShader(fragmentShaderObject); + if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) { + throw curContext.getShaderInfoLog(fragmentShaderObject); + } + + var programObject = curContext.createProgram(); + curContext.attachShader(programObject, vertexShaderObject); + curContext.attachShader(programObject, fragmentShaderObject); + curContext.linkProgram(programObject); + if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) { + throw "Error linking shaders."; + } + + return programObject; + }; + + //////////////////////////////////////////////////////////////////////////// + // Char handling + //////////////////////////////////////////////////////////////////////////// + var charMap = {}; + + var Char = function Char(chr) { + if (typeof chr === 'string' && chr.length === 1) { + this.code = chr.charCodeAt(0); + } else { + this.code = NaN; + } + + return (typeof charMap[this.code] === 'undefined') ? charMap[this.code] = this : charMap[this.code]; + }; + + Char.prototype.toString = function() { + return String.fromCharCode(this.code); + }; + + Char.prototype.valueOf = function() { + return this.code; + }; + + //////////////////////////////////////////////////////////////////////////// + // PVector + //////////////////////////////////////////////////////////////////////////// + var PVector = function(x, y, z) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + }, + createPVectorMethod = function(method) { + return function(v1, v2) { + var v = v1.get(); + v[method](v2); + return v; + }; + }, + createSimplePVectorMethod = function(method) { + return function(v1, v2) { + return v1[method](v2); + }; + }, + simplePVMethods = "dist dot cross".split(" "), + method = simplePVMethods.length; + + PVector.angleBetween = function(v1, v2) { + return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag())); + }; + + // Common vector operations for PVector + PVector.prototype = { + set: function(v, y, z) { + if (arguments.length === 1) { + this.set(v.x || v[0], v.y || v[1], v.z || v[2]); + } else { + this.x = v; + this.y = y; + this.z = z; + } + }, + get: function() { + return new PVector(this.x, this.y, this.z); + }, + mag: function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); + }, + add: function(v, y, z) { + if (arguments.length === 3) { + this.x += v; + this.y += y; + this.z += z; + } else if (arguments.length === 1) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + } + }, + sub: function(v, y, z) { + if (arguments.length === 3) { + this.x -= v; + this.y -= y; + this.z -= z; + } else if (arguments.length === 1) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } + }, + mult: function(v) { + if (typeof v === 'number') { + this.x *= v; + this.y *= v; + this.z *= v; + } else if (typeof v === 'object') { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + } + }, + div: function(v) { + if (typeof v === 'number') { + this.x /= v; + this.y /= v; + this.z /= v; + } else if (typeof v === 'object') { + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + } + }, + dist: function(v) { + var dx = this.x - v.x, + dy = this.y - v.y, + dz = this.z - v.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + }, + dot: function(v, y, z) { + var num; + if (arguments.length === 3) { + num = this.x * v + this.y * y + this.z * z; + } else if (arguments.length === 1) { + num = this.x * v.x + this.y * v.y + this.z * v.z; + } + return num; + }, + cross: function(v) { + var + crossX = this.y * v.z - v.y * this.z, + crossY = this.z * v.x - v.z * this.x, + crossZ = this.x * v.y - v.x * this.y; + return new PVector(crossX, crossY, crossZ); + }, + normalize: function() { + var m = this.mag(); + if (m > 0) { + this.div(m); + } + }, + limit: function(high) { + if (this.mag() > high) { + this.normalize(); + this.mult(high); + } + }, + heading2D: function() { + var angle = Math.atan2(-this.y, this.x); + return -angle; + }, + toString: function() { + return "[" + this.x + ", " + this.y + ", " + this.z + "]"; + }, + array: function() { + return [this.x, this.y, this.z]; + } + }; + + while (method--) { + PVector[simplePVMethods[method]] = createSimplePVectorMethod(simplePVMethods[method]); + } + + for (method in PVector.prototype) { + if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) { + PVector[method] = createPVectorMethod(method); + } + } + + p.PVector = PVector; + + //////////////////////////////////////////////////////////////////////////// + // 2D Matrix + //////////////////////////////////////////////////////////////////////////// + + /* + Helper function for printMatrix(). Finds the largest scalar + in the matrix, then number of digits left of the decimal. + Call from PMatrix2D and PMatrix3D's print() function. + */ + var printMatrixHelper = function printMatrixHelper(elements) { + var big = 0; + for (var i = 0; i < elements.length; i++) { + + if (i !== 0) { + big = Math.max(big, Math.abs(elements[i])); + } else { + big = Math.abs(elements[i]); + } + } + + var digits = (big + "").indexOf("."); + if (digits === 0) { + digits = 1; + } else if (digits === -1) { + digits = (big + "").length; + } + + return digits; + }; + + var PMatrix2D = function() { + if (arguments.length === 0) { + this.reset(); + } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { + this.set(arguments[0].array()); + } else if (arguments.length === 6) { + this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); + } + }; + + PMatrix2D.prototype = { + set: function() { + if (arguments.length === 6) { + var a = arguments; + this.set([a[0], a[1], a[2], + a[3], a[4], a[5]]); + } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { + this.elements = arguments[0].array(); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + this.elements = arguments[0].slice(); + } + }, + get: function() { + var outgoing = new PMatrix2D(); + outgoing.set(this.elements); + return outgoing; + }, + reset: function() { + this.set([1, 0, 0, 0, 1, 0]); + }, + // Returns a copy of the element values. + array: function array() { + return this.elements.slice(); + }, + translate: function(tx, ty) { + this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2]; + this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5]; + }, + // Does nothing in Processing. + transpose: function() { + }, + mult: function(source, target) { + var x, y; + if (source instanceof PVector) { + x = source.x; + y = source.y; + if (!target) { + target = new PVector(); + } + } else if (source instanceof Array) { + x = source[0]; + y = source[1]; + if (!target) { + target = []; + } + } + if (target instanceof Array) { + target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2]; + target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5]; + } else if (target instanceof PVector) { + target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2]; + target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5]; + target.z = 0; + } + return target; + }, + multX: function(x, y) { + return x * this.elements[0] + y * this.elements[1] + this.elements[2]; + }, + multY: function(x, y) { + return x * this.elements[3] + y * this.elements[4] + this.elements[5]; + }, + skewX: function(angle) { + this.apply(1, 0, 1, angle, 0, 0); + }, + skewY: function(angle) { + this.apply(1, 0, 1, 0, angle, 0); + }, + determinant: function() { + return this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]; + }, + invert: function() { + var d = this.determinant(); + if ( Math.abs( d ) > p.FLOAT_MIN ) { + var old00 = this.elements[0]; + var old01 = this.elements[1]; + var old02 = this.elements[2]; + var old10 = this.elements[3]; + var old11 = this.elements[4]; + var old12 = this.elements[5]; + this.elements[0] = old11 / d; + this.elements[3] = -old10 / d; + this.elements[1] = -old01 / d; + this.elements[1] = old00 / d; + this.elements[2] = (old01 * old12 - old11 * old02) / d; + this.elements[5] = (old10 * old02 - old00 * old12) / d; + return true; + } + return false; + }, + scale: function(sx, sy) { + if (sx && !sy) { + sy = sx; + } + if (sx && sy) { + this.elements[0] *= sx; + this.elements[1] *= sy; + this.elements[3] *= sx; + this.elements[4] *= sy; + } + }, + apply: function() { + if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { + this.apply(arguments[0].array()); + } else if (arguments.length === 6) { + var a = arguments; + this.apply([a[0], a[1], a[2], + a[3], a[4], a[5]]); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + var source = arguments[0]; + var result = [0, 0, this.elements[2], + 0, 0, this.elements[5]]; + var e = 0; + for (var row = 0; row < 2; row++) { + for (var col = 0; col < 3; col++, e++) { + result[e] += this.elements[row * 3 + 0] * source[col + 0] + this.elements[row * 3 + 1] * source[col + 3]; + } + } + this.elements = result.slice(); + } + }, + preApply: function() { + if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { + this.preApply(arguments[0].array()); + } else if (arguments.length === 6) { + var a = arguments; + this.preApply([a[0], a[1], a[2], + a[3], a[4], a[5]]); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + var source = arguments[0]; + var result = [0, 0, source[2], + 0, 0, source[5]]; + result[2]= source[2] + this.elements[2] * source[0] + this.elements[5] * source[1]; + result[5]= source[5] + this.elements[2] * source[3] + this.elements[5] * source[4]; + result[0] = this.elements[0] * source[0] + this.elements[3] * source[1]; + result[3] = this.elements[0] * source[3] + this.elements[3] * source[4]; + result[1] = this.elements[1] * source[0] + this.elements[4] * source[1]; + result[4] = this.elements[1] * source[3] + this.elements[4] * source[4]; + this.elements = result.slice(); + } + }, + rotate: function(angle) { + var c = Math.cos(angle); + var s = Math.sin(angle); + var temp1 = this.elements[0]; + var temp2 = this.elements[1]; + this.elements[0] = c * temp1 + s * temp2; + this.elements[1] = -s * temp1 + c * temp2; + temp1 = this.elements[3]; + temp2 = this.elements[4]; + this.elements[3] = c * temp1 + s * temp2; + this.elements[4] = -s * temp1 + c * temp2; + }, + rotateZ: function(angle) { + this.rotate(angle); + }, + print: function() { + var digits = printMatrixHelper(this.elements); + var output = ""; + output += p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + " " + p.nfs(this.elements[2], digits, 4) + "\n"; + output += p.nfs(this.elements[3], digits, 4) + " " + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + "\n\n"; + p.println(output); + } + }; + + //////////////////////////////////////////////////////////////////////////// + // PMatrix3D + //////////////////////////////////////////////////////////////////////////// + var PMatrix3D = function PMatrix3D() { + //When a matrix is created, it is set to an identity matrix + this.reset(); + }; + + PMatrix3D.prototype = { + set: function() { + if (arguments.length === 16) { + var a = arguments; + this.set([a[0], a[1], a[2], a[3], + a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], + a[12], a[13], a[14], a[15]]); + } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { + this.elements = arguments[0].array(); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + this.elements = arguments[0].slice(); + } + }, + get: function() { + var outgoing = new PMatrix3D(); + outgoing.set(this.elements); + return outgoing; + }, + reset: function() { + this.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }, + // Returns a copy of the element values. + array: function array() { + return this.elements.slice(); + }, + translate: function(tx, ty, tz) { + if (typeof tz === 'undefined') { + tx = 0; + } + + this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2]; + this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6]; + this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10]; + this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14]; + }, + transpose: function() { + var temp = this.elements.slice(); + this.elements[0] = temp[0]; + this.elements[1] = temp[4]; + this.elements[2] = temp[8]; + this.elements[3] = temp[12]; + this.elements[4] = temp[1]; + this.elements[5] = temp[5]; + this.elements[6] = temp[9]; + this.elements[7] = temp[13]; + this.elements[8] = temp[2]; + this.elements[9] = temp[6]; + this.elements[10] = temp[10]; + this.elements[11] = temp[14]; + this.elements[12] = temp[3]; + this.elements[13] = temp[7]; + this.elements[14] = temp[11]; + this.elements[15] = temp[15]; + }, + /* + You must either pass in two PVectors or two arrays, + don't mix between types. You may also omit a second + argument and simply read the result from the return. + */ + mult: function(source, target) { + var x, y, z, w; + if (source instanceof PVector) { + x = source.x; + y = source.y; + z = source.z; + w = 1; + if (!target) { + target = new PVector(); + } + } else if (source instanceof Array) { + x = source[0]; + y = source[1]; + z = source[2]; + w = source[3] || 1; + + if (!target || target.length !== 3 && target.length !== 4) { + target = [0, 0, 0]; + } + } + + if (target instanceof Array) { + if (target.length === 3) { + target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; + target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; + target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; + } else if (target.length === 4) { + target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; + target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; + target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; + target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; + } + } + if (target instanceof PVector) { + target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; + target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; + target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; + } + return target; + }, + preApply: function() { + if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { + this.preApply(arguments[0].array()); + } else if (arguments.length === 16) { + var a = arguments; + this.preApply([a[0], a[1], a[2], a[3], + a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], + a[12], a[13], a[14], a[15]]); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + var source = arguments[0]; + + var result = [0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0]; + var e = 0; + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++, e++) { + result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] * source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] + this.elements[col + 12] * source[row * 4 + 3]; + } + } + this.elements = result.slice(); + } + }, + apply: function() { + if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { + this.apply(arguments[0].array()); + } else if (arguments.length === 16) { + var a = arguments; + this.apply([a[0], a[1], a[2], a[3], + a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], + a[12], a[13], a[14], a[15]]); + } else if (arguments.length === 1 && arguments[0] instanceof Array) { + var source = arguments[0]; + + var result = [0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0]; + var e = 0; + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++, e++) { + result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] * source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] + this.elements[row * 4 + 3] * source[col + 12]; + } + } + this.elements = result.slice(); + } + }, + rotate: function(angle, v0, v1, v2) { + if (!v1) { + this.rotateZ(angle); + } else { + // TODO should make sure this vector is normalized + var c = p.cos(angle); + var s = p.sin(angle); + var t = 1.0 - c; + + this.apply((t * v0 * v0) + c, (t * v0 * v1) - (s * v2), (t * v0 * v2) + (s * v1), 0, (t * v0 * v1) + (s * v2), (t * v1 * v1) + c, (t * v1 * v2) - (s * v0), 0, (t * v0 * v2) - (s * v1), (t * v1 * v2) + (s * v0), (t * v2 * v2) + c, 0, 0, 0, 0, 1); + } + }, + invApply: function() { + if (typeof inverseCopy === "undefined") { + inverseCopy = new PMatrix3D(); + } + var a = arguments; + inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); + + if (!inverseCopy.invert()) { + return false; + } + this.preApply(inverseCopy); + return true; + }, + rotateX: function(angle) { + var c = p.cos(angle); + var s = p.sin(angle); + this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); + }, + + rotateY: function(angle) { + var c = p.cos(angle); + var s = p.sin(angle); + this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); + }, + rotateZ: function(angle) { + var c = Math.cos(angle); + var s = Math.sin(angle); + this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }, + // Uniform scaling if only one value passed in + scale: function(sx, sy, sz) { + if (sx && !sy && !sz) { + sy = sz = sx; + } else if (sx && sy && !sz) { + sz = 1; + } + + if (sx && sy && sz) { + this.elements[0] *= sx; + this.elements[1] *= sy; + this.elements[2] *= sz; + this.elements[4] *= sx; + this.elements[5] *= sy; + this.elements[6] *= sz; + this.elements[8] *= sx; + this.elements[9] *= sy; + this.elements[10] *= sz; + this.elements[12] *= sx; + this.elements[13] *= sy; + this.elements[14] *= sz; + } + }, + skewX: function(angle) { + var t = p.tan(angle); + this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + }, + skewY: function(angle) { + var t = Math.tan(angle); + this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + }, + multX: function(x, y, z, w) { + if (!z) { + return this.elements[0] * x + this.elements[1] * y + this.elements[3]; + } else if (!w) { + return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; + } else { + return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; + } + }, + multY: function(x, y, z, w) { + if (!z) { + return this.elements[4] * x + this.elements[5] * y + this.elements[7]; + } else if (!w) { + return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; + } else { + return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; + } + }, + multZ: function(x, y, z, w) { + if (!w) { + return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; + } else { + return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; + } + }, + multW: function(x, y, z, w) { + if (!w) { + return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15]; + } else { + return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; + } + }, + invert: function() { + var kInv = []; + var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4]; + var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4]; + var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4]; + var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5]; + var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5]; + var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6]; + var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12]; + var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12]; + var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12]; + var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13]; + var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13]; + var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14]; + + // Determinant + var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; + + // Account for a very small value + // return false if not successful. + if (Math.abs(fDet) <= 1e-9) { + return false; + } + + kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3; + kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1; + kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0; + kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0; + kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3; + kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1; + kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0; + kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0; + kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3; + kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1; + kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0; + kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0; + kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3; + kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1; + kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0; + kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0; + + // Inverse using Determinant + var fInvDet = 1.0 / fDet; + kInv[0] *= fInvDet; + kInv[1] *= fInvDet; + kInv[2] *= fInvDet; + kInv[3] *= fInvDet; + kInv[4] *= fInvDet; + kInv[5] *= fInvDet; + kInv[6] *= fInvDet; + kInv[7] *= fInvDet; + kInv[8] *= fInvDet; + kInv[9] *= fInvDet; + kInv[10] *= fInvDet; + kInv[11] *= fInvDet; + kInv[12] *= fInvDet; + kInv[13] *= fInvDet; + kInv[14] *= fInvDet; + kInv[15] *= fInvDet; + + this.elements = kInv.slice(); + return true; + }, + toString: function() { + var str = ""; + for (var i = 0; i < 15; i++) { + str += this.elements[i] + ", "; + } + str += this.elements[15]; + return str; + }, + print: function() { + var digits = printMatrixHelper(this.elements); + + var output = ""; + output += p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) + "\n"; + output += p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) + "\n"; + output += p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) + " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) + "\n"; + output += p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) + " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n"; + + p.println(output); + }, + invTranslate: function(tx, ty, tz) { + this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1); + }, + invRotateX: function(angle) { + var c = p.cos(-angle); + var s = p.sin(-angle); + this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); + }, + invRotateY: function(angle) { + var c = p.cos(-angle); + var s = p.sin(-angle); + this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); + }, + invRotateZ: function(angle) { + var c = p.cos(-angle); + var s = p.sin(-angle); + this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }, + invScale: function(x, y, z) { + this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]); + } + }; + + //////////////////////////////////////////////////////////////////////////// + // Matrix Stack + //////////////////////////////////////////////////////////////////////////// + var PMatrixStack = function PMatrixStack() { + this.matrixStack = []; + }; + + PMatrixStack.prototype.load = function load() { + var tmpMatrix; + if (p.use3DContext) { + tmpMatrix = new PMatrix3D(); + } else { + tmpMatrix = new PMatrix2D(); + } + + if (arguments.length === 1) { + tmpMatrix.set(arguments[0]); + } else { + tmpMatrix.set(arguments); + } + this.matrixStack.push(tmpMatrix); + }; + + PMatrixStack.prototype.push = function push() { + this.matrixStack.push(this.peek()); + }; + + PMatrixStack.prototype.pop = function pop() { + return this.matrixStack.pop(); + }; + + PMatrixStack.prototype.peek = function peek() { + var tmpMatrix; + if (p.use3DContext) { + tmpMatrix = new PMatrix3D(); + } else { + tmpMatrix = new PMatrix2D(); + } + + tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]); + return tmpMatrix; + }; + + PMatrixStack.prototype.mult = function mult(matrix) { + this.matrixStack[this.matrixStack.length - 1].apply(matrix); + }; + + //////////////////////////////////////////////////////////////////////////// + // Array handling + //////////////////////////////////////////////////////////////////////////// + p.split = function(str, delim) { + return str.split(delim); + }; + + p.splitTokens = function(str, tokens) { + if (arguments.length === 1) { + tokens = "\n\t\r\f "; + } + + tokens = "[" + tokens + "]"; + + var ary = new Array(0); + var index = 0; + var pos = str.search(tokens); + + while (pos >= 0) { + if (pos === 0) { + str = str.substring(1); + } else { + ary[index] = str.substring(0, pos); + index++; + str = str.substring(pos); + } + pos = str.search(tokens); + } + + if (str.length > 0) { + ary[index] = str; + } + + if (ary.length === 0) { + ary = undefined; + } + + return ary; + }; + + p.append = function(array, element) { + array[array.length] = element; + return array; + }; + + p.concat = function(array1, array2) { + return array1.concat(array2); + }; + + p.sort = function(array, numElem) { + var ret = []; + + // depending on the type used (int, float) or string + // we'll need to use a different compare function + if (array.length > 0) { + // copy since we need to return another array + var elemsToCopy = numElem > 0 ? numElem : array.length; + for (var i = 0; i < elemsToCopy; i++) { + ret.push(array[i]); + } + if (typeof array[0] === "string") { + ret.sort(); + } + // int or float + else { + ret.sort(function(a, b) { + return a - b; + }); + } + + // copy on the rest of the elements that were not sorted in case the user + // only wanted a subset of an array to be sorted. + if (numElem > 0) { + for (var j = ret.length; j < array.length; j++) { + ret.push(array[j]); + } + } + } + return ret; + }; + + p.splice = function(array, value, index) { + if (array.length === 0 && value.length === 0) { + return array; + } + + if (value instanceof Array) { + for (var i = 0, j = index; i < value.length; j++, i++) { + array.splice(j, 0, value[i]); + } + } else { + array.splice(index, 0, value); + } + + return array; + }; + + p.subset = function(array, offset, length) { + if (arguments.length === 2) { + return p.subset(array, offset, array.length - offset); + } else if (arguments.length === 3) { + return array.slice(offset, offset + length); + } + }; + + p.join = function(array, seperator) { + return array.join(seperator); + }; + + p.shorten = function(ary) { + var newary = new Array(0); + + // copy array into new array + var len = ary.length; + for (var i = 0; i < len; i++) { + newary[i] = ary[i]; + } + newary.pop(); + + return newary; + }; + + p.expand = function(ary, newSize) { + var newary = new Array(0); + + var len = ary.length; + for (var i = 0; i < len; i++) { + newary[i] = ary[i]; + } + + if (arguments.length === 1) { + // double size of array + newary.length *= 2; + } else if (arguments.length === 2) { + // size is newSize + newary.length = newSize; + } + + return newary; + }; + + p.arrayCopy = function(src, srcPos, dest, destPos, length) { + if (arguments.length === 2) { + // recall itself and copy src to dest from start index 0 to 0 of src.length + p.arrayCopy(src, 0, srcPos, 0, src.length); + } else if (arguments.length === 3) { + // recall itself and copy src to dest from start index 0 to 0 of length + p.arrayCopy(src, 0, srcPos, 0, dest); + } else if (arguments.length === 5) { + // copy src to dest from index srcPos to index destPos of length recursivly on objects + for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) { + if (src[i] && typeof src[i] === "object") { + // src[i] is not null and is another object or array. go recursive + p.arrayCopy(src[i], 0, dest[j], 0, src[i].length); + } else { + // standard type, just copy + dest[j] = src[i]; + } + } + } + }; + + p.ArrayList = function() { + var createArrayList = function(args){ + var array = []; + for (var i = 0; i < args[0]; i++){ + array[i] = (args.length > 1 ? createArrayList(args.slice(1)) : 0 ); + } + + array.get = function(i) { + return this[i]; + }; + array.contains = function(item) { + return this.indexOf(item) !== -1; + }; + array.add = function(item) { + return this.push(item); + }; + array.size = function() { + return this.length; + }; + array.clear = function() { + this.length = 0; + }; + array.remove = function(i) { + return this.splice(i, 1)[0]; + }; + array.isEmpty = function() { + return !this.length; + }; + array.clone = function() { + var size = this.length; + var a = new p.ArrayList(size); + for (var i = 0; i < size; i++) { + a[i] = this[i]; + } + return a; + }; + + return array; + }; + return createArrayList(Array.prototype.slice.call(arguments)); + }; + + p.reverse = function(array) { + return array.reverse(); + }; + + //////////////////////////////////////////////////////////////////////////// + // HashMap + //////////////////////////////////////////////////////////////////////////// + + var virtHashCode = function virtHashCode(obj) { + if (obj.constructor === String) { + var hash = 0; + for (var i = 0; i < obj.length; ++i) { + hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF; + } + return hash; + } else if (typeof(obj) !== "object") { + return obj & 0xFFFFFFFF; + } else if ("hashCode" in obj) { + return obj.hashCode.call(obj); + } else { + if (obj.$id === undefined) { + obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000); + } + return obj.$id; + } + }; + + var virtEquals = function virtEquals(obj, other) { + if (obj === null || other === null) { + return (obj === null) && (other === null); + } else if (obj.constructor === String) { + return obj === other; + } else if (typeof(obj) !== "object") { + return obj === other; + } else if ("equals" in obj) { + return obj.equals.call(obj, other); + } else { + return obj === other; + } + }; + + p.HashMap = function HashMap() { + if (arguments.length === 1 && arguments[0].constructor === HashMap) { + return arguments[0].clone(); + } + + var initialCapacity = arguments.length > 0 ? arguments[0] : 16; + var loadFactor = arguments.length > 1 ? arguments[1] : 0.75; + + var buckets = new Array(initialCapacity); + var count = 0; + var hashMap = this; + + function ensureLoad() { + if (count <= loadFactor * buckets.length) { + return; + } + var allEntries = []; + for (var i = 0; i < buckets.length; ++i) { + if (buckets[i] !== undefined) { + allEntries = allEntries.concat(buckets[i]); + } + } + buckets = new Array(buckets.length * 2); + for (var j = 0; j < allEntries.length; ++j) { + var index = virtHashCode(allEntries[j].key) % buckets.length; + var bucket = buckets[index]; + if (bucket === undefined) { + buckets[index] = bucket = []; + } + bucket.push(allEntries[j]); + } + } + + function Iterator(conversion, removeItem) { + var bucketIndex = 0; + var itemIndex = -1; + var endOfBuckets = false; + + function findNext() { + while (!endOfBuckets) { + ++itemIndex; + if (bucketIndex >= buckets.length) { + endOfBuckets = true; + } else if (typeof(buckets[bucketIndex]) === 'undefined' || itemIndex >= buckets[bucketIndex].length) { + itemIndex = -1; + ++bucketIndex; + } else { + return; + } + } + } + + this.hasNext = function() { + return !endOfBuckets; + }; + this.next = function() { + var result = conversion(buckets[bucketIndex][itemIndex]); + findNext(); + return result; + }; + this.remove = function() { + removeItem(this.next()); + --itemIndex; + }; + + findNext(); + } + + function Set(conversion, isIn, removeItem) { + this.clear = function() { + hashMap.clear(); + }; + this.contains = function(o) { + return isIn(o); + }; + this.containsAll = function(o) { + var it = o.iterator(); + while (it.hasNext()) { + if (!this.contains(it.next())) { + return false; + } + } + return true; + }; + this.isEmpty = function() { + return hashMap.isEmpty(); + }; + this.iterator = function() { + return new Iterator(conversion, removeItem); + }; + this.remove = function(o) { + if (this.contains(o)) { + removeItem(o); + return true; + } + return false; + }; + this.removeAll = function(c) { + var it = c.iterator(); + var changed = false; + while (it.hasNext()) { + var item = it.next(); + if (this.contains(item)) { + removeItem(item); + changed = true; + } + } + return true; + }; + this.retainAll = function(c) { + var it = this.iterator(); + var toRemove = []; + while (it.hasNext()) { + var entry = it.next(); + if (!c.contains(entry)) { + toRemove.push(entry); + } + } + for (var i = 0; i < toRemove.length; ++i) { + removeItem(toRemove[i]); + } + return toRemove.length > 0; + }; + this.size = function() { + return hashMap.size(); + }; + this.toArray = function() { + var result = new p.ArrayList(0); + var it = this.iterator(); + while (it.hasNext()) { + result.push(it.next()); + } + return result; + }; + } + + function Entry(pair) { + this._isIn = function(map) { + return map === hashMap && (typeof(pair.removed) === 'undefined'); + }; + this.equals = function(o) { + return virtEquals(pair.key, o.getKey()); + }; + this.getKey = function() { + return pair.key; + }; + this.getValue = function() { + return pair.value; + }; + this.hashCode = function(o) { + return virtHashCode(pair.key); + }; + this.setValue = function(value) { + var old = pair.value; + pair.value = value; + return old; + }; + } + + this.clear = function() { + count = 0; + buckets = new Array(initialCapacity); + }; + this.clone = function() { + var map = new p.HashMap(); + map.putAll(this); + return map; + }; + this.containsKey = function(key) { + var index = virtHashCode(key) % buckets.length; + var bucket = buckets[index]; + if (bucket === undefined) { + return false; + } + for (var i = 0; i < bucket.length; ++i) { + if (virtEquals(bucket[i].key, key)) { + return true; + } + } + return false; + }; + this.containsValue = function(value) { + for (var i = 0; i < buckets.length; ++i) { + var bucket = buckets[i]; + if (bucket === undefined) { + continue; + } + for (var j = 0; j < bucket.length; ++j) { + if (virtEquals(bucket[j].value, value)) { + return true; + } + } + } + return false; + }; + this.entrySet = function() { + return new Set( + + function(pair) { + return new Entry(pair); + }, + + function(pair) { + return pair.constructor === Entry && pair._isIn(hashMap); + }, + + function(pair) { + return hashMap.remove(pair.getKey()); + }); + }; + this.get = function(key) { + var index = virtHashCode(key) % buckets.length; + var bucket = buckets[index]; + if (bucket === undefined) { + return null; + } + for (var i = 0; i < bucket.length; ++i) { + if (virtEquals(bucket[i].key, key)) { + return bucket[i].value; + } + } + return null; + }; + this.isEmpty = function() { + return count === 0; + }; + this.keySet = function() { + return new Set( + + function(pair) { + return pair.key; + }, + + function(key) { + return hashMap.containsKey(key); + }, + + function(key) { + return hashMap.remove(key); + }); + }; + this.put = function(key, value) { + var index = virtHashCode(key) % buckets.length; + var bucket = buckets[index]; + if (bucket === undefined) { + ++count; + buckets[index] = [{ + key: key, + value: value + }]; + ensureLoad(); + return null; + } + for (var i = 0; i < bucket.length; ++i) { + if (virtEquals(bucket[i].key, key)) { + var previous = bucket[i].value; + bucket[i].value = value; + return previous; + } + }++count; + bucket.push({ + key: key, + value: value + }); + ensureLoad(); + return null; + }; + this.putAll = function(m) { + var it = m.entrySet().iterator(); + while (it.hasNext()) { + var entry = it.next(); + this.put(entry.getKey(), entry.getValue()); + } + }; + this.remove = function(key) { + var index = virtHashCode(key) % buckets.length; + var bucket = buckets[index]; + if (bucket === undefined) { + return null; + } + for (var i = 0; i < bucket.length; ++i) { + if (virtEquals(bucket[i].key, key)) { + --count; + var previous = bucket[i].value; + bucket[i].removed = true; + if (bucket.length > 1) { + bucket.splice(i, 1); + } else { + buckets[index] = undefined; + } + return previous; + } + } + return null; + }; + this.size = function() { + return count; + }; + this.values = function() { + var result = new p.ArrayList(0); + var it = this.entrySet().iterator(); + while (it.hasNext()) { + var entry = it.next(); + result.push(entry.getValue()); + } + return result; + }; + }; + + //////////////////////////////////////////////////////////////////////////// + // Color functions + //////////////////////////////////////////////////////////////////////////// + + // helper functions for internal blending modes + p.mix = function(a, b, f) { + return a + (((b - a) * f) >> 8); + }; + + p.peg = function(n) { + return (n < 0) ? 0 : ((n > 255) ? 255 : n); + }; + + // blending modes + p.modes = { + replace: function(c1, c2) { + return c2; + }, + blend: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | p.mix(c1 & p.RED_MASK, c2 & p.RED_MASK, f) & p.RED_MASK | p.mix(c1 & p.GREEN_MASK, c2 & p.GREEN_MASK, f) & p.GREEN_MASK | p.mix(c1 & p.BLUE_MASK, c2 & p.BLUE_MASK, f)); + }, + add: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.min(((c1 & p.RED_MASK) + ((c2 & p.RED_MASK) >> 8) * f), p.RED_MASK) & p.RED_MASK | Math.min(((c1 & p.GREEN_MASK) + ((c2 & p.GREEN_MASK) >> 8) * f), p.GREEN_MASK) & p.GREEN_MASK | Math.min((c1 & p.BLUE_MASK) + (((c2 & p.BLUE_MASK) * f) >> 8), p.BLUE_MASK)); + }, + subtract: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.max(((c1 & p.RED_MASK) - ((c2 & p.RED_MASK) >> 8) * f), p.GREEN_MASK) & p.RED_MASK | Math.max(((c1 & p.GREEN_MASK) - ((c2 & p.GREEN_MASK) >> 8) * f), p.BLUE_MASK) & p.GREEN_MASK | Math.max((c1 & p.BLUE_MASK) - (((c2 & p.BLUE_MASK) * f) >> 8), 0)); + }, + lightest: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.max(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f) & p.RED_MASK | Math.max(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f) & p.GREEN_MASK | Math.max(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8)); + }, + darkest: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | p.mix(c1 & p.RED_MASK, Math.min(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f), f) & p.RED_MASK | p.mix(c1 & p.GREEN_MASK, Math.min(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f), f) & p.GREEN_MASK | p.mix(c1 & p.BLUE_MASK, Math.min(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8), f)); + }, + difference: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (ar > br) ? (ar - br) : (br - ar); + var cg = (ag > bg) ? (ag - bg) : (bg - ag); + var cb = (ab > bb) ? (ab - bb) : (bb - ab); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + exclusion: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = ar + br - ((ar * br) >> 7); + var cg = ag + bg - ((ag * bg) >> 7); + var cb = ab + bb - ((ab * bb) >> 7); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + multiply: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (ar * br) >> 8; + var cg = (ag * bg) >> 8; + var cb = (ab * bb) >> 8; + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + screen: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = 255 - (((255 - ar) * (255 - br)) >> 8); + var cg = 255 - (((255 - ag) * (255 - bg)) >> 8); + var cb = 255 - (((255 - ab) * (255 - bb)) >> 8); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + hard_light: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)); + var cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)); + var cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7)); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + soft_light: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15); + var cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15); + var cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + overlay: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)); + var cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)); + var cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7)); + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + dodge: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (br === 255) ? 255 : p.peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing + var cg = (bg === 255) ? 255 : p.peg((ag << 8) / (255 - bg)); // " + var cb = (bb === 255) ? 255 : p.peg((ab << 8) / (255 - bb)); // " + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + }, + burn: function(c1, c2) { + var f = (c2 & p.ALPHA_MASK) >>> 24; + var ar = (c1 & p.RED_MASK) >> 16; + var ag = (c1 & p.GREEN_MASK) >> 8; + var ab = (c1 & p.BLUE_MASK); + var br = (c2 & p.RED_MASK) >> 16; + var bg = (c2 & p.GREEN_MASK) >> 8; + var bb = (c2 & p.BLUE_MASK); + // formula: + var cr = (br === 0) ? 0 : 255 - p.peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing + var cg = (bg === 0) ? 0 : 255 - p.peg(((255 - ag) << 8) / bg); // " + var cb = (bb === 0) ? 0 : 255 - p.peg(((255 - ab) << 8) / bb); // " + // alpha blend (this portion will always be the same) + return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8)))); + } + }; + + p.color = function color(aValue1, aValue2, aValue3, aValue4) { + var r, g, b, a, rgb, aColor; + + // 4 arguments: (R, G, B, A) or (H, S, B, A) + if (aValue1 != null && aValue2 != null && aValue3 != null && aValue4 != null) { + if (curColorMode === p.HSB) { + rgb = p.color.toRGB(aValue1, aValue2, aValue3); + r = rgb[0]; + g = rgb[1]; + b = rgb[2]; + } else { + r = Math.round(255 * (aValue1 / colorModeX)); + g = Math.round(255 * (aValue2 / colorModeY)); + b = Math.round(255 * (aValue3 / colorModeZ)); + } + + a = Math.round(255 * (aValue4 / colorModeA)); + + // Limit values greater than 255 + r = (r > 255) ? 255 : r; + g = (g > 255) ? 255 : g; + b = (b > 255) ? 255 : b; + a = (a > 255) ? 255 : a; + + // Create color int + aColor = (a << 24) & p.ALPHA_MASK | (r << 16) & p.RED_MASK | (g << 8) & p.GREEN_MASK | b & p.BLUE_MASK; + } + + // 3 arguments: (R, G, B) or (H, S, B) + else if (aValue1 != null && aValue2 != null && aValue3 != null) { + aColor = p.color(aValue1, aValue2, aValue3, colorModeA); + } + + // 2 arguments: (Color, A) or (Grayscale, A) + else if (aValue1 != null && aValue2 != null) { + // Color int and alpha + if (aValue1 & p.ALPHA_MASK) { + a = Math.round(255 * (aValue2 / colorModeA)); + a = (a > 255) ? 255 : a; + + aColor = aValue1 - (aValue1 & p.ALPHA_MASK) + ((a << 24) & p.ALPHA_MASK); + } + // Grayscale and alpha + else { + switch(curColorMode) { + case p.RGB: aColor = p.color(aValue1, aValue1, aValue1, aValue2); break; + case p.HSB: aColor = p.color(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2); break; + } + } + } + + // 1 argument: (Grayscale) or (Color) + else if (typeof aValue1 === "number") { + // Grayscale + if (aValue1 <= colorModeX && aValue1 >= 0) { + switch(curColorMode) { + case p.RGB: aColor = p.color(aValue1, aValue1, aValue1, colorModeA); break; + case p.HSB: aColor = p.color(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA); break; + } + } + // Color int + else if (aValue1) { + aColor = aValue1; + } + } + + // Default + else { + aColor = p.color(colorModeX, colorModeY, colorModeZ, colorModeA); + } + + return aColor; + }; + + // Ease of use function to extract the colour bits into a string + p.color.toString = function(colorInt) { + return "rgba(" + ((colorInt & p.RED_MASK) >>> 16) + "," + ((colorInt & p.GREEN_MASK) >>> 8) + "," + ((colorInt & p.BLUE_MASK)) + "," + ((colorInt & p.ALPHA_MASK) >>> 24) / 255 + ")"; + }; + + // Easy of use function to pack rgba values into a single bit-shifted color int. + p.color.toInt = function(r, g, b, a) { + return (a << 24) & p.ALPHA_MASK | (r << 16) & p.RED_MASK | (g << 8) & p.GREEN_MASK | b & p.BLUE_MASK; + }; + + // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255] + p.color.toArray = function(colorInt) { + return [(colorInt & p.RED_MASK) >>> 16, (colorInt & p.GREEN_MASK) >>> 8, colorInt & p.BLUE_MASK, (colorInt & p.ALPHA_MASK) >>> 24]; + }; + + // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1] + p.color.toGLArray = function(colorInt) { + return [((colorInt & p.RED_MASK) >>> 16) / 255, ((colorInt & p.GREEN_MASK) >>> 8) / 255, (colorInt & p.BLUE_MASK) / 255, ((colorInt & p.ALPHA_MASK) >>> 24) / 255]; + }; + + // HSB conversion function from Mootools, MIT Licensed + p.color.toRGB = function(h, s, b) { + // Limit values greater than range + h = (h > colorModeX) ? colorModeX : h; + s = (s > colorModeY) ? colorModeY : s; + b = (b > colorModeZ) ? colorModeZ : b; + + h = (h / colorModeX) * 360; + s = (s / colorModeY) * 100; + b = (b / colorModeZ) * 100; + + var br = Math.round(b / 100 * 255); + + if (s === 0) { // Grayscale + return [br, br, br]; + } else { + var hue = h % 360; + var f = hue % 60; + var p = Math.round((b * (100 - s)) / 10000 * 255); + var q = Math.round((b * (6000 - s * f)) / 600000 * 255); + var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255); + switch (Math.floor(hue / 60)) { + case 0: + return [br, t, p]; + case 1: + return [q, br, p]; + case 2: + return [p, br, t]; + case 3: + return [p, q, br]; + case 4: + return [t, p, br]; + case 5: + return [br, p, q]; + } + } + }; + + p.color.toHSB = function( colorInt ) { + var red, green, blue; + + red = ((colorInt & p.RED_MASK) >>> 16) / 255; + green = ((colorInt & p.GREEN_MASK) >>> 8) / 255; + blue = (colorInt & p.BLUE_MASK) / 255; + + var max = p.max(p.max(red,green), blue), + min = p.min(p.min(red,green), blue), + hue, saturation; + + if (min === max) { + return [0, 0, max]; + } else { + saturation = (max - min) / max; + + if (red === max) { + hue = (green - blue) / (max - min); + } else if (green === max) { + hue = 2 + ((blue - red) / (max - min)); + } else { + hue = 4 + ((red - green) / (max - min)); + } + + hue /= 6; + + if (hue < 0) { + hue += 1; + } else if (hue > 1) { + hue -= 1; + } + } + return [hue*colorModeX, saturation*colorModeY, max*colorModeZ]; + }; + + p.brightness = function(colInt){ + return p.color.toHSB(colInt)[2]; + }; + + p.saturation = function(colInt){ + return p.color.toHSB(colInt)[1]; + }; + + p.hue = function(colInt){ + return p.color.toHSB(colInt)[0]; + }; + + var verifyChannel = function verifyChannel(aColor) { + if (aColor.constructor === Array) { + return aColor; + } else { + return p.color(aColor); + } + }; + + p.red = function(aColor) { + return ((aColor & p.RED_MASK) >>> 16) / 255 * colorModeX; + }; + + p.green = function(aColor) { + return ((aColor & p.GREEN_MASK) >>> 8) / 255 * colorModeY; + }; + + p.blue = function(aColor) { + return (aColor & p.BLUE_MASK) / 255 * colorModeZ; + }; + + p.alpha = function(aColor) { + return ((aColor & p.ALPHA_MASK) >>> 24) / 255 * colorModeA; + }; + + p.lerpColor = function lerpColor(c1, c2, amt) { + // Get RGBA values for Color 1 to floats + var colorBits1 = p.color(c1); + var r1 = (colorBits1 & p.RED_MASK) >>> 16; + var g1 = (colorBits1 & p.GREEN_MASK) >>> 8; + var b1 = (colorBits1 & p.BLUE_MASK); + var a1 = ((colorBits1 & p.ALPHA_MASK) >>> 24) / colorModeA; + + // Get RGBA values for Color 2 to floats + var colorBits2 = p.color(c2); + var r2 = (colorBits2 & p.RED_MASK) >>> 16; + var g2 = (colorBits2 & p.GREEN_MASK) >>> 8; + var b2 = (colorBits2 & p.BLUE_MASK); + var a2 = ((colorBits2 & p.ALPHA_MASK) >>> 24) / colorModeA; + + // Return lerp value for each channel, INT for color, Float for Alpha-range + var r = parseInt(p.lerp(r1, r2, amt), 10); + var g = parseInt(p.lerp(g1, g2, amt), 10); + var b = parseInt(p.lerp(b1, b2, amt), 10); + var a = parseFloat(p.lerp(a1, a2, amt) * colorModeA, 10); + + return p.color.toInt(r, g, b, a); + }; + + // Forced default color mode for #aaaaaa style + p.defaultColor = function(aValue1, aValue2, aValue3) { + var tmpColorMode = curColorMode; + curColorMode = p.RGB; + var c = p.color(aValue1 / 255 * colorModeX, aValue2 / 255 * colorModeY, aValue3 / 255 * colorModeZ); + curColorMode = tmpColorMode; + return c; + }; + + p.colorMode = function colorMode(mode, range1, range2, range3, range4) { + curColorMode = mode; + if (arguments.length >= 4) { + colorModeX = range1; + colorModeY = range2; + colorModeZ = range3; + } + if (arguments.length === 5) { + colorModeA = range4; + } + if (arguments.length === 2) { + p.colorMode(mode, range1, range1, range1, range1); + } + }; + + p.blendColor = function(c1, c2, mode) { + var color = 0; + switch (mode) { + case p.REPLACE: + color = p.modes.replace(c1, c2); + break; + case p.BLEND: + color = p.modes.blend(c1, c2); + break; + case p.ADD: + color = p.modes.add(c1, c2); + break; + case p.SUBTRACT: + color = p.modes.subtract(c1, c2); + break; + case p.LIGHTEST: + color = p.modes.lightest(c1, c2); + break; + case p.DARKEST: + color = p.modes.darkest(c1, c2); + break; + case p.DIFFERENCE: + color = p.modes.difference(c1, c2); + break; + case p.EXCLUSION: + color = p.modes.exclusion(c1, c2); + break; + case p.MULTIPLY: + color = p.modes.multiply(c1, c2); + break; + case p.SCREEN: + color = p.modes.screen(c1, c2); + break; + case p.HARD_LIGHT: + color = p.modes.hard_light(c1, c2); + break; + case p.SOFT_LIGHT: + color = p.modes.soft_light(c1, c2); + break; + case p.OVERLAY: + color = p.modes.overlay(c1, c2); + break; + case p.DODGE: + color = p.modes.dodge(c1, c2); + break; + case p.BURN: + color = p.modes.burn(c1, c2); + break; + } + return color; + }; + + //////////////////////////////////////////////////////////////////////////// + // Canvas-Matrix manipulation + //////////////////////////////////////////////////////////////////////////// + + p.printMatrix = function printMatrix() { + modelView.print(); + }; + + p.translate = function translate(x, y, z) { + if (p.use3DContext) { + forwardTransform.translate(x, y, z); + reverseTransform.invTranslate(x, y, z); + } else { + curContext.translate(x, y); + } + }; + + p.scale = function scale(x, y, z) { + if (p.use3DContext) { + forwardTransform.scale(x, y, z); + reverseTransform.invScale(x, y, z); + } else { + curContext.scale(x, y || x); + } + }; + + p.pushMatrix = function pushMatrix() { + if (p.use3DContext) { + userMatrixStack.load(modelView); + } else { + curContext.save(); + } + }; + + p.popMatrix = function popMatrix() { + if (p.use3DContext) { + modelView.set(userMatrixStack.pop()); + } else { + curContext.restore(); + } + }; + + p.resetMatrix = function resetMatrix() { + forwardTransform.reset(); + reverseTransform.reset(); + }; + + p.applyMatrix = function applyMatrix() { + var a = arguments; + if (!p.use3DContext) { + for (var cnt = a.length; cnt < 16; cnt++) { + a[cnt] = 0; + } + a[10] = a[15] = 1; + } + + forwardTransform.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); + reverseTransform.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); + }; + + p.rotateX = function(angleInRadians) { + forwardTransform.rotateX(angleInRadians); + reverseTransform.invRotateX(angleInRadians); + }; + + p.rotateZ = function(angleInRadians) { + forwardTransform.rotateZ(angleInRadians); + reverseTransform.invRotateZ(angleInRadians); + }; + + p.rotateY = function(angleInRadians) { + forwardTransform.rotateY(angleInRadians); + reverseTransform.invRotateY(angleInRadians); + }; + + p.rotate = function rotate(angleInRadians) { + if (p.use3DContext) { + forwardTransform.rotateZ(angleInRadians); + reverseTransform.invRotateZ(angleInRadians); + } else { + curContext.rotate(angleInRadians); + } + }; + + p.pushStyle = function pushStyle() { + // Save the canvas state. + curContext.save(); + + p.pushMatrix(); + + var newState = { + 'doFill': doFill, + 'doStroke': doStroke, + 'curTint': curTint, + 'curRectMode': curRectMode, + 'curColorMode': curColorMode, + 'colorModeX': colorModeX, + 'colorModeZ': colorModeZ, + 'colorModeY': colorModeY, + 'colorModeA': colorModeA, + 'curTextFont': curTextFont, + 'curTextSize': curTextSize + }; + + styleArray.push(newState); + }; + + p.popStyle = function popStyle() { + var oldState = styleArray.pop(); + + if (oldState) { + curContext.restore(); + + p.popMatrix(); + + doFill = oldState.doFill; + doStroke = oldState.doStroke; + curTint = oldState.curTint; + curRectMode = oldState.curRectmode; + curColorMode = oldState.curColorMode; + colorModeX = oldState.colorModeX; + colorModeZ = oldState.colorModeZ; + colorModeY = oldState.colorModeY; + colorModeA = oldState.colorModeA; + curTextFont = oldState.curTextFont; + curTextSize = oldState.curTextSize; + } else { + throw "Too many popStyle() without enough pushStyle()"; + } + }; + + //////////////////////////////////////////////////////////////////////////// + // Time based functions + //////////////////////////////////////////////////////////////////////////// + + p.year = function year() { + return new Date().getFullYear(); + }; + p.month = function month() { + return new Date().getMonth() + 1; + }; + p.day = function day() { + return new Date().getDate(); + }; + p.hour = function hour() { + return new Date().getHours(); + }; + p.minute = function minute() { + return new Date().getMinutes(); + }; + p.second = function second() { + return new Date().getSeconds(); + }; + p.millis = function millis() { + return new Date().getTime() - start; + }; + + p.noLoop = function noLoop() { + doLoop = false; + loopStarted = false; + clearInterval(looping); + }; + + p.redraw = function redraw() { + var sec = (new Date().getTime() - timeSinceLastFPS) / 1000; + framesSinceLastFPS++; + var fps = framesSinceLastFPS / sec; + + // recalculate FPS every half second for better accuracy. + if (sec > 0.5) { + timeSinceLastFPS = new Date().getTime(); + framesSinceLastFPS = 0; + p.FRAME_RATE = fps; + } + + p.frameCount++; + + inDraw = true; + + if (p.use3DContext) { + // Delete all the lighting states and the materials the + // user set in the last draw() call. + p.noLights(); + p.lightFalloff(1, 0, 0); + p.shininess(1); + p.ambient(255, 255, 255); + p.specular(0, 0, 0); + p.camera(); + p.draw(); + } else { + curContext.save(); + p.draw(); + curContext.restore(); + } + + inDraw = false; + }; + + p.loop = function loop() { + if (loopStarted) { + return; + } + + looping = window.setInterval(function() { + try { + try { + p.focused = document.hasFocus(); + } catch(e) {} + p.redraw(); + } catch(e_loop) { + window.clearInterval(looping); + throw e_loop; + } + }, curMsPerFrame); + + doLoop = true; + loopStarted = true; + }; + + p.frameRate = function frameRate(aRate) { + curFrameRate = aRate; + curMsPerFrame = 1000 / curFrameRate; + }; + + p.exit = function exit() { + window.clearInterval(looping); + + for (var i=0, ehl=p.pjs.eventHandlers.length; i 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) { + var image = arguments[0], + x, y; + if (arguments.length >= 3) { + x = arguments[1]; + y = arguments[2]; + if (x < 0 || y < 0 || y >= image.height || x >= image.width) { + throw "x and y must be non-negative and less than the dimensions of the image"; + } + } else { + x = image.width >>> 1; + y = image.height >>> 1; + } + + // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property + var imageDataURL = image.toDataURL(); + var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default"; + curCursor = curElement.style.cursor = style; + } else if (arguments.length === 1) { + var mode = arguments[0]; + curCursor = curElement.style.cursor = mode; + } else { + curCursor = curElement.style.cursor = oldCursor; + } + }; + + p.noCursor = function noCursor() { + curCursor = curElement.style.cursor = p.NOCURSOR; + }; + + p.link = function(href, target) { + if (typeof target !== 'undefined') { + window.open(href, target); + } else { + window.location = href; + } + }; + + // PGraphics methods + // TODO: These functions are suppose to be called before any operations are called on the + // PGraphics object. They currently do nothing. + p.beginDraw = function beginDraw() {}; + p.endDraw = function endDraw() {}; + + // Imports an external Processing.js library + p.Import = function Import(lib) { + // Replace evil-eval method with a DOM + * + * (If you are including this file into your page via Server-Side + * Includes, you should remove line above.) + * + * You can make copies of this file with different settings + * if you need to have several different configurations. + * + **********************************************************************/ + +if (!window.jsMath) {window.jsMath = {}} + +jsMath.Easy = { + // + // The URL of the root jsMath directory on your server + // (it must be in the same domain as the HTML page). + // It should include "http://yoursite.com/", or should + // be relative to the root of your server. It is possible + // to be a relative URL, but it will be relative to the + // HTML page loading this file. + // + // If you leave this blank, jsMath will try to look it up from + // the URL where it loaded this file, but that may not work. + // + root: "", + + // + // The default scaling factor for mathematics compared to the + // surrounding text. + // + scale: 120, + + // + // 1 means use the autoload plug-in to decide if jsMath should be loaded + // 0 means always load jsMath + // + autoload: 1, + + // + // Setting any of these will cause the tex2math plugin to be used + // to add the
      and tags that jsMath needs. See the + // documentation for the tex2math plugin for more information. + // + processSlashParens: 1, // process \(...\) in text? + processSlashBrackets: 1, // process \[...\] in text? + processDoubleDollars: 1, // process $$...$$ in text? + processSingleDollars: 0, // process $...$ in text? + processLaTeXenvironments: 0, // process \begin{xxx}...\end{xxx} outside math mode? + fixEscapedDollars: 0, // convert \$ to $ outside of math mode? + doubleDollarsAreInLine: 0, // make $$...$$ be in-line math? + allowDisableTag: 1, // allow ID="tex2math_off" to disable tex2math? + // + // If you want to use your own custom delimiters for math instead + // of the usual ones, then uncomment the following four lines and + // insert your own delimiters within the quotes. You may want to + // turn off processing of the dollars and other delimiters above + // as well, though you can use them in combination with the + // custom delimiters if you wish. See the tex2math documentation + // for more details. + // + //customDelimiters: [ + // '[math]','[/math]', // to begin and end in-line math + // '[display]','[/display]' // to begin and end display math + //], + + // + // Disallow the use of the @(...) mechanism for including raw HTML + // in the contents of \hbox{}? (If used in a content-management system + // where users are allowed to enter mathematics, setting this to 0 + // would allow them to enter arbitrary HTML code within their + // math formulas, and that poses a security risk.) + // + safeHBoxes: 1, + + // + // Show TeX source when mathematics is double-clicked? + // + allowDoubleClicks: 1, + + // + // Show jsMath font warning messages? (Disabling this prevents yours + // users from finding out that they can have a better experience on your + // site by installing some fonts, so don't disable this). + // + showFontWarnings: 1, + + // + // Use "Process" or "ProcessBeforeShowing". See the jsMath + // author's documentation for the difference between these + // two routines. + // + method: "Process", + + // + // List of plug-ins and extensions that you want to be + // loaded automatically. E.g. + // ["plugins/mimeTeX.js","extensions/AMSsymbols.js"] + // + loadFiles: [], + + // + // List of fonts to load automatically. E.g. + // ["cmmib10"] + // + loadFonts: [], + + // + // List of macros to define. These are of the form + // name: value + // where 'value' is the replacement text for the macro \name. + // The 'value' can also be [value,n] where 'value' is the replacement + // text and 'n' is the number of parameters for the macro. + // Note that backslashes must be doubled in the replacement string. + // E.g., + // { + // RR: '{\\bf R}', + // bold: ['{\\bf #1}', 1] + // } + // + macros: {}, + + // + // Allow jsMath to enter global mode? + // (Uses frames, so may not always work with complex web sites) + // + allowGlobal: 1, + + // + // Disable image fonts? (In case you don't load them on your server.) + // + noImageFonts: 0 + +}; + +/****************************************************************/ +/****************************************************************/ +// +// DO NOT MAKE CHANGES BELOW THIS +// +/****************************************************************/ +/****************************************************************/ + +if (jsMath.Easy.root == "") { + jsMath.Easy.root = document.getElementsByTagName("script"); + jsMath.Easy.root = jsMath.Easy.root[jsMath.Easy.root.length-1].src + if (jsMath.Easy.root.match(/\/easy\/[^\/]*$/)) { + jsMath.Easy.root = jsMath.Easy.root.replace(/\/easy\/[^\/]*$/,""); + } else { + jsMath.Easy.root = jsMath.Easy.root.replace(/\/(jsMath\/(easy\/)?)?[^\/]*$/,"/jsMath"); + } +} +jsMath.Easy.root = jsMath.Easy.root.replace(/\/$/,""); // trim trailing "/" if any + +document.write(' + + + + + diff --git a/htdocs/jsMath/jsMath-controls.html b/htdocs/jsMath/jsMath-controls.html new file mode 100644 index 0000000000..e395ebed0d --- /dev/null +++ b/htdocs/jsMath/jsMath-controls.html @@ -0,0 +1,467 @@ + + + + + + + + + + + + +
      +
      +jsMath v +(type fonts) +[help] +

      + + + + + + + + + + + + +
      + + + + + + + + + + + + + +
      +
        + + + + + + + + + + +
      + +
      +
      +
      +
      +  + +
      +
      +
      +
      +
      +  + + +
      +Click the jsMath button or ALT-click +on mathematics to reopen this panel. +

      +
      +
      + + + +
      +
      +jsMath Options +[help] +

      +

      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + +
      Autoselect best font
      Show font warnings
      Use image alpha channels
      Print image-font help
      Always use hi-res fonts
      Show progress messages
      Force asynchronous processing
      Don't show page until complete
      Show jsMath button
      +
      + + + + + + + + + + + + + + + + + + + + +
      Scale all mathematics to %
      +Use native TeX fonts +(download) +
      +Use image fonts +( scalable)
      +Use images for symbols only
      +Use native Unicode fonts
      Use Global mode + +
      Save settings for + +
      + + + + + + +
        + +   + + +  +
      + +
      +
      +
      +
      +

      + + + + + + + diff --git a/htdocs/jsMath/jsMath-easy-load.js b/htdocs/jsMath/jsMath-easy-load.js new file mode 100644 index 0000000000..49fe20a148 --- /dev/null +++ b/htdocs/jsMath/jsMath-easy-load.js @@ -0,0 +1,165 @@ +/* + * jsMath-easy-load.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file is used to load jsMath with one easy '); + +} else { + jsMath.Easy.tex2math = + (jsMath.Easy.processSingleDollars || + jsMath.Easy.processDoubleDollars || + jsMath.Easy.processSlashParens || + jsMath.Easy.processSlashBrackets || + jsMath.Easy.processLaTeXenvironments || + jsMath.Easy.fixEscapedDollars || + jsMath.Easy.customDelimiters); + + if (!jsMath.Setup) {jsMath.Setup = {}} + if (!jsMath.Setup.UserEvent) {jsMath.Setup.UserEvent = {}} + jsMath.Setup.UserEvent.onload = function () { + var easy = jsMath.Easy; + if (easy.tex2math) jsMath.Setup.Script("plugins/tex2math.js"); + var i; + if (easy.loadFiles) { + for (i = 0; i < easy.loadFiles.length; i++) + jsMath.Setup.Script(easy.loadFiles[i]); + } + if (easy.loadFonts) { + for (i = 0; i < easy.loadFonts.length; i++) + jsMath.Font.Load(easy.loadFonts[i]); + } + if (easy.macros) { + for (i in easy.macros) { + if (typeof(easy.macros[i]) == 'string') { + jsMath.Macro(i,easy.macros[i]); + } else { + jsMath.Macro(i,easy.macros[i][0],easy.macros[i][1]); + } + } + } + } + document.write(''+"\n"); +} + +jsMath.Easy.onload = function () { + if (jsMath.Easy.loaded) {return} else {jsMath.Easy.loaded = 1} + if (jsMath.Easy.autoloadCheck) jsMath.Autoload.Check(); + if (jsMath.Easy.tex2math) { + jsMath.Synchronize(function () { + if (jsMath.Easy.findCustomSettings) + jsMath.tex2math.Convert(document,jsMath.Easy.findCustomSettings); + if (jsMath.Easy.customDelimiters) { + var s = jsMath.Easy.customDelimiters; + jsMath.tex2math.CustomSearch(s[0],s[1],s[2],s[3]); + jsMath.tex2math.ConvertCustom(); + } + }); + } + (jsMath[jsMath.Easy.method])(); +} + +if (window.addEventListener) {window.addEventListener("load",jsMath.Easy.onload,false)} +else if (window.attachEvent) {window.attachEvent("onload",jsMath.Easy.onload)} +else {window.onload = jsMath.Easy.onload} diff --git a/htdocs/jsMath/jsMath-fallback-mac-mozilla.js b/htdocs/jsMath/jsMath-fallback-mac-mozilla.js new file mode 100644 index 0000000000..bd2d429fed --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-mac-mozilla.js @@ -0,0 +1,107 @@ +/* + * jsMath-fallback-mac-mozilla.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed by Mozilla-based browsers on the Mac + * for when the TeX fonts are not available. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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. + */ + + + +/******************************************************************** + * + * Fix the default non-TeX-font characters to work with Mozilla + * + */ + +jsMath.Update.TeXfonts({ + cmmi10: { +// '41': // leftharpoondown +// '43': // rightharpoondown + '44': {c: '˓'}, + '45': {c: '˒'}, + '47': {c: ''}, +// '92': // natural + '126': {c: ''} + }, + + cmsy10: { + '0': {c: '–', tclass: 'normal'}, + '11': {c: '/', tclass: 'normal'}, + '42': {c: '⥣'}, '43': {c: '⥥'}, + '48': {c: '', tclass: 'normal'}, + '93': {c: '∪+'}, + '104': {c: ''}, + '105': {c: ''}, + '109': {c: '⥣'} +//, '116': // sqcup +// '117': // sqcap +// '118': // sqsubseteq +// '119': // sqsupseteq + }, + + cmex10: { + '10': {c: ''}, + '11': {c: ''}, + '14': {c: '/'}, '15': {c: '\\'}, + '28': {c: ''}, + '29': {c: ''}, + '30': {c: '/'}, '31': {c: '\\'}, + '42': {c: ''}, + '43': {c: ''}, + '44': {c: '/'}, '45': {c: '\\'}, + '46': {c: '/'}, '47': {c: '\\'}, + '68': {c: ''}, + '69': {c: ''}, +// '70': // sqcup +// '71': // big sqcup + '72': {ic: .194}, '73': {ic: .444}, + '82': {tclass: 'bigop1cx', ic: .15}, '90': {tclass: 'bigop2cx', ic:.6}, + '85': {c: '∪+'}, + '93': {c: '∪+'} + } + +}); + +jsMath.Setup.Styles({ + '.typeset .symbol': "font-family: Osaka", + '.typeset .arrow1': "font-family: Osaka; position: relative; top: .125em; margin: -1px", + '.typeset .arrow2': "font-family: AppleGothic; font-size: 100%; position:relative; top: .11em; margin:-1px", + '.typeset .bigop1': "font-family: AppleGothic; font-size: 110%; position:relative; top: .9em; margin:-.05em", + '.typeset .bigop1b': "font-family: Osaka; font-size: 140%; position: relative; top: .8em; margin:-.1em", + '.typeset .bigop1c': "font-family: AppleGothic; font-size: 125%; position:relative; top: .85em; margin:-.3em", + '.typeset .bigop1cx': "font-family: 'Apple Chancery'; font-size: 125%; position:relative; top: .7em; margin:-.1em", + '.typeset .bigop2': "font-family: AppleGothic; font-size: 175%; position:relative; top: .85em; margin:-.1em", + '.typeset .bigop2b': "font-family: Osaka; font-size: 200%; position: relative; top: .75em; margin:-.15em", + '.typeset .bigop2c': "font-family: AppleGothic; font-size: 300%; position:relative; top: .75em; margin:-.35em", + '.typeset .bigop2cx': "font-family: 'Apple Chancery'; font-size: 250%; position:relative; top: .7em; margin-left:-.1em; margin-right:-.2em", + '.typeset .delim1b': "font-family: Times; font-size: 150%; position:relative; top:.8em; margin:.01em", + '.typeset .delim2b': "font-family: Times; font-size: 210%; position:relative; top:.8em; margin:.01em", + '.typeset .delim3b': "font-family: Times; font-size: 300%; position:relative; top:.75em; margin:.01em", + '.typeset .delim4b': "font-family: Times; font-size: 400%; position:relative; top:.725em; margin:.01em" +}); + + +/* + * replace \not and \joinrel with better dimensions + */ + +jsMath.Macro('not','\\mathrel{\\rlap{\\kern 3mu/}}'); +jsMath.Macro('joinrel','\\mathrel{\\kern-3mu}'); diff --git a/htdocs/jsMath/jsMath-fallback-mac-msie.js b/htdocs/jsMath/jsMath-fallback-mac-msie.js new file mode 100644 index 0000000000..97bd15eb13 --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-mac-msie.js @@ -0,0 +1,200 @@ +/* + * jsMath-fallback-mac-msie.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed by Internet Explorer on the Mac + * for when the TeX fonts are not available. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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. + */ + + + +/******************************************************************** + * + * Fix the default non-TeX-font characters to work with MSIE + * + */ + +jsMath.Update.TeXfonts({ + cmr10: { + '0': {c: 'G', tclass: 'greek'}, + '1': {c: 'D', tclass: 'greek'}, + '2': {c: 'Q', tclass: 'greek'}, + '3': {c: 'L', tclass: 'greek'}, + '4': {c: 'X', tclass: 'greek'}, + '5': {c: 'P', tclass: 'greek'}, + '6': {c: 'S', tclass: 'greek'}, + '7': {c: '¡', tclass: 'greek'}, + '8': {c: 'F', tclass: 'greek'}, + '9': {c: 'Y', tclass: 'greek'}, + '10': {c: 'W', tclass: 'greek'}, + '22': {c: '`', tclass: 'symbol3'} + }, + + cmti10: { + '0': {c: 'G', tclass: 'igreek'}, + '1': {c: 'D', tclass: 'igreek'}, + '2': {c: 'Q', tclass: 'igreek'}, + '3': {c: 'L', tclass: 'igreek'}, + '4': {c: 'X', tclass: 'igreek'}, + '5': {c: 'P', tclass: 'igreek'}, + '6': {c: 'S', tclass: 'igreek'}, + '7': {c: '¡', tclass: 'igreek'}, + '8': {c: 'F', tclass: 'igreek'}, + '9': {c: 'Y', tclass: 'igreek'}, + '10': {c: 'W', tclass: 'igreek'}, + '22': {c: '`', tclass: 'symbol3'} + }, + + cmbx10: { + '0': {c: 'G', tclass: 'bgreek'}, + '1': {c: 'D', tclass: 'bgreek'}, + '2': {c: 'Q', tclass: 'bgreek'}, + '3': {c: 'L', tclass: 'bgreek'}, + '4': {c: 'X', tclass: 'bgreek'}, + '5': {c: 'P', tclass: 'bgreek'}, + '6': {c: 'S', tclass: 'bgreek'}, + '7': {c: '¡', tclass: 'bgreek'}, + '8': {c: 'F', tclass: 'bgreek'}, + '9': {c: 'Y', tclass: 'bgreek'}, + '10': {c: 'W', tclass: 'bgreek'}, + '22': {c: '`', tclass: 'symbol3'} + }, + cmmi10: { + '0': {c: 'G', tclass: 'igreek'}, + '1': {c: 'D', tclass: 'igreek'}, + '2': {c: 'Q', tclass: 'igreek'}, + '3': {c: 'L', tclass: 'igreek'}, + '4': {c: 'X', tclass: 'igreek'}, + '5': {c: 'P', tclass: 'igreek'}, + '6': {c: 'S', tclass: 'igreek'}, + '7': {c: '¡', tclass: 'igreek'}, + '8': {c: 'F', tclass: 'igreek'}, + '9': {c: 'Y', tclass: 'igreek'}, + '10': {c: 'W', tclass: 'igreek'}, + '11': {c: 'a', tclass: 'greek'}, + '12': {c: 'b', tclass: 'greek'}, + '13': {c: 'g', tclass: 'greek'}, + '14': {c: 'd', tclass: 'greek'}, + '15': {c: 'e', tclass: 'greek'}, + '16': {c: 'z', tclass: 'greek'}, + '17': {c: 'h', tclass: 'greek'}, + '18': {c: 'q', tclass: 'greek'}, + '19': {c: 'i', tclass: 'greek'}, + '20': {c: 'k', tclass: 'greek'}, + '21': {c: 'l', tclass: 'greek'}, + '22': {c: 'm', tclass: 'greek'}, + '23': {c: 'n', tclass: 'greek'}, + '24': {c: 'x', tclass: 'greek'}, + '25': {c: 'p', tclass: 'greek'}, + '26': {c: 'r', tclass: 'greek'}, + '27': {c: 's', tclass: 'greek'}, + '28': {c: 't', tclass: 'greek'}, + '29': {c: 'u', tclass: 'greek'}, + '30': {c: 'f', tclass: 'greek'}, + '31': {c: 'c', tclass: 'greek'}, + '32': {c: 'y', tclass: 'greek'}, + '33': {c: 'w', tclass: 'greek'}, +// '41': // leftharpoondown +// '43': // rightharpoondown +// '44': // hook left +// '45': // hook right +// '92': // natural + '94': {c: ''}, + '95': {c: ''} +// '127': // half-circle down accent? + }, + + cmsy10: { + '0': {c: '–', tclass: 'normal'}, + '11': {c: '/', tclass: 'normal'}, + '16': {c: '', tclass: 'normal'}, + '48': {c: ''}, + '93': {c: '∪+'}, + '96': {c: '|', tclass: 'normal'}, + '104': {c: ''}, + '105': {c: ''}, + '109': {c: '⇑'}, + '110': {c: '\\', d:0, tclass: 'normal'} +// '111': // wr +//, '113': // amalg +// '116': // sqcup +// '117': // sqcap +// '118': // sqsubseteq +// '119': // sqsupseteq + }, + + cmex10: { + '10': {c: ''}, + '11': {c: ''}, + '14': {c: '/'}, '15': {c: '\\'}, + '28': {c: ''}, + '29': {c: ''}, + '30': {c: '/'}, '31': {c: '\\'}, + '42': {c: ''}, + '43': {c: ''}, + '44': {c: '/'}, '45': {c: '\\'}, + '46': {c: '/'}, '47': {c: '\\'}, + '68': {c: ''}, + '69': {c: ''}, +// '70': // sqcup +// '71': // big sqcup + '72': {ic: 0}, '73': {ic: 0}, + '82': {tclass: 'bigop1cx', ic: .15}, '90': {tclass: 'bigop2cx', ic:.6}, + '85': {c: '∪+'}, + '93': {c: '∪+'}, +// '96': // coprod +// '97': // big coprod + '98': {c: '︿', h: 0.722, w: .58, tclass: 'wide1'}, + '99': {c: '︿', h: 0.722, w: .58, tclass: 'wide2'}, + '100': {c: '︿', h: 0.722, w: .58, tclass: 'wide3'}, + '101': {c: '~', h: 0.722, w: .42, tclass: 'wide1a'}, + '102': {c: '~', h: 0.8, w: .73, tclass: 'wide2a'}, + '103': {c: '~', h: 0.8, w: 1.1, tclass: 'wide3a'} + } + +}); + +jsMath.Setup.Styles({ + '.typeset .arrow1': "font-family: Osaka; position: relative; top: .125em; margin: -1px", + '.typeset .arrow2': "font-family: Osaka; position: relative; top: .1em; margin:-1px", + '.typeset .bigop1': "font-family: Symbol; font-size: 110%; position:relative; top: .8em; margin:-.05em", + '.typeset .bigop1b': "font-family: Symbol; font-size: 140%; position: relative; top: .8em; margin:-.1em", + '.typeset .bigop1c': "font-family: Osaka; font-size: 125%; position:relative; top: .85em; margin:-.3em", + '.typeset .bigop1cx': "font-family: 'Apple Chancery'; font-size: 125%; position:relative; top: .7em; margin:-.1em", + '.typeset .bigop2': "font-family: Symbol; font-size: 175%; position:relative; top: .8em; margin:-.07em", + '.typeset .bigop2a': "font-family: Baskerville; font-size: 175%; position: relative; top: .65em", + '.typeset .bigop2b': "font-family: Symbol; font-size: 175%; position: relative; top: .8em; margin:-.07em", + '.typeset .bigop2c': "font-family: Osaka; font-size: 230%; position:relative; top: .85em; margin:-.35em", + '.typeset .bigop2cx': "font-family: 'Apple Chancery'; font-size: 250%; position:relative; top: .6em; margin-left:-.1em; margin-right:-.2em", + '.typeset .delim1b': "font-family: Times; font-size: 150%; position:relative; top:.8em", + '.typeset .delim2b': "font-family: Times; font-size: 210%; position:relative; top:.75em;", + '.typeset .delim3b': "font-family: Times; font-size: 300%; position:relative; top:.7em;", + '.typeset .delim4b': "font-family: Times; font-size: 400%; position:relative; top:.65em;", + '.typeset .symbol3': "font-family: Symbol", + '.typeset .wide1': "font-size: 50%; position: relative; top:-1.1em", + '.typeset .wide2': "font-size: 80%; position: relative; top:-.7em", + '.typeset .wide3': "font-size: 125%; position: relative; top:-.5em", + '.typeset .wide1a': "font-size: 75%; position: relative; top:-.5em", + '.typeset .wide2a': "font-size: 133%; position: relative; top: -.15em", + '.typeset .wide3a': "font-size: 200%; position: relative; top: -.05em", + '.typeset .greek': "font-family: Symbol", + '.typeset .igreek': "font-family: Symbol; font-style:italic", + '.typeset .bgreek': "font-family: Symbol; font-weight:bold" +}); diff --git a/htdocs/jsMath/jsMath-fallback-mac.js b/htdocs/jsMath/jsMath-fallback-mac.js new file mode 100644 index 0000000000..f1a3749a31 --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-mac.js @@ -0,0 +1,29 @@ +/* + * jsMath-fallback-mac.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser on the Mac. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2010 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.Script.Uncompress([ + ['jsMath.Add(jsMath.TeX,{cmr10',':[{c:"Γ",','tclass:"greek"},{c:"&','Delta;",',2,'Theta;",',2,'Lambda;",',2,'Xi;",',2,'Pi;",',2,'Sigma;",',2,'Upsilon;",',2,'Phi;",',2,'Psi;",',2,'Omega;",','tclass:"','greek','"},{c:"','ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"normal"},{c',':"fi",',28,':"fl",',28,':"ffi",',28,':"ffl",',28,':"ı',';",a:0,',28,':"j",d:0.2,',28,':"`",',22,'accent','"},{c:"&#','xB4;",',22,44,45,'x2C7;",',22,44,45,'x2D8;",',22,44,'"},{c:\'ˉ',';\',',22,44,45,'x2DA;",',22,44,45,'x0327;",',28,':"ß",',28,':"æ',38,28,':"œ',38,28,':"ø",',28,':"Æ",',28,':"Œ",',28,':"Ø",',28,':"?",krn:{"108":-0.278,"76":-0.319},',28,':"!",lig:{"96":60},',28,':"”",',28,':"#",',28,':"$",',28,':"%",',28,':"&",',28,':"’",krn:{"63":0.111,"33":0.','111},','lig:{"39":34},',28,':"(",','d:0.2,',28,':")",',105,28,':"*",',28,':"+",','a:0.1,',28,':",",a:-0.3,d:0.2,','w:0.278,',28,':"-",a:0,lig:{"45":123},',28,':".",a:-0.','25,',28,':"/",',28,':"0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,':":",',28,':";",',28,':"¡",',28,':"=",a:0,d:-0.1,',28,':"¿",',28,':"?",lig:{"96":62},',28,':"@",',28,':"A','",krn:{"','116','":-0.0278,"','67',162,'79',162,'71',162,'85',162,'81',162,'84','":-0.0833,"','89',174,'86','":-0.111',',"87":-0.',101,28,':"B",',28,':"C",',28,':"D',160,'88',162,'87',162,'65',162,'86',162,'89','":-0.0278','},',28,':"E",',28,':"F',160,'111',174,'101',174,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',162,'67',162,'71',162,'81',197,'},',28,':"G",',28,':"H",',28,':"I',160,'73":','0.0278','},',28,':"J",',28,':"K',160,'79',162,'67',162,'71',162,'81',197,'},',28,':"L',160,'84',174,'89',174,'86',178,179,101,28,':"M",',28,':"N",',28,':"O',160,'88',162,'87',162,'65',162,'86',162,'89',197,'},',28,':"P',160,'65',174,'111',162,'101',162,'97',162,'46',174,'44":-0.0833},',28,':"Q",','d:0.1,',28,':"R',160,'116',162,'67',162,'79',162,'71',162,'85',162,'81',162,'84',174,'89',174,'86',178,179,101,28,':"S",',28,':"T',160,'121',162,'101',174,'111',209,'0833,"117":-0.0833','},',28,':"U",',28,':"V",','ic:0.0139,krn:{"','111',174,'101',174,'117',209,'111,"79',162,'67',162,'71',162,'81',197,'},',28,':"W",',329,'111',174,'101',174,'117',209,'111,"79',162,'67',162,'71',162,'81',197,'},',28,':"X',160,'79',162,'67',162,'71',162,'81',197,'},',28,':"Y",ic:0.025,','krn:{"101',174,'111',209,323,'},',28,':"Z",',28,':"[",',288,28,':"“",',28,':"]",',288,28,':"ˆ",',22,44,45,'x2D9;",',22,44,45,'x2018;",lig:{"96":92},',28,':"a','",a:0,krn:{"','118',162,'106','":0.0556,"','121',162,'119',197,'},',28,':"b',160,'101','":0.0278,"','111',419,'120',162,'100',419,'99',419,'113',419,'118',162,'106',409,'121',162,'119',197,'},',28,':"c',405,'104',162,'107',197,'},',28,':"d",',28,':"e",a:0,',28,':"f',26,'12,"102":11,"108":13},',28,':"g','",a:0,d:0.2,',329,'106":',227,'},',28,':"h',160,'116',162,'117',162,'98',162,'121',162,'118',162,'119',197,'},',28,':"i",',28,40,28,':"k',160,'97','":-0.0556,"','101',162,'97',162,'111',162,'99',197,'},',28,':"l",',28,':"m',405,'116',162,'117',162,'98',162,'121',162,'118',162,'119',197,'},',28,':"n',405,'116',162,'117',162,'98',162,'121',162,'118',162,'119',197,'},',28,':"o',405,'101',419,'111',419,'120',162,'100',419,'99',419,'113',419,'118',162,'106',409,'121',162,'119',197,'},',28,':"p',457,377,419,'111',419,'120',162,'100',419,'99',419,'113',419,'118',162,'106',409,'121',162,'119',197,'},',28,':"q',457,28,':"','r",a:0,',28,':"s",a:0,',28,':"t',160,'121',162,'119',197,'},',28,':"u',405,'119',197,'},',28,':"v",a:0,',329,'97',486,'101',162,'97',162,'111',162,'99',197,'},',28,':"w",a:0,',329,'101',162,'97',162,'111',162,'99',197,'},',28,':"x",a:0,',28,':"y',457,329,'111',162,'101',162,'97',162,'46',174,'44":-0.0833},',28,':"z",a:0,',28,':"–',';",a:0.1,','ic:0.0278,','lig:{"45":124},',28,':"—',645,646,28,':"˝",',22,44,45,'x2DC;",',22,44,45,'xA8;",',22,44,'"}],cmmi10',1,'ic:0.139',',krn:{"61":-0.0556,"59":-0.111,"58":-0.111,"127":0.','0833},',22,'igreek"},{c:"&',3,'krn:{"127":0.','167},',22,670,5,'ic:',227,',krn:{"127":0.','0833},',22,670,7,672,'167},',22,670,9,'ic:0.0757',679,'0833},',22,670,11,'ic:0.0812,krn:{"61',486,'59":-0.0556,"58":-0.0556,"127":0.','0556},',22,670,13,'ic:0.0576',679,'0833},',22,670,15,'ic:0.139',667,'0556},',22,670,17,672,'0833},',22,670,19,'ic:0.11,krn:{"61',486,697,'0556},',22,670,21,'ic:0.0502',679,'0833},',22,670,'alpha',645,'ic:0.0037',679,'0278},',2,'beta',';",d:0.1,','ic:0.0528',679,'0833},',2,'gamma',645,288,'ic:0.0556,',2,'delta;",ic:0.0378,krn:{"',697,'0556},',2,'epsilon;",a:0',679,'0556},',22,'lucida"},{c:"&zeta',738,'ic:0.0738',679,'0833},',2,'eta',645,288,'ic:0.0359',679,'0556},',2,'theta;",ic:',227,679,'0833},',2,'iota;",a:0.1',679,'0556},',2,'kappa',645,2,'lambda;",',2,'mu',645,'d:0.1',679,'0278},',2,'nu',645,'ic:0.0637,krn:{"',697,'0278},',2,'xi',738,'ic:0.046',679,101,2,'pi',645,'ic:0.0359,',2,'rho',645,'d:0.1',679,'0833},',2,'sigma',645,803,'krn:{"59',486,'58":-0.0556','},',2,'tau',645,'ic:0.113,krn:{"',697,'0278},',2,'upsilon',645,'ic:0.0359',679,'0278},',2,'phi;",a:0.3,d:0.1',679,'0833},',2,'chi',645,'d:0.1',679,'0556},',2,'psi;",','a:0.3,d:0.1,','ic:0.0359',679,101,2,'omega',645,803,2,752,'.1',679,'0833},',22,'greek',45,'x3D1;",',672,'0833},',22,'greek',45,'x3D6',645,646,22,'lucida',45,'x3F1',645,'d:0.2',679,'0833},',22,'lucida',45,'x3C2',645,'d:0.2,ic:0.','0799',679,'0833},',22,'greek',45,'x3D5',645,'d:0.1',679,'0833},',22,'greek',45,'x21BC',';",a:0,d:-0.','2,',22,'harpoon',45,'x21BD',896,'1,',22,'harpoon',45,'x21C0',896,'2,',22,'harpoon',45,'x21C1',896,'1,',22,'harpoon',57,'font-size: 133%; ',58,':.1em; margin:-.2em; left:-.05em">&#','x02D3',60,113,22,'lucida',57,919,58,921,'x02D2',60,113,22,'lucida',57,'font-size:50%">&#','x25B7',';
      \',a:0,',22,'symbol',57,937,'x25C1',939,22,941,24,'0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,120,'3,',28,115,28,':"<',645,28,':\'/\',',288,'krn:{"1',486,'65',486,'77',486,'78',486,'89',409,'90":-0.0556},',28,':">',645,28,977,937,'x2605',939,'tclass:"symbol"},{c:"&#','x2202;",ic:0.0556',679,'0833},',28,':"A",',672,'139','},tclass:"italic"},{c:"','B",ic:0.0502',679,'0833',1011,'C",ic:0.0715,krn:{"61',162,697,'0833',1011,'D",ic:',227,679,'0556',1011,'E",ic:0.0576',679,'0833',1011,'F",ic:0.139',667,'0833',1011,'G",',672,'0833',1011,'H",ic:0.0812,krn:{"61',486,697,'0556',1011,'I",ic:0.0785',679,'111',1011,'J",ic:0.0962',667,'167',1011,'K",ic:0.0715,krn:{"61',486,697,'0556',1011,'L",',672,'0278',1011,'M','",ic:0.109,krn:{"','61',486,697,'0833',1011,'N',1061,'61',174,'61',162,697,'0833',1011,'O",ic:',227,679,'0833',1011,'P",ic:0.139',667,'0833',1011,'Q",d:0.2',679,'0833',1011,'R",ic:0.00773',679,'0833',1011,'S",ic:0.0576,krn:{"61',486,697,'0833',1011,'T','",ic:0.139,krn:{"','61',162,697,'0833',1011,'U',1061,'59',178,',"58',178,',"61',486,'127":',227,1011,'V",ic:0.222,krn:{"59','":-0.167,"','58',1117,'61',178,1011,'W',1099,'59',1117,'58',1117,'61',178,1011,'X",ic:0.0785,krn:{"61',174,'61',162,697,'0833',1011,'Y",ic:0.222,krn:{"59',1117,'58',1117,'61',178,1011,'Z",ic:0.0715,krn:{"61',486,697,'0833},',22,'italic',45,'x266D;",',22,'symbol2',45,'x266E;",',22,'symbol2',45,'x266F;",',22,'symbol2',57,'position: relative; top',':.5em">⌣',939,'d:-0.1,',28,977,1165,':-.3em">⌢',939,'d:-0.1,',28,977,'margin:-.','2em">ℓ',60,672,101,22,941,24,'a",a:0,',22,'italic"},{c:"','b",',22,1187,'c",a:0',679,'0556',1011,'d',160,'89',409,'90',486,'106',178,',"102',1117,'127":0.167',1011,'e",a:0',679,'0556',1011,'f",',880,'108,krn:{"',697,'167',1011,'g',457,'ic:0.0359',679,'0278',1011,'h',160,'127',197,1011,'i",',22,1187,'j",',880,'0572,krn:{"59',486,816,1011,'k",ic:0.0315,',22,1187,'l",ic:0.0197',679,'0833',1011,'m",a:0,',22,1187,'n",a:0,',22,1187,'o",a:0',679,'0556',1011,'p",a:0,d:0.2',679,'0833',1011,'q',457,'ic:0.0359',679,'0833',1011,583,646,'krn:{"',697,'0556',1011,'s",a:0',679,'0556',1011,'t",',672,'0833',1011,'u",a:0',679,'0278',1011,'v",a:0,ic:0.0359',679,'0278',1011,'w",a:0,ic:0.0269',679,'0833',1011,'x",a:0',679,'0278',1011,'y',457,'ic:0.0359',679,'0556',1011,'z",a:0,ic:0.044',679,'0556},',22,'italic',45,'x131;",a:0',679,'0278},',22,'italic',45,'x237',38,'d:0.2',679,'0833},',22,'italic',45,'x2118;",d:0',679,101,22,'asymbol',57,'position:relative; left: .4em; top: -.8em; ',978,': 50%">→',60,'ic:0.154,',1003,'x0311;",ic:0.399,',22,'normal"}],cmsy10:[{c:"−',645,1003,'xB7',896,'2,',1003,'xD7',38,22,941,57,58,':.3em">*',939,1003,'xF7',38,1003,'x25CA;",',22,'lucida',45,'xB1',645,1003,'x2213;",',1003,'x2295;",',1003,'x2296;",',1003,'x2297;",',1003,'x2298;",',1003,'x2299;",',22,'symbol3',45,'x25EF;",',22,941,57,58,':.25em;">°',60,'a:0.3,d:-0.3,',1003,'x2022;",a:0.2,d:-0.25,',1003,'x224D',645,1003,'x2261',645,1003,'x2286;",',1003,'x2287;",',1003,'x2264;",',1003,'x2265;",',1003,'x2AAF;",',1003,'x2AB0;",',22,941,'"},{c:"~",a:0,d:-0.2,',28,':"≈',645,'d:-0.1,',1003,'x2282;",',1003,'x2283;",',1003,'x226A;",',1003,'x226B;",',1003,'x227A;",',1003,'x227B;",',1003,'x2190',896,'15,',22,'arrows',45,'x2192',896,'15,',22,'arrows',45,'x2191',';",h:0.85,',22,'arrow1a',45,'x2193',1435,22,'arrow1a',45,'x2194',38,22,'arrow1',45,'x2197',1435,288,22,'arrows',45,'x2198',1435,288,22,'arrows',45,'x2243',645,1003,'x21D0',645,22,'arrows',45,'x21D2',645,22,'arrows',45,'x21D1;",h:0.7,',22,'asymbol"},{c:"&#','x21D3;",h:0.7,',22,1476,'x21D4',645,22,'arrows',45,'x2196',1435,288,22,'arrows',45,'x2199',1435,288,22,'arrows',45,'x221D',645,'d:-0.1,',22,941,57,919,'margin-right',': -.1em; ',1165,':.4em">′',939,22,'lucida',45,'x221E',645,1003,'x2208;",',1003,'x220B;",',1003,'x25B3;",',1003,'x25BD;",',22,941,'"},{c:"/",',22,941,57,978,':50%; ',58,':-.3em; ',1504,':-.2em">|∖',60,842,22,'lucida',45,'x2240;",',22,'asymbol',57,58,': .86em">√',60,'h:0.04,d:0.9,',22,'lucida',45,'x2210',738,1003,'x2207;",',1003,'x222B;",h:1,',288,'ic:0.111,',22,'root',45,'x2294;",',1003,'x2293;",',1003,'x2291;",',1003,'x2292;",',1003,'xA7',738,28,':"†',738,28,':"‡',738,28,':"¶",',842,22,'lucida',45,'x2663;",',1003,'x2666;",',1003,'x2665;",',1003,'x2660;",',22,941,'"}],cmex10:[{c',104,'h:0.04,d:1.16,n:','16,',22,'delim1"},{c',107,1829,'17,',22,1832,386,1829,'104,',22,1832,391,1829,'105,',22,'delim1',45,1714,'",',1829,'106,',22,'delim1a',45,'x23A6;",',1829,'107,',22,'delim1a',45,'x23A1;",',1829,'108,',22,'delim1a',45,'x23A4;",',1829,'109,',22,'delim1a"},{c:"{",',1829,'110,',22,1832,1737,1829,'111,',22,'delim1',45,'x3008;",',1829,'68,',22,'delim1c',45,'x3009;",',1829,'69,',22,'delim1c"},{c:"|",','h:0.7,d:0.15,delim:{rep:','12},',22,'vertical1',1752,1894,'13},',22,'vertical1',45,'x2215;",',1829,'46,',22,'delim1b',45,'x2216;",',1829,'47,',22,'delim1b','"},{c:"(",','h:0.04,d:1.76,n:','18,',22,'delim2"},{c',107,1916,'19,',22,'delim2',1915,'h:0.04,d:2.36,n:','32,',22,'delim3"},{c',107,1926,'33,',22,1929,386,1926,'34,',22,1929,391,1926,'35,',22,'delim3',45,1714,';",',1926,'36,',22,'delim3a',45,'x23A6;",',1926,'37,',22,'delim3a',45,'x23A1;",',1926,'38,',22,'delim3a',45,'x23A4;",',1926,'39,',22,'delim3a',57,'margin: -.1em','">{}{}{\',',23,'26,',1,224,'"},{',255,256,'">}\',',23,'27,',1,224,'"},{c:\'\',h:0.','04,d:1.16,n:113,',1,'root',270,': 190',272,'925em">√\',',23,'114,',1,277,270,': 250',272,'925em',274,'06,d:2.36,n:115,',1,277,270,': 320',272,'92em',274,'08,d:2.96,n:116,',1,277,270,': 400',272,'92em',274,'1,d:3.75,n:117,',1,277,270,': 500',272,'9em',274,'12,d:4.5,n:118,',1,277,270,': 625',272,'9em',274,'14,d:5.7,',1,277,270,':130%">‖\',h:0.9,delim:{top:126,bot:127,rep:119},',1,2,3,'x2191',';",h:0.9,d:0,delim:{','top:120,rep:63},',1,'arrow1a',3,'x2193',332,'bot:121,rep:63},',1,335,270,': 67',272,'35em','; margin-left:-.','5em">&#','x256D',';\',h:0.1,',1,'symbol',270,': 67',272,'35em','; margin-right:-.',347,'x256E',349,1,351,270,': 67',272,'35em',346,347,'x2570',349,1,351,270,': 67',272,'35em',356,347,'x256F',349,1,351,3,'x21D1',';",h:0.7,delim:{','top:126,rep:119},',1,2,3,'x21D3',384,'bot:127,rep:119},',1,2,'"}],cmti10',':[{c:"Γ",','ic:0.133,',1,'igreek"},{c:"&','Delta;",',1,398,'Theta;",','ic:0.094,',1,398,'Lambda;",',1,398,'Xi;",ic:0.153,',1,398,'Pi;",ic:0.164,',1,398,'Sigma;",','ic',':0.12,',1,398,'Upsilon;",','ic:0.111,',1,398,'Phi;",','ic:0.0599,',1,398,'Psi;",','ic:0.111,',1,398,'Omega;",','ic:0.103,',1,'igreek','"},{c:"ff','",ic:0.212,krn:{"39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":','14,"108":15},','tclass:"italic"},{c',':"fi','",ic:0.103,',439,':"fl',441,439,':"ffi',441,439,':"ffl',441,439,':"ı",a:0,','ic:0.','0767,',439,':"j",d:0.2,','ic:0.0374,',439,':"`",',1,'iaccent',3,'xB4;",','ic:0.0969,',1,461,3,'x2C7;",','ic:0.083,',1,461,3,'x2D8;",','ic:0.108,',1,461,3,'x2C9;",',433,1,461,3,'x2DA;",',1,461,'"},{c:"?",','d:0.17,w:0.46,',439,':"ß",','ic:0.105,',439,':"æ",a:0,','ic:0.0751,',439,':"œ",a:0,',493,439,':"ø",','ic:0.0919,',439,':"Æ",','ic',417,439,':"Œ",','ic',417,439,':"Ø",',403,439,':"?",krn:{"108":-0.','256,"76":-0.321},',439,':"!",','ic:0.124,lig:{"96":','60},',439,':"”",','ic:0.0696,',439,':"#",ic:0.0662,',439,':"$",',439,':"%",ic:0.136,',439,':"&",','ic:0.0969,',439,':"’",','ic:0.124,','krn:{"63":0.','102,"33":0.102},lig:{"39":34},',439,':"(",d:0.2,','ic:0.162,',439,':")",d:0.2,','ic:0.0369,',439,':"*",ic:0.149,',439,':"+",a:0.1,','ic:0.0369,',439,':",",a:-0.3,d:0.2,w:0.278,',439,':"-",a:0,ic:0.0283',',lig:{"45":','123},',439,':".",a:-0.25,',439,':"/",ic:0.162,',439,':"0",ic:0.136,',439,':"1",ic:0.136,',439,':"2",ic:0.136,',439,':"3",ic:0.136,',439,':"4",ic:0.136,',439,':"5",ic:0.136,',439,':"6",ic:0.136,',439,':"7",ic:0.136,',439,':"8",ic:0.136,',439,':"9",ic:0.136,',439,':":",ic:0.0582,',439,':";",ic:0.0582,',439,':"¡",','ic:0.0756,',439,':"=",a:0,d:-0.1,','ic:0.0662,',439,':"¿",',439,':"?",','ic:0.122,','lig:{"96":','62},',439,':"@",ic:0.096,',439,':"A",','krn:{"110":-0.0256,"108":-0.0256,"114":-0.0256,"117":-0.0256,"109":-0.0256,"116":-0.0256,"105":-0.0256,"67":-0.0256,"79":-0.0256,"71":-0.0256,"104":-0.0256,"98":-0.0256,"85":-0.0256,"107":-0.0256,"118":-0.0256,"119":-0.0256,"81":-','0.0256,"84','":-0.0767,"','89',599,'86','":-0.102,"','87',603,'101','":-0.0511,"','97',607,'111',607,'100',607,'99',607,'103',607,'113','":-0.0511','},',439,':"B',441,439,':"C",','ic:0.145,',439,':"D",',403,'krn:{"88','":-0.0256,"','87',631,'65',631,'86',631,'89":-0.','0256},',439,':"E",ic',417,439,':"F','",ic:0.133,krn:{"','111',599,'101',599,'117','":-0.0767,"114":-0.0767,"97":-0.0767,"','65',603,'79',631,'67',631,'71',631,'81":-0.0256','},',439,':"G",ic:0.0872,',439,':"H",ic:0.164,',439,':"I",ic:0.158,',439,':"J",ic:0.14,',439,':"K",',626,'krn:{"79',631,'67',631,'71',631,660,'},',439,':"L",krn:{"84',599,'89',599,'86',603,'87',603,'101',607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"M",ic:0.164,',439,':"N",ic:0.164,',439,':"O",',403,'krn:{"88',631,'87',631,'65',631,'86',631,638,'0256},',439,':"P',441,'krn:{"65":-0.0767},',439,':"Q",d:0.1,',403,439,':"R",ic:0.0387,',597,'0.0256,"84',599,'89',599,'86',603,'87',603,'101',607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"S",ic',417,439,':"T',645,'121',599,'101',599,'111',651,'117',599,'65":-0.0767},',439,':"U",ic:0.164,',439,':"V",ic:0.','184,krn:{"','111',599,'101',599,'117',651,'65',603,'79',631,'67',631,'71',631,660,'},',439,':"W",ic:0.',774,'65":-0.0767},',439,':"X",ic:0.158,krn:{"79',631,'67',631,'71',631,660,'},',439,':"Y",ic:0.','194',',krn:{"101',599,'111',651,'117',599,'65":-0.0767},',439,':"Z",',626,439,':"[",d:0.1,','ic:0.188,',439,':"“",','ic:0.169,',439,':"]",d:0.1,','ic:0.105,',439,':"ˆ",','ic:0.0665,',1,461,3,'x2D9;",','ic:0.118,',1,461,3,'x2018;",',516,'92},',439,':"a','",a:0,ic:0.',454,439,':"b",ic:0.0631',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"c',842,'0565',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"d',441,'krn:{"108":','0.0511},',439,':"e',842,'0751',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"f',437,'12,"102":11,"108":13},',439,':"g','",a:0,d:0.2,ic:0.','0885,',439,':"h",ic:0.',454,439,':"i",ic:0.102,',439,456,626,439,':"k",',474,439,':"l',441,883,'0.0511},',439,':"m',842,454,439,':"n',842,454,'krn:{"39":-0.102},',439,':"o',842,'0631',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"p',910,'0631',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"q',910,'0885,',439,':"r',842,'108',807,607,'97',607,'111',607,'100',607,'99',607,'103',607,'113',619,'},',439,':"s',842,'0821,',439,':"t",ic:0.0949,',439,':"u',842,454,439,':"v',842,'108,',439,':"w',842,1011,883,'0.0511},',439,':"x',842,'12,',439,':"y',910,'0885,',439,':"z',842,'123,',439,':"–",a:0.1,ic:0.','0921',550,'124},',439,':"—",a:0.1,ic:0.','0921,',439,':"˝",',590,1,461,3,'x2DC;",','ic:0.116,',1,461,3,'xA8;",',1,461,'"}],cmbx10',395,1,'bgreek"},{c:"&','Delta;",',1,1055,402,1,1055,'Lambda;",',1,1055,'Xi;",',1,1055,'Pi;",',1,1055,415,1,1055,420,1,1055,424,1,1055,428,1,1055,432,1,'bgreek','"},{c:"ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"bold"},{c',':"fi",',1089,':"fl",',1089,':"ffi",',1089,':"ffl",',1089,452,1089,456,1089,':"`",',1,'baccent',3,463,1,1104,3,468,1,1104,3,473,1,1104,3,478,1,1104,3,'x2DA;",',1,1104,486,1089,489,1089,492,1089,495,1089,498,1089,501,1089,505,1089,509,1089,512,'278,"76":-0.319},',1089,515,591,'60},',1089,519,1089,':"#",',1089,':"$",',1089,':"%",',1089,528,1089,531,533,'111,"33":0.111},lig:{"39":34},',1089,536,1089,539,1089,':"*",',1089,544,1089,':",",a:-0.3,d:0.2,w:0.278,',1089,':"-",a:0',550,'123},',1089,':".",a:-0.25,',1089,':"/",',1089,':"0",',1089,':"1",',1089,':"2",',1089,':"3",',1089,':"4",',1089,':"5",',1089,':"6",',1089,':"7",',1089,':"8",',1089,':"9",',1089,':":",',1089,':";",',1089,581,1089,584,1089,':"¿",',1089,589,591,'62},',1089,':"@",',1089,':"A",krn:{"116','":-0.0278,"','67',1217,'79',1217,'71',1217,'85',1217,'81',1217,'84','":-0.0833,"','89',1229,'86":-0.','111,"87":-0.111},',1089,':"B",',1089,625,1089,':"D",krn:{"88',1217,'87',1217,'65',1217,'86',1217,638,'0278},',1089,':"E",',1089,':"F",krn:{"111',1229,'101',1229,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',1217,'67',1217,'71',1217,'81":-0.0278','},',1089,':"G",',1089,':"H",',1089,':"I",krn:{"73":0.0278},',1089,':"J",',1089,':"K",krn:{"79',1217,'67',1217,'71',1217,1264,'},',1089,':"L",krn:{"84',1229,'89',1229,1232,'111,"87":-0.111},',1089,':"M",',1089,':"N",',1089,':"O",krn:{"88',1217,'87',1217,'65',1217,'86',1217,638,'0278},',1089,':"P",krn:{"65',1229,'111',1217,'101',1217,'97',1217,'46',1229,'44":-0.0833},',1089,727,1089,':"R",krn:{"116',1217,'67',1217,'79',1217,'71',1217,'85',1217,'81',1217,'84',1229,'89',1229,1232,'111,"87":-0.111},',1089,':"S",',1089,':"T",krn:{"121',1217,'101',1229,'111',1257,'0833,"117":-0.0833','},',1089,':"U",',1089,773,'0139,krn:{"','111',1229,'101',1229,'117',1257,'111,"79',1217,'67',1217,'71',1217,1264,'},',1089,792,1353,'111',1229,'101',1229,'117',1257,'111,"79',1217,'67',1217,'71',1217,1264,'},',1089,':"X",krn:{"79',1217,'67',1217,'71',1217,1264,'},',1089,805,'025',807,1229,'111',1257,1347,'},',1089,815,1089,818,1089,821,1089,824,1089,827,1,1104,3,832,1,1104,3,837,591,'92},',1089,':"a','",a:0,krn:{"','118',1217,'106":0.','0556,"121',1217,'119":-0.','0278},',1089,':"b",krn:{"101','":0.0278,"','111',1435,'120',1217,'100',1435,'99',1435,'113',1435,'118',1217,1428,'0556,"121',1217,1431,'0278},',1089,':"c',1425,'104',1217,'107":-0.0278},',1089,':"d",',1089,':"e",a:0,',1089,':"f',1087,'12,"102":11,"108":13},',1089,':"g',910,1353,1428,'0278},',1089,':"h",krn:{"116',1217,'117',1217,'98',1217,'121',1217,'118',1217,1431,'0278},',1089,':"i",',1089,456,1089,':"k",krn:{"97":-0.0556,"101',1217,'97',1217,'111',1217,'99":-0.','0278},',1089,':"l",',1089,':"m',1425,'116',1217,'117',1217,'98',1217,'121',1217,'118',1217,1431,'0278},',1089,':"n',1425,'116',1217,'117',1217,'98',1217,'121',1217,'118',1217,1431,'0278},',1089,':"o",a:0',807,1435,'111',1435,'120',1217,'100',1435,'99',1435,'113',1435,'118',1217,1428,'0556,"121',1217,1431,'0278},',1089,':"p",a:0,d:0.2',807,1435,'111',1435,'120',1217,'100',1435,'99',1435,'113',1435,'118',1217,1428,'0556,"121',1217,1431,'0278},',1089,':"q",a:0,d:0.2,',1089,':"r",a:0,',1089,':"s",a:0,',1089,':"t",krn:{"121',1217,1431,'0278},',1089,':"u',1425,1431,'0278},',1089,':"v',842,1353,'97":-0.0556,"101',1217,'97',1217,'111',1217,1497,'0278},',1089,':"w',842,'0139',807,1217,'97',1217,'111',1217,1497,'0278},',1089,':"x",a:0,',1089,':"y',910,1353,'111',1217,'101',1217,'97',1217,'46',1229,'44":-0.0833},',1089,':"z",a:0,',1089,1031,'0278',550,'124},',1089,1036,'0278,',1089,1039,1,1104,3,1044,1,1104,3,'xA8;",',1,1104,'"}]});','jsMath.Setup.Styles({".typeset .','cmr10','":"font-family: ','serif','",".typeset .','italic":"font-style: italic',1655,'bold":"font-weight: bold',1655,'lucida','":"font-family: \'','Lucida Grande','\'",".typeset .',2,'":"font-family: \'Apple Symbols\'; font-size: ','125','%",".typeset .','cal',1661,'Apple Chancery',1663,'arrows','":"font-family: \'Hiragino Mincho Pro',1663,'Arrows',1661,'AppleGothic',1663,'arrow1',1673,'\'; ','position: relative; top',': .075em; margin: -1px',1655,335,1673,'\'; margin:-.','3em',1655,'harpoon',1653,1677,'; ','font-size: ','90',1667,351,1673,1663,'symbol2',1673,1687,'2em',1655,'symbol3',1653,1677,1655,'delim1',1653,'Times; ',1694,'133',272,'7em',1655,'delim1a',1665,'125',272,'75em',1655,'delim1b',1673,'\'; ',1694,'133',272,'8em; ',256,1655,'delim1c',1653,'Symbol; ',1694,'120',272,'8em',';",".typeset .',224,1653,'Baskerville','; ',1694,'180',272,1721,1655,235,1665,'175',272,'8em',1655,'delim2b',1673,'\'; ',1694,'190',272,'8em; ',256,1655,26,1653,'Symbol; ',1694,'167',272,'8em',1739,'delim3',1653,1742,'; ',1694,'250',272,'725em',1655,'delim3a',1665,'240',272,'78em',1655,'delim3b',1673,'\'; ',1694,'250',272,'8em; ',256,1655,'delim3c',1653,351,'; ',1694,'240',272,'775em',1739,'delim4',1653,1742,'; ',1694,'325',272,'7em',1655,'delim4a',1665,'310',272,1721,1655,'delim4b',1673,'\'; ',1694,'325',272,'8em; ',256,1655,'delim4c',1653,'Symbol; ',1694,'300',272,'8em',1739,'vertical',1653,'Copperplate',1655,'vertical1',1653,1839,'; ',1694,'85%; margin: .','15em',1739,'vertical2',1653,1839,'; ',1694,1846,'17em',1739,'greek',1673,'\', serif',346,'2em',356,'2em',1739,435,1673,'\', serif','; font-style:italic',346,'2em',356,'1em',1655,1085,1673,'\', serif','; font-weight:bold',1655,37,1673,'\'; ',1694,'133%; ',1682,': .7em; margin:-.','05em',1655,92,1653,1742,'; ',1694,'100%; ',1682,': .775em',1739,110,1673,'\'; ',1694,'160%; ',1682,': .67em','; margin:-.1em',1655,48,1665,'125%; ',1682,': .',1721,1904,1739,42,1673,'\'; ',1694,'200%; ',1682,': .6em; margin:-.07em',1655,139,1653,1742,'; ',1694,'175%; ',1682,1903,1739,155,1673,'\'; ',1694,'270%; ',1682,': .62em',1904,1655,52,1665,'250%; ',1682,1885,'17em',1739,194,'":"',1694,'67%; ',1682,':-.8em',1655,199,'":"',1694,'110%; ',1682,':-.5em',1655,204,'":"',1694,'175%; ',1682,':-.32em',1655,210,'":"',1694,'75%; ',1682,1959,1655,215,'":"',1694,'133%; ',1682,': -.15em',1655,'wide3a":"',1694,'200%; ',1682,': -.05em',1655,277,1653,1742,1739,'accent":"',1682,': .02em',1655,461,'":"',1682,1994,1868,1655,1104,'":"',1682,1994,1877,'"});','if(jsMath.browser=="','OmniWeb"){jsMath.Update.TeXfonts({cmsy10:{"55":{',255,1694,'75%; position:relative; left:.3em; top:-.15em',346,'3em">˫\'},"104','":{c:\'\'},"105',2015,'right:-.55em">〉\'}}});',1651,'arrow2',1653,'Symbol; ',1694,'100%; ',1682,': -.1em; margin:-1px"})}',2008,'Opera"){',1651,48,'":"margin:0pt .12em 0pt 0pt',1739,52,2031,';"})}',2008,'Mozilla','"){jsMath.Setup.Script("jsMath-fallback-mac-','mozilla.js")}',2008,'MSIE',2038,'msie.js")}jsMath.Macro("not","\\\\mathrel{\\\\rlap{\\\\kern 4mu/}}");jsMath.Box.defaultH=0.8;'] +]); diff --git a/htdocs/jsMath/jsMath-fallback-pc.js b/htdocs/jsMath/jsMath-fallback-pc.js new file mode 100644 index 0000000000..aa3bf67b3d --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-pc.js @@ -0,0 +1,29 @@ +/* + * jsMath-fallback-pc.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser on the PC. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2010 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.Script.Uncompress([ + ['jsMath.Add(jsMath.TeX,{cmr10',':[{c:"Γ",','tclass:"greek"},{c:"&','Delta;",',2,'Theta;",',2,'Lambda;",',2,'Xi;",',2,'Pi;",',2,'Sigma;",',2,'Upsilon;",',2,'Phi;",',2,'Psi;",',2,'Omega;",','tclass:"','greek','"},{c',':"ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"normal"},{c',':"fi",',28,':"fl",',28,':"ffi",',28,':"ffl",',28,':"ı',';",a:0,',28,':"j",d:0.2,',28,':"ˋ",',22,'accent','"},{c:"&#','x2CA;",',22,44,45,'x2C7;",',22,44,45,'x2D8;",',22,44,45,'x2C9;",',22,44,45,'x2DA;",',22,44,45,'x0327;",',28,':"ß",',28,':"æ',38,28,':"œ',38,28,':"ø",',28,':"Æ",',28,':"Œ",',28,':"Ø",',28,':"?",krn:{"108":-0.278,"76":-0.319},',28,':"!",lig:{"96":60},',28,':"”",',28,':"#",',28,':"$",',28,':"%",',28,':"&",',28,':"’",krn:{"63":0.111,"33":0.','111},','lig:{"39":34},',28,':"(",','d:0.2,',28,':")",',103,28,':"*",',28,':"+",a',':0.1,',28,':",",a:-','0.3,d:0.2,','w:0.278,',28,':"-",a:0,lig:{"45":123},',28,':".",a:-0.','25,',28,':"/",',28,':"0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,':":",',28,':";",',28,':"¡",',28,':"=",a:0,d:-0.1,',28,':"¿",',28,':"?",lig:{"96":62},',28,':"@",',28,':"A','",krn:{"','116','":-0.0278,"','67',161,'79',161,'71',161,'85',161,'81',161,'84','":-0.0833,"','89',173,'86','":-0.111',',"87":-0.',99,28,':"B",',28,':"C",',28,':"D',159,'88',161,'87',161,'65',161,'86',161,'89','":-0.0278','},',28,':"E",',28,':"F',159,'111',173,'101',173,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',161,'67',161,'71',161,'81',196,'},',28,':"G",',28,':"H",',28,':"I',159,'73":0.','0278','},',28,':"J",',28,':"K',159,'79',161,'67',161,'71',161,'81',196,'},',28,':"L',159,'84',173,'89',173,'86',177,178,99,28,':"M",',28,':"N",',28,':"O',159,'88',161,'87',161,'65',161,'86',161,'89',196,'},',28,':"P',159,'65',173,'111',161,'101',161,'97',161,'46',173,'44":-0.0833},',28,':"Q",',103,28,':"R',159,'116',161,'67',161,'79',161,'71',161,'85',161,'81',161,'84',173,'89',173,'86',177,178,99,28,':"S",',28,':"T',159,'121',161,'101',173,'111',208,'0833,"117":-0.0833','},',28,':"U",',28,':"V",','ic:0.0139,krn:{"','111',173,'101',173,'117',208,'111,"79',161,'67',161,'71',161,'81',196,'},',28,':"W",',328,'111',173,'101',173,'117',208,'111,"79',161,'67',161,'71',161,'81',196,'},',28,':"X',159,'79',161,'67',161,'71',161,'81',196,'},',28,':"Y",ic:0.025,','krn:{"101',173,'111',208,322,'},',28,':"Z",',28,':"[",d',111,28,':"“",',28,':"]",d',111,28,':"ˆ",',22,44,45,'x2D9;",',22,44,45,'x2018;",lig:{"96":92},',28,':"a','",a:0,krn:{"','118',161,'106":0.','0556,"121',161,'119',196,'},',28,':"b',159,'101','":0.0278,"','111',417,'120',161,'100',417,'99',417,'113',417,'118',161,407,'0556,"121',161,'119',196,'},',28,':"c',404,'104',161,'107',196,'},',28,':"d",',28,':"e",a:0,',28,':"f',26,'12,"102":11,"108":13},',28,':"g",','a:0,d:0.2,ic:0.','0139,krn:{"',407,226,'},',28,':"h',159,'116',161,'117',161,'98',161,'121',161,'118',161,'119',196,'},',28,':"i",',28,40,28,':"k',159,'97','":-0.0556,"','101',161,'97',161,'111',161,'99',196,'},',28,':"l",',28,':"m',404,'116',161,'117',161,'98',161,'121',161,'118',161,'119',196,'},',28,':"n',404,'116',161,'117',161,'98',161,'121',161,'118',161,'119',196,'},',28,':"o',404,'101',417,'111',417,'120',161,'100',417,'99',417,'113',417,'118',161,407,'0556,"121',161,'119',196,'},',28,':"p",a:0,',103,376,417,'111',417,'120',161,'100',417,'99',417,'113',417,'118',161,407,'0556,"121',161,'119',196,'},',28,':"q",a:0,',103,28,':"','r",a:0,',28,':"s",a:0,',28,':"t',159,'121',161,'119',196,'},',28,':"u',404,'119',196,'},',28,':"v",a:0,',328,'97',483,'101',161,'97',161,'111',161,'99',196,'},',28,':"w",a:0,',328,'101',161,'97',161,'111',161,'99',196,'},',28,':"x",a:0,',28,':"y",',454,455,'111',161,'101',161,'97',161,'46',173,'44":-0.0833},',28,':"z",a:0,',28,':"–',';",a:0.1,','ic:0.0278,','lig:{"45":124},',28,':"—',640,641,28,':"˝",',22,44,45,'x2DC;",',22,44,45,'xA8;",',22,44,'"}],cmmi10',1,'ic:0.139',',krn:{"61":-0.0556,"59":-0.111,"58":-0.111,"127":0.','0833},',22,'igreek"},{c:"&',3,'krn:{"127":0.','167},',22,665,5,'ic:0.',226,',krn:{"127":0.','0833},',22,665,7,667,'167},',22,665,9,'ic:0.0757',674,'0833},',22,665,11,'ic:0.0812,krn:{"61',483,'59":-0.0556,"58":-0.0556,"127":0.','0556},',22,665,13,'ic:0.0576',674,'0833},',22,665,15,'ic:0.139',662,'0556},',22,665,17,667,'0833},',22,665,19,'ic:0.11,krn:{"61',483,692,'0556},',22,665,21,'ic:0.0502',674,'0833},',22,665,'alpha',';",a:0,ic:0.','0037',674,226,'},',2,'beta;",','d:0.2,ic:0.','0528',674,'0833},',2,'gamma;",',454,'0556,',2,'delta;",ic:0.0378,krn:{"',692,'0556},',2,'epsilon;",a:0',674,'0556},',22,'lucida',24,':"ζ",',734,'0738',674,'0833},',2,'eta;",',454,'0359',674,'0556},',2,'theta;",ic:0.',226,674,'0833},',2,'iota;",a:0',674,'0556},',2,'kappa',38,2,'lambda;",',2,'mu',38,'d:0.2',674,226,'},',2,'nu',727,'0637,krn:{"',692,226,'},',2,'xi;",',734,'046',674,99,2,'pi',727,'0359,',2,'rho',38,'d:0.2',674,'0833},',2,'sigma',727,801,'krn:{"59',483,'58":-0.0556','},',2,'tau',727,'113,krn:{"',692,226,'},',2,'upsilon',727,'0359',674,226,'},',2,'phi',640,'d:0.2',674,'0833},',2,'chi',38,'d:0.2',674,'0556},',2,'psi',640,734,'0359',674,99,2,'omega',727,801,2,'epsilon;",a:0',674,'0833},',22,'greek',45,'x3D1;",',667,'0833},',22,'lucida',45,'x3D6',727,226,',',22,'lucida',45,'x3F1',38,'d:0.2',674,'0833},',22,'lucida',45,'x3C2;",',454,'0799',674,'0833},',22,'lucida',45,'x3D5',640,'d:0.2',674,'0833},',22,'lucida',45,'x21BC',';",a:0,d:-0.','2,',22,'arrows',45,'x21BD',898,'1,',22,'arrows',45,'x21C0',898,'2,',22,'arrows',45,'x21C1',898,'1,',22,'arrows',24,':\'&#','x02D3',';\',a:0','.1,',22,'symbol"},{c',921,922,'top:-.1em; ',924,'x02D2',926,'.1,','tclass:"symbol"},{c:"&#','x25B9;",',937,'x25C3;",',22,929,':"0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,119,'3,',28,':",",a:-',114,28,':"<',640,28,921,'font-size:','133%; ',922,'top:.1em','; display:inline-block">/\',d:0.','1,krn:{"1',483,'65',483,'77',483,'78',483,'89":0.0556,"90":-0.0556},',28,':">',640,28,':"⋆',38,22,'arial',45,'x2202;",ic:0.0556',674,'0833},',28,':"A",',667,'139','},tclass:"italic"},{c:"','B",ic:0.0502',674,'0833',1003,'C",ic:0.0715,krn:{"61',161,692,'0833',1003,'D",ic:0.',226,674,'0556',1003,'E",ic:0.0576',674,'0833',1003,'F",ic:0.139',662,'0833',1003,'G",',667,'0833',1003,'H",ic:0.0812,krn:{"61',483,692,'0556',1003,'I",ic:0.0785',674,'111',1003,'J",ic:0.0962',662,'167',1003,'K",ic:0.0715,krn:{"61',483,692,'0556',1003,'L",',667,226,1003,'M','",ic:0.109,krn:{"','61',483,692,'0833',1003,'N',1053,'61',173,'61',161,692,'0833',1003,'O",ic:0.',226,674,'0833',1003,'P",ic:0.139',662,'0833',1003,'Q",d:0.2',674,'0833',1003,'R",ic:0.00773',674,'0833',1003,'S",ic:0.0576,krn:{"61',483,692,'0833',1003,'T','",ic:0.139,krn:{"','61',161,692,'0833',1003,'U',1053,'59',177,',"58',177,',"61',483,'127":0.',226,1003,'V",ic:0.222,krn:{"59','":-0.167,"','58',1109,'61',177,1003,'W',1091,'59',1109,'58',1109,'61',177,1003,'X",ic:0.0785,krn:{"61',173,'61',161,692,'0833',1003,'Y",ic:0.222,krn:{"59',1109,'58',1109,'61',177,1003,'Z",ic:0.0715,krn:{"61',483,692,'0833},',22,'italic',45,'x266D;",',937,'x266E;",',937,'x266F;",',22,929,921,922,'top:-.3em; ',973,'75%; ',924,'x203F',926,',d:-0.1,',22,994,24,921,922,'top',':.4em; ',973,'75%; ',924,'x2040',926,',d:-0.1,',22,994,45,'x2113;",',667,'111',1003,'a",a:0,',22,'italic"},{c:"','b",',22,1183,'c",a:0',674,'0556',1003,'d',159,'89":0.0556,"90',483,'106',177,',"102',1109,1105,'167',1003,'e",a:0',674,'0556',1003,'f",',734,'108,krn:{"',692,'167',1003,'g",',454,'0359',674,226,1003,'h',159,'127',196,1003,'i",',22,1183,'j",',734,'0572,krn:{"59',483,814,1003,'k",ic:0.0315,',22,1183,'l",ic:0.0197',674,'0833',1003,'m",a:0,',22,1183,'n",a:0,',22,1183,'o",a:0',674,'0556',1003,'p",a:0,d:0.2',674,'0833',1003,'q",',454,'0359',674,'0833',1003,578,641,'krn:{"',692,'0556',1003,'s",a:0',674,'0556',1003,'t",',667,'0833',1003,'u",a:0',674,226,1003,'v",a:0,ic:0.0359',674,226,1003,'w",a:0,ic:0.0269',674,'0833',1003,'x",a:0',674,226,1003,'y",',454,'0359',674,'0556',1003,'z",a:0,ic:0.044',674,'0556},',22,'italic',45,'x131;",a:0',674,226,1003,'j",d:0.2',674,'0833},',22,'italic',45,'x2118',38,'d:0.2',674,99,22,994,24,921,922,'left:.3em; top:-.65em; font-size: 67%; ',924,'x2192',';\',','ic:0.154,',937,'x0311;",ic:0.399,',22,'normal"}],cmsy10:[{c',921,922,'top:.1em; ',924,'x2212',926,'.1,',937,'xB7',898,'2,',28,':"×',38,28,921,922,'top:.3em; ',924,'x2A',926,',',28,':"÷',38,28,':"◊",',937,'xB1',640,28,':"∓",',937,'x2295;",',937,'x2296;",',937,'x2297;",',937,'x2298;",',937,'x2299;",',937,'x25EF;",',22,994,45,'x2218',898,'1,',22,'symbol2',45,'x2022',898,'2,',937,'x224D',640,22,'symbol2',45,'x2261',640,22,'symbol2',45,'x2286;",',937,'x2287;",',937,'x2264;",',937,'x2265;",',937,'x227C;",',937,'x227D;",',22,929,':"~",a:0,d:-0.2,',28,':"≈',640,'d:-0.1,',937,'x2282;",',937,'x2283;",',937,'x226A;",',937,'x226B;",',937,'x227A;",',937,'x227B;",',937,'x2190;",a:-0.1,',22,'arrow1',45,'x2192;",a:-0.1,',22,'arrow1',45,'x2191',';",a:0.2,d:0',',',22,'arrow1a',45,'x2193',1437,',',22,'arrow1a',45,'x2194;",a:-0.1,',22,'arrow1',45,'x2197',640,22,'arrows',45,'x2198',640,22,'arrows',45,'x2243',640,22,'symbol2',45,'x21D0;",a:-0.1,',22,'arrow2',45,'x21D2;",a:-0.1,',22,'arrow2',45,'x21D1',1437,'.1,',22,'arrow1a',45,'x21D3',1437,'.1,',22,'arrow1a',45,'x21D4;",a:-0.1,',22,'arrow2',45,'x2196',640,22,'arrows',45,'x2199',640,22,'arrows',45,'x221D',640,28,921,973,974,'margin-right:-.','1em; ','position: relative; top',1167,924,'x2032',926,',',22,'lucida',45,'x221E',640,937,'x2208;",',937,'x220B;",',22,929,921,973,'150%; ',922,'top:.2em; ',924,'x25B3',1324,22,929,921,973,'150%; ',922,'top:.2em; ',924,'x25BD',1324,22,929,921,973,974,922,'top:.2em',977,'2,',28,921,973,'67%; ',1509,':-.15em; ',1507,'3em; ',924,'x22A2',1324,937,'x2200;",',937,'x2203;",',937,'xAC',898,'1,',937,'x2205;",',937,'x211C;",',937,'x2111;",',937,'x22A4;",',937,'x22A5;",',937,'x2135;",',22,929,':"A',159,'48":0.194},',22,'cal"},{c:"','B",ic:0.0304',',krn:{"48":0.','139},',22,1590,'C",ic:0.0583',1592,'139},',22,1590,'D",ic:0.',226,1592,'0833},',22,1590,'E",ic:0.0894',1592,99,22,1590,'F",ic:0.0993',1592,99,22,1590,'G",',734,'0593',1592,99,22,1590,'H",ic:0.00965',1592,99,22,1590,'I",ic:0.0738',1592,226,'},',22,1590,'J",',734,'185',1592,'167},',22,1590,'K",ic:0.0144',1592,'0556},',22,1590,'L',159,'48":0.139},',22,1590,'M',159,'48":0.139},',22,1590,'N",ic:0.147',1592,'0833},',22,1590,'O",ic:0.',226,1592,99,22,1590,'P",ic:0.0822',1592,'0833},',22,1590,'Q",d:0.2',1592,99,22,1590,'R',159,'48":0.0833},',22,1590,'S",ic:0.075',1592,'139},',22,1590,'T",ic:0.254',1592,226,'},',22,1590,'U",ic:0.0993',1592,'0833},',22,1590,'V",ic:0.0822',1592,226,'},',22,1590,'W",ic:0.0822',1592,'0833},',22,1590,'X",ic:0.146',1592,'139},',22,1590,'Y",ic:0.0822',1592,'0833},',22,1590,'Z",ic:0.0794',1592,'139},',22,'cal',45,'x22C3;",',937,'x22C2;",',937,'x228E;",',937,'x22C0;",',937,'x22C1;",',937,'x22A2;",',937,'x22A3;",',937,'x230A;",','a:',114,22,'lucida',45,'x230B;",','a:',114,22,'lucida',45,'x2308;",','a:',114,22,'lucida',45,'x2309;",','a:',114,22,'lucida',24,':"{",',103,28,':"}",',103,28,':"&#','x2329;",','a:',114,937,'x232A;",','a:',114,937,'x2223;",d',111,937,'x2225;",d',111,937,'x2195',1437,',',22,'arrow1a',45,'x21D5;",a:0.3,d:0,',22,'arrow1a',45,'x2216;",','a:0.3,d',111,937,'x2240;",',22,'symbol','"},{c:\'√',1324,'h:0.04,d:0.8,',937,'x2210;",a:0.4,',937,'x2207;",',22,'symbol',1802,922,973,'85%; ','left:-.1em; margin-right:-.','2em">∫',926,'.4,d',111,'ic:0.111,',22,'lucida',45,'x2294;",',937,'x2293;",',937,'x2291;",',937,'x2292;",',937,'xA7;",d',111,28,':"†",d',111,28,':"‡",d',111,28,':"¶",a:0.3,d',111,22,'lucida',45,'x2663;",',22,994,45,'x2662;",',22,994,45,'x2661;",',22,994,45,'x2660;",',22,994,'"}],cmex10:[{c',102,'h:0.04,d:1.16,n:','16,',22,'delim1','"},{c:")",',1865,'17,',22,'delim1','"},{c:"[",',1865,'104,',22,'delim1','"},{c:"]",',1865,'105,',22,'delim1',45,1740,1865,'106,',22,'delim1a',45,1746,1865,'107,',22,'delim1a',45,1752,1865,'108,',22,'delim1a',45,1758,1865,'109,',22,'delim1a',1802,'margin-left',':-.1em">{\',',1865,'110,',22,'delim1',1802,1507,'1em">}{}{}|{\',',6,'26',8,212,243,244,'; position:relative',249,'1em; ','left:-.05em">}\',',6,'27',8,212,243,'font-size:','150%; ','position:relative; top:.','8em; ',244,'">√\',h:0.','04,d:1.16,n:113',8,'root',243,266,'220%; ',268,269,244,271,'04,d:1.76,n:114',8,274,243,266,'310%; ',268,'8em',249,'01em; ',244,271,'06,d:2.36,n:115',8,274,243,266,'400%; ',268,'8em',249,'025em; ',244,271,'08,d:2.96,n:116',8,274,243,266,'490%; ',268,'8em',249,'03em; ',244,271,'1,d:3.75,n:117',8,274,243,266,'580%; ',268,'775em',249,'04em; ',244,271,'12,d:4.5,n:118',8,274,243,266,'750%; ',268,'775em',249,'04em; ',244,271,'14,d:5.7',8,274,243,244,'; margin-left:.','04em">||\',delim:{top:126,bot:127,rep:119},',2,'normal',4,'x2191',';",h:0.7,d:0,delim:{','top:120,rep:63},',2,'arrow1a',4,'x2193',357,'bot:121,rep:63},',2,360,243,'margin-left:-.','1em">◜',';\',h:0.','05',8,'symbol',243,368,'3em">◝',377,'05',8,380,243,368,'1em">◟',377,'05',8,380,243,368,'3em">◞',377,'05',8,380,4,'x21D1',';",h:0.65,d:0,delim:{','top:126,rep:119},',2,360,4,'x21D3',425,'bot:127,rep:119},',2,360,'"}],cmti10:[{c:"Γ",ic:0.133',8,'igreek"},{c:"&','Delta;",',2,437,'Theta;",ic:0.094',8,437,'Lambda;",',2,437,'Xi;",ic:0.153',8,437,'Pi;",ic:0.164',8,437,'Sigma;",ic:0.12',8,437,'Upsilon;",ic:0.111',8,437,'Phi;",ic:0.0599',8,437,'Psi;",ic:0.111',8,437,'Omega;",','ic:0.103',8,'igreek',203,':"ff','",ic:0.212,krn:{"39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":','14,"108":15},','tclass:"italic"},{c',':"fi','",ic:0.103,',473,':"fl',475,473,':"ffi',475,473,':"ffl',475,473,0,'x131;",a:0,','ic:0.','0767,',473,':"j",d:0.2,','ic:0.0374,',473,0,'x2CB;",',2,'iaccent',4,'x2CA;",ic:0.0969',8,497,4,'x2C7;",ic:0.083',8,497,4,'x2D8;",ic:0.108',8,497,4,'x2C9;",ic:0.103',8,497,4,'x2DA;",',2,497,'"},{c:"?",','d:0.17,w:0.46,',473,0,'xDF;",','ic:0.105,',473,0,'xE6;",a:0,','ic:0.0751,',473,0,'x153;",a:0,',527,473,0,'xF8;",','ic:0.0919,',473,0,'xC6;",','ic:0.12,',473,0,'x152;",','ic:0.12,',473,0,'xD8;",','ic:0.094,',473,':"?",krn:{"108":-0.','256,"76":-0.321},',473,':"!",','ic:0.124,lig:{"96":','60},',473,0,'x201D;",','ic:0.0696,',473,':"#",ic:0.0662,',473,':"$",',473,':"%",ic:0.136,',473,':"&",','ic:0.0969,',473,0,'x2019;",ic:0.124,krn:{"63":0.102,"33":0.102},lig:{"39":34},',473,':"(",d:0.2,','ic:0.162,',473,':")",d:0.2,','ic:0.0369,',473,':"*",ic:0.149,',473,':"+",a:0.1,','ic:0.0369,',473,':",",a:-0.3,d:0.2,w:0.278,',473,':"-",a:0,ic:0.0283',',lig:{"45":','123},',473,':".",a:-0.25,',473,':"/",ic:0.162,',473,':"0",ic:0.136,',473,':"1",ic:0.136,',473,':"2",ic:0.136,',473,':"3",ic:0.136,',473,':"4",ic:0.136,',473,':"5",ic:0.136,',473,':"6",ic:0.136,',473,':"7",ic:0.136,',473,':"8",ic:0.136,',473,':"9",ic:0.136,',473,':":",ic:0.0582,',473,':";",ic:0.0582,',473,0,'xA1;",','ic:0.0756,',473,':"=",a:0,d:-0.1,','ic:0.0662,',473,0,'xBF;",',473,':"?",ic:0.122,','lig:{"96":','62},',473,':"@",ic:0.096,',473,':"A",','krn:{"110":-0.0256,"108":-0.0256,"114":-0.0256,"117":-0.0256,"109":-0.0256,"116":-0.0256,"105":-0.0256,"67":-0.0256,"79":-0.0256,"71":-0.0256,"104":-0.0256,"98":-0.0256,"85":-0.0256,"107":-0.0256,"118":-0.0256,"119":-0.0256,"81":-','0.0256,"84','":-0.0767,"','89',636,'86','":-0.102,"','87',640,'101','":-0.0511,"','97',644,'111',644,'100',644,'99',644,'103',644,'113','":-0.0511','},',473,':"B',475,473,':"C",','ic:0.145,',473,':"D",',547,'krn:{"88','":-0.0256,"','87',668,'65',668,'86',668,'89":-0.','0256},',473,':"E",ic:0.12,',473,':"F','",ic:0.133,krn:{"','111',636,'101',636,'117','":-0.0767,"114":-0.0767,"97":-0.0767,"','65',640,'79',668,'67',668,'71',668,'81":-0.0256','},',473,':"G",ic:0.0872,',473,':"H",ic:0.164,',473,':"I",ic:0.158,',473,':"J",ic:0.14,',473,':"K",',663,'krn:{"79',668,'67',668,'71',668,696,'},',473,':"L",krn:{"84',636,'89',636,'86',640,'87',640,'101',644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"M",ic:0.164,',473,':"N",ic:0.164,',473,':"O",',547,'krn:{"88',668,'87',668,'65',668,'86',668,675,'0256},',473,':"P',475,'krn:{"65":-0.0767},',473,':"Q",d:0.2,',547,473,':"R",ic:0.0387,',634,'0.0256,"84',636,'89',636,'86',640,'87',640,'101',644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"S",ic:0.12,',473,':"T',681,'121',636,'101',636,'111',687,'117',636,'65":-0.0767},',473,':"U",ic:0.164,',473,':"V",ic:0.','184,krn:{"','111',636,'101',636,'117',687,'65',640,'79',668,'67',668,'71',668,696,'},',473,':"W",ic:0.',809,'65":-0.0767},',473,':"X",ic:0.158,krn:{"79',668,'67',668,'71',668,696,'},',473,':"Y",ic:0.','194',',krn:{"101',636,'111',687,'117',636,'65":-0.0767},',473,':"Z",',663,473,':"[",d:0.1,','ic:0.188,',473,0,'x201C;",','ic:0.169,',473,':"]",d:0.1,','ic:0.105,',473,0,'x2C6;",ic:0.0665',8,497,4,'x2D9;",ic:0.118',8,497,4,'x2018;",',553,'92},',473,':"a','",a:0,ic:0.',489,473,':"b",ic:0.0631',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"c',877,'0565',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"d',475,'krn:{"108":','0.0511},',473,':"e',877,'0751',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"f',471,'12,"102":11,"108":13},',473,':"g','",a:0,d:0.2,ic:0.','0885,',473,':"h",ic:0.',489,473,':"i",ic:0.102,',473,491,663,473,':"k",ic:0.','108,',473,':"l',475,918,'0.0511},',473,':"m',877,489,473,':"n',877,489,'krn:{"39":-0.102},',473,':"o',877,'0631',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"p',945,'0631',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"q',945,'0885,',473,':"r',877,'108',842,644,'97',644,'111',644,'100',644,'99',644,'103',644,'113',656,'},',473,':"s',877,'0821,',473,':"t",ic:0.0949,',473,':"u',877,489,473,':"v',877,957,473,':"w',877,957,918,'0.0511},',473,':"x',877,'12,',473,':"y',945,'0885,',473,':"z',877,'123,',473,0,'x2013',';",a:0.1,ic:0.','0921',586,'124},',473,0,'x2014',1068,'0921,',473,0,'x2DD;",ic:0.122',8,497,4,'x2DC;",ic:0.116',8,497,4,'xA8;",',2,497,'"}],cmbx10:[{c:"&Gamma',';",tclass:"bgreek"},{c:"&','Delta',1091,'Theta',1091,'Lambda',1091,'Xi',1091,'Pi',1091,'Sigma',1091,'Upsilon',1091,'Phi',1091,'Psi',1091,465,2,'bgreek',203,':"ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"bold"},{c',':"fi",',1117,':"fl",',1117,':"ffi",',1117,':"ffl",',1117,0,487,1117,491,1117,0,'x2CB',';",tclass:"baccent',4,'x2CA',1133,4,'x2C7',1133,4,'x2D8',1133,4,'x2C9',1133,4,'x2DA',1133,518,1117,0,522,1117,0,526,1117,0,530,1117,0,534,1117,0,538,1117,0,542,1117,0,546,1117,549,'278,"76":-0.319},',1117,552,628,'60},',1117,0,557,1117,':"#",',1117,':"$",',1117,':"%",',1117,566,1117,0,'x2019;",krn:{"63":0.111,"33":0.111},lig:{"39":34},',1117,572,1117,575,1117,':"*",',1117,580,1117,':",",a:-0.3,d:0.2,w:0.278,',1117,':"-",a:0',586,'123},',1117,':".",a:-0.25,',1117,':"/",',1117,':"0",',1117,':"1",',1117,':"2",',1117,':"3",',1117,':"4",',1117,':"5",',1117,':"6",',1117,':"7",',1117,':"8",',1117,':"9",',1117,':":",',1117,':";",',1117,0,618,1117,621,1117,0,'xBF;",',1117,':"?",',628,'62},',1117,':"@",',1117,':"A",krn:{"116','":-0.0278,"','67',1250,'79',1250,'71',1250,'85',1250,'81',1250,'84','":-0.0833,"','89',1262,'86":-0.','111,"87":-0.111},',1117,':"B",',1117,662,1117,':"D",krn:{"88',1250,'87',1250,'65',1250,'86',1250,675,'0278},',1117,':"E",',1117,':"F",krn:{"111',1262,'101',1262,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',1250,'67',1250,'71',1250,'81":-0.0278','},',1117,':"G",',1117,':"H",',1117,':"I",krn:{"73":0.0278},',1117,':"J",',1117,':"K",krn:{"79',1250,'67',1250,'71',1250,1297,'},',1117,':"L",krn:{"84',1262,'89',1262,1265,'111,"87":-0.111},',1117,':"M",',1117,':"N",',1117,':"O",krn:{"88',1250,'87',1250,'65',1250,'86',1250,675,'0278},',1117,':"P",krn:{"65',1262,'111',1250,'101',1250,'97',1250,'46',1262,'44":-0.0833},',1117,763,1117,':"R",krn:{"116',1250,'67',1250,'79',1250,'71',1250,'85',1250,'81',1250,'84',1262,'89',1262,1265,'111,"87":-0.111},',1117,':"S",',1117,':"T",krn:{"121',1250,'101',1262,'111',1290,'0833,"117":-0.0833','},',1117,':"U",',1117,808,'0139,krn:{"','111',1262,'101',1262,'117',1290,'111,"79',1250,'67',1250,'71',1250,1297,'},',1117,827,1386,'111',1262,'101',1262,'117',1290,'111,"79',1250,'67',1250,'71',1250,1297,'},',1117,':"X",krn:{"79',1250,'67',1250,'71',1250,1297,'},',1117,840,'025',842,1262,'111',1290,1380,'},',1117,850,1117,853,1117,0,857,1117,860,1117,0,'x2C6',1133,4,'x2D9',1133,4,872,628,'92},',1117,':"a','",a:0,krn:{"','118',1250,'106":0.','0556,"121',1250,'119":-0.','0278},',1117,':"b",krn:{"101','":0.0278,"','111',1468,'120',1250,'100',1468,'99',1468,'113',1468,'118',1250,1461,'0556,"121',1250,1464,'0278},',1117,':"c',1458,'104',1250,'107":-0.0278},',1117,':"d",',1117,':"e",a:0,',1117,':"f',1115,'12,"102":11,"108":13},',1117,':"g',945,1386,1461,'0278},',1117,':"h",krn:{"116',1250,'117',1250,'98',1250,'121',1250,'118',1250,1464,'0278},',1117,':"i",',1117,491,1117,':"k",krn:{"97":-0.0556,"101',1250,'97',1250,'111',1250,'99":-0.','0278},',1117,':"l",',1117,':"m',1458,'116',1250,'117',1250,'98',1250,'121',1250,'118',1250,1464,'0278},',1117,':"n',1458,'116',1250,'117',1250,'98',1250,'121',1250,'118',1250,1464,'0278},',1117,':"o",a:0',842,1468,'111',1468,'120',1250,'100',1468,'99',1468,'113',1468,'118',1250,1461,'0556,"121',1250,1464,'0278},',1117,':"p",a:0,d:0.2',842,1468,'111',1468,'120',1250,'100',1468,'99',1468,'113',1468,'118',1250,1461,'0556,"121',1250,1464,'0278},',1117,':"q",a:0,d:0.2,',1117,':"r",a:0,',1117,':"s",a:0,',1117,':"t",krn:{"121',1250,1464,'0278},',1117,':"u',1458,1464,'0278},',1117,':"v',877,1386,'97":-0.0556,"101',1250,'97',1250,'111',1250,1530,'0278},',1117,':"w',877,'0139',842,1250,'97',1250,'111',1250,1530,'0278},',1117,':"x",a:0,',1117,':"y',945,1386,'111',1250,'101',1250,'97',1250,'46',1262,'44":-0.0833},',1117,':"z",a:0,',1117,0,'x2013',1068,'0278',586,'124},',1117,0,'x2014',1068,'0278,',1117,0,'x2DD',1133,4,'x2DC',1133,4,'xA8',1133,'"}]});','jsMath.Setup.Styles({".typeset .','cmr10','":"font-family: ','serif','",".typeset .','italic":"font-style: italic',1690,'bold":"font-weight: bold',1690,'lucida','":"font-family: \'',1695,' sans unicode','\'",".typeset .','arial','":"font-family: \'Arial unicode MS',1699,'cal',1696,'Script MT\', \'Script MT Bold\', cursive',1690,'arrows',1701,1699,'arrow1',1701,1699,360,1701,'\'; ',268,'05em;left:-.15em',249,'15em','; display:inline-block",".typeset .','arrow2',1701,'\'; ',246,'top:-.',259,244,';",".typeset .','arrow3',1701,'\'; margin',':.1em',1690,380,1701,1699,'symbol2',1701,1699,'delim1','":"font-family: \'Times New Roman\'; font-','size: ','133%; ',268,'7em',1720,'delim1a',1696,'Lucida',1698,'\'; font-size: ','133%; ',268,'8em',1720,'delim1b',1701,1751,'133%; ',268,'7em',1720,212,1741,1742,'180%; ',268,'75em',1720,224,1696,'Lucida',1698,1751,'180%; ',268,'8em',1720,9,1701,1751,'180%; ',268,'7em',1720,'delim3',1741,1742,'250%; ',268,'725em',1720,'delim3a',1696,'Lucida',1698,1751,'250%; ',268,'775em',1720,'delim3b',1701,1751,'250%; ',268,'7em',1720,'delim4',1741,1742,'325%; ',268,'7em',1720,'delim4a',1696,'Lucida',1698,1751,'325%; ',268,'775em',1720,'delim4b',1701,1751,'325%; ',268,'7em',1720,3,1688,'Symbol',1690,'greek',1696,'Times New Roman',1699,468,1741,'style:italic',1690,1112,1741,'weight:bold',1690,21,1701,1751,'130','%; position: relative; top',': .7em; margin:-.05em',1720,79,1701,1751,'110',1852,': .85em; ',244,1728,97,1701,1751,'180',1852,': .6em',1720,33,1701,1751,'85',1852,': 1em',1720,27,1701,1751,'230',1852,': .6em; margin:-.05em',1720,127,1701,1751,'185',1852,': .75em',1720,143,1701,1751,'275',1852,': .55em',1720,37,1701,1751,'185',1852,': 1em',249,'1em',1720,184,1701,1751,'67',1852,':-.75em; ',244,1728,189,1701,1751,'110',1852,':-.4em; ',244,1728,194,1701,1751,'175',1852,':-.25em',1720,198,1741,1742,'75',1852,':-.5em',1720,202,1741,1742,'133',1852,':-.2em',1720,206,1741,1742,'200',1852,248,1720,274,1701,1731,'-right:-.075em',1720,'accent',1701,'\'; ',268,'05em; left:.','15em',1720,497,1701,'\'; ',268,1960,'15em; font-',1842,1720,'baccent',1701,'\'; ',268,1960,1968,1846,'; ',244,'"});','if(jsMath.browser=="','Mozilla"){jsMath.Update.TeXfonts({cmex10:{"48":{c',0,'xF8EB;"},"49":{c',0,'xF8F6;"},"50":{c',0,'xF8EE;"},"51":{c',0,'xF8F9;"},"52":{c',0,'xF8F0;"},"53":{c',0,'xF8FB;"},"54":{c',0,'xF8EF;"},"55":{c',0,'xF8FA;"},"56":{c',0,'xF8F1;"},"57":{c',0,'xF8FC;"},"58":{c',0,'xF8F3;"},"59":{c',0,'xF8FE;"},"60":{c',0,'xF8F2;"},"61":{c',0,'xF8FD;"},"62":{c',0,'xF8F4;"},"64":{c',0,'xF8ED;"},"65":{c',0,'xF8F8;"},"66":{c',0,'xF8EC;"},"67":{c',0,'xF8F7;"}}});',1686,'accent',1688,'Arial unicode MS; ',268,1960,'05em"})}',1981,'MSIE"){jsMath.Browser.msieFontBug=1}jsMath.Macro("not","\\\\mathrel{\\\\rlap{\\\\kern 3mu/}}");jsMath.Macro("bowtie","\\\\mathrel\\\\triangleright\\\\kern-6mu\\\\mathrel\\\\triangleleft");jsMath.Box.defaultH=0.8;'] +]); \ No newline at end of file diff --git a/htdocs/jsMath/jsMath-fallback-symbols.js b/htdocs/jsMath/jsMath-fallback-symbols.js new file mode 100644 index 0000000000..8db31f49d7 --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-symbols.js @@ -0,0 +1,28 @@ +/* + * jsMath-fallback-symbols.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed to use image fonts for symbols + * but standard native fonts for letters and numbers. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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.Script.Uncompress([ + ['jsMath.','Add(','jsMath.Img',',{','UpdateTeXFonts',':function(_1){for(var _2 in _1){for(var _3 in _1[_2]){',0,'TeX[_2][_3',']=_1[_2][_3];',0,7,'].tclass="i"+_2;}}}});',2,'.',4,'({cmr10',':{"33":{c:"!",lig:{"96":60}},"35":{c:"#"},"36":{c:"$"},"37":{c:"%"},"38":{c:"&"},"40":{c:"(",d:0.2},"41":{c:")",d:0.2},"42":{c',':"*",d:-0.3},"','43":{c:"+",a:0.1},"44":{c:",",a:-0.3','},"45":{c:"-",a:0',',lig:{"45":123}},"46":{c:".",a:-0.25},"47":{c',':"/"},"','48":{c:"0','"},"49":{c:"1"},"50":{c:"2"},"51":{c:"3"},"52":{c:"4"},"53":{c:"5"},"54":{c:"6"},"55":{c:"7"},"56":{c:"8"},"57":{c:"9"},"58":{c:":"},"59":{c:";"},"61":{c:"=",a:0,d:-0.1},"63":{c:"?",lig:{"96":62}},"64":{c:"@"},"65":{c:"A",krn:{"116','":-0.0278,"','67',24,'79',24,'71',24,'85',24,'81',24,'84":-0.0833,"89":-0.0833,"86":-0.111,"87":-0.111}},"','66":{c:"B','"},"','67":{c:"C',37,'68":{c:"D','",krn:{"','88',24,'87',24,'65',24,'86',24,'89','":-0.0278}},"','69":{c:"E',37,'70":{c:"F",','krn:{"111":-0.0833,"101":-0.0833,"117":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.111,"79":-0.0278,"67":-0.0278,"71":-0.0278,"81":-0.0278}},"','71":{c:"G',37,'72":{c:"H',37,'73":{c:"I',41,'73','":0.0278}},"','74":{c:"J',37,'75":{c:"K',41,'79',24,'67',24,'71',24,'81',51,'76":{c:"L",krn:{"',35,'77":{c:"M',37,'78":{c:"N',37,'79":{c:"O',41,'88',24,'87',24,'65',24,'86',24,'89',51,'80":{c:"P",','krn:{"65":-0.','0833,"111',24,'101',24,'97',24,'46":-0.0833,"44":-0.0833}},"','81":{c:"Q",d:','1},"82":{c:"R',41,'116',24,'67',24,'79',24,'71',24,'85',24,'81',24,35,'83":{c:"S',37,'84":{c:"T',41,'121',24,'101":-0.0833,"111":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.0833,"117":-0.0833}},"','85":{c:"U',37,'86":{c:"V",ic:0.','0139,',55,'87":{c:"W",ic:0.','0139,',55,'88":{c:"X",','krn:{"79',24,'67',24,'71',24,'81',51,'89":{c:"Y",ic:0.','025',',krn:{"',125,'90":{c:"Z',37,'91":{c:"[",d:0.1','},"93":{c:"]",d:0.1','},"97":{c:"a','",a:0,krn:{"','118',24,'106":0.0556,"121',24,'119',51,'98":{c:"b",','krn:{"101":0.0278,"111":0.0278,"120":-0.0278,"100":0.0278,"99":0.0278,"113":0.0278,"118":-0.0278,"106":0.0556,"121":-0.0278,"119":-0.0278}},"','99":{c:"c',152,'104',24,'107',51,'100":{c:"d',37,'101":{c:"e",a',':0},"102":{c:"f",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":12,"102":11,"108":13}},"103":{c:"g",a:0,d:0.2,ic:0.0139,krn:{"106":0.0278}},"104":{c:"h",krn:{"116',24,'117',24,'98',24,'121',24,'118',24,'119',51,'105":{c:"i"},"106":{c:"j",d:','1},"107":{c:"k',41,'97','":-0.0556,"','101',24,'97',24,'111',24,'99',51,'108":{c:"l',37,'109":{c:"m',152,'116',24,'117',24,'98',24,'121',24,'118',24,'119',51,'110":{c:"n',152,'116',24,'117',24,'98',24,'121',24,'118',24,'119',51,'111":{c:"o",a:0,',160,'112":{c:"p",a:0,d:0.2,',160,'113":{c:"q",a:0,d:','1','},"114":{c:"r",a:0','},"','115":{c:"s",a:0','},"116":{c:"t',41,'121',24,'119',51,'117":{c:"u',152,'119',51,'118":{c:"v','",a:0,ic:0.','0139',145,'97',186,'101',24,'97',24,'111',24,'99',51,'119":{c:"w',245,'0139',145,'101',24,'97',24,'111',24,'99',51,'120":{c:"x",a:0','},"121":{c:"y','",a:0,d:0.2,ic:0.','0139',145,'111',24,'101',24,'97',24,102,'122":{c:"z",a:0','}},cmmi10:{"65":{c:"A',41,'127":0.139}},"',36,'",ic:0.','0502,','krn:{"127":0.0833}},"',38,287,'0715',145,'61',24,'59":-0.0556,"58":-0.0556,"127":0.','0833}},"','68":{c:"D',287,'0278',',krn:{"127":0.0556}},"',52,287,'0576,',289,54,'ic:0.139',',krn:{"61":-0.0556,"59":-0.111,"58":-0.111,"127":0.',297,56,'",',289,58,287,'0812',145,'61',186,296,'0556}},"','73":{c:"I",ic:0.','0785',145,'127":0.111}},"',64,287,'0962',308,'167}},"75":{c:"K',287,'0715',145,'61',186,296,320,'76":{c:"L",','krn:{"127":0.0278}},"',78,287,'109',145,'61',186,296,297,80,287,'109',145,'61":-0.0833,"61',24,296,297,'79":{c:"O',287,300,',',289,94,'ic:0.139',308,297,103,'0.2,',289,'82":{c:"R',287,'00773,',289,119,287,'0576',145,'61',186,296,297,'84":{c:"T",ic:0.','139',145,'61',24,296,297,126,287,'109',145,'59":-0.111,"58":-0.111,"61',186,'127',63,128,'222',',krn:{"59":-0.167,"58":-0.167,"61":-0.111}},"',131,'139',396,134,'ic:0.0785',145,'61":-0.0833,"61',24,296,297,143,'222',396,147,287,'0715',145,'61',186,296,297,'97":{c:"a",a:0},"98":{c:"b',37,'99":{c:"c",a:0',301,167,41,'89":0.0556,"90',186,'106":-0.111,"102":-0.167,"127":0.167}},"101":{c:"e",a:0',301,'102":{c:"f",d:','0.2,ic:0.','108',145,296,'167}},"103":{c:"g',272,'0359,',338,'104":{c:"h',41,'127',51,182,429,'0572',145,'59',186,'58":-0.',320,'107":{c:"k',287,'0315},"',195,287,'0197,',289,'109":{c:"m",a:0},"110":{c:"n",a:0},"111":{c:"o",a:0',301,227,289,'113":{c:"q',272,'0359,',289,'114":{c:"r',245,300,145,296,320,233,301,'116":{c:"t",',289,'117":{c:"u",a:0,',338,'118":{c:"v',245,'0359,',338,'119":{c:"w',245,'0269,',289,270,',',338,'121":{c:"y',272,'0359',301,'122":{c:"z',245,'044',145,'127":0.0556}}},cmsy10:{"0":{c:"−",a:0.1}},cmti10:{"33":{c:"!",lig:{"96":60}},"35":{c:"#",ic:0.0662},"37":{c:"%",ic:0.136},"38":{c:"&",ic:0.0969},"40":{c:"(",d:',429,'162},"41":{c:")",d:',429,'0369},"42":{c:"*",ic:0.149},"43":{c:"+",a:0.1,ic:0.0369},"44":{c:",",a:-0.3,d:0.2,w:0.278},"45":{c:"-",a:0,ic:0.0283',20,':"/",ic:0.162},"',22,'",ic:0.136},"','49":{c:"1',503,'50":{c:"2',503,'51":{c:"3',503,'52":{c:"4',503,'53":{c:"5',503,'54":{c:"6',503,'55":{c:"7',503,'56":{c:"8',503,'57":{c:"9',503,'58":{c:":",ic:0.0582},"59":{c:";",ic:0.0582},"61":{c:"=",a:0,d:-0.1,ic:0.0662},"63":{c:"?",ic:0.122,lig:{"96":62}},"64":{c:"@",ic:0.096},"65":{c:"A",','krn:{"110":-0.0256,"108":-0.0256,"114":-0.0256,"117":-0.0256,"109":-0.0256,"116":-0.0256,"105":-0.0256,"67":-0.0256,"79":-0.0256,"71":-0.0256,"104":-0.0256,"98":-0.0256,"85":-0.0256,"107":-0.0256,"118":-0.0256,"119":-0.0256,"81":-','0.0256,"84','":-0.0767,"','89',525,'86','":-0.102,"','87',529,'101":-0.0511,"97":-0.0511,"111":-0.0511,"100":-0.0511,"99":-0.0511,"103":-0.0511,"113":-0.0511}},"',36,'",ic:0.103','},"',38,287,'145},"','68":{c:"D','",ic:0.094,krn:{"88":-0.0256,"87":-0.0256,"65":-0.0256,"86":-0.0256,"89":-0.0256}},"',52,'",ic:0.12},"',54,'ic:0.133',145,'111',525,'101',525,'117','":-0.0767,"114":-0.0767,"97":-0.0767,"','65',529,'79":-0.0256,"67":-0.0256,"71":-0.0256,"81":-0.0256}},"',56,287,'0872},"',58,'",ic:0.164},"',321,'158},"',64,287,'14},"75":{c:"K',287,'145',145,554,76,'84',525,'89',525,'86',529,'87',529,532,78,559,80,559,'79":{c:"O',540,94,'ic:0.103',145,'65":-0.0767}},"',103,429,'094},"82":{c:"R',287,'0387,',523,'0.0256,"84',525,'89',525,'86',529,'87',529,532,119,542,379,'133',145,'121',525,'101',525,'111',551,'117',525,588,126,559,128,'184',145,'111',525,'101',525,'117',551,'65',529,554,131,'184',145,588,134,'ic:0.158',145,554,143,'194',145,'101',525,'111',551,'117',525,588,147,287,538,149,',ic:0.188',150,',ic:0.105},"97":{c:"a',245,'0767},"',159,'ic:0.0631',145,532,'99":{c:"c',245,'0565',145,532,167,534,145,'108":0.0511}},"','101":{c:"e',245,'0751',145,532,'102":{c:"f',287,'212',145,'39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":12,"102":11,"108":13}},"103":{c:"g',272,'0885},"','104":{c:"h',287,658,'105":{c:"i',287,'102},"106":{c:"j",d:',429,538,'107":{c:"k',287,'108},"',195,534,145,671,'109":{c:"m',245,658,'110":{c:"n',245,'0767',145,'39":-0.102}},"111":{c:"o',245,'0631',145,532,'112":{c:"p',272,'0631',145,532,'113":{c:"q',272,683,'114":{c:"r',245,'108',145,532,'115":{c:"s',245,'0821},"116":{c:"t',287,'0949},"117":{c:"u',245,658,'118":{c:"v',245,694,'119":{c:"w',245,'108',145,671,'120":{c:"x',245,'12},"121":{c:"y',272,683,'122":{c:"z',245,'123}},cmbx10',16,':"*"},"',18,',d:0.2,w:0.278},"45":{c:"-",a:0',20,':"/"},"',22,23,24,'67',24,'79',24,'71',24,'85',24,'81',24,35,36,37,38,37,'68":{c:"D',41,'88',24,'87',24,'65',24,'86',24,'89',51,52,37,54,55,56,37,58,37,'73":{c:"I',41,'73',63,64,37,'75":{c:"K',41,'79',24,'67',24,'71',24,'81',51,76,35,78,37,80,37,'79":{c:"O',41,'88',24,'87',24,'65',24,'86',24,'89',51,94,95,'0833,"111',24,'101',24,'97',24,102,103,'1},"82":{c:"R',41,'116',24,'67',24,'79',24,'71',24,'85',24,'81',24,35,119,37,'84":{c:"T',41,'121',24,125,126,37,128,'0139,',55,131,'0139,',55,134,'krn:{"79',24,'67',24,'71',24,'81',51,143,'025',145,125,147,37,149,150,'},"97":{c:"a',152,'118',24,'106":0.0556,"121',24,'119',51,159,160,'99":{c:"c',152,'104',24,'107',51,167,37,'101":{c:"e",a',170,24,'117',24,'98',24,'121',24,'118',24,'119',51,182,'1},"107":{c:"k',41,'97',186,'101',24,'97',24,'111',24,'99',51,195,37,'109":{c:"m',152,'116',24,'117',24,'98',24,'121',24,'118',24,'119',51,'110":{c:"n',152,'116',24,'117',24,'98',24,'121',24,'118',24,'119',51,225,160,227,160,229,'1',231,'},"',233,'},"116":{c:"t',41,'121',24,'119',51,'117":{c:"u',152,'119',51,'118":{c:"v',245,'0139',145,'97',186,'101',24,'97',24,'111',24,'99',51,'119":{c:"w',245,'0139',145,'101',24,'97',24,'111',24,'99',51,270,'},"121":{c:"y',272,'0139',145,'111',24,'101',24,'97',24,102,282,'}}});if(',0,'browser=="MSIE"&&',0,'platform=="mac"){','jsMath.Setup.Styles({".typeset .math":"font-style: normal",".typeset .typeset":"font-style: normal",".typeset .icmr10":"font-family: ','Times','",".typeset .icmmi10":"font-family: ',1020,'; font-style: italic",".typeset .icmbx10":"font-family: ',1020,'; font-weight: bold",".typeset .icmti10":"font-family: ',1020,'; font-style: italic"});}','else{',1019,'serif',1021,1030,1023,1030,1025,1030,1027,0,'Add(',2,',{symbols:[0,','1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,','34,39,60,62,92,94,95,96',',123,124,125,126,127',']});',2,'.SetFont',15,':',2,'.symbols',',cmmi10:[0,',1042,'33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,','91,92,93,94,95,96',1044,'],cmsy10:[',1042,1054,'65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122',1044,'],cmex10:["all"],cmti10:',2,1051,'.concat(36),cmbx10:',2,1051,'});',2,'.LoadFont("cm-fonts");'] +]); diff --git a/htdocs/jsMath/jsMath-fallback-unix.js b/htdocs/jsMath/jsMath-fallback-unix.js new file mode 100644 index 0000000000..543c1fdee6 --- /dev/null +++ b/htdocs/jsMath/jsMath-fallback-unix.js @@ -0,0 +1,29 @@ +/* + * jsMath-fallback-unix.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser under Unix. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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.Script.Uncompress([ + ['jsMath.Add(jsMath.TeX,{cmr10',':[{c:"Γ",','tclass:"greek"},{c:"&','Delta;",',2,'Theta;",',2,'Lambda;",',2,'Xi;",',2,'Pi;",',2,'Sigma;",',2,'Upsilon;",',2,'Phi;",',2,'Psi;",',2,'Omega;",','tclass:"','greek','"},{c',':"ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"normal"},{c',':"fi",',28,':"fl",',28,':"ffi",',28,':"ffl",',28,':"&#','x131',';",a:0,',28,':"j",d:0.2,',28,37,'x60;",',22,'accent','"},{c:"&#','xB4;",',22,46,47,'x2C7;",',22,46,47,'x2D8;",',22,46,24,':"ˉ',';",',22,46,47,'x2DA;",',22,46,47,'x0327;",',28,37,'xDF;",',28,37,'xE6',39,28,37,'x153',39,28,37,'xF8;",',28,37,'xC6;",',28,37,'x152;",',28,37,'xD8;",',28,':"?",krn:{"108":-0.278,"76":-0.319},',28,':"!",lig:{"96":60},',28,37,'x201D;",',28,':"#",',28,':"$",',28,':"%",',28,':"&",',28,37,'x2019;",krn:{"63":0.111,"33":0.','111},','lig:{"39":34},',28,':"(",','d:0.2,',28,':")",',117,28,':"*",',28,':"+",a',':0.1,',28,':",",a:-','0.3,d:0.2,','w:0.278,',28,':"-",a:0,lig:{"45":123},',28,':".",a:-0.','25,',28,':"/",',28,':"0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,':":",',28,':";",',28,37,'xA1;",',28,':"=",','a:0,d:-0.','1,',28,37,'xBF;",',28,':"?",lig:{"96":62},',28,':"@",',28,':"A','",krn:{"','116','":-0.0278,"','67',179,'79',179,'71',179,'85',179,'81',179,'84','":-0.0833,"','89',191,'86','":-0.111',',"87":-0.',113,28,':"B",',28,':"C",',28,':"D',177,'88',179,'87',179,'65',179,'86',179,'89','":-0.0278','},',28,':"E",',28,':"F',177,'111',191,'101',191,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',179,'67',179,'71',179,'81',214,'},',28,':"G",',28,':"H",',28,':"I',177,'73":0.','0278','},',28,':"J",',28,':"K',177,'79',179,'67',179,'71',179,'81',214,'},',28,':"L',177,'84',191,'89',191,'86',195,196,113,28,':"M",',28,':"N",',28,':"O',177,'88',179,'87',179,'65',179,'86',179,'89',214,'},',28,':"P',177,'65',191,'111',179,'101',179,'97',179,'46',191,'44":-0.0833},',28,':"Q",d:1,',28,':"R',177,'116',179,'67',179,'79',179,'71',179,'85',179,'81',179,'84',191,'89',191,'86',195,196,113,28,':"S",',28,':"T',177,'121',179,'101',191,'111',226,'0833,"117":-0.0833','},',28,':"U",',28,':"V",','ic:0.0139,krn:{"','111',191,'101',191,'117',226,'111,"79',179,'67',179,'71',179,'81',214,'},',28,':"W",',345,'111',191,'101',191,'117',226,'111,"79',179,'67',179,'71',179,'81',214,'},',28,':"X',177,'79',179,'67',179,'71',179,'81',214,'},',28,':"Y",ic:0.025,','krn:{"101',191,'111',226,339,'},',28,':"Z",',28,':"[",','d',125,28,37,'x201C;",',28,':"]",','d',125,28,37,'x2C6;",',22,46,47,'x2D9;",',22,46,47,'x2018;",lig:{"96":92},',28,':"a','",a:0,krn:{"','118',179,'106":0.','0556,"121',179,'119',214,'},',28,':"b',177,'101','":0.0278,"','111',438,'120',179,'100',438,'99',438,'113',438,'118',179,428,'0556,"121',179,'119',214,'},',28,':"c',425,'104',179,'107',214,'},',28,':"d",',28,':"e",a:0,',28,':"f',26,'12,"102":11,"108":13},',28,':"g",','a:0,d:0.2,ic:0.','0139,krn:{"',428,244,'},',28,':"h',177,'116',179,'117',179,'98',179,'121',179,'118',179,'119',214,'},',28,':"i",',28,41,28,':"k',177,'97','":-0.0556,"','101',179,'97',179,'111',179,'99',214,'},',28,':"l",',28,':"m',425,'116',179,'117',179,'98',179,'121',179,'118',179,'119',214,'},',28,':"n',425,'116',179,'117',179,'98',179,'121',179,'118',179,'119',214,'},',28,':"o',425,'101',438,'111',438,'120',179,'100',438,'99',438,'113',438,'118',179,428,'0556,"121',179,'119',214,'},',28,':"p",a:0,',117,393,438,'111',438,'120',179,'100',438,'99',438,'113',438,'118',179,428,'0556,"121',179,'119',214,'},',28,':"q",a:0,',117,28,':"','r",a:0,',28,':"s",a:0,',28,':"t',177,'121',179,'119',214,'},',28,':"u',425,'119',214,'},',28,':"v",a:0,',345,'97',504,'101',179,'97',179,'111',179,'99',214,'},',28,':"w",a:0,',345,'101',179,'97',179,'111',179,'99',214,'},',28,':"x",a:0,',28,':"y",',475,476,'111',179,'101',179,'97',179,'46',191,'44":-0.0833},',28,':"z",a:0,',28,37,'x2013',';",a:0.1,','ic:0.0278,','lig:{"45":124},',28,37,'x2014',662,663,28,37,'x2DD;",',22,46,47,'x2DC;",',22,46,47,'xA8;",',22,46,'"}],cmmi10',1,'ic:0.139',',krn:{"61":-0.0556,"59":-0.111,"58":-0.111,"127":0.','0833},',22,'igreek"},{c:"&',3,'krn:{"127":0.','167},',22,689,5,'ic:0.',244,',krn:{"127":0.','0833},',22,689,7,691,'167},',22,689,9,'ic:0.0757',698,'0833},',22,689,11,'ic:0.0812,krn:{"61',504,'59":-0.0556,"58":-0.0556,"127":0.','0556},',22,689,13,'ic:0.0576',698,'0833},',22,689,15,'ic:0.139',686,'0556},',22,689,17,691,'0833},',22,689,19,'ic:0.11,krn:{"61',504,716,'0556},',22,689,21,'ic:0.0502',698,'0833},',22,689,'alpha',';",a:0,ic:0.','0037',698,244,'},',2,'beta;",','d:0.2,ic:0.','0528',698,'0833},',2,'gamma;",',475,'0556,',2,'delta;",ic:0.0378,krn:{"',716,'0556},',2,'epsilon;",a:0',698,'0556},',22,'symbol"},{c',':"ζ",',758,'0738',698,'0833},',2,'eta;",',475,'0359',698,'0556},',2,'theta;",ic:0.',244,698,'0833},',2,'iota;",a:0',698,'0556},',2,'kappa',39,2,'lambda;",',2,'mu',39,'d:0.2',698,244,'},',2,'nu',751,'0637,krn:{"',716,244,'},',2,'xi;",',758,'046',698,113,2,'pi',751,'0359,',2,'rho',39,'d:0.2',698,'0833},',2,'sigma',751,824,'krn:{"59',504,'58":-0.0556','},',2,'tau',751,'113,krn:{"',716,244,'},',2,'upsilon',751,'0359',698,244,'},',2,'phi',662,'d:0.2',698,'0833},',2,'chi',39,'d:0.2',698,'0556},',2,'psi',662,758,'0359',698,113,2,'omega',751,824,2,'epsilon;",a:0',698,'0833},',22,'greek',47,'x3D1;",',691,'0833},',28,37,'x3D6',751,244,',',28,37,'x3F1',39,'d:0.2',698,'0833},',28,37,'x3C2;",',475,'0799',698,'0833},',28,37,'x3D5',662,'d:0.2',698,'0833},',28,37,'x21BC',';",a:0,d:-0.','2,',22,'harpoon',47,'x21BD',916,'1,',22,'harpoon',47,'x21C0',916,'2,',22,'harpoon',47,'x21C1',916,'1,',22,'harpoon',24,60,'font-size: 133%; ',61,':-.1em; margin:-.2em; left:-.05em\\">&#','x02D3',63,'a',125,22,'symbol"},{c:"&#','x25B7',63,22,948,958,'x25C1',63,22,775,':"0",',28,':"1",',28,':"2",',28,':"3",',28,':"4",',28,':"5",',28,':"6",',28,':"7",',28,':"8",',28,':"9",',28,133,'3,',28,':",",a:-',128,28,':"<',662,28,136,'krn:{"1',504,'65',504,'77',504,'78',504,'89":0.0556,"90":-0.0556},',28,':">',662,28,60,958,'x2605',63,'a:0,','tclass:"symbol"},{c:"&#','x2202;",ic:0.0556',698,'0833},',28,':"A",',691,'139','},tclass:"italic"},{c:"','B",ic:0.0502',698,'0833',1024,'C",ic:0.0715,krn:{"61',179,716,'0833',1024,'D",ic:0.',244,698,'0556',1024,'E",ic:0.0576',698,'0833',1024,'F",ic:0.139',686,'0833',1024,'G",',691,'0833',1024,'H",ic:0.0812,krn:{"61',504,716,'0556',1024,'I",ic:0.0785',698,'111',1024,'J",ic:0.0962',686,'167',1024,'K",ic:0.0715,krn:{"61',504,716,'0556',1024,'L",',691,244,1024,'M','",ic:0.109,krn:{"','61',504,716,'0833',1024,'N',1074,'61',191,'61',179,716,'0833',1024,'O",ic:0.',244,698,'0833',1024,'P",ic:0.139',686,'0833',1024,'Q",d:0.2',698,'0833',1024,'R",ic:0.00773',698,'0833',1024,'S",ic:0.0576,krn:{"61',504,716,'0833',1024,'T','",ic:0.139,krn:{"','61',179,716,'0833',1024,'U',1074,'59',195,',"58',195,',"61',504,'127":0.',244,1024,'V",ic:0.222,krn:{"59','":-0.167,"','58',1130,'61',195,1024,'W',1112,'59',1130,'58',1130,'61',195,1024,'X",ic:0.0785,krn:{"61',191,'61',179,716,'0833',1024,'Y",ic:0.222,krn:{"59',1130,'58',1130,'61',195,1024,'Z",ic:0.0715,krn:{"61',504,716,'0833},',22,'italic',47,'x266D;",',22,'symbol2',47,'x266E;",',22,'symbol2',47,'x266F;",',22,'symbol2',47,'x2323',916,'1,',28,37,'x2322',916,'1,',28,37,'x2113;",',691,113,22,775,':"a",a:0,',22,'italic"},{c:"','b",',22,1195,'c",a:0',698,'0556',1024,'d',177,'89":0.0556,"90',504,'106',195,',"102',1130,1126,'167',1024,'e",a:0',698,'0556',1024,'f",',758,'108,krn:{"',716,'167',1024,'g",',475,'0359',698,244,1024,'h',177,'127',214,1024,'i",',22,1195,'j",',758,'0572,krn:{"59',504,837,1024,'k",ic:0.0315,',22,1195,'l",ic:0.0197',698,'0833',1024,'m",a:0,',22,1195,'n",a:0,',22,1195,'o",a:0',698,'0556',1024,'p",a:0,d:0.2',698,'0833',1024,'q",',475,'0359',698,'0833',1024,599,663,'krn:{"',716,'0556',1024,'s",a:0',698,'0556',1024,'t",',691,'0833',1024,'u",a:0',698,244,1024,'v",a:0,ic:0.0359',698,244,1024,'w",a:0,ic:0.0269',698,'0833',1024,'x",a:0',698,244,1024,'y",',475,'0359',698,'0556',1024,'z",a:0,ic:0.044',698,'0556},',22,'italic',47,'x131;",a:0',698,244,1024,'j",d:0.2',698,'0833},',22,'italic',47,'x2118',39,'d:0.2',698,113,28,60,'position:relative; left: .4em; top: -.8em; font-size: 50%\\">→',63,'ic:0.154,',1016,'x0311;",ic:0.399,',22,'normal"}],cmsy10:[{c',37,'x2212',662,1016,'xB7',916,'2,',1016,'xD7',39,22,948,61,':.2em\\">*',63,'a:0,',1016,'xF7',39,1016,'x25CA;",',1016,'xB1',662,1016,'x2213;",',1016,'x2295;",',1016,'x2296;",',1016,'x2297;",',1016,'x2298;",',1016,'x2299;",',1016,'x25EF;",',22,948,61,':.25em;\\">°',63,166,'1,',1016,'x2022',916,'2,',1016,'x224D',662,1016,'x2261',662,1016,'x2286;",',1016,'x2287;",',1016,'x2264;",',1016,'x2265;",',1016,'x227C;",',1016,'x227D;",',22,775,':"~",',166,'2,',28,37,'x2248',662,'d:-0.1,',1016,'x2282;",',1016,'x2283;",',1016,'x226A;",',1016,'x226B;",',1016,'x227A;",',1016,'x227B;",',1016,'x2190',916,'15,',22,'arrows"},{c:"&#','x2192',916,'15,',22,1431,'x2191',';",h:1,',22,1431,'x2193',1438,22,1431,'x2194',39,22,1431,'x2197',1438,22,1431,'x2198',1438,22,1431,'x2243',662,1016,'x21D0',662,22,1431,'x21D2',662,22,1431,'x21D1;",h:0.9,d',125,22,1431,'x21D3;",h:0.9,d',125,22,1431,'x21D4',662,22,1431,'x2196',1438,22,1431,'x2199',1438,22,1431,'x221D',662,22,948,940,'margin-right',': -.1em; position: relative; top:.4em\\">′',63,'a:0,',1016,'x221E',662,1016,'x2208;",',1016,'x220B;",',1016,'x25B3;",',1016,'x25BD;",',22,775,136,22,948,'font-size:50%; ',61,':-.3em; ',1493,':-.2em\\">|",a:0,',28,37,'x2200;",',1016,'x2203;",',1016,'xAC',916,'1,',22,'symbol1',47,'x2205;",',1016,'x211C;",',1016,'x2111;",',1016,'x22A4;",',1016,'x22A5;",',1016,'x2135;",',22,775,':"A',177,'48":0.194},',22,'cal"},{c:"','B",ic:0.0304',',krn:{"48":0.','139},',22,1547,'C",ic:0.0583',1549,'139},',22,1547,'D",ic:0.',244,1549,'0833},',22,1547,'E",ic:0.0894',1549,113,22,1547,'F",ic:0.0993',1549,113,22,1547,'G",',758,'0593',1549,113,22,1547,'H",ic:0.00965',1549,113,22,1547,'I",ic:0.0738',1549,244,'},',22,1547,'J",',758,'185',1549,'167},',22,1547,'K",ic:0.0144',1549,'0556},',22,1547,'L',177,'48":0.139},',22,1547,'M',177,'48":0.139},',22,1547,'N",ic:0.147',1549,'0833},',22,1547,'O",ic:0.',244,1549,113,22,1547,'P",ic:0.0822',1549,'0833},',22,1547,'Q",d:0.2',1549,113,22,1547,'R',177,'48":0.0833},',22,1547,'S",ic:0.075',1549,'139},',22,1547,'T",ic:0.254',1549,244,'},',22,1547,'U",ic:0.0993',1549,'0833},',22,1547,'V",ic:0.0822',1549,244,'},',22,1547,'W",ic:0.0822',1549,'0833},',22,1547,'X",ic:0.146',1549,'139},',22,1547,'Y",ic:0.0822',1549,'0833},',22,1547,'Z",ic:0.0794',1549,'139},',22,'cal',47,'x22C3;",',1016,'x22C2;",',1016,'x228E;",',1016,'x22C0;",',1016,'x22C1;",',1016,'x22A2;",',1016,'x22A3;",',22,'symbol2',47,'xF8F0;",','a:',128,28,37,'xF8FB;",','a:',128,28,37,'xF8EE;",','a:',128,28,37,'xF8F9;",','a:',128,28,':"{",',117,28,':"}",',117,28,37,'x3008;",','a:',128,28,37,'x3009;",','a:',128,28,':"|",d',125,22,'vertical"},{c',':"||",','d:0,',22,'vertical',47,'x2195',1438,'d:0.15,',22,1431,'x21D5;",a:0.2,d',125,22,1431,'x2216;",','a:0.3,d',125,28,37,'x2240;",',22,948,61,': .8em\\">√',63,'h:0.04,d:0.9,',28,37,'x2210;",a:0.4,',1016,'x2207;",',1016,'x222B',1438,'d',125,'ic:0.111,',22,'root',47,'x2294;",',1016,'x2293;",',1016,'x2291;",',1016,'x2292;",',1016,'xA7;",d',125,28,37,'x2020;",d',125,28,37,'x2021;",d',125,28,37,'xB6;",a:0.3,d',125,28,37,'x2663;",',1016,'x2666;",',1016,'x2665;",',1016,'x2660;",',22,'symbol"}],cmex10:[{c',116,'h:0.04,d:1.16,n:','16,',22,'delim1"},{c',119,1812,'17,',22,1815,402,1812,'104,',22,1815,409,1812,'105,',22,'delim1',47,'xF8F0",',1812,'106,',22,'delim1',47,1704,1812,'107,',22,'delim1',47,1709,1812,'108,',22,'delim1',47,1714,1812,'109,',22,1815,1718,1812,'110,',22,1815,1721,1812,'111,',22,'delim1',47,1725,1812,'68,',22,'delim1c',47,1730,1812,'69,',22,'delim1c',24,':"|",','h:0.7,d:0,delim:{rep:','12},',22,1737,1738,1879,'13},',22,1737,136,1812,'46,',22,'delim1b',47,1752,1812,'47,',22,'delim1b','"},{c:"(",','h:0.04,d:1.76,n:','18,',22,'delim2',24,119,1900,'19,',22,'delim2',1899,'h:0.04,d:2.36,n:','32,',22,'delim3"},{c',119,1911,'33,',22,1914,402,1911,'34,',22,1914,409,1911,'35,',22,'delim3',47,1699,1911,'36,',22,'delim3',47,1704,1911,'37,',22,'delim3',47,1709,1911,'38,',22,'delim3',47,1714,1911,'39,',22,1914,1718,1911,'40,',22,1914,1721,1911,'41,',22,'delim3',47,1725,1911,'42,',22,'delim3c',47,1730,1911,'43,',22,'delim3c',24,136,1911,'44,',22,'delim3b',47,1752,1911,'45,',22,'delim3b',1899,'h:0.04,d:2.96,','n:48,',22,'delim4"},{c',119,1989,'n:49,',22,1992,402,1989,'n:50,',22,1992,409,1989,'n:51,',22,'delim4',47,1699,1989,'n:52,',22,'delim4',47,1704,1989,'n:53,',22,'delim4',47,1709,1989,'n:54,',22,'delim4',47,1714,1989,'n:55,',22,1992,1718,1989,'n:56,',22,1992,1721,1989,'n:57,',22,'delim4',47,1725,1989,22,'delim4c',47,1730,1989,22,'delim4c',24,136,1989,22,'delim4b',47,1752,1989,22,'delim4b',24,136,1900,'30,',22,'delim2b',47,1752,1900,'31,',22,'delim2b',47,'xF8EB;",h:0.8,d:0.15,delim:{top:48,bot:64,rep:66},',22,'delim',24,':"&'], + ['#xF8F6',';",h:0.8,d:0.15,delim:{','top:','49,bot:65,rep:67','},tclass:"delim"},{c:"&#','xF8EE',1,2,'50,bot:52,rep:54',4,'xF8F9',1,2,'51,bot:53,rep:55',4,'xF8F0',1,'bot:52,rep:54',4,'xF8FB',1,'bot:53,rep:55',4,'xF8EF',1,2,'50,rep:54',4,'xF8FA',1,2,'51,rep:55',4,'xF8F1',1,2,'56,mid:60,bot:58,rep:62',4,'xF8FC',1,2,'57,mid:61,bot:59,rep:62',4,'xF8F3',1,'top:56,bot:','58,rep:62',4,'xF8FE',1,'top:57,bot:','59,rep:62',4,'xF8F2',1,'rep:63',4,'xF8FD',1,'rep:119',4,'xF8F4',1,'rep:62},tclass:"delim"},{c:"|",','h:0.65,d:0,delim:{top:','120,bot:121',',rep:63},tclass:"','vertical','"},{c:"&#','xF8ED',1,45,'59,rep:62',4,'xF8F8',1,50,'58,rep:62',4,'xF8EC',1,'rep:66',4,'xF8F7',1,'rep:67',4,'x3008;",','h:0.04,d:1.76,n:','28',',tclass:"','delim2c',68,'x3009;",',88,'29',90,91,68,'x2294',';",h:0,d:1,n:','71',90,'bigop1',68,'x2294',';",h:0.1,d:1.5,tclass:"','bigop2',68,'x222E',';",h:0,d:1.11,ic:0.095,n:','73',90,'bigop1c',68,'x222E;",h:0,d:2.22,ic:0.222',90,'bigop2c',68,'x2299',100,'75',90,103,68,'x2299',106,107,68,'x2295',100,'77',90,103,68,'x2295',106,107,68,'x2297',100,'79',90,103,68,'x2297',106,107,68,'x2211',100,'88',90,'bigop1a',68,'x220F',100,'89',90,153,68,'x222B',110,'90',90,113,68,'x222A',100,'91',90,'bigop1b',68,'x2229',100,'92',90,171,68,'x228E',100,'93',90,171,68,'x2227',100,'94',90,103,68,'x2228',100,'95',90,103,68,'x2211;",h:0.1,d:1.6',90,'bigop2a',68,'x220F',106,199,68,'x222B;",h:0,d:2.22,ic:0.222',90,117,68,'x222A',106,'bigop2b',68,'x2229',106,211,68,'x228E',106,211,68,'x2227',106,107,68,'x2228',106,107,68,'x2210',100,'97',90,153,68,'x2210',106,199,68,'xFE3F;",h:0.','722,w:0.65,n:99',90,'wide1',68,239,'85,w:1.1,n:100',90,'wide2',68,239,'99,w:1.65',90,'wide3',68,'x2053;",h:0.','722,w:0.75,n:102',90,'wide1a',68,254,'8,w:1.35,n:103',90,'wide2a',68,254,'99,w:2',90,'wide3a','"},{c:"[",',88,'20',90,'delim2','"},{c:"]",',88,'21',90,272,68,'xF8F0;",',88,'22',90,272,68,'xF8FB;",',88,'23',90,272,68,'xF8EE;",',88,'24',90,272,68,10,'",',88,'25',90,272,'"},{c:"{",',88,'26',90,272,'"},{c:"}",',88,'27',90,272,'"},{c:"",h:0.','04,d:1.16,n:113',90,'root',313,'190',315,'925em',317,'04,d:1.76,n:114',90,320,313,'250',315,'925em',317,'06,d:2.36,n:115',90,320,313,'320',315,'92em',317,'08,d:2.96,n:116',90,320,313,'400',315,'92em',317,'1,d:3.75,n:117',90,320,313,'500',315,'9em',317,'12,d:4.5,n:118',90,320,313,'625',315,'9em',317,'14,d:5.7',90,320,'"},{c:"||",',64,'126,bot:127',',rep:119},tclass:"',67,68,'x25B5;",h:0.','45,delim:{',2,'120',66,'arrow1',68,'x25BF;",h:0.',376,'bot:121',66,380,313,'67',315,'35em; margin-','left:-.5em\\">&#','x256D',';",h:0.','1',90,'symbol',313,'67',315,390,'right:-.5em\\">&#','x256E',393,'1',90,396,313,'67',315,390,391,'x2570',393,'1',90,396,313,'67',315,390,401,'x256F',393,'1',90,396,68,375,'5,delim:{',2,'126',372,'arrow2',68,382,429,'bot:127',372,433,'"}],cmti10:[{c:"Γ",ic:0.133,','tclass:"igreek"},{c:"&','Delta;",',441,'Theta;",','ic:0.094,',441,'Lambda;",',441,'Xi;",ic:0.153,',441,'Pi;",ic:0.164,',441,'Sigma;",ic',':0.12,',441,'Upsilon;",ic:0.111,',441,'Phi;",ic:0.0599,',441,'Psi;",ic:0.111,',441,'Omega;",ic:0.103',90,'igreek','"},{c:"ff','",ic:0.212,krn:{"39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":','14,"108":15},','tclass:"italic"},{c',':"fi','",ic:0.103,',468,':"fl',470,468,':"ffi',470,468,':"ffl',470,468,':"ı",a:0,','ic:0.','0767,',468,':"j",d:0.2,','ic:0.0374,',468,':"`",','tclass:"iaccent"},{c:"&#','xB4;",ic:0.0969,',489,'x2C7;",ic:0.083,',489,'x2D8;",','ic:0.108,',489,'x2C9;",ic:0.103,',489,'x2DA;",tclass:"','iaccent','"},{c:"?",','d:0.17,w:0.46,',468,':"ß",','ic:0.105,',468,':"æ",a:0,','ic:0.0751,',468,':"œ",a:0,',508,468,':"ø",','ic:0.0919,',468,':"Æ",','ic',454,468,':"Œ",','ic',454,468,':"Ø",',445,468,':"?",krn:{"108":-0.','256,"76":-0.321},',468,':"!",','ic:0.124,lig:{"96":','60},',468,':"”",','ic:0.0696,',468,':"#",ic:0.0662,',468,':"$",',468,':"%",ic:0.136,',468,':"&",','ic:0.0969,',468,':"’",','ic:0.124,','krn:{"63":0.','102,"33":0.102},lig:{"39":34},',468,':"(",d:0.2,','ic:0.162,',468,':")",d:0.2,','ic:0.0369,',468,':"*",ic:0.149,',468,':"+",a:0.1,','ic:0.0369,',468,':",",a:-0.3,d:0.2,w:0.278,',468,':"-",a:0,ic:0.0283',',lig:{"45":','123},',468,':".",a:-0.25,',468,':"/",ic:0.162,',468,':"0",ic:0.136,',468,':"1",ic:0.136,',468,':"2",ic:0.136,',468,':"3",ic:0.136,',468,':"4",ic:0.136,',468,':"5",ic:0.136,',468,':"6",ic:0.136,',468,':"7",ic:0.136,',468,':"8",ic:0.136,',468,':"9",ic:0.136,',468,':":",ic:0.0582,',468,':";",ic:0.0582,',468,':"¡",','ic:0.0756,',468,':"=",a:0,d:-0.1,','ic:0.0662,',468,':"¿",',468,':"?",','ic:0.122,','lig:{"96":','62},',468,':"@",ic:0.096,',468,':"A",','krn:{"110":-0.0256,"108":-0.0256,"114":-0.0256,"117":-0.0256,"109":-0.0256,"116":-0.0256,"105":-0.0256,"67":-0.0256,"79":-0.0256,"71":-0.0256,"104":-0.0256,"98":-0.0256,"85":-0.0256,"107":-0.0256,"118":-0.0256,"119":-0.0256,"81":-','0.0256,"84','":-0.0767,"','89',614,'86','":-0.102,"','87',618,'101','":-0.0511,"','97',622,'111',622,'100',622,'99',622,'103',622,'113','":-0.0511','},',468,':"B',470,468,':"C",','ic:0.145,',468,':"D",',445,'krn:{"88','":-0.0256,"','87',646,'65',646,'86',646,'89":-0.','0256},',468,':"E",ic',454,468,':"F','",ic:0.133,krn:{"','111',614,'101',614,'117','":-0.0767,"114":-0.0767,"97":-0.0767,"','65',618,'79',646,'67',646,'71',646,'81":-0.0256','},',468,':"G",ic:0.0872,',468,':"H",ic:0.164,',468,':"I",ic:0.158,',468,':"J",ic:0.14,',468,':"K",',641,'krn:{"79',646,'67',646,'71',646,675,'},',468,':"L",krn:{"84',614,'89',614,'86',618,'87',618,'101',622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"M",ic:0.164,',468,':"N",ic:0.164,',468,':"O",',445,'krn:{"88',646,'87',646,'65',646,'86',646,653,'0256},',468,':"P',470,'krn:{"65":-0.0767},',468,':"Q",d:1,',445,468,':"R",ic:0.0387,',612,'0.0256,"84',614,'89',614,'86',618,'87',618,'101',622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"S",ic',454,468,':"T',660,'121',614,'101',614,'111',666,'117',614,'65":-0.0767},',468,':"U",ic:0.164,',468,':"V",ic:0.','184,krn:{"','111',614,'101',614,'117',666,'65',618,'79',646,'67',646,'71',646,675,'},',468,':"W",ic:0.',789,'65":-0.0767},',468,':"X",ic:0.158,krn:{"79',646,'67',646,'71',646,675,'},',468,':"Y",ic:0.','194',',krn:{"101',614,'111',666,'117',614,'65":-0.0767},',468,':"Z",',641,468,':"[",d:0.1,','ic:0.188,',468,':"“",','ic:0.169,',468,':"]",d:0.1,','ic:0.105,',468,':"ˆ",ic:0.0665,',489,'x2D9;",ic:0.118,',489,'x2018;",',531,'92},',468,':"a','",a:0,ic:0.',483,468,':"b",ic:0.0631',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"c',851,'0565',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"d',470,'krn:{"108":','0.0511},',468,':"e',851,'0751',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"f',466,'12,"102":11,"108":13},',468,':"g','",a:0,d:0.2,ic:0.','0885,',468,':"h",ic:0.',483,468,':"i",ic:0.102,',468,485,641,468,':"k",',495,468,':"l',470,892,'0.0511},',468,':"m',851,483,468,':"n',851,483,'krn:{"39":-0.102},',468,':"o',851,'0631',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"p',919,'0631',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"q',919,'0885,',468,':"r',851,'108',822,622,'97',622,'111',622,'100',622,'99',622,'103',622,'113',634,'},',468,':"s',851,'0821,',468,':"t",ic:0.0949,',468,':"u',851,483,468,':"v',851,'108,',468,':"w',851,1020,892,'0.0511},',468,':"x',851,'12,',468,':"y',919,'0885,',468,':"z',851,'123,',468,':"–",a:0.1,ic:0.','0921',565,'124},',468,':"—",a:0.1,ic:0.','0921,',468,':"˝",',605,489,'x2DC;",ic:0.116,',489,'xA8;",tclass:"',500,'"}],cmbx10:[{c:"&Gamma',';",tclass:"bgreek"},{c:"&','Delta',1056,'Theta',1056,'Lambda',1056,'Xi',1056,'Pi',1056,'Sigma',1056,'Upsilon',1056,'Phi',1056,'Psi',1056,'Omega;",tclass:"bgreek"},{c:"ff','",ic:0.0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15},','tclass:"bold"},{c',':"fi",',1078,':"fl",',1078,':"ffi",',1078,':"ffl",',1078,481,1078,485,1078,':"`',';",tclass:"baccent"},{c:"&#','xB4',1092,'x2C7',1092,'x2D8',1092,'x2C9',1092,499,'baccent',501,1078,504,1078,507,1078,510,1078,513,1078,516,1078,520,1078,524,1078,527,'278,"76":-0.319},',1078,530,606,'60},',1078,534,1078,':"#",',1078,':"$",',1078,':"%",',1078,543,1078,546,548,'111,"33":0.111},lig:{"39":34},',1078,551,1078,554,1078,':"*",',1078,559,1078,':",",a:-0.3,d:0.2,w:0.278,',1078,':"-",a:0',565,'123},',1078,':".",a:-0.25,',1078,':"/",',1078,':"0",',1078,':"1",',1078,':"2",',1078,':"3",',1078,':"4",',1078,':"5",',1078,':"6",',1078,':"7",',1078,':"8",',1078,':"9",',1078,':":",',1078,':";",',1078,596,1078,599,1078,':"¿",',1078,604,606,'62},',1078,':"@",',1078,':"A",krn:{"116','":-0.0278,"','67',1195,'79',1195,'71',1195,'85',1195,'81',1195,'84','":-0.0833,"','89',1207,'86":-0.','111,"87":-0.111},',1078,':"B",',1078,640,1078,':"D",krn:{"88',1195,'87',1195,'65',1195,'86',1195,653,'0278},',1078,':"E",',1078,':"F",krn:{"111',1207,'101',1207,'117','":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.','111,"79',1195,'67',1195,'71',1195,'81":-0.0278','},',1078,':"G",',1078,':"H",',1078,':"I",krn:{"73":0.0278},',1078,':"J",',1078,':"K",krn:{"79',1195,'67',1195,'71',1195,1242,'},',1078,':"L",krn:{"84',1207,'89',1207,1210,'111,"87":-0.111},',1078,':"M",',1078,':"N",',1078,':"O",krn:{"88',1195,'87',1195,'65',1195,'86',1195,653,'0278},',1078,':"P",krn:{"65',1207,'111',1195,'101',1195,'97',1195,'46',1207,'44":-0.0833},',1078,742,1078,':"R",krn:{"116',1195,'67',1195,'79',1195,'71',1195,'85',1195,'81',1195,'84',1207,'89',1207,1210,'111,"87":-0.111},',1078,':"S",',1078,':"T",krn:{"121',1195,'101',1207,'111',1235,'0833,"117":-0.0833','},',1078,':"U",',1078,788,'0139,krn:{"','111',1207,'101',1207,'117',1235,'111,"79',1195,'67',1195,'71',1195,1242,'},',1078,807,1331,'111',1207,'101',1207,'117',1235,'111,"79',1195,'67',1195,'71',1195,1242,'},',1078,':"X",krn:{"79',1195,'67',1195,'71',1195,1242,'},',1078,820,'025',822,1207,'111',1235,1325,'},',1078,830,1078,833,1078,836,1078,839,1078,':"ˆ',1092,'x2D9',1092,846,606,'92},',1078,':"a','",a:0,krn:{"','118',1195,'106":0.','0556,"121',1195,'119":-0.','0278},',1078,':"b",krn:{"101','":0.0278,"','111',1409,'120',1195,'100',1409,'99',1409,'113',1409,'118',1195,1402,'0556,"121',1195,1405,'0278},',1078,':"c',1399,'104',1195,'107":-0.0278},',1078,':"d",',1078,':"e",a:0,',1078,':"f',1076,'12,"102":11,"108":13},',1078,':"g',919,1331,1402,'0278},',1078,':"h",krn:{"116',1195,'117',1195,'98',1195,'121',1195,'118',1195,1405,'0278},',1078,':"i",',1078,485,1078,':"k",krn:{"97":-0.0556,"101',1195,'97',1195,'111',1195,'99":-0.','0278},',1078,':"l",',1078,':"m',1399,'116',1195,'117',1195,'98',1195,'121',1195,'118',1195,1405,'0278},',1078,':"n',1399,'116',1195,'117',1195,'98',1195,'121',1195,'118',1195,1405,'0278},',1078,':"o",a:0',822,1409,'111',1409,'120',1195,'100',1409,'99',1409,'113',1409,'118',1195,1402,'0556,"121',1195,1405,'0278},',1078,':"p",a:0,d:0.2',822,1409,'111',1409,'120',1195,'100',1409,'99',1409,'113',1409,'118',1195,1402,'0556,"121',1195,1405,'0278},',1078,':"q",a:0,d:0.2,',1078,':"r",a:0,',1078,':"s",a:0,',1078,':"t",krn:{"121',1195,1405,'0278},',1078,':"u',1399,1405,'0278},',1078,':"v',851,1331,'97":-0.0556,"101',1195,'97',1195,'111',1195,1471,'0278},',1078,':"w',851,'0139',822,1195,'97',1195,'111',1195,1471,'0278},',1078,':"x",a:0,',1078,':"y',919,1331,'111',1195,'101',1195,'97',1195,'46',1207,'44":-0.0833},',1078,':"z",a:0,',1078,1040,'0278',565,'124},',1078,1045,'0278,',1078,':"˝',1092,'x2DC',1092,1053,1102,'"}]});','jsMath.Setup.Styles','({".typeset .math','":"font-style: ','normal','",".typeset .','italic',1622,1625,1624,'bold":"','font-weight: bold',1624,'cmr10','":"font-family: ','serif',1624,'cal',1633,'cursive',1624,'arrows','":"",".typeset .',380,1641,433,1641,'harpoon','":"font-size: ','125%",".typeset .',396,1641,'symbol2',1641,'delim1',1647,'133',315,'75em',1624,'delim1b',1647,'133',315,'8em; margin',': -.1em',1624,'delim1c',1647,'120',315,'8em',';",".typeset .',272,1647,'180',315,1657,1624,'delim2b',1647,'190',315,1663,': -.1em',1624,91,1647,'167',315,'8em',1671,'delim3',1647,'250',315,'725em',1624,'delim3b',1647,'250',315,1663,': -.1em',1624,'delim3c',1647,'240',315,'775em',1671,'delim4',1647,'325',315,'7em',1624,'delim4b',1647,'325',315,1663,': -.1em',1624,'delim4c',1647,'300',315,'8em',1671,'delim',1641,67,1641,'greek',1641,464,1622,1625,1624,'bgreek":"',1630,1624,103,1647,'133%; ','position: relative; top',': .85em; margin:-.05em',1624,153,1647,'100%; ',1745,': .775em',1671,171,1647,'160%; ',1745,': .7em','; margin:-.1em',1624,113,1647,'125%; ',1745,': .',1657,1759,1671,107,1647,'200%; ',1745,': .',1663,':-.07em',1624,199,1647,'175%; ',1745,1758,1671,211,1647,'270%; ',1745,': .62em',1759,1624,117,1647,'250%; ',1745,1758,'; margin:-.17em',1671,242,1647,'67%; ',1745,':-.8em',1624,247,1647,'110%; ',1745,':-.5em',1624,252,1647,'175%; ',1745,':-.32em',1624,257,1647,'75%; ',1745,1807,1624,262,1647,'133%; ',1745,': -.15em',1624,267,1647,'200%; ',1745,': -.05em',1624,320,1641,'accent":"',1745,': .02em',1624,500,'":"',1745,1837,'; font-style: ',1625,1624,1102,'":"',1745,1837,'; ',1630,'"});',1620,'();jsMath.Macro("not','","\\\\mathrel{\\\\','rlap{\\\\kern 4mu/}}");jsMath.Macro("joinrel',1855,'kern-2mu}");jsMath.Box.DelimExtend=jsMath.Box.DelimExtendRelative;jsMath.Box.defaultH=0.8;'] +]); \ No newline at end of file diff --git a/htdocs/jsMath/jsMath-fonts.tar.gz b/htdocs/jsMath/jsMath-fonts.tar.gz new file mode 100644 index 0000000000..31946bb6f0 Binary files /dev/null and b/htdocs/jsMath/jsMath-fonts.tar.gz differ diff --git a/htdocs/jsMath/jsMath-global-controls.html b/htdocs/jsMath/jsMath-global-controls.html new file mode 100644 index 0000000000..0c5ffe7c1b --- /dev/null +++ b/htdocs/jsMath/jsMath-global-controls.html @@ -0,0 +1,106 @@ + + + + + + + + + + +
      + + + +
      + + + + + + + + + +
      +
      + + + diff --git a/htdocs/jsMath/jsMath-global.html b/htdocs/jsMath/jsMath-global.html new file mode 100644 index 0000000000..1732662f2d --- /dev/null +++ b/htdocs/jsMath/jsMath-global.html @@ -0,0 +1,414 @@ + + + +jsMath Global Frame + + + + + + + + diff --git a/htdocs/jsMath/jsMath-loader-omniweb4.js b/htdocs/jsMath/jsMath-loader-omniweb4.js new file mode 100644 index 0000000000..051110eaf1 --- /dev/null +++ b/htdocs/jsMath/jsMath-loader-omniweb4.js @@ -0,0 +1,39 @@ +/* + * jsMath-loader-omniweb4.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file synchronizes the jsMath-loader.html file with + * the actual loading of the source javascript file. + * OmniWeb 4 has a serious bug where the loader file is run + * several times (and out of sequence), which plays havoc + * with the Start() and End() calls. + * + * --------------------------------------------------------------------- + * + * Copyright 2006 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. + */ + +if (window.jsMath.Autoload) { + jsMath.Autoload.Script.endLoad(); +} else { + if (!window.phase2) { + jsMath.Script.Start(); + window.phase2 = 1; + } else { + jsMath.Script.End(); + jsMath.Script.endLoad(); + } +} \ No newline at end of file diff --git a/htdocs/jsMath/jsMath-loader-post.html b/htdocs/jsMath/jsMath-loader-post.html new file mode 100644 index 0000000000..0de7cb9833 --- /dev/null +++ b/htdocs/jsMath/jsMath-loader-post.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + diff --git a/htdocs/jsMath/jsMath-loader.html b/htdocs/jsMath/jsMath-loader.html new file mode 100644 index 0000000000..ae9c81ed26 --- /dev/null +++ b/htdocs/jsMath/jsMath-loader.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + diff --git a/htdocs/jsMath/jsMath-msie-mac.js b/htdocs/jsMath/jsMath-msie-mac.js new file mode 100644 index 0000000000..cf19f76f07 --- /dev/null +++ b/htdocs/jsMath/jsMath-msie-mac.js @@ -0,0 +1,53 @@ +/* + * jsMath-msie-mac.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for use with MSIE on the Mac. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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. + */ + + + +/* + * MSIE crashes if it changes the page too quickly, so we add a + * delay between processing math entries. Unfortunately, this really + * slows down math in MSIE on the mac. + */ + +jsMath.Add(jsMath,{ + + msieProcess: jsMath.Process, + msieProcessBeforeShowing: jsMath.ProcessBeforeShowing, + + Process: function () { + // we need to delay a bit before starting to process the page + // in order to avoid an MSIE display bug + jsMath.Message.Set("Processing Math: 0%"); + setTimeout('jsMath.msieProcess()',jsMath.Browser.delay); + }, + + ProcessBeforeShowing: function () { + // we need to delay a bit before starting to process the page + // in order to avoid an MSIE display bug + setTimeout('jsMath.msieProcessBeforeShowing()',5*jsMath.Browser.delay); + } + +}); + +jsMath.Browser.delay = 75; // hope this is enough of a delay! diff --git a/htdocs/jsMath/jsMath-old-browsers.js b/htdocs/jsMath/jsMath-old-browsers.js new file mode 100644 index 0000000000..35dcf189a5 --- /dev/null +++ b/htdocs/jsMath/jsMath-old-browsers.js @@ -0,0 +1,58 @@ +/* + * jsMath-old-browsers.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed by older versions of some browsers + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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.Add(jsMath.HTML,{ + /* + * Use the blank GIF image for spacing and rules + */ + Blank: function (w,h,d,isRule) { + var style = ''; + if (isRule) { + if (h*jsMath.em < 1.5) {h = '1px'} else {h = jsMath.HTML.Em(h)} + style = 'border-top:'+h+' solid;'; h = 0; + } + if (d == null) {d = 0} + style += 'width:'+this.Em(w)+'; height:'+this.Em(h+d)+';'; + if (d) {style += 'vertical-align:'+this.Em(-d)} + return ''; + } +}); + +if (jsMath.browser == 'Konqueror') { + + jsMath.Package(jsMath.Box,{Remeasured: function() {return this}}); + + jsMath.Add(jsMath.HTML,{ + Spacer: function (w) { + if (w == 0) {return ''}; + return '' + + ' '; + } + }); + + jsMath.Browser.spaceWidth = this.EmBoxFor('     ').w/5; + +} + +jsMath.styles['.typeset .spacer'] = ''; diff --git a/htdocs/jsMath/jsMath-ww.js b/htdocs/jsMath/jsMath-ww.js new file mode 100644 index 0000000000..b2c9474662 --- /dev/null +++ b/htdocs/jsMath/jsMath-ww.js @@ -0,0 +1,85 @@ +/* + * This file customizes jsMath for use with WeBWorK + */ + +if (!window.jsMath || !window.jsMath.loaded) { + + /* + * WW customization of jsMath values + */ + + var jsMath = { + + styles: { + '.math': 'font-family: serif; font-style: normal; color: grey; font-size: 75%' + }, + Controls: {cookie: {scale: 133}}, + Parser: {prototype: {macros: { + setlength: ['Macro','',2], + boldsymbol: ['Macro','{\\bf #1}',1], + verb: ['Extension','verb'] + }}}, + Font: {}, + + // + // Look for jsMath-ww.js file and replace by jsMath.js + // Cause the jsMath.js file to be loaded + // + wwSource: function () { + var script = document.getElementsByTagName('SCRIPT'); + var src = script[script.length-1].getAttribute('SRC'); + if (src.match('(^|/)jsMath-ww.js$')) { + src = src.replace(/jsMath-ww.js$/,'jsMath.js'); + document.write(''); + } + }, + + wwCount: 0, // count if called more than once + + wwProcess: function () { + if (this.wwCount > 1) return; + if (this.wwCount == 0) { + // + // This is the first call to jsMath, so install handler + // + if (window.addEventListener) {window.addEventListener("load",jsMath.wwOnLoad,false)} + else if (window.attachEvent) {window.attachEvent("onload",jsMath.wwOnLoad)} + else {window.onload = jsMath.wwOnLoad} + // + // Process the page synchronously + // + this.wwProcessWW = jsMath.ProcessBeforeShowing; + } else { + // + // There are multiple calls, so we're in the Library Browser + // Process the page asynchronously + // + this.wwProcessWW = jsMath.Process; + } + this.wwCount++; + }, + + // + // The actual onload handler calls whichever of the two + // processing commands has been saved + // + wwOnLoad: function () {jsMath.wwProcessWW()} + + }; + + if (window.noFontMessage) {jsMath.styles['#jsMath_Warning'] = "display: none"} + if (window.missingFontMessage) {jsMath.Font.message = missingFontMessage} + if (!window.processDoubleClicks) {jsMath.Click = {CheckDblClick: function () {}}} + + // + // Load jsMath.js + // + jsMath.wwSource(); + + // + // Make sure answer boxes are above jsMath output + // (avoids deep baselines in jsMath fonts) + // + document.write(''); + +} diff --git a/htdocs/jsMath/jsMath.js b/htdocs/jsMath/jsMath.js new file mode 100644 index 0000000000..4dc9cf00ba --- /dev/null +++ b/htdocs/jsMath/jsMath.js @@ -0,0 +1,51 @@ +/***************************************************************************** + * + * jsMath: Mathematics on the Web + * + * This jsMath package makes it possible to display mathematics in HTML pages + * that are viewable by a wide range of browsers on both the Mac and the IBM PC, + * including browsers that don't process MathML. See + * + * http://www.math.union.edu/locate/jsMath + * + * for the latest version, and for documentation on how to use jsMath. + * + * Copyright 2004-2010 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. + * + *****************************************************************************/ + +if (!window.jsMath) {jsMath = {}} +if (!jsMath.Script) {jsMath.Script = {}} + +jsMath.Script.Uncompress = function (data) { + for (var k = 0; k < data.length; k++) { + var d = data[k]; var n = d.length; + for (var i = 0; i < n; i++) {if (typeof(d[i]) == 'number') {d[i] = d[d[i]]}} + data[k] = d.join(''); + } + eval(data.join('')); +} + +//start = new Date().getTime(); +jsMath.Script.Uncompress([ + ['if(!','window.','jsMath','||!',1,'jsMath.','loaded','){var ','jsMath_old','=',1,2,';',0,'document.','getElementById','||!',14,'childNodes||!',14,'createElement','){','alert("','The',' mathematics ','on this page requires W3C DOM support in its JavaScript. Unfortunately, your ','browser',' doesn\'t seem to have this.")}','else{',1,2,'={version:"3.6e",document:document,','window',':',32,',','platform',':(','navigator.',36,'.match(/','Mac/)?"mac":',38,36,40,'Win/)?"pc":"unix"),','sizes',':[50,60,70,85,100,120,144,173,207,249],styles:{".math','":{"font-family":"serif","font-style":"normal","font-weight":"normal','"},".typeset',48,'","line-height":"normal','","text-indent":"0px','","white-space":"','normal','"},".typeset .',54,48,'"},"div.','typeset','":{"text-align":"','center",margin:"1em 0px"},"span.',59,60,'left',49,' span',60,'left",border',':"0px",margin:"0px','",padding',':"0px"},"a .',59,' img, .',59,' a img','":{border:"0px','","border-bottom":"','1px solid',' blue;"},".',59,' .size0','":{"font-size":"','50','%"},".typeset .','size1',82,'60',84,'size2',82,'70',84,'size3',82,'85',84,'size4',82,'100',84,'size5',82,'120',84,'size6',82,'144',84,'size7',82,'173',84,'size8',82,'207',84,'size9',82,'249',84,'cmr10','":{"font-family":"jsMath-',121,', serif',55,'cmbx10',122,126,', ',2,'-cmr10',55,'cmti10',122,133,', ',2,131,55,'cmmi10',122,140,55,'cmsy10',122,144,55,'cmex10',122,148,55,'textit','":{"font-family":"','serif","','font-style":"italic',55,'textbf',153,'serif","font-weight":"bold',55,'link":{"','text-decoration":"none',55,'error',82,'90%","',155,'","background-color','":"#FFFFCC',70,':"1px','",border:"',78,' #CC0000',55,'blank','":{display:"','inline-block','",overflow:"','hidden',172,'0px none",width:"0px",height:"0px',55,'spacer',177,178,'"},"#','jsMath_hiddenSpan":{','visibility:"hidden",position:"absolute",','top:"0px",left:"0px',51,52,187,'jsMath_message','":{position:"fixed",bottom:"','1px",left:"2px',168,'":"#E6E6E6','",border:"solid 1px #959595",margin:"0px",padding:"','1px 8px','","z-index":"','102','",color:"black","font-size":"','small",width:"auto','"},"#jsMath_panel',195,'1.75em",right:"1.5em',70,':".8em 1.6em',168,'":"#DDDDDD',172,'outset 2px',201,'103",','width:"auto',203,'10pt","font-style":"',54,205,' .disabled":{color:"#888888',205,' .infoLink',82,'85%"},"#jsMath_panel *":{"','font-size":"inherit","font-style":"inherit","font-family":"inherit',51,205,' div":{"','background-color":"inherit",color:"inherit"},"#jsMath_panel ','span":{"',230,'td',76,70,69,'","',230,'tr',76,70,69,'","',230,'table',76,70,69,168,'":"inherit",color:"inherit",height:"auto",',216,187,'jsMath_button',195,'1px",right:"2px',168,'":"white',199,'0px 3px 1px 3px',201,'102",color:"black","',162,'","font-size":"x-',204,'",cursor:"hand"},"#',253,' *":{padding:"0px",border',69,51,'","',226,187,'jsMath_global":{"',155,187,'jsMath_noFont',' .message":{"text-align":"center",padding:".8em 1.6em",border:"3px solid #DD0000","background-color":"#FFF8F8",color:"#AA0000","font-size":"',204,187,'jsMath_noFont .link":{padding:"0px 5px 2px 5px',172,'2px outset',168,'":"#E8E8E8',203,'80%",',216,265,'jsMath_PrintWarning',277,'x-',204,'"},"@media print":{"#',253,177,'none',187,'jsMath_Warning',177,'none"}},"@media screen":{"#',289,177,'none"}}},Element',':function(','a){','return ',5,14,15,'("jsMath_"+a)},BBoxFor',304,'a','){this.','hidden.innerHTML','=\'<','span class="',59,'"><',316,'scale">\'+a+"";var b=(',5,'Browser.','msieBBoxBug?this.','hidden.firstChild','.firstChild',':this.hidden',');var c={w:','b.offsetWidth',',h',326,'.offsetHeight','};this.',314,'="";',306,'c},EmBoxFor',304,'b){var a=',5,'Global.cache','.R;',0,'a[this.em',']){',343,']={}}',0,343,'][b]){','var c=','this.BBoxFor','(b);',343,'][b]={w:c.w/this.em,h:c.h/this.em}}',306,343,'][b]},Init',':function(){','if(',5,'Setup.inited','!=1){',0,5,361,'){',5,'Setup.','Body()}if(',5,361,'!=1){if(',5,361,'==-100','){return}',22,'It looks like ',2,' failed to set up properly (error code "+',5,361,'+"). I will try to keep going, but it could get ugly.");',5,361,'=1}}this.em=this.CurrentEm();','var a=',5,340,'.B;',0,343,']){',343,']={};',343,'].bb=',351,'("x");',350,343,'].bb.h;',343,'].d=',351,'("x"+',5,'HTML.Strut','(c/this.em)).h-c}',5,322,'italicCorrection=',343,'].ic;var g=',343,'].bb;var e=g.h;var f=',343,'].d;this.h=(e-f)/this.em;this.d=f/this.em;this.hd=this.h+','this.d;this.',368,'TeXfonts','();var b=this.EmBoxFor(\'<',316,121,'">M\').w/2;this.TeX.M_height=b*(26/14);this.TeX.h=this.h;this.TeX.d=',419,'TeX.hd=this.hd;this.Img.Scale();',0,'this.initialized',313,368,'Sizes','();this.','Img.UpdateFonts()}this.p_height=(','this.TeX.cmex10[0].','h+',435,'d)/0.85;',429,'=1},ReInit',358,'if(this.','em!=this.CurrentEm()){this.Init()}},CurrentEm',358,387,351,'(\'\').','w/27;if(a>0){',306,'a}',306,351,'(\'\').w/13},Loaded',358,'if(',8,7,'b=["Process","ProcessBeforeShowing","ProcessElement","ConvertTeX","ConvertTeX2","ConvertLaTeX","ConvertCustom","CustomSearch","Synchronize","Macro","document"];','for(var a=0;a<','b','.length;a++){','if(',8,'[b[a]]){','delete ',8,'[b[a]]}}}if(',8,'){this.Insert(',2,',',8,')}',8,'=null;',5,6,'=1},Add',304,'c,a){for(var b in a){','c[b]=a[b]}},Insert',304,487,'if(c[b]&&typeof(a[b])=="object"&&(','typeof(c[b])=="','object"||',492,'function")){this.Insert(c[b],a[b])}',28,'c[b]=a[b]}}},Package',304,'b,a',476,'b.prototype,a)}};',5,'Global={isLocal:1,cache:{','T:{},D:{},R:{},B',':{}},ClearCache',358,5,340,'={',504,':{}}},GoGlobal',304,338,'String(',5,1,'location',');var d=(',5,'isCHMmode','?"#":"?");if(b){a=a.replace(/\\?.*/,"")+"?"+b}',5,'Controls.','Reload(',5,'root+"',2,'-global.html"+d+escape(a))},Init',358,'if(',5,'Controls.cookie.','global=="always"&&!',5,'noGoGlobal','){if(',38,'accentColorName',376,0,5,32,'){',5,32,'=',32,'}',5,523,6,'=1;',5,523,'defaults.hiddenGlobal=null;this.GoGlobal(',5,523,'SetCookie(2))}},Register',358,387,5,1,'parent;',0,5,520,'){',5,520,'=(',5,1,517,'.protocol','=="mk:")}try{',0,5,520,313,'Domain()}if(a.',2,'&&a.',5,'isGlobal){a.',5,'Register(',5,32,')}}catch(b){',5,535,'=1}},Domain',358,'if(',38,'appName=="Microsoft Internet Explorer"&&',5,36,'=="mac"&&',38,'userProfile','!=null',376,'if(',5,14,'all&&!',5,1,'opera',376,'if(',32,'==parent',376,'var b=',5,14,'domain',';try{while(true){try{if(parent.',14,'title',602,'){return}}','catch(a){}',0,14,619,'.match(/\\..*\\./)){break}',5,14,619,'=',5,14,619,'.replace(/^[^.]*\\./,"")}}',625,5,14,619,'=b}};',5,'Script={request:null,Init',358,'if(!(',5,532,'asynch&&',5,532,'progress',')){if(',1,'XMLHttpRequest','){try{','this.request','=new ',655,'}catch(c){}if(',657,'&&',5,'root.match','(/^file:\\/\\//)){try{',657,'.open("GET",',5,526,5,'js",false);',657,'.send(null)}catch(','c){',657,'=null;if(',1,'postMessage&&',1,'addEventListener',313,'mustPost','=1;',5,1,680,'("message",',5,'Post.','Listener,false)}}}}',0,657,'&&',1,'ActiveXObject','&&!this.',682,7,'a=["MSXML2.XMLHTTP.6','.0","MSXML2.XMLHTTP','.5',700,'.4',700,'.3',700,'","Microsoft.XMLHTTP"];','for(var b=0;b<','a.length&&!',657,';b++){try{',657,658,695,'(a[b])}catch(c){}}}}',0,657,'||',5,368,'domainChanged',313,'Load=this.delayedLoad;this.needsBody=1}},Load',304,'b,a){','if(a){',5,'Message.Set("Loading "+','b);',5,'Script.','Delay(1);',5,'Script.Push(','this,"xmlRequest",b',');',5,734,5,'Message',',"Clear")}',28,5,734,735,')}},xmlRequest',304,'url){','this.blocking','=1;try{',657,667,'url,false);',657,673,'err){',749,'=0;if(',5,'Translate.','restart&&',5,'Translate.asynchronous){return""}throw Error("jsMath can\'t load the file \'"+url+"\'\\','nMessage: "+err.message)}if(',657,'.status',602,'&&(',657,766,'>=400||',657,766,'<0)){',749,'=0;if(',5,760,'restart&&',5,763,'nError status: "+',657,766,')}',0,'url','.match(/\\.js$/)){','return(',657,'.responseText',')}var tmpQueue','=this.queue;this.queue','=[];',5,1,'eval(',657,791,');',749,'=0;','this.queue=this.queue.','concat(','tmpQueue);this.Process();return""},cancelTimeout:30*1000,blocking:0,cancelTimer:null,needsBody:0,queue:[],Synchronize',304,'a,b','){if(typeof(','a)!="string"){',5,734,'null,a,b)}',28,5,734,5,32,',"eval",a)}},Push',304,'a,c,b',313,'queue[','this.queue.length',']=[a,c,b];if(!(',749,'||(this.needsBody&&!',5,14,'body','))){this.Process()}},Process',358,'while(',823,'&&!',749,7,'c=this.queue[0];',803,'slice(1);',387,'this.SaveQueue();var b=c[0];var e=c[1];var d=c[2];if(b){b[e](d)}',28,'if(e){e(d)}}this.','RestoreQueue','(a)}},SaveQueue',358,'var a',793,'=[];',306,'a},',844,304,'a){',803,804,'a)},delayedLoad',304,'a',313,'Push(','this,"','startLoad','",a)},',863,304,'a',7,'b=',5,14,20,'("iframe");b','.style.visibility="',180,'";b.style.','position="absolute";','b','.style.width="','0px";b','.style.height="','0px";if(',5,14,829,325,'){',5,14,829,'.insertBefore(','b,',5,14,829,325,')}',28,5,14,829,'.appendChild(','b)}',749,'=1;this.','url=a;if(a','.substr(0,',5,'root.length',')==',5,'root){a=a.substr(',5,909,')}',5,728,'a);this.cancelTimer=setTimeout("',5,731,'cancelLoad','()",this.cancelTimeout);',442,682,'){','b.src=',5,689,863,'(a,b)}',28,'if(a',788,926,5,526,2,'-loader.html"}',28,926,'this.url}}},','endLoad',304,'a){if(this.cancelTimer){clearTimeout(this.cancelTimer);this.cancelTimer=null}',5,689,942,'();',5,740,'.Clear();if(a!="cancel"){',749,'=0;this.Process','()}},Start',358,'this.tmpQueue',793,'=[]},End',358,803,804,956,');',472,956,'},',921,304,'b,',944,'if(b==null){b','="Can\'t load file"}if(a==null){a=2000}',5,740,'.Set(b);setTimeout(\'',5,731,942,'("cancel")\',a)},Delay',304,'a){',749,'=1;setTimeout("',5,731,'endDelay','()",a)},',986,358,749,953,'()},','imageCount',':0,WaitForImage',304,'b){',749,905,993,'++;',442,'img==null',313,'img=[]}',387,'new Image',433,'img[this.img.length]=a;a.onload=function(){if(--',5,731,993,'==0){',5,731,986,'()}};a.onerror=a.onload;a.onabort=a.onload;a.src=b},Uncompress',304,'data){for(var k=0;k\'+a[65].c','+"").h;a.d=',5,1572,'+',5,408,'(a.hd)+"").h-a.hd;a.h=a.hd-a.d;','if(b=="',140,'"){a.skewchar=','127}',28,1580,144,1582,'48}}},',421,358,466,5,1555,468,'if(',5,1555,'[a]){this.TeXfont(',5,1555,'[a])}}},Sizes',358,5,'TeXparams','=[];var b;var a;for(a=0;a<',5,46,468,5,1604,'[a]={}}for(b in ',5,'TeX',808,5,'TeX[b])!="object"){for(a=0;a<',5,46,468,5,1604,'[a][b]=',5,46,'[a]*',5,'TeX[b]/100}}}},Styles',304,'a){',0,'a){a=',5,'styles;a[".',59,' .scale"]={"font-size":',5,532,'scale+"%"};this.stylesReady=1}',5,734,862,'AddStyleSheet','",a);if(',5,322,'styleChangeDelay','){',5,734,5,'Script,"Delay",1)}},StyleString',304,'e',7,'a={},f;for(f in e){if(typeof e[f]==="string"){a[f]=e[f]}',28,'if(f',907,'1)==="@"){a[f]=','this.StyleString(','e[f])}',28,'if(e[f]!=null',7,'d=[];for(var c in e[f]){if(e[f][c]!=null){d[d.length]=c+": "+e[f][c]}}a[f]=d.join("; ")}}}}var b="";for(f in a){b+=f+" {"+a[f]+"}\\n"}',306,'b},',1642,304,'d',7,'b=',5,14,1379,'head")[0];if(',338,1660,'d);if(',5,14,'createStyleSheet){b.insertAdjacentHTML("beforeEnd",\'<','span style="display:','none">x")}',28,350,5,14,20,'("style");c.type="text/css";c',902,5,14,1162,'(a));b',902,'c)}}',28,0,5,'noHEAD){',5,'noHEAD=1;',22,'Document is missing its section. Style sheet can\'t be created without it.")}}},Body',358,442,'inited',376,'this.inited=-','1;',5,368,'Hidden();',1710,'2;',5,322,1184,1710,'3;if(',5,532,176,'){',5,740,'.Blank()}',1710,'4;',5,368,'Styles();',1710,'5;',5,523,1184,1710,'6;',5,734,5,'Setup,"User","pre-font");',1710,'7;',5,734,5,'Font,"Check");if(',5,'Font.register.length){',5,734,5,'Font,"LoadRegistered")}this.inited=1},User',304,'a){if(',5,'Setup.UserEvent[a',']){(',5,1760,'])()}},UserEvent:{"pre-font":null,onload:null}};',5,'Update={',421,304,'d){for(var a in d){for(var b in d[a]){for(var c in d[a',349,5,'TeX[a][b][c]=d[a][b][c]}}}},TeXfontCodes',304,'c){',1320,708,'c[a].',1384,5,'TeX[a][b].c=c[a][b]}}}};',5,'Browser={allowAbsolute:1,allowAbsoluteDelim:0,','separateSkips',':0,valignBug:0,operaHiddenFix:"",','msieCenterBugFix',':"",','msieInlineBlockFix',':"",msieSpaceFix:"",imgScale:1,renameOK:1,',1646,':0,delay:1,','processAtOnce',':16,version:0,','TestSpanHeight',358,5,314,'=\'<','span style="\'+this.block','+\';height:2em',';width:1px','">x','\';var b=',5,324,';',387,'b',325,';this.spanHeightVaries=(b',331,'>=a',331,'&&b',331,'>0);this.spanHeightTooBig=(b',331,'>a',331,');',5,314,'=""},','TestInlineBlock',358,'this.block="display',':-','moz-inline-box";','this.hasInlineBlock','=',5,'BBoxFor(\'\').w>0;if','(',1828,'){',5,'styles[".typeset',' .',176,'"].display="-',1827,472,5,1837,' .spacer"].display','}',28,1825,':',178,'";',1828,'=',5,1831,1832,'(!',1828,624,'this.block+=";overflow:',180,'";var d=',5,'BBoxFor("x").h;this.mozInlineBlockBug=',5,1831,'+";height:"+d+\'px;width:1px',1801,'<',1798,'+";height:"+d+"px',1800,';vertical-align:-"+d+\'px',451,'h>2*d;this.widthAddsBorder=',5,1831,'+\';overflow:',180,';height:1px;width:10px',';border-left:','10px solid',451,'w>10;',350,5,1831,1867,1801,'\').h,b=',5,1831,1867,1881,78,1801,'\').h,a=',5,1831,1799,451,'h;this.msieBlockDepthBug=(c==d);','this.msieRuleDepthBug','=(b==d',');this.blankWidthBug=(','a==0)},','TestRenameOK',358,5,314,'="";',387,5,324,';a.setAttribute("name","','jsMath_test','");this.renameOK=(',5,14,'getElementsByName("',1916,'").length>0);',5,314,1822,'TestStyleChange',358,5,314,'=\'x\';var b=',5,324,';',387,328,';',5,368,1642,'({"#',1916,'":"font-size:200%"});this.',1646,'=(',328,'==a);',5,314,1822,'VersionAtLeast',304,338,1430,'this.version',').split(".");','b=',1430,'b',1957,'if(b[1]==null){b[1]="0"}',306,'a[0]>b[0]||(a[0]==b[0]&&a[1]>=b[1])},Init',358,5,26,'="unknown";this.',1823,433,1793,433,1907,433,1926,433,'MSIE',433,'Mozilla',433,'Opera',433,'OmniWeb',433,'Safari',433,'Konqueror();if(','this.allowAbsoluteDelim','){',5,'Box.DelimExtend=',5,'Box.DelimExtendAbsolute;',5,'Box.Layout=',5,'Box.LayoutAbsolute}',28,5,'Box.DelimExtend=',5,'Box.DelimExtendRelative;',5,'Box.Layout=',5,'Box.LayoutRelative}',442,1783,'){',5,'HTML.Place=',5,'HTML.','PlaceSeparateSkips',';',5,'Typeset.prototype.','Place=',5,2017,2014,'}},MSIE',358,'if(',5,'BBoxFor("").w','){',5,26,'="MSIE";if(',5,36,'=="pc"){this.','IE7=(',1,655,'!=null);this.','IE8=(',5,2026,'gte IE 8]>IE8',2028,'>0);','this.isReallyIE8','=(',5,14,'documentMode',2039,'quirks=(',5,14,'compatMode=="BackCompat");','this.msieMode','=(',5,14,2050,'||(this.quirks?5:7));this.msieStandard6=!this.quirks&&!this.IE7;',1988,905,1783,'=1',';this.buttonCheck=1;this.','msieBlankBug=1;this.','msieAccentBug',905,'msieRelativeClipBug','=1;this.msieDivWidthBug=1;this.',1271,905,'msieIntegralBug',905,'waitForImages',905,'msieAlphaBug','=!this.IE7;this.','alphaPrintBug',2079,1785,'="position:relative; ";this.',1787,'=" display:',178,';";this.msie8HeightBug=this.msieBBoxBug=(',2056,'==8',1905,2056,'!=8);this.msieSpaceFix=(',2046,'?\'<',1683,178,'; ','margin-right:-1px; width:1px">\':\'‹\',"',54,'"];',5,2137,'width="42em";',5,532,'printwarn=0}}this.',1791,'=Math.max(Math.floor((this.',1791,'+1)/2),1);',5,2103,'not',2105,2121,'kern3mu/}}");',5,'Macro("angle","\\\\raise1.','84pt','{\\\\kern2.5mu\\\\rlap{\\\\scriptstyle/}\\\\','kern.5pt\\\\','rule{.4em}{-','1.5pt}{1.84pt}\\\\kern2.5mu}")}},',1979,358,'if(',5,180,'.ATTRIBUTE_NODE&&',5,1,'directories){',5,26,'="',1979,'";if(',5,36,2035,2080,'=1}',1988,'=1;',5,2144,'cursor=',5,2179,'cursor="pointer",',5,2103,'not',2105,2121,'kern3mu/}}");',5,2271,'34pt',2273,2275,'1pt}{1.34pt}\\\\kern2.5mu}");if(',38,'vendor=="Firefox"){',1956,'=',38,'vendorSub}',28,'if(',38,'userAgent.match(" Firefox/([0-9.]+)([a-z ]|$)")){',1956,'=RegExp.$1}}',442,1952,'("3.0")){this.mozImageSizeBug=this.lineBreakBug=1}}},',1983,358,'if(',38,'accentColorName){',5,26,'="',1983,'";','this.allowAbsolute','=',1828,';',1988,'=',2341,';this.valignBug=!',2341,2066,1191,'=1;',5,'noChangeGlobal=1;',0,1828,'){',5,368,'Scri'], + ['pt("','jsMath','-old-browsers.js','")}}},Opera',':function(){','if(this.','spanHeightTooBig){',1,'.browser="','Opera";','var b=','navigator.userAgent.match','("Opera 7");','this.allowAbsolute=0;this.','delay=10;this.operaHiddenFix="[Processing]";if(b){',1,'.Setup.','Script','("',1,2,'")}','var a=','navigator.appVersion.match','(/^(\\d+\\.\\d+)/);if(a){','this.version=a','[1]}else{this.vesion=0}this.operaAbsoluteWidthBug=this.operaLineHeightBug=(a[1]>=9.5&&a[1]<9.6)}},Safari',4,'if(',23,'(/Safari\\//)){',1,8,'Safari";if(navigator.vendor','.match(/','Google/)){',1,8,'Chrome"}',22,11,'("Safari/([0-9]+)");a=(a)?a[1]:400;',25,';',1,'.TeX.','axis_height','+=0.05;this.','allowAbsoluteDelim=','a>=125;this.safariIFRAMEbug=a>=312&&a<412;this.safariButtonBug=a<412;this.safariImgBug=1;this.textNodeBug=1;this.lineBreakBug=a>=526;this.buttonCheck=a<500;this.styleChangeDelay=1;',1,'.Macro("not","\\\\mathrel{\\\\rlap{\\\\kern3.25mu/}}")}},','Konqueror',4,'if(','navigator.product','&&',55,'.match("',52,'")){',1,8,52,'";',13,48,'0;if(',11,'(/',52,'\\/(\\d+)\\.(\\d+)/)){if(RegExp.$1<3||(RegExp.$1==3&&RegExp.$2<3)){this.separateSkips=1;this.valignBug=1;',1,16,17,'("',1,2,'")}}',1,'.Add(',1,'.styles,{".typeset .cmr10":"','font-family: ',1,'-','cmr10, ',1,' cmr10',', serif','",".typeset .','cmbx10":"',83,1,'-cmbx10, ',1,' cmbx10, ',1,'-',86,1,88,90,'cmti10":"',83,1,'-cmti10, ',1,' cmti10, ',1,'-',86,1,88,90,'cmmi10":"',83,1,'-cmmi10, ',1,' cmmi10',90,'cmsy10":"',83,1,'-cmsy10, ',1,' cmsy10',90,'cmex10":"',83,1,'-cmex10',', ',1,' cmex10"});',1,'.Font.testFont','="',1,132,', ',1,' cmex10"}}};',1,'.Font={testFont:"',1,132,'",fallback:"symbol",register:[],message:"No ',1,' TeX fonts ','found -- using',' image fonts instead','.
      \\nThese may be slow and might not print well.
      \\nUse the ',1,' control panel to get additional information','.",','extra_message',':\'Extra',150,'not found:
      Using',152,'. This may be slow and might not print well.
      \\nUse the ',1,155,'.\',','print_message',':"To print higher-resolution math symbols, click the
      \\nHi-Res Fonts for Printing button on the ',1,' control panel.
      \\n",','alpha_message',':"If the math symbols print as black boxes, turn off image alpha channels
      \\nusing the Options pane of the ',1,169,'Test1',':function(','c,','f,d,e){if(f==null){f=124}if(d==null){d=2}if(e==null){e=""}var b=jsMath.BBoxFor(\'\'+jsMath.TeX[c][f].c+"");var a=jsMath.BBoxFor(\'\'+jsMath.TeX[c][f].c+"");return(','b.w>d*a.w&&b.h!=0)},Test2',175,'c,',177,'a.w>d*b.w&&b.h!=0)},CheckTeX',4,22,1,'.BBoxFor(\'\'+',1,45,'cmex10[1].c+"");',1,'.nofonts','=((a.w*3>a.h||a.h==0)&&!this.Test1("cmr10",null,null,"',1,'-"));if(!',1,196,'){return}},Check',4,22,1,'.Controls.','cookie;this.CheckTeX();if(',1,196,'){if(a.autofont||a','.font=="tex"){','a.font=this.fallback;if(a.warn){',1,'.nofontMessage=1;a.warn=0;',1,206,'SetCookie(0);if','(',1,'.window.NoFontMessage','){',1,220,'()}else{','this.Message(this.','message)}}}}else{if(a.autofont){a.font="tex"}if(a',211,'return}}if(',1,'.noImgFonts){','a.font="unicode"}if(a','.font=="unicode','"){',1,16,17,'("',1,'-fallback','-"+',1,'.platform+".js");',1,'.Box.TeXnonfallback=',1,'.Box.TeX',';',1,246,'=',1,'.Box.TeXfallback;return}','if(!a.print&&a.printwarn){this.','PrintMessage','((',1,'.Browser.','alphaPrintBug&&',1,206,'cookie.alpha)?this.',166,'+this.',170,':this.',166,')}if(',1,257,'waitForImages){',1,'.Script.','Push(',1,'.',17,',"WaitForImage",',1,'.blank)}if(a.font=="symbol"){',1,16,17,'("',1,239,'-symbols.js");',1,'.Box.TeXnonfallback=',1,246,';',1,246,'=',1,252,1,'.Img.SetFont','({cmr10:["all"],cmmi10:["all"],cmsy10:["all"],cmex10:["all"],cmbx10:["all"],cmti10:["all"]});',1,'.Img.LoadFont','("cm-fonts")},Message',175,'a){if(',1,'.Element("Warning',60,'return}',10,1,16,'DIV("Warning','",{});b.innerHTML=\'
      \'+a','+\'
      ',1,323,'


      \'},',331,4,22,1,306,'");if(a','){a.style.display="none"}},',254,175,304,1,'.Element','("PrintWarning',60,308,10,1,16,'DIV',350,313,315,'+\'',1,'
      \';if(!',1,'.Global.','isLocal&&!',1,'.noShowGlobal){a',426,'+=\'Global \'}if(a.offsetWidth<30){a.style.width="auto"}if(!','this.cookie','.button',344,'MoveButton',4,'var a=(',1,257,'quirks?document.body:document.documentElement);',1,'.fixedDiv.style.','left=a.scrollLeft+"px";',1,554,'top=a.scrollTop+a.clientHeight+"px";',1,554,'width=a.clientWidth+"px"},GetCookie',4,'if(','this.defaults','==null){',564,'={}}',1,80,564,',',544,');this.userSet={};var c=',1,'.document.cookie',';if(',1,'.window.location','.protocol.match(this.',481,')){c=this.','localGetCookie','();this.','isLocalCookie','=1}if(c',34,1,'=([^;]+)/)){var d=unescape(RegExp.$1).split(/,/);for(',10,'0;b\'},Blank',941,'b,f,i,g){var a','="";var e="";','if(g){e+="border-left',':"+this.Em(','b)+" solid;";if(',1023,'widthAddsBorder){b=0}}if(b==0){if(',1023,'blankWidthBug){if(',1023,'quirks','){e+="width:1px',';";a=\'<',1035,'right:-1px">\'}else{if(!g',1050,';margin-right:-1px;"}}}}else{e+="width',1042,'b)+";"}if(i==null){i=0}if(f){var c=this.Em(f+i);if(g&&f*jsMath.em<=1.5){c="1.5px";f=1.5/jsMath.em}e+="height:"+c+";"}if(',1023,'mozInlineBlockBug){i=-f}if(',1023,'msieBlockDepthBug','&&!g){i-=jsMath.d}if(i){e+="','vertical-align:"+','this.Em(-i)}return a+\'\'},Rule',941,'a,b){if(b==null){b','=jsMath.TeX.default_rule_thickness}',1001,'Blank(a,b,0,1)},Strut',941,'a){',1001,'Blank(1,a,0,1)},msieStrut',941,'a){return','\'\'},Class',941,'a,b){return\'\'+b+""},Place',941,'b,a,d){',990,'a)<0.0001){a=0}',990,'d)<0.0001){d=0}if(a||d){var c=\'<','span style="position',': relative',';\';if(a){c+=" margin-left',1042,'a)+";"}if(d){c+=" top:"+this.Em(-d)+";"}b=c',1081,'>"}return b},PlaceSeparateSkips',941,'e',',g,f,i,a,h){if(Math.abs(g)<0.0001){g=0}if(Math.abs(f)<0.0001){f=0}','if(f){var d=0;var c=0;','var b="";if(','i!=null){c=','a-h;d=i;b=" width',1042,'a-i)+";"}e=','this.Spacer','(d-c)+\'<',1089,1090,'; top:\'+this.Em(-f)+";left',1042,'c',')+";"+b+\'">\'+',1105,'(-d)+e+',1105,'(c)+""}if(g){e=',1105,'(g)+e}return e},PlaceAbsolute',941,'d',1098,'var c',1040,1100,1023,'msieRelativeClipBug&&',1101,1105,'(-i);g+=i;e=',1105,'(a-h)}if(',1023,'operaAbsoluteWidthBug){b=" width: "+this.Em(h+2)}d=\'<',1089,':absolute; ',1036,'g)+"; top',1042,'f',1112,'c+d+e+" ";','return d},','Absolute',941,'b,a,c,e,f){if(f!="none"){',990,'f)<0.0001){f=0}b=\'<',1089,1135,'top:\'+','jsMath.HTML.','Em(f)+\'; left:0em;">\'+b+" "}if(e=="none"){e=0}b+=this.Blank((',1023,'lineBreakBug','?0:a),c-e,e);if(',1023,'msieAbsoluteBug){b=\'<',1089,':relative',';">\'+b+""}b=\'<',1089,1159,';\'+',1023,'msieInlineBlockFix',1081,'>";if(',1023,1154,'){b=\'\'+b+""}return b}};jsMath.Box=function(c,f,a,b,e){','if(e==null){','e=jsMath.d}this.type="typeset";this.w=a;this.h=b;this.d=e;this.bh=b;this.bd=e;this.x=0;this.y=0;this.mw=0;this.Mw=a;this.html=f;this.format=c};jsMath.Add(jsMath.Box,{defaultH:0,Null',959,'return ','new jsMath.Box("','null","",0,0,0)},Text',941,'l,k,b,m,j,i){var g=','jsMath.Typeset.AddClass(','k,l);g=','jsMath.Typeset.','AddStyle(','b,m,g);var c','=jsMath.EmBoxFor(','g);',960,1183,'TeX(b,m);var h=((k=="cmsy10"||k=="cmex10")?c.h-e.h:e.d*c.h/e.hd);var f=',1177,'text",l,c.w,c.h-h,h);f.style=b;f.size=m;f.tclass=k;if(i!=null){f.d=i*e.scale}else{f.d=0}if(j==null||j==1){f.h=0.9*e.M_height}else{f.h=1.1*e.x_height+e.scale*j}return f},TeX',941,'g,a,d,b){var h=',965,'a][g];if(h.d==null){h.d=0}if(h.h==null){h.h=0}if(h.img!=null&&h.c!=""){this.TeXIMG(a,g,',1183,'StyleSize(d,b))}var f=',1183,'TeX(d,b).scale;',960,1177,'text",h.c,h.w*f,h.h*f,h.d*f);e.style=d;e.size=b;if(h.tclass){e.tclass=h.tclass;if(h.img){e.bh=h.img.bh;e.bd=h.img.bd}else{e.bh=f*jsMath.h;e.bd=f*jsMath.d}}else{e.tclass=a;e.bh=f*',965,'a].h;e.bd=f*',965,'a].d;if(',1023,'msieFontBug&&','e','.html.match(/&#/)){','e','.html+=\'x\'}}return e},TeXfallback',941,'b,f,e,o){var m=',965,'f][b];if(!m.tclass){m.tclass=f}if(m.img!=null){',1001,'TeXnonfallback(b,f,e,o)}if(m.h!=null&&m.a==null){m.a=m.h-1.1*jsMath.TeX.x_height}var n=m.a;var l=m.d;var k=this.Text(m.c,m.tclass,e,o,n,l);var g=',1183,'TeX(e,o).scale;if(m.bh!=null){k.bh=m.bh*g;k.bd=m.bd*g}else{var j=k.bd+k.bh;var i=',1181,'k.tclass,k.html);i=',1183,1184,'e,o,i);k.bd',1186,'i+',1151,'Strut(j)).h-j;k.bh=j-k.bd;if(g==1){m.bh=k.bh;m.bd=k.bd}}if(jsMath.',1209,'k',1211,'k',1213,'>\'}return k},TeXIMG',941,'f,a,t){var o=',965,'f][a];if(o.img.size!=null&&o.img.size==t&&o.img.best!=null&&o.img.best==','jsMath.Img.best','){return}var g=(',939,'.scale!=1);var b=',1242,'+t-4;if(b<0){b=0;g=1','}else{if(','b>=','jsMath.Img.fonts','.length){','b=',1250,'.length-1',';g=1}}var s=',939,'[',1250,'[b]];var k=s[f][a];var i=1/',939,'.w[',1250,'[b]];if(b!=',1242,'+t-4){if(o.w!=null){i=o.w/k[0]}else{i*=',1250,'[t]/',1250,'[4]*',1250,'[',1242,']/',1250,'[b]}}var p=k[0]*i;var l=k[1]*i;var n=-k[2]*i;var q;var j=(o.w==null||Math.abs(o.w-p)<0.01)?"":" margin-right:"+',1151,'Em(o.w-p)+";";var e="";a=',939,'.HexCode[a];if(!g&&!',997,'scaleImg){if(',1023,'mozImageSizeBug||2*p"}else{',1326,'m+\'" ',1065,1023,1330,'+\'" />\'}o.tclass="normal";o.img.bh=l+n;o.img.bd=-n;o.img.size=t;o.img.best=',1242,'},Space',941,1077,' ','new jsMath.Box("html",',1151,'Spacer(a),a,0,0)},Rule',941,'a,c){if(c==null){c',1069,'var b=',1151,'Rule(a,c);return ',1343,'b,a,c,0)},GetChar',941,'b,a){var d=',965,'a][b];','if(d.img!=null){this.TeXIMG(a,b',',4)}if(d.tclass',999,'.tclass=a}if(!d.computedW){d.w',1186,1181,'d.tclass,d.c)).w',';if(d.h',999,'.h=jsMath.Box.defaultH}if(','d.d',999,'.d=0}d.computedW=1}',1142,'DelimBestFit',941,'e,j,d,g){if(j==0&&d==0){return null}var i;var f;d','=jsMath.TeX.fam[','d];var a=(g.charAt(1)=="S");var b=(g.charAt(0)=="S");while(j!=null){i=',965,'d][j];if(i.h==null){i',1367,'i.d==null){i.d=0}f=i.h+i.d;if(i.delim','){return[j,d',',"",e]}if(a&&0.5*f>=e',1381,',"SS",0.5*f]}if(b&&0.7*f>=e',1381,',"S",0.7*f]}if(f>=e||i.n==null',1381,',"T",f]}j=i.n}return null},DelimExtendRelative',941,'k,x,r,A,e){var s=',965,'r][x];var q','=this.GetChar(s.delim.','top?s','.delim.top',':s.delim.rep,r);var ','b',1393,'rep,r);var p',1393,'bot?s','.delim.bot',1396,'f=',1181,'b.tclass,b.c);','var l=b.w;var v=b.h+b.d;var g;var d;var m;var o;var u;var t;if(s','.delim.mid){var ','z',1393,'mid,r);t','=Math.ceil((k-(','q.h+q.d)-(','z.h+z.d)-(p.h+p.d))/(2*(b.h+b.d',')));k=2*','t*(b.h+b.d)+(q.h+q.d)+(','z.h+z.d)+(p.h+p.d);if(e){g=0}else{g=k/2+A}d=g;m=',1151,'Place(',1181,'q.tclass,q.c),0,g-q.h',')+',1151,1419,1181,'p.tclass,p.c),-(','q.w+p.w)/2,g-(k-p.d))+',1151,1419,1181,'z.tclass,z.c),-(p.w+z.w)/2,g-(k+z.h-z.d)/2);o=(l-z','.w)/2;if(Math.abs(o)<0.0001){o=0}if(o){m+=jsMath.HTML.Spacer(o)}g-=q.h+q.d+b.h;for(u=0;uv[E]){v[E]=o[C].h}if(o[C].d>x[E]){x[E]=o[C].d}if(C>=g',1251,'g[C]=o[C].w',1248,'o[C].w>g[C]){',1608,'}}if(o[C].bh>f){f=o[C].bh}if(o[C].bd>m){m=o[C].bd}}}if(l[J','.length]==null){','l[J.length]=0}if(f==e){f=0}if(m==e){m=0}var a=G','*(jsMath.hd-0.01)*','L;var u=(p||1)*L/6;var t="";var n=0;var I=0;var s;var F;var q;var r;var c;var b;for(C=0;C0){r',1574,'(c,"T",z);t+=',1151,1419,'r.html,I,0);I=g[C]-r.w+k[C]}else{I+=k[C]}}s=-k[g',1254,'];q=(v',1254,')*u+l[0];for(E=0;Eq[A]){q[A]=g[v].h}if(g[v].d>s[A]){s[A]=g[v].d}if(v>=c',1251,'c[v]=g[v].w',1248,'g[v].w>c[v]){',1658,'}}}}if(f[E',1613,'f[E.length]=0}l=(q',1254,')*p+f[0];for(A=0;A")}if(!h.match(/\\$|\\\\\\(/)){',1001,'Text(this.safeHTML(h),"normal","T",l).Styled()}',960,'0;var d=0;var g;var f="";var a=[];var b,j;while(e/g,">")}','return ','a},Set',1,'c,b,a',',d){if(','c&&c.type){if(c','.type=="','typeset"){',5,'c}if(c',11,'mlist"){c.mlist.','Atomize','(b,a);',5,'c.mlist','.Typeset(','b,a)}if(c',11,'text"){c=this.Text(c.text,c.tclass,b,a,c.ascend||null,c.descend||null);if(d!=0){c','.Styled()}',5,'c}c=this','.TeX(','c.c,c.font,b,a);if(d!=0){c',25,5,'c}',5,3,'Box.Null','()},SetList',1,'d,f,c){var a=[];var g;','for(var b=0;b<','d.length;b++){g=d[b];if(g',11,12,'g=','jsMath.mItem',21,'g)}a[a','.length]=','g}var e=new ','jsMath.Typeset','(a);',5,'e',21,'f,c)}});',3,'Package(',3,'Box,{Styled',':function(){','if(this.','format=="text"){','this.html','=',49,'.AddClass(','this.tclass',',',62,');',62,'=',49,'.AddStyle(','this.style',',this.size,',62,');delete ',66,';delete ',74,';this.format="html"}',5,'this},Remeasured',59,60,'w>0){var a=this.w;this.w=',3,'EmBoxFor(',62,').w;',60,'w','>this.Mw){this.Mw=this.w','}a=this.w/a;if(Math.abs(a-1)>0.05){this.h*=a;this.d*=a}}',5,'this}});',44,'=function(a',',b','){this.type','=a;',3,'Add(this,b)};',3,'Add(',44,',{Atom',1,'b,a){',5,'new ',44,'(b,{atom:1,nuc',':a})},TextAtom',1,'e,h,c,b,g','){var f=','new ',44,'(e,{atom:1,nuc:{type:"text",text:h,tclass:c}});if(b','!=null){f.nuc.','ascend=b}if(g',121,'descend=g}',5,'f},TeXAtom',1,'b,d,a){',5,'new ',44,113,':{type:"TeX",c:d,font:a}})},Fraction',1,'b,a,f,d,e,c){',5,'new ',44,'("fraction",{from:b,num:a,den:f,','thickness',':d,left:e,right:c})},Space',1,'a){',5,'new ',44,'("space",{w:a})},','Typeset',1,'a){',5,'new ',44,'("ord",{atom:1,nuc:a})},HTML',1,'a){',5,'new ',44,'("html",{html:a})}});',3,'mList','=function(d,a,b,c){','if(d){','this.mlist','=d}else{',165,'=[]}','if(c==null){c','="T"}if(b==null){b=4}','this.data','={openI:null,overI:null,overF:null,font:a,','size:b,style:c','};this.init={',173,'}};',3,'Package(',3,162,',{Add',1,'a){return(',165,'[','this.mlist.length',']=a)},Get',1,'a){',5,165,'[a]},Length',59,5,186,'},Last',59,'if(',186,'==0){',5,'null}',5,165,'[',186,'-1]},Range',1,'b,',2,'a==null){a=',186,'}',5,'new ',3,162,'(',165,'.slice(b,a+1))},Delete',1,'d,c){',169,'=d}if(',165,'.splice){',165,'.splice(d,c-d+1)}else{var a=[];',39,186,';b++){if(bc){a[a',47,165,'[b]}}',165,'=a}},Open',1,'d){var c=this.Add(new ',44,'("boundary",{','data:',171,'}));var a=',171,';',171,'={};for(var b in a){',171,'[b]=a[b]}delete ',171,'.overI',79,171,'.overF;',171,'.openI=',186,'-1;if(d!=null){c.left=d}',5,'c},Close',1,'c){if(c!=null){c=new ',44,240,'right:c})}var e;var b=',171,'.openI;var f=',171,'.overI;var ','g=',171,254,171,'=',165,'[b].data;if(f){e=',44,'.Fraction(','g.name,{','type:"mlist",mlist:this.Range(','b+1,f-1)},{',280,'f)},g.',140,',g.left,g.right);if(c){var a=new ',3,162,'([',165,'[b],e,c]);e=',44,'.Atom("inner",{type:"mlist",mlist:a})}}else{var d=b+1;if(c){this.Add(c);d--}e=',44,'.Atom((c)?"inner":"ord",{',280,'d)})}this.Delete(b,this.Length());',5,'this.Add(e)},Over',59,'var b=',171,269,'c=',171,254,'var a=',44,278,'c.name,{',280,'open+1,b-1)},{',280,'b)},c.',140,',c.left,c.right);',165,'=[a]},',17,1,222,'var a;var e="";',74,'=d;this.size=c;',39,186,';b++){a=',165,'[b];a.delta=0;if(a',11,'choice"){',165,'=this.',17,'.choice(',74,',a,b,',165,');b--}else{',60,17,'[a.type]){var g=this.',17,'[a.type];g(',74,75,'a,e,this,b)}}e=a}if(a&&a',11,'bin"){a','.type="ord"}','if(',186,'>=2&&a',11,'boundary"&&',165,'[0].type=="boundary"){this.','AddDelimiters','(d,c)}},',357,1,'b,q){var p=-10000;var g=p;var k=p;for(var f=0;f<',186,';f++){var j=',165,'[f];if(j.atom||j',11,'box"){g','=Math.max(','g,j.nuc.h+j.nuc.y);k',368,'k,j.nuc.d-j.nuc.y)}}var e=',3,'TeX;var l=',49,28,'b,q).','axis_height',';var o',368,'g-l,k+l);var n',368,'Math.floor(e.integer*o/500)*e.delimiterfactor,e.integer*(2*o-e.delimitershortfall))/e.integer;var c=',165,'[0];var m=',165,'[',186,'-1];c.nuc=',3,'Box.Delimiter(','n,c.left,b);m.nuc=',3,390,'n,m.right,b);c.type="open";c.atom=1',79,'c.left;m.type="close";m.atom=1',79,'m.right},',148,1,'c,a){var b=new ',49,'(',165,');',5,'b',21,'c,a)}});',3,'Add(',3,162,'.prototype.',17,',{style',1,'d,c,a,e,b){b.','style=a.style},size',1,418,'size=a.size},phantom',1,8,'){var d=','a.nuc','=jsMath.Box.Set(','a.phantom',',c,b);if(a.h){d.','Remeasured();','d.html=',3,'HTML.Spacer','(d.w)}else{d.html="",d.w=d.Mw=d.mw=0}if(!a.v){','d.h=d.d=0','}d.bd=d.bh=0',79,428,';a.type="box"},','smash',1,8,425,'a.nuc',427,'a.smash',',c,b).',430,435,79,446,439,'raise',1,8,'){a.nuc',427,'a.nuc,c,b);var ','d=a.raise;','a.nuc.html','=',3,'HTML.Place(',460,',0,d,a.nuc.mw,a.nuc.Mw,a.nuc.w);a.nuc.h+=d;a.nuc.d-=d;a','.type="ord";','a.atom=1},lap',1,'d,c,a){var ','e',427,'a.nuc,','d,c).',430,'var b=[e];if(a.lap=="llap"){e.x=-e.w}','else{if(a.lap=="','rlap"){b[1]=',44,'.Space(-e.w)}',476,'ulap"){e.y=e.d;e.h=e.d=0}',476,'dlap"){e.y=-e.h;e.h=e.d=0}}}}a','.nuc=jsMath.Box.SetList','(b,d,c);if(a.lap=="ulap"||a.lap=="dlap"){a.nuc.h=a.nuc.d=0}a.type="box";delete a.atom},bin',1,'d,b,a,e){if(e&&e.type){var c=e.type;if(c=="bin"||c=="op"||c=="rel"||c=="open"||c=="punct"||c==""||(c=="',354,'e.left!="")){a.type="ord"}}else{a',349,'jsMath.mList.prototype.Atomize.SupSub(','d,b,a)},rel',1,8,9,'d.type&&','d',11,'bin"){d',349,491,8,')},close',1,8,9,496,'d',11,'bin"){d',349,491,8,')},punct',1,8,9,496,'d',11,'bin"){d',349,491,8,')},open',1,8,'){',491,8,')},inner',1,8,'){',491,8,')},','vcenter',1,8,'){var d',427,458,'e=',49,28,'c,b);d.y=e.',377,'-(d.h-d.d)/2;a.nuc=d;a',466,491,8,')},','overline',1,469,'g=',49,28,'d,c);var e',427,472,49,'.PrimeStyle(d),c).Remeasured();var b=g.default_rule_thickness;var f=jsMath.Box.Rule(e.w,b);f.x=-f.w;f.y','=e.h+3*b;a',484,'([e,f],d,c);a.nuc.','h+=b;a',466,491,'d,c,a)},','underline',1,469,'g=',49,28,'d,c);var e',427,472,49,564,'=-e.d-3*b-b;a',484,567,'d+=b;a',466,491,571,'radical',1,'b,m,g',425,49,28,'b,m);var k=',49,'.PrimeStyle(b);var ','f',427,'g.nuc,k,m).',430,'var l=d','.default_rule_thickness',';var c=l;if(b=="D"||b=="D\'"){c=d.x_height}var a=l+c/4;var e=',3,390,'f.h+f.d+a+l,[0,2,112,3,112],b,1);if(e.d>f.h+f.d+a){a=(a+e.d-f.h-f.d)/2}e.y=f.h+a;var j=',3,'Box.Rule(','f.w,l);j.y=e.y-l/2;j.h+=3*l/2;f.x=-f.w;var i=',49,'.UpStyle(',49,613,'b));var h',427,'g.root||null,i,m).',430,'if(g.root){h.y=0.55*(f.h+f.d+3*l+a)-f.d;e.x',368,'h.w-(11/18)*e.w,0);j.x=(7/18)*e.w;h.x=-(h.w+j.x)}g',484,'([e,h,j,f],b,m);g',466,491,'b,m,g)},accent',1,'b,q,j',117,49,28,'b,q);var m=',49,598,'i',427,'j.nuc,m,q);var o=i.w;var p;var k;var d=0;if(j.nuc',11,'TeX"){k=',3,'TeX[j.nuc.font];if(k[j.nuc.c].krn&&k.skewchar){p=k[j.nuc.c].krn[k.skewchar]}d=k[j.nuc.c].ic;if(d==null){d=0}}if(p==null){p=0}var l=j.accent[2];var e=',3,'TeX.fam[j.accent[1]];k=',3,'TeX[e];while(k[l].n&&k[k[l].n].w<=o){l=k[l].n}var n=Math.min(i.h,f.x_height);if(j.nuc',11,'TeX"){var ','h=',44,'.Atom("ord",j.nuc);h.sup=j.sup;h.sub=j.sub;h.delta=0;',491,'b,q,h);n+=(h.nuc.h-i.h);i=j.nuc=h.nuc',79,'j.sup',79,'j.sub}var g=',3,'Box',28,'l,e,b,q);g.y=i.h-n;g.x=-i.w+p+(o-g.w)/2;if(',3,'Browser.msieAccentBug){g.html+=',3,433,'(0.1);g.w+=0.1;g.Mw+=0.1}if(k[l].ic||d){g.x+=(d-(k[l].ic||0))*f.scale}j',484,'([i,g],b,q);if(j.nuc.w!=i.w){var a=',44,'.Space(','i.w-j.nuc.w);j',484,'([j.nuc,a],b,q)}j',466,491,'b,q,j)},op',1,'c,n,i',117,49,28,'c,n);var h;i.delta=0;var m=(c','.charAt(0)=="','D");if(i.limits==null&&m){i.limits=1}if(i.nuc',11,648,'b=',3,'TeX[i.nuc.font][','i.nuc.c];if(m&&b.n){i.nuc.c=b.n;b=',3,689,'b.n]}h=i.nuc',427,'i.nuc,c,n',');if(b.ic){i.delta=b.ic*f.scale;if(i.limits||!i.sub||',3,'Browser.msieIntegralBug','){h=i',484,'([h,',44,670,'i.delta)],c,n)}}h.y=-((h.h+h.d)/2-h.d-f.',377,');if(Math.abs(h.y)<0.0001){h.y=0}}if(!h){h=i.nuc',427,695,').Remeasured()}if(i.limits){var e=h.w;var k=h.w;var d=[h];var j=0;var l=0;if(i.sup){var g',427,'i.sup,',49,613,'c),n).',430,'g.x=((h.w-g.w)/2+i.delta/2)-k;j=f.big_op_spacing5;e',368,'e,g.w);k+=g.x+g.w;g.y=h.h+g.d+h.y+Math.max(f.big_op_spacing1,f.big_op_spacing3-g.d);d[d',47,'g',79,'i.sup}if(i.sub){var a',427,'i.sub,',49,'.DownStyle(','c),n).',430,'a.x=((h.w-a.w)/2-i.delta/2)-k;l=f.big_op_spacing5;e',368,'e,a.w);k+=a.x+a.w;a.y=-h.d-a.h+h.y-Math.max(f.big_op_spacing2,f.big_op_spacing4-a.h);d[d',47,'a',79,'i.sub}if(e>h.w){h.x=(e-h.w)/2;k+=h.x}if(ke;c--){f.mlist[c+1]=f.mlist[c]}f.mlist[e+1]=',44,670,'h[d.nuc.c])}}}}',491,'a,j,g)},fraction',1,'x,n,c){var A=',49,28,'x,n);var l=0;if(c.',140,'!=null){l=c.',140,742,'c.from.match(/over/)){l=A',604,'}}var s=(x',683,'D");var g=(x=="D")?"T":(x=="D\'")?"T\'":',49,613,'x);var q=(s)?"T\'":',49,726,'x);var f',427,'c.num,g,n).',430,'var e',427,'c.den,q,n).',430,'var k;var j;var i;var o;var m;var h=(s)?A.delim1:A.delim2;var b=[',3,390,'h,c.left,x)];var y=',3,390,'h,c.right,x);if(f.w0){n+=f;m-=f}}i.',430,'a.',430,'i.y=n;a.y=-m;i.x=k.delta;if(i.w+i.x>a.w){i.x-=a.w;k',484,'([j,a,i],e,w)}else{a.x-=(i.w+i.x);k',484,'([j,i,a],e,w)}delete k.sup',79,'k.sub}});',49,98,100,'="typeset";',165,'=a};',3,'Add(',49,',{upStyle:{D:"S",T:"S","','D\'":"S\'","T\'":"S\'",S:"SS','",SS:"SS","','S\'":"SS\'","SS\'":"SS\'"},','downStyle:{D:"S\'",T:"S\'","',942,'\'",SS:"SS\'","',944,'UpStyle',1,'a){',5,'this.upStyle[a]},DownStyle',1,'a){',5,'this.downStyle[a]},PrimeStyle',1,2,'a',902,'a',904,5,'a}',5,'a+"\'"},StyleValue',1,'b,',2,'b=="S"||b=="S\'"){',5,'0.7*a}if(b=="SS"||b=="SS\'"){',5,'0.5*a}',5,'a},StyleSize',1,'b,a){if(b=="S"||b=="S\'"){a=Math.max(0,a-2)}else{if(b=="SS"||b=="SS\'"){a=Math.max(0,a-4)}}return ','a},TeX',1,979,3,'TeXparams[a]},AddStyle',1,8,'){if(c=="S"||c=="S\'"){b',368,'0,b-2)}else{if(c=="SS"||c=="SS\'"){b',368,'0,b-4)}}if(b!=4){a=\'\'+a+""}',5,'a},AddClass',1,'a,b){if(a!=""&&a!="normal"){b=',3,'HTML.Class(a,b)}',5,'b}});',3,'Package(',49,',{DTsep:{ord',':{op:1,bin:2,rel:3,inner:1},','op',':{ord:1,op:1',',rel:3,inner:1},bin:{ord:2,op:2,open:2,inner:2},rel:{ord:3,op:3,open:3,inner:3},open:{},close',1004,'punct',1006,',rel:1,open:1,close',':1,punct:1,inner:1','},inner',1006,',bin:2,rel:3,open',1012,'}},SSsep:{ord:{op:1},op',1006,'},bin:{},rel:{},open:{},close:{op:1},punct:{},inner:{op:1}},sepW:["","thinmuskip","medmuskip","thickmuskip"],','GetSeparation',1,'a,d,b){if(a&&a.atom&&d.atom){var c=this.DTsep;if(b',683,'S"){c=this.SSsep}var e=c[a.type];if(e&&e[d.type]!=null){',5,3,'TeX[this.sepW[e[d.type]]]}}',5,'0},',148,1,222,74,323,'var g=-10000;this.w=0;this.mw=0;this.Mw=0;this.h=g;this.d=g;this.bh=this.h;this.bd=this.d;this.tbuf="";this.tx=0;',66,'="";','this.cbuf','="";this.hbuf="";this.hx=0;var a=null;var f;this.x=0;','this.dx=0;',39,186,';b++){f=a;a=',165,'[b];switch(a.type){case"size":','this.FlushClassed();','this.size=a.size;','a=f;break;case"','style":',1046,'if(',74,902,74,904,74,'=a.style+"\'"}else{',74,'=a.style}',1048,'space":if(typeof(a.w)=="object"){if(',74,902,'1)=="S"){a.w=0.5*a.w[0]/18',742,74,683,'S"){a.w=0.7*a.w[0]/18}else{a.w=a.w[0]/18}}}this.dx+=a.w-0;',1048,'html":',1046,'if(this.hbuf==""){this.hx=this.','x}','this.hbuf+=','a.html;a=f;break;default:if(!a.atom&&a.type!="box"){break}a.nuc.x+=this.dx+this.',1020,'(f,a,',74,');','if(a.nuc.x||a.nuc.y){','a.nuc',25,1040,'this.x=this.x+this.w;',60,'x','+a.nuc.x+a.nuc.','mw','\'+b.html+"";','b.h+=b.y;b.d-=b.y;b.x=0;b.y=0','},PlaceSeparateSkips',1,'b){if(b.y',425,'b.Mw-b.w;var c=b.mw;var a=b.Mw-b.mw;b.html=',3,433,'(c-d)+\'<',1192,'; top:\'+',3,1195,'(-b.y)+"; left:"+',3,1195,'(d)+"; width:"+',3,1195,'(a)+\';">\'+',3,433,'(-c)+b.html+',3,433,'(d)+""}if(b.x){b.html=',3,433,'(b.x)+b.html}',1200,'}});',3,'Parse',163,'var e=new ',3,'Parser','(d,a,b,c);e.Parse();',5,'e};',3,1236,163,'this.string=d;this.i=0;',165,'=new ',3,162,'(null,a,b,c)};',3,'Package(',3,1236,',{cmd:"\\\\",open:"{",close:"}",letter:/[a-z]/i,number:/[0-9]/,scriptargs:/^((math|text)..|mathcal|[hm]box)$/,mathchar:{"!":[5,0,33],"(":[4,0,40],")":[5,0,41],"*":[2,2,3],"+":[2,0,43],",":[6,1,59],"-":[2,2,0],".":[0,1,58],"/":[0,1,61],":":[3,0,58],";":[6,0,59],"<":[3,1,60],"=":[3,0,61],">":[3,1,62],"?":[5,0,63],"[":[4,0,91],"]":[5,0,93],"|":[0,2,106]},special:{"~":"Tilde","^":"HandleSuperscript",_:"HandleSubscript"," ":"Space","\\01','":"Space","\\','t',1254,'r',1254,'n":"Space","\'":"Prime","%":"HandleComment","&":"HandleEntry","#":"Hash"},mathchardef:{braceld:[0,3,122],bracerd:[0,3,123],bracelu:[0,3,124],braceru:[0,3,125],alpha:[0,1,11],beta:[0,1,12],gamma:[0,1,13],delta:[0,1,14],epsilon:[0,1,15],zeta:[0,1,16],eta:[0,1,17],theta:[0,1,18],iota:[0,1,19],kappa:[0,1,20],lambda:[0,1,21],mu:[0,1,22],nu:[0,1,23],xi:[0,1,24],pi:[0,1,25],rho:[0,1,26],sigma:[0,1,27],tau:[0,1,28],upsilon:[0,1,29],phi:[0,1,30],chi:[0,1,31],psi:[0,1,32],omega:[0,1,33],varepsilon:[0,1,34],vartheta:[0,1,35],varpi:[0,1,36],varrho:[0,1,37],varsigma:[0,1,38],varphi:[0,1,39],Gamma:[7,0,0],Delta:[7,0,1],Theta:[7,0,2],Lambda:[7,0,3],Xi:[7,0,4],Pi:[7,0,5],Sigma:[7,0,6],Upsilon:[7,0,7],Phi:[7,0,8],Psi:[7,0,9],Omega:[7,0,10],aleph:[0,2,64],imath:[0,1,123],jmath:[0,1,124],ell:[0,1,96],wp:[0,1,125],Re:[0,2,60],Im:[0,2,61],partial:[0,1,64],infty:[0,2,49],prime:[0,2,48],emptyset:[0,2,59],nabla:[0,2,114],surd:[1,2,112],top:[0,2,62],bot:[0,2,63],triangle:[0,2,52],forall:[0,2,56],exists:[0,2,57],neg:[0,2,58],lnot:[0,2,58],flat:[0,1,91],natural:[0,1,92],sharp:[0,1,93],clubsuit:[0,2,124],diamondsuit:[0,2,125],heartsuit:[0,2,126],spadesuit:[0,2,127],coprod:[1,3,96],bigvee:[1,3,87],bigwedge:[1,3,86],biguplus:[1,3,85],bigcap:[1,3,84],bigcup:[1,3,83],intop:[1,3,82],prod:[1,3,81],sum:[1,3,80],bigotimes:[1,3,78],bigoplus:[1,3,76],bigodot:[1,3,74],ointop:[1,3,72],bigsqcup:[1,3,70],smallint:[1,2,115],triangleleft:[2,1,47],triangleright:[2,1,46],bigtriangleup:[2,2,52],bigtriangledown:[2,2,53],wedge:[2,2,94],land:[2,2,94],vee:[2,2,95],lor:[2,2,95],cap:[2,2,92],cup:[2,2,91],ddagger:[2,2,122],dagger:[2,2,121],sqcap:[2,2,117],sqcup:[2,2,116],uplus:[2,2,93],amalg:[2,2,113],diamond:[2,2,5],bullet:[2,2,15],wr:[2,2,111],div:[2,2,4],odot:[2,2,12],oslash:[2,2,11],otimes:[2,2,10],ominus:[2,2,9],oplus:[2,2,8],mp:[2,2,7],pm:[2,2,6],circ:[2,2,14],bigcirc:[2,2,13],setminus:[2,2,110],cdot:[2,2,1],ast:[2,2,3],times:[2,2,2],star:[2,1,63],propto:[3,2,47],sqsubseteq:[3,2,118],sqsupseteq:[3,2,119],parallel:[3,2,107],mid:[3,2,106],dashv:[3,2,97],vdash:[3,2,96],leq:[3,2,20],le:[3,2,20],geq:[3,2,21],ge:[3,2,21],lt:[3,1,60],gt:[3,1,62],succ:[3,2,31],prec:[3,2,30],approx:[3,2,25],succeq:[3,2,23],preceq:[3,2,22],supset:[3,2,27],subset:[3,2,26],supseteq:[3,2,19],subseteq:[3,2,18],"in":[3,2,50],ni:[3,2,51],owns:[3,2,51],gg:[3,2,29],ll:[3,2,28],not:[3,2,54],sim:[3,2,24],simeq:[3,2,39],perp:[3,2,63],equiv:[3,2,17],asymp:[3,2,16],smile:[3,1,94],frown:[3,1,95],Leftrightarrow:[3,2,44],Leftarrow:[3,2,40],Rightarrow:[3,2,41],leftrightarrow:[3,2,36],leftarrow:[3,2,32],gets:[3,2,32],rightarrow:[3,2,33],to:[3,2,33],mapstochar:[3,2,55],leftharpoonup:[3,1,40],leftharpoondown:[3,1,41],rightharpoonup:[3,1,42],rightharpoondown:[3,1,43],nearrow:[3,2,37],searrow:[3,2,38],nwarrow:[3,2,45],swarrow:[3,2,46],minuschar:[3,2,0],hbarchar:[0,0,22],lhook:[3,1,44],rhook:[3,1,45],ldotp:[6,1,58],cdotp:[6,2,1],colon:[6,0,58],"#":[7,0,35],"$":[7,0,36],"%":[7,0,37],"&":[7,0,38]},delimiter:{"(":[0,0,40,3,0],")":[0,0,41,3,1],"[":[0,0,91,3,2],"]":[0,0,93,3,3],"<":[0,2,104,3,10],">":[0',',2,105,3,11],"\\\\','lt":[0',',2,104,3,10],"\\\\','gt":[0,2,105,3,11],"/":[0,0,47,3,14],"|":[0,2,106,3,12],".":[0,0,0,0,0],"\\\\":[','0,2,110,3,15],"\\\\','lmoustache":[4,3,122,3,64],"\\\\rmoustache":[5,3,123,3,65],"\\\\lgroup":[4,6,40,3,58],"\\\\rgroup":[5,6,41,3,59],"\\\\arrowvert','":[0,2,106,3,','60],"\\\\Arrowvert":[0,2,107,3,61],"\\\\bracevert',1266,'62],"\\\\Vert":[0,2,107,3,13],"\\\\|":[0,2,107,3,13],"\\\\vert',1266,'12],"\\\\uparrow":[3,2,34,3,120],"\\\\downarrow":[3,2,35,3,121],"\\\\updownarrow":[3,2,108,3,63],"\\\\Uparrow":[3,2,42,3,126],"\\\\Downarrow":[3,2,43,3,127],"\\\\Updownarrow":[3,2,109,3,119],"\\\\backslash":[',1264,'rangle":[5',1260,'langle":[4',1262,'rbrace":[5,2,103,3,9],"\\\\lbrace":[4,2,102,3,8],"\\\\}":[5,2,103,3,9],"\\\\{":[4,2,102,3,8],"\\\\rceil":[5,2,101,3,7],"\\\\lceil":[4,2,100,3,6],"\\\\rfloor":[5,2,99,3,5],"\\\\lfloor":[4,2,98,3,4],"\\\\lbrack":[0,0,91,3,2],"\\\\rbrack":[0,0,93,3,3]},macros:{displaystyle',':["HandleStyle","','D"],textstyle',1278,'T"],scriptstyle',1278,'S"],scriptscriptstyle',1278,'SS"],rm',':["HandleFont",','0],mit',1286,'1],oldstyle',1286,'1],cal',1286,'2],it',1286,'4],bf',1286,'6],font',':["Extension","','font"],left:"HandleLeft",right:"HandleRight",arcsin',':["NamedOp",0],','arccos',1300,'arctan',1300,'arg',1300,'cos',1300,'cosh',1300,'cot',1300,'coth',1300,'csc',1300,'deg',1300,'det',':"NamedOp",','dim',1300,'exp',1300,'gcd',1320,'hom',1300,'inf',1320,'ker',1300,'lg',1300,'lim',1320,'liminf',':["NamedOp",null,\'lim','inf\'],limsup',1338,'sup\'],ln',1300,'log',1300,'max',1320,'min',1320,'Pr',1320,'sec',1300,'sin',1300,'sinh',1300,'sup',1320,'tan',1300,'tanh',1300,538,':["HandleAtom","',538,'"],',554,1364,554,'"],',572,1364,572,'"],over',':"HandleOver",','overwithdelims',1375,'atop',1375,'atopwithdelims',1375,'above',1375,'abovewithdelims',1375,'brace',':["HandleOver','","\\\\{","\\\\}"],brack',1387,'","[","]"],choose',1387,'","(",")"],overbrace',':["Extension","leaders"],','underbrace',1393,'overrightarrow',1393,'underrightarrow',1393,'overleftarrow',1393,'underleftarrow',1393,'overleftrightarrow',1393,'underleftrightarrow',1393,'overset',':["Extension","underset-overset"],','underset',1409,'llap',':"HandleLap",','rlap',1413,'ulap',1413,'dlap',1413,'raise:"RaiseLower",lower:"RaiseLower",moveleft',':"MoveLeftRight",','moveright',1421,'frac:"Frac",root:"Root",sqrt:"Sqrt",hbar',':["Macro","\\\\','hbarchar\\\\kern-.5em h"],ne',1425,'not="],neq',1425,'not="],notin',1425,'mathrel{\\\\','rlap{\\\\kern2mu/}}\\\\in"],cong',1425,1432,'lower2mu{\\\\mathrel{{\\\\rlap{=}\\\\raise6mu\\\\sim}}}}"],bmod',1425,'mathbin{\\\\rm mod}"],pmod',1425,'kern 18mu ({\\\\rm mod}\\\\,\\\\,#1)",1],"int":["Macro","\\\\intop\\\\nolimits"],oint',1425,'ointop\\\\nolimits"],doteq',1425,'buildrel\\\\textstyle.\\\\over="],ldots',1425,'mathinner{\\\\','ldotp\\\\ldotp\\\\ldotp}"],cdots',1425,1446,'cdotp\\\\cdotp\\\\cdotp}"],vdots',1425,1446,'rlap{\\\\raise8pt{.\\\\rule 0pt 6pt 0pt}}\\\\rlap{\\\\raise4pt{.}}.}"],ddots',1425,1446,'kern1mu\\\\raise7pt{\\\\rule 0pt 7pt 0pt .}\\\\kern2mu\\\\raise4pt{.}\\\\kern2mu\\\\raise1pt{.}\\\\kern1mu}"],joinrel',1425,1432,'kern-4mu}"],relbar',1425,1432,'smash-}"],Relbar',1425,'mathrel="],bowtie',1425,'mathrel\\\\triangleright\\\\joinrel\\\\mathrel\\\\triangleleft"],models',1425,'mathrel|\\\\joinrel="],mapsto',1425,1432,'mapstochar\\\\','rightarrow}"],rightleftharpoons',1425,538,'{\\\\',1432,'rlap{\\\\raise3mu{\\\\rightharpoonup}}}\\\\leftharpoondown}"],hookrightarrow',1425,'lhook','\\\\joinrel\\\\rightarrow','"],hookleftarrow',1425,'leftarrow\\\\joinrel\\\\','rhook"],Longrightarrow',1425,'Relbar\\\\joinrel\\\\','Rightarrow"],','longrightarrow',1425,'relbar',1480,'"],longleftarrow',1425,1483,'relbar"],Longleftarrow',1425,'Leftarrow\\\\joinrel\\\\','Relbar"],longmapsto',1425,1432,1471,'minuschar',1480,'}"],longleftrightarrow',1425,'leftarrow',1480,'"],','Longleftrightarrow',1425,1497,1487,'iff:["Macro","\\\\;\\\\',1509,'\\\\;"],mathcal',':["Macro","{\\\\','cal #1}",1],mathrm',1516,'rm #1}",1],mathbf',1516,'bf #1}",1],','mathbb',1516,1521,'mathit',1516,'it #1}",1],textrm',1425,'mathord{\\\\hbox{#1}}",1],textit',1425,'mathord{\\\\class{','textit','}{\\\\hbox{#1}}}",1],','textbf',1425,1531,1534,1533,'pmb',1425,'rlap{#1}\\\\kern1px{#1}",1],TeX:["Macro","T\\\\kern-.1667em\\\\lower.5ex{E}\\\\kern-.125em X"],limits:["Limits",1],nolimits:["Limits",0],",":["Spacer",1/6],":":["Spacer",1/6],">":["Spacer",2/9],";":["Spacer",5/18],"!":["Spacer",-1/6],enspace:["Spacer",1/2],quad:["Spacer",1],qquad:["Spacer",2],thinspace:["Spacer",1/6],negthinspace:["Spacer",-1/6],hskip:"Hskip",kern:"Hskip",rule:["Rule","colored"],space:["Rule","blank"],big',':["MakeBig","','ord",0.85],Big',1542,'ord",1.15],bigg',1542,'ord",1.45],Bigg',1542,'ord",1.75],bigl',1542,'open",0.85],Bigl',1542,'open",1.','15],biggl',1542,1553,'45],Biggl',1542,1553,'75],bigr',1542,'close",0.85],Bigr',1542,'close",1.','15],biggr',1542,1564,'45],Biggr',1542,1564,'75],bigm',1542,'rel",0.85],Bigm',1542,'rel",1.15],biggm',1542,'rel",1.45],Biggm',1542,'rel",1.75],mathord',1364,'ord"],mathop',1364,'op"],mathopen',1364,'open"],mathclose',1364,'close"],mathbin',1364,'bin"],mathrel',1364,'rel"],mathpunct',1364,'punct"],mathinner',1364,'inner"],','mathchoice',1298,1596,'"],buildrel:"BuildRel",hbox:"HBox",text:"HBox",mbox:"HBox",fbox',1298,'fbox"],strut:"Strut",mathstrut',1425,'vphantom{(}"],phantom:["Phantom",1,1],vphantom:["Phantom",1,0],hphantom:["Phantom",0,1],smash:"Smash",acute:["MathAccent",[7,0,19]],grave:["MathAccent",'], + ['[','7,0,','18]],ddot',':["MathAccent",[',1,'127]],tilde',3,1,'126]],bar',3,1,'22]],breve',3,1,'21]],check',3,1,'20]],hat',3,1,'94]],vec',3,'0,1,126]],dot',3,1,'95]],widetilde',3,'0,3,101]],widehat',3,'0,3,98]],_:["Replace","ord","_","normal",-0.4,0.1]," ":["Replace","','ord"," ","normal','"],angle:["Macro","\\\\kern2.5mu\\\\raise1.54pt{\\\\rlap{\\\\scriptstyle \\\\char{cmsy10}{54}}\\\\kern1pt\\\\rule{.45em}{-1.2pt}{1.54pt}\\\\kern2.5mu}"],matrix:"Matrix",array:"Matrix",pmatrix:["Matrix','","(",")","c"],','cases:["Matrix","\\\\{",".",["l","l"],null,2],eqalign',':["Matrix",null,null,["','r","l"],[5/18],3,"D"],displaylines',34,'c"],null,3,"D"],cr:"','HandleRow','","\\\\":"',38,'",newline:"',38,'",noalign:"','HandleNoAlign','",eqalignno',34,'r","l","r"],[5/8,3],3,"D"],','leqalignno',34,47,'begin:"Begin",end:"End",tiny',':["HandleSize",','0],Tiny',52,'1],scriptsize',52,'2],small',52,'3],normalsize',52,'4],large',52,'5],Large',52,'6],LARGE',52,'7],huge',52,'8],Huge',52,'9],dots:["Macro","\\\\ldots"],','newcommand',':["Extension","',72,'"],newenvironment',73,72,'"],def',73,72,'"],color',73,'HTML"],','href',73,'HTML"],"class":["','Extension','","',83,'style',73,83,'cssId',73,83,'unicode',73,83,'bbox',73,'bbox"],require:"Require","char":"Char"},','environments',':{array:"Array",matrix',':["Array','",null,null,"c"],pmatrix',104,32,'bmatrix',104,'","[","]","c"],Bmatrix',104,'","\\\\{","\\\\}","c"],vmatrix',104,'","\\\\vert","\\\\vert","c"],Vmatrix',104,'","\\\\Vert","\\\\Vert","c"],cases',104,'","\\\\{",".","ll",null,2],eqnarray:["','Array",null,null,"rcl",[5/18,5/18],3,"D','"],"eqnarray*":["',119,'"],equation:"Equation","equation*":"Equation",align',73,'AMSmath','"],"align','*":["Extension","AMSmath"],','aligned',73,124,'"],','multline',73,124,'"],"',131,126,'split',73,124,'"],gather',73,124,'"],"gather',126,'gathered',73,124,'"]},AddSpecial',':function(','a){for(var b in a){','jsMath.Parser.prototype','.special[',151,'[b]]=a[b]}},Error',149,'a){this.i=','this.string','.length;','if(a','.error){this.error=a','.error}','else{if(!','this',160,'}}},nextIsSpace',':function(){','return ','this.string.charAt(this.i',').match(/[ \\n\\r\\t]/)},trimSpaces',149,'a){if(typeof(a)!="string"){',167,'a}',167,'a.replace(/^\\s+|\\s+$/g',',"")},','Process',149,'a){var ','c=','this.mlist.data',';a','=jsMath.Parse(','a,c.font,c.size,c.style);if(a','.error){this.Error(','a);','return null','}if(a.mlist.Length()==','0){',187,188,'1','){var b=','a.mlist.','Last();if(','b.atom&&b.type=="ord"&&b.nuc&&!b.sub&&!b.sup&&(b.nuc.type=="text"||b.nuc.type=="TeX")){',167,'b.nuc}}return{type:"mlist",mlist:a.mlist}},GetCommand',':function(){var ','a=/^([a-z]+|.) ?/i;var b=a.exec(','this.string.slice(this.i','));if(b){this.i+=b[1].length;',167,'b[1]}','this.i++;','return" "},GetArgument',149,'b,d){while','(this.nextIsSpace()){this.i','++}','if(this.','i>=',157,'.length){','if(!d){','this.Error("','Missing',' argument for "+b)}',187,'}if(',168,')==this.close){',215,216,'Extra close brace','")}',187,'}if(',168,')==this.cmd){',205,167,'this.cmd+','this.GetCommand','()}if(',168,')!=this.open){',167,168,'++)}var a=++this.i;var e=1;var f="";','while(this.i0){e--;this.i+=2}}}',247,'if(',201,427,'d.length)==d){f=',168,'+d','.length);','if(f.match(/[^a-z]/i)||!d.match(/[a-z]/i)){',273,157,'.slice(g,this.i-1);this.i+=d',158,167,'a}}}this.i++}}}}',216,401,403,'+d+" for "+b);',187,'},','ProcessUpto',149,'b,c){var ','a=this.GetUpto(b,c);',267,268,269,'GetEnd',149,'c){var a="";var b="";while(b!=c){a+=this.GetUpto("begin{"+c+"}","end");',267,268,'b=','this.GetArgument(this.cmd','+"end");',267,324,167,'a},Space',':function(){},','Prime',149,'d',193,'this.mlist.',195,'b==null||(!b.atom&&b','.type!="box"&&','b','.type!="frac")){','b=','this.mlist.Add(jsMath.mItem.','Atom("ord",{type:null}))}','if(b.sup){',216,'Prime causes double exponent',': use braces to clarify");return','}var a','="";while(d=="\'"){a+=this.cmd+"prime";d',381,'d=="\'"){this.i++}}b.sup=this.',177,'(a);b.sup.isPrime=1},RaiseLower',149,179,'b','=this.GetDimen(this.cmd+','a,1);',267,'}var c','=this.ProcessScriptArg','(',233,'a);',267,'}if(a=="lower"){b=-b}','this.mlist.Add(new jsMath.mItem("','raise",{nuc:c,raise:b}))},MoveLeftRight',149,263,'a',499,'b,1);',267,'}var c',503,'(',233,'b);',267,'}if(b=="moveleft"){a=-a}',484,'Space(a','));',484,'Atom("ord",c));',484,'Space(-a))},Require',149,179,'b=',466,404,267,'}b=','jsMath.Extension','.URL(b);if(','jsMath.Setup.','loaded[b]){return}','this.Extension(null,[','b])},',87,149,'a,b){','jsMath.Translate','.restart=1;if(a','!=null){','delete ',151,'[b[1]||"macros"][a]}',538,'.Require(b[0],',547,'.asynchronous',');throw"restart"},Frac',149,263,'a','=this.ProcessArg(this.cmd+','b);',267,'}var c',561,'b);',267,'}',484,'Fraction("over",a,c))},Sqrt',149,263,'d=',291,233,'b);',267,490,561,'b);',267,'}var c=jsMath.mItem.Atom("','radical",a);','if(d!=""){c.root=this.',177,'(d);',267,'}}',477,'Add(c)},','Root',149,263,'d=this.',453,'(',233,'b,"of");',267,490,561,'b);',267,582,583,'c.root=d;',477,590,'BuildRel',149,179,'b=this.',453,'(',233,'a,"over");',267,'}var d',561,'a);',267,582,'op",d);c.limits=1;c.sup=b;',477,590,'MakeBig',149,337,'c=d[0];var b=d[1]*jsMath.p_height;var e','=this.GetDelimiter(this.cmd+','a);',267,'}',484,'Atom(c,','jsMath.Box.Delimiter(','b,e,"T")))},Char',149,263,'a=',466,'+b);',267,'}var c=',466,'+b);',267,'}if(!',365,'[a]){',365,'[a]=[];',542,'jsMath.Font.URL(a',')])}else{',484,'Typeset(','jsMath.Box.TeX(c-0,a,',181,'.style,',181,'.size)))}},Matrix',149,'b,h){var e=',181,';var a=',466,'+b);',267,'}var g=','new jsMath.','Parser(a','+this.cmd+"\\\\",null,','e.size,h[5]||"T");g.matrix=b;g.row=[];g.table=[];g.rspacing=[];g.Parse();if(g',185,'g);return}g.',38,'(',515,'var d','=jsMath.Box.','Layout(e.size,g.table,h[2]||null,h[3]||null,g.rspacing,h[4]||null);if(h[0]&&h[1]){',412,636,'d.h+d','.d-jsMath.hd/4,this.delimiter[','h[0]],"T");var c=',636,'d.h+d',686,'h[1]],"T");d',681,'SetList([','f,d,c],e.style,e.size)}',484,'Atom((h','[0]?"inner":"ord"),','d))},HandleEntry',149,'b){if(!this.matrix){this.Error(b','+" can only appear in a matrix or array");return}if(',181,'.openI',549,273,477,'Get(',181,'.openI);if','(a.left){',216,217,403,'+"right','")}else{',216,257,'")}}if(',181,'.overI',549,477,'Over()}var d=',181,';',477,'Atomize(','d.style,d.size);','var c=',477,657,728,'c.entry=d.entry;delete d.entry;if(!c.entry){c.entry={}}this.row[','this.row.length',']=c;this.mlist=',671,'mList(null,null,d.size,d.style)},',38,149,'a,c){var b;if(!this.matrix){','this.Error(this.cmd+a',701,'a=="\\\\"){b=',291,233,'a);',267,'}if(b){b=',348,'b,',233,'a,0,1)}}this.HandleEntry(a);if(!c||',734,'>1||this.row[0].format!="null"){this.table[this.table.length]=this.row}if(b){','this.rspacing[this.table.length',']=b}this.row=[]},',44,149,263,'a=',466,'+b);',267,644,'a.replace(/^.*(vskip|vspace)([^a-z])/i,"$2");if(c.length==a',214,'return}var ','e=',348,'c,',233,'RegExp.$1,0,1);',267,'}',755,']=(',755,']||0)+e},Array',149,455,'e=c[2];var i=c[3];if(!e){e=',466,'+"begin{"+b+"}");',267,'}}e=e.replace(/[^clr]/g,"");e=e.split("");var g=',181,666,'c[5]||"T";var k=this.GetEnd(b);',267,'}',412,671,'Parser(k',673,'g.size,a);f.matrix=b;f.row=[];f.table=[];f.rspacing=[];f.Parse();if(f',185,'f);return}f.',38,'(',515,'var h',681,'Layout(g.size,f.table,e,i,f.rspacing,c[4],c[6],c[7]);if(c[0]&&c[1]){var d=',636,'h.h+h',686,'c[0]],"T");var j=',636,'h.h+h',686,'c[1]],"T");h',681,693,'d,h,j],g.style,g.size)}',484,'Atom((c',697,'h))},Begin',149,179,'b=',466,404,267,'}if(b.match(/[^a-z*]/i)){this.Error(\'Invalid environment name "\'+b+\'"\');return}if(!this.',102,'[b]){this.Error(\'Unknown environment "\'+b+\'"\');return',644,'this.',102,'[b];if(typeof(','c',')=="string"){','c=[c]}this[c[0]](b,c.slice(1))},End',149,179,'b=',466,404,267,'}',741,'+"{"+b+"} without matching',403,'+"begin")},Equation',149,263,'a=this.GetEnd(b);',267,'}',157,'=a+',201,');this.i=0},Spacer',149,'b,a){',484,525,'-0))},Hskip',149,263,'a',499,'b);',267,'}',484,525,'))},HBox',149,179,'c=',466,404,267,'}var b',681,'InternalMath(c,',181,'.size);',484,657,'b))},Rule',149,'b,f){var a',499,515,267,'}var e',499,515,267,'}var g',499,515,267,'}e+=g;var c;if(e!=0){e=Math.max(1.05/jsMath.em,e)}if(e==0||a==0||f=="blank"){c=','jsMath.HTML.','Blank(a,e)}else{c=',898,'Rule(a,e)}if(g){c=\'\'+c','+""}',484,657,671,'Box("html",c,a,e-g,g)))},Strut',199,'a=',181,'.size;var b',681,'Text("","normal","T",a).Styled();b.bh=b.bd=0;b.h=0.8;b.d=0.3;b.w=b.Mw=0;',484,657,'b))},Phantom',149,455,'a',561,'b);',267,'}',509,'phantom",{phantom:a,v:c[0],h:c[1]}))},Smash',149,455,'a',561,'b);',267,'}',509,'smash",{smash:a}))},MathAccent',149,'b,',179,'e',561,'b);',267,'}var d','=jsMath.mItem.','Atom("accent",e);d.accent=a[0];',477,'Add(d)},NamedOp',149,'c,f){var b=(c.match(/[^acegm-su-z]/))?1:0;var g=(c.match(/[gjpqy]/))?0.2:0;if(f[1]){c=f[1]}var e',944,'TextAtom("','op",c,',365,'.fam[0],b,g);if(f[0]!=null){e.limits=f[0]}',477,'Add(e)},Limits',149,'a,c',193,477,'Last();if(!b||b.type!="op"){',741,'+" is allowed only on operators");return}b.limits=c[0]},Macro',149,'b,d){var e=d[0];if(d[1]){var a=[];','for(var c=0;c<','d[1];c++){a[a.length]=',466,'+b);',267,'}}e=this.','SubstituteArgs','(a,e)}',157,'=','this.AddArgs(e,',201,'));this.i=0},',972,149,'b,',179,'f="";var e="";var g;var d=0;while(db',214,216,'Illegal ','macro parameter ','reference");',187,'}e=this.AddArgs(',976,'f),b[g-1]);f=""}}else{f+=g}}}',167,976,'f)},AddArgs',149,856,'if(a.match','(/^[a-z]/i)&&b.match(/(^|[^\\\\])(\\\\\\\\)*\\\\[a-z]+$/i)){b+=" "}',167,'b+a},Replace',149,546,484,'TextAtom(','b[0],b[1],b[2','],b[3]))},Hash',149,'a){',216,'You can\'t use \'',995,'character #\' in math mode")},Tilde',149,'a){',484,951,30,'"))},HandleLap',149,179,612,261,'();',267,'}b=',509,'lap",{nuc:b,lap:a}))},HandleAtom',149,455,'a',561,'b);',267,'}',484,'Atom(c[0],a))},HandleMathCode',149,'a,b','){this.HandleTeXchar(',1014,'])},HandleTeXchar',149,'b,a,c){if(b==7&&',181,'.font',549,'a=',181,'.font}a=',365,'.fam[a];if(!',365,'[a]){',365,'[a]=[];',542,654,')])}else{',484,'TeXAtom(',365,'.atom[b],c,a))}},','HandleVariable',149,'a',1048,'7,1,','a.charCodeAt(0))},','HandleNumber',149,'a',1048,1,1077,'HandleOther',149,'a){',484,951,'ord",a,"normal"))},HandleComment',199,'a;',241,'a=',168,'++);if(a=="\\r"||a=="\\n"){return}}},HandleStyle',149,546,181,'.style=b[0];',509,'style",{style:b[0]}))},HandleSize',149,546,181,'.size=b[0];',509,'size",{size:b[0]}))},HandleFont',149,856,181,'.font=a[0]},HandleCS',199,'b=',234,'();',267,'}',211,'macros[b]){',273,'this','.macros',831,'a',833,'a=[a]}this[a[0]](b,a.slice(1','));return}',211,'mathchardef','[','b]){this.HandleMathCode(b,this.',1129,'[b]);return}if(',326,233,1131,'delimiter[',233,'b].slice(0,3',1127,216,'Unknown control sequence \'"+',233,'b+"\'")},HandleOpen',166,477,'Open()},HandleClose',166,'if(',181,703,'==null){',216,225,'");',767,'a=',477,'Get(',181,709,'(!a||a.left',1152,477,'Close()}else{',216,225,' or missing',403,714,'");return}},HandleLeft',149,179,'b',630,'a);',267,'}',477,'Open(b)},HandleRight',149,263,'c',630,'b);',267,490,'=',477,'Get(',181,709,'(a&&a.left',549,477,'Close(c)}else{',216,'Extra open brace or missing',403,'+"left")}},HandleOver',149,546,'if(',181,720,549,216,'Ambiguous use of',403,404,'return}',181,720,'=',477,'Length();',181,'.overF={name:a};if(b.length>0){',181,'.overF.','left=',326,'b[0]];',181,1220,'right=',326,'b[1]]}else{',1006,'(/withdelims$/)){',181,1220,'left',630,'a);',267,'}',181,1220,'right',630,'a);',267,'}}else{',181,1220,'left=null;',181,1220,1226,'null}}',1006,'(/^above/)){',181,1220,'thickness',499,'a,1);',267,1244,181,1220,'thickness=null}},HandleSuperscript',199,'a=',477,195,181,720,'==',477,'Length()){a=null}','if(a==null','||(!a.atom&&a',480,'a',482,'a=',484,485,'if(a.sup){if(a.sup.isPrime){a=',484,485,'else{',216,'Double exponent',489,'}}a.sup',503,'("superscript");',267,'}},HandleSubscript',199,'a=',477,195,181,720,'==',477,'Length()){a=null}',1273,'||(!a.atom&&a',480,'a',482,'a=',484,485,'if(a.sub){',216,'Double subscripts',489,'}a.sub',503,'("subscript");',267,'}},Parse',199,'b;',241,'b=',168,'++);',211,'mathchar[',1131,'mathchar[b])}else{',211,'special[b]){this[this',152,'b]](b)}else{',211,'letter','.test(b)){this.',1072,'(b)}else{',211,'number',1335,1078,1337,'this.',1084,'(b)}}}}}if(',181,703,549,273,477,'Get(',181,709,'(a.left){',216,217,403,714,715,216,257,'")}}if(',181,720,549,477,'Over()}},Atomize',199,'a=',477,'init;if(!this.error){',477,727,'a.style,a.size)}},Typeset',199,'f=',477,'init;var ',594,'typeset=',477,657,'f.style,f',880,267,'\'\'+this.error',904,'if(d.format=="null"){return""}d.Styled().Remeasured();var e=0;var c=0;if(d.bh>d.h&&','d.bh>jsMath.h+0.001','){e=1}if(d.bd>d.d&&d.bd>jsMath.d+0.001){e=1}if(d.h>jsMath.h||d.d>jsMath.d){c=1}var b=d.html;if(e){if(','jsMath.Browser.','allowAbsolute){var g=(',1389,'?jsMath.h-d.bh:0);b=',898,'Absolute(b,d.w,jsMath.h,0,g)}else{if(',1391,'valignBug){','b=\'\'+b',904,162,1391,'operaLineHeightBug){',273,898,'Em(Math.max(0,d.bd-jsMath.hd)/3);',1399,')+"; position:relative; top:"+a+"; vertical-align:"+a+\'">\'+b+""}}}c=1}if(c){b+=',898,'Blank(0,d.h+0.05,d.d+0.05)}return\'\'+b+""}});',151,'.AddSpecial({cmd:"HandleCS",open:"HandleOpen",close:"HandleClose"});','jsMath.Add(jsMath,{','Macro',149,179,'c=',151,1122,';c[a]=["Macro"];for(var b=1;b=0){','c=c.replace(/&','lt;/g,"<");',1574,'gt;/g,">");',1574,'quot;/g,\'"\');',1574,'amp;/g,"&")}',167,'c},',1572,149,1560,'b.nodeValue',549,'if(b.nodeName!=="#',1565,167,1587,'}',167,1587,'.replace(/^\\[CDATA\\[((.|\\n)*)\\]\\]$/,"$1")}if(',1561,'===0){return" "}var c="";for(',273,'0;a<',1561,';a++){c+=this.',1572,'(',1563,'a])}',167,'c},ResetHidden',149,'a){a.innerHTML=\'\'+',1391,'operaHiddenFix;a','.className','="";','jsMath.hidden','=a.firstChild;if(!jsMath.BBoxFor("x").w){','jsMath.hidden=jsMath.hiddenTop','}jsMath.ReInit()},ConvertMath',149,'c,b,',179,594,1558,'(b);this.ResetHidden(b);if(d.match(/^\\s*\\\\nocache([^a-zA-Z])/)){a=true;d=d.replace(/\\s*\\\\nocache/,"")}',594,'Parse(c,d,a);b',1613,'="typeset";b.innerHTML=d},',1520,149,'c){this.restart=0;if(!c',1613,'.match(/(^| )math( |$)/)){',767,'a=(c',1613,'.toLowerCase','().match(/(^| )nocache( |$)/)!=null);try{var d=(c.tagName',1637,'()=="div"?"D":"T");this.ConvertMath(d,c,a);c.onclick=jsMath.Click.CheckClick;c.ondblclick=jsMath.Click.CheckDblClick}catch(e){if(c.alt',193,'c.alt;b=b.replace(/&/g,"&").replace(//g,">");c.innerHTML=b;c',1613,'="math";if(a){c',1613,'+=" nocache"}}',1617,'}},','ProcessElements',149,'b){',1498,'blocking=','1;',966,1391,'processAtOnce;c++,b++){if(b>=','this.element','.length||this.cancel){this.','ProcessComplete','();',211,'cancel){','jsMath.Message.','Set("',177,' Math: Canceled");',1664,'Clear()}',1498,1653,'0;',1498,177,'();return}','else{',273,1498,'SaveQueue();','this.ProcessElement(this.element[','b]);',211,'restart){',1498,1499,'this,"',1649,'",b);',1498,'RestoreQueue(a);',1498,1653,'0;setTimeout("',1498,177,'()",',1391,'delay);return}}}',1498,1690,'var d=Math.floor(100*b/',1658,439,'jsMath.Message.Set("Processing Math',': "+d+"%");setTimeout("',547,'.',1649,'("+b+")",',1391,'delay)},',1502,149,'a','){if(!jsMath.initialized){jsMath.Init()}this.element','=this.GetMathElements(','a);',1498,1653,'1;this.cancel=0;this',556,'=1;',1704,': 0%",1);setTimeout("',547,'.',1649,'(0)",',1391,1711,1514,149,856,1273,1715,1716,'b);a=0}this',556,'=0;while(a<',1658,214,1680,'a]);',211,'restart){','jsMath.Synchronize','("',547,'.',1514,'(null,"+a+")");',1498,177,1675,'a++}this.',1660,'(1)},ProcessOne',149,'a',1715,'=[a];this.',1514,'(null,0)},GetMathElements',149,'d){var b=[];var a;',215,'d=','jsMath.document','}if(typeof(d',833,'d=',1768,'.getElementById(d)}if(!','d.getElementsByTagName','){',187,644,1774,'("div','");for(a=0;a=0;a--){b[a].removeAttribute("name")}}',1617,';',1658,'=[];this.restart=null;if(!c){',1704,': Done");',1664,'Clear()}',1664,'UnBlank();if(',1391,'safariImgBug&&(',1510,'font=="symbol"||',1510,'font=="image")){',211,'timeout){clearTimeout(this.timeout)}this.timeout=setTimeout("','jsMath.window.resizeBy','(-1,0); ',1836,'(1,0); ',547,'.timeout = null",2000)}},Cancel',166,547,'.cancel=1;if(',1498,'cancelTimer){',1498,'cancelLoad()}}};',1414,'ConvertTeX',149,'a){',1498,'Push(jsMath.tex2math,"',1850,1503,'ConvertTeX2',149,'a){',1498,1854,1857,1503,'ConvertLaTeX',149,'a){',1498,1854,1864,1503,'ConvertCustom',149,'a){',1498,1854,1871,1503,'CustomSearch',149,'c,b,a,d){',1498,1499,'null,function(){jsMath.tex2math.',1878,'(c,b,a,d)})},tex2math:{',1850,472,1857,472,1864,472,1871,472,1878,':function(){}}});',1746,'=',1498,'Synchronize;try{if(','window.parent','!=window&&window.jsMathAutoload){',1900,'.jsMath=jsMath;',1768,'=',1900,'.document;jsMath.window=',1900,'}}catch(err){}',1535,'Register();jsMath.Loaded();jsMath.Controls.GetCookie();',540,'Source();',1535,'Init();',1498,'Init();',540,'Fonts();if(',1768,'.body){',540,'Body()}',540,'User("onload")}};'] + +]); +//end = new Date().getTime(); +//alert(end-start); diff --git a/htdocs/jsMath/local/macros.js b/htdocs/jsMath/local/macros.js new file mode 100644 index 0000000000..974fba98bc --- /dev/null +++ b/htdocs/jsMath/local/macros.js @@ -0,0 +1,17 @@ +/* + * local/macros.js + * + * Use jsMath.Macro() to declare your own local macro definitions here, + * and add "local/macros.js" to the loadFiles array in easy/load.js so + * that they will be loaded automatically when jsMath is used. + * + * See http://www.math.union.edu/locate/jsMath/authors/macros.html + * for details on defining macros for jsMath. + */ + +// +// Examples: +// +// jsMath.Macro('R','{\\bf R}'); +// jsMath.Macro('red','\\color{red}{#1}',1); +// diff --git a/htdocs/jsMath/plugins/CHMmode.js b/htdocs/jsMath/plugins/CHMmode.js new file mode 100644 index 0000000000..1bab915f05 --- /dev/null +++ b/htdocs/jsMath/plugins/CHMmode.js @@ -0,0 +1,85 @@ +/* + * CHMmode.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes jsMath work with MicroSoft's HTML Help system + * from within .chm files (compiled help archives). + * + * This file should be loaded BEFORE jsMath.js. + * + * --------------------------------------------------------------------- + * + * 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. + * 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. + */ + +if (!window.jsMath) {window.jsMath = {}} +if (!jsMath.Controls) {jsMath.Controls = {}} +if (!jsMath.Controls.cookie) {jsMath.Controls.cookie = {}} + +jsMath.isCHMmode = 1; + +jsMath.noChangeGlobal = 1; +jsMath.noShowGlobal = 1; +jsMath.noImgFonts = 1; +jsMath.Controls.cookie.global = 'always'; +jsMath.Controls.cookie.hiddenGlobal = 1; + +if (window.location.protocol == "mk:") { + + /* + * Work around bug in hh.exe that causes it to run at 100% CPU + * and not exit if the page is reloaded after an IFRAME is used + * 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 hide the other panel and open the correct one. + */ + + jsMath.Controls.Init = function () { + this.controlPanels = jsMath.Setup.DIV("controlPanels"); + if (!jsMath.Browser.msieButtonBug) {this.Button()} + else {setTimeout("jsMath.Controls.Button()",500)} + } + + jsMath.Controls.Panel = function () { + jsMath.Translate.Cancel(); + jsMath.Setup.AddStyleSheet({ + '#jsMath_options': jsMath.styles['#jsMath_panel'], + '#jsMath_options .disabled': jsMath.styles['#jsMath_panel .disabled'], + '#jsMath_options .infoLink': jsMath.styles['#jsMath_panel .infoLink'] + }); + if (this.loaded) {this.panel = jsMath.Element("panel"); this.Main(); return} + var html = jsMath.Script.xmlRequest(jsMath.root+"jsMath-controls.html"); + var body = (html.match(/([\s\S]*)<\/body>/))[1]; + this.controlPanels.innerHTML = body; + var script = (body.match(/ + + + +
      + +

      jsMath Image Mode Test Page

      + +If you see typeset mathematics below, then jsMath Image Mode is working. If you see +TeX code instead, jsMath Image Mode is not working for you. +

      + +


      + + +
      +\left(\, \sum_{k=1}^n a_k b_k \right)^2 \le +\left(\, \sum_{k=1}^n a_k^2 \right) \left(\, \sum_{k=1}^n b_k^2 \right) +
      +

      + +

      +\warning{       jsMath image mode is not working!       } +
      + + +
      +

      +If the mathematics does not show up properly, you may not have not +installed the jsMath +image fonts file correctly. Follow the instructions on the linked +page. +

      + +Once you have jsMath working, go on to the sample +page to see if easy/load.js is configured properly. + +

      + + + + + + +

      +


      + + + +
      + + + + + + +
      [HOME]jsMath web pages
      +Created: 14 Feb 2007
      + +Last modified: Jun 16, 2007 8:56:17 AM + +
      +Comments to: dpvc@union.edu
      +
       
      +
      + + + diff --git a/htdocs/jsMath/test/index.html b/htdocs/jsMath/test/index.html new file mode 100644 index 0000000000..559d9e4fdb --- /dev/null +++ b/htdocs/jsMath/test/index.html @@ -0,0 +1,114 @@ + + +jsMath (Test): jsMath Test Page + + + + + + + + +
      + + +
      jsMath (Test)
      +
      +

      + + + + + + + +

      +Warning: jsMath +requires JavaScript to process the mathematics on this page.
      +If your browser supports JavaScript, be sure it is enabled. +
      +
      + + +
      + +
      + +
      + +

      jsMath Test Page

      + +If you see typeset mathematics below, then jsMath is working. If you see +TeX code instead, jsMath is not working for you. +

      + +


      + + +
      +\left(\, \sum_{k=1}^n a_k b_k \right)^2 \le +\left(\, \sum_{k=1}^n a_k^2 \right) \left(\, \sum_{k=1}^n b_k^2 \right) +
      +

      + +

      +\warning{       jsMath is not working!       } +
      + + +
      +

      + +Once you have jsMath working properly, view the image mode test page to make sure that the +image fallback mode is working as well. + +

      + + + + + + +

      +


      + + + +
      + + + + + + +
      [HOME]jsMath web pages
      +Created: 14 Feb 2007
      + +Last modified: Feb 12, 2009 6:14:47 PM + +
      +Comments to: dpvc@union.edu
      +
       
      +
      + + + diff --git a/htdocs/jsMath/test/jsMath40.jpg b/htdocs/jsMath/test/jsMath40.jpg new file mode 100644 index 0000000000..db5379477e Binary files /dev/null and b/htdocs/jsMath/test/jsMath40.jpg differ diff --git a/htdocs/jsMath/test/sample.html b/htdocs/jsMath/test/sample.html new file mode 100644 index 0000000000..4497e7a35d --- /dev/null +++ b/htdocs/jsMath/test/sample.html @@ -0,0 +1,165 @@ + + +jsMath (Test): jsMath Sample Page + + + + + + + + + +
      + +

      jsMath Sample Page

      + +This is a sample file showing you how to use jsMath to display mathematics +in your web pages. Be sure you have followed the installation +instructions before loading this file. Also, you may need to edit the +jsMath/easy/load.js 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 +jsMath/easy/load.js, not all of the mathematics below will be +processed by jsMath. Experiment with the settings in that file to see how +they work. +

      + +


      +

      + +

      Some mathematics using tex2math

      + +The easiest way to enter mathematics is to use jsMath's tex2math +plugin to identify the mathematics in your document by looking for \rm\TeX-like math delimiters. Here are some math +equations using those markers. Some inline math: $\sqrt{1-x^2}$ or \(ax^2+bx+c\), +and some displayed math: +$$\int {1\over x}\,dx = \ln(x)+C$$ +and +\[\sum_{i=1}^n i = {n(n+1)\over 2}.\] +Note that the first of these will not be processed unless you have enabled +processSingleDollars in jsMath/easy/load.js, +which is disabled by default. That is because a single dollar sign can +appear in normal text (as in "That will cost from $3.50 to +$5.00 to repair"), and you don't want jsMath to try to typeset +the "3.50 to " as mathematics. +

      + +If you enable processSingleDollars, you might also want to +enable fixEscapedDollars, so that it is possible to enter +dollar signs by preceding them with a backslash. Here's one that you can +use to see the results of these settings: \$ (an escaped dollar) and $x+1$ +(not escaped). +

      + +It is also possible to use your own custom delimiters for marking the +mathematics within your pages. If you uncomment the customDelimiters +array in jsMath/easy/load.js, then the following math will be +typeset by jsMath: some inline math [math]\sin(2\pi x)[/math] and some +display math [display]x={-b\pm \sqrt{b^2-4ac}\over 2a}.[/display] +You may change the delimiters to nearly anything you want, but they can not +look like HTML tags, since some browsers will eliminate unknown tags, and +jsMath doesn't get to look for the custom delimiters until after the +browser has interpreted the page. +

      + +

      +You can prevent the tex2math plugin from processing a portion +of a page by enclosing it in a tag that is of +CLASS="tex2math_ignore". Often, that tag will be a +DIV or SPAN, but it can be anything. This +paragraph is wrapped in a DIV tag with +CLASS="tex2math_ignore", and so no math delimiters will be +processed: $f\colon X\to Y$, \(x^2 \gt 5\), $$1\over 1+x^2$$ and +\[\matrix{a& b\cr c& d}.\] +Note that this includes the processing of escaped dollars (\$) and +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. +
      +

      +JsMath will automatically ignore the text within +PRE tags, so you can easily enter examples that way as well: +

      +   $f\colon X\to Y$, \(x^2 \gt 5\),
      +   $$1\over 1+x^2$$ and \[\matrix{a& b\cr c& d}.\]
      +
      +

      + +Note that since the < and > symbols are used to denote HTML tags, +these can be hard to incorporate into your \rm\TeX +code. Often, putting spaces around the < or > will make it work, but +it is probably better to use \lt and \gt instead. +Also note that the tex2math plugin does not allow any HTML +tags to be within the math delimiters, with the exception of +<BR>, which is ignored. +

      + +See the tex2math +documentation for more information. +

      + +


      +

      + +

      Mathematics without tex2math

      +

      + +If you are not using tex2math, then you will need to enclose +your mathematics within SPAN or DIV tags that +are of CLASS="math". Use a SPAN for in-line math +and a DIV for displayed math. For instance, P = (x_1,\ldots,x_n) and +

      + A = \left\lgroup\matrix{a_{11}& \cdots& a_{1m}\cr + \vdots& \ddots& \vdots\cr + a_{n1}& \cdots& a_{nm}\cr}\right\rgroup. +
      +

      +


      +

      +

      More information

      +

      + +See the jsMath +example files for more examples of using jsMath. There are several extensions +to \rm\TeX that allow jsMath to interact better +with HTML. These provide features such as colored text, tagging mathematics +with CSS styles, and so on. +

      + +More information is available from the jsMath author's +documentation site. JsMath also has a home page at +SourceForge, and that includes public forums +for jsMath where you can ask the jsMath user community for help. +

      + + + diff --git a/htdocs/jsMath/uncompressed/def.js b/htdocs/jsMath/uncompressed/def.js new file mode 100644 index 0000000000..0b5b6a199d --- /dev/null +++ b/htdocs/jsMath/uncompressed/def.js @@ -0,0 +1,1442 @@ +jsMath.Img.AddFont(50,{ + cmr10: [ + [5,5,0], [6,6,0], [6,6,1], [5,6,0], [5,5,0], [6,5,0], [5,5,0], [6,5,0], + [5,5,0], [6,5,0], [5,5,0], [5,5,0], [4,5,0], [4,5,0], [6,5,0], [6,5,0], + [2,4,0], [3,6,2], [3,2,-3], [3,2,-3], [3,2,-3], [3,2,-3], [4,2,-3], [4,3,-3], + [3,3,2], [4,6,1], [5,5,1], [6,5,1], [4,5,1], [7,5,0], [7,6,1], [6,7,1], + [2,2,-1], [2,6,0], [3,3,-2], [6,7,2], [4,7,1], [6,7,1], [6,7,1], [2,3,-2], + [3,8,2], [3,8,2], [4,4,-2], [6,6,1], [2,3,2], [2,1,-1], [2,1,0], [4,8,2], + [4,6,1], [3,5,0], [4,5,0], [4,6,1], [4,5,0], [4,6,1], [4,6,1], [4,6,1], + [4,6,1], [4,6,1], [2,4,0], [2,6,2], [2,6,2], [6,3,0], [3,6,2], [3,5,0], + [6,6,1], [6,6,0], [5,5,0], [5,6,1], [5,5,0], [5,5,0], [5,5,0], [6,6,1], + [6,5,0], [3,5,0], [4,6,1], [6,5,0], [5,5,0], [7,5,0], [6,5,0], [6,6,1], + [5,5,0], [6,7,2], [6,6,1], [4,6,1], [5,5,0], [6,6,1], [6,6,1], [8,6,1], + [6,5,0], [6,5,0], [4,5,0], [2,8,2], [4,3,-2], [2,8,2], [3,2,-3], [2,2,-3], + [2,3,-2], [4,5,1], [4,6,1], [3,5,1], [4,6,1], [3,5,1], [3,5,0], [4,6,2], + [4,5,0], [2,5,0], [3,7,2], [4,5,0], [2,5,0], [6,4,0], [4,4,0], [4,5,1], + [4,6,2], [4,6,2], [3,4,0], [3,5,1], [3,6,1], [4,5,1], [4,5,1], [5,5,1], + [4,4,0], [4,6,2], [3,4,0], [4,1,-1], [7,1,-1], [3,2,-3], [3,1,-4], [3,2,-3] + ], + cmmi10: [ + [6,5,0], [6,6,0], [6,6,1], [5,6,0], [6,5,0], [7,5,0], [6,5,0], [5,5,0], + [5,5,0], [5,5,0], [6,5,0], [5,5,1], [5,7,2], [4,6,2], [4,6,1], [3,5,1], + [4,7,2], [4,6,2], [4,6,1], [3,5,1], [4,5,1], [4,6,1], [4,6,2], [4,4,0], + [4,7,2], [4,5,1], [4,6,2], [4,5,1], [4,5,1], [4,5,1], [5,7,2], [5,6,2], + [5,7,2], [5,5,1], [3,5,1], [4,6,1], [6,5,1], [4,6,2], [3,5,1], [5,6,2], + [7,3,-1], [7,3,1], [7,3,-1], [7,3,1], [2,3,-1], [2,3,-1], [4,5,1], [4,5,1], + [4,5,1], [3,4,0], [4,4,0], [4,6,2], [4,6,2], [4,6,2], [4,6,1], [4,6,2], + [4,6,1], [4,6,2], [2,1,0], [2,3,2], [5,5,1], [4,8,2], [5,5,1], [4,4,0], + [4,7,1], [6,6,0], [6,5,0], [6,6,1], [6,5,0], [6,5,0], [6,5,0], [6,6,1], + [7,5,0], [4,5,0], [5,6,1], [7,5,0], [5,5,0], [8,5,0], [7,5,0], [6,6,1], + [6,5,0], [6,7,2], [6,6,1], [5,6,1], [5,5,0], [6,6,1], [6,6,1], [8,6,1], + [6,5,0], [6,5,0], [6,5,0], [3,7,1], [3,8,2], [3,8,2], [7,3,0], [7,3,0], + [3,6,1], [4,5,1], [3,6,1], [4,5,1], [4,6,1], [4,5,1], [4,7,2], [4,6,2], + [4,6,1], [3,6,1], [4,7,2], [4,6,1], [2,6,1], [6,5,1], [4,5,1], [4,5,1], + [5,6,2], [4,6,2], [4,5,1], [3,5,1], [3,6,1], [4,5,1], [4,5,1], [5,5,1], + [4,5,1], [4,6,2], [4,5,1], [3,5,1], [4,6,2], [5,6,2], [5,2,-3], [5,2,-3] + ], + cmsy10: [ + [5,1,-1], [2,2,-1], [5,4,0], [4,4,0], [6,5,1], [4,4,0], [6,5,0], [6,6,2], + [6,6,1], [6,6,1], [6,6,1], [6,6,1], [6,6,1], [7,8,2], [4,4,0], [4,4,0], + [6,4,0], [6,4,0], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], + [6,3,0], [6,4,0], [5,5,1], [5,5,1], [7,5,1], [7,5,1], [5,5,1], [5,5,1], + [7,3,0], [7,3,0], [3,7,2], [3,7,2], [7,3,0], [7,7,2], [7,7,2], [6,4,0], + [7,5,1], [7,5,1], [5,7,2], [5,7,2], [7,5,1], [7,7,2], [7,7,2], [6,5,1], + [2,4,0], [7,5,1], [5,5,1], [5,5,1], [6,6,0], [6,6,2], [5,8,2], [1,4,0], + [4,6,1], [4,5,0], [5,3,0], [4,7,1], [5,7,1], [5,6,1], [6,5,0], [6,5,0], + [4,5,0], [6,7,1], [5,6,1], [4,6,1], [6,5,0], [4,6,1], [6,6,1], [5,6,1], + [6,6,1], [6,5,0], [6,6,1], [6,6,1], [5,6,1], [8,6,1], [8,7,1], [6,6,1], + [6,6,1], [6,6,1], [6,6,1], [5,6,1], [6,6,0], [6,6,1], [5,6,1], [8,6,1], + [6,5,0], [6,6,1], [6,5,0], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], + [4,5,0], [4,5,0], [3,8,2], [2,8,2], [3,8,2], [2,8,2], [3,8,2], [3,8,2], + [3,8,2], [2,8,2], [2,8,2], [3,8,2], [3,8,2], [5,8,2], [4,8,2], [2,6,1], + [6,8,7], [5,5,0], [6,6,1], [4,8,2], [5,5,0], [5,5,0], [5,6,1], [5,6,1], + [3,7,2], [3,7,2], [3,7,2], [5,7,2], [6,7,1], [6,8,2], [6,7,1], [6,7,1] + ], + cmex10: [ + [3,10,9], [3,10,9], [3,10,9], [2,10,9], [4,10,9], [2,10,9], [4,10,9], [2,10,9], + [4,10,9], [4,10,9], [3,10,9], [3,10,9], [2,6,5], [3,6,5], [4,10,9], [4,10,9], + [4,14,13], [3,14,13], [5,18,17], [4,18,17], [4,18,17], [2,18,17], [4,18,17], [3,18,17], + [4,18,17], [3,18,17], [5,18,17], [5,18,17], [5,18,17], [5,18,17], [7,18,17], [7,18,17], + [6,22,21], [4,22,21], [4,22,21], [3,22,21], [5,22,21], [3,22,21], [5,22,21], [3,22,21], + [5,22,21], [5,22,21], [5,22,21], [5,22,21], [9,22,21], [9,22,21], [6,14,13], [6,14,13], + [6,14,13], [5,14,13], [5,14,13], [3,14,13], [5,14,13], [3,14,13], [3,6,5], [3,6,5], + [6,8,7], [4,8,7], [6,8,7], [4,8,7], [4,14,13], [6,14,13], [4,4,3], [3,6,5], + [6,14,13], [5,14,13], [3,6,5], [5,6,5], [4,14,13], [4,14,13], [6,8,7], [8,11,10], + [5,9,8], [7,17,16], [8,8,7], [11,11,10], [8,8,7], [11,11,10], [8,8,7], [11,11,10], + [7,8,7], [7,8,7], [5,9,8], [6,8,7], [6,8,7], [6,8,7], [6,8,7], [6,8,7], + [10,11,10], [9,11,10], [7,17,16], [8,11,10], [8,11,10], [8,11,10], [8,11,10], [8,11,10], + [7,8,7], [9,11,10], [5,3,-3], [9,2,-4], [12,2,-4], [4,2,-4], [7,2,-4], [11,2,-4], + [4,14,13], [2,14,13], [4,14,13], [3,14,13], [4,14,13], [3,14,13], [4,14,13], [4,14,13], + [8,10,9], [8,14,13], [8,18,17], [8,22,21], [6,14,13], [6,6,5], [8,6,5], [4,6,5], + [4,6,5], [4,6,5], [5,3,2], [5,3,2], [5,3,0], [5,3,0], [6,6,5], [6,6,5] + ], + cmbx10: [ + [5,5,0], [7,5,0], [6,6,1], [6,5,0], [6,5,0], [7,5,0], [6,5,0], [6,5,0], + [6,5,0], [6,5,0], [6,5,0], [6,5,0], [5,5,0], [5,5,0], [7,5,0], [7,5,0], + [2,4,0], [3,6,2], [3,2,-3], [4,2,-3], [4,2,-3], [4,2,-3], [4,2,-3], [4,2,-3], + [4,3,2], [4,6,1], [6,5,1], [7,5,1], [4,5,1], [8,5,0], [8,6,1], [6,7,1], + [3,2,-1], [2,5,0], [4,3,-2], [7,7,2], [4,7,1], [7,7,1], [6,6,1], [2,3,-2], + [3,8,2], [3,8,2], [4,4,-2], [6,6,1], [2,4,2], [3,1,-1], [2,2,0], [4,8,2], + [4,6,1], [4,5,0], [4,5,0], [4,6,1], [4,5,0], [4,6,1], [4,6,1], [4,6,1], + [4,6,1], [4,6,1], [2,4,0], [2,6,2], [2,6,2], [6,3,0], [4,6,2], [4,5,0], + [6,6,1], [6,5,0], [6,5,0], [6,6,1], [6,5,0], [6,5,0], [5,5,0], [6,6,1], + [7,5,0], [3,5,0], [4,6,1], [6,5,0], [5,5,0], [8,5,0], [7,5,0], [6,6,1], + [6,5,0], [6,7,2], [6,6,1], [5,6,1], [6,5,0], [6,6,1], [6,6,1], [9,6,1], + [6,5,0], [6,5,0], [5,5,0], [3,8,2], [4,3,-2], [2,8,2], [4,2,-3], [2,2,-3], + [2,3,-2], [4,5,1], [5,6,1], [4,5,1], [5,6,1], [4,5,1], [4,5,0], [4,6,2], + [5,5,0], [2,5,0], [3,7,2], [5,5,0], [3,5,0], [7,4,0], [5,4,0], [4,5,1], + [5,6,2], [5,6,2], [4,4,0], [3,5,1], [3,6,1], [5,5,1], [5,5,1], [6,5,1], + [5,4,0], [5,6,2], [4,4,0], [5,2,-1], [9,2,-1], [4,2,-3], [4,1,-4], [4,2,-3] + ], + cmti10: [ + [5,5,0], [6,6,0], [6,6,1], [5,6,0], [6,5,0], [6,5,0], [6,5,0], [6,5,0], + [6,5,0], [6,5,0], [6,5,0], [7,7,2], [6,7,2], [6,7,2], [8,7,2], [8,7,2], + [3,5,1], [4,6,2], [4,2,-3], [4,2,-3], [4,2,-3], [4,2,-3], [4,2,-3], [5,3,-3], + [3,3,2], [6,7,2], [6,5,1], [6,5,1], [4,5,1], [7,5,0], [8,6,1], [6,7,1], + [3,2,-1], [3,6,0], [4,3,-2], [6,7,2], [5,6,1], [6,7,1], [6,7,1], [3,3,-2], + [4,8,2], [3,8,2], [5,4,-2], [6,5,1], [2,3,2], [3,1,-1], [2,1,0], [5,8,2], + [4,6,1], [4,5,0], [4,6,1], [4,6,1], [4,7,2], [4,6,1], [4,6,1], [5,6,1], + [4,6,1], [4,6,1], [3,4,0], [3,6,2], [3,6,2], [6,3,0], [4,6,2], [4,6,0], + [6,6,1], [5,6,0], [6,5,0], [6,6,1], [6,5,0], [6,5,0], [6,5,0], [6,6,1], + [6,5,0], [4,5,0], [5,6,1], [7,5,0], [5,5,0], [8,5,0], [6,5,0], [6,6,1], + [6,5,0], [6,7,2], [6,6,1], [5,6,1], [6,5,0], [6,6,1], [7,6,1], [8,6,1], + [6,5,0], [7,5,0], [5,5,0], [4,8,2], [5,3,-2], [4,8,2], [4,2,-3], [3,2,-3], + [3,3,-2], [4,5,1], [4,6,1], [4,5,1], [4,6,1], [4,5,1], [5,7,2], [4,6,2], + [4,6,1], [3,6,1], [4,7,2], [4,6,1], [3,6,1], [6,5,1], [5,5,1], [4,5,1], + [4,6,2], [4,6,2], [4,5,1], [3,5,1], [3,6,1], [4,5,1], [4,5,1], [5,5,1], + [4,5,1], [4,6,2], [4,5,1], [4,1,-1], [8,1,-1], [5,2,-3], [4,2,-3], [4,2,-3] + ] +}); + +jsMath.Img.AddFont(60,{ + cmr10: [ + [5,6,0], [7,6,0], [6,7,1], [6,6,0], [5,6,0], [6,6,0], [6,6,0], [6,6,0], + [6,6,0], [6,6,0], [6,6,0], [6,6,0], [5,6,0], [5,6,0], [7,6,0], [7,6,0], + [2,4,0], [3,6,2], [3,2,-4], [4,2,-4], [4,2,-4], [4,2,-4], [4,1,-4], [4,2,-4], + [3,3,2], [4,7,1], [6,5,1], [6,5,1], [4,6,1], [7,6,0], [8,7,1], [6,7,1], + [3,2,-2], [2,6,0], [3,3,-3], [7,8,2], [4,7,1], [7,7,1], [6,7,1], [2,3,-3], + [3,8,2], [3,8,2], [4,4,-2], [6,6,1], [2,3,2], [3,1,-1], [2,1,0], [4,8,2], + [4,7,1], [4,6,0], [4,6,0], [4,7,1], [4,6,0], [4,7,1], [4,7,1], [4,7,1], + [4,7,1], [4,7,1], [2,4,0], [2,6,2], [2,6,2], [6,2,-1], [4,6,2], [4,6,0], + [6,7,1], [6,6,0], [6,6,0], [6,7,1], [6,6,0], [6,6,0], [5,6,0], [6,7,1], + [6,6,0], [3,6,0], [4,7,1], [6,6,0], [5,6,0], [8,6,0], [6,6,0], [6,7,1], + [5,6,0], [6,8,2], [6,7,1], [4,7,1], [6,6,0], [6,7,1], [6,7,1], [9,7,1], + [6,6,0], [6,6,0], [5,6,0], [3,8,2], [4,3,-3], [2,8,2], [4,2,-4], [2,2,-4], + [2,3,-3], [4,5,1], [5,7,1], [4,5,1], [5,7,1], [4,5,1], [3,6,0], [4,6,2], + [5,6,0], [2,6,0], [3,8,2], [5,6,0], [3,6,0], [7,4,0], [5,4,0], [4,5,1], + [5,6,2], [5,6,2], [3,4,0], [3,5,1], [3,6,1], [5,5,1], [5,5,1], [6,5,1], + [5,4,0], [5,6,2], [4,4,0], [4,1,-2], [8,1,-2], [4,2,-4], [4,2,-4], [4,2,-4] + ], + cmmi10: [ + [6,6,0], [7,6,0], [6,7,1], [6,6,0], [7,6,0], [8,6,0], [7,6,0], [6,6,0], + [6,6,0], [6,6,0], [7,6,0], [5,5,1], [5,8,2], [5,6,2], [4,7,1], [4,5,1], + [4,8,2], [4,6,2], [4,7,1], [3,5,1], [5,5,1], [5,7,1], [5,6,2], [5,4,0], + [4,8,2], [5,5,1], [5,6,2], [5,5,1], [5,5,1], [5,5,1], [5,8,2], [5,6,2], + [6,8,2], [5,5,1], [4,5,1], [5,7,1], [7,5,1], [5,6,2], [4,5,1], [5,6,2], + [8,4,-1], [8,4,1], [8,4,-1], [8,4,1], [2,3,-1], [2,3,-1], [4,6,1], [4,6,1], + [4,5,1], [4,4,0], [4,4,0], [4,6,2], [4,6,2], [4,6,2], [4,7,1], [4,6,2], + [4,7,1], [4,6,2], [2,1,0], [2,3,2], [6,6,1], [4,8,2], [6,6,1], [4,4,0], + [5,7,1], [6,6,0], [7,6,0], [7,7,1], [7,6,0], [7,6,0], [7,6,0], [7,7,1], + [8,6,0], [4,6,0], [6,7,1], [8,6,0], [6,6,0], [9,6,0], [8,6,0], [6,7,1], + [7,6,0], [6,8,2], [7,7,1], [6,7,1], [6,6,0], [7,7,1], [7,7,1], [9,7,1], + [7,6,0], [7,6,0], [6,6,0], [3,7,1], [3,8,2], [3,8,2], [8,2,-1], [8,3,-1], + [4,7,1], [4,5,1], [4,7,1], [4,5,1], [5,7,1], [4,5,1], [5,8,2], [4,6,2], + [5,7,1], [3,7,1], [5,8,2], [5,7,1], [3,7,1], [7,5,1], [5,5,1], [4,5,1], + [5,6,2], [4,6,2], [4,5,1], [4,5,1], [3,7,1], [5,5,1], [4,5,1], [6,5,1], + [5,5,1], [4,6,2], [4,5,1], [3,5,1], [4,6,2], [5,6,2], [5,2,-4], [6,2,-4] + ], + cmsy10: [ + [6,2,-1], [2,2,-1], [6,4,0], [4,4,0], [6,6,1], [4,4,0], [6,6,0], [6,6,2], + [6,6,1], [6,6,1], [6,6,1], [6,6,1], [6,6,1], [8,8,2], [4,4,0], [4,4,0], + [6,4,0], [6,4,0], [6,8,2], [6,8,2], [6,8,2], [6,8,2], [6,8,2], [6,8,2], + [6,2,-1], [6,4,0], [6,6,1], [6,6,1], [8,6,1], [8,6,1], [6,6,1], [6,6,1], + [8,4,0], [8,4,0], [4,8,2], [4,8,2], [8,4,0], [8,8,2], [8,8,2], [6,4,0], + [8,6,1], [8,6,1], [5,8,2], [5,8,2], [8,6,1], [8,8,2], [8,8,2], [6,5,1], + [3,5,0], [8,5,1], [5,6,1], [5,6,1], [7,6,0], [7,6,2], [6,8,2], [1,4,0], + [5,7,1], [4,6,0], [5,3,0], [4,8,1], [6,7,1], [6,7,1], [6,6,0], [6,6,0], + [5,6,0], [7,7,1], [6,7,1], [5,7,1], [7,6,0], [5,7,1], [7,7,1], [5,7,1], + [7,7,1], [7,6,0], [7,7,1], [6,7,1], [6,7,1], [9,7,1], [9,8,1], [7,7,1], + [6,7,1], [7,7,1], [7,7,1], [6,7,1], [7,6,0], [7,7,1], [6,7,1], [9,7,1], + [7,6,0], [6,8,2], [7,6,0], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], + [5,6,0], [5,6,0], [4,8,2], [3,8,2], [4,8,2], [3,8,2], [4,8,2], [4,8,2], + [3,8,2], [3,8,2], [2,8,2], [3,8,2], [4,10,3], [5,10,3], [4,8,2], [2,6,1], + [7,9,8], [6,6,0], [7,7,1], [4,8,2], [5,5,0], [5,5,0], [6,8,2], [6,8,2], + [3,8,2], [4,8,2], [4,8,2], [5,8,2], [6,8,2], [6,8,2], [6,7,1], [6,8,2] + ], + cmex10: [ + [4,11,10], [3,11,10], [4,11,10], [2,11,10], [4,11,10], [3,11,10], [4,11,10], [3,11,10], + [4,11,10], [4,11,10], [4,11,10], [3,11,10], [2,6,5], [4,6,5], [5,11,10], [5,11,10], + [5,16,15], [4,16,15], [6,20,19], [5,20,19], [5,20,19], [3,20,19], [5,20,19], [3,20,19], + [5,20,19], [3,20,19], [5,20,19], [5,20,19], [6,20,19], [5,20,19], [8,20,19], [8,20,19], + [7,25,24], [5,25,24], [5,25,24], [3,25,24], [6,25,24], [3,25,24], [6,25,24], [3,25,24], + [6,25,24], [6,25,24], [6,25,24], [6,25,24], [10,25,24], [10,25,24], [7,16,15], [7,16,15], + [7,16,15], [5,16,15], [6,16,15], [3,16,15], [6,16,15], [3,16,15], [4,6,5], [3,6,5], + [6,9,8], [5,9,8], [6,9,8], [5,9,8], [5,16,15], [6,16,15], [5,4,3], [3,6,5], + [7,16,15], [5,16,15], [4,6,5], [5,6,5], [5,16,15], [4,16,15], [7,9,8], [9,13,12], + [5,10,9], [8,19,18], [9,9,8], [12,13,12], [9,9,8], [12,13,12], [9,9,8], [12,13,12], + [8,9,8], [8,9,8], [5,10,9], [7,9,8], [7,9,8], [7,9,8], [7,9,8], [7,9,8], + [12,13,12], [10,13,12], [8,19,18], [9,13,12], [9,13,12], [9,13,12], [9,13,12], [9,13,12], + [8,9,8], [10,13,12], [6,2,-4], [10,3,-4], [13,3,-4], [5,2,-4], [8,2,-4], [12,2,-4], + [4,16,15], [2,16,15], [5,16,15], [3,16,15], [5,16,15], [3,16,15], [5,16,15], [5,16,15], + [9,11,10], [9,16,15], [9,20,19], [9,25,24], [6,16,15], [6,6,5], [9,6,5], [5,6,5], + [5,6,5], [5,6,5], [5,3,2], [5,3,2], [5,3,0], [5,3,0], [6,6,5], [6,6,5] + ], + cmbx10: [ + [6,6,0], [8,6,0], [7,7,1], [7,6,0], [6,6,0], [7,6,0], [7,6,0], [7,6,0], + [7,6,0], [7,6,0], [7,6,0], [6,6,0], [5,6,0], [5,6,0], [8,6,0], [8,6,0], + [3,4,0], [4,6,2], [3,2,-4], [4,2,-4], [4,2,-4], [4,2,-4], [4,1,-4], [5,2,-4], + [4,3,2], [5,7,1], [7,5,1], [7,5,1], [5,6,1], [9,6,0], [10,7,1], [7,7,1], + [3,2,-2], [3,6,0], [4,4,-2], [8,8,2], [5,7,1], [8,7,1], [7,7,1], [3,4,-2], + [4,8,2], [3,8,2], [4,4,-2], [7,8,2], [2,4,2], [3,2,-1], [2,2,0], [5,8,2], + [5,7,1], [4,6,0], [5,6,0], [5,7,1], [5,6,0], [5,7,1], [5,7,1], [5,7,1], + [5,7,1], [5,7,1], [2,4,0], [2,6,2], [3,6,2], [7,4,0], [4,6,2], [4,6,0], + [7,7,1], [7,6,0], [7,6,0], [7,7,1], [7,6,0], [6,6,0], [6,6,0], [7,7,1], + [7,6,0], [4,6,0], [5,7,1], [7,6,0], [6,6,0], [9,6,0], [7,6,0], [7,7,1], + [6,6,0], [7,8,2], [7,7,1], [5,7,1], [7,6,0], [7,7,1], [7,7,1], [10,7,1], + [7,6,0], [7,6,0], [6,6,0], [3,8,2], [5,4,-2], [2,8,2], [4,2,-4], [2,2,-4], + [2,4,-2], [5,5,1], [5,7,1], [4,5,1], [5,7,1], [4,5,1], [4,6,0], [5,6,2], + [5,6,0], [3,6,0], [4,8,2], [5,6,0], [3,6,0], [8,4,0], [5,4,0], [5,5,1], + [5,6,2], [5,6,2], [4,4,0], [4,5,1], [4,7,1], [5,5,1], [5,5,1], [7,5,1], + [5,4,0], [5,6,2], [4,4,0], [5,1,-2], [10,1,-2], [4,2,-4], [4,2,-4], [4,2,-4] + ], + cmti10: [ + [6,6,0], [7,6,0], [7,7,1], [6,6,0], [7,6,0], [7,6,0], [7,6,0], [7,6,0], + [6,6,0], [7,6,0], [7,6,0], [8,8,2], [6,8,2], [7,8,2], [9,8,2], [9,8,2], + [3,5,1], [4,6,2], [4,2,-4], [5,2,-4], [5,2,-4], [5,2,-4], [5,1,-4], [6,2,-4], + [3,3,2], [6,8,2], [6,5,1], [6,5,1], [5,6,1], [8,6,0], [9,7,1], [7,7,1], + [3,2,-2], [3,6,0], [5,3,-3], [7,8,2], [6,7,1], [7,7,1], [7,7,1], [3,3,-3], + [5,8,2], [4,8,2], [5,4,-2], [7,6,1], [2,3,2], [3,1,-1], [2,1,0], [5,8,2], + [5,7,1], [4,6,0], [5,7,1], [5,7,1], [4,8,2], [5,7,1], [5,7,1], [5,7,1], + [5,7,1], [5,7,1], [3,4,0], [3,6,2], [3,6,2], [7,2,-1], [4,6,2], [5,6,0], + [7,7,1], [6,6,0], [6,6,0], [7,7,1], [7,6,0], [6,6,0], [6,6,0], [7,7,1], + [7,6,0], [5,6,0], [5,7,1], [7,6,0], [5,6,0], [9,6,0], [7,6,0], [7,7,1], + [6,6,0], [7,8,2], [6,7,1], [6,7,1], [7,6,0], [7,7,1], [7,7,1], [9,7,1], + [7,6,0], [7,6,0], [6,6,0], [4,8,2], [5,3,-3], [4,8,2], [5,2,-4], [3,2,-4], + [3,3,-3], [5,5,1], [4,7,1], [4,5,1], [5,7,1], [4,5,1], [5,8,2], [4,6,2], + [5,7,1], [3,7,1], [4,8,2], [5,7,1], [3,7,1], [7,5,1], [5,5,1], [5,5,1], + [5,6,2], [4,6,2], [4,5,1], [4,5,1], [3,7,1], [5,5,1], [4,5,1], [6,5,1], + [5,5,1], [5,6,2], [4,5,1], [5,1,-2], [9,1,-2], [5,2,-4], [5,2,-4], [5,2,-4] + ] +}); + +jsMath.Img.AddFont(70,{ + cmr10: [ + [6,7,0], [8,8,0], [8,9,1], [7,8,0], [7,7,0], [8,7,0], [7,7,0], [8,8,0], + [7,7,0], [8,7,0], [7,8,0], [7,8,0], [6,8,0], [6,8,0], [9,8,0], [9,8,0], + [3,5,0], [4,8,3], [3,2,-5], [4,2,-5], [4,2,-5], [4,2,-5], [5,1,-5], [5,3,-5], + [4,4,3], [5,9,1], [7,6,1], [8,6,1], [5,8,2], [9,7,0], [10,9,1], [8,9,1], + [3,2,-2], [2,8,0], [4,4,-3], [8,9,2], [5,9,1], [8,9,1], [8,9,1], [3,4,-3], + [4,11,3], [3,11,3], [5,5,-3], [8,7,1], [3,4,2], [3,2,-1], [2,2,0], [5,11,3], + [5,8,1], [5,7,0], [5,7,0], [5,8,1], [5,7,0], [5,8,1], [5,8,1], [5,8,1], + [5,8,1], [5,8,1], [2,5,0], [2,7,2], [2,8,3], [8,3,-1], [5,8,3], [5,8,0], + [8,9,1], [8,8,0], [7,7,0], [7,9,1], [8,7,0], [7,7,0], [7,7,0], [8,9,1], + [8,7,0], [4,7,0], [5,8,1], [8,7,0], [6,7,0], [9,7,0], [8,7,0], [8,9,1], + [7,7,0], [8,10,2], [8,8,1], [5,9,1], [7,7,0], [8,8,1], [8,8,1], [11,8,1], + [8,7,0], [8,7,0], [6,7,0], [3,11,3], [5,4,-3], [2,11,3], [4,2,-5], [2,2,-5], + [2,4,-3], [5,6,1], [6,8,1], [5,6,1], [6,8,1], [5,6,1], [4,8,0], [5,8,3], + [6,7,0], [3,7,0], [4,10,3], [6,7,0], [3,7,0], [9,5,0], [6,5,0], [5,6,1], + [6,7,2], [6,7,2], [4,5,0], [4,6,1], [4,8,1], [6,6,1], [6,6,1], [8,6,1], + [6,5,0], [6,8,3], [5,5,0], [5,1,-2], [10,1,-2], [5,2,-5], [5,2,-5], [4,2,-5] + ], + cmmi10: [ + [8,7,0], [8,8,0], [8,9,1], [7,8,0], [8,7,0], [9,7,0], [9,7,0], [8,8,0], + [7,7,0], [7,7,0], [8,8,0], [7,6,1], [6,10,2], [6,8,3], [5,9,1], [4,6,1], + [5,10,3], [5,8,3], [5,9,1], [4,6,1], [6,6,1], [6,8,1], [6,8,3], [6,5,0], + [5,10,3], [6,6,1], [6,8,3], [6,6,1], [6,6,1], [6,6,1], [6,10,3], [6,8,3], + [7,10,3], [7,6,1], [5,6,1], [6,9,1], [9,6,1], [6,7,2], [5,7,2], [7,8,3], + [10,4,-2], [10,4,1], [10,4,-2], [10,4,1], [3,3,-2], [3,3,-2], [5,7,1], [5,7,1], + [5,6,1], [5,5,0], [5,5,0], [5,8,3], [5,7,2], [5,8,3], [5,8,1], [5,8,3], + [5,8,1], [5,8,3], [2,2,0], [3,4,2], [7,7,1], [5,11,3], [7,7,1], [5,5,0], + [6,9,1], [8,8,0], [8,7,0], [8,9,1], [9,7,0], [8,7,0], [8,7,0], [8,9,1], + [9,7,0], [5,7,0], [7,8,1], [9,7,0], [7,7,0], [11,7,0], [9,7,0], [8,9,1], + [8,7,0], [8,10,2], [8,8,1], [7,9,1], [8,7,0], [8,8,1], [8,8,1], [11,8,1], + [9,7,0], [8,7,0], [8,7,0], [4,9,1], [4,11,3], [4,11,3], [10,3,-1], [10,3,-1], + [4,9,1], [5,6,1], [5,8,1], [5,6,1], [6,8,1], [5,6,1], [6,11,3], [5,8,3], + [6,8,1], [3,8,1], [5,10,3], [6,8,1], [3,8,1], [9,6,1], [6,6,1], [5,6,1], + [6,7,2], [5,7,2], [5,6,1], [5,6,1], [4,8,1], [6,6,1], [5,6,1], [7,6,1], + [6,6,1], [5,8,3], [5,6,1], [3,6,1], [5,8,3], [7,8,3], [7,3,-5], [7,2,-5] + ], + cmsy10: [ + [7,1,-2], [2,3,-1], [7,5,0], [5,5,0], [8,7,1], [5,5,0], [8,7,0], [8,7,2], + [8,7,1], [8,7,1], [8,7,1], [8,7,1], [8,7,1], [10,11,3], [5,5,0], [5,5,0], + [8,5,0], [8,5,0], [7,9,2], [7,9,2], [7,9,2], [7,9,2], [7,9,2], [7,9,2], + [8,3,-1], [8,5,0], [7,7,1], [7,7,1], [10,7,1], [10,7,1], [7,7,1], [7,7,1], + [10,5,0], [10,5,0], [5,9,2], [5,9,2], [10,5,0], [10,9,2], [10,9,2], [8,5,0], + [10,7,1], [10,7,1], [6,9,2], [6,9,2], [10,7,1], [10,9,2], [10,9,2], [8,6,1], + [3,6,0], [10,6,1], [6,7,1], [6,7,1], [9,8,0], [9,8,3], [7,11,3], [2,5,0], + [6,8,1], [5,7,0], [7,4,0], [5,9,1], [8,9,1], [7,9,1], [8,7,0], [8,7,0], + [6,7,0], [8,9,1], [7,9,1], [6,9,1], [8,7,0], [6,9,1], [9,8,1], [7,10,2], + [9,8,1], [8,7,0], [9,9,2], [8,9,1], [7,9,1], [12,9,1], [11,9,1], [8,9,1], + [8,8,1], [8,10,2], [9,8,1], [7,9,1], [8,8,0], [8,8,1], [7,8,1], [11,8,1], + [9,7,0], [8,9,2], [8,7,0], [7,7,1], [7,7,1], [7,7,1], [7,7,1], [7,7,1], + [6,7,0], [6,7,0], [5,11,3], [3,11,3], [5,11,3], [3,11,3], [5,11,3], [5,11,3], + [4,11,3], [3,11,3], [2,11,3], [4,11,3], [5,11,3], [6,11,3], [5,11,3], [3,7,1], + [9,11,10], [8,7,0], [8,8,1], [5,11,3], [7,6,0], [7,6,0], [8,9,2], [7,9,2], + [4,11,3], [4,11,3], [4,11,3], [6,9,2], [8,10,2], [8,10,2], [8,9,1], [8,10,2] + ], + cmex10: [ + [5,13,12], [4,13,12], [4,13,12], [3,13,12], [5,13,12], [3,13,12], [5,13,12], [3,13,12], + [5,13,12], [5,13,12], [4,13,12], [4,13,12], [2,8,7], [5,8,7], [6,13,12], [6,13,12], + [6,19,18], [5,19,18], [7,25,24], [6,25,24], [6,25,24], [3,25,24], [6,25,24], [4,25,24], + [6,25,24], [4,25,24], [7,25,24], [7,25,24], [7,25,24], [7,25,24], [10,25,24], [10,25,24], + [8,31,30], [6,31,30], [6,31,30], [4,31,30], [7,31,30], [4,31,30], [7,31,30], [4,31,30], + [7,31,30], [7,31,30], [7,31,30], [7,31,30], [13,31,30], [13,31,30], [8,19,18], [8,19,18], + [9,19,18], [6,19,18], [7,19,18], [4,19,18], [7,19,18], [4,19,18], [4,8,7], [4,8,7], + [8,11,10], [6,11,10], [8,10,9], [6,10,9], [6,20,19], [8,20,19], [6,5,4], [4,8,7], + [9,19,18], [6,19,18], [5,8,7], [6,8,7], [6,19,18], [5,19,18], [8,11,10], [11,15,14], + [7,13,12], [10,24,23], [11,11,10], [15,15,14], [11,11,10], [15,15,14], [11,11,10], [15,15,14], + [10,11,10], [9,11,10], [7,13,12], [8,11,10], [8,11,10], [8,11,10], [8,11,10], [8,11,10], + [14,15,14], [13,15,14], [10,24,23], [11,15,14], [11,15,14], [11,15,14], [11,15,14], [11,15,14], + [9,11,10], [13,15,14], [7,3,-5], [12,3,-5], [16,3,-5], [6,2,-6], [10,2,-6], [15,2,-6], + [5,19,18], [3,19,18], [6,19,18], [4,19,18], [6,19,18], [4,19,18], [6,19,18], [6,19,18], + [11,13,12], [11,19,18], [11,25,24], [11,31,30], [8,19,18], [8,8,7], [11,7,6], [6,8,7], + [6,7,6], [6,7,6], [6,5,3], [6,5,3], [6,4,0], [6,4,0], [8,7,6], [8,7,6] + ], + cmbx10: [ + [7,7,0], [9,7,0], [9,8,1], [8,7,0], [8,7,0], [9,7,0], [8,7,0], [9,7,0], + [8,7,0], [9,7,0], [8,7,0], [8,7,0], [7,7,0], [7,7,0], [10,7,0], [10,7,0], + [3,5,0], [4,7,2], [4,3,-5], [5,3,-5], [5,2,-5], [5,2,-5], [5,2,-5], [6,3,-5], + [5,3,2], [6,8,1], [8,6,1], [9,6,1], [6,8,2], [11,7,0], [12,8,1], [9,9,1], + [4,3,-2], [3,8,0], [5,4,-3], [9,9,2], [6,9,1], [9,9,1], [9,9,1], [3,4,-3], + [4,11,3], [4,11,3], [5,5,-3], [9,9,2], [3,4,2], [4,2,-1], [3,2,0], [6,11,3], + [6,8,1], [5,7,0], [6,7,0], [6,8,1], [6,7,0], [6,8,1], [6,8,1], [6,8,1], + [6,8,1], [6,8,1], [3,5,0], [3,7,2], [3,8,3], [9,3,-1], [5,7,2], [5,7,0], + [9,8,1], [9,7,0], [8,7,0], [8,8,1], [9,7,0], [8,7,0], [7,7,0], [9,8,1], + [9,7,0], [5,7,0], [6,8,1], [9,7,0], [7,7,0], [11,7,0], [9,7,0], [8,8,1], + [8,7,0], [9,9,2], [9,8,1], [6,8,1], [8,7,0], [9,8,1], [9,8,1], [12,8,1], + [9,7,0], [9,7,0], [7,7,0], [3,11,3], [6,4,-3], [2,11,3], [5,2,-5], [3,2,-5], + [3,4,-3], [6,6,1], [6,8,1], [5,6,1], [7,8,1], [5,6,1], [5,7,0], [6,8,3], + [7,7,0], [3,7,0], [4,9,2], [6,7,0], [3,7,0], [10,5,0], [7,5,0], [6,6,1], + [6,7,2], [7,7,2], [5,5,0], [5,6,1], [4,8,1], [7,6,1], [6,6,1], [9,6,1], + [6,5,0], [6,7,2], [5,5,0], [6,1,-2], [12,1,-2], [5,3,-5], [5,2,-5], [5,2,-5] + ], + cmti10: [ + [8,7,0], [8,8,0], [8,9,1], [7,8,0], [8,7,0], [9,7,0], [8,7,0], [9,8,0], + [8,7,0], [9,7,0], [8,8,0], [9,11,3], [7,11,3], [8,11,3], [11,11,3], [11,11,3], + [4,6,1], [5,8,3], [5,2,-5], [6,2,-5], [6,2,-5], [6,2,-5], [6,1,-5], [7,3,-5], + [4,3,2], [7,11,3], [8,6,1], [8,6,1], [6,8,2], [10,7,0], [11,9,1], [9,9,1], + [4,2,-2], [4,8,0], [6,4,-3], [9,9,2], [7,9,1], [9,9,1], [9,9,1], [4,4,-3], + [6,11,3], [4,11,3], [6,5,-3], [8,7,1], [3,4,2], [4,2,-1], [3,2,0], [7,11,3], + [6,8,1], [5,7,0], [6,8,1], [6,8,1], [5,9,2], [6,8,1], [6,8,1], [7,8,1], + [6,8,1], [6,8,1], [4,5,0], [4,7,2], [4,8,3], [8,3,-1], [5,8,3], [6,8,0], + [8,9,1], [7,8,0], [8,7,0], [9,9,1], [8,7,0], [8,7,0], [8,7,0], [9,9,1], + [9,7,0], [6,7,0], [7,8,1], [9,7,0], [7,7,0], [11,7,0], [9,7,0], [8,9,1], + [8,7,0], [8,10,2], [8,8,1], [7,9,1], [9,7,0], [9,8,1], [9,8,1], [12,8,1], + [9,7,0], [9,7,0], [8,7,0], [5,11,3], [7,4,-3], [5,11,3], [6,2,-5], [4,2,-5], + [4,4,-3], [6,6,1], [5,8,1], [5,6,1], [6,8,1], [5,6,1], [6,11,3], [5,8,3], + [6,8,1], [4,8,1], [5,10,3], [6,8,1], [4,8,1], [9,6,1], [6,6,1], [6,6,1], + [6,7,2], [5,7,2], [5,6,1], [5,6,1], [4,8,1], [6,6,1], [5,6,1], [7,6,1], + [6,6,1], [6,8,3], [5,6,1], [6,1,-2], [11,1,-2], [6,2,-5], [6,2,-5], [6,2,-5] + ] +}); + +jsMath.Img.AddFont(85,{ + cmr10: [ + [7,9,0], [10,9,0], [9,10,1], [8,9,0], [8,9,0], [9,9,0], [8,9,0], [9,9,0], + [8,9,0], [9,9,0], [9,9,0], [8,9,0], [7,9,0], [7,9,0], [10,9,0], [10,9,0], + [3,6,0], [4,9,3], [4,3,-6], [5,3,-6], [5,2,-6], [5,3,-6], [6,2,-6], [6,3,-6], + [5,4,3], [6,10,1], [9,7,1], [9,7,1], [6,9,2], [11,9,0], [12,10,1], [9,10,1], + [4,2,-3], [3,9,0], [5,5,-4], [10,12,3], [6,10,1], [10,10,1], [9,10,1], [3,5,-4], + [4,12,3], [4,12,3], [6,6,-3], [9,8,1], [3,5,3], [4,1,-2], [3,2,0], [6,12,3], + [6,9,1], [6,8,0], [6,8,0], [6,9,1], [6,9,0], [6,9,1], [6,9,1], [6,10,1], + [6,9,1], [6,9,1], [3,6,0], [3,9,3], [3,9,3], [9,4,-1], [5,9,3], [5,9,0], + [9,10,1], [9,9,0], [8,9,0], [8,10,1], [9,9,0], [8,9,0], [8,9,0], [9,10,1], + [9,9,0], [4,9,0], [6,10,1], [9,9,0], [7,9,0], [11,9,0], [9,9,0], [9,10,1], + [8,9,0], [9,12,3], [9,10,1], [6,10,1], [9,9,0], [9,10,1], [9,10,1], [13,10,1], + [9,9,0], [9,9,0], [7,9,0], [4,12,3], [6,5,-4], [2,12,3], [5,3,-6], [3,3,-6], + [3,5,-4], [6,7,1], [7,10,1], [5,7,1], [7,10,1], [5,7,1], [5,9,0], [6,9,3], + [7,9,0], [3,9,0], [4,12,3], [7,9,0], [4,9,0], [10,6,0], [7,6,0], [6,7,1], + [7,9,3], [7,9,3], [5,6,0], [5,7,1], [4,9,1], [7,7,1], [7,7,1], [9,7,1], + [7,6,0], [7,9,3], [5,6,0], [6,1,-3], [12,1,-3], [6,3,-6], [5,3,-6], [5,3,-6] + ], + cmmi10: [ + [9,9,0], [10,9,0], [9,10,1], [8,9,0], [10,9,0], [11,9,0], [10,9,0], [9,9,0], + [8,9,0], [9,9,0], [10,9,0], [8,7,1], [8,12,3], [7,9,3], [6,10,1], [5,7,1], + [6,12,3], [6,9,3], [6,10,1], [4,7,1], [7,7,1], [7,10,1], [7,9,3], [7,6,0], + [6,12,3], [7,7,1], [7,9,3], [7,7,1], [7,7,1], [7,7,1], [7,12,3], [8,9,3], + [8,12,3], [8,7,1], [6,7,1], [7,10,1], [10,7,1], [7,9,3], [5,8,2], [8,9,3], + [12,5,-2], [12,5,1], [12,5,-2], [12,5,1], [3,4,-2], [3,4,-2], [6,8,1], [6,8,1], + [6,7,1], [6,6,0], [6,6,0], [6,9,3], [6,9,3], [6,9,3], [6,9,1], [6,9,3], + [6,9,1], [6,9,3], [3,2,0], [3,5,3], [9,8,1], [6,12,3], [9,8,1], [6,6,0], + [7,10,1], [9,9,0], [10,9,0], [10,10,1], [10,9,0], [10,9,0], [10,9,0], [10,10,1], + [11,9,0], [6,9,0], [8,10,1], [11,9,0], [8,9,0], [13,9,0], [11,9,0], [9,10,1], + [10,9,0], [9,12,3], [10,10,1], [8,10,1], [9,9,0], [10,10,1], [10,10,1], [13,10,1], + [11,9,0], [10,9,0], [9,9,0], [4,10,1], [4,12,3], [4,12,3], [12,4,-1], [12,4,-1], + [5,10,1], [6,7,1], [5,10,1], [6,7,1], [7,10,1], [6,7,1], [7,12,3], [6,9,3], + [7,10,1], [4,9,1], [6,11,3], [7,10,1], [4,10,1], [11,7,1], [7,7,1], [6,7,1], + [7,9,3], [6,9,3], [6,7,1], [6,7,1], [4,9,1], [7,7,1], [6,7,1], [9,7,1], + [7,7,1], [6,9,3], [6,7,1], [4,7,1], [6,9,3], [8,9,3], [8,3,-6], [8,2,-6] + ], + cmsy10: [ + [9,2,-2], [3,2,-2], [8,6,0], [6,6,0], [9,8,1], [6,6,0], [9,8,0], [9,8,2], + [9,8,1], [9,8,1], [9,8,1], [9,8,1], [9,8,1], [12,12,3], [6,6,0], [6,6,0], + [9,6,0], [9,6,0], [9,10,2], [9,10,2], [9,10,2], [9,10,2], [9,10,2], [9,10,2], + [9,4,-1], [9,6,0], [9,8,1], [9,8,1], [12,8,1], [12,8,1], [9,8,1], [9,8,1], + [12,6,0], [12,6,0], [6,12,3], [6,12,3], [12,6,0], [12,12,3], [12,12,3], [9,6,0], + [12,8,1], [12,8,1], [7,12,3], [7,12,3], [12,8,1], [12,12,3], [12,12,3], [9,7,1], + [4,7,0], [12,7,1], [7,8,1], [7,8,1], [10,9,0], [10,9,3], [8,12,3], [2,6,0], + [7,10,1], [6,9,0], [8,4,-1], [6,11,1], [9,10,1], [9,10,1], [9,8,0], [9,8,0], + [7,9,0], [10,10,1], [8,10,1], [7,10,1], [10,9,0], [7,10,1], [10,10,1], [8,11,2], + [10,10,1], [9,9,0], [11,11,2], [9,10,1], [8,10,1], [14,10,1], [13,11,1], [10,10,1], + [9,10,1], [10,11,2], [11,10,1], [8,10,1], [10,9,0], [10,10,1], [8,10,1], [13,10,1], + [10,9,0], [9,11,2], [10,9,0], [8,9,1], [8,9,1], [8,9,1], [8,9,1], [8,9,1], + [7,9,0], [7,9,0], [6,12,3], [4,12,3], [6,12,3], [4,12,3], [6,12,3], [6,12,3], + [4,12,3], [4,12,3], [2,12,3], [5,12,3], [6,14,4], [7,14,4], [6,12,3], [3,8,1], + [11,13,12], [9,9,0], [10,10,1], [6,12,3], [8,8,0], [8,8,0], [9,10,2], [9,10,2], + [5,12,3], [5,12,3], [5,12,3], [7,12,3], [9,11,2], [9,11,2], [9,10,1], [9,11,2] + ], + cmex10: [ + [5,15,14], [4,15,14], [5,15,14], [3,15,14], [6,15,14], [4,15,14], [6,15,14], [4,15,14], + [6,15,14], [6,15,14], [5,15,14], [5,15,14], [3,9,8], [5,9,8], [7,15,14], [7,15,14], + [7,23,22], [5,23,22], [9,30,29], [7,30,29], [7,30,29], [4,30,29], [7,30,29], [4,30,29], + [7,30,29], [4,30,29], [8,30,29], [8,30,29], [8,30,29], [8,30,29], [12,30,29], [12,30,29], + [10,37,36], [7,37,36], [7,37,36], [4,37,36], [8,37,36], [5,37,36], [8,37,36], [5,37,36], + [8,37,36], [8,37,36], [9,37,36], [8,37,36], [15,37,36], [15,37,36], [10,23,22], [10,23,22], + [11,23,22], [7,23,22], [8,23,22], [5,23,22], [8,23,22], [5,23,22], [5,9,8], [5,9,8], + [9,12,11], [7,12,11], [9,12,11], [7,12,11], [7,23,22], [9,23,22], [7,5,4], [5,9,8], + [11,23,22], [7,23,22], [5,9,8], [7,9,8], [7,23,22], [6,23,22], [10,13,12], [13,18,17], + [8,15,14], [12,28,27], [13,13,12], [18,18,17], [13,13,12], [18,18,17], [13,13,12], [18,18,17], + [12,13,12], [11,13,12], [8,15,14], [10,13,12], [10,13,12], [10,13,12], [10,13,12], [10,13,12], + [17,18,17], [15,18,17], [12,28,27], [13,18,17], [13,18,17], [13,18,17], [13,18,17], [13,18,17], + [11,13,12], [15,18,17], [8,3,-6], [14,4,-6], [19,4,-6], [7,2,-7], [12,2,-7], [18,2,-7], + [6,23,22], [3,23,22], [7,23,22], [4,23,22], [7,23,22], [4,23,22], [7,23,22], [7,23,22], + [13,15,14], [13,23,22], [13,30,29], [13,37,36], [9,23,22], [9,9,8], [13,8,7], [7,9,8], + [7,9,8], [7,9,8], [7,5,3], [7,5,3], [7,5,0], [7,5,0], [9,9,8], [9,9,8] + ], + cmbx10: [ + [8,9,0], [11,9,0], [10,10,1], [10,9,0], [9,9,0], [11,9,0], [10,9,0], [10,9,0], + [10,9,0], [10,9,0], [10,9,0], [9,9,0], [8,9,0], [8,9,0], [12,9,0], [12,9,0], + [4,6,0], [5,9,3], [5,3,-6], [6,3,-6], [6,2,-6], [6,3,-6], [6,2,-6], [7,3,-6], + [6,4,3], [7,10,1], [10,7,1], [11,7,1], [7,9,2], [13,9,0], [14,10,1], [10,10,1], + [4,2,-3], [4,9,0], [6,5,-4], [11,12,3], [7,10,1], [11,10,1], [11,10,1], [4,5,-4], + [5,12,3], [5,12,3], [6,6,-3], [10,10,2], [3,5,3], [4,2,-2], [3,2,0], [7,12,3], + [7,9,1], [6,8,0], [7,8,0], [7,9,1], [7,8,0], [7,9,1], [7,9,1], [7,10,1], + [7,9,1], [7,9,1], [3,6,0], [3,9,3], [4,9,3], [10,4,-1], [6,9,3], [6,9,0], + [10,10,1], [10,9,0], [10,9,0], [10,10,1], [10,9,0], [9,9,0], [9,9,0], [11,10,1], + [11,9,0], [5,9,0], [7,10,1], [11,9,0], [8,9,0], [13,9,0], [11,9,0], [10,10,1], + [9,9,0], [10,12,3], [11,10,1], [7,10,1], [10,9,0], [11,10,1], [11,10,1], [14,10,1], + [11,9,0], [11,9,0], [8,9,0], [4,12,3], [7,5,-4], [3,12,3], [6,3,-6], [3,3,-6], + [3,5,-4], [7,7,1], [8,10,1], [6,7,1], [8,10,1], [6,7,1], [6,9,0], [7,9,3], + [8,9,0], [4,9,0], [5,12,3], [8,9,0], [4,9,0], [12,6,0], [8,6,0], [7,7,1], + [8,9,3], [8,9,3], [6,6,0], [5,7,1], [5,9,1], [8,7,1], [7,7,1], [10,7,1], + [8,6,0], [7,9,3], [6,6,0], [7,1,-3], [14,1,-3], [6,3,-6], [6,2,-7], [6,3,-6] + ], + cmti10: [ + [9,9,0], [10,9,0], [10,10,1], [8,9,0], [10,9,0], [11,9,0], [10,9,0], [10,9,0], + [9,9,0], [10,9,0], [10,9,0], [11,12,3], [9,12,3], [9,12,3], [12,12,3], [13,12,3], + [4,7,1], [5,9,3], [6,3,-6], [7,3,-6], [7,2,-6], [7,3,-6], [7,2,-6], [9,3,-6], + [5,4,3], [8,12,3], [9,7,1], [9,7,1], [7,9,2], [12,9,0], [13,10,1], [10,10,1], + [5,2,-3], [5,9,0], [7,5,-4], [10,12,3], [9,10,1], [11,10,1], [10,10,1], [5,5,-4], + [7,12,3], [5,12,3], [7,6,-3], [10,8,1], [3,5,3], [5,1,-2], [3,2,0], [8,12,3], + [7,9,1], [6,8,0], [7,9,1], [7,9,1], [6,11,3], [7,9,1], [7,9,1], [8,9,1], + [7,9,1], [7,9,1], [4,6,0], [4,9,3], [4,9,3], [10,4,-1], [6,9,3], [7,9,0], + [10,10,1], [9,9,0], [9,9,0], [10,10,1], [10,9,0], [9,9,0], [9,9,0], [10,10,1], + [11,9,0], [7,9,0], [8,10,1], [11,9,0], [8,9,0], [13,9,0], [11,9,0], [10,10,1], + [9,9,0], [10,12,3], [9,10,1], [8,10,1], [10,9,0], [11,10,1], [11,10,1], [14,10,1], + [10,9,0], [11,9,0], [9,9,0], [6,12,3], [8,5,-4], [6,12,3], [7,3,-6], [5,3,-6], + [5,5,-4], [7,7,1], [6,10,1], [6,7,1], [7,10,1], [6,7,1], [7,12,3], [6,9,3], + [7,10,1], [4,9,1], [6,11,3], [7,10,1], [4,10,1], [11,7,1], [8,7,1], [7,7,1], + [7,9,3], [6,9,3], [6,7,1], [6,7,1], [5,9,1], [7,7,1], [6,7,1], [9,7,1], + [7,7,1], [7,9,3], [6,7,1], [7,1,-3], [13,1,-3], [7,3,-6], [7,3,-6], [7,3,-6] + ] +}); + +jsMath.Img.AddFont(100,{ + cmr10: [ + [9,10,0], [11,11,0], [11,11,1], [10,11,0], [9,10,0], [11,10,0], [10,10,0], [11,10,0], + [10,10,0], [11,10,0], [10,10,0], [9,10,0], [8,10,0], [8,10,0], [12,10,0], [12,10,0], + [4,7,0], [4,10,3], [5,3,-7], [6,3,-7], [6,2,-7], [6,3,-7], [7,2,-7], [7,4,-7], + [6,4,3], [7,11,1], [10,8,1], [11,8,1], [7,10,2], [13,10,0], [14,11,1], [11,12,1], + [4,3,-3], [3,11,0], [5,5,-5], [11,13,3], [7,12,1], [11,12,1], [11,12,1], [3,5,-5], + [5,15,4], [5,15,4], [7,7,-4], [11,11,2], [3,5,3], [4,2,-2], [3,2,0], [7,15,4], + [7,11,1], [6,10,0], [7,10,0], [7,11,1], [7,10,0], [7,11,1], [7,11,1], [7,11,1], + [7,11,1], [7,11,1], [3,7,0], [3,10,3], [3,11,4], [11,5,-1], [6,10,3], [6,10,0], + [11,11,1], [11,11,0], [10,10,0], [10,11,1], [10,10,0], [10,10,0], [9,10,0], [11,11,1], + [11,10,0], [5,10,0], [7,11,1], [11,10,0], [9,10,0], [13,10,0], [11,10,0], [11,11,1], + [9,10,0], [11,13,3], [11,11,1], [7,11,1], [10,10,0], [11,11,1], [11,11,1], [15,11,1], + [11,10,0], [11,10,0], [8,10,0], [4,15,4], [7,5,-5], [3,15,4], [6,3,-7], [3,3,-7], + [3,5,-5], [7,8,1], [8,11,1], [6,8,1], [8,11,1], [6,8,1], [5,10,0], [7,10,3], + [8,10,0], [4,10,0], [4,13,3], [8,10,0], [4,10,0], [12,7,0], [8,7,0], [7,8,1], + [8,10,3], [8,10,3], [6,7,0], [6,8,1], [5,10,1], [8,8,1], [8,8,1], [10,8,1], + [8,7,0], [8,10,3], [6,7,0], [7,1,-3], [14,1,-3], [6,3,-7], [6,2,-8], [6,3,-7] + ], + cmmi10: [ + [11,10,0], [12,11,0], [11,11,1], [10,11,0], [11,10,0], [13,10,0], [12,10,0], [10,10,0], + [9,10,0], [10,10,0], [11,10,0], [9,8,1], [9,13,3], [8,11,4], [7,11,1], [6,8,1], + [7,13,3], [7,11,4], [7,11,1], [5,8,1], [8,8,1], [8,11,1], [9,11,4], [8,7,0], + [7,13,3], [8,8,1], [8,11,4], [8,8,1], [8,8,1], [8,8,1], [9,13,3], [9,10,3], + [9,13,3], [9,8,1], [6,8,1], [8,11,1], [12,8,1], [8,10,3], [6,9,2], [9,11,4], + [14,5,-3], [14,5,1], [14,5,-3], [14,5,1], [4,4,-3], [4,4,-3], [7,9,1], [7,9,1], + [7,8,1], [6,7,0], [7,7,0], [7,11,4], [7,10,3], [7,11,4], [7,11,1], [7,11,4], + [7,11,1], [7,11,4], [3,2,0], [3,5,3], [10,9,1], [7,15,4], [10,9,1], [7,7,0], + [8,12,1], [11,11,0], [11,10,0], [11,11,1], [12,10,0], [11,10,0], [11,10,0], [11,11,1], + [13,10,0], [7,10,0], [9,11,1], [13,10,0], [9,10,0], [15,10,0], [13,10,0], [11,11,1], + [11,10,0], [11,13,3], [11,11,1], [10,11,1], [10,10,0], [11,11,1], [11,11,1], [15,11,1], + [12,10,0], [11,10,0], [11,10,0], [5,12,1], [5,15,4], [5,15,4], [14,5,-1], [14,5,-1], + [6,11,1], [7,8,1], [6,11,1], [7,8,1], [8,11,1], [7,8,1], [8,13,3], [7,10,3], + [8,11,1], [5,11,1], [7,13,3], [8,11,1], [4,11,1], [12,8,1], [8,8,1], [7,8,1], + [8,10,3], [7,10,3], [7,8,1], [6,8,1], [5,10,1], [8,8,1], [7,8,1], [10,8,1], + [8,8,1], [7,10,3], [7,8,1], [5,8,1], [7,10,3], [9,11,4], [9,3,-7], [10,3,-7] + ], + cmsy10: [ + [10,1,-3], [3,3,-2], [9,7,0], [7,7,0], [11,9,1], [7,7,0], [11,10,0], [11,10,3], + [11,11,2], [11,11,2], [11,11,2], [11,11,2], [11,11,2], [14,15,4], [7,7,0], [7,7,0], + [11,7,0], [11,7,0], [10,11,2], [10,11,2], [10,11,2], [10,11,2], [10,11,2], [10,11,2], + [11,5,-1], [11,7,0], [10,9,1], [10,9,1], [14,9,1], [14,9,1], [10,9,1], [10,9,1], + [14,5,-1], [14,5,-1], [6,13,3], [6,13,3], [14,5,-1], [14,13,3], [14,13,3], [11,7,0], + [14,9,1], [14,9,1], [9,13,3], [9,13,3], [14,9,1], [14,13,3], [14,13,3], [11,8,1], + [4,8,0], [14,8,1], [9,9,1], [9,9,1], [12,11,0], [12,11,4], [9,15,4], [2,7,0], + [8,11,1], [7,10,0], [9,4,-1], [7,13,2], [10,12,1], [10,11,1], [11,10,0], [11,10,0], + [8,10,0], [12,12,1], [10,11,1], [8,11,1], [11,10,0], [8,11,1], [12,11,1], [9,12,2], + [12,11,1], [10,10,0], [12,12,2], [11,11,1], [10,11,1], [16,11,1], [15,12,1], [11,11,1], + [11,11,1], [12,12,2], [12,11,1], [9,11,1], [12,11,0], [11,11,1], [10,11,1], [15,11,1], + [12,10,0], [11,12,2], [11,10,0], [9,10,1], [9,10,1], [9,10,1], [9,10,1], [9,10,1], + [8,10,0], [8,10,0], [6,15,4], [4,15,4], [6,15,4], [4,15,4], [6,15,4], [6,15,4], + [5,15,4], [4,15,4], [3,15,4], [6,15,4], [6,15,4], [9,15,4], [7,15,4], [4,11,2], + [12,15,14], [10,10,0], [11,11,1], [7,15,4], [9,9,0], [9,9,0], [10,11,2], [10,11,2], + [6,13,3], [6,14,4], [6,13,3], [9,13,3], [11,13,2], [11,14,3], [11,12,1], [11,13,2] + ], + cmex10: [ + [6,18,17], [5,18,17], [6,18,17], [3,18,17], [7,18,17], [4,18,17], [7,18,17], [4,18,17], + [7,18,17], [7,18,17], [6,18,17], [6,18,17], [3,10,9], [6,10,9], [8,18,17], [8,18,17], + [8,26,25], [6,26,25], [10,35,34], [8,35,34], [8,35,34], [4,35,34], [8,35,34], [5,35,34], + [8,35,34], [5,35,34], [9,35,34], [9,35,34], [10,35,34], [9,35,34], [14,35,34], [14,35,34], + [11,43,42], [8,43,42], [8,43,42], [5,43,42], [9,43,42], [6,43,42], [9,43,42], [6,43,42], + [10,43,42], [10,43,42], [10,43,42], [10,43,42], [18,43,42], [18,43,42], [11,26,25], [11,26,25], + [12,26,25], [9,26,25], [10,26,25], [5,26,25], [10,26,25], [5,26,25], [6,10,9], [5,10,9], + [11,14,13], [8,14,13], [11,14,13], [8,14,13], [8,27,26], [11,27,26], [8,6,5], [5,10,9], + [12,26,25], [9,26,25], [6,10,9], [9,10,9], [8,26,25], [7,26,25], [11,15,14], [15,21,20], + [9,17,16], [14,33,32], [15,15,14], [21,21,20], [15,15,14], [21,21,20], [15,15,14], [21,21,20], + [14,15,14], [13,15,14], [9,17,16], [11,15,14], [11,15,14], [11,15,14], [11,15,14], [11,15,14], + [20,21,20], [18,21,20], [14,33,32], [15,21,20], [15,21,20], [15,21,20], [15,21,20], [15,21,20], + [13,15,14], [18,21,20], [9,4,-7], [16,3,-8], [22,3,-8], [8,3,-8], [14,3,-8], [21,3,-8], + [7,26,25], [4,26,25], [8,26,25], [5,26,25], [8,26,25], [5,26,25], [8,26,25], [8,26,25], + [15,18,17], [15,26,25], [15,35,34], [15,43,42], [11,27,26], [11,10,9], [16,10,9], [8,10,9], + [8,10,9], [8,10,9], [8,5,3], [8,5,3], [8,5,0], [8,5,0], [11,10,9], [11,10,9] + ], + cmbx10: [ + [9,10,0], [13,10,0], [12,11,1], [11,10,0], [11,10,0], [13,10,0], [11,10,0], [12,10,0], + [11,10,0], [12,10,0], [11,10,0], [11,10,0], [9,10,0], [9,10,0], [13,10,0], [13,10,0], + [4,7,0], [5,10,3], [5,3,-7], [7,3,-7], [7,3,-7], [7,3,-7], [7,2,-7], [8,3,-7], + [7,4,3], [8,11,1], [12,8,1], [13,8,1], [8,10,2], [15,10,0], [16,11,1], [12,12,1], + [5,3,-3], [4,10,0], [7,6,-4], [13,13,3], [8,12,1], [13,12,1], [12,11,1], [4,6,-4], + [6,15,4], [5,15,4], [7,7,-4], [12,11,2], [4,6,3], [5,2,-2], [4,3,0], [8,15,4], + [8,11,1], [7,10,0], [8,10,0], [8,11,1], [8,10,0], [8,11,1], [8,11,1], [8,11,1], + [8,11,1], [8,11,1], [4,7,0], [4,10,3], [4,10,3], [12,5,-1], [7,10,3], [7,10,0], + [12,11,1], [12,10,0], [11,10,0], [11,11,1], [12,10,0], [11,10,0], [10,10,0], [12,11,1], + [13,10,0], [6,10,0], [8,11,1], [12,10,0], [9,10,0], [15,10,0], [13,10,0], [12,11,1], + [11,10,0], [12,13,3], [13,11,1], [9,11,1], [11,10,0], [12,11,1], [12,11,1], [17,11,1], + [12,10,0], [12,10,0], [10,10,0], [5,15,4], [8,6,-4], [3,15,4], [7,3,-7], [4,3,-7], + [4,6,-4], [8,8,1], [9,11,1], [7,8,1], [9,11,1], [7,8,1], [7,10,0], [8,10,3], + [9,10,0], [4,10,0], [5,13,3], [9,10,0], [5,10,0], [14,7,0], [9,7,0], [8,8,1], + [9,10,3], [9,10,3], [7,7,0], [6,8,1], [6,10,1], [9,8,1], [9,8,1], [12,8,1], + [9,7,0], [9,10,3], [7,7,0], [9,2,-3], [17,2,-3], [7,3,-7], [7,2,-8], [7,3,-7] + ], + cmti10: [ + [10,10,0], [11,11,0], [12,11,1], [9,11,0], [11,10,0], [12,10,0], [11,10,0], [12,10,0], + [11,10,0], [12,10,0], [11,10,0], [12,13,3], [10,13,3], [10,13,3], [14,13,3], [15,13,3], + [5,8,1], [6,10,3], [7,3,-7], [8,3,-7], [8,2,-7], [8,3,-7], [8,2,-7], [10,4,-7], + [5,4,3], [10,13,3], [11,8,1], [11,8,1], [8,10,2], [14,10,0], [15,11,1], [12,12,1], + [5,3,-3], [6,11,0], [8,5,-5], [12,13,3], [10,11,1], [12,12,1], [12,12,1], [6,5,-5], + [8,15,4], [6,15,4], [9,7,-4], [11,9,1], [4,5,3], [5,2,-2], [4,2,0], [9,15,4], + [8,11,1], [7,10,0], [8,11,1], [8,11,1], [7,13,3], [8,11,1], [8,11,1], [9,11,1], + [8,11,1], [8,11,1], [5,7,0], [5,10,3], [5,11,4], [11,5,-1], [7,11,4], [8,11,0], + [12,11,1], [10,11,0], [11,10,0], [12,11,1], [11,10,0], [11,10,0], [11,10,0], [12,11,1], + [12,10,0], [8,10,0], [9,11,1], [13,10,0], [9,10,0], [15,10,0], [12,10,0], [12,11,1], + [11,10,0], [12,13,3], [11,11,1], [9,11,1], [12,10,0], [12,11,1], [13,11,1], [16,11,1], + [12,10,0], [13,10,0], [10,10,0], [7,15,4], [9,5,-5], [7,15,4], [8,3,-7], [6,3,-7], + [6,5,-5], [8,8,1], [7,11,1], [7,8,1], [8,11,1], [7,8,1], [8,13,3], [7,10,3], + [8,11,1], [5,11,1], [6,13,3], [8,11,1], [5,11,1], [12,8,1], [9,8,1], [8,8,1], + [8,10,3], [7,10,3], [7,8,1], [6,8,1], [6,10,1], [8,8,1], [7,8,1], [10,8,1], + [8,8,1], [8,10,3], [7,8,1], [8,1,-3], [15,1,-3], [9,3,-7], [9,3,-7], [8,3,-7] + ] +}); + +jsMath.Img.AddFont(120,{ + cmr10: [ + [10,12,0], [14,13,0], [13,13,1], [12,13,0], [11,12,0], [13,12,0], [12,12,0], [13,12,0], + [12,12,0], [13,12,0], [12,12,0], [11,12,0], [9,12,0], [9,12,0], [14,12,0], [14,12,0], + [5,8,0], [5,12,4], [5,4,-8], [7,4,-8], [7,3,-8], [7,4,-8], [8,2,-9], [8,4,-9], + [7,5,4], [8,13,1], [12,9,1], [13,9,1], [8,12,2], [15,12,0], [17,13,1], [13,14,1], + [5,3,-4], [4,13,0], [6,6,-6], [14,16,4], [8,14,1], [14,14,1], [13,14,1], [4,6,-6], + [6,18,5], [5,18,5], [8,8,-5], [13,12,2], [4,6,4], [5,2,-3], [4,2,0], [8,18,5], + [8,13,1], [8,12,0], [8,12,0], [8,13,1], [8,12,0], [8,13,1], [8,13,1], [9,13,1], + [8,13,1], [8,13,1], [4,8,0], [4,12,4], [4,13,4], [13,5,-2], [8,13,4], [8,12,0], + [13,13,1], [13,13,0], [12,12,0], [12,13,1], [13,12,0], [12,12,0], [11,12,0], [13,13,1], + [13,12,0], [6,12,0], [8,13,1], [13,12,0], [10,12,0], [15,12,0], [13,12,0], [13,13,1], + [11,12,0], [13,16,4], [13,13,1], [9,13,1], [12,12,0], [13,13,1], [13,13,1], [18,13,1], + [13,12,0], [13,12,0], [10,12,0], [5,18,5], [8,6,-6], [3,18,5], [7,3,-9], [4,3,-9], + [4,6,-6], [9,9,1], [9,13,1], [8,9,1], [9,13,1], [8,9,1], [7,12,0], [9,12,4], + [10,12,0], [5,12,0], [5,16,4], [9,12,0], [5,12,0], [14,8,0], [10,8,0], [8,9,1], + [9,12,4], [9,12,4], [7,8,0], [7,9,1], [6,12,1], [10,9,1], [9,9,1], [12,9,1], + [9,8,0], [9,12,4], [7,8,0], [9,1,-4], [17,1,-4], [8,4,-8], [8,3,-9], [7,3,-9] + ], + cmmi10: [ + [13,12,0], [14,13,0], [13,13,1], [12,13,0], [14,12,0], [15,12,0], [14,12,0], [12,12,0], + [11,12,0], [12,12,0], [14,12,0], [11,9,1], [11,16,4], [10,12,4], [8,14,1], [7,9,1], + [9,16,4], [9,12,4], [8,13,1], [6,9,1], [10,9,1], [10,13,1], [10,12,4], [9,8,0], + [8,16,4], [10,9,1], [9,12,4], [10,9,1], [9,9,1], [9,9,1], [10,16,4], [11,12,4], + [11,16,4], [11,9,1], [8,9,1], [10,13,1], [14,9,1], [9,12,4], [7,10,2], [11,12,4], + [17,6,-3], [17,6,1], [17,6,-3], [17,6,1], [4,5,-3], [4,5,-3], [9,10,1], [9,10,1], + [8,9,1], [8,8,0], [8,8,0], [8,12,4], [8,12,4], [8,12,4], [8,13,1], [9,12,4], + [8,13,1], [8,12,4], [4,2,0], [4,6,4], [12,11,1], [8,18,5], [12,11,1], [9,9,0], + [10,14,1], [13,13,0], [13,12,0], [13,13,1], [14,12,0], [14,12,0], [13,12,0], [13,13,1], + [15,12,0], [9,12,0], [11,13,1], [16,12,0], [11,12,0], [18,12,0], [15,12,0], [13,13,1], + [13,12,0], [13,16,4], [13,13,1], [11,13,1], [12,12,0], [13,13,1], [14,13,1], [18,13,1], + [15,12,0], [13,12,0], [13,12,0], [6,14,1], [6,17,4], [6,17,4], [17,5,-2], [17,5,-2], + [7,13,1], [9,9,1], [8,13,1], [8,9,1], [9,13,1], [8,9,1], [10,16,4], [9,12,4], + [10,13,1], [5,13,1], [8,16,4], [9,13,1], [5,13,1], [15,9,1], [10,9,1], [8,9,1], + [10,12,4], [8,12,4], [8,9,1], [8,9,1], [6,12,1], [10,9,1], [8,9,1], [12,9,1], + [9,9,1], [9,12,4], [8,9,1], [5,9,1], [8,12,4], [11,12,4], [11,5,-8], [12,3,-9] + ], + cmsy10: [ + [12,2,-3], [4,3,-3], [11,9,0], [8,8,0], [13,11,1], [9,9,0], [13,12,0], [13,12,3], + [13,12,2], [13,12,2], [13,12,2], [13,12,2], [13,12,2], [17,17,4], [8,8,0], [8,8,0], + [13,9,0], [13,8,0], [12,14,3], [12,14,3], [12,14,3], [12,14,3], [12,14,3], [12,14,3], + [13,5,-2], [13,9,0], [12,11,1], [12,11,1], [17,12,2], [17,12,2], [12,11,1], [12,11,1], + [17,7,-1], [17,7,-1], [8,16,4], [8,16,4], [17,7,-1], [17,16,4], [17,16,4], [13,8,0], + [17,10,1], [17,10,1], [10,16,4], [10,16,4], [17,10,1], [17,16,4], [17,16,4], [13,9,1], + [5,10,0], [17,9,1], [10,11,1], [10,11,1], [15,13,0], [15,13,4], [11,17,4], [3,7,-1], + [10,13,1], [9,12,0], [11,6,-1], [8,16,2], [13,14,1], [12,13,1], [13,12,0], [13,12,0], + [10,12,0], [14,14,1], [12,13,1], [10,13,1], [14,12,0], [10,13,1], [15,13,1], [11,15,3], + [14,13,1], [12,12,0], [15,15,3], [13,13,1], [12,13,1], [19,13,1], [18,15,1], [14,13,1], + [13,13,1], [14,15,3], [15,13,1], [11,13,1], [14,13,0], [13,13,1], [12,13,1], [18,13,1], + [14,12,0], [13,15,3], [14,12,0], [11,12,1], [11,12,1], [11,12,1], [11,12,1], [11,12,1], + [10,12,0], [10,12,0], [8,18,5], [5,18,5], [8,18,5], [5,18,5], [8,18,5], [8,18,5], + [6,18,5], [5,18,5], [3,18,5], [7,18,5], [8,19,5], [10,19,5], [8,18,5], [4,12,2], + [15,18,17], [13,12,0], [14,13,1], [8,17,4], [11,11,0], [11,11,0], [13,14,3], [12,14,3], + [7,16,4], [7,16,4], [7,16,4], [10,16,4], [13,16,3], [13,16,3], [13,14,1], [13,16,3] + ], + cmex10: [ + [8,21,20], [6,21,20], [7,21,20], [4,21,20], [8,21,20], [5,21,20], [8,21,20], [5,21,20], + [8,21,20], [8,21,20], [7,21,20], [7,21,20], [4,12,11], [7,12,11], [9,21,20], [9,21,20], + [10,31,30], [8,31,30], [12,42,41], [9,42,41], [9,42,41], [5,42,41], [10,42,41], [6,42,41], + [10,42,41], [6,42,41], [11,42,41], [11,42,41], [12,42,41], [11,42,41], [17,42,41], [17,42,41], + [13,52,51], [10,52,51], [10,52,51], [6,52,51], [11,52,51], [7,52,51], [11,52,51], [7,52,51], + [12,52,51], [12,52,51], [12,52,51], [12,52,51], [21,52,51], [21,52,51], [13,31,30], [13,31,30], + [15,32,31], [10,32,31], [12,31,30], [6,31,30], [12,31,30], [6,31,30], [7,12,11], [6,12,11], + [13,17,16], [9,17,16], [13,17,16], [9,17,16], [9,32,31], [13,32,31], [9,7,6], [7,12,11], + [15,31,30], [10,31,30], [7,12,11], [10,12,11], [9,31,30], [9,31,30], [14,18,17], [18,25,24], + [11,20,19], [17,39,38], [18,18,17], [25,25,24], [18,18,17], [25,25,24], [18,18,17], [25,25,24], + [17,18,17], [16,18,17], [11,20,19], [14,18,17], [14,18,17], [14,18,17], [14,18,17], [14,18,17], + [24,25,24], [21,25,24], [17,39,38], [18,25,24], [18,25,24], [18,25,24], [18,25,24], [18,25,24], + [16,18,17], [21,25,24], [11,4,-9], [19,5,-9], [26,5,-9], [10,3,-10], [17,3,-10], [25,3,-10], + [8,31,30], [5,31,30], [9,31,30], [6,31,30], [9,31,30], [6,31,30], [10,31,30], [10,31,30], + [18,21,20], [18,31,30], [18,42,41], [18,52,51], [13,32,31], [13,12,11], [19,11,10], [9,12,11], + [10,12,11], [10,12,11], [9,7,4], [10,7,4], [9,6,0], [10,6,0], [13,12,11], [13,12,11] + ], + cmbx10: [ + [11,12,0], [16,12,0], [15,13,1], [13,12,0], [13,12,0], [15,12,0], [14,12,0], [15,12,0], + [14,12,0], [15,12,0], [14,12,0], [13,12,0], [11,12,0], [11,12,0], [16,12,0], [16,12,0], + [5,8,0], [6,12,4], [6,4,-8], [8,4,-8], [8,4,-8], [8,4,-8], [9,2,-9], [10,3,-9], + [8,5,4], [10,13,1], [14,9,1], [15,9,1], [10,12,2], [18,12,0], [20,13,1], [15,15,2], + [6,3,-4], [5,12,0], [8,7,-5], [16,16,4], [9,14,1], [16,14,1], [15,13,1], [5,7,-5], + [7,18,5], [6,18,5], [9,8,-5], [15,14,3], [5,7,4], [6,3,-2], [5,3,0], [9,18,5], + [9,13,1], [9,12,0], [9,12,0], [9,13,1], [10,12,0], [9,13,1], [9,13,1], [10,13,1], + [9,13,1], [9,13,1], [5,8,0], [5,12,4], [5,13,4], [15,6,-1], [9,13,4], [9,12,0], + [15,13,1], [15,12,0], [13,12,0], [14,13,1], [14,12,0], [13,12,0], [12,12,0], [15,13,1], + [15,12,0], [7,12,0], [9,13,1], [15,12,0], [11,12,0], [18,12,0], [15,12,0], [14,13,1], + [13,12,0], [14,16,4], [15,13,1], [10,13,1], [13,12,0], [15,13,1], [15,13,1], [20,13,1], + [15,12,0], [15,12,0], [11,12,0], [5,18,5], [10,7,-5], [4,18,5], [8,3,-9], [5,3,-9], + [5,7,-5], [10,9,1], [11,13,1], [9,9,1], [11,13,1], [9,9,1], [8,12,0], [10,12,4], + [11,12,0], [5,12,0], [6,16,4], [10,12,0], [5,12,0], [16,8,0], [11,8,0], [10,9,1], + [11,12,4], [11,12,4], [8,8,0], [8,9,1], [7,12,1], [11,9,1], [10,9,1], [14,9,1], + [10,8,0], [10,12,4], [8,8,0], [10,1,-4], [20,1,-4], [9,5,-8], [9,3,-9], [8,3,-9] + ], + cmti10: [ + [13,12,0], [13,13,0], [14,13,1], [11,13,0], [13,12,0], [15,12,0], [14,12,0], [15,12,0], + [13,12,0], [15,12,0], [13,12,0], [14,16,4], [12,16,4], [12,16,4], [17,16,4], [18,16,4], + [6,9,1], [7,12,4], [8,4,-8], [10,4,-8], [10,3,-8], [10,4,-8], [10,2,-9], [12,4,-9], + [6,5,4], [11,16,4], [13,9,1], [13,9,1], [10,12,2], [17,12,0], [18,13,1], [14,15,2], + [6,3,-4], [7,13,0], [9,6,-6], [15,16,4], [12,13,1], [15,14,1], [14,14,1], [7,6,-6], + [9,18,5], [7,18,5], [10,8,-5], [13,11,1], [4,6,4], [6,2,-3], [4,2,0], [11,18,5], + [10,13,1], [8,12,0], [10,13,1], [10,13,1], [9,16,4], [10,13,1], [10,13,1], [11,13,1], + [10,13,1], [10,13,1], [6,8,0], [6,12,4], [6,13,4], [14,5,-2], [8,13,4], [10,13,0], + [14,13,1], [12,13,0], [13,12,0], [14,13,1], [14,12,0], [13,12,0], [13,12,0], [14,13,1], + [15,12,0], [9,12,0], [11,13,1], [15,12,0], [11,12,0], [18,12,0], [15,12,0], [14,13,1], + [13,12,0], [14,16,4], [13,13,1], [11,13,1], [14,12,0], [15,13,1], [15,13,1], [20,13,1], + [15,12,0], [15,12,0], [12,12,0], [8,18,5], [11,6,-6], [8,18,5], [9,3,-9], [7,3,-9], + [7,6,-6], [10,9,1], [8,13,1], [8,9,1], [10,13,1], [8,9,1], [9,16,4], [9,12,4], + [10,13,1], [6,13,1], [8,16,4], [9,13,1], [6,13,1], [15,9,1], [10,9,1], [9,9,1], + [9,12,4], [9,12,4], [9,9,1], [8,9,1], [7,12,1], [10,9,1], [9,9,1], [12,9,1], + [9,9,1], [9,12,4], [8,9,1], [10,1,-4], [18,1,-4], [10,4,-8], [10,3,-9], [10,3,-9] + ] +}); + +jsMath.Img.AddFont(144,{ + cmr10: [ + [12,14,0], [16,15,0], [15,16,1], [14,15,0], [13,14,0], [15,14,0], [14,14,0], [15,15,0], + [14,14,0], [15,14,0], [14,15,0], [13,15,0], [11,15,0], [11,15,0], [17,15,0], [17,15,0], + [5,9,0], [6,14,5], [6,4,-10], [8,4,-10], [8,3,-10], [8,4,-10], [9,1,-11], [10,5,-10], + [8,6,5], [10,16,1], [14,10,1], [15,10,1], [10,14,3], [18,14,0], [20,16,1], [15,17,2], + [6,3,-5], [4,15,0], [7,7,-7], [16,18,4], [9,17,2], [16,17,2], [15,16,1], [5,7,-7], + [7,20,5], [6,20,5], [9,9,-6], [15,14,2], [5,7,4], [6,2,-3], [4,3,0], [9,20,5], + [10,15,1], [9,14,0], [9,14,0], [10,15,1], [10,14,0], [9,15,1], [10,15,1], [10,15,1], + [10,15,1], [10,15,1], [4,9,0], [4,13,4], [4,15,5], [15,6,-2], [9,15,5], [9,15,0], + [15,16,1], [15,15,0], [14,14,0], [14,16,1], [15,14,0], [14,14,0], [13,14,0], [15,16,1], + [15,14,0], [7,14,0], [10,15,1], [15,14,0], [12,14,0], [18,14,0], [15,14,0], [15,16,1], + [13,14,0], [15,19,4], [15,15,1], [10,16,1], [14,14,0], [15,15,1], [15,15,1], [21,15,1], + [15,14,0], [15,14,0], [12,14,0], [6,20,5], [10,7,-7], [4,20,5], [8,4,-10], [4,3,-11], + [4,7,-7], [10,10,1], [11,15,1], [9,10,1], [11,15,1], [9,10,1], [8,15,0], [10,15,5], + [11,14,0], [5,14,0], [6,19,5], [11,14,0], [6,14,0], [17,9,0], [11,9,0], [10,10,1], + [11,13,4], [11,13,4], [8,9,0], [8,10,1], [7,14,1], [11,10,1], [11,10,1], [15,10,1], + [11,9,0], [11,14,5], [9,9,0], [10,1,-5], [20,1,-5], [9,4,-10], [9,3,-11], [8,3,-11] + ], + cmmi10: [ + [15,14,0], [16,15,0], [15,16,1], [14,15,0], [16,14,0], [18,14,0], [17,14,0], [15,15,0], + [13,14,0], [14,14,0], [16,15,0], [13,10,1], [12,19,4], [11,14,5], [10,16,1], [8,10,1], + [10,19,5], [10,14,5], [10,16,1], [7,10,1], [11,10,1], [11,15,1], [12,14,5], [11,9,0], + [9,19,5], [12,10,1], [11,14,5], [12,10,1], [11,10,1], [11,10,1], [12,19,5], [12,14,5], + [13,19,5], [13,10,1], [9,11,1], [12,16,1], [17,10,1], [11,13,4], [9,12,3], [13,14,5], + [19,7,-4], [19,7,1], [19,7,-4], [19,7,1], [5,6,-4], [5,6,-4], [10,12,1], [10,12,1], + [10,11,1], [9,10,0], [9,10,0], [10,15,5], [10,14,4], [9,15,5], [10,15,1], [10,15,5], + [10,15,1], [10,15,5], [4,3,0], [5,7,4], [14,12,1], [9,20,5], [14,12,1], [10,10,0], + [12,16,1], [15,15,0], [16,14,0], [16,16,1], [17,14,0], [16,14,0], [16,14,0], [16,16,1], + [18,14,0], [10,14,0], [13,15,1], [18,14,0], [13,14,0], [21,14,0], [18,14,0], [15,16,1], + [16,14,0], [15,19,4], [16,15,1], [13,16,1], [15,14,0], [16,15,1], [16,15,1], [21,15,1], + [18,14,0], [16,14,0], [15,14,0], [7,16,1], [7,20,5], [7,20,5], [19,6,-2], [19,6,-2], + [8,16,1], [10,10,1], [9,15,1], [9,10,1], [11,15,1], [9,10,1], [12,20,5], [10,14,5], + [11,15,1], [6,15,1], [9,19,5], [11,15,1], [6,15,1], [17,10,1], [12,10,1], [10,10,1], + [11,13,4], [10,13,4], [9,10,1], [9,10,1], [7,14,1], [11,10,1], [10,10,1], [14,10,1], + [11,10,1], [10,14,5], [10,10,1], [6,10,1], [9,14,5], [13,15,5], [13,5,-10], [13,4,-10] + ], + cmsy10: [ + [14,2,-4], [4,4,-3], [13,10,0], [9,10,0], [15,12,1], [10,10,0], [15,14,0], [15,14,4], + [15,14,2], [15,14,2], [15,14,2], [15,14,2], [15,14,2], [19,20,5], [9,8,-1], [9,8,-1], + [15,10,0], [15,10,0], [14,16,3], [14,16,3], [14,16,3], [14,16,3], [14,16,3], [14,16,3], + [15,6,-2], [15,9,-1], [14,12,1], [14,12,1], [19,14,2], [19,14,2], [14,12,1], [14,12,1], + [19,8,-1], [19,8,-1], [9,18,4], [9,18,4], [19,8,-1], [19,18,4], [19,18,4], [15,10,0], + [19,12,1], [19,12,1], [12,18,4], [12,18,4], [20,12,1], [19,18,4], [19,18,4], [15,10,1], + [6,12,0], [19,10,1], [12,12,1], [12,12,1], [17,15,0], [17,15,5], [13,20,5], [3,8,-1], + [12,15,1], [10,14,0], [13,7,-1], [10,18,2], [15,16,1], [14,16,1], [15,14,0], [15,14,0], + [12,14,0], [16,16,1], [14,16,1], [11,16,1], [16,14,0], [12,16,1], [17,15,1], [13,18,3], + [17,15,1], [14,14,0], [17,17,3], [15,16,1], [14,16,1], [23,16,1], [21,17,1], [16,16,1], + [15,15,1], [16,18,3], [17,15,1], [13,16,1], [16,15,0], [15,15,1], [14,15,1], [21,15,1], + [17,14,0], [15,17,3], [16,14,0], [13,13,1], [13,13,1], [13,13,1], [13,13,1], [13,13,1], + [12,14,0], [12,14,0], [9,20,5], [6,20,5], [9,20,5], [6,20,5], [9,20,5], [9,20,5], + [7,20,5], [6,20,5], [4,20,5], [8,20,5], [9,22,6], [12,22,6], [9,20,5], [5,14,2], + [18,21,20], [15,14,0], [16,15,1], [10,20,5], [13,12,0], [13,12,0], [15,16,3], [14,16,3], + [8,20,5], [8,20,5], [8,20,5], [12,18,4], [15,18,3], [15,19,4], [15,16,1], [15,18,3] + ], + cmex10: [ + [9,25,24], [7,25,24], [8,25,24], [5,25,24], [9,25,24], [6,25,24], [9,25,24], [6,25,24], + [10,25,24], [10,25,24], [8,25,24], [8,25,24], [4,14,13], [9,14,13], [11,25,24], [11,25,24], + [12,37,36], [9,37,36], [14,49,48], [11,49,48], [11,49,48], [6,49,48], [12,49,48], [7,49,48], + [12,49,48], [7,49,48], [13,49,48], [13,49,48], [14,49,48], [13,49,48], [20,49,48], [20,49,48], + [16,61,60], [12,61,60], [12,61,60], [7,61,60], [13,61,60], [8,61,60], [13,61,60], [8,61,60], + [14,61,60], [14,61,60], [14,61,60], [14,61,60], [25,61,60], [25,61,60], [16,37,36], [16,37,36], + [17,37,36], [12,37,36], [14,37,36], [7,37,36], [14,37,36], [7,37,36], [8,14,13], [7,14,13], + [15,20,19], [11,20,19], [15,19,18], [11,19,18], [11,38,37], [15,38,37], [11,8,7], [8,14,13], + [17,37,36], [12,37,36], [9,14,13], [12,14,13], [11,37,36], [10,37,36], [16,21,20], [22,29,28], + [13,24,23], [19,46,45], [22,21,20], [30,29,28], [22,21,20], [30,29,28], [22,21,20], [30,29,28], + [20,21,20], [18,21,20], [13,24,23], [16,21,20], [16,21,20], [16,21,20], [16,21,20], [16,21,20], + [28,29,28], [25,29,28], [19,46,45], [22,29,28], [22,29,28], [22,29,28], [22,29,28], [22,29,28], + [18,21,20], [25,29,28], [13,4,-11], [22,5,-11], [30,5,-11], [12,3,-12], [20,3,-12], [29,3,-12], + [10,37,36], [5,37,36], [11,37,36], [7,37,36], [11,37,36], [7,37,36], [11,37,36], [11,37,36], + [21,25,24], [21,37,36], [21,49,48], [21,61,60], [15,37,36], [15,14,13], [22,13,12], [11,14,13], + [12,13,12], [12,13,12], [11,8,5], [11,8,5], [11,7,0], [11,7,0], [15,13,12], [15,13,12] + ], + cmbx10: [ + [13,14,0], [18,14,0], [17,15,1], [16,14,0], [15,14,0], [18,14,0], [16,14,0], [17,14,0], + [16,14,0], [17,14,0], [16,14,0], [15,14,0], [13,14,0], [13,14,0], [19,14,0], [19,14,0], + [6,9,0], [8,13,4], [7,5,-10], [10,5,-10], [9,4,-10], [10,4,-10], [10,2,-11], [12,5,-10], + [9,5,4], [12,15,1], [16,11,1], [18,11,1], [11,15,3], [21,14,0], [23,15,1], [17,17,2], + [7,4,-5], [6,15,0], [10,8,-6], [18,18,4], [11,17,2], [18,17,2], [17,16,1], [6,8,-6], + [8,20,5], [7,20,5], [10,9,-6], [17,16,3], [5,8,4], [7,3,-3], [5,4,0], [11,20,5], + [11,15,1], [10,14,0], [11,14,0], [11,15,1], [11,14,0], [11,15,1], [11,15,1], [12,15,1], + [11,15,1], [11,15,1], [5,9,0], [5,13,4], [6,15,5], [17,6,-2], [10,14,4], [10,14,0], + [17,15,1], [17,14,0], [16,14,0], [16,15,1], [17,14,0], [15,14,0], [14,14,0], [17,15,1], + [18,14,0], [9,14,0], [11,15,1], [18,14,0], [13,14,0], [22,14,0], [18,14,0], [16,15,1], + [15,14,0], [17,18,4], [18,15,1], [12,15,1], [16,14,0], [17,15,1], [17,15,1], [24,15,1], + [17,14,0], [17,14,0], [13,14,0], [6,20,5], [12,8,-6], [4,20,5], [9,4,-10], [5,4,-10], + [5,8,-6], [12,11,1], [12,15,1], [10,11,1], [13,15,1], [10,11,1], [9,14,0], [12,15,5], + [13,14,0], [6,14,0], [8,18,4], [12,14,0], [6,14,0], [19,9,0], [13,9,0], [11,11,1], + [12,13,4], [13,13,4], [9,9,0], [9,11,1], [8,14,1], [13,10,1], [12,10,1], [17,10,1], + [12,9,0], [12,13,4], [10,9,0], [12,1,-5], [23,1,-5], [10,5,-10], [10,3,-11], [10,3,-11] + ], + cmti10: [ + [15,14,0], [16,15,0], [16,16,1], [13,15,0], [16,14,0], [18,14,0], [16,14,0], [17,15,0], + [15,14,0], [17,14,0], [16,15,0], [17,20,5], [13,20,5], [14,20,5], [20,20,5], [20,20,5], + [7,10,1], [8,14,5], [9,4,-10], [12,4,-10], [11,3,-10], [12,4,-10], [12,1,-11], [14,5,-10], + [7,5,4], [13,20,5], [15,10,1], [15,10,1], [11,14,3], [20,14,0], [22,16,1], [17,17,2], + [7,3,-5], [8,15,0], [11,7,-7], [17,18,4], [14,16,1], [17,17,2], [17,16,1], [8,7,-7], + [11,20,5], [8,20,5], [12,9,-6], [16,14,2], [5,7,4], [7,2,-3], [5,3,0], [13,20,5], + [12,15,1], [10,14,0], [12,15,1], [12,15,1], [10,18,4], [12,15,1], [12,15,1], [13,15,1], + [12,15,1], [12,15,1], [7,9,0], [7,13,4], [7,15,5], [16,6,-2], [9,15,5], [12,15,0], + [16,16,1], [14,15,0], [15,14,0], [17,16,1], [16,14,0], [15,14,0], [15,14,0], [17,16,1], + [18,14,0], [11,14,0], [13,15,1], [18,14,0], [13,14,0], [21,14,0], [18,14,0], [16,16,1], + [15,14,0], [16,19,4], [15,15,1], [13,16,1], [17,14,0], [18,15,1], [18,15,1], [23,15,1], + [17,14,0], [18,14,0], [15,14,0], [9,20,5], [13,7,-7], [9,20,5], [11,4,-10], [8,3,-11], + [8,7,-7], [11,10,1], [10,15,1], [10,10,1], [12,15,1], [10,10,1], [11,20,5], [10,14,5], + [11,15,1], [7,15,1], [9,19,5], [11,15,1], [7,15,1], [17,10,1], [12,10,1], [11,10,1], + [11,13,4], [10,13,4], [10,10,1], [9,10,1], [8,14,1], [12,10,1], [10,10,1], [14,10,1], + [11,10,1], [11,14,5], [10,10,1], [12,1,-5], [21,1,-5], [12,4,-10], [12,3,-11], [11,3,-11] + ] +}); + +jsMath.Img.AddFont(173,{ + cmr10: [ + [14,17,0], [19,18,0], [18,18,1], [16,18,0], [15,17,0], [18,17,0], [16,17,0], [18,17,0], + [16,17,0], [18,17,0], [17,17,0], [16,17,0], [13,17,0], [13,17,0], [20,17,0], [20,17,0], + [6,11,0], [7,16,5], [8,5,-12], [10,5,-12], [10,4,-12], [10,5,-12], [11,2,-13], [12,6,-12], + [9,6,5], [12,18,1], [17,12,1], [18,12,1], [12,16,3], [21,17,0], [24,18,1], [18,20,2], + [7,4,-6], [5,18,0], [9,8,-9], [19,22,5], [11,20,2], [19,20,2], [18,19,1], [5,8,-9], + [8,24,6], [7,24,6], [11,11,-7], [18,16,2], [5,8,5], [7,2,-4], [5,3,0], [11,24,6], + [12,17,1], [11,16,0], [11,16,0], [11,17,1], [12,17,0], [11,17,1], [11,17,1], [12,18,1], + [11,17,1], [11,17,1], [5,11,0], [5,16,5], [5,18,6], [18,6,-3], [10,17,5], [10,17,0], + [18,18,1], [18,18,0], [16,17,0], [16,18,1], [17,17,0], [16,17,0], [15,17,0], [18,18,1], + [18,17,0], [8,17,0], [12,18,1], [18,17,0], [14,17,0], [22,17,0], [18,17,0], [18,18,1], + [15,17,0], [18,22,5], [18,18,1], [12,18,1], [17,17,0], [18,18,1], [18,18,1], [25,18,1], + [18,17,0], [18,17,0], [14,17,0], [7,24,6], [12,8,-9], [4,24,6], [10,5,-12], [5,4,-13], + [5,8,-9], [12,12,1], [13,18,1], [10,12,1], [13,18,1], [10,12,1], [9,17,0], [12,16,5], + [13,17,0], [6,17,0], [7,22,5], [13,17,0], [7,17,0], [20,11,0], [13,11,0], [12,12,1], + [13,16,5], [13,16,5], [9,11,0], [9,12,1], [8,16,1], [13,12,1], [13,12,1], [17,12,1], + [13,11,0], [13,16,5], [10,11,0], [12,1,-6], [24,1,-6], [11,5,-12], [10,4,-13], [10,4,-13] + ], + cmmi10: [ + [18,17,0], [19,18,0], [18,18,1], [16,18,0], [19,17,0], [22,17,0], [20,17,0], [17,17,0], + [16,17,0], [17,17,0], [19,17,0], [15,12,1], [15,22,5], [14,17,6], [11,19,1], [10,12,1], + [12,22,5], [12,17,6], [11,18,1], [8,12,1], [14,12,1], [14,18,1], [14,17,6], [13,11,0], + [11,22,5], [14,12,1], [13,17,6], [14,12,1], [13,12,1], [13,12,1], [14,22,5], [15,16,5], + [16,22,5], [15,12,1], [11,12,1], [14,18,1], [20,12,1], [13,16,5], [10,14,3], [15,17,6], + [23,8,-5], [23,8,1], [23,8,-5], [23,8,1], [6,7,-5], [6,7,-5], [12,14,1], [12,14,1], + [12,12,1], [11,11,0], [11,11,0], [11,17,6], [12,17,5], [11,17,6], [11,17,1], [12,18,6], + [11,17,1], [11,17,6], [5,3,0], [5,8,5], [17,14,1], [11,24,6], [17,14,1], [12,12,0], + [14,19,1], [18,18,0], [19,17,0], [19,18,1], [20,17,0], [19,17,0], [19,17,0], [19,18,1], + [22,17,0], [12,17,0], [16,18,1], [22,17,0], [16,17,0], [26,17,0], [22,17,0], [18,18,1], + [19,17,0], [18,22,5], [19,18,1], [16,18,1], [17,17,0], [19,18,1], [19,18,1], [26,18,1], + [21,17,0], [19,17,0], [18,17,0], [8,19,1], [8,24,6], [8,24,6], [23,6,-3], [23,7,-3], + [10,18,1], [12,12,1], [10,18,1], [11,12,1], [13,18,1], [11,12,1], [14,22,5], [12,16,5], + [14,18,1], [8,17,1], [11,21,5], [13,18,1], [7,18,1], [21,12,1], [14,12,1], [12,12,1], + [13,16,5], [11,16,5], [11,12,1], [11,12,1], [8,17,1], [14,12,1], [12,12,1], [17,12,1], + [13,12,1], [12,16,5], [12,12,1], [8,12,1], [10,16,5], [15,17,6], [15,6,-12], [16,4,-12] + ], + cmsy10: [ + [17,2,-5], [5,4,-4], [16,12,0], [11,12,0], [18,14,1], [12,12,0], [18,16,0], [18,16,4], + [18,16,2], [18,16,2], [18,16,2], [18,16,2], [18,16,2], [23,24,6], [11,10,-1], [11,10,-1], + [18,12,0], [18,12,0], [17,20,4], [17,20,4], [17,20,4], [17,20,4], [17,20,4], [17,20,4], + [18,6,-3], [18,11,-1], [17,14,1], [17,14,1], [23,16,2], [23,16,2], [17,14,1], [17,14,1], + [23,10,-1], [23,10,-1], [11,22,5], [11,22,5], [23,10,-1], [23,22,5], [23,22,5], [18,12,0], + [23,14,1], [23,14,1], [14,22,5], [14,22,5], [24,14,1], [23,22,5], [23,22,5], [18,12,1], + [7,13,-1], [23,12,1], [14,14,1], [14,14,1], [20,18,0], [20,18,6], [16,24,6], [3,10,-1], + [14,18,1], [12,17,0], [15,7,-2], [11,21,2], [18,19,1], [17,18,1], [18,16,0], [18,16,0], + [14,17,0], [20,20,2], [16,18,1], [13,18,1], [19,17,0], [14,18,1], [20,18,1], [15,20,3], + [20,19,2], [17,17,0], [21,20,3], [18,18,1], [16,18,1], [27,19,2], [25,21,2], [19,18,1], + [18,19,2], [19,20,3], [21,18,1], [16,18,1], [20,18,0], [18,18,1], [16,19,2], [25,19,2], + [20,17,0], [18,21,4], [19,17,0], [15,16,1], [15,16,1], [15,16,1], [15,16,1], [15,16,1], + [14,17,0], [14,17,0], [11,24,6], [7,24,6], [11,24,6], [7,24,6], [11,24,6], [11,24,6], + [8,24,6], [7,24,6], [4,24,6], [9,24,6], [11,26,7], [14,26,7], [11,24,6], [6,16,2], + [21,25,24], [18,17,0], [19,18,1], [12,24,6], [15,15,0], [15,15,0], [18,20,4], [17,20,4], + [9,22,5], [10,23,6], [10,22,5], [14,22,5], [18,22,4], [18,22,4], [18,19,1], [18,22,4] + ], + cmex10: [ + [10,29,28], [8,29,28], [10,29,28], [6,29,28], [11,29,28], [7,29,28], [11,29,28], [7,29,28], + [12,29,28], [12,29,28], [10,29,28], [9,29,28], [5,16,15], [10,16,15], [13,29,28], [13,29,28], + [14,44,43], [10,44,43], [17,58,57], [13,58,57], [13,58,57], [7,58,57], [14,58,57], [8,58,57], + [14,58,57], [8,58,57], [15,58,57], [15,58,57], [16,58,57], [15,58,57], [24,58,57], [24,58,57], + [19,73,72], [14,73,72], [14,73,72], [8,73,72], [16,73,72], [9,73,72], [16,73,72], [9,73,72], + [16,73,72], [16,73,72], [17,73,72], [16,73,72], [30,73,72], [30,73,72], [19,44,43], [19,44,43], + [21,44,43], [14,44,43], [16,44,43], [9,44,43], [16,44,43], [9,44,43], [10,16,15], [9,16,15], + [18,23,22], [13,23,22], [18,23,22], [13,23,22], [13,45,44], [18,45,44], [13,9,8], [9,16,15], + [21,45,43], [14,45,43], [10,16,15], [14,16,15], [13,44,43], [12,44,43], [19,25,24], [26,35,34], + [15,28,27], [23,55,54], [26,25,24], [35,35,34], [26,25,24], [35,35,34], [26,25,24], [35,35,34], + [24,25,24], [22,25,24], [15,28,27], [19,25,24], [19,25,24], [19,25,24], [19,25,24], [19,25,24], + [34,35,34], [30,35,34], [23,55,54], [26,35,34], [26,35,34], [26,35,34], [26,35,34], [26,35,34], + [22,25,24], [30,35,34], [15,5,-13], [26,6,-13], [36,6,-13], [14,4,-14], [24,4,-14], [35,4,-14], + [11,44,43], [6,44,43], [13,44,43], [8,44,43], [13,44,43], [8,44,43], [14,44,43], [14,44,43], + [25,29,28], [25,44,43], [25,58,57], [25,73,72], [18,45,44], [18,16,15], [26,15,14], [13,16,15], + [14,16,15], [14,16,15], [13,9,6], [13,9,6], [13,9,0], [13,9,0], [18,16,15], [18,16,15] + ], + cmbx10: [ + [16,17,0], [22,17,0], [20,18,1], [19,17,0], [18,17,0], [21,17,0], [19,17,0], [20,17,0], + [19,17,0], [20,17,0], [19,17,0], [18,17,0], [15,17,0], [15,17,0], [23,17,0], [23,17,0], + [7,11,0], [9,16,5], [9,5,-12], [11,5,-12], [11,4,-12], [12,5,-12], [12,2,-13], [14,5,-12], + [11,6,5], [14,18,1], [20,12,1], [21,12,1], [14,17,3], [25,17,0], [28,18,1], [20,20,2], + [8,4,-6], [7,17,0], [12,9,-8], [22,22,5], [13,20,2], [22,20,2], [21,18,1], [7,9,-8], + [10,24,6], [9,24,6], [12,11,-7], [20,20,4], [6,9,5], [8,3,-4], [6,4,0], [13,24,6], + [13,17,1], [12,16,0], [13,16,0], [13,17,1], [14,16,0], [13,17,1], [13,17,1], [14,18,1], + [13,17,1], [13,17,1], [6,11,0], [6,16,5], [7,17,5], [20,8,-2], [12,17,5], [12,17,0], + [20,18,1], [20,17,0], [19,17,0], [19,18,1], [20,17,0], [18,17,0], [17,17,0], [21,18,1], + [21,17,0], [10,17,0], [13,18,1], [21,17,0], [16,17,0], [26,17,0], [21,17,0], [20,18,1], + [18,17,0], [20,22,5], [21,18,1], [14,18,1], [19,17,0], [21,18,1], [21,18,1], [28,18,1], + [21,17,0], [21,17,0], [16,17,0], [8,24,6], [14,9,-8], [5,24,6], [11,5,-12], [6,5,-12], + [6,9,-8], [14,12,1], [15,18,1], [12,12,1], [15,18,1], [12,12,1], [11,17,0], [14,16,5], + [15,17,0], [7,17,0], [9,22,5], [15,17,0], [8,17,0], [23,11,0], [15,11,0], [14,12,1], + [15,16,5], [15,16,5], [11,11,0], [10,12,1], [10,17,1], [15,12,1], [14,12,1], [20,12,1], + [15,11,0], [14,16,5], [12,11,0], [14,2,-6], [28,2,-6], [12,6,-12], [12,3,-14], [12,4,-13] + ], + cmti10: [ + [17,17,0], [19,18,0], [19,18,1], [16,18,0], [19,17,0], [21,17,0], [19,17,0], [20,17,0], + [18,17,0], [20,17,0], [19,17,0], [20,22,5], [16,22,5], [17,22,5], [23,22,5], [24,22,5], + [8,12,1], [9,16,5], [11,5,-12], [14,5,-12], [13,4,-12], [14,5,-12], [14,2,-13], [17,6,-12], + [9,6,5], [15,22,5], [18,12,1], [18,12,1], [14,16,3], [23,17,0], [26,18,1], [20,20,2], + [9,4,-6], [9,18,0], [13,8,-9], [20,22,5], [17,18,1], [21,20,2], [20,19,1], [9,8,-9], + [13,24,6], [10,24,6], [14,11,-7], [19,16,2], [6,8,5], [9,2,-4], [6,3,0], [15,24,6], + [14,17,1], [12,16,0], [14,17,1], [14,17,1], [12,21,5], [14,17,1], [14,17,1], [15,17,1], + [14,17,1], [14,17,1], [8,11,0], [8,16,5], [8,18,6], [19,6,-3], [11,18,6], [14,18,0], + [19,18,1], [17,18,0], [18,17,0], [20,18,1], [19,17,0], [18,17,0], [18,17,0], [20,18,1], + [21,17,0], [13,17,0], [15,18,1], [21,17,0], [15,17,0], [25,17,0], [21,17,0], [19,18,1], + [18,17,0], [19,22,5], [18,18,1], [16,18,1], [20,17,0], [21,18,1], [21,18,1], [27,18,1], + [20,17,0], [21,17,0], [17,17,0], [11,24,6], [15,8,-9], [10,24,6], [13,5,-12], [9,4,-13], + [9,8,-9], [13,12,1], [12,18,1], [12,12,1], [14,18,1], [12,12,1], [12,22,5], [12,16,5], + [13,18,1], [8,17,1], [10,21,5], [13,18,1], [8,18,1], [21,12,1], [15,12,1], [13,12,1], + [13,16,5], [12,16,5], [12,12,1], [11,12,1], [9,17,1], [14,12,1], [12,12,1], [17,12,1], + [13,12,1], [13,16,5], [12,12,1], [14,1,-6], [25,1,-6], [14,5,-12], [14,4,-13], [14,4,-13] + ] +}); + +jsMath.Img.AddFont(207,{ + cmr10: [ + [17,20,0], [23,21,0], [21,22,1], [20,21,0], [19,20,0], [21,20,0], [20,20,0], [21,21,0], + [20,20,0], [21,20,0], [20,21,0], [19,21,0], [16,21,0], [16,21,0], [24,21,0], [24,21,0], + [8,13,0], [9,19,6], [9,7,-14], [12,7,-14], [12,5,-14], [12,6,-15], [13,2,-16], [14,6,-15], + [11,7,6], [14,22,1], [21,14,1], [22,14,1], [14,19,3], [26,20,0], [29,22,1], [21,24,2], + [8,4,-8], [6,21,0], [11,10,-11], [23,27,6], [13,24,2], [23,24,2], [22,22,1], [6,10,-11], + [10,30,8], [9,30,8], [13,13,-9], [21,20,3], [6,10,6], [8,3,-5], [6,4,0], [13,30,8], + [14,21,1], [13,20,0], [14,20,0], [14,21,1], [14,20,0], [14,21,1], [14,21,1], [15,21,1], + [14,21,1], [14,21,1], [6,13,0], [6,19,6], [6,22,7], [21,8,-3], [13,21,6], [13,21,0], + [21,22,1], [21,21,0], [19,20,0], [20,22,1], [21,20,0], [19,20,0], [18,20,0], [22,22,1], + [21,20,0], [10,20,0], [14,21,1], [22,20,0], [17,20,0], [26,20,0], [21,20,0], [21,22,1], + [19,20,0], [22,27,6], [22,21,1], [15,22,1], [20,20,0], [21,21,1], [22,21,1], [30,21,1], + [22,20,0], [22,20,0], [17,20,0], [8,30,8], [14,10,-11], [5,30,8], [12,6,-15], [6,4,-16], + [6,10,-11], [15,14,1], [16,22,1], [13,14,1], [16,22,1], [13,14,1], [11,21,0], [15,20,6], + [16,21,0], [8,20,0], [9,26,6], [15,21,0], [8,21,0], [24,13,0], [16,13,0], [14,14,1], + [16,19,6], [16,19,6], [11,13,0], [11,14,1], [10,19,1], [16,14,1], [15,14,1], [21,14,1], + [15,13,0], [15,19,6], [12,13,0], [15,2,-7], [29,2,-7], [13,7,-14], [13,4,-16], [12,4,-16] + ], + cmmi10: [ + [21,20,0], [23,21,0], [22,22,1], [20,21,0], [23,20,0], [26,20,0], [24,20,0], [21,21,0], + [19,20,0], [21,20,0], [23,21,0], [18,14,1], [18,27,6], [16,20,7], [14,22,1], [11,14,1], + [14,27,6], [15,20,7], [14,22,1], [10,14,1], [16,14,1], [16,22,1], [17,20,7], [16,13,0], + [13,27,6], [17,14,1], [15,20,7], [17,14,1], [15,14,1], [16,14,1], [17,27,6], [18,19,6], + [19,27,6], [18,14,1], [13,15,1], [17,22,1], [24,14,1], [15,19,6], [12,17,4], [18,20,7], + [28,9,-6], [28,9,1], [28,9,-6], [28,9,1], [7,8,-6], [7,8,-6], [14,16,1], [14,16,1], + [14,15,1], [13,14,0], [14,14,0], [14,21,7], [14,20,6], [14,21,7], [14,21,1], [15,21,7], + [14,21,1], [14,21,7], [6,4,0], [6,10,6], [21,18,2], [13,30,8], [21,18,2], [15,15,0], + [17,22,1], [21,21,0], [22,20,0], [23,22,1], [24,20,0], [23,20,0], [22,20,0], [23,22,1], + [26,20,0], [15,20,0], [19,21,1], [26,20,0], [19,20,0], [31,20,0], [26,20,0], [22,22,1], + [22,20,0], [22,27,6], [22,21,1], [19,22,1], [21,20,0], [23,21,1], [23,21,1], [31,21,1], + [25,20,0], [23,20,0], [21,20,0], [10,23,1], [9,29,7], [10,28,7], [28,8,-3], [28,9,-3], + [12,22,1], [15,14,1], [13,22,1], [13,14,1], [15,22,1], [13,14,1], [17,27,6], [14,19,6], + [16,22,1], [9,21,1], [13,26,6], [15,22,1], [8,22,1], [25,14,1], [17,14,1], [14,14,1], + [16,19,6], [14,19,6], [13,14,1], [13,14,1], [10,20,1], [16,14,1], [14,14,1], [21,14,1], + [16,14,1], [15,19,6], [14,14,1], [9,14,1], [12,19,6], [18,21,7], [19,7,-14], [19,5,-15] + ], + cmsy10: [ + [21,2,-6], [6,4,-5], [19,15,0], [13,14,0], [21,17,1], [15,15,0], [21,20,0], [21,20,5], + [21,20,3], [21,20,3], [21,20,3], [21,20,3], [21,20,3], [28,28,7], [13,12,-1], [13,12,-1], + [21,15,0], [21,13,-1], [21,23,4], [21,23,4], [21,23,4], [21,23,4], [21,23,4], [21,23,4], + [21,8,-3], [21,13,-1], [21,18,2], [21,18,2], [28,19,2], [28,19,2], [21,18,2], [21,18,2], + [28,11,-2], [28,11,-2], [13,27,6], [13,27,6], [28,11,-2], [28,27,6], [28,27,6], [21,13,-1], + [28,17,1], [28,17,1], [17,27,6], [17,27,6], [28,17,1], [28,27,6], [28,27,6], [21,14,1], + [8,16,-1], [28,14,1], [17,18,2], [17,18,2], [25,21,0], [25,22,7], [19,28,7], [4,12,-1], + [17,22,1], [15,21,0], [18,9,-2], [14,26,3], [21,22,1], [21,22,1], [21,20,0], [21,20,0], + [17,21,0], [24,23,2], [20,22,1], [16,22,1], [23,20,0], [17,22,1], [25,21,1], [18,25,4], + [24,22,2], [20,20,0], [25,24,4], [22,22,1], [20,22,1], [33,23,2], [30,25,2], [23,22,1], + [22,22,2], [23,25,4], [25,21,1], [19,22,1], [24,21,0], [21,21,1], [20,22,2], [31,22,2], + [24,20,0], [21,24,4], [23,20,0], [18,19,1], [18,19,1], [18,19,1], [18,19,1], [18,19,1], + [17,21,0], [17,21,0], [13,30,8], [8,30,8], [13,30,8], [8,30,8], [13,30,8], [13,30,8], + [10,30,8], [9,30,8], [5,30,8], [11,30,8], [13,31,8], [17,31,8], [13,30,8], [7,20,3], + [25,30,28], [21,20,0], [23,21,1], [14,28,7], [18,18,0], [18,18,0], [21,23,4], [21,23,4], + [11,27,6], [12,28,7], [12,27,6], [17,27,6], [22,26,4], [21,27,5], [21,22,1], [21,26,4] + ], + cmex10: [ + [12,36,34], [9,36,34], [12,36,34], [7,36,34], [14,36,34], [8,36,34], [14,36,34], [8,36,34], + [14,36,34], [14,36,34], [12,36,34], [11,36,34], [6,20,19], [12,20,19], [16,36,34], [16,36,34], + [17,54,52], [13,54,52], [21,71,69], [16,71,69], [15,71,69], [9,71,69], [17,71,69], [10,71,69], + [17,71,69], [10,71,69], [18,71,69], [18,71,69], [19,71,69], [19,71,69], [29,71,69], [29,71,69], + [22,88,86], [17,88,86], [17,88,86], [9,88,86], [19,88,86], [11,88,86], [19,88,86], [11,88,86], + [20,88,86], [20,88,86], [21,88,86], [20,88,86], [36,88,86], [36,88,86], [22,54,52], [22,54,52], + [25,54,52], [17,54,52], [20,54,52], [10,54,52], [20,54,52], [10,54,52], [12,19,18], [10,19,18], + [21,28,27], [15,28,27], [21,28,27], [15,28,27], [15,54,53], [21,54,53], [15,10,9], [11,19,18], + [25,54,52], [17,54,52], [12,19,18], [17,19,18], [16,54,52], [15,54,52], [23,30,29], [31,42,41], + [18,34,33], [28,66,65], [31,30,29], [43,42,41], [31,30,29], [43,42,41], [31,30,29], [43,42,41], + [29,30,29], [26,30,29], [18,34,33], [23,30,29], [23,30,29], [23,30,29], [23,30,29], [23,30,29], + [41,42,41], [36,42,41], [28,66,65], [31,42,41], [31,42,41], [31,42,41], [31,42,41], [31,42,41], + [26,30,29], [36,42,41], [18,6,-16], [31,7,-16], [43,7,-16], [17,4,-17], [29,4,-18], [42,4,-18], + [14,54,52], [8,54,52], [15,54,52], [9,54,52], [15,54,52], [9,54,52], [16,54,52], [16,54,52], + [30,36,34], [30,54,52], [30,71,69], [30,88,86], [22,54,53], [22,19,18], [32,19,17], [16,19,18], + [17,19,18], [17,19,18], [15,11,7], [15,11,7], [15,10,0], [15,10,0], [21,19,18], [21,19,18] + ], + cmbx10: [ + [19,20,0], [27,21,0], [25,22,1], [23,21,0], [21,20,0], [25,20,0], [23,20,0], [25,21,0], + [23,20,0], [25,20,0], [23,21,0], [22,21,0], [18,21,0], [18,21,0], [27,21,0], [27,21,0], + [9,14,0], [10,20,6], [10,7,-14], [14,7,-14], [13,4,-15], [14,7,-14], [15,2,-16], [17,6,-15], + [13,7,6], [17,22,1], [24,15,1], [25,15,1], [16,21,4], [30,20,0], [33,22,1], [25,24,2], + [10,4,-8], [8,21,0], [14,12,-9], [26,27,6], [15,24,2], [26,24,2], [25,22,1], [8,12,-9], + [12,30,8], [10,30,8], [15,14,-8], [25,23,4], [8,11,6], [10,3,-5], [7,5,0], [15,30,8], + [16,20,1], [15,19,0], [15,19,0], [16,20,1], [16,20,0], [15,20,1], [16,20,1], [17,21,1], + [16,20,1], [16,20,1], [7,13,0], [7,19,6], [8,21,6], [25,9,-3], [14,21,6], [14,21,0], + [25,22,1], [24,21,0], [22,20,0], [23,22,1], [24,20,0], [21,20,0], [20,20,0], [25,22,1], + [25,20,0], [12,20,0], [16,21,1], [25,20,0], [19,20,0], [31,20,0], [25,20,0], [24,22,1], + [21,20,0], [24,27,6], [25,21,1], [17,22,1], [22,20,0], [25,21,1], [25,21,1], [34,21,1], + [25,20,0], [25,20,0], [19,20,0], [9,30,8], [17,12,-9], [6,30,8], [13,6,-15], [7,6,-15], + [7,12,-9], [17,15,1], [18,22,1], [14,15,1], [18,22,1], [15,15,1], [13,21,0], [17,20,6], + [18,21,0], [9,21,0], [10,27,6], [18,21,0], [9,21,0], [28,14,0], [18,14,0], [16,15,1], + [18,20,6], [18,20,6], [13,14,0], [13,15,1], [12,20,1], [18,15,1], [17,14,1], [24,14,1], + [17,13,0], [17,19,6], [14,13,0], [17,2,-7], [34,2,-7], [15,7,-14], [14,4,-17], [14,6,-15] + ], + cmti10: [ + [21,20,0], [22,21,0], [23,22,1], [19,21,0], [22,20,0], [25,20,0], [23,20,0], [25,21,0], + [22,20,0], [24,20,0], [23,21,0], [23,27,6], [19,27,6], [20,27,6], [28,27,6], [29,27,6], + [10,14,1], [12,19,6], [13,7,-14], [16,7,-14], [16,5,-14], [17,6,-15], [17,2,-16], [20,6,-15], + [10,7,6], [18,27,6], [21,14,1], [21,14,1], [16,20,4], [28,20,0], [31,22,1], [24,24,2], + [10,4,-8], [11,21,0], [15,10,-11], [25,27,6], [21,22,1], [25,24,2], [24,22,1], [11,10,-11], + [15,30,8], [12,30,8], [17,13,-9], [22,19,2], [7,10,6], [10,3,-5], [7,4,0], [18,30,8], + [17,21,1], [14,20,0], [16,21,1], [17,21,1], [14,26,6], [17,21,1], [17,21,1], [19,21,1], + [17,21,1], [17,21,1], [9,13,0], [9,19,6], [10,22,7], [23,8,-3], [13,22,7], [16,21,0], + [23,22,1], [21,21,0], [22,20,0], [24,22,1], [23,20,0], [22,20,0], [22,20,0], [24,22,1], + [25,20,0], [15,20,0], [19,21,1], [25,20,0], [19,20,0], [30,20,0], [25,20,0], [23,22,1], + [22,20,0], [23,27,6], [21,21,1], [19,22,1], [24,20,0], [25,21,1], [26,21,1], [33,21,1], + [24,20,0], [26,20,0], [21,20,0], [13,30,8], [18,10,-11], [12,30,8], [16,6,-15], [11,4,-16], + [11,10,-11], [16,14,1], [14,22,1], [14,14,1], [17,22,1], [14,14,1], [15,27,6], [15,19,6], + [16,22,1], [10,21,1], [13,26,6], [15,22,1], [9,22,1], [25,14,1], [17,14,1], [15,14,1], + [15,19,6], [15,19,6], [15,14,1], [13,14,1], [11,20,1], [17,14,1], [15,14,1], [21,14,1], + [16,14,1], [15,19,6], [14,14,1], [16,2,-7], [31,2,-7], [17,7,-14], [17,4,-16], [16,4,-16] + ] +}); + +jsMath.Img.AddFont(249,{ + cmr10: [ + [20,24,0], [27,25,0], [25,25,1], [23,25,0], [22,24,0], [25,24,0], [23,24,0], [25,24,0], + [23,24,0], [25,24,0], [24,24,0], [22,24,0], [18,24,0], [18,24,0], [28,24,0], [28,24,0], + [9,16,0], [10,23,7], [10,7,-17], [14,7,-17], [13,5,-17], [14,7,-17], [15,2,-19], [16,7,-18], + [13,8,7], [17,25,1], [24,17,1], [26,17,1], [16,23,4], [30,24,0], [34,25,1], [25,28,2], + [9,5,-9], [7,25,0], [12,11,-13], [27,31,7], [16,28,2], [27,28,2], [25,26,1], [7,11,-13], + [12,35,9], [10,35,9], [15,16,-10], [25,23,3], [7,11,7], [10,3,-6], [7,4,0], [16,35,9], + [16,24,1], [15,23,0], [16,23,0], [16,24,1], [17,24,0], [16,24,1], [16,24,1], [17,24,1], + [16,24,1], [16,24,1], [7,15,0], [7,22,7], [7,25,8], [25,9,-4], [15,24,7], [15,24,0], + [25,25,1], [25,25,0], [23,24,0], [23,25,1], [25,24,0], [23,24,0], [21,24,0], [25,25,1], + [25,24,0], [12,24,0], [16,25,1], [26,24,0], [20,24,0], [30,24,0], [25,24,0], [25,25,1], + [22,24,0], [25,31,7], [25,25,1], [17,25,1], [24,24,0], [25,25,1], [25,25,1], [35,25,1], + [25,24,0], [26,24,0], [20,24,0], [9,35,9], [16,11,-13], [6,35,9], [14,6,-18], [7,4,-19], + [7,11,-13], [17,17,1], [18,25,1], [15,17,1], [18,25,1], [15,17,1], [13,24,0], [17,23,7], + [19,24,0], [9,23,0], [10,30,7], [18,24,0], [9,24,0], [28,16,0], [19,16,0], [17,17,1], + [18,23,7], [18,23,7], [13,16,0], [13,17,1], [12,22,1], [19,17,1], [18,16,1], [24,16,1], + [18,15,0], [18,22,7], [14,15,0], [17,2,-8], [34,2,-8], [15,7,-17], [15,4,-19], [14,4,-19] + ], + cmmi10: [ + [25,24,0], [27,25,0], [26,25,1], [23,25,0], [27,24,0], [30,24,0], [28,24,0], [24,24,0], + [22,24,0], [24,24,0], [27,24,0], [21,17,1], [21,31,7], [19,24,8], [16,26,1], [13,16,1], + [17,31,7], [17,24,8], [16,25,1], [12,17,1], [19,17,1], [19,25,1], [20,24,8], [18,16,0], + [16,31,7], [20,16,1], [18,24,8], [20,16,1], [18,16,1], [18,17,1], [20,31,7], [21,23,7], + [22,31,7], [21,17,1], [15,17,1], [20,25,1], [28,16,1], [18,23,7], [14,20,4], [22,24,8], + [33,11,-7], [33,11,1], [33,11,-7], [33,11,1], [8,9,-7], [8,9,-7], [17,19,1], [17,19,1], + [16,17,1], [15,16,0], [16,16,0], [16,24,8], [17,23,7], [16,24,8], [16,24,1], [17,24,8], + [16,24,1], [16,24,8], [7,4,0], [7,11,7], [24,21,2], [16,35,9], [24,21,2], [17,17,0], + [20,26,1], [25,25,0], [26,24,0], [26,25,1], [28,24,0], [27,24,0], [26,24,0], [26,25,1], + [30,24,0], [17,24,0], [22,25,1], [31,24,0], [22,24,0], [36,24,0], [30,24,0], [26,25,1], + [26,24,0], [26,31,7], [26,25,1], [22,25,1], [24,24,0], [26,25,1], [27,25,1], [36,25,1], + [29,24,0], [26,24,0], [25,24,0], [12,27,1], [11,33,8], [12,33,8], [33,9,-4], [33,9,-4], + [14,25,1], [17,17,1], [15,25,1], [15,17,1], [18,25,1], [15,17,1], [19,31,7], [17,23,7], + [19,25,1], [10,24,1], [15,30,7], [18,25,1], [9,25,1], [29,17,1], [20,17,1], [16,17,1], + [19,23,7], [16,23,7], [15,17,1], [15,17,1], [12,23,1], [19,17,1], [16,17,1], [24,17,1], + [18,17,1], [17,23,7], [16,17,1], [10,17,1], [14,23,7], [22,24,8], [22,8,-17], [23,5,-18] + ], + cmsy10: [ + [24,3,-7], [7,5,-6], [22,17,0], [15,15,-1], [25,21,2], [17,17,0], [25,23,0], [25,23,6], + [25,23,3], [25,23,3], [25,23,3], [25,23,3], [25,23,3], [33,33,8], [16,15,-1], [16,15,-1], + [25,17,0], [25,15,-1], [24,27,5], [24,27,5], [24,27,5], [24,27,5], [24,27,5], [24,27,5], + [25,9,-4], [25,16,-1], [24,21,2], [24,21,2], [33,23,3], [33,23,3], [24,21,2], [24,21,2], + [33,13,-2], [33,13,-2], [15,31,7], [15,31,7], [33,13,-2], [33,31,7], [33,31,7], [25,15,-1], + [33,19,1], [33,19,1], [20,31,7], [20,31,7], [33,19,1], [33,31,7], [33,31,7], [25,17,1], + [9,18,-1], [33,17,1], [20,21,2], [20,21,2], [29,25,0], [29,25,8], [22,33,8], [5,13,-2], + [19,25,1], [17,24,0], [21,10,-3], [16,30,3], [25,26,1], [24,25,1], [25,23,0], [25,23,0], + [19,24,0], [28,27,2], [23,25,1], [19,25,1], [27,24,0], [20,25,1], [29,26,2], [21,29,5], + [28,26,2], [23,24,0], [29,29,5], [25,25,1], [23,25,1], [38,26,2], [35,29,2], [27,25,1], + [25,26,2], [27,29,5], [29,25,1], [22,25,1], [28,25,0], [25,25,1], [23,26,2], [36,26,2], + [28,24,0], [25,29,5], [27,24,0], [21,22,1], [21,22,1], [21,22,1], [21,22,1], [21,22,1], + [19,24,0], [19,24,0], [15,35,9], [10,35,9], [15,35,9], [10,35,9], [15,35,9], [15,35,9], + [12,35,9], [10,35,9], [6,35,9], [13,35,9], [15,37,10], [20,37,10], [16,35,9], [8,23,3], + [29,35,33], [25,24,0], [27,26,2], [17,33,8], [21,21,0], [21,21,0], [25,27,5], [24,27,5], + [13,31,7], [14,32,8], [14,31,7], [20,31,7], [26,30,5], [25,31,6], [25,27,2], [25,30,5] + ], + cmex10: [ + [15,42,40], [11,42,40], [14,42,40], [8,42,40], [16,42,40], [10,42,40], [16,42,40], [10,42,40], + [16,42,40], [16,42,40], [14,42,40], [13,42,40], [7,23,22], [14,23,22], [18,42,40], [18,42,40], + [20,62,60], [15,62,60], [24,83,81], [18,83,81], [18,83,81], [10,83,81], [20,83,81], [12,83,81], + [20,83,81], [12,83,81], [22,83,81], [22,83,81], [23,83,81], [22,83,81], [34,83,81], [34,83,81], + [26,103,101], [19,103,101], [20,103,101], [11,103,101], [22,103,101], [13,103,101], [22,103,101], [13,103,101], + [23,103,101], [23,103,101], [24,103,101], [23,103,101], [42,103,101], [42,103,101], [26,62,60], [26,62,60], + [29,63,61], [20,63,61], [23,62,60], [12,62,60], [23,62,60], [12,62,60], [14,22,21], [12,22,21], + [25,32,31], [18,32,31], [25,32,31], [18,32,31], [18,63,62], [25,63,62], [18,12,11], [13,22,21], + [29,62,60], [20,62,60], [14,22,21], [20,22,21], [18,62,60], [17,62,60], [27,35,34], [36,49,48], + [21,39,38], [33,77,76], [36,35,34], [50,49,48], [36,35,34], [50,49,48], [36,35,34], [50,49,48], + [34,35,34], [31,35,34], [21,39,38], [27,35,34], [27,35,34], [27,35,34], [27,35,34], [27,35,34], + [48,49,48], [42,49,48], [33,77,76], [36,49,48], [36,49,48], [36,49,48], [36,49,48], [36,49,48], + [31,35,34], [42,49,48], [21,7,-19], [36,8,-19], [51,8,-19], [19,5,-20], [34,5,-21], [50,5,-21], + [16,62,60], [9,62,60], [18,62,60], [11,62,60], [18,62,60], [11,62,60], [19,62,60], [19,62,60], + [35,42,40], [35,62,60], [35,83,81], [35,103,101], [26,63,62], [26,23,22], [37,22,20], [18,22,21], + [19,22,21], [19,22,21], [17,13,8], [18,13,8], [17,12,0], [18,12,0], [25,22,21], [25,22,21] + ], + cmbx10: [ + [22,24,0], [31,24,0], [29,25,1], [27,24,0], [25,23,0], [30,24,0], [27,24,0], [29,24,0], + [27,24,0], [29,24,0], [27,24,0], [26,24,0], [21,24,0], [21,24,0], [32,24,0], [32,24,0], + [10,16,0], [12,23,7], [12,7,-17], [16,7,-17], [15,6,-17], [16,7,-17], [17,3,-18], [19,6,-18], + [15,8,7], [20,25,1], [28,17,1], [30,17,1], [19,23,4], [35,24,0], [39,25,1], [29,29,3], + [11,5,-9], [9,24,0], [16,13,-11], [31,31,7], [18,28,2], [31,28,2], [29,25,1], [9,13,-11], + [13,35,9], [12,35,9], [17,16,-10], [29,27,5], [9,13,7], [11,5,-5], [9,6,0], [18,35,9], + [18,24,1], [17,23,0], [18,23,0], [18,24,1], [19,23,0], [18,24,1], [18,24,1], [19,24,1], + [18,24,1], [18,24,1], [9,16,0], [9,23,7], [9,24,7], [29,11,-3], [17,24,7], [17,24,0], + [29,25,1], [29,24,0], [26,24,0], [27,25,1], [28,24,0], [25,24,0], [23,24,0], [29,25,1], + [30,24,0], [14,24,0], [18,25,1], [29,24,0], [22,24,0], [36,24,0], [30,24,0], [28,25,1], + [25,24,0], [28,31,7], [30,25,1], [20,25,1], [26,23,0], [29,25,1], [29,25,1], [40,25,1], + [29,24,0], [29,24,0], [22,24,0], [10,35,9], [20,13,-11], [7,35,9], [16,7,-17], [9,6,-18], + [9,13,-11], [19,17,1], [21,25,1], [17,17,1], [21,25,1], [17,17,1], [15,24,0], [19,23,7], + [21,24,0], [10,24,0], [12,31,7], [20,24,0], [10,24,0], [32,16,0], [21,16,0], [19,17,1], + [21,23,7], [21,23,7], [16,16,0], [15,17,1], [13,23,1], [21,17,1], [20,17,1], [28,17,1], + [20,16,0], [20,23,7], [16,16,0], [20,2,-8], [40,2,-8], [17,8,-17], [17,5,-19], [17,6,-18] + ], + cmti10: [ + [25,24,0], [26,25,0], [27,25,1], [22,25,0], [26,24,0], [29,24,0], [27,24,0], [29,24,0], + [25,24,0], [29,24,0], [26,24,0], [27,31,7], [22,31,7], [23,31,7], [33,31,7], [34,31,7], + [12,17,1], [13,23,7], [15,7,-17], [19,7,-17], [19,5,-17], [20,7,-17], [20,2,-19], [23,7,-18], + [12,8,7], [21,31,7], [25,17,1], [25,17,1], [19,23,4], [33,24,0], [36,25,1], [28,29,3], + [12,5,-9], [13,25,0], [18,11,-13], [29,31,7], [24,25,1], [29,28,2], [28,26,1], [13,11,-13], + [18,35,9], [13,35,9], [20,16,-10], [26,21,2], [8,11,7], [12,3,-6], [8,4,0], [21,35,9], + [19,24,1], [16,23,0], [19,24,1], [20,24,1], [17,30,7], [20,24,1], [20,24,1], [22,24,1], + [19,24,1], [19,24,1], [11,15,0], [11,22,7], [11,25,8], [27,9,-4], [16,25,8], [19,25,0], + [27,25,1], [24,25,0], [25,24,0], [28,25,1], [27,24,0], [26,24,0], [25,24,0], [28,25,1], + [29,24,0], [18,24,0], [22,25,1], [30,24,0], [22,24,0], [35,24,0], [29,24,0], [27,25,1], + [25,24,0], [27,31,7], [25,25,1], [22,25,1], [28,24,0], [29,25,1], [30,25,1], [39,25,1], + [29,24,0], [30,24,0], [24,24,0], [16,35,9], [21,11,-13], [14,35,9], [18,6,-18], [13,4,-19], + [13,11,-13], [19,17,1], [16,25,1], [16,17,1], [20,25,1], [16,17,1], [17,31,7], [17,23,7], + [19,25,1], [12,24,1], [15,30,7], [18,25,1], [11,25,1], [29,17,1], [20,17,1], [18,17,1], + [18,23,7], [17,23,7], [17,17,1], [15,17,1], [13,23,1], [20,17,1], [17,17,1], [24,17,1], + [18,17,1], [18,23,7], [16,17,1], [19,2,-8], [36,2,-8], [20,7,-17], [20,4,-19], [19,4,-19] + ] +}); + +jsMath.Img.AddFont(298,{ + cmr10: [ + [24,28,0], [33,30,0], [30,30,1], [28,30,0], [26,28,0], [30,28,0], [28,28,0], [30,29,0], + [28,28,0], [30,28,0], [28,29,0], [26,29,0], [22,29,0], [22,29,0], [33,29,0], [33,29,0], + [11,19,0], [11,28,9], [13,9,-20], [17,9,-20], [16,6,-21], [17,8,-21], [18,3,-22], [20,8,-22], + [16,10,9], [20,30,1], [29,20,1], [31,20,1], [20,27,5], [36,28,0], [41,30,1], [30,34,3], + [11,6,-11], [8,30,0], [15,13,-16], [32,37,8], [19,34,3], [32,34,3], [30,31,1], [9,13,-16], + [14,42,11], [12,42,11], [18,18,-13], [30,28,4], [9,13,8], [12,4,-7], [8,5,0], [19,42,11], + [19,29,1], [18,28,0], [19,28,0], [19,29,1], [20,28,0], [19,29,1], [19,29,1], [20,29,1], + [19,29,1], [19,29,1], [8,18,0], [8,26,8], [8,30,9], [30,11,-5], [18,30,9], [18,29,0], + [30,30,1], [30,30,0], [27,28,0], [28,30,1], [29,28,0], [27,28,0], [26,28,0], [31,30,1], + [30,28,0], [14,28,0], [20,29,1], [31,28,0], [24,28,0], [37,28,0], [30,28,0], [30,30,1], + [26,28,0], [30,37,8], [31,29,1], [21,30,1], [29,28,0], [30,29,1], [30,29,1], [42,29,1], + [30,28,0], [31,28,0], [23,28,0], [11,42,11], [20,13,-16], [7,42,11], [16,7,-22], [8,5,-23], + [8,13,-16], [21,20,1], [22,30,1], [18,20,1], [22,30,1], [18,20,1], [15,29,0], [20,28,9], + [22,29,0], [11,28,0], [11,37,9], [21,29,0], [11,29,0], [34,19,0], [22,19,0], [20,20,1], + [22,27,8], [22,27,8], [15,19,0], [15,20,1], [14,27,1], [22,20,1], [21,19,1], [29,19,1], + [22,18,0], [21,27,9], [17,18,0], [21,2,-10], [41,2,-10], [18,8,-21], [18,5,-23], [17,5,-23] + ], + cmmi10: [ + [30,28,0], [33,30,0], [31,30,1], [28,30,0], [32,28,0], [37,28,0], [34,28,0], [29,29,0], + [27,28,0], [29,28,0], [33,29,0], [25,20,1], [25,37,8], [23,28,9], [19,31,1], [16,19,1], + [20,38,9], [21,28,9], [19,30,1], [14,20,1], [23,20,1], [23,30,1], [24,28,9], [22,19,0], + [19,38,9], [24,19,1], [21,28,9], [24,19,1], [21,19,1], [22,20,1], [24,38,9], [25,28,9], + [27,38,9], [25,20,1], [18,20,1], [23,30,1], [34,19,1], [21,27,8], [17,24,5], [26,28,9], + [39,12,-9], [39,13,1], [39,12,-9], [39,13,1], [10,11,-9], [10,11,-9], [20,22,1], [20,22,1], + [19,20,1], [18,19,0], [19,19,0], [19,28,9], [20,28,8], [19,28,9], [19,29,1], [20,28,9], + [19,29,1], [19,28,9], [8,5,0], [9,13,8], [29,25,2], [19,42,11], [29,25,2], [21,20,0], + [24,31,1], [30,30,0], [31,28,0], [32,30,1], [33,28,0], [32,28,0], [31,28,0], [32,30,1], + [37,28,0], [21,28,0], [26,29,1], [37,28,0], [27,28,0], [43,28,0], [37,28,0], [31,30,1], + [31,28,0], [31,37,8], [31,29,1], [27,30,1], [29,28,0], [32,29,1], [32,29,1], [43,29,1], + [35,28,0], [32,28,0], [30,28,0], [14,32,1], [13,39,9], [14,39,9], [39,11,-5], [39,11,-5], + [17,30,1], [21,20,1], [18,30,1], [18,20,1], [22,30,1], [18,20,1], [23,38,9], [20,28,9], + [23,30,1], [13,29,1], [18,37,9], [21,30,1], [11,30,1], [35,20,1], [24,20,1], [20,20,1], + [23,27,8], [19,27,8], [18,20,1], [18,20,1], [14,27,1], [23,20,1], [20,20,1], [29,20,1], + [22,20,1], [21,28,9], [20,20,1], [13,20,1], [16,28,9], [26,28,9], [26,9,-21], [27,6,-22] + ], + cmsy10: [ + [29,3,-9], [8,5,-8], [26,21,0], [18,19,-1], [30,24,2], [21,21,0], [30,28,0], [30,28,7], + [30,28,4], [30,28,4], [30,28,4], [30,28,4], [30,28,4], [39,39,9], [19,17,-2], [19,17,-2], + [30,20,0], [30,19,-1], [29,33,6], [29,33,6], [29,33,6], [29,33,6], [29,33,6], [29,33,6], + [30,11,-5], [30,18,-2], [29,25,2], [29,25,2], [39,27,3], [39,27,3], [29,25,2], [29,25,2], + [39,16,-2], [39,16,-2], [18,37,8], [18,37,8], [39,16,-2], [39,37,8], [39,38,9], [30,19,-1], + [39,24,2], [39,24,2], [24,37,8], [24,37,8], [40,24,2], [39,37,8], [39,38,9], [30,20,1], + [11,22,-1], [39,20,1], [24,25,2], [24,25,2], [34,30,0], [34,30,9], [27,39,9], [6,16,-2], + [23,30,1], [21,29,0], [26,12,-3], [19,36,4], [30,31,1], [29,30,1], [30,28,0], [30,28,0], + [23,29,0], [33,33,3], [28,30,1], [22,30,1], [32,28,0], [24,30,1], [34,30,2], [25,34,5], + [34,30,2], [28,28,0], [35,33,5], [31,30,1], [27,30,1], [46,32,3], [43,35,3], [32,30,1], + [31,31,3], [33,35,6], [35,29,1], [27,30,1], [33,30,0], [30,30,2], [28,30,2], [43,30,2], + [34,28,0], [30,34,6], [32,28,0], [26,26,1], [26,26,1], [26,26,1], [26,26,1], [26,26,1], + [23,29,0], [23,29,0], [18,42,11], [12,42,11], [18,42,11], [12,42,11], [18,42,11], [18,42,11], + [14,42,11], [12,42,11], [7,42,11], [16,42,11], [18,44,12], [24,44,12], [19,42,11], [10,28,4], + [35,42,40], [30,28,0], [33,30,2], [20,39,9], [25,25,0], [25,25,0], [30,33,6], [29,33,6], + [16,38,9], [16,38,9], [16,38,9], [24,37,8], [31,36,6], [30,37,7], [30,32,2], [30,36,6] + ], + cmex10: [ + [17,50,48], [13,50,48], [17,50,48], [9,50,48], [19,50,48], [12,50,48], [19,50,48], [12,50,48], + [20,50,48], [20,50,48], [17,50,48], [16,50,48], [8,27,26], [17,27,26], [22,50,48], [22,50,48], + [23,75,73], [18,75,73], [29,99,97], [22,99,97], [22,99,97], [12,99,97], [24,99,97], [14,99,97], + [24,99,97], [14,99,97], [26,99,97], [26,99,97], [27,99,97], [26,99,97], [41,99,97], [41,99,97], + [32,124,122], [23,124,122], [24,124,122], [13,124,122], [26,124,122], [15,124,122], [26,124,122], [15,124,122], + [28,124,122], [28,124,122], [29,124,122], [28,124,122], [51,124,122], [51,124,122], [31,75,73], [31,75,73], + [35,75,73], [24,75,73], [28,75,73], [14,75,73], [28,75,73], [14,75,73], [17,26,25], [14,26,25], + [30,39,38], [21,39,38], [30,38,37], [21,38,37], [21,76,75], [30,76,75], [21,14,13], [15,26,25], + [35,76,73], [24,76,73], [17,27,26], [24,27,26], [22,75,73], [21,75,73], [32,42,41], [44,59,58], + [25,47,46], [39,93,92], [44,42,41], [60,59,58], [44,42,41], [60,59,58], [44,42,41], [60,59,58], + [41,42,41], [37,42,41], [25,47,46], [32,42,41], [32,42,41], [32,42,41], [32,42,41], [32,42,41], + [57,59,58], [51,59,58], [39,93,92], [44,59,58], [44,59,58], [44,59,58], [44,59,58], [44,59,58], + [37,42,41], [51,59,58], [24,8,-23], [43,9,-23], [61,9,-23], [23,6,-24], [41,6,-25], [60,6,-25], + [19,75,73], [11,75,73], [21,75,73], [13,75,73], [21,75,73], [13,75,73], [23,75,73], [23,75,73], + [42,50,48], [42,75,73], [42,99,97], [42,124,122], [31,75,74], [31,27,26], [45,26,24], [22,26,25], + [23,26,25], [23,26,25], [20,14,9], [21,14,9], [20,14,0], [21,14,0], [30,26,25], [30,26,25] + ], + cmbx10: [ + [27,28,0], [37,29,0], [34,30,1], [32,29,0], [30,28,0], [36,28,0], [32,29,0], [34,29,0], + [32,29,0], [34,29,0], [32,29,0], [31,29,0], [25,29,0], [25,29,0], [38,29,0], [38,29,0], + [12,19,0], [14,28,9], [14,9,-20], [19,9,-20], [19,6,-21], [20,9,-20], [21,3,-22], [23,8,-21], + [19,10,9], [24,30,1], [33,20,1], [36,20,1], [23,28,5], [42,29,0], [47,30,1], [34,34,3], + [14,6,-11], [11,29,0], [19,15,-14], [37,37,8], [21,34,3], [37,34,3], [35,30,1], [11,15,-14], + [16,42,11], [14,42,11], [21,19,-12], [34,32,6], [11,15,8], [14,5,-7], [10,7,0], [21,42,11], + [22,28,1], [21,27,0], [22,27,0], [22,28,1], [23,27,0], [22,28,1], [22,28,1], [23,29,1], + [22,28,1], [22,28,1], [10,19,0], [10,27,8], [11,30,9], [34,13,-4], [20,30,9], [20,29,0], + [34,30,1], [34,29,0], [31,29,0], [32,30,1], [34,29,0], [30,28,0], [28,28,0], [35,30,1], + [36,29,0], [17,29,0], [22,30,1], [35,29,0], [27,29,0], [44,29,0], [36,29,0], [33,30,1], + [30,29,0], [33,37,8], [36,30,1], [24,30,1], [32,28,0], [35,30,1], [35,30,1], [48,30,1], + [35,29,0], [35,29,0], [27,29,0], [13,42,11], [24,15,-14], [8,42,11], [19,8,-21], [10,7,-22], + [10,15,-14], [23,20,1], [25,30,1], [20,20,1], [25,30,1], [21,20,1], [18,29,0], [23,28,9], + [26,29,0], [12,29,0], [14,38,9], [25,29,0], [13,29,0], [39,19,0], [26,19,0], [23,20,1], + [25,27,8], [25,27,8], [19,19,0], [18,20,1], [16,28,1], [26,20,1], [24,20,1], [33,20,1], + [24,19,0], [24,28,9], [19,19,0], [24,2,-10], [48,2,-10], [21,9,-21], [20,5,-24], [20,7,-22] + ], + cmti10: [ + [29,28,0], [31,30,0], [33,30,1], [27,30,0], [31,28,0], [35,28,0], [33,28,0], [35,29,0], + [30,28,0], [34,28,0], [32,29,0], [34,38,9], [27,38,9], [29,38,9], [40,38,9], [41,38,9], + [14,20,1], [16,28,9], [18,9,-20], [23,9,-20], [23,6,-20], [24,8,-21], [24,3,-22], [28,8,-22], + [14,9,8], [25,38,9], [30,20,1], [30,20,1], [23,28,5], [39,28,0], [44,30,1], [34,34,3], + [15,6,-11], [16,30,0], [21,13,-16], [34,37,8], [29,30,1], [35,34,3], [33,31,1], [16,13,-16], + [22,42,11], [16,42,11], [24,18,-13], [31,26,3], [10,13,8], [14,3,-7], [10,5,0], [26,42,11], + [23,29,1], [19,28,0], [23,29,1], [24,29,1], [20,36,8], [24,29,1], [24,29,1], [26,29,1], + [23,29,1], [23,29,1], [13,18,0], [13,26,8], [14,30,9], [32,11,-5], [19,30,9], [23,30,0], + [33,30,1], [29,30,0], [31,28,0], [34,30,1], [32,28,0], [31,28,0], [31,28,0], [34,30,1], + [35,28,0], [21,28,0], [26,29,1], [36,28,0], [26,28,0], [42,28,0], [35,28,0], [33,30,1], + [30,28,0], [33,37,8], [30,29,1], [26,30,1], [34,28,0], [35,29,1], [36,29,1], [47,29,1], + [34,28,0], [36,28,0], [29,28,0], [19,42,11], [26,13,-16], [16,42,11], [22,8,-21], [15,5,-23], + [15,13,-16], [22,20,1], [19,30,1], [20,20,1], [23,30,1], [20,20,1], [21,38,9], [20,28,9], + [22,30,1], [14,28,1], [17,36,9], [21,30,1], [13,30,1], [35,20,1], [25,20,1], [21,20,1], + [21,27,8], [21,27,8], [21,20,1], [18,20,1], [16,27,1], [23,20,1], [21,20,1], [29,20,1], + [22,20,1], [22,28,9], [20,20,1], [23,2,-10], [43,2,-10], [24,9,-20], [24,5,-23], [23,5,-23] + ] +}); + +jsMath.Img.AddFont(358,{ + cmr10: [ + [29,34,0], [39,36,0], [36,37,2], [33,36,0], [31,34,0], [36,34,0], [33,34,0], [36,35,0], + [33,34,0], [36,34,0], [34,35,0], [31,35,0], [26,35,0], [26,35,0], [40,35,0], [40,35,0], + [13,22,0], [13,33,11], [15,11,-24], [20,11,-24], [19,7,-25], [20,9,-25], [22,2,-27], [24,10,-26], + [18,11,10], [24,36,1], [34,23,1], [37,23,1], [23,32,5], [43,34,0], [49,37,2], [36,40,3], + [13,7,-13], [10,36,0], [17,15,-19], [39,44,10], [22,40,3], [39,40,3], [36,38,2], [11,15,-19], + [17,50,13], [15,50,13], [22,22,-15], [36,34,5], [10,16,10], [14,3,-9], [10,6,0], [22,50,13], + [23,35,2], [21,33,0], [22,33,0], [23,35,2], [24,34,0], [22,35,2], [23,35,2], [24,36,2], + [23,35,2], [23,35,2], [10,22,0], [10,32,10], [10,36,11], [36,12,-6], [21,36,11], [21,35,0], + [36,36,1], [36,36,0], [32,34,0], [33,37,2], [35,34,0], [32,34,0], [30,34,0], [37,37,2], + [36,34,0], [17,34,0], [23,36,2], [37,34,0], [29,34,0], [44,34,0], [36,34,0], [36,37,2], + [31,34,0], [36,45,10], [36,36,2], [25,37,2], [34,34,0], [36,36,2], [36,36,2], [50,36,2], + [36,34,0], [37,34,0], [28,34,0], [13,50,13], [23,15,-19], [8,50,13], [19,8,-26], [10,6,-27], + [10,15,-19], [25,23,1], [26,35,1], [21,23,1], [26,35,1], [21,23,1], [18,35,0], [24,34,11], + [27,34,0], [13,33,0], [13,44,11], [26,34,0], [13,34,0], [40,22,0], [27,22,0], [24,23,1], + [26,32,10], [26,32,10], [18,22,0], [18,23,1], [17,32,1], [27,23,1], [25,23,1], [35,23,1], + [26,22,0], [25,33,11], [20,22,0], [25,2,-12], [49,2,-12], [21,10,-25], [21,5,-28], [20,6,-27] + ], + cmmi10: [ + [36,34,0], [39,36,0], [37,37,2], [33,36,0], [39,34,0], [44,34,0], [40,34,0], [35,35,0], + [32,34,0], [34,34,0], [39,35,0], [30,23,1], [29,45,10], [27,33,11], [23,36,1], [19,23,1], + [24,46,11], [25,33,11], [23,36,1], [16,23,1], [27,23,1], [27,35,1], [29,33,11], [26,22,0], + [22,46,11], [28,23,1], [25,33,11], [28,23,1], [26,23,1], [26,23,1], [29,45,11], [30,33,11], + [32,45,11], [30,23,1], [22,25,2], [28,36,1], [41,23,1], [25,32,10], [21,28,6], [31,33,11], + [47,15,-11], [47,15,1], [47,15,-11], [47,15,1], [11,12,-11], [11,12,-11], [24,26,1], [24,26,1], + [23,25,2], [21,23,0], [22,23,0], [23,34,11], [24,33,10], [22,34,11], [23,35,2], [24,34,11], + [23,35,2], [23,34,11], [10,6,0], [10,16,10], [34,29,2], [22,50,13], [34,29,2], [25,24,0], + [28,38,2], [36,36,0], [38,34,0], [38,37,2], [40,34,0], [38,34,0], [37,34,0], [38,37,2], + [44,34,0], [25,34,0], [32,36,2], [44,34,0], [32,34,0], [52,34,0], [44,34,0], [37,37,2], + [37,34,0], [37,45,10], [37,36,2], [32,37,2], [35,34,0], [38,36,2], [38,36,2], [52,36,2], + [42,34,0], [38,34,0], [36,34,0], [17,39,2], [16,47,11], [17,47,11], [47,13,-6], [47,13,-6], + [20,36,1], [25,23,1], [21,35,1], [22,23,1], [26,35,1], [22,23,1], [28,46,11], [24,33,11], + [27,35,1], [15,34,1], [21,44,11], [25,35,1], [13,35,1], [42,23,1], [28,23,1], [23,23,1], + [27,32,10], [23,32,10], [22,23,1], [21,23,1], [17,32,1], [27,23,1], [23,23,1], [34,23,1], + [26,23,1], [25,33,11], [23,23,1], [15,23,1], [19,33,11], [31,34,11], [31,10,-25], [32,7,-26] + ], + cmsy10: [ + [34,3,-11], [10,6,-9], [31,25,0], [22,22,-1], [36,28,2], [24,24,0], [36,33,0], [36,34,9], + [36,34,5], [36,34,5], [36,34,5], [36,34,5], [36,34,5], [47,47,11], [22,20,-2], [22,20,-2], + [36,24,0], [36,22,-1], [34,39,7], [34,39,7], [34,39,7], [34,39,7], [34,39,7], [34,39,7], + [36,12,-6], [36,22,-2], [34,29,2], [34,29,2], [47,32,4], [47,32,4], [34,29,2], [34,29,2], + [47,18,-3], [47,18,-3], [21,44,10], [21,44,10], [47,18,-3], [47,45,10], [47,44,10], [36,22,-1], + [47,28,2], [47,28,2], [29,44,10], [29,44,10], [48,28,2], [47,45,10], [47,44,10], [36,23,1], + [13,26,-2], [47,23,1], [29,29,2], [29,29,2], [41,36,0], [41,36,11], [32,47,11], [7,19,-3], + [28,36,2], [25,34,0], [30,14,-4], [23,42,4], [35,38,2], [34,36,1], [36,33,0], [36,33,0], + [28,34,0], [40,39,3], [33,37,2], [27,37,2], [38,34,0], [28,37,2], [41,36,2], [30,41,6], + [41,37,3], [34,34,0], [42,40,6], [36,37,2], [33,37,2], [55,38,3], [50,41,3], [39,37,2], + [36,37,3], [39,42,7], [42,36,2], [32,37,2], [40,36,0], [35,36,2], [33,37,3], [51,37,3], + [40,34,0], [36,41,7], [38,34,0], [30,32,2], [30,32,2], [30,32,2], [30,32,2], [30,32,2], + [28,34,0], [28,34,0], [21,50,13], [14,50,13], [21,50,13], [14,50,13], [21,50,13], [21,50,13], + [17,50,13], [14,50,13], [8,50,13], [18,50,13], [21,52,14], [29,52,14], [22,50,13], [11,34,5], + [42,50,48], [35,34,0], [39,36,2], [24,47,11], [30,30,0], [30,30,0], [35,39,7], [34,39,7], + [19,46,11], [19,46,11], [19,46,11], [29,44,10], [37,43,7], [36,44,8], [36,38,2], [36,43,7] + ], + cmex10: [ + [21,59,57], [15,59,57], [20,59,57], [11,59,57], [22,59,57], [14,59,57], [22,59,57], [14,59,57], + [23,59,57], [23,59,57], [20,59,57], [19,59,57], [10,33,31], [21,33,31], [26,59,57], [26,59,57], + [28,89,87], [21,89,87], [35,118,116], [26,118,116], [26,118,116], [14,118,116], [28,118,116], [17,118,116], + [28,118,116], [17,118,116], [31,118,116], [31,118,116], [32,118,116], [31,118,116], [49,118,116], [49,118,116], + [38,147,145], [28,147,145], [28,147,145], [16,147,145], [31,147,145], [18,147,145], [31,147,145], [18,147,145], + [33,147,145], [33,147,145], [35,148,146], [33,148,146], [60,147,145], [60,147,145], [37,89,87], [37,89,87], + [42,89,87], [29,89,87], [33,89,87], [17,89,87], [33,89,87], [17,89,87], [20,31,30], [17,31,30], + [36,46,45], [25,46,45], [36,46,45], [25,46,45], [25,90,89], [36,90,89], [25,17,16], [18,31,30], + [42,90,87], [29,90,87], [20,31,30], [29,31,30], [26,89,87], [25,89,87], [39,50,49], [52,70,69], + [30,56,55], [47,110,109], [52,50,49], [72,70,69], [52,50,49], [72,70,69], [52,50,49], [72,70,69], + [49,50,49], [44,50,49], [30,56,55], [39,50,49], [39,50,49], [39,50,49], [39,50,49], [39,50,49], + [68,70,69], [60,70,69], [47,110,109], [52,70,69], [52,70,69], [52,70,69], [52,70,69], [52,70,69], + [44,50,49], [60,70,69], [29,10,-27], [51,10,-28], [72,10,-28], [28,7,-29], [49,7,-30], [71,7,-30], + [23,89,87], [12,89,87], [25,89,87], [15,89,87], [25,89,87], [15,89,87], [27,89,87], [27,89,87], + [50,59,57], [50,89,87], [50,118,116], [50,148,146], [37,90,89], [37,32,31], [53,31,29], [26,31,30], + [28,31,30], [28,31,30], [25,17,11], [25,17,11], [25,17,0], [25,17,0], [36,31,30], [36,31,30] + ], + cmbx10: [ + [32,34,0], [45,35,0], [41,36,1], [38,35,0], [36,34,0], [43,34,0], [38,34,0], [41,35,0], + [38,34,0], [41,34,0], [39,35,0], [37,35,0], [30,35,0], [30,35,0], [46,35,0], [46,35,0], + [15,23,0], [17,33,10], [17,11,-24], [23,11,-24], [22,7,-25], [23,10,-24], [25,3,-27], [28,9,-26], + [22,11,10], [28,36,1], [40,24,1], [43,24,1], [27,34,6], [50,34,0], [56,36,1], [41,40,3], + [16,7,-13], [13,35,0], [23,18,-16], [44,44,10], [25,40,3], [44,40,3], [41,36,1], [13,18,-16], + [19,50,13], [17,50,13], [25,22,-15], [41,39,7], [13,18,10], [16,6,-8], [12,8,0], [25,50,13], + [26,34,1], [25,33,0], [26,33,0], [26,34,1], [27,33,0], [26,34,1], [26,34,1], [28,35,1], + [26,34,1], [26,34,1], [12,22,0], [12,32,10], [13,36,11], [41,15,-5], [24,35,10], [24,35,0], + [41,36,1], [41,35,0], [37,34,0], [38,36,1], [41,34,0], [36,34,0], [34,34,0], [42,36,1], + [43,34,0], [20,34,0], [26,35,1], [42,34,0], [32,34,0], [52,34,0], [43,34,0], [40,36,1], + [36,34,0], [40,45,10], [43,35,1], [29,36,1], [38,34,0], [42,35,1], [42,35,1], [58,35,1], + [41,34,0], [42,34,0], [32,34,0], [15,50,13], [28,18,-16], [10,50,13], [22,9,-25], [12,9,-26], + [12,18,-16], [28,24,1], [30,35,1], [24,24,1], [30,35,1], [25,24,1], [22,35,0], [28,33,10], + [31,34,0], [15,35,0], [17,45,10], [29,34,0], [15,34,0], [46,23,0], [31,23,0], [27,24,1], + [30,33,10], [30,33,10], [22,23,0], [21,24,1], [19,33,1], [31,24,1], [29,23,1], [40,23,1], + [29,22,0], [29,32,10], [23,22,0], [29,3,-12], [57,3,-12], [25,10,-25], [24,6,-28], [24,9,-26] + ], + cmti10: [ + [35,34,0], [37,36,0], [39,37,2], [32,36,0], [37,34,0], [42,34,0], [39,34,0], [41,35,0], + [36,34,0], [41,34,0], [38,35,0], [40,46,11], [32,46,11], [34,46,11], [47,46,11], [49,46,11], + [17,23,1], [18,33,11], [22,11,-24], [27,11,-24], [27,7,-24], [28,9,-25], [28,2,-27], [33,10,-26], + [17,11,10], [30,46,11], [36,23,1], [36,23,1], [27,33,6], [47,34,0], [52,37,2], [41,40,3], + [17,7,-13], [19,36,0], [26,15,-19], [41,44,10], [35,36,1], [42,40,3], [40,38,2], [19,15,-19], + [26,50,13], [19,50,13], [29,22,-15], [37,31,3], [11,16,10], [17,3,-9], [11,6,0], [31,50,13], + [28,35,2], [23,33,0], [27,35,2], [28,35,2], [24,43,10], [28,35,2], [28,35,2], [31,35,2], + [28,35,2], [28,35,2], [15,22,0], [15,32,10], [16,36,11], [39,12,-6], [22,36,11], [27,36,0], + [39,36,1], [34,36,0], [36,34,0], [40,37,2], [38,34,0], [37,34,0], [36,34,0], [40,37,2], + [42,34,0], [25,34,0], [31,36,2], [43,34,0], [31,34,0], [50,34,0], [42,34,0], [39,37,2], + [36,34,0], [39,45,10], [36,36,2], [31,37,2], [40,34,0], [42,36,2], [43,36,2], [56,36,2], + [41,34,0], [43,34,0], [35,34,0], [22,50,13], [30,15,-19], [19,50,13], [26,8,-26], [18,6,-27], + [18,15,-19], [27,23,1], [23,35,1], [24,23,1], [28,35,1], [23,23,1], [25,46,11], [24,33,11], + [27,35,1], [17,34,1], [20,44,11], [25,35,1], [15,35,1], [42,23,1], [29,23,1], [25,23,1], + [25,32,10], [25,32,10], [24,23,1], [21,23,1], [19,32,1], [28,23,1], [25,23,1], [35,23,1], + [26,23,1], [26,33,11], [23,23,1], [27,2,-12], [51,2,-12], [29,11,-24], [29,6,-27], [27,6,-27] + ] +}); + +jsMath.Img.AddFont(430,{ + cmr10: [ + [35,41,0], [47,43,0], [43,44,2], [39,43,0], [37,40,0], [43,41,0], [40,41,0], [43,42,0], + [40,41,0], [43,41,0], [40,42,0], [38,42,0], [32,42,0], [32,42,0], [48,42,0], [48,42,0], + [15,27,0], [16,40,13], [18,12,-30], [24,12,-30], [23,8,-30], [24,11,-30], [26,3,-32], [28,12,-31], + [22,13,12], [28,43,1], [41,28,1], [45,28,1], [28,39,7], [52,41,0], [59,44,2], [43,48,4], + [16,8,-16], [12,43,0], [21,18,-23], [46,53,12], [27,49,4], [46,49,4], [43,45,2], [13,18,-23], + [20,60,15], [18,60,15], [26,27,-18], [43,40,5], [12,19,12], [17,4,-11], [12,7,0], [27,60,15], + [28,42,2], [25,40,0], [27,40,0], [27,42,2], [28,40,0], [27,42,2], [27,42,2], [29,42,2], + [27,42,2], [27,42,2], [12,26,0], [12,38,12], [12,43,13], [43,15,-7], [25,43,13], [25,42,0], + [43,43,1], [43,43,0], [39,41,0], [40,44,2], [42,41,0], [39,41,0], [36,41,0], [44,44,2], + [43,41,0], [20,41,0], [28,43,2], [44,41,0], [35,41,0], [52,41,0], [43,41,0], [43,44,2], + [37,41,0], [43,54,12], [44,43,2], [30,44,2], [41,40,0], [43,43,2], [44,43,2], [60,43,2], + [43,41,0], [44,41,0], [34,41,0], [16,60,15], [28,18,-23], [10,60,15], [23,10,-31], [12,7,-33], + [12,18,-23], [30,28,1], [31,42,1], [25,28,1], [32,42,1], [25,28,1], [22,42,0], [29,40,13], + [32,41,0], [15,40,0], [16,53,13], [31,41,0], [16,41,0], [48,27,0], [32,27,0], [28,28,1], + [31,39,12], [32,39,12], [22,27,0], [22,28,1], [20,38,1], [32,28,1], [30,27,1], [42,27,1], + [31,26,0], [30,39,13], [24,26,0], [30,2,-15], [59,2,-15], [25,12,-30], [25,7,-33], [24,7,-33] + ], + cmmi10: [ + [43,41,0], [47,43,0], [44,44,2], [40,43,0], [46,40,0], [52,41,0], [48,41,0], [42,42,0], + [38,41,0], [41,41,0], [47,42,0], [36,28,1], [35,54,12], [33,40,13], [27,43,1], [23,27,1], + [28,55,13], [30,40,13], [27,43,1], [20,28,1], [33,28,1], [33,42,1], [34,40,13], [31,27,0], + [27,55,13], [34,27,1], [30,40,13], [34,27,1], [31,27,1], [31,28,1], [34,54,13], [36,40,13], + [38,54,13], [36,28,1], [26,29,2], [34,43,1], [49,27,1], [30,39,12], [25,34,7], [37,40,13], + [56,18,-13], [56,17,1], [56,18,-13], [56,17,1], [14,15,-13], [14,15,-13], [28,31,1], [28,31,1], + [28,29,2], [25,27,0], [27,27,0], [27,40,13], [28,40,12], [27,40,13], [27,42,2], [29,41,13], + [27,42,2], [27,40,13], [12,7,0], [12,19,12], [41,35,3], [27,60,15], [41,35,3], [30,29,0], + [34,45,2], [43,43,0], [45,41,0], [45,44,2], [48,41,0], [46,41,0], [45,41,0], [45,44,2], + [52,41,0], [30,41,0], [38,43,2], [53,41,0], [38,41,0], [62,41,0], [52,41,0], [44,44,2], + [45,41,0], [44,54,12], [45,43,2], [39,44,2], [42,40,0], [45,43,2], [46,43,2], [62,43,2], + [51,41,0], [45,41,0], [43,41,0], [20,47,2], [19,56,13], [20,56,13], [56,15,-7], [56,16,-7], + [24,43,1], [30,28,1], [25,42,1], [26,28,1], [31,42,1], [26,28,1], [33,55,13], [28,40,13], + [33,42,1], [18,40,1], [25,52,13], [30,42,1], [16,42,1], [51,28,1], [34,28,1], [28,28,1], + [31,39,12], [27,39,12], [26,28,1], [25,28,1], [20,38,1], [33,28,1], [28,28,1], [41,28,1], + [32,28,1], [29,40,13], [28,28,1], [18,28,1], [23,40,13], [37,40,13], [37,13,-30], [39,9,-31] + ], + cmsy10: [ + [41,3,-13], [12,7,-11], [38,29,0], [26,26,-2], [43,34,2], [29,29,0], [43,40,0], [43,40,10], + [43,40,5], [43,40,5], [43,40,5], [43,40,5], [43,40,5], [56,56,13], [27,24,-3], [27,24,-3], + [43,29,0], [43,26,-2], [41,47,9], [41,47,9], [41,47,9], [41,47,9], [41,47,9], [41,47,9], + [43,15,-7], [43,26,-3], [41,35,3], [41,35,3], [56,38,4], [56,38,4], [41,35,3], [41,35,3], + [56,22,-4], [56,22,-4], [26,53,12], [26,53,12], [56,22,-4], [56,54,12], [56,53,12], [43,26,-2], + [56,33,2], [56,33,2], [35,53,12], [35,53,12], [57,33,2], [56,54,12], [56,53,12], [43,28,1], + [16,31,-2], [56,28,1], [35,35,3], [35,35,3], [49,43,0], [49,43,13], [38,56,13], [8,23,-3], + [33,43,2], [30,41,0], [36,16,-5], [27,51,5], [43,45,2], [41,43,1], [43,40,0], [43,40,0], + [33,41,0], [48,46,3], [40,44,2], [32,44,2], [46,41,0], [34,44,2], [49,43,2], [36,50,8], + [49,44,3], [40,41,0], [50,49,8], [44,44,2], [39,44,2], [66,45,3], [60,49,3], [46,44,2], + [44,44,3], [47,50,8], [50,43,2], [38,44,2], [48,43,0], [42,43,2], [39,44,3], [62,44,3], + [48,41,0], [43,49,8], [46,41,0], [36,38,2], [36,38,2], [36,38,2], [36,38,2], [36,38,2], + [33,41,0], [33,41,0], [25,60,15], [16,60,15], [25,60,15], [16,60,15], [26,60,15], [26,60,15], + [20,60,15], [17,60,15], [10,60,15], [22,60,15], [26,63,17], [35,63,17], [27,60,15], [14,40,5], + [51,60,57], [43,41,0], [47,43,2], [28,56,13], [36,36,0], [36,36,0], [43,47,9], [41,47,9], + [23,55,13], [23,55,13], [23,55,13], [35,53,12], [45,51,8], [43,53,10], [43,45,2], [43,51,8] + ], + cmex10: [ + [25,72,69], [18,72,69], [24,72,69], [13,72,69], [27,72,69], [16,72,69], [27,72,69], [16,72,69], + [28,72,69], [28,72,69], [24,72,69], [22,72,69], [12,39,37], [25,39,37], [31,72,69], [31,72,69], + [34,107,104], [25,107,104], [42,143,140], [32,143,140], [31,143,140], [17,143,140], [34,143,140], [20,143,140], + [34,143,140], [20,143,140], [37,143,140], [37,143,140], [39,143,140], [37,143,140], [59,143,140], [59,143,140], + [45,178,175], [33,178,175], [34,178,175], [19,178,175], [37,178,175], [22,178,175], [37,178,175], [22,178,175], + [39,178,175], [39,178,175], [42,178,175], [40,178,175], [73,178,175], [73,178,175], [45,107,104], [45,107,104], + [50,108,105], [35,108,105], [39,107,104], [21,107,104], [39,107,104], [21,107,104], [24,37,36], [21,37,36], + [43,55,54], [30,55,54], [43,55,54], [30,55,54], [30,108,107], [43,108,107], [30,20,19], [21,37,36], + [50,107,104], [35,107,104], [24,37,36], [35,37,36], [31,107,104], [30,107,104], [46,60,59], [63,84,83], + [36,67,66], [56,133,132], [63,60,59], [86,84,83], [63,60,59], [86,84,83], [63,60,59], [86,84,83], + [59,60,59], [53,60,59], [36,67,66], [46,60,59], [46,60,59], [46,60,59], [46,60,59], [46,60,59], + [82,84,83], [73,84,83], [56,133,132], [63,84,83], [63,84,83], [63,84,83], [63,84,83], [63,84,83], + [53,60,59], [73,84,83], [35,11,-33], [61,13,-33], [87,13,-33], [33,8,-35], [59,9,-36], [86,9,-36], + [27,107,104], [15,107,104], [31,107,104], [18,107,104], [31,107,104], [18,107,104], [33,107,104], [33,107,104], + [61,72,69], [61,107,104], [61,143,140], [61,178,175], [44,109,107], [44,39,37], [64,38,35], [31,37,36], + [33,37,36], [33,37,36], [30,21,13], [29,21,13], [30,20,0], [29,20,0], [43,37,36], [43,37,36] + ], + cmbx10: [ + [38,41,0], [54,42,0], [49,43,1], [46,42,0], [43,40,0], [51,41,0], [46,41,0], [49,42,0], + [46,41,0], [49,41,0], [46,42,0], [45,42,0], [36,42,0], [36,42,0], [55,42,0], [55,42,0], + [17,27,0], [20,39,12], [20,12,-30], [27,12,-30], [27,9,-30], [28,12,-29], [30,4,-32], [33,11,-31], + [26,13,12], [34,43,1], [48,28,1], [51,28,1], [32,40,7], [60,41,0], [68,43,1], [49,49,4], + [19,8,-16], [15,42,0], [28,21,-20], [53,53,12], [31,49,4], [53,49,4], [50,43,1], [15,21,-20], + [23,60,15], [20,60,15], [30,27,-18], [49,46,8], [15,22,12], [19,6,-10], [14,10,0], [31,60,15], + [32,40,1], [30,39,0], [31,39,0], [32,40,1], [32,39,0], [31,40,1], [32,40,1], [33,41,1], + [32,40,1], [32,40,1], [14,27,0], [15,39,12], [15,43,13], [49,18,-6], [29,42,12], [29,42,0], + [49,43,1], [49,42,0], [45,41,0], [46,43,1], [49,41,0], [43,41,0], [40,41,0], [50,43,1], + [51,41,0], [24,41,0], [32,42,1], [51,41,0], [38,41,0], [63,41,0], [51,41,0], [48,43,1], + [43,41,0], [48,54,12], [51,42,1], [34,43,1], [45,40,0], [50,42,1], [50,42,1], [69,42,1], + [50,41,0], [51,41,0], [39,41,0], [18,60,15], [34,21,-20], [12,60,15], [27,10,-31], [14,10,-31], + [14,21,-20], [33,28,1], [36,42,1], [29,28,1], [36,42,1], [30,28,1], [26,42,0], [33,39,12], + [37,41,0], [17,41,0], [20,53,12], [35,41,0], [18,41,0], [56,27,0], [37,27,0], [32,28,1], + [36,39,12], [36,39,12], [27,27,0], [25,28,1], [23,39,1], [37,28,1], [35,28,1], [48,28,1], + [35,27,0], [35,39,12], [28,27,0], [34,3,-15], [68,3,-15], [29,12,-30], [29,7,-34], [28,9,-32] + ], + cmti10: [ + [42,41,0], [45,43,0], [47,44,2], [38,43,0], [45,40,0], [51,41,0], [47,41,0], [50,42,0], + [44,41,0], [49,41,0], [45,42,0], [47,55,13], [38,55,13], [40,55,13], [57,55,13], [58,55,13], + [20,28,1], [22,40,13], [26,13,-29], [33,13,-29], [32,9,-29], [34,11,-30], [34,3,-32], [40,12,-31], + [20,13,12], [37,55,13], [43,28,1], [43,28,1], [33,39,7], [57,41,0], [63,44,2], [49,48,4], + [21,8,-16], [23,43,0], [31,18,-23], [49,53,12], [42,43,1], [50,49,4], [48,45,2], [22,18,-23], + [31,60,15], [23,60,15], [35,27,-18], [45,37,4], [14,19,12], [20,4,-11], [14,7,0], [37,60,15], + [33,42,2], [28,40,0], [33,42,2], [34,42,2], [28,52,12], [34,42,2], [34,42,2], [37,42,2], + [33,42,2], [33,42,2], [18,26,0], [18,38,12], [19,43,13], [46,15,-7], [27,43,13], [33,43,0], + [47,43,1], [41,43,0], [44,41,0], [48,44,2], [46,41,0], [44,41,0], [44,41,0], [48,44,2], + [51,41,0], [30,41,0], [37,43,2], [51,41,0], [37,41,0], [60,41,0], [51,41,0], [47,44,2], + [44,41,0], [47,54,12], [43,43,2], [38,44,2], [48,40,0], [51,43,2], [52,43,2], [67,43,2], + [49,41,0], [52,41,0], [42,41,0], [27,60,15], [36,18,-23], [23,60,15], [31,10,-31], [22,7,-33], + [22,18,-23], [32,28,1], [28,42,1], [28,28,1], [34,42,1], [28,28,1], [29,55,13], [29,40,13], + [32,42,1], [20,40,1], [25,52,13], [30,42,1], [19,42,1], [50,28,1], [35,28,1], [31,28,1], + [31,39,12], [30,39,12], [29,28,1], [25,28,1], [22,38,1], [34,28,1], [30,28,1], [42,28,1], + [31,28,1], [31,40,13], [28,28,1], [33,2,-15], [62,2,-15], [34,13,-29], [34,7,-33], [33,7,-33] + ] +}); diff --git a/htdocs/jsMath/uncompressed/font.js b/htdocs/jsMath/uncompressed/font.js new file mode 100644 index 0000000000..51f0a2fe63 --- /dev/null +++ b/htdocs/jsMath/uncompressed/font.js @@ -0,0 +1,1677 @@ +jsMath.Img.AddFont(50,{ + cmr10: [ + [4,5,0], [6,5,0], [5,6,1], [5,5,0], [5,5,0], [5,5,0], [5,5,0], [5,5,0], + [5,5,0], [5,5,0], [5,5,0], [5,5,0], [4,5,0], [4,5,0], [6,5,0], [6,5,0], + [2,3,0], [3,5,2,1], [2,2,-3], [2,2,-3,-1], [2,2,-3,-1], [3,2,-3], [3,2,-3], [2,2,-3,-2], + [2,2,2,-1], [4,5,0], [5,4,0], [6,4,0], [4,5,1], [6,5,0], [7,6,1], [5,6,1], + [2,1,-2], [2,5,0], [3,2,-3], [6,7,2], [4,6,1], [6,7,1], [5,6,1], [2,2,-3], + [3,7,2], [2,7,2], [3,4,-2], [5,5,1], [2,2,1], [2,1,-1], [2,1,0], [3,7,2], + [4,6,1], [3,5,0], [4,5,0], [4,6,1], [4,5,0], [4,6,1], [4,6,1], [4,6,1], + [4,6,1], [4,6,1], [2,3,0], [2,4,1], [2,6,2], [5,3,0], [3,6,2], [3,5,0], + [5,5,0], [5,5,0], [5,5,0], [5,6,1], [5,5,0], [5,5,0], [5,5,0], [5,6,1], + [5,5,0], [3,5,0], [3,6,1], [5,5,0], [4,5,0], [6,5,0], [5,5,0], [5,6,1], + [5,5,0], [5,7,2], [5,6,1], [4,6,1], [5,5,0], [5,6,1], [5,5,0], [7,5,0], + [7,5,0,2], [5,5,0], [4,5,0], [2,8,2], [3,3,-2,-1], [2,8,2], [2,1,-4,-1], [2,1,-4], + [2,3,-2], [4,4,0], [4,5,0], [3,4,0], [4,5,0], [3,4,0], [3,5,0], [4,5,2], + [4,5,0], [2,5,0], [3,7,2,1], [4,5,0], [2,5,0], [6,3,0], [4,3,0], [4,4,0], + [4,5,2], [4,5,2], [3,3,0], [3,4,0], [3,5,0], [4,3,0], [4,3,0], [5,3,0], + [4,3,0], [4,5,2], [3,3,0], [4,1,-1], [7,1,-1], [2,2,-3,-1], [3,1,-4], [3,1,-4], + [3,11,20,28,37,45,54,63,71,80,88,97,105,114,122,131], + [9,17,26,34,42,51,59,67], + [141,73] + ], + cmmi10: [ + [5,5,0], [6,5,0], [6,6,1], [5,5,0], [6,5,0], [6,5,0], [6,5,0], [5,5,0], + [5,5,0], [5,5,0], [6,5,0], [5,3,0], [4,6,1], [4,5,2], [3,5,0], [3,3,0], + [4,7,2], [4,5,2], [4,5,0], [3,3,0], [4,3,0], [4,5,0], [4,5,2], [4,3,0], + [3,7,2], [4,3,0], [4,5,2], [4,3,0], [4,3,0], [4,3,0], [4,7,2], [4,5,2], + [5,7,2], [5,3,0], [3,5,1], [4,5,0], [6,3,0], [4,5,2], [3,4,1], [5,5,2], + [7,3,-1], [9,2,0,2], [7,3,-1], [9,2,0,2], [4,3,-1,2], [2,3,-1], [4,4,0], [4,4,0], + [4,5,1], [3,3,0], [4,4,0], [4,6,2], [4,6,2], [3,5,2], [4,6,1], [4,5,2], + [4,6,1], [4,6,2], [2,1,0], [2,2,1], [5,5,1], [3,7,2], [5,5,1], [4,4,0], + [4,6,1], [5,5,0], [6,5,0], [6,6,1], [6,5,0], [6,5,0], [6,5,0], [6,6,1], + [6,5,0], [4,5,0], [5,6,1], [6,5,0], [5,5,0], [7,5,0], [8,5,0,2], [6,6,1], + [6,5,0], [6,7,2], [6,6,1], [5,6,1], [5,5,0], [6,6,1], [6,5,0], [7,5,0], + [8,5,0,2], [6,5,0], [5,5,0], [3,5,0], [3,7,2], [3,7,2], [7,2,-1], [9,2,-1,2], + [3,5,0], [4,3,0], [3,5,0], [3,3,0], [4,5,0], [3,3,0], [4,7,2], [4,5,2], + [4,5,0], [2,5,0], [3,7,2], [4,5,0], [2,5,0], [6,3,0], [4,3,0], [4,3,0], + [4,5,2], [3,5,2], [3,3,0], [3,3,0], [3,5,0], [4,3,0], [4,3,0], [5,3,0], + [4,3,0], [4,5,2], [3,3,0], [2,3,0], [3,5,2], [5,5,2], [4,2,-3,-1], [3,1,-4,-2], + [3,11,19,28,36,44,53,61,69,77,86,94,102,111,119,127], + [9,17,26,34,42,51,59,67], + [137,73] + ], + cmsy10: [ + [5,1,-1], [2,1,-1], [4,4,0,-1], [3,4,0], [5,5,1], [4,4,0], [5,5,0], [5,5,1], + [5,5,1], [5,5,1], [5,5,1], [5,5,1], [5,5,1], [7,7,2], [3,4,0], [3,4,0], + [5,4,0], [5,4,0], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], [5,6,1], + [5,2,-1], [5,4,0], [5,5,1], [5,5,1], [7,5,1], [7,5,1], [5,5,1], [5,5,1], + [7,3,0], [7,3,0], [3,7,2], [3,7,2], [7,3,0], [7,7,2], [7,7,2], [5,4,0], + [7,4,0], [7,4,0], [4,7,2], [4,7,2], [7,4,0], [7,7,2], [7,7,2], [5,3,0], + [2,4,0], [7,3,0], [4,5,1], [4,5,1], [6,5,0], [6,6,2], [4,7,2,-1], [1,3,0], + [4,5,0], [4,5,0], [5,3,0], [4,7,1], [5,6,1], [5,5,0], [5,5,0], [5,5,0], + [4,5,0], [6,6,1], [5,6,1], [4,6,1], [6,5,0], [4,6,1], [6,6,1], [5,6,1], + [6,6,1], [5,5,0], [6,6,1], [5,6,1], [5,6,1], [8,6,1], [9,7,1,2], [6,6,1], + [6,6,1], [6,6,1], [6,6,1], [5,6,1], [6,5,0], [6,6,1,1], [5,6,1], [8,6,1], + [6,5,0], [5,6,1], [6,5,0], [5,5,1], [5,5,0], [5,5,1], [5,4,0], [5,4,0], + [4,5,0], [4,5,0], [2,8,2,-1], [2,8,2], [2,8,2,-1], [2,8,2], [3,8,2], [3,8,2], + [3,8,2], [2,8,2], [2,7,2], [3,8,2], [3,8,2], [4,8,2], [3,8,2], [2,5,1], + [6,8,7], [5,5,0], [6,6,1], [4,7,2], [5,4,0], [5,5,0], [5,6,1], [5,6,1], + [3,7,2], [3,7,2], [3,7,2], [4,7,2], [6,6,1], [5,6,1], [5,6,1], [5,6,1], + [3,13,23,33,43,53,63,73,83,93,103,113,123,133,142,152], + [9,24,38,52,66,80,95,109], + [164,121] + ], + cmex10: [ + [6,9,8,3], [3,9,8], [2,10,9,-1], [2,10,9], [2,10,9,-1], [2,10,9], [2,9,8,-1], [2,9,8], + [2,9,8,-1], [2,9,8,-1], [3,9,8], [3,9,8], [1,5,5,-1], [2,5,5,-1], [4,9,8], [4,9,8], + [7,14,13,3], [3,14,13], [4,18,17,-1], [4,18,17], [3,18,17,-1], [2,18,17], [3,18,17,-1], [3,18,17], + [3,18,17,-1], [3,18,17], [4,18,17,-1], [4,18,17,-1], [4,18,17,-1], [5,18,17], [7,18,17], [7,18,17], + [9,22,21,3], [4,22,21], [3,22,21,-1], [3,22,21], [4,22,21,-1], [3,22,21], [4,22,21,-1], [3,22,21], + [4,22,21,-1], [4,22,21,-1], [4,22,21,-1], [5,22,21], [9,22,21], [9,22,21], [6,14,13], [6,14,13], + [9,14,13,3], [5,14,13], [3,14,13,-2], [3,14,13], [3,14,13,-2], [3,14,13], [1,5,5,-2], [2,5,5,-1], + [3,7,7,-2], [3,7,7,-1], [3,7,7,-2], [3,7,7,-1], [3,13,13,-1], [3,13,13,-2], [2,3,3,-2], [1,5,5,-2], + [9,14,13,3], [5,14,13], [1,5,5,-2], [2,5,5,-3], [4,14,13], [4,14,13], [6,7,7], [8,10,10], + [5,8,8], [7,16,16], [8,7,7], [11,10,10], [11,7,7,3], [11,10,10], [11,7,7,3], [11,10,10], + [10,8,7,3], [7,7,7], [5,8,8], [6,7,7], [6,7,7], [6,7,7], [6,7,7], [6,7,7], + [10,10,10], [9,10,10], [7,16,16], [8,10,10], [8,10,10], [8,10,10], [8,10,10], [8,10,10], + [10,8,7,3], [9,10,10], [4,1,-4], [7,2,-4], [10,2,-4], [4,1,-4], [7,2,-4], [10,2,-4], + [6,19,13,3], [2,14,13], [3,14,13,-1], [3,14,13], [3,14,13,-1], [3,14,13], [3,14,13,-1], [3,14,13,-1], + [10,9,8,3], [6,13,13,-1], [6,17,17,-1], [6,21,21,-1], [5,13,13,-1], [2,5,5,-4], [4,5,4,-4], [3,5,5,-1], + [3,5,5,-1], [4,4,4], [5,3,2,1], [4,3,2], [4,3,0], [4,3,0], [5,5,5], [5,4,4], + [3,16,28,41,53,66,78,91,103,116,129,141,154,166,179,191], + [11,42,72,103,134,165,196,226], + [205,256] + ], + cmbx10: [ + [5,5,0], [7,5,0], [6,5,0], [6,5,0], [5,5,0], [6,5,0], [6,5,0], [6,5,0], + [6,5,0], [6,5,0], [6,5,0], [6,5,0], [5,5,0], [5,5,0], [7,5,0], [7,5,0], + [2,4,0], [3,6,2,1], [3,2,-3], [3,2,-3,-1], [2,2,-3,-1], [4,2,-3], [4,2,-3], [2,2,-3,-2], + [2,2,2,-1], [4,5,0], [6,4,0], [6,4,0], [4,5,1], [7,5,0], [8,6,1], [6,6,1], + [3,1,-2], [2,5,0], [4,3,-2], [7,7,2], [4,6,1], [7,7,1], [6,5,0], [2,3,-2], + [3,8,2], [3,8,2], [4,4,-2], [6,6,1], [2,2,1], [3,1,-1], [2,1,0], [4,8,2], + [4,5,0], [4,5,0], [4,5,0], [4,5,0], [4,5,0], [4,5,0], [4,5,0], [4,5,0], + [4,5,0], [4,5,0], [2,3,0], [2,4,1], [2,6,2], [6,3,0], [4,6,2], [4,5,0], + [6,5,0], [6,5,0], [6,5,0], [6,5,0], [6,5,0], [5,5,0], [5,5,0], [6,5,0], + [6,5,0], [3,5,0], [4,6,1], [6,5,0], [5,5,0], [8,5,0], [6,5,0], [6,5,0], + [5,5,0], [6,7,2], [6,5,0], [4,5,0], [6,5,0], [6,5,0], [6,5,0], [8,5,0], + [6,5,0], [6,5,0], [5,5,0], [2,8,2], [3,3,-2,-1], [2,8,2], [2,2,-3,-1], [2,2,-3], + [2,3,-2], [4,4,0], [5,5,0], [4,4,0], [5,5,0], [4,4,0], [3,5,0], [4,6,2], + [5,5,0], [2,5,0], [3,7,2,1], [4,5,0], [2,5,0], [7,4,0], [5,4,0], [4,4,0], + [5,6,2], [5,6,2], [3,4,0], [3,4,0], [3,5,0], [5,4,0], [4,4,0], [6,4,0], + [4,4,0], [4,6,2], [4,4,0], [4,1,-1], [8,1,-1], [3,2,-3,-1], [4,1,-4], [4,2,-3], + [3,13,23,33,42,52,62,72,82,92,102,111,121,131,141,151], + [9,17,26,34,42,51,59,67], + [162,73] + ], + cmti10: [ + [5,5,0], [6,5,0], [5,6,1,-1], [5,5,0], [6,5,0], [6,5,0], [6,5,0], [5,5,0,-1], + [5,5,0,-1], [5,5,0,-1], [6,5,0], [6,7,2], [5,7,2], [5,7,2], [7,7,2], [9,7,2,2], + [3,3,0], [4,5,2,1], [1,2,-3,-2], [2,2,-3,-2], [2,2,-3,-2], [2,2,-3,-2], [2,2,-3,-2], [2,2,-3,-3], + [2,2,2,-1], [4,7,2], [5,3,0], [5,3,0], [4,5,1], [7,5,0], [6,6,1,-1], [8,6,1,2], + [3,1,-2], [3,5,0], [3,2,-3,-1], [5,7,2,-1], [5,5,0], [5,7,1,-1], [6,6,1], [2,2,-3,-1], + [3,7,2,-1], [3,7,2], [3,3,-2,-1], [5,5,1,-1], [2,3,2], [3,1,-1], [2,1,0], [5,8,2], + [4,6,1], [3,5,0,-1], [4,6,1], [4,6,1], [4,7,2], [4,6,1], [4,6,1], [4,5,0,-1], + [4,6,1], [4,6,1], [2,3,0], [2,5,2], [3,6,2], [5,3,0,-1], [3,6,2], [3,5,0,-1], + [5,5,0,-1], [5,5,0], [6,5,0], [5,6,1,-1], [6,5,0], [6,5,0], [6,5,0], [5,6,1,-1], + [6,5,0], [4,5,0], [5,6,1], [6,5,0], [5,5,0], [7,5,0], [6,5,0], [5,6,1,-1], + [5,5,0], [5,7,2,-1], [5,6,1], [5,6,1], [5,5,0,-1], [5,6,1,-1], [5,5,0,-1], [7,5,0,-1], + [8,5,0,2], [5,5,0,-1], [5,5,0], [3,8,2], [2,3,-2,-2], [3,8,2], [2,1,-4,-2], [2,1,-4,-1], + [2,3,-2,-1], [4,3,0], [4,5,0], [4,3,0], [4,5,0], [4,3,0], [4,7,2], [4,5,2], + [4,5,0], [3,5,0], [4,7,2,1], [4,5,0], [2,5,0], [6,3,0], [4,3,0], [4,3,0], + [4,5,2], [4,5,2], [4,3,0], [3,3,0], [3,5,0], [4,3,0], [4,3,0], [5,3,0], + [4,3,0], [4,5,2], [3,3,0], [3,1,-1,-1], [6,1,-1,-1], [6,4,-1,2], [2,1,-4,-2], [2,1,-4,-2], + [3,11,20,28,37,45,54,62,71,79,88,96,105,113,122,130], + [9,17,26,34,42,51,59,67], + [140,73] + ] +}); + +jsMath.Img.AddFont(60,{ + cmr10: [ + [5,6,0], [7,6,0], [6,7,1], [6,6,0], [5,6,0], [6,6,0], [6,6,0], [6,6,0], + [6,6,0], [6,6,0], [6,6,0], [5,6,0], [4,6,0], [4,6,0], [7,6,0], [7,6,0], + [2,4,0], [3,6,2,1], [2,2,-4,-1], [2,2,-4,-1], [2,1,-4,-1], [4,2,-4], [4,1,-4], [2,2,-4,-2], + [2,2,2,-1], [4,6,0], [6,5,1], [6,4,0], [4,5,1], [7,6,0], [8,7,1], [6,7,1], + [2,1,-2], [2,6,0], [3,3,-3], [6,8,2], [4,7,1], [7,7,1], [6,7,1], [2,3,-3], + [3,8,2], [3,8,2], [4,4,-2], [6,6,1], [2,3,2], [3,1,-1], [2,1,0], [4,8,2], + [4,7,1], [4,6,0], [4,6,0], [4,7,1], [4,6,0], [4,7,1], [4,7,1], [4,7,1], + [4,7,1], [4,7,1], [2,4,0], [2,6,2], [2,6,2], [6,2,-1], [4,6,2], [4,6,0], + [6,7,1], [6,6,0], [6,6,0], [6,7,1], [6,6,0], [6,6,0], [5,6,0], [6,7,1], + [6,6,0], [3,6,0], [4,7,1], [6,6,0], [5,6,0], [7,6,0], [6,6,0], [6,7,1], + [5,6,0], [6,8,2], [6,7,1], [4,7,1], [6,6,0], [6,7,1], [6,6,0], [8,6,0], + [6,6,0], [6,6,0], [5,6,0], [2,8,2], [3,3,-3,-1], [2,8,2], [2,2,-4,-1], [2,2,-4], + [2,3,-3], [4,5,1], [5,7,1], [4,4,0], [4,6,0], [4,4,0], [3,6,0], [4,6,2], + [5,6,0], [2,6,0], [3,8,2,1], [4,6,0], [2,6,0], [7,4,0], [5,4,0], [4,4,0], + [5,6,2], [4,6,2], [3,4,0], [3,5,1], [3,5,0], [5,4,0], [4,4,0], [6,4,0], + [4,4,0], [4,6,2], [4,4,0], [4,1,-2], [8,1,-2], [3,2,-4,-1], [4,2,-4], [2,2,-4,-1], + [3,14,24,34,44,55,65,75,85,95,106,116,126,136,147,157], + [11,21,31,41,51,61,71,81], + [169,87] + ], + cmmi10: [ + [6,6,0], [7,6,0], [6,7,1], [6,6,0], [7,6,0], [7,6,0], [7,6,0], [6,6,0], + [5,6,0], [6,6,0], [7,6,0], [5,5,1], [5,8,2], [5,6,2], [4,6,0], [3,4,0], + [4,8,2], [4,6,2], [4,6,0], [3,4,0], [5,4,0], [5,6,0], [5,6,2], [5,4,0], + [4,8,2], [5,4,0], [4,6,2], [5,5,1], [4,4,0], [5,4,0], [5,8,2], [5,6,2], + [5,8,2], [5,4,0], [4,5,1], [5,6,0], [7,4,0], [4,6,2], [4,5,1], [5,6,2], + [8,3,-1], [8,3,0], [8,3,-1], [8,3,0], [2,3,-1], [2,3,-1], [4,4,0], [4,4,0], + [4,5,1], [3,4,0,-1], [4,4,0], [4,6,2], [4,6,2], [4,6,2], [4,7,1], [4,6,2], + [4,7,1], [4,6,2], [2,1,0], [2,3,2], [6,6,1], [4,8,2], [6,6,1], [4,4,0], + [5,7,1], [6,6,0], [6,6,0], [6,7,1], [7,6,0], [6,6,0], [6,6,0], [6,7,1], + [7,6,0], [4,6,0], [5,7,1], [7,6,0], [5,6,0], [9,6,0], [9,6,0,2], [6,7,1], + [6,6,0], [6,8,2], [6,7,1], [6,7,1], [6,6,0], [6,7,1], [6,6,0], [9,6,0], + [9,6,0,2], [6,6,0], [6,6,0], [3,6,0], [3,8,2], [3,8,2], [8,2,-1], [8,2,-1], + [4,6,0], [4,4,0], [4,6,0], [4,5,1], [4,6,0], [4,4,0], [5,8,2], [4,6,2], + [5,6,0], [3,6,0], [3,8,2], [4,6,0], [2,6,0], [7,4,0], [5,4,0], [4,5,1], + [4,6,2], [4,6,2], [4,4,0], [4,5,1], [3,5,0], [5,4,0], [4,4,0], [6,4,0], + [5,4,0], [4,6,2], [4,4,0], [3,4,0], [3,6,2], [5,6,2], [4,2,-4,-1], [4,2,-4,-2], + [3,13,23,33,43,53,63,73,83,93,103,113,123,133,143,153], + [11,21,31,41,51,61,71,81], + [164,87] + ], + cmsy10: [ + [5,2,-1,-1], [2,2,-1], [4,4,0,-1], [4,4,0], [6,6,1], [4,4,0], [6,6,0], [6,6,2], + [6,6,1], [6,6,1], [6,6,1], [6,6,1], [6,6,1], [8,8,2], [4,4,0], [4,4,0], + [6,4,0], [6,4,0], [6,8,2], [6,8,2], [6,7,2], [6,7,2], [6,7,2], [6,7,2], + [6,2,-1], [6,4,0], [6,6,1], [6,6,1], [8,6,1], [8,6,1], [5,6,1,-1], [6,6,1], + [8,4,0], [8,4,0], [4,8,2], [4,8,2], [8,4,0], [8,8,2], [8,8,2], [6,4,0], + [8,6,1], [8,6,1], [5,8,2], [5,8,2], [8,6,1], [8,8,2], [8,8,2], [6,4,0], + [2,5,0], [8,4,0], [5,6,1], [5,6,1], [7,6,0], [7,6,2], [4,8,2,-1], [1,4,0], + [5,6,0], [4,6,0], [5,3,0], [4,7,1], [6,7,1], [6,7,1], [6,6,0], [6,6,0], + [5,6,0], [7,7,1], [6,7,1], [5,7,1], [7,6,0], [5,7,1], [7,7,1], [5,7,1], + [7,7,1], [5,6,0], [7,7,1], [6,7,1], [5,7,1], [9,7,1], [9,8,1,1], [7,7,1], + [6,7,1], [6,7,1,-1], [7,7,1], [6,7,1], [7,6,0], [7,7,1,1], [6,7,1], [9,7,1], + [7,6,0], [6,7,1], [6,6,0], [5,6,1], [5,5,0], [5,6,1], [5,5,0], [5,5,0], + [5,6,0], [5,6,0], [3,8,2,-1], [3,8,2], [3,8,2,-1], [3,8,2], [3,8,2,-1], [3,8,2], + [2,8,2,-1], [3,8,2], [1,8,2,-1], [2,8,2,-1], [4,8,2], [5,8,2], [4,8,2], [2,6,1], + [7,9,8], [6,6,0], [7,7,1], [4,8,2], [5,5,0], [5,5,0], [6,8,2], [6,8,2], + [3,8,2], [3,8,2], [3,8,2], [5,8,2], [6,7,1], [6,8,2], [6,7,1], [6,7,1], + [3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183], + [11,28,45,62,80,97,114,131], + [196,145] + ], + cmex10: [ + [3,10,9,-1], [3,11,10], [2,11,10,-1], [2,11,10], [3,11,10,-1], [3,10,10], [3,11,10,-1], [3,10,9], + [3,11,10,-1], [3,11,10,-1], [3,9,9], [3,11,10], [1,5,5,-1], [3,5,5,-1], [4,10,10], [4,10,9], + [4,15,14,-1], [4,15,14], [5,20,19,-1], [5,20,19], [2,20,19,-2], [3,20,19], [3,20,19,-2], [3,20,19], + [3,20,19,-2], [3,20,19], [4,20,19,-1], [4,20,19,-1], [5,20,19,-1], [5,20,19], [8,20,19], [8,20,19], + [5,25,24,-1], [5,25,24], [3,25,24,-2], [3,25,24], [3,25,24,-2], [3,25,24], [3,25,24,-2], [3,25,24], + [5,25,24,-1], [5,25,24,-1], [5,25,24,-1], [5,25,24,-1], [10,25,24], [10,25,24], [6,15,14], [6,15,14], + [5,16,15,-2], [5,16,15], [4,15,14,-2], [3,15,14], [4,16,15,-2], [3,16,15], [2,5,5,-2], [1,5,5,-2], + [3,8,8,-3], [3,8,8,-1], [3,9,8,-3], [3,9,8,-1], [3,16,15,-1], [3,16,15,-3], [1,4,3,-3], [1,5,5,-2], + [5,15,14,-2], [5,15,14], [2,5,5,-2], [2,5,5,-3], [4,15,14,-1], [4,15,14], [7,8,8], [9,12,12], + [5,9,9], [8,18,18], [9,8,8], [12,12,12], [9,8,8], [12,12,12], [9,8,8], [12,12,12], + [8,8,8], [7,8,8], [5,9,9], [7,8,8], [7,8,8], [7,8,8], [7,8,8], [7,8,8], + [11,12,12], [10,12,12], [8,18,18], [9,12,12], [9,12,12], [9,12,12], [9,12,12], [9,12,12], + [7,8,8], [10,12,12], [5,2,-4], [8,2,-4], [12,3,-4], [5,1,-5], [8,1,-5], [12,1,-5], + [3,16,15,-1], [2,16,15], [3,15,15,-1], [3,16,15], [3,15,14,-1], [3,15,14], [4,15,14,-1], [4,15,14,-1], + [7,10,10,-1], [7,14,14,-1], [7,19,19,-1], [7,24,24,-1], [5,15,15,-1], [1,5,5,-5], [4,6,5,-5], [3,5,5,-2], + [4,5,5,-1], [4,5,5,-1], [5,3,2,1], [5,3,2,1], [5,3,0,1], [5,3,0,1], [6,5,5], [6,5,5], + [4,19,34,49,64,79,94,109,124,139,154,169,184,199,215,230], + [13,50,87,124,161,198,235,272], + [246,308] + ], + cmbx10: [ + [5,6,0], [7,6,0], [7,7,1], [6,6,0], [6,6,0], [7,6,0], [6,6,0], [7,6,0], + [7,6,0], [7,6,0], [7,6,0], [6,6,0], [5,6,0], [5,6,0], [8,6,0], [8,6,0], + [3,4,0], [4,6,2,1], [2,2,-4,-1], [2,2,-4,-2], [3,2,-4,-1], [3,2,-4,-1], [4,1,-4], [3,2,-4,-2], + [3,2,2,-1], [5,6,0], [7,4,0], [7,4,0], [5,6,1], [8,6,0], [9,7,1], [7,7,1], + [3,2,-2], [2,6,0], [4,4,-2], [7,8,2], [4,7,1], [8,7,1], [7,7,1], [2,3,-3], + [3,8,2], [3,8,2], [4,4,-2], [7,6,1], [2,4,2], [3,2,-1], [2,2,0], [4,8,2], + [5,6,0], [4,6,0], [5,6,0], [5,7,1], [5,6,0], [5,6,0], [5,7,1], [5,6,0], + [5,7,1], [5,6,0], [2,4,0], [2,6,2], [2,6,2], [7,4,0], [4,6,2], [4,6,0], + [7,6,0], [7,6,0], [6,6,0], [7,7,1], [7,6,0], [6,6,0], [6,6,0], [7,7,1], + [7,6,0], [4,6,0], [5,7,1], [7,6,0], [6,6,0], [9,6,0], [7,6,0], [7,7,1], + [6,6,0], [7,8,2], [7,7,1], [5,7,1], [6,6,0], [7,7,1], [7,6,0], [10,6,0], + [7,6,0], [7,6,0], [6,6,0], [2,8,2,-1], [4,4,-2,-1], [2,8,2], [3,2,-4,-1], [2,2,-4], + [2,4,-2], [5,4,0], [5,6,0], [4,4,0], [5,6,0], [4,4,0], [4,6,0], [5,6,2], + [5,6,0], [3,6,0], [4,8,2,1], [5,6,0], [3,6,0], [8,4,0], [5,4,0], [5,4,0], + [5,6,2], [5,6,2], [4,4,0], [4,4,0], [3,5,0], [5,4,0], [5,4,0], [7,4,0], + [5,4,0], [5,6,2], [4,4,0], [5,1,-2], [9,1,-2], [3,2,-4,-1], [3,2,-4,-1], [3,2,-4,-1], + [3,15,27,39,51,63,75,86,98,110,122,134,146,157,169,181], + [11,21,31,41,51,61,71,81], + [194,87] + ], + cmti10: [ + [6,6,0], [6,6,0], [6,7,1,-1], [5,6,0], [6,6,0], [7,6,0], [7,6,0], [6,6,0,-1], + [5,6,0,-1], [6,6,0,-1], [6,6,0], [7,8,2,1], [6,8,2,1], [6,8,2,1], [9,8,2,1], [9,8,2,1], + [3,4,0], [4,6,2,1], [2,2,-4,-2], [3,2,-4,-2], [3,1,-4,-2], [3,2,-4,-2], [3,1,-4,-2], [3,2,-4,-3], + [2,2,2,-1], [6,8,2,1], [6,4,0], [6,4,0], [4,5,1], [8,6,0], [8,7,1,-1], [9,7,1,2], + [3,1,-2], [2,6,0,-1], [3,3,-3,-1], [6,8,2,-1], [6,6,0], [6,7,1,-1], [6,7,1,-1], [1,3,-3,-2], + [3,8,2,-1], [3,8,2], [4,4,-2,-1], [5,6,1,-1], [2,3,2], [3,1,-1], [1,1,0,-1], [5,8,2], + [4,7,1,-1], [3,6,0,-1], [5,7,1], [5,7,1], [4,8,2], [5,7,1], [4,7,1,-1], [4,7,1,-1], + [5,7,1], [5,7,1], [2,4,0,-1], [3,6,2], [3,6,2], [6,2,-1,-1], [4,6,2], [4,6,0,-1], + [6,7,1,-1], [6,6,0], [6,6,0], [6,7,1,-1], [7,6,0], [6,6,0], [6,6,0], [6,7,1,-1], + [7,6,0], [4,6,0], [5,7,1], [7,6,0], [5,6,0], [8,6,0], [7,6,0], [6,7,1,-1], + [6,6,0], [6,8,2,-1], [6,7,1], [5,7,1], [6,6,0,-1], [6,7,1,-1], [6,6,0,-1], [8,6,0,-1], + [9,6,0,2], [6,6,0,-1], [6,6,0], [4,8,2], [3,3,-3,-2], [3,8,2], [2,2,-4,-2], [1,2,-4,-2], + [2,3,-3,-1], [5,4,0], [3,6,0,-1], [4,4,0], [5,6,0], [4,4,0], [5,8,2,1], [4,6,2], + [5,6,0], [3,6,0], [4,8,2,1], [4,6,0], [3,6,0], [7,4,0], [5,4,0], [4,4,0], + [4,6,2], [4,6,2], [4,4,0], [4,5,1], [3,5,0], [5,4,0], [4,4,0], [6,4,0], + [4,4,0], [4,6,2], [4,4,0], [4,1,-2,-1], [7,1,-2,-1], [3,2,-4,-2], [3,2,-4,-2], [3,2,-4,-2], + [3,14,24,34,44,54,64,75,85,95,105,115,126,136,146,156], + [11,21,31,41,51,61,71,81], + [168,87] + ] +}); + +jsMath.Img.AddFont(70,{ + cmr10: [ + [6,7,0], [8,7,0], [8,8,1], [7,7,0], [7,7,0], [7,7,0], [7,7,0], [8,7,0], + [7,7,0], [7,7,0], [7,7,0], [7,7,0], [6,7,0], [6,7,0], [8,7,0], [8,7,0], + [3,5,0], [4,7,2,1], [2,2,-5,-1], [2,2,-5,-2], [3,2,-5,-1], [3,2,-5,-1], [5,1,-5], [3,3,-5,-2], + [3,2,2,-1], [5,7,0], [7,6,1], [8,6,1], [5,7,1], [9,7,0], [10,8,1], [8,9,1], + [3,2,-2], [2,8,0], [4,3,-4], [8,9,2], [5,9,1], [8,9,1], [8,9,1], [2,3,-4], + [3,10,2,-1], [3,10,2], [5,5,-3], [8,7,1], [2,3,2], [3,2,-1], [2,1,0], [5,10,2], + [5,8,1], [3,7,0,-1], [5,7,0], [5,8,1], [5,7,0], [5,8,1], [5,8,1], [5,8,1], + [5,10,1], [5,10,1], [2,5,0], [2,7,2], [2,7,2], [8,3,-1], [5,7,2], [5,9,0], + [8,8,1], [7,7,0], [7,7,0], [7,8,1], [7,7,0], [7,7,0], [6,7,0], [8,8,1], + [7,7,0], [4,7,0], [5,8,1], [8,7,0], [6,7,0], [9,7,0], [7,7,0], [8,8,1], + [7,7,0], [8,9,2], [8,8,1], [5,8,1], [7,7,0], [7,8,1], [8,8,1], [10,8,1], + [10,7,0,2], [8,7,0], [6,7,0], [2,10,2,-1], [4,3,-4,-1], [2,10,2], [3,2,-5,-1], [2,2,-5], + [2,3,-4], [5,5,0], [6,7,0], [4,6,1], [6,8,1], [5,6,1], [4,7,0], [5,7,2], + [6,7,0], [3,7,0], [4,9,2,1], [5,9,0], [3,7,0], [8,9,0], [6,5,0], [5,6,1], + [6,7,2], [6,7,2], [4,5,0], [4,5,0], [4,7,1], [6,6,1], [5,5,0], [7,5,0], + [5,5,0], [5,7,2], [4,5,0], [5,1,-2], [10,1,-2], [4,2,-5,-1], [3,2,-5,-1], [3,2,-5,-1], + [4,16,28,40,52,64,76,88,99,111,123,135,147,159,171,183], + [13,24,36,47,59,71,82,94], + [197,102] + ], + cmmi10: [ + [8,7,0], [8,7,0], [8,8,1], [7,7,0], [8,7,0], [9,7,0], [8,7,0], [7,7,0], + [7,7,0], [7,7,0], [8,7,0], [6,6,1], [6,9,2], [6,7,2], [5,8,0], [4,6,1], + [5,9,2], [5,7,2], [5,8,1], [4,6,1], [6,6,1], [6,7,0], [6,7,2], [6,5,0], + [5,9,2], [6,5,0], [5,7,2], [6,5,0], [5,5,0], [6,6,1], [6,9,2], [6,7,2], + [7,9,2], [6,6,1], [5,6,1], [6,8,1], [8,5,0], [5,7,2], [4,6,1], [7,7,2], + [10,3,-2], [12,3,0,2], [10,3,-2], [10,3,0], [5,3,-2,2], [3,3,-2], [5,5,0], [5,5,0], + [5,6,1], [3,5,0,-1], [5,5,0], [5,7,2], [5,7,2], [5,7,2], [5,8,1], [5,7,2], + [5,8,1], [5,7,2], [2,1,0], [2,3,2], [6,7,1,-1], [5,10,2], [6,7,1,-1], [5,5,0], + [6,9,1], [7,7,0], [8,7,0], [8,8,1], [8,7,0], [8,7,0], [8,7,0], [8,8,1], + [9,7,0], [5,7,0], [7,8,1], [9,7,0], [7,7,0], [11,7,0], [11,7,0,2], [8,8,1], + [8,7,0], [8,9,2], [8,8,1], [7,8,1], [7,7,0], [8,8,1], [8,8,1], [11,8,1], + [11,7,0,2], [8,7,0], [8,7,0], [4,9,1], [4,9,2], [4,9,2], [10,3,-1], [12,3,-1,2], + [4,7,0], [5,6,1], [5,8,1], [5,5,0], [6,8,1], [5,5,0], [6,9,2], [5,7,2], + [6,7,0], [3,7,0], [4,9,2], [5,7,0], [3,7,0], [9,5,0], [6,6,1], [5,5,0], + [6,7,2,1], [5,7,2], [5,5,0], [5,5,0], [4,8,1], [6,6,1], [5,6,1], [7,6,1], + [6,6,1], [5,7,2], [5,5,0], [3,5,0], [5,7,2,1], [7,7,2], [5,2,-5,-2], [5,2,-5,-2], + [4,15,27,39,50,62,74,85,97,108,120,132,143,155,167,178], + [13,24,36,47,59,71,82,94], + [191,102] + ], + cmsy10: [ + [6,1,-2,-1], [2,1,-2], [6,5,0,-1], [5,5,0], [8,7,1], [5,5,0], [8,7,0], [8,7,2], + [8,7,1], [8,7,1], [8,7,1], [8,7,1], [8,7,1], [10,11,3], [5,5,0], [5,5,0], + [8,5,0], [8,5,0], [7,9,2], [6,9,2,-1], [6,9,2,-1], [6,9,2,-1], [6,9,2,-1], [7,9,2], + [8,3,-1], [8,5,0], [7,7,1], [6,7,1,-1], [10,7,1], [10,7,1], [6,7,1,-1], [6,7,1,-1], + [10,5,0], [10,5,0], [5,9,2], [5,9,2], [10,5,0], [10,9,2], [10,9,2], [8,5,0], + [10,5,0], [10,5,0], [6,9,2], [6,9,2], [10,7,1], [10,9,2], [10,9,2], [8,6,1], + [3,6,0], [10,6,1], [6,7,1], [5,7,1,-1], [9,7,0], [9,7,2], [6,9,2,-1], [2,5,0], + [6,8,1], [5,7,0], [7,3,-1], [5,9,1], [7,9,1], [7,8,1], [8,7,0], [8,7,0], + [6,7,0], [8,9,1], [7,8,1], [6,8,1], [8,7,0], [6,8,1], [9,8,1], [6,9,2], + [8,8,1], [7,7,0], [9,9,2], [8,8,1], [7,8,1], [11,8,1], [13,9,1,3], [8,8,1], + [8,8,1], [7,9,2,-1], [9,8,1], [7,8,1], [8,8,0], [8,8,1,1], [7,8,1], [11,8,1], + [8,7,0], [8,9,2], [8,7,0], [7,7,1], [7,7,1], [7,7,1], [6,7,1], [6,7,1], + [6,7,0], [6,7,0], [4,11,3,-1], [3,11,3], [4,11,3,-1], [3,11,3], [3,11,3,-1], [3,11,3,-1], + [3,11,3,-1], [3,11,3], [1,11,3,-1], [3,11,3,-1], [3,11,3,-1], [6,11,3], [5,11,3], [3,7,1], + [9,11,10], [7,7,0], [8,8,1], [5,11,3], [6,6,0], [6,6,0], [7,9,2], [7,9,2], + [4,9,2], [4,9,2], [4,9,2], [6,9,2], [8,10,2], [8,10,2], [8,9,1], [8,10,2], + [4,18,32,46,60,74,88,102,116,130,144,158,172,186,199,213], + [13,33,53,73,93,113,133,152], + [229,169] + ], + cmex10: [ + [3,13,12,-1], [3,13,12], [2,13,12,-2], [3,13,12], [3,13,12,-2], [3,13,12], [3,13,12,-2], [3,13,12], + [4,13,12,-1], [4,13,12,-1], [3,13,12,-1], [4,13,12], [1,8,7,-1], [4,8,7,-1], [6,13,12], [6,13,12], + [5,19,18,-1], [5,19,18], [5,25,24,-2], [6,25,24], [4,25,24,-2], [3,25,24], [4,25,24,-2], [4,25,24], + [4,25,24,-2], [4,25,24], [6,25,24,-1], [6,25,24,-1], [6,25,24,-1], [6,25,24,-1], [10,25,24], [10,25,24], + [6,31,30,-2], [6,31,30], [4,31,30,-2], [4,31,30], [5,31,30,-2], [4,31,30], [5,31,30,-2], [4,31,30], + [6,31,30,-1], [6,31,30,-1], [6,31,30,-1], [6,31,30,-1], [13,31,30], [13,31,30], [8,19,18], [8,19,18], + [7,19,18,-2], [6,19,18], [4,19,18,-3], [4,19,18], [4,19,18,-3], [4,19,18], [1,6,6,-3], [2,6,6,-2], + [5,10,10,-3], [4,10,10,-1], [5,10,9,-3], [4,10,9,-1], [4,20,19,-1], [4,20,19,-3], [2,5,4,-3], [1,6,6,-3], + [7,19,18,-2], [6,19,18], [2,8,7,-2], [2,8,7,-4], [5,19,18,-1], [4,19,18,-1], [8,10,10], [11,14,14], + [6,12,12], [10,23,23], [11,10,10], [15,14,14], [14,10,10,3], [15,14,14], [14,10,10,3], [15,14,14], + [10,10,10], [9,10,10], [6,12,12], [8,10,10], [8,10,10], [8,10,10], [8,10,10], [8,10,10], + [14,14,14], [13,14,14], [10,23,23], [11,14,14], [11,14,14], [11,14,14], [11,14,14], [11,14,14], + [9,10,10], [13,14,14], [6,3,-5], [10,3,-5], [15,3,-5], [9,3,-5,3], [10,2,-6], [15,2,-6], + [3,19,18,-2], [3,19,18], [3,19,18,-2], [3,19,18], [3,19,18,-2], [3,19,18], [5,19,18,-1], [5,19,18,-1], + [10,13,12,-1], [10,19,18,-1], [10,25,24,-1], [10,31,30,-1], [7,19,18,-1], [1,7,6,-7], [4,7,6,-7], [4,6,6,-2], + [5,6,6,-1], [5,6,6,-1], [6,4,2,1], [6,5,3,1], [6,4,0,1], [6,4,0,1], [8,6,6], [8,6,6], + [4,22,39,57,75,92,110,127,145,162,180,198,215,233,250,268], + [15,58,101,145,188,231,274,317], + [287,359] + ], + cmbx10: [ + [7,7,0], [9,7,0], [9,8,1], [8,7,0], [8,7,0], [9,7,0], [8,7,0], [9,7,0], + [8,7,0], [9,7,0], [8,7,0], [8,7,0], [6,7,0], [6,7,0], [10,7,0], [10,7,0], + [3,5,0], [4,7,2,1], [3,2,-5,-1], [3,2,-5,-2], [4,2,-5,-1], [4,2,-5,-1], [5,2,-5], [3,2,-5,-3], + [4,2,2,-1], [6,7,0], [8,5,0], [9,5,0], [6,7,1], [10,7,0], [11,8,1], [12,9,1,3], + [4,2,-2], [2,7,0,-1], [5,4,-3], [9,9,2], [5,9,1], [9,9,1], [9,8,1], [3,4,-3], + [3,10,2,-1], [4,10,2], [5,5,-3], [9,9,2], [3,4,2], [4,2,-1], [3,2,0], [5,10,2], + [6,8,1], [5,7,0], [6,7,0], [6,8,1], [6,7,0], [6,8,1], [6,8,1], [6,7,0], + [6,10,1], [6,10,1], [3,5,0], [3,7,2], [2,7,2,-1], [9,3,-1], [5,7,2], [5,9,0], + [9,7,0], [9,7,0], [8,7,0], [8,8,1], [9,7,0], [8,7,0], [7,7,0], [9,8,1], + [9,7,0], [4,7,0], [6,8,1], [9,7,0], [7,7,0], [11,7,0], [9,7,0], [8,8,1], + [8,7,0], [8,9,2], [9,8,1], [6,8,1], [8,7,0], [9,8,1], [9,7,0], [12,7,0], + [12,7,0,3], [9,7,0], [7,7,0], [2,10,2,-1], [5,4,-3,-1], [2,10,2], [4,2,-5,-1], [3,2,-5], + [3,4,-3], [6,5,0], [6,7,0], [5,5,0], [6,7,0], [5,5,0], [5,7,0], [6,7,2], + [7,7,0], [3,7,0], [4,9,2,1], [6,9,0], [3,7,0], [10,9,0], [7,5,0], [6,5,0], + [6,7,2], [6,7,2], [5,5,0], [5,5,0], [4,7,0], [7,5,0], [6,5,0], [8,5,0], + [6,5,0], [6,7,2], [5,5,0], [6,1,-2], [12,1,-2], [8,5,-2,3], [4,2,-5,-1], [4,2,-5,-1], + [4,18,32,46,59,73,87,101,115,128,142,156,170,184,198,211], + [13,24,36,47,59,71,82,94], + [227,102] + ], + cmti10: [ + [7,7,0], [8,7,0], [7,8,1,-1], [7,7,0], [8,7,0], [9,7,0], [7,7,0,-1], [7,7,0,-2], + [7,7,0,-1], [6,7,0,-2], [7,7,0,-1], [9,9,2,1], [7,9,2,1], [8,9,2,1], [11,9,2,1], [11,9,2,1], + [3,5,0,-1], [5,7,2,1], [2,2,-5,-3], [3,2,-5,-3], [3,2,-5,-3], [4,2,-5,-2], [4,1,-5,-2], [3,3,-5,-4], + [3,2,2,-1], [7,9,2,1], [7,6,1,-1], [7,6,1,-1], [6,7,1], [10,7,0], [10,8,1,-1], [10,9,1,2], + [3,2,-2,-1], [3,8,0,-1], [4,3,-4,-2], [8,9,2,-1], [6,8,1,-1], [8,9,1,-1], [7,9,1,-1], [2,3,-4,-2], + [4,10,2,-1], [4,10,2], [4,5,-3,-2], [7,7,1,-1], [3,3,2], [3,2,-1,-1], [2,1,0,-1], [6,10,2], + [5,8,1,-1], [4,7,0,-1], [5,8,1,-1], [5,8,1,-1], [5,9,2], [5,8,1,-1], [5,8,1,-1], [6,8,1,-1], + [5,10,1,-1], [6,10,1], [2,5,0,-1], [3,7,2], [4,7,2], [7,3,-1,-1], [5,7,2], [6,9,0], + [7,8,1,-1], [7,7,0], [8,7,0], [7,8,1,-1], [8,7,0], [8,7,0], [8,7,0], [7,8,1,-1], + [9,7,0], [5,7,0], [6,8,1,-1], [9,7,0], [7,7,0], [10,7,0], [9,7,0], [7,8,1,-1], + [8,7,0], [7,9,2,-1], [8,8,1], [7,8,1], [7,7,0,-1], [7,8,1,-2], [7,8,1,-2], [9,8,1,-2], + [11,7,0,2], [7,7,0,-2], [7,7,0], [5,10,2], [4,3,-4,-2], [3,10,2,-1], [4,2,-5,-2], [2,2,-5,-2], + [2,3,-4,-2], [5,6,1,-1], [4,8,1,-1], [4,6,1,-1], [5,8,1,-1], [4,6,1,-1], [6,9,2,1], [5,7,2], + [6,7,0], [3,7,0,-1], [5,9,2,1], [5,9,0], [2,7,0,-1], [9,9,0], [5,5,0,-1], [4,6,1,-1], + [5,7,2], [4,7,2,-1], [4,5,0,-1], [5,5,0], [3,7,0,-1], [5,6,1,-1], [4,6,1,-1], [6,6,1,-1], + [6,6,1], [4,7,2,-1], [5,5,0], [5,1,-2,-1], [10,1,-2,-1], [8,5,-2,2], [4,2,-5,-2], [4,2,-5,-2], + [4,16,28,40,51,63,75,87,99,111,123,135,146,158,170,182], + [13,24,36,47,59,71,82,94], + [195,102] + ] +}); + +jsMath.Img.AddFont(85,{ + cmr10: [ + [7,9,0], [10,9,0], [9,10,1], [8,9,0], [8,9,0], [9,9,0], [8,9,0], [9,9,0], + [8,9,0], [9,9,0], [8,9,0], [8,9,0], [7,9,0], [7,9,0], [10,9,0], [10,9,0], + [3,6,0], [4,9,3,1], [3,3,-6,-1], [3,3,-6,-2], [4,2,-6,-1], [4,3,-6,-1], [4,2,-6,-1], [3,3,-6,-3], + [4,3,3,-1], [6,10,1], [9,7,1], [9,7,1], [6,8,1], [11,9,0], [12,10,1], [12,10,1,3], + [3,2,-3], [2,9,0,-1], [5,5,-4], [10,12,3], [6,10,1], [10,10,1], [9,10,1], [2,5,-4,-1], + [3,12,3,-1], [4,12,3], [6,5,-4], [9,8,1], [2,5,3,-1], [4,1,-2], [2,2,0,-1], [6,12,3], + [6,9,1], [4,8,0,-1], [6,8,0], [6,9,1], [6,8,0], [6,9,1], [6,9,1], [6,9,1], + [6,9,1], [6,9,1], [2,6,0,-1], [2,9,3,-1], [2,9,3,-1], [9,4,-1], [5,9,3], [5,9,0], + [9,10,1], [9,9,0], [8,9,0], [8,10,1], [9,9,0], [8,9,0], [8,9,0], [9,10,1], + [9,9,0], [4,9,0], [6,10,1], [9,9,0], [7,9,0], [11,9,0], [9,9,0], [9,10,1], + [8,9,0], [9,12,3], [9,10,1], [6,10,1], [9,9,0], [9,10,1], [9,10,1], [12,10,1], + [9,9,0], [9,9,0], [7,9,0], [2,12,3,-1], [5,5,-4,-1], [2,12,3], [4,2,-6,-1], [2,2,-6,-1], + [3,5,-4], [6,7,1], [7,10,1], [5,7,1], [7,10,1], [5,7,1], [5,9,0], [6,9,3], + [7,9,0], [3,8,0], [4,11,3,1], [6,9,0], [3,9,0], [10,6,0], [7,6,0], [6,7,1], + [7,9,3], [7,9,3], [5,6,0], [5,7,1], [4,9,1], [7,7,1], [6,6,0], [9,6,0], + [7,6,0], [6,9,3], [5,6,0], [6,1,-3], [12,1,-3], [8,6,-3,3], [4,1,-7,-1], [4,2,-6,-1], + [5,19,34,48,63,77,92,106,121,135,150,164,179,193,208,222], + [16,30,45,59,73,87,101,115], + [239,125] + ], + cmmi10: [ + [9,9,0], [10,9,0], [9,10,1], [8,9,0], [10,9,0], [11,9,0], [10,9,0], [9,9,0], + [8,9,0], [9,9,0], [9,9,0,-1], [8,7,1], [7,12,3], [7,9,3], [6,10,1], [5,7,1], + [6,12,3], [6,9,3], [6,9,0], [4,7,1], [7,7,1], [7,10,1], [7,9,3], [7,6,0], + [6,12,3], [7,6,0], [6,9,3], [7,7,1], [6,7,1], [7,7,1], [7,12,3], [7,9,3], + [8,12,3], [8,7,1], [5,7,1], [7,10,1], [10,7,1], [6,9,3], [5,8,2], [8,9,3], + [12,4,-2], [15,4,0,3], [15,4,-2,3], [15,4,0,3], [6,4,-2,3], [3,4,-2], [6,6,0], [6,6,0], + [6,7,1], [4,6,0,-1], [6,6,0], [6,9,3], [6,9,3], [6,9,3], [6,9,1], [6,9,3], + [6,9,1], [6,9,3], [2,2,0,-1], [2,5,3,-1], [8,8,1,-1], [6,12,3], [8,8,1,-1], [6,6,0], + [7,10,1], [9,9,0], [9,9,0], [9,10,1], [10,9,0], [10,9,0], [9,9,0], [9,10,1], + [11,9,0], [6,9,0], [8,10,1], [11,9,0], [8,9,0], [12,9,0], [14,9,0,3], [9,10,1], + [9,9,0], [9,12,3], [9,10,1], [8,10,1], [9,9,0], [9,10,1], [8,10,1,-1], [12,10,1], + [13,9,0,3], [9,9,0], [9,9,0], [4,10,1], [3,12,3,-1], [4,12,3], [12,4,-1], [15,4,-1,3], + [5,10,1], [6,7,1], [5,10,1], [6,7,1], [6,10,1], [5,7,1], [7,12,3], [6,9,3], + [7,9,0], [4,8,0], [6,11,3,1], [6,10,1], [3,10,1], [11,7,1], [7,7,1], [6,7,1], + [7,9,3,1], [6,9,3], [6,6,0], [5,7,1], [4,9,1], [7,7,1], [6,7,1], [9,7,1], + [7,7,1], [6,9,3], [6,7,1], [4,6,0], [6,9,3,1], [8,9,3], [6,3,-6,-2], [5,2,-6,-3], + [5,19,33,47,61,75,89,104,118,132,146,160,174,188,202,216], + [16,30,45,59,73,87,101,115], + [232,125] + ], + cmsy10: [ + [8,2,-2,-1], [2,2,-2,-1], [7,6,0,-1], [6,6,0], [9,8,1], [6,6,0], [9,8,0], [9,8,2], + [9,8,1], [9,8,1], [9,8,1], [9,8,1], [9,8,1], [12,12,3], [6,6,0], [6,6,0], + [9,6,0], [9,6,0], [8,10,2,-1], [8,10,2,-1], [8,10,2,-1], [8,10,2,-1], [8,10,2,-1], [8,10,2,-1], + [9,4,-1], [9,6,0], [8,8,1,-1], [8,8,1,-1], [12,8,1], [12,8,1], [8,8,1,-1], [8,8,1,-1], + [11,4,-1,-1], [11,4,-1], [4,11,3,-1], [4,11,2,-1], [10,4,-1,-1], [12,12,3], [12,12,3], [9,6,0], + [11,8,1,-1], [11,8,1], [7,12,3], [7,12,3], [12,8,1], [12,12,3], [12,12,3], [9,7,1], + [3,7,0], [12,7,1], [6,8,1,-1], [6,8,1,-1], [10,9,0], [10,9,3], [7,12,3,-1], [2,6,0], + [7,10,1], [6,9,0], [8,4,-1], [6,11,1], [9,10,1], [9,10,1], [9,8,0], [9,8,0], + [7,9,0], [10,10,1], [8,10,1], [7,10,1], [10,9,0], [7,10,1], [10,10,1], [8,11,2], + [10,10,1], [9,9,0,1], [10,11,2], [9,10,1], [8,10,1], [14,10,1], [13,11,1,1], [10,10,1], + [9,10,1], [9,11,2,-1], [10,10,1], [8,10,1], [10,9,0], [10,10,1,1], [8,10,1], [13,10,1], + [10,9,0], [9,11,2], [9,9,0], [8,8,1], [8,9,1], [8,8,1], [8,8,1], [8,7,0], + [7,9,0], [7,9,0], [3,12,3,-2], [4,12,3], [3,12,3,-2], [4,12,3], [4,12,3,-1], [4,12,3,-1], + [3,12,3,-1], [4,12,3], [1,12,3,-1], [4,12,3,-1], [4,12,3,-1], [7,14,4], [6,12,3], [3,8,1], + [10,13,12,-1], [9,9,0], [10,10,1], [6,12,3], [8,7,0], [8,8,0], [8,10,2,-1], [9,10,2], + [5,12,3], [5,12,3], [5,12,3], [7,12,3], [9,11,2], [9,11,2], [9,10,1], [9,11,2], + [5,22,39,56,73,90,107,124,141,157,174,191,208,225,242,259], + [17,41,65,90,114,138,162,186], + [278,206] + ], + cmex10: [ + [4,15,14,-1], [4,15,14], [3,15,14,-2], [3,15,14], [4,15,14,-2], [4,15,14], [4,15,14,-2], [4,15,14], + [5,15,14,-1], [5,15,14,-1], [4,15,14,-1], [4,15,14,-1], [2,8,8,-1], [4,9,8,-1], [7,15,14], [7,15,14], + [5,22,21,-2], [5,22,21], [7,30,29,-2], [7,30,29], [4,30,29,-3], [4,30,29], [4,30,29,-3], [4,30,29], + [4,30,29,-3], [4,30,29], [7,30,29,-1], [7,30,29,-1], [7,30,29,-1], [7,30,29,-1], [12,30,29], [12,30,29], + [7,37,36,-2], [7,37,36], [4,37,36,-3], [4,37,36], [5,37,36,-3], [5,37,36], [5,37,36,-3], [5,37,36], + [7,37,36,-1], [7,37,36,-1], [8,37,36,-1], [7,37,36,-1], [15,37,36], [15,37,36], [9,22,21], [9,22,21], + [7,23,22,-3], [7,23,22], [5,23,22,-3], [5,23,22], [5,23,22,-3], [5,23,22], [2,8,8,-3], [2,8,8,-3], + [5,11,11,-4], [4,11,11,-2], [5,12,11,-4], [4,12,11,-2], [4,23,22,-2], [5,23,22,-4], [2,5,4,-4], [2,7,7,-3], + [7,22,21,-3], [7,22,21], [2,9,8,-3], [2,9,8,-5], [6,22,21,-1], [5,22,21,-1], [10,12,12], [13,17,17], + [8,14,14], [12,27,27], [13,12,12], [18,17,17], [13,12,12], [18,17,17], [13,12,12], [18,17,17], + [12,12,12], [11,12,12], [8,14,14], [10,12,12], [10,12,12], [10,12,12], [10,12,12], [10,12,12], + [17,17,17], [15,17,17], [12,27,27], [13,17,17], [13,17,17], [13,17,17], [13,17,17], [13,17,17], + [11,12,12], [15,17,17], [7,3,-6], [12,3,-7], [18,3,-7], [7,2,-7], [12,2,-7], [18,2,-7], + [4,23,22,-2], [3,23,22], [4,23,22,-2], [4,23,22], [4,22,21,-2], [4,23,22], [6,22,21,-1], [6,22,21,-1], + [12,15,14,-1], [12,22,21,-1], [12,30,29,-1], [12,37,36,-1], [8,23,22,-1], [1,9,8,-8], [5,8,7,-8], [4,8,8,-3], + [6,7,7,-1], [6,7,7,-1], [7,5,3,1], [7,5,3,1], [7,4,0,1], [7,4,0,1], [9,8,8], [9,7,7], + [5,27,48,69,91,112,133,155,176,197,219,240,261,283,304,325], + [19,72,124,177,229,281,334,386], + [349,437] + ], + cmbx10: [ + [8,9,0], [11,9,0], [10,10,1], [10,9,0], [9,9,0], [11,9,0], [10,9,0], [10,9,0], + [10,9,0], [10,9,0], [10,9,0], [9,9,0], [8,9,0], [8,9,0], [11,9,0], [11,9,0], + [4,6,0], [5,9,3,1], [3,3,-6,-1], [3,3,-6,-3], [5,2,-6,-1], [5,3,-6,-1], [5,2,-6,-1], [4,3,-6,-3], + [5,3,3,-1], [7,9,0], [10,7,1], [11,7,1], [7,9,2], [12,9,0], [13,10,1,-1], [10,10,1], + [4,2,-3], [2,9,0,-1], [6,5,-4], [11,12,3], [7,10,1], [11,10,1], [10,10,1], [2,5,-4,-1], + [4,12,3,-1], [4,12,3], [5,6,-3,-1], [10,10,2], [2,5,3,-1], [4,2,-2], [2,2,0,-1], [6,12,3], + [7,9,1], [5,8,0,-1], [7,8,0], [7,9,1], [7,8,0], [7,9,1], [7,9,1], [7,9,0], + [7,9,1], [7,9,1], [2,6,0,-1], [2,9,3,-1], [2,9,3,-1], [10,4,-1], [6,9,3], [6,9,0], + [10,10,1], [10,9,0], [9,9,0], [10,10,1], [10,9,0], [9,9,0], [8,9,0], [10,10,1], + [11,9,0], [5,9,0], [7,10,1], [11,9,0], [8,9,0], [13,9,0], [11,9,0], [10,10,1], + [9,9,0], [10,12,3], [11,10,1], [7,10,1], [9,9,0], [10,10,1], [10,9,0], [14,9,0], + [10,9,0], [11,9,0], [8,9,0], [3,12,3,-1], [6,5,-4,-1], [3,12,3], [5,3,-6,-1], [2,3,-6,-1], + [3,5,-4], [7,7,1], [8,9,0], [6,6,0], [8,10,1], [6,7,1], [6,9,0], [7,9,3], + [8,9,0], [4,9,0], [5,12,3,1], [7,9,0], [4,9,0], [12,6,0], [8,6,0], [7,7,1], + [8,9,3], [8,9,3], [6,6,0], [5,7,1], [5,8,0], [8,7,1], [7,6,0], [10,6,0], + [7,6,0], [7,9,3], [6,6,0], [7,1,-3], [14,1,-3], [5,3,-6,-1], [5,2,-7,-1], [5,3,-6,-1], + [5,22,38,55,72,89,106,122,139,156,173,190,206,223,240,257], + [16,30,45,59,73,87,101,115], + [275,125] + ], + cmti10: [ + [9,9,0], [8,9,0,-1], [9,10,1,-1], [8,9,0], [8,9,0,-1], [10,9,0], [9,9,0,-1], [8,9,0,-2], + [8,9,0,-1], [8,9,0,-2], [8,9,0,-1], [10,12,3,1], [9,12,3,1], [9,12,3,1], [12,12,3,1], [13,12,3,1], + [3,7,1,-1], [5,9,3,1], [3,3,-6,-3], [3,3,-6,-4], [4,2,-6,-3], [4,3,-6,-3], [4,2,-6,-3], [3,3,-6,-5], + [3,3,3,-1], [8,12,3,1], [8,7,1,-1], [8,7,1,-1], [6,8,1,-1], [12,9,0], [10,10,1,-2], [13,10,1,3], + [3,2,-3,-1], [4,9,0,-1], [5,5,-4,-2], [9,12,3,-1], [8,10,1,-1], [9,10,1,-1], [9,10,1,-1], [3,5,-4,-2], + [5,12,3,-1], [5,12,3], [5,5,-4,-2], [8,8,1,-1], [2,5,3,-1], [3,1,-2,-1], [2,2,0,-1], [8,12,3], + [6,9,1,-1], [5,8,0,-1], [6,9,1,-1], [6,9,1,-1], [6,11,3], [6,9,1,-1], [6,9,1,-1], [7,9,1,-1], + [6,9,1,-1], [6,9,1,-1], [3,6,0,-1], [3,9,3,-1], [4,9,3], [9,4,-1,-1], [5,9,3,-1], [5,9,0,-2], + [9,10,1,-1], [9,9,0], [9,9,0], [9,10,1,-1], [10,9,0], [9,9,0], [9,9,0], [9,10,1,-1], + [10,9,0], [6,9,0], [7,10,1,-1], [10,9,0], [8,9,0], [12,9,0], [10,9,0], [9,10,1,-1], + [9,9,0], [9,12,3,-1], [9,10,1], [7,10,1,-1], [8,9,0,-2], [8,10,1,-2], [9,10,1,-2], [10,10,1,-2], + [13,9,0,3], [9,9,0,-2], [8,9,0,-1], [5,12,3,-1], [5,5,-4,-3], [6,12,3,1], [4,3,-6,-3], [2,2,-6,-3], + [3,5,-4,-2], [6,7,1,-1], [5,10,1,-1], [5,7,1,-1], [6,10,1,-1], [5,7,1,-1], [7,12,3,1], [6,9,3], + [6,9,0,-1], [3,9,1,-1], [6,11,3,1], [5,10,1,-1], [3,9,0,-1], [9,7,1,-1], [6,7,1,-1], [6,7,1,-1], + [7,9,3], [5,9,3,-1], [5,6,0,-1], [4,7,1,-1], [4,9,1,-1], [6,7,1,-1], [5,7,1,-1], [8,7,1,-1], + [7,7,1], [6,9,3,-1], [6,7,1], [6,1,-3,-1], [11,1,-3,-1], [10,6,-3,3], [4,2,-6,-3], [4,2,-6,-3], + [5,19,34,48,62,77,91,106,120,135,149,163,178,192,207,221], + [16,30,45,59,73,87,101,115], + [237,125] + ] +}); + +jsMath.Img.AddFont(100,{ + cmr10: [ + [9,10,0], [11,10,0], [11,11,1], [10,10,0], [9,10,0], [10,10,0], [10,10,0], [10,10,0], + [10,10,0], [9,10,0,168], [10,10,0], [9,10,0], [8,10,0], [8,10,0], [12,10,0], [12,10,0], + [4,7,0], [4,10,3,1], [3,3,-7,-1], [3,3,-7,-3], [5,2,-7,-1], [5,3,-7,-1], [5,2,-7,-1], [3,3,-7,-4], + [4,3,3,-2], [7,11,1], [10,8,1], [11,8,1], [7,10,2], [13,10,0], [13,11,1,-1], [11,12,1], + [4,2,-4], [2,10,0,-1], [5,5,-5], [11,13,3], [7,12,1], [11,12,1], [11,11,1], [2,5,-5,-1], + [4,15,4,-1], [3,15,4,-1], [5,7,-4,-1], [10,9,1], [2,5,3,-1], [4,2,-2], [2,2,0,-1], [7,15,4], + [7,11,1], [5,10,0,-1], [7,10,0], [7,11,1], [7,10,0], [7,11,1], [7,11,1], [7,11,1], + [7,11,1], [7,11,1], [2,6,0,-1], [2,9,3,-1], [2,10,3,-1], [10,5,-1], [6,10,3], [6,10,0], + [11,11,1], [10,10,0], [10,10,0], [10,11,1], [10,10,0], [9,10,0], [9,10,0], [11,11,1], + [10,10,0], [5,10,0], [7,11,1], [11,10,0], [9,10,0], [13,10,0], [10,10,0], [11,11,1], + [9,10,0], [11,13,3], [11,11,1], [7,11,1], [10,10,0], [10,11,1], [11,11,1], [14,11,1], + [11,10,0], [11,10,0], [8,10,0], [3,15,4,-1], [5,5,-5,-2], [3,15,4], [5,3,-7,-1], [2,2,-8,-1], + [2,5,-5,-1], [7,8,1], [8,11,1], [6,8,1], [8,11,1], [6,8,1], [5,10,0], [7,10,3], + [8,10,0], [4,10,0], [4,13,3,1], [8,10,0], [4,10,0], [12,7,0], [8,7,0], [7,8,1], + [8,10,3], [8,10,3], [5,7,0], [5,8,1], [5,10,1], [8,8,1], [7,6,0], [10,6,0], + [8,6,0], [7,9,3], [6,6,0], [7,1,-3], [14,1,-3], [5,3,-7,-1], [5,2,-8,-1], [5,2,-8,-1], + [6,23,40,57,74,91,108,125,142,159,176,193,210,227,244,262], + [19,36,52,69,85,102,119,135], + [281,146] + ], + cmmi10: [ + [10,10,0], [11,10,0], [11,11,1], [10,10,0], [11,10,0], [13,10,0], [11,10,0,-1], [10,10,0], + [9,10,0], [10,10,0], [10,10,0,-1], [9,8,1], [9,13,3], [8,10,3], [7,11,1], [6,7,1], + [7,13,3], [7,10,3], [7,11,1], [5,8,1], [8,8,1], [8,11,1], [8,9,3], [8,7,0], + [7,13,3], [8,7,1], [7,10,3], [8,7,1], [8,7,1], [8,8,1], [8,13,3], [9,10,3], + [9,13,3], [9,8,1], [6,8,1], [8,11,1], [12,7,1], [6,10,3,-1], [6,9,2], [9,10,3], + [14,4,-3], [14,4,0], [14,4,-3], [17,4,0,3], [3,4,-3], [3,4,-3], [7,7,0], [7,7,0], + [7,8,1], [5,7,0,-1], [7,7,0], [7,10,3], [7,10,3], [7,10,3], [7,11,1], [7,10,3], + [7,11,1], [7,10,3], [2,2,0,-1], [2,5,3,-1], [9,9,1,-1], [7,15,4], [9,9,1,-1], [7,7,0], + [8,11,1], [10,10,0], [11,10,0], [11,11,1], [12,10,0], [11,10,0], [11,10,0], [11,11,1], + [13,10,0], [7,10,0], [8,11,1,-1], [13,10,0], [9,10,0], [15,10,0], [16,10,0,3], [11,11,1], + [11,10,0], [11,13,3], [11,11,1], [9,11,1], [10,10,0], [10,11,1,-1], [11,11,1], [14,11,1], + [15,10,0,3], [11,10,0], [10,10,0], [5,12,1], [4,13,3,-1], [5,13,3], [12,4,-1,-1], [12,4,-2,-1], + [6,11,1], [7,8,1], [6,11,1], [6,8,1], [8,11,1], [6,8,1], [8,13,3], [7,10,3], + [8,11,1], [4,11,1], [7,13,3,1], [7,11,1], [4,11,1], [12,8,1], [8,8,1], [7,8,1], + [8,10,3,1], [7,10,3], [6,8,1], [6,8,1], [5,10,1], [8,8,1], [7,8,1], [10,8,1], + [8,8,1], [7,10,3], [7,8,1], [4,8,1], [6,10,3,1], [8,10,3,-1], [7,3,-7,-2], [6,3,-7,-3], + [6,22,39,55,72,89,105,122,138,155,172,188,205,221,238,255], + [19,36,52,69,85,102,119,135], + [273,146] + ], + cmsy10: [ + [9,1,-3,-1], [2,3,-2,-1], [7,7,0,-2], [5,7,0,-1], [10,9,1], [7,7,0], [10,10,0], [10,10,3], + [11,11,2], [11,11,2], [11,11,2], [11,11,2], [11,11,2], [14,13,3], [7,7,0], [7,7,0], + [10,7,0], [10,7,0], [9,11,2,-1], [9,11,2,-1], [9,11,2,-1], [9,11,2,-1], [9,11,2,-1], [9,11,2,-1], + [11,5,-1], [10,7,0], [9,9,1,-1], [9,9,1,-1], [14,9,1], [14,9,1], [9,9,1,-1], [9,9,1,-1], + [13,5,-1,-1], [13,5,-1], [5,13,3,-1], [5,13,3,-1], [12,5,-1,-1], [14,13,3], [14,13,3], [10,7,0], + [13,9,1,-1], [13,9,1], [8,13,3], [8,13,3], [14,9,1], [14,13,3], [14,13,3], [10,8,1], + [4,8,0], [14,8,1], [7,9,1,-1], [8,9,1,-1], [12,10,0], [12,10,3], [7,13,3,-2], [2,5,-1], + [8,11,1], [7,10,0], [9,4,-1], [7,12,1], [10,11,1], [10,11,1], [9,10,0,-1], [10,10,0], + [8,10,0], [11,11,1], [10,11,1], [8,11,1], [11,10,0], [8,11,1], [12,11,1], [9,12,2], + [12,11,1], [10,10,0,1], [12,12,2], [11,11,1], [9,11,1], [16,11,1], [15,12,1,1], [11,11,1], + [11,11,1], [10,12,2,-1], [12,11,1], [9,11,1], [11,10,0], [11,11,1,1], [10,11,1], [15,11,1], + [12,10,0], [10,12,2], [11,10,0], [9,10,1], [9,10,1], [9,10,1], [8,10,1,-1], [9,10,1], + [8,10,0], [8,10,0], [4,15,4,-2], [4,15,4], [4,15,4,-2], [4,15,4], [5,15,4,-1], [5,15,4,-1], + [4,15,4,-1], [4,15,4], [2,15,4,-1], [5,15,4,-1], [5,15,4,-1], [8,15,4], [7,15,4], [4,11,2], + [11,15,14,-1], [10,10,0], [11,11,1], [7,13,3], [9,9,0], [9,9,0], [9,11,2,-1], [9,11,2,-1], + [5,13,3,-1], [6,13,3], [6,13,3], [8,13,3], [11,13,2], [10,14,3], [11,11,1], [11,13,2], + [6,26,46,66,86,106,125,145,165,185,205,225,245,265,285,305], + [20,48,77,105,134,162,190,219], + [327,243] + ], + cmex10: [ + [4,18,17,-2], [5,18,17], [4,18,17,-2], [3,18,17], [5,18,17,-2], [4,18,17], [5,18,17,-2], [4,18,17], + [6,18,17,-1], [6,18,17,-1], [5,18,17,-1], [5,18,17,-1], [1,10,9,-2], [4,10,9,-2], [8,18,17], [8,17,16], + [6,26,25,-2], [6,26,25], [8,34,33,-2], [8,34,33], [5,34,33,-3], [4,34,33], [5,34,33,-3], [5,34,33], + [5,34,33,-3], [5,34,33], [8,34,33,-1], [8,34,33,-1], [8,34,33,-1], [8,34,33,-1], [14,34,33], [14,34,33], + [8,43,42,-3], [8,43,42], [5,43,42,-3], [5,43,42], [6,43,42,-3], [6,43,42], [6,43,42,-3], [6,43,42], + [8,43,42,-2], [8,43,42,-2], [8,43,42,-2], [9,43,42,-1], [17,43,42], [17,43,42], [11,26,25], [11,26,25], + [8,26,25,-4], [9,26,25], [6,26,25,-4], [5,26,25], [6,26,25,-4], [5,26,25], [2,9,9,-4], [2,9,9,-3], + [5,13,13,-5], [6,13,13,-2], [5,14,13,-5], [6,14,13,-2], [6,27,26,-2], [5,27,26,-5], [3,6,5,-5], [1,9,9,-4], + [8,26,25,-4], [9,26,25], [2,10,9,-4], [3,10,9,-6], [7,26,25,-1], [6,26,25,-1], [11,14,14], [15,20,20], + [9,16,16], [14,31,31], [15,14,14], [21,20,20], [15,14,14], [21,20,20], [15,14,14], [21,20,20], + [14,14,14], [13,14,14], [9,16,16], [11,14,14], [11,14,14], [11,14,14], [11,14,14], [11,14,14], + [20,20,20], [18,20,20], [14,31,31], [15,20,20], [15,20,20], [15,20,20], [15,20,20], [15,20,20], + [13,14,14], [18,20,20], [8,3,-8], [14,3,-8], [21,3,-8], [8,3,-8], [14,3,-8], [20,3,-8], + [4,26,25,-3], [4,26,25], [5,26,25,-3], [5,26,25], [5,26,25,-3], [5,26,25], [7,26,25,-1], [7,26,25,-1], + [14,18,17,-1], [14,26,25,-1], [14,34,33,-1], [14,43,42,-1], [10,26,25,-1], [2,10,9,-9], [6,9,8,-9], [5,9,9,-3], + [7,9,9,-1], [7,9,9,-1], [8,5,3,1], [8,5,3,1], [8,5,0,1], [8,5,0,1], [11,9,9], [11,9,9], + [6,31,56,82,107,132,157,182,207,232,257,282,307,332,358,383], + [23,84,146,208,269,331,392,454], + [410,514] + ], + cmbx10: [ + [9,10,0], [13,10,0], [12,11,1], [11,10,0], [10,10,0], [12,10,0], [10,10,0,-1], [11,10,0,-1], + [11,10,0], [11,10,0,-1], [11,10,0], [11,10,0], [9,10,0], [9,10,0], [13,10,0], [13,10,0], + [4,7,0], [5,10,3,1], [4,3,-7,-1], [4,3,-7,-3], [4,2,-7,-2], [6,3,-7,-1], [6,2,-7,-1], [4,3,-7,-4], + [6,3,3,-1], [8,10,0], [12,8,1], [12,8,1], [8,10,2], [14,10,0], [15,11,1,-1], [12,12,1], + [5,2,-4], [3,10,0,-1], [7,6,-4], [12,13,3,-1], [8,12,1], [13,12,1], [12,11,1], [3,5,-5,-1], + [5,15,4,-1], [4,15,4,-1], [6,7,-4,-1], [11,11,2,-1], [3,6,3,-1], [5,2,-2], [3,3,0,-1], [6,15,4,-1], + [8,11,1], [6,10,0,-1], [8,10,0], [8,11,1], [8,10,0], [8,11,1], [8,11,1], [7,11,1,-1], + [8,11,1], [8,11,1], [3,7,0,-1], [3,10,3,-1], [3,10,3,-1], [11,5,-1,-1], [6,10,3,-1], [6,10,0,-1], + [12,11,1], [12,10,0], [11,10,0], [11,11,1], [12,10,0], [10,10,0], [10,10,0], [12,11,1], + [12,10,0], [6,10,0], [8,11,1], [12,10,0], [9,10,0], [15,10,0], [12,10,0], [12,11,1], + [10,10,0], [12,13,3], [12,11,1], [8,11,1], [11,10,0], [12,11,1], [12,10,0], [17,10,0], + [12,10,0], [12,10,0], [8,10,0,-1], [3,15,4,-1], [6,6,-4,-2], [3,15,4], [4,3,-7,-2], [3,3,-7,-1], + [4,6,-4], [8,7,0], [9,10,0], [7,7,0], [9,11,1], [7,7,0], [7,10,0], [8,10,3], + [9,10,0], [4,10,0], [5,13,3,1], [9,10,0], [5,10,0], [14,7,0], [9,7,0], [8,7,0], + [9,10,3], [9,10,3], [7,7,0], [6,8,1], [6,10,1], [9,7,0], [8,7,0], [12,7,0], + [9,7,0], [8,10,3], [7,7,0], [8,2,-3], [16,2,-3], [5,3,-7,-2], [6,2,-8,-1], [6,3,-7,-1], + [6,26,45,65,85,104,124,144,164,183,203,223,243,262,282,302], + [19,36,52,69,85,102,119,135], + [324,146] + ], + cmti10: [ + [9,10,0,-1], [10,10,0,-1], [9,11,1,-2], [9,10,0], [10,10,0,-1], [11,10,0,-1], [10,10,0,-1], [9,10,0,-3], + [9,10,0,-2], [9,10,0,-3], [10,10,0,-1], [12,13,3,1], [10,13,3,1], [10,13,3,1], [14,13,3,1], [15,13,3,1], + [4,8,1,-1], [6,10,3,1], [2,3,-7,-4], [4,3,-7,-4], [4,2,-7,-4], [4,3,-7,-4], [5,2,-7,-3], [4,3,-7,-6], + [4,3,3,-1], [9,13,3,1], [9,8,1,-1], [9,8,1,-1], [7,10,2,-1], [13,10,0,-1], [13,11,1,-2], [15,12,1,3], + [4,2,-4,-1], [5,10,0,-1], [6,5,-5,-2], [11,13,3,-1], [9,11,1,-1], [10,12,1,-2], [11,11,1,-1], [3,5,-5,-3], + [6,15,4,-2], [6,15,4], [6,7,-4,-2], [9,9,1,-2], [3,5,3,-1], [4,2,-2,-1], [2,2,0,-1], [9,15,4], + [7,11,1,-1], [6,10,0,-1], [7,11,1,-1], [7,11,1,-1], [7,13,3], [7,11,1,-1], [7,11,1,-1], [7,11,1,-2], + [7,11,1,-1], [7,11,1,-1], [4,6,0,-1], [4,9,3,-1], [5,10,3], [10,5,-1,-1], [6,10,3,-1], [6,10,0,-2], + [9,11,1,-2], [9,10,0,-1], [10,10,0,-1], [10,11,1,-2], [10,10,0,-1], [10,10,0,-1], [10,10,0,-1], [10,11,1,-2], + [11,10,0,-1], [7,10,0], [8,11,1,-1], [11,10,0,-1], [8,10,0,-1], [13,10,0,-1], [11,10,0,-1], [9,11,1,-2], + [10,10,0,-1], [9,13,3,-2], [9,11,1,-1], [8,11,1,-1], [10,10,0,-2], [10,11,1,-2], [9,11,1,-3], [12,11,1,-3], + [15,10,0,3], [10,10,0,-3], [9,10,0,-1], [6,15,4,-1], [6,5,-5,-3], [6,15,4,1], [5,3,-7,-3], [2,2,-8,-3], + [3,5,-5,-2], [7,8,1,-1], [6,11,1,-1], [6,8,1,-1], [7,11,1,-1], [6,8,1,-1], [8,13,3,1], [7,10,3], + [7,11,1,-1], [4,11,1,-1], [6,13,3,1], [6,11,1,-1], [4,11,1,-1], [11,8,1,-1], [8,8,1,-1], [7,8,1,-1], + [8,10,3], [6,10,3,-1], [6,8,1,-1], [5,8,1,-1], [5,10,1,-1], [7,8,1,-1], [6,8,1,-1], [9,8,1,-1], + [8,8,1], [7,10,3,-1], [6,8,1,-1], [7,1,-3,-1], [14,1,-3,-1], [11,7,-3,3], [5,2,-8,-3], [5,2,-8,-3], + [6,23,40,56,73,90,107,124,141,158,175,192,209,226,243,260], + [19,36,52,69,85,102,119,135], + [279,146] + ] +}); + +jsMath.Img.AddFont(120,{ + cmr10: [ + [10,12,0], [14,12,0], [12,13,1,-1], [12,13,0], [11,12,0], [13,12,0], [11,12,0,-1], [12,12,0,-1], + [11,12,0,-1], [12,12,0,-1], [12,12,0], [11,12,0], [9,12,0], [9,12,0], [14,12,0], [14,12,0], + [5,8,0], [5,12,4,1], [4,4,-8,-1], [4,4,-8,-3], [5,3,-8,-2], [6,4,-8,-1], [7,1,-9,-1], [4,4,-9,-4], + [5,4,4,-2], [8,13,1], [12,9,1], [13,9,1], [8,11,2], [15,12,0], [16,13,1,-1], [12,14,1,-1], + [5,3,-4], [3,13,0,-1], [6,6,-6], [13,16,4,-1], [7,14,1,-1], [13,14,1,-1], [13,14,1], [3,6,-6,-1], + [5,17,4,-1], [4,17,4,-1], [7,8,-5,-1], [12,12,2,-1], [3,6,4,-1], [5,2,-3], [3,2,0,-1], [7,17,4,-1], + [8,13,1], [7,12,0,-1], [8,12,0], [8,13,1], [8,12,0], [8,13,1], [8,13,1], [8,13,1,-1], + [8,13,1], [8,13,1], [3,8,0,-1], [3,12,4,-1], [3,13,4,-1], [12,5,-2,-1], [6,13,4,-1], [6,12,0,-1], + [12,13,1,-1], [13,13,0], [11,12,0], [11,13,1,-1], [12,12,0], [11,12,0], [11,12,0], [12,13,1,-1], + [13,12,0], [6,12,0], [8,13,1], [13,12,0], [10,12,0], [15,12,0], [13,12,0], [12,13,1,-1], + [11,12,0], [12,16,4,-1], [13,13,1], [8,13,1,-1], [12,12,0], [13,13,1], [13,13,1], [18,13,1], + [13,12,0], [13,12,0], [9,12,0,-1], [3,17,4,-2], [6,6,-6,-2], [3,17,4], [5,3,-9,-2], [3,3,-9,-1], + [3,6,-6,-1], [9,9,1], [9,13,1], [7,9,1], [9,13,1], [7,9,1], [6,12,0], [9,12,4], + [9,12,0], [5,12,0], [5,16,4,1], [9,12,0], [5,12,0], [14,8,0], [9,8,0], [8,9,1], + [9,12,4], [9,12,4], [7,8,0], [7,9,1], [6,12,1], [9,9,1], [9,9,1], [12,9,1], + [9,8,0], [9,12,4], [7,8,0], [9,1,-4], [17,1,-4], [6,4,-8,-2], [6,3,-9,-1], [6,3,-9,-1], + [7,27,48,68,89,109,130,150,171,191,211,232,252,273,293,314], + [22,42,61,81,101,121,141,161], + [337,175] + ], + cmmi10: [ + [13,12,0], [13,13,0,-1], [13,13,1], [12,12,0], [13,12,0,-1], [15,12,0], [13,12,0,-1], [12,12,0], + [11,12,0], [12,12,0], [13,12,0,-1], [11,9,1], [10,16,4], [10,12,4], [8,14,1], [7,9,1], + [8,16,4], [9,12,4], [8,13,1], [5,9,1,-1], [9,9,1,-1], [9,13,1,-1], [10,12,4], [8,8,0,-1], + [8,16,4], [10,9,1], [9,12,4], [10,9,1], [9,9,1], [9,9,1], [10,16,4], [10,12,4], + [11,16,4], [11,9,1], [8,9,1], [10,13,1], [14,9,1], [8,12,4,-1], [7,10,2], [11,12,4], + [15,6,-3,-1], [15,6,1,-1], [15,6,-3,-1], [15,6,1,-1], [3,4,-4,-1], [3,5,-3,-1], [8,9,0], [8,9,0], + [8,9,1], [6,8,0,-1], [8,8,0], [8,12,4], [8,12,4], [8,12,4], [8,13,1], [8,12,4,-1], + [8,13,1], [8,12,4], [3,2,0,-1], [3,6,4,-1], [11,11,1,-1], [7,17,4,-1], [11,11,1,-1], [9,9,0], + [10,14,1], [13,13,0], [13,12,0], [13,13,1], [14,12,0], [13,12,0], [13,12,0], [13,13,1], + [15,12,0], [9,12,0], [10,13,1,-1], [15,12,0], [11,12,0], [18,12,0], [18,12,0,3], [13,13,1], + [13,12,0], [13,16,4], [13,13,1], [10,13,1,-1], [12,12,0], [12,13,1,-1], [12,13,1,-1], [17,13,1,-1], + [18,12,0,3], [13,12,0], [12,12,0,-1], [6,14,1], [5,17,4,-1], [5,16,4,-1], [15,5,-2,-1], [15,5,-2,-1], + [7,13,1], [9,9,1], [7,13,1], [8,9,1], [9,13,1], [8,9,1], [9,16,4,-1], [8,12,4], + [9,13,1,-1], [5,13,1], [8,16,4,1], [8,13,1,-1], [5,13,1], [15,9,1], [10,9,1], [8,9,1], + [10,12,4,1], [8,12,4], [8,9,1], [7,9,1], [6,12,1], [10,9,1], [8,9,1], [12,9,1], + [9,9,1], [9,12,4], [8,9,1], [5,9,1], [8,12,4,1], [10,12,4,-1], [8,4,-8,-3], [7,3,-9,-4], + [7,27,46,66,86,106,126,146,166,186,206,226,246,266,286,306], + [22,42,61,81,101,121,141,161], + [328,175] + ], + cmsy10: [ + [11,2,-3,-1], [3,3,-3,-1], [9,9,0,-2], [7,8,0,-1], [12,10,1,-1], [9,9,0], [12,12,0,-1], [12,12,3,-1], + [12,12,2,-1], [12,12,2,-1], [12,12,2,-1], [12,12,2,-1], [12,12,2,-1], [15,17,4,-1], [7,7,-1,-1], [7,7,-1,-1], + [12,9,0,-1], [12,8,0,-1], [11,14,3,-1], [11,14,3,-1], [11,14,3,-1], [11,14,3,-1], [11,14,3,-1], [11,14,3,-1], + [12,5,-2,-1], [12,8,-1,-1], [11,11,1,-1], [11,11,1,-1], [15,11,1,-1], [15,11,1,-1], [11,11,1,-1], [11,11,1,-1], + [15,7,-1,-1], [15,7,-1,-1], [7,16,4,-1], [7,16,4,-1], [15,7,-1,-1], [15,16,4,-1], [15,16,4,-1], [12,8,0,-1], + [15,10,1,-1], [15,10,1,-1], [10,16,4], [10,16,4], [17,10,1], [15,16,4,-1], [15,16,4,-1], [12,9,1,-1], + [5,10,0], [15,9,1,-1], [9,11,1,-1], [9,11,1,-1], [13,13,0,-1], [13,13,4,-1], [9,17,4,-2], [1,7,-1,-1], + [10,13,1], [8,12,0,-1], [10,5,-1,-1], [8,15,2], [12,14,1], [11,13,1,-1], [12,12,0,-1], [12,12,0,-1], + [9,12,0,-1], [14,14,1], [12,13,1], [9,13,1], [13,12,0], [10,13,1], [14,13,1], [11,14,2], + [14,13,1], [12,12,0,1], [15,14,2], [13,13,1], [11,13,1], [19,13,1], [18,15,1,1], [13,13,1,-1], + [13,13,1], [12,15,3,-2], [14,13,1], [11,13,1], [14,13,0], [13,13,1,1], [12,13,1], [18,13,1], + [14,12,0], [13,15,3], [13,12,0], [10,12,1,-1], [10,12,1,-1], [10,12,1,-1], [10,12,1,-1], [10,12,1,-1], + [9,12,0,-1], [9,12,0,-1], [4,18,5,-3], [5,18,5], [5,18,5,-3], [5,18,5], [7,18,5,-1], [7,18,5,-1], + [5,18,5,-1], [4,18,5,-1], [1,18,5,-2], [5,18,5,-2], [7,18,5,-1], [10,18,5], [7,18,5,-1], [3,12,2,-1], + [14,18,17,-1], [12,12,0], [14,13,1], [7,17,4,-1], [10,11,0,-1], [10,11,0,-1], [11,14,3,-1], [11,14,3,-1], + [6,16,4,-1], [6,16,4,-1], [6,16,4,-1], [9,16,4,-1], [13,16,3], [12,16,3,-1], [12,14,1,-1], [12,16,3,-1], + [7,31,55,79,103,127,151,174,198,222,246,270,294,318,342,366], + [23,57,91,125,159,193,227,261], + [392,290] + ], + cmex10: [ + [5,21,20,-2], [6,21,20], [4,21,20,-3], [4,21,20], [5,21,20,-3], [5,21,20], [5,21,20,-3], [5,21,20], + [6,21,20,-2], [6,21,20,-2], [6,21,20,-1], [6,21,20,-1], [2,12,11,-2], [5,12,11,-2], [8,21,20,-1], [8,21,20,-1], + [7,31,30,-3], [8,31,30], [9,42,41,-3], [9,41,40], [5,42,41,-4], [5,42,41], [6,42,41,-4], [6,42,41], + [6,42,41,-4], [6,42,41], [9,41,40,-2], [9,42,41,-2], [9,41,40,-2], [10,41,40,-1], [16,41,40,-1], [16,41,40,-1], + [9,52,51,-4], [10,52,51], [6,52,51,-4], [6,52,51], [7,52,51,-4], [7,52,51], [7,52,51,-4], [7,52,51], + [10,52,51,-2], [10,52,51,-2], [10,52,51,-2], [11,52,51,-1], [20,52,51,-1], [20,52,51,-1], [12,31,30,-1], [12,31,30,-1], + [10,32,31,-5], [10,32,31], [7,31,30,-5], [6,31,30], [7,31,30,-5], [6,31,30], [2,11,11,-5], [2,11,11,-4], + [7,16,16,-6], [7,16,16,-2], [7,17,16,-6], [6,17,16,-3], [7,32,31,-2], [7,32,31,-6], [3,7,6,-6], [1,11,11,-5], + [10,31,30,-5], [10,31,30], [2,12,11,-5], [2,12,11,-8], [7,31,30,-2], [8,31,30,-1], [13,17,17,-1], [17,24,24,-1], + [10,19,19,-1], [15,38,38,-1], [17,17,17,-1], [24,24,24,-1], [17,17,17,-1], [24,24,24,-1], [17,17,17,-1], [24,24,24,-1], + [16,17,17,-1], [14,17,17,-1], [10,19,19,-1], [13,17,17,-1], [13,17,17,-1], [13,17,17,-1], [13,17,17,-1], [13,17,17,-1], + [23,24,24,-1], [20,24,24,-1], [15,38,38,-1], [17,24,24,-1], [17,24,24,-1], [17,24,24,-1], [17,24,24,-1], [17,24,24,-1], + [14,17,17,-1], [20,24,24,-1], [10,4,-9], [17,4,-9], [25,4,-9], [10,3,-10], [17,3,-10], [25,3,-10], + [5,31,30,-3], [5,31,30], [6,31,30,-3], [6,31,30], [6,31,30,-3], [6,31,30], [8,31,30,-2], [8,31,30,-2], + [16,21,20,-2], [16,31,30,-2], [16,42,41,-2], [16,52,51,-2], [11,32,31,-2], [2,12,11,-11], [8,11,10,-11], [5,11,11,-4], + [9,11,11,-1], [8,10,10,-2], [9,6,4,1], [9,6,4,1], [9,6,0,1], [9,6,0,1], [12,11,11,-1], [12,10,10,-1], + [7,38,68,98,128,158,188,218,248,278,309,339,369,399,429,459], + [26,100,174,248,322,396,470,544], + [492,616] + ], + cmbx10: [ + [11,12,0], [15,12,0,-1], [14,13,1,-1], [13,12,0], [13,12,0], [15,12,0], [12,12,0,-1], [13,12,0,-1], + [12,12,0,-1], [13,12,0,-1], [13,12,0,-1], [13,12,0], [11,12,0], [11,12,0], [16,12,0], [16,12,0], + [5,8,0], [6,12,4,1], [4,4,-8,-2], [4,4,-8,-4], [6,2,-9,-2], [7,4,-8,-1], [8,2,-9,-1], [5,3,-9,-5], + [6,4,4,-2], [10,13,1], [14,9,1], [15,9,1], [10,12,2], [18,12,0], [19,13,1,-1], [14,14,1,-1], + [6,3,-4], [4,12,0,-1], [8,6,-6], [15,16,4,-1], [8,14,1,-1], [15,14,1,-1], [15,13,1], [4,6,-6,-1], + [6,17,4,-1], [5,17,4,-1], [8,8,-5,-1], [13,14,3,-1], [4,7,4,-1], [6,2,-3], [3,3,0,-1], [8,17,4,-1], + [9,13,1], [8,12,0,-1], [8,12,0,-1], [9,13,1], [10,12,0], [8,12,1,-1], [9,13,1], [9,13,1,-1], + [9,13,1], [9,13,1], [3,8,0,-1], [4,12,4,-1], [4,13,4,-1], [13,6,-1,-1], [8,13,4,-1], [8,12,0,-1], + [14,13,1,-1], [14,12,0], [13,12,0], [12,13,1,-1], [14,12,0], [13,12,0], [12,12,0], [14,13,1,-1], + [15,12,0], [7,12,0], [9,13,1], [15,12,0], [11,12,0], [18,12,0], [15,12,0], [13,13,1,-1], + [13,12,0], [13,16,4,-1], [15,13,1], [9,13,1,-1], [13,12,0], [15,13,1], [15,13,1], [20,13,1], + [15,12,0], [15,12,0], [10,12,0,-1], [3,17,4,-2], [8,7,-5,-2], [4,17,4], [6,3,-9,-2], [3,3,-9,-1], + [3,7,-5,-1], [10,9,1], [11,13,1], [8,9,1], [11,13,1], [9,9,1], [8,12,0], [10,12,4], + [11,12,0], [5,12,0], [6,16,4,1], [10,12,0], [5,12,0], [16,8,0], [11,8,0], [10,9,1], + [11,12,4], [11,12,4], [8,8,0], [7,9,1], [7,12,1], [11,9,1], [10,8,0], [14,8,0], + [10,8,0], [10,12,4], [8,8,0], [10,1,-4], [20,1,-4], [7,4,-8,-2], [7,2,-10,-1], [7,3,-9,-1], + [7,31,54,78,102,125,149,173,196,220,244,268,291,315,339,362], + [22,42,61,81,101,121,141,161], + [388,175] + ], + cmti10: [ + [11,12,0,-1], [12,12,0,-1], [12,13,1,-2], [10,13,0,-1], [12,12,0,-1], [14,12,0,-1], [13,12,0,-1], [12,12,0,-3], + [11,12,0,-2], [11,12,0,-3], [12,12,0,-1], [14,16,4,1], [12,16,4,1], [12,16,4,1], [17,16,4,1], [17,16,4,1], + [5,9,1,-1], [7,12,4,1], [3,4,-8,-5], [5,4,-8,-5], [5,3,-8,-4], [6,4,-8,-4], [6,1,-9,-4], [4,4,-9,-8], + [5,4,4,-1], [11,16,4,1], [12,9,1,-1], [12,9,1,-1], [9,11,2,-1], [15,12,0,-1], [16,13,1,-2], [17,14,1,3], + [5,3,-4,-1], [5,13,0,-2], [6,6,-6,-3], [12,16,4,-2], [11,13,1,-1], [13,14,1,-2], [12,14,1,-2], [4,6,-6,-3], + [7,17,4,-2], [7,17,4], [7,8,-5,-3], [11,11,1,-2], [3,6,4,-1], [5,2,-3,-1], [2,2,0,-2], [11,17,4], + [8,13,1,-2], [6,12,0,-2], [9,13,1,-1], [9,13,1,-1], [8,16,4], [9,13,1,-1], [8,13,1,-2], [9,13,1,-2], + [9,13,1,-1], [9,13,1,-1], [4,8,0,-2], [5,12,4,-1], [5,13,4,-1], [12,5,-2,-2], [7,13,4,-1], [7,13,0,-3], + [12,13,1,-2], [11,13,0,-1], [12,12,0,-1], [12,13,1,-2], [13,12,0,-1], [12,12,0,-1], [12,12,0,-1], [12,13,1,-2], + [14,12,0,-1], [8,12,0,-1], [10,13,1,-1], [14,12,0,-1], [10,12,0,-1], [16,12,0,-1], [14,12,0,-1], [12,13,1,-2], + [12,12,0,-1], [12,16,4,-2], [12,13,1,-1], [10,13,1,-1], [11,12,0,-3], [12,13,1,-3], [12,13,1,-3], [16,13,1,-3], + [17,12,0,3], [12,12,0,-3], [11,12,0,-1], [7,17,4,-1], [7,6,-6,-4], [7,17,4,1], [5,3,-9,-4], [3,3,-9,-4], + [3,6,-6,-3], [8,9,1,-1], [6,13,1,-2], [7,9,1,-1], [9,13,1,-1], [6,9,1,-2], [9,16,4,1], [8,12,4,-1], + [8,13,1,-1], [5,12,1,-1], [7,16,4,1], [8,13,1,-1], [5,13,1,-1], [14,9,1,-1], [9,9,1,-1], [8,9,1,-1], + [9,12,4], [8,12,4,-1], [8,9,1,-1], [6,9,1,-1], [6,12,1,-1], [9,9,1,-1], [8,9,1,-1], [11,9,1,-1], + [8,9,1,-1], [8,12,4,-1], [7,9,1,-1], [9,1,-4,-1], [16,1,-4,-2], [13,8,-4,3], [6,3,-9,-4], [6,3,-9,-4], + [7,27,47,68,88,109,129,149,170,190,210,231,251,271,292,312], + [22,42,61,81,101,121,141,161], + [335,175] + ] +}); + +jsMath.Img.AddFont(144,{ + cmr10: [ + [12,14,0], [15,15,0,-1], [14,16,1,-1], [14,15,0], [13,14,0], [15,14,0], [13,14,0,-1], [14,15,0,-1], + [13,14,0,-1], [14,14,0,-1], [13,15,0,-1], [13,15,0], [11,15,0], [11,15,0], [16,15,0], [16,15,0], + [5,9,0], [6,14,5,1], [4,4,-10,-2], [4,4,-10,-4], [6,3,-10,-2], [6,4,-10,-2], [8,1,-11,-1], [5,5,-10,-5], + [6,5,5,-2], [10,16,1], [13,10,1,-1], [15,10,1], [10,13,2], [18,14,0], [19,15,1,-1], [14,16,1,-1], + [6,3,-5], [3,15,0,-1], [7,6,-8], [15,18,4,-1], [8,16,1,-1], [15,17,2,-1], [15,16,1], [4,6,-8,-1], + [5,20,5,-2], [5,20,5,-1], [8,9,-6,-1], [14,14,2,-1], [3,7,4,-1], [6,2,-3], [3,3,0,-1], [8,20,5,-1], + [10,15,1], [8,14,0,-1], [8,14,0,-1], [10,15,1], [10,14,0], [8,15,1,-1], [10,15,1], [9,15,1,-1], + [10,15,1], [10,15,1], [3,9,0,-1], [3,13,4,-1], [3,15,5,-1], [14,6,-2,-1], [8,15,5,-1], [8,15,0,-1], + [14,16,1,-1], [15,15,0], [13,14,0], [13,16,1,-1], [15,14,0], [13,14,0], [13,14,0], [14,16,1,-1], + [15,14,0], [7,14,0], [10,15,1], [15,14,0], [12,14,0], [18,14,0], [15,14,0], [14,16,1,-1], + [13,14,0], [14,19,4,-1], [15,15,1], [9,16,1,-1], [14,14,0], [15,15,1], [15,15,1], [21,15,1], + [15,14,0], [15,14,0], [11,14,0,-1], [4,20,5,-2], [7,7,-7,-3], [4,20,5], [6,3,-11,-2], [3,3,-11,-1], + [3,7,-7,-1], [10,10,1], [11,15,1], [9,10,1], [11,15,1], [9,10,1], [8,15,0], [10,14,5], + [11,14,0], [5,14,0], [6,19,5,1], [11,14,0], [5,14,0], [17,9,0], [11,9,0], [10,10,1], + [11,13,4], [11,13,4], [8,9,0], [8,10,1], [7,14,1], [11,10,1], [11,10,1], [14,10,1], + [11,9,0], [11,13,4], [8,9,0], [10,1,-5], [20,1,-5], [7,4,-10,-2], [8,3,-11,-1], [6,3,-11,-2], + [8,33,57,82,106,131,155,180,205,229,254,278,303,327,352,377], + [26,50,74,98,122,145,169,193], + [404,209] + ], + cmmi10: [ + [15,14,0], [15,15,0,-1], [14,16,1,-1], [14,15,0], [15,14,0,-1], [18,14,0], [15,14,0,-1], [14,14,0], + [13,14,0], [14,14,0], [15,15,0,-1], [12,10,1], [12,18,4], [11,14,5], [9,16,1], [7,10,1,-1], + [10,19,5], [10,14,5], [10,15,1], [6,10,1,-1], [10,10,1,-1], [10,15,1,-1], [12,14,5], [10,9,0,-1], + [9,19,5], [12,10,1], [10,14,5], [12,10,1], [11,10,1], [11,10,1], [11,18,4,-1], [12,14,5], + [13,18,4], [13,10,1], [9,10,1], [12,16,1], [17,10,1], [9,13,4,-1], [9,12,3], [12,14,5,-1], + [18,7,-4,-1], [18,7,1,-1], [18,7,-4,-1], [18,7,1,-1], [4,6,-4,-1], [4,6,-4,-1], [10,10,0], [10,10,0], + [10,10,1], [7,9,0,-2], [8,9,0,-1], [10,14,5], [10,14,4], [8,14,5,-1], [10,15,1], [9,15,5,-1], + [10,15,1], [10,14,5], [3,3,0,-1], [3,7,4,-1], [13,12,1,-1], [8,20,5,-1], [13,12,1,-1], [10,10,0], + [12,16,1], [15,15,0], [16,14,0], [15,16,1,-1], [17,14,0], [16,14,0], [15,14,0], [15,16,1,-1], + [18,14,0], [10,14,0], [12,15,1,-1], [18,14,0], [13,14,0], [21,14,0], [18,14,0], [14,16,1,-1], + [15,14,0], [14,19,4,-1], [15,15,1], [12,16,1,-1], [14,14,0], [15,15,1,-1], [15,15,1,-1], [20,15,1,-1], + [17,14,0], [16,14,0], [14,14,0,-1], [6,16,1,-1], [6,20,5,-1], [6,20,5,-1], [18,6,-2,-1], [18,6,-2,-1], + [8,15,1], [10,10,1], [8,15,1,-1], [9,10,1], [11,15,1], [9,10,1], [10,19,4,-1], [10,14,5], + [10,15,1,-1], [6,15,1], [9,19,5,1], [10,15,1,-1], [6,15,1], [17,10,1], [12,10,1], [10,10,1], + [11,13,4,1], [9,13,4], [9,10,1], [8,10,1,-1], [7,14,1], [11,10,1], [10,10,1], [14,10,1], + [11,10,1], [10,14,5], [9,10,1,-1], [6,10,1], [9,14,5,1], [12,14,5,-1], [10,5,-10,-3], [8,4,-10,-5], + [8,32,56,80,104,128,151,175,199,223,247,271,295,319,343,367], + [26,50,74,98,122,145,169,193], + [393,209] + ], + cmsy10: [ + [13,2,-4,-1], [3,2,-4,-1], [10,10,0,-3], [8,10,0,-1], [14,12,1,-1], [10,10,0], [14,14,0,-1], [14,14,4,-1], + [14,14,2,-1], [14,14,2,-1], [14,14,2,-1], [14,14,2,-1], [14,14,2,-1], [18,20,5,-1], [8,8,-1,-1], [8,8,-1,-1], + [14,10,0,-1], [14,10,0,-1], [13,16,3,-1], [13,16,3,-1], [13,16,3,-1], [13,16,3,-1], [13,16,3,-1], [13,16,3,-1], + [14,6,-2,-1], [14,9,-1,-1], [13,12,1,-1], [13,12,1,-1], [18,14,2,-1], [18,14,2,-1], [13,12,1,-1], [13,12,1,-1], + [18,8,-1,-1], [18,8,-1,-1], [8,18,4,-1], [8,18,4,-1], [18,8,-1,-1], [18,18,4,-1], [18,18,4,-1], [14,10,0,-1], + [18,12,1,-1], [18,12,1,-1], [12,18,4], [12,18,4], [19,12,1], [18,18,4,-1], [18,18,4,-1], [14,10,1,-1], + [6,11,-1], [18,10,1,-1], [11,12,1,-1], [11,12,1,-1], [16,15,0,-1], [16,15,5,-1], [11,20,5,-2], [2,8,-1,-1], + [11,15,1], [9,14,0,-1], [12,7,-1,-1], [9,18,2], [14,16,1,-1], [13,16,1,-1], [14,14,0,-1], [14,14,0,-1], + [11,14,0,-1], [16,16,1], [14,16,1], [11,15,1], [16,14,0], [12,16,1], [17,15,1], [12,18,3], + [17,15,1], [14,14,0,1], [16,17,3,-1], [15,16,1], [13,16,1], [23,15,1], [21,17,1,1], [15,15,1,-1], + [15,15,1], [14,18,3,-2], [17,15,1], [13,16,1], [16,15,0], [15,15,1,1], [14,15,1], [21,15,1], + [16,14,0,-1], [15,17,3], [16,14,0], [12,13,1,-1], [12,13,1,-1], [12,13,1,-1], [11,13,1,-1], [12,13,1,-1], + [10,14,0,-1], [11,14,0,-1], [6,20,5,-3], [6,20,5], [6,20,5,-3], [6,20,5], [8,20,5,-1], [8,20,5,-1], + [5,20,5,-2], [5,20,5,-1], [2,20,5,-2], [6,20,5,-2], [8,22,6,-1], [12,22,6], [8,20,5,-1], [4,14,2,-1], + [16,21,20,-1], [15,14,0], [15,15,1,-1], [9,20,5,-1], [12,12,0,-1], [12,12,0,-1], [14,16,3,-1], [13,16,3,-1], + [7,20,5,-1], [7,19,5,-1], [7,18,4,-1], [11,18,4,-1], [15,18,3], [14,19,4,-1], [14,16,1,-1], [14,18,3,-1], + [8,37,66,95,123,152,181,209,238,267,295,324,353,382,410,439], + [27,68,109,150,191,232,273,314], + [471,348] + ], + cmex10: [ + [6,24,23,-3], [6,24,23,-1], [4,25,24,-4], [5,25,24], [5,25,24,-4], [6,25,24], [5,25,24,-4], [6,25,24], + [8,25,24,-2], [8,25,24,-2], [6,25,24,-2], [7,24,23,-1], [2,14,13,-2], [7,14,13,-2], [10,25,24,-1], [10,24,23,-1], + [9,37,36,-3], [9,37,36], [10,49,48,-4], [11,49,48], [6,49,48,-5], [6,49,48], [7,49,48,-5], [7,49,48], + [7,49,48,-5], [7,49,48], [11,49,48,-2], [11,49,48,-2], [11,49,48,-2], [11,49,48,-2], [19,49,48,-1], [19,49,48,-1], + [11,61,60,-4], [12,61,60], [7,61,60,-5], [7,61,60], [8,61,60,-5], [8,61,60], [8,61,60,-5], [8,61,60], + [12,61,60,-2], [12,61,60,-2], [12,61,60,-2], [12,61,60,-2], [24,61,60,-1], [24,61,60,-1], [14,37,36,-1], [14,37,36,-1], + [12,37,36,-5], [12,37,36], [8,37,36,-6], [7,37,36], [8,37,36,-6], [7,37,36], [2,12,12,-6], [2,12,12,-5], + [8,19,19,-7], [8,19,19,-3], [8,19,18,-7], [8,19,18,-3], [8,38,37,-3], [8,38,37,-7], [4,8,7,-7], [2,12,12,-6], + [12,37,36,-5], [12,37,36], [3,14,13,-5], [3,14,13,-9], [9,36,35,-2], [9,37,36,-1], [15,20,20,-1], [21,28,28,-1], + [12,23,23,-1], [18,45,45,-1], [20,20,20,-1], [28,28,28,-1], [20,20,20,-1], [28,28,28,-1], [20,20,20,-1], [28,28,28,-1], + [19,20,20,-1], [17,20,20,-1], [12,23,23,-1], [15,20,20,-1], [15,20,20,-1], [15,20,20,-1], [15,20,20,-1], [15,20,20,-1], + [27,28,28,-1], [24,28,28,-1], [18,45,45,-1], [21,28,28,-1], [21,28,28,-1], [21,28,28,-1], [20,28,28,-1], [20,28,28,-1], + [17,20,20,-1], [24,28,28,-1], [12,4,-11], [20,5,-11], [29,5,-11], [11,3,-12], [20,3,-12], [29,3,-12], + [5,37,36,-4], [5,37,36], [7,37,36,-4], [6,37,36], [7,37,36,-4], [6,37,36], [9,37,36,-2], [9,37,36,-2], + [19,25,24,-2], [19,37,36,-2], [19,49,48,-2], [19,61,60,-2], [13,37,36,-2], [1,14,13,-14], [8,13,12,-14], [6,12,12,-5], + [9,12,12,-2], [9,12,12,-2], [11,8,5,1], [11,8,5,1], [11,7,0,1], [11,7,0,1], [14,12,12,-1], [14,12,12,-1], + [9,45,81,117,154,190,226,262,298,334,370,406,443,479,515,551], + [31,120,209,297,386,475,564,652], + [591,739] + ], + cmbx10: [ + [13,14,0], [17,14,0,-1], [16,15,1,-1], [16,14,0], [14,14,0,-1], [18,14,0], [15,14,0,-1], [16,14,0,-1], + [15,14,0,-1], [16,14,0,-1], [15,14,0,-1], [15,14,0], [12,14,0], [12,14,0], [19,14,0], [19,14,0], + [5,9,0,-1], [8,13,4,2], [5,4,-10,-2], [5,4,-10,-5], [7,3,-10,-2], [8,4,-10,-2], [9,2,-11,-1], [5,4,-10,-6], + [7,4,4,-2], [12,15,1], [16,10,1], [18,10,1], [11,13,2], [21,14,0], [22,15,1,-1], [16,16,1,-1], + [7,3,-5], [3,15,0,-2], [10,7,-7], [17,18,4,-1], [10,17,2,-1], [17,17,2,-1], [16,15,1,-1], [4,7,-7,-1], + [6,20,5,-2], [6,20,5,-1], [9,9,-6,-1], [16,16,3,-1], [4,8,4,-1], [7,3,-3], [4,4,0,-1], [10,20,5,-1], + [11,15,1], [9,13,0,-1], [10,14,0,-1], [10,15,1,-1], [11,14,0], [10,14,1,-1], [10,15,1,-1], [11,15,1,-1], + [10,15,1,-1], [10,15,1,-1], [4,9,0,-1], [4,13,4,-1], [3,15,5,-2], [16,6,-2,-1], [9,14,4,-1], [9,14,0,-1], + [16,15,1,-1], [17,14,0], [15,14,0], [15,15,1,-1], [17,14,0], [15,14,0], [14,14,0], [16,15,1,-1], + [18,14,0], [8,14,0], [11,15,1], [17,14,0], [13,14,0], [21,14,0], [18,14,0], [15,15,1,-1], + [15,14,0], [16,18,4,-1], [18,15,1], [11,15,1,-1], [16,14,0], [17,15,1], [17,15,1], [24,15,1], + [17,14,0], [17,14,0], [12,14,0,-1], [4,20,5,-2], [10,8,-6,-2], [4,20,5], [7,4,-10,-2], [4,4,-10,-1], + [4,8,-6,-1], [12,10,1], [12,15,1], [10,10,1], [12,15,1], [10,10,1], [9,14,0], [12,13,4], + [13,14,0], [5,14,0,-1], [8,18,4,2], [12,14,0], [5,14,0,-1], [19,9,0], [13,9,0], [11,10,1], + [12,13,4], [12,13,4], [9,9,0], [9,10,1], [8,14,1], [13,10,1], [12,9,0], [17,9,0], + [12,9,0], [12,13,4], [10,9,0], [12,1,-5], [23,1,-5], [7,5,-10,-3], [8,3,-11,-2], [8,3,-11,-2], + [8,37,65,94,122,150,179,207,236,264,293,321,349,378,406,435], + [26,50,74,98,122,145,169,193], + [466,209] + ], + cmti10: [ + [13,14,0,-1], [14,15,0,-1], [13,16,1,-3], [12,15,0,-1], [14,14,0,-1], [16,14,0,-1], [15,14,0,-1], [13,15,0,-4], + [12,14,0,-3], [13,14,0,-4], [14,15,0,-2], [17,19,4,1], [13,19,4,1], [14,19,4,1], [20,20,5,1], [20,20,5,1], + [6,10,1,-1], [8,13,4,1], [4,4,-10,-5], [5,4,-10,-6], [6,3,-10,-5], [7,4,-10,-5], [8,1,-11,-4], [5,5,-10,-9], + [5,4,4,-2], [13,19,4,1], [14,10,1,-1], [13,10,1,-2], [10,13,2,-1], [18,14,0,-1], [18,16,1,-3], [15,17,2,-2], + [5,3,-5,-2], [6,15,0,-2], [8,6,-8,-3], [15,18,4,-2], [13,16,1,-1], [14,17,2,-3], [14,16,1,-2], [4,6,-8,-4], + [8,20,5,-3], [8,20,5], [8,9,-6,-4], [12,13,2,-3], [4,7,4,-1], [6,2,-3,-1], [3,3,0,-2], [13,20,5], + [10,15,1,-2], [8,14,0,-2], [10,15,1,-1], [11,15,1,-1], [9,18,4,-1], [10,15,1,-2], [10,15,1,-2], [10,15,1,-3], + [9,15,1,-2], [9,15,1,-2], [4,9,0,-2], [5,13,4,-1], [6,15,5,-1], [14,6,-2,-2], [8,15,5,-1], [8,15,0,-3], + [13,16,1,-3], [13,15,0,-1], [14,14,0,-1], [14,16,1,-3], [15,14,0,-1], [14,14,0,-1], [14,14,0,-1], [14,16,1,-3], + [16,14,0,-1], [9,14,0,-1], [12,15,1,-1], [17,14,0,-1], [12,14,0,-1], [19,14,0,-1], [16,14,0,-1], [13,16,1,-3], + [14,14,0,-1], [13,19,4,-3], [14,15,1,-1], [12,16,1,-1], [13,14,0,-3], [13,15,1,-4], [14,15,1,-4], [18,15,1,-4], + [21,14,0,4], [14,14,0,-4], [13,14,0,-1], [8,20,5,-1], [8,7,-7,-5], [9,20,5,1], [6,4,-10,-5], [3,3,-11,-5], + [4,6,-8,-4], [9,10,1,-2], [8,15,1,-2], [8,10,1,-2], [10,15,1,-2], [8,10,1,-2], [10,19,4,1], [9,14,5,-1], + [10,15,1,-1], [6,14,1,-1], [9,18,4,1], [9,15,1,-1], [5,15,1,-1], [16,10,1,-1], [11,10,1,-1], [9,10,1,-2], + [11,13,4], [8,13,4,-2], [9,10,1,-1], [8,10,1,-1], [7,14,1,-1], [11,10,1,-1], [9,10,1,-1], [13,10,1,-1], + [10,10,1,-1], [10,13,4,-1], [9,10,1,-1], [9,1,-5,-2], [19,1,-5,-2], [7,4,-10,-5], [7,3,-11,-5], [6,3,-11,-5], + [8,32,57,81,106,130,155,179,204,228,252,277,301,326,350,375], + [26,50,74,98,122,145,169,193], + [402,209] + ] +}); + +jsMath.Img.AddFont(173,{ + cmr10: [ + [14,17,0], [18,18,0,-1], [17,18,1,-1], [16,18,0], [14,17,0,-1], [18,17,0], [15,17,0,-1], [17,17,0,-1], + [15,17,0,-1], [17,17,0,-1], [16,17,0,-1], [15,17,0], [13,17,0], [13,17,0], [20,17,0], [20,17,0], + [6,11,0], [6,16,5,1], [5,5,-12,-2], [5,5,-12,-5], [6,4,-12,-3], [8,5,-12,-2], [10,2,-13,-1], [6,5,-13,-6], + [6,5,5,-3], [12,18,1], [16,12,1,-1], [18,12,1], [12,16,3], [21,17,0], [23,18,1,-1], [17,20,2,-1], + [7,4,-6], [3,18,0,-2], [9,8,-9], [18,22,5,-1], [10,20,2,-1], [18,20,2,-1], [17,19,1,-1], [3,8,-9,-2], + [6,24,6,-2], [6,24,6,-1], [10,11,-7,-1], [17,16,2,-1], [3,8,5,-2], [7,2,-4], [3,3,0,-2], [10,24,6,-1], + [11,17,1], [8,16,0,-2], [10,16,0,-1], [10,17,1,-1], [12,17,0], [10,17,1,-1], [10,17,1,-1], [11,18,1,-1], + [10,17,1,-1], [10,17,1,-1], [3,11,0,-2], [3,16,5,-2], [3,18,6,-2], [17,6,-3,-1], [9,17,5,-1], [9,17,0,-1], + [17,18,1,-1], [18,18,0], [16,17,0], [15,18,1,-1], [17,17,0], [16,17,0], [15,17,0], [17,18,1,-1], + [18,17,0], [8,17,0], [11,18,1,-1], [18,17,0], [14,17,0], [22,17,0], [18,17,0], [17,18,1,-1], + [15,17,0], [17,22,5,-1], [18,18,1], [11,18,1,-1], [17,17,0], [18,18,1], [18,18,1], [25,18,1], + [18,17,0], [18,17,0], [13,17,0,-1], [5,24,6,-2], [9,8,-9,-3], [4,24,6], [6,4,-13,-3], [3,3,-13,-2], + [4,8,-9,-1], [11,12,1,-1], [13,18,1], [10,12,1], [13,18,1], [10,12,1], [9,17,0], [12,16,5], + [13,17,0], [6,16,0], [6,21,5,1], [13,17,0], [7,17,0], [20,11,0], [13,11,0], [12,12,1], + [13,16,5], [13,16,5], [9,11,0], [9,12,1], [8,16,1], [13,12,1], [13,12,1], [17,12,1], + [13,11,0], [13,16,5], [10,11,0], [12,1,-6], [24,1,-6], [7,5,-12,-3], [8,3,-13,-2], [8,3,-13,-2], + [10,39,69,98,128,157,187,216,246,275,305,334,364,393,423,453], + [32,61,90,118,147,176,204,233], + [485,253] + ], + cmmi10: [ + [17,17,0,-1], [18,18,0,-1], [17,18,1,-1], [16,18,0], [18,17,0,-1], [20,17,0,-1], [19,17,0,-1], [17,17,0], + [16,17,0], [17,17,0], [18,17,0,-1], [14,12,1,-1], [15,22,5], [13,16,5], [10,18,1,-1], [8,12,1,-1], + [11,22,5,-1], [12,17,6], [10,18,1,-1], [7,12,1,-1], [12,12,1,-1], [12,18,1,-1], [14,17,6], [12,11,0,-1], + [11,22,5], [14,12,1], [12,17,6], [14,12,1], [13,12,1], [13,12,1], [13,22,5,-1], [15,16,5], + [16,22,5], [15,12,1], [11,12,1], [14,18,1], [20,12,1], [11,16,5,-1], [10,14,3], [14,17,6,-1], + [22,8,-5,-1], [22,8,1,-1], [22,8,-5,-1], [22,8,1,-1], [5,7,-5,-1], [5,7,-5,-1], [12,12,0], [12,12,0], + [10,12,1,-1], [8,11,0,-2], [10,11,0,-1], [10,17,6,-1], [12,17,5], [10,17,6,-1], [10,17,1,-1], [11,17,6,-1], + [10,17,1,-1], [10,17,6,-1], [3,3,0,-2], [3,8,5,-2], [15,14,1,-2], [10,24,6,-1], [15,14,1,-2], [12,12,0], + [13,19,1,-1], [18,18,0], [18,17,0,-1], [18,18,1,-1], [19,17,0,-1], [18,17,0,-1], [17,17,0,-1], [18,18,1,-1], + [20,17,0,-1], [12,17,0], [14,18,1,-1], [21,17,0,-1], [15,17,0,-1], [24,17,0,-1], [20,17,0,-1], [17,18,1,-1], + [18,17,0,-1], [17,22,5,-1], [17,18,1,-1], [15,18,1,-1], [17,17,0], [18,18,1,-1], [18,18,1,-1], [24,18,1,-1], + [25,17,0,4], [18,17,0,-1], [17,17,0,-1], [7,19,1,-1], [7,24,6,-1], [7,24,6,-1], [22,6,-3,-1], [22,7,-3,-1], + [10,18,1], [11,12,1,-1], [9,18,1,-1], [10,12,1,-1], [12,18,1,-1], [10,12,1,-1], [13,22,5,-1], [12,16,5], + [12,18,1,-1], [7,17,1], [11,21,5,1], [12,18,1,-1], [6,18,1,-1], [21,12,1], [14,12,1], [11,12,1,-1], + [13,16,5,1], [10,16,5,-1], [11,12,1], [9,12,1,-1], [8,16,1], [13,12,1], [12,12,1], [17,12,1], + [13,12,1], [12,16,5], [11,12,1,-1], [7,12,1], [10,16,5,1], [14,17,6,-1], [11,5,-12,-4], [10,3,-13,-6], + [10,38,67,96,124,153,182,211,239,268,297,326,354,383,412,440], + [32,61,90,118,147,176,204,233], + [472,253] + ], + cmsy10: [ + [15,2,-5,-2], [3,4,-4,-2], [12,12,0,-3], [10,11,0,-1], [17,14,1,-1], [12,12,0], [17,16,0,-1], [17,16,4,-1], + [17,16,2,-1], [17,16,2,-1], [17,16,2,-1], [17,16,2,-1], [17,16,2,-1], [22,24,6,-1], [10,10,-1,-1], [10,10,-1,-1], + [17,12,0,-1], [17,12,0,-1], [15,20,4,-2], [15,20,4,-2], [15,20,4,-2], [15,20,4,-2], [15,20,4,-2], [15,20,4,-2], + [17,6,-3,-1], [17,11,-1,-1], [15,14,1,-2], [15,14,1,-2], [22,16,2,-1], [22,16,2,-1], [15,14,1,-2], [15,14,1,-2], + [22,10,-1,-1], [22,10,-1,-1], [10,22,5,-1], [10,22,5,-1], [22,10,-1,-1], [22,22,5,-1], [22,22,5,-1], [17,12,0,-1], + [22,14,1,-1], [22,14,1,-1], [14,22,5], [14,22,5], [22,14,1,-1], [22,22,5,-1], [22,22,5,-1], [17,12,1,-1], + [7,13,-1], [22,12,1,-1], [12,14,1,-2], [12,14,1,-2], [19,18,0,-1], [19,18,6,-1], [13,24,6,-3], [2,10,-1,-1], + [14,18,1], [11,17,0,-1], [14,7,-2,-1], [10,21,2,-1], [16,19,1,-1], [16,18,1,-1], [17,16,0,-1], [17,16,0,-1], + [13,17,0,-1], [19,20,2], [16,18,1], [13,18,1], [19,17,0], [14,18,1], [20,18,1], [14,20,3,-1], + [20,19,2], [17,17,0,1], [19,20,3,-1], [18,18,1], [16,18,1], [27,19,2], [25,21,2,1], [18,18,1,-1], + [18,19,2], [17,20,3,-2], [20,18,1], [16,18,1], [19,18,0], [18,18,1,1], [15,18,1,-1], [24,18,1,-1], + [19,17,0,-1], [18,21,4], [18,17,0,-1], [14,16,1,-1], [14,16,1,-1], [14,16,1,-1], [14,16,1,-1], [14,16,1,-1], + [13,17,0,-1], [13,17,0,-1], [6,24,6,-4], [7,24,6], [6,24,6,-4], [7,24,6], [9,24,6,-2], [9,24,6,-1], + [6,24,6,-2], [6,24,6,-1], [2,24,6,-2], [6,24,6,-3], [10,26,7,-1], [14,26,7], [10,24,6,-1], [5,16,2,-1], + [20,24,23,-1], [17,17,0], [18,18,1,-1], [11,24,6,-1], [14,15,0,-1], [14,15,0,-1], [16,20,4,-2], [16,20,4,-1], + [8,22,5,-1], [9,23,6,-1], [9,22,5,-1], [13,22,5,-1], [18,22,4], [17,22,4,-1], [17,19,1,-1], [17,22,4,-1], + [10,45,79,114,148,183,217,252,286,321,355,390,424,459,493,528], + [34,83,132,181,230,279,329,378], + [565,419] + ], + cmex10: [ + [7,29,28,-3], [7,29,28,-1], [6,29,28,-4], [6,29,28], [7,29,28,-4], [7,29,28], [7,29,28,-4], [7,29,28], + [10,29,28,-2], [10,29,28,-2], [8,29,28,-2], [8,29,28,-1], [2,16,15,-3], [7,16,15,-3], [12,29,28,-1], [12,29,28,-1], + [10,44,43,-4], [9,44,43,-1], [12,58,57,-5], [12,58,57,-1], [7,58,57,-6], [7,58,57], [8,58,57,-6], [8,58,57], + [8,58,57,-6], [8,58,57], [12,58,57,-3], [12,58,57,-3], [13,58,57,-3], [13,58,57,-2], [23,58,57,-1], [23,58,57,-1], + [14,72,71,-5], [14,72,71], [8,72,71,-6], [8,72,71], [9,72,71,-6], [9,72,71], [9,72,71,-6], [9,72,71], + [13,72,71,-3], [13,72,71,-3], [14,72,71,-3], [14,72,71,-2], [29,72,71,-1], [29,72,71,-1], [17,44,43,-1], [17,44,43,-1], + [14,44,43,-7], [14,44,43], [9,44,43,-7], [9,44,43], [9,44,43,-7], [9,44,43], [3,15,15,-7], [3,15,15,-6], + [9,22,22,-9], [9,22,22,-4], [9,23,22,-9], [9,23,22,-4], [9,45,44,-4], [9,45,44,-9], [4,9,8,-9], [2,15,15,-7], + [13,45,43,-7], [14,45,43], [3,16,15,-7], [3,16,15,-11], [11,44,43,-2], [10,44,43,-2], [18,24,24,-1], [25,34,34,-1], + [14,27,27,-1], [22,54,54,-1], [25,24,24,-1], [34,34,34,-1], [25,24,24,-1], [34,34,34,-1], [25,24,24,-1], [34,34,34,-1], + [23,24,24,-1], [21,24,24,-1], [14,27,27,-1], [18,24,24,-1], [18,24,24,-1], [18,24,24,-1], [18,24,24,-1], [18,24,24,-1], + [33,34,34,-1], [29,34,34,-1], [22,54,54,-1], [25,34,34,-1], [25,34,34,-1], [25,34,34,-1], [25,34,34,-1], [25,34,34,-1], + [21,24,24,-1], [29,34,34,-1], [14,5,-13], [24,6,-13], [35,6,-13], [14,4,-14], [24,3,-15], [35,3,-15], + [6,44,43,-5], [6,44,43], [8,44,43,-5], [8,44,43], [8,44,43,-5], [8,44,43], [12,44,43,-2], [12,44,43,-2], + [23,29,28,-2], [23,44,43,-2], [23,58,57,-2], [23,72,71,-2], [16,45,44,-2], [2,16,15,-16], [10,15,14,-16], [7,15,15,-6], + [12,15,15,-2], [12,15,15,-2], [12,9,6,1], [13,9,6,1], [12,8,0,1], [13,8,0,1], [17,15,15,-1], [17,15,15,-1], + [11,54,98,141,184,228,271,315,358,401,445,488,532,575,619,662], + [39,145,252,358,465,571,678,785], + [709,888] + ], + cmbx10: [ + [16,17,0], [21,17,0,-1], [19,18,1,-1], [18,17,0,-1], [17,17,0,-1], [21,17,0], [18,17,0,-1], [19,17,0,-1], + [18,17,0,-1], [19,17,0,-1], [18,17,0,-1], [18,17,0], [15,17,0], [15,17,0], [23,17,0], [23,17,0], + [6,11,0,-1], [9,16,5,2], [6,5,-12,-2], [6,5,-12,-5], [8,4,-12,-3], [10,5,-12,-2], [10,2,-13,-2], [7,5,-12,-7], + [8,5,5,-3], [14,18,1], [20,12,1], [21,12,1], [13,17,3], [24,17,0,-1], [26,18,1,-2], [19,20,2,-1], + [8,4,-6], [4,17,0,-2], [11,9,-8,-1], [21,22,5,-1], [12,20,2,-1], [21,20,2,-1], [19,18,1,-1], [5,9,-8,-2], + [8,24,6,-2], [8,24,6,-1], [11,11,-7,-1], [19,20,4,-1], [4,9,5,-2], [8,3,-4], [4,4,0,-2], [12,24,6,-1], + [12,17,1,-1], [10,16,0,-2], [12,16,0,-1], [12,17,1,-1], [13,16,0], [12,17,1,-1], [12,17,1,-1], [13,18,1,-1], + [12,17,1,-1], [12,17,1,-1], [4,11,0,-2], [4,16,5,-2], [4,17,5,-2], [19,8,-2,-1], [11,17,5,-1], [11,17,0,-1], + [19,18,1,-1], [19,17,0,-1], [19,17,0], [18,18,1,-1], [20,17,0], [18,17,0], [17,17,0], [20,18,1,-1], + [21,17,0], [10,17,0], [13,18,1], [21,17,0], [16,17,0], [25,17,0,-1], [21,17,0], [19,18,1,-1], + [18,17,0], [19,22,5,-1], [21,18,1], [13,18,1,-1], [18,17,0,-1], [20,18,1,-1], [21,18,1], [28,18,1], + [20,17,0], [21,17,0], [15,17,0,-1], [4,24,6,-3], [11,9,-8,-3], [5,24,6], [8,5,-12,-3], [4,4,-13,-2], + [5,9,-8,-1], [14,12,1], [15,18,1], [12,12,1], [15,18,1], [12,12,1], [10,17,0,-1], [14,16,5], + [14,17,0,-1], [6,17,0,-1], [9,22,5,2], [15,17,0], [6,17,0,-1], [22,11,0,-1], [14,11,0,-1], [13,12,1], + [15,16,5], [15,16,5], [11,11,0], [10,12,1], [10,17,1], [14,12,1,-1], [14,12,1], [20,12,1], + [14,11,0], [14,16,5], [11,11,0], [14,1,-6], [28,1,-6], [9,5,-12,-3], [10,3,-14,-2], [10,4,-13,-2], + [10,44,78,112,147,181,215,249,283,317,352,386,420,454,488,522], + [32,61,90,118,147,176,204,233], + [560,253] + ], + cmti10: [ + [16,17,0,-1], [17,18,0,-1], [16,18,1,-3], [15,18,0,-1], [17,17,0,-1], [20,17,0,-1], [17,17,0,-2], [15,17,0,-5], + [15,17,0,-3], [15,17,0,-5], [17,17,0,-2], [20,22,5,1], [16,22,5,1], [17,22,5,1], [23,22,5,1], [24,22,5,1], + [6,12,1,-2], [9,16,5,1], [4,5,-12,-7], [6,5,-12,-8], [7,3,-12,-6], [8,5,-12,-6], [9,2,-13,-5], [5,5,-13,-11], + [6,5,5,-2], [15,22,5,1], [16,12,1,-2], [16,12,1,-2], [13,16,3,-1], [22,17,0,-1], [23,18,1,-3], [24,20,2,4], + [7,4,-6,-2], [7,18,0,-2], [9,8,-9,-4], [18,22,5,-2], [15,18,1,-2], [18,20,2,-3], [17,19,1,-3], [4,8,-9,-5], + [10,24,6,-3], [10,24,6], [10,11,-7,-4], [15,16,2,-3], [5,8,5,-1], [7,2,-4,-2], [4,3,0,-2], [15,24,6], + [12,17,1,-2], [9,16,0,-2], [12,17,1,-2], [12,17,1,-2], [11,21,5,-1], [12,17,1,-2], [12,17,1,-2], [12,17,1,-3], + [12,17,1,-2], [12,17,1,-2], [6,11,0,-2], [7,16,5,-1], [7,18,6,-1], [17,6,-3,-2], [9,18,6,-2], [10,18,0,-4], + [16,18,1,-3], [16,18,0,-1], [17,17,0,-1], [17,18,1,-3], [18,17,0,-1], [17,17,0,-1], [17,17,0,-1], [17,18,1,-3], + [20,17,0,-1], [11,17,0,-1], [13,18,1,-2], [20,17,0,-1], [14,17,0,-1], [23,17,0,-1], [20,17,0,-1], [16,18,1,-3], + [17,17,0,-1], [16,22,5,-3], [17,18,1,-1], [15,18,1,-1], [16,17,0,-4], [17,18,1,-4], [16,18,1,-5], [22,18,1,-5], + [24,17,0,4], [16,17,0,-5], [15,17,0,-2], [10,24,6,-1], [9,8,-9,-6], [10,24,6,1], [7,4,-13,-6], [3,3,-13,-6], + [4,8,-9,-5], [11,12,1,-2], [9,18,1,-2], [10,12,1,-2], [12,18,1,-2], [10,12,1,-2], [12,22,5,1], [11,16,5,-1], + [12,18,1,-1], [6,17,1,-2], [10,21,5,1], [11,18,1,-1], [6,18,1,-2], [19,12,1,-2], [12,12,1,-2], [11,12,1,-2], + [13,16,5], [10,16,5,-2], [10,12,1,-2], [9,12,1,-1], [7,16,1,-2], [12,12,1,-2], [10,12,1,-2], [15,12,1,-2], + [12,12,1,-1], [11,16,5,-2], [10,12,1,-1], [12,1,-6,-2], [22,1,-6,-3], [8,5,-12,-6], [8,3,-13,-6], [8,3,-13,-6], + [10,39,68,98,127,156,186,215,245,274,303,333,362,391,421,450], + [32,61,90,118,147,176,204,233], + [482,253] + ] +}); + +jsMath.Img.AddFont(207,{ + cmr10: [ + [16,20,0,-1], [22,21,0,-1], [20,22,1,-1], [20,21,0], [18,20,0,-1], [20,20,0,-1], [19,20,0,-1], [20,21,0,-1], + [19,20,0,-1], [20,20,0,-1], [19,21,0,-1], [19,21,0], [16,21,0], [16,21,0], [24,21,0], [24,21,0], + [7,13,0,-1], [9,19,6,2], [6,6,-15,-3], [6,6,-15,-6], [8,4,-15,-3], [9,6,-15,-3], [11,2,-16,-2], [6,6,-15,-8], + [8,6,6,-3], [14,22,1], [20,14,1,-1], [22,14,1], [13,19,3,-1], [26,20,0], [27,22,1,-2], [20,24,2,-1], + [8,4,-8], [4,21,0,-2], [9,10,-11,-1], [22,27,6,-1], [12,24,2,-1], [22,24,2,-1], [20,22,1,-1], [4,9,-11,-2], + [8,30,8,-2], [8,30,8,-1], [11,13,-9,-2], [20,20,3,-1], [4,9,6,-2], [8,3,-5], [4,3,0,-2], [12,30,8,-1], + [13,21,1,-1], [11,20,0,-2], [12,20,0,-1], [13,21,1,-1], [14,20,0], [12,21,1,-1], [13,21,1,-1], [13,21,1,-1], + [13,21,1,-1], [13,21,1,-1], [4,13,0,-2], [4,19,6,-2], [4,22,7,-2], [20,8,-3,-1], [11,21,6,-1], [11,21,0,-1], + [20,22,1,-1], [21,21,0], [18,20,0,-1], [19,22,1,-1], [20,20,0,-1], [18,20,0,-1], [17,20,0,-1], [21,22,1,-1], + [20,20,0,-1], [10,20,0], [13,21,1,-1], [21,20,0,-1], [16,20,0,-1], [25,20,0,-1], [20,20,0,-1], [20,22,1,-1], + [18,20,0,-1], [21,27,6,-1], [21,21,1,-1], [14,22,1,-1], [19,20,0,-1], [20,21,1,-1], [22,21,1], [30,21,1], + [21,20,0], [22,20,0], [16,20,0,-1], [5,30,8,-3], [10,9,-11,-4], [5,30,8], [8,5,-15,-3], [4,4,-16,-2], + [4,9,-11,-2], [14,14,1,-1], [16,22,1], [11,14,1,-1], [15,22,1,-1], [12,14,1], [10,21,0,-1], [14,20,6], + [16,21,0], [7,20,0,-1], [9,26,6,2], [15,21,0], [7,21,0,-1], [24,13,0], [16,13,0], [14,14,1], + [16,19,6], [15,19,6,-1], [11,13,0], [10,14,1,-1], [10,19,1], [15,14,1,-1], [15,14,1], [21,14,1], + [15,13,0], [15,19,6], [12,13,0], [15,1,-7], [29,1,-7], [10,6,-15,-3], [10,4,-16,-2], [9,4,-16,-3], + [12,47,82,118,153,188,223,259,294,329,365,400,435,471,506,541], + [37,72,106,140,175,209,243,278], + [580,301] + ], + cmmi10: [ + [20,20,0,-1], [22,21,0,-1], [21,22,1,-1], [19,21,0,-1], [22,20,0,-1], [25,20,0,-1], [23,20,0,-1], [21,21,0], + [19,20,0], [20,20,0], [21,21,0,-2], [17,14,1,-1], [18,27,6], [16,20,7], [13,22,1,-1], [10,14,1,-1], + [13,27,6,-1], [15,20,7], [13,22,1,-1], [9,14,1,-1], [15,14,1,-1], [15,22,1,-1], [17,20,7], [15,13,0,-1], + [13,27,6], [17,14,1], [15,20,7], [16,14,1,-1], [15,14,1], [16,14,1], [16,26,6,-1], [17,19,6,-1], + [19,26,6], [18,14,1], [13,15,1], [17,22,1], [24,14,1], [13,19,6,-2], [12,17,4], [17,20,7,-1], + [27,9,-6,-1], [27,9,1,-1], [27,9,-6,-1], [27,9,1,-1], [6,8,-6,-1], [6,8,-6,-1], [14,16,1], [14,16,1], + [13,15,1,-1], [11,13,0,-2], [12,14,0,-1], [13,21,7,-1], [14,20,6], [12,20,7,-1], [13,21,1,-1], [13,21,7,-1], + [13,21,1,-1], [13,21,7,-1], [4,3,0,-2], [4,9,6,-2], [18,18,2,-2], [12,30,8,-1], [18,17,1,-2], [15,14,0], + [16,22,1,-1], [20,21,0,-1], [21,20,0,-1], [21,22,1,-1], [23,20,0,-1], [22,20,0,-1], [21,20,0,-1], [21,22,1,-1], + [25,20,0,-1], [14,20,0,-1], [17,21,1,-2], [25,20,0,-1], [18,20,0,-1], [30,20,0,-1], [25,20,0,-1], [21,22,1,-1], + [21,20,0,-1], [21,27,6,-1], [21,21,1,-1], [18,22,1,-1], [21,20,0], [20,21,1,-2], [22,21,1,-1], [30,21,1,-1], + [29,20,0,4], [21,20,0,-1], [20,20,0,-1], [9,23,1,-1], [7,28,7,-2], [9,28,7,-1], [27,8,-3,-1], [27,8,-3,-1], + [12,22,1], [14,14,1,-1], [11,22,1,-1], [12,14,1,-1], [14,22,1,-1], [12,14,1,-1], [15,27,6,-1], [14,19,6], + [15,22,1,-1], [9,21,1], [13,26,6,1], [14,22,1,-1], [7,22,1,-1], [25,14,1], [17,14,1], [13,14,1,-1], + [16,19,6,1], [12,19,6,-1], [13,14,1], [12,14,1,-1], [10,20,1], [16,14,1], [14,14,1], [20,14,1], + [16,14,1], [15,19,6], [13,14,1,-1], [9,14,1], [12,19,6,1], [16,20,7,-2], [13,6,-15,-5], [12,5,-15,-7], + [11,46,80,115,149,183,218,252,286,321,355,390,424,458,493,527], + [37,72,106,140,175,209,243,278], + [565,301] + ], + cmsy10: [ + [19,2,-6,-2], [4,4,-5,-2], [15,15,0,-4], [11,13,-1,-2], [20,17,1,-1], [15,15,0], [20,20,0,-1], [20,20,5,-1], + [20,20,3,-1], [20,20,3,-1], [20,20,3,-1], [20,20,3,-1], [20,20,3,-1], [27,28,7,-1], [12,12,-1,-1], [12,12,-1,-1], + [20,14,0,-1], [20,13,-1,-1], [19,23,4,-2], [19,23,4,-2], [19,23,4,-2], [19,23,4,-2], [19,23,4,-2], [19,23,4,-2], + [20,8,-3,-1], [20,13,-1,-1], [19,18,2,-2], [19,18,2,-2], [27,19,2,-1], [27,19,2,-1], [19,18,2,-2], [18,17,1,-2], + [27,11,-2,-1], [27,11,-2,-1], [11,26,6,-2], [11,26,6,-2], [27,11,-2,-1], [27,27,6,-1], [27,26,6,-1], [20,13,-1,-1], + [27,17,1,-1], [27,17,1,-1], [17,26,6], [17,27,6], [27,17,1,-1], [27,27,6,-1], [27,26,6,-1], [20,14,1,-1], + [7,16,-1,-1], [27,14,1,-1], [15,18,2,-2], [15,18,2,-2], [23,21,0,-1], [23,22,7,-1], [15,28,7,-4], [3,12,-1,-1], + [17,22,1], [14,21,0,-1], [17,9,-2,-1], [13,26,3,-1], [20,22,1,-1], [19,22,1,-1], [20,20,0,-1], [20,20,0,-1], + [15,20,0,-1], [23,23,2], [19,22,1,-1], [16,22,1], [23,20,0], [17,22,1], [24,21,1], [17,25,4,-1], + [24,22,2], [20,20,0,1], [24,24,4,-1], [21,22,1,-1], [18,22,1,-1], [33,23,2], [30,25,2,1], [22,22,1,-1], + [22,22,2], [20,25,4,-3], [25,21,1], [19,22,1], [23,21,0], [21,21,1,1], [19,22,2,-1], [29,22,2,-1], + [23,20,0,-1], [21,24,4], [22,20,0,-1], [17,19,1,-1], [17,19,1,-1], [17,19,1,-1], [17,19,1,-1], [17,19,1,-1], + [15,20,0,-1], [16,21,0,-1], [8,30,8,-5], [8,30,8], [8,30,8,-5], [8,30,8], [11,30,8,-2], [11,30,8,-2], + [7,30,8,-3], [7,30,8,-1], [2,30,8,-3], [8,30,8,-3], [11,31,8,-2], [17,31,8], [12,30,8,-1], [6,20,3,-1], + [23,30,28,-2], [20,20,0,-1], [22,21,1,-1], [13,28,7,-1], [17,18,0,-1], [17,18,0,-1], [19,23,4,-2], [20,23,4,-1], + [9,27,6,-2], [11,28,7,-1], [11,27,6,-1], [16,27,6,-1], [22,25,4], [20,26,5,-1], [20,22,1,-1], [20,25,4,-1], + [12,53,95,136,177,218,260,301,342,384,425,466,507,549,590,631], + [39,98,157,216,274,333,392,451], + [676,500] + ], + cmex10: [ + [8,36,34,-4], [8,36,34,-1], [7,36,34,-5], [7,36,34], [8,36,34,-5], [8,36,34], [8,36,34,-5], [8,36,34], + [11,36,34,-3], [11,36,34,-3], [10,36,34,-2], [9,36,34,-2], [2,19,18,-4], [8,19,18,-4], [14,36,34,-1], [14,36,34,-1], + [12,53,51,-5], [11,53,51,-1], [15,71,69,-6], [15,71,69,-1], [8,71,69,-7], [8,71,69], [10,71,69,-7], [10,71,69], + [10,71,69,-7], [10,71,69], [15,71,69,-3], [15,71,69,-3], [16,71,69,-3], [16,71,69,-2], [28,71,69,-1], [28,71,69,-1], + [16,88,86,-6], [16,88,86,-1], [9,88,86,-8], [9,88,86], [11,88,86,-8], [11,88,86], [11,88,86,-8], [11,88,86], + [16,88,86,-4], [16,88,86,-4], [17,88,86,-4], [17,88,86,-3], [35,88,86,-1], [35,88,86,-1], [21,53,51,-1], [21,53,51,-1], + [17,54,52,-8], [16,54,52,-1], [11,53,51,-9], [10,53,51], [11,53,51,-9], [10,53,51], [3,18,18,-9], [3,18,18,-7], + [10,27,27,-11], [10,27,27,-5], [10,27,26,-11], [10,28,27,-5], [10,54,53,-5], [10,54,53,-11], [4,10,9,-11], [2,18,18,-9], + [17,53,51,-8], [16,53,51,-1], [4,19,18,-8], [4,19,18,-13], [13,53,51,-3], [13,53,51,-2], [22,29,29,-1], [30,41,41,-1], + [17,33,33,-1], [27,65,65,-1], [30,29,29,-1], [42,41,41,-1], [30,29,29,-1], [42,41,41,-1], [30,29,29,-1], [42,41,41,-1], + [28,29,29,-1], [25,29,29,-1], [17,33,33,-1], [22,29,29,-1], [22,29,29,-1], [22,29,29,-1], [22,29,29,-1], [22,29,29,-1], + [40,41,41,-1], [35,41,41,-1], [27,65,65,-1], [30,41,41,-1], [30,41,41,-1], [30,41,41,-1], [30,41,41,-1], [30,41,41,-1], + [25,29,29,-1], [35,41,41,-1], [17,6,-16], [29,7,-16], [42,7,-16], [16,4,-17], [29,4,-18], [42,4,-18], + [8,53,51,-6], [8,53,51], [9,53,51,-6], [9,53,51], [9,53,51,-6], [9,53,51], [13,53,51,-3], [13,53,51,-3], + [27,35,34,-3], [27,52,51,-3], [27,70,69,-3], [27,87,86,-3], [19,54,53,-3], [2,19,18,-20], [12,19,17,-20], [9,18,18,-7], + [14,18,18,-3], [14,18,18,-3], [15,11,7,1], [15,11,7,1], [15,10,0,1], [15,10,0,1], [20,18,18,-1], [20,18,18,-1], + [13,65,117,169,221,273,325,377,428,480,532,584,636,688,740,792], + [45,173,300,428,555,683,810,938], + [849,1062] + ], + cmbx10: [ + [18,20,0,-1], [25,21,0,-1], [23,22,1,-1], [22,21,0,-1], [20,20,0,-1], [24,20,0,-1], [22,20,0,-1], [23,21,0,-1], + [22,20,0,-1], [23,20,0,-1], [22,21,0,-1], [22,21,0], [18,21,0], [18,21,0], [27,21,0], [27,21,0], + [8,13,0,-1], [10,19,6,2], [7,7,-14,-3], [7,7,-14,-7], [9,4,-15,-4], [11,7,-14,-3], [13,2,-16,-2], [7,6,-15,-9], + [10,6,6,-3], [16,22,1,-1], [23,15,1,-1], [24,15,1,-1], [16,19,3], [29,20,0,-1], [31,22,1,-2], [23,24,2,-1], + [10,4,-8], [6,21,0,-2], [13,11,-10,-1], [25,27,6,-1], [14,24,2,-1], [25,24,2,-1], [24,22,1,-1], [6,11,-10,-2], + [8,30,8,-3], [9,30,8,-1], [13,14,-8,-2], [23,23,4,-1], [6,11,6,-2], [10,3,-5], [5,5,0,-2], [14,30,8,-1], + [15,20,1,-1], [13,19,0,-2], [14,19,0,-1], [15,20,1,-1], [16,19,0], [14,20,1,-1], [15,20,1,-1], [16,21,1,-1], + [15,20,1,-1], [15,20,1,-1], [5,13,0,-2], [5,19,6,-2], [6,21,6,-2], [23,9,-3,-1], [13,21,6,-1], [13,21,0,-1], + [23,22,1,-1], [23,21,0,-1], [21,20,0,-1], [22,22,1,-1], [23,20,0,-1], [20,20,0,-1], [19,20,0,-1], [24,22,1,-1], + [24,20,0,-1], [11,20,0,-1], [16,21,1], [24,20,0,-1], [18,20,0,-1], [30,20,0,-1], [24,20,0,-1], [23,22,1,-1], + [20,20,0,-1], [23,27,6,-1], [24,21,1,-1], [16,22,1,-1], [21,20,0,-1], [24,21,1,-1], [25,21,1], [34,21,1], + [24,20,0,-1], [25,20,0], [18,20,0,-1], [6,30,8,-3], [13,10,-10,-4], [6,30,8], [10,5,-15,-3], [5,6,-15,-2], + [6,10,-10,-1], [16,15,1,-1], [17,22,1,-1], [13,15,1,-1], [17,22,1,-1], [15,15,1], [12,21,0,-1], [16,20,6,-1], + [17,21,0,-1], [8,21,0,-1], [10,27,6,2], [16,21,0,-1], [8,21,0,-1], [27,13,0,-1], [17,13,0,-1], [16,15,1], + [17,19,6,-1], [17,19,6,-1], [12,13,0,-1], [11,15,1,-1], [12,20,1], [17,14,1,-1], [17,14,1], [24,14,1], + [17,13,0], [17,19,6], [13,13,0,-1], [17,2,-7], [34,2,-7], [11,6,-15,-4], [11,4,-17,-3], [11,5,-16,-3], + [12,53,94,135,175,216,257,298,339,380,421,462,502,543,584,625], + [37,72,106,140,175,209,243,278], + [670,301] + ], + cmti10: [ + [20,20,0,-1], [20,21,0,-2], [19,22,1,-4], [18,21,0,-1], [20,20,0,-2], [24,20,0,-1], [21,20,0,-2], [19,21,0,-6], + [18,20,0,-4], [18,20,0,-6], [20,21,0,-2], [23,27,6,1], [19,27,6,1], [20,27,6,1], [28,27,6,1], [29,27,6,1], + [8,14,1,-2], [11,19,6,1], [5,7,-14,-8], [6,7,-14,-10], [8,5,-14,-8], [9,5,-15,-8], [11,2,-16,-6], [7,6,-15,-13], + [7,6,6,-3], [18,27,6,1], [19,14,1,-2], [18,14,1,-3], [14,19,3,-2], [27,20,0,-1], [27,22,1,-4], [21,24,2,-3], + [8,4,-8,-2], [8,21,0,-3], [10,10,-11,-5], [21,27,6,-3], [19,22,1,-2], [21,24,2,-4], [21,22,1,-3], [5,10,-11,-6], + [11,30,8,-4], [12,30,8], [12,13,-9,-5], [18,18,2,-4], [5,9,6,-2], [8,2,-5,-2], [4,3,0,-3], [18,30,8], + [14,21,1,-3], [11,20,0,-3], [14,21,1,-2], [15,21,1,-2], [13,26,6,-1], [14,21,1,-3], [14,21,1,-3], [14,21,1,-4], + [14,21,1,-2], [13,21,1,-3], [6,13,0,-3], [7,19,6,-2], [9,22,7,-1], [20,8,-3,-3], [11,22,7,-2], [11,21,0,-5], + [19,22,1,-4], [19,21,0,-1], [20,20,0,-2], [20,22,1,-4], [22,20,0,-1], [21,20,0,-1], [21,20,0,-1], [20,22,1,-4], + [24,20,0,-1], [14,20,0,-1], [16,21,1,-2], [24,20,0,-1], [17,20,0,-1], [27,20,0,-2], [24,20,0,-1], [19,22,1,-4], + [21,20,0,-1], [19,27,6,-4], [20,21,1,-1], [17,22,1,-2], [19,20,0,-5], [20,21,1,-5], [20,21,1,-6], [27,21,1,-6], + [28,20,0,4], [21,20,0,-5], [19,20,0,-2], [11,30,8,-2], [10,9,-11,-8], [12,30,8,1], [8,5,-15,-7], [4,4,-16,-7], + [5,9,-11,-6], [13,14,1,-3], [11,22,1,-3], [11,14,1,-3], [14,22,1,-3], [11,14,1,-3], [15,27,6,1], [14,19,6,-1], + [14,22,1,-2], [8,20,1,-2], [12,25,6,1], [13,22,1,-2], [7,22,1,-2], [23,14,1,-2], [15,14,1,-2], [12,14,1,-3], + [15,19,6], [12,19,6,-3], [13,14,1,-2], [11,14,1,-2], [9,20,1,-2], [15,14,1,-2], [13,14,1,-2], [19,14,1,-2], + [14,14,1,-1], [13,19,6,-2], [12,14,1,-2], [14,1,-7,-2], [27,1,-7,-3], [10,7,-14,-7], [10,4,-16,-7], [9,4,-16,-7], + [12,47,82,117,152,187,222,257,293,328,363,398,433,468,503,539], + [37,72,106,140,175,209,243,278], + [577,301] + ] +}); + +jsMath.Img.AddFont(249,{ + cmr10: [ + [19,24,0,-1], [26,25,0,-1], [24,25,1,-1], [22,25,0,-1], [21,23,0,-1], [24,24,0,-1], [21,24,0,-2], [23,24,0,-2], + [22,24,0,-1], [23,24,0,-2], [22,24,0,-1], [21,24,0,-1], [17,24,0,-1], [17,24,0,-1], [27,24,0,-1], [27,24,0,-1], + [8,15,0,-1], [10,22,7,2], [7,7,-17,-3], [7,7,-17,-7], [9,5,-17,-4], [11,7,-17,-3], [13,2,-19,-2], [7,7,-18,-9], + [9,7,7,-4], [15,25,1,-1], [23,17,1,-1], [25,17,1,-1], [15,22,4,-1], [29,24,0,-1], [32,25,1,-2], [24,27,2,-1], + [8,5,-9,-1], [4,25,0,-3], [11,11,-13,-1], [25,31,7,-2], [14,28,2,-1], [26,28,2,-1], [24,26,1,-1], [4,11,-13,-3], + [9,35,9,-3], [8,35,9,-2], [13,16,-10,-2], [23,23,3,-2], [4,11,7,-3], [10,3,-6], [4,4,0,-3], [13,35,9,-2], + [15,24,1,-1], [12,23,0,-3], [15,23,0,-1], [15,24,1,-1], [15,23,0,-1], [15,24,1,-1], [15,24,1,-1], [15,24,1,-2], + [15,24,1,-1], [15,24,1,-1], [4,15,0,-3], [4,22,7,-3], [4,25,8,-3], [23,9,-4,-2], [14,24,7,-1], [14,24,0,-1], + [24,25,1,-1], [24,25,0,-1], [22,24,0,-1], [22,25,1,-1], [23,24,0,-1], [22,24,0,-1], [20,24,0,-1], [24,25,1,-1], + [24,24,0,-1], [11,24,0,-1], [15,25,1,-1], [24,24,0,-1], [19,24,0,-1], [29,24,0,-1], [24,24,0,-1], [24,25,1,-1], + [21,24,0,-1], [24,31,7,-1], [24,25,1,-1], [16,25,1,-1], [23,23,0,-1], [24,25,1,-1], [25,25,1], [35,25,1], + [25,24,0], [26,24,0], [18,24,0,-1], [5,35,9,-4], [11,11,-13,-5], [6,35,9], [9,6,-18,-4], [4,4,-19,-3], + [5,11,-13,-2], [16,17,1,-1], [17,25,1,-1], [13,17,1,-1], [17,25,1,-1], [14,17,1,-1], [12,24,0,-1], [16,23,7,-1], + [18,24,0,-1], [8,23,0,-1], [10,30,7,2], [17,24,0,-1], [8,24,0,-1], [27,15,0,-1], [18,15,0,-1], [15,17,1,-1], + [17,22,7,-1], [17,22,7,-1], [12,15,0,-1], [12,17,1,-1], [12,22,1], [18,16,1,-1], [18,16,1], [24,16,1], + [18,15,0], [18,22,7], [13,15,0,-1], [17,2,-8], [34,2,-8], [11,7,-17,-4], [11,4,-19,-3], [11,4,-19,-3], + [14,56,99,141,184,226,269,311,354,396,439,481,524,566,609,651], + [46,87,128,170,211,253,294,335], + [698,363] + ], + cmmi10: [ + [24,24,0,-1], [26,25,0,-1], [25,25,1,-1], [22,25,0,-1], [26,23,0,-1], [29,24,0,-1], [26,24,0,-2], [23,24,0,-1], + [22,24,0], [23,24,0,-1], [25,24,0,-2], [20,16,1,-1], [20,31,7], [19,23,8], [15,26,1,-1], [12,16,1,-1], + [15,31,7,-1], [16,23,8,-1], [15,25,1,-1], [10,16,1,-1], [18,16,1,-1], [18,25,1,-1], [19,23,8,-1], [17,15,0,-1], + [16,31,7], [19,16,1,-1], [16,23,8,-1], [19,16,1,-1], [17,16,1,-1], [17,16,1,-1], [19,31,7,-1], [20,22,7,-1], + [21,31,7,-1], [21,16,1], [15,17,1], [18,25,1,-1], [27,16,1,-1], [15,22,7,-2], [13,19,4,-1], [20,23,8,-1], + [31,11,-7,-1], [31,11,1,-1], [31,11,-7,-1], [31,11,1,-1], [7,9,-7,-1], [7,9,-7,-1], [16,19,1], [16,19,1,-1], + [15,17,1,-1], [12,16,0,-3], [15,16,0,-1], [15,24,8,-1], [15,23,7,-1], [15,24,8,-1], [15,24,1,-1], [16,24,8,-1], + [15,24,1,-1], [15,24,8,-1], [4,4,0,-3], [4,11,7,-3], [22,21,2,-2], [13,35,9,-2], [22,21,2,-2], [17,17,0], + [19,26,1,-1], [24,25,0,-1], [25,24,0,-1], [25,25,1,-1], [27,24,0,-1], [25,24,0,-1], [25,24,0,-1], [25,25,1,-1], + [29,24,0,-1], [16,24,0,-1], [20,25,1,-2], [30,24,0,-1], [21,24,0,-1], [35,24,0,-1], [29,24,0,-1], [25,25,1,-1], + [25,24,0,-1], [25,31,7,-1], [25,25,1,-1], [21,25,1,-1], [24,23,0], [24,25,1,-2], [25,25,1,-2], [35,25,1,-1], + [28,24,0,-1], [25,24,0,-1], [23,24,0,-2], [11,27,1,-1], [9,33,8,-2], [11,33,8,-1], [30,9,-4,-2], [30,9,-4,-2], + [14,25,1], [16,16,1,-1], [14,25,1,-1], [14,16,1,-1], [17,25,1,-1], [14,16,1,-1], [18,31,7,-1], [17,22,7], + [18,25,1,-1], [9,24,1,-1], [15,30,7,1], [17,25,1,-1], [8,25,1,-1], [28,16,1,-1], [19,16,1,-1], [15,16,1,-1], + [18,22,7,1], [15,22,7,-1], [14,16,1,-1], [14,16,1,-1], [12,23,1], [18,16,1,-1], [15,16,1,-1], [23,16,1,-1], + [17,16,1,-1], [16,22,7,-1], [15,16,1,-1], [9,16,1,-1], [14,22,7,1], [19,24,8,-2], [16,8,-17,-6], [13,5,-18,-9], + [14,55,96,138,179,221,262,303,345,386,427,469,510,551,593,634], + [46,87,128,170,211,253,294,335], + [679,363] + ], + cmsy10: [ + [22,3,-7,-2], [4,5,-6,-3], [17,17,0,-5], [13,15,-1,-2], [23,19,1,-2], [17,17,0], [23,23,0,-2], [23,23,6,-2], + [24,23,3,-1], [24,23,3,-1], [24,23,3,-1], [24,23,3,-1], [24,23,3,-1], [31,33,8,-1], [14,15,-1,-1], [14,15,-1,-1], + [23,17,0,-2], [23,15,-1,-2], [22,27,5,-2], [22,27,5,-2], [22,27,5,-2], [22,27,5,-2], [22,27,5,-2], [22,27,5,-2], + [24,9,-4,-1], [23,16,-1,-2], [22,21,2,-2], [22,21,2,-2], [30,23,3,-2], [30,23,3,-2], [22,21,2,-2], [22,21,2,-2], + [30,13,-2,-2], [30,13,-2,-2], [13,31,7,-2], [13,31,7,-2], [30,13,-2,-2], [31,31,7,-2], [31,31,7,-2], [24,15,-1,-1], + [30,19,1,-2], [30,19,1,-2], [19,31,7,-1], [19,31,7,-1], [32,19,1,-1], [31,31,7,-1], [31,31,7,-1], [24,16,1,-1], + [8,18,-1,-1], [31,16,1,-1], [18,21,2,-2], [18,21,2,-2], [27,25,0,-2], [27,25,8,-2], [18,33,8,-4], [4,13,-2,-1], + [19,25,1], [15,24,0,-2], [19,10,-3,-2], [15,30,3,-1], [24,26,1,-1], [23,25,1,-1], [23,23,0,-2], [23,23,0,-2], + [18,24,0,-1], [26,27,2,-1], [22,25,1,-1], [19,25,1], [26,24,0], [19,25,1,-1], [29,26,2], [20,28,4,-1], + [28,26,2], [23,24,0,1], [28,28,4,-1], [24,25,1,-1], [22,25,1,-1], [37,26,2,-1], [35,29,2,1], [25,25,1,-2], + [25,26,2], [24,29,5,-3], [29,25,1], [22,25,1], [26,25,0,-1], [25,25,1,1], [22,26,2,-1], [35,26,2,-1], + [27,24,0,-1], [24,29,5,-1], [25,24,0,-1], [20,22,1,-1], [20,22,1,-1], [20,22,1,-1], [19,22,1,-2], [19,22,1,-2], + [18,24,0,-1], [17,24,0,-2], [10,35,9,-5], [10,35,9], [10,35,9,-5], [10,35,9], [13,35,9,-2], [13,35,9,-2], + [9,35,9,-3], [8,35,9,-2], [2,35,9,-4], [9,35,9,-4], [13,37,10,-2], [19,37,10,-1], [13,35,9,-2], [7,23,3,-1], + [27,35,33,-2], [24,24,0,-1], [26,26,2,-1], [15,33,8,-1], [19,21,0,-2], [19,21,0,-2], [22,27,5,-3], [22,27,5,-2], + [11,31,7,-2], [12,32,8,-2], [12,31,7,-2], [19,31,7,-1], [25,30,5,-1], [23,31,6,-2], [24,27,2,-1], [24,30,5,-1], + [14,64,114,163,213,263,312,362,412,461,511,561,610,660,710,759], + [48,119,190,260,331,402,472,543], + [814,603] + ], + cmex10: [ + [9,42,40,-5], [10,42,40,-1], [8,42,40,-6], [8,42,40], [10,42,40,-6], [10,42,40], [10,42,40,-6], [10,42,40], + [12,42,40,-4], [12,42,40,-4], [11,42,40,-3], [11,42,40,-2], [3,22,21,-4], [10,22,21,-4], [16,42,40,-2], [16,42,40,-2], + [13,62,60,-6], [14,62,60,-1], [17,83,81,-7], [17,83,81,-1], [10,83,81,-8], [10,83,81], [12,83,81,-8], [12,83,81], + [12,83,81,-8], [12,83,81], [17,83,81,-4], [17,83,81,-4], [19,83,81,-4], [19,83,81,-3], [32,83,81,-2], [32,83,81,-2], + [18,103,101,-8], [18,103,101,-1], [11,103,101,-9], [11,103,101], [13,103,101,-9], [13,103,101], [13,103,101,-9], [13,103,101], + [19,103,101,-4], [19,103,101,-4], [20,103,101,-4], [20,103,101,-3], [40,103,101,-2], [40,103,101,-2], [24,62,60,-2], [24,62,60,-2], + [20,63,61,-9], [19,63,61,-1], [12,62,60,-11], [12,62,60], [12,62,60,-11], [12,62,60], [3,21,21,-11], [3,21,21,-9], + [12,31,31,-13], [13,31,31,-5], [12,32,31,-13], [13,32,31,-5], [13,63,62,-5], [12,63,62,-13], [5,12,11,-13], [3,21,21,-10], + [20,62,60,-9], [19,62,60,-1], [5,22,21,-9], [4,22,21,-16], [15,62,60,-3], [14,62,60,-3], [26,34,34,-1], [35,48,48,-1], + [19,38,38,-2], [31,76,76,-1], [35,34,34,-1], [49,48,48,-1], [35,34,34,-1], [49,48,48,-1], [35,34,34,-1], [49,48,48,-1], + [33,34,34,-1], [30,34,34,-1], [19,38,38,-2], [26,34,34,-1], [26,34,34,-1], [26,34,34,-1], [25,34,34,-2], [25,34,34,-2], + [46,48,48,-1], [41,48,48,-1], [31,76,76,-1], [35,48,48,-1], [35,48,48,-1], [35,48,48,-1], [34,48,48,-2], [35,48,48,-1], + [30,34,34,-1], [41,48,48,-1], [19,7,-19], [34,8,-19], [49,8,-19], [19,5,-20], [34,5,-21], [49,5,-21], + [9,62,60,-7], [9,62,60], [11,62,60,-7], [11,62,60], [11,62,60,-7], [11,62,60], [15,62,60,-4], [15,62,60,-4], + [32,42,40,-3], [32,62,60,-3], [32,83,81,-3], [32,103,101,-3], [23,63,62,-3], [3,22,21,-23], [14,22,20,-23], [10,21,21,-8], + [16,21,21,-3], [16,21,21,-3], [17,13,8,1], [18,13,8,1], [17,12,0,1], [18,12,0,1], [24,21,21,-1], [24,21,21,-1], + [16,78,141,203,265,328,390,453,515,578,640,703,765,828,890,953], + [55,209,362,515,669,822,976,1129], + [1021,1278] + ], + cmbx10: [ + [21,24,0,-1], [29,24,0,-2], [27,25,1,-2], [25,24,0,-1], [24,23,0,-1], [29,24,0,-1], [24,24,0,-2], [27,24,0,-2], + [24,24,0,-2], [27,24,0,-2], [26,24,0,-1], [25,24,0,-1], [20,24,0,-1], [20,24,0,-1], [31,24,0,-1], [31,24,0,-1], + [9,16,0,-1], [12,23,7,2], [8,7,-17,-4], [8,7,-17,-8], [11,6,-17,-4], [13,7,-17,-3], [15,3,-18,-2], [9,6,-18,-10], + [11,7,7,-4], [19,25,1,-1], [27,17,1,-1], [29,17,1,-1], [18,23,4,-1], [34,24,0,-1], [37,25,1,-2], [27,28,2,-2], + [11,5,-9], [6,24,0,-3], [15,13,-11,-1], [29,31,7,-2], [16,28,2,-2], [29,28,2,-2], [28,25,1,-1], [7,13,-11,-2], + [10,35,9,-3], [10,35,9,-2], [15,16,-10,-2], [27,27,5,-2], [7,13,7,-2], [11,5,-5], [6,6,0,-2], [16,35,9,-2], + [17,24,1,-1], [15,23,0,-2], [17,23,0,-1], [17,24,1,-1], [18,23,0,-1], [16,24,1,-2], [17,24,1,-1], [17,24,1,-2], + [17,24,1,-1], [17,24,1,-1], [6,16,0,-2], [7,23,7,-2], [6,24,7,-3], [27,11,-3,-2], [15,24,7,-2], [15,24,0,-2], + [27,25,1,-2], [28,24,0,-1], [25,24,0,-1], [24,25,1,-2], [27,24,0,-1], [24,24,0,-1], [22,24,0,-1], [27,25,1,-2], + [29,24,0,-1], [13,24,0,-1], [18,25,1], [28,24,0,-1], [21,24,0,-1], [35,24,0,-1], [29,24,0,-1], [26,25,1,-2], + [24,24,0,-1], [26,31,7,-2], [29,25,1,-1], [18,25,1,-2], [25,23,0,-1], [28,25,1,-1], [29,25,1], [40,25,1], + [28,24,0,-1], [29,24,0], [20,24,0,-2], [6,35,9,-4], [16,13,-11,-4], [7,35,9], [11,6,-18,-4], [6,6,-18,-2], + [6,13,-11,-2], [18,17,1,-1], [20,25,1,-1], [16,17,1,-1], [20,25,1,-1], [16,17,1,-1], [14,24,0,-1], [18,23,7,-1], + [20,24,0,-1], [9,24,0,-1], [12,31,7,2], [19,24,0,-1], [9,24,0,-1], [31,16,0,-1], [20,16,0,-1], [18,17,1,-1], + [20,23,7,-1], [20,23,7,-1], [14,16,0,-1], [14,17,1,-1], [13,23,1], [20,17,1,-1], [20,17,1], [28,17,1], + [20,16,0], [20,23,7], [15,16,0,-1], [20,2,-8], [39,2,-8], [13,8,-17,-4], [14,4,-20,-3], [13,6,-18,-3], + [14,64,113,162,211,260,309,359,408,457,506,555,604,653,703,752], + [46,87,128,170,211,253,294,335], + [806,363] + ], + cmti10: [ + [22,24,0,-2], [24,25,0,-2], [22,25,1,-5], [20,25,0,-2], [24,23,0,-2], [27,24,0,-2], [25,24,0,-2], [22,24,0,-7], + [20,24,0,-5], [21,24,0,-7], [23,24,0,-3], [27,31,7,1], [22,31,7,1], [23,31,7,1], [33,31,7,1], [33,31,7,1], + [10,16,1,-2], [13,22,7,2], [6,7,-17,-9], [8,7,-17,-11], [10,5,-17,-9], [11,7,-17,-9], [12,1,-19,-8], [7,7,-18,-16], + [9,7,7,-3], [21,31,7,1], [22,16,1,-3], [22,16,1,-3], [17,23,4,-2], [31,24,0,-2], [31,25,1,-5], [24,28,2,-4], + [9,5,-9,-3], [10,25,0,-3], [12,11,-13,-6], [25,31,7,-4], [21,25,1,-3], [24,28,2,-5], [24,26,1,-4], [6,11,-13,-7], + [13,35,9,-5], [13,35,9], [14,16,-10,-6], [22,21,2,-4], [6,11,7,-2], [9,3,-6,-3], [5,4,0,-3], [21,35,9], + [15,24,1,-4], [12,23,0,-4], [17,24,1,-2], [17,24,1,-3], [15,30,7,-1], [17,24,1,-3], [16,24,1,-4], [17,24,1,-5], + [16,24,1,-3], [16,24,1,-3], [8,15,0,-3], [9,22,7,-2], [9,25,8,-2], [23,9,-4,-4], [13,25,8,-2], [13,25,0,-6], + [22,25,1,-5], [22,25,0,-2], [23,24,0,-2], [23,25,1,-5], [25,24,0,-2], [24,24,0,-2], [23,24,0,-2], [23,25,1,-5], + [27,24,0,-2], [15,24,0,-2], [19,25,1,-3], [28,24,0,-2], [20,24,0,-2], [32,24,0,-2], [27,24,0,-2], [22,25,1,-5], + [23,24,0,-2], [22,31,7,-5], [23,25,1,-2], [20,25,1,-2], [22,23,0,-6], [23,25,1,-6], [23,25,1,-7], [31,25,1,-7], + [33,24,0,5], [24,24,0,-6], [22,24,0,-2], [13,35,9,-2], [12,11,-13,-9], [14,35,9,1], [9,6,-18,-9], [5,4,-19,-8], + [6,11,-13,-7], [16,16,1,-3], [13,25,1,-3], [13,16,1,-3], [16,25,1,-3], [13,16,1,-3], [17,31,7,1], [16,22,7,-1], + [17,25,1,-2], [10,24,-294,-2], [15,30,7,2], [15,25,1,-2], [8,25,1,-3], [27,16,1,-2], [18,16,1,-2], [15,16,1,-3], + [18,22,7], [14,22,7,-3], [15,16,1,-2], [13,16,1,-2], [10,23,1,-3], [17,16,1,-2], [15,16,1,-2], [22,16,1,-2], + [17,16,1,-1], [16,22,7,-2], [14,16,1,-2], [16,2,-8,-3], [32,2,-8,-4], [11,7,-17,-9], [12,4,-19,-8], [10,4,-19,-9], + [14,56,98,141,183,225,267,310,352,394,437,479,521,563,606,648], + [46,87,128,170,211,253,294,335], + [694,363] + ] +}); + +jsMath.Img.AddFont(298,{ + cmr10: [ + [23,28,0,-1], [31,30,0,-2], [28,30,1,-2], [27,30,0,-1], [25,28,0,-1], [29,28,0,-1], [26,28,0,-2], [28,29,0,-2], + [26,28,0,-2], [28,28,0,-2], [27,29,0,-1], [25,29,0,-1], [21,29,0,-1], [21,29,0,-1], [32,29,0,-1], [32,29,0,-1], + [10,19,0,-1], [11,28,9,2], [8,8,-21,-4], [8,8,-21,-8], [11,5,-21,-5], [13,8,-21,-4], [16,3,-22,-2], [9,8,-22,-11], + [10,9,9,-5], [19,30,1,-1], [28,20,1,-1], [30,20,1,-1], [18,26,4,-1], [35,28,0,-1], [39,30,1,-2], [28,34,3,-2], + [10,5,-11,-1], [5,30,0,-3], [14,13,-16,-1], [30,37,8,-2], [17,34,3,-2], [30,34,3,-2], [29,31,1,-1], [6,13,-16,-3], + [10,42,11,-4], [10,42,11,-2], [16,18,-13,-2], [28,28,4,-2], [6,13,8,-3], [12,3,-7], [5,5,0,-3], [17,42,11,-2], + [18,29,1,-1], [15,28,0,-3], [17,28,0,-2], [18,29,1,-1], [19,28,0,-1], [17,29,1,-2], [18,29,1,-1], [18,29,1,-2], + [18,29,1,-1], [18,29,1,-1], [5,18,0,-3], [5,26,8,-3], [5,30,9,-3], [28,10,-5,-2], [15,30,9,-2], [15,29,0,-2], + [28,30,1,-2], [29,30,0,-1], [26,28,0,-1], [26,30,1,-2], [28,28,0,-1], [26,28,0,-1], [24,28,0,-1], [29,30,1,-2], + [29,28,0,-1], [13,28,0,-1], [19,29,1,-1], [30,28,0,-1], [23,28,0,-1], [35,28,0,-1], [29,28,0,-1], [28,30,1,-2], + [25,28,0,-1], [28,37,8,-2], [29,29,1,-1], [19,30,1,-2], [27,28,0,-1], [29,29,1,-1], [30,29,1], [42,29,1], + [29,28,0,-1], [31,28,0], [21,28,0,-2], [7,42,11,-4], [14,13,-16,-6], [7,42,11], [11,7,-22,-5], [5,5,-23,-3], + [5,13,-16,-3], [20,20,1,-1], [21,30,1,-1], [16,20,1,-1], [21,30,1,-1], [16,20,1,-1], [14,29,0,-1], [19,28,9,-1], + [21,29,0,-1], [10,28,0,-1], [11,37,9,2], [20,29,0,-1], [10,29,0,-1], [33,19,0,-1], [21,19,0,-1], [19,20,1,-1], + [21,27,8,-1], [21,27,8,-1], [14,19,0,-1], [14,20,1,-1], [14,27,1], [21,20,1,-1], [21,19,1], [29,19,1], + [22,18,0], [21,27,9], [16,18,0,-1], [21,2,-10], [41,2,-10], [13,8,-21,-5], [14,5,-23,-3], [13,5,-23,-4], + [17,67,118,169,220,271,322,373,423,474,525,576,627,678,729,780], + [55,104,154,203,253,302,352,401], + [835,434] + ], + cmmi10: [ + [29,28,0,-1], [31,30,0,-2], [29,30,1,-2], [27,30,0,-1], [30,28,0,-2], [35,28,0,-1], [31,28,0,-2], [28,29,0,-1], + [26,28,0,-1], [28,28,0,-1], [30,29,0,-3], [24,20,1,-1], [24,37,8,-1], [23,28,9], [18,31,1,-1], [15,19,1,-1], + [19,38,9,-1], [20,28,9,-1], [18,30,1,-1], [12,20,1,-2], [21,20,1,-2], [21,30,1,-2], [23,27,9,-1], [20,19,0,-2], + [18,38,9,-1], [23,19,1,-1], [20,28,9,-1], [23,19,1,-1], [20,19,1,-1], [21,20,1,-1], [22,38,9,-2], [24,28,9,-1], + [25,38,9,-1], [25,19,1], [17,20,1,-1], [22,30,1,-1], [33,19,1,-1], [18,27,8,-3], [16,24,5,-1], [24,28,9,-2], + [37,12,-9,-2], [37,13,1,-2], [37,12,-9,-2], [37,13,1,-2], [7,10,-9,-2], [8,10,-9,-2], [19,22,1,-1], [19,22,1,-1], + [18,20,1,-1], [15,19,0,-3], [18,19,0,-1], [18,28,9,-1], [19,27,8,-1], [17,28,9,-2], [18,29,1,-1], [18,28,9,-2], + [18,29,1,-1], [18,28,9,-1], [5,5,0,-3], [6,13,8,-3], [26,25,2,-3], [17,42,11,-2], [26,25,2,-3], [21,20,0], + [23,31,1,-1], [29,30,0,-1], [30,28,0,-1], [30,30,1,-2], [32,28,0,-1], [31,28,0,-1], [30,28,0,-1], [30,30,1,-2], + [36,28,0,-1], [20,28,0,-1], [24,29,1,-2], [36,28,0,-1], [26,28,0,-1], [42,28,0,-1], [36,28,0,-1], [29,30,1,-2], + [30,28,0,-1], [29,37,8,-2], [30,29,1,-1], [25,30,1,-2], [28,28,0,-1], [30,29,1,-2], [30,29,1,-2], [41,29,1,-2], + [34,28,0,-1], [31,28,0,-1], [28,28,0,-2], [12,32,1,-2], [10,39,9,-3], [12,39,9,-2], [37,11,-5,-2], [37,11,-5,-2], + [17,30,1], [20,20,1,-1], [16,30,1,-1], [17,20,1,-1], [20,30,1,-1], [17,20,1,-1], [21,38,9,-2], [20,28,9], + [21,30,1,-2], [11,28,1,-1], [18,36,9,1], [19,30,1,-2], [10,30,1,-1], [34,20,1,-1], [23,20,1,-1], [19,20,1,-1], + [23,27,8,2], [18,27,8,-1], [17,20,1,-1], [16,20,1,-2], [13,27,1,-1], [22,20,1,-1], [19,20,1,-1], [28,20,1,-1], + [21,20,1,-1], [20,28,9,-1], [18,20,1,-1], [11,20,1,-1], [16,28,9,1], [23,28,9,-3], [19,9,-21,-7], [17,6,-22,-10], + [16,66,115,165,214,264,313,363,412,462,511,561,610,660,709,759], + [55,104,154,203,253,302,352,401], + [813,434] + ], + cmsy10: [ + [26,3,-9,-3], [5,5,-8,-3], [20,20,0,-6], [16,18,-1,-2], [28,24,2,-2], [20,20,0], [28,28,0,-2], [28,28,7,-2], + [28,28,4,-2], [28,28,4,-2], [28,28,4,-2], [28,28,4,-2], [28,28,4,-2], [37,39,9,-2], [17,17,-2,-2], [17,17,-2,-2], + [28,20,0,-2], [28,18,-1,-2], [26,33,6,-3], [26,33,6,-3], [26,32,6,-3], [26,32,6,-3], [26,32,6,-3], [26,32,6,-3], + [28,10,-5,-2], [28,18,-2,-2], [26,25,2,-3], [26,25,2,-3], [37,27,3,-2], [37,27,3,-2], [26,24,2,-3], [26,24,2,-3], + [37,15,-3,-2], [37,15,-3,-2], [15,37,8,-3], [15,37,8,-3], [37,15,-3,-2], [37,37,8,-2], [37,37,8,-2], [28,18,-1,-2], + [37,23,1,-2], [37,23,1,-2], [23,37,8,-1], [23,37,8,-1], [39,23,1,-1], [37,37,8,-2], [37,37,8,-2], [28,20,1,-2], + [10,22,-1,-1], [37,20,1,-2], [21,25,2,-3], [21,25,2,-3], [32,30,0,-2], [32,30,9,-2], [22,39,9,-5], [3,16,-2,-2], + [23,30,1], [19,29,0,-2], [23,12,-3,-2], [18,36,4,-1], [28,31,1,-2], [27,30,1,-2], [28,28,0,-2], [28,28,0,-2], + [21,29,0,-2], [32,32,2,-1], [27,30,1,-1], [22,30,1], [32,28,0], [23,30,1,-1], [34,30,2], [24,34,5,-1], + [34,30,2], [27,28,0,1], [34,33,5,-1], [29,30,1,-1], [26,30,1,-1], [45,31,2,-1], [42,34,2,2], [30,30,1,-2], + [30,30,2], [29,35,6,-4], [35,29,1], [27,30,1], [32,30,0,-1], [30,30,2,1], [26,30,2,-1], [42,30,2,-1], + [32,28,0,-2], [29,34,6,-1], [31,28,0,-1], [23,26,1,-2], [23,26,1,-2], [23,26,1,-2], [23,26,1,-2], [23,26,1,-2], + [21,29,0,-2], [21,29,0,-2], [11,42,11,-7], [11,42,11], [11,42,11,-7], [10,42,11,-1], [15,42,11,-3], [15,42,11,-3], + [10,42,11,-4], [10,42,11,-2], [3,42,11,-4], [10,42,11,-5], [15,44,12,-3], [23,44,12,-1], [17,42,11,-2], [7,28,4,-2], + [32,42,40,-3], [29,28,0,-1], [31,30,2,-2], [18,39,9,-2], [23,25,0,-2], [23,25,0,-2], [27,33,6,-3], [27,33,6,-2], + [14,38,9,-2], [14,38,9,-2], [14,38,9,-2], [22,37,8,-2], [30,36,6,-1], [28,37,7,-2], [28,32,2,-2], [28,36,6,-2], + [17,77,136,196,255,314,374,433,493,552,612,671,730,790,849,909], + [58,142,227,311,396,481,565,650], + [974,721] + ], + cmex10: [ + [11,50,48,-6], [12,50,48,-1], [9,50,48,-8], [9,50,48], [11,50,48,-8], [11,50,48], [11,50,48,-8], [11,50,48], + [16,50,48,-4], [16,50,48,-4], [12,50,48,-4], [13,50,48,-3], [3,27,26,-5], [12,27,26,-5], [20,50,48,-2], [20,50,48,-2], + [16,75,73,-7], [17,75,73,-1], [21,99,97,-8], [21,99,97,-1], [11,99,97,-10], [12,99,97], [14,99,97,-10], [14,99,97], + [14,99,97,-10], [14,99,97], [21,99,97,-5], [21,99,97,-5], [22,99,97,-5], [22,99,97,-4], [39,99,97,-2], [39,99,97,-2], + [22,124,122,-9], [22,124,122,-1], [13,124,122,-11], [13,124,122], [15,124,122,-11], [15,124,122], [15,124,122,-11], [15,124,122], + [23,124,122,-5], [23,124,122,-5], [24,124,122,-5], [24,124,122,-4], [48,124,122,-2], [48,124,122,-2], [29,75,73,-2], [29,74,72,-2], + [24,75,73,-11], [23,75,73,-1], [14,75,73,-13], [14,75,73], [14,75,73,-13], [14,75,73], [4,25,25,-13], [3,25,25,-11], + [15,38,38,-15], [14,38,38,-7], [15,38,37,-15], [14,38,37,-7], [14,76,75,-7], [15,76,75,-15], [6,14,13,-15], [3,25,25,-12], + [24,75,73,-11], [23,75,73,-1], [6,26,25,-11], [5,26,25,-19], [18,75,73,-4], [18,75,73,-3], [30,41,41,-2], [42,58,58,-2], + [23,46,46,-2], [37,92,92,-2], [42,41,41,-2], [58,58,58,-2], [42,41,41,-2], [58,58,58,-2], [42,41,41,-2], [58,58,58,-2], + [39,41,41,-2], [35,41,41,-2], [23,46,46,-2], [30,41,41,-2], [30,41,41,-2], [30,41,41,-2], [30,41,41,-2], [30,41,41,-2], + [55,58,58,-2], [49,58,58,-2], [37,92,92,-2], [42,58,58,-2], [42,58,58,-2], [42,58,58,-2], [42,58,58,-2], [42,58,58,-2], + [35,41,41,-2], [49,58,58,-2], [23,8,-23], [41,9,-23], [60,9,-23], [23,6,-24], [41,6,-25], [59,6,-25], + [10,75,73,-9], [10,75,73], [12,75,73,-9], [13,75,73], [12,75,73,-9], [13,75,73], [19,75,73,-4], [19,75,73,-4], + [38,50,48,-4], [38,75,73,-4], [38,99,97,-4], [38,124,122,-4], [27,75,74,-4], [3,27,26,-28], [16,26,24,-28], [12,25,25,-10], + [19,25,25,-4], [19,25,25,-4], [20,14,9,1], [21,14,9,1], [20,14,0,1], [21,14,0,1], [28,25,25,-2], [28,25,25,-2], + [19,93,168,243,318,392,467,542,617,692,766,841,916,991,1066,1140], + [66,249,433,617,800,984,1167,1351], + [1222,1530] + ], + cmbx10: [ + [26,28,0,-1], [35,29,0,-2], [32,30,1,-2], [31,29,0,-1], [28,28,0,-2], [35,28,0,-1], [30,29,0,-2], [32,29,0,-2], + [30,29,0,-2], [32,29,0,-2], [30,29,0,-2], [30,29,0,-1], [24,29,0,-1], [24,29,0,-1], [37,29,0,-1], [37,29,0,-1], + [11,19,0,-1], [14,28,9,3], [10,8,-21,-4], [10,8,-21,-9], [13,6,-21,-5], [15,9,-20,-4], [18,3,-22,-3], [10,7,-22,-13], + [13,9,9,-5], [23,30,1,-1], [32,20,1,-1], [35,20,1,-1], [22,28,5,-1], [41,29,0,-1], [44,30,1,-3], [32,34,3,-2], + [14,6,-11], [7,29,0,-4], [18,15,-14,-1], [35,37,8,-2], [19,34,3,-2], [35,34,3,-2], [33,30,1,-2], [8,15,-14,-3], + [12,42,11,-4], [12,42,11,-2], [18,19,-12,-3], [32,32,6,-2], [8,15,8,-3], [13,5,-7], [7,7,0,-3], [19,42,11,-2], + [21,28,1,-1], [18,27,0,-3], [20,27,0,-2], [20,28,1,-2], [22,27,0,-1], [20,28,1,-2], [20,28,1,-2], [21,29,1,-2], + [20,28,1,-2], [20,28,1,-2], [7,19,0,-3], [7,27,8,-3], [7,30,9,-4], [32,13,-4,-2], [18,30,9,-2], [18,29,0,-2], + [32,30,1,-2], [33,29,0,-1], [30,29,0,-1], [30,30,1,-2], [33,29,0,-1], [29,28,0,-1], [27,28,0,-1], [33,30,1,-2], + [35,29,0,-1], [16,29,0,-1], [21,30,1,-1], [34,29,0,-1], [26,29,0,-1], [43,29,0,-1], [35,29,0,-1], [31,30,1,-2], + [29,29,0,-1], [31,37,8,-2], [35,30,1,-1], [22,30,1,-2], [30,28,0,-1], [34,30,1,-1], [34,30,1,-1], [47,30,1,-1], + [34,29,0,-1], [35,29,0], [25,29,0,-2], [7,42,11,-5], [19,15,-14,-5], [7,42,11,-1], [13,8,-21,-5], [7,7,-22,-3], + [8,15,-14,-2], [22,20,1,-1], [24,30,1,-1], [19,20,1,-1], [24,30,1,-1], [20,20,1,-1], [17,29,0,-1], [22,28,9,-1], + [25,29,0,-1], [11,29,0,-1], [14,38,9,3], [24,29,0,-1], [11,29,0,-1], [38,19,0,-1], [25,19,0,-1], [22,20,1,-1], + [24,27,8,-1], [24,27,8,-1], [18,19,0,-1], [16,20,1,-1], [16,27,1], [25,20,1,-1], [23,20,1,-1], [32,20,1,-1], + [24,19,0], [24,28,9], [18,19,0,-1], [24,2,-10], [48,2,-10], [16,9,-21,-5], [16,5,-24,-4], [16,7,-22,-4], + [17,76,135,194,253,311,370,429,488,547,606,664,723,782,841,900], + [55,104,154,203,253,302,352,401], + [964,434] + ], + cmti10: [ + [27,28,0,-2], [28,30,0,-3], [27,30,1,-6], [25,30,0,-2], [28,28,0,-3], [33,28,0,-2], [29,28,0,-3], [27,29,0,-8], + [24,28,0,-6], [26,28,0,-8], [28,29,0,-4], [32,38,9,1], [26,38,9,1], [27,38,9,1], [39,38,9,1], [40,38,9,1], + [11,20,1,-3], [16,28,9,2], [6,9,-20,-12], [9,9,-20,-14], [11,6,-20,-11], [13,8,-21,-11], [15,3,-22,-9], [9,8,-22,-19], + [10,8,8,-4], [25,38,9,1], [27,20,1,-3], [26,20,1,-4], [21,27,5,-2], [37,28,0,-2], [37,30,1,-6], [30,34,3,-4], + [11,5,-11,-3], [12,30,0,-4], [14,13,-16,-7], [30,37,8,-4], [26,30,1,-3], [29,34,3,-6], [28,31,1,-5], [7,13,-16,-9], + [16,42,11,-6], [16,42,11], [16,18,-13,-8], [26,26,3,-5], [7,13,8,-3], [11,3,-7,-3], [6,5,0,-4], [26,42,11], + [19,29,1,-4], [15,28,0,-4], [20,29,1,-3], [20,29,1,-3], [19,36,8,-1], [20,29,1,-4], [20,29,1,-4], [20,29,1,-6], + [19,29,1,-4], [19,29,1,-4], [9,18,0,-4], [10,26,8,-3], [12,30,9,-2], [28,10,-5,-4], [16,30,9,-3], [15,30,0,-8], + [27,30,1,-6], [27,30,0,-2], [29,28,0,-2], [28,30,1,-6], [30,28,0,-2], [29,28,0,-2], [28,28,0,-2], [28,30,1,-6], + [33,28,0,-2], [19,28,0,-2], [23,29,1,-3], [34,28,0,-2], [24,28,0,-2], [39,28,0,-2], [33,28,0,-2], [27,30,1,-6], + [28,28,0,-2], [27,37,8,-6], [28,29,1,-2], [23,30,1,-3], [26,28,0,-7], [27,29,1,-8], [28,29,1,-8], [37,29,1,-8], + [40,28,0,6], [28,28,0,-8], [26,28,0,-3], [16,42,11,-3], [14,13,-16,-11], [16,42,11,1], [11,7,-22,-11], [5,5,-23,-10], + [7,13,-16,-8], [18,20,1,-4], [15,30,1,-4], [16,20,1,-4], [19,30,1,-4], [16,20,1,-4], [20,38,9,1], [18,28,9,-2], + [19,30,1,-3], [11,28,1,-3], [17,36,9,2], [18,30,1,-3], [10,30,1,-3], [32,20,1,-3], [21,20,1,-3], [17,20,1,-4], + [21,27,8], [17,27,8,-4], [17,20,1,-3], [15,20,1,-3], [13,27,1,-3], [20,20,1,-3], [18,20,1,-3], [26,20,1,-3], + [20,20,1,-2], [19,28,9,-3], [17,20,1,-2], [20,2,-10,-3], [38,2,-10,-5], [13,9,-20,-11], [14,5,-23,-10], [12,5,-23,-11], + [17,67,118,168,219,270,320,371,421,472,522,573,624,674,725,775], + [55,104,154,203,253,302,352,401], + [831,434] + ] +}); + +jsMath.Img.AddFont(358,{ + cmr10: [ + [28,34,0,-1], [37,35,0,-2], [34,36,1,-2], [32,35,0,-1], [29,34,0,-2], [35,34,0,-1], [31,34,0,-2], [34,35,0,-2], + [31,34,0,-2], [34,34,0,-2], [32,35,0,-2], [30,35,0,-1], [25,35,0,-1], [25,35,0,-1], [39,35,0,-1], [39,35,0,-1], + [12,22,0,-1], [13,32,10,2], [10,10,-25,-5], [10,10,-25,-10], [13,7,-25,-6], [16,9,-25,-4], [18,2,-27,-3], [10,10,-26,-13], + [12,9,10,-6], [23,36,1,-1], [32,23,1,-2], [36,23,1,-1], [22,31,5,-1], [42,34,0,-1], [46,37,2,-3], [34,39,3,-2], + [12,6,-13,-1], [6,35,0,-4], [16,15,-19,-1], [36,44,10,-2], [20,40,3,-2], [36,40,3,-2], [34,38,2,-2], [7,15,-19,-4], + [13,50,13,-4], [13,50,13,-2], [19,22,-15,-3], [34,33,4,-2], [6,16,10,-4], [14,3,-9], [6,6,0,-4], [20,50,13,-2], + [22,35,2,-1], [17,33,0,-4], [20,33,0,-2], [21,35,2,-2], [23,34,0,-1], [20,35,2,-2], [21,35,2,-2], [22,35,1,-2], + [21,35,2,-2], [21,34,1,-2], [6,22,0,-4], [6,32,10,-4], [6,36,11,-4], [34,12,-6,-2], [19,35,10,-2], [19,35,0,-2], + [34,36,1,-2], [35,35,0,-1], [31,34,0,-1], [31,37,2,-2], [34,34,0,-1], [31,34,0,-1], [29,34,0,-1], [34,37,2,-2], + [35,34,0,-1], [16,34,0,-1], [21,35,1,-2], [36,34,0,-1], [28,34,0,-1], [43,34,0,-1], [35,34,0,-1], [34,36,1,-2], + [30,34,0,-1], [34,45,10,-2], [35,35,1,-1], [23,37,2,-2], [33,34,0,-1], [35,35,1,-1], [36,35,1], [50,35,1], + [35,34,0,-1], [37,34,0], [26,34,0,-2], [8,50,13,-5], [16,15,-19,-7], [7,50,13,-1], [14,8,-26,-5], [6,6,-27,-4], + [7,15,-19,-3], [23,23,1,-2], [25,35,1,-1], [20,23,1,-1], [25,35,1,-1], [20,23,1,-1], [17,35,0,-1], [23,34,11,-1], + [26,34,0,-1], [12,33,0,-1], [13,43,10,2], [24,34,0,-1], [12,34,0,-1], [39,22,0,-1], [26,22,0,-1], [23,23,1,-1], + [25,32,10,-1], [25,32,10,-1], [17,22,0,-1], [17,23,1,-1], [16,32,1,-1], [26,23,1,-1], [25,23,1], [35,23,1], + [26,22,0], [25,32,10], [19,22,0,-1], [25,2,-12], [49,2,-12], [15,10,-25,-6], [17,5,-28,-4], [15,6,-27,-5], + [20,81,142,203,264,325,387,448,509,570,631,692,753,814,875,937], + [64,124,183,243,302,362,421,481], + [1003,521] + ], + cmmi10: [ + [34,34,0,-2], [37,35,0,-2], [35,37,2,-2], [32,35,0,-1], [36,34,0,-2], [42,34,0,-2], [38,34,0,-2], [34,35,0,-1], + [31,34,0,-1], [33,34,0,-1], [36,35,0,-3], [28,23,1,-2], [28,45,10,-1], [27,33,11], [21,36,1,-2], [17,23,1,-2], + [22,45,10,-2], [24,33,11,-1], [21,36,1,-2], [14,23,1,-2], [25,23,1,-2], [25,35,1,-2], [27,33,11,-1], [24,22,0,-2], + [21,45,10,-1], [27,23,1,-1], [24,33,11,-1], [27,23,1,-1], [24,23,1,-1], [25,23,1,-1], [26,44,10,-2], [28,32,10,-1], + [31,44,10,-1], [30,23,1], [20,25,2,-1], [27,36,1,-1], [39,23,1,-1], [22,32,10,-3], [19,28,6,-1], [29,33,11,-2], + [45,14,-11,-2], [45,15,1,-2], [45,14,-11,-2], [45,15,1,-2], [9,12,-11,-2], [9,12,-11,-2], [23,26,1,-1], [23,26,1,-1], + [21,25,2,-2], [17,23,0,-4], [20,23,0,-2], [21,34,11,-2], [22,33,10,-1], [20,34,11,-2], [21,35,2,-2], [22,34,11,-2], + [21,35,2,-2], [21,34,11,-2], [6,6,0,-4], [6,16,10,-4], [30,29,2,-4], [20,50,13,-2], [30,29,2,-4], [25,24,0], + [26,36,1,-2], [35,35,0,-1], [35,34,0,-2], [36,37,2,-2], [38,34,0,-2], [37,34,0,-1], [36,34,0,-1], [36,37,2,-2], + [42,34,0,-2], [24,34,0,-1], [28,35,1,-3], [43,34,0,-1], [30,34,0,-2], [50,34,0,-2], [42,34,0,-2], [35,37,2,-2], + [35,34,0,-2], [35,45,10,-2], [35,35,1,-2], [30,37,2,-2], [34,34,0,-1], [35,36,2,-3], [36,35,1,-2], [50,35,1,-2], + [41,34,0,-1], [37,34,0,-1], [34,34,0,-2], [15,38,1,-2], [13,47,11,-3], [15,46,11,-2], [45,13,-6,-2], [45,13,-6,-2], + [20,36,1], [23,23,1,-2], [19,35,1,-2], [19,23,1,-2], [24,35,1,-2], [19,23,1,-2], [25,45,10,-2], [24,32,10], + [25,35,1,-2], [14,34,1,-1], [21,43,10,1], [23,35,1,-2], [11,35,1,-2], [41,23,1,-1], [27,23,1,-1], [21,23,1,-2], + [26,32,10,2], [20,32,10,-2], [21,23,1,-1], [19,23,1,-2], [16,32,1,-1], [26,23,1,-1], [22,23,1,-1], [33,23,1,-1], + [25,23,1,-1], [23,32,10,-1], [21,23,1,-2], [14,23,1,-1], [19,32,10,1], [28,34,11,-3], [22,10,-25,-9], [19,7,-26,-13], + [20,79,139,198,258,317,377,436,495,555,614,674,733,793,852,912], + [64,124,183,243,302,362,421,481], + [977,521] + ], + cmsy10: [ + [30,3,-11,-4], [6,6,-9,-4], [24,24,0,-7], [19,22,-1,-3], [34,28,2,-2], [24,24,0], [34,33,0,-2], [34,34,9,-2], + [34,33,4,-2], [34,33,4,-2], [34,33,4,-2], [34,33,4,-2], [34,33,4,-2], [45,47,11,-2], [20,20,-2,-2], [20,20,-2,-2], + [34,24,0,-2], [34,22,-1,-2], [30,39,7,-4], [30,39,7,-4], [30,38,7,-4], [30,39,7,-4], [30,38,7,-4], [30,39,7,-4], + [34,12,-6,-2], [34,22,-2,-2], [30,29,2,-4], [30,29,2,-4], [45,32,4,-2], [45,32,4,-2], [30,29,2,-4], [30,29,2,-4], + [45,18,-3,-2], [45,18,-3,-2], [18,44,10,-3], [18,44,10,-3], [45,18,-3,-2], [45,45,10,-2], [45,44,10,-2], [34,22,-1,-2], + [45,28,2,-2], [45,28,2,-2], [28,44,10,-1], [28,44,10,-1], [47,28,2,-1], [45,45,10,-2], [45,44,10,-2], [34,23,1,-2], + [12,26,-2,-1], [45,23,1,-2], [25,29,2,-4], [25,29,2,-4], [38,35,0,-3], [39,36,11,-2], [26,46,11,-6], [4,19,-3,-2], + [28,35,1], [23,34,0,-2], [28,14,-4,-2], [21,42,4,-2], [33,38,2,-2], [32,36,1,-2], [34,33,0,-2], [34,33,0,-2], + [26,34,0,-2], [38,39,3,-1], [32,37,2,-1], [27,37,2], [37,34,0,-1], [27,37,2,-1], [41,36,2], [28,41,6,-2], + [39,37,3,-1], [33,34,0,2], [39,40,6,-2], [35,37,2,-1], [31,37,2,-1], [54,38,3,-1], [50,41,3,2], [37,37,2,-2], + [35,37,3,-1], [34,42,7,-5], [40,36,2,-1], [32,37,2], [38,36,0,-1], [35,36,2,1], [32,37,3,-1], [50,37,3,-1], + [38,34,0,-2], [35,41,7,-1], [37,34,0,-1], [28,32,2,-2], [28,31,1,-2], [28,32,2,-2], [28,31,1,-2], [28,31,1,-2], + [25,34,0,-2], [26,34,0,-2], [13,50,13,-8], [13,50,13,-1], [13,50,13,-8], [13,50,13,-1], [18,50,13,-3], [18,50,13,-3], + [12,50,13,-5], [12,50,13,-2], [3,50,13,-5], [12,50,13,-6], [18,52,14,-3], [28,52,14,-1], [20,50,13,-2], [9,33,4,-2], + [39,49,47,-3], [34,34,0,-1], [37,36,2,-2], [21,46,11,-2], [27,30,0,-3], [27,30,0,-3], [31,39,7,-4], [31,39,7,-3], + [16,45,10,-3], [17,46,11,-2], [17,45,10,-2], [27,44,10,-2], [36,43,7,-1], [34,44,8,-2], [34,38,2,-2], [34,43,7,-2], + [21,92,164,235,306,378,449,521,592,663,735,806,878,949,1020,1092], + [68,170,271,373,475,576,678,780], + [1170,865] + ], + cmex10: [ + [14,59,57,-7], [13,59,57,-2], [11,59,57,-9], [10,59,57,-1], [13,59,57,-9], [13,59,57,-1], [13,59,57,-9], [13,59,57,-1], + [18,59,57,-5], [18,59,57,-5], [16,59,57,-4], [16,59,57,-3], [3,32,31,-7], [14,32,31,-7], [24,59,57,-2], [24,59,57,-2], + [20,89,87,-8], [20,89,87,-1], [25,118,116,-10], [25,118,116,-1], [14,118,116,-12], [14,118,116], [16,118,116,-12], [17,118,116], + [16,118,116,-12], [17,118,116], [25,118,116,-6], [25,118,116,-6], [26,118,116,-6], [27,118,116,-4], [47,118,116,-2], [47,118,116,-2], + [26,147,145,-11], [27,147,145,-1], [15,147,145,-13], [15,147,145], [18,147,145,-13], [18,147,145], [18,147,145,-13], [18,147,145], + [26,147,145,-7], [26,147,145,-7], [29,147,145,-6], [28,147,145,-5], [58,147,145,-2], [58,147,145,-2], [35,89,87,-2], [35,89,87,-2], + [28,89,87,-14], [28,89,87,-1], [17,89,87,-16], [17,89,87], [17,89,87,-16], [17,89,87], [4,30,30,-16], [4,30,30,-13], + [18,45,45,-18], [17,45,45,-8], [18,45,44,-18], [17,45,44,-8], [17,90,89,-8], [18,90,89,-18], [7,17,16,-18], [3,30,30,-15], + [28,90,87,-14], [28,90,87,-1], [6,31,30,-14], [6,31,30,-23], [21,89,87,-5], [21,89,87,-4], [36,49,49,-2], [50,69,69,-2], + [28,55,55,-2], [45,109,109,-2], [50,49,49,-2], [70,69,69,-2], [50,49,49,-2], [70,69,69,-2], [50,49,49,-2], [70,69,69,-2], + [47,49,49,-2], [42,49,49,-2], [28,55,55,-2], [36,49,49,-2], [36,49,49,-2], [36,49,49,-2], [36,49,49,-2], [36,49,49,-2], + [66,69,69,-2], [58,69,69,-2], [45,109,109,-2], [50,69,69,-2], [50,69,69,-2], [50,69,69,-2], [50,69,69,-2], [50,69,69,-2], + [42,49,49,-2], [58,69,69,-2], [29,10,-27,1], [49,10,-28], [71,10,-28], [27,7,-29], [49,7,-30], [71,7,-30], + [12,89,87,-11], [12,89,87], [14,89,87,-11], [15,89,87], [14,89,87,-11], [15,89,87], [22,89,87,-5], [22,89,87,-5], + [45,59,57,-5], [45,89,87,-5], [45,118,116,-5], [45,147,145,-5], [32,90,89,-5], [3,32,31,-34], [19,31,29,-34], [14,30,30,-12], + [23,30,30,-5], [23,30,30,-5], [25,17,11,2], [25,17,11,1], [25,17,0,2], [25,17,0,1], [34,30,30,-2], [34,29,29,-2], + [22,112,202,292,382,472,561,651,741,831,921,1011,1100,1190,1280,1370], + [78,298,519,740,960,1181,1401,1622], + [1468,1836] + ], + cmbx10: [ + [31,34,0,-1], [42,35,0,-2], [38,36,1,-3], [36,35,0,-2], [34,34,0,-2], [42,34,0,-1], [35,34,0,-3], [38,35,0,-3], + [35,34,0,-3], [38,34,0,-3], [37,35,0,-2], [36,35,0,-1], [29,35,0,-1], [29,35,0,-1], [45,35,0,-1], [45,35,0,-1], + [12,22,0,-2], [17,32,10,3], [12,10,-25,-5], [12,10,-25,-11], [16,7,-25,-6], [18,10,-24,-5], [22,3,-27,-3], [13,9,-26,-15], + [16,10,10,-6], [27,36,1,-1], [39,24,1,-1], [42,24,1,-1], [26,32,5,-1], [48,34,0,-2], [52,36,1,-4], [38,40,3,-3], + [15,7,-13,-1], [9,35,0,-4], [22,18,-16,-1], [41,44,10,-3], [22,40,3,-3], [41,40,3,-3], [39,36,1,-2], [9,18,-16,-4], + [14,50,13,-5], [14,50,13,-3], [22,22,-15,-3], [38,38,7,-3], [9,18,10,-4], [16,6,-8], [8,8,0,-4], [22,50,13,-3], + [24,34,1,-2], [21,32,0,-4], [24,33,0,-2], [24,34,1,-2], [26,33,0,-1], [24,33,1,-2], [24,34,1,-2], [25,35,1,-3], + [24,34,1,-2], [24,34,1,-2], [8,22,0,-4], [8,32,10,-4], [9,35,10,-4], [38,15,-5,-3], [21,35,10,-3], [21,35,0,-3], + [38,36,1,-3], [39,35,0,-2], [36,34,0,-1], [35,36,1,-3], [39,34,0,-1], [35,34,0,-1], [32,34,0,-1], [39,36,1,-3], + [42,34,0,-1], [19,34,0,-1], [25,35,1,-1], [41,34,0,-1], [31,34,0,-1], [50,34,0,-2], [42,34,0,-1], [37,36,1,-3], + [35,34,0,-1], [37,45,10,-3], [41,35,1,-1], [26,36,1,-3], [35,34,0,-2], [41,35,1,-1], [41,35,1,-1], [56,35,1,-1], + [40,34,0,-1], [42,34,0], [29,34,0,-3], [9,50,13,-6], [22,18,-16,-6], [9,50,13,-1], [16,8,-26,-6], [8,8,-26,-4], + [9,18,-16,-3], [27,24,1,-1], [29,35,1,-1], [23,24,1,-1], [29,35,1,-1], [24,24,1,-1], [20,35,0,-2], [27,33,10,-1], + [29,34,0,-2], [12,34,0,-2], [17,44,10,3], [28,34,0,-1], [13,34,0,-2], [44,22,0,-2], [29,22,0,-2], [26,24,1,-1], + [29,32,10,-1], [29,32,10,-1], [21,22,0,-1], [20,24,1,-1], [18,33,1,-1], [29,23,1,-2], [28,23,1,-1], [39,23,1,-1], + [28,22,0,-1], [28,32,10,-1], [22,22,0,-1], [29,3,-12], [57,3,-12], [17,10,-25,-7], [20,6,-28,-4], [19,7,-27,-5], + [21,91,162,233,303,374,445,516,586,657,728,798,869,940,1010,1081], + [64,124,183,243,302,362,421,481], + [1158,521] + ], + cmti10: [ + [32,34,0,-3], [34,35,0,-3], [32,37,2,-7], [30,35,0,-2], [34,34,0,-3], [39,34,0,-3], [35,34,0,-4], [31,35,0,-10], + [29,34,0,-7], [31,34,0,-10], [34,35,0,-4], [40,45,10,2], [32,45,10,2], [34,45,10,2], [47,45,10,2], [49,45,10,2], + [13,23,1,-4], [18,32,10,2], [8,10,-24,-14], [11,11,-24,-16], [14,7,-24,-13], [15,9,-25,-13], [17,2,-27,-11], [10,9,-26,-23], + [12,10,10,-5], [30,45,10,1], [32,23,1,-4], [31,23,1,-5], [24,33,6,-3], [45,34,0,-2], [44,37,2,-8], [36,40,3,-5], + [13,7,-13,-4], [14,35,0,-5], [18,15,-19,-8], [36,44,10,-5], [31,36,1,-4], [35,40,3,-7], [34,37,2,-6], [9,15,-19,-10], + [19,50,13,-7], [18,50,13,-1], [20,22,-15,-9], [31,31,3,-6], [8,16,10,-3], [13,3,-9,-4], [6,6,0,-5], [30,50,13,-1], + [23,35,2,-5], [18,33,0,-5], [23,34,1,-4], [24,35,2,-4], [22,43,10,-2], [23,34,1,-5], [23,34,1,-5], [24,34,1,-7], + [24,35,2,-4], [23,34,1,-5], [10,22,0,-5], [12,32,10,-3], [14,36,11,-2], [33,12,-6,-5], [18,36,11,-4], [18,35,0,-9], + [32,36,1,-7], [32,35,0,-2], [33,34,0,-3], [33,37,2,-7], [35,34,0,-3], [34,34,0,-3], [33,34,0,-3], [33,37,2,-7], + [39,34,0,-3], [23,34,0,-2], [27,36,2,-4], [39,34,0,-3], [28,34,0,-3], [46,34,0,-3], [39,34,0,-3], [32,37,2,-7], + [33,34,0,-3], [32,45,10,-7], [33,36,2,-3], [28,37,2,-3], [32,34,0,-8], [33,36,2,-9], [33,35,1,-10], [45,36,2,-10], + [48,34,0,7], [34,34,0,-9], [31,34,0,-4], [19,50,13,-3], [17,15,-19,-13], [19,50,13,1], [13,8,-26,-13], [6,6,-27,-12], + [8,15,-19,-10], [22,23,1,-5], [18,35,1,-5], [18,23,1,-5], [23,35,1,-5], [18,23,1,-5], [25,45,10,2], [22,32,10,-2], + [24,35,1,-3], [13,34,1,-4], [20,43,10,2], [22,35,1,-3], [11,35,1,-4], [38,23,1,-4], [25,23,1,-4], [20,23,1,-5], + [25,32,10], [20,32,10,-5], [20,23,1,-4], [18,23,1,-3], [15,32,1,-4], [24,23,1,-4], [21,23,1,-4], [31,23,1,-4], + [24,23,1,-2], [22,32,10,-4], [20,23,1,-3], [23,2,-12,-4], [45,2,-12,-6], [16,11,-24,-13], [16,6,-27,-12], [14,6,-27,-13], + [20,81,141,202,263,324,385,445,506,567,628,688,749,810,871,932], + [64,124,183,243,302,362,421,481], + [998,521] + ] +}); + +jsMath.Img.AddFont(430,{ + cmr10: [ + [33,41,0,-2], [45,43,0,-2], [40,44,2,-3], [38,43,0,-1], [35,40,0,-2], [41,41,0,-2], [37,41,0,-3], [40,42,0,-3], + [37,41,0,-3], [40,41,0,-3], [38,42,0,-2], [36,42,0,-1], [31,42,0,-1], [31,42,0,-1], [47,42,0,-1], [47,42,0,-1], + [13,26,0,-2], [16,39,13,3], [12,12,-30,-6], [11,12,-30,-12], [16,8,-30,-7], [19,11,-30,-5], [22,2,-33,-4], [12,11,-32,-16], + [15,11,12,-7], [27,43,1,-1], [39,28,1,-2], [44,28,1,-1], [26,38,6,-2], [51,41,0,-1], [54,44,2,-4], [40,48,4,-3], + [15,7,-16,-1], [7,43,0,-5], [19,18,-23,-2], [43,53,12,-3], [24,49,4,-3], [43,49,4,-3], [41,45,2,-2], [8,18,-23,-5], + [15,60,15,-5], [14,60,15,-3], [23,27,-18,-3], [40,40,5,-3], [7,19,12,-5], [17,4,-11], [7,7,0,-5], [24,60,15,-3], + [26,42,2,-2], [20,40,0,-5], [24,40,0,-3], [25,42,2,-2], [27,40,0,-1], [24,42,2,-3], [25,42,2,-2], [26,42,2,-3], + [25,42,2,-2], [25,42,2,-2], [7,26,0,-5], [7,38,12,-5], [7,43,13,-5], [40,15,-7,-3], [22,43,13,-3], [22,42,0,-3], + [40,43,1,-3], [42,43,0,-1], [37,41,0,-2], [37,44,2,-3], [40,41,0,-2], [37,41,0,-2], [34,41,0,-2], [41,44,2,-3], + [41,41,0,-2], [19,41,0,-1], [26,43,2,-2], [42,41,0,-2], [33,41,0,-2], [50,41,0,-2], [41,41,0,-2], [40,44,2,-3], + [35,41,0,-2], [40,54,12,-3], [42,43,2,-2], [27,44,2,-3], [39,40,0,-2], [41,43,2,-2], [43,43,2,-1], [59,43,2,-1], + [42,41,0,-1], [44,41,0], [30,41,0,-3], [8,60,15,-7], [19,18,-23,-9], [9,60,15,-1], [16,9,-32,-7], [7,7,-33,-5], + [8,18,-23,-4], [28,28,1,-2], [30,42,1,-1], [23,28,1,-2], [30,42,1,-2], [24,28,1,-1], [19,42,0,-2], [28,40,13,-1], + [31,41,0,-1], [13,40,0,-2], [16,53,13,3], [30,41,0,-1], [13,41,0,-2], [47,27,0,-1], [31,27,0,-1], [27,28,1,-1], + [30,39,12,-1], [30,39,12,-2], [21,26,0,-1], [20,28,1,-2], [19,38,1,-1], [31,27,1,-1], [29,27,1,-1], [41,27,1,-1], + [31,26,0], [29,39,13,-1], [23,26,0,-1], [30,2,-15], [59,2,-15], [18,12,-30,-7], [20,7,-33,-5], [18,7,-33,-6], + [24,97,171,244,317,391,464,538,611,684,758,831,904,978,1051,1125], + [77,149,220,292,363,434,506,577], + [1205,625] + ], + cmmi10: [ + [41,41,0,-2], [44,43,0,-3], [42,44,2,-2], [38,43,0,-2], [43,40,0,-3], [50,41,0,-2], [45,41,0,-3], [41,42,0,-1], + [37,41,0,-1], [40,41,0,-1], [43,42,0,-4], [34,28,1,-2], [34,54,12,-1], [31,40,13,-1], [25,43,1,-2], [21,27,1,-2], + [26,55,13,-2], [29,40,13,-1], [25,43,1,-2], [17,27,1,-3], [30,27,1,-3], [30,42,1,-3], [33,39,13,-1], [28,26,0,-3], + [26,54,13,-1], [33,27,1,-1], [29,40,13,-1], [32,27,1,-2], [30,27,1,-1], [30,27,1,-1], [32,53,12,-2], [34,39,13,-1], + [37,53,12,-1], [36,27,1], [25,29,2,-1], [32,43,1,-1], [48,27,1,-1], [26,39,12,-4], [24,34,7,-1], [34,39,13,-3], + [53,18,-13,-3], [53,17,1,-3], [53,18,-13,-3], [53,17,1,-3], [10,15,-13,-3], [11,15,-13,-3], [27,31,1,-1], [27,31,1,-1], + [26,29,2,-2], [20,27,0,-5], [25,27,0,-2], [25,40,13,-2], [27,40,12,-1], [24,40,13,-3], [25,42,2,-2], [26,41,13,-3], + [25,42,2,-2], [25,40,13,-2], [7,7,0,-5], [7,19,12,-5], [37,35,3,-4], [24,60,15,-3], [37,35,3,-4], [30,28,-1], + [32,45,2,-2], [41,43,0,-2], [43,41,0,-2], [42,44,2,-3], [46,41,0,-2], [43,41,0,-2], [43,41,0,-2], [42,44,2,-3], + [50,41,0,-2], [28,41,0,-2], [34,43,2,-4], [51,41,0,-2], [36,41,0,-2], [60,41,0,-2], [50,41,0,-2], [42,44,2,-2], + [43,41,0,-2], [42,54,12,-2], [43,43,2,-2], [35,44,2,-3], [41,40,0,-1], [41,43,2,-4], [43,43,2,-3], [59,43,2,-3], + [50,41,0,-1], [43,41,0,-2], [40,41,0,-3], [17,47,2,-3], [15,56,13,-4], [17,56,13,-3], [53,15,-7,-3], [53,16,-7,-3], + [24,43,1], [28,28,1,-2], [23,42,1,-2], [24,28,1,-2], [29,42,1,-2], [24,28,1,-2], [30,54,12,-3], [28,40,13], + [30,42,1,-3], [17,40,1,-1], [25,52,13,1], [27,42,1,-3], [14,42,1,-2], [49,28,1,-1], [33,28,1,-1], [26,28,1,-2], + [31,38,12,2], [25,39,12,-2], [25,28,1,-1], [22,28,1,-3], [19,38,1,-1], [31,27,1,-1], [27,27,1,-1], [40,27,1,-1], + [31,28,1,-1], [28,39,13,-1], [26,27,1,-2], [17,27,1,-1], [23,39,13,1], [33,40,13,-4], [27,13,-30,-10], [24,9,-31,-15], + [24,95,167,238,309,381,452,524,595,666,738,809,880,952,1023,1095], + [77,149,220,292,363,434,506,577], + [1172,625] + ], + cmsy10: [ + [36,3,-13,-5], [7,7,-11,-5], [30,29,0,-8], [23,26,-2,-3], [40,34,2,-3], [29,29,0], [40,40,0,-3], [40,40,10,-3], + [40,40,5,-3], [40,40,5,-3], [40,40,5,-3], [40,40,5,-3], [40,40,5,-3], [53,56,13,-3], [24,24,-3,-3], [24,24,-3,-3], + [40,28,-1,-3], [40,26,-2,-3], [37,47,9,-4], [37,47,9,-4], [37,47,9,-4], [37,47,9,-4], [37,47,9,-4], [37,47,9,-4], + [40,15,-7,-3], [40,26,-3,-3], [37,35,3,-4], [37,35,3,-4], [53,38,4,-3], [53,38,4,-3], [37,35,3,-4], [37,35,3,-4], + [53,22,-4,-3], [53,22,-4,-3], [22,53,12,-4], [22,53,12,-4], [53,22,-4,-3], [53,53,12,-3], [53,53,12,-3], [40,26,-2,-3], + [53,33,2,-3], [53,33,2,-3], [34,53,12,-1], [34,53,12,-1], [55,33,2,-2], [53,54,12,-3], [53,53,12,-3], [40,28,1,-3], + [15,31,-2,-1], [53,28,1,-3], [31,35,3,-4], [31,35,3,-4], [46,43,0,-3], [46,43,13,-3], [30,56,13,-8], [5,23,-3,-3], + [33,43,2], [27,41,0,-3], [33,16,-5,-3], [25,51,5,-2], [40,45,2,-2], [38,43,1,-3], [40,40,0,-3], [40,40,0,-3], + [30,41,0,-3], [46,46,3,-1], [39,44,2,-1], [32,44,2], [45,41,0,-1], [33,44,2,-1], [48,43,2,-1], [34,49,7,-2], + [48,44,3,-1], [40,41,0,2], [48,48,7,-2], [43,44,2,-1], [38,44,2,-1], [65,45,3,-1], [60,49,3,2], [43,44,2,-3], + [43,44,3,-1], [41,50,8,-6], [49,43,2,-1], [37,44,2,-1], [46,43,0,-1], [42,43,2,1], [37,44,3,-2], [60,44,3,-2], + [45,41,0,-3], [42,49,8,-1], [44,41,0,-2], [33,38,2,-3], [33,38,2,-3], [33,38,2,-3], [33,38,2,-3], [33,38,2,-3], + [30,41,0,-3], [30,41,0,-3], [15,60,15,-10], [15,60,15,-1], [15,60,15,-10], [15,60,15,-1], [22,60,15,-4], [22,60,15,-4], + [14,60,15,-6], [14,60,15,-3], [3,60,15,-7], [15,60,15,-7], [22,62,16,-4], [34,62,16,-1], [24,60,15,-3], [10,40,5,-3], + [47,60,57,-4], [40,41,0,-2], [45,43,2,-2], [25,56,13,-3], [33,36,0,-3], [33,36,0,-3], [38,47,9,-5], [38,47,9,-3], + [18,55,13,-4], [20,55,13,-3], [20,54,12,-3], [32,53,12,-3], [44,51,8,-1], [40,53,10,-3], [40,45,2,-3], [40,51,8,-3], + [25,111,196,282,368,454,539,625,711,797,882,968,1054,1140,1225,1311], + [82,204,326,448,570,692,814,936], + [1404,1039] + ], + cmex10: [ + [16,72,69,-9], [16,72,69,-2], [13,72,69,-11], [12,72,69,-1], [16,72,69,-11], [15,72,69,-1], [16,72,69,-11], [15,72,69,-1], + [22,72,69,-6], [22,72,69,-6], [19,72,69,-5], [18,72,69,-4], [4,39,37,-8], [17,39,37,-8], [28,72,69,-3], [28,72,69,-3], + [23,107,104,-10], [23,107,104,-2], [30,143,140,-12], [30,143,140,-2], [17,143,140,-14], [17,143,140], [20,143,140,-14], [20,143,140], + [20,143,140,-14], [20,143,140], [30,143,140,-7], [30,143,140,-7], [32,143,140,-7], [32,143,140,-5], [56,143,140,-3], [56,143,140,-3], + [32,178,175,-13], [31,178,175,-2], [18,178,175,-16], [19,178,175], [21,178,175,-16], [22,178,175], [21,178,175,-16], [22,178,175], + [31,178,175,-8], [31,178,175,-8], [34,178,175,-8], [34,178,175,-6], [69,178,175,-3], [69,178,175,-3], [42,107,104,-3], [42,107,104,-3], + [33,108,105,-17], [33,108,105,-2], [20,107,104,-19], [21,107,104], [20,107,104,-19], [21,107,104], [5,37,36,-19], [5,37,36,-16], + [21,54,54,-22], [20,54,54,-10], [21,54,53,-22], [20,54,53,-10], [20,108,107,-10], [21,108,107,-22], [8,20,19,-22], [3,37,36,-18], + [33,107,104,-17], [33,107,104,-2], [7,37,36,-17], [8,37,36,-27], [25,107,104,-6], [25,107,104,-5], [43,59,59,-3], [60,83,83,-3], + [33,66,66,-3], [53,132,132,-3], [60,59,59,-3], [83,83,83,-3], [60,59,59,-3], [83,83,83,-3], [60,59,59,-3], [83,83,83,-3], + [56,59,59,-3], [50,59,59,-3], [33,66,66,-3], [43,59,59,-3], [43,59,59,-3], [43,59,59,-3], [43,59,59,-3], [43,59,59,-3], + [79,83,83,-3], [69,83,83,-3], [53,132,132,-3], [60,83,83,-3], [60,83,83,-3], [60,83,83,-3], [60,83,83,-3], [60,83,83,-3], + [50,59,59,-3], [69,83,83,-3], [34,11,-33,1], [60,12,-34,1], [86,12,-34], [33,8,-35], [59,9,-36], [85,9,-36], + [14,107,104,-13], [14,107,104,-1], [17,107,104,-13], [17,107,104,-1], [17,107,104,-13], [17,107,104,-1], [26,107,104,-7], [26,107,104,-7], + [55,72,69,-6], [55,107,104,-6], [55,143,140,-6], [55,178,175,-6], [38,109,107,-6], [3,39,37,-41], [23,38,35,-41], [16,37,36,-15], + [27,36,36,-6], [27,36,36,-6], [30,21,13,2], [29,21,13,1], [30,20,0,2], [29,20,0,1], [40,36,36,-3], [40,35,35,-3], + [27,135,243,350,458,566,674,782,890,998,1106,1213,1321,1429,1537,1645], + [93,358,623,888,1153,1418,1683,1947], + [1762,2205] + ], + cmbx10: [ + [36,41,0,-2], [50,42,0,-3], [46,43,1,-3], [44,42,0,-2], [41,40,0,-2], [49,41,0,-2], [43,41,0,-3], [46,42,0,-3], + [43,41,0,-3], [46,41,0,-3], [43,42,0,-3], [43,42,0,-1], [35,42,0,-1], [35,42,0,-1], [54,42,0,-1], [54,42,0,-1], + [15,27,0,-2], [20,39,12,4], [13,12,-30,-7], [13,12,-30,-14], [18,9,-30,-8], [22,12,-29,-6], [26,4,-32,-4], [15,11,-31,-18], + [19,12,12,-7], [32,43,1,-2], [46,28,1,-2], [50,28,1,-1], [31,40,7,-1], [58,41,0,-2], [63,43,1,-4], [46,48,4,-3], + [18,8,-16,-1], [10,42,0,-5], [26,21,-20,-2], [50,53,12,-3], [28,49,4,-3], [50,49,4,-3], [48,43,1,-2], [11,21,-20,-4], + [17,60,15,-6], [17,60,15,-3], [26,27,-18,-4], [46,46,8,-3], [11,22,12,-4], [19,6,-10], [10,10,0,-4], [28,60,15,-3], + [30,40,1,-2], [25,39,0,-5], [28,39,0,-3], [29,40,1,-2], [31,39,0,-1], [28,40,1,-3], [29,40,1,-2], [30,41,1,-3], + [29,40,1,-2], [29,40,1,-2], [10,27,0,-4], [11,39,12,-4], [10,43,13,-5], [46,18,-6,-3], [26,42,12,-3], [26,42,0,-3], + [46,43,1,-3], [47,42,0,-2], [43,41,0,-2], [43,43,1,-3], [47,41,0,-2], [41,41,0,-2], [38,41,0,-2], [47,43,1,-3], + [49,41,0,-2], [22,41,0,-2], [31,42,1,-1], [49,41,0,-2], [36,41,0,-2], [60,41,0,-2], [49,41,0,-2], [45,43,1,-3], + [41,41,0,-2], [45,54,12,-3], [49,42,1,-2], [31,43,1,-3], [43,40,0,-2], [48,42,1,-2], [49,42,1,-1], [68,42,1,-1], + [48,41,0,-2], [50,41,0,-1], [35,41,0,-3], [11,60,15,-7], [26,21,-20,-8], [11,60,15,-1], [19,10,-31,-7], [10,10,-31,-4], + [11,21,-20,-3], [32,28,1,-1], [34,42,1,-2], [27,28,1,-2], [34,42,1,-2], [29,28,1,-1], [24,42,0,-2], [32,39,12,-1], + [35,41,0,-2], [15,41,0,-2], [20,53,12,4], [33,41,0,-2], [16,41,0,-2], [54,27,0,-2], [35,27,0,-2], [31,28,1,-1], + [34,39,12,-2], [34,39,12,-2], [24,27,0,-2], [23,28,1,-2], [22,39,1,-1], [35,28,1,-2], [34,28,1,-1], [47,28,1,-1], + [34,27,0,-1], [34,39,12,-1], [27,27,0,-1], [34,3,-15], [68,3,-15], [21,12,-30,-8], [23,7,-34,-5], [22,9,-32,-6], + [25,110,195,280,364,449,534,619,704,789,874,959,1043,1128,1213,1298], + [77,149,220,292,363,434,506,577], + [1390,625], + ], + cmti10: [ + [39,41,0,-3], [41,43,0,-4], [39,44,2,-8], [35,43,0,-3], [41,40,0,-4], [48,41,0,-3], [43,41,0,-4], [38,42,0,-12], + [34,41,0,-9], [37,41,0,-12], [40,42,0,-5], [47,55,13,2], [38,55,13,2], [40,55,13,2], [56,55,13,2], [58,55,13,2], + [15,28,1,-5], [21,39,13,2], [9,12,-29,-17], [13,12,-29,-20], [16,9,-29,-16], [18,11,-30,-16], [21,2,-33,-13], [12,12,-31,-28], + [14,12,12,-6], [37,55,13,2], [38,28,1,-5], [37,28,1,-6], [29,39,7,-4], [53,41,0,-3], [53,44,2,-9], [42,48,4,-7], + [16,8,-16,-5], [17,43,0,-6], [21,18,-23,-10], [43,53,12,-6], [37,43,1,-5], [42,49,4,-8], [41,45,2,-7], [9,18,-23,-13], + [22,60,15,-9], [22,60,15,-1], [24,27,-18,-11], [37,37,4,-8], [10,19,12,-4], [15,4,-11,-5], [7,7,0,-6], [36,60,15,-1], + [27,42,2,-6], [21,40,0,-7], [28,42,2,-5], [29,42,2,-5], [26,52,12,-2], [28,42,2,-6], [27,42,2,-7], [29,42,2,-8], + [28,42,2,-5], [27,42,2,-6], [12,26,0,-6], [14,38,12,-4], [16,43,13,-3], [40,15,-7,-6], [21,43,13,-5], [22,43,0,-11], + [39,43,1,-8], [38,43,0,-3], [41,41,0,-3], [40,44,2,-8], [43,41,0,-3], [41,41,0,-3], [41,41,0,-3], [40,44,2,-8], + [48,41,0,-3], [27,41,0,-3], [32,43,2,-5], [48,41,0,-3], [34,41,0,-3], [56,41,0,-4], [48,41,0,-3], [39,44,2,-8], + [40,41,0,-3], [39,54,12,-8], [40,43,2,-3], [34,44,2,-4], [38,40,0,-10], [40,43,2,-11], [40,43,2,-12], [54,43,2,-12], + [57,41,0,8], [40,41,0,-12], [38,41,0,-4], [23,60,15,-4], [20,18,-23,-16], [23,60,15,996], [16,10,-31,-15], [7,7,-33,-15], + [10,18,-23,-12], [26,27,1,-6], [21,42,1,-6], [22,28,1,-6], [27,42,1,-6], [22,28,1,-6], [29,54,12,2], [26,39,13,-3], + [28,42,1,-4], [15,40,1,-5], [23,52,13,2], [26,42,1,-4], [13,42,1,-5], [45,28,1,-5], [30,28,1,-5], [24,28,1,-6], + [31,38,12], [24,38,12,-6], [24,28,1,-5], [21,28,1,-4], [17,38,1,-5], [28,28,1,-5], [25,28,1,-5], [37,28,1,-5], + [28,28,1,-3], [26,40,13,-5], [25,28,1,-3], [28,2,-15,-5], [55,2,-15,-7], [18,11,-30,-16], [20,7,-33,-14], [17,7,-33,-16], + [24,97,170,243,316,389,462,535,608,681,754,827,900,973,1046,1119], + [77,149,220,292,363,434,506,577], + [1198,625] + ] +}); + diff --git a/htdocs/jsMath/uncompressed/jsMath-fallback-mac.js b/htdocs/jsMath/uncompressed/jsMath-fallback-mac.js new file mode 100644 index 0000000000..a45476f372 --- /dev/null +++ b/htdocs/jsMath/uncompressed/jsMath-fallback-mac.js @@ -0,0 +1,970 @@ +/* + * jsMath-fallback-mac.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser on the Mac. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2010 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. + */ + + + +/******************************************************************** + * + * Here we replace the TeX character mappings by equivalent unicode + * points when possible, and adjust the character dimensions + * based on the fonts we hope we get them from (the styles are set + * to try to use the best characters available in the standard + * fonts). + */ + +jsMath.Add(jsMath.TeX,{ + + cmr10: [ + // 00 - 0F + {c: 'Γ', tclass: 'greek'}, + {c: 'Δ', tclass: 'greek'}, + {c: 'Θ', tclass: 'greek'}, + {c: 'Λ', tclass: 'greek'}, + {c: 'Ξ', tclass: 'greek'}, + {c: 'Π', tclass: 'greek'}, + {c: 'Σ', tclass: 'greek'}, + {c: 'Υ', tclass: 'greek'}, + {c: 'Φ', tclass: 'greek'}, + {c: 'Ψ', tclass: 'greek'}, + {c: 'Ω', tclass: 'greek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'normal'}, + {c: 'fi', tclass: 'normal'}, + {c: 'fl', tclass: 'normal'}, + {c: 'ffi', tclass: 'normal'}, + {c: 'ffl', tclass: 'normal'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: '`', tclass: 'accent'}, + {c: '´', tclass: 'accent'}, + {c: 'ˇ', tclass: 'accent'}, + {c: '˘', tclass: 'accent'}, + {c: 'ˉ', tclass: 'accent'}, + {c: '˚', tclass: 'accent'}, + {c: '̧', tclass: 'normal'}, + {c: 'ß', tclass: 'normal'}, + {c: 'æ', a:0, tclass: 'normal'}, + {c: 'œ', a:0, tclass: 'normal'}, + {c: 'ø', tclass: 'normal'}, + {c: 'Æ', tclass: 'normal'}, + {c: 'Œ', tclass: 'normal'}, + {c: 'Ø', tclass: 'normal'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'normal'}, + {c: '!', lig: {'96': 60}, tclass: 'normal'}, + {c: '”', tclass: 'normal'}, + {c: '#', tclass: 'normal'}, + {c: '$', tclass: 'normal'}, + {c: '%', tclass: 'normal'}, + {c: '&', tclass: 'normal'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'normal'}, + {c: '(', d:.2, tclass: 'normal'}, + {c: ')', d:.2, tclass: 'normal'}, + {c: '*', tclass: 'normal'}, + {c: '+', a:.1, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'normal'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'normal'}, + {c: '.', a:-.25, tclass: 'normal'}, + {c: '/', tclass: 'normal'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: ':', tclass: 'normal'}, + {c: ';', tclass: 'normal'}, + {c: '¡', tclass: 'normal'}, + {c: '=', a:0, d:-.1, tclass: 'normal'}, + {c: '¿', tclass: 'normal'}, + {c: '?', lig: {'96': 62}, tclass: 'normal'}, + // 40 - 4F + {c: '@', tclass: 'normal'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'B', tclass: 'normal'}, + {c: 'C', tclass: 'normal'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + {c: 'E', tclass: 'normal'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'G', tclass: 'normal'}, + {c: 'H', tclass: 'normal'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'normal'}, + {c: 'J', tclass: 'normal'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'M', tclass: 'normal'}, + {c: 'N', tclass: 'normal'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'Q', d:.1, tclass: 'normal'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'S', tclass: 'normal'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'U', tclass: 'normal'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'Z', tclass: 'normal'}, + {c: '[', d:.1, tclass: 'normal'}, + {c: '“', tclass: 'normal'}, + {c: ']', d:.1, tclass: 'normal'}, + {c: 'ˆ', tclass: 'accent'}, + {c: '˙', tclass: 'accent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'normal'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'normal'}, + {c: 'd', tclass: 'normal'}, + {c: 'e', a:0, tclass: 'normal'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'normal'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'normal'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'i', tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'l', tclass: 'normal'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'q', a:0, d:.2, tclass: 'normal'}, + {c: 'r', a:0, tclass: 'normal'}, + {c: 's', a:0, tclass: 'normal'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'normal'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'x', a:0, tclass: 'normal'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'z', a:0, tclass: 'normal'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'normal'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'normal'}, + {c: '˝', tclass: 'accent'}, + {c: '˜', tclass: 'accent'}, + {c: '¨', tclass: 'accent'} + ], + + cmmi10: [ + // 00 - 0F + {c: 'Γ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'igreek'}, + {c: 'Δ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Λ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Ξ', ic: 0.0757, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Π', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Σ', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Υ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Φ', krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Ω', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'α', a:.1, ic: 0.0037, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'β', d:.1, ic: 0.0528, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'γ', a:.1, d:.1, ic: 0.0556, tclass: 'greek'}, + {c: 'δ', ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'greek'}, + {c: 'ε', a:0, krn: {'127': 0.0556}, tclass: 'lucida'}, + // 10 - 1F + {c: 'ζ', d:.1, ic: 0.0738, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'η', a:.1, d:.1, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ι', a:.1, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'κ', a:.1, tclass: 'greek'}, + {c: 'λ', tclass: 'greek'}, + {c: 'μ', a:.1, d:.1, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'ν', a:.1, ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'ξ', d:.1, ic: 0.046, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'π', a:.1, ic: 0.0359, tclass: 'greek'}, + {c: 'ρ', a:.1, d:.1, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'σ', a:.1, ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'greek'}, + {c: 'τ', a:.1, ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'υ', a:.1, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'φ', a:.3, d:.1, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'χ', a:.1, d:.1, krn: {'127': 0.0556}, tclass: 'greek'}, + // 20 - 2F + {c: 'ψ', a:.3, d:.1, ic: 0.0359, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'ω', a:.1, ic: 0.0359, tclass: 'greek'}, + {c: 'ε', a:.1, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ϑ', krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ϖ', a:.1, ic: 0.0278, tclass: 'lucida'}, + {c: 'ϱ', a:.1, d:.2, krn: {'127': 0.0833}, tclass: 'lucida'}, + {c: 'ς', a:.1, d:.2, ic: 0.0799, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ϕ', a:.1, d:.1, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: '↼', a:0, d:-.2, tclass: 'harpoon'}, + {c: '↽', a:0, d:-.1, tclass: 'harpoon'}, + {c: '⇀', a:0, d:-.2, tclass: 'harpoon'}, + {c: '⇁', a:0, d:-.1, tclass: 'harpoon'}, + {c: '˓', a:.1, tclass: 'lucida'}, + {c: '˒', a:.1, tclass: 'lucida'}, + {c: '', a:0, tclass: 'symbol'}, + {c: '', a:0, tclass: 'symbol'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: '.', a:-.3, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, tclass: 'normal'}, + {c: '<', a:.1, tclass: 'normal'}, + {c: '/', d:.1, krn: {'1': -0.0556, '65': -0.0556, '77': -0.0556, '78': -0.0556, '89': 0.0556, '90': -0.0556}, tclass: 'normal'}, + {c: '>', a:.1, tclass: 'normal'}, + {c: '', a:0, tclass: 'symbol'}, + // 40 - 4F + {c: '∂', ic: 0.0556, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'A', krn: {'127': 0.139}, tclass: 'italic'}, + {c: 'B', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'C', ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'D', ic: 0.0278, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'E', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'F', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'G', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'H', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'I', ic: 0.0785, krn: {'127': 0.111}, tclass: 'italic'}, + {c: 'J', ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}, tclass: 'italic'}, + {c: 'K', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'L', krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'M', ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'N', ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'O', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'Q', d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'R', ic: 0.00773, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'S', ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'T', ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'U', ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}, tclass: 'italic'}, + {c: 'V', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'W', ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'X', ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'Y', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'Z', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: '♭', tclass: 'symbol2'}, + {c: '♮', tclass: 'symbol2'}, + {c: '♯', tclass: 'symbol2'}, + {c: '', a:0, d:-.1, tclass: 'normal'}, + {c: '', a:0, d:-.1, tclass: 'normal'}, + // 60 - 6F + {c: '', krn: {'127': 0.111}, tclass: 'symbol'}, + {c: 'a', a:0, tclass: 'italic'}, + {c: 'b', tclass: 'italic'}, + {c: 'c', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'd', krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}, tclass: 'italic'}, + {c: 'e', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'f', d:.2, ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'h', krn: {'127': -0.0278}, tclass: 'italic'}, + {c: 'i', tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'italic'}, + {c: 'k', ic: 0.0315, tclass: 'italic'}, + {c: 'l', ic: 0.0197, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'm', a:0, tclass: 'italic'}, + {c: 'n', a:0, tclass: 'italic'}, + {c: 'o', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 's', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 't', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'u', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.0269, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'x', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.044, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'ı', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'ȷ', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: '℘', d:0, krn: {'127': 0.111}, tclass: 'asymbol'}, + {c: '', ic: 0.154, tclass: 'symbol'}, + {c: '̑', ic: 0.399, tclass: 'normal'} + ], + + cmsy10: [ + // 00 - 0F + {c: '−', a:.1, tclass: 'symbol'}, + {c: '·', a:0, d:-.2, tclass: 'symbol'}, + {c: '×', a:0, tclass: 'symbol'}, + {c: '*', a:0, tclass: 'symbol'}, + {c: '÷', a:0, tclass: 'symbol'}, + {c: '◊', tclass: 'lucida'}, + {c: '±', a:.1, tclass: 'symbol'}, + {c: '∓', tclass: 'symbol'}, + {c: '⊕', tclass: 'symbol'}, + {c: '⊖', tclass: 'symbol'}, + {c: '⊗', tclass: 'symbol'}, + {c: '⊘', tclass: 'symbol'}, + {c: '⊙', tclass: 'symbol3'}, + {c: '◯', tclass: 'symbol'}, + {c: '°', a:.3, d:-.3, tclass: 'symbol'}, + {c: '•', a:.2, d:-.25, tclass: 'symbol'}, + // 10 - 1F + {c: '≍', a:.1, tclass: 'symbol'}, + {c: '≡', a:.1, tclass: 'symbol'}, + {c: '⊆', tclass: 'symbol'}, + {c: '⊇', tclass: 'symbol'}, + {c: '≤', tclass: 'symbol'}, + {c: '≥', tclass: 'symbol'}, + {c: '⪯', tclass: 'symbol'}, + {c: '⪰', tclass: 'symbol'}, + {c: '~', a:0, d: -.2, tclass: 'normal'}, + {c: '≈', a:.1, d:-.1, tclass: 'symbol'}, + {c: '⊂', tclass: 'symbol'}, + {c: '⊃', tclass: 'symbol'}, + {c: '≪', tclass: 'symbol'}, + {c: '≫', tclass: 'symbol'}, + {c: '≺', tclass: 'symbol'}, + {c: '≻', tclass: 'symbol'}, + // 20 - 2F + {c: '←', a:0, d:-.15, tclass: 'arrows'}, + {c: '→', a:0, d:-.15, tclass: 'arrows'}, + {c: '↑', h:.85, tclass: 'arrow1a'}, + {c: '↓', h:.85, tclass: 'arrow1a'}, + {c: '↔', a:0, tclass: 'arrow1'}, + {c: '↗', h:.85, d:.1, tclass: 'arrows'}, + {c: '↘', h:.85, d:.1, tclass: 'arrows'}, + {c: '≃', a: .1, tclass: 'symbol'}, + {c: '⇐', a:.1, tclass: 'arrows'}, + {c: '⇒', a:.1, tclass: 'arrows'}, + {c: '⇑', h:.7, tclass: 'asymbol'}, + {c: '⇓', h:.7, tclass: 'asymbol'}, + {c: '⇔', a:.1, tclass: 'arrows'}, + {c: '↖', h:.85, d:.1, tclass: 'arrows'}, + {c: '↙', h:.85, d:.1, tclass: 'arrows'}, + {c: '∝', a:.1, d:-.1, tclass: 'symbol'}, + // 30 - 3F + {c: '', a: 0, tclass: 'lucida'}, + {c: '∞', a:.1, tclass: 'symbol'}, + {c: '∈', tclass: 'symbol'}, + {c: '∋', tclass: 'symbol'}, + {c: '△', tclass: 'symbol'}, + {c: '▽', tclass: 'symbol'}, + {c: '/', tclass: 'symbol'}, + {c: '|', a:0, tclass: 'normal'}, + {c: '∀', tclass: 'symbol'}, + {c: '∃', tclass: 'symbol'}, + {c: '¬', a:0, d:-.1, tclass: 'symbol1'}, + {c: '∅', tclass: 'symbol'}, + {c: 'ℜ', tclass: 'asymbol'}, + {c: 'ℑ', tclass: 'asymbol'}, + {c: '⊤', tclass: 'symbol'}, + {c: '⊥', tclass: 'symbol'}, + // 40 - 4F + {c: 'ℵ', tclass: 'symbol'}, + {c: 'A', krn: {'48': 0.194}, tclass: 'cal'}, + {c: 'B', ic: 0.0304, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'C', ic: 0.0583, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'D', ic: 0.0278, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'E', ic: 0.0894, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'F', ic: 0.0993, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'G', d:.2, ic: 0.0593, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'H', ic: 0.00965, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'I', ic: 0.0738, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'J', d:.2, ic: 0.185, krn: {'48': 0.167}, tclass: 'cal'}, + {c: 'K', ic: 0.0144, krn: {'48': 0.0556}, tclass: 'cal'}, + {c: 'L', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'M', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'N', ic: 0.147, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'O', ic: 0.0278, krn: {'48': 0.111}, tclass: 'cal'}, + // 50 - 5F + {c: 'P', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Q', d:.2, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'R', krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'S', ic: 0.075, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'T', ic: 0.254, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'U', ic: 0.0993, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'V', ic: 0.0822, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'W', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'X', ic: 0.146, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'Y', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Z', ic: 0.0794, krn: {'48': 0.139}, tclass: 'cal'}, + {c: '⋃', tclass: 'symbol'}, + {c: '⋂', tclass: 'symbol'}, + {c: '⊎', tclass: 'symbol'}, + {c: '⋀', tclass: 'symbol'}, + {c: '⋁', tclass: 'symbol'}, + // 60 - 6F + {c: '⊢', tclass: 'symbol'}, + {c: '⊣', tclass: 'symbol'}, + {c: '⎣', h:1, d:.3, tclass: 'asymbol'}, + {c: '⎦', h:1, d:.3, tclass: 'asymbol'}, + {c: '⎡', h:1, d:.3, tclass: 'asymbol'}, + {c: '⎤', h:1, d:.3, tclass: 'asymbol'}, + {c: '{', d:.2, tclass: 'normal'}, + {c: '}', d:.2, tclass: 'normal'}, + {c: '⟨', h:.9, d:.1, tclass: 'asymbol'}, + {c: '⟩', h:.9, d:.1, tclass: 'asymbol'}, + {c: '|', d:.2, tclass: 'vertical'}, + {c: '||', d:.2, tclass: 'vertical'}, + {c: '↕', d:.1, tclass: 'arrow1'}, + {c: '⇕', d:.1, tclass: 'arrow1'}, + {c: '', a:.3, d:.1, tclass: 'lucida'}, + {c: '≀', tclass: 'asymbol'}, + // 70 - 7F + {c: '', h:.04, d:.9, tclass: 'lucida'}, + {c: '∐', d:.1, tclass: 'symbol'}, + {c: '∇', tclass: 'symbol'}, + {c: '∫', h:1, d:.1, ic: 0.111, tclass: 'root'}, + {c: '⊔', tclass: 'symbol'}, + {c: '⊓', tclass: 'symbol'}, + {c: '⊑', tclass: 'symbol'}, + {c: '⊒', tclass: 'symbol'}, + {c: '§', d:.1, tclass: 'normal'}, + {c: '†', d:.1, tclass: 'normal'}, + {c: '‡', d:.1, tclass: 'normal'}, + {c: '¶', a:.3, d:.1, tclass: 'lucida'}, + {c: '♣', tclass: 'symbol'}, + {c: '♦', tclass: 'symbol'}, + {c: '♥', tclass: 'symbol'}, + {c: '♠', tclass: 'symbol'} + ], + + cmex10: [ + // 00 - 0F + {c: '(', h: 0.04, d: 1.16, n: 16, tclass: 'delim1'}, + {c: ')', h: 0.04, d: 1.16, n: 17, tclass: 'delim1'}, + {c: '[', h: 0.04, d: 1.16, n: 104, tclass: 'delim1'}, + {c: ']', h: 0.04, d: 1.16, n: 105, tclass: 'delim1'}, + {c: '⎣', h: 0.04, d: 1.16, n: 106, tclass: 'delim1a'}, + {c: '⎦', h: 0.04, d: 1.16, n: 107, tclass: 'delim1a'}, + {c: '⎡', h: 0.04, d: 1.16, n: 108, tclass: 'delim1a'}, + {c: '⎤', h: 0.04, d: 1.16, n: 109, tclass: 'delim1a'}, + {c: '{', h: 0.04, d: 1.16, n: 110, tclass: 'delim1'}, + {c: '}', h: 0.04, d: 1.16, n: 111, tclass: 'delim1'}, + {c: '〈', h: 0.04, d: 1.16, n: 68, tclass: 'delim1c'}, + {c: '〉', h: 0.04, d: 1.16, n: 69, tclass: 'delim1c'}, + {c: '|', h:.7, d:.15, delim: {rep: 12}, tclass: 'vertical1'}, + {c: '||', h:.7, d:.15, delim: {rep: 13}, tclass: 'vertical1'}, + {c: '∕', h: 0.04, d: 1.16, n: 46, tclass: 'delim1b'}, + {c: '∖', h: 0.04, d: 1.16, n: 47, tclass: 'delim1b'}, + // 10 - 1F + {c: '(', h: 0.04, d: 1.76, n: 18, tclass: 'delim2'}, + {c: ')', h: 0.04, d: 1.76, n: 19, tclass: 'delim2'}, + {c: '(', h: 0.04, d: 2.36, n: 32, tclass: 'delim3'}, + {c: ')', h: 0.04, d: 2.36, n: 33, tclass: 'delim3'}, + {c: '[', h: 0.04, d: 2.36, n: 34, tclass: 'delim3'}, + {c: ']', h: 0.04, d: 2.36, n: 35, tclass: 'delim3'}, + {c: '⎣', h: 0.04, d: 2.36, n: 36, tclass: 'delim3a'}, + {c: '⎦', h: 0.04, d: 2.36, n: 37, tclass: 'delim3a'}, + {c: '⎡', h: 0.04, d: 2.36, n: 38, tclass: 'delim3a'}, + {c: '⎤', h: 0.04, d: 2.36, n: 39, tclass: 'delim3a'}, + {c: '{', h: 0.04, d: 2.36, n: 40, tclass: 'delim3'}, + {c: '}', h: 0.04, d: 2.36, n: 41, tclass: 'delim3'}, + {c: '〈', h: 0.04, d: 2.36, n: 42, tclass: 'delim3c'}, + {c: '〉', h: 0.04, d: 2.36, n: 43, tclass: 'delim3c'}, + {c: '∕', h: 0.04, d: 2.36, n: 44, tclass: 'delim3b'}, + {c: '∖', h: 0.04, d: 2.36, n: 45, tclass: 'delim3b'}, + // 20 - 2F + {c: '(', h: 0.04, d: 2.96, n: 48, tclass: 'delim4'}, + {c: ')', h: 0.04, d: 2.96, n: 49, tclass: 'delim4'}, + {c: '[', h: 0.04, d: 2.96, n: 50, tclass: 'delim4'}, + {c: ']', h: 0.04, d: 2.96, n: 51, tclass: 'delim4'}, + {c: '⎣', h: 0.04, d: 2.96, n: 52, tclass: 'delim4a'}, + {c: '⎦', h: 0.04, d: 2.96, n: 53, tclass: 'delim4a'}, + {c: '⎡', h: 0.04, d: 2.96, n: 54, tclass: 'delim4a'}, + {c: '⎤', h: 0.04, d: 2.96, n: 55, tclass: 'delim4a'}, + {c: '{', h: 0.04, d: 2.96, n: 56, tclass: 'delim4'}, + {c: '}', h: 0.04, d: 2.96, n: 57, tclass: 'delim4'}, + {c: '〈', h: 0.04, d: 2.96, tclass: 'delim4c'}, + {c: '〉', h: 0.04, d: 2.96, tclass: 'delim4c'}, + {c: '∕', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '∖', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '∕', h: 0.04, d: 1.76, n: 30, tclass: 'delim2b'}, + {c: '∖', h: 0.04, d: 1.76, n: 31, tclass: 'delim2b'}, + // 30 - 3F + {c: '⎛', h: 1, d: .2, delim: {top: 48, bot: 64, rep: 66}, tclass: 'asymbol'}, + {c: '⎞', h: 1, d: .2, delim: {top: 49, bot: 65, rep: 67}, tclass: 'asymbol'}, + {c: '⎡', h: 1, d: .2, delim: {top: 50, bot: 52, rep: 54}, tclass: 'asymbol'}, + {c: '⎤', h: 1, d: .2, delim: {top: 51, bot: 53, rep: 55}, tclass: 'asymbol'}, + {c: '⎣', h: 1, d: .2, delim: {bot: 52, rep: 54}, tclass: 'asymbol'}, + {c: '⎦', h: 1, d: .2, delim: {bot: 53, rep: 55}, tclass: 'asymbol'}, + {c: '⎢', h: 1, d: .2, delim: {top: 50, rep: 54}, tclass: 'asymbol'}, + {c: '⎥', h: 1, d: .2, delim: {top: 51, rep: 55}, tclass: 'asymbol'}, + {c: '⎧', h: 1, d: .2, delim: {top: 56, mid: 60, bot: 58, rep: 62}, tclass: 'asymbol'}, + {c: '⎫', h: 1, d: .2, delim: {top: 57, mid: 61, bot: 59, rep: 62}, tclass: 'asymbol'}, + {c: '⎩', h: 1, d: .2, delim: {top: 56, bot: 58, rep: 62}, tclass: 'asymbol'}, + {c: '⎭', h: 1, d: .2, delim: {top: 57, bot: 59, rep: 62}, tclass: 'asymbol'}, + {c: '⎨', h: 1, d: .2, delim: {rep: 63}, tclass: 'asymbol'}, + {c: '⎬', h: 1, d: .2, delim: {rep: 119}, tclass: 'asymbol'}, + {c: '⎪', h: 1, d: .2, delim: {rep: 62}, tclass: 'asymbol'}, + {c: '⎪', h: .95, d: .05, delim: {top: 120, bot: 121, rep: 63}, tclass: 'arrow1a'}, + // 40 - 4F + {c: '⎝', h: 1, d: .2, delim: {top: 56, bot: 59, rep: 62}, tclass: 'asymbol'}, + {c: '⎠', h: 1, d: .2, delim: {top: 57, bot: 58, rep: 62}, tclass: 'asymbol'}, + {c: '⎜', h: 1, d: .2, delim: {rep: 66}, tclass: 'asymbol'}, + {c: '⎟', h: 1, d: .2, delim: {rep: 67}, tclass: 'asymbol'}, + {c: '〈', h: 0.04, d: 1.76, n: 28, tclass: 'delim2c'}, + {c: '〉', h: 0.04, d: 1.76, n: 29, tclass: 'delim2c'}, + {c: '⊔', h: -.2, d: 1, n: 71, tclass: 'bigop1'}, + {c: '⊔', h: -.1, d: 1.2, tclass: 'bigop2'}, + {c: '∮', h: 0, d: 1.11, ic: 0.095, n: 73, tclass: 'bigop1c'}, + {c: '∮', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '⊙', h: 0, d: 1, n: 75, tclass: 'bigop1'}, + {c: '⊙', h: 0.1, d: 1.2, tclass: 'bigop2'}, + {c: '⊕', h: .2, d: .9, n: 77, tclass: 'bigop1'}, + {c: '⊕', h: 0.4, d: 1.2, tclass: 'bigop2'}, + {c: '⊗', h: .2, d: .9, n: 79, tclass: 'bigop1'}, + {c: '⊗', h: 0.4, d: 1.2, tclass: 'bigop2'}, + // 50 - 5F + {c: '∑', h: 0, d: 1, n: 88, tclass: 'bigop1a'}, + {c: '∏', h: 0, d: 1, n: 89, tclass: 'bigop1a'}, + {c: '∫', h: 0, d: 1.11, ic: 0.095, n: 90, tclass: 'bigop1c'}, + {c: '∪', h: 0, d: 1, n: 91, tclass: 'bigop1b'}, + {c: '∩', h: 0, d: 1, n: 92, tclass: 'bigop1b'}, + {c: '⊎', h: -.2, d: 1.1, n: 93, tclass: 'bigop1b'}, + {c: '∧', h: 0, d: 1, n: 94, tclass: 'bigop1'}, + {c: '∨', h: 0, d: 1, n: 95, tclass: 'bigop1'}, + {c: '∑', h: 0.1, d: 1.6, tclass: 'bigop2a'}, + {c: '∏', h: 0.1, d: 1.6, tclass: 'bigop2a'}, + {c: '∫', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '∪', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '∩', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '⊎', h: -0.3, d: 1.6, tclass: 'bigop2b'}, + {c: '∧', h: 0.1, d: 1.2, tclass: 'bigop2'}, + {c: '∨', h: 0.1, d: 1.2, tclass: 'bigop2'}, + // 60 - 6F + {c: '∐', h: 0, d: .9, n: 97, tclass: 'bigop1a'}, + {c: '∐', h: 0.1, d: 1.4, tclass: 'bigop2a'}, + {c: '︿', h: 0.722, w: .65, n: 99, tclass: 'wide1'}, + {c: '︿', h: 0.85, w: 1.1, n: 100, tclass: 'wide2'}, + {c: '︿', h: 0.99, w: 1.65, tclass: 'wide3'}, + {c: '⁓', h: 0.722, w: .75, n: 102, tclass: 'wide1a'}, + {c: '⁓', h: 0.8, w: 1.35, n: 103, tclass: 'wide2a'}, + {c: '⁓', h: 0.99, w: 2, tclass: 'wide3a'}, + {c: '[', h: 0.04, d: 1.76, n: 20, tclass: 'delim2'}, + {c: ']', h: 0.04, d: 1.76, n: 21, tclass: 'delim2'}, + {c: '⎣', h: 0.04, d: 1.76, n: 22, tclass: 'delim2a'}, + {c: '⎦', h: 0.04, d: 1.76, n: 23, tclass: 'delim2a'}, + {c: '⎡', h: 0.04, d: 1.76, n: 24, tclass: 'delim2a'}, + {c: '⎤', h: 0.04, d: 1.76, n: 25, tclass: 'delim2a'}, + {c: '{', h: 0.04, d: 1.76, n: 26, tclass: 'delim2'}, + {c: '}', h: 0.04, d: 1.76, n: 27, tclass: 'delim2'}, + // 70 - 7F + {c: '', h: 0.04, d: 1.16, n: 113, tclass: 'root'}, + {c: '', h: 0.04, d: 1.76, n: 114, tclass: 'root'}, + {c: '', h: 0.06, d: 2.36, n: 115, tclass: 'root'}, + {c: '', h: 0.08, d: 2.96, n: 116, tclass: 'root'}, + {c: '', h: 0.1, d: 3.75, n: 117, tclass: 'root'}, + {c: '', h: .12, d: 4.5, n: 118, tclass: 'root'}, + {c: '', h: .14, d: 5.7, tclass: 'root'}, + {c: '', h:.9, delim: {top: 126, bot: 127, rep: 119}, tclass: 'asymbol'}, + {c: '↑', h:.9, d:0, delim: {top: 120, rep: 63}, tclass: 'arrow1a'}, + {c: '↓', h:.9, d:0, delim: {bot: 121, rep: 63}, tclass: 'arrow1a'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '⇑', h:.7, delim: {top: 126, rep: 119}, tclass: 'asymbol'}, + {c: '⇓', h:.7, delim: {bot: 127, rep: 119}, tclass: 'asymbol'} + ], + + cmti10: [ + // 00 - 0F + {c: 'Γ', ic: 0.133, tclass: 'igreek'}, + {c: 'Δ', tclass: 'igreek'}, + {c: 'Θ', ic: 0.094, tclass: 'igreek'}, + {c: 'Λ', tclass: 'igreek'}, + {c: 'Ξ', ic: 0.153, tclass: 'igreek'}, + {c: 'Π', ic: 0.164, tclass: 'igreek'}, + {c: 'Σ', ic: 0.12, tclass: 'igreek'}, + {c: 'Υ', ic: 0.111, tclass: 'igreek'}, + {c: 'Φ', ic: 0.0599, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.111, tclass: 'igreek'}, + {c: 'Ω', ic: 0.103, tclass: 'igreek'}, + {c: 'ff', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 14, '108': 15}, tclass: 'italic'}, + {c: 'fi', ic: 0.103, tclass: 'italic'}, + {c: 'fl', ic: 0.103, tclass: 'italic'}, + {c: 'ffi', ic: 0.103, tclass: 'italic'}, + {c: 'ffl', ic: 0.103, tclass: 'italic'}, + // 10 - 1F + {c: 'ı', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0374, tclass: 'italic'}, + {c: '`', tclass: 'iaccent'}, + {c: '´', ic: 0.0969, tclass: 'iaccent'}, + {c: 'ˇ', ic: 0.083, tclass: 'iaccent'}, + {c: '˘', ic: 0.108, tclass: 'iaccent'}, + {c: 'ˉ', ic: 0.103, tclass: 'iaccent'}, + {c: '˚', tclass: 'iaccent'}, + {c: '?', d: 0.17, w: 0.46, tclass: 'italic'}, + {c: 'ß', ic: 0.105, tclass: 'italic'}, + {c: 'æ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'œ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'ø', ic: 0.0919, tclass: 'italic'}, + {c: 'Æ', ic: 0.12, tclass: 'italic'}, + {c: 'Œ', ic: 0.12, tclass: 'italic'}, + {c: 'Ø', ic: 0.094, tclass: 'italic'}, + // 20 - 2F + {c: '?', krn: {'108': -0.256, '76': -0.321}, tclass: 'italic'}, + {c: '!', ic: 0.124, lig: {'96': 60}, tclass: 'italic'}, + {c: '”', ic: 0.0696, tclass: 'italic'}, + {c: '#', ic: 0.0662, tclass: 'italic'}, + {c: '$', tclass: 'italic'}, + {c: '%', ic: 0.136, tclass: 'italic'}, + {c: '&', ic: 0.0969, tclass: 'italic'}, + {c: '’', ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}, tclass: 'italic'}, + {c: '(', d:.2, ic: 0.162, tclass: 'italic'}, + {c: ')', d:.2, ic: 0.0369, tclass: 'italic'}, + {c: '*', ic: 0.149, tclass: 'italic'}, + {c: '+', a:.1, ic: 0.0369, tclass: 'italic'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'italic'}, + {c: '-', a:0, ic: 0.0283, lig: {'45': 123}, tclass: 'italic'}, + {c: '.', a:-.25, tclass: 'italic'}, + {c: '/', ic: 0.162, tclass: 'italic'}, + // 30 - 3F + {c: '0', ic: 0.136, tclass: 'italic'}, + {c: '1', ic: 0.136, tclass: 'italic'}, + {c: '2', ic: 0.136, tclass: 'italic'}, + {c: '3', ic: 0.136, tclass: 'italic'}, + {c: '4', ic: 0.136, tclass: 'italic'}, + {c: '5', ic: 0.136, tclass: 'italic'}, + {c: '6', ic: 0.136, tclass: 'italic'}, + {c: '7', ic: 0.136, tclass: 'italic'}, + {c: '8', ic: 0.136, tclass: 'italic'}, + {c: '9', ic: 0.136, tclass: 'italic'}, + {c: ':', ic: 0.0582, tclass: 'italic'}, + {c: ';', ic: 0.0582, tclass: 'italic'}, + {c: '¡', ic: 0.0756, tclass: 'italic'}, + {c: '=', a:0, d:-.1, ic: 0.0662, tclass: 'italic'}, + {c: '¿', tclass: 'italic'}, + {c: '?', ic: 0.122, lig: {'96': 62}, tclass: 'italic'}, + // 40 - 4F + {c: '@', ic: 0.096, tclass: 'italic'}, + {c: 'A', krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'B', ic: 0.103, tclass: 'italic'}, + {c: 'C', ic: 0.145, tclass: 'italic'}, + {c: 'D', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + {c: 'E', ic: 0.12, tclass: 'italic'}, + {c: 'F', ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'G', ic: 0.0872, tclass: 'italic'}, + {c: 'H', ic: 0.164, tclass: 'italic'}, + {c: 'I', ic: 0.158, tclass: 'italic'}, + {c: 'J', ic: 0.14, tclass: 'italic'}, + {c: 'K', ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'L', krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'M', ic: 0.164, tclass: 'italic'}, + {c: 'N', ic: 0.164, tclass: 'italic'}, + {c: 'O', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.103, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'Q', d:.1, ic: 0.094, tclass: 'italic'}, + {c: 'R', ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'S', ic: 0.12, tclass: 'italic'}, + {c: 'T', ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'U', ic: 0.164, tclass: 'italic'}, + {c: 'V', ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'W', ic: 0.184, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'X', ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'Y', ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'Z', ic: 0.145, tclass: 'italic'}, + {c: '[', d:.1, ic: 0.188, tclass: 'italic'}, + {c: '“', ic: 0.169, tclass: 'italic'}, + {c: ']', d:.1, ic: 0.105, tclass: 'italic'}, + {c: 'ˆ', ic: 0.0665, tclass: 'iaccent'}, + {c: '˙', ic: 0.118, tclass: 'iaccent'}, + // 60 - 6F + {c: '‘', ic: 0.124, lig: {'96': 92}, tclass: 'italic'}, + {c: 'a', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'b', ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'c', a:0, ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'd', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'e', a:0, ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'f', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'h', ic: 0.0767, tclass: 'italic'}, + {c: 'i', ic: 0.102, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.145, tclass: 'italic'}, + {c: 'k', ic: 0.108, tclass: 'italic'}, + {c: 'l', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'm', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'n', a:0, ic: 0.0767, krn: {'39': -0.102}, tclass: 'italic'}, + {c: 'o', a:0, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 's', a:0, ic: 0.0821, tclass: 'italic'}, + {c: 't', ic: 0.0949, tclass: 'italic'}, + {c: 'u', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.108, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.108, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'x', a:0, ic: 0.12, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.123, tclass: 'italic'}, + {c: '–', a:.1, ic: 0.0921, lig: {'45': 124}, tclass: 'italic'}, + {c: '—', a:.1, ic: 0.0921, tclass: 'italic'}, + {c: '˝', ic: 0.122, tclass: 'iaccent'}, + {c: '˜', ic: 0.116, tclass: 'iaccent'}, + {c: '¨', tclass: 'iaccent'} + ], + + cmbx10: [ + // 00 - 0F + {c: 'Γ', tclass: 'bgreek'}, + {c: 'Δ', tclass: 'bgreek'}, + {c: 'Θ', tclass: 'bgreek'}, + {c: 'Λ', tclass: 'bgreek'}, + {c: 'Ξ', tclass: 'bgreek'}, + {c: 'Π', tclass: 'bgreek'}, + {c: 'Σ', tclass: 'bgreek'}, + {c: 'Υ', tclass: 'bgreek'}, + {c: 'Φ', tclass: 'bgreek'}, + {c: 'Ψ', tclass: 'bgreek'}, + {c: 'Ω', tclass: 'bgreek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'bold'}, + {c: 'fi', tclass: 'bold'}, + {c: 'fl', tclass: 'bold'}, + {c: 'ffi', tclass: 'bold'}, + {c: 'ffl', tclass: 'bold'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: '`', tclass: 'baccent'}, + {c: '´', tclass: 'baccent'}, + {c: 'ˇ', tclass: 'baccent'}, + {c: '˘', tclass: 'baccent'}, + {c: 'ˉ', tclass: 'baccent'}, + {c: '˚', tclass: 'baccent'}, + {c: '?', tclass: 'bold'}, + {c: 'ß', tclass: 'bold'}, + {c: 'æ', a:0, tclass: 'bold'}, + {c: 'œ', a:0, tclass: 'bold'}, + {c: 'ø', tclass: 'bold'}, + {c: 'Æ', tclass: 'bold'}, + {c: 'Œ', tclass: 'bold'}, + {c: 'Ø', tclass: 'bold'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'bold'}, + {c: '!', lig: {'96': 60}, tclass: 'bold'}, + {c: '”', tclass: 'bold'}, + {c: '#', tclass: 'bold'}, + {c: '$', tclass: 'bold'}, + {c: '%', tclass: 'bold'}, + {c: '&', tclass: 'bold'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'bold'}, + {c: '(', d:.2, tclass: 'bold'}, + {c: ')', d:.2, tclass: 'bold'}, + {c: '*', tclass: 'bold'}, + {c: '+', a:.1, tclass: 'bold'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'bold'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'bold'}, + {c: '.', a:-.25, tclass: 'bold'}, + {c: '/', tclass: 'bold'}, + // 30 - 3F + {c: '0', tclass: 'bold'}, + {c: '1', tclass: 'bold'}, + {c: '2', tclass: 'bold'}, + {c: '3', tclass: 'bold'}, + {c: '4', tclass: 'bold'}, + {c: '5', tclass: 'bold'}, + {c: '6', tclass: 'bold'}, + {c: '7', tclass: 'bold'}, + {c: '8', tclass: 'bold'}, + {c: '9', tclass: 'bold'}, + {c: ':', tclass: 'bold'}, + {c: ';', tclass: 'bold'}, + {c: '¡', tclass: 'bold'}, + {c: '=', a:0, d:-.1, tclass: 'bold'}, + {c: '¿', tclass: 'bold'}, + {c: '?', lig: {'96': 62}, tclass: 'bold'}, + // 40 - 4F + {c: '@', tclass: 'bold'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'B', tclass: 'bold'}, + {c: 'C', tclass: 'bold'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + {c: 'E', tclass: 'bold'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'G', tclass: 'bold'}, + {c: 'H', tclass: 'bold'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'bold'}, + {c: 'J', tclass: 'bold'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'M', tclass: 'bold'}, + {c: 'N', tclass: 'bold'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'Q', d:.1, tclass: 'bold'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'S', tclass: 'bold'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'U', tclass: 'bold'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'Z', tclass: 'bold'}, + {c: '[', d:.1, tclass: 'bold'}, + {c: '“', tclass: 'bold'}, + {c: ']', d:.1, tclass: 'bold'}, + {c: 'ˆ', tclass: 'baccent'}, + {c: '˙', tclass: 'baccent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'bold'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'bold'}, + {c: 'd', tclass: 'bold'}, + {c: 'e', a:0, tclass: 'bold'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'bold'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'bold'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'i', tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'l', tclass: 'bold'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'q', a:0, d:.2, tclass: 'bold'}, + {c: 'r', a:0, tclass: 'bold'}, + {c: 's', a:0, tclass: 'bold'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'bold'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'x', a:0, tclass: 'bold'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'z', a:0, tclass: 'bold'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'bold'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'bold'}, + {c: '˝', tclass: 'baccent'}, + {c: '˜', tclass: 'baccent'}, + {c: '¨', tclass: 'baccent'} + ] +}); + + +jsMath.Setup.Styles({ + '.typeset .cmr10': "font-family: serif", + '.typeset .italic': "font-style: italic", + '.typeset .bold': "font-weight: bold", + '.typeset .lucida': "font-family: 'Lucida Grande'", + '.typeset .asymbol': "font-family: 'Apple Symbols'; font-size: 125%", + '.typeset .cal': "font-family: 'Apple Chancery'", + '.typeset .arrows': "font-family: 'Hiragino Mincho Pro'", + '.typeset .Arrows': "font-family: 'AppleGothic'", + '.typeset .arrow1': "font-family: 'Hiragino Mincho Pro'; position: relative; top: .075em; margin: -1px", + '.typeset .arrow1a': "font-family: 'Hiragino Mincho Pro'; margin:-.3em", + '.typeset .harpoon': "font-family: AppleGothic; font-size: 90%", + '.typeset .symbol': "font-family: 'Hiragino Mincho Pro'", + '.typeset .symbol2': "font-family: 'Hiragino Mincho Pro'; margin:-.2em", + '.typeset .symbol3': "font-family: AppleGothic", + '.typeset .delim1': "font-family: Times; font-size: 133%; position:relative; top:.7em", + '.typeset .delim1a': "font-family: 'Apple Symbols'; font-size: 125%; position:relative; top:.75em", + '.typeset .delim1b': "font-family: 'Hiragino Mincho Pro'; font-size: 133%; position:relative; top:.8em; margin: -.1em", + '.typeset .delim1c': "font-family: Symbol; font-size: 120%; position:relative; top:.8em;", + '.typeset .delim2': "font-family: Baskerville; font-size: 180%; position:relative; top:.75em", + '.typeset .delim2a': "font-family: 'Apple Symbols'; font-size: 175%; position:relative; top:.8em", + '.typeset .delim2b': "font-family: 'Hiragino Mincho Pro'; font-size: 190%; position:relative; top:.8em; margin: -.1em", + '.typeset .delim2c': "font-family: Symbol; font-size: 167%; position:relative; top:.8em;", + '.typeset .delim3': "font-family: Baskerville; font-size: 250%; position:relative; top:.725em", + '.typeset .delim3a': "font-family: 'Apple Symbols'; font-size: 240%; position:relative; top:.78em", + '.typeset .delim3b': "font-family: 'Hiragino Mincho Pro'; font-size: 250%; position:relative; top:.8em; margin: -.1em", + '.typeset .delim3c': "font-family: symbol; font-size: 240%; position:relative; top:.775em;", + '.typeset .delim4': "font-family: Baskerville; font-size: 325%; position:relative; top:.7em", + '.typeset .delim4a': "font-family: 'Apple Symbols'; font-size: 310%; position:relative; top:.75em", + '.typeset .delim4b': "font-family: 'Hiragino Mincho Pro'; font-size: 325%; position:relative; top:.8em; margin: -.1em", + '.typeset .delim4c': "font-family: Symbol; font-size: 300%; position:relative; top:.8em;", + '.typeset .vertical': "font-family: Copperplate", + '.typeset .vertical1': "font-family: Copperplate; font-size: 85%; margin: .15em;", + '.typeset .vertical2': "font-family: Copperplate; font-size: 85%; margin: .17em;", + '.typeset .greek': "font-family: 'Hiragino Mincho Pro', serif; margin-left:-.2em; margin-right:-.2em;", + '.typeset .igreek': "font-family: 'Hiragino Mincho Pro', serif; font-style:italic; margin-left:-.2em; margin-right:-.1em", + '.typeset .bgreek': "font-family: 'Hiragino Mincho Pro', serif; font-weight:bold", + '.typeset .bigop1': "font-family: 'Hiragino Mincho Pro'; font-size: 133%; position: relative; top: .7em; margin:-.05em", + '.typeset .bigop1a': "font-family: Baskerville; font-size: 100%; position: relative; top: .775em;", + '.typeset .bigop1b': "font-family: 'Hiragino Mincho Pro'; font-size: 160%; position: relative; top: .67em; margin:-.1em", + '.typeset .bigop1c': "font-family: 'Apple Symbols'; font-size: 125%; position: relative; top: .75em; margin:-.1em;", + '.typeset .bigop2': "font-family: 'Hiragino Mincho Pro'; font-size: 200%; position: relative; top: .6em; margin:-.07em", + '.typeset .bigop2a': "font-family: Baskerville; font-size: 175%; position: relative; top: .67em;", + '.typeset .bigop2b': "font-family: 'Hiragino Mincho Pro'; font-size: 270%; position: relative; top: .62em; margin:-.1em", + '.typeset .bigop2c': "font-family: 'Apple Symbols'; font-size: 250%; position: relative; top: .7em; margin:-.17em;", + '.typeset .wide1': "font-size: 67%; position: relative; top:-.8em", + '.typeset .wide2': "font-size: 110%; position: relative; top:-.5em", + '.typeset .wide3': "font-size: 175%; position: relative; top:-.32em", + '.typeset .wide1a': "font-size: 75%; position: relative; top:-.5em", + '.typeset .wide2a': "font-size: 133%; position: relative; top: -.15em", + '.typeset .wide3a': "font-size: 200%; position: relative; top: -.05em", + '.typeset .root': "font-family: Baskerville;", + '.typeset .accent': "position: relative; top: .02em", + '.typeset .iaccent': "position: relative; top: .02em; font-style:italic", + '.typeset .baccent': "position: relative; top: .02em; font-weight:bold" +}); + +// +// Adjust for OmniWeb +// +if (jsMath.browser == 'OmniWeb') { + jsMath.Update.TeXfonts({ + cmsy10: { + '55': {c: '˫'}, + '104': {c: ''}, + '105': {c: ''} + } + }); + + jsMath.Setup.Styles({ + '.typeset .arrow2': 'font-family: Symbol; font-size: 100%; position: relative; top: -.1em; margin:-1px' + }); + +} + +// +// Adjust for Opera +// +if (jsMath.browser == 'Opera') { + jsMath.Setup.Styles({ + '.typeset .bigop1c': "margin:0pt .12em 0pt 0pt;", + '.typeset .bigop2c': "margin:0pt .12em 0pt 0pt;" + }); +} + +if (jsMath.browser == 'Mozilla') {jsMath.Setup.Script('jsMath-fallback-mac-mozilla.js')} +if (jsMath.browser == 'MSIE') {jsMath.Setup.Script('jsMath-fallback-mac-msie.js')} + + +/* + * No access to TeX "not" character, so fake this + */ +jsMath.Macro('not','\\mathrel{\\rlap{\\kern 4mu/}}'); + +jsMath.Box.defaultH = 0.8; + diff --git a/htdocs/jsMath/uncompressed/jsMath-fallback-pc.js b/htdocs/jsMath/uncompressed/jsMath-fallback-pc.js new file mode 100644 index 0000000000..f7fb8ba0f8 --- /dev/null +++ b/htdocs/jsMath/uncompressed/jsMath-fallback-pc.js @@ -0,0 +1,971 @@ +/* + * jsMath-fallback-pc.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser on the PC. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2010 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. + */ + + + +/******************************************************************** + * + * Here we replace the TeX character mappings by equivalent unicode + * points when possible, and adjust the character dimensions + * based on the fonts we hope we get them from (the styles are set + * to try to use the best characters available in the standard + * fonts). + */ + +jsMath.Add(jsMath.TeX,{ + + cmr10: [ + // 00 - 0F + {c: 'Γ', tclass: 'greek'}, + {c: 'Δ', tclass: 'greek'}, + {c: 'Θ', tclass: 'greek'}, + {c: 'Λ', tclass: 'greek'}, + {c: 'Ξ', tclass: 'greek'}, + {c: 'Π', tclass: 'greek'}, + {c: 'Σ', tclass: 'greek'}, + {c: 'Υ', tclass: 'greek'}, + {c: 'Φ', tclass: 'greek'}, + {c: 'Ψ', tclass: 'greek'}, + {c: 'Ω', tclass: 'greek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'normal'}, + {c: 'fi', tclass: 'normal'}, + {c: 'fl', tclass: 'normal'}, + {c: 'ffi', tclass: 'normal'}, + {c: 'ffl', tclass: 'normal'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: 'ˋ', tclass: 'accent'}, + {c: 'ˊ', tclass: 'accent'}, + {c: 'ˇ', tclass: 'accent'}, + {c: '˘', tclass: 'accent'}, + {c: 'ˉ', tclass: 'accent'}, + {c: '˚', tclass: 'accent'}, + {c: '̧', tclass: 'normal'}, + {c: 'ß', tclass: 'normal'}, + {c: 'æ', a:0, tclass: 'normal'}, + {c: 'œ', a:0, tclass: 'normal'}, + {c: 'ø', tclass: 'normal'}, + {c: 'Æ', tclass: 'normal'}, + {c: 'Œ', tclass: 'normal'}, + {c: 'Ø', tclass: 'normal'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'normal'}, + {c: '!', lig: {'96': 60}, tclass: 'normal'}, + {c: '”', tclass: 'normal'}, + {c: '#', tclass: 'normal'}, + {c: '$', tclass: 'normal'}, + {c: '%', tclass: 'normal'}, + {c: '&', tclass: 'normal'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'normal'}, + {c: '(', d:.2, tclass: 'normal'}, + {c: ')', d:.2, tclass: 'normal'}, + {c: '*', tclass: 'normal'}, + {c: '+', a:.1, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'normal'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'normal'}, + {c: '.', a:-.25, tclass: 'normal'}, + {c: '/', tclass: 'normal'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: ':', tclass: 'normal'}, + {c: ';', tclass: 'normal'}, + {c: '¡', tclass: 'normal'}, + {c: '=', a:0, d:-.1, tclass: 'normal'}, + {c: '¿', tclass: 'normal'}, + {c: '?', lig: {'96': 62}, tclass: 'normal'}, + // 40 - 4F + {c: '@', tclass: 'normal'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'B', tclass: 'normal'}, + {c: 'C', tclass: 'normal'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + {c: 'E', tclass: 'normal'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'G', tclass: 'normal'}, + {c: 'H', tclass: 'normal'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'normal'}, + {c: 'J', tclass: 'normal'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'M', tclass: 'normal'}, + {c: 'N', tclass: 'normal'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'Q', d:.2, tclass: 'normal'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'S', tclass: 'normal'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'U', tclass: 'normal'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'Z', tclass: 'normal'}, + {c: '[', d:.1, tclass: 'normal'}, + {c: '“', tclass: 'normal'}, + {c: ']', d:.1, tclass: 'normal'}, + {c: 'ˆ', tclass: 'accent'}, + {c: '˙', tclass: 'accent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'normal'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'normal'}, + {c: 'd', tclass: 'normal'}, + {c: 'e', a:0, tclass: 'normal'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'normal'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'normal'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'i', tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'l', tclass: 'normal'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'q', a:0, d:.2, tclass: 'normal'}, + {c: 'r', a:0, tclass: 'normal'}, + {c: 's', a:0, tclass: 'normal'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'normal'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'x', a:0, tclass: 'normal'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'z', a:0, tclass: 'normal'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'normal'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'normal'}, + {c: '˝', tclass: 'accent'}, + {c: '˜', tclass: 'accent'}, + {c: '¨', tclass: 'accent'} + ], + + cmmi10: [ + // 00 - 0F + {c: 'Γ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'igreek'}, + {c: 'Δ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Λ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Ξ', ic: 0.0757, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Π', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Σ', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Υ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Φ', krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Ω', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'α', a:0, ic: 0.0037, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'β', d:.2, ic: 0.0528, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'γ', a:0, d:.2, ic: 0.0556, tclass: 'greek'}, + {c: 'δ', ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'greek'}, + {c: 'ε', a:0, krn: {'127': 0.0556}, tclass: 'lucida'}, + // 10 - 1F + {c: 'ζ', d:.2, ic: 0.0738, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'η', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ι', a:0, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'κ', a:0, tclass: 'greek'}, + {c: 'λ', tclass: 'greek'}, + {c: 'μ', a:0, d:.2, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'ν', a:0, ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'ξ', d:.2, ic: 0.046, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'π', a:0, ic: 0.0359, tclass: 'greek'}, + {c: 'ρ', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'σ', a:0, ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'greek'}, + {c: 'τ', a:0, ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'υ', a:0, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'φ', a:.1, d:.2, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'χ', a:0, d:.2, krn: {'127': 0.0556}, tclass: 'greek'}, + // 20 - 2F + {c: 'ψ', a:.1, d:.2, ic: 0.0359, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'ω', a:0, ic: 0.0359, tclass: 'greek'}, + {c: 'ε', a:0, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ϑ', krn: {'127': 0.0833}, tclass: 'lucida'}, + {c: 'ϖ', a:0, ic: 0.0278, tclass: 'lucida'}, + {c: 'ϱ', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'lucida'}, + {c: 'ς', a:0, d:.2, ic: 0.0799, krn: {'127': 0.0833}, tclass: 'lucida'}, + {c: 'ϕ', a:.1, d:.2, krn: {'127': 0.0833}, tclass: 'lucida'}, + {c: '↼', a:0, d:-.2, tclass: 'arrows'}, + {c: '↽', a:0, d:-.1, tclass: 'arrows'}, + {c: '⇀', a:0, d:-.2, tclass: 'arrows'}, + {c: '⇁', a:0, d:-.1, tclass: 'arrows'}, + {c: '˓', a:.1, tclass: 'symbol'}, + {c: '˒', a:.1, tclass: 'symbol'}, + {c: '▹', tclass: 'symbol'}, + {c: '◃', tclass: 'symbol'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: '.', a:-.3, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, tclass: 'normal'}, + {c: '<', a:.1, tclass: 'normal'}, + {c: '/', d:.1, krn: {'1': -0.0556, '65': -0.0556, '77': -0.0556, '78': -0.0556, '89': 0.0556, '90': -0.0556}, tclass: 'normal'}, + {c: '>', a:.1, tclass: 'normal'}, + {c: '⋆', a:0, tclass: 'arial'}, + // 40 - 4F + {c: '∂', ic: 0.0556, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'A', krn: {'127': 0.139}, tclass: 'italic'}, + {c: 'B', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'C', ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'D', ic: 0.0278, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'E', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'F', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'G', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'H', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'I', ic: 0.0785, krn: {'127': 0.111}, tclass: 'italic'}, + {c: 'J', ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}, tclass: 'italic'}, + {c: 'K', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'L', krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'M', ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'N', ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'O', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'Q', d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'R', ic: 0.00773, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'S', ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'T', ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'U', ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}, tclass: 'italic'}, + {c: 'V', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'W', ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'X', ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'Y', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'Z', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: '♭', tclass: 'symbol'}, + {c: '♮', tclass: 'symbol'}, + {c: '♯', tclass: 'symbol'}, + {c: '', a:0, d:-.1, tclass: 'arial'}, + {c: '', a:0, d:-.1, tclass: 'arial'}, + // 60 - 6F + {c: 'ℓ', krn: {'127': 0.111}, tclass: 'italic'}, + {c: 'a', a:0, tclass: 'italic'}, + {c: 'b', tclass: 'italic'}, + {c: 'c', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'd', krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}, tclass: 'italic'}, + {c: 'e', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'f', d:.2, ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'h', krn: {'127': -0.0278}, tclass: 'italic'}, + {c: 'i', tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'italic'}, + {c: 'k', ic: 0.0315, tclass: 'italic'}, + {c: 'l', ic: 0.0197, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'm', a:0, tclass: 'italic'}, + {c: 'n', a:0, tclass: 'italic'}, + {c: 'o', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 's', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 't', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'u', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.0269, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'x', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.044, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'ı', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'j', d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: '℘', a:0, d:.2, krn: {'127': 0.111}, tclass: 'arial'}, + {c: '', ic: 0.154, tclass: 'symbol'}, + {c: '̑', ic: 0.399, tclass: 'normal'} + ], + + cmsy10: [ + // 00 - 0F + {c: '', a:.1, tclass: 'symbol'}, + {c: '·', a:0, d:-.2, tclass: 'normal'}, + {c: '×', a:0, tclass: 'normal'}, + {c: '*', a:0, tclass: 'normal'}, + {c: '÷', a:0, tclass: 'normal'}, + {c: '◊', tclass: 'symbol'}, + {c: '±', a:.1, tclass: 'normal'}, + {c: '∓', tclass: 'symbol'}, + {c: '⊕', tclass: 'symbol'}, + {c: '⊖', tclass: 'symbol'}, + {c: '⊗', tclass: 'symbol'}, + {c: '⊘', tclass: 'symbol'}, + {c: '⊙', tclass: 'symbol'}, + {c: '◯', tclass: 'arial'}, + {c: '∘', a:0, d:-.1, tclass: 'symbol2'}, + {c: '•', a:0, d:-.2, tclass: 'symbol'}, + // 10 - 1F + {c: '≍', a:.1, tclass: 'symbol2'}, + {c: '≡', a:.1, tclass: 'symbol2'}, + {c: '⊆', tclass: 'symbol'}, + {c: '⊇', tclass: 'symbol'}, + {c: '≤', tclass: 'symbol'}, + {c: '≥', tclass: 'symbol'}, + {c: '≼', tclass: 'symbol'}, + {c: '≽', tclass: 'symbol'}, + {c: '~', a:0, d: -.2, tclass: 'normal'}, + {c: '≈', a:.1, d:-.1, tclass: 'symbol'}, + {c: '⊂', tclass: 'symbol'}, + {c: '⊃', tclass: 'symbol'}, + {c: '≪', tclass: 'symbol'}, + {c: '≫', tclass: 'symbol'}, + {c: '≺', tclass: 'symbol'}, + {c: '≻', tclass: 'symbol'}, + // 20 - 2F + {c: '←', a:-.1, tclass: 'arrow1'}, + {c: '→', a:-.1, tclass: 'arrow1'}, + {c: '↑', a:.2, d:0, tclass: 'arrow1a'}, + {c: '↓', a:.2, d:0, tclass: 'arrow1a'}, + {c: '↔', a:-.1, tclass: 'arrow1'}, + {c: '↗', a:.1, tclass: 'arrows'}, + {c: '↘', a:.1, tclass: 'arrows'}, + {c: '≃', a: .1, tclass: 'symbol2'}, + {c: '⇐', a:-.1, tclass: 'arrow2'}, + {c: '⇒', a:-.1, tclass: 'arrow2'}, + {c: '⇑', a:.2, d:.1, tclass: 'arrow1a'}, + {c: '⇓', a:.2, d:.1, tclass: 'arrow1a'}, + {c: '⇔', a:-.1, tclass: 'arrow2'}, + {c: '↖', a:.1, tclass: 'arrows'}, + {c: '↙', a:.1, tclass: 'arrows'}, + {c: '∝', a:.1, tclass: 'normal'}, + // 30 - 3F + {c: '', a: 0, tclass: 'lucida'}, + {c: '∞', a:.1, tclass: 'symbol'}, + {c: '∈', tclass: 'symbol'}, + {c: '∋', tclass: 'symbol'}, + {c: '', tclass: 'symbol'}, + {c: '', tclass: 'symbol'}, + {c: '/', d:.2, tclass: 'normal'}, + {c: '', tclass: 'symbol'}, + {c: '∀', tclass: 'symbol'}, + {c: '∃', tclass: 'symbol'}, + {c: '¬', a:0, d:-.1, tclass: 'symbol'}, + {c: '∅', tclass: 'symbol'}, + {c: 'ℜ', tclass: 'symbol'}, + {c: 'ℑ', tclass: 'symbol'}, + {c: '⊤', tclass: 'symbol'}, + {c: '⊥', tclass: 'symbol'}, + // 40 - 4F + {c: 'ℵ', tclass: 'symbol'}, + {c: 'A', krn: {'48': 0.194}, tclass: 'cal'}, + {c: 'B', ic: 0.0304, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'C', ic: 0.0583, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'D', ic: 0.0278, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'E', ic: 0.0894, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'F', ic: 0.0993, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'G', d:.2, ic: 0.0593, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'H', ic: 0.00965, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'I', ic: 0.0738, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'J', d:.2, ic: 0.185, krn: {'48': 0.167}, tclass: 'cal'}, + {c: 'K', ic: 0.0144, krn: {'48': 0.0556}, tclass: 'cal'}, + {c: 'L', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'M', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'N', ic: 0.147, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'O', ic: 0.0278, krn: {'48': 0.111}, tclass: 'cal'}, + // 50 - 5F + {c: 'P', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Q', d:.2, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'R', krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'S', ic: 0.075, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'T', ic: 0.254, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'U', ic: 0.0993, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'V', ic: 0.0822, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'W', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'X', ic: 0.146, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'Y', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Z', ic: 0.0794, krn: {'48': 0.139}, tclass: 'cal'}, + {c: '⋃', tclass: 'symbol'}, + {c: '⋂', tclass: 'symbol'}, + {c: '⊎', tclass: 'symbol'}, + {c: '⋀', tclass: 'symbol'}, + {c: '⋁', tclass: 'symbol'}, + // 60 - 6F + {c: '⊢', tclass: 'symbol'}, + {c: '⊣', tclass: 'symbol'}, + {c: '⌊', a:.3, d:.2, tclass: 'lucida'}, + {c: '⌋', a:.3, d:.2, tclass: 'lucida'}, + {c: '⌈', a:.3, d:.2, tclass: 'lucida'}, + {c: '⌉', a:.3, d:.2, tclass: 'lucida'}, + {c: '{', d:.2, tclass: 'normal'}, + {c: '}', d:.2, tclass: 'normal'}, + {c: '〈', a:.3, d:.2, tclass: 'symbol'}, + {c: '〉', a:.3, d:.2, tclass: 'symbol'}, + {c: '∣', d:.1, tclass: 'symbol'}, + {c: '∥', d:.1, tclass: 'symbol'}, + {c: '↕', a:.2, d:0, tclass: 'arrow1a'}, + {c: '⇕', a:.3, d:0, tclass: 'arrow1a'}, + {c: '∖', a:.3, d:.1, tclass: 'symbol'}, + {c: '≀', tclass: 'symbol'}, + // 70 - 7F + {c: '', h:.04, d:.8, tclass: 'symbol'}, + {c: '∐', a:.4, tclass: 'symbol'}, + {c: '∇', tclass: 'symbol'}, + {c: '', a:.4, d:.1, ic: 0.111, tclass: 'lucida'}, + {c: '⊔', tclass: 'symbol'}, + {c: '⊓', tclass: 'symbol'}, + {c: '⊑', tclass: 'symbol'}, + {c: '⊒', tclass: 'symbol'}, + {c: '§', d:.1, tclass: 'normal'}, + {c: '†', d:.1, tclass: 'normal'}, + {c: '‡', d:.1, tclass: 'normal'}, + {c: '¶', a:.3, d:.1, tclass: 'lucida'}, + {c: '♣', tclass: 'arial'}, + {c: '♢', tclass: 'arial'}, + {c: '♡', tclass: 'arial'}, + {c: '♠', tclass: 'arial'} + ], + + cmex10: [ + // 00 - 0F + {c: '(', h: 0.04, d: 1.16, n: 16, tclass: 'delim1'}, + {c: ')', h: 0.04, d: 1.16, n: 17, tclass: 'delim1'}, + {c: '[', h: 0.04, d: 1.16, n: 104, tclass: 'delim1'}, + {c: ']', h: 0.04, d: 1.16, n: 105, tclass: 'delim1'}, + {c: '⌊', h: 0.04, d: 1.16, n: 106, tclass: 'delim1a'}, + {c: '⌋', h: 0.04, d: 1.16, n: 107, tclass: 'delim1a'}, + {c: '⌈', h: 0.04, d: 1.16, n: 108, tclass: 'delim1a'}, + {c: '⌉', h: 0.04, d: 1.16, n: 109, tclass: 'delim1a'}, + {c: '{', h: 0.04, d: 1.16, n: 110, tclass: 'delim1'}, + {c: '}', h: 0.04, d: 1.16, n: 111, tclass: 'delim1'}, + {c: '〈', h: 0.04, d: 1.16, n: 68, tclass: 'delim1b'}, + {c: '〉', h: 0.04, d: 1.16, n: 69, tclass: 'delim1b'}, + {c: '∣', h:.7, d:.1, delim: {rep: 12}, tclass: 'symbol'}, + {c: '∥', h:.7, d:.1, delim: {rep: 13}, tclass: 'symbol'}, + {c: '/', h: 0.04, d: 1.16, n: 46, tclass: 'delim1a'}, + {c: '∖', h: 0.04, d: 1.16, n: 47, tclass: 'delim1a'}, + // 10 - 1F + {c: '(', h: 0.04, d: 1.76, n: 18, tclass: 'delim2'}, + {c: ')', h: 0.04, d: 1.76, n: 19, tclass: 'delim2'}, + {c: '(', h: 0.04, d: 2.36, n: 32, tclass: 'delim3'}, + {c: ')', h: 0.04, d: 2.36, n: 33, tclass: 'delim3'}, + {c: '[', h: 0.04, d: 2.36, n: 34, tclass: 'delim3'}, + {c: ']', h: 0.04, d: 2.36, n: 35, tclass: 'delim3'}, + {c: '⌊', h: 0.04, d: 2.36, n: 36, tclass: 'delim3a'}, + {c: '⌋', h: 0.04, d: 2.36, n: 37, tclass: 'delim3a'}, + {c: '⌈', h: 0.04, d: 2.36, n: 38, tclass: 'delim3a'}, + {c: '⌉', h: 0.04, d: 2.36, n: 39, tclass: 'delim3a'}, + {c: '{', h: 0.04, d: 2.36, n: 40, tclass: 'delim3'}, + {c: '}', h: 0.04, d: 2.36, n: 41, tclass: 'delim3'}, + {c: '〈', h: 0.04, d: 2.36, n: 42, tclass: 'delim3b'}, + {c: '〉', h: 0.04, d: 2.36, n: 43, tclass: 'delim3b'}, + {c: '/', h: 0.04, d: 2.36, n: 44, tclass: 'delim3a'}, + {c: '∖', h: 0.04, d: 2.36, n: 45, tclass: 'delim3a'}, + // 20 - 2F + {c: '(', h: 0.04, d: 2.96, n: 48, tclass: 'delim4'}, + {c: ')', h: 0.04, d: 2.96, n: 49, tclass: 'delim4'}, + {c: '[', h: 0.04, d: 2.96, n: 50, tclass: 'delim4'}, + {c: ']', h: 0.04, d: 2.96, n: 51, tclass: 'delim4'}, + {c: '⌊', h: 0.04, d: 2.96, n: 52, tclass: 'delim4a'}, + {c: '⌋', h: 0.04, d: 2.96, n: 53, tclass: 'delim4a'}, + {c: '⌈', h: 0.04, d: 2.96, n: 54, tclass: 'delim4a'}, + {c: '⌉', h: 0.04, d: 2.96, n: 55, tclass: 'delim4a'}, + {c: '{', h: 0.04, d: 2.96, n: 56, tclass: 'delim4'}, + {c: '}', h: 0.04, d: 2.96, n: 57, tclass: 'delim4'}, + {c: '〈', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '〉', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '/', h: 0.04, d: 2.96, tclass: 'delim4a'}, + {c: '∖', h: 0.04, d: 2.96, tclass: 'delim4a'}, + {c: '/', h: 0.04, d: 1.76, n: 30, tclass: 'delim2a'}, + {c: '∖', h: 0.04, d: 1.76, n: 31, tclass: 'delim2a'}, + // 30 - 3F + {c: 'æ', h: 1, delim: {top: 48, bot: 64, rep: 66}, tclass: 'delimx'}, + {c: 'ö', h: 1, delim: {top: 49, bot: 65, rep: 67}, tclass: 'delimx'}, + {c: 'é', h: 1, delim: {top: 50, bot: 52, rep: 54}, tclass: 'delimx'}, + {c: 'ù', h: 1, delim: {top: 51, bot: 53, rep: 55}, tclass: 'delimx'}, + {c: 'ë', h: 1, delim: {bot: 52, rep: 54}, tclass: 'delimx'}, + {c: 'û', h: 1, delim: {bot: 53, rep: 55}, tclass: 'delimx'}, + {c: 'ê', h: 1, delim: {top: 50, rep: 54}, tclass: 'delimx'}, + {c: 'ú', h: 1, delim: {top: 51, rep: 55}, tclass: 'delimx'}, + {c: 'ì', h: 1, delim: {top: 56, mid: 60, bot: 58, rep: 62}, tclass: 'delimx'}, + {c: 'ü', h: 1, delim: {top: 57, mid: 61, bot: 59, rep: 62}, tclass: 'delimx'}, + {c: 'î', h: 1, delim: {top: 56, bot: 58, rep: 62}, tclass: 'delimx'}, + {c: 'þ', h: 1, delim: {top: 57, bot: 59, rep: 62}, tclass: 'delimx'}, + {c: 'í', h: 1, delim: {rep: 63}, tclass: 'delimx'}, + {c: 'ý', h: 1, delim: {rep: 119}, tclass: 'delimx'}, + {c: 'ï', h: 1, delim: {rep: 62}, tclass: 'delimx'}, + {c: '|', delim: {top: 120, bot: 121, rep: 63}, tclass: 'normal'}, + // 40 - 4F + {c: 'è', h: 1, delim: {top: 56, bot: 59, rep: 62}, tclass: 'delimx'}, + {c: 'ø', h: 1, delim: {top: 57, bot: 58, rep: 62}, tclass: 'delimx'}, + {c: 'ç', h: 1, delim: {rep: 66}, tclass: 'delimx'}, + {c: '÷', h: 1, delim: {rep: 67}, tclass: 'delimx'}, + {c: '〈', h: 0.04, d: 1.76, n: 28, tclass: 'delim2b'}, + {c: '〉', h: 0.04, d: 1.76, n: 29, tclass: 'delim2b'}, + {c: '⊔', h: 0, d: 1, n: 71, tclass: 'bigop1'}, + {c: '⊔', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '∮', h: 0, d: 1.11, ic: 0.095, n: 73, tclass: 'bigop1c'}, + {c: '∮', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '⊙', h: 0, d: 1, n: 75, tclass: 'bigop1'}, + {c: '⊙', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '⊕', h: 0, d: 1, n: 77, tclass: 'bigop1'}, + {c: '⊕', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '⊗', h: 0, d: 1, n: 79, tclass: 'bigop1'}, + {c: '⊗', h: 0.1, d: 1.5, tclass: 'bigop2'}, + // 50 - 5F + {c: '∑', h: 0, d: 1, n: 88, tclass: 'bigop1a'}, + {c: '∏', h: 0, d: 1, n: 89, tclass: 'bigop1a'}, + {c: '∫', h: 0, d: 1.11, ic: 0.095, n: 90, tclass: 'bigop1c'}, + {c: '∪', h: 0, d: 1, n: 91, tclass: 'bigop1b'}, + {c: '∩', h: 0, d: 1, n: 92, tclass: 'bigop1b'}, + {c: '⊎', h: 0, d: 1, n: 93, tclass: 'bigop1b'}, + {c: '⋀', h: 0, d: 1, n: 94, tclass: 'bigop1'}, + {c: '⋁', h: 0, d: 1, n: 95, tclass: 'bigop1'}, + {c: '∑', h: 0.1, d: 1.6, tclass: 'bigop2a'}, + {c: '∏', h: 0.1, d: 1.5, tclass: 'bigop2a'}, + {c: '∫', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '∪', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '∩', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '⊎', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '⋀', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '⋁', h: 0.1, d: 1.5, tclass: 'bigop2'}, + // 60 - 6F + {c: '∐', h: 0, d: 1, n: 97, tclass: 'bigop1a'}, + {c: '∐', h: 0.1, d: 1.5, tclass: 'bigop2a'}, + {c: '︿', h: 0.8, d:0, w: .65, n: 99, tclass: 'wide1'}, + {c: '︿', h: 0.85, w: 1.1, n: 100, tclass: 'wide2'}, + {c: '︿', h: 0.99, w: 1.65, tclass: 'wide3'}, + {c: '~', h: 1, w: .5, n: 102, tclass: 'wide1a'}, + {c: '~', h: 1, w: .8, n: 103, tclass: 'wide2a'}, + {c: '~', h: 0.99, w: 1.3, tclass: 'wide3a'}, + {c: '[', h: 0.04, d: 1.76, n: 20, tclass: 'delim2'}, + {c: ']', h: 0.04, d: 1.76, n: 21, tclass: 'delim2'}, + {c: '⌈', h: 0.04, d: 1.76, n: 22, tclass: 'delim2a'}, + {c: '⌉', h: 0.04, d: 1.76, n: 23, tclass: 'delim2a'}, + {c: '⌊', h: 0.04, d: 1.76, n: 24, tclass: 'delim2a'}, + {c: '⌋', h: 0.04, d: 1.76, n: 25, tclass: 'delim2a'}, + {c: '{', h: 0.04, d: 1.76, n: 26, tclass: 'delim2'}, + {c: '}', h: 0.04, d: 1.76, n: 27, tclass: 'delim2'}, + // 70 - 7F + {c: '', h: 0.04, d: 1.16, n: 113, tclass: 'root'}, + {c: '', h: 0.04, d: 1.76, n: 114, tclass: 'root'}, + {c: '', h: 0.06, d: 2.36, n: 115, tclass: 'root'}, + {c: '', h: 0.08, d: 2.96, n: 116, tclass: 'root'}, + {c: '', h: 0.1, d: 3.75, n: 117, tclass: 'root'}, + {c: '', h: .12, d: 4.5, n: 118, tclass: 'root'}, + {c: '', h: .14, d: 5.7, tclass: 'root'}, + {c: '||', delim: {top: 126, bot: 127, rep: 119}, tclass: 'normal'}, + {c: '↑', h:.7, d:0, delim: {top: 120, rep: 63}, tclass: 'arrow1a'}, + {c: '↓', h:.7, d:0, delim: {bot: 121, rep: 63}, tclass: 'arrow1a'}, + {c: '', h: 0.05, tclass: 'symbol'}, + {c: '', h: 0.05, tclass: 'symbol'}, + {c: '', h: 0.05, tclass: 'symbol'}, + {c: '', h: 0.05, tclass: 'symbol'}, + {c: '⇑', h: .65, d:0, delim: {top: 126, rep: 119}, tclass: 'arrow1a'}, + {c: '⇓', h: .65, d:0, delim: {bot: 127, rep: 119}, tclass: 'arrow1a'} + ], + + cmti10: [ + // 00 - 0F + {c: 'Γ', ic: 0.133, tclass: 'igreek'}, + {c: 'Δ', tclass: 'igreek'}, + {c: 'Θ', ic: 0.094, tclass: 'igreek'}, + {c: 'Λ', tclass: 'igreek'}, + {c: 'Ξ', ic: 0.153, tclass: 'igreek'}, + {c: 'Π', ic: 0.164, tclass: 'igreek'}, + {c: 'Σ', ic: 0.12, tclass: 'igreek'}, + {c: 'Υ', ic: 0.111, tclass: 'igreek'}, + {c: 'Φ', ic: 0.0599, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.111, tclass: 'igreek'}, + {c: 'Ω', ic: 0.103, tclass: 'igreek'}, + {c: 'ff', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 14, '108': 15}, tclass: 'italic'}, + {c: 'fi', ic: 0.103, tclass: 'italic'}, + {c: 'fl', ic: 0.103, tclass: 'italic'}, + {c: 'ffi', ic: 0.103, tclass: 'italic'}, + {c: 'ffl', ic: 0.103, tclass: 'italic'}, + // 10 - 1F + {c: 'ı', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0374, tclass: 'italic'}, + {c: 'ˋ', tclass: 'iaccent'}, + {c: 'ˊ', ic: 0.0969, tclass: 'iaccent'}, + {c: 'ˇ', ic: 0.083, tclass: 'iaccent'}, + {c: '˘', ic: 0.108, tclass: 'iaccent'}, + {c: 'ˉ', ic: 0.103, tclass: 'iaccent'}, + {c: '˚', tclass: 'iaccent'}, + {c: '?', d: 0.17, w: 0.46, tclass: 'italic'}, + {c: 'ß', ic: 0.105, tclass: 'italic'}, + {c: 'æ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'œ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'ø', ic: 0.0919, tclass: 'italic'}, + {c: 'Æ', ic: 0.12, tclass: 'italic'}, + {c: 'Œ', ic: 0.12, tclass: 'italic'}, + {c: 'Ø', ic: 0.094, tclass: 'italic'}, + // 20 - 2F + {c: '?', krn: {'108': -0.256, '76': -0.321}, tclass: 'italic'}, + {c: '!', ic: 0.124, lig: {'96': 60}, tclass: 'italic'}, + {c: '”', ic: 0.0696, tclass: 'italic'}, + {c: '#', ic: 0.0662, tclass: 'italic'}, + {c: '$', tclass: 'italic'}, + {c: '%', ic: 0.136, tclass: 'italic'}, + {c: '&', ic: 0.0969, tclass: 'italic'}, + {c: '’', ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}, tclass: 'italic'}, + {c: '(', d:.2, ic: 0.162, tclass: 'italic'}, + {c: ')', d:.2, ic: 0.0369, tclass: 'italic'}, + {c: '*', ic: 0.149, tclass: 'italic'}, + {c: '+', a:.1, ic: 0.0369, tclass: 'italic'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'italic'}, + {c: '-', a:0, ic: 0.0283, lig: {'45': 123}, tclass: 'italic'}, + {c: '.', a:-.25, tclass: 'italic'}, + {c: '/', ic: 0.162, tclass: 'italic'}, + // 30 - 3F + {c: '0', ic: 0.136, tclass: 'italic'}, + {c: '1', ic: 0.136, tclass: 'italic'}, + {c: '2', ic: 0.136, tclass: 'italic'}, + {c: '3', ic: 0.136, tclass: 'italic'}, + {c: '4', ic: 0.136, tclass: 'italic'}, + {c: '5', ic: 0.136, tclass: 'italic'}, + {c: '6', ic: 0.136, tclass: 'italic'}, + {c: '7', ic: 0.136, tclass: 'italic'}, + {c: '8', ic: 0.136, tclass: 'italic'}, + {c: '9', ic: 0.136, tclass: 'italic'}, + {c: ':', ic: 0.0582, tclass: 'italic'}, + {c: ';', ic: 0.0582, tclass: 'italic'}, + {c: '¡', ic: 0.0756, tclass: 'italic'}, + {c: '=', a:0, d:-.1, ic: 0.0662, tclass: 'italic'}, + {c: '¿', tclass: 'italic'}, + {c: '?', ic: 0.122, lig: {'96': 62}, tclass: 'italic'}, + // 40 - 4F + {c: '@', ic: 0.096, tclass: 'italic'}, + {c: 'A', krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'B', ic: 0.103, tclass: 'italic'}, + {c: 'C', ic: 0.145, tclass: 'italic'}, + {c: 'D', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + {c: 'E', ic: 0.12, tclass: 'italic'}, + {c: 'F', ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'G', ic: 0.0872, tclass: 'italic'}, + {c: 'H', ic: 0.164, tclass: 'italic'}, + {c: 'I', ic: 0.158, tclass: 'italic'}, + {c: 'J', ic: 0.14, tclass: 'italic'}, + {c: 'K', ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'L', krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'M', ic: 0.164, tclass: 'italic'}, + {c: 'N', ic: 0.164, tclass: 'italic'}, + {c: 'O', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.103, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'Q', d:.2, ic: 0.094, tclass: 'italic'}, + {c: 'R', ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'S', ic: 0.12, tclass: 'italic'}, + {c: 'T', ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'U', ic: 0.164, tclass: 'italic'}, + {c: 'V', ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'W', ic: 0.184, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'X', ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'Y', ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'Z', ic: 0.145, tclass: 'italic'}, + {c: '[', d:.1, ic: 0.188, tclass: 'italic'}, + {c: '“', ic: 0.169, tclass: 'italic'}, + {c: ']', d:.1, ic: 0.105, tclass: 'italic'}, + {c: 'ˆ', ic: 0.0665, tclass: 'iaccent'}, + {c: '˙', ic: 0.118, tclass: 'iaccent'}, + // 60 - 6F + {c: '‘', ic: 0.124, lig: {'96': 92}, tclass: 'italic'}, + {c: 'a', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'b', ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'c', a:0, ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'd', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'e', a:0, ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'f', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'h', ic: 0.0767, tclass: 'italic'}, + {c: 'i', ic: 0.102, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.145, tclass: 'italic'}, + {c: 'k', ic: 0.108, tclass: 'italic'}, + {c: 'l', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'm', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'n', a:0, ic: 0.0767, krn: {'39': -0.102}, tclass: 'italic'}, + {c: 'o', a:0, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 's', a:0, ic: 0.0821, tclass: 'italic'}, + {c: 't', ic: 0.0949, tclass: 'italic'}, + {c: 'u', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.108, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.108, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'x', a:0, ic: 0.12, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.123, tclass: 'italic'}, + {c: '–', a:.1, ic: 0.0921, lig: {'45': 124}, tclass: 'italic'}, + {c: '—', a:.1, ic: 0.0921, tclass: 'italic'}, + {c: '˝', ic: 0.122, tclass: 'iaccent'}, + {c: '˜', ic: 0.116, tclass: 'iaccent'}, + {c: '¨', tclass: 'iaccent'} + ], + + cmbx10: [ + // 00 - 0F + {c: 'Γ', tclass: 'bgreek'}, + {c: 'Δ', tclass: 'bgreek'}, + {c: 'Θ', tclass: 'bgreek'}, + {c: 'Λ', tclass: 'bgreek'}, + {c: 'Ξ', tclass: 'bgreek'}, + {c: 'Π', tclass: 'bgreek'}, + {c: 'Σ', tclass: 'bgreek'}, + {c: 'Υ', tclass: 'bgreek'}, + {c: 'Φ', tclass: 'bgreek'}, + {c: 'Ψ', tclass: 'bgreek'}, + {c: 'Ω', tclass: 'bgreek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'bold'}, + {c: 'fi', tclass: 'bold'}, + {c: 'fl', tclass: 'bold'}, + {c: 'ffi', tclass: 'bold'}, + {c: 'ffl', tclass: 'bold'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: 'ˋ', tclass: 'baccent'}, + {c: 'ˊ', tclass: 'baccent'}, + {c: 'ˇ', tclass: 'baccent'}, + {c: '˘', tclass: 'baccent'}, + {c: 'ˉ', tclass: 'baccent'}, + {c: '˚', tclass: 'baccent'}, + {c: '?', tclass: 'bold'}, + {c: 'ß', tclass: 'bold'}, + {c: 'æ', a:0, tclass: 'bold'}, + {c: 'œ', a:0, tclass: 'bold'}, + {c: 'ø', tclass: 'bold'}, + {c: 'Æ', tclass: 'bold'}, + {c: 'Œ', tclass: 'bold'}, + {c: 'Ø', tclass: 'bold'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'bold'}, + {c: '!', lig: {'96': 60}, tclass: 'bold'}, + {c: '”', tclass: 'bold'}, + {c: '#', tclass: 'bold'}, + {c: '$', tclass: 'bold'}, + {c: '%', tclass: 'bold'}, + {c: '&', tclass: 'bold'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'bold'}, + {c: '(', d:.2, tclass: 'bold'}, + {c: ')', d:.2, tclass: 'bold'}, + {c: '*', tclass: 'bold'}, + {c: '+', a:.1, tclass: 'bold'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'bold'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'bold'}, + {c: '.', a:-.25, tclass: 'bold'}, + {c: '/', tclass: 'bold'}, + // 30 - 3F + {c: '0', tclass: 'bold'}, + {c: '1', tclass: 'bold'}, + {c: '2', tclass: 'bold'}, + {c: '3', tclass: 'bold'}, + {c: '4', tclass: 'bold'}, + {c: '5', tclass: 'bold'}, + {c: '6', tclass: 'bold'}, + {c: '7', tclass: 'bold'}, + {c: '8', tclass: 'bold'}, + {c: '9', tclass: 'bold'}, + {c: ':', tclass: 'bold'}, + {c: ';', tclass: 'bold'}, + {c: '¡', tclass: 'bold'}, + {c: '=', a:0, d:-.1, tclass: 'bold'}, + {c: '¿', tclass: 'bold'}, + {c: '?', lig: {'96': 62}, tclass: 'bold'}, + // 40 - 4F + {c: '@', tclass: 'bold'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'B', tclass: 'bold'}, + {c: 'C', tclass: 'bold'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + {c: 'E', tclass: 'bold'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'G', tclass: 'bold'}, + {c: 'H', tclass: 'bold'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'bold'}, + {c: 'J', tclass: 'bold'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'M', tclass: 'bold'}, + {c: 'N', tclass: 'bold'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'Q', d:.2, tclass: 'bold'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'S', tclass: 'bold'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'U', tclass: 'bold'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'Z', tclass: 'bold'}, + {c: '[', d:.1, tclass: 'bold'}, + {c: '“', tclass: 'bold'}, + {c: ']', d:.1, tclass: 'bold'}, + {c: 'ˆ', tclass: 'baccent'}, + {c: '˙', tclass: 'baccent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'bold'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'bold'}, + {c: 'd', tclass: 'bold'}, + {c: 'e', a:0, tclass: 'bold'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'bold'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'bold'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'i', tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'l', tclass: 'bold'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'q', a:0, d:.2, tclass: 'bold'}, + {c: 'r', a:0, tclass: 'bold'}, + {c: 's', a:0, tclass: 'bold'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'bold'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'x', a:0, tclass: 'bold'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'z', a:0, tclass: 'bold'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'bold'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'bold'}, + {c: '˝', tclass: 'baccent'}, + {c: '˜', tclass: 'baccent'}, + {c: '¨', tclass: 'baccent'} + ] +}); + + +jsMath.Setup.Styles({ + '.typeset .cmr10': "font-family: serif", + '.typeset .italic': "font-style: italic", + '.typeset .bold': "font-weight: bold", + '.typeset .lucida': "font-family: 'lucida sans unicode'", + '.typeset .arial': "font-family: 'Arial unicode MS'", + '.typeset .cal': "font-family: 'Script MT', 'Script MT Bold', cursive", + '.typeset .arrows': "font-family: 'Arial unicode MS'", + '.typeset .arrow1': "font-family: 'Arial unicode MS'", + '.typeset .arrow1a': "font-family: 'Arial unicode MS'; position:relative; top:.05em;left:-.15em; margin-right:-.15em; display:inline-block", + '.typeset .arrow2': "font-family: 'Arial unicode MS'; position:relative; top:-.1em; display:inline-block;", + '.typeset .arrow3': "font-family: 'Arial unicode MS'; margin:.1em", + '.typeset .symbol': "font-family: 'Arial unicode MS'", + '.typeset .symbol2': "font-family: 'Arial unicode MS'", + '.typeset .delim1': "font-family: 'Times New Roman'; font-size: 133%; position:relative; top:.7em; display:inline-block", + '.typeset .delim1a': "font-family: 'Lucida sans unicode'; font-size: 133%; position:relative; top:.8em; display:inline-block", + '.typeset .delim1b': "font-family: 'Arial unicode MS'; font-size: 133%; position:relative; top:.7em; display:inline-block", + '.typeset .delim2': "font-family: 'Times New Roman'; font-size: 180%; position:relative; top:.75em; display:inline-block", + '.typeset .delim2a': "font-family: 'Lucida sans unicode'; font-size: 180%; position:relative; top:.8em; display:inline-block", + '.typeset .delim2b': "font-family: 'Arial unicode MS'; font-size: 180%; position:relative; top:.7em; display:inline-block", + '.typeset .delim3': "font-family: 'Times New Roman'; font-size: 250%; position:relative; top:.725em; display:inline-block", + '.typeset .delim3a': "font-family: 'Lucida sans unicode'; font-size: 250%; position:relative; top:.775em; display:inline-block", + '.typeset .delim3b': "font-family: 'Arial unicode MS'; font-size: 250%; position:relative; top:.7em; display:inline-block", + '.typeset .delim4': "font-family: 'Times New Roman'; font-size: 325%; position:relative; top:.7em; display:inline-block", + '.typeset .delim4a': "font-family: 'Lucida sans unicode'; font-size: 325%; position:relative; top:.775em; display:inline-block", + '.typeset .delim4b': "font-family: 'Arial unicode MS'; font-size: 325%; position:relative; top:.7em; display:inline-block", + '.typeset .delimx': "font-family: Symbol", + '.typeset .greek': "font-family: 'Times New Roman'", + '.typeset .igreek': "font-family: 'Times New Roman'; font-style:italic", + '.typeset .bgreek': "font-family: 'Times New Roman'; font-weight:bold", + '.typeset .bigop1': "font-family: 'Arial unicode MS'; font-size: 130%; position: relative; top: .7em; margin:-.05em; display:inline-block", + '.typeset .bigop1a': "font-family: 'Arial unicode MS'; font-size: 110%; position: relative; top: .85em; display:inline-block;", + '.typeset .bigop1b': "font-family: 'Arial unicode MS'; font-size: 180%; position: relative; top: .6em; display:inline-block", + '.typeset .bigop1c': "font-family: 'Arial unicode MS'; font-size: 85%; position: relative; top: 1em; display:inline-block", + '.typeset .bigop2': "font-family: 'Arial unicode MS'; font-size: 230%; position: relative; top: .6em; margin:-.05em; display:inline-block", + '.typeset .bigop2a': "font-family: 'Arial unicode MS'; font-size: 185%; position: relative; top: .75em; display:inline-block", + '.typeset .bigop2b': "font-family: 'Arial unicode MS'; font-size: 275%; position: relative; top: .55em; display:inline-block", + '.typeset .bigop2c': "font-family: 'Arial unicode MS'; font-size: 185%; position: relative; top: 1em; margin-right:-.1em; display:inline-block", + '.typeset .wide1': "font-family: 'Arial unicode MS'; font-size: 67%; position: relative; top:-.75em; display:inline-block;", + '.typeset .wide2': "font-family: 'Arial unicode MS'; font-size: 110%; position: relative; top:-.4em; display:inline-block;", + '.typeset .wide3': "font-family: 'Arial unicode MS'; font-size: 175%; position: relative; top:-.25em; display:inline-block", + '.typeset .wide1a': "font-family: 'Times New Roman'; font-size: 75%; position: relative; top:-.5em; display:inline-block", + '.typeset .wide2a': "font-family: 'Times New Roman'; font-size: 133%; position: relative; top:-.2em; display:inline-block", + '.typeset .wide3a': "font-family: 'Times New Roman'; font-size: 200%; position: relative; top:-.1em; display:inline-block", + '.typeset .root': "font-family: 'Arial unicode MS'; margin-right:-.075em; display:inline-block", + '.typeset .accent': "font-family: 'Arial unicode MS'; position:relative; top:.05em; left:.15em; display:inline-block", + '.typeset .iaccent': "font-family: 'Arial unicode MS'; position:relative; top:.05em; left:.15em; font-style:italic; display:inline-block", + '.typeset .baccent': "font-family: 'Arial unicode MS'; position:relative; top:.05em; left:.15em; font-weight:bold; display:inline-block" +}); + +// +// adjust for Mozilla +// +if (jsMath.browser == 'Mozilla') { + jsMath.Update.TeXfonts({ + cmex10: { + '48': {c: ''}, + '49': {c: ''}, + '50': {c: ''}, + '51': {c: ''}, + '52': {c: ''}, + '53': {c: ''}, + '54': {c: ''}, + '55': {c: ''}, + '56': {c: ''}, + '57': {c: ''}, + '58': {c: ''}, + '59': {c: ''}, + '60': {c: ''}, + '61': {c: ''}, + '62': {c: ''}, + '64': {c: ''}, + '65': {c: ''}, + '66': {c: ''}, + '67': {c: ''} + } + }); + jsMath.Setup.Styles({ + '.typeset .accent': 'font-family: Arial unicode MS; position:relative; top:.05em; left:.05em' + }); +} + +// +// adjust for MSIE +// +if (jsMath.browser == "MSIE") { + jsMath.Browser.msieFontBug = 1; +} + +/* + * No access to TeX "not" character, so fake this + * Also ajust the bowtie spacing + */ +jsMath.Macro('not','\\mathrel{\\rlap{\\kern 3mu/}}'); +jsMath.Macro('bowtie','\\mathrel\\triangleright\\kern-6mu\\mathrel\\triangleleft'); + +jsMath.Box.defaultH = 0.8; diff --git a/htdocs/jsMath/uncompressed/jsMath-fallback-symbols.js b/htdocs/jsMath/uncompressed/jsMath-fallback-symbols.js new file mode 100644 index 0000000000..05e56e65df --- /dev/null +++ b/htdocs/jsMath/uncompressed/jsMath-fallback-symbols.js @@ -0,0 +1,415 @@ +/* + * jsMath-fallback-symbols.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed to use image fonts for symbols + * but standard native fonts for letters and numbers. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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.Add(jsMath.Img,{ + UpdateTeXFonts: function (change) { + for (var font in change) { + for (var code in change[font]) { + jsMath.TeX[font][code] = change[font][code]; + jsMath.TeX[font][code].tclass = 'i' + font; + } + } + } +}); + + +jsMath.Img.UpdateTeXFonts({ + cmr10: { + '33': {c: '!', lig: {'96': 60}}, + '35': {c: '#'}, + '36': {c: '$'}, + '37': {c: '%'}, + '38': {c: '&'}, + '40': {c: '(', d:.2}, + '41': {c: ')', d:.2}, + '42': {c: '*', d:-.3}, + '43': {c: '+', a:.1}, + '44': {c: ',', a:-.3}, + '45': {c: '-', a:0, lig: {'45': 123}}, + '46': {c: '.', a:-.25}, + '47': {c: '/'}, + '48': {c: '0'}, + '49': {c: '1'}, + '50': {c: '2'}, + '51': {c: '3'}, + '52': {c: '4'}, + '53': {c: '5'}, + '54': {c: '6'}, + '55': {c: '7'}, + '56': {c: '8'}, + '57': {c: '9'}, + '58': {c: ':'}, + '59': {c: ';'}, + '61': {c: '=', a:0, d:-.1}, + '63': {c: '?', lig: {'96': 62}}, + '64': {c: '@'}, + '65': {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '66': {c: 'B'}, + '67': {c: 'C'}, + '68': {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}, + '69': {c: 'E'}, + '70': {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '71': {c: 'G'}, + '72': {c: 'H'}, + '73': {c: 'I', krn: {'73': 0.0278}}, + '74': {c: 'J'}, + '75': {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '76': {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '77': {c: 'M'}, + '78': {c: 'N'}, + '79': {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}, + '80': {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}, + '81': {c: 'Q', d: 1}, + '82': {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '83': {c: 'S'}, + '84': {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}, + '85': {c: 'U'}, + '86': {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '87': {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '88': {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '89': {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}, + '90': {c: 'Z'}, + '91': {c: '[', d:.1}, + '93': {c: ']', d:.1}, + '97': {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '98': {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '99': {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}}, + '100': {c: 'd'}, + '101': {c: 'e', a:0}, + '102': {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}}, + '103': {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}}, + '104': {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '105': {c: 'i'}, + '106': {c: 'j', d:1}, + '107': {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '108': {c: 'l'}, + '109': {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '110': {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '111': {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '112': {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '113': {c: 'q', a:0, d:1}, + '114': {c: 'r', a:0}, + '115': {c: 's', a:0}, + '116': {c: 't', krn: {'121': -0.0278, '119': -0.0278}}, + '117': {c: 'u', a:0, krn: {'119': -0.0278}}, + '118': {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '119': {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '120': {c: 'x', a:0}, + '121': {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}, + '122': {c: 'z', a:0} + }, + cmmi10: { + '65': {c: 'A', krn: {'127': 0.139}}, + '66': {c: 'B', ic: 0.0502, krn: {'127': 0.0833}}, + '67': {c: 'C', ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '68': {c: 'D', ic: 0.0278, krn: {'127': 0.0556}}, + '69': {c: 'E', ic: 0.0576, krn: {'127': 0.0833}}, + '70': {c: 'F', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}, + '71': {c: 'G', krn: {'127': 0.0833}}, + '72': {c: 'H', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}, + '73': {c: 'I', ic: 0.0785, krn: {'127': 0.111}}, + '74': {c: 'J', ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}}, + '75': {c: 'K', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}, + '76': {c: 'L', krn: {'127': 0.0278}}, + '77': {c: 'M', ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '78': {c: 'N', ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '79': {c: 'O', ic: 0.0278, krn: {'127': 0.0833}}, + '80': {c: 'P', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}, + '81': {c: 'Q', d:.2, krn: {'127': 0.0833}}, + '82': {c: 'R', ic: 0.00773, krn: {'127': 0.0833}}, + '83': {c: 'S', ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '84': {c: 'T', ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '85': {c: 'U', ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}}, + '86': {c: 'V', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}, + '87': {c: 'W', ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}, + '88': {c: 'X', ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '89': {c: 'Y', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}, + '90': {c: 'Z', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}, + '97': {c: 'a', a:0}, + '98': {c: 'b'}, + '99': {c: 'c', a:0, krn: {'127': 0.0556}}, + '100': {c: 'd', krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}}, + '101': {c: 'e', a:0, krn: {'127': 0.0556}}, + '102': {c: 'f', d:.2, ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}}, + '103': {c: 'g', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0278}}, + '104': {c: 'h', krn: {'127': -0.0278}}, + '105': {c: 'i'}, + '106': {c: 'j', d:.2, ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}}, + '107': {c: 'k', ic: 0.0315}, + '108': {c: 'l', ic: 0.0197, krn: {'127': 0.0833}}, + '109': {c: 'm', a:0}, + '110': {c: 'n', a:0}, + '111': {c: 'o', a:0, krn: {'127': 0.0556}}, + '112': {c: 'p', a:0, d:.2, krn: {'127': 0.0833}}, + '113': {c: 'q', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0833}}, + '114': {c: 'r', a:0, ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}, + '115': {c: 's', a:0, krn: {'127': 0.0556}}, + '116': {c: 't', krn: {'127': 0.0833}}, + '117': {c: 'u', a:0, krn: {'127': 0.0278}}, + '118': {c: 'v', a:0, ic: 0.0359, krn: {'127': 0.0278}}, + '119': {c: 'w', a:0, ic: 0.0269, krn: {'127': 0.0833}}, + '120': {c: 'x', a:0, krn: {'127': 0.0278}}, + '121': {c: 'y', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}}, + '122': {c: 'z', a:0, ic: 0.044, krn: {'127': 0.0556}} + }, + cmsy10: { + '0': {c:'−', a:.1} + }, + cmti10: { + '33': {c: '!', lig: {'96': 60}}, + '35': {c: '#', ic: 0.0662}, + '37': {c: '%', ic: 0.136}, + '38': {c: '&', ic: 0.0969}, + '40': {c: '(', d:.2, ic: 0.162}, + '41': {c: ')', d:.2, ic: 0.0369}, + '42': {c: '*', ic: 0.149}, + '43': {c: '+', a:.1, ic: 0.0369}, + '44': {c: ',', a:-.3, d:.2, w: 0.278}, + '45': {c: '-', a:0, ic: 0.0283, lig: {'45': 123}}, + '46': {c: '.', a:-.25}, + '47': {c: '/', ic: 0.162}, + '48': {c: '0', ic: 0.136}, + '49': {c: '1', ic: 0.136}, + '50': {c: '2', ic: 0.136}, + '51': {c: '3', ic: 0.136}, + '52': {c: '4', ic: 0.136}, + '53': {c: '5', ic: 0.136}, + '54': {c: '6', ic: 0.136}, + '55': {c: '7', ic: 0.136}, + '56': {c: '8', ic: 0.136}, + '57': {c: '9', ic: 0.136}, + '58': {c: ':', ic: 0.0582}, + '59': {c: ';', ic: 0.0582}, + '61': {c: '=', a:0, d:-.1, ic: 0.0662}, + '63': {c: '?', ic: 0.122, lig: {'96': 62}}, + '64': {c: '@', ic: 0.096}, + '65': {c: 'A', krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '66': {c: 'B', ic: 0.103}, + '67': {c: 'C', ic: 0.145}, + '68': {c: 'D', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}, + '69': {c: 'E', ic: 0.12}, + '70': {c: 'F', ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}, + '71': {c: 'G', ic: 0.0872}, + '72': {c: 'H', ic: 0.164}, + '73': {c: 'I', ic: 0.158}, + '74': {c: 'J', ic: 0.14}, + '75': {c: 'K', ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}, + '76': {c: 'L', krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '77': {c: 'M', ic: 0.164}, + '78': {c: 'N', ic: 0.164}, + '79': {c: 'O', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}, + '80': {c: 'P', ic: 0.103, krn: {'65': -0.0767}}, + '81': {c: 'Q', d:.2, ic: 0.094}, + '82': {c: 'R', ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '83': {c: 'S', ic: 0.12}, + '84': {c: 'T', ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}, + '85': {c: 'U', ic: 0.164}, + '86': {c: 'V', ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}, + '87': {c: 'W', ic: 0.184, krn: {'65': -0.0767}}, + '88': {c: 'X', ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}, + '89': {c: 'Y', ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}, + '90': {c: 'Z', ic: 0.145}, + '91': {c: '[', d:.1, ic: 0.188}, + '93': {c: ']', d:.1, ic: 0.105}, + '97': {c: 'a', a:0, ic: 0.0767}, + '98': {c: 'b', ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '99': {c: 'c', a:0, ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '100': {c: 'd', ic: 0.103, krn: {'108': 0.0511}}, + '101': {c: 'e', a:0, ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '102': {c: 'f', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}}, + '103': {c: 'g', a:0, d:.2, ic: 0.0885}, + '104': {c: 'h', ic: 0.0767}, + '105': {c: 'i', ic: 0.102}, + '106': {c: 'j', d:.2, ic: 0.145}, + '107': {c: 'k', ic: 0.108}, + '108': {c: 'l', ic: 0.103, krn: {'108': 0.0511}}, + '109': {c: 'm', a:0, ic: 0.0767}, + '110': {c: 'n', a:0, ic: 0.0767, krn: {'39': -0.102}}, + '111': {c: 'o', a:0, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '112': {c: 'p', a:0, d:.2, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '113': {c: 'q', a:0, d:.2, ic: 0.0885}, + '114': {c: 'r', a:0, ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}, + '115': {c: 's', a:0, ic: 0.0821}, + '116': {c: 't', ic: 0.0949}, + '117': {c: 'u', a:0, ic: 0.0767}, + '118': {c: 'v', a:0, ic: 0.108}, + '119': {c: 'w', a:0, ic: 0.108, krn: {'108': 0.0511}}, + '120': {c: 'x', a:0, ic: 0.12}, + '121': {c: 'y', a:0, d:.2, ic: 0.0885}, + '122': {c: 'z', a:0, ic: 0.123} + }, + cmbx10: { + '33': {c: '!', lig: {'96': 60}}, + '35': {c: '#'}, + '36': {c: '$'}, + '37': {c: '%'}, + '38': {c: '&'}, + '40': {c: '(', d:.2}, + '41': {c: ')', d:.2}, + '42': {c: '*'}, + '43': {c: '+', a:.1}, + '44': {c: ',', a:-.3, d:.2, w: 0.278}, + '45': {c: '-', a:0, lig: {'45': 123}}, + '46': {c: '.', a:-.25}, + '47': {c: '/'}, + '48': {c: '0'}, + '49': {c: '1'}, + '50': {c: '2'}, + '51': {c: '3'}, + '52': {c: '4'}, + '53': {c: '5'}, + '54': {c: '6'}, + '55': {c: '7'}, + '56': {c: '8'}, + '57': {c: '9'}, + '58': {c: ':'}, + '59': {c: ';'}, + '61': {c: '=', a:0, d:-.1}, + '63': {c: '?', lig: {'96': 62}}, + '64': {c: '@'}, + '65': {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '66': {c: 'B'}, + '67': {c: 'C'}, + '68': {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}, + '69': {c: 'E'}, + '70': {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '71': {c: 'G'}, + '72': {c: 'H'}, + '73': {c: 'I', krn: {'73': 0.0278}}, + '74': {c: 'J'}, + '75': {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '76': {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '77': {c: 'M'}, + '78': {c: 'N'}, + '79': {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}, + '80': {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}, + '81': {c: 'Q', d: 1}, + '82': {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}, + '83': {c: 'S'}, + '84': {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}, + '85': {c: 'U'}, + '86': {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '87': {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '88': {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}, + '89': {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}, + '90': {c: 'Z'}, + '91': {c: '[', d:.1}, + '93': {c: ']', d:.1}, + '97': {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '98': {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '99': {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}}, + '100': {c: 'd'}, + '101': {c: 'e', a:0}, + '102': {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}}, + '103': {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}}, + '104': {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '105': {c: 'i'}, + '106': {c: 'j', d:1}, + '107': {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '108': {c: 'l'}, + '109': {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '110': {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}, + '111': {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '112': {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}, + '113': {c: 'q', a:0, d:1}, + '114': {c: 'r', a:0}, + '115': {c: 's', a:0}, + '116': {c: 't', krn: {'121': -0.0278, '119': -0.0278}}, + '117': {c: 'u', a:0, krn: {'119': -0.0278}}, + '118': {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '119': {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}, + '120': {c: 'x', a:0}, + '121': {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}, + '122': {c: 'z', a:0} + } +}); + + +if (jsMath.browser == 'MSIE' && jsMath.platform == 'mac') { + jsMath.Setup.Styles({ + '.typeset .math': 'font-style: normal', + '.typeset .typeset': 'font-style: normal', + '.typeset .icmr10': 'font-family: Times', + '.typeset .icmmi10': 'font-family: Times; font-style: italic', + '.typeset .icmbx10': 'font-family: Times; font-weight: bold', + '.typeset .icmti10': 'font-family: Times; font-style: italic' + }); +} else { + jsMath.Setup.Styles({ + '.typeset .math': 'font-style: normal', + '.typeset .typeset': 'font-style: normal', + '.typeset .icmr10': 'font-family: serif', + '.typeset .icmmi10': 'font-family: serif; font-style: italic', + '.typeset .icmbx10': 'font-family: serif; font-weight: bold', + '.typeset .icmti10': 'font-family: serif; font-style: italic' + }); +} + + +jsMath.Add(jsMath.Img,{ + symbols: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 34, 39, + 60, 62, + + 92, 94, 95, + 96, + 123,124,125,126,127 + ] +}); + +/* + * for now, use images for everything + */ +jsMath.Img.SetFont({ + cmr10: jsMath.Img.symbols, + cmmi10: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, + 91, 92, 93, 94, 95, + 96, + 123,124,125,126,127 + ], + cmsy10: [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, 120,121,122,123,124,125,126,127 + ], + cmex10: ['all'], + cmti10: jsMath.Img.symbols.concat(36), + cmbx10: jsMath.Img.symbols +}); + +jsMath.Img.LoadFont('cm-fonts'); + diff --git a/htdocs/jsMath/uncompressed/jsMath-fallback-unix.js b/htdocs/jsMath/uncompressed/jsMath-fallback-unix.js new file mode 100644 index 0000000000..63e7856a61 --- /dev/null +++ b/htdocs/jsMath/uncompressed/jsMath-fallback-unix.js @@ -0,0 +1,935 @@ +/* + * jsMath-fallback-mac.js + * + * Part of the jsMath package for mathematics on the web. + * + * This file makes changes needed for when the TeX fonts are not available + * with a browser on the Mac. + * + * --------------------------------------------------------------------- + * + * Copyright 2004-2006 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. + */ + + + +/******************************************************************** + * + * Here we replace the TeX character mappings by equivalent unicode + * points when possible, and adjust the character dimensions + * based on the fonts we hope we get them from (the styles are set + * to try to use the best characters available in the standard + * fonts). + */ + +jsMath.Add(jsMath.TeX,{ + + cmr10: [ + // 00 - 0F + {c: 'Γ', tclass: 'greek'}, + {c: 'Δ', tclass: 'greek'}, + {c: 'Θ', tclass: 'greek'}, + {c: 'Λ', tclass: 'greek'}, + {c: 'Ξ', tclass: 'greek'}, + {c: 'Π', tclass: 'greek'}, + {c: 'Σ', tclass: 'greek'}, + {c: 'Υ', tclass: 'greek'}, + {c: 'Φ', tclass: 'greek'}, + {c: 'Ψ', tclass: 'greek'}, + {c: 'Ω', tclass: 'greek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'normal'}, + {c: 'fi', tclass: 'normal'}, + {c: 'fl', tclass: 'normal'}, + {c: 'ffi', tclass: 'normal'}, + {c: 'ffl', tclass: 'normal'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: '`', tclass: 'accent'}, + {c: '´', tclass: 'accent'}, + {c: 'ˇ', tclass: 'accent'}, + {c: '˘', tclass: 'accent'}, + {c: 'ˉ', tclass: 'accent'}, + {c: '˚', tclass: 'accent'}, + {c: '̧', tclass: 'normal'}, + {c: 'ß', tclass: 'normal'}, + {c: 'æ', a:0, tclass: 'normal'}, + {c: 'œ', a:0, tclass: 'normal'}, + {c: 'ø', tclass: 'normal'}, + {c: 'Æ', tclass: 'normal'}, + {c: 'Œ', tclass: 'normal'}, + {c: 'Ø', tclass: 'normal'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'normal'}, + {c: '!', lig: {'96': 60}, tclass: 'normal'}, + {c: '”', tclass: 'normal'}, + {c: '#', tclass: 'normal'}, + {c: '$', tclass: 'normal'}, + {c: '%', tclass: 'normal'}, + {c: '&', tclass: 'normal'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'normal'}, + {c: '(', d:.2, tclass: 'normal'}, + {c: ')', d:.2, tclass: 'normal'}, + {c: '*', tclass: 'normal'}, + {c: '+', a:.1, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'normal'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'normal'}, + {c: '.', a:-.25, tclass: 'normal'}, + {c: '/', tclass: 'normal'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: ':', tclass: 'normal'}, + {c: ';', tclass: 'normal'}, + {c: '¡', tclass: 'normal'}, + {c: '=', a:0, d:-.1, tclass: 'normal'}, + {c: '¿', tclass: 'normal'}, + {c: '?', lig: {'96': 62}, tclass: 'normal'}, + // 40 - 4F + {c: '@', tclass: 'normal'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'B', tclass: 'normal'}, + {c: 'C', tclass: 'normal'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + {c: 'E', tclass: 'normal'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'G', tclass: 'normal'}, + {c: 'H', tclass: 'normal'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'normal'}, + {c: 'J', tclass: 'normal'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'M', tclass: 'normal'}, + {c: 'N', tclass: 'normal'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'normal'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'Q', d: 1, tclass: 'normal'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'normal'}, + {c: 'S', tclass: 'normal'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'U', tclass: 'normal'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'normal'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'normal'}, + {c: 'Z', tclass: 'normal'}, + {c: '[', d:.1, tclass: 'normal'}, + {c: '“', tclass: 'normal'}, + {c: ']', d:.1, tclass: 'normal'}, + {c: 'ˆ', tclass: 'accent'}, + {c: '˙', tclass: 'accent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'normal'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'normal'}, + {c: 'd', tclass: 'normal'}, + {c: 'e', a:0, tclass: 'normal'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'normal'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'normal'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'i', tclass: 'normal'}, + {c: 'j', d:.2, tclass: 'normal'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'l', tclass: 'normal'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'q', a:0, d:.2, tclass: 'normal'}, + {c: 'r', a:0, tclass: 'normal'}, + {c: 's', a:0, tclass: 'normal'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'normal'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'normal'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'normal'}, + {c: 'x', a:0, tclass: 'normal'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'normal'}, + {c: 'z', a:0, tclass: 'normal'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'normal'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'normal'}, + {c: '˝', tclass: 'accent'}, + {c: '˜', tclass: 'accent'}, + {c: '¨', tclass: 'accent'} + ], + + cmmi10: [ + // 00 - 0F + {c: 'Γ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'igreek'}, + {c: 'Δ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Λ', krn: {'127': 0.167}, tclass: 'igreek'}, + {c: 'Ξ', ic: 0.0757, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Π', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Σ', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Υ', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Φ', krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'igreek'}, + {c: 'Ω', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'igreek'}, + {c: 'α', a:0, ic: 0.0037, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'β', d:.2, ic: 0.0528, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'γ', a:0, d:.2, ic: 0.0556, tclass: 'greek'}, + {c: 'δ', ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'greek'}, + {c: 'ε', a:0, krn: {'127': 0.0556}, tclass: 'symbol'}, + // 10 - 1F + {c: 'ζ', d:.2, ic: 0.0738, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'η', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'θ', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ι', a:0, krn: {'127': 0.0556}, tclass: 'greek'}, + {c: 'κ', a:0, tclass: 'greek'}, + {c: 'λ', tclass: 'greek'}, + {c: 'μ', a:0, d:.2, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'ν', a:0, ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'ξ', d:.2, ic: 0.046, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'π', a:0, ic: 0.0359, tclass: 'greek'}, + {c: 'ρ', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'σ', a:0, ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'greek'}, + {c: 'τ', a:0, ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}, tclass: 'greek'}, + {c: 'υ', a:0, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'greek'}, + {c: 'φ', a:.1, d:.2, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'χ', a:0, d:.2, krn: {'127': 0.0556}, tclass: 'greek'}, + // 20 - 2F + {c: 'ψ', a:.1, d:.2, ic: 0.0359, krn: {'127': 0.111}, tclass: 'greek'}, + {c: 'ω', a:0, ic: 0.0359, tclass: 'greek'}, + {c: 'ε', a:0, krn: {'127': 0.0833}, tclass: 'greek'}, + {c: 'ϑ', krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'ϖ', a:0, ic: 0.0278, tclass: 'normal'}, + {c: 'ϱ', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'ς', a:0, d:.2, ic: 0.0799, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'ϕ', a:.1, d:.2, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: '↼', a:0, d:-.2, tclass: 'harpoon'}, + {c: '↽', a:0, d:-.1, tclass: 'harpoon'}, + {c: '⇀', a:0, d:-.2, tclass: 'harpoon'}, + {c: '⇁', a:0, d:-.1, tclass: 'harpoon'}, + {c: '˓', a:.1, tclass: 'symbol'}, + {c: '˒', a:.1, tclass: 'symbol'}, + {c: '', tclass: 'symbol'}, + {c: '', tclass: 'symbol'}, + // 30 - 3F + {c: '0', tclass: 'normal'}, + {c: '1', tclass: 'normal'}, + {c: '2', tclass: 'normal'}, + {c: '3', tclass: 'normal'}, + {c: '4', tclass: 'normal'}, + {c: '5', tclass: 'normal'}, + {c: '6', tclass: 'normal'}, + {c: '7', tclass: 'normal'}, + {c: '8', tclass: 'normal'}, + {c: '9', tclass: 'normal'}, + {c: '.', a:-.3, tclass: 'normal'}, + {c: ',', a:-.3, d:.2, tclass: 'normal'}, + {c: '<', a:.1, tclass: 'normal'}, + {c: '/', krn: {'1': -0.0556, '65': -0.0556, '77': -0.0556, '78': -0.0556, '89': 0.0556, '90': -0.0556}, tclass: 'normal'}, + {c: '>', a:.1, tclass: 'normal'}, + {c: '', a:0, tclass: 'symbol'}, + // 40 - 4F + {c: '∂', ic: 0.0556, krn: {'127': 0.0833}, tclass: 'normal'}, + {c: 'A', krn: {'127': 0.139}, tclass: 'italic'}, + {c: 'B', ic: 0.0502, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'C', ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'D', ic: 0.0278, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'E', ic: 0.0576, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'F', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'G', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'H', ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'I', ic: 0.0785, krn: {'127': 0.111}, tclass: 'italic'}, + {c: 'J', ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}, tclass: 'italic'}, + {c: 'K', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 'L', krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'M', ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'N', ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'O', ic: 0.0278, krn: {'127': 0.0833}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}, tclass: 'italic'}, + {c: 'Q', d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'R', ic: 0.00773, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'S', ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'T', ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'U', ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}, tclass: 'italic'}, + {c: 'V', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'W', ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'X', ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: 'Y', ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}, tclass: 'italic'}, + {c: 'Z', ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}, tclass: 'italic'}, + {c: '♭', tclass: 'symbol2'}, + {c: '♮', tclass: 'symbol2'}, + {c: '♯', tclass: 'symbol2'}, + {c: '⌣', a:0, d:-.1, tclass: 'normal'}, + {c: '⌢', a:0, d:-.1, tclass: 'normal'}, + // 60 - 6F + {c: 'ℓ', krn: {'127': 0.111}, tclass: 'symbol'}, + {c: 'a', a:0, tclass: 'italic'}, + {c: 'b', tclass: 'italic'}, + {c: 'c', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'd', krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}, tclass: 'italic'}, + {c: 'e', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'f', d:.2, ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'h', krn: {'127': -0.0278}, tclass: 'italic'}, + {c: 'i', tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}, tclass: 'italic'}, + {c: 'k', ic: 0.0315, tclass: 'italic'}, + {c: 'l', ic: 0.0197, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'm', a:0, tclass: 'italic'}, + {c: 'n', a:0, tclass: 'italic'}, + {c: 'o', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}, tclass: 'italic'}, + {c: 's', a:0, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 't', krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'u', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.0359, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.0269, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: 'x', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0359, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.044, krn: {'127': 0.0556}, tclass: 'italic'}, + {c: 'ı', a:0, krn: {'127': 0.0278}, tclass: 'italic'}, + {c: 'j', d:.2, krn: {'127': 0.0833}, tclass: 'italic'}, + {c: '℘', a:0, d:.2, krn: {'127': 0.111}, tclass: 'normal'}, + {c: '', ic: 0.154, tclass: 'symbol'}, + {c: '̑', ic: 0.399, tclass: 'normal'} + ], + + cmsy10: [ + // 00 - 0F + {c: '−', a:.1, tclass: 'symbol'}, + {c: '·', a:0, d:-.2, tclass: 'symbol'}, + {c: '×', a:0, tclass: 'symbol'}, + {c: '*', a:0, tclass: 'symbol'}, + {c: '÷', a:0, tclass: 'symbol'}, + {c: '◊', tclass: 'symbol'}, + {c: '±', a:.1, tclass: 'symbol'}, + {c: '∓', tclass: 'symbol'}, + {c: '⊕', tclass: 'symbol'}, + {c: '⊖', tclass: 'symbol'}, + {c: '⊗', tclass: 'symbol'}, + {c: '⊘', tclass: 'symbol'}, + {c: '⊙', tclass: 'symbol'}, + {c: '◯', tclass: 'symbol'}, + {c: '°', a:0, d:-.1, tclass: 'symbol'}, + {c: '•', a:0, d:-.2, tclass: 'symbol'}, + // 10 - 1F + {c: '≍', a:.1, tclass: 'symbol'}, + {c: '≡', a:.1, tclass: 'symbol'}, + {c: '⊆', tclass: 'symbol'}, + {c: '⊇', tclass: 'symbol'}, + {c: '≤', tclass: 'symbol'}, + {c: '≥', tclass: 'symbol'}, + {c: '≼', tclass: 'symbol'}, + {c: '≽', tclass: 'symbol'}, + {c: '~', a:0, d: -.2, tclass: 'normal'}, + {c: '≈', a:.1, d:-.1, tclass: 'symbol'}, + {c: '⊂', tclass: 'symbol'}, + {c: '⊃', tclass: 'symbol'}, + {c: '≪', tclass: 'symbol'}, + {c: '≫', tclass: 'symbol'}, + {c: '≺', tclass: 'symbol'}, + {c: '≻', tclass: 'symbol'}, + // 20 - 2F + {c: '←', a:0, d:-.15, tclass: 'arrows'}, + {c: '→', a:0, d:-.15, tclass: 'arrows'}, + {c: '↑', h:1, tclass: 'arrows'}, + {c: '↓', h:1, tclass: 'arrows'}, + {c: '↔', a:0, tclass: 'arrows'}, + {c: '↗', h:1, tclass: 'arrows'}, + {c: '↘', h:1, tclass: 'arrows'}, + {c: '≃', a: .1, tclass: 'symbol'}, + {c: '⇐', a:.1, tclass: 'arrows'}, + {c: '⇒', a:.1, tclass: 'arrows'}, + {c: '⇑', h:.9, d:.1, tclass: 'arrows'}, + {c: '⇓', h:.9, d:.1, tclass: 'arrows'}, + {c: '⇔', a:.1, tclass: 'arrows'}, + {c: '↖', h:1, tclass: 'arrows'}, + {c: '↙', h:1, tclass: 'arrows'}, + {c: '∝', a:.1, tclass: 'symbol'}, + // 30 - 3F + {c: '', a: 0, tclass: 'symbol'}, + {c: '∞', a:.1, tclass: 'symbol'}, + {c: '∈', tclass: 'symbol'}, + {c: '∋', tclass: 'symbol'}, + {c: '△', tclass: 'symbol'}, + {c: '▽', tclass: 'symbol'}, + {c: '/', tclass: 'symbol'}, + {c: '|', a:0, tclass: 'normal'}, + {c: '∀', tclass: 'symbol'}, + {c: '∃', tclass: 'symbol'}, + {c: '¬', a:0, d:-.1, tclass: 'symbol1'}, + {c: '∅', tclass: 'symbol'}, + {c: 'ℜ', tclass: 'symbol'}, + {c: 'ℑ', tclass: 'symbol'}, + {c: '⊤', tclass: 'symbol'}, + {c: '⊥', tclass: 'symbol'}, + // 40 - 4F + {c: 'ℵ', tclass: 'symbol'}, + {c: 'A', krn: {'48': 0.194}, tclass: 'cal'}, + {c: 'B', ic: 0.0304, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'C', ic: 0.0583, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'D', ic: 0.0278, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'E', ic: 0.0894, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'F', ic: 0.0993, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'G', d:.2, ic: 0.0593, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'H', ic: 0.00965, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'I', ic: 0.0738, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'J', d:.2, ic: 0.185, krn: {'48': 0.167}, tclass: 'cal'}, + {c: 'K', ic: 0.0144, krn: {'48': 0.0556}, tclass: 'cal'}, + {c: 'L', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'M', krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'N', ic: 0.147, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'O', ic: 0.0278, krn: {'48': 0.111}, tclass: 'cal'}, + // 50 - 5F + {c: 'P', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Q', d:.2, krn: {'48': 0.111}, tclass: 'cal'}, + {c: 'R', krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'S', ic: 0.075, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'T', ic: 0.254, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'U', ic: 0.0993, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'V', ic: 0.0822, krn: {'48': 0.0278}, tclass: 'cal'}, + {c: 'W', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'X', ic: 0.146, krn: {'48': 0.139}, tclass: 'cal'}, + {c: 'Y', ic: 0.0822, krn: {'48': 0.0833}, tclass: 'cal'}, + {c: 'Z', ic: 0.0794, krn: {'48': 0.139}, tclass: 'cal'}, + {c: '⋃', tclass: 'symbol'}, + {c: '⋂', tclass: 'symbol'}, + {c: '⊎', tclass: 'symbol'}, + {c: '⋀', tclass: 'symbol'}, + {c: '⋁', tclass: 'symbol'}, + // 60 - 6F + {c: '⊢', tclass: 'symbol'}, + {c: '⊣', tclass: 'symbol2'}, + {c: '', a:.3, d:.2, tclass: 'normal'}, + {c: '', a:.3, d:.2, tclass: 'normal'}, + {c: '', a:.3, d:.2, tclass: 'normal'}, + {c: '', a:.3, d:.2, tclass: 'normal'}, + {c: '{', d:.2, tclass: 'normal'}, + {c: '}', d:.2, tclass: 'normal'}, + {c: '〈', a:.3, d:.2, tclass: 'normal'}, + {c: '〉', a:.3, d:.2, tclass: 'normal'}, + {c: '|', d:.1, tclass: 'vertical'}, + {c: '||', d:0, tclass: 'vertical'}, + {c: '↕', h:1, d:.15, tclass: 'arrows'}, + {c: '⇕', a:.2, d:.1, tclass: 'arrows'}, + {c: '∖', a:.3, d:.1, tclass: 'normal'}, + {c: '≀', tclass: 'symbol'}, + // 70 - 7F + {c: '', h:.04, d:.9, tclass: 'normal'}, + {c: '∐', a:.4, tclass: 'symbol'}, + {c: '∇', tclass: 'symbol'}, + {c: '∫', h:1, d:.1, ic: 0.111, tclass: 'root'}, + {c: '⊔', tclass: 'symbol'}, + {c: '⊓', tclass: 'symbol'}, + {c: '⊑', tclass: 'symbol'}, + {c: '⊒', tclass: 'symbol'}, + {c: '§', d:.1, tclass: 'normal'}, + {c: '†', d:.1, tclass: 'normal'}, + {c: '‡', d:.1, tclass: 'normal'}, + {c: '¶', a:.3, d:.1, tclass: 'normal'}, + {c: '♣', tclass: 'symbol'}, + {c: '♦', tclass: 'symbol'}, + {c: '♥', tclass: 'symbol'}, + {c: '♠', tclass: 'symbol'} + ], + + cmex10: [ + // 00 - 0F + {c: '(', h: 0.04, d: 1.16, n: 16, tclass: 'delim1'}, + {c: ')', h: 0.04, d: 1.16, n: 17, tclass: 'delim1'}, + {c: '[', h: 0.04, d: 1.16, n: 104, tclass: 'delim1'}, + {c: ']', h: 0.04, d: 1.16, n: 105, tclass: 'delim1'}, + {c: '', h: 0.04, d: 1.16, n: 106, tclass: 'delim1'}, + {c: '', h: 0.04, d: 1.16, n: 107, tclass: 'delim1'}, + {c: '', h: 0.04, d: 1.16, n: 108, tclass: 'delim1'}, + {c: '', h: 0.04, d: 1.16, n: 109, tclass: 'delim1'}, + {c: '{', h: 0.04, d: 1.16, n: 110, tclass: 'delim1'}, + {c: '}', h: 0.04, d: 1.16, n: 111, tclass: 'delim1'}, + {c: '〈', h: 0.04, d: 1.16, n: 68, tclass: 'delim1c'}, + {c: '〉', h: 0.04, d: 1.16, n: 69, tclass: 'delim1c'}, + {c: '|', h:.7, d:0, delim: {rep: 12}, tclass: 'vertical'}, + {c: '||', h:.7, d:0, delim: {rep: 13}, tclass: 'vertical'}, + {c: '/', h: 0.04, d: 1.16, n: 46, tclass: 'delim1b'}, + {c: '∖', h: 0.04, d: 1.16, n: 47, tclass: 'delim1b'}, + // 10 - 1F + {c: '(', h: 0.04, d: 1.76, n: 18, tclass: 'delim2'}, + {c: ')', h: 0.04, d: 1.76, n: 19, tclass: 'delim2'}, + {c: '(', h: 0.04, d: 2.36, n: 32, tclass: 'delim3'}, + {c: ')', h: 0.04, d: 2.36, n: 33, tclass: 'delim3'}, + {c: '[', h: 0.04, d: 2.36, n: 34, tclass: 'delim3'}, + {c: ']', h: 0.04, d: 2.36, n: 35, tclass: 'delim3'}, + {c: '', h: 0.04, d: 2.36, n: 36, tclass: 'delim3'}, + {c: '', h: 0.04, d: 2.36, n: 37, tclass: 'delim3'}, + {c: '', h: 0.04, d: 2.36, n: 38, tclass: 'delim3'}, + {c: '', h: 0.04, d: 2.36, n: 39, tclass: 'delim3'}, + {c: '{', h: 0.04, d: 2.36, n: 40, tclass: 'delim3'}, + {c: '}', h: 0.04, d: 2.36, n: 41, tclass: 'delim3'}, + {c: '〈', h: 0.04, d: 2.36, n: 42, tclass: 'delim3c'}, + {c: '〉', h: 0.04, d: 2.36, n: 43, tclass: 'delim3c'}, + {c: '/', h: 0.04, d: 2.36, n: 44, tclass: 'delim3b'}, + {c: '∖', h: 0.04, d: 2.36, n: 45, tclass: 'delim3b'}, + // 20 - 2F + {c: '(', h: 0.04, d: 2.96, n: 48, tclass: 'delim4'}, + {c: ')', h: 0.04, d: 2.96, n: 49, tclass: 'delim4'}, + {c: '[', h: 0.04, d: 2.96, n: 50, tclass: 'delim4'}, + {c: ']', h: 0.04, d: 2.96, n: 51, tclass: 'delim4'}, + {c: '', h: 0.04, d: 2.96, n: 52, tclass: 'delim4'}, + {c: '', h: 0.04, d: 2.96, n: 53, tclass: 'delim4'}, + {c: '', h: 0.04, d: 2.96, n: 54, tclass: 'delim4'}, + {c: '', h: 0.04, d: 2.96, n: 55, tclass: 'delim4'}, + {c: '{', h: 0.04, d: 2.96, n: 56, tclass: 'delim4'}, + {c: '}', h: 0.04, d: 2.96, n: 57, tclass: 'delim4'}, + {c: '〈', h: 0.04, d: 2.96, tclass: 'delim4c'}, + {c: '〉', h: 0.04, d: 2.96, tclass: 'delim4c'}, + {c: '/', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '∖', h: 0.04, d: 2.96, tclass: 'delim4b'}, + {c: '/', h: 0.04, d: 1.76, n: 30, tclass: 'delim2b'}, + {c: '∖', h: 0.04, d: 1.76, n: 31, tclass: 'delim2b'}, + // 30 - 3F + {c: '', h: .8, d: .15, delim: {top: 48, bot: 64, rep: 66}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 49, bot: 65, rep: 67}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 50, bot: 52, rep: 54}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 51, bot: 53, rep: 55}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {bot: 52, rep: 54}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {bot: 53, rep: 55}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 50, rep: 54}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 51, rep: 55}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 56, mid: 60, bot: 58, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 57, mid: 61, bot: 59, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 56, bot: 58, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 57, bot: 59, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {rep: 63}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {rep: 119}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {rep: 62}, tclass: 'delim'}, + {c: '|', h: .65, d: 0, delim: {top: 120, bot: 121, rep: 63}, tclass: 'vertical'}, + // 40 - 4F + {c: '', h: .8, d: .15, delim: {top: 56, bot: 59, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {top: 57, bot: 58, rep: 62}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {rep: 66}, tclass: 'delim'}, + {c: '', h: .8, d: .15, delim: {rep: 67}, tclass: 'delim'}, + {c: '〈', h: 0.04, d: 1.76, n: 28, tclass: 'delim2c'}, + {c: '〉', h: 0.04, d: 1.76, n: 29, tclass: 'delim2c'}, + {c: '⊔', h: 0, d: 1, n: 71, tclass: 'bigop1'}, + {c: '⊔', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '∮', h: 0, d: 1.11, ic: 0.095, n: 73, tclass: 'bigop1c'}, + {c: '∮', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '⊙', h: 0, d: 1, n: 75, tclass: 'bigop1'}, + {c: '⊙', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '⊕', h: 0, d: 1, n: 77, tclass: 'bigop1'}, + {c: '⊕', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '⊗', h: 0, d: 1, n: 79, tclass: 'bigop1'}, + {c: '⊗', h: 0.1, d: 1.5, tclass: 'bigop2'}, + // 50 - 5F + {c: '∑', h: 0, d: 1, n: 88, tclass: 'bigop1a'}, + {c: '∏', h: 0, d: 1, n: 89, tclass: 'bigop1a'}, + {c: '∫', h: 0, d: 1.11, ic: 0.095, n: 90, tclass: 'bigop1c'}, + {c: '∪', h: 0, d: 1, n: 91, tclass: 'bigop1b'}, + {c: '∩', h: 0, d: 1, n: 92, tclass: 'bigop1b'}, + {c: '⊎', h: 0, d: 1, n: 93, tclass: 'bigop1b'}, + {c: '∧', h: 0, d: 1, n: 94, tclass: 'bigop1'}, + {c: '∨', h: 0, d: 1, n: 95, tclass: 'bigop1'}, + {c: '∑', h: 0.1, d: 1.6, tclass: 'bigop2a'}, + {c: '∏', h: 0.1, d: 1.5, tclass: 'bigop2a'}, + {c: '∫', h: 0, d: 2.22, ic: 0.222, tclass: 'bigop2c'}, + {c: '∪', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '∩', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '⊎', h: 0.1, d: 1.5, tclass: 'bigop2b'}, + {c: '∧', h: 0.1, d: 1.5, tclass: 'bigop2'}, + {c: '∨', h: 0.1, d: 1.5, tclass: 'bigop2'}, + // 60 - 6F + {c: '∐', h: 0, d: 1, n: 97, tclass: 'bigop1a'}, + {c: '∐', h: 0.1, d: 1.5, tclass: 'bigop2a'}, + {c: '︿', h: 0.722, w: .65, n: 99, tclass: 'wide1'}, + {c: '︿', h: 0.85, w: 1.1, n: 100, tclass: 'wide2'}, + {c: '︿', h: 0.99, w: 1.65, tclass: 'wide3'}, + {c: '⁓', h: 0.722, w: .75, n: 102, tclass: 'wide1a'}, + {c: '⁓', h: 0.8, w: 1.35, n: 103, tclass: 'wide2a'}, + {c: '⁓', h: 0.99, w: 2, tclass: 'wide3a'}, + {c: '[', h: 0.04, d: 1.76, n: 20, tclass: 'delim2'}, + {c: ']', h: 0.04, d: 1.76, n: 21, tclass: 'delim2'}, + {c: '', h: 0.04, d: 1.76, n: 22, tclass: 'delim2'}, + {c: '', h: 0.04, d: 1.76, n: 23, tclass: 'delim2'}, + {c: '', h: 0.04, d: 1.76, n: 24, tclass: 'delim2'}, + {c: '', h: 0.04, d: 1.76, n: 25, tclass: 'delim2'}, + {c: '{', h: 0.04, d: 1.76, n: 26, tclass: 'delim2'}, + {c: '}', h: 0.04, d: 1.76, n: 27, tclass: 'delim2'}, + // 70 - 7F + {c: '', h: 0.04, d: 1.16, n: 113, tclass: 'root'}, + {c: '', h: 0.04, d: 1.76, n: 114, tclass: 'root'}, + {c: '', h: 0.06, d: 2.36, n: 115, tclass: 'root'}, + {c: '', h: 0.08, d: 2.96, n: 116, tclass: 'root'}, + {c: '', h: 0.1, d: 3.75, n: 117, tclass: 'root'}, + {c: '', h: .12, d: 4.5, n: 118, tclass: 'root'}, + {c: '', h: .14, d: 5.7, tclass: 'root'}, + {c: '||', h:.65, d:0, delim: {top: 126, bot: 127, rep: 119}, tclass: 'vertical'}, + {c: '▵', h:.45, delim: {top: 120, rep: 63}, tclass: 'arrow1'}, + {c: '▿', h:.45, delim: {bot: 121, rep: 63}, tclass: 'arrow1'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '', h:.1, tclass: 'symbol'}, + {c: '▵', h:.5, delim: {top: 126, rep: 119}, tclass: 'arrow2'}, + {c: '▿', h:.5, delim: {bot: 127, rep: 119}, tclass: 'arrow2'} + ], + + cmti10: [ + // 00 - 0F + {c: 'Γ', ic: 0.133, tclass: 'igreek'}, + {c: 'Δ', tclass: 'igreek'}, + {c: 'Θ', ic: 0.094, tclass: 'igreek'}, + {c: 'Λ', tclass: 'igreek'}, + {c: 'Ξ', ic: 0.153, tclass: 'igreek'}, + {c: 'Π', ic: 0.164, tclass: 'igreek'}, + {c: 'Σ', ic: 0.12, tclass: 'igreek'}, + {c: 'Υ', ic: 0.111, tclass: 'igreek'}, + {c: 'Φ', ic: 0.0599, tclass: 'igreek'}, + {c: 'Ψ', ic: 0.111, tclass: 'igreek'}, + {c: 'Ω', ic: 0.103, tclass: 'igreek'}, + {c: 'ff', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 14, '108': 15}, tclass: 'italic'}, + {c: 'fi', ic: 0.103, tclass: 'italic'}, + {c: 'fl', ic: 0.103, tclass: 'italic'}, + {c: 'ffi', ic: 0.103, tclass: 'italic'}, + {c: 'ffl', ic: 0.103, tclass: 'italic'}, + // 10 - 1F + {c: 'ı', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.0374, tclass: 'italic'}, + {c: '`', tclass: 'iaccent'}, + {c: '´', ic: 0.0969, tclass: 'iaccent'}, + {c: 'ˇ', ic: 0.083, tclass: 'iaccent'}, + {c: '˘', ic: 0.108, tclass: 'iaccent'}, + {c: 'ˉ', ic: 0.103, tclass: 'iaccent'}, + {c: '˚', tclass: 'iaccent'}, + {c: '?', d: 0.17, w: 0.46, tclass: 'italic'}, + {c: 'ß', ic: 0.105, tclass: 'italic'}, + {c: 'æ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'œ', a:0, ic: 0.0751, tclass: 'italic'}, + {c: 'ø', ic: 0.0919, tclass: 'italic'}, + {c: 'Æ', ic: 0.12, tclass: 'italic'}, + {c: 'Œ', ic: 0.12, tclass: 'italic'}, + {c: 'Ø', ic: 0.094, tclass: 'italic'}, + // 20 - 2F + {c: '?', krn: {'108': -0.256, '76': -0.321}, tclass: 'italic'}, + {c: '!', ic: 0.124, lig: {'96': 60}, tclass: 'italic'}, + {c: '”', ic: 0.0696, tclass: 'italic'}, + {c: '#', ic: 0.0662, tclass: 'italic'}, + {c: '$', tclass: 'italic'}, + {c: '%', ic: 0.136, tclass: 'italic'}, + {c: '&', ic: 0.0969, tclass: 'italic'}, + {c: '’', ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}, tclass: 'italic'}, + {c: '(', d:.2, ic: 0.162, tclass: 'italic'}, + {c: ')', d:.2, ic: 0.0369, tclass: 'italic'}, + {c: '*', ic: 0.149, tclass: 'italic'}, + {c: '+', a:.1, ic: 0.0369, tclass: 'italic'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'italic'}, + {c: '-', a:0, ic: 0.0283, lig: {'45': 123}, tclass: 'italic'}, + {c: '.', a:-.25, tclass: 'italic'}, + {c: '/', ic: 0.162, tclass: 'italic'}, + // 30 - 3F + {c: '0', ic: 0.136, tclass: 'italic'}, + {c: '1', ic: 0.136, tclass: 'italic'}, + {c: '2', ic: 0.136, tclass: 'italic'}, + {c: '3', ic: 0.136, tclass: 'italic'}, + {c: '4', ic: 0.136, tclass: 'italic'}, + {c: '5', ic: 0.136, tclass: 'italic'}, + {c: '6', ic: 0.136, tclass: 'italic'}, + {c: '7', ic: 0.136, tclass: 'italic'}, + {c: '8', ic: 0.136, tclass: 'italic'}, + {c: '9', ic: 0.136, tclass: 'italic'}, + {c: ':', ic: 0.0582, tclass: 'italic'}, + {c: ';', ic: 0.0582, tclass: 'italic'}, + {c: '¡', ic: 0.0756, tclass: 'italic'}, + {c: '=', a:0, d:-.1, ic: 0.0662, tclass: 'italic'}, + {c: '¿', tclass: 'italic'}, + {c: '?', ic: 0.122, lig: {'96': 62}, tclass: 'italic'}, + // 40 - 4F + {c: '@', ic: 0.096, tclass: 'italic'}, + {c: 'A', krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'B', ic: 0.103, tclass: 'italic'}, + {c: 'C', ic: 0.145, tclass: 'italic'}, + {c: 'D', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + {c: 'E', ic: 0.12, tclass: 'italic'}, + {c: 'F', ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'G', ic: 0.0872, tclass: 'italic'}, + {c: 'H', ic: 0.164, tclass: 'italic'}, + {c: 'I', ic: 0.158, tclass: 'italic'}, + {c: 'J', ic: 0.14, tclass: 'italic'}, + {c: 'K', ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'L', krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'M', ic: 0.164, tclass: 'italic'}, + {c: 'N', ic: 0.164, tclass: 'italic'}, + {c: 'O', ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}, tclass: 'italic'}, + // 50 - 5F + {c: 'P', ic: 0.103, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'Q', d: 1, ic: 0.094, tclass: 'italic'}, + {c: 'R', ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'S', ic: 0.12, tclass: 'italic'}, + {c: 'T', ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'U', ic: 0.164, tclass: 'italic'}, + {c: 'V', ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'W', ic: 0.184, krn: {'65': -0.0767}, tclass: 'italic'}, + {c: 'X', ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}, tclass: 'italic'}, + {c: 'Y', ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}, tclass: 'italic'}, + {c: 'Z', ic: 0.145, tclass: 'italic'}, + {c: '[', d:.1, ic: 0.188, tclass: 'italic'}, + {c: '“', ic: 0.169, tclass: 'italic'}, + {c: ']', d:.1, ic: 0.105, tclass: 'italic'}, + {c: 'ˆ', ic: 0.0665, tclass: 'iaccent'}, + {c: '˙', ic: 0.118, tclass: 'iaccent'}, + // 60 - 6F + {c: '‘', ic: 0.124, lig: {'96': 92}, tclass: 'italic'}, + {c: 'a', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'b', ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'c', a:0, ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'd', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'e', a:0, ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'f', ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'italic'}, + {c: 'g', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'h', ic: 0.0767, tclass: 'italic'}, + {c: 'i', ic: 0.102, tclass: 'italic'}, + {c: 'j', d:.2, ic: 0.145, tclass: 'italic'}, + {c: 'k', ic: 0.108, tclass: 'italic'}, + {c: 'l', ic: 0.103, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'm', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'n', a:0, ic: 0.0767, krn: {'39': -0.102}, tclass: 'italic'}, + {c: 'o', a:0, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + // 70 - 7F + {c: 'p', a:0, d:.2, ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 'q', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'r', a:0, ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}, tclass: 'italic'}, + {c: 's', a:0, ic: 0.0821, tclass: 'italic'}, + {c: 't', ic: 0.0949, tclass: 'italic'}, + {c: 'u', a:0, ic: 0.0767, tclass: 'italic'}, + {c: 'v', a:0, ic: 0.108, tclass: 'italic'}, + {c: 'w', a:0, ic: 0.108, krn: {'108': 0.0511}, tclass: 'italic'}, + {c: 'x', a:0, ic: 0.12, tclass: 'italic'}, + {c: 'y', a:0, d:.2, ic: 0.0885, tclass: 'italic'}, + {c: 'z', a:0, ic: 0.123, tclass: 'italic'}, + {c: '–', a:.1, ic: 0.0921, lig: {'45': 124}, tclass: 'italic'}, + {c: '—', a:.1, ic: 0.0921, tclass: 'italic'}, + {c: '˝', ic: 0.122, tclass: 'iaccent'}, + {c: '˜', ic: 0.116, tclass: 'iaccent'}, + {c: '¨', tclass: 'iaccent'} + ], + + cmbx10: [ + // 00 - 0F + {c: 'Γ', tclass: 'bgreek'}, + {c: 'Δ', tclass: 'bgreek'}, + {c: 'Θ', tclass: 'bgreek'}, + {c: 'Λ', tclass: 'bgreek'}, + {c: 'Ξ', tclass: 'bgreek'}, + {c: 'Π', tclass: 'bgreek'}, + {c: 'Σ', tclass: 'bgreek'}, + {c: 'Υ', tclass: 'bgreek'}, + {c: 'Φ', tclass: 'bgreek'}, + {c: 'Ψ', tclass: 'bgreek'}, + {c: 'Ω', tclass: 'bgreek'}, + {c: 'ff', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}, tclass: 'bold'}, + {c: 'fi', tclass: 'bold'}, + {c: 'fl', tclass: 'bold'}, + {c: 'ffi', tclass: 'bold'}, + {c: 'ffl', tclass: 'bold'}, + // 10 - 1F + {c: 'ı', a:0, tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: '`', tclass: 'baccent'}, + {c: '´', tclass: 'baccent'}, + {c: 'ˇ', tclass: 'baccent'}, + {c: '˘', tclass: 'baccent'}, + {c: 'ˉ', tclass: 'baccent'}, + {c: '˚', tclass: 'baccent'}, + {c: '?', tclass: 'bold'}, + {c: 'ß', tclass: 'bold'}, + {c: 'æ', a:0, tclass: 'bold'}, + {c: 'œ', a:0, tclass: 'bold'}, + {c: 'ø', tclass: 'bold'}, + {c: 'Æ', tclass: 'bold'}, + {c: 'Œ', tclass: 'bold'}, + {c: 'Ø', tclass: 'bold'}, + // 20 - 2F + {c: '?', krn: {'108': -0.278, '76': -0.319}, tclass: 'bold'}, + {c: '!', lig: {'96': 60}, tclass: 'bold'}, + {c: '”', tclass: 'bold'}, + {c: '#', tclass: 'bold'}, + {c: '$', tclass: 'bold'}, + {c: '%', tclass: 'bold'}, + {c: '&', tclass: 'bold'}, + {c: '’', krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}, tclass: 'bold'}, + {c: '(', d:.2, tclass: 'bold'}, + {c: ')', d:.2, tclass: 'bold'}, + {c: '*', tclass: 'bold'}, + {c: '+', a:.1, tclass: 'bold'}, + {c: ',', a:-.3, d:.2, w: 0.278, tclass: 'bold'}, + {c: '-', a:0, lig: {'45': 123}, tclass: 'bold'}, + {c: '.', a:-.25, tclass: 'bold'}, + {c: '/', tclass: 'bold'}, + // 30 - 3F + {c: '0', tclass: 'bold'}, + {c: '1', tclass: 'bold'}, + {c: '2', tclass: 'bold'}, + {c: '3', tclass: 'bold'}, + {c: '4', tclass: 'bold'}, + {c: '5', tclass: 'bold'}, + {c: '6', tclass: 'bold'}, + {c: '7', tclass: 'bold'}, + {c: '8', tclass: 'bold'}, + {c: '9', tclass: 'bold'}, + {c: ':', tclass: 'bold'}, + {c: ';', tclass: 'bold'}, + {c: '¡', tclass: 'bold'}, + {c: '=', a:0, d:-.1, tclass: 'bold'}, + {c: '¿', tclass: 'bold'}, + {c: '?', lig: {'96': 62}, tclass: 'bold'}, + // 40 - 4F + {c: '@', tclass: 'bold'}, + {c: 'A', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'B', tclass: 'bold'}, + {c: 'C', tclass: 'bold'}, + {c: 'D', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + {c: 'E', tclass: 'bold'}, + {c: 'F', krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'G', tclass: 'bold'}, + {c: 'H', tclass: 'bold'}, + {c: 'I', krn: {'73': 0.0278}, tclass: 'bold'}, + {c: 'J', tclass: 'bold'}, + {c: 'K', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'L', krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'M', tclass: 'bold'}, + {c: 'N', tclass: 'bold'}, + {c: 'O', krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}, tclass: 'bold'}, + // 50 - 5F + {c: 'P', krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'Q', d: 1, tclass: 'bold'}, + {c: 'R', krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}, tclass: 'bold'}, + {c: 'S', tclass: 'bold'}, + {c: 'T', krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'U', tclass: 'bold'}, + {c: 'V', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'W', ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'X', krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}, tclass: 'bold'}, + {c: 'Y', ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}, tclass: 'bold'}, + {c: 'Z', tclass: 'bold'}, + {c: '[', d:.1, tclass: 'bold'}, + {c: '“', tclass: 'bold'}, + {c: ']', d:.1, tclass: 'bold'}, + {c: 'ˆ', tclass: 'baccent'}, + {c: '˙', tclass: 'baccent'}, + // 60 - 6F + {c: '‘', lig: {'96': 92}, tclass: 'bold'}, + {c: 'a', a:0, krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'b', krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'c', a:0, krn: {'104': -0.0278, '107': -0.0278}, tclass: 'bold'}, + {c: 'd', tclass: 'bold'}, + {c: 'e', a:0, tclass: 'bold'}, + {c: 'f', ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}, tclass: 'bold'}, + {c: 'g', a:0, d:.2, ic: 0.0139, krn: {'106': 0.0278}, tclass: 'bold'}, + {c: 'h', krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'i', tclass: 'bold'}, + {c: 'j', d:.2, tclass: 'bold'}, + {c: 'k', krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'l', tclass: 'bold'}, + {c: 'm', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'n', a:0, krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'o', a:0, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + // 70 - 7F + {c: 'p', a:0, d:.2, krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'q', a:0, d:.2, tclass: 'bold'}, + {c: 'r', a:0, tclass: 'bold'}, + {c: 's', a:0, tclass: 'bold'}, + {c: 't', krn: {'121': -0.0278, '119': -0.0278}, tclass: 'bold'}, + {c: 'u', a:0, krn: {'119': -0.0278}, tclass: 'bold'}, + {c: 'v', a:0, ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'w', a:0, ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}, tclass: 'bold'}, + {c: 'x', a:0, tclass: 'bold'}, + {c: 'y', a:0, d:.2, ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}, tclass: 'bold'}, + {c: 'z', a:0, tclass: 'bold'}, + {c: '–', a:.1, ic: 0.0278, lig: {'45': 124}, tclass: 'bold'}, + {c: '—', a:.1, ic: 0.0278, tclass: 'bold'}, + {c: '˝', tclass: 'baccent'}, + {c: '˜', tclass: 'baccent'}, + {c: '¨', tclass: 'baccent'} + ] +}); + +jsMath.Setup.Styles({ + '.typeset .math': 'font-style: normal', + '.typeset .italic': 'font-style: italic', + '.typeset .bold': 'font-weight: bold', + '.typeset .cmr10': 'font-family: serif', + '.typeset .cal': 'font-family: cursive', + '.typeset .arrows': '', + '.typeset .arrow1': '', + '.typeset .arrow2': '', + '.typeset .harpoon': 'font-size: 125%', + '.typeset .symbol': '', + '.typeset .symbol2': '', + '.typeset .delim1': 'font-size: 133%; position:relative; top:.75em', + '.typeset .delim1b': 'font-size: 133%; position:relative; top:.8em; margin: -.1em', + '.typeset .delim1c': 'font-size: 120%; position:relative; top:.8em;', + '.typeset .delim2': 'font-size: 180%; position:relative; top:.75em', + '.typeset .delim2b': 'font-size: 190%; position:relative; top:.8em; margin: -.1em', + '.typeset .delim2c': 'font-size: 167%; position:relative; top:.8em;', + '.typeset .delim3': 'font-size: 250%; position:relative; top:.725em', + '.typeset .delim3b': 'font-size: 250%; position:relative; top:.8em; margin: -.1em', + '.typeset .delim3c': 'font-size: 240%; position:relative; top:.775em;', + '.typeset .delim4': 'font-size: 325%; position:relative; top:.7em', + '.typeset .delim4b': 'font-size: 325%; position:relative; top:.8em; margin: -.1em', + '.typeset .delim4c': 'font-size: 300%; position:relative; top:.8em;', + '.typeset .delim': '', + '.typeset .vertical': '', + '.typeset .greek': '', + '.typeset .igreek': 'font-style: italic', + '.typeset .bgreek': 'font-weight: bold', + '.typeset .bigop1': 'font-size: 133%; position: relative; top: .85em; margin:-.05em', + '.typeset .bigop1a': 'font-size: 100%; position: relative; top: .775em;', + '.typeset .bigop1b': 'font-size: 160%; position: relative; top: .7em; margin:-.1em', + '.typeset .bigop1c': 'font-size: 125%; position: relative; top: .75em; margin:-.1em;', + '.typeset .bigop2': 'font-size: 200%; position: relative; top: .8em; margin:-.07em', + '.typeset .bigop2a': 'font-size: 175%; position: relative; top: .7em;', + '.typeset .bigop2b': 'font-size: 270%; position: relative; top: .62em; margin:-.1em', + '.typeset .bigop2c': 'font-size: 250%; position: relative; top: .7em; margin:-.17em;', + '.typeset .wide1': 'font-size: 67%; position: relative; top:-.8em', + '.typeset .wide2': 'font-size: 110%; position: relative; top:-.5em', + '.typeset .wide3': 'font-size: 175%; position: relative; top:-.32em', + '.typeset .wide1a': 'font-size: 75%; position: relative; top:-.5em', + '.typeset .wide2a': 'font-size: 133%; position: relative; top: -.15em', + '.typeset .wide3a': 'font-size: 200%; position: relative; top: -.05em', + '.typeset .root': '', + '.typeset .accent': 'position: relative; top: .02em', + '.typeset .iaccent': 'position: relative; top: .02em; font-style: italic', + '.typeset .baccent': 'position: relative; top: .02em; font-weight: bold' +}); + + +jsMath.Setup.Styles(); + +/* + * No access to TeX "not" character, so fake this + */ +jsMath.Macro('not','\\mathrel{\\rlap{\\kern 4mu/}}'); +jsMath.Macro('joinrel','\\mathrel{\\kern-2mu}'); + + +jsMath.Box.DelimExtend = jsMath.Box.DelimExtendRelative; + +jsMath.Box.defaultH = 0.8; diff --git a/htdocs/jsMath/uncompressed/jsMath.js b/htdocs/jsMath/uncompressed/jsMath.js new file mode 100644 index 0000000000..3080cea643 --- /dev/null +++ b/htdocs/jsMath/uncompressed/jsMath.js @@ -0,0 +1,6577 @@ +/***************************************************************************** + * + * jsMath: Mathematics on the Web + * + * This jsMath package makes it possible to display mathematics in HTML pages + * that are viewable by a wide range of browsers on both the Mac and the IBM PC, + * including browsers that don't process MathML. See + * + * http://www.math.union.edu/locate/jsMath + * + * for the latest version, and for documentation on how to use jsMath. + * + * Copyright 2004-2010 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. + * + *****************************************************************************/ + +/* + * Prevent running everything again if this file is loaded twice + */ +if (!window.jsMath || !window.jsMath.loaded) { + +var jsMath_old = window.jsMath; // save user customizations + +// +// debugging routine +// +/* + * function ShowObject (obj,spaces) { + * var s = ''; if (!spaces) {spaces = ""} + * for (var i in obj) { + * if (obj[i] != null) { + * if (typeof(obj[i]) == "object") { + * s += spaces + i + ": {\n" + * + ShowObject(obj[i],spaces + ' ') + * + spaces + "}\n"; + * } else if (typeof(obj[i]) != "function") { + * s += spaces + i + ': ' + obj[i] + "\n"; + * } + * } + * } + * return s; + * } + */ + +/***************************************************************************/ +// +// Check for DOM support +// +if (!document.getElementById || !document.childNodes || !document.createElement) { + alert('The mathematics on this page requires W3C DOM support in its JavaScript. ' + + 'Unfortunately, your browser doesn\'t seem to have this.'); +} else { + +/***************************************************************************/ + +window.jsMath = { + + version: "3.6e", // 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 + + platform: (navigator.platform.match(/Mac/) ? "mac" : + navigator.platform.match(/Win/) ? "pc" : "unix"), + + // Font sizes for \tiny, \small, etc. (must match styles below) + sizes: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249], + + // + // The styles needed for the TeX fonts and other jsMath elements + // + styles: { + '.math': { // unprocessed mathematics + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal' + }, + + '.typeset': { // final typeset mathematics + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal', + 'line-height': 'normal', + 'text-indent': '0px', + 'white-space': 'normal' + }, + + '.typeset .normal': { // \hbox contents style + 'font-family': 'serif', + 'font-style': 'normal', + 'font-weight': 'normal' + }, + + 'div.typeset': { // display mathematics + 'text-align': 'center', + margin: '1em 0px' + }, + + 'span.typeset': { // in-line mathematics + 'text-align': 'left' + }, + + '.typeset span': { // prevent outside CSS from setting these + 'text-align': 'left', + border: '0px', + margin: '0px', + padding: '0px' + }, + + 'a .typeset img, .typeset a img': { // links in image mode + border: '0px', + 'border-bottom': '1px solid blue;' + }, + + // Font sizes + '.typeset .size0': {'font-size': '50%'}, // tiny (\scriptscriptsize) + '.typeset .size1': {'font-size': '60%'}, // (50% of \large for consistency) + '.typeset .size2': {'font-size': '70%'}, // scriptsize + '.typeset .size3': {'font-size': '85%'}, // small (70% of \large for consistency) + '.typeset .size4': {'font-size': '100%'}, // normalsize + '.typeset .size5': {'font-size': '120%'}, // large + '.typeset .size6': {'font-size': '144%'}, // Large + '.typeset .size7': {'font-size': '173%'}, // LARGE + '.typeset .size8': {'font-size': '207%'}, // huge + '.typeset .size9': {'font-size': '249%'}, // Huge + + // TeX fonts + '.typeset .cmr10': {'font-family': 'jsMath-cmr10, serif'}, + '.typeset .cmbx10': {'font-family': 'jsMath-cmbx10, jsMath-cmr10'}, + '.typeset .cmti10': {'font-family': 'jsMath-cmti10, jsMath-cmr10'}, + '.typeset .cmmi10': {'font-family': 'jsMath-cmmi10'}, + '.typeset .cmsy10': {'font-family': 'jsMath-cmsy10'}, + '.typeset .cmex10': {'font-family': 'jsMath-cmex10'}, + + '.typeset .textit': {'font-family': 'serif', 'font-style': 'italic'}, + '.typeset .textbf': {'font-family': 'serif', 'font-weight': 'bold'}, + + '.typeset .link': {'text-decoration': 'none'}, // links in mathematics + + '.typeset .error': { // in-line error messages + 'font-size': '90%', + 'font-style': 'italic', + 'background-color': '#FFFFCC', + padding: '1px', + border: '1px solid #CC0000' + }, + + '.typeset .blank': { // internal use + display: 'inline-block', + overflow: 'hidden', + border: '0px none', + width: '0px', + height: '0px' + }, + '.typeset .spacer': { // internal use + display: 'inline-block' + }, + + '#jsMath_hiddenSpan': { // used for measuring BBoxes + visibility: 'hidden', + position: 'absolute', + top: '0px', + left: '0px', + 'line-height': 'normal', + 'text-indent': '0px' + }, + + '#jsMath_message': { // percentage complete message + position: 'fixed', + bottom: '1px', + left: '2px', + 'background-color': '#E6E6E6', + border: 'solid 1px #959595', + margin: '0px', + padding: '1px 8px', + 'z-index': '102', + color: 'black', + 'font-size': 'small', + width: 'auto' + }, + + '#jsMath_panel': { // control panel + position: 'fixed', + bottom: '1.75em', + right: '1.5em', + padding: '.8em 1.6em', + 'background-color': '#DDDDDD', + border: 'outset 2px', + 'z-index': '103', + width: 'auto', + color: 'black', + 'font-size': '10pt', + 'font-style': 'normal' + }, + '#jsMath_panel .disabled': {color: '#888888'}, // disabled items in the panel + '#jsMath_panel .infoLink': {'font-size': '85%'}, // links to web pages + + // avoid CSS polution from outside the panel + '#jsMath_panel *': { + 'font-size': 'inherit', + 'font-style': 'inherit', + 'font-family': 'inherit', + 'line-height': 'normal' + }, + '#jsMath_panel div': {'background-color': 'inherit', color: 'inherit'}, + '#jsMath_panel span': {'background-color': 'inherit', color: 'inherit'}, + '#jsMath_panel td': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit' + }, + '#jsMath_panel tr': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit' + }, + '#jsMath_panel table': { + border: '0px', padding: '0px', margin: '0px', + 'background-color': 'inherit', color: 'inherit', + height: 'auto', width: 'auto' + }, + + '#jsMath_button': { // the jsMath floating button (to open control panel) + position: 'fixed', + bottom: '1px', + right: '2px', + 'background-color': 'white', + border: 'solid 1px #959595', + margin: '0px', + padding: '0px 3px 1px 3px', + 'z-index': '102', + color: 'black', + 'text-decoration': 'none', + 'font-size': 'x-small', + width: 'auto', + cursor: 'hand' + }, + '#jsMath_button *': { + padding: '0px', border: '0px', margin: '0px', 'line-height': 'normal', + 'font-size': 'inherit', 'font-style': 'inherit', 'font-family': 'inherit' + }, + + '#jsMath_global': {'font-style': 'italic'}, // 'global' in jsMath button + + '#jsMath_noFont .message': { // missing font message window + 'text-align': 'center', + padding: '.8em 1.6em', + border: '3px solid #DD0000', + 'background-color': '#FFF8F8', + color: '#AA0000', + 'font-size': 'small', + width: 'auto' + }, + '#jsMath_noFont .link': { + padding: '0px 5px 2px 5px', + border: '2px outset', + 'background-color': '#E8E8E8', + color: 'black', + 'font-size': '80%', + width: 'auto', + cursor: 'hand' + }, + + '#jsMath_PrintWarning .message': { // warning on print pages + 'text-align': 'center', + padding: '.8em 1.6em', + border: '3px solid #DD0000', + 'background-color': '#FFF8F8', + color: '#AA0000', + 'font-size': 'x-small', + width: 'auto' + }, + + '@media print': { + '#jsMath_button': {display: 'none'}, + '#jsMath_Warning': {display: 'none'} + }, + + '@media screen': { + '#jsMath_PrintWarning': {display:'none'} + } + + }, + + + /***************************************************************************/ + + /* + * Get a jsMath DOM element + */ + Element: function (name) {return jsMath.document.getElementById('jsMath_'+name)}, + + /* + * Get the width and height (in pixels) of an HTML string + */ + BBoxFor: function (s) { + this.hidden.innerHTML = + ''+s+''; + var math = (jsMath.Browser.msieBBoxBug ? this.hidden.firstChild.firstChild : this.hidden); + var bbox = {w: math.offsetWidth, h: this.hidden.offsetHeight}; + this.hidden.innerHTML = ''; + return bbox; + }, + + /* + * Get the width and height (in ems) of an HTML string. + * Check the cache first to see if we've already measured it. + */ + EmBoxFor: function (s) { + var cache = jsMath.Global.cache.R; + if (!cache[this.em]) {cache[this.em] = {}} + if (!cache[this.em][s]) { + var bbox = this.BBoxFor(s); + cache[this.em][s] = {w: bbox.w/this.em, h: bbox.h/this.em}; + } + return cache[this.em][s]; + }, + + /* + * Initialize jsMath. This determines the em size, and a variety + * of other parameters used throughout jsMath. + */ + Init: function () { + if (jsMath.Setup.inited != 1) { + if (!jsMath.Setup.inited) {jsMath.Setup.Body()} + if (jsMath.Setup.inited != 1) { + if (jsMath.Setup.inited == -100) return; + alert("It looks like jsMath failed to set up properly (error code " + + jsMath.Setup.inited + "). " + + "I will try to keep going, but it could get ugly."); + jsMath.Setup.inited = 1; + } + } + this.em = this.CurrentEm(); + var cache = jsMath.Global.cache.B; + if (!cache[this.em]) { + cache[this.em] = {}; + cache[this.em].bb = this.BBoxFor('x'); var hh = cache[this.em].bb.h; + cache[this.em].d = this.BBoxFor('x'+jsMath.HTML.Strut(hh/this.em)).h - hh; + } + jsMath.Browser.italicCorrection = cache[this.em].ic; + 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.Setup.TeXfonts(); + + var x_height = this.EmBoxFor('M').w/2; + this.TeX.M_height = x_height*(26/14); + this.TeX.h = this.h; this.TeX.d = this.d; this.TeX.hd = this.hd; + + this.Img.Scale(); + if (!this.initialized) { + this.Setup.Sizes(); + this.Img.UpdateFonts(); + } + + // factor for \big and its brethren + this.p_height = (this.TeX.cmex10[0].h + this.TeX.cmex10[0].d) / .85; + + this.initialized = 1; + }, + + /* + * Get the x size and if it has changed, reinitialize the sizes + */ + ReInit: function () { + if (this.em != this.CurrentEm()) {this.Init()} + }, + + /* + * Find the em size in effect at the current text location + */ + CurrentEm: function () { + var em = this.BBoxFor('').w/27; + if (em > 0) {return em} + // handle older browsers + return this.BBoxFor('').w/13; + }, + + /* + * Mark jsMath as loaded and copy any user-provided overrides + */ + Loaded: function () { + if (jsMath_old) { + var override = ['Process', 'ProcessBeforeShowing','ProcessElement', + 'ConvertTeX','ConvertTeX2','ConvertLaTeX','ConvertCustom', + 'CustomSearch', 'Synchronize', 'Macro', 'document']; + for (var i = 0; i < override.length; i++) { + if (jsMath_old[override[i]]) {delete jsMath_old[override[i]]} + } + } + if (jsMath_old) {this.Insert(jsMath,jsMath_old)} + jsMath_old = null; + jsMath.loaded = 1; + }, + + /* + * Manage JavaScript objects: + * + * Add: add/replace items in an object + * Insert: add items to an object + * Package: add items to an object prototype + */ + Add: function (dst,src) {for (var id in src) {dst[id] = src[id]}}, + Insert: function (dst,src) { + for (var id in src) { + if (dst[id] && typeof(src[id]) == 'object' + && (typeof(dst[id]) == 'object' + || typeof(dst[id]) == 'function')) { + this.Insert(dst[id],src[id]); + } else { + dst[id] = src[id]; + } + } + }, + Package: function (obj,def) {this.Insert(obj.prototype,def)} + +}; + + +/***************************************************************************/ + + /* + * Implements items associated with the global cache. + * + * This object will be replaced by a global version when + * (and if) jsMath-global.html is loaded. + */ +jsMath.Global = { + isLocal: 1, // a local copy if jsMath-global.html hasn't been loaded + cache: {T: {}, D: {}, R: {}, B: {}}, + + /* + * Clear the global (or local) cache + */ + ClearCache: function () {jsMath.Global.cache = {T: {}, D: {}, R: {}, B: {}}}, + + /* + * Initiate global mode + */ + GoGlobal: function (cookie) { + var url = String(jsMath.window.location); + var c = (jsMath.isCHMmode ? '#' : '?'); + if (cookie) {url = url.replace(/\?.*/,'') + '?' + cookie} + jsMath.Controls.Reload(jsMath.root + "jsMath-global.html" + c +escape(url)); + }, + + /* + * Check if we need to go to global mode + */ + Init: function () { + if (jsMath.Controls.cookie.global == "always" && !jsMath.noGoGlobal) { + if (navigator.accentColorName) return; // OmniWeb crashes on GoGlobal + if (!jsMath.window) {jsMath.window = window} + jsMath.Controls.loaded = 1; + jsMath.Controls.defaults.hiddenGlobal = null; + this.GoGlobal(jsMath.Controls.SetCookie(2)); + } + }, + + /* + * Try to register with a global.html window that contains us + */ + Register: function () { + var parent = jsMath.window.parent; + if (!jsMath.isCHMmode) + {jsMath.isCHMmode = (jsMath.window.location.protocol == 'mk:')} + try { + if (!jsMath.isCHMmode) this.Domain(); + if (parent.jsMath && parent.jsMath.isGlobal) + {parent.jsMath.Register(jsMath.window)} + } catch (err) {jsMath.noGoGlobal = 1} + }, + + /* + * If we're not the parent window, try to set the domain to + * match the parent's domain (so we can use the Global data + * if the surrounding frame is a Global frame). + */ + Domain: function () { + // MSIE/Mac can't do domain changes, so don't bother trying + if (navigator.appName == 'Microsoft Internet Explorer' && + jsMath.platform == 'mac' && navigator.userProfile != null) return; + // MSIE/PC can do domain change, but gets mixed up if we don't + // find a domain that works, and then can't look in window.location + // any longer. So don't try, since we can't afford to leave it confused. + if (jsMath.document.all && !jsMath.window.opera) return; + + if (window == parent) return; + var oldDomain = jsMath.document.domain; + try { + while (true) { + try {if (parent.document.title != null) return} catch (err) {} + if (!document.domain.match(/\..*\./)) break; + jsMath.document.domain = jsMath.document.domain.replace(/^[^.]*\./,''); + } + } catch (err) {} + jsMath.document.domain = oldDomain; + } + +}; + + + +/***************************************************************************/ + +/* + * + * Implement loading of remote scripts using XMLHttpRequest, if + * possible, otherwise use a hidden IFRAME and fake it. That + * method runs asynchronously, which causes lots of headaches. + * Solve these using Push command, which queues actions + * until files have loaded. + */ + +jsMath.Script = { + + request: null, // the XMLHttpRequest object + + /* + * Create the XMLHttpRequest object, if we can. + * Otherwise, use the iframe-based fallback method. + */ + Init: function () { + if (!(jsMath.Controls.cookie.asynch && jsMath.Controls.cookie.progress)) { + 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 && window.addEventListener) { + this.mustPost = 1; + jsMath.window.addEventListener("message",jsMath.Post.Listener,false); + } + } + } + } + if (!this.request && window.ActiveXObject && !this.mustPost) { + var xml = ["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0", + "MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"]; + for (var i = 0; i < xml.length && !this.request; i++) { + try {this.request = new ActiveXObject(xml[i])} catch (err) {} + } + } + } + // + // Use the delayed-script fallback for MSIE/Mac and old versions + // of several browsers (Opera 7.5, OmniWeb 4.5). + // + if (!this.request || jsMath.Setup.domainChanged) + {this.Load = this.delayedLoad; this.needsBody = 1} + }, + + /* + * Load a script and evaluate it in the window's context + */ + Load: function (url,show) { + if (show) { + jsMath.Message.Set("Loading "+url); + jsMath.Script.Delay(1); + jsMath.Script.Push(this,'xmlRequest',url); + jsMath.Script.Push(jsMath.Message,'Clear'); + } else { + jsMath.Script.Push(this,'xmlRequest',url); + } + }, + + /* + * Load a URL and run the contents of the file + */ + xmlRequest: function (url) { + this.blocking = 1; +// this.debug('xmlRequest: '+url); + try { + this.request.open("GET",url,false); + this.request.send(null); + } catch (err) { + this.blocking = 0; + if (jsMath.Translate.restart && jsMath.Translate.asynchronous) {return ""} + throw Error("jsMath can't load the file '"+url+"'\n" + + "Message: "+err.message); + } + 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 ""} + throw Error("jsMath can't load the file '"+url+"'\n" + + "Error status: "+this.request.status); + } + if (!url.match(/\.js$/)) {return(this.request.responseText)} + var tmpQueue = this.queue; this.queue = []; +// this.debug('xml Eval ['+tmpQueue.length+']'); + jsMath.window.eval(this.request.responseText); +// this.debug('xml Done ['+this.queue.length+' + '+tmpQueue.length+']'); + this.blocking = 0; this.queue = this.queue.concat(tmpQueue); + this.Process(); + return ""; + }, + + /******************************************************************** + * + * Implement asynchronous loading and execution of scripts + * (via hidden IFRAME) interleved with other JavaScript commands + * that must be synchronized with the file loading. (Basically, this + * is for MSIE/Mac and Opera 7.5, which don't have XMLHttpRequest.) + */ + + cancelTimeout: 30*1000, // delay for canceling load (30 sec) + + 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 + + queue: [], // the stack of pending actions + + /* + * Provide mechanism for synchronizing with the asynchronous jsMath + * file-loading mechanism. 'code' can be a string or a function. + */ + Synchronize: function (code,data) { + if (typeof(code) != 'string') {jsMath.Script.Push(null,code,data)} + else {jsMath.Script.Push(jsMath.window,'eval',code)} + }, + + /* + * Queue a function to be processed. + * If nothing is being loaded, do the pending commands. + */ + Push: function (object,method,data) { +// 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(); + }, + + /* + * Do any pending functions (stopping if a file load is started) + */ + Process: function () { + while (this.queue.length && !this.blocking) { + 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+' ['+savedQueue.length+']'); // debug + if (object) {object[method](data)} else if (method) {method(data)} +// 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) { +// this.debug('Loading: '+url); + this.Push(this,'startLoad',url); + }, + startLoad: function (url) { + 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(iframe,jsMath.document.body.firstChild); + } else { + jsMath.document.body.appendChild(iframe); + } + this.blocking = 1; this.url = url; + 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} + jsMath.Post.endLoad(); + jsMath.Message.Clear(); + if (action != 'cancel') {this.blocking = 0; this.Process()} + }, + + Start: function () { +// this.debug('Starting: ['+this.queue.length+'] '+this.url); + this.tmpQueue = this.queue; this.queue = []; + }, + End: function () { +// this.debug('Ending: ['+this.queue.length+' + '+this.tmpQueue.length+'] '+this.url); + this.queue = this.queue.concat(this.tmpQueue); delete this.tmpQueue; + }, + + /* + * If the loading takes too long, cancel it and end the load. + */ + 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); + }, + + /* + * Perform a delay (to let the browser catch up) + */ + Delay: function (time) { + this.blocking = 1; + setTimeout('jsMath.Script.endDelay()',time); + }, + endDelay: function () { +// this.debug('endDelay'); + this.blocking = 0; + this.Process(); + }, + + /* + * Load an image and wait for it + * (so MSIE won't load extra copies of it) + */ + imageCount: 0, + WaitForImage: function (file) { + this.blocking = 1; this.imageCount++; + if (this.img == null) {this.img = []} + var img = new Image(); this.img[this.img.length] = img; + img.onload = function () {if (--jsMath.Script.imageCount == 0) jsMath.Script.endDelay()} + img.onerror = img.onload; img.onabort = img.onload; + img.src = file; + }, + + /* + * The code uncompressor + */ + Uncompress: function (data) { + for (var k = 0; k < data.length; k++) { + var d = data[k]; var n = d.length; + for (var i = 0; i < n; i++) {if (typeof(d[i]) == 'number') {d[i] = d[d[i]]}} + 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)} + * } + */ + +}; + +/***************************************************************************/ + +/* + * 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.replace(/^file:\/\//,''); + var ddomain = document.domain.replace(/^file:\/\//,''); + if (domain == null || domain == "" || domain == "null") {domain = "localhost"} + if (ddomain == null || ddomain == "" || ddomain == "null") {ddomain = "localhost"} + if (domain != ddomain || !event.data.substr(0,6).match(/jsM(CP|LD|AL):/)) 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) {if (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} +}; + +/***************************************************************************/ + +/* + * Message and screen blanking facility + */ + +jsMath.Message = { + + blank: null, // the div to blank out the screen + message: null, // the div for the messages + text: null, // the text node for messages + clear: null, // timer for clearing message + + /* + * Create the elements needed for the message box + */ + Init: function () { + if (!jsMath.document.body || !jsMath.Controls.cookie.progress) return; + this.message = jsMath.Element('message'); + if (!this.message) { + if (jsMath.Setup.stylesReady) { + this.message = jsMath.Setup.DIV('message',{visibility:'hidden'},jsMath.fixedDiv); + } else { + this.message = jsMath.Setup.DIV('message',{ + visibility:'hidden', position:'absolute', bottom:'1px', left:'2px', + backgroundColor:'#E6E6E6', border:'solid 1px #959595', + margin:'0px', padding:'1px 8px', zIndex:102, + color:'black', fontSize:'small', width:'auto' + },jsMath.fixedDiv); + } + } + this.text = jsMath.document.createTextNode(''); + this.message.appendChild(this.text); + this.message.onmousedown = jsMath.Translate.Cancel; + }, + + /* + * Set the contents of the message box, or use the window status line + */ + Set: function (text,canCancel) { + if (this.clear) {clearTimeout(this.clear); this.clear = null} + if (jsMath.Controls.cookie.progress) { + if (!this.text) {this.Init(); if (!this.text) return} + if (jsMath.Browser.textNodeBug) {this.message.innerHTML = text} + else {this.text.nodeValue = text} + this.message.style.visibility = 'visible'; + if (canCancel) { + this.message.style.cursor = 'pointer'; + if (!this.message.style.cursor) {this.message.style.cursor = 'hand'} + this.message.title = ' Cancel Processing of Math '; + } else { + this.message.style.cursor = ''; + this.message.title = ''; + } + } else { + if (text.substr(0,8) != "Loading ") {jsMath.window.status = text} + } + }, + + /* + * Clear the message box or status line + */ + Clear: function () { + if (this.clear) {clearTimeout(this.clear)} + this.clear = setTimeout("jsMath.Message.doClear()",1000); + }, + doClear: function () { + if (this.clear) { + this.clear = null; + jsMath.window.status = ''; + if (this.text) {this.text.nodeValue = ''} + if (this.message) {this.message.style.visibility = 'hidden'} + } + }, + + + /* + * Put up a DIV that covers the window so that the + * "flicker" of processing the mathematics will not be visible + */ + Blank: function () { + if (this.blank || !jsMath.document.body) return; + this.blank = jsMath.Setup.DIV("blank",{ + position:(jsMath.Browser.msiePositionFixedBug? 'absolute': 'fixed'), + top:'0px', left:'0px', bottom:'0px', right:'0px', + zIndex:101, backgroundColor:'white' + },jsMath.fixedDiv); + if (jsMath.Browser.msieBlankBug) { + this.blank.innerHTML = ' '; + this.blank.style.width = "110%"; + this.blank.style.height = "110%"; + } + }, + + UnBlank: function () { + if (this.blank) {jsMath.document.body.removeChild(this.blank)} + this.blank = null; + } +}; + + +/***************************************************************************/ + +/* + * Miscellaneous setup and initialization + */ +jsMath.Setup = { + + loaded: [], // array of files already loaded + + /* + * Insert a DIV at the top of the page with given ID, + * attributes, and style settings + */ + DIV: function (id,styles,parent) { + if (parent == null) {parent = jsMath.document.body} + var div = jsMath.document.createElement('div'); + div.id = 'jsMath_'+id; + for (var i in styles) {div.style[i]= styles[i]} + if (!parent.hasChildNodes) {parent.appendChild(div)} + else {parent.insertBefore(div,parent.firstChild)} + return div; + }, + + /* + * Source a jsMath JavaScript file (only load any given file once) + */ + Script: function (file,show) { + if (this.loaded[file]) {return} else {this.loaded[file] = 1} + if (!file.match('^([a-zA-Z]+:/?)?/')) {file = jsMath.root + file} + jsMath.Script.Load(file,show); + }, + + /* + * Use a hidden
      for measuring the BBoxes of things + */ + Hidden: function () { + jsMath.hidden = this.DIV("Hidden",{ + visibility: 'hidden', position:"absolute", + top:0, left:0, border:0, padding:0, margin:0 + }); + jsMath.hiddenTop = jsMath.hidden; + return; + }, + + /* + * Find the root URL for the jsMath files (so we can load + * the other .js and .gif files) + */ + Source: function () { + if (jsMath.Autoload && jsMath.Autoload.root) { + jsMath.root = jsMath.Autoload.root; + } else { + jsMath.root = ''; + var script = jsMath.document.getElementsByTagName('script'); + if (script) { + for (var i = 0; i < script.length; i++) { + var src = script[i].src; + if (src && src.match('(^|/|\\\\)jsMath.js$')) { + jsMath.root = src.replace(/jsMath.js$/,''); + break; + } + } + } + } + if (jsMath.root.charAt(0) == '\\') {jsMath.root = jsMath.root.replace(/\\/g,'/')} + if (jsMath.root.charAt(0) == '/') { + if (jsMath.root.charAt(1) != '/') + {jsMath.root = '//' + jsMath.document.location.host + jsMath.root} + jsMath.root = jsMath.document.location.protocol + jsMath.root; + } else if (!jsMath.root.match(/^[a-z]+:/i)) { + var src = new String(jsMath.document.location); + var pattern = new RegExp('/[^/]*/\\.\\./') + jsMath.root = src.replace(new RegExp('[^/]*$'),'') + jsMath.root; + while (jsMath.root.match(pattern)) + {jsMath.root = jsMath.root.replace(pattern,'/')} + } + jsMath.Img.root = jsMath.root + "fonts/"; + jsMath.blank = jsMath.root + "blank.gif"; + this.Domain(); + }, + + /* + * Find the most restricted common domain for the main + * page and jsMath. Report an error if jsMath is outside + * the domain of the calling page. + */ + Domain: function () { + try {jsMath.document.domain} catch (err) {return} + var jsDomain = ''; var pageDomain = jsMath.document.domain; + if (jsMath.root.match('://([^/]*)/')) {jsDomain = RegExp.$1} + jsDomain = jsDomain.replace(/:\d+$/,''); + if (jsDomain == "" || jsDomain == pageDomain) return; + // + // MSIE on the Mac can't change jsMath.document.domain and 'try' won't + // catch the error (Grrr!), so exit for them. + // + if (navigator.appName == 'Microsoft Internet Explorer' && + jsMath.platform == 'mac' && navigator.onLine && + navigator.userProfile && jsMath.document.all) return; + jsDomain = jsDomain.split(/\./); pageDomain = pageDomain.split(/\./); + if (jsDomain.length < 2 || pageDomain.length < 2 || + jsDomain[jsDomain.length-1] != pageDomain[pageDomain.length-1] || + jsDomain[jsDomain.length-2] != pageDomain[pageDomain.length-2]) { + this.DomainWarning(); + return; + } + var domain = jsDomain[jsDomain.length-2] + '.' + jsDomain[jsDomain.length-1]; + for (var i = 3; i <= jsDomain.length && i <= pageDomain.length; i++) { + if (jsDomain[jsDomain.length-i] != pageDomain[pageDomain.length-i]) break; + domain = jsDomain[jsDomain.length-i] + '.' + domain; + } + jsMath.document.domain = domain; + this.domainChanged = 1; + }, + + DomainWarning: function () { + alert("In order for jsMath to be able to load the additional " + + "components that it may need, the jsMath.js file must be " + + "loaded from a server in the same domain as the page that " + + "contains it. Because that is not the case for this page, " + + "the mathematics displayed here may not appear correctly."); + }, + + /* + * Initialize a font's encoding array + */ + EncodeFont: function (name) { + var font = jsMath.TeX[name]; + if (font[0].c != null) return; + for (var k = 0; k < 128; k++) { + var data = font[k]; font[k] = data[3]; + if (font[k] == null) {font[k] = {}}; + font[k].w = data[0]; font[k].h = data[1]; + if (data[2] != null) {font[k].d = data[2]} + font[k].c = jsMath.TeX.encoding[k]; + } + }, + + /* + * Initialize the encodings for all fonts + */ + Fonts: function () { + for (var i = 0; i < jsMath.TeX.fam.length; i++) { + var name = jsMath.TeX.fam[i]; + if (name) {this.EncodeFont(name)} + } + }, + + /* + * Look up the default height and depth for a TeX font + * and set the skewchar + */ + TeXfont: function (name) { + var font = jsMath.TeX[name]; if (font == null) return; + font.hd = jsMath.EmBoxFor(''+font[65].c+'').h; + font.d = jsMath.EmBoxFor(''+font[65].c+jsMath.HTML.Strut(font.hd)+'').h - font.hd; + font.h = font.hd - font.d; + if (name == 'cmmi10') {font.skewchar = 0177} + else if (name == 'cmsy10') {font.skewchar = 060} + }, + + /* + * Init all the TeX fonts + */ + TeXfonts: function () { + for (var i = 0; i < jsMath.TeX.fam.length; i++) + {if (jsMath.TeX.fam[i]) {this.TeXfont(jsMath.TeX.fam[i])}} + }, + + /* + * Compute font parameters for various sizes + */ + Sizes: function () { + 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 (j=0; j < jsMath.sizes.length; j++) { + jsMath.TeXparams[j][i] = jsMath.sizes[j]*jsMath.TeX[i]/100; + } + } + } + }, + + /* + * Send the style definitions to the browser (these may be adjusted + * by the browser-specific code) + */ + Styles: function (styles) { + if (!styles) { + styles = jsMath.styles; + styles['.typeset .scale'] = {'font-size': jsMath.Controls.cookie.scale+'%'}; + this.stylesReady = 1; + } + jsMath.Script.Push(this,'AddStyleSheet',styles); + if (jsMath.Browser.styleChangeDelay) {jsMath.Script.Push(jsMath.Script,'Delay',1)} + }, + + /* + * Make a style string from a hash of style definitions, which are + * either strings themselves or hashes of style settings. + */ + StyleString: function (styles) { + var styleString = {}, id; + for (id in styles) { + if (typeof styles[id] === 'string') { + styleString[id] = styles[id]; + } else if (id.substr(0,1) === '@') { + styleString[id] = this.StyleString(styles[id]); + } else if (styles[id] != null) { + var style = []; + for (var name in styles[id]) { + if (styles[id][name] != null) + {style[style.length] = name + ': ' + styles[id][name]} + } + styleString[id] = style.join('; '); + } + } + var string = ''; + for (id in styleString) {string += id + " {"+styleString[id]+"}\n"} + return string; + }, + + AddStyleSheet: function (styles) { + var head = jsMath.document.getElementsByTagName('head')[0]; + if (head) { + var string = this.StyleString(styles); + if (jsMath.document.createStyleSheet) {// check for MSIE + head.insertAdjacentHTML('beforeEnd', + 'x' // MSIE needs this for some reason + + ''); + } else { + var style = jsMath.document.createElement('style'); style.type = "text/css"; + style.appendChild(jsMath.document.createTextNode(string)); + head.appendChild(style); + } + } else if (!jsMath.noHEAD) { + jsMath.noHEAD = 1; + alert("Document is missing its section. Style sheet can't be created without it."); + } + }, + + /* + * Do the initialization that requires the to be in place. + */ + Body: function () { + if (this.inited) return; + + this.inited = -1; + + jsMath.Setup.Hidden(); this.inited = -2; + jsMath.Browser.Init(); this.inited = -3; + + // blank screen if necessary + if (jsMath.Controls.cookie.blank) {jsMath.Message.Blank()}; this.inited = -4; + + jsMath.Setup.Styles(); this.inited = -5; + jsMath.Controls.Init(); this.inited = -6; + + // do user-specific initialization + jsMath.Script.Push(jsMath.Setup,'User','pre-font'); this.inited = -7; + + // make sure browser-specific loads are done before this + jsMath.Script.Push(jsMath.Font,'Check'); + if (jsMath.Font.register.length) + {jsMath.Script.Push(jsMath.Font,'LoadRegistered')} + + this.inited = 1; + }, + + /* + * Web page author can override the entries to the UserEvent hash + * functions that will be run at various times during jsMath's setup + * process. + */ + User: function (when) { + if (jsMath.Setup.UserEvent[when]) {(jsMath.Setup.UserEvent[when])()} + }, + + UserEvent: { + "pre-font": null, // after browser is set up but before fonts are tested + "onload": null // after jsMath.js is loaded and finished running + } + +}; + +jsMath.Update = { + + /* + * Update specific parameters for a limited number of font entries + */ + TeXfonts: function (change) { + for (var font in change) { + for (var code in change[font]) { + for (var id in change[font][code]) { + jsMath.TeX[font][code][id] = change[font][code][id]; + } + } + } + }, + + /* + * Update the character code for every character in a list + * of fonts + */ + TeXfontCodes: function (change) { + for (var font in change) { + for (var i = 0; i < change[font].length; i++) { + jsMath.TeX[font][i].c = change[font][i]; + } + } + } + +}; + +/***************************************************************************/ + +/* + * Implement browser-specific checks + */ + +jsMath.Browser = { + + allowAbsolute: 1, // tells if browser can nest absolutely positioned + // SPANs inside relative SPANs + allowAbsoluteDelim: 0, // OK to use absolute placement for building delims? + separateSkips: 0, // MSIE doesn't do negative left margins, and + // Netscape doesn't combine skips well + + valignBug: 0, // Konqueror doesn't nest vertical-align + operaHiddenFix: '', // for Opera to fix bug with math in tables + msieCenterBugFix: '', // for MSIE centering bug with image fonts + msieInlineBlockFix: '', // for MSIE alignment bug in non-quirks mode + msieSpaceFix: '', // for MSIE to avoid dropping empty spans + imgScale: 1, // MSI scales images for 120dpi screens, so compensate + + renameOK: 1, // tells if brower will find a tag whose name + // has been set via setAttributes + styleChangeDelay: 0, // true if style changes need a delay in order + // for them to be available + + delay: 1, // delay for asynchronous math processing + processAtOnce: 16, // number of math elements to process before screen update + + version: 0, // browser version number (when needed) + + /* + * Determine if the "top" of a is always at the same height + * or varies with the height of the rest of the line (MSIE). + */ + TestSpanHeight: function () { + jsMath.hidden.innerHTML = 'x'; + var span = jsMath.hidden.firstChild; + var img = span.firstChild; + this.spanHeightVaries = (span.offsetHeight >= img.offsetHeight && span.offsetHeight > 0); + this.spanHeightTooBig = (span.offsetHeight > img.offsetHeight); + jsMath.hidden.innerHTML = ''; + }, + + /* + * Determine if an inline-block with 0 width is OK or not + * and decide whether to use spans or images for spacing + */ + TestInlineBlock: function () { + this.block = "display:-moz-inline-box"; + this.hasInlineBlock = jsMath.BBoxFor('').w > 0; + if (this.hasInlineBlock) { + jsMath.styles['.typeset .blank'].display = '-moz-inline-box'; + delete jsMath.styles['.typeset .spacer'].display; + } else { + this.block = "display:inline-block"; + this.hasInlineBlock = jsMath.BBoxFor('').w > 0; + if (!this.hasInlineBlock) return; + } + this.block += ';overflow:hidden'; + var h = jsMath.BBoxFor('x').h; + this.mozInlineBlockBug = jsMath.BBoxFor( + 'x'+ + '').h > 2*h; + this.widthAddsBorder = jsMath.BBoxFor('').w > 10; + var h1 = jsMath.BBoxFor('x').h, + h2 = jsMath.BBoxFor('x').h, + h3 = jsMath.BBoxFor('').h; + this.msieBlockDepthBug = (h1 == h); + this.msieRuleDepthBug = (h2 == h); + this.blankWidthBug = (h3 == 0); + }, + + /* + * Determine if the NAME attribute of a tag can be changed + * using the setAttribute function, and then be properly + * returned by getElementByName. + */ + TestRenameOK: function () { + jsMath.hidden.innerHTML = ''; + var test = jsMath.hidden.firstChild; + test.setAttribute('name','jsMath_test'); + this.renameOK = (jsMath.document.getElementsByName('jsMath_test').length > 0); + jsMath.hidden.innerHTML = ''; + }, + + /* + * See if style changes occur immediately, or if we need to delay + * in order to let them take effect. + */ + TestStyleChange: function () { + jsMath.hidden.innerHTML = 'x'; + var span = jsMath.hidden.firstChild; + var w = span.offsetWidth; + jsMath.Setup.AddStyleSheet({'#jsMath_test': 'font-size:200%'}); + this.styleChangeDelay = (span.offsetWidth == w); + jsMath.hidden.innerHTML = ''; + }, + + /* + * Perform a version check on a standard version string + */ + VersionAtLeast: function (v) { + var bv = new String(this.version).split('.'); + v = new String(v).split('.'); if (v[1] == null) {v[1] = '0'} + return bv[0] > v[0] || (bv[0] == v[0] && bv[1] >= v[1]); + }, + + /* + * Test for browser characteristics, and adjust things + * to overcome specific browser bugs + */ + Init: function () { + jsMath.browser = 'unknown'; + this.TestInlineBlock(); + this.TestSpanHeight(); + this.TestRenameOK(); + this.TestStyleChange(); + + this.MSIE(); + this.Mozilla(); + this.Opera(); + this.OmniWeb(); + this.Safari(); + this.Konqueror(); + + // + // Change some routines depending on the browser + // + if (this.allowAbsoluteDelim) { + jsMath.Box.DelimExtend = jsMath.Box.DelimExtendAbsolute; + jsMath.Box.Layout = jsMath.Box.LayoutAbsolute; + } else { + jsMath.Box.DelimExtend = jsMath.Box.DelimExtendRelative; + jsMath.Box.Layout = jsMath.Box.LayoutRelative; + } + + if (this.separateSkips) { + jsMath.HTML.Place = jsMath.HTML.PlaceSeparateSkips; + jsMath.Typeset.prototype.Place = jsMath.Typeset.prototype.PlaceSeparateSkips; + } + }, + + // + // Handle bug-filled Internet Explorer + // + MSIE: function () { + if (jsMath.BBoxFor("").w) { + jsMath.browser = 'MSIE'; + if (jsMath.platform == 'pc') { + this.IE7 = (window.XMLHttpRequest != null); + this.IE8 = (jsMath.BBoxFor("").w > 0); + this.isReallyIE8 = (jsMath.document.documentMode != null); + this.quirks = (jsMath.document.compatMode == "BackCompat"); + this.msieMode = (jsMath.document.documentMode || (this.quirks ? 5 : 7)); + 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;'; + this.msie8HeightBug = this.msieBBoxBug = (this.msieMode == 8); + this.blankWidthBug = (this.msieMode != 8); + this.msieSpaceFix = (this.isReallyIE8 ? + '' : + ''); + 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 + jsMath.styles['#jsMath_message'].position = 'absolute'; + delete jsMath.styles['#jsMath_message'].width; + jsMath.styles['#jsMath_panel'].position = 'absolute'; + delete jsMath.styles['#jsMath_panel'].width; + jsMath.styles['#jsMath_button'].width = '1px'; + jsMath.styles['#jsMath_button'].position = 'absolute' + delete jsMath.styles['#jsMath_button'].width; + jsMath.fixedDiv = jsMath.Setup.DIV("fixedDiv",{position:'absolute', zIndex: 101}); + jsMath.window.attachEvent("onscroll",jsMath.Controls.MoveButton); + jsMath.window.attachEvent("onresize",jsMath.Controls.MoveButton); + jsMath.Controls.MoveButton(); + } + // Make MSIE put borders around the whole button + jsMath.styles['#jsMath_noFont .link'].display = "inline-block"; + // MSIE needs this NOT to be inline-block + delete jsMath.styles['.typeset .spacer'].display; + // MSIE can't insert DIV's into text nodes, so tex2math must use SPAN's to fake DIV's + jsMath.styles['.tex2math_div'] = {}; jsMath.Add(jsMath.styles['.tex2math_div'],jsMath.styles['div.typeset']); + jsMath.styles['.tex2math_div'].width = '100%'; + jsMath.styles['.tex2math_div'].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) { + this.imgScale *= screen.logicalXDPI/screen.deviceXDPI; + jsMath.Controls.cookie.alpha = 0; + } + // IE8 doesn't puts ALL boxes at the bottom rather than on the baseline + if (this.msieRuleDepthBug) {jsMath.HTML.Strut = jsMath.HTML.msieStrut} + } else if (jsMath.platform == 'mac') { + this.msieAbsoluteBug = 1; this.msieButtonBug = 1; + this.msieDivWidthBug = 1; this.msieBlankBug = 1; + this.quirks = 1; + jsMath.Setup.Script('jsMath-msie-mac.js'); + jsMath.Parser.prototype.macros.angle = ['Replace','ord','','normal']; + jsMath.styles['#jsMath_panel'].width = '42em'; + jsMath.Controls.cookie.printwarn = 0; // MSIE/Mac doesn't handle '@media screen' + } + this.processAtOnce = Math.max(Math.floor((this.processAtOnce+1)/2),1); + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}'); + jsMath.Macro('angle','\\raise1.84pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\kern.5pt\\rule{.4em}{-1.5pt}{1.84pt}\\kern2.5mu}'); + } + }, + + // + // Handle Netscape/Mozilla (any flavor) + // + Mozilla: function () { + if (jsMath.hidden.ATTRIBUTE_NODE && jsMath.window.directories) { + jsMath.browser = 'Mozilla'; + if (jsMath.platform == 'pc') {this.alphaPrintBug = 1} + this.allowAbsoluteDelim = 1; + jsMath.styles['#jsMath_button'].cursor = jsMath.styles['#jsMath_noFont .link'].cursor = 'pointer', + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3mu/}}'); + jsMath.Macro('angle','\\raise1.34pt{\\kern2.5mu\\rlap{\\scriptstyle/}\\rule{.4em}{-1pt}{1.34pt}\\kern2.5mu}'); + if (navigator.vendor == 'Firefox') { + this.version = navigator.vendorSub; + } else if (navigator.userAgent.match(' Firefox/([0-9.]+)([a-z ]|$)')) { + this.version = RegExp.$1; + } + if (this.VersionAtLeast("3.0")) {this.mozImageSizeBug = this.lineBreakBug = 1} + } + }, + + // + // Handle OmniWeb + // + OmniWeb: function () { + if (navigator.accentColorName) { + jsMath.browser = 'OmniWeb'; + this.allowAbsolute = this.hasInlineBlock; + this.allowAbsoluteDelim = this.allowAbsolute; + this.valignBug = !this.allowAbsolute; + this.buttonCheck = 1; this.textNodeBug = 1; + jsMath.noChangeGlobal = 1; // OmniWeb craches on GoGlobal + if (!this.hasInlineBlock) {jsMath.Setup.Script('jsMath-old-browsers.js')} + } + }, + + // + // Handle Opera + // + Opera: function () { + if (this.spanHeightTooBig) { + jsMath.browser = 'Opera'; + var isOld = navigator.userAgent.match("Opera 7"); + this.allowAbsolute = 0; + this.delay = 10; + this.operaHiddenFix = '[Processing]'; + if (isOld) {jsMath.Setup.Script('jsMath-old-browsers.js')} + var version = navigator.appVersion.match(/^(\d+\.\d+)/); + if (version) {this.version = version[1]} else {this.vesion = 0} + this.operaAbsoluteWidthBug = this.operaLineHeightBug = (version[1] >= 9.5 && version[1] < 9.6); + } + }, + + // + // Handle Safari + // + Safari: function () { + if (navigator.appVersion.match(/Safari\//)) { + jsMath.browser = 'Safari'; + if (navigator.vendor.match(/Google/)) {jsMath.browser = 'Chrome'} + var version = navigator.userAgent.match("Safari/([0-9]+)"); + version = (version)? version[1] : 400; this.version = version; + jsMath.TeX.axis_height += .05; + this.allowAbsoluteDelim = version >= 125; + this.safariIFRAMEbug = version >= 312 && version < 412; + this.safariButtonBug = version < 412; + this.safariImgBug = 1; this.textNodeBug = 1; + this.lineBreakBug = version >= 526; + this.buttonCheck = version < 500; + this.styleChangeDelay = 1; + jsMath.Macro('not','\\mathrel{\\rlap{\\kern3.25mu/}}'); + } + }, + + // + // Handle Konqueror + // + Konqueror: function () { + if (navigator.product && navigator.product.match("Konqueror")) { + jsMath.browser = 'Konqueror'; + this.allowAbsolute = 0; + this.allowAbsoluteDelim = 0; + if (navigator.userAgent.match(/Konqueror\/(\d+)\.(\d+)/)) { + if (RegExp.$1 < 3 || (RegExp.$1 == 3 && RegExp.$2 < 3)) { + this.separateSkips = 1; + this.valignBug = 1; + jsMath.Setup.Script('jsMath-old-browsers.js'); + } + } + // Apparently, Konqueror wants the names without the hyphen + jsMath.Add(jsMath.styles,{ + '.typeset .cmr10': 'font-family: jsMath-cmr10, jsMath cmr10, serif', + '.typeset .cmbx10': 'font-family: jsMath-cmbx10, jsMath cmbx10, jsMath-cmr10, jsMath cmr10', + '.typeset .cmti10': 'font-family: jsMath-cmti10, jsMath cmti10, jsMath-cmr10, jsMath cmr10', + '.typeset .cmmi10': 'font-family: jsMath-cmmi10, jsMath cmmi10', + '.typeset .cmsy10': 'font-family: jsMath-cmsy10, jsMath cmsy10', + '.typeset .cmex10': 'font-family: jsMath-cmex10, jsMath cmex10' + }); + jsMath.Font.testFont = "jsMath-cmex10, jsMath cmex10"; + } + } + +}; + +/***************************************************************************/ + +/* + * Implement font check and messages + */ +jsMath.Font = { + + testFont: "jsMath-cmex10", + fallback: "symbol", // the default fallback method + register: [], // list of fonts registered before jsMath.Init() + + // the HTML for the missing font message + message: + 'No jsMath TeX fonts found -- using image fonts instead.
      \n' + + 'These may be slow and might not print well.
      \n' + + 'Use the jsMath control panel to get additional information.', + + extra_message: + 'Extra TeX fonts not found:
      ' + + 'Using image fonts instead. This may be slow and might not print well.
      \n' + + 'Use the jsMath control panel to get additional information.', + + print_message: + 'To print higher-resolution math symbols, click the
      \n' + + 'Hi-Res Fonts for Printing button on the jsMath control panel.
      \n', + + alpha_message: + 'If the math symbols print as black boxes, turn off image alpha channels
      \n' + + 'using the Options pane of the jsMath control panel.
      \n', + + /* + * Look to see if a font is found. + * Check the character in a given position, and see if it is + * wider than the usual one in that position. + */ + Test1: function (name,n,factor,prefix) { + if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''} + var wh1 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + var wh2 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + //alert([wh1.w,wh2.w,wh1.h,factor*wh2.w]); + return (wh1.w > factor*wh2.w && wh1.h != 0); + }, + + Test2: function (name,n,factor,prefix) { + if (n == null) {n = 0x7C}; if (factor == null) {factor = 2}; if (prefix == null) {prefix = ''} + var wh1 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + var wh2 = jsMath.BBoxFor(''+jsMath.TeX[name][n].c+''); + //alert([wh2.w,wh1.w,wh1.h,factor*wh1.w]); + return (wh2.w > factor*wh1.w && wh1.h != 0); + }, + + /* + * Check for the new jsMath versions of the fonts (blacker with + * different encoding) and if not found, look for old-style fonts. + * If they are found, load the BaKoMa encoding information. + */ + CheckTeX: function () { + var wh = jsMath.BBoxFor(''+jsMath.TeX.cmex10[1].c+''); + jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10',null,null,'jsMath-')); + 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(''+jsMath.TeX.cmex10[1].c+''); + * jsMath.nofonts = ((wh.w*3 > wh.h || wh.h == 0) && !this.Test1('cmr10')); + * if (!jsMath.nofonts) {jsMath.Setup.Script("jsMath-BaKoMa-fonts.js")} + * } + */ + }, + + /* + * Check for the availability of TeX fonts. We do this by looking at + * the width and height of a character in the cmex10 font. The cmex10 + * font has depth considerably greater than most characters' widths (the + * whole font has the depth of the character with greatest depth). This + * is not the case for most fonts, so if we can access cmex10, the + * height of a character should be much bigger than the width. + * Otherwise, if we don't have cmex10, we'll get a character in another + * font with normal height and width. In this case, we insert a message + * pointing the user to the jsMath site, and load one of the fallback + * definitions. + * + */ + Check: function () { + var cookie = jsMath.Controls.cookie; + this.CheckTeX(); + if (jsMath.nofonts) { + if (cookie.autofont || cookie.font == 'tex') { + cookie.font = this.fallback; + if (cookie.warn) { + jsMath.nofontMessage = 1; + cookie.warn = 0; jsMath.Controls.SetCookie(0); + if (jsMath.window.NoFontMessage) {jsMath.window.NoFontMessage()} + else {this.Message(this.message)} + } + } + } else { + if (cookie.autofont) {cookie.font = 'tex'} + if (cookie.font == 'tex') return; + } + if (jsMath.noImgFonts) {cookie.font = 'unicode'} + if (cookie.font == 'unicode') { + jsMath.Setup.Script('jsMath-fallback-'+jsMath.platform+'.js'); + jsMath.Box.TeXnonfallback = jsMath.Box.TeX; + jsMath.Box.TeX = jsMath.Box.TeXfallback; + return; + } + if (!cookie.print && cookie.printwarn) { + this.PrintMessage( + (jsMath.Browser.alphaPrintBug && jsMath.Controls.cookie.alpha) ? + this.print_message + this.alpha_message : this.print_message); + } + if (jsMath.Browser.waitForImages) { + jsMath.Script.Push(jsMath.Script,"WaitForImage",jsMath.blank); + } + if (cookie.font == 'symbol') { + jsMath.Setup.Script('jsMath-fallback-symbols.js'); + jsMath.Box.TeXnonfallback = jsMath.Box.TeX; + jsMath.Box.TeX = jsMath.Box.TeXfallback; + return; + } + jsMath.Img.SetFont({ + cmr10: ['all'], cmmi10: ['all'], cmsy10: ['all'], + cmex10: ['all'], cmbx10: ['all'], cmti10: ['all'] + }); + jsMath.Img.LoadFont('cm-fonts'); + }, + + /* + * The message for when no TeX fonts. You can eliminate this message + * by including + * + * + * + * in your HTML file, before loading jsMath.js, if you want. But this + * means the user may not know that he or she can get a better version + * of your page. + */ + Message: function (message) { + if (jsMath.Element("Warning")) return; + var div = jsMath.Setup.DIV("Warning",{}); + div.innerHTML = + '
      ' + + '
      ' + message + + '
      ' + + 'jsMath Control Panel' + + '' + + 'Hide this Message' + + '

      ' + + '
      ' + + '

      '; + }, + + HideMessage: function () { + var message = jsMath.Element("Warning"); + if (message) {message.style.display = "none"} + }, + + PrintMessage: function (message) { + if (jsMath.Element("PrintWarning")) return; + var div = jsMath.Setup.DIV("PrintWarning",{}); + div.innerHTML = + '
      ' + + '
      ' + message + '
      ' + + '
      ' + + '

      '; + }, + + /* + * Register an extra font so jsMath knows about it + */ + Register: function (data,force) { + if (typeof(data) == 'string') {data = {name: data}} + if (!jsMath.Setup.inited && !force) { + this.register[this.register.length] = data; + return; + } + var fontname = data.name; var name = fontname.replace(/10$/,''); + var fontfam = jsMath.TeX.fam.length; + if (data.prefix == null) {data.prefix = ""} + if (!data.style) {data.style = "font-family: "+data.prefix+fontname+", serif"} + if (!data.styles) {data.styles = {}} + if (!data.macros) {data.macros = {}} + /* + * Register font family + */ + jsMath.TeX.fam[fontfam] = fontname; + jsMath.TeX.famName[fontname] = fontfam; + data.macros[name] = ['HandleFont',fontfam]; + jsMath.Add(jsMath.Parser.prototype.macros,data.macros); + /* + * Set up styles + */ + data.styles['.typeset .'+fontname] = data.style; + jsMath.Setup.Styles(data.styles); + if (jsMath.initialized) {jsMath.Script.Push(jsMath.Setup,'TeXfont',fontname)} + /* + * Check for font and give message if missing + */ + var cookie = jsMath.Controls.cookie; + var hasTeXfont = !jsMath.nofonts && + data.test(fontname,data.testChar,data.testFactor,data.prefix); + if (hasTeXfont && cookie.font == 'tex') { + if (data.tex) {data.tex(fontname,fontfam,data)} + return; + } + if (!hasTeXfont && cookie.warn && cookie.font == 'tex' && !jsMath.nofonts) { + if (!cookie.fonts.match("/"+fontname+"/")) { + cookie.fonts += fontname + "/"; jsMath.Controls.SetCookie(0); + if (!jsMath.Element("Warning")) this.Message(this.extra_message); + var extra = jsMath.Element("ExtraFonts"); + if (extra) { + if (extra.innerHTML != "") {extra.innerHTML += ','} + extra.innerHTML += " " + data.prefix+fontname; + } + } + } + if (cookie.font == 'unicode' || jsMath.noImgFonts) { + if (data.fallback) {data.fallback(fontname,fontfam,data)} + return; + } + // Image fonts + 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) { + jsMath.Script.Push(jsMath.Img,'Scale'); + jsMath.Script.Push(jsMath.Img,'UpdateFonts'); + } + }, + /* + * If fonts are registered before jsMath.Init() is called, jsMath.em + * will not be available, so they need to be delayed. + */ + LoadRegistered: function () { + var i = 0; + while (i < this.register.length) {this.Register(this.register[i++],1)} + this.register = []; + }, + + /* + * Load a font + */ + Load: function (name) {jsMath.Setup.Script(this.URL(name))}, + URL: function (name) {return jsMath.Img.root+name+'/def.js'} + +}; + +/***************************************************************************/ + +/* + * Implements the jsMath control panel. + * Much of the code is in jsMath-controls.html, which is + * loaded into a hidden IFRAME on demand + */ +jsMath.Controls = { + + // Data stored in the jsMath cookie + cookie: { + scale: 100, + font: 'tex', autofont: 1, scaleImg: 0, alpha: 1, + warn: 1, fonts: '/', printwarn: 1, stayhires: 0, + button: 1, progress: 1, asynch: 0, blank: 0, + print: 0, keep: '0D', global: 'auto', hiddenGlobal: 1 + }, + + cookiePath: '/', // can also set cookieDomain + noCookiePattern: /^(file|mk):$/, // pattern for handling cookies locally + + + /* + * Create the HTML needed for control panel + */ + Init: function () { + this.panel = jsMath.Setup.DIV("panel",{display:'none'},jsMath.fixedDiv); + if (!jsMath.Browser.msieButtonBug) {this.Button()} + else {setTimeout("jsMath.Controls.Button()",500)} + }, + + /* + * Load the control panel + */ + Panel: function () { + jsMath.Translate.Cancel(); + if (this.loaded) {this.Main()} + else {jsMath.Script.delayedLoad(jsMath.root+"jsMath-controls.html")} + }, + + /* + * Create the control panel button + */ + Button: function () { + var button = jsMath.Setup.DIV("button",{},jsMath.fixedDiv); + button.title = ' Open jsMath Control Panel '; + button.innerHTML = + 'jsMath'; + if (!jsMath.Global.isLocal && !jsMath.noShowGlobal) { + button.innerHTML += + 'Global '; + } + if (button.offsetWidth < 30) {button.style.width = "auto"} + if (!this.cookie.button) {button.style.display = "none"} + }, + + /* + * Since MSIE doesn't handle position:float, we need to have the + * window repositioned every time the window scrolls. We do that + * putting the floating elements into a window-sized DIV, but + * absolutely positioned, and then move the DIV. + */ + MoveButton: function () { + var body = (jsMath.Browser.quirks ? document.body : document.documentElement); + jsMath.fixedDiv.style.left = body.scrollLeft + 'px'; + jsMath.fixedDiv.style.top = body.scrollTop + body.clientHeight + 'px'; + jsMath.fixedDiv.style.width = body.clientWidth + 'px'; +// jsMath.fixedDiv.style.top = body.scrollTop + 'px'; +// jsMath.fixedDiv.style.height = body.clientHeight + 'px'; + }, + + /* + * Get the cookie data from the browser + * (for file: references, use url '?' syntax) + */ + GetCookie: function () { + // save the current cookie settings as the defaults + if (this.defaults == null) {this.defaults = {}} + jsMath.Add(this.defaults,this.cookie); this.userSet = {}; + // get the browser's cookie data + var cookies = jsMath.document.cookie; + if (jsMath.window.location.protocol.match(this.noCookiePattern)) { + cookies = this.localGetCookie(); + this.isLocalCookie = 1; + } + if (cookies.match(/jsMath=([^;]+)/)) { + var data = unescape(RegExp.$1).split(/,/); + for (var i = 0; i < data.length; i++) { + var x = data[i].match(/(.*):(.*)/); + if (x[2].match(/^\d+$/)) {x[2] = 1*x[2]} // convert from string + this.cookie[x[1]] = x[2]; + this.userSet[x[1]] = 1; + } + } + }, + localGetCookie: function () { + return jsMath.window.location.search.substr(1); + }, + + /* + * Save the cookie data in the browser + * (for file: urls, append data like CGI reference) + */ + SetCookie: function (warn) { + var cookie = []; + for (var id in this.cookie) { + if (this.defaults[id] == null || this.cookie[id] != this.defaults[id]) + {cookie[cookie.length] = id + ':' + this.cookie[id]} + } + cookie = cookie.join(','); + if (this.isLocalCookie) { + if (warn == 2) {return 'jsMath='+escape(cookie)} + this.localSetCookie(cookie,warn); + } else { + cookie = escape(cookie); + if (cookie == '') {warn = 0} + if (this.cookiePath) {cookie += '; path='+this.cookiePath} + if (this.cookieDomain) {cookie += '; domain='+this.cookieDomain} + if (this.cookie.keep != '0D') { + var ms = { + D: 1000*60*60*24, + W: 1000*60*60*24*7, + M: 1000*60*60*24*30, + Y: 1000*60*60*24*365 + }; + var exp = new Date; + exp.setTime(exp.getTime() + + this.cookie.keep.substr(0,1) * ms[this.cookie.keep.substr(1,1)]); + cookie += '; expires=' + exp.toGMTString(); + } + if (cookie != '') { + jsMath.document.cookie = 'jsMath='+cookie; + var cookies = jsMath.document.cookie; + if (warn && !cookies.match(/jsMath=/)) + {alert("Cookies must be enabled in order to save jsMath options")} + } + } + return null; + }, + localSetCookie: function (cookie,warn) { + if (!warn) return; + var href = String(jsMath.window.location).replace(/\?.*/,""); + if (cookie != '') {href += '?jsMath=' + escape(cookie)} + if (href != jsMath.window.location.href) {this.Reload(href)} + }, + + /* + * Reload the page (with the given URL) + */ + Reload: function (url) { + if (!this.loaded) return; + this.loaded = 0; jsMath.Setup.inited = -100; + jsMath.Global.ClearCache(); + if (url) {jsMath.window.location.replace(url)} + else {jsMath.window.location.reload()} + } + +}; + +/***************************************************************************/ + +/* + * Implements the actions for clicking and double-clicking + * on math formulas + */ +jsMath.Click = { + + /* + * Handle clicking on math to get control panel + */ + CheckClick: function (event) { + if (!event) {event = jsMath.window.event} + if (event.altKey) jsMath.Controls.Panel(); + }, + + /* + * Handle double-click for seeing TeX code + */ + + CheckDblClick: function (event) { + if (!event) {event = jsMath.window.event} + if (!jsMath.Click.DblClick) { + jsMath.Extension.Require('double-click',1); + // Firefox clears the event, so copy it + var tmpEvent = event; event = {}; + for (var id in tmpEvent) {event[id] = tmpEvent[id]} + } + jsMath.Script.Push(jsMath.Click,'DblClick',[event,this.alt]); + } + +}; + +/***************************************************************************/ + +/* + * The TeX font information + */ +jsMath.TeX = { + + // + // The TeX font parameters + // + thinmuskip: 3/18, + medmuskip: 4/18, + thickmuskip: 5/18, + + x_height: .430554, + quad: 1, + num1: .676508, + num2: .393732, + num3: .44373, + denom1: .685951, + denom2: .344841, + sup1: .412892, + sup2: .362892, + sup3: .288888, + sub1: .15, + sub2: .247217, + sup_drop: .386108, + sub_drop: .05, + delim1: 2.39, + delim2: 1.0, + axis_height: .25, + default_rule_thickness: .06, + big_op_spacing1: .111111, + big_op_spacing2: .166666, + big_op_spacing3: .2, + big_op_spacing4: .6, + big_op_spacing5: .1, + + integer: 6553.6, // conversion of em's to TeX internal integer + scriptspace: .05, + nulldelimiterspace: .12, + delimiterfactor: 901, + delimitershortfall: .5, + scale: 1, // scaling factor for font dimensions + + // The TeX math atom types (see Appendix G of the TeXbook) + atom: ['ord', 'op', 'bin', 'rel', 'open', 'close', 'punct', 'ord'], + + // The TeX font families + fam: ['cmr10','cmmi10','cmsy10','cmex10','cmti10','','cmbx10',''], + famName: {cmr10:0, cmmi10:1, cmsy10:2, cmex10:3, cmti10:4, cmbx10:6}, + + // Encoding used by jsMath fonts + encoding: [ + 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', + 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', + + '°', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '·', + 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'µ', '¶', 'ß', + + 'ï', '!', '"', '#', '$', '%', '&', ''', + '(', ')', '*', '+', ',', '-', '.', '/', + + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\', ']', '^', '_', + + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', 'ÿ' + ], + + /* + * The following are the TeX font mappings and metrics. The metric + * information comes directly from the TeX .tfm files. Browser-specific + * adjustments are made to these tables in the Browser.Init() routine + */ + cmr10: [ + [0.625,0.683], [0.833,0.683], [0.778,0.683], [0.694,0.683], + [0.667,0.683], [0.75,0.683], [0.722,0.683], [0.778,0.683], + [0.722,0.683], [0.778,0.683], [0.722,0.683], + [0.583,0.694,0,{ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 14, '108': 15}}], + [0.556,0.694], [0.556,0.694], [0.833,0.694], [0.833,0.694], + + [0.278,0.431], [0.306,0.431,0.194], [0.5,0.694], [0.5,0.694], + [0.5,0.628], [0.5,0.694], [0.5,0.568], [0.75,0.694], + [0.444,0,0.17], [0.5,0.694], [0.722,0.431], [0.778,0.431], + [0.5,0.528,0.0972], [0.903,0.683], [1.01,0.683], [0.778,0.732,0.0486], + + [0.278,0.431,0,{krn: {'108': -0.278, '76': -0.319}}], + [0.278,0.694,0,{lig: {'96': 60}}], + [0.5,0.694], [0.833,0.694,0.194], [0.5,0.75,0.0556], + [0.833,0.75,0.0556], [0.778,0.694], + [0.278,0.694,0,{krn: {'63': 0.111, '33': 0.111}, lig: {'39': 34}}], + [0.389,0.75,0.25], [0.389,0.75,0.25], [0.5,0.75], + [0.778,0.583,0.0833], [0.278,0.106,0.194], + [0.333,0.431,0,{lig: {'45': 123}}], + [0.278,0.106], [0.5,0.75,0.25], + + [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644], + [0.5,0.644], [0.5,0.644], [0.5,0.644], [0.5,0.644], + [0.5,0.644], [0.5,0.644], [0.278,0.431], [0.278,0.431,0.194], + [0.278,0.5,0.194], [0.778,0.367,-0.133], [0.472,0.5,0.194], + [0.472,0.694,0,{lig: {'96': 62}}], + + [0.778,0.694], + [0.75,0.683,0,{krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}], + [0.708,0.683], [0.722,0.683], + [0.764,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}], + [0.681,0.683], + [0.653,0.683,0,{krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [0.785,0.683], [0.75,0.683], [0.361,0.683,0,{krn: {'73': 0.0278}}], + [0.514,0.683], + [0.778,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [0.625,0.683,0,{krn: {'84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}], + [0.917,0.683], [0.75,0.683], + [0.778,0.683,0,{krn: {'88': -0.0278, '87': -0.0278, '65': -0.0278, '86': -0.0278, '89': -0.0278}}], + + [0.681,0.683,0,{krn: {'65': -0.0833, '111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}], + [0.778,0.683,0.194], + [0.736,0.683,0,{krn: {'116': -0.0278, '67': -0.0278, '79': -0.0278, '71': -0.0278, '85': -0.0278, '81': -0.0278, '84': -0.0833, '89': -0.0833, '86': -0.111, '87': -0.111}}], + [0.556,0.683], + [0.722,0.683,0,{krn: {'121': -0.0278, '101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}], + [0.75,0.683], + [0.75,0.683,0,{ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [1.03,0.683,0,{ic: 0.0139, krn: {'111': -0.0833, '101': -0.0833, '117': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.111, '79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [0.75,0.683,0,{krn: {'79': -0.0278, '67': -0.0278, '71': -0.0278, '81': -0.0278}}], + [0.75,0.683,0,{ic: 0.025, krn: {'101': -0.0833, '111': -0.0833, '114': -0.0833, '97': -0.0833, '65': -0.0833, '117': -0.0833}}], + [0.611,0.683], [0.278,0.75,0.25], [0.5,0.694], + [0.278,0.75,0.25], [0.5,0.694], [0.278,0.668], + + [0.278,0.694,0,{lig: {'96': 92}}], + [0.5,0.431,0,{krn: {'118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}], + [0.556,0.694,0,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}], + [0.444,0.431,0,{krn: {'104': -0.0278, '107': -0.0278}}], + [0.556,0.694], [0.444,0.431], + [0.306,0.694,0,{ic: 0.0778, krn: {'39': 0.0778, '63': 0.0778, '33': 0.0778, '41': 0.0778, '93': 0.0778}, lig: {'105': 12, '102': 11, '108': 13}}], + [0.5,0.431,0.194,{ic: 0.0139, krn: {'106': 0.0278}}], + [0.556,0.694,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [0.278,0.668], [0.306,0.668,0.194], + [0.528,0.694,0,{krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.278,0.694], + [0.833,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [0.556,0.431,0,{krn: {'116': -0.0278, '117': -0.0278, '98': -0.0278, '121': -0.0278, '118': -0.0278, '119': -0.0278}}], + [0.5,0.431,0,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}], + + [0.556,0.431,0.194,{krn: {'101': 0.0278, '111': 0.0278, '120': -0.0278, '100': 0.0278, '99': 0.0278, '113': 0.0278, '118': -0.0278, '106': 0.0556, '121': -0.0278, '119': -0.0278}}], + [0.528,0.431,0.194], [0.392,0.431], [0.394,0.431], + [0.389,0.615,0,{krn: {'121': -0.0278, '119': -0.0278}}], + [0.556,0.431,0,{krn: {'119': -0.0278}}], + [0.528,0.431,0,{ic: 0.0139, krn: {'97': -0.0556, '101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.722,0.431,0,{ic: 0.0139, krn: {'101': -0.0278, '97': -0.0278, '111': -0.0278, '99': -0.0278}}], + [0.528,0.431], + [0.528,0.431,0.194,{ic: 0.0139, krn: {'111': -0.0278, '101': -0.0278, '97': -0.0278, '46': -0.0833, '44': -0.0833}}], + [0.444,0.431], [0.5,0.431,0,{ic: 0.0278, lig: {'45': 124}}], + [1,0.431,0,{ic: 0.0278}], [0.5,0.694], [0.5,0.668], [0.5,0.668] + ], + + cmmi10: [ + [0.615,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.833,0.683,0,{krn: {'127': 0.167}}], + [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}], + [0.694,0.683,0,{krn: {'127': 0.167}}], + [0.742,0.683,0,{ic: 0.0757, krn: {'127': 0.0833}}], + [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.78,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}], + [0.583,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0556}}], + [0.667,0.683,0,{krn: {'127': 0.0833}}], + [0.612,0.683,0,{ic: 0.11, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.772,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}], + [0.64,0.431,0,{ic: 0.0037, krn: {'127': 0.0278}}], + [0.566,0.694,0.194,{ic: 0.0528, krn: {'127': 0.0833}}], + [0.518,0.431,0.194,{ic: 0.0556}], + [0.444,0.694,0,{ic: 0.0378, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.406,0.431,0,{krn: {'127': 0.0556}}], + + [0.438,0.694,0.194,{ic: 0.0738, krn: {'127': 0.0833}}], + [0.497,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}], + [0.469,0.694,0,{ic: 0.0278, krn: {'127': 0.0833}}], + [0.354,0.431,0,{krn: {'127': 0.0556}}], + [0.576,0.431], [0.583,0.694], + [0.603,0.431,0.194,{krn: {'127': 0.0278}}], + [0.494,0.431,0,{ic: 0.0637, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}], + [0.438,0.694,0.194,{ic: 0.046, krn: {'127': 0.111}}], + [0.57,0.431,0,{ic: 0.0359}], + [0.517,0.431,0.194,{krn: {'127': 0.0833}}], + [0.571,0.431,0,{ic: 0.0359, krn: {'59': -0.0556, '58': -0.0556}}], + [0.437,0.431,0,{ic: 0.113, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0278}}], + [0.54,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.596,0.694,0.194,{krn: {'127': 0.0833}}], + [0.626,0.431,0.194,{krn: {'127': 0.0556}}], + + [0.651,0.694,0.194,{ic: 0.0359, krn: {'127': 0.111}}], + [0.622,0.431,0,{ic: 0.0359}], + [0.466,0.431,0,{krn: {'127': 0.0833}}], + [0.591,0.694,0,{krn: {'127': 0.0833}}], + [0.828,0.431,0,{ic: 0.0278}], + [0.517,0.431,0.194,{krn: {'127': 0.0833}}], + [0.363,0.431,0.0972,{ic: 0.0799, krn: {'127': 0.0833}}], + [0.654,0.431,0.194,{krn: {'127': 0.0833}}], + [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], [1,0.367,-0.133], + [0.278,0.464,-0.0363], [0.278,0.464,-0.0363], [0.5,0.465,-0.0347], [0.5,0.465,-0.0347], + + [0.5,0.431], [0.5,0.431], [0.5,0.431], [0.5,0.431,0.194], + [0.5,0.431,0.194], [0.5,0.431,0.194], [0.5,0.644], [0.5,0.431,0.194], + [0.5,0.644], [0.5,0.431,0.194], [0.278,0.106], [0.278,0.106,0.194], + [0.778,0.539,0.0391], + [0.5,0.75,0.25,{krn: {'1': -0.0556, '65': -0.0556, '77': -0.0556, '78': -0.0556, '89': 0.0556, '90': -0.0556}}], + [0.778,0.539,0.0391], [0.5,0.465,-0.0347], + + [0.531,0.694,0,{ic: 0.0556, krn: {'127': 0.0833}}], + [0.75,0.683,0,{krn: {'127': 0.139}}], + [0.759,0.683,0,{ic: 0.0502, krn: {'127': 0.0833}}], + [0.715,0.683,0,{ic: 0.0715, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.828,0.683,0,{ic: 0.0278, krn: {'127': 0.0556}}], + [0.738,0.683,0,{ic: 0.0576, krn: {'127': 0.0833}}], + [0.643,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.786,0.683,0,{krn: {'127': 0.0833}}], + [0.831,0.683,0,{ic: 0.0812, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.44,0.683,0,{ic: 0.0785, krn: {'127': 0.111}}], + [0.555,0.683,0,{ic: 0.0962, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.167}}], + [0.849,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.681,0.683,0,{krn: {'127': 0.0278}}], + [0.97,0.683,0,{ic: 0.109, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.803,0.683,0,{ic: 0.109, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.763,0.683,0,{ic: 0.0278, krn: {'127': 0.0833}}], + + [0.642,0.683,0,{ic: 0.139, krn: {'61': -0.0556, '59': -0.111, '58': -0.111, '127': 0.0833}}], + [0.791,0.683,0.194,{krn: {'127': 0.0833}}], + [0.759,0.683,0,{ic: 0.00773, krn: {'127': 0.0833}}], + [0.613,0.683,0,{ic: 0.0576, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.584,0.683,0,{ic: 0.139, krn: {'61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.683,0.683,0,{ic: 0.109, krn: {'59': -0.111, '58': -0.111, '61': -0.0556, '127': 0.0278}}], + [0.583,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.944,0.683,0,{ic: 0.139, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.828,0.683,0,{ic: 0.0785, krn: {'61': -0.0833, '61': -0.0278, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.581,0.683,0,{ic: 0.222, krn: {'59': -0.167, '58': -0.167, '61': -0.111}}], + [0.683,0.683,0,{ic: 0.0715, krn: {'61': -0.0556, '59': -0.0556, '58': -0.0556, '127': 0.0833}}], + [0.389,0.75], [0.389,0.694,0.194], [0.389,0.694,0.194], + [1,0.358,-0.142], [1,0.358,-0.142], + + [0.417,0.694,0,{krn: {'127': 0.111}}], + [0.529,0.431], [0.429,0.694], [0.433,0.431,0,{krn: {'127': 0.0556}}], + [0.52,0.694,0,{krn: {'89': 0.0556, '90': -0.0556, '106': -0.111, '102': -0.167, '127': 0.167}}], + [0.466,0.431,0,{krn: {'127': 0.0556}}], + [0.49,0.694,0.194,{ic: 0.108, krn: {'59': -0.0556, '58': -0.0556, '127': 0.167}}], + [0.477,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.576,0.694,0,{krn: {'127': -0.0278}}], [0.345,0.66], + [0.412,0.66,0.194,{ic: 0.0572, krn: {'59': -0.0556, '58': -0.0556}}], + [0.521,0.694,0,{ic: 0.0315}], [0.298,0.694,0,{ic: 0.0197, krn: {'127': 0.0833}}], + [0.878,0.431], [0.6,0.431], [0.485,0.431,0,{krn: {'127': 0.0556}}], + + [0.503,0.431,0.194,{krn: {'127': 0.0833}}], + [0.446,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0833}}], + [0.451,0.431,0,{ic: 0.0278, krn: {'59': -0.0556, '58': -0.0556, '127': 0.0556}}], + [0.469,0.431,0,{krn: {'127': 0.0556}}], [0.361,0.615,0,{krn: {'127': 0.0833}}], + [0.572,0.431,0,{krn: {'127': 0.0278}}], + [0.485,0.431,0,{ic: 0.0359, krn: {'127': 0.0278}}], + [0.716,0.431,0,{ic: 0.0269, krn: {'127': 0.0833}}], + [0.572,0.431,0,{krn: {'127': 0.0278}}], + [0.49,0.431,0.194,{ic: 0.0359, krn: {'127': 0.0556}}], + [0.465,0.431,0,{ic: 0.044, krn: {'127': 0.0556}}], + [0.322,0.431,0,{krn: {'127': 0.0278}}], + [0.384,0.431,0.194,{krn: {'127': 0.0833}}], + [0.636,0.431,0.194,{krn: {'127': 0.111}}], + [0.5,0.714,0,{ic: 0.154}], [0.278,0.694,0,{ic: 0.399}] + ], + + cmsy10: [ + [0.778,0.583,0.0833], [0.278,0.444,-0.0556], [0.778,0.583,0.0833], + [0.5,0.465,-0.0347], [0.778,0.583,0.0833], [0.5,0.444,-0.0556], + [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833], + [0.778,0.583,0.0833], [0.778,0.583,0.0833], [0.778,0.583,0.0833], + [0.778,0.583,0.0833], [1,0.694,0.194], [0.5,0.444,-0.0556], [0.5,0.444,-0.0556], + + [0.778,0.464,-0.0363], [0.778,0.464,-0.0363], [0.778,0.636,0.136], + [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.636,0.136], + [0.778,0.636,0.136], [0.778,0.636,0.136], [0.778,0.367,-0.133], + [0.778,0.483,-0.0169], [0.778,0.539,0.0391], [0.778,0.539,0.0391], + [1,0.539,0.0391], [1,0.539,0.0391], [0.778,0.539,0.0391], [0.778,0.539,0.0391], + + [1,0.367,-0.133], [1,0.367,-0.133], [0.5,0.694,0.194], [0.5,0.694,0.194], + [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.464,-0.0363], + [1,0.367,-0.133], [1,0.367,-0.133], [0.611,0.694,0.194], [0.611,0.694,0.194], + [1,0.367,-0.133], [1,0.694,0.194], [1,0.694,0.194], [0.778,0.431], + + [0.275,0.556], [1,0.431], [0.667,0.539,0.0391], [0.667,0.539,0.0391], + [0.889,0.694,0.194], [0.889,0.694,0.194], [0,0.694,0.194], [0,0.367,-0.133], + [0.556,0.694], [0.556,0.694], [0.667,0.431], [0.5,0.75,0.0556], + [0.722,0.694], [0.722,0.694], [0.778,0.694], [0.778,0.694], + + [0.611,0.694], [0.798,0.683,0,{krn: {'48': 0.194}}], + [0.657,0.683,0,{ic: 0.0304, krn: {'48': 0.139}}], + [0.527,0.683,0,{ic: 0.0583, krn: {'48': 0.139}}], + [0.771,0.683,0,{ic: 0.0278, krn: {'48': 0.0833}}], + [0.528,0.683,0,{ic: 0.0894, krn: {'48': 0.111}}], + [0.719,0.683,0,{ic: 0.0993, krn: {'48': 0.111}}], + [0.595,0.683,0.0972,{ic: 0.0593, krn: {'48': 0.111}}], + [0.845,0.683,0,{ic: 0.00965, krn: {'48': 0.111}}], + [0.545,0.683,0,{ic: 0.0738, krn: {'48': 0.0278}}], + [0.678,0.683,0.0972,{ic: 0.185, krn: {'48': 0.167}}], + [0.762,0.683,0,{ic: 0.0144, krn: {'48': 0.0556}}], + [0.69,0.683,0,{krn: {'48': 0.139}}], [1.2,0.683,0,{krn: {'48': 0.139}}], + [0.82,0.683,0,{ic: 0.147, krn: {'48': 0.0833}}], + [0.796,0.683,0,{ic: 0.0278, krn: {'48': 0.111}}], + + [0.696,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.817,0.683,0.0972,{krn: {'48': 0.111}}], + [0.848,0.683,0,{krn: {'48': 0.0833}}], + [0.606,0.683,0,{ic: 0.075, krn: {'48': 0.139}}], + [0.545,0.683,0,{ic: 0.254, krn: {'48': 0.0278}}], + [0.626,0.683,0,{ic: 0.0993, krn: {'48': 0.0833}}], + [0.613,0.683,0,{ic: 0.0822, krn: {'48': 0.0278}}], + [0.988,0.683,0,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.713,0.683,0,{ic: 0.146, krn: {'48': 0.139}}], + [0.668,0.683,0.0972,{ic: 0.0822, krn: {'48': 0.0833}}], + [0.725,0.683,0,{ic: 0.0794, krn: {'48': 0.139}}], + [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556], [0.667,0.556], + + [0.611,0.694], [0.611,0.694], [0.444,0.75,0.25], [0.444,0.75,0.25], + [0.444,0.75,0.25], [0.444,0.75,0.25], [0.5,0.75,0.25], [0.5,0.75,0.25], + [0.389,0.75,0.25], [0.389,0.75,0.25], [0.278,0.75,0.25], [0.5,0.75,0.25], + [0.5,0.75,0.25], [0.611,0.75,0.25], [0.5,0.75,0.25], [0.278,0.694,0.194], + + [0.833,0.04,0.96], [0.75,0.683], [0.833,0.683], [0.417,0.694,0.194,{ic: 0.111}], + [0.667,0.556], [0.667,0.556], [0.778,0.636,0.136], [0.778,0.636,0.136], + [0.444,0.694,0.194], [0.444,0.694,0.194], [0.444,0.694,0.194], + [0.611,0.694,0.194], [0.778,0.694,0.13], [0.778,0.694,0.13], + [0.778,0.694,0.13], [0.778,0.694,0.13] + ], + + cmex10: [ + [0.458,0.04,1.16,{n: 16}], [0.458,0.04,1.16,{n: 17}], + [0.417,0.04,1.16,{n: 104}], [0.417,0.04,1.16,{n: 105}], + [0.472,0.04,1.16,{n: 106}], [0.472,0.04,1.16,{n: 107}], + [0.472,0.04,1.16,{n: 108}], [0.472,0.04,1.16,{n: 109}], + [0.583,0.04,1.16,{n: 110}], [0.583,0.04,1.16,{n: 111}], + [0.472,0.04,1.16,{n: 68}], [0.472,0.04,1.16,{n: 69}], + [0.333,0,0.6,{delim: {rep: 12}}], [0.556,0,0.6,{delim: {rep: 13}}], + [0.578,0.04,1.16,{n: 46}], [0.578,0.04,1.16,{n: 47}], + + [0.597,0.04,1.76,{n: 18}], [0.597,0.04,1.76,{n: 19}], + [0.736,0.04,2.36,{n: 32}], [0.736,0.04,2.36,{n: 33}], + [0.528,0.04,2.36,{n: 34}], [0.528,0.04,2.36,{n: 35}], + [0.583,0.04,2.36,{n: 36}], [0.583,0.04,2.36,{n: 37}], + [0.583,0.04,2.36,{n: 38}], [0.583,0.04,2.36,{n: 39}], + [0.75,0.04,2.36,{n: 40}], [0.75,0.04,2.36,{n: 41}], + [0.75,0.04,2.36,{n: 42}], [0.75,0.04,2.36,{n: 43}], + [1.04,0.04,2.36,{n: 44}], [1.04,0.04,2.36,{n: 45}], + + [0.792,0.04,2.96,{n: 48}], [0.792,0.04,2.96,{n: 49}], + [0.583,0.04,2.96,{n: 50}], [0.583,0.04,2.96,{n: 51}], + [0.639,0.04,2.96,{n: 52}], [0.639,0.04,2.96,{n: 53}], + [0.639,0.04,2.96,{n: 54}], [0.639,0.04,2.96,{n: 55}], + [0.806,0.04,2.96,{n: 56}], [0.806,0.04,2.96,{n: 57}], + [0.806,0.04,2.96], [0.806,0.04,2.96], + [1.28,0.04,2.96], [1.28,0.04,2.96], + [0.811,0.04,1.76,{n: 30}], [0.811,0.04,1.76,{n: 31}], + + [0.875,0.04,1.76,{delim: {top: 48, bot: 64, rep: 66}}], + [0.875,0.04,1.76,{delim: {top: 49, bot: 65, rep: 67}}], + [0.667,0.04,1.76,{delim: {top: 50, bot: 52, rep: 54}}], + [0.667,0.04,1.76,{delim: {top: 51, bot: 53, rep: 55}}], + [0.667,0.04,1.76,{delim: {bot: 52, rep: 54}}], + [0.667,0.04,1.76,{delim: {bot: 53, rep: 55}}], + [0.667,0,0.6,{delim: {top: 50, rep: 54}}], + [0.667,0,0.6,{delim: {top: 51, rep: 55}}], + [0.889,0,0.9,{delim: {top: 56, mid: 60, bot: 58, rep: 62}}], + [0.889,0,0.9,{delim: {top: 57, mid: 61, bot: 59, rep: 62}}], + [0.889,0,0.9,{delim: {top: 56, bot: 58, rep: 62}}], + [0.889,0,0.9,{delim: {top: 57, bot: 59, rep: 62}}], + [0.889,0,1.8,{delim: {rep: 63}}], + [0.889,0,1.8,{delim: {rep: 119}}], + [0.889,0,0.3,{delim: {rep: 62}}], + [0.667,0,0.6,{delim: {top: 120, bot: 121, rep: 63}}], + + [0.875,0.04,1.76,{delim: {top: 56, bot: 59, rep: 62}}], + [0.875,0.04,1.76,{delim: {top: 57, bot: 58, rep: 62}}], + [0.875,0,0.6,{delim: {rep: 66}}], [0.875,0,0.6,{delim: {rep: 67}}], + [0.611,0.04,1.76,{n: 28}], [0.611,0.04,1.76,{n: 29}], + [0.833,0,1,{n: 71}], [1.11,0.1,1.5], [0.472,0,1.11,{ic: 0.194, n: 73}], + [0.556,0,2.22,{ic: 0.444}], [1.11,0,1,{n: 75}], [1.51,0.1,1.5], + [1.11,0,1,{n: 77}], [1.51,0.1,1.5], [1.11,0,1,{n: 79}], [1.51,0.1,1.5], + + [1.06,0,1,{n: 88}], [0.944,0,1,{n: 89}], [0.472,0,1.11,{ic: 0.194, n: 90}], + [0.833,0,1,{n: 91}], [0.833,0,1,{n: 92}], [0.833,0,1,{n: 93}], + [0.833,0,1,{n: 94}], [0.833,0,1,{n: 95}], [1.44,0.1,1.5], + [1.28,0.1,1.5], [0.556,0,2.22,{ic: 0.444}], [1.11,0.1,1.5], + [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5], [1.11,0.1,1.5], + + [0.944,0,1,{n: 97}], [1.28,0.1,1.5], [0.556,0.722,0,{n: 99}], + [1,0.75,0,{n: 100}], [1.44,0.75], [0.556,0.722,0,{n: 102}], + [1,0.75,0,{n: 103}], [1.44,0.75], [0.472,0.04,1.76,{n: 20}], + [0.472,0.04,1.76,{n: 21}], [0.528,0.04,1.76,{n: 22}], + [0.528,0.04,1.76,{n: 23}], [0.528,0.04,1.76,{n: 24}], + [0.528,0.04,1.76,{n: 25}], [0.667,0.04,1.76,{n: 26}], + [0.667,0.04,1.76,{n: 27}], + + [1,0.04,1.16,{n: 113}], [1,0.04,1.76,{n: 114}], [1,0.04,2.36,{n: 115}], + [1,0.04,2.96,{n: 116}], [1.06,0,1.8,{delim: {top: 118, bot: 116, rep: 117}}], + [1.06,0,0.6], [1.06,0.04,0.56], + [0.778,0,0.6,{delim: {top: 126, bot: 127, rep: 119}}], + [0.667,0,0.6,{delim: {top: 120, rep: 63}}], + [0.667,0,0.6,{delim: {bot: 121, rep: 63}}], + [0.45,0.12], [0.45,0.12], [0.45,0.12], [0.45,0.12], + [0.778,0,0.6,{delim: {top: 126, rep: 119}}], + [0.778,0,0.6,{delim: {bot: 127, rep: 119}}] + ], + + cmti10: [ + [0.627,0.683,0,{ic: 0.133}], [0.818,0.683], [0.767,0.683,0,{ic: 0.094}], + [0.692,0.683], [0.664,0.683,0,{ic: 0.153}], [0.743,0.683,0,{ic: 0.164}], + [0.716,0.683,0,{ic: 0.12}], [0.767,0.683,0,{ic: 0.111}], + [0.716,0.683,0,{ic: 0.0599}], [0.767,0.683,0,{ic: 0.111}], + [0.716,0.683,0,{ic: 0.103}], + [0.613,0.694,0.194,{ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 14, '108': 15}}], + [0.562,0.694,0.194,{ic: 0.103}], [0.588,0.694,0.194,{ic: 0.103}], + [0.882,0.694,0.194,{ic: 0.103}], [0.894,0.694,0.194,{ic: 0.103}], + + [0.307,0.431,0,{ic: 0.0767}], [0.332,0.431,0.194,{ic: 0.0374}], + [0.511,0.694], [0.511,0.694,0,{ic: 0.0969}], [0.511,0.628,0,{ic: 0.083}], + [0.511,0.694,0,{ic: 0.108}], [0.511,0.562,0,{ic: 0.103}], [0.831,0.694], + [0.46,0,0.17], [0.537,0.694,0.194,{ic: 0.105}], [0.716,0.431,0,{ic: 0.0751}], + [0.716,0.431,0,{ic: 0.0751}], [0.511,0.528,0.0972,{ic: 0.0919}], + [0.883,0.683,0,{ic: 0.12}], [0.985,0.683,0,{ic: 0.12}], + [0.767,0.732,0.0486,{ic: 0.094}], + + [0.256,0.431,0,{krn: {'108': -0.256, '76': -0.321}}], + [0.307,0.694,0,{ic: 0.124, lig: {'96': 60}}], + [0.514,0.694,0,{ic: 0.0696}], [0.818,0.694,0.194,{ic: 0.0662}], + [0.769,0.694], [0.818,0.75,0.0556,{ic: 0.136}], + [0.767,0.694,0,{ic: 0.0969}], + [0.307,0.694,0,{ic: 0.124, krn: {'63': 0.102, '33': 0.102}, lig: {'39': 34}}], + [0.409,0.75,0.25,{ic: 0.162}], [0.409,0.75,0.25,{ic: 0.0369}], + [0.511,0.75,0,{ic: 0.149}], [0.767,0.562,0.0567,{ic: 0.0369}], + [0.307,0.106,0.194], [0.358,0.431,0,{ic: 0.0283, lig: {'45': 123}}], + [0.307,0.106], [0.511,0.75,0.25,{ic: 0.162}], + + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0.194,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0.194,{ic: 0.136}], + [0.511,0.644,0,{ic: 0.136}], [0.511,0.644,0,{ic: 0.136}], + [0.307,0.431,0,{ic: 0.0582}], [0.307,0.431,0.194,{ic: 0.0582}], + [0.307,0.5,0.194,{ic: 0.0756}], [0.767,0.367,-0.133,{ic: 0.0662}], + [0.511,0.5,0.194], [0.511,0.694,0,{ic: 0.122, lig: {'96': 62}}], + + [0.767,0.694,0,{ic: 0.096}], + [0.743,0.683,0,{krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.704,0.683,0,{ic: 0.103}], [0.716,0.683,0,{ic: 0.145}], + [0.755,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}], + [0.678,0.683,0,{ic: 0.12}], + [0.653,0.683,0,{ic: 0.133, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [0.774,0.683,0,{ic: 0.0872}], [0.743,0.683,0,{ic: 0.164}], + [0.386,0.683,0,{ic: 0.158}], [0.525,0.683,0,{ic: 0.14}], + [0.769,0.683,0,{ic: 0.145, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [0.627,0.683,0,{krn: {'84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.897,0.683,0,{ic: 0.164}], [0.743,0.683,0,{ic: 0.164}], + [0.767,0.683,0,{ic: 0.094, krn: {'88': -0.0256, '87': -0.0256, '65': -0.0256, '86': -0.0256, '89': -0.0256}}], + + [0.678,0.683,0,{ic: 0.103, krn: {'65': -0.0767}}], + [0.767,0.683,0.194,{ic: 0.094}], + [0.729,0.683,0,{ic: 0.0387, krn: {'110': -0.0256, '108': -0.0256, '114': -0.0256, '117': -0.0256, '109': -0.0256, '116': -0.0256, '105': -0.0256, '67': -0.0256, '79': -0.0256, '71': -0.0256, '104': -0.0256, '98': -0.0256, '85': -0.0256, '107': -0.0256, '118': -0.0256, '119': -0.0256, '81': -0.0256, '84': -0.0767, '89': -0.0767, '86': -0.102, '87': -0.102, '101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.562,0.683,0,{ic: 0.12}], + [0.716,0.683,0,{ic: 0.133, krn: {'121': -0.0767, '101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}], + [0.743,0.683,0,{ic: 0.164}], + [0.743,0.683,0,{ic: 0.184, krn: {'111': -0.0767, '101': -0.0767, '117': -0.0767, '114': -0.0767, '97': -0.0767, '65': -0.102, '79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [0.999,0.683,0,{ic: 0.184, krn: {'65': -0.0767}}], + [0.743,0.683,0,{ic: 0.158, krn: {'79': -0.0256, '67': -0.0256, '71': -0.0256, '81': -0.0256}}], + [0.743,0.683,0,{ic: 0.194, krn: {'101': -0.0767, '111': -0.0767, '114': -0.0767, '97': -0.0767, '117': -0.0767, '65': -0.0767}}], + [0.613,0.683,0,{ic: 0.145}], [0.307,0.75,0.25,{ic: 0.188}], + [0.514,0.694,0,{ic: 0.169}], [0.307,0.75,0.25,{ic: 0.105}], + [0.511,0.694,0,{ic: 0.0665}], [0.307,0.668,0,{ic: 0.118}], + + [0.307,0.694,0,{ic: 0.124, lig: {'96': 92}}], [0.511,0.431,0,{ic: 0.0767}], + [0.46,0.694,0,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.46,0.431,0,{ic: 0.0565, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.511,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}], + [0.46,0.431,0,{ic: 0.0751, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.307,0.694,0.194,{ic: 0.212, krn: {'39': 0.104, '63': 0.104, '33': 0.104, '41': 0.104, '93': 0.104}, lig: {'105': 12, '102': 11, '108': 13}}], + [0.46,0.431,0.194,{ic: 0.0885}], [0.511,0.694,0,{ic: 0.0767}], + [0.307,0.655,0,{ic: 0.102}], [0.307,0.655,0.194,{ic: 0.145}], + [0.46,0.694,0,{ic: 0.108}], [0.256,0.694,0,{ic: 0.103, krn: {'108': 0.0511}}], + [0.818,0.431,0,{ic: 0.0767}], [0.562,0.431,0,{ic: 0.0767, krn: {'39': -0.102}}], + [0.511,0.431,0,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + + [0.511,0.431,0.194,{ic: 0.0631, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.46,0.431,0.194,{ic: 0.0885}], + [0.422,0.431,0,{ic: 0.108, krn: {'101': -0.0511, '97': -0.0511, '111': -0.0511, '100': -0.0511, '99': -0.0511, '103': -0.0511, '113': -0.0511}}], + [0.409,0.431,0,{ic: 0.0821}], [0.332,0.615,0,{ic: 0.0949}], + [0.537,0.431,0,{ic: 0.0767}], [0.46,0.431,0,{ic: 0.108}], + [0.664,0.431,0,{ic: 0.108, krn: {'108': 0.0511}}], + [0.464,0.431,0,{ic: 0.12}], [0.486,0.431,0.194,{ic: 0.0885}], + [0.409,0.431,0,{ic: 0.123}], [0.511,0.431,0,{ic: 0.0921, lig: {'45': 124}}], + [1.02,0.431,0,{ic: 0.0921}], [0.511,0.694,0,{ic: 0.122}], + [0.511,0.668,0,{ic: 0.116}], [0.511,0.668,0,{ic: 0.105}] + ], + + cmbx10: [ + [0.692,0.686], [0.958,0.686], [0.894,0.686], [0.806,0.686], + [0.767,0.686], [0.9,0.686], [0.831,0.686], [0.894,0.686], + [0.831,0.686], [0.894,0.686], [0.831,0.686], + [0.671,0.694,0,{ic: 0.109, krn: {'39': 0.109, '63': 0.109, '33': 0.109, '41': 0.109, '93': 0.109}, lig: {'105': 14, '108': 15}}], + [0.639,0.694], [0.639,0.694], [0.958,0.694], [0.958,0.694], + + [0.319,0.444], [0.351,0.444,0.194], [0.575,0.694], [0.575,0.694], + [0.575,0.632], [0.575,0.694], [0.575,0.596], [0.869,0.694], + [0.511,0,0.17], [0.597,0.694], [0.831,0.444], [0.894,0.444], + [0.575,0.542,0.0972], [1.04,0.686], [1.17,0.686], [0.894,0.735,0.0486], + + [0.319,0.444,0,{krn: {'108': -0.319, '76': -0.378}}], + [0.35,0.694,0,{lig: {'96': 60}}], [0.603,0.694], [0.958,0.694,0.194], + [0.575,0.75,0.0556], [0.958,0.75,0.0556], [0.894,0.694], + [0.319,0.694,0,{krn: {'63': 0.128, '33': 0.128}, lig: {'39': 34}}], + [0.447,0.75,0.25], [0.447,0.75,0.25], [0.575,0.75], [0.894,0.633,0.133], + [0.319,0.156,0.194], [0.383,0.444,0,{lig: {'45': 123}}], + [0.319,0.156], [0.575,0.75,0.25], + + [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644], + [0.575,0.644], [0.575,0.644], [0.575,0.644], [0.575,0.644], + [0.575,0.644], [0.575,0.644], [0.319,0.444], [0.319,0.444,0.194], + [0.35,0.5,0.194], [0.894,0.391,-0.109], [0.543,0.5,0.194], + [0.543,0.694,0,{lig: {'96': 62}}], + + [0.894,0.694], + [0.869,0.686,0,{krn: {'116': -0.0319, '67': -0.0319, '79': -0.0319, '71': -0.0319, '85': -0.0319, '81': -0.0319, '84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}], + [0.818,0.686], [0.831,0.686], + [0.882,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}], + [0.756,0.686], + [0.724,0.686,0,{krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [0.904,0.686], [0.9,0.686], [0.436,0.686,0,{krn: {'73': 0.0319}}], + [0.594,0.686], + [0.901,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [0.692,0.686,0,{krn: {'84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}], + [1.09,0.686], [0.9,0.686], + [0.864,0.686,0,{krn: {'88': -0.0319, '87': -0.0319, '65': -0.0319, '86': -0.0319, '89': -0.0319}}], + + [0.786,0.686,0,{krn: {'65': -0.0958, '111': -0.0319, '101': -0.0319, '97': -0.0319, '46': -0.0958, '44': -0.0958}}], + [0.864,0.686,0.194], + [0.862,0.686,0,{krn: {'116': -0.0319, '67': -0.0319, '79': -0.0319, '71': -0.0319, '85': -0.0319, '81': -0.0319, '84': -0.0958, '89': -0.0958, '86': -0.128, '87': -0.128}}], + [0.639,0.686], + [0.8,0.686,0,{krn: {'121': -0.0319, '101': -0.0958, '111': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.0958, '117': -0.0958}}], + [0.885,0.686], + [0.869,0.686,0,{ic: 0.016, krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [1.19,0.686,0,{ic: 0.016, krn: {'111': -0.0958, '101': -0.0958, '117': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.128, '79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [0.869,0.686,0,{krn: {'79': -0.0319, '67': -0.0319, '71': -0.0319, '81': -0.0319}}], + [0.869,0.686,0,{ic: 0.0287, krn: {'101': -0.0958, '111': -0.0958, '114': -0.0958, '97': -0.0958, '65': -0.0958, '117': -0.0958}}], + [0.703,0.686], [0.319,0.75,0.25], [0.603,0.694], [0.319,0.75,0.25], + [0.575,0.694], [0.319,0.694], + + [0.319,0.694,0,{lig: {'96': 92}}], + [0.559,0.444,0,{krn: {'118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}], + [0.639,0.694,0,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}], + [0.511,0.444,0,{krn: {'104': -0.0319, '107': -0.0319}}], + [0.639,0.694], [0.527,0.444], + [0.351,0.694,0,{ic: 0.109, krn: {'39': 0.109, '63': 0.109, '33': 0.109, '41': 0.109, '93': 0.109}, lig: {'105': 12, '102': 11, '108': 13}}], + [0.575,0.444,0.194,{ic: 0.016, krn: {'106': 0.0319}}], + [0.639,0.694,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [0.319,0.694], [0.351,0.694,0.194], + [0.607,0.694,0,{krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.319,0.694], + [0.958,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [0.639,0.444,0,{krn: {'116': -0.0319, '117': -0.0319, '98': -0.0319, '121': -0.0319, '118': -0.0319, '119': -0.0319}}], + [0.575,0.444,0,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}], + + [0.639,0.444,0.194,{krn: {'101': 0.0319, '111': 0.0319, '120': -0.0319, '100': 0.0319, '99': 0.0319, '113': 0.0319, '118': -0.0319, '106': 0.0639, '121': -0.0319, '119': -0.0319}}], + [0.607,0.444,0.194], [0.474,0.444], [0.454,0.444], + [0.447,0.635,0,{krn: {'121': -0.0319, '119': -0.0319}}], + [0.639,0.444,0,{krn: {'119': -0.0319}}], + [0.607,0.444,0,{ic: 0.016, krn: {'97': -0.0639, '101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.831,0.444,0,{ic: 0.016, krn: {'101': -0.0319, '97': -0.0319, '111': -0.0319, '99': -0.0319}}], + [0.607,0.444], + [0.607,0.444,0.194,{ic: 0.016, krn: {'111': -0.0319, '101': -0.0319, '97': -0.0319, '46': -0.0958, '44': -0.0958}}], + [0.511,0.444], [0.575,0.444,0,{ic: 0.0319, lig: {'45': 124}}], + [1.15,0.444,0,{ic: 0.0319}], [0.575,0.694], [0.575,0.694], [0.575,0.694] + ] +}; + +/***************************************************************************/ + +/* + * Implement image-based fonts for fallback method + */ +jsMath.Img = { + + // font sizes available + fonts: [50, 60, 70, 85, 100, 120, 144, 173, 207, 249, 298, 358, 430], + + // em widths for the various font size directories + w: {'50': 6.9, '60': 8.3, '70': 9.7, '85': 11.8, '100': 13.9, + '120': 16.7, '144': 20.0, '173': 24.0, '207': 28.8, '249': 34.6, + '298': 41.4, '358': 49.8, '430': 59.8}, + + best: 4, // index of best font size in the fonts list + update: {}, // fonts to update (see UpdateFonts below) + factor: 1, // factor by which to shrink images (for better printing) + loaded: 0, // image fonts are loaded + + // add characters to be drawn using images + SetFont: function (change) { + for (var font in change) { + if (!this.update[font]) {this.update[font] = []} + this.update[font] = this.update[font].concat(change[font]); + } + }, + + /* + * Called by the exta-font definition files to add an image font + * into the mix + */ + AddFont: function (size,def) { + if (!jsMath.Img[size]) {jsMath.Img[size] = {}}; + jsMath.Add(jsMath.Img[size],def); + }, + + /* + * Update font(s) to use image data rather than native fonts + * It looks in the jsMath.Img.update array to find the names + * of the fonts to udpate, and the arrays of character codes + * to set (or 'all' to change every character); + */ + UpdateFonts: function () { + var change = this.update; if (!this.loaded) return; + for (var font in change) { + for (var i = 0; i < change[font].length; i++) { + var c = change[font][i]; + if (c == 'all') {for (c in jsMath.TeX[font]) {jsMath.TeX[font][c].img = {}}} + else {jsMath.TeX[font][c].img = {}} + } + } + this.update = {}; + }, + + /* + * Find the font size that best fits our current font + * (this is the directory name for the img files used + * in some fallback modes). + */ + BestSize: function () { + var w = jsMath.em * this.factor; + var m = this.w[this.fonts[0]]; + for (var i = 1; i < this.fonts.length; i++) { + if (w < (this.w[this.fonts[i]] + 2*m) / 3) {return i-1} + m = this.w[this.fonts[i]]; + } + return i-1; + }, + + /* + * Get the scaling factor for the image fonts + */ + Scale: function () { + if (!this.loaded) return; + this.best = this.BestSize(); + this.em = jsMath.Img.w[this.fonts[this.best]]; + this.scale = (jsMath.em/this.em); + if (Math.abs(this.scale - 1) < .12) {this.scale = 1} + }, + + /* + * Get URL to directory for given font and size, based on the + * user's alpha/plain setting + */ + URL: function (name,size,C) { + var type = (jsMath.Controls.cookie.alpha) ? '/alpha/': '/plain/'; + if (C == null) {C = "def.js"} else {C = 'char'+C+'.png'} + if (size != "") {size += '/'} + return this.root+name+type+size+C; + }, + + /* + * Laod the data for an image font + */ + LoadFont: function (name) { + if (!this.loaded) this.Init(); + jsMath.Setup.Script(this.URL(name,"")); + }, + + /* + * Setup for print mode, and create the hex code table + */ + Init: function () { + if (jsMath.Controls.cookie.print || jsMath.Controls.cookie.stayhires) { + jsMath.Controls.cookie.print = jsMath.Controls.cookie.stayhires; + this.factor *= 3; + if (!jsMath.Controls.isLocalCookie || !jsMath.Global.isLocal) {jsMath.Controls.SetCookie(0)} + if (jsMath.Browser.alphaPrintBug) {jsMath.Controls.cookie.alpha = 0} + } + var codes = '0123456789ABCDEF'; + this.HexCode = []; + for (var i = 0; i < 128; i++) { + var h = Math.floor(i/16); var l = i - 16*h; + this.HexCode[i] = codes.charAt(h)+codes.charAt(l); + } + this.loaded = 1; + } + +}; + +/***************************************************************************/ + +/* + * jsMath.HTML handles creation of most of the HTML needed for + * presenting mathematics in HTML pages. + */ + +jsMath.HTML = { + + /* + * Produce a string version of a measurement in ems, + * showing only a limited number of digits, and + * using 0 when the value is near zero. + */ + Em: function (m) { + if (Math.abs(m) < .000001) {m = 0} + var s = String(m); s = s.replace(/(\.\d\d\d).+/,'$1'); + return s+'em' + }, + + /* + * Create a horizontal space of width w + */ + Spacer: function (w) { + if (w == 0) {return ''}; + return jsMath.Browser.msieSpaceFix+''; + }, + + /* + * Create a blank rectangle of the given size + * If the height is small, it is converted to pixels so that it + * will not disappear at small font sizes. + */ + + Blank: function (w,h,d,isRule) { + var backspace = ''; var style = '' + if (isRule) { + style += 'border-left:'+this.Em(w)+' solid;'; + if (jsMath.Browser.widthAddsBorder) {w = 0}; + } + if (w == 0) { + if (jsMath.Browser.blankWidthBug) { + if (jsMath.Browser.quirks) { + style += 'width:1px;'; + backspace = '' + } else if (!isRule) { + style += 'width:1px;margin-right:-1px;'; + } + } + } else {style += 'width:'+this.Em(w)+';'} + if (d == null) {d = 0} + if (h) { + var H = this.Em(h+d); + if (isRule && h*jsMath.em <= 1.5) {H = "1.5px"; h = 1.5/jsMath.em} + style += 'height:'+H+';'; + } + if (jsMath.Browser.mozInlineBlockBug) {d = -h} + if (jsMath.Browser.msieBlockDepthBug && !isRule) {d -= jsMath.d} + if (d) {style += 'vertical-align:'+this.Em(-d)} + return backspace+''; + }, + + /* + * Create a rule line for fractions, etc. + */ + Rule: function (w,h) { + if (h == null) {h = jsMath.TeX.default_rule_thickness} + return this.Blank(w,h,0,1); + }, + + /* + * Create a strut for measuring position of baseline + */ + Strut: function (h) {return this.Blank(1,h,0,1)}, + msieStrut: function (h) { + return '' + }, + + /* + * Add a tag to activate a specific CSS class + */ + Class: function (tclass,html) { + return ''+html+''; + }, + + /* + * Use a to place some HTML at a specific position. + * (This can be replaced by the ones below to overcome + * some browser-specific bugs.) + */ + Place: function (html,x,y) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + if (x || y) { + var span = '' + html + ''; + } + return html; + }, + + /* + * For MSIE on Windows, backspacing must be done in a separate + * , otherwise the contents will be clipped. Netscape + * also doesn't combine vertical and horizontal spacing well. + * Here the x and y positioning are done in separate tags + */ + PlaceSeparateSkips: function (html,x,y,mw,Mw,w) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + 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) + + '' + + this.Spacer(-lw) + + html + + this.Spacer(rw) + + '' + } + if (x) {html = this.Spacer(x) + html} + return html; + }, + + /* + * Place a SPAN with absolute coordinates + */ + PlaceAbsolute: function (html,x,y,mw,Mw,w) { + if (Math.abs(x) < .0001) {x = 0} + if (Math.abs(y) < .0001) {y = 0} + var leftSpace = ""; var rightSpace = ""; var width = ""; + if (jsMath.Browser.msieRelativeClipBug && mw != null) { + leftSpace = this.Spacer(-mw); x += mw; + rightSpace = this.Spacer(Mw-w); + } + if (jsMath.Browser.operaAbsoluteWidthBug) {width = " width: "+this.Em(w+2)} + html = + '' + + leftSpace + html + rightSpace + + ' ' + // space normalizes line height in script styles + ''; + return html; + }, + + Absolute: function(html,w,h,d,y) { + if (y != "none") { + if (Math.abs(y) < .0001) {y = 0} + html = '' + + html + ' ' // space normalizes line height in script styles + + ''; + } + if (d == "none") {d = 0} + html += this.Blank((jsMath.Browser.lineBreakBug ? 0 : w),h-d,d); + if (jsMath.Browser.msieAbsoluteBug) { // for MSIE (Mac) + html = '' + html + ''; + } + html = '' + html + ''; + if (jsMath.Browser.lineBreakBug) + {html = ''+html+''} + return html; + } + +}; + + +/***************************************************************************/ + +/* + * jsMath.Box handles TeX's math boxes and jsMath's equivalent of hboxes. + */ + +jsMath.Box = function (format,text,w,h,d) { + 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.x = 0; this.y = 0; this.mw = 0; this.Mw = w; + this.html = text; this.format = format; +}; + + +jsMath.Add(jsMath.Box,{ + + defaultH: 0, // default height for characters with none specified + + /* + * An empty box + */ + Null: function () {return new jsMath.Box('null','',0,0,0)}, + + /* + * A box containing only text whose class and style haven't been added + * yet (so that we can combine ones with the same styles). It gets + * the text dimensions, if needed. (In general, this has been + * replaced by TeX() below, but is still used in fallback mode.) + */ + Text: function (text,tclass,style,size,a,d) { + var html = jsMath.Typeset.AddClass(tclass,text); + html = jsMath.Typeset.AddStyle(style,size,html); + var BB = jsMath.EmBoxFor(html); var TeX = jsMath.Typeset.TeX(style,size); + var bd = ((tclass == 'cmsy10' || tclass == 'cmex10')? BB.h-TeX.h: TeX.d*BB.h/TeX.hd); + var box = new jsMath.Box('text',text,BB.w,BB.h-bd,bd); + box.style = style; box.size = size; box.tclass = tclass; + if (d != null) {box.d = d*TeX.scale} else {box.d = 0} + if (a == null || a == 1) {box.h = .9*TeX.M_height} + else {box.h = 1.1*TeX.x_height + TeX.scale*a} + return box; + }, + + /* + * Produce a box containing a given TeX character from a given font. + * The box is a text box (like the ones above), so that characters from + * the same font can be combined. + */ + TeX: function (C,font,style,size) { + var c = jsMath.TeX[font][C]; + if (c.d == null) {c.d = 0}; if (c.h == null) {c.h = 0} + if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); + var scale = jsMath.Typeset.TeX(style,size).scale; + var box = new jsMath.Box('text',c.c,c.w*scale,c.h*scale,c.d*scale); + box.style = style; box.size = size; + if (c.tclass) { + box.tclass = c.tclass; + if (c.img) {box.bh = c.img.bh; box.bd = c.img.bd} + else {box.bh = scale*jsMath.h; box.bd = scale*jsMath.d} + } else { + box.tclass = font; + box.bh = scale*jsMath.TeX[font].h; + box.bd = scale*jsMath.TeX[font].d; + if (jsMath.Browser.msieFontBug && box.html.match(/&#/)) { + // hack to avoid font changing back to the default + // font when a unicode reference is not followed + // by a letter or number + box.html += 'x'; + } + } + return box; + }, + + /* + * In fallback modes, handle the fact that we don't have the + * sizes of the characters precomputed + */ + TeXfallback: function (C,font,style,size) { + var c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} + if (c.img != null) {return this.TeXnonfallback(C,font,style,size)} + if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} + var a = c.a; var d = c.d; // avoid Firefox warnings + var box = this.Text(c.c,c.tclass,style,size,a,d); + var scale = jsMath.Typeset.TeX(style,size).scale; + if (c.bh != null) { + box.bh = c.bh*scale; + box.bd = c.bd*scale; + } else { + var h = box.bd+box.bh; + var html = jsMath.Typeset.AddClass(box.tclass,box.html); + html = jsMath.Typeset.AddStyle(style,size,html); + box.bd = jsMath.EmBoxFor(html + jsMath.HTML.Strut(h)).h - h; + box.bh = h - box.bd; + if (scale == 1) {c.bh = box.bh; c.bd = box.bd} + } + if (jsMath.msieFontBug && box.html.match(/&#/)) + {box.html += 'x'} + return box; + }, + + /* + * Set the character's string to the appropriate image file + */ + TeXIMG: function (font,C,size) { + var c = jsMath.TeX[font][C]; + if (c.img.size != null && c.img.size == size && + c.img.best != null && c.img.best == jsMath.Img.best) return; + var mustScale = (jsMath.Img.scale != 1); + var id = jsMath.Img.best + size - 4; + if (id < 0) {id = 0; mustScale = 1} else + if (id >= jsMath.Img.fonts.length) {id = jsMath.Img.fonts.length-1; mustScale = 1} + var imgFont = jsMath.Img[jsMath.Img.fonts[id]]; + var img = imgFont[font][C]; + var scale = 1/jsMath.Img.w[jsMath.Img.fonts[id]]; + if (id != jsMath.Img.best + size - 4) { + if (c.w != null) {scale = c.w/img[0]} else { + scale *= jsMath.Img.fonts[size]/jsMath.Img.fonts[4] + * jsMath.Img.fonts[jsMath.Img.best]/jsMath.Img.fonts[id]; + } + } + var w = img[0]*scale; var h = img[1]*scale; var d = -img[2]*scale; var v; + var wadjust = (c.w == null || Math.abs(c.w-w) < .01)? "" : " margin-right:"+jsMath.HTML.Em(c.w-w)+';'; + var resize = ""; C = jsMath.Img.HexCode[C]; + if (!mustScale && !jsMath.Controls.cookie.scaleImg) { + if (jsMath.Browser.mozImageSizeBug || 2*w < h || + (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha)) + {resize = "height:"+(img[1]*jsMath.Browser.imgScale)+'px;'} + resize += " width:"+(img[0]*jsMath.Browser.imgScale)+'px;' + v = -img[2]+'px'; + } else { + if (jsMath.Browser.mozImageSizeBug || 2*w < h || + (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha)) + {resize = "height:"+jsMath.HTML.Em(h*jsMath.Browser.imgScale)+';'} + resize += " width:"+jsMath.HTML.Em(w*jsMath.Browser.imgScale)+';' + v = jsMath.HTML.Em(d); + } + var vadjust = (Math.abs(d) < .01 && !jsMath.Browser.valignBug)? + "": " vertical-align:"+v+';'; + var URL = jsMath.Img.URL(font,jsMath.Img.fonts[id],C); + if (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha) { + c.c = ''; + } else { + c.c = ''; + } + c.tclass = "normal"; + c.img.bh = h+d; c.img.bd = -d; + c.img.size = size; c.img.best = jsMath.Img.best; + }, + + /* + * A box containing a spacer of a specific width + */ + Space: function (w) { + return new jsMath.Box('html',jsMath.HTML.Spacer(w),w,0,0); + }, + + /* + * A box containing a horizontal rule + */ + Rule: function (w,h) { + if (h == null) {h = jsMath.TeX.default_rule_thickness} + var html = jsMath.HTML.Rule(w,h); + return new jsMath.Box('html',html,w,h,0); + }, + + /* + * Get a character from a TeX font, and make sure that it has + * its metrics specified. + */ + GetChar: function (code,font) { + var c = jsMath.TeX[font][code]; + if (c.img != null) {this.TeXIMG(font,code,4)} + if (c.tclass == null) {c.tclass = font} + if (!c.computedW) { + c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w; + if (c.h == null) {c.h = jsMath.Box.defaultH}; if (c.d == null) {c.d = 0} + c.computedW = 1; + } + return c; + }, + + /* + * Locate the TeX delimiter character that matches a given height. + * Return the character, font, style and actual height used. + */ + DelimBestFit: function (H,c,font,style) { + if (c == 0 && font == 0) return null; + var C; var h; font = jsMath.TeX.fam[font]; + var isSS = (style.charAt(1) == 'S'); + var isS = (style.charAt(0) == 'S'); + while (c != null) { + C = jsMath.TeX[font][c]; + if (C.h == null) {C.h = jsMath.Box.defaultH}; if (C.d == null) {C.d = 0} + h = C.h+C.d; + if (C.delim) {return [c,font,'',H]} + if (isSS && .5*h >= H) {return [c,font,'SS',.5*h]} + if (isS && .7*h >= H) {return [c,font,'S',.7*h]} + if (h >= H || C.n == null) {return [c,font,'T',h]} + c = C.n; + } + return null; + }, + + /* + * Create the HTML needed for a stretchable delimiter of a given height, + * either centered or not. This version uses relative placement (i.e., + * backspaces, not line-breaks). This works with more browsers, but + * if the font size changes, the backspacing may not be right, so the + * delimiters may become jagged. + */ + DelimExtendRelative: function (H,c,font,a,nocenter) { + var C = jsMath.TeX[font][c]; + var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font); + var rep = this.GetChar(C.delim.rep,font); + var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font); + var ext = jsMath.Typeset.AddClass(rep.tclass,rep.c); + var w = rep.w; var h = rep.h+rep.d + var y; var Y; var html; var dx; var i; var n; + if (C.delim.mid) {// braces + var mid = this.GetChar(C.delim.mid,font); + n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d)-(bot.h+bot.d))/(2*(rep.h+rep.d))); + H = 2*n*(rep.h+rep.d) + (top.h+top.d) + (mid.h+mid.d) + (bot.h+bot.d); + if (nocenter) {y = 0} else {y = H/2+a}; Y = y; + html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h) + + jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(top.w+bot.w)/2,y-(H-bot.d)) + + jsMath.HTML.Place(jsMath.Typeset.AddClass(mid.tclass,mid.c),-(bot.w+mid.w)/2,y-(H+mid.h-mid.d)/2); + dx = (w-mid.w)/2; if (Math.abs(dx) < .0001) {dx = 0} + if (dx) {html += jsMath.HTML.Spacer(dx)} + y -= top.h+top.d + rep.h; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + y -= H/2 - rep.h/2; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + } else {// everything else + n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d))/(rep.h+rep.d)); + // make sure two-headed arrows have an extender + if (top.h+top.d < .9*(rep.h+rep.d)) {n = Math.max(1,n)} + H = n*(rep.h+rep.d) + (top.h+top.d) + (bot.h+bot.d); + if (nocenter) {y = 0} else {y = H/2+a}; Y = y; + html = jsMath.HTML.Place(jsMath.Typeset.AddClass(top.tclass,top.c),0,y-top.h) + dx = (w-top.w)/2; if (Math.abs(dx) < .0001) {dx = 0} + if (dx) {html += jsMath.HTML.Spacer(dx)} + y -= top.h+top.d + rep.h; + for (i = 0; i < n; i++) {html += jsMath.HTML.Place(ext,-w,y-i*h)} + html += jsMath.HTML.Place(jsMath.Typeset.AddClass(bot.tclass,bot.c),-(w+bot.w)/2,Y-(H-bot.d)); + } + if (nocenter) {h = top.h} else {h = H/2+a} + var box = new jsMath.Box('html',html,rep.w,h,H-h); + box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d; + return box; + }, + + /* + * Create the HTML needed for a stretchable delimiter of a given height, + * either centered or not. This version uses absolute placement (i.e., + * line-breaks, not backspacing). This gives more reliable results, + * but doesn't work with all browsers. + */ + DelimExtendAbsolute: function (H,c,font,a,nocenter) { + var Font = jsMath.TeX[font]; + var C = Font[c]; var html; + var top = this.GetChar(C.delim.top? C.delim.top: C.delim.rep,font); + var rep = this.GetChar(C.delim.rep,font); + var bot = this.GetChar(C.delim.bot? C.delim.bot: C.delim.rep,font); + var n; var h; var y; var ext; var i; + + if (C.delim.mid) {// braces + var mid = this.GetChar(C.delim.mid,font); + n = Math.ceil((H-(top.h+top.d)-(mid.h+mid.d-.05)-(bot.h+bot.d-.05))/(2*(rep.h+rep.d-.05))); + H = 2*n*(rep.h+rep.d-.05) + (top.h+top.d) + (mid.h+mid.d-.05) + (bot.h+bot.d-.05); + + html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0); + h = rep.h+rep.d - .05; y = top.d-.05 + rep.h; + ext = jsMath.Typeset.AddClass(rep.tclass,rep.c) + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(mid.tclass,mid.c),0,y+n*h-rep.h+mid.h); + y += n*h + mid.h+mid.d - .05; + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h); + } else {// all others + n = Math.ceil((H - (top.h+top.d) - (bot.h+bot.d-.05))/(rep.h+rep.d-.05)); + H = n*(rep.h+rep.d-.05) + (top.h+top.d) + (bot.h+bot.d-.05); + + html = jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(top.tclass,top.c),0,0); + h = rep.h+rep.d-.05; y = top.d-.05 + rep.h; + ext = jsMath.Typeset.AddClass(rep.tclass,rep.c); + for (i = 0; i < n; i++) {html += jsMath.HTML.PlaceAbsolute(ext,0,y+i*h)} + html += jsMath.HTML.PlaceAbsolute(jsMath.Typeset.AddClass(bot.tclass,bot.c),0,y+n*h-rep.h+bot.h); + } + + var w = top.w; + if (nocenter) {h = top.h; y = 0} else {h = H/2 + a; y = h - top.h} + if (jsMath.Controls.cookie.font === "unicode") { + if (jsMath.Browser.msie8HeightBug) {y -= jsMath.hd} + else if (jsMath.Browser.msieBlockDepthBug) {y += jsMath.d} + } + html = jsMath.HTML.Absolute(html,w,Font.h,"none",-y); + var box = new jsMath.Box('html',html,rep.w,h,H-h); + box.bh = jsMath.TeX[font].h; box.bd = jsMath.TeX[font].d; + return box; + }, + + /* + * Get the HTML for a given delimiter of a given height. + * It will return either a single character, if one exists, or the + * more complex HTML needed for a stretchable delimiter. + */ + Delimiter: function (H,delim,style,nocenter) { + var size = 4; //### pass this? + var TeX = jsMath.Typeset.TeX(style,size); + if (!delim) {return this.Space(TeX.nulldelimiterspace)} + var CFSH = this.DelimBestFit(H,delim[2],delim[1],style); + if (CFSH == null || CFSH[3] < H) + {CFSH = this.DelimBestFit(H,delim[4],delim[3],style)} + if (CFSH == null) {return this.Space(TeX.nulldelimiterspace)} + if (CFSH[2] == '') + {return this.DelimExtend(H,CFSH[0],CFSH[1],TeX.axis_height,nocenter)} + var box = jsMath.Box.TeX(CFSH[0],CFSH[1],CFSH[2],size).Styled(); + if (!nocenter) {box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height)} + if (Math.abs(box.y) < .0001) {box.y = 0} + if (box.y) {box = jsMath.Box.SetList([box],CFSH[2],size)} + return box; + }, + + /* + * Get a character by its TeX charcode, and make sure its width + * is specified. + */ + GetCharCode: function (code) { + var font = jsMath.TeX.fam[code[0]]; + var Font = jsMath.TeX[font]; + var c = Font[code[1]]; + if (c.img != null) {this.TeXIMG(font,code[1],4)} + if (c.w == null) {c.w = jsMath.EmBoxFor(jsMath.Typeset.AddClass(c.tclass,c.c)).w} + if (c.font == null) {c.font = font} + return c; + }, + + /* + * Add the class to the html, and use the font if there isn't one + * specified already + */ + + AddClass: function (tclass,html,font) { + if (tclass == null) {tclass = font} + return jsMath.Typeset.AddClass(tclass,html); + }, + + /* + * Create the HTML for an alignment (e.g., array or matrix) + * Since the widths are not really accurate (they are based on pixel + * widths not the sub-pixel widths of the actual characters), there + * is some drift involved. We lay out the table column by column + * to help reduce the problem. + * + * ### still need to allow users to specify row and column attributes, + * and do things like \span and \multispan ### + */ + LayoutRelative: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) { + if (align == null) {align = []} + if (cspacing == null) {cspacing = []} + if (rspacing == null) {rspacing = []} + if (useStrut == null) {useStrut = 1} + if (addWidth == null) {addWidth = 1} + + // get row and column maximum dimensions + var scale = jsMath.sizes[size]/100; + var W = []; var H = []; var D = []; + var unset = -1000; var bh = unset; var bd = unset; + var i; var j; var row; + for (i = 0; i < table.length; i++) { + if (rspacing[i] == null) {rspacing[i] = 0} + row = table[i]; + H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale; + for (j = 0; j < row.length; j++) { + row[j] = row[j].Remeasured(); + if (row[j].h > H[i]) {H[i] = row[j].h} + if (row[j].d > D[i]) {D[i] = row[j].d} + if (j >= W.length) {W[j] = row[j].w} + else if (row[j].w > W[j]) {W[j] = row[j].w} + if (row[j].bh > bh) {bh = row[j].bh} + if (row[j].bd > bd) {bd = row[j].bd} + } + } + if (rspacing[table.length] == null) {rspacing[table.length] = 0} + if (bh == unset) {bh = 0}; if (bd == unset) {bd = 0} + + // lay out the columns + var HD = useStrut*(jsMath.hd-.01)*scale; + var dy = (vspace || 1) * scale/6; + var html = ''; var pW = 0; var cW = 0; + var w; var h; var y; + var box; var mlist; var entry; + for (j = 0; j < W.length; j++) { + mlist = []; y = -H[0]-rspacing[0]; pW = 0; + for (i = 0; i < table.length; i++) { + entry = table[i][j]; + if (entry && entry.format != 'null') { + if (align[j] == 'l') {w = 0} else + if (align[j] == 'r') {w = W[j] - entry.w} else + {w = (W[j] - entry.w)/2} + entry.x = w - pW; pW = entry.w + w; entry.y = y; + mlist[mlist.length] = entry; + } + if (i+1 < table.length) {y -= Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]} + } + if (cspacing[j] == null) cspacing[j] = scale; + if (mlist.length > 0) { + box = jsMath.Box.SetList(mlist,'T',size); + html += jsMath.HTML.Place(box.html,cW,0); + cW = W[j] - box.w + cspacing[j]; + } else {cW += cspacing[j]} + } + + // get the full width and height + w = -cspacing[W.length-1]; y = (H.length-1)*dy + rspacing[0]; + for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]} + for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]} + h = y/2 + jsMath.TeX.axis_height; var d = y-h; + + // adjust the final row width, and vcenter the table + // (add 1/6em at each side for the \,) + html += jsMath.HTML.Spacer(cW-cspacing[W.length-1] + addWidth*scale/6); + html = jsMath.HTML.Place(html,addWidth*scale/6,h); + box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d); + box.bh = bh; box.bd = bd; + return box; + }, + + /* + * Create the HTML for an alignment (e.g., array or matrix) + * Use absolute position for elements in the array. + * + * ### still need to allow users to specify row and column attributes, + * and do things like \span and \multispan ### + */ + LayoutAbsolute: function (size,table,align,cspacing,rspacing,vspace,useStrut,addWidth) { + if (align == null) {align = []} + if (cspacing == null) {cspacing = []} + if (rspacing == null) {rspacing = []} + if (useStrut == null) {useStrut = 1} + if (addWidth == null) {addWidth = 1} + // get row and column maximum dimensions + var scale = jsMath.sizes[size]/100; + var HD = useStrut*(jsMath.hd-.01)*scale; + var dy = (vspace || 1) * scale/6; + var W = []; var H = []; var D = []; + var w = 0; var h; var x; var y; + var i; var j; var row; + for (i = 0; i < table.length; i++) { + if (rspacing[i] == null) {rspacing[i] = 0} + row = table[i]; + H[i] = useStrut*jsMath.h*scale; D[i] = useStrut*jsMath.d*scale; + for (j = 0; j < row.length; j++) { + row[j] = row[j].Remeasured(); + if (row[j].h > H[i]) {H[i] = row[j].h} + if (row[j].d > D[i]) {D[i] = row[j].d} + if (j >= W.length) {W[j] = row[j].w} + else if (row[j].w > W[j]) {W[j] = row[j].w} + } + } + if (rspacing[table.length] == null) {rspacing[table.length] = 0} + + // get the height and depth of the centered table + y = (H.length-1)*dy + rspacing[0]; + for (i = 0; i < H.length; i++) {y += Math.max(HD,H[i]+D[i]) + rspacing[i+1]} + h = y/2 + jsMath.TeX.axis_height; var d = y - h; + + // lay out the columns + var html = ''; var entry; w = addWidth*scale/6; + for (j = 0; j < W.length; j++) { + y = H[0]-h + rspacing[0]; + for (i = 0; i < table.length; i++) { + entry = table[i][j]; + if (entry && entry.format != 'null') { + if (align[j] && align[j] == 'l') {x = 0} else + if (align[j] && align[j] == 'r') {x = W[j] - entry.w} else + {x = (W[j] - entry.w)/2} + html += jsMath.HTML.PlaceAbsolute(entry.html,w+x, + y-Math.max(0,entry.bh-jsMath.h*scale), + entry.mw,entry.Mw,entry.w); + } + if (i+1 < table.length) {y += Math.max(HD,D[i]+H[i+1]) + dy + rspacing[i+1]} + } + if (cspacing[j] == null) cspacing[j] = scale; + w += W[j] + cspacing[j]; + } + + // get the full width + w = -cspacing[W.length-1]+addWidth*scale/3; + for (i = 0; i < W.length; i++) {w += W[i] + cspacing[i]} + + html = jsMath.HTML.Spacer(addWidth*scale/6)+html+jsMath.HTML.Spacer(addWidth*scale/6); + if (jsMath.Browser.spanHeightVaries) {y = h-jsMath.h} else {y = 0} + if (jsMath.Browser.msie8HeightBug) {y = d-jsMath.d} + html = jsMath.HTML.Absolute(html,w,h+d,d,y); + var box = new jsMath.Box('html',html,w+addWidth*scale/3,h,d); + return box; + }, + + /* + * Look for math within \hbox and other non-math text + */ + InternalMath: function (text,size) { + if (!jsMath.safeHBoxes) {text = text.replace(/@\(([^)]*)\)/g,'<$1>')} + if (!text.match(/\$|\\\(/)) + {return this.Text(this.safeHTML(text),'normal','T',size).Styled()} + + var i = 0; var k = 0; var c; var match = ''; + var mlist = []; var parse, s; + while (i < text.length) { + c = text.charAt(i++); + if (c == '$') { + if (match == '$') { + parse = jsMath.Parse(text.slice(k,i-1),null,size); + if (parse.error) { + mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2); + } else { + parse.Atomize(); + mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled(); + } + match = ''; k = i; + } else { + s = this.safeHTML(text.slice(k,i-1)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + match = '$'; k = i; + } + } else if (c == '\\') { + c = text.charAt(i++); + if (c == '(' && match == '') { + s = this.safeHTML(text.slice(k,i-2)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + match = ')'; k = i; + } else if (c == ')' && match == ')') { + parse = jsMath.Parse(text.slice(k,i-2),null,size); + if (parse.error) { + mlist[mlist.length] = this.Text(parse.error,'error','T',size,1,.2); + } else { + parse.Atomize(); + mlist[mlist.length] = parse.mlist.Typeset('T',size).Styled(); + } + match = ''; k = i; + } + } + } + s = this.safeHTML(text.slice(k)); + mlist[mlist.length] = this.Text(s,'normal','T',size,1,.2); + return this.SetList(mlist,'T',size); + }, + + /* + * Quote HTML characters if we are in safe mode + */ + safeHTML: function (s) { + if (jsMath.safeHBoxes) { + s = s.replace(/&/g,'&') + .replace(//g,'>'); + } + return s; + }, + + /* + * Convert an abitrary box to a typeset box. I.e., make an + * HTML version of the contents of the box, at its desired (x,y) + * position. + */ + Set: function (box,style,size,addstyle) { + if (box && box.type) { + if (box.type == 'typeset') {return box} + if (box.type == 'mlist') { + box.mlist.Atomize(style,size); + return box.mlist.Typeset(style,size); + } + if (box.type == 'text') { + box = this.Text(box.text,box.tclass,style,size,box.ascend||null,box.descend||null); + if (addstyle != 0) {box.Styled()} + return box; + } + box = this.TeX(box.c,box.font,style,size); + if (addstyle != 0) {box.Styled()} + return box; + } + return jsMath.Box.Null(); + }, + + /* + * Convert a list of boxes to a single typeset box. I.e., finalize + * the HTML for the list of boxes, properly spaced and positioned. + */ + SetList: function (boxes,style,size) { + var mlist = []; var box; + for (var i = 0; i < boxes.length; i++) { + box = boxes[i]; + if (box.type == 'typeset') {box = jsMath.mItem.Typeset(box)} + mlist[mlist.length] = box; + } + var typeset = new jsMath.Typeset(mlist); + return typeset.Typeset(style,size); + } + +}); + + +jsMath.Package(jsMath.Box,{ + + /* + * Add the class and style to a text box (i.e., finalize the + * unpositioned HTML for the box). + */ + Styled: function () { + if (this.format == 'text') { + this.html = jsMath.Typeset.AddClass(this.tclass,this.html); + this.html = jsMath.Typeset.AddStyle(this.style,this.size,this.html); + delete this.tclass; delete this.style; + this.format = 'html'; + } + return this; + }, + + /* + * Recompute the box width to make it more accurate. + */ + Remeasured: function () { + if (this.w > 0) { + var w = this.w; this.w = jsMath.EmBoxFor(this.html).w; + if (this.w > this.Mw) {this.Mw = this.w} + w = this.w/w; if (Math.abs(w-1) > .05) {this.h *= w; this.d *= w} + } + return this; + } + +}); + + +/***************************************************************************/ + +/* + * mItems are the building blocks of mLists (math lists) used to + * store the information about a mathematical expression. These are + * basically the items listed in the TeXbook in Appendix G (plus some + * minor extensions). + */ +jsMath.mItem = function (type,def) { + this.type = type; + jsMath.Add(this,def); +} + +jsMath.Add(jsMath.mItem,{ + + /* + * A general atom (given a nucleus for the atom) + */ + Atom: function (type,nucleus) { + return new jsMath.mItem(type,{atom: 1, nuc: nucleus}); + }, + + /* + * An atom whose nucleus is a piece of text, in a given + * class, with a given additional height and depth + */ + TextAtom: function (type,text,tclass,a,d) { + var atom = new jsMath.mItem(type,{ + atom: 1, + nuc: { + type: 'text', + text: text, + tclass: tclass + } + }); + if (a != null) {atom.nuc.ascend = a} + if (d != null) {atom.nuc.descend = d} + return atom; + }, + + /* + * An atom whose nucleus is a TeX character in a specific font + */ + TeXAtom: function (type,c,font) { + return new jsMath.mItem(type,{ + atom: 1, + nuc: { + type: 'TeX', + c: c, + font: font + } + }); + }, + + /* + * A generalized fraction atom, with given delimiters, rule + * thickness, and a numerator and denominator. + */ + Fraction: function (name,num,den,thickness,left,right) { + return new jsMath.mItem('fraction',{ + from: name, num: num, den: den, + thickness: thickness, left: left, right: right + }); + }, + + /* + * An atom that inserts some glue + */ + Space: function (w) {return new jsMath.mItem('space',{w: w})}, + + /* + * An atom that contains a typeset box (like an hbox or vbox) + */ + Typeset: function (box) {return new jsMath.mItem('ord',{atom:1, nuc: box})}, + + /* + * An atom that contains some finished HTML (acts like a typeset box) + */ + HTML: function (html) {return new jsMath.mItem('html',{html: html})} + +}); + +/***************************************************************************/ + +/* + * mLists are lists of mItems, and encode the contents of + * mathematical expressions and sub-expressions. They act as + * the expression "stack" as the mathematics is parsed, and + * contain some state information, like the position of the + * most recent open paren and \over command, and the current font. + */ +jsMath.mList = function (list,font,size,style) { + if (list) {this.mlist = list} else {this.mlist = []} + if (style == null) {style = 'T'}; if (size == null) {size = 4} + this.data = {openI: null, overI: null, overF: null, + font: font, size: size, style: style}; + this.init = {size: size, style: style}; +} + +jsMath.Package(jsMath.mList,{ + + /* + * Add an mItem to the list + */ + Add: function (box) {return (this.mlist[this.mlist.length] = box)}, + + /* + * Get the i-th mItem from the list + */ + Get: function (i) {return this.mlist[i]}, + + /* + * Get the length of the list + */ + Length: function() {return this.mlist.length}, + + /* + * Get the tail mItem of the list + */ + Last: function () { + if (this.mlist.length == 0) {return null} + return this.mlist[this.mlist.length-1] + }, + + /* + * Get a sublist of an mList + */ + Range: function (i,j) { + if (j == null) {j = this.mlist.length} + return new jsMath.mList(this.mlist.slice(i,j+1)); + }, + + /* + * Remove a range of mItems from the list. + */ + Delete: function (i,j) { + if (j == null) {j = i} + if (this.mlist.splice) {this.mlist.splice(i,j-i+1)} else { + var mlist = []; + for (var k = 0; k < this.mlist.length; k++) + {if (k < i || k > j) {mlist[mlist.length] = this.mlist[k]}} + this.mlist = mlist; + } + }, + + /* + * Add an open brace and maintain the stack information + * about the previous open brace so we can recover it + * when this one os closed. + */ + Open: function (left) { + var box = this.Add(new jsMath.mItem('boundary',{data: this.data})); + var olddata = this.data; + this.data = {}; for (var i in olddata) {this.data[i] = olddata[i]} + delete this.data.overI; delete this.data.overF; + this.data.openI = this.mlist.length-1; + if (left != null) {box.left = left} + return box; + }, + + /* + * Attempt to close a brace. Recover the stack information + * about previous open braces and \over commands. If there was an + * \over (or \above, etc) in this set of braces, create a fraction + * atom from the two halves, otherwise create an inner or ord + * from the contents of the braces. + * Remove the braced material from the list and add the newly + * created atom (the fraction, inner or ord). + */ + Close: function (right) { + if (right != null) {right = new jsMath.mItem('boundary',{right: right})} + var atom; var open = this.data.openI; + var over = this.data.overI; var from = this.data.overF; + this.data = this.mlist[open].data; + if (over) { + atom = jsMath.mItem.Fraction(from.name, + {type: 'mlist', mlist: this.Range(open+1,over-1)}, + {type: 'mlist', mlist: this.Range(over)}, + from.thickness,from.left,from.right); + if (right) { + var mlist = new jsMath.mList([this.mlist[open],atom,right]); + atom = jsMath.mItem.Atom('inner',{type: 'mlist', mlist: mlist}); + } + } else { + var openI = open+1; if (right) {this.Add(right); openI--} + atom = jsMath.mItem.Atom((right)?'inner':'ord', + {type: 'mlist', mlist: this.Range(openI)}); + } + this.Delete(open,this.Length()); + return this.Add(atom); + }, + + /* + * Create a generalized fraction from an mlist that + * contains an \over (or \above, etc). + */ + Over: function () { + var over = this.data.overI; var from = this.data.overF; + var atom = jsMath.mItem.Fraction(from.name, + {type: 'mlist', mlist: this.Range(open+1,over-1)}, + {type: 'mlist', mlist: this.Range(over)}, + from.thickness,from.left,from.right); + this.mlist = [atom]; + }, + + /* + * Take a raw mList (that has been produced by parsing some TeX + * expression), and perform the modifications outlined in + * Appendix G of the TeXbook. + */ + Atomize: function (style,size) { + var mitem; var prev = ''; + this.style = style; this.size = size; + for (var i = 0; i < this.mlist.length; i++) { + mitem = this.mlist[i]; mitem.delta = 0; + if (mitem.type == 'choice') + {this.mlist = this.Atomize.choice(this.style,mitem,i,this.mlist); i--} + else if (this.Atomize[mitem.type]) { + var f = this.Atomize[mitem.type]; // Opera needs separate name + f(this.style,this.size,mitem,prev,this,i); + } + prev = mitem; + } + if (mitem && mitem.type == 'bin') {mitem.type = 'ord'} + if (this.mlist.length >= 2 && mitem.type == 'boundary' && + this.mlist[0].type == 'boundary') {this.AddDelimiters(style,size)} + }, + + /* + * For a list that has boundary delimiters as its first and last + * entries, we replace the boundary atoms by open and close + * atoms whose nuclii are the specified delimiters properly sized + * for the contents of the list. (Rule 19) + */ + AddDelimiters: function(style,size) { + var unset = -10000; var h = unset; var d = unset; + for (var i = 0; i < this.mlist.length; i++) { + var mitem = this.mlist[i]; + if (mitem.atom || mitem.type == 'box') { + h = Math.max(h,mitem.nuc.h+mitem.nuc.y); + d = Math.max(d,mitem.nuc.d-mitem.nuc.y); + } + } + var TeX = jsMath.TeX; var a = jsMath.Typeset.TeX(style,size).axis_height; + var delta = Math.max(h-a,d+a); + var H = Math.max(Math.floor(TeX.integer*delta/500)*TeX.delimiterfactor, + TeX.integer*(2*delta-TeX.delimitershortfall))/TeX.integer; + var left = this.mlist[0]; var right = this.mlist[this.mlist.length-1]; + left.nuc = jsMath.Box.Delimiter(H,left.left,style); + right.nuc = jsMath.Box.Delimiter(H,right.right,style); + left.type = 'open'; left.atom = 1; delete left.left; + right.type = 'close'; right.atom = 1; delete right.right; + }, + + /* + * Typeset a math list to produce final HTML for the list. + */ + Typeset: function (style,size) { + var typeset = new jsMath.Typeset(this.mlist); + return typeset.Typeset(style,size); + } + +}); + + +/* + * These routines implement the main rules given in Appendix G of the + * TeXbook + */ + +jsMath.Add(jsMath.mList.prototype.Atomize,{ + + /* + * Handle \displaystyle, \textstyle, etc. + */ + style: function (style,size,mitem,prev,mlist) { + mlist.style = mitem.style; + }, + + /* + * Handle \tiny, \small, etc. + */ + size: function (style,size,mitem,prev,mlist) { + mlist.size = mitem.size; + }, + + /* + * Create empty boxes of the proper sizes for the various + * phantom-type commands + */ + phantom: function (style,size,mitem) { + var box = mitem.nuc = jsMath.Box.Set(mitem.phantom,style,size); + if (mitem.h) {box.Remeasured(); box.html = jsMath.HTML.Spacer(box.w)} + else {box.html = '', box.w = box.Mw = box.mw = 0;} + if (!mitem.v) {box.h = box.d = 0} + box.bd = box.bh = 0; + delete mitem.phantom; + mitem.type = 'box'; + }, + + /* + * Create a box of zero height and depth containing the + * contents of the atom + */ + smash: function (style,size,mitem) { + var box = mitem.nuc = jsMath.Box.Set(mitem.smash,style,size).Remeasured(); + box.h = box.d = 0; + delete mitem.smash; + mitem.type = 'box'; + }, + + /* + * Move a box up or down vertically + */ + raise: function (style,size,mitem) { + mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size); + var y = mitem.raise; + mitem.nuc.html = + jsMath.HTML.Place(mitem.nuc.html,0,y,mitem.nuc.mw,mitem.nuc.Mw,mitem.nuc.w); + mitem.nuc.h += y; mitem.nuc.d -= y; + mitem.type = 'ord'; mitem.atom = 1; + }, + + /* + * Hide the size of a box so that it laps to the left or right, or + * up or down. + */ + lap: function (style,size,mitem) { + var box = jsMath.Box.Set(mitem.nuc,style,size).Remeasured(); + var mlist = [box]; + if (mitem.lap == 'llap') {box.x = -box.w} else + if (mitem.lap == 'rlap') {mlist[1] = jsMath.mItem.Space(-box.w)} else + if (mitem.lap == 'ulap') {box.y = box.d; box.h = box.d = 0} else + if (mitem.lap == 'dlap') {box.y = -box.h; box.h = box.d = 0} + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + if (mitem.lap == 'ulap' || mitem.lap == 'dlap') {mitem.nuc.h = mitem.nuc.d = 0} + mitem.type = 'box'; delete mitem.atom; + }, + + /* + * Handle a Bin atom. (Rule 5) + */ + bin: function (style,size,mitem,prev) { + if (prev && prev.type) { + var type = prev.type; + if (type == 'bin' || type == 'op' || type == 'rel' || + type == 'open' || type == 'punct' || type == '' || + (type == 'boundary' && prev.left != '')) {mitem.type = 'ord'} + } else {mitem.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Rel atom. (Rule 6) + */ + rel: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Close atom. (Rule 6) + */ + close: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Punct atom. (Rule 6) + */ + punct: function (style,size,mitem,prev) { + if (prev.type && prev.type == 'bin') {prev.type = 'ord'} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Open atom. (Rule 7) + */ + open: function (style,size,mitem) { + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Inner atom. (Rule 7) + */ + inner: function (style,size,mitem) { + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Vcent atom. (Rule 8) + */ + vcenter: function (style,size,mitem) { + var box = jsMath.Box.Set(mitem.nuc,style,size); + var TeX = jsMath.Typeset.TeX(style,size); + box.y = TeX.axis_height - (box.h-box.d)/2; + mitem.nuc = box; mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Over atom. (Rule 9) + */ + overline: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured(); + var t = TeX.default_rule_thickness; + var rule = jsMath.Box.Rule(box.w,t); + rule.x = -rule.w; rule.y = box.h + 3*t; + mitem.nuc = jsMath.Box.SetList([box,rule],style,size); + mitem.nuc.h += t; + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Under atom. (Rule 10) + */ + underline: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var box = jsMath.Box.Set(mitem.nuc,jsMath.Typeset.PrimeStyle(style),size).Remeasured(); + var t = TeX.default_rule_thickness; + var rule = jsMath.Box.Rule(box.w,t); + rule.x = -rule.w; rule.y = -box.d - 3*t - t; + mitem.nuc = jsMath.Box.SetList([box,rule],style,size); + mitem.nuc.d += t; + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a Rad atom. (Rule 11 plus stuff for \root..\of) + */ + radical: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var Cp = jsMath.Typeset.PrimeStyle(style); + var box = jsMath.Box.Set(mitem.nuc,Cp,size).Remeasured(); + var t = TeX.default_rule_thickness; + var p = t; if (style == 'D' || style == "D'") {p = TeX.x_height} + var r = t + p/4; + var surd = jsMath.Box.Delimiter(box.h+box.d+r+t,[0,2,0x70,3,0x70],style,1); +// if (surd.h > 0) {t = surd.h} // thickness of rule is height of surd character + if (surd.d > box.h+box.d+r) {r = (r+surd.d-box.h-box.d)/2} + surd.y = box.h+r; + var rule = jsMath.Box.Rule(box.w,t); + rule.y = surd.y-t/2; rule.h += 3*t/2; box.x = -box.w; + var Cr = jsMath.Typeset.UpStyle(jsMath.Typeset.UpStyle(style)); + var root = jsMath.Box.Set(mitem.root || null,Cr,size).Remeasured(); + if (mitem.root) { + root.y = .55*(box.h+box.d+3*t+r)-box.d; + surd.x = Math.max(root.w-(11/18)*surd.w,0); + rule.x = (7/18)*surd.w; + root.x = -(root.w+rule.x); + } + mitem.nuc = jsMath.Box.SetList([surd,root,rule,box],style,size); + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Acc atom. (Rule 12) + */ + accent: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var Cp = jsMath.Typeset.PrimeStyle(style); + var box = jsMath.Box.Set(mitem.nuc,Cp,size); + var u = box.w; var s; var Font; var ic = 0; + if (mitem.nuc.type == 'TeX') { + Font = jsMath.TeX[mitem.nuc.font]; + if (Font[mitem.nuc.c].krn && Font.skewchar) + {s = Font[mitem.nuc.c].krn[Font.skewchar]} + ic = Font[mitem.nuc.c].ic; if (ic == null) {ic = 0} + } + if (s == null) {s = 0} + + var c = mitem.accent[2]; + var font = jsMath.TeX.fam[mitem.accent[1]]; Font = jsMath.TeX[font]; + while (Font[c].n && Font[Font[c].n].w <= u) {c = Font[c].n} + + var delta = Math.min(box.h,TeX.x_height); + if (mitem.nuc.type == 'TeX') { + var nitem = jsMath.mItem.Atom('ord',mitem.nuc); + nitem.sup = mitem.sup; nitem.sub = mitem.sub; nitem.delta = 0; + jsMath.mList.prototype.Atomize.SupSub(style,size,nitem); + delta += (nitem.nuc.h - box.h); + box = mitem.nuc = nitem.nuc; + delete mitem.sup; delete mitem.sub; + } + var acc = jsMath.Box.TeX(c,font,style,size); + acc.y = box.h - delta; acc.x = -box.w + s + (u-acc.w)/2; + if (jsMath.Browser.msieAccentBug) + {acc.html += jsMath.HTML.Spacer(.1); acc.w += .1; acc.Mw += .1} + if (Font[c].ic || ic) {acc.x += (ic - (Font[c].ic||0)) * TeX.scale} + + mitem.nuc = jsMath.Box.SetList([box,acc],style,size); + if (mitem.nuc.w != box.w) { + var space = jsMath.mItem.Space(box.w-mitem.nuc.w); + mitem.nuc = jsMath.Box.SetList([mitem.nuc,space],style,size); + } + mitem.type = 'ord'; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle an Op atom. (Rules 13 and 13a) + */ + op: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); var box; + mitem.delta = 0; var isD = (style.charAt(0) == 'D'); + if (mitem.limits == null && isD) {mitem.limits = 1} + + if (mitem.nuc.type == 'TeX') { + var C = jsMath.TeX[mitem.nuc.font][mitem.nuc.c]; + if (isD && C.n) {mitem.nuc.c = C.n; C = jsMath.TeX[mitem.nuc.font][C.n]} + box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size); + if (C.ic) { + mitem.delta = C.ic * TeX.scale; + if (mitem.limits || !mitem.sub || jsMath.Browser.msieIntegralBug) { + box = mitem.nuc = jsMath.Box.SetList([box,jsMath.mItem.Space(mitem.delta)],style,size); + } + } + box.y = -((box.h+box.d)/2 - box.d - TeX.axis_height); + if (Math.abs(box.y) < .0001) {box.y = 0} + } + + if (!box) {box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size).Remeasured()} + if (mitem.limits) { + var W = box.w; var x = box.w; + var mlist = [box]; var dh = 0; var dd = 0; + if (mitem.sup) { + var sup = jsMath.Box.Set(mitem.sup,jsMath.Typeset.UpStyle(style),size).Remeasured(); + sup.x = ((box.w-sup.w)/2 + mitem.delta/2) - x; dh = TeX.big_op_spacing5; + W = Math.max(W,sup.w); x += sup.x + sup.w; + sup.y = box.h+sup.d + box.y + + Math.max(TeX.big_op_spacing1,TeX.big_op_spacing3-sup.d); + mlist[mlist.length] = sup; delete mitem.sup; + } + if (mitem.sub) { + var sub = jsMath.Box.Set(mitem.sub,jsMath.Typeset.DownStyle(style),size).Remeasured(); + sub.x = ((box.w-sub.w)/2 - mitem.delta/2) - x; dd = TeX.big_op_spacing5; + W = Math.max(W,sub.w); x += sub.x + sub.w; + sub.y = -box.d-sub.h + box.y - + Math.max(TeX.big_op_spacing2,TeX.big_op_spacing4-sub.h); + mlist[mlist.length] = sub; delete mitem.sub; + } + if (W > box.w) {box.x = (W-box.w)/2; x += box.x} + if (x < W) {mlist[mlist.length] = jsMath.mItem.Space(W-x)} + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + mitem.nuc.h += dh; mitem.nuc.d += dd; + } else { + if (jsMath.Browser.msieIntegralBug && mitem.sub && C && C.ic) + {mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(-C.ic*TeX.scale)],style,size)} + else if (box.y) {mitem.nuc = jsMath.Box.SetList([box],style,size)} + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + } + }, + + /* + * Handle an Ord atom. (Rule 14) + */ + ord: function (style,size,mitem,prev,mList,i) { + if (mitem.nuc.type == 'TeX' && !mitem.sup && !mitem.sub) { + var nitem = mList.mlist[i+1]; + if (nitem && nitem.atom && nitem.type && + (nitem.type == 'ord' || nitem.type == 'op' || nitem.type == 'bin' || + nitem.type == 'rel' || nitem.type == 'open' || + nitem.type == 'close' || nitem.type == 'punct')) { + if (nitem.nuc.type == 'TeX' && nitem.nuc.font == mitem.nuc.font) { + mitem.textsymbol = 1; + var krn = jsMath.TeX[mitem.nuc.font][mitem.nuc.c].krn; + krn *= jsMath.Typeset.TeX(style,size).scale; + if (krn && krn[nitem.nuc.c]) { + for (var k = mList.mlist.length-1; k > i; k--) + {mList.mlist[k+1] = mList.mlist[k]} + mList.mlist[i+1] = jsMath.mItem.Space(krn[nitem.nuc.c]); + } + } + } + } + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Handle a generalized fraction. (Rules 15 to 15e) + */ + fraction: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); var t = 0; + if (mitem.thickness != null) {t = mitem.thickness} + else if (mitem.from.match(/over/)) {t = TeX.default_rule_thickness} + var isD = (style.charAt(0) == 'D'); + var Cn = (style == 'D')? 'T': (style == "D'")? "T'": jsMath.Typeset.UpStyle(style); + var Cd = (isD)? "T'": jsMath.Typeset.DownStyle(style); + var num = jsMath.Box.Set(mitem.num,Cn,size).Remeasured(); + var den = jsMath.Box.Set(mitem.den,Cd,size).Remeasured(); + + var u; var v; var w; var p; var r; + var H = (isD)? TeX.delim1 : TeX.delim2; + var mlist = [jsMath.Box.Delimiter(H,mitem.left,style)] + var right = jsMath.Box.Delimiter(H,mitem.right,style); + + if (num.w < den.w) { + num.x = (den.w-num.w)/2; + den.x = -(num.w + num.x); + w = den.w; mlist[1] = num; mlist[2] = den; + } else { + den.x = (num.w-den.w)/2; + num.x = -(den.w + den.x); + w = num.w; mlist[1] = den; mlist[2] = num; + } + if (isD) {u = TeX.num1; v = TeX.denom1} else { + u = (t != 0)? TeX.num2: TeX.num3; + v = TeX.denom2; + } + if (t == 0) {// atop + p = (isD)? 7*TeX.default_rule_thickness: 3*TeX.default_rule_thickness; + r = (u - num.d) - (den.h - v); + if (r < p) {u += (p-r)/2; v += (p-r)/2} + } else {// over + p = (isD)? 3*t: t; var a = TeX.axis_height; + r = (u-num.d)-(a+t/2); if (r < p) {u += p-r} + r = (a-t/2)-(den.h-v); if (r < p) {v += p-r} + var rule = jsMath.Box.Rule(w,t); rule.x = -w; rule.y = a - t/2; + mlist[mlist.length] = rule; + } + num.y = u; den.y = -v; + + mlist[mlist.length] = right; + mitem.nuc = jsMath.Box.SetList(mlist,style,size); + mitem.type = 'ord'; mitem.atom = 1; + delete mitem.num; delete mitem.den; + jsMath.mList.prototype.Atomize.SupSub(style,size,mitem); + }, + + /* + * Add subscripts and superscripts. (Rules 17-18f) + */ + SupSub: function (style,size,mitem) { + var TeX = jsMath.Typeset.TeX(style,size); + var nuc = mitem.nuc; + var box = mitem.nuc = jsMath.Box.Set(mitem.nuc,style,size,0); + if (box.format == 'null') + {box = mitem.nuc = jsMath.Box.Text('','normal',style,size)} + + if (nuc.type == 'TeX') { + if (!mitem.textsymbol) { + var C = jsMath.TeX[nuc.font][nuc.c]; + if (C.ic) { + mitem.delta = C.ic * TeX.scale; + if (!mitem.sub) { + box = mitem.nuc = jsMath.Box.SetList([box,jsMath.Box.Space(mitem.delta)],style,size); + mitem.delta = 0; + } + } + } else {mitem.delta = 0} + } + + if (!mitem.sup && !mitem.sub) return; + mitem.nuc.Styled(); + + var Cd = jsMath.Typeset.DownStyle(style); + var Cu = jsMath.Typeset.UpStyle(style); + var q = jsMath.Typeset.TeX(Cu,size).sup_drop; + var r = jsMath.Typeset.TeX(Cd,size).sub_drop; + var u = 0; var v = 0; var p; + if (nuc.type && nuc.type != 'text' && nuc.type != 'TeX' && nuc.type != 'null') + {u = box.h - q; v = box.d + r} + + if (mitem.sub) { + var sub = jsMath.Box.Set(mitem.sub,Cd,size); + sub = jsMath.Box.SetList([sub,jsMath.mItem.Space(TeX.scriptspace)],style,size); + } + + if (!mitem.sup) { + sub.y = -Math.max(v,TeX.sub1,sub.h-(4/5)*jsMath.Typeset.TeX(Cd,size).x_height); + mitem.nuc = jsMath.Box.SetList([box,sub],style,size).Styled(); delete mitem.sub; + return; + } + + var sup = jsMath.Box.Set(mitem.sup,Cu,size); + sup = jsMath.Box.SetList([sup,jsMath.mItem.Space(TeX.scriptspace)],style,size); + if (style == 'D') {p = TeX.sup1} + else if (style.charAt(style.length-1) == "'") {p = TeX.sup3} + else {p = TeX.sup2} + u = Math.max(u,p,sup.d+jsMath.Typeset.TeX(Cu,size).x_height/4); + + if (!mitem.sub) { + sup.y = u; + mitem.nuc = jsMath.Box.SetList([box,sup],style,size); delete mitem.sup; + return; + } + + v = Math.max(v,jsMath.Typeset.TeX(Cd,size).sub2); + var t = TeX.default_rule_thickness; + if ((u-sup.d) - (sub.h -v) < 4*t) { + v = 4*t + sub.h - (u-sup.d); + p = (4/5)*TeX.x_height - (u-sup.d); + if (p > 0) {u += p; v -= p} + } + sup.Remeasured(); sub.Remeasured(); + sup.y = u; sub.y = -v; sup.x = mitem.delta; + if (sup.w+sup.x > sub.w) + {sup.x -= sub.w; mitem.nuc = jsMath.Box.SetList([box,sub,sup],style,size)} else + {sub.x -= (sup.w+sup.x); mitem.nuc = jsMath.Box.SetList([box,sup,sub],style,size)} + + delete mitem.sup; delete mitem.sub; + } + +}); + + +/***************************************************************************/ + +/* + * The Typeset object handles most of the TeX-specific processing + */ + +jsMath.Typeset = function (mlist) { + this.type = 'typeset'; + this.mlist = mlist; +} + +jsMath.Add(jsMath.Typeset,{ + + /* + * The "C-uparrow" style table (TeXbook, p. 441) + */ + upStyle: { + D: "S", T: "S", "D'": "S'", "T'": "S'", + S: "SS", SS: "SS", "S'": "SS'", "SS'": "SS'" + }, + + /* + * The "C-downarrow" style table (TeXbook, p. 441) + */ + downStyle: { + D: "S'", T: "S'", "D'": "S'", "T'": "S'", + S: "SS'", SS: "SS'", "S'": "SS'", "SS'": "SS'" + }, + + /* + * Get the various styles given the current style + * (see TeXbook, p. 441) + */ + UpStyle: function (style) {return this.upStyle[style]}, + DownStyle: function (style) {return this.downStyle[style]}, + PrimeStyle: function (style) { + if (style.charAt(style.length-1) == "'") {return style} + return style + "'" + }, + + /* + * A value scaled to the appropriate size for scripts + */ + StyleValue: function (style,v) { + if (style == "S" || style == "S'") {return .7*v} + if (style == "SS" || style == "SS'") {return .5*v} + return v; + }, + + /* + * Return the size associated with a given style and size + */ + StyleSize: function (style,size) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + return size; + }, + + /* + * Return the font parameter table for the given style + */ + TeX: function (style,size) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + return jsMath.TeXparams[size]; + }, + + + /* + * Add the CSS class for the given TeX style + */ + AddStyle: function (style,size,html) { + if (style == "S" || style == "S'") {size = Math.max(0,size-2)} + else if (style == "SS" || style == "SS'") {size = Math.max(0,size-4)} + if (size != 4) {html = '' + html + ''} + return html; + }, + + /* + * Add the font class, if needed + */ + AddClass: function (tclass,html) { + if (tclass != '' && tclass != 'normal') {html = jsMath.HTML.Class(tclass,html)} + return html; + } + +}); + + +jsMath.Package(jsMath.Typeset,{ + + /* + * The spacing tables for inter-atom spacing + * (See rule 20, and Chapter 18, p 170) + */ + DTsep: { + ord: {op: 1, bin: 2, rel: 3, inner: 1}, + op: {ord: 1, op: 1, rel: 3, inner: 1}, + bin: {ord: 2, op: 2, open: 2, inner: 2}, + rel: {ord: 3, op: 3, open: 3, inner: 3}, + open: {}, + close: {op: 1, bin:2, rel: 3, inner: 1}, + punct: {ord: 1, op: 1, rel: 1, open: 1, close: 1, punct: 1, inner: 1}, + inner: {ord: 1, op: 1, bin: 2, rel: 3, open: 1, punct: 1, inner: 1} + }, + + SSsep: { + ord: {op: 1}, + op: {ord: 1, op: 1}, + bin: {}, + rel: {}, + open: {}, + close: {op: 1}, + punct: {}, + inner: {op: 1} + }, + + /* + * The sizes used in the tables above + */ + sepW: ['','thinmuskip','medmuskip','thickmuskip'], + + + /* + * Find the amount of separation to use between two adjacent + * atoms in the given style + */ + GetSeparation: function (l,r,style) { + if (l && l.atom && r.atom) { + var table = this.DTsep; if (style.charAt(0) == "S") {table = this.SSsep} + var row = table[l.type]; + if (row && row[r.type] != null) {return jsMath.TeX[this.sepW[row[r.type]]]} + } + return 0; + }, + + /* + * Typeset an mlist (i.e., turn it into HTML). + * Here, text items of the same class and style are combined + * to reduce the number of tags used (though it is still + * huge). Spaces are combined, when possible. + * ### More needs to be done with that. ### + * The width of the final box is recomputed at the end, since + * the final width is not necessarily the sum of the widths of + * the individual parts (widths are in pixels, but the browsers + * puts pieces together using sub-pixel accuracy). + */ + Typeset: function (style,size) { + this.style = style; this.size = size; var unset = -10000 + this.w = 0; this.mw = 0; this.Mw = 0; + this.h = unset; this.d = unset; + this.bh = this.h; this.bd = this.d; + this.tbuf = ''; this.tx = 0; this.tclass = ''; + this.cbuf = ''; this.hbuf = ''; this.hx = 0; + var mitem = null; var prev; this.x = 0; this.dx = 0; + + for (var i = 0; i < this.mlist.length; i++) { + prev = mitem; mitem = this.mlist[i]; + switch (mitem.type) { + + case 'size': + this.FlushClassed(); + this.size = mitem.size; + mitem = prev; // hide this from TeX + break; + + case 'style': + this.FlushClassed(); + if (this.style.charAt(this.style.length-1) == "'") + {this.style = mitem.style + "'"} else {this.style = mitem.style} + mitem = prev; // hide this from TeX + break; + + case 'space': + if (typeof(mitem.w) == 'object') { + if (this.style.charAt(1) == 'S') {mitem.w = .5*mitem.w[0]/18} + else if (this.style.charAt(0) == 'S') {mitem.w = .7*mitem.w[0]/18} + else {mitem.w = mitem.w[0]/18} + } + this.dx += mitem.w-0; // mitem.w is sometimes a string? + mitem = prev; // hide this from TeX + break; + + case 'html': + this.FlushClassed(); + if (this.hbuf == '') {this.hx = this.x} + this.hbuf += mitem.html; + mitem = prev; // hide this from TeX + break; + + default: // atom + if (!mitem.atom && mitem.type != 'box') break; + mitem.nuc.x += this.dx + this.GetSeparation(prev,mitem,this.style); + if (mitem.nuc.x || mitem.nuc.y) mitem.nuc.Styled(); + this.dx = 0; this.x = this.x + this.w; + if (this.x + mitem.nuc.x + mitem.nuc.mw < this.mw) + {this.mw = this.x + mitem.nuc.x + mitem.nuc.mw} + if (this.w + mitem.nuc.x + mitem.nuc.Mw > this.Mw) + {this.Mw = this.w + mitem.nuc.x + mitem.nuc.Mw} + this.w += mitem.nuc.w + mitem.nuc.x; + if (mitem.nuc.format == 'text') { + if (this.tclass != mitem.nuc.tclass && this.tclass != '') this.FlushText(); + if (this.tbuf == '' && this.cbuf == '') {this.tx = this.x} + this.tbuf += mitem.nuc.html; this.tclass = mitem.nuc.tclass; + } else { + this.FlushClassed(); + if (mitem.nuc.x || mitem.nuc.y) this.Place(mitem.nuc); + if (this.hbuf == '') {this.hx = this.x} + this.hbuf += mitem.nuc.html; + } + this.h = Math.max(this.h,mitem.nuc.h+mitem.nuc.y); this.bh = Math.max(this.bh,mitem.nuc.bh); + this.d = Math.max(this.d,mitem.nuc.d-mitem.nuc.y); this.bd = Math.max(this.bd,mitem.nuc.bd); + break; + } + } + + this.FlushClassed(); // make sure scaling is included + if (this.dx) { + this.hbuf += jsMath.HTML.Spacer(this.dx); this.w += this.dx; + if (this.w > this.Mw) {this.Mw = this.w} + if (this.w < this.mw) {this.mw = this.w} + } + if (this.hbuf == '') {return jsMath.Box.Null()} + if (this.h == unset) {this.h = 0} + if (this.d == unset) {this.d = 0} + var box = new jsMath.Box('html',this.hbuf,this.w,this.h,this.d); + box.bh = this.bh; box.bd = this.bd; + box.mw = this.mw; box.Mw = this.Mw; + return box; + }, + + /* + * Add the font to the buffered text and move it to the + * classed-text buffer. + */ + FlushText: function () { + if (this.tbuf == '') return; + this.cbuf += jsMath.Typeset.AddClass(this.tclass,this.tbuf); + this.tbuf = ''; this.tclass = ''; + }, + + /* + * Add the script or scriptscript style to the text and + * move it to the HTML buffer + */ + FlushClassed: function () { + this.FlushText(); + if (this.cbuf == '') return; + if (this.hbuf == '') {this.hx = this.tx} + this.hbuf += jsMath.Typeset.AddStyle(this.style,this.size,this.cbuf); + this.cbuf = ''; + }, + + /* + * Add a to position an item's HTML, and + * adjust the item's height and depth. + * (This may be replaced buy one of the following browser-specific + * versions by Browser.Init().) + */ + Place: function (item) { + var html = '' + item.html + ''; + item.h += item.y; item.d -= item.y; + item.x = 0; item.y = 0; + }, + + /* + * For MSIE on Windows, backspacing must be done in a separate + * , otherwise the contents will be clipped. Netscape + * also doesn't combine vertical and horizontal spacing well. + * Here, the horizontal and vertical spacing are done separately. + */ + + PlaceSeparateSkips: function (item) { + if (item.y) { + var rw = item.Mw - item.w; var lw = item.mw; + var W = item.Mw - item.mw; + item.html = + jsMath.HTML.Spacer(lw-rw) + + '' + + jsMath.HTML.Spacer(-lw) + + item.html + + jsMath.HTML.Spacer(rw) + + '' + } + if (item.x) {item.html = jsMath.HTML.Spacer(item.x) + item.html} + item.h += item.y; item.d -= item.y; + item.x = 0; item.y = 0; + } + +}); + + + +/***************************************************************************/ + +/* + * The Parse object handles the parsing of the TeX input string, and creates + * the mList to be typeset by the Typeset object above. + */ + +jsMath.Parse = function (s,font,size,style) { + var parse = new jsMath.Parser(s,font,size,style); + parse.Parse(); + return parse; +} + +jsMath.Parser = function (s,font,size,style) { + this.string = s; this.i = 0; + this.mlist = new jsMath.mList(null,font,size,style); +} + +jsMath.Package(jsMath.Parser,{ + + // special characters + cmd: '\\', + open: '{', + close: '}', + + // patterns for letters and numbers + letter: /[a-z]/i, + number: /[0-9]/, + // pattern for macros to ^ and _ that should be read with arguments + scriptargs: /^((math|text)..|mathcal|[hm]box)$/, + + // the \mathchar definitions (see Appendix B of the TeXbook). + mathchar: { + '!': [5,0,0x21], + '(': [4,0,0x28], + ')': [5,0,0x29], + '*': [2,2,0x03], // \ast + '+': [2,0,0x2B], + ',': [6,1,0x3B], + '-': [2,2,0x00], + '.': [0,1,0x3A], + '/': [0,1,0x3D], + ':': [3,0,0x3A], + ';': [6,0,0x3B], + '<': [3,1,0x3C], + '=': [3,0,0x3D], + '>': [3,1,0x3E], + '?': [5,0,0x3F], + '[': [4,0,0x5B], + ']': [5,0,0x5D], +// '{': [4,2,0x66], +// '}': [5,2,0x67], + '|': [0,2,0x6A] + }, + + // handle special \catcode characters + special: { + '~': 'Tilde', + '^': 'HandleSuperscript', + '_': 'HandleSubscript', + ' ': 'Space', + '\01': 'Space', + "\t": 'Space', + "\r": 'Space', + "\n": 'Space', + "'": 'Prime', + '%': 'HandleComment', + '&': 'HandleEntry', + '#': 'Hash' + }, + + // the \mathchardef table (see Appendix B of the TeXbook). + mathchardef: { + // brace parts + braceld: [0,3,0x7A], + bracerd: [0,3,0x7B], + bracelu: [0,3,0x7C], + braceru: [0,3,0x7D], + + // Greek letters + alpha: [0,1,0x0B], + beta: [0,1,0x0C], + gamma: [0,1,0x0D], + delta: [0,1,0x0E], + epsilon: [0,1,0x0F], + zeta: [0,1,0x10], + eta: [0,1,0x11], + theta: [0,1,0x12], + iota: [0,1,0x13], + kappa: [0,1,0x14], + lambda: [0,1,0x15], + mu: [0,1,0x16], + nu: [0,1,0x17], + xi: [0,1,0x18], + pi: [0,1,0x19], + rho: [0,1,0x1A], + sigma: [0,1,0x1B], + tau: [0,1,0x1C], + upsilon: [0,1,0x1D], + phi: [0,1,0x1E], + chi: [0,1,0x1F], + psi: [0,1,0x20], + omega: [0,1,0x21], + varepsilon: [0,1,0x22], + vartheta: [0,1,0x23], + varpi: [0,1,0x24], + varrho: [0,1,0x25], + varsigma: [0,1,0x26], + varphi: [0,1,0x27], + + Gamma: [7,0,0x00], + Delta: [7,0,0x01], + Theta: [7,0,0x02], + Lambda: [7,0,0x03], + Xi: [7,0,0x04], + Pi: [7,0,0x05], + Sigma: [7,0,0x06], + Upsilon: [7,0,0x07], + Phi: [7,0,0x08], + Psi: [7,0,0x09], + Omega: [7,0,0x0A], + + // Ord symbols + aleph: [0,2,0x40], + imath: [0,1,0x7B], + jmath: [0,1,0x7C], + ell: [0,1,0x60], + wp: [0,1,0x7D], + Re: [0,2,0x3C], + Im: [0,2,0x3D], + partial: [0,1,0x40], + infty: [0,2,0x31], + prime: [0,2,0x30], + emptyset: [0,2,0x3B], + nabla: [0,2,0x72], + surd: [1,2,0x70], + top: [0,2,0x3E], + bot: [0,2,0x3F], + triangle: [0,2,0x34], + forall: [0,2,0x38], + exists: [0,2,0x39], + neg: [0,2,0x3A], + lnot: [0,2,0x3A], + flat: [0,1,0x5B], + natural: [0,1,0x5C], + sharp: [0,1,0x5D], + clubsuit: [0,2,0x7C], + diamondsuit: [0,2,0x7D], + heartsuit: [0,2,0x7E], + spadesuit: [0,2,0x7F], + + // big ops + coprod: [1,3,0x60], + bigvee: [1,3,0x57], + bigwedge: [1,3,0x56], + biguplus: [1,3,0x55], + bigcap: [1,3,0x54], + bigcup: [1,3,0x53], + intop: [1,3,0x52], + prod: [1,3,0x51], + sum: [1,3,0x50], + bigotimes: [1,3,0x4E], + bigoplus: [1,3,0x4C], + bigodot: [1,3,0x4A], + ointop: [1,3,0x48], + bigsqcup: [1,3,0x46], + smallint: [1,2,0x73], + + // binary operations + triangleleft: [2,1,0x2F], + triangleright: [2,1,0x2E], + bigtriangleup: [2,2,0x34], + bigtriangledown: [2,2,0x35], + wedge: [2,2,0x5E], + land: [2,2,0x5E], + vee: [2,2,0x5F], + lor: [2,2,0x5F], + cap: [2,2,0x5C], + cup: [2,2,0x5B], + ddagger: [2,2,0x7A], + dagger: [2,2,0x79], + sqcap: [2,2,0x75], + sqcup: [2,2,0x74], + uplus: [2,2,0x5D], + amalg: [2,2,0x71], + diamond: [2,2,0x05], + bullet: [2,2,0x0F], + wr: [2,2,0x6F], + div: [2,2,0x04], + odot: [2,2,0x0C], + oslash: [2,2,0x0B], + otimes: [2,2,0x0A], + ominus: [2,2,0x09], + oplus: [2,2,0x08], + mp: [2,2,0x07], + pm: [2,2,0x06], + circ: [2,2,0x0E], + bigcirc: [2,2,0x0D], + setminus: [2,2,0x6E], // for set difference A\setminus B + cdot: [2,2,0x01], + ast: [2,2,0x03], + times: [2,2,0x02], + star: [2,1,0x3F], + + // Relations + propto: [3,2,0x2F], + sqsubseteq: [3,2,0x76], + sqsupseteq: [3,2,0x77], + parallel: [3,2,0x6B], + mid: [3,2,0x6A], + dashv: [3,2,0x61], + vdash: [3,2,0x60], + leq: [3,2,0x14], + le: [3,2,0x14], + geq: [3,2,0x15], + ge: [3,2,0x15], + lt: [3,1,0x3C], // extra since < and > are hard + gt: [3,1,0x3E], // to get in HTML + succ: [3,2,0x1F], + prec: [3,2,0x1E], + approx: [3,2,0x19], + succeq: [3,2,0x17], + preceq: [3,2,0x16], + supset: [3,2,0x1B], + subset: [3,2,0x1A], + supseteq: [3,2,0x13], + subseteq: [3,2,0x12], + 'in': [3,2,0x32], + ni: [3,2,0x33], + owns: [3,2,0x33], + gg: [3,2,0x1D], + ll: [3,2,0x1C], + not: [3,2,0x36], + sim: [3,2,0x18], + simeq: [3,2,0x27], + perp: [3,2,0x3F], + equiv: [3,2,0x11], + asymp: [3,2,0x10], + smile: [3,1,0x5E], + frown: [3,1,0x5F], + + // Arrows + Leftrightarrow: [3,2,0x2C], + Leftarrow: [3,2,0x28], + Rightarrow: [3,2,0x29], + leftrightarrow: [3,2,0x24], + leftarrow: [3,2,0x20], + gets: [3,2,0x20], + rightarrow: [3,2,0x21], + to: [3,2,0x21], + mapstochar: [3,2,0x37], + leftharpoonup: [3,1,0x28], + leftharpoondown: [3,1,0x29], + rightharpoonup: [3,1,0x2A], + rightharpoondown: [3,1,0x2B], + nearrow: [3,2,0x25], + searrow: [3,2,0x26], + nwarrow: [3,2,0x2D], + swarrow: [3,2,0x2E], + + minuschar: [3,2,0x00], // for longmapsto + hbarchar: [0,0,0x16], // for \hbar + lhook: [3,1,0x2C], + rhook: [3,1,0x2D], + + ldotp: [6,1,0x3A], // ldot as a punctuation mark + cdotp: [6,2,0x01], // cdot as a punctuation mark + colon: [6,0,0x3A], // colon as a punctuation mark + + '#': [7,0,0x23], + '$': [7,0,0x24], + '%': [7,0,0x25], + '&': [7,0,0x26] + }, + + // The delimiter table (see Appendix B of the TeXbook) + delimiter: { + '(': [0,0,0x28,3,0x00], + ')': [0,0,0x29,3,0x01], + '[': [0,0,0x5B,3,0x02], + ']': [0,0,0x5D,3,0x03], + '<': [0,2,0x68,3,0x0A], + '>': [0,2,0x69,3,0x0B], + '\\lt': [0,2,0x68,3,0x0A], // extra since < and > are + '\\gt': [0,2,0x69,3,0x0B], // hard to get in HTML + '/': [0,0,0x2F,3,0x0E], + '|': [0,2,0x6A,3,0x0C], + '.': [0,0,0x00,0,0x00], + '\\': [0,2,0x6E,3,0x0F], + '\\lmoustache': [4,3,0x7A,3,0x40], // top from (, bottom from ) + '\\rmoustache': [5,3,0x7B,3,0x41], // top from ), bottom from ( + '\\lgroup': [4,6,0x28,3,0x3A], // extensible ( with sharper tips + '\\rgroup': [5,6,0x29,3,0x3B], // extensible ) with sharper tips + '\\arrowvert': [0,2,0x6A,3,0x3C], // arrow without arrowheads + '\\Arrowvert': [0,2,0x6B,3,0x3D], // double arrow without arrowheads +// '\\bracevert': [0,7,0x7C,3,0x3E], // the vertical bar that extends braces + '\\bracevert': [0,2,0x6A,3,0x3E], // we don't load tt, so use | instead + '\\Vert': [0,2,0x6B,3,0x0D], + '\\|': [0,2,0x6B,3,0x0D], + '\\vert': [0,2,0x6A,3,0x0C], + '\\uparrow': [3,2,0x22,3,0x78], + '\\downarrow': [3,2,0x23,3,0x79], + '\\updownarrow': [3,2,0x6C,3,0x3F], + '\\Uparrow': [3,2,0x2A,3,0x7E], + '\\Downarrow': [3,2,0x2B,3,0x7F], + '\\Updownarrow': [3,2,0x6D,3,0x77], + '\\backslash': [0,2,0x6E,3,0x0F], // for double coset G\backslash H + '\\rangle': [5,2,0x69,3,0x0B], + '\\langle': [4,2,0x68,3,0x0A], + '\\rbrace': [5,2,0x67,3,0x09], + '\\lbrace': [4,2,0x66,3,0x08], + '\\}': [5,2,0x67,3,0x09], + '\\{': [4,2,0x66,3,0x08], + '\\rceil': [5,2,0x65,3,0x07], + '\\lceil': [4,2,0x64,3,0x06], + '\\rfloor': [5,2,0x63,3,0x05], + '\\lfloor': [4,2,0x62,3,0x04], + '\\lbrack': [0,0,0x5B,3,0x02], + '\\rbrack': [0,0,0x5D,3,0x03] + }, + + /* + * The basic macros for plain TeX. + * + * When the control sequence on the left is called, the JavaScript + * funtion on the right is called, with the name of the control sequence + * as its first parameter (this way, the same function can be called by + * several different control sequences to do similar actions, and the + * function can still tell which TeX command was issued). If the right + * is an array, the first entry is the routine to call, and the + * remaining entries in the array are parameters to pass to the function + * as the second parameter (they are in an array reference). + * + * Note: TeX macros as defined by the user are discussed below. + */ + macros: { + displaystyle: ['HandleStyle','D'], + textstyle: ['HandleStyle','T'], + scriptstyle: ['HandleStyle','S'], + scriptscriptstyle: ['HandleStyle','SS'], + + rm: ['HandleFont',0], + mit: ['HandleFont',1], + oldstyle: ['HandleFont',1], + cal: ['HandleFont',2], + it: ['HandleFont',4], + bf: ['HandleFont',6], + + font: ['Extension','font'], + + left: 'HandleLeft', + right: 'HandleRight', + + arcsin: ['NamedOp',0], + arccos: ['NamedOp',0], + arctan: ['NamedOp',0], + arg: ['NamedOp',0], + cos: ['NamedOp',0], + cosh: ['NamedOp',0], + cot: ['NamedOp',0], + coth: ['NamedOp',0], + csc: ['NamedOp',0], + deg: ['NamedOp',0], + det: 'NamedOp', + dim: ['NamedOp',0], + exp: ['NamedOp',0], + gcd: 'NamedOp', + hom: ['NamedOp',0], + inf: 'NamedOp', + ker: ['NamedOp',0], + lg: ['NamedOp',0], + lim: 'NamedOp', + liminf: ['NamedOp',null,'liminf'], + limsup: ['NamedOp',null,'limsup'], + ln: ['NamedOp',0], + log: ['NamedOp',0], + max: 'NamedOp', + min: 'NamedOp', + Pr: 'NamedOp', + sec: ['NamedOp',0], + sin: ['NamedOp',0], + sinh: ['NamedOp',0], + sup: 'NamedOp', + tan: ['NamedOp',0], + tanh: ['NamedOp',0], + + vcenter: ['HandleAtom','vcenter'], + overline: ['HandleAtom','overline'], + underline: ['HandleAtom','underline'], + over: 'HandleOver', + overwithdelims: 'HandleOver', + atop: 'HandleOver', + atopwithdelims: 'HandleOver', + above: 'HandleOver', + abovewithdelims: 'HandleOver', + brace: ['HandleOver','\\{','\\}'], + brack: ['HandleOver','[',']'], + choose: ['HandleOver','(',')'], + + overbrace: ['Extension','leaders'], + underbrace: ['Extension','leaders'], + overrightarrow: ['Extension','leaders'], + underrightarrow: ['Extension','leaders'], + overleftarrow: ['Extension','leaders'], + underleftarrow: ['Extension','leaders'], + overleftrightarrow: ['Extension','leaders'], + underleftrightarrow: ['Extension','leaders'], + overset: ['Extension','underset-overset'], + underset: ['Extension','underset-overset'], + + llap: 'HandleLap', + rlap: 'HandleLap', + ulap: 'HandleLap', + dlap: 'HandleLap', + raise: 'RaiseLower', + lower: 'RaiseLower', + moveleft: 'MoveLeftRight', + moveright: 'MoveLeftRight', + + frac: 'Frac', + root: 'Root', + sqrt: 'Sqrt', + + // TeX substitution macros + hbar: ['Macro','\\hbarchar\\kern-.5em h'], + ne: ['Macro','\\not='], + neq: ['Macro','\\not='], + notin: ['Macro','\\mathrel{\\rlap{\\kern2mu/}}\\in'], + cong: ['Macro','\\mathrel{\\lower2mu{\\mathrel{{\\rlap{=}\\raise6mu\\sim}}}}'], + bmod: ['Macro','\\mathbin{\\rm mod}'], + pmod: ['Macro','\\kern 18mu ({\\rm mod}\\,\\,#1)',1], + 'int': ['Macro','\\intop\\nolimits'], + oint: ['Macro','\\ointop\\nolimits'], + doteq: ['Macro','\\buildrel\\textstyle.\\over='], + ldots: ['Macro','\\mathinner{\\ldotp\\ldotp\\ldotp}'], + cdots: ['Macro','\\mathinner{\\cdotp\\cdotp\\cdotp}'], + vdots: ['Macro','\\mathinner{\\rlap{\\raise8pt{.\\rule 0pt 6pt 0pt}}\\rlap{\\raise4pt{.}}.}'], + ddots: ['Macro','\\mathinner{\\kern1mu\\raise7pt{\\rule 0pt 7pt 0pt .}\\kern2mu\\raise4pt{.}\\kern2mu\\raise1pt{.}\\kern1mu}'], + joinrel: ['Macro','\\mathrel{\\kern-4mu}'], + relbar: ['Macro','\\mathrel{\\smash-}'], // \smash, because - has the same height as + + Relbar: ['Macro','\\mathrel='], + bowtie: ['Macro','\\mathrel\\triangleright\\joinrel\\mathrel\\triangleleft'], + models: ['Macro','\\mathrel|\\joinrel='], + mapsto: ['Macro','\\mathrel{\\mapstochar\\rightarrow}'], + rightleftharpoons: ['Macro','\\vcenter{\\mathrel{\\rlap{\\raise3mu{\\rightharpoonup}}}\\leftharpoondown}'], + hookrightarrow: ['Macro','\\lhook\\joinrel\\rightarrow'], + hookleftarrow: ['Macro','\\leftarrow\\joinrel\\rhook'], + Longrightarrow: ['Macro','\\Relbar\\joinrel\\Rightarrow'], + longrightarrow: ['Macro','\\relbar\\joinrel\\rightarrow'], + longleftarrow: ['Macro','\\leftarrow\\joinrel\\relbar'], + Longleftarrow: ['Macro','\\Leftarrow\\joinrel\\Relbar'], + longmapsto: ['Macro','\\mathrel{\\mapstochar\\minuschar\\joinrel\\rightarrow}'], + longleftrightarrow: ['Macro','\\leftarrow\\joinrel\\rightarrow'], + Longleftrightarrow: ['Macro','\\Leftarrow\\joinrel\\Rightarrow'], + iff: ['Macro','\\;\\Longleftrightarrow\\;'], + mathcal: ['Macro','{\\cal #1}',1], + mathrm: ['Macro','{\\rm #1}',1], + mathbf: ['Macro','{\\bf #1}',1], + mathbb: ['Macro','{\\bf #1}',1], + mathit: ['Macro','{\\it #1}',1], + textrm: ['Macro','\\mathord{\\hbox{#1}}',1], + textit: ['Macro','\\mathord{\\class{textit}{\\hbox{#1}}}',1], + textbf: ['Macro','\\mathord{\\class{textbf}{\\hbox{#1}}}',1], + pmb: ['Macro','\\rlap{#1}\\kern1px{#1}',1], + + TeX: ['Macro','T\\kern-.1667em\\lower.5ex{E}\\kern-.125em X'], + + limits: ['Limits',1], + nolimits: ['Limits',0], + + ',': ['Spacer',1/6], + ':': ['Spacer',1/6], // for LaTeX + '>': ['Spacer',2/9], + ';': ['Spacer',5/18], + '!': ['Spacer',-1/6], + enspace: ['Spacer',1/2], + quad: ['Spacer',1], + qquad: ['Spacer',2], + thinspace: ['Spacer',1/6], + negthinspace: ['Spacer',-1/6], + + hskip: 'Hskip', + kern: 'Hskip', + rule: ['Rule','colored'], + space: ['Rule','blank'], + + big: ['MakeBig','ord',0.85], + Big: ['MakeBig','ord',1.15], + bigg: ['MakeBig','ord',1.45], + Bigg: ['MakeBig','ord',1.75], + bigl: ['MakeBig','open',0.85], + Bigl: ['MakeBig','open',1.15], + biggl: ['MakeBig','open',1.45], + Biggl: ['MakeBig','open',1.75], + bigr: ['MakeBig','close',0.85], + Bigr: ['MakeBig','close',1.15], + biggr: ['MakeBig','close',1.45], + Biggr: ['MakeBig','close',1.75], + bigm: ['MakeBig','rel',0.85], + Bigm: ['MakeBig','rel',1.15], + biggm: ['MakeBig','rel',1.45], + Biggm: ['MakeBig','rel',1.75], + + mathord: ['HandleAtom','ord'], + mathop: ['HandleAtom','op'], + mathopen: ['HandleAtom','open'], + mathclose: ['HandleAtom','close'], + mathbin: ['HandleAtom','bin'], + mathrel: ['HandleAtom','rel'], + mathpunct: ['HandleAtom','punct'], + mathinner: ['HandleAtom','inner'], + + mathchoice: ['Extension','mathchoice'], + buildrel: 'BuildRel', + + hbox: 'HBox', + text: 'HBox', + mbox: 'HBox', + fbox: ['Extension','fbox'], + + strut: 'Strut', + mathstrut: ['Macro','\\vphantom{(}'], + phantom: ['Phantom',1,1], + vphantom: ['Phantom',1,0], + hphantom: ['Phantom',0,1], + smash: 'Smash', + + acute: ['MathAccent', [7,0,0x13]], + grave: ['MathAccent', [7,0,0x12]], + ddot: ['MathAccent', [7,0,0x7F]], + tilde: ['MathAccent', [7,0,0x7E]], + bar: ['MathAccent', [7,0,0x16]], + breve: ['MathAccent', [7,0,0x15]], + check: ['MathAccent', [7,0,0x14]], + hat: ['MathAccent', [7,0,0x5E]], + vec: ['MathAccent', [0,1,0x7E]], + dot: ['MathAccent', [7,0,0x5F]], + widetilde: ['MathAccent', [0,3,0x65]], + widehat: ['MathAccent', [0,3,0x62]], + + '_': ['Replace','ord','_','normal',-.4,.1], + ' ': ['Replace','ord',' ','normal'], +// angle: ['Replace','ord','∠','normal'], + angle: ['Macro','\\kern2.5mu\\raise1.54pt{\\rlap{\\scriptstyle \\char{cmsy10}{54}}\\kern1pt\\rule{.45em}{-1.2pt}{1.54pt}\\kern2.5mu}'], + + matrix: 'Matrix', + array: 'Matrix', // ### still need to do alignment options ### + pmatrix: ['Matrix','(',')','c'], + cases: ['Matrix','\\{','.',['l','l'],null,2], + eqalign: ['Matrix',null,null,['r','l'],[5/18],3,'D'], + displaylines: ['Matrix',null,null,['c'],null,3,'D'], + cr: 'HandleRow', + '\\': 'HandleRow', + newline: 'HandleRow', + noalign: 'HandleNoAlign', + eqalignno: ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'], + leqalignno: ['Matrix',null,null,['r','l','r'],[5/8,3],3,'D'], + + // LaTeX + begin: 'Begin', + end: 'End', + tiny: ['HandleSize',0], + Tiny: ['HandleSize',1], // non-standard + scriptsize: ['HandleSize',2], + small: ['HandleSize',3], + normalsize: ['HandleSize',4], + large: ['HandleSize',5], + Large: ['HandleSize',6], + LARGE: ['HandleSize',7], + huge: ['HandleSize',8], + Huge: ['HandleSize',9], + dots: ['Macro','\\ldots'], + + newcommand: ['Extension','newcommand'], + newenvironment: ['Extension','newcommand'], + def: ['Extension','newcommand'], + + // Extensions to TeX + color: ['Extension','HTML'], + href: ['Extension','HTML'], + 'class': ['Extension','HTML'], + style: ['Extension','HTML'], + cssId: ['Extension','HTML'], + unicode: ['Extension','HTML'], + bbox: ['Extension','bbox'], + + require: 'Require', + + // debugging and test routines + 'char': 'Char' + }, + + /* + * LaTeX environments + */ + environments: { + array: 'Array', + matrix: ['Array',null,null,'c'], + pmatrix: ['Array','(',')','c'], + bmatrix: ['Array','[',']','c'], + Bmatrix: ['Array','\\{','\\}','c'], + vmatrix: ['Array','\\vert','\\vert','c'], + Vmatrix: ['Array','\\Vert','\\Vert','c'], + cases: ['Array','\\{','.','ll',null,2], + eqnarray: ['Array',null,null,'rcl',[5/18,5/18],3,'D'], + 'eqnarray*': ['Array',null,null,'rcl',[5/18,5/18],3,'D'], + equation: 'Equation', + 'equation*': 'Equation', + + align: ['Extension','AMSmath'], + 'align*': ['Extension','AMSmath'], + aligned: ['Extension','AMSmath'], + multline: ['Extension','AMSmath'], + 'multline*': ['Extension','AMSmath'], + split: ['Extension','AMSmath'], + gather: ['Extension','AMSmath'], + 'gather*': ['Extension','AMSmath'], + gathered: ['Extension','AMSmath'] + }, + + + /***************************************************************************/ + + /* + * Add special characters to list above. (This makes it possible + * to define them in a variable that the user can change.) + */ + AddSpecial: function (obj) { + for (var id in obj) { + jsMath.Parser.prototype.special[jsMath.Parser.prototype[id]] = obj[id]; + } + }, + + /* + * Throw an error + */ + Error: function (s) { + this.i = this.string.length; + if (s.error) {this.error = s.error} else { + if (!this.error) {this.error = s} + } + }, + + /***************************************************************************/ + + /* + * Check if the next character is a space + */ + nextIsSpace: function () { + return this.string.charAt(this.i).match(/[ \n\r\t]/); + }, + + /* + * Trim spaces from a string + */ + trimSpaces: function (text) { + if (typeof(text) != 'string') {return text} + return text.replace(/^\s+|\s+$/g,''); + }, + + /* + * Parse a substring to get its mList, and return it. + * Check that no errors occured + */ + Process: function (arg) { + var data = this.mlist.data; + arg = jsMath.Parse(arg,data.font,data.size,data.style); + if (arg.error) {this.Error(arg); return null} + if (arg.mlist.Length() == 0) {return null} + if (arg.mlist.Length() == 1) { + var atom = arg.mlist.Last(); + if (atom.atom && atom.type == 'ord' && atom.nuc && + !atom.sub && !atom.sup && (atom.nuc.type == 'text' || atom.nuc.type == 'TeX')) + {return atom.nuc} + } + return {type: 'mlist', mlist: arg.mlist}; + }, + + /* + * Get and return a control-sequence name from the TeX string + */ + GetCommand: function () { + var letter = /^([a-z]+|.) ?/i; + var cmd = letter.exec(this.string.slice(this.i)); + if (cmd) {this.i += cmd[1].length; return cmd[1]} + this.i++; return " "; + }, + + /* + * Get and return a TeX argument (either a single character or control sequence, + * or the contents of the next set of braces). + */ + GetArgument: function (name,noneOK) { + while (this.nextIsSpace()) {this.i++} + if (this.i >= this.string.length) {if (!noneOK) this.Error("Missing argument for "+name); return null} + if (this.string.charAt(this.i) == this.close) {if (!noneOK) this.Error("Extra close brace"); return null} + if (this.string.charAt(this.i) == this.cmd) {this.i++; return this.cmd+this.GetCommand()} + if (this.string.charAt(this.i) != this.open) {return this.string.charAt(this.i++)} + var j = ++this.i; var pcount = 1; var c = ''; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == this.cmd) {this.i++} + else if (c == this.open) {pcount++} + else if (c == this.close) { + if (pcount == 0) {this.Error("Extra close brace"); return null} + if (--pcount == 0) {return this.string.slice(j,this.i-1)} + } + } + this.Error("Missing close brace"); + return null; + }, + + /* + * Get an argument and process it into an mList + */ + ProcessArg: function (name) { + var arg = this.GetArgument(name); if (this.error) {return null} + return this.Process(arg); + }, + + /* + * Get and process an argument for a super- or subscript. + * (read extra args for \frac, \sqrt, \mathrm, etc.) + * This handles these macros as special cases, so is really + * rather a hack. A more general method for indicating + * how to handle macros in scripts needs to be developed. + */ + ProcessScriptArg: function (name) { + var arg = this.GetArgument(name); if (this.error) {return null} + if (arg.charAt(0) == this.cmd) { + var csname = arg.substr(1); + if (csname == "frac") { + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } else if (csname == "sqrt") { + arg += '['+this.GetBrackets(csname)+']'; if (this.error) {return null} + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } else if (csname.match(this.scriptargs)) { + arg += '{'+this.GetArgument(csname)+'}'; if (this.error) {return null} + } + } + return this.Process(arg); + }, + + /* + * Get the name of a delimiter (check it in the delimiter list). + */ + GetDelimiter: function (name) { + while (this.nextIsSpace()) {this.i++} + var c = this.string.charAt(this.i); + if (this.i < this.string.length) { + this.i++; + if (c == this.cmd) {c = '\\'+this.GetCommand(name); if (this.error) return null} + if (this.delimiter[c] != null) {return this.delimiter[c]} + } + this.Error("Missing or unrecognized delimiter for "+name); + return null; + }, + + /* + * Get a dimension (including its units). + * Convert the dimen to em's, except for mu's, which must be + * converted when typeset. + */ + GetDimen: function (name,nomu) { + var rest; var advance = 0; + if (this.nextIsSpace()) {this.i++} + if (this.string.charAt(this.i) == '{') { + rest = this.GetArgument(name); + } else { + rest = this.string.slice(this.i); + advance = 1; + } + return this.ParseDimen(rest,name,advance,nomu); + }, + + ParseDimen: function (dimen,name,advance,nomu) { + var match = dimen.match(/^\s*([-+]?(\.\d+|\d+(\.\d*)?))(pt|em|ex|mu|px)/); + if (!match) {this.Error("Missing dimension or its units for "+name); return null} + if (advance) { + this.i += match[0].length; + if (this.nextIsSpace()) {this.i++} + } + var d = match[1]-0; + if (match[4] == 'px') {d /= jsMath.em} + else if (match[4] == 'pt') {d /= 10} + else if (match[4] == 'ex') {d *= jsMath.TeX.x_height} + else if (match[4] == 'mu') {if (nomu) {d = d/18} else {d = [d,'mu']}} + return d; + }, + + /* + * Get the next non-space character + */ + GetNext: function () { + while (this.nextIsSpace()) {this.i++} + return this.string.charAt(this.i); + }, + + /* + * Get an optional LaTeX argument in brackets + */ + GetBrackets: function (name) { + var c = this.GetNext(); if (c != '[') return ''; + var start = ++this.i; var pcount = 0; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == '{') {pcount++} + else if (c == '}') { + if (pcount == 0) + {this.Error("Extra close brace while looking for ']'"); return null} + pcount --; + } else if (c == this.cmd) { + this.i++; + } else if (c == ']') { + if (pcount == 0) {return this.string.slice(start,this.i-1)} + } + } + this.Error("Couldn't find closing ']' for argument to "+this.cmd+name); + return null; + }, + + /* + * Get everything up to the given control sequence name (token) + */ + GetUpto: function (name,token) { + while (this.nextIsSpace()) {this.i++} + var start = this.i; var pcount = 0; + while (this.i < this.string.length) { + var c = this.string.charAt(this.i++); + if (c == '{') {pcount++} + else if (c == '}') { + if (pcount == 0) + {this.Error("Extra close brace while looking for "+this.cmd+token); return null} + pcount --; + } else if (c == this.cmd) { + // really need separate counter for begin/end + // and it should really be a stack (new pcount for each begin) + if (this.string.slice(this.i,this.i+5) == "begin") {pcount++; this.i+=4} + else if (this.string.slice(this.i,this.i+3) == "end") { + if (pcount > 0) {pcount--; this.i += 2} + } + if (pcount == 0) { + if (this.string.slice(this.i,this.i+token.length) == token) { + c = this.string.charAt(this.i+token.length); + if (c.match(/[^a-z]/i) || !token.match(/[a-z]/i)) { + var arg = this.string.slice(start,this.i-1); + this.i += token.length; + return arg; + } + } + } + this.i++; + } + } + this.Error("Couldn't find "+this.cmd+token+" for "+name); + return null; + }, + + /* + * Get a parameter delimited by a control sequence, and + * process it to get its mlist + */ + ProcessUpto: function (name,token) { + var arg = this.GetUpto(name,token); if (this.error) return null; + return this.Process(arg); + }, + + /* + * Get everything up to \end{env} + */ + GetEnd: function (env) { + var body = ''; var name = ''; + while (name != env) { + body += this.GetUpto('begin{'+env+'}','end'); if (this.error) return null; + name = this.GetArgument(this.cmd+'end'); if (this.error) return null; + } + return body; + }, + + + /***************************************************************************/ + + + /* + * Ignore spaces + */ + Space: function () {}, + + /* + * Collect together any primes and convert them to a superscript + */ + Prime: function (c) { + var base = this.mlist.Last(); + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sup) {this.Error("Prime causes double exponent: use braces to clarify"); return} + var sup = ''; + while (c == "'") {sup += this.cmd+'prime'; c = this.GetNext(); if (c == "'") {this.i++}} + base.sup = this.Process(sup); + base.sup.isPrime = 1; + }, + + /* + * Raise or lower its parameter by a given amount + * @@@ Note that this is different from TeX, which requires an \hbox @@@ + * ### make this work with mu's ### + */ + RaiseLower: function (name) { + var h = this.GetDimen(this.cmd+name,1); if (this.error) return; + var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return; + if (name == 'lower') {h = -h} + this.mlist.Add(new jsMath.mItem('raise',{nuc: box, raise: h})); + }, + + /* + * Shift an expression to the right or left + * @@@ Note that this is different from TeX, which requires a \vbox @@@ + * ### make this work with mu's ### + */ + MoveLeftRight: function (name) { + var x = this.GetDimen(this.cmd+name,1); if (this.error) return; + var box = this.ProcessScriptArg(this.cmd+name); if (this.error) return; + if (name == 'moveleft') {x = -x} + this.mlist.Add(jsMath.mItem.Space(x)); + this.mlist.Add(jsMath.mItem.Atom('ord',box)); + this.mlist.Add(jsMath.mItem.Space(-x)); + }, + + /* + * Load an extension if it has not already been loaded + */ + Require: function (name) { + var file = this.GetArgument(this.cmd+name); if (this.error) return; + file = jsMath.Extension.URL(file); + if (jsMath.Setup.loaded[file]) return; + this.Extension(null,[file]); + }, + + /* + * Load an extension file and restart processing the math + */ + Extension: function (name,data) { + jsMath.Translate.restart = 1; + if (name != null) {delete jsMath.Parser.prototype[data[1]||'macros'][name]} + jsMath.Extension.Require(data[0],jsMath.Translate.asynchronous); + throw "restart"; + }, + + /* + * Implements \frac{num}{den} + */ + Frac: function (name) { + var num = this.ProcessArg(this.cmd+name); if (this.error) return; + var den = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Fraction('over',num,den)); + }, + + /* + * Implements \sqrt[n]{...} + */ + Sqrt: function (name) { + var n = this.GetBrackets(this.cmd+name); if (this.error) return; + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + var box = jsMath.mItem.Atom('radical',arg); + if (n != '') {box.root = this.Process(n); if (this.error) return} + this.mlist.Add(box); + }, + + /* + * Implements \root...\of{...} + */ + Root: function (name) { + var n = this.ProcessUpto(this.cmd+name,'of'); if (this.error) return; + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + var box = jsMath.mItem.Atom('radical',arg); + box.root = n; this.mlist.Add(box); + }, + + + /* + * Implements \buildrel...\over{...} + */ + BuildRel: function (name) { + var top = this.ProcessUpto(this.cmd+name,'over'); if (this.error) return; + var bot = this.ProcessArg(this.cmd+name); if (this.error) return; + var op = jsMath.mItem.Atom('op',bot); + op.limits = 1; op.sup = top; + this.mlist.Add(op); + }, + + /* + * Create a delimiter of the type and size specified in the parameters + */ + MakeBig: function (name,data) { + var type = data[0]; var h = data[1] * jsMath.p_height; + var delim = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Atom(type,jsMath.Box.Delimiter(h,delim,'T'))); + }, + + /* + * Insert the specified character in the given font. + * (Try to load the font if it is not already available.) + */ + Char: function (name) { + var font = this.GetArgument(this.cmd+name); if (this.error) return; + var n = this.GetArgument(this.cmd+name); if (this.error) return; + if (!jsMath.TeX[font]) { + jsMath.TeX[font] = []; + this.Extension(null,[jsMath.Font.URL(font)]); + } else { + this.mlist.Add(jsMath.mItem.Typeset(jsMath.Box.TeX(n-0,font,this.mlist.data.style,this.mlist.data.size))); + } + }, + + /* + * Create an array or matrix. + */ + Matrix: function (name,delim) { + var data = this.mlist.data; + var arg = this.GetArgument(this.cmd+name); if (this.error) return; + var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,delim[5] || 'T'); + 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 + var box = jsMath.Box.Layout(data.size,parse.table,delim[2]||null,delim[3]||null,parse.rspacing,delim[4]||null); + // Add parentheses, if needed + if (delim[0] && delim[1]) { + var left = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T'); + var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T'); + box = jsMath.Box.SetList([left,box,right],data.style,data.size); + } + this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box)); + }, + + /* + * When we see an '&', try to add a matrix entry to the row data. + * (Use all the data in the current mList, and then clear it) + */ + HandleEntry: function (name) { + if (!this.matrix) + {this.Error(name+" can only appear in a matrix or array"); return} + if (this.mlist.data.openI != null) { + var open = this.mlist.Get(this.mlist.data.openI); + if (open.left) {this.Error("Missing "+this.cmd+"right")} + else {this.Error("Missing close brace")} + } + if (this.mlist.data.overI != null) {this.mlist.Over()} + var data = this.mlist.data; + this.mlist.Atomize(data.style,data.size); + var box = this.mlist.Typeset(data.style,data.size); + box.entry = data.entry; delete data.entry; if (!box.entry) {box.entry = {}}; + this.row[this.row.length] = box; + this.mlist = new jsMath.mList(null,null,data.size,data.style); + }, + + /* + * When we see a \cr or \\, try to add a row to the table + */ + HandleRow: function (name,last) { + var dimen; + if (!this.matrix) {this.Error(this.cmd+name+" can only appear in a matrix or array"); return} + if (name == "\\") { + dimen = this.GetBrackets(this.cmd+name); if (this.error) return; + if (dimen) {dimen = this.ParseDimen(dimen,this.cmd+name,0,1)} + } + this.HandleEntry(name); + if (!last || this.row.length > 1 || this.row[0].format != 'null') + {this.table[this.table.length] = this.row} + if (dimen) {this.rspacing[this.table.length] = dimen} + this.row = []; + }, + + /* + * Look for \vskip or \vspace in \noalign parameters + */ + HandleNoAlign: function (name) { + var arg = this.GetArgument(this.cmd+name); if (this.error) return; + var skip = arg.replace(/^.*(vskip|vspace)([^a-z])/i,'$2'); + if (skip.length == arg.length) return; + var d = this.ParseDimen(skip,this.cmd+RegExp.$1,0,1); if (this.error) return; + this.rspacing[this.table.length] = (this.rspacing[this.table.length] || 0) + d; + }, + + /* + * LaTeX array environment + */ + Array: function (name,delim) { + var columns = delim[2]; var cspacing = delim[3]; + if (!columns) { + columns = this.GetArgument(this.cmd+'begin{'+name+'}'); + if (this.error) return; + } + columns = columns.replace(/[^clr]/g,''); + columns = columns.split(''); + var data = this.mlist.data; var style = delim[5] || 'T'; + var arg = this.GetEnd(name); if (this.error) return; + var parse = new jsMath.Parser(arg+this.cmd+'\\',null,data.size,style); + 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 + var box = jsMath.Box.Layout(data.size,parse.table,columns,cspacing,parse.rspacing,delim[4],delim[6],delim[7]); + // Add parentheses, if needed + if (delim[0] && delim[1]) { + var left = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[0]],'T'); + var right = jsMath.Box.Delimiter(box.h+box.d-jsMath.hd/4,this.delimiter[delim[1]],'T'); + box = jsMath.Box.SetList([left,box,right],data.style,data.size); + } + this.mlist.Add(jsMath.mItem.Atom((delim[0]? 'inner': 'ord'),box)); + }, + + /* + * LaTeX \begin{env} + */ + Begin: function (name) { + var env = this.GetArgument(this.cmd+name); if (this.error) return; + if (env.match(/[^a-z*]/i)) {this.Error('Invalid environment name "'+env+'"'); return} + if (!this.environments[env]) {this.Error('Unknown environment "'+env+'"'); return} + var cmd = this.environments[env]; + if (typeof(cmd) == "string") {cmd = [cmd]} + this[cmd[0]](env,cmd.slice(1)); + }, + + /* + * LaTeX \end{env} + */ + End: function (name) { + var env = this.GetArgument(this.cmd+name); if (this.error) return; + this.Error(this.cmd+name+'{'+env+'} without matching '+this.cmd+'begin'); + }, + + /* + * LaTeX equation environment (just remove the environment) + */ + Equation: function (name) { + var arg = this.GetEnd(name); if (this.error) return; + this.string = arg+this.string.slice(this.i); this.i = 0; + }, + + /* + * Add a fixed amount of horizontal space + */ + Spacer: function (name,w) { + this.mlist.Add(jsMath.mItem.Space(w-0)); + }, + + /* + * Add horizontal space given by the argument + */ + Hskip: function (name) { + var w = this.GetDimen(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Space(w)); + }, + + /* + * Typeset the argument as plain text rather than math. + */ + HBox: function (name) { + var text = this.GetArgument(this.cmd+name); if (this.error) return; + var box = jsMath.Box.InternalMath(text,this.mlist.data.size); + this.mlist.Add(jsMath.mItem.Typeset(box)); + }, + + /* + * Insert a rule of a particular width, height and depth + * This replaces \hrule and \vrule + * @@@ not a standard TeX command, and all three parameters must be given @@@ + */ + Rule: function (name,style) { + var w = this.GetDimen(this.cmd+name,1); if (this.error) return; + var h = this.GetDimen(this.cmd+name,1); if (this.error) return; + var d = this.GetDimen(this.cmd+name,1); if (this.error) return; + h += d; var html; + if (h != 0) {h = Math.max(1.05/jsMath.em,h)} + if (h == 0 || w == 0 || style == "blank") + {html = jsMath.HTML.Blank(w,h)} else {html = jsMath.HTML.Rule(w,h)} + if (d) { + html = '' + + html + ''; + } + this.mlist.Add(jsMath.mItem.Typeset(new jsMath.Box('html',html,w,h-d,d))); + }, + + /* + * Inserts an empty box of a specific height and depth + */ + Strut: function () { + var size = this.mlist.data.size; + var box = jsMath.Box.Text('','normal','T',size).Styled(); + box.bh = box.bd = 0; box.h = .8; box.d = .3; box.w = box.Mw = 0; + this.mlist.Add(jsMath.mItem.Typeset(box)); + }, + + /* + * Handles \phantom, \vphantom and \hphantom + */ + Phantom: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(new jsMath.mItem('phantom',{phantom: arg, v: data[0], h: data[1]})); + }, + + /* + * Implements \smash + */ + Smash: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(new jsMath.mItem('smash',{smash: arg})); + }, + + /* + * Puts an accent on the following argument + */ + MathAccent: function (name,accent) { + var c = this.ProcessArg(this.cmd+name); if (this.error) return; + var atom = jsMath.mItem.Atom('accent',c); atom.accent = accent[0]; + this.mlist.Add(atom); + }, + + /* + * Handles functions and operators like sin, cos, sum, etc. + */ + NamedOp: function (name,data) { + var a = (name.match(/[^acegm-su-z]/)) ? 1: 0; + var d = (name.match(/[gjpqy]/)) ? .2: 0; + if (data[1]) {name = data[1]} + var box = jsMath.mItem.TextAtom('op',name,jsMath.TeX.fam[0],a,d); + if (data[0] != null) {box.limits = data[0]} + this.mlist.Add(box); + }, + + /* + * Implements \limits + */ + Limits: function (name,data) { + var atom = this.mlist.Last(); + if (!atom || atom.type != 'op') + {this.Error(this.cmd+name+" is allowed only on operators"); return} + atom.limits = data[0]; + }, + + /* + * Implements macros like those created by \def. The named control + * sequence is replaced by the string given as the first data value. + * If there is a second data value, this specifies how many arguments + * the macro uses, and in this case, those arguments are substituted + * for #1, #2, etc. within the replacement string. + * + * See the jsMath.Macro() command below for more details. + * The "newcommand" extension implements \newcommand and \def + * and are loaded automatically if needed. + */ + Macro: function (name,data) { + var text = data[0]; + if (data[1]) { + var args = []; + for (var i = 0; i < data[1]; i++) + {args[args.length] = this.GetArgument(this.cmd+name); if (this.error) return} + text = this.SubstituteArgs(args,text); + } + this.string = this.AddArgs(text,this.string.slice(this.i)); + this.i = 0; + }, + + /* + * Replace macro paramters with their values + */ + SubstituteArgs: function (args,string) { + var text = ''; var newstring = ''; var c; var i = 0; + while (i < string.length) { + c = string.charAt(i++); + if (c == this.cmd) {text += c + string.charAt(i++)} + else if (c == '#') { + c = string.charAt(i++); + if (c == "#") {text += c} else { + if (!c.match(/[1-9]/) || c > args.length) + {this.Error("Illegal macro parameter reference"); return null} + newstring = this.AddArgs(this.AddArgs(newstring,text),args[c-1]); + text = ''; + } + } else {text += c} + } + return this.AddArgs(newstring,text); + }, + + /* + * Make sure that macros are followed by a space if their names + * could accidentally be continued into the following text. + */ + AddArgs: function (s1,s2) { + if (s2.match(/^[a-z]/i) && s1.match(/(^|[^\\])(\\\\)*\\[a-z]+$/i)) {s1 += ' '} + return s1+s2; + }, + + /* + * Replace the control sequence with the given text + */ + Replace: function (name,data) { + this.mlist.Add(jsMath.mItem.TextAtom(data[0],data[1],data[2],data[3])); + }, + + /* + * Error for # (must use \#) + */ + Hash: function (name) { + this.Error("You can't use 'macro parameter character #' in math mode"); + }, + + /* + * Insert space for ~ + */ + Tilde: function (name) { + this.mlist.Add(jsMath.mItem.TextAtom('ord',' ','normal')); + }, + + /* + * Implements \llap, \rlap, etc. + */ + HandleLap: function (name) { + var box = this.ProcessArg(); if (this.error) return; + box = this.mlist.Add(new jsMath.mItem('lap',{nuc: box, lap: name})); + }, + + /* + * Adds the argument as a specific type of atom (for commands like + * \overline, etc.) + */ + HandleAtom: function (name,data) { + var arg = this.ProcessArg(this.cmd+name); if (this.error) return; + this.mlist.Add(jsMath.mItem.Atom(data[0],arg)); + }, + + + /* + * Process the character associated with a specific \mathcharcode + */ + HandleMathCode: function (name,code) { + this.HandleTeXchar(code[0],code[1],code[2]); + }, + + /* + * Add a specific character from a TeX font (use the current + * font if the type is 7 (variable) or the font is not specified) + * Load the font if it is not already loaded. + */ + HandleTeXchar: function (type,font,code) { + if (type == 7 && this.mlist.data.font != null) {font = this.mlist.data.font} + font = jsMath.TeX.fam[font]; + if (!jsMath.TeX[font]) { + jsMath.TeX[font] = []; + this.Extension(null,[jsMath.Font.URL(font)]); + } else { + this.mlist.Add(jsMath.mItem.TeXAtom(jsMath.TeX.atom[type],code,font)); + } + }, + + /* + * Add a TeX variable character or number + */ + HandleVariable: function (c) {this.HandleTeXchar(7,1,c.charCodeAt(0))}, + HandleNumber: function (c) {this.HandleTeXchar(7,0,c.charCodeAt(0))}, + + /* + * For unmapped characters, just add them in as normal + * (non-TeX) characters + */ + HandleOther: function (c) { + this.mlist.Add(jsMath.mItem.TextAtom('ord',c,'normal')); + }, + + /* + * Ignore comments in TeX data + * ### Some browsers remove the newlines, so this might cause + * extra stuff to be ignored; look into this ### + */ + HandleComment: function () { + var c; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (c == "\r" || c == "\n") return; + } + }, + + /* + * Add a style change (e.g., \displaystyle, etc) + */ + HandleStyle: function (name,style) { + this.mlist.data.style = style[0]; + this.mlist.Add(new jsMath.mItem('style',{style: style[0]})); + }, + + /* + * Implements \small, \large, etc. + */ + HandleSize: function (name,size) { + this.mlist.data.size = size[0]; + this.mlist.Add(new jsMath.mItem('size',{size: size[0]})); + }, + + /* + * Set the current font (e.g., \rm, etc) + */ + HandleFont: function (name,font) { + this.mlist.data.font = font[0]; + }, + + /* + * Look for and process a control sequence + */ + HandleCS: function () { + var cmd = this.GetCommand(); if (this.error) return; + if (this.macros[cmd]) { + var macro = this.macros[cmd]; + if (typeof(macro) == "string") {macro = [macro]} + this[macro[0]](cmd,macro.slice(1)); return; + } + if (this.mathchardef[cmd]) { + this.HandleMathCode(cmd,this.mathchardef[cmd]); + return; + } + if (this.delimiter[this.cmd+cmd]) { + this.HandleMathCode(cmd,this.delimiter[this.cmd+cmd].slice(0,3)) + return; + } + this.Error("Unknown control sequence '"+this.cmd+cmd+"'"); + }, + + /* + * Process open and close braces + */ + HandleOpen: function () {this.mlist.Open()}, + HandleClose: function () { + if (this.mlist.data.openI == null) {this.Error("Extra close brace"); return} + var open = this.mlist.Get(this.mlist.data.openI); + if (!open || open.left == null) {this.mlist.Close()} + else {this.Error("Extra close brace or missing "+this.cmd+"right"); return} + }, + + /* + * Implements \left + */ + HandleLeft: function (name) { + var left = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.Open(left); + }, + + /* + * Implements \right + */ + HandleRight: function (name) { + var right = this.GetDelimiter(this.cmd+name); if (this.error) return; + var open = this.mlist.Get(this.mlist.data.openI); + if (open && open.left != null) {this.mlist.Close(right)} + else {this.Error("Extra open brace or missing "+this.cmd+"left");} + }, + + /* + * Implements generalized fractions (\over, \above, etc.) + */ + HandleOver: function (name,data) { + if (this.mlist.data.overI != null) + {this.Error('Ambiguous use of '+this.cmd+name); return} + this.mlist.data.overI = this.mlist.Length(); + this.mlist.data.overF = {name: name}; + if (data.length > 0) { + this.mlist.data.overF.left = this.delimiter[data[0]]; + this.mlist.data.overF.right = this.delimiter[data[1]]; + } else if (name.match(/withdelims$/)) { + this.mlist.data.overF.left = this.GetDelimiter(this.cmd+name); if (this.error) return; + this.mlist.data.overF.right = this.GetDelimiter(this.cmd+name); if (this.error) return; + } else { + this.mlist.data.overF.left = null; + this.mlist.data.overF.right = null; + } + if (name.match(/^above/)) { + this.mlist.data.overF.thickness = this.GetDimen(this.cmd+name,1); + if (this.error) return; + } else { + this.mlist.data.overF.thickness = null; + } + }, + + /* + * Add a superscript to the preceeding atom + */ + HandleSuperscript: function () { + var base = this.mlist.Last(); + if (this.mlist.data.overI == this.mlist.Length()) {base = null} + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sup) { + if (base.sup.isPrime) {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + else {this.Error("Double exponent: use braces to clarify"); return} + } + base.sup = this.ProcessScriptArg('superscript'); if (this.error) return; + }, + + /* + * Add a subscript to the preceeding atom + */ + HandleSubscript: function () { + var base = this.mlist.Last(); + if (this.mlist.data.overI == this.mlist.Length()) {base = null} + if (base == null || (!base.atom && base.type != 'box' && base.type != 'frac')) + {base = this.mlist.Add(jsMath.mItem.Atom('ord',{type:null}))} + if (base.sub) {this.Error("Double subscripts: use braces to clarify"); return} + base.sub = this.ProcessScriptArg('subscript'); if (this.error) return; + }, + + /* + * Parse a TeX math string, handling macros, etc. + */ + Parse: function () { + var c; + while (this.i < this.string.length) { + c = this.string.charAt(this.i++); + if (this.mathchar[c]) {this.HandleMathCode(c,this.mathchar[c])} + else if (this.special[c]) {this[this.special[c]](c)} + else if (this.letter.test(c)) {this.HandleVariable(c)} + else if (this.number.test(c)) {this.HandleNumber(c)} + else {this.HandleOther(c)} + } + if (this.mlist.data.openI != null) { + var open = this.mlist.Get(this.mlist.data.openI); + if (open.left) {this.Error("Missing "+this.cmd+"right")} + else {this.Error("Missing close brace")} + } + if (this.mlist.data.overI != null) {this.mlist.Over()} + }, + + /* + * Perform the processing of Appendix G + */ + Atomize: function () { + var data = this.mlist.init; + if (!this.error) this.mlist.Atomize(data.style,data.size) + }, + + /* + * Produce the final HTML. + * + * We have to wrap the HTML it appropriate tags to hide its + * actual dimensions when these don't match the TeX dimensions of the + * results. We also include an image to force the results to take up + * the right amount of space. The results may need to be vertically + * adjusted to make the baseline appear in the correct place. + */ + Typeset: function () { + var data = this.mlist.init; + var box = this.typeset = this.mlist.Typeset(data.style,data.size); + if (this.error) {return ''+this.error+''} + if (box.format == 'null') {return ''}; + + box.Styled().Remeasured(); var isSmall = 0; var isBig = 0; + if (box.bh > box.h && box.bh > jsMath.h+.001) {isSmall = 1} + if (box.bd > box.d && box.bd > jsMath.d+.001) {isSmall = 1} + if (box.h > jsMath.h || box.d > jsMath.d) {isBig = 1} + + var html = box.html; + if (isSmall) {// hide the extra size + if (jsMath.Browser.allowAbsolute) { + var y = (box.bh > jsMath.h+.001 ? jsMath.h - box.bh : 0); + html = jsMath.HTML.Absolute(html,box.w,jsMath.h,0,y); + } else if (jsMath.Browser.valignBug) { + // remove line height + html = '' + + html + ''; + } else if (!jsMath.Browser.operaLineHeightBug) { + // remove line height and try to hide the depth + var dy = jsMath.HTML.Em(Math.max(0,box.bd-jsMath.hd)/3); + html = '' + html + ''; + } + isBig = 1; + } + if (isBig) { + // add height and depth to the line + // (force a little extra to separate lines if needed) + html += jsMath.HTML.Blank(0,box.h+.05,box.d+.05); + } + return ''+html+''; + } + +}); + +/* + * Make these characters special (and call the given routines) + */ +jsMath.Parser.prototype.AddSpecial({ + cmd: 'HandleCS', + open: 'HandleOpen', + close: 'HandleClose' +}); + + +/* + * The web-page author can call jsMath.Macro to create additional + * TeX macros for use within his or her mathematics. See the + * author's documentation for more details. + */ + +jsMath.Add(jsMath,{ + Macro: function (name) { + var macro = jsMath.Parser.prototype.macros; + macro[name] = ['Macro']; + for (var i = 1; i < arguments.length; i++) + {macro[name][macro[name].length] = arguments[i]} + } +}); + +/* + * Use these commands to create macros that load + * JavaScript files and reprocess the mathematics when + * the file is loaded. This lets you to have macros or + * LaTeX environments that autoload their own definitions + * only when they are needed, saving initial download time + * on pages where they are not used. See the author's + * documentation for more details. + * + */ + +jsMath.Extension = { + + safeRequire: 1, // disables access to files outside of jsMath/extensions + + Macro: function (name,file) { + var macro = jsMath.Parser.prototype.macros; + if (file == null) {file = name} + macro[name] = ['Extension',file]; + }, + + LaTeX: function (env,file) { + var latex = jsMath.Parser.prototype.environments; + latex[env] = ['Extension',file,'environments']; + }, + + Font: function (name,font) { + if (font == null) {font = name + "10"} + var macro = jsMath.Parser.prototype.macros; + macro[name] = ['Extension',jsMath.Font.URL(font)]; + }, + + MathChar: function (font,defs) { + var fam = jsMath.TeX.famName[font]; + if (fam == null) { + fam = jsMath.TeX.fam.length; + jsMath.TeX.fam[fam] = font; + jsMath.TeX.famName[font] = fam; + } + var mathchardef = jsMath.Parser.prototype.mathchardef; + for (var c in defs) {mathchardef[c] = [defs[c][0],fam,defs[c][1]]} + }, + + Require: function (file,show) { + if (this.safeRequire && (file.match(/\.\.\/|[^-a-z0-9.\/:_+=%~]/i) || + (file.match(/:/) && file.substr(0,jsMath.root.length) != jsMath.root))) { + jsMath.Setup.loaded[file] = 1; + return; + } + jsMath.Setup.Script(this.URL(file),show); + }, + + URL: function (file) { + file = file.replace(/^\s+|\s+$/g,''); + if (!file.match(/^([a-z]+:|\/|fonts|extensions\/)/i)) {file = 'extensions/'+file} + if (!file.match(/\.js$/)) {file += '.js'} + return file; + } +} + + +/***************************************************************************/ + +/* + * These routines look through the web page for math elements to process. + * There are two main entry points you can call: + * + * + * or + * + * + * The first will process the page asynchronously (so the user can start + * reading the top of the file while jsMath is still processing the bottom) + * while the second does not update until all the mathematics is typeset. + */ + +jsMath.Add(jsMath,{ + /* + * Call this at the bottom of your HTML page to have the + * mathematics typeset asynchronously. This lets the user + * start reading the mathematics while the rest of the page + * is being processed. + */ + Process: function (obj) { + jsMath.Setup.Body(); + jsMath.Script.Push(jsMath.Translate,'Asynchronous',obj); + }, + + /* + * Call this at the bottom of your HTML page to have the + * mathematics typeset before the page is displayed. + * This can take a long time, so the user could cancel the + * page before it is complete; use it with caution, and only + * when there is a relatively small amount of math on the page. + */ + ProcessBeforeShowing: function (obj) { + jsMath.Setup.Body(); + var method = (jsMath.Controls.cookie.asynch ? "Asynchronous": "Synchronous"); + jsMath.Script.Push(jsMath.Translate,method,obj); + }, + + /* + * Process the contents of a single element. It must be of + * class "math". + */ + ProcessElement: function (obj) { + jsMath.Setup.Body(); + jsMath.Script.Push(jsMath.Translate,'ProcessOne',obj); + } + +}); + +jsMath.Translate = { + + element: [], // the list of math elements on the page + cancel: 0, // set to 1 to cancel asynchronous processing + + /* + * Parse a TeX string in Text or Display mode and return + * the HTML for it (taking it from the cache, if available) + */ + Parse: function (style,s,noCache) { + var cache = jsMath.Global.cache[style]; + if (!cache[jsMath.em]) {cache[jsMath.em] = {}} + var HTML = cache[jsMath.em][s]; + if (!HTML || noCache) { + var parse = jsMath.Parse(s,null,null,style); + parse.Atomize(); HTML = parse.Typeset(); + if (!noCache) {cache[jsMath.em][s] = HTML} + } + return HTML; + }, + + TextMode: function (s,noCache) {this.Parse('T',s,noCache)}, + DisplayMode: function (s,noCache) {this.Parse('D',s,noCache)}, + + /* + * Return the text of a given DOM element + */ + GetElementText: function (element) { + if (element.childNodes.length == 1 && element.childNodes[0].nodeName === "#comment") { + var result = element.childNodes[0].nodeValue.match(/^\[CDATA\[(.*)\]\]$/); + if (result != null) {return result[1]}; + } + var text = this.recursiveElementText(element); + element.alt = text; + if (text.search('&') >= 0) { + text = text.replace(/</g,'<'); + text = text.replace(/>/g,'>'); + text = text.replace(/"/g,'"'); + text = text.replace(/&/g,'&'); + } + return text; + }, + recursiveElementText: function (element) { + if (element.nodeValue != null) { + if (element.nodeName !== "#comment") {return element.nodeValue} + return element.nodeValue.replace(/^\[CDATA\[((.|\n)*)\]\]$/,"$1"); + } + if (element.childNodes.length === 0) {return " "} + var text = ''; + for (var i = 0; i < element.childNodes.length; i++) + {text += this.recursiveElementText(element.childNodes[i])} + return text; + }, + + /* + * Move hidden to the location of the math element to be + * processed and reinitialize sizes for that location. + */ + ResetHidden: function (element) { + element.innerHTML = + '' + + jsMath.Browser.operaHiddenFix; // needed by Opera in tables + element.className = ''; + jsMath.hidden = element.firstChild; + if (!jsMath.BBoxFor("x").w) {jsMath.hidden = jsMath.hiddenTop} + jsMath.ReInit(); + }, + + + /* + * Typeset the contents of an element in \textstyle or \displaystyle + */ + ConvertMath: function (style,element,noCache) { + var text = this.GetElementText(element); + this.ResetHidden(element); + if (text.match(/^\s*\\nocache([^a-zA-Z])/)) + {noCache = true; text = text.replace(/\s*\\nocache/,'')} + text = this.Parse(style,text,noCache); + element.className = 'typeset'; + element.innerHTML = text; + }, + + /* + * Process a math element + */ + ProcessElement: function (element) { + this.restart = 0; + if (!element.className.match(/(^| )math( |$)/)) return; // don't reprocess elements + var noCache = (element.className.toLowerCase().match(/(^| )nocache( |$)/) != null); + try { + var style = (element.tagName.toLowerCase() == 'div' ? 'D' : 'T'); + this.ConvertMath(style,element,noCache); + element.onclick = jsMath.Click.CheckClick; + element.ondblclick = jsMath.Click.CheckDblClick; + } catch (err) { + if (element.alt) { + var tex = element.alt; + tex = tex.replace(/&/g,'&') + .replace(//g,'>'); + element.innerHTML = tex; + element.className = 'math'; + if (noCache) {element.className += ' nocache'} + } + jsMath.hidden = jsMath.hiddenTop; + } + }, + + /* + * Asynchronously process all the math elements starting with + * the k-th one. Do them in blocks of 8 (more efficient than one + * at a time, but still allows screen updates periodically). + */ + ProcessElements: function (k) { + jsMath.Script.blocking = 1; + for (var i = 0; i < jsMath.Browser.processAtOnce; i++, k++) { + if (k >= this.element.length || this.cancel) { + this.ProcessComplete(); + if (this.cancel) { + jsMath.Message.Set("Process Math: Canceled"); + jsMath.Message.Clear() + } + jsMath.Script.blocking = 0; + jsMath.Script.Process(); + return; + } else { + var savedQueue = jsMath.Script.SaveQueue(); + this.ProcessElement(this.element[k]); + if (this.restart) { + jsMath.Script.Push(this,'ProcessElements',k); + jsMath.Script.RestoreQueue(savedQueue); + jsMath.Script.blocking = 0; + setTimeout('jsMath.Script.Process()',jsMath.Browser.delay); + return; + } + } + } + jsMath.Script.RestoreQueue(savedQueue); + var p = Math.floor(100 * k / this.element.length); + jsMath.Message.Set('Processing Math: '+p+'%'); + setTimeout('jsMath.Translate.ProcessElements('+k+')',jsMath.Browser.delay); + }, + + /* + * Start the asynchronous processing of mathematics + */ + Asynchronous: function (obj) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = this.GetMathElements(obj); + jsMath.Script.blocking = 1; + this.cancel = 0; this.asynchronous = 1; + jsMath.Message.Set('Processing Math: 0%',1); + setTimeout('jsMath.Translate.ProcessElements(0)',jsMath.Browser.delay); + }, + + /* + * Do synchronous processing of mathematics + */ + Synchronous: function (obj,i) { + if (i == null) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = this.GetMathElements(obj); + i = 0; + } + this.asynchronous = 0; + while (i < this.element.length) { + this.ProcessElement(this.element[i]); + if (this.restart) { + jsMath.Synchronize('jsMath.Translate.Synchronous(null,'+i+')'); + jsMath.Script.Process(); + return; + } + i++; + } + this.ProcessComplete(1); + }, + + /* + * Synchronously process the contents of a single element + */ + ProcessOne: function (obj) { + if (!jsMath.initialized) {jsMath.Init()} + this.element = [obj]; + this.Synchronous(null,0); + }, + + /* + * Look up all the math elements on the page and + * put them in a list sorted from top to bottom of the page + */ + GetMathElements: function (obj) { + var element = []; var k; + if (!obj) {obj = jsMath.document} + if (typeof(obj) == 'string') {obj = jsMath.document.getElementById(obj)} + if (!obj.getElementsByTagName) return null; + var math = obj.getElementsByTagName('div'); + for (k = 0; k < math.length; k++) { + if (math[k].className && math[k].className.match(/(^| )math( |$)/)) { + if (jsMath.Browser.renameOK && obj.getElementsByName) + {math[k].setAttribute('name','_jsMath_')} + else {element[element.length] = math[k]} + } + } + math = obj.getElementsByTagName('span'); + for (k = 0; k < math.length; k++) { + if (math[k].className && math[k].className.match(/(^| )math( |$)/)) { + if (jsMath.Browser.renameOK && obj.getElementsByName) + {math[k].setAttribute('name','_jsMath_')} + else {element[element.length] = math[k]} + } + } + // this gets the SPAN and DIV elements interleaved in order + if (jsMath.Browser.renameOK && obj.getElementsByName) { + element = obj.getElementsByName('_jsMath_'); + } else if (jsMath.hidden.sourceIndex) { + element.sort(function (a,b) {return a.sourceIndex - b.sourceIndex}); + } + return element; + }, + + /* + * Remove the window message about processing math + * and clean up any marked or
      tags + */ + ProcessComplete: function (noMessage) { + if (jsMath.Browser.renameOK) { + var element = jsMath.document.getElementsByName('_jsMath_'); + for (var i = element.length-1; i >= 0; i--) { + element[i].removeAttribute('name'); + } + } + jsMath.hidden = jsMath.hiddenTop; + this.element = []; this.restart = null; + if (!noMessage) { + jsMath.Message.Set('Processing Math: Done'); + jsMath.Message.Clear(); + } + jsMath.Message.UnBlank(); + if (jsMath.Browser.safariImgBug && + (jsMath.Controls.cookie.font == 'symbol' || + jsMath.Controls.cookie.font == 'image')) { + // + // For Safari, the images don't always finish + // updating, so nudge the window to cause a + // redraw. (Hack!) + // + if (this.timeout) {clearTimeout(this.timeout)} + this.timeout = setTimeout("jsMath.window.resizeBy(-1,0); " + + "jsMath.window.resizeBy(1,0); " + + "jsMath.Translate.timeout = null",2000); + } + }, + + /* + * Cancel procesing elements + */ + Cancel: function () { + jsMath.Translate.cancel = 1; + if (jsMath.Script.cancelTimer) {jsMath.Script.cancelLoad()} + } + +}; + +jsMath.Add(jsMath,{ + // + // Synchronize these with the loading of the tex2math plugin. + // + ConvertTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX',element)}, + ConvertTeX2: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertTeX2',element)}, + ConvertLaTeX: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertLaTeX',element)}, + ConvertCustom: function (element) {jsMath.Script.Push(jsMath.tex2math,'ConvertCustom',element)}, + CustomSearch: function (om,cm,od,cd) {jsMath.Script.Push(null,function () {jsMath.tex2math.CustomSearch(om,cm,od,cd)})}, + tex2math: { + ConvertTeX: function () {}, + ConvertTeX2: function () {}, + ConvertLaTeX: function () {}, + ConvertCustom: function () {}, + CustomSearch: function () {} + } +}); +jsMath.Synchronize = jsMath.Script.Synchronize; + +/***************************************************************************/ + + +/* + * Initialize things + */ +try { + if (window.parent != window && window.jsMathAutoload) { + window.parent.jsMath = jsMath; + jsMath.document = window.parent.document; + jsMath.window = window.parent; + } +} catch (err) {} + + +jsMath.Global.Register(); +jsMath.Loaded(); +jsMath.Controls.GetCookie(); +jsMath.Setup.Source(); +jsMath.Global.Init(); +jsMath.Script.Init(); +jsMath.Setup.Fonts(); +if (jsMath.document.body) {jsMath.Setup.Body()} +jsMath.Setup.User("onload"); + +}} diff --git a/htdocs/show-source.cgi b/htdocs/show-source.cgi new file mode 100644 index 0000000000..5ef8268932 --- /dev/null +++ b/htdocs/show-source.cgi @@ -0,0 +1,72 @@ +#!/usr/bin/env perl + +print "Content-type: text/HTML\n\n"; + +$file = $ENV{PATH_INFO}; + +Error("Can't load specified file") if ($file =~ m!(\.\./|//|:|~)!); +Error("Can't load specified file") if ($file !~ m!\.p[lg]$!); + + +#$root = '/usr/local/WeBWorK'; +$root = '/opt/webwork/'; +$filedir = $file; $filedir =~ s!/[^/]+$!!; +$file =~ s!.*/!!; + +@PGdirs = ( + "../templates$filedir", + "../templates/macros", +# "$root/local/macros", + "$root/pg/macros", +); + +foreach $dir (@PGdirs) {ShowSource("$dir/$file") if (-e "$dir/$file")} + +Error("Can't find specified file",join(",",@PGdirs).": ".$file); + +sub Error { + print "\n\nShow-Source Error\n\n"; + print "\n\n

      Show-Source Error:

      \n\n"; + print join("\n",@_),"\n"; + print "\n"; + exit; +} + +sub ShowSource { + my $file = shift; + my $name = $file; $name =~ s!.*/!!; + + open(PGFILE,$file); + $program = join('',); + close(PGFILE); + + $program =~ s/&/\&/g; + $program =~ s//\>/g; + $program =~ s/\t/ /g; + print "\n\nProblem Source Code\n\n"; + print "\n\n

      Source Code for $name:

      \n\n"; + print "
      \n
      \n"; + print "
      ";
      +  print MarkSource($program);
      +  print "
      \n"; + print "
      \n
      \n"; + print "\n\n"; + exit; +} + +sub MarkSource { + my $program = shift; + local $cgi = $ENV{SCRIPT_NAME}; + $program =~ s/loadMacros *\(([^\)]*)\)/MakeLinks($1)/ge; + return $program; +} + +sub MakeLinks { + my $macros = shift; + $macros =~ s!"([^\"<]*)"!"\1"!g; + $macros =~ s!'([^\'<]*)'!'\1'!g; + return 'loadMacros('.$macros.')'; +} + +1; diff --git a/htdocs/site_info.txt b/htdocs/site_info.txt new file mode 100644 index 0000000000..7cd4c0eb99 --- /dev/null +++ b/htdocs/site_info.txt @@ -0,0 +1,5 @@ +This is the webwork2/htdocs/site_info.txt file. +You can put site information here. + + + diff --git a/htdocs/tmp/README b/htdocs/tmp/README new file mode 100644 index 0000000000..6beacb7643 --- /dev/null +++ b/htdocs/tmp/README @@ -0,0 +1,3 @@ +$CVSHeader$ + +Web-accessible temporary files are stored in this directory. diff --git a/htdocs/tmp/equations/README b/htdocs/tmp/equations/README new file mode 100644 index 0000000000..9967464ff4 --- /dev/null +++ b/htdocs/tmp/equations/README @@ -0,0 +1,3 @@ +$CVSHeader$ + +Equation images are stored in this directory. diff --git a/lib/Apache/AuthenWeBWorK.pm b/lib/Apache/AuthenWeBWorK.pm new file mode 100644 index 0000000000..cb6d5fa512 --- /dev/null +++ b/lib/Apache/AuthenWeBWorK.pm @@ -0,0 +1,173 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/Apache/AuthenWeBWorK.pm,v 1.2 2006/06/28 16:19:57 sh002i 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 Apache::AuthenWeBWorK; + +=head1 NAME + +Apache::AuthenWeBWorK - Authenticate against WeBWorK::Authen framework. + +=head1 CONFIGURATION + + PerlSetVar authen_webwork_root /path/to/webwork2 + PerlSetVar authen_webwork_course "some-course-id" + PerlSetVar authen_webwork_module "WeBWorK::Authen::something" + +=cut + +use strict; +use warnings; +#use Apache::Constants qw(:common); +use Apache2::Const -compile => qw(OK DECLINED HTTP_UNAUTHORIZED); +use Apache2::Access (); +use Apache2::RequestUtil (); + +use WeBWorK::Debug; +use WeBWorK::Request; +use WeBWorK::ContentGenerator; +use WeBWorK::DB; +use WeBWorK::Authz; +use WeBWorK::Utils qw/runtime_use/; + +################################################################################ + +=head1 APACHE AUTHEN HANDLER + +=over + +=item handler($r) + +=cut + +sub handler($) { + my ($apache) = @_; + my $r = new WeBWorK::Request($apache); + + my ($res, $sent_pw) = $r->get_basic_auth_pw; + return $res unless $res == Apache2::Const::OK; + + my $webwork_root = $r->dir_config('authen_webwork_root'); + my $webwork_course = $r->dir_config('authen_webwork_course'); + + return fail($r, "authen_webwork_root not set") + unless defined $webwork_root and $webwork_root ne ""; + return fail($r, "authen_webwork_course not set") + unless defined $webwork_course and $webwork_course ne ""; + + # FIXME most of this build-up code is yoinked from lib/WeBWorK.pm + # needs to be factored out somehow + # (for example, the authen module selection code probably belongs in a factory) + + my $ce = eval { new WeBWorK::CourseEnvironment({ + webwork_dir => $webwork_root, + courseName => $webwork_course, + }) }; + $@ and return fail($r, "failed to initialize the course environment: $@"); + $r->ce($ce); + + my $authz = new WeBWorK::Authz($r); + $r->authz($authz); + + # figure out which authentication module to use + my $user_authen_module; + my $proctor_authen_module; + if (ref $ce->{authen}{user_module} eq "HASH") { + if (exists $ce->{authen}{user_module}{$ce->{dbLayoutName}}) { + $user_authen_module = $ce->{authen}{user_module}{$ce->{dbLayoutName}}; + } else { + $user_authen_module = $ce->{authen}{user_module}{"*"}; + } + } else { + $user_authen_module = $ce->{authen}{user_module}; + } + + runtime_use $user_authen_module; + my $authen = $user_authen_module->new($r); + $r->authen($authen); + + my $db = new WeBWorK::DB($ce->{dbLayout}); + $r->db($db); + + # now, here's the problem... WeBWorK::Authen looks at $r->params directly, whereas we + # need to look at $user and $sent_pw. this is a perfect opportunity for a mixin, i think. + my $authenOK; + { + no warnings 'redefine'; + local *WeBWorK::Authen::get_credentials = \&Authen::WeBWorK::HTTPBasic::get_credentials; + local *WeBWorK::Authen::maybe_send_cookie = \&Authen::WeBWorK::HTTPBasic::noop; + local *WeBWorK::Authen::maybe_kill_cookie = \&Authen::WeBWorK::HTTPBasic::noop; + local *WeBWorK::Authen::set_params = \&Authen::WeBWorK::HTTPBasic::noop; + + $authenOK = $authen->verify; + } + + + debug("verify said: '$authenOK'"); + + if ($authenOK) { + debug("this will work!!!"); + #return OK; + return Apache2::Const::OK; + } else { + #return AUTH_REQUIRED; + return Apache2::Const::HTTP_UNAUTHORIZED; + } +} + +sub fail { + my ($r, $msg) = @_; + $r->note_basic_auth_failure; + $r->log_reason($msg, $r->filename); + #return AUTH_REQUIRED; + return Apache2::Const::HTTP_UNAUTHORIZED; +} + +=back + +=cut + +package Authen::WeBWorK::HTTPBasic; + +use strict; +use warnings; +use Apache2::Const -compile => qw(OK DECLINED HTTP_UNAUTHORIZED); +use Apache2::Access (); +use Apache2::RequestUtil (); +#use Apache::Constants qw(:common); +use WeBWorK::Debug; + +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + + my ($res, $sent_pw) = $r->get_basic_auth_pw; + #return unless $res == OK; + return unless $res == Apache2::Const::OK; + my $user_id = $r->user; + #my $user_id = $r->connection->user; + + #if (defined $r->connection->user) { + if (defined $r->user) { + $self->{user_id} = $r->user; + $self->{password} = $sent_pw; + $self->{credential_source} = "http_basic"; + return 1; + } +} + +sub noop {} + +1; diff --git a/lib/Apache/WeBWorK.pm b/lib/Apache/WeBWorK.pm index 31d9252b73..77b176c007 100644 --- a/lib/Apache/WeBWorK.pm +++ b/lib/Apache/WeBWorK.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under @@ -15,146 +15,209 @@ ################################################################################ package Apache::WeBWorK; -#use base qw(DB); =head1 NAME -Apache::WeBWorK - mod_perl handler for WeBWorK. +Apache::WeBWorK - mod_perl handler for WeBWorK 2. =head1 CONFIGURATION This module should be installed as a Handler for the location selected for -WeBWorK on your webserver. Here is an example of a stanza that can be added to -your httpd.conf file to achieve this: - - - PerlFreshRestart On - - SetHandler perl-script - PerlHandler Apache::WeBWorK - - PerlSetVar webwork_root WEBWORK_DIR/webwork-modperl - PerlSetVar pg_root /WEBWORK_DIR/pg - - - use lib 'WEBWORK_DIR/webwork-modperl/lib'; - use lib 'WEBWORK_DIR/pg/lib'; - - - +WeBWorK on your webserver. Refer to the file F for +details. =cut use strict; use warnings; +use HTML::Entities; +use Date::Format; use WeBWorK; +use mod_perl; +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); + +# load correct modules +BEGIN { + if (MP2) { + require Apache2::Log; + Apache2::Log->import; + } else { + require Apache::Log; + Apache::Log->import; + } +} + +################################################################################ + +=head1 APACHE REQUEST HANDLER + +=over + +=item handler($r) + +=cut + sub handler($) { my ($r) = @_; + my $log = $r->log; + my $uri = $r->uri; - my $result; - { # limit the scope of signal localization - # the __WARN__ handler stores warnings for later retrieval - local $SIG{__WARN__} = sub { + # the warning handler accumulates warnings in $r->notes("warnings") for + # later cumulative reporting + my $warning_handler; + if (MP2) { + $warning_handler = sub { + my ($warning) = @_; + chomp $warning; + + my $warnings = $r->notes->get("warnings"); + $warnings .= "$warning\n"; + #my $backtrace = join("\n",backtrace()); + #$warnings .= "$backtrace\n\n"; + $r->notes->set(warnings => $warnings); + + $log->warn("[$uri] $warning"); + }; + } else { + $warning_handler = sub { my ($warning) = @_; + chomp $warning; + my $warnings = $r->notes("warnings"); $warnings .= "$warning\n"; + #my $backtrace = join("\n",backtrace()); + #$warnings .= "$backtrace\n\n"; $r->notes("warnings" => $warnings); - warn $warning; # send it to the log + + $log->warn("[$uri] $warning"); }; - # the __DIE__ handler stores the call stack at the time of an error - local $SIG{__DIE__} = sub { - my ($error) = @_; - # Traces are still causing problems - #my $trace = join "\n", Apache::WeBWorK->backtrace(); - #$r->notes("lastCallStack" => $trace); - die $error; + # the exception handler generates a backtrace when catching an exception + my @backtrace; + my $exception_handler = sub { + @backtrace = backtrace(); + die @_; }; - - $result = eval { WeBWorK::dispatch($r) }; } + # the exception handler generates a backtrace when catching an exception + my @backtrace; + my $exception_handler = sub { + @backtrace = backtrace(); + die @_; + }; + + my $result = do { + local $SIG{__WARN__} = $warning_handler; + local $SIG{__DIE__} = $exception_handler; + + eval { WeBWorK::dispatch($r) }; + }; + if ($@) { - print STDERR "uncaught exception in Apache::WeBWorK::handler: $@"; - my $message = message($r, $@); + my $exception = $@; + + my $warnings = MP2 ? $r->notes->get("warnings") : $r->notes("warnings"); + my $htmlMessage = htmlMessage($r, $warnings, $exception, @backtrace); unless ($r->bytes_sent) { $r->content_type("text/html"); - $r->send_http_header; - $message = "$message"; + $r->send_http_header unless MP2; # not needed for Apache2 + $htmlMessage = "$htmlMessage"; } - $r->print($message); - $r->exit; + print $htmlMessage; + + # log the error to the apache error log + my $textMessage = textMessage($r, $warnings, $exception, @backtrace); + $log->error($textMessage); } return $result; } -sub htmlBacktrace(@) { - foreach (@_) { - s/\/>/g; - $_ = "
    • $_
    • "; - } - return join "\n", @_; -} +=back -sub htmlWarningsList(@) { - foreach (@_) { - next unless m/\S/; - s/\/>/g; - $_ = "
    • $_
    • "; +=cut + +################################################################################ + +=head1 ERROR HANDLING ROUTINES + +=over + +=item backtrace() + +Produce a stack-frame traceback for the calls up through the ones in +Apache::WeBWorK. + +=cut + +sub backtrace { + my $frame = 2; + my @trace; + + while (my ($pkg, $file, $line, $subname) = caller($frame++)) { + last if $pkg eq "Apache::WeBWorK"; + push @trace, "in $subname called at line $line of $file"; } - return join "\n", @_; + + return @trace; } -sub htmlEscape($) { - $_[0] =~ s/\/>/g; - return $_[0]; -} +=back + +=cut + +################################################################################ + +=head1 ERROR OUTPUT FUNCTIONS -sub message($$) { - my ($r, $exception) = @_; +=over + +=item htmlMessage($r, $warnings, $exception, @backtrace) + +Format a message for HTML output reporting an exception, backtrace, and any +associated warnings. + +=cut + +sub htmlMessage($$$@) { + my ($r, $warnings, $exception, @backtrace) = @_; + $warnings = htmlEscape($warnings); $exception = htmlEscape($exception); + + my @warnings = defined $warnings ? split m|<br />|, $warnings : (); #fragile + $warnings = htmlWarningsList(@warnings); + my $backtrace = htmlBacktrace(@backtrace); + my $admin = ($ENV{SERVER_ADMIN} - ? "($ENV{SERVER_ADMIN})" + ? " ($ENV{SERVER_ADMIN})" : ""); - my $context = $r->notes("lastCallStack") - ? htmlBacktrace(split m/\n/, $r->notes("lastCallStack")) - : ""; - my $warnings = $r->notes("warnings") - ? htmlWarningsList(split m/\n/, $r->notes("warnings")) - : ""; - my $method = $r->method; - my $uri = $r->uri; + my $time = time2str("%a %b %d %H:%M:%S %Y", time); + my $method = htmlEscape( $r->method ); + my $uri = htmlEscape( $r->uri ); my $headers = do { - my %headers = $r->headers_in; - join("", map { "$_$headers{$_}" } keys %headers); + my %headers = MP2 ? %{$r->headers_in} : $r->headers_in; + join("", map { "" . htmlEscape($_). "" . + htmlEscape($headers{$_}) . " " } keys %headers); }; return < -

      Software Error

      -

      An error has occured while trying to process your request. For help, please - send mail to this site's webmaster $admin giving the following information - about the error and the date and time that the error occured. Some hints:

      -
        -
      • An error about an undefined value often means that you asked for - an object (like a user, problem set, or problem) that does not exist, and the - we (the programmers) were negligent in checking for that.
      • -
      • An error about permission denied might suggest that the web - server does not have permission to read or write a file or directory.
      • -
      -

      Error message

      -

      $exception

      -

      Call stack

      -
        $context
      -

      Warnings

      +
      +

      WeBWorK error

      +

      An error occured while processing your request. For help, please send mail + to this site's webmaster$admin, including all of the following information as + well as what what you were doing when the error occured.

      +

      $time

      +

      Warning messages

        $warnings
      -

      Request information

      +

      Error messages

      +
      $exception
      +

      Call stack

      +

      The information below can help locate the source of the problem.

      +
        $backtrace
      +

      Request information

      @@ -168,4 +231,99 @@ sub message($$) { EOF } +=item textMessage($r, $warnings, $exception, @backtrace) + +Format a message for HTML output reporting an exception, backtrace, and any +associated warnings. + +=cut + +sub textMessage($$$@) { + my ($r, $warnings, $exception, @backtrace) = @_; + + #my @warnings = defined $warnings ? split m/\n+/, $warnings : (); + #$warnings = textWarningsList(@warnings); + chomp $exception; + my $backtrace = textBacktrace(@backtrace); + my $uri = $r->uri; + + return "[$uri] $exception\n$backtrace"; +} + +=item htmlBacktrace(@frames) + +Formats a list of stack frames in a backtrace as list items for HTML output. + +=cut + +sub htmlBacktrace(@) { + my (@frames) = @_; + foreach my $frame (@frames) { + $frame = htmlEscape($frame); + $frame = "
    • $frame
    • "; + } + return join "\n", @frames; +} + +=item textBacktrace(@frames) + +Formats a list of stack frames in a backtrace as list items for text output. + +=cut + +sub textBacktrace(@) { + my (@frames) = @_; + foreach my $frame (@frames) { + $frame = " * $frame"; + } + return join "\n", @frames; +} + +=item htmlWarningsList(@warnings) + +Formats a list of warning strings as list items for HTML output. + +=cut + +sub htmlWarningsList(@) { + my (@warnings) = @_; + foreach my $warning (@warnings) { + $warning = htmlEscape($warning); + $warning = "
    • $warning
    • "; + } + return join "\n", @warnings; +} + +=item textWarningsList(@warnings) + +Formats a list of warning strings as list items for text output. + +=cut + +sub textWarningsList(@) { + my (@warnings) = @_; + foreach my $warning (@warnings) { + $warning = " * $warning"; + } + return join "\n", @warnings; +} + +=item htmlEscape($string) + +Protect characters that would be interpreted as HTML entities. Then, replace +line breaks with HTML "
      " tags. + +=cut + +sub htmlEscape($) { + my ($string) = @_; + $string = encode_entities($string); + $string =~ s|\n|
      |g; + return $string; +} + +=back + +=cut + 1; diff --git a/lib/MySOAP.pm b/lib/MySOAP.pm new file mode 100644 index 0000000000..0fd13573ae --- /dev/null +++ b/lib/MySOAP.pm @@ -0,0 +1,73 @@ +package MySOAP; + +use constant DEBUG =>0; + + use Apache::Request; + use Apache::Constants qw(:common); + use Apache::File (); + use SOAP::Transport::HTTP; + + my $server = SOAP::Transport::HTTP::Apache + -> dispatch_to('RQP'); + + sub handler { + my $save = $_[0]; + my $r = Apache::Request->instance($_[0]); + + my $header = $r->as_string; + my $args = $r->args; + my $content = $r->content; + my $body=""; + # this will read everything, but then it won't be available for SOAP + my $r2 = Apache::Request->instance($save) if DEBUG; + $r2->read($body, $r2->header_in('Content-length')) if DEBUG; + # + local(*DEBUGLOG); + open DEBUGLOG, ">>/home/gage/debug_info.txt" || die "can't open debug file"; + + + + + ################ + # Handle a wsdl rquest + ################ + my %args_hash = $r->args; + if (exists $args_hash{wsdl}) { + $r->print( $wsdl); + print DEBUGLOG "----------start-------------\n"; + print DEBUGLOG "handle wsdl request\n"; + print DEBUGLOG "\n-header =\n $header\n" ; + + + my $wsdl = `cat /home/gage/rqp.wsdl`; + $r->content_type('application/wsdl+xml'); + $r->send_http_header; + $r->print( $wsdl); + + + print DEBUGLOG "---end--- \n"; + close(DEBUGLOG); + return OK; + ############### + # Handle SOAP request + ############### + } else { + print DEBUGLOG "----------start-------------\n"; + print DEBUGLOG "handle soap request\n"; + print DEBUGLOG "\n-header =\n $header\n" ; #if DEBUG; + print DEBUGLOG "args= $args\n"; + print DEBUGLOG "\nbody= $body\n" if DEBUG; + + $server->handler(@_); + + print DEBUGLOG "---end--- \n"; + close(DEBUGLOG); + + } + + + + + }; + +1; diff --git a/lib/RQP.pm b/lib/RQP.pm new file mode 100644 index 0000000000..cc1bc98021 --- /dev/null +++ b/lib/RQP.pm @@ -0,0 +1,130 @@ +#!/usr/local/bin/perl -w + + +use SOAP::Lite +trace; +#+trace => +#[parameters,trace=>sub{ local($|=1); print LOG "start>>", +#WebworkWebservice::pretty_print_rh(\@_ ),"<new($WW_DIRECTORY, "", "", $COURSENAME); +#print "\$ce = \n", WeBWorK::Utils::pretty_print_rh($ce); +our $db = WeBWorK::DB->new($ce->{dbLayout}); + +#print MYLOG "restarting server\n\n"; +sub test { + open MYLOG, ">>/home/gage/debug_info.txt" ; + local($|=1); + my $som = pop; + my $self = shift; + my $rh_parameter = $som->method; + #$som->match('/'); + #print MYLOG "headers\n", WebworkWebservice::pretty_print_rh($rh_parameter),"\n"; + #warn "parameters ", WebworkWebservice::pretty_print_rh($rh_parameter); + #my $params = $som->paramsall(); + #warn "params ", join(" ", @{$params}); + print MYLOG "body ", WebworkWebservice::pretty_print_rh($som->body); + close(MYLOG); + return "test hi bye"; +} + +sub RQP_ServerInformation { + my $class = shift; + my $soap_som = pop; + my $rh_params= $soap_som->method; + local(*DEBUGLOG); + open DEBUGLOG, ">>/home/gage/debug_info.txt" || die "can't open debug file"; + print DEBUGLOG "--RQP_ServerInformation\n"; + + $rh_out = { 'identifier' => 'http://www.openwebwork.org', + 'name' => 'WeBWorK', + 'description' => 'WeBWorK server. See http://webwork.math.rochester.edu', + 'cloning' => 0, + 'implicitCloning' => 1, + 'rendering' => 1, + 'itemFormats' => ['pg'], + 'renderFormats' => ['xml'], + input => '
      '.WebworkWebservice::pretty_print_rh($rh_params).'
      ', + }; + return $rh_out; +} + +sub RQP_ItemInformation { + my $class = shift; + my $soap_som = pop; + my $rh_params= $soap_som->method; + local(*DEBUGLOG); + open DEBUGLOG, ">>/home/gage/debug_info.txt" || die "can't open debug file"; + print DEBUGLOG "--RQP_ItemInformation\n"; + my $format = 'HTML'; + my $sourceErrors = ''; + $rh_out = { + 'format' => $format, + 'sourceErrors' => $sourceErrors, + 'template' => 1, + 'adaptive' => 1, + 'timeDependent' => 0, + 'canComputeScore' => 1, + 'solutionAvailable' => 0, + 'hintAvailable' => 0, + 'validationPossible' => 1, + 'maxScore' => 1, + 'length' => 1, + input => '
      '.WebworkWebservice::pretty_print_rh($rh_params).'
      ', + }; + close(DEBUGLOG); + return $rh_out; +} + +sub RQP_ProcessTemplate { + + +} + +sub RQP_Clone { + +} +sub RQP_SessionInformation { + my $class = shift; + my $soap_som = pop; + my $rh_params= $soap_som->method; + local(*DEBUGLOG); + open DEBUGLOG, ">>/home/gage/debug_info.txt" || die "can't open debug file"; + print DEBUGLOG "--RQP_SessionInformation\n"; + my $templatevars = $rh_params->{templatevars}; + $templatevars->{seed}=4321; + my $correctResponses = []; + $rh_out = { + 'outcomevars' => {id=>45}, + 'templatevars' => $templatevars, + 'correctResponses' => $correctResponses, + input => '
      '.WebworkWebservice::pretty_print_rh($rh_params).'
      ', + }; + close(DEBUGLOG); + return $rh_out; +} + + +sub RQP_Render { + RQP::Render::RQP_Render(@_); + +} + + +1; diff --git a/lib/RQP/Render.pm b/lib/RQP/Render.pm new file mode 100644 index 0000000000..16b031f8a2 --- /dev/null +++ b/lib/RQP/Render.pm @@ -0,0 +1,150 @@ +#!/usr/local/bin/perl -w + +package RQP::Render; +@ISA = qw( RQP ); +use WebworkWebservice; +use WeBWorK::Utils::Tasks qw(fake_set fake_problem); + +our $WW_DIRECTORY = $WebworkWebservice::WW_DIRECTORY; +our $PG_DIRECTORY = $WebworkWebservice::PG_DIRECTORY; +our $COURSENAME = $WebworkWebservice::COURSENAME; +our $HOST_NAME = $WebworkWebservice::HOST_NAME; +our $HOSTURL ="http://$HOST_NAME:8002"; #FIXME +our $ce =$WebworkWebservice::SeedCE; +# create a local course environment for some course + $ce = WeBWorK::CourseEnvironment->new($WW_DIRECTORY, "", "", $COURSENAME); +#print "\$ce = \n", WeBWorK::Utils::pretty_print_rh($ce); +our $db = WeBWorK::DB->new($ce->{dbLayout}); + +sub RQP_Render { + my $class = shift; + my $soap_som = pop; + my $rh_params= $soap_som->method; + + local(*DEBUGLOG); + open DEBUGLOG, ">>/home/gage/debug_info.txt" || die "can't open debug file"; + print DEBUGLOG "--RQP_Render\n"; + #my $output = WebworkWebservice::pretty_print_rh(\%parameters); + my $source = $rh_params->{source}; + $source =~s//>/g; + my $output = "the first element is ". $self. " and the last ". ref($envelope)."\n\n"; + $output .= WebworkWebservice::pretty_print_rh($rh_params); + + my %templatevars = @{$rh_params->{templatevars}}; + my $templatevars =\%templatevars; + print DEBUGLOG "templateVars (", join("|", @{$rh_params->{templateVars}}),")\n"; + + ############# + # Default environment + ############# + my $userName = "foobar"; + my $user = $db->getUser($userName); + my $key = "asdfasdfasdf"; + + my $problemNumber = (defined($templatevars->{envir}->{probNum}) ) ? $templatevars->{envir}->{probNum} : 1 ; + my $problemSeed = (defined($templatevars->{envir}->{problemSeed})) ? $templatevars->{envir}->{problemSeed} : 1 ; + my $psvn = (defined($templatevars->{envir}->{psvn}) ) ? $templatevars->{envir}->{psvn} : 1234 ; + my $problemStatus = $templatevars->{problem_state}->{recorded_score}|| 0 ; + my $problemValue = (defined($templatevars->{envir}->{problemValue})) ? $templatevars->{envir}->{problemValue} : 1 ; + my $num_correct = $templatevars->{problem_state}->{num_correct} || 0 ; + my $num_incorrect = $templatevars->{problem_state}->{num_incorrect} || 0 ; + my $problemAttempted = ($num_correct && $num_incorrect); + my $lastAnswer = ''; + ######## + # set + ######## + my $setRecord = initializeDefaultSet($db); + + ######## + # problem + ######## + my $problemRecord = initializeDefaultProblem($db); + + + + my $formFields = {}; + my $translationOptions = {}; + my $rh_envir = WeBWorK::PG::defineProblemEnvir( + $class, + $ce, + $user, + $key, + $setRecord, + $problemRecord, + $setRecord->psvn, + $formFields, + $translationOptions, + ); + + $templatevars->{envir} = $rh_envir; + #hack -- root is a restricted term + $templatevars->{envir}->{__files__} = ''; + $templatevars->{envir}->{problemSeed}++; + + ############## + my @templatevars = %$templatevars; + my $rh_out = { + templateVars => packRQParray($templatevars), + index => 'index', + advanceState => 0, + embedPrefix => 'AnSwErAnSwEr', + appletBase => 'unknown', + mediaBase => 'unknown url', + renderFormat => 'HTML', + modalFormat => 'dvipng', + persistentData => 'this is a string', + outcomeVars => [{identifier=>'id',values=>345}], + output => packRQParray($output), + source => $source, + input => '
      '.WebworkWebservice::pretty_print_rh($rh_params).'
      ', + + }; + print DEBUGLOG $output; + close(DEBUGLOG); + return $rh_out; + +} +sub packRQParray { + my $rh_hash=shift; + my @array = (); + foreach $key (keys %{$rh_hash}) { + push @array, {identifier => $key, values => $rh_hash->{$key}}; + } + \@array; +} +sub initializeDefaultSet { + my $db = shift; + + my $setName = 'set0'; + my $setRecord = fake_set($db); + $setRecord->set_id($setName); + $setRecord->set_header("defaultHeader"); + $setRecord->hardcopy_header("defaultHeader"); + $setRecord->open_date(time()-60*60*24*7); # one week ago + $setRecord->due_date(time()+60*60*24*7*2); # in two weeks + $setRecord->answer_date(time()+60*60*24*7*3); # in three weeks + $setRecord->psvn(0); + $setRecord; +} + +sub initializeDefaultProblem { + my $db = shift; + my $userName = 'foobar'; + my $problemNumber = 0; + my $setName = 'set0'; + my $problemRecord = fake_problem($db); + $problemRecord->user_id($userName); + $problemRecord->problem_id(0); + $problemRecord->set_id($setName); + $problemRecord->problem_seed(0); + $problemRecord->status(0); + $problemRecord->value(1); + $problemRecord->attempted(0); + $problemRecord->last_answer(''); + $problemRecord->num_correct(0); + $problemRecord->num_incorrect(0); + $problemRecord; + +} +1; \ No newline at end of file diff --git a/lib/WWSafe.pm b/lib/WWSafe.pm new file mode 100644 index 0000000000..24b4c97f0e --- /dev/null +++ b/lib/WWSafe.pm @@ -0,0 +1,627 @@ +package WWSafe; + +use 5.003_11; +use strict; + +$Safe::VERSION = "2.16"; + +# *** Don't declare any lexicals above this point *** +# +# This function should return a closure which contains an eval that can't +# see any lexicals in scope (apart from __ExPr__ which is unavoidable) + +sub lexless_anon_sub { + # $_[0] is package; + # $_[1] is strict flag; + my $__ExPr__ = $_[2]; # must be a lexical to create the closure that + # can be used to pass the value into the safe + # world + + # Create anon sub ref in root of compartment. + # Uses a closure (on $__ExPr__) to pass in the code to be executed. + # (eval on one line to keep line numbers as expected by caller) + eval sprintf + 'package %s; %s strict; sub { @_=(); eval q[my $__ExPr__;] . $__ExPr__; }', + $_[0], $_[1] ? 'use' : 'no'; +} + +use Carp; +BEGIN { eval q{ + use Carp::Heavy; +} } + +use Opcode 1.01, qw( + opset opset_to_ops opmask_add + empty_opset full_opset invert_opset verify_opset + opdesc opcodes opmask define_optag opset_to_hex +); + +*ops_to_opset = \&opset; # Temporary alias for old Penguins + + +my $default_root = 0; +# share *_ and functions defined in universal.c +# Don't share stuff like *UNIVERSAL:: otherwise code from the +# compartment can 0wn functions in UNIVERSAL +my $default_share = [qw[ + *_ + &PerlIO::get_layers + &UNIVERSAL::isa + &UNIVERSAL::can + &UNIVERSAL::VERSION + &utf8::is_utf8 + &utf8::valid + &utf8::encode + &utf8::decode + &utf8::upgrade + &utf8::downgrade + &utf8::native_to_unicode + &utf8::unicode_to_native +], ($] >= 5.008001 && qw[ + &Regexp::DESTROY +]), ($] >= 5.010 && qw[ + &re::is_regexp + &re::regname + &re::regnames + &re::regnames_count + &Tie::Hash::NamedCapture::FETCH + &Tie::Hash::NamedCapture::STORE + &Tie::Hash::NamedCapture::DELETE + &Tie::Hash::NamedCapture::CLEAR + &Tie::Hash::NamedCapture::EXISTS + &Tie::Hash::NamedCapture::FIRSTKEY + &Tie::Hash::NamedCapture::NEXTKEY + &Tie::Hash::NamedCapture::SCALAR + &Tie::Hash::NamedCapture::flags + &UNIVERSAL::DOES + &version::() + &version::new + &version::("" + &version::stringify + &version::(0+ + &version::numify + &version::normal + &version::(cmp + &version::(<=> + &version::vcmp + &version::(bool + &version::boolean + &version::(nomethod + &version::noop + &version::is_alpha + &version::qv +]), ($] >= 5.011 && qw[ + &re::regexp_pattern +])]; + +sub new { + my($class, $root, $mask) = @_; + my $obj = {}; + bless $obj, $class; + + if (defined($root)) { + croak "Can't use \"$root\" as root name" + if $root =~ /^main\b/ or $root !~ /^\w[:\w]*$/; + $obj->{Root} = $root; + $obj->{Erase} = 0; + } + else { + $obj->{Root} = "Safe::Root".$default_root++; + $obj->{Erase} = 1; + } + + # use permit/deny methods instead till interface issues resolved + # XXX perhaps new Safe 'Root', mask => $mask, foo => bar, ...; + croak "Mask parameter to new no longer supported" if defined $mask; + $obj->permit_only(':default'); + + # We must share $_ and @_ with the compartment or else ops such + # as split, length and so on won't default to $_ properly, nor + # will passing argument to subroutines work (via @_). In fact, + # for reasons I don't completely understand, we need to share + # the whole glob *_ rather than $_ and @_ separately, otherwise + # @_ in non default packages within the compartment don't work. + $obj->share_from('main', $default_share); + Opcode::_safe_pkg_prep($obj->{Root}) if($Opcode::VERSION > 1.04); + return $obj; +} + +sub DESTROY { + my $obj = shift; + $obj->erase('DESTROY') if $obj->{Erase}; +} + +sub erase { + my ($obj, $action) = @_; + my $pkg = $obj->root(); + my ($stem, $leaf); + + no strict 'refs'; + $pkg = "main::$pkg\::"; # expand to full symbol table name + ($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/; + + # The 'my $foo' is needed! Without it you get an + # 'Attempt to free unreferenced scalar' warning! + my $stem_symtab = *{$stem}{HASH}; + + #warn "erase($pkg) stem=$stem, leaf=$leaf"; + #warn " stem_symtab hash ".scalar(%$stem_symtab)."\n"; + # ", join(', ', %$stem_symtab),"\n"; + +# delete $stem_symtab->{$leaf}; + + my $leaf_glob = $stem_symtab->{$leaf}; + my $leaf_symtab = *{$leaf_glob}{HASH}; +# warn " leaf_symtab ", join(', ', %$leaf_symtab),"\n"; + %$leaf_symtab = (); + #delete $leaf_symtab->{'__ANON__'}; + #delete $leaf_symtab->{'foo'}; + #delete $leaf_symtab->{'main::'}; +# my $foo = undef ${"$stem\::"}{"$leaf\::"}; + + if ($action and $action eq 'DESTROY') { + delete $stem_symtab->{$leaf}; + } else { + $obj->share_from('main', $default_share); + } + 1; +} + + +sub reinit { + my $obj= shift; + $obj->erase; + $obj->share_redo; +} + +sub root { + my $obj = shift; + croak("Safe root method now read-only") if @_; + return $obj->{Root}; +} + + +sub mask { + my $obj = shift; + return $obj->{Mask} unless @_; + $obj->deny_only(@_); +} + +# v1 compatibility methods +sub trap { shift->deny(@_) } +sub untrap { shift->permit(@_) } + +sub deny { + my $obj = shift; + $obj->{Mask} |= opset(@_); +} +sub deny_only { + my $obj = shift; + $obj->{Mask} = opset(@_); +} + +sub permit { + my $obj = shift; + # XXX needs testing + $obj->{Mask} &= invert_opset opset(@_); +} +sub permit_only { + my $obj = shift; + $obj->{Mask} = invert_opset opset(@_); +} + + +sub dump_mask { + my $obj = shift; + print opset_to_hex($obj->{Mask}),"\n"; +} + + + +sub share { + my($obj, @vars) = @_; + $obj->share_from(scalar(caller), \@vars); +} + +sub share_from { + my $obj = shift; + my $pkg = shift; + my $vars = shift; + my $no_record = shift || 0; + my $root = $obj->root(); + croak("vars not an array ref") unless ref $vars eq 'ARRAY'; + no strict 'refs'; + # Check that 'from' package actually exists + croak("Package \"$pkg\" does not exist") + unless keys %{"$pkg\::"}; + my $arg; + foreach $arg (@$vars) { + # catch some $safe->share($var) errors: + my ($var, $type); + $type = $1 if ($var = $arg) =~ s/^(\W)//; + # warn "share_from $pkg $type $var"; + *{$root."::$var"} = (!$type) ? \&{$pkg."::$var"} + : ($type eq '&') ? \&{$pkg."::$var"} + : ($type eq '$') ? \${$pkg."::$var"} + : ($type eq '@') ? \@{$pkg."::$var"} + : ($type eq '%') ? \%{$pkg."::$var"} + : ($type eq '*') ? *{$pkg."::$var"} + : croak(qq(Can't share "$type$var" of unknown type)); + } + $obj->share_record($pkg, $vars) unless $no_record or !$vars; +} + +sub share_record { + my $obj = shift; + my $pkg = shift; + my $vars = shift; + my $shares = \%{$obj->{Shares} ||= {}}; + # Record shares using keys of $obj->{Shares}. See reinit. + @{$shares}{@$vars} = ($pkg) x @$vars if @$vars; +} +sub share_redo { + my $obj = shift; + my $shares = \%{$obj->{Shares} ||= {}}; + my($var, $pkg); + while(($var, $pkg) = each %$shares) { + # warn "share_redo $pkg\:: $var"; + $obj->share_from($pkg, [ $var ], 1); + } +} +sub share_forget { + delete shift->{Shares}; +} + +sub varglob { + my ($obj, $var) = @_; + no strict 'refs'; + return *{$obj->root()."::$var"}; +} + + +sub reval { + my ($obj, $expr, $strict) = @_; + my $root = $obj->{Root}; + + my $evalsub = lexless_anon_sub($root,$strict, $expr); + return Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub); +} + +sub rdo { + my ($obj, $file) = @_; + my $root = $obj->{Root}; + + my $evalsub = eval + sprintf('package %s; sub { @_ = (); do $file }', $root); + return Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub); +} + + +1; + +__END__ + +=head1 NAME + +Safe - Compile and execute code in restricted compartments + +=head1 SYNOPSIS + + use WWSafe; + + $compartment = new Safe; + + $compartment->permit(qw(time sort :browse)); + + $result = $compartment->reval($unsafe_code); + +=head1 DESCRIPTION + +The Safe extension module allows the creation of compartments +in which perl code can be evaluated. Each compartment has + +=over 8 + +=item a new namespace + +The "root" of the namespace (i.e. "main::") is changed to a +different package and code evaluated in the compartment cannot +refer to variables outside this namespace, even with run-time +glob lookups and other tricks. + +Code which is compiled outside the compartment can choose to place +variables into (or I variables with) the compartment's namespace +and only that data will be visible to code evaluated in the +compartment. + +By default, the only variables shared with compartments are the +"underscore" variables $_ and @_ (and, technically, the less frequently +used %_, the _ filehandle and so on). This is because otherwise perl +operators which default to $_ will not work and neither will the +assignment of arguments to @_ on subroutine entry. + +=item an operator mask + +Each compartment has an associated "operator mask". Recall that +perl code is compiled into an internal format before execution. +Evaluating perl code (e.g. via "eval" or "do 'file'") causes +the code to be compiled into an internal format and then, +provided there was no error in the compilation, executed. +Code evaluated in a compartment compiles subject to the +compartment's operator mask. Attempting to evaluate code in a +compartment which contains a masked operator will cause the +compilation to fail with an error. The code will not be executed. + +The default operator mask for a newly created compartment is +the ':default' optag. + +It is important that you read the L module documentation +for more information, especially for detailed definitions of opnames, +optags and opsets. + +Since it is only at the compilation stage that the operator mask +applies, controlled access to potentially unsafe operations can +be achieved by having a handle to a wrapper subroutine (written +outside the compartment) placed into the compartment. For example, + + $cpt = new Safe; + sub wrapper { + # vet arguments and perform potentially unsafe operations + } + $cpt->share('&wrapper'); + +=back + + +=head1 WARNING + +The authors make B, implied or otherwise, about the +suitability of this software for safety or security purposes. + +The authors shall not in any case be liable for special, incidental, +consequential, indirect or other similar damages arising from the use +of this software. + +Your mileage will vary. If in any doubt B. + + +=head2 RECENT CHANGES + +The interface to the Safe module has changed quite dramatically since +version 1 (as supplied with Perl5.002). Study these pages carefully if +you have code written to use Safe version 1 because you will need to +makes changes. + + +=head2 Methods in class Safe + +To create a new compartment, use + + $cpt = new Safe; + +Optional argument is (NAMESPACE), where NAMESPACE is the root namespace +to use for the compartment (defaults to "Safe::Root0", incremented for +each new compartment). + +Note that version 1.00 of the Safe module supported a second optional +parameter, MASK. That functionality has been withdrawn pending deeper +consideration. Use the permit and deny methods described below. + +The following methods can then be used on the compartment +object returned by the above constructor. The object argument +is implicit in each case. + + +=over 8 + +=item permit (OP, ...) + +Permit the listed operators to be used when compiling code in the +compartment (in I to any operators already permitted). + +You can list opcodes by names, or use a tag name; see +L. + +=item permit_only (OP, ...) + +Permit I the listed operators to be used when compiling code in +the compartment (I other operators are permitted). + +=item deny (OP, ...) + +Deny the listed operators from being used when compiling code in the +compartment (other operators may still be permitted). + +=item deny_only (OP, ...) + +Deny I the listed operators from being used when compiling code +in the compartment (I other operators will be permitted). + +=item trap (OP, ...) + +=item untrap (OP, ...) + +The trap and untrap methods are synonyms for deny and permit +respectfully. + +=item share (NAME, ...) + +This shares the variable(s) in the argument list with the compartment. +This is almost identical to exporting variables using the L +module. + +Each NAME must be the B of a non-lexical variable, typically +with the leading type identifier included. A bareword is treated as a +function name. + +Examples of legal names are '$foo' for a scalar, '@foo' for an +array, '%foo' for a hash, '&foo' or 'foo' for a subroutine and '*foo' +for a glob (i.e. all symbol table entries associated with "foo", +including scalar, array, hash, sub and filehandle). + +Each NAME is assumed to be in the calling package. See share_from +for an alternative method (which share uses). + +=item share_from (PACKAGE, ARRAYREF) + +This method is similar to share() but allows you to explicitly name the +package that symbols should be shared from. The symbol names (including +type characters) are supplied as an array reference. + + $safe->share_from('main', [ '$foo', '%bar', 'func' ]); + + +=item varglob (VARNAME) + +This returns a glob reference for the symbol table entry of VARNAME in +the package of the compartment. VARNAME must be the B of a +variable without any leading type marker. For example, + + $cpt = new Safe 'Root'; + $Root::foo = "Hello world"; + # Equivalent version which doesn't need to know $cpt's package name: + ${$cpt->varglob('foo')} = "Hello world"; + + +=item reval (STRING) + +This evaluates STRING as perl code inside the compartment. + +The code can only see the compartment's namespace (as returned by the +B method). The compartment's root package appears to be the +C package to the code inside the compartment. + +Any attempt by the code in STRING to use an operator which is not permitted +by the compartment will cause an error (at run-time of the main program +but at compile-time for the code in STRING). The error is of the form +"'%s' trapped by operation mask...". + +If an operation is trapped in this way, then the code in STRING will +not be executed. If such a trapped operation occurs or any other +compile-time or return error, then $@ is set to the error message, just +as with an eval(). + +If there is no error, then the method returns the value of the last +expression evaluated, or a return statement may be used, just as with +subroutines and B. The context (list or scalar) is determined +by the caller as usual. + +This behaviour differs from the beta distribution of the Safe extension +where earlier versions of perl made it hard to mimic the return +behaviour of the eval() command and the context was always scalar. + +Some points to note: + +If the entereval op is permitted then the code can use eval "..." to +'hide' code which might use denied ops. This is not a major problem +since when the code tries to execute the eval it will fail because the +opmask is still in effect. However this technique would allow clever, +and possibly harmful, code to 'probe' the boundaries of what is +possible. + +Any string eval which is executed by code executing in a compartment, +or by code called from code executing in a compartment, will be eval'd +in the namespace of the compartment. This is potentially a serious +problem. + +Consider a function foo() in package pkg compiled outside a compartment +but shared with it. Assume the compartment has a root package called +'Root'. If foo() contains an eval statement like eval '$foo = 1' then, +normally, $pkg::foo will be set to 1. If foo() is called from the +compartment (by whatever means) then instead of setting $pkg::foo, the +eval will actually set $Root::pkg::foo. + +This can easily be demonstrated by using a module, such as the Socket +module, which uses eval "..." as part of an AUTOLOAD function. You can +'use' the module outside the compartment and share an (autoloaded) +function with the compartment. If an autoload is triggered by code in +the compartment, or by any code anywhere that is called by any means +from the compartment, then the eval in the Socket module's AUTOLOAD +function happens in the namespace of the compartment. Any variables +created or used by the eval'd code are now under the control of +the code in the compartment. + +A similar effect applies to I runtime symbol lookups in code +called from a compartment but not compiled within it. + + + +=item rdo (FILENAME) + +This evaluates the contents of file FILENAME inside the compartment. +See above documentation on the B method for further details. + +=item root (NAMESPACE) + +This method returns the name of the package that is the root of the +compartment's namespace. + +Note that this behaviour differs from version 1.00 of the Safe module +where the root module could be used to change the namespace. That +functionality has been withdrawn pending deeper consideration. + +=item mask (MASK) + +This is a get-or-set method for the compartment's operator mask. + +With no MASK argument present, it returns the current operator mask of +the compartment. + +With the MASK argument present, it sets the operator mask for the +compartment (equivalent to calling the deny_only method). + +=back + + +=head2 Some Safety Issues + +This section is currently just an outline of some of the things code in +a compartment might do (intentionally or unintentionally) which can +have an effect outside the compartment. + +=over 8 + +=item Memory + +Consuming all (or nearly all) available memory. + +=item CPU + +Causing infinite loops etc. + +=item Snooping + +Copying private information out of your system. Even something as +simple as your user name is of value to others. Much useful information +could be gleaned from your environment variables for example. + +=item Signals + +Causing signals (especially SIGFPE and SIGALARM) to affect your process. + +Setting up a signal handler will need to be carefully considered +and controlled. What mask is in effect when a signal handler +gets called? If a user can get an imported function to get an +exception and call the user's signal handler, does that user's +restricted mask get re-instated before the handler is called? +Does an imported handler get called with its original mask or +the user's one? + +=item State Changes + +Ops such as chdir obviously effect the process as a whole and not just +the code in the compartment. Ops such as rand and srand have a similar +but more subtle effect. + +=back + +=head2 AUTHOR + +Originally designed and implemented by Malcolm Beattie. + +Reworked to use the Opcode module and other changes added by Tim Bunce. + +Currently maintained by the Perl 5 Porters, . + +=cut + diff --git a/lib/WeBWorK.pm b/lib/WeBWorK.pm index 3dadcece3e..d7fc724447 100644 --- a/lib/WeBWorK.pm +++ b/lib/WeBWorK.pm @@ -1,7 +1,7 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 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 @@ -20,7 +20,6 @@ package WeBWorK; WeBWorK - Dispatch requests to the appropriate content generator. - =head1 SYNOPSIS my $r = Apache->request; @@ -33,147 +32,215 @@ C is the dispatcher for the WeBWorK system. Given an Apache request object, it performs authentication and determines which subclass of C to call. -=head1 REQUEST FORMAT - - FIXME: write this part - summary: the URI controls - =cut - -BEGIN { $main::VERSION = "2.0"; } - -my $timingON = 0; +BEGIN { $main::VERSION = "2.4.9"; } use strict; use warnings; -use Apache::Constants qw(:common REDIRECT DONE); -use Apache::Request; +use Time::HiRes qw/time/; +use WeBWorK::Localize; +# load WeBWorK::Constants before anything else +# this sets package variables in several packages +use WeBWorK::Constants; + use WeBWorK::Authen; use WeBWorK::Authz; use WeBWorK::CourseEnvironment; use WeBWorK::DB; -use WeBWorK::Timing; +use WeBWorK::Debug; +use WeBWorK::Request; use WeBWorK::Upload; -use WeBWorK::Utils qw(runtime_use); - -=head1 THE C<&dispatch> FUNCTION - -The C<&dispatch> function takes an Apache request object (REQUEST) and returns -an apache status code. Below is an overview of its operation: +use WeBWorK::URLPath; +use WeBWorK::CGI; +use WeBWorK::Utils qw(runtime_use writeTimingLogEntry); + +use mod_perl; +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); + +# Apache2 needs upload class +BEGIN { + if (MP2) { + require Apache2::Upload; + Apache2::Upload->import(); + require Apache2::RequestUtil; + Apache2::RequestUtil->import(); + } +} -=over +use constant LOGIN_MODULE => "WeBWorK::ContentGenerator::Login"; +use constant PROCTOR_LOGIN_MODULE => "WeBWorK::ContentGenerator::LoginProctor"; + +BEGIN { + # pre-compile all content generators + # Login and LoginProctor need to be handled separately, since they don't have paths + map { eval "require $_"; die $@ if $@ } + WeBWorK::URLPath->all_modules, + LOGIN_MODULE, + PROCTOR_LOGIN_MODULE; + # other candidates for preloading: + # - DB Record, Schema, and Driver classes (esp. Driver::SQL as it loads DBI) + # - CourseManagement subclasses (ditto. sql_single.pm) + # - WeBWorK::PG::Local, which loads WeBWorK::PG::Translator + # - Authen subclasses +} -=cut +our %SeedCE; sub dispatch($) { my ($apache) = @_; - my $r = Apache::Request->new($apache); - # have to deal with unpredictable GET or POST data, and sift - # through it for the key. So use Apache::Request + my $r = WeBWorK::Request->new($apache); - # This stuff is pretty much copied out of the O'Reilly mod_perl book. - # It's for figuring out the basepath. I may change this up if I find a - # better way to do it. - my $path_info = $r->path_info || ""; - $path_info =~ s!/+!/!g; # strip multiple forward slashes - my $current_uri = $r->uri; - my $args = $r->args; + my $method = $r->method; + my $location = $r->location; + my $uri = $r->uri; + my $path_info = $r->path_info | ""; + my $args = $r->args || ""; + my $dir_config = $r->dir_config; + my %conf_vars = map { $_ => $dir_config->{$_} } grep { /^webwork_/ } keys %$dir_config; + @SeedCE{keys %conf_vars} = values %conf_vars; - my ($urlRoot) = $current_uri =~ m/^(.*)$path_info/; - -=item Ensure that the URI ends with a "/" - -Parts of WeBWorK assume that the current URI of a request ends with a "/". If -this is not the case, a redirection is issued to add the "/". This action will -discard any POST data associated with the request, so it is essential that all -POST requests include a "/" at the end of the URI. - -=cut - - # If it's a valid WeBWorK URI, it ends in a /. This is assumed - # alllll over the place. - unless (substr($current_uri,-1) eq '/') { - $r->header_out(Location => "$current_uri/" . ($args ? "?$args" : "")); - return REDIRECT; - # *** any post data gets lost here -- fix that. - # (actually, it's not a problem, since all URLs generated - # from within the system have trailing slashes, and we don't - # need POST data from outside the system anyway!) + debug("\n\n===> Begin " . __PACKAGE__ . "::dispatch() <===\n\n"); + debug("Hi, I'm the new dispatcher!\n"); + debug(("-" x 80) . "\n"); + + debug("Okay, I got some basic information:\n"); + debug("The apache location is $location\n"); + debug("The request method is $method\n"); + debug("The URI is $uri\n"); + debug("The path-info is $path_info\n"); + debug("The argument string is $args\n"); + #debug("The WeBWorK root directory is $webwork_root\n"); + #debug("The PG root directory is $pg_root\n"); + debug(("-" x 80) . "\n"); + + debug("The first thing we need to do is munge the path a little:\n"); + + ###################################################################### + # Create a URLPath object + ###################################################################### + my ($path) = $uri =~ m/$location(.*)/; + $path = "/" if $path eq ""; # no path at all + + debug("We can't trust the path-info, so we make our own path.\n"); + debug("path-info claims: $path_info\n"); + debug("but it's really: $path\n"); + debug("(if it's empty, we set it to \"/\".)\n"); + + $path =~ s|/+|/|g; + debug("...and here it is without repeated slashes: $path\n"); + + # lookbehind assertion for "not a slash" + # matches the boundary after the last char + $path =~ s|(?<=[^/])$|/|; + debug("...and here it is with a trailing slash: $path\n"); + + debug(("-" x 80) . "\n"); + + debug("Now we need to look at the path a little to figure out where we are\n"); + + debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); + my $urlPath = WeBWorK::URLPath->newFromPath($path, $r); + # pointer to parent request for access to the $ce and language translation ability + # need to add this pointer whenever a new URLPath is created. + debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); + + unless ($urlPath) { + debug("This path is invalid... see you later!\n"); + die "The path '$path' is not valid.\n"; } - # Create the @components array, which contains the path specified in the URL - my($junk, @components) = split "/", $path_info; - my $webwork_root = $r->dir_config('webwork_root'); # From a PerlSetVar in httpd.conf - my $pg_root = $r->dir_config('pg_root'); # From a PerlSetVar in httpd.conf - my $course = shift @components; - -=item Read the course environment - -C is used to read the F configuration -file. If a course name was given in the request's URI, it is passed to -C. In this case, the course-specific configuration -file (usually F) is also read by C at -this point. - -See also L. - -=cut - - # Try to get the course environment. - my $ce = eval {WeBWorK::CourseEnvironment->new($webwork_root, $urlRoot, $pg_root, $course);}; - if ($@) { # If there was an error getting the requested course - die "Failed to read course environment for $course: $@"; + my $displayModule = $urlPath->module; + my %displayArgs = $urlPath->args; + + unless ($displayModule) { + debug("The display module is empty, so we can DECLINE here.\n"); + die "No display module found for path '$path'."; } - -=item If no course was given, go to the site home page - -If the URI did not include the name of a course, a redirection is issued to the -site home page, given but the course environemnt variable -C<$ce-E{webworkURLs}-E{home}>. - -=cut - - # If no course was specified, redirect to the home URL - unless (defined $course) { - $r->header_out(Location => $ce->{webworkURLs}->{home}); - return REDIRECT; + + debug("The display module for this path is: $displayModule\n"); + debug("...and here are the arguments we'll pass to it:\n"); + foreach my $key (keys %displayArgs) { + debug("\t$key => $displayArgs{$key}\n"); } - -=item If the given course does not exist, fail - -If the URI did include the name of a course, but the course directory was not -found, an exception is thrown. - -=cut - - # Freak out if the requested course doesn't exist. For now, this is just a - # check to see if the course directory exists. - my $courseDir = $ce->{webworkDirs}->{courses} . "/$course"; - unless (-e $courseDir) { - die "Course directory for $course ($courseDir) not found. Perhaps the course does not exist?"; + + my $selfPath = $urlPath->path; + my $parent = $urlPath->parent; + my $parentPath = $parent ? $parent->path : ""; + + debug("Reconstructing the original path gets us: $selfPath\n"); + debug("And we can generate the path to our parent, too: $parentPath\n"); + debug("(We could also figure out who our children are, but we'd need to supply additional arguments.)\n"); + debug(("-" x 80) . "\n"); + + debug("The URLPath looks good, we'll add it to the request.\n"); + $r->urlpath($urlPath); + + debug("Now we want to look at the parameters we got.\n"); + + debug("The raw params:\n"); + foreach my $key ($r->param) { + my @vals = $r->param($key); + my $vals = join(", ", map { "'$_'" } @vals); + debug("\t$key => $vals\n"); + } + + #mungeParams($r); + # + #debug("The munged params:\n"); + #foreach my $key ($r->param) { + # debug("\t$key\n"); + # debug("\t\t$_\n") foreach $r->param($key); + #} + + debug(("-" x 80) . "\n"); + + my $apache_hostname = $r->hostname; + my $apache_port = $r->get_server_port; + my $apache_is_ssl = ($r->subprocess_env('https') ? 1 : ""); + my $apache_root_url; + if ($apache_is_ssl) { + $apache_root_url = "https://$apache_hostname"; + $apache_root_url .= ":$apache_port" if $apache_port != 443; + } else { + $apache_root_url = "http://$apache_hostname"; + $apache_root_url .= ":$apache_port" if $apache_port != 80; + } + + + #################################################################### + # Create Course Environment $ce + #################################################################### + debug("We need to get a course environment (with or without a courseID!)\n"); + my $ce = eval { new WeBWorK::CourseEnvironment({ + %SeedCE, + courseName => $displayArgs{courseID}, + # this is kind of a hack, but it's really the only sane way to get this + # server information into the PG box + apache_hostname => $apache_hostname, + apache_port => $apache_port, + apache_is_ssl => $apache_is_ssl, + apache_root_url => $apache_root_url, + }) }; + $@ and die "Failed to initialize course environment: $@\n"; + debug("Here's the course environment: $ce\n"); + $r->ce($ce); + + + ###################### + # Localizing language + ###################### + my $language= $ce->{language} || "en"; + # $r->language_handle( WeBWorK::Localize->get_handle($language) ); + $r->language_handle( WeBWorK::Localize::getLoc($language) ); + + my @uploads; + if (MP2) { + my $upload_table = $r->upload; + @uploads = values %$upload_table if defined $upload_table; + } else { + @uploads = $r->upload; } - -=item Initialize the database system - -A C object is created from the current course environment. - -See also L. - -=cut - - # Bring up a connection to the database (for Authen/Authz, and eventually - # to be passed to content generators, when we clean this file up). - my $db = WeBWorK::DB->new($ce); - -=item Capture any uploads - -Before checking authentication, we store any uploads sent by the client -and replace them with parameters referencing the stored uploads. - -=cut - - my @uploads = $r->upload; foreach my $u (@uploads) { # make sure it's a "real" upload next unless $u->filename; @@ -188,252 +255,321 @@ and replace them with parameters referencing the stored uploads. my $hash = $upload->hash; $r->param($u->name => "$id $hash"); } - -=item Check authentication - -Use C to verify that the remote user has authenticated. - -See also L. - -=cut - - ### Begin dispatching ### - my $contentGenerator = ""; - my @arguments = (); + # create these out here. they should fail if they don't have the right information + # this lets us not be so careful about whether these objects are defined when we use them. + # instead, we just create the behavior that if they don't have a valid $db they fail. + my $authz = new WeBWorK::Authz($r); + $r->authz($authz); + + # figure out which authentication modules to use + #my $user_authen_module; + #my $proctor_authen_module; + #if (ref $ce->{authen}{user_module} eq "HASH") { + # if (exists $ce->{authen}{user_module}{$ce->{dbLayoutName}}) { + # $user_authen_module = $ce->{authen}{user_module}{$ce->{dbLayoutName}}; + # } else { + # $user_authen_module = $ce->{authen}{user_module}{"*"}; + # } + #} else { + # $user_authen_module = $ce->{authen}{user_module}; + #} + #if (ref $ce->{authen}{proctor_module} eq "HASH") { + # if (exists $ce->{authen}{proctor_module}{$ce->{dbLayoutName}}) { + # $proctor_authen_module = $ce->{authen}{proctor_module}{$ce->{dbLayoutName}}; + # } else { + # $proctor_authen_module = $ce->{authen}{proctor_module}{"*"}; + # } + #} else { + # $proctor_authen_module = $ce->{authen}{proctor_module}; + #} + + my $user_authen_module = WeBWorK::Authen::class($ce, "user_module"); + + runtime_use $user_authen_module; + my $authen = $user_authen_module->new($r); + debug("Using user_authen_module $user_authen_module: $authen\n"); + $r->authen($authen); + + my $db; + + if ($displayArgs{courseID}) { + debug("We got a courseID from the URLPath, now we can do some stuff:\n"); - # WeBWorK::Authen::verify erases the passwd field and sets the key field - # if login is successful. - if (!WeBWorK::Authen->new($r, $ce, $db)->verify) { - $contentGenerator = "WeBWorK::ContentGenerator::Login"; - @arguments = (); - } - else { - -=item Determine if the user is allowed to set C - -Use C to determine if the user is allowed to set -C. If so, set it to the requested value (or set it to the real -user name if no value is supplied). If not, set it to the real user name. - -See also L. - -=cut - - # After we are authenticated, there are some things that need to be - # sorted out, Authorization-wize, before we start dispatching to individual - # content generators. - my $user = $r->param("user"); - my $effectiveUser = $r->param("effectiveUser") || $user; - my $authz = WeBWorK::Authz->new($r, $ce, $db); - my $su_authorized = $authz->hasPermissions($user, "become_student", $effectiveUser); - $effectiveUser = $user unless $su_authorized; - $r->param("effectiveUser", $effectiveUser); - -=item Determine the appropriate subclass of C to call based on the URI. - -The dispatcher implements a virtual heirarchy that looks like this: - - $courseID ($courseID) - list of sets - hardcopy (Hardcopy Generator) - generate hardcopy for user/set pairs - options (User Options) - change email address and password - feedback (Feedback) - send feedback to professor via email - logout (Logout) - expire session and erase authentication tokens - test (Test) - display request information - quiz_mode (Quiz) - "quiz" containing all problems from a set - instructor (Instructor Tools) - main menu for instructor tools - add_users (Add Users) - to be removed - scoring (Scoring Tools) - generate scoring files for problem sets - scoringDownload - send a scoring file to the client - scoring_totals - ??? - users (Users) - view/edit users - $userID ($userID) - user detail for given user - sets (Assigned Sets) - view/edit sets assigned to given user - sets (Sets) - list of sets, add new sets, delete existing sets - $setID - view/edit the given set - problems (Problems) - view/edit problems in the given set - $problemID - this is where the pg problem editor SHOULD be - users (Users Assigned) - view/edit users to whom the given set is assigned - pgProblemEditor (Problem Source) - edit the source of a problem - send_mail (Mail Merge) - send mail to users in course - show_answers (Answers Submitted) - show submitted answers - stats (Statistics) - show statistics - files (File Transfer) - transfer files to/from the client - $setID ($setID) - list of problems in the given set - $problemID ($problemID) - interactive display of problem - -=cut - - my $arg = shift @components; - if (not defined $arg) { # We want the list of problem sets - $contentGenerator = "WeBWorK::ContentGenerator::ProblemSets"; - @arguments = (); - } - elsif ($arg eq "hardcopy") { - my $setID = shift @components; - $contentGenerator = "WeBWorK::ContentGenerator::Hardcopy"; - @arguments = ($setID); + unless (-e $ce->{courseDirs}->{root}) { + die "Course '$displayArgs{courseID}' not found: $!"; } - elsif ($arg eq "options") { - $contentGenerator = "WeBWorK::ContentGenerator::Options"; - @arguments = (); - } - elsif ($arg eq "feedback") { - $contentGenerator = "WeBWorK::ContentGenerator::Feedback"; - @arguments = (); - } - elsif ($arg eq "logout") { - $contentGenerator = "WeBWorK::ContentGenerator::Logout"; - @arguments = (); - } - elsif ($arg eq "test") { - $contentGenerator = "WeBWorK::ContentGenerator::Test"; - @arguments = (); - } - elsif ($arg eq "quiz_mode" ) { - $contentGenerator = "WeBWorK::ContentGenerator::GatewayQuiz"; - @arguments = @components; - } - elsif ($arg eq "instructor") { - my $instructorArgument = shift @components; + + debug("...we can create a database object...\n"); + $db = new WeBWorK::DB($ce->{dbLayout}); + debug("(here's the DB handle: $db)\n"); + $r->db($db); + + my $authenOK = $authen->verify; + if ($authenOK) { + my $userID = $r->param("user"); + debug("Hi, $userID, glad you made it.\n"); - if (not defined $instructorArgument) { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Index"; - @arguments = (); - } - elsif ($instructorArgument eq "add_users") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::AddUsers"; - @arguments = (); - } - elsif ($instructorArgument eq "scoring") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Scoring"; - @arguments = (); - } -# elsif ($instructorArgument eq "scoring_totals") { -# $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ScoringTotals"; -# @arguments = (); -# } - elsif ($instructorArgument eq "scoringDownload") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ScoringDownload"; - @arguments = (); - } - elsif ($instructorArgument eq "users") { - my $userID = shift @components; - - if (defined $userID) { - my $userArg = shift @components; - if (defined $userArg) { - if ($userArg eq "sets") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser"; - @arguments = ($userID); - } - } - else { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::UserDetail"; - @arguments = ($userID); - } - } - else { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::UserList"; - @arguments = (); + # tell authorizer to cache this user's permission level + $authz->setCachedUser($userID); + + debug("Now we deal with the effective user:\n"); + my $eUserID = $r->param("effectiveUser") || $userID; + debug("userID=$userID eUserID=$eUserID\n"); + if ($userID ne $eUserID) { + debug("userID and eUserID differ... seeing if userID has 'become_student' permission.\n"); + my $su_authorized = $authz->hasPermissions($userID, "become_student"); + if ($su_authorized) { + debug("Ok, looks like you're allowed to become $eUserID. Whoopie!\n"); + } else { + debug("Uh oh, you're not allowed to become $eUserID. Nice try!\n"); + die "You are not allowed to act as another user.\n"; } } - elsif ($instructorArgument eq "sets") { - my $setID = shift @components; + + # set effectiveUser in case it was changed or not set to begin with + $r->param("effectiveUser" => $eUserID); + + # if we're doing a proctored test, after the user has been authenticated + # we need to also check on the proctor. note that in the gateway quiz + # module we double check this, to be sure that someone isn't taking a + # proctored quiz but calling the unproctored ContentGenerator + my $urlProducedPath = $urlPath->path(); + if ( $urlProducedPath =~ /proctored_quiz_mode/i ) { + my $proctor_authen_module = WeBWorK::Authen::class($ce, "proctor_module"); + runtime_use $proctor_authen_module; + my $authenProctor = $proctor_authen_module->new($r); + debug("Using proctor_authen_module $proctor_authen_module: $authenProctor\n"); + my $procAuthOK = $authenProctor->verify(); - if (defined $setID) { - my $setArg = shift @components; - - if (defined $setArg) { - if ($setArg eq "problems") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemList"; - @arguments = ($setID); - } - elsif ($setArg eq "users") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Assigner"; - @arguments = ($setID); - } - } - else { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemSetEditor"; - @arguments = ($setID); - } - } - else { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemSetList"; - @arguments = (); - + if (not $procAuthOK) { + $displayModule = PROCTOR_LOGIN_MODULE; } } - elsif ($instructorArgument eq "pgProblemEditor") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::PGProblemEditor"; - @arguments = @components; - } - elsif ($instructorArgument eq "send_mail") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::SendMail"; - @arguments = @components; - } - elsif ($instructorArgument eq "show_answers") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ShowAnswers"; - @arguments = @components; - } - elsif ($instructorArgument eq "stats") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Stats"; - @arguments = @components; - } - elsif ($instructorArgument eq "files") { - $contentGenerator = "WeBWorK::ContentGenerator::Instructor::FileXfer"; - @arguments = @components; - } + } else { + debug("Bad news: authentication failed!\n"); + $displayModule = LOGIN_MODULE; + debug("set displayModule to $displayModule\n"); } - else { - # $arg is a set ID - my $setID = $arg; - my $problemID = shift @components; - - if (defined $problemID) { - $contentGenerator = "WeBWorK::ContentGenerator::Problem"; - @arguments = ($setID, $problemID); - } - else { - $contentGenerator = "WeBWorK::ContentGenerator::ProblemSet"; - @arguments = ($setID); + } + + # store the time before we invoke the content generator + my $cg_start = time; # this is Time::HiRes's time, which gives floating point values + + debug(("-" x 80) . "\n"); + debug("Finally, we'll load the display module...\n"); + + runtime_use($displayModule); + + debug("...instantiate it...\n"); + + my $instance = $displayModule->new($r); + + debug("...and call it:\n"); + debug("-------------------- call to ${displayModule}::go\n"); + + my $result = $instance->go(); + + debug("-------------------- call to ${displayModule}::go\n"); + + my $cg_end = time; + my $cg_duration = $cg_end - $cg_start; + writeTimingLogEntry($ce, "[".$r->uri."]", sprintf("runTime = %.3f sec", $cg_duration)." ".$ce->{dbLayoutName}, ""); + + debug("returning result: " . (defined $result ? $result : "UNDEF") . "\n"); + #@LimitedPolynomial::BOP::ISA; #FIXME this is needed to zero out + #@LimitedPolynomial::UOP::ISA; + #\@LimitedPolynomial::BOP::ISA and prevent error messages of the form + #[Sat May 15 14:23:08 2010] [warn] [client 127.0.0.1] [/webwork2/gage_course/test_set/6/] + #Can't locate package LimitedPolynomial::BOP for @LimitedPolynomial::BOP::add::ISA at /opt/webwork/webwork2/lib/Apache/WeBWorK.pm line 115., referer: http://localhost/webwork2/gage_course/test_set/6/ no one knows why + return $result; +} + +sub mungeParams { + my ($r) = @_; + + my @paramQueue; + + # remove all the params from the request, and store them in the param queue + foreach my $key ($r->param) { + push @paramQueue, [ $key => [ $r->param($key) ] ]; + $r->parms->unset($key) + } + + # exhaust the param queue, decoding encoded params + while (@paramQueue) { + my ($key, $values) = @{ shift @paramQueue }; + + if ($key =~ m/\,/) { + # we have multiple params encoded in a single param + # split them up and add them to the end of the queue + push @paramQueue, map { [ $_, $values ] } split m/\,/, $key; + } elsif ($key =~ m/\:/) { + # we have a whole param encoded in a key + # split it up and add it to the end of the queue + my ($newKey, $newValue) = split m/\:/, $key; + push @paramQueue, [ $newKey, [ $newValue ] ]; + } else { + # this is a "normal" param + # add it to the param list + if (defined $r->param($key)) { + # the param already exists -- append the values we have + $r->param($key => [ $r->param($key), @$values ]); + } else { + # the param doesn't exist -- create it with the values we have + $r->param($key => $values); } } } +} -=item Call the selected content generator -Instantiate the selected subclass of content generator and call its C<&go> method. Store the result. +# labeled_input subroutine +# +# Creates a form input element with a label added to the correct place. +# Takes in up to six parameters: +# +# -type (type of input element), -name (name of input element), -id (id of the input element), -value (value of the input element), -label_text (the text on the label), -label_id (the id of the label) +# +# If any of the parameters are not specified, they default to "none". +# +# UPDATE: updated lable tags so that their "for" property will point to the id of the element that they are labeling. This means that entering an id for the input element becomes essentially mandatory if you want the tag to work correctly. -=cut +# DEPRECATED - see below - my $result; - if ($contentGenerator) { - runtime_use($contentGenerator); - my $cg = $contentGenerator->new($r, $ce, $db); - - $WeBWorK::timer = WeBWorK::Timing->new("${contentGenerator}::go(@arguments)") if $timingON == 1; - $WeBWorK::timer->start if $timingON == 1; - - $result = $cg->go(@arguments); - - $WeBWorK::timer->stop if $timingON == 1; - $WeBWorK::timer->save if $timingON == 1; - } else { - $result = NOT_FOUND; +# sub labeled_input +# { + # my %param = (-type=>"none", -name=>"none", -value=>"none", -id=>"none", -label_text=>"none", -label_id=>"none", @_); + + # if($param{-type} eq "text" or $param{-type} eq "password" or $param{-type} eq "file"){ + # return CGI::label({-id=>$param{-label_id}, -for=>$param{-id}},$param{-label_text}).CGI::input({-type=>$param{-type}, -name=>$param{-name}, -value=>$param{-value}, -id=>$param{-id}}).CGI::br(); + # } + # elsif($param{-type} eq "checkbox" or $param{-type} eq "radio"){ + # return CGI::input({-type=>$param{-type}, -name=>$param{-name}, -value=>$param{-value}, -id=>$param{-id}}).CGI::label({-id=>$param{-label_id}, -for=>$param{-id}},$param{-label_text}).CGI::br(); + # } + # elsif($param{-type} eq "submit" or $param{-type} eq "button" or $param{-type} eq "reset"){ + # return CGI::input({-type=>$param{-type}, -name=>$param{-name}, -value=>$param{-value}, -id=>$param{-id}}).CGI::br(); + # } + # else{ + # return "Not a valid input type"; + # } +# } + + +# CGI_labeled_input subroutine + +# A replacement to the labeled_input subroutine above, created when it was determined that the old subroutine was limited in that it did not allow for attributes other than the ones that it specified. + +# This subroutine rectifies that problem by taking in attributes for the input elements and label elements as hashes and simply entering them into the CGI routines, which already support attributes as hash parameters. + +# The way it attaches label tags is similar to the labeled_input subroutine. + +# This subroutine has also been expanded to be able to handle select elements. + +# Five parameters are taken in as a hash: -type (specifying the type of the input element), -id (specifying the id of the input element), -label_text (specifying the text to go in the label), -input_attr (a hash specifying any additional attributes for the input element, if any), and -label_attr (a hash specifying additional attributes for the label element, if any). + +# As before, all parameters are optional, with the scalar parameters defaulting to "none" and the hash parameters defaulting to empty. + + +sub CGI_labeled_input +{ + my %param = (-type=>"none", -id=>"none", -label_text=>"none", -input_attr=>{}, -label_attr=>{}, @_); + + $param{-input_attr}{-type} = $param{-type}; + $param{-input_attr}{-id} = $param{-id}; + $param{-label_attr}{-for} = $param{-id}; + + if($param{-type} eq "text" or $param{-type} eq "password" or $param{-type} eq "file"){ + return CGI::label($param{-label_attr},$param{-label_text}).CGI::input($param{-input_attr}); + } + elsif($param{-type} eq "checkbox" or $param{-type} eq "radio"){ + return CGI::input($param{-input_attr}).CGI::label($param{-label_attr},$param{-label_text}); + } + elsif($param{-type} eq "submit" or $param{-type} eq "button" or $param{-type} eq "reset"){ + return CGI::input($param{-input_attr}); } + elsif($param{-type} eq "select"){ + return CGI::label($param{-label_attr},$param{-label_text}).CGI::popup_menu($param{-input_attr}); + } + elsif($param{-type} eq "textarea"){ + return CGI::label($param{-label_attr},$param{-label_text}).CGI::br().CGI::br().CGI::textarea($param{-input_attr}); + } + else{ + "Not a valid input type"; + } +} -=item Return the result of calling the content generator +# split_cap subroutine - ghe3 -The return value of the content generator's C<&go> function is returned. +# A sort of wrapper for the built-in split function which uses capital letters as a delimiter, and returns a string containing the separated substrings separated by a whitespace. Used to make actionID's more readable. -=cut +sub split_cap +{ + my $str = shift; + + my @str_arr = split(//,$str); + my $count = scalar(@str_arr); + + my $i = 0; + my $prev = 0; + my @result = (); + my $hasCapital = 0; + foreach(@str_arr){ + if($_ =~ /[A-Z]/){ + $hasCapital = 1; + push(@result, join("", @str_arr[$prev..$i-1])); + $prev = $i; + } + $i++; + } + + unless($hasCapital){ + return $str; + } + else{ + push(@result, join("", @str_arr[$prev..$count-1])); + return join(" ",@result); + } +} + +# underscore_to_whitespace subroutine +# a simple subroutine for converting underscores in a given string to whitespace + +sub underscore_to_whitespace{ + my $str = shift; + + my @strArr = split("",$str); + foreach(@strArr){ + if($_ eq "_"){ + $_ = " " + } + } + + my $result = join("",@strArr); + return $result; } -=back +sub remove_duplicates{ + my @arr = @_; + + my %unique; + my @result; + + foreach(@arr){ + if(defined $unique{$_}){ + next; + } + else{ + push(@result, $_); + $unique{$_} = "seen"; + } + } + + return @result; + +} =head1 AUTHOR diff --git a/lib/WeBWorK/Authen.pm b/lib/WeBWorK/Authen.pm index 537aa77342..81c0968a53 100644 --- a/lib/WeBWorK/Authen.pm +++ b/lib/WeBWorK/Authen.pm @@ -1,7 +1,7 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authen.pm,v 1.62 2007/03/06 22:03:15 glarose 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 @@ -20,235 +20,737 @@ package WeBWorK::Authen; WeBWorK::Authen - Check user identity, manage session keys. +=head1 SYNOPSIS + + # get the name of the appropriate Authen class, based on the %authen hash in $ce + my $class_name = WeBWorK::Authen::class($ce, "user_module"); + + # load that class + require $class_name; + + # create an authen object + my $authen = $class_name->new($r); + + # verify credentials + $authen->verify or die "Authentication failed"; + + # verification status is stored for quick retrieval later + my $auth_ok = $authen->was_verified; + + # for some reason, you might want to clear that cache + $authen->forget_verification; + +=head1 DESCRIPTION + +WeBWorK::Authen is the base class for all WeBWorK authentication classes. It +provides default authentication behavior which can be selectively overridden in +subclasses. + =cut use strict; use warnings; +use WeBWorK::Cookie; +use Date::Format; +use Socket qw/unpack_sockaddr_in inet_ntoa/; # for logging +use WeBWorK::Debug; +use WeBWorK::Utils qw/writeCourseLog/; +use WeBWorK::Localize; +use URI::Escape; -sub new($$$) { - my $invocant = shift; - my $class = ref($invocant) || $invocant; - my $self = {}; - ($self->{r}, $self->{ce}, $self->{db}) = @_; - bless $self, $class; - return $self; -} +use mod_perl; +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); -# um, this isn't used. move it to Utils? -#sub generatePassword($$$) { -# my ($self, $userID, $clearPassword) = @_; -# my $salt = join("", ('.','/','0'..'9','A'..'Z','a'..'z')[rand 64, rand 64]); -# my $cryptPassword = crypt($clearPassword, $salt); -# return WeBWorK::DB::Record::Password->new(user_id=>$userID, password=>$password); -#} -sub checkPassword($$$) { - my ($self, $userID, $possibleClearPassword) = @_; - my $Password = $self->{db}->getPassword($userID); # checked - return 0 unless defined $Password; - my $possibleCryptPassword = crypt($possibleClearPassword, $Password->password()); - return $possibleCryptPassword eq $Password->password(); -} +##################### +## WeBWorK-tr modification +## If GENERIC_ERROR_MESSAGE is constant, we can't translate it -sub generateKey($$) { - my ($self, $userID) = @_; - my @chars = @{ $self->{ce}->{sessionKeyChars} }; - my $length = $self->{ce}->{sessionKeyLength}; - srand; - my $key = join ("", @chars[map rand(@chars), 1 .. $length]); - return WeBWorK::DB::Record::Key->new(user_id=>$userID, key=>$key, timestamp=>time); +#use vars qw($GENERIC_ERROR_MESSAGE); +our $GENERIC_ERROR_MESSAGE = ""; # define in new + +## WeBWorK-tr end modification +##################### + +use constant COOKIE_LIFESPAN => 60*60*24*30; # 30 days +#use constant GENERIC_ERROR_MESSAGE => "Invalid user ID or password."; + + +BEGIN { + if (MP2) { + require APR::SockAddr; + APR::SockAddr->import(); + require Apache2::Connection; + Apache2::Connection->import(); + require APR::Request::Error; + APR::Request::Error->import; + } } -sub checkKey($$$) { - my ($self, $userID, $possibleKey) = @_; - my $Key = $self->{db}->getKey($userID); # checked - return 0 unless defined $Key; - if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) { - if ($possibleKey eq $Key->key()) { - # unexpired and matches -- update timestamp - $Key->timestamp(time); - $self->{db}->putKey($Key); - return 1; +################################################################################ +# Public API +################################################################################ + +=head1 FACTORY + +=over + +=item class($ce, $type) + +This subroutine consults the given WeBWorK::CourseEnvironment object to +determine which WeBWorK::Authen subclass should be used. $type can be any key +given in the %authen hash in the course environment. If the type is not found in +the %authen hash, an exception is thrown. + +=cut + +sub class { + my ($ce, $type) = @_; + + if (exists $ce->{authen}{$type}) { + if (ref $ce->{authen}{$type} eq "HASH") { + if (exists $ce->{authen}{$type}{$ce->{dbLayoutName}}) { + return $ce->{authen}{$type}{$ce->{dbLayoutName}}; + } elsif (exists $ce->{authen}{$type}{"*"}) { + return $ce->{authen}{$type}{"*"}; + } else { + die "authentication type '$type' in %authen hash in course environemnt has no entry for db layout '", $ce->{dbLayoutName}, "' and no default entry (*)"; + } } else { - # unexpired but doesn't match -- leave timestamp alone - # we do this to keep an attacker from keeping someone's session - # alive. (yeah, we don't match IPs.) - return 0; + return $ce->{authen}{$type}; } } else { - # expired -- delete key - $self->{db}->deleteKey($userID); - return 0; + die "authentication type '$type' not found in course environment \%authen hash"; } } -sub unexpiredKeyExists($$) { - my ($self, $userID) = @_; - my $Key = $self->{db}->getKey($userID); # checked - return 0 unless defined $Key; - if (time <= $Key->timestamp()+$self->{ce}->{sessionKeyTimeout}) { - # unexpired, but leave timestamp alone - return 1; - } else { - # expired -- delete key - $self->{db}->deleteKey($userID); - return 0; - } +=back + +=cut + +=head1 CONSTRUCTOR + +=over + +=item new($r) + +Instantiates a new WeBWorK::Authen object for the given WeBWorK::Requst ($r). + +=cut + +sub new { + my ($invocant, $r) = @_; + my $class = ref($invocant) || $invocant; + my $self = { + r => $r, + }; + #initialize + $GENERIC_ERROR_MESSAGE = $r->maketext("Invalid user ID or password."); + bless $self, $class; + return $self; } -# verify will return 1 if the person is who they say the are. -# If the verification failed because of of invalid authentication data, -# a note will be written in the request explaining why it failed. -# If the request failed because no authentication data was provided, however, -# no note will be written, as this is expected to happen whenever someone -# types in a URL manually, and is not considered an error condition. -sub verify($) { +=back + +=cut + +=head1 METHODS + +=over + +=cut + +sub verify { + debug("BEGIN VERIFY"); my $self = shift; my $r = $self->{r}; - my $ce = $self->{ce}; - my $db = $self->{db}; - my $practiceUserPrefix = $ce->{practiceUserPrefix}; - my $debugPracticeUser = $ce->{debugPracticeUser}; + my $result = $self->do_verify; + my $error = $self->{error}; + my $log_error = $self->{log_error}; - my $user = $r->param('user'); - my $passwd = $r->param('passwd'); - my $key = $r->param('key'); - my $force_passwd_authen = $r->param('force_passwd_authen'); + $self->{was_verified} = $result ? 1 : 0; - my $error; - my $failWithoutError = 0; + if ($self->can("site_fixup")) { + $self->site_fixup; + } - VERIFY: { - # This block is here so we can "last" out of it when we've - # decided whether we're going to succeed or fail. - - # no authentication data was given. this is OK. - unless (defined $user or defined $passwd or defined $key) { - $failWithoutError = 1; - last VERIFY; + if ($result) { + $self->write_log_entry("LOGIN OK") if $self->{initial_login}; + $self->maybe_send_cookie; + $self->set_params; + } else { + if (defined $log_error) { + $self->write_log_entry("LOGIN FAILED $log_error"); } - - if (defined $user and $force_passwd_authen) { - $failWithoutError = 1; - last VERIFY; + $self->maybe_kill_cookie; + if ($error) { + MP2 ? $r->notes->set(authen_error => $error) : $r->notes("authen_error" => $error); } + } + + debug("END VERIFY"); + return $result; +} + +=item was_verified() + +Returns true if verify() returned true the last time it was called. + +=cut + +sub was_verified { + my ($self) = @_; + + return 1 if exists $self->{was_verified} and $self->{was_verified}; + return 0; +} + +=item forget_verification() + +Future calls to was_verified() will return false, until verify() is called again and succeeds. + +=cut + +sub forget_verification { + my ($self) = @_; + + $self->{was_verified} = 0; +} + +=back + +=cut + +################################################################################ +# Helper functions (called by verify) +################################################################################ + +sub do_verify { + my $self = shift; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + return 0 unless $db; + + return 0 unless $self->get_credentials; + + return 0 unless $self->check_user; + + my $practiceUserPrefix = $ce->{practiceUserPrefix}; + if (defined($self->{login_type}) && $self->{login_type} eq "guest"){ + return $self->verify_practice_user; + } else { + return $self->verify_normal_user; + } +} + +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + # allow guest login: if the "Guest Login" button was clicked, we find an unused + # practice user and create a session for it. + if ($r->param("login_practice_user")) { + my $practiceUserPrefix = $ce->{practiceUserPrefix}; + # DBFIX search should happen in database + my @guestUserIDs = grep m/^$practiceUserPrefix/, $db->listUsers; + my @GuestUsers = $db->getUsers(@guestUserIDs); + my @allowedGuestUsers = grep { $ce->status_abbrev_has_behavior($_->status, "allow_course_access") } @GuestUsers; + my @allowedGestUserIDs = map { $_->user_id } @allowedGuestUsers; - # no user was supplied. somebody's building their own GET - unless ($user) { - $error = "You must specify a username."; - last VERIFY; + foreach my $userID (@allowedGestUserIDs) { + if (not $self->unexpired_session_exists($userID)) { + my $newKey = $self->create_session($userID); + $self->{initial_login} = 1; + + $self->{user_id} = $userID; + $self->{session_key} = $newKey; + $self->{login_type} = "guest"; + $self->{credential_source} = "none"; + debug("guest user '", $userID. "' key '", $newKey. "'"); + return 1; + } } - # it's a practice user. - if ($practiceUserPrefix and $user =~ /^$practiceUserPrefix/) { - # we're not interested in a practice user's password - $r->param("passwd", ""); - - # it's a practice user that doesn't exist. - unless (defined $db->getUser($user)) { # checked - $error = "That practice account does not exist."; - last VERIFY; + $self->{log_error} = "no guest logins are available"; + $self->{error} = "No guest logins are available. Please try again in a few minutes."; + return 0; + } + + # at least the user ID is available in request parameters + if (defined $r->param("user")) { + $self->{user_id} = $r->param("user"); + $self->{session_key} = $r->param("key"); + $self->{password} = $r->param("passwd"); + $self->{login_type} = "normal"; + $self->{credential_source} = "params"; + debug("params user '", $self->{user_id}, "' password '", $self->{password}, "' key '", $self->{session_key}, "'"); + return 1; + } + + my ($cookieUser, $cookieKey) = $self->fetchCookie; + if (defined $cookieUser) { + $self->{user_id} = $cookieUser; + $self->{session_key} = $cookieKey; + $self->{login_type} = "normal"; + $self->{credential_source} = "cookie"; + debug("cookie user '", $self->{user_id}, "' key '", $self->{session_key}, "'"); + return 1; + } +} + +sub check_user { + my $self = shift; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + my $authz = $r->authz; + + my $user_id = $self->{user_id}; + + if (defined $user_id and $user_id eq "") { + $self->{log_error} = "no user id specified"; + $self->{error} = $r->maketext("You must specify a user ID."); + return 0; + } + + my $User = $db->getUser($user_id); + + unless ($User) { + $self->{log_error} = "user unknown"; + $self->{error} = $GENERIC_ERROR_MESSAGE; + return 0; + } + + # FIXME "fix invalid status values" used to be here, but it needs to move to $db->getUser + + unless ($ce->status_abbrev_has_behavior($User->status, "allow_course_access")) { + $self->{log_error} = "user not allowed course access"; + $self->{error} = $GENERIC_ERROR_MESSAGE; + return 0; + } + + unless ($authz->hasPermissions($user_id, "login")) { + $self->{log_error} = "user not permitted to login"; + $self->{error} = $GENERIC_ERROR_MESSAGE; + return 0; + } + + return 1; +} + +sub verify_practice_user { + my $self = shift; + my $r = $self->{r}; + my $ce = $r->ce; + + my $user_id = $self->{user_id}; + my $session_key = $self->{session_key}; + + my ($sessionExists, $keyMatches, $timestampValid) = $self->check_session($user_id, $session_key, 1); + debug("sessionExists='", $sessionExists, "' keyMatches='", $keyMatches, "' timestampValid='", $timestampValid, "'"); + + if ($sessionExists) { + if ($keyMatches) { + if ($timestampValid) { + return 1; + } else { + $self->{session_key} = $self->create_session($user_id); + $self->{initial_login} = 1; + return 1; } - - # we've got a key. - if ($key) { - if ($self->checkKey($user, $key)) { - # they key was valid. - last VERIFY; + } else { + if ($timestampValid) { + my $debugPracticeUser = $ce->{debugPracticeUser}; + if (defined $debugPracticeUser and $user_id eq $debugPracticeUser) { + $self->{session_key} = $self->create_session($user_id); + $self->{initial_login} = 1; + return 1; } else { - # the key was invalid. - $error = "Your session has timed out due to inactivity. You must login again."; - last VERIFY; + $self->{log_error} = "guest account in use"; + $self->{error} = "That guest account is in use."; + return 0; } + } else { + $self->{session_key} = $self->create_session($user_id); + $self->{initial_login} = 1; + return 1; } - - # -- here we know that a key was not supplied. -- - - # it's the debug user. - if ($debugPracticeUser and $user eq $debugPracticeUser) { - # clobber any existing session, valid or not. - my $Key = $self->generateKey($user); - eval { $db->deleteKey($user) }; - $db->addKey($Key); - $r->param("key", $Key->key()); - last VERIFY; - } - - # an unexpired key exists -- the account is in use. - if ($self->unexpiredKeyExists($user)) { - $error = "That practice account is in use."; - last VERIFY; - } - - # here we know the account is not in use, so we - # generate a new session key (unexpiredKeyExists - # deleted any expired key) and succeed! - my $Key = $self->generateKey($user); - $db->addKey($Key); - $r->param("key", $Key->key()); - last VERIFY; } + } else { + $self->{session_key} = $self->create_session($user_id); + $self->{initial_login} = 1; + return 1; + } +} + +sub verify_normal_user { + my $self = shift; + my $r = $self->{r}; + + my $user_id = $self->{user_id}; + my $session_key = $self->{session_key}; + + my ($sessionExists, $keyMatches, $timestampValid) = $self->check_session($user_id, $session_key, 1); + debug("sessionExists='", $sessionExists, "' keyMatches='", $keyMatches, "' timestampValid='", $timestampValid, "'"); + + if ($keyMatches and $timestampValid) { + return 1; + } else { + my $auth_result = $self->authenticate; - # -- here we know it's a regular user. -- - - # a key was supplied. - if ($key) { - # we're not interested in a user's password if they're - # supplying a key - $r->param("passwd", ""); - - if ($self->checkKey($user, $key)) { - # valid key, so succeed. - last VERIFY; - } else { - # invalid key. the login page doesn't propogate the key, - # so we know this is an expired session. - $error = "Your session has timed out due to inactivity. You must login again."; - last VERIFY; + if ($auth_result > 0) { + $self->{session_key} = $self->create_session($user_id); + $self->{initial_login} = 1; + return 1; + } elsif ($auth_result == 0) { + $self->{log_error} = "authentication failed"; + $self->{error} = $GENERIC_ERROR_MESSAGE; + return 0; + } else { # ($auth_result < 0) => required data was not present + if ($keyMatches and not $timestampValid) { + $self->{error} = $r->maketext("Your session has timed out due to inactivity. Please log in again."); } + return 0; } - - # a password was supplied. - if ($passwd) { - if ($self->checkPassword($user, $passwd)) { - # valid password, so create a new session. (we don't want - # to reuse an old one, duh.) - my $Key = $self->generateKey($user); - eval { $db->deleteKey($user) }; - $db->addKey($Key); - $r->param("key", $Key->key()); - # also delete the password - $r->param("passwd", ""); - last VERIFY; + } +} + +# 1 == authentication succeeded +# 0 == required data was present, but authentication failed +# -1 == required data was not present (i.e. password missing) +sub authenticate { + my $self = shift; + my $r = $self->{r}; + + my $user_id = $self->{user_id}; + my $password = $self->{password}; + + if (defined $password) { + return $self->checkPassword($user_id, $password); + } else { + return -1; + } +} + +sub maybe_send_cookie { + my $self = shift; + my $r = $self->{r}; + + my ($cookie_user, $cookie_key) = $self->fetchCookie; + + # we send a cookie if any of these conditions are met: + + # (a) a cookie was used for authentication + my $used_cookie = ($self->{credential_source} eq "cookie"); + + # (b) a cookie was sent but not used for authentication, and the + # credentials used for authentication were the same as those in + # the cookie + my $unused_valid_cookie = ($self->{credential_source} ne "cookie" + and defined $cookie_user and $self->{user_id} eq $cookie_user + and defined $cookie_key and $self->{session_key} eq $cookie_key); + + # (c) the user asked to have a cookie sent and is not a guest user. + my $user_requests_cookie = ($self->{login_type} ne "guest" + and $r->param("send_cookie")); + + debug("used_cookie='", $used_cookie, "' unused_valid_cookie='", $unused_valid_cookie, "' user_requests_cookie='", $user_requests_cookie, "'"); + + if ($used_cookie or $unused_valid_cookie or $user_requests_cookie) { + $self->sendCookie($self->{user_id}, $self->{session_key}); + } else { + $self->killCookie; + } +} + +sub maybe_kill_cookie { + my $self = shift; + $self->killCookie(@_); +} + +sub set_params { + my $self = shift; + my $r = $self->{r}; + + # A2 - params are not non-modifiable, with no explanation or workaround given in docs. WTF! + $r->param("user", $self->{user_id}); + $r->param("key", $self->{session_key}); + $r->param("passwd", ""); + + debug("params user='", $r->param("user"), "' key='", $r->param("key"), "' passwd='", $r->param("passwd"), "'"); +} + +################################################################################ +# Password management +################################################################################ + +sub checkPassword { + my ($self, $userID, $possibleClearPassword) = @_; + my $db = $self->{r}->db; + + my $Password = $db->getPassword($userID); # checked + if (defined $Password) { + # check against WW password database + my $possibleCryptPassword = crypt $possibleClearPassword, $Password->password; + if ($possibleCryptPassword eq $Password->password) { + $self->write_log_entry("AUTH WWDB: password accepted"); + return 1; + } else { + if ($self->can("site_checkPassword")) { + $self->write_log_entry("AUTH WWDB: password rejected, deferring to site_checkPassword"); + return $self->site_checkPassword($userID, $possibleClearPassword); } else { - # incorrect password. fail. - $error = "Incorrect username or password."; - last VERIFY; + $self->write_log_entry("AUTH WWDB: password rejected"); + return 0; } } - - # neither a key or a password were supplied. - $error = "You must enter a password." + } else { + $self->write_log_entry("AUTH WWDB: user has no password record"); + return 0; } +} + +# Site-specific password checking +# +# The site_checkPassword routine can be used to provide a hook to your institution's +# authentication system. If authentication against the course's password database, the +# method $self->site_checkPassword($userID, $clearTextPassword) is called. If this +# method returns a true value, authentication succeeds. +# +# Here is an example site_checkPassword which checks the password against the Ohio State +# popmail server: +# sub site_checkPassword { +# my ($self, $userID, $clearTextPassword) = @_; +# use Net::POP3; +# my $pop = Net::POP3->new('pop.service.ohio-state.edu', Timeout => 60); +# if ($pop->login($userID, $clearTextPassword)) { +# return 1; +# } +# return 0; +# } +# +# Since you have access to the WeBWorK::Authen object, the possibilities are limitless! +# This example checks the password against the system password database and updates the +# user's password in the course database if it succeeds: +# sub site_checkPassword { +# my ($self, $userID, $clearTextPassword) = @_; +# my $realCryptPassword = (getpwnam $userID)[1] or return 0; +# my $possibleCryptPassword = crypt($possibleClearPassword, $realCryptPassword); # user real PW as salt +# if ($possibleCryptPassword eq $realCryptPassword) { +# # update WeBWorK password +# use WeBWorK::Utils qw(cryptPassword); +# my $db = $self->{r}->db; +# my $Password = $db->getPassword($userID); +# my $pass = cryptPassword($clearTextPassword); +# $Password->password($pass); +# $db->putPassword($Password); +# return 1; +# } else { +# return 0; +# } +# } + +################################################################################ +# Session key management +################################################################################ + +sub unexpired_session_exists { + my ($self, $userID) = @_; + my $ce = $self->{r}->ce; + my $db = $self->{r}->db; - if (defined $error) { - $r->notes("authen_error",$error); - return 0; + my $Key = $db->getKey($userID); # checked + return 0 unless defined $Key; + if (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}) { + # unexpired, but leave timestamp alone + return 1; } else { - return not $failWithoutError; + # expired -- delete key + # NEW: no longer delete the key here -- a user re-visiting with a formerly-valid key should + # always get a "session expired" message. formerly, if they i.e. reload the login screen + # the message disappears, which is confusing (i claim ;) + #$db->deleteKey($userID); + return 0; + } +} + +# clobbers any existing session for this $userID +# if $newKey is not specified, a random key is generated +# the key is returned +sub create_session { + my ($self, $userID, $newKey) = @_; + my $ce = $self->{r}->ce; + my $db = $self->{r}->db; + + my $timestamp = time; + unless ($newKey) { + my @chars = @{ $ce->{sessionKeyChars} }; + my $length = $ce->{sessionKeyLength}; + + srand; + $newKey = join ("", @chars[map rand(@chars), 1 .. $length]); } - # Whatever you do, don't delete this! - critical($r); + my $Key = $db->newKey(user_id=>$userID, key=>$newKey, timestamp=>$timestamp); + # DBFIXME this should be a REPLACE + eval { $db->deleteKey($userID) }; + $db->addKey($Key); + return $newKey; } -1; +# returns ($sessionExists, $keyMatches, $timestampValid) +# if $updateTimestamp is true, the timestamp on a valid session is updated +sub check_session { + my ($self, $userID, $possibleKey, $updateTimestamp) = @_; + my $ce = $self->{r}->ce; + my $db = $self->{r}->db; + + my $Key = $db->getKey($userID); # checked + return 0 unless defined $Key; + + my $keyMatches = (defined $possibleKey and $possibleKey eq $Key->key); + my $timestampValid = (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}); + + if ($keyMatches and $timestampValid and $updateTimestamp) { + $Key->timestamp(time); + $db->putKey($Key); + } + + return (1, $keyMatches, $timestampValid); +} + +################################################################################ +# Cookie management +################################################################################ + +sub fetchCookie { + my $self = shift; + my $r = $self->{r}; + my $ce = $r->ce; + my $urlpath = $r->urlpath; + + my $courseID = $urlpath->arg("courseID"); + + # AP2 - Apache2::Cookie needs $r, Apache::Cookie doesn't + #my %cookies = WeBWorK::Cookie->fetch( MP2 ? $r : () ); + #my $cookie = $cookies{"WeBWorKCourseAuthen.$courseID"}; + + my $cookie = undef; + if (MP2) { + + my $jar = undef; + eval { + $jar = $r->jar; #table of cookies + }; + if (ref $@ and $@->isa("APR::Request::Error") ) { + debug("Error parsing cookies, will use a partial result"); + $jar = $@->jar; # table of successfully parsed cookies + }; + if ($jar) { + $cookie = uri_unescape( $jar->get("WeBWorKCourseAuthen.$courseID") ); + }; + } else { + my %cookies = WeBWorK::Cookie->fetch(); + $cookie = $cookies{"WeBWorKCourseAuthen.$courseID"}; + if ($cookie) { + debug("found a cookie for this course: '", $cookie->as_string, "'"); + $cookie = $cookie->value; + } + } + + if ($cookie) { + #debug("found a cookie for this course: '", $cookie->as_string, "'"); + #debug("cookie has this value: '", $cookie->value, "'"); + #my ($userID, $key) = split "\t", $cookie->value; + debug("cookie has this value: '", $cookie, "'"); + my ($userID, $key) = split "\t", $cookie; + if (defined $userID and defined $key and $userID ne "" and $key ne "") { + debug("looks good, returning userID='$userID' key='$key'"); + return $userID, $key; + } else { + debug("malformed cookie. returning nothing."); + return; + } + } else { + debug("found no cookie for this course. returning nothing."); + return; + } +} + +sub sendCookie { + my ($self, $userID, $key) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + + my $courseID = $r->urlpath->arg("courseID"); + + my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time+COOKIE_LIFESPAN, "GMT"); + my $cookie = WeBWorK::Cookie->new($r, + -name => "WeBWorKCourseAuthen.$courseID", + -value => "$userID\t$key", + -expires => $expires, + -domain => $r->hostname, + -path => $ce->{webworkURLRoot}, + -secure => 0, + ); + + debug("about to add Set-Cookie header with this string: '", $cookie->as_string, "'"); + $r->headers_out->set("Set-Cookie" => $cookie->as_string); +} -__END__ +sub killCookie { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + + my $courseID = $r->urlpath->arg("courseID"); + + my $expires = time2str("%a, %d-%h-%Y %H:%M:%S %Z", time-60*60*24, "GMT"); + my $cookie = WeBWorK::Cookie->new($r, + -name => "WeBWorKCourseAuthen.$courseID", + -value => "\t", + -expires => $expires, + -domain => $r->hostname, + -path => $ce->{webworkURLRoot}, + -secure => 0, + ); + + debug("about to add Set-Cookie header with this string: '", $cookie->as_string, "'"); + $r->headers_out->set("Set-Cookie" => $cookie->as_string); +} -=head1 AUTHOR +################################################################################ +# Utilities +################################################################################ -Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu, and Sam Hathaway, sh002i (at) math.rochester.edu. +sub write_log_entry { + my ($self, $message) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + + my $user_id = defined $self->{user_id} ? $self->{user_id} : ""; + my $login_type = defined $self->{login_type} ? $self->{login_type} : ""; + my $credential_source = defined $self->{credential_source} ? $self->{credential_source} : ""; + + my ($remote_host, $remote_port); + if (MP2) { + $remote_host = $r->connection->remote_addr->ip_get || "UNKNOWN"; + $remote_port = $r->connection->remote_addr->port || "UNKNOWN"; + } else { + ($remote_port, $remote_host) = unpack_sockaddr_in($r->connection->remote_addr); + $remote_host = defined $remote_host ? inet_ntoa($remote_host) : "UNKNOWN"; + $remote_port = "UNKNOWN" unless defined $remote_port; + } + my $user_agent = $r->headers_in->{"User-Agent"}; + + my $log_msg = "$message user_id=$user_id login_type=$login_type credential_source=$credential_source host=$remote_host port=$remote_port UA=$user_agent"; + debug("Writing to login log: '$log_msg'.\n"); + writeCourseLog($ce, "login_log", $log_msg); +} -=cut +1; diff --git a/lib/WeBWorK/Authen/Cosign.pm b/lib/WeBWorK/Authen/Cosign.pm new file mode 100644 index 0000000000..d1b8d152ff --- /dev/null +++ b/lib/WeBWorK/Authen/Cosign.pm @@ -0,0 +1,137 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authen/Cosign.pm,v 1.2 2007/03/27 17:06:04 glarose 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::Authen::Cosign; +use base qw/WeBWorK::Authen/; + +=head1 NAME + +WeBWorK::Authen::Cosign - Authentication plug in for cosign + +to use: include in global.conf or course.conf + $authen{user_module} = "WeBWorK::Authen::Cosign"; +and add /webwork2 or /webwork2/courseName as a CosignProtected +Location + +if $r->ce->{cosignoff} is set for a course, authentication reverts +to standard WeBWorK authentication. + +=cut + +use strict; +use warnings; +use WeBWorK::Debug; + +# this is similar to the method in the base class, except that cosign +# ensures that we don't get to the address without a login. this means +# that we can't allow guest logins, but don't have to do any password +# checking or cookie management. + +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + if ( $ce->{cosignoff} ) { + return $self->SUPER::get_credentials( @_ ); + } else { + if ( defined( $ENV{'REMOTE_USER'} ) ) { + $self->{'user_id'} = $ENV{'REMOTE_USER'}; + $self->{r}->param("user", $ENV{'REMOTE_USER'}); + } else { + return 0; + } + # set external auth parameter so that Login.pm knows + # not to rely on internal logins if there's a check_user + # failure. + $self->{external_auth} = 1; + + # the session key isn't used (cosign is managing this + # for us), and we want to force checking against the + # site_checkPassword + $self->{'session_key'} = undef; + $self->{'password'} = 1; + $self->{'credential_source'} = "params"; + + return 1; + } +} + +sub site_checkPassword { + my ( $self, $userID, $clearTextPassword ) = @_; + + if ( $self->{r}->ce->{cosignoff} ) { + return $self->SUPER::checkPassword( @_ ); + } else { + # this is easy; if we're here at all, we've authenticated + # through cosign + return 1; + } +} + +# disable cookie functionality +sub maybe_send_cookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{cosignoff} ) { + return $self->SUPER::maybe_send_cookie( @_ ); + } else { + # nothing to do here + } +} +sub fetchCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{cosignoff} ) { + return $self->SUPER::fetchCookie( @_ ); + } else { + # nothing to do here + } +} +sub sendCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{cosignoff} ) { + return $self->SUPER::sendCookie( @_ ); + } else { + # nothing to do here + } +} +sub killCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{cosignoff} ) { + return $self->SUPER::killCookie( @_ ); + } else { + # nothing to do here + } +} + +# this is a bit of a cheat, because it does the redirect away from the +# logout script or what have you, but I don't see a way around that. +sub forget_verification { + my ($self, @args) = @_; + my $r = $self->{r}; + + if ( $r->ce->{cosignoff} ) { + return $self->SUPER::forget_verification( @_ ); + } else { + $self->{was_verified} = 0; +# $r->headers_out->{"Location"} = $r->ce->{cosign_logout_script}; +# $r->send_http_header; +# return; + $self->{redirect} = $r->ce->{cosign_logout_script}; + } +} + +1; diff --git a/lib/WeBWorK/Authen/LDAP.pm b/lib/WeBWorK/Authen/LDAP.pm new file mode 100644 index 0000000000..190c8ecf8d --- /dev/null +++ b/lib/WeBWorK/Authen/LDAP.pm @@ -0,0 +1,127 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authen/LDAP.pm,v 1.4 2007/08/13 22:59:54 sh002i 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::Authen::LDAP; +use base qw/WeBWorK::Authen/; + +use strict; +use warnings; +use WeBWorK::Debug; +use Net::LDAP qw/LDAP_INVALID_CREDENTIALS/; + +sub checkPassword { + my ($self, $userID, $possibleClearPassword) = @_; + my $ce = $self->{r}->ce; + my $failover = $ce->{authen}{ldap_options}{failover}; + + debug("LDAP module is doing the password checking.\n"); + + # check against LDAP server + my $ret = $self->ldap_authen_uid($userID, $possibleClearPassword); + return 1 if ($ret == 1); + + #return 0 if ($userID !~ /admin/); + + # optional: fail over to superclass checkPassword + if (($failover eq "all" or $failover == 1) || ($failover eq "local" and $ret < 0)) { + $self->write_log_entry("AUTH LDAP: authentication failed, deferring to superclass"); + return $self->SUPER::checkPassword($userID, $possibleClearPassword); + } + + # fail by default + return 0; +} + +sub ldap_authen_uid { + my ($self, $uid, $password) = @_; + my $ce = $self->{r}->ce; + my $hosts = $ce->{authen}{ldap_options}{net_ldap_hosts}; + my $opts = $ce->{authen}{ldap_options}{net_ldap_opts}; + my $base = $ce->{authen}{ldap_options}{net_ldap_base}; + my $searchdn = $ce->{authen}{ldap_options}{searchDN}; + my $bindAccount = $ce->{authen}{ldap_options}{bindAccount}; + my $bindpassword = $ce->{authen}{ldap_options}{bindPassword}; + # Be backwards-compatible with releases that hardcode this value. + my $rdn = "sAMAccountName"; + if (defined $ce->{authen}{ldap_options}{net_ldap_rdn}) { + $rdn = $ce->{authen}{ldap_options}{net_ldap_rdn}; + } + + + + # connect to LDAP server + my $ldap = new Net::LDAP($hosts, @$opts); + if (not defined $ldap) { + warn "AUTH LDAP: couldn't connect to any of ", join(", ", @$hosts), ".\n"; + return 0; + } + + my $msg; + + + if($bindAccount){ + # bind with a bind USER + $msg = $ldap->bind( $searchdn, password => $bindpassword ); + if ($msg->is_error) { + warn "AUTH LDAP: bind error ", $msg->code, ": ", $msg->error_text, ".\n"; + return 0; + } + } + else{ + # bind anonymously + $msg = $ldap->bind; + if ($msg->is_error) { + warn "AUTH LDAP: bind error ", $msg->code, ": ", $msg->error_text, ".\n"; + return 0; + } + } + + # look up user's DN + $msg = $ldap->search(base => $base, filter => "$rdn=$uid"); + if ($msg->is_error) { + warn "AUTH LDAP: search error ", $msg->code, ": ", $msg->error_text, ".\n",$searchdn,"\n",$base,"\n",$uid,"\n"; + return 0; + } + if ($msg->count > 1) { + warn "AUTH LDAP: more than one result returned when searching for UID '$uid'.\n"; + return 0; + } + if ($msg->count == 0) { + $self->write_log_entry("AUTH LDAP: UID not found"); + return -1; + } + my $dn = $msg->shift_entry->dn; + if (not defined $dn) { + warn "AUTH LDAP: got null DN when looking up UID '$uid'.\n"; + return 0; + } + + # re-bind as user. if that works, we've authenticated! + $msg = $ldap->bind($dn, password => $password); + if ($msg->code == LDAP_INVALID_CREDENTIALS) { + $self->write_log_entry("AUTH LDAP: server rejected password for UID."); + return 0; + } + if ($msg->is_error) { + warn "AUTH LDAP: bind error ", $msg->code, ": ", $msg->error_text, ".\n"; + return 0; + } + + # it worked! we win! + return 1; +} + +1; diff --git a/lib/WeBWorK/Authen/Moodle.pm b/lib/WeBWorK/Authen/Moodle.pm new file mode 100644 index 0000000000..37f752e8b4 --- /dev/null +++ b/lib/WeBWorK/Authen/Moodle.pm @@ -0,0 +1,262 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authen/Moodle.pm,v 1.14 2007/02/14 19:08:46 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::Authen::Moodle; +use base qw/WeBWorK::Authen/; + +=head1 NAME + +WeBWorK::Authen::Moodle - Allow moodle cookies to be used for WeBWorK authentication. + +=cut + +=for comment + +TODO + +* Modules that modify data that's being taken from moodle should check for "alternative URLs" in the +CE that can point back to the moodle installation. operations include: change password, change user +data, change permission level, add user, delete user. Run this for a rough estimate: + pcregrep -r '\$db->(add|put)(User|Password|PermissionLevel)\b' lib + +=cut + +use strict; +use warnings; +use Digest::MD5 qw/md5_hex/; +use WeBWorK::Cookie; +use WeBWorK::Debug; +use Date::Parse; # for moodle 1.7 date parsing + +use mod_perl; +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); + +sub new { + my $self = shift->SUPER::new(@_); + + $self->init_mdl_session; + + return $self; +} + +# call superclass get_credentials. if no credentials were found, look for a moodle cooke. +# if a moodle cookie is found, a new webwork session is created and the session key is used. +# (this is similar to what happens when a guest user is selected.) +sub get_credentials { + my $self = shift; + my $r = $self->{r}; + + my $super_result = $self->SUPER::get_credentials; + if ($super_result) { + debug("Superclass's get_credentials found credentials. Using them.\n"); + return $super_result; + } + + my ($moodle_user_id, $moodle_expiration_time) = $self->fetch_moodle_session; + #debug("fetch_moodle_session returned: moodle_user_id='$moodle_user_id' moodle_expiration_time='$moodle_expiration_time'.\n"); # causes errors when undefined + + if (defined $moodle_user_id and defined $moodle_expiration_time and time <= $moodle_expiration_time) { + my $newKey = $self->create_session($moodle_user_id); + debug("Unexpired moodle session found. Created new WeBWorK session with newKey='$newKey'.\n"); + + $self->{user_id} = $moodle_user_id; + $self->{session_key} = $newKey; + $self->{login_type} = "normal"; + $self->{credential_source} = "moodle"; + return 1; + } else { + debug("No moodle session found or moodle session expired. No credentials to be had.\n"); + warn("No moodle session found or moodle sessioin expired. If this happens repeatedly and you are constantly being asked + to log back in ask your moodle admin to check that the Moodle item: + Server -> Session Handling -> dbsessions (Use database for session information) has been checked."); + } + + return 0; +} + +# extend the moodle session if authentication succeeded +sub site_fixup { + my $self = shift; + + if ($self->was_verified) { + debug("User was verified, updating moodle session.\n"); + $self->update_moodle_session; + } +} + +# we assume that the database is set up to use the moodle password table, which uses MD5 passwords. +# this is overridden to accommodate this. +sub checkPassword { + my ($self, $userID, $possibleClearPassword) = @_; + my $db = $self->{r}->db; + + debug("Moodle module is doing the password checking.\n"); + + my $Password = $db->getPassword($userID); # checked + if (defined $Password) { + # check against Moodle password database + my $possibleMD5Password = md5_hex($possibleClearPassword); + debug("Hashed password from supplied cleartext: '$possibleMD5Password'.\n"); + debug("Hashed password from Password record: '", $Password->password, "'.\n"); + if ($possibleMD5Password eq $Password->password) { + $self->write_log_entry("AUTH MDL: password accepted"); + return 1; + } else { + if ($self->can("site_checkPassword")) { + $self->write_log_entry("AUTH MDL: password rejected, deferring to site_checkPassword"); + return $self->site_checkPassword($userID, $possibleClearPassword); + } else { + $self->write_log_entry("AUTH MDL: password rejected"); + return 0; + } + } + + } +} + +sub check_session { + my ($self, $user_id, $session_key, $update_timestamp) = @_; + + my ($sessionExists, $keyMatches, $timestampValid) = $self->SUPER::check_session($user_id, $session_key, $update_timestamp); + debug("SUPER::check_session returned: sessionExists='", $sessionExists, "' keyMatches='", $keyMatches, "' timestampValid='", $timestampValid, "'"); + + if ($update_timestamp and $sessionExists and $keyMatches and not $timestampValid) { + debug("special case: webwork key matches an expired session (check for a unexpired moodle session)"); + my ($moodle_user_id, $moodle_expiration_time) = $self->fetch_moodle_session; + debug("fetch_moodle_session returned: moodle_user_id='$moodle_user_id' moodle_expiration_time='$moodle_expiration_time'.\n"); + if (defined $moodle_user_id and $moodle_user_id eq $user_id + and defined $moodle_expiration_time and time <= $moodle_expiration_time) { + $self->{session_key} = $self->create_session($moodle_user_id); + $timestampValid = 1; + } + } + + return $sessionExists, $keyMatches, $timestampValid; +} + +################################################################################ + +use DBI; +use PHP::Serialization qw/unserialize/; + +use constant DEFAULT_EXPIRY => 7200; + +sub init_mdl_session { + my $self = shift; + + # version-specific stuff + $self->{moodle17} = $self->{r}->ce->{authen}{moodle_options}{moodle17}; + $self->{sql_session_table} = $self->{moodle17} ? "sessions2" : "sessions"; + $self->{sql_data_field} = $self->{moodle17} ? "sessdata" : "data"; + + $self->{mdl_dbh} = DBI->connect_cached( + $self->{r}->ce->{authen}{moodle_options}{dsn}, + $self->{r}->ce->{authen}{moodle_options}{username}, + $self->{r}->ce->{authen}{moodle_options}{password}, + { + PrintError => 0, + RaiseError => 1, + }, + ); + die $DBI::errstr unless defined $self->{mdl_dbh}; +} + +sub fetch_moodle_session { + # fetches the basic information from the moodle session. + # returns the user name and expiration time of the moodle session + # Note that we don't worry about the user being in this course at this point. + # That is taken care of in Schema::Moodle::User. + my ($self) = @_; + my $r = $self->{r}; + my $db = $r->db; + + my %cookies = WeBWorK::Cookie->fetch( MP2 ? $r : () ); + my $cookie = $cookies{"MoodleSession"}; + return unless $cookie; + + my $session_table = $self->prefix_table($self->{sql_session_table}); + my $data_field = $self->{sql_data_field}; + my $stmt = "SELECT `expiry`,`$data_field` FROM `$session_table` WHERE `sesskey`=?"; + my @bind_vals = $cookie->value; + + my $sth = $self->{mdl_dbh}->prepare_cached($stmt, undef, 3); # 3: see DBI docs + $sth->execute(@bind_vals); + my $row = $sth->fetchrow_arrayref; + $sth->finish; + return unless defined $row; + + my ($expires, $data_string) = @$row; + + # Moodle 1.7 stores expiry as a DATETIME, but WeBWorK wants a UNIX timestamp. + $expires = str2time($expires) if $self->{moodle17}; + + my $data = unserialize_session($data_string); + my $username = $data->{"USER"}{"username"}; + + return $username, $expires; +} + +sub update_moodle_session { + # extend the timeout of the current moodle session, if one exists. + my ($self) = @_; + my $r = $self->{r}; + my $db = $r->db; + + my %cookies = WeBWorK::Cookie->fetch( MP2 ? $r : () ); + my $cookie = $cookies{"MoodleSession"}; + return unless $cookie; + + my $config_table = $self->prefix_table("config"); + my $value = "IFNULL((SELECT `value` FROM `$config_table` WHERE `name`=?),?)+?"; + + # Moodle 1.7 stores expiry as a DATETIME, but WeBWorK supplies a UNIX timestamp. + $value = "FROM_UNIXTIME($value)" if $self->{moodle17}; + + my $session_table = $self->prefix_table($self->{sql_session_table}); + my $stmt = "UPDATE `$session_table` SET `expiry`=$value WHERE `sesskey`=?"; + my @bind_vals = ("sessiontimeout", DEFAULT_EXPIRY, time, $cookie->value); + + my $sth = $self->{mdl_dbh}->prepare_cached($stmt, undef, 3); # 3: see DBI docs + my $result = $sth->execute(@bind_vals); + $sth->finish; + + return defined $result; +} + +sub prefix_table { + my ($self, $base) = @_; + if (defined $self->{r}->ce->{authen}{moodle_options}{table_prefix}) { + return $self->{r}->ce->{authen}{moodle_options}{table_prefix} . $base; + } else { + return $base; + } +} + +sub unserialize_session { + my $serialData = shift; + # first, url decode: + $serialData =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg; + # then, split it up by |, it's some ADODB sillyness + my @serialArray = split(/(\w+)\|/, $serialData); + my %variables; + # finally, actually deserialize it: + for( my $i = 1; $i < $#serialArray; $i += 2 ) { + $variables{$serialArray[$i]} = unserialize($serialArray[$i+1]); + } + return \%variables; +} + +1; diff --git a/lib/WeBWorK/Authen/Proctor.pm b/lib/WeBWorK/Authen/Proctor.pm new file mode 100644 index 0000000000..607c7775b0 --- /dev/null +++ b/lib/WeBWorK/Authen/Proctor.pm @@ -0,0 +1,185 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authen/Proctor.pm,v 1.5 2007/04/04 15:05:27 glarose 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::Authen::Proctor; +use base qw/WeBWorK::Authen/; + +=head1 NAME + +WeBWorK::Authen::Proctor - Authenticate gateway test proctors. + +=cut + +use strict; +use warnings; +use WeBWorK::Debug; +use WeBWorK::DB::Utils qw(grok_vsetID); + +use constant GENERIC_ERROR_MESSAGE => "Invalid user ID or password."; + +## this is only overridden for debug logging +#sub verify { +# debug("BEGIN PROCTOR VERIFY"); +# my $result = $_[0]->SUPER::verify(@_[1..$#_]); +# debug("END PROCTOR VERIFY"); +# return $result; +#} + +# this is similar to the method in the base class, with these differences: +# 1. no guest logins +# 2. no cookie +# 3. user_id/session_key/password come from params proctor_user/proctor_key/proctor_passwd +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + my $urlpath = $r->urlpath; + my ($set_id, $version_id) = grok_vsetID( $urlpath->arg('setID') ); + + # at least the user ID is available in request parameters + if (defined $r->param("proctor_user")) { + my $student_user_id = $r->param("effectiveUser"); + $self->{user_id} = $r->param("proctor_user"); + if ( $self->{user_id} eq $set_id ) { + $self->{user_id} = "set_id:$set_id"; + } + $self->{session_key} = $r->param("proctor_key"); + $self->{password} = $r->param("proctor_passwd"); + $self->{login_type} = $r->param("submitAnswers") + ? "proctor_grading:$student_user_id" + : "proctor_login:$student_user_id"; + $self->{credential_source} = "params"; + return 1; + } +} + +# duplicates method in superclass, adding additional check for permission +# to proctor quizzes +sub check_user { + my $self = shift; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + my $authz = $r->authz; + + my $submitAnswers = $r->param("submitAnswers"); + my $user_id = $self->{user_id}; + my $past_proctor_id = $r->param("past_proctor_user") || $user_id; + + # for set-level authentication we prepended "set_id:" + my $show_user_id = $user_id; + $show_user_id =~ s/^set_id://; + + if (defined $user_id and ($user_id eq "" || $show_user_id eq "")) { + $self->{log_error} = "no user id specified"; + $self->{error} = "You must specify a user ID."; + return 0; + } + + my $User = $db->getUser($user_id); + + unless ($User) { + $self->{log_error} = "user unknown"; + $self->{error} = GENERIC_ERROR_MESSAGE; + return 0; + } + + # proctors may be tas, instructors, or proctors; if the last, they + # do not have the behavior course_access, so we don't bother to + # check that here. they must, however, be able to login, which + # it seems to me is an overlap between course permissions and + # course status behaviors. + + unless ($authz->hasPermissions($user_id, "login")) { + $self->{log_error} = "user not permitted to login"; + $self->{error} = GENERIC_ERROR_MESSAGE; + return 0; + } + + if ( $submitAnswers ) { + unless ($authz->hasPermissions($user_id,"proctor_quiz_grade")) { + # only set the error if this proctor is different + # than the past proctor, implying that we have + # tried to grade with a new proctor id + if ( $past_proctor_id ne $user_id ) { + $self->{log_error} = "user not permitted " . + "to proctor quiz grading."; + $self->{error} = "User $show_user_id is not " . + "authorized to proctor test grade " . + "submissions in this course."; + } + + return 0; + } + } else { + unless ($authz->hasPermissions($user_id,"proctor_quiz_login")) { + $self->{log_error} = "user not permitted to proctor " . + "quiz logins."; + $self->{error} = "User $show_user_id is not " . + "authorized to proctor test logins in this " . + "course."; + return 0; + } + } +} + +# this is similar to the method in the base class, excpet that the parameters +# proctor_user, proctor_key, and proctor_passwd are used +sub set_params { + my $self = shift; + my $r = $self->{r}; + + $r->param("proctor_user", $self->{user_id}); + $r->param("proctor_key", $self->{session_key}); + $r->param("proctor_passwd", ""); +} + +# rewrite the userID to include both the proctor's and the student's user ID +# and then call the default create_session method. +sub create_session { + my ($self, $userID, $newKey) = @_; + + return $self->SUPER::create_session($self->proctor_key_id($userID), $newKey); +} + +# rewrite the userID to include bith the proctor's and the student's user ID +# and then call the default check_session method. +sub check_session { + my ($self, $userID, $possibleKey, $updateTimestamp) = @_; + + return $self->SUPER::check_session($self->proctor_key_id($userID), $possibleKey, $updateTimestamp); +} + +# proctor key ID rewriting helper +sub proctor_key_id { + my ($self, $userID, $newKey) = @_; + my $r = $self->{r}; + + my $proctor_key_id = $r->param("effectiveUser") . "," . $userID; + $proctor_key_id .= ",g" if $self->{login_type} =~ /^proctor_grading/; + + return $proctor_key_id; +} + +# disable cookie functionality for proctors +sub maybe_send_cookie {} +sub fetchCookie {} +sub sendCookie {} +sub killCookie {} + +1; diff --git a/lib/WeBWorK/Authen/Shibboleth.pm b/lib/WeBWorK/Authen/Shibboleth.pm new file mode 100644 index 0000000000..39eba60348 --- /dev/null +++ b/lib/WeBWorK/Authen/Shibboleth.pm @@ -0,0 +1,203 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# +# 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::Authen::Shibboleth; +use base qw/WeBWorK::Authen/; + +=head1 NAME + +WeBWorK::Authen::Shibboleth - Authentication plug in for Shibboleth. +This is basd on Cosign.pm + +For Shibboleth installation and configuration, please refer to: +http://webwork.maa.org/wiki/External_(Shibboleth)_Authentication + +to use: include in global.conf or course.conf + $authen{user_module} = "WeBWorK::Authen::Shibboleth"; +and add /webwork2/courseName as a Shibboleth Protected +Location + +if $r->ce->{shiboff} is set for a course, authentication reverts +to standard WeBWorK authentication. + +add the following to global.conf to setup the Shibboleth + +$shibboleth{logout_script} = "/Shibboleth.sso/Logout"?return=".$server_root_url.$webwork_url; # return URL after logout +$shibboleth{session_header} = "Shib-Session-ID"; # the header to identify if there is an existing shibboleth session +$shibboleth{manage_session_timeout} = 1; # allow shib to manage session time instead of webwork +$shibboleth{hash_user_id_method} = "MD5"; # possible values none, MD5. Use it when you want to hide real user_ids from showing in url. +$shibboleth{hash_user_id_salt} = ""; # salt for hash function +#define mapping between shib and webwork +$shibboleth{mapping}{user_id} = "username"; + +=cut + +use strict; +use warnings; +use WeBWorK::Debug; +use Data::Dumper; + +# this is similar to the method in the base class, except that Shibboleth +# ensures that we don't get to the address without a login. this means +# that we can't allow guest logins, but don't have to do any password +# checking or cookie management. + +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + if ( $ce->{shiboff} || $r->param('bypassShib')) { + return $self->SUPER::get_credentials( @_ ); + } else { + debug("Shib is on!"); + + # set external auth parameter so that Login.pm knows + # not to rely on internal logins if there's a check_user + # failure. + $self->{external_auth} = 1; + + if ( $r->param("user") && ! $r->param("force_passwd_authen") ) { + return $self->SUPER::get_credentials( @_ ); + } + + if ( defined ($ENV{$ce->{shibboleth}{session_header}}) && defined( $ENV{$ce->{shibboleth}{mapping}{user_id}} ) ) { + debug('Got shib header and user_id'); + my $user_id = $ENV{$ce->{shibboleth}{mapping}{user_id}}; + if ( defined ($ce->{shibboleth}{hash_user_id_method}) && + $ce->{shibboleth}{hash_user_id_method} ne "none" && + $ce->{shibboleth}{hash_user_id_method} ne "" ) { + use Digest; + my $digest = Digest->new($ce->{shibboleth}{hash_user_id_method}); + $digest->add(uc($user_id). ( defined $ce->{shibboleth}{hash_user_id_salt} ? $ce->{shibboleth}{hash_user_id_salt} : "")); + $user_id = $digest->hexdigest; + } + $self->{'user_id'} = $user_id; + $self->{r}->param("user", $user_id); + + # set external auth parameter so that login.pm + # knows not to rely on internal logins if + # there's a check_user failure. + $self->{session_key} = undef; + $self->{password} = "youwouldneverpickthispassword"; + $self->{login_type} = "normal"; + $self->{credential_source} = "params"; + } else { + debug("Couldn't shib header or user_id"); + return 0; + } + + # the session key isn't used (Shibboleth is managing this + # for us), and we want to force checking against the + # site_checkPassword + $self->{'session_key'} = undef; + $self->{'password'} = 1; + $self->{'credential_source'} = "params"; + + return 1; + } +} + +sub site_checkPassword { + my ( $self, $userID, $clearTextPassword ) = @_; + + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::checkPassword( @_ ); + } else { + # this is easy; if we're here at all, we've authenticated + # through shib + return 1; + } +} + +# disable cookie functionality +sub maybe_send_cookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::maybe_send_cookie( @_ ); + } else { + # nothing to do here + } +} +sub fetchCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::fetchCookie( @_ ); + } else { + # nothing to do here + } +} +sub sendCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::sendCookie( @_ ); + } else { + # nothing to do here + } +} +sub killCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::killCookie( @_ ); + } else { + # nothing to do here + } +} + +# this is a bit of a cheat, because it does the redirect away from the +# logout script or what have you, but I don't see a way around that. +sub forget_verification { + my ($self, @args) = @_; + my $r = $self->{r}; + + if ( $r->ce->{shiboff} ) { + return $self->SUPER::forget_verification( @_ ); + } else { + $self->{was_verified} = 0; + $self->{redirect} = $r->ce->{shibboleth}{logout_script}; + } +} + +# returns ($sessionExists, $keyMatches, $timestampValid) +# if $updateTimestamp is true, the timestamp on a valid session is updated +# override function: allow shib to handle the session time out +sub check_session { + my ($self, $userID, $possibleKey, $updateTimestamp) = @_; + my $ce = $self->{r}->ce; + my $db = $self->{r}->db; + + if ( $ce->{shiboff} ) { + return $self->SUPER::check_session( @_ ); + } else { + my $Key = $db->getKey($userID); # checked + return 0 unless defined $Key; + + my $keyMatches = (defined $possibleKey and $possibleKey eq $Key->key); + my $timestampValid = (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}); + if ($ce->{shibboleth}{manage_session_timeout}) { + # always valid to allow shib to take control of timeout + $timestampValid = 1; + } + + if ($keyMatches and $timestampValid and $updateTimestamp) { + $Key->timestamp(time); + $db->putKey($Key); + } + return (1, $keyMatches, $timestampValid); + } +} + +1; diff --git a/lib/WeBWorK/Authz.pm b/lib/WeBWorK/Authz.pm index 25e29f7b6a..6755fc1d1c 100644 --- a/lib/WeBWorK/Authz.pm +++ b/lib/WeBWorK/Authz.pm @@ -1,7 +1,7 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Authz.pm,v 1.36 2007/08/13 22:59:54 sh002i 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 @@ -20,38 +20,449 @@ package WeBWorK::Authz; WeBWorK::Authz - check user permissions. +=head1 SYNOPSIS + + # create new authorizer -- $r is a WeBWorK::Request object. + my $authz = new WeBWorK::Authz($r); + + # tell authorizer to cache permission level of user spammy. + $authz->setCachedUser("spammy"); + + # this call will use the cached data. + if ($authz->hasPermissions("spammy", "eat_breakfast")) { + eat_breakfast(); + } + + # this call will not use the cached data, and will cause a database lookup. + if ($authz->hasPermissions("hammy", "go_to_bed")) { + go_to_bed(); + } + +=head1 DESCRIPTION + +WeBWorK::Authen determines if a user is authorized to perform a specific +activity, based on the user's PermissionLevel record in the WeBWorK database and +the contents of the %permissionLevels hash in the course environment. + +=head2 Format of the %permissionLevels hash + +%permissionLevels maps text strings describing activities to numeric permission +levels. The definitive list of activities is contained in the default version of +%permissionLevels, in the file F. + +A user is able to engage in an activity if their permission level is greater +than or equal to the level associated with the activity. If the level associated +with an activity is undefiend, then no user is permitted to perform the +activity, regardless of their permission level. + =cut use strict; use warnings; +use Carp qw/croak/; +# FIXME SET: set-level auth add +use WeBWorK::Utils qw(before after between); +use WeBWorK::Authen::Proctor; +use Net::IP; + +################################################################################ + +=head1 CONSTRUCTOR + +=over + +=item WeBWorK::Authz->new($r) + +Creates a new authorizer instance. $r is a WeBWorK::Request object. It must +already have its C and C fields set. + +=cut -# WeBWorK::Authz->new($r, $ce, $db) -sub new($$$$) { - my $invocant = shift; +sub new { + my ($invocant, $r) = @_; my $class = ref($invocant) || $invocant; - my $self = {}; - ($self->{r}, $self->{ce}, $self->{db}) = @_; + my $self = { + r => $r, + }; + bless $self, $class; return $self; } +=back + +=cut + +################################################################################ + +=head1 METHODS + +=over + +=item setCachedUser($userID) + +Caches the PermissionLevel of the user $userID in an existing authorizer. If a +user's PermissionLevel is cached, it will be used whenever hasPermissions() is +called on the same user. Only one user can be cached at a time. This is used by +WeBWorK to cache the "real" user. + +=cut + +sub setCachedUser { + my ($self, $userID) = @_; + my $r = $self->{r}; + my $db = $r->db; + + delete $self->{userID}; + delete $self->{PermissionLevel}; + + if (defined $userID) { + $self->{userID} = $userID; + my $PermissionLevel = $db->getPermissionLevel($userID); # checked + if (defined $PermissionLevel) { + # store permission level record in database to avoid later database calls + $self->{PermissionLevel} = $PermissionLevel; + } + } else { + warn "setCachedUser() called with userID undefined."; + } +} + +=item hasPermissions($userID, $activity) + +Checks the %permissionLevels hash in the course environment to determine if the +user $userID has permission to engage in the activity $activity. If the user's +permission level is greater than or equal to the level associated with $activty, +a true value is returned. Otherwise, a false value is returned. + +If $userID has been cached using the setCachedUser() call, the cached data is +used. Otherwise, the user's PermissionLevel is looked up in the WeBWorK +database. + +If the user does not have a PermissionLevel record, the permission level record +is empty, or the activity does not appear in %permissionLevels, hasPermissions() +assumes that the user does not have permission. + +=cut + # This currently only uses two of it's arguments, but it accepts any number, in # case in the future calculating certain permissions requires more information. sub hasPermissions { - my ($self, $user, $activity) = @_; + if (@_ != 3) { + shift @_; # get rid of self + my $nargs = @_; + croak "hasPermissions called with $nargs arguments instead of the expected 2: '@_'" + } + + my ($self, $userID, $activity) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + # this may need to be changed if we get other permission level data sources + return 0 unless defined $db; + + # this may need to be changed if we want to control what unauthenticated users + # can do with the permissions system + return 0 unless defined $userID and $userID ne ""; + + my $PermissionLevel; + + my $cachedUserID = $self->{userID}; + if (defined $cachedUserID and $cachedUserID ne "" and $cachedUserID eq $userID) { + # this is the same user -- we can skip the database call + $PermissionLevel = $self->{PermissionLevel}; + } else { + # a different user, or no user was defined before + #my $prettyCachedUserID = defined $cachedUserID ? "'$cachedUserID'" : "undefined"; + #warn "hasPermissions called with user '$userID', but cached user is $prettyCachedUserID. Accessing database.\n"; + $PermissionLevel = $db->getPermissionLevel($userID); # checked + } + + my $permission_level; + + if (defined $PermissionLevel) { + $permission_level = $PermissionLevel->permission; + } else { + # uh, oh. this user has no permission level record! + warn "User '$userID' has no PermissionLevel record -- assuming no permission."; + return 0; + } + + unless (defined $permission_level and $permission_level ne "") { + warn "User '$userID' has empty permission level -- assuming no permission."; + return 0; + } + + my $userRoles = $ce->{userRoles}; + my $permissionLevels = $ce->{permissionLevels}; + + if (exists $permissionLevels->{$activity}) { + my $activity_role = $permissionLevels->{$activity}; + if (defined $activity_role) { + if (exists $userRoles->{$activity_role}) { + my $role_permlevel = $userRoles->{$activity_role}; + if (defined $role_permlevel) { + return $permission_level >= $role_permlevel; + } else { + warn "Role '$activity_role' has undefined permisison level -- assuming no permission."; + return 0; + } + } else { + warn "Role '$activity_role' for activity '$activity' not found in \%userRoles -- assuming no permission."; + return 0; + } + } else { + return 0; # undefiend $activity_role, no one has permission to perform $activity + } + } else { + warn "Activity '$activity' not found in \%permissionLevels -- assuming no permission."; + return 0; + } +} + +#### set-level authorization routines + +sub checkSet { + my $self = shift; my $r = $self->{r}; - my $courseEnvironment = $self->{ce}; - my $permissionLevels = $courseEnvironment->{permissionLevels}; - - my $Permission = $self->{db}->getPermissionLevel($user); # checked - return 0 unless defined $Permission; - my $permissionLevel = $Permission->permission(); - if (defined $permissionLevels->{$activity} - and $permissionLevel >= $permissionLevels->{$activity}) { - return 1; + my $ce = $r->ce; + my $db = $r->db; + my $urlPath = $r->urlpath; + + my $node_name = $urlPath->type; + + # first check to see if we have to worried about set-level access + # restrictions + return 0 unless (grep {/^$node_name$/} + (qw(problem_list problem_detail gateway_quiz + proctored_gateway_quiz))); + + # to check set restrictions we need a set and a user + my $setName = $urlPath->arg("setID"); + my $userName = $r->param("user"); + my $effectiveUserName = $r->param("effectiveUser"); + + # if there is no input userName, then the content generator will + # be forcing a login, so just bail + return 0 if ( ! $userName || ! $effectiveUserName ); + + # do we have a cached set that we can use? + my $set = $self->{merged_set}; + + if ( $setName =~ /,v(\d+)$/ ) { + my $verNum = $1; + $setName =~ s/,v\d+$//; + + if ( $set && $set->set_id eq $setName && + $set->user_id eq $effectiveUserName && + $set->version_id eq $verNum ) { + # then we can just use this set and skip the rest + + } elsif ( $setName eq 'Undefined_Set' and + $self->hasPermissions($userName, "access_instructor_tools") ) { + # this is the case of previewing a problem + # from a 'try it' link + return 0; + } else { + if ($db->existsSetVersion($effectiveUserName,$setName,$verNum)) { + $set = $db->getMergedSetVersion($effectiveUserName,$setName,$verNum); + } else { + return "Requested version ($verNum) of set " . + "'$setName' is not assigned to user " . + "$effectiveUserName."; + } + } + if ( ! $set ) { + return "Requested set '$setName' could not be found " . + "in the database for user $effectiveUserName."; + } + } else { + + if ( $set && $set->set_id eq $setName && + $set->user_id eq $effectiveUserName ) { + # then we can just use this set, and skip the rest + + } else { + if ( $db->existsUserSet($effectiveUserName,$setName) ) { + $set = $db->getMergedSet($effectiveUserName,$setName); + } elsif ( $setName eq 'Undefined_Set' and + $self->hasPermissions($userName, "access_instructor_tools") ) { + # this is the weird case of the library + # browser, when we don't actually have + # a set to look at, but this only happens among + # instructor tool users. + return 0; + } else { + return "Requested set '$setName' is not " . + "assigned to user $effectiveUserName."; + } + } + if ( ! $set ) { + return "Requested set '$setName' could not be found " . + "in the database for user $effectiveUserName."; + } + } + # cache the set for future use as needed. this should probably + # be more sophisticated than this + $self->{merged_set} = $set; + + # now we know that the set is assigned to the appropriate user; + # check to see if we're trying to access a set that's not open + if ( before($set->open_date) && + ! $self->hasPermissions($userName, "view_unopened_sets") ) { + return "Requested set '$setName' is not yet open."; + } + + # also check to make sure that the set is visible, or that we're + # allowed to view hidden sets + # (do we need to worry about visible not being set at this point?) + my $visible = ( $set && $set->visible ne '0' && + $set->visible ne '1' ) ? 1 : $set->visible; + if ( ! $visible && + ! $self->hasPermissions($userName, "view_hidden_sets") ) { + return "Requested set '$setName' is not available yet."; + } + + # check to be sure that gateways are being sent to the correct + # content generator + if (defined($set->assignment_type) && + $set->assignment_type =~ /gateway/ && + ($node_name eq 'problem_list' || $node_name eq 'problem_detail')) { + return "Requested set '$setName' is a test/quiz assignment " . + "but the regular homework assignment content " . + "generator $node_name was called. Try re-entering " . + "the set from the problem sets listing page."; + } elsif ( (! defined($set->assignment_type) || + $set->assignment_type eq 'homework') && + $node_name =~ /gateway/ ) { + return "Requested set '$setName' is a homework assignment " . + "but the gateway/quiz content " . + "generator $node_name was called. Try re-entering " . + "the set from the problem sets listing page."; + } + + # and check that if we're entering a proctored assignment that we + # have a valid proctor login; this is necessary to make sure that + # someone doesn't use the unproctored url path to obtain access + # to a proctored assignment. + if (defined($set->assignment_type) && + $set->assignment_type =~ /proctored/ && + ! WeBWorK::Authen::Proctor->new($r,$ce,$db)->verify() ) { + return "Requested set '$setName' is a proctored test/quiz " . + "assignment, but no valid proctor authorization " . + "has been obtained."; + } + + # and whether there are ip restrictions that we need to check + my $badIP = $self->invalidIPAddress($set); + return $badIP if $badIP; + + return 0; +} + +sub invalidIPAddress { +# this exists as a separate routine because we need to check multiple +# sets in Hardcopy; having this routine to check the set allows us to do +# that for all sets individually there. + + my $self = shift; + my $set = shift; + + my $r = $self->{r}; + my $db = $r->db; + my $urlPath = $r->urlpath; +# my $setName = $urlPath->arg("setID"); # not always defined + my $setName = $set->set_id; + my $userName = $r->param("user"); + my $effectiveUserName = $r->param("effectiveUser"); + + return 0 if ($set->restrict_ip eq '' || $set->restrict_ip eq 'No' || + $self->hasPermissions($userName,'view_ip_restricted_sets')); + + my $clientIP = new Net::IP($r->connection->remote_ip); + # make sure that we're using the non-versioned set name + $setName =~ s/,v\d+$//; + + my $restrictType = $set->restrict_ip; + my @restrictLocations = $db->getAllMergedSetLocations($effectiveUserName,$setName); + my @locationIDs = ( map {$_->location_id} @restrictLocations ); + my @restrictAddresses = ( map {$db->listLocationAddresses($_)} @locationIDs ); + + # if there are no addresses in the locations, return an error that + # says this + return "Client ip address " . $clientIP->ip() . " is not allowed to " . + "work this assignment, because the assignment has ip address " . + "restrictions and there are no allowed locations associated " . + "with the restriction. Contact your professor to have this " . + "problem resolved." if ( ! @restrictAddresses ); + + # build a set of IP objects to match against + my @restrictIPs = ( map {new Net::IP($_)} @restrictAddresses ); + + # and check the clientAddress against these: is $clientIP + # in @restrictIPs? + my $inRestrict = 0; + foreach my $rIP ( @restrictIPs ) { + if ($rIP->overlaps($clientIP) == $IP_B_IN_A_OVERLAP || + $rIP->overlaps($clientIP) == $IP_IDENTICAL) { + $inRestrict = $rIP->ip(); + last; + } + } + + # this is slightly complicated by having to check relax_restrict_ip + my $badIP = ''; + if ( $restrictType eq 'RestrictTo' && ! $inRestrict ) { + $badIP = "Client ip address " . $clientIP->ip() . + " is not in the list of addresses from " . + "which this assignment may be worked."; + } elsif ( $restrictType eq 'DenyFrom' && $inRestrict ) { + $badIP = "Client ip address " . $clientIP->ip() . + " is in the list of addresses from " . + "which this assignment may not be worked."; } else { return 0; } + + # if we're here, we failed the IP check, and so need to consider + # if ip restrictions were relaxed. the set we were passed in + # is either the merged userset or the merged versioned userset, + # depending on whether the set is versioned or not + + my $relaxRestrict = $set->relax_restrict_ip; + return $badIP if ( $relaxRestrict eq 'No' ); + + if ( $set->assignment_type =~ /gateway/ ) { + if ( $relaxRestrict eq 'AfterAnswerDate' ) { + # in this case we need to go and get the userset, + # not the versioned set (which we already have) + # drat! + my $userset = $db->getMergedSet($set->user_id,$setName); + return( ! $userset || before($userset->answer_date) + ? $badIP : 0 ); + } else { + # this is easier; just look at the current answer date + return( before($set->answer_date) ? $badIP : 0 ); + } + } else { + # the set isn't versioned, so assume that $relaxRestrict + # is 'AfterAnswerDate', regardless of what it actually + # is; 'AfterVersionAnswerDate' doesn't make sense in + # this case + return( before($set->answer_date) ? $badIP : 0 ); + } } +=back + +=cut + +=head1 AUTHOR + +Written by Dennis Lambe, malsyned at math.rochester.edu. Modified by Sam +Hathaway, sh002i at math.rochester.edu. + +=cut + 1; diff --git a/bin/gif2png b/lib/WeBWorK/CGI.pm old mode 100755 new mode 100644 similarity index 57% rename from bin/gif2png rename to lib/WeBWorK/CGI.pm index 1c57e4cb53..1dfa80cc40 --- a/bin/gif2png +++ b/lib/WeBWorK/CGI.pm @@ -1,8 +1,7 @@ -#!/bin/sh ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/CGI.pm,v 1.27 2006/09/15 22:02:37 sh002i 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 @@ -15,19 +14,24 @@ # Artistic License for more details. ################################################################################ -# This script wraps system-dependant calls to several netpbm utilities. -# In order to use this script, set the NETPBM variable below to the -# directory which contains giftopnm, ppmtopgm, and pnmtops. +package WeBWorK::CGI; -NETPBM=/usr/local/bin +use strict; +use warnings; -# If you wish to set the paths to each utility separately, i.e. if they -# are in different locations, do so below: +# from http://search.cpan.org/src/LDS/CGI.pm-3.20/cgi_docs.html#subclassing +use vars qw/@ISA $VERSION/; +require CGI; +@ISA = 'CGI'; +$VERSION = "0.1"; -GIFTOPNM=$NETPBM/giftopnm; -PNMTOPNG=$NETPBM/pnmtopng; +$CGI::DefaultClass = __PACKAGE__; +$WeBWorK::CGI::AutoloadClass = 'CGI'; -# There should be no need for customization beyond this point. +sub new { + my $self = shift->SUPER::new(@_); + $self->delete_all; + return $self; +} -umask 022 -cat $1 | $GIFTOPNM | $PNMTOPNG > $2 +1; diff --git a/lib/WeBWorK/Constants.pm b/lib/WeBWorK/Constants.pm index d5da847ddd..032180b94d 100644 --- a/lib/WeBWorK/Constants.pm +++ b/lib/WeBWorK/Constants.pm @@ -1,13 +1,13 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Constants.pm,v 1.62 2010/02/01 01:57:56 apizer 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 @@ -15,7 +15,6 @@ ################################################################################ package WeBWorK::Constants; -use base qw(Exporter); =head1 NAME @@ -26,7 +25,325 @@ WeBWorK::Constants - provide constant values for other WeBWorK modules. use strict; use warnings; -our @EXPORT = qw(); -our @EXPORT_OK = qw(SECRET); +################################################################################ +# WeBWorK::Debug +################################################################################ + +# If true, WeBWorK::Debug will print debugging output. +# +$WeBWorK::Debug::Enabled = 0; + +# If non-empty, debugging output will be sent to the file named rather than STDERR. +# +$WeBWorK::Debug::Logfile = ""; + +# If defined, prevent subroutines matching the following regular expression from +# logging. +# +# For example, this pattern prevents the dispatch() function from logging: +# $WeBWorK::Debug::DenySubroutineOutput = qr/^WeBWorK::dispatch$/; +# +$WeBWorK::Debug::DenySubroutineOutput = undef; + +# If defined, allow only subroutines matching the following regular expression +# to log. +# +# For example, this pattern allow only some function being worked on to log: +# $WeBWorK::Debug::AllowSubroutineOutput = qr/^WeBWorK::SomePkg::myFunc$/; +# +$WeBWorK::Debug::AllowSubroutineOutput = undef; + +################################################################################ +# WeBWorK::ContentGenerator::Hardcopy +################################################################################ + +# If true, don't delete temporary files +# +$WeBWorK::ContentGenerator::Hardcopy::PreserveTempFiles = 0; + +################################################################################ +# WeBWorK::PG::Local +################################################################################ +# The maximum amount of time (in seconds) to work on a single problem. +# At the end of this time a timeout message is sent to the browser. + +$WeBWorK::PG::Local::TIMEOUT = 60; + +################################################################################ +# WeBWorK::PG::ImageGenerator +################################################################################ + +# Arguments to pass to dvipng. This is dependant on the version of dvipng. +# +# For dvipng versions 0.x +# $WeBWorK::PG::ImageGenerator::DvipngArgs = "-x4000.5 -bgTransparent -Q6 -mode toshiba -D180"; +# For dvipng versions 1.0 to 1.5 +# $WeBWorK::PG::ImageGenerator::DvipngArgs = "-bgTransparent -D120 -q -depth"; +# +# For dvipng versions 1.6 (and probably above) +# $WeBWorK::PG::ImageGenerator::DvipngArgs = "-bgtransparent -D120 -q -depth"; +# Note: In 1.6 and later, bgTransparent gives alpha-channel transparency while +# bgtransparent gives single-bit transparency. If you use alpha-channel transparency, +# the images will not be viewable with MSIE. bgtransparent works for version 1.5, +# but does not give transparent backgrounds. It does not work for version 1.2. It has not +# been tested with other versions. +# +$WeBWorK::PG::ImageGenerator::DvipngArgs = "-bgTransparent -D120 -q -depth"; + +# If true, don't delete temporary files +# +$WeBWorK::PG::ImageGenerator::PreserveTempFiles = 0; + +# TeX to prepend to equations to be processed. +# +$WeBWorK::PG::ImageGenerator::TexPreamble = <<'EOF'; +\documentclass[12pt]{article} +\nonstopmode +\usepackage{amsmath,amsfonts,amssymb} +\def\gt{>} +\def\lt{<} +\usepackage[active,textmath,displaymath]{preview} +\begin{document} +EOF + +# TeX to append to equations to be processed. +# +$WeBWorK::PG::ImageGenerator::TexPostamble = <<'EOF'; +\end{document} +EOF + +################################################################################ +# WeBWorK::ContentGenerator::Instructor::Config +################################################################################ + +# Configuation data +# It is organized by section. The allowable types are +# 'text' for a text string (no quote marks allowed), +# 'number' for a number, +# 'list' for a list of text strings, +# 'permission' for a permission value, +# 'boolean' for variables which really hold 0/1 values as flags. +# 'checkboxlist' for variables which really hold a list of values which +# can be independently picked yes/no as checkboxes + +$WeBWorK::ContentGenerator::Instructor::Config::ConfigValues = [ + ['General', + { var => 'courseFiles{course_info}', + doc => 'Name of course information file', + doc2 => 'The name of course information file (located in the templates directory). Its contents are displayed in the right panel next to the list of homework sets.', + type => 'text'}, + { var => 'defaultTheme', + doc => 'Theme (refresh page after saving changes to reveal new theme.)', + doc2 => 'There are currently five themes (or skins) to choose from: ur, math, math2, and dgage. The theme +specifies a unified look and feel for the WeBWorK course web pages.', + values => [qw(math math2 math3 ur dgage union)], + type => 'popuplist'}, + { var => 'language', + doc => 'Language (refresh page after saving changes to reveal new language.)', + doc2 => 'WeBWorK currently has translations for four languages: "English en", "Turkish tr", "Spanish es", and "French fr" ', + values => [qw(en tr es fr zh_hk heb)], + type => 'popuplist'}, + { var => 'sessionKeyTimeout', + doc => 'Inactivity time before a user is required to login again', + doc2 => 'Length of time, in seconds, a user has to be inactive before he is required to login again.

      This value should be entered as a number, so as 3600 instead of 60*60 for one hour', + type => 'number'}, + { var => 'siteDefaults{timezone}', + doc => 'Timezone for the course', + doc2 => 'Some servers handle courses taking place in different timezones. If this course is not showing the correct timezone, enter the correct value here. The format consists of unix times, such as "America/New_York","America/Chicago", "America/Denver", "America/Phoenix" or "America/Los_Angeles". Complete list: TimeZoneFiles', + type => 'text'},], + ['Permissions', + { var => 'permissionLevels{login}', + doc => 'Allowed to login to the course', + type => 'permission'}, + { var => 'permissionLevels{change_password}', + doc => 'Allowed to change their password', + doc2 => 'Users at this level and higher are allowed to change their password. Normally guest users are not allowed to change their password.', + type => 'permission'}, + { var => 'permissionLevels{become_student}', + doc => 'Allowed to act as another user', + type => 'permission'}, + { var => 'permissionLevels{submit_feedback}', + doc => 'Can e-mail instructor', + doc2 => 'Only this permission level and higher get buttons for sending e-mail to the instructor.', + type => 'permission'}, + { var => 'permissionLevels{record_answers_when_acting_as_student}', + doc => 'Can submit answers for a student', + doc2 => 'When acting as a student, this permission level and higher can submit answers for that student.', + type => 'permission'}, + { var => 'permissionLevels{report_bugs}', + doc => 'Can report bugs', + doc2 => 'Users with at least this permission level get a link in the left panel for reporting bugs to the bug tracking system in Rochester', + type => 'permission'}, + { var => 'permissionLevels{change_email_address}', + doc => 'Allowed to change their e-mail address', + doc2 => 'Users at this level and higher are allowed to change their e-mail address. Normally guest users are not allowed to change the e-mail address since it does not make sense to send e-mail to anonymous accounts.', + type => 'permission'}, + { var => 'permissionLevels{view_answers}', + doc => 'Allowed to view past answers', + doc2 => 'These users and higher get the "Show Past Answers" button on the problem page.', + type => 'permission'}, + { var => 'permissionLevels{view_unopened_sets}', + doc => 'Allowed to view problems in sets which are not open yet', + type => 'permission'}, + { var => 'permissionLevels{show_correct_answers_before_answer_date}', + doc => 'Allowed to see the correct answers before the answer date', + type => 'permission'}, + { var => 'permissionLevels{show_solutions_before_answer_date}', + doc => 'Allowed to see solutions before the answer date', + type => 'permission'}, + { var => 'permissionLevels{can_show_old_answers_by_default}', + doc => 'Can show old answers by default', + doc2 => 'When viewing a problem, WeBWorK usually puts the previously submitted answer in the answer blank if it is before the due date. Below this level, old answers are never initially shown. Typically, that is the desired behaviour for guest accounts.', + type => 'permission'}, + { var => 'permissionLevels{can_always_use_show_old_answers_default}', + doc => 'Can always show old answers by default', + doc2 => 'When viewing a problem, WeBWorK usually puts the previously submitted answer in the answer blank if it is before the due date. At this level and higher, old answers are always shown (independent of the answer date).', + type => 'permission'}, + ], + ['PG - Problem Display/Answer Checking', + { var => 'pg{displayModes}', + doc => 'List of display modes made available to students', + doc2 => 'When viewing a problem, users may choose different methods of rendering + formulas via an options box in the left panel. Here, you can adjust what display modes are + listed.

      + Some display modes require other software to be installed on the server. Be sure to check + that all display modes selected here work from your server.

      + The display modes are

        +
      • plainText: shows the raw LaTeX strings for formulas. +
      • formattedText: formulas are passed through the external program tth, + which produces an HTML version of them. Some browsers do not display all of the fonts + properly. +
      • images: produces images using the external programs LaTeX and dvipng. +
      • jsMath: uses javascript to place symbols, which may come from fonts or images + (the choice is configurable by the end user). +
      • MathJax: a successor to jsMath, uses javascript to place render mathematics. +
      • asciimath: renders formulas client side using ASCIIMathML +
      • LaTeXMathML: renders formulas client side using LaTeXMathML +
      +

      +You must use at least one display mode. If you select only one, then the options box will + not give a choice of modes (since there will only be one active).', + min => 1, + values => ["MathJax", "images", "plainText", "formattedText", "jsMath", "asciimath", "LaTeXMathML"], + type => 'checkboxlist'}, + + { var => 'pg{options}{displayMode} ', + doc => 'The default display mode', + doc2 => 'Enter one of the allowed display mode types above. See \'display modes entry\' for descriptions.', + min => 1, + type => 'text'}, + + { var => 'pg{options}{showEvaluatedAnswers}', + doc => 'Display the evaluated student answer', + doc2 => 'Set to true to display the "Entered" column which automatically shows the evaluated student answer, e.g. 1 + if student input is sin(pi/2). If this is set to false, e.g. to save space in the response area, the student can + still see their evaluated answer by hovering the mouse pointer over the typeset version of their answer.', + type => 'boolean'}, + + { var => 'pg{options}{showEvaluatedAnswers}', + doc => 'Display the evaluated student answer', + doc2 => 'Set to true to display the "Entered" column which automatically shows the evaluated student answer, e.g. 1 + if student input is sin(pi/2). If this is set to false, e.g. to save space in the response area, the student can + still see their evaluated answer by hovering the mouse pointer over the typeset version of their answer.', + type => 'boolean'}, + + { var => 'pg{ansEvalDefaults}{useBaseTenLog}', + doc => 'Use log base 10 instead of base e', + doc2 => 'Set to true for log to mean base 10 log and false for log to mean natural logarithm', + type => 'boolean'}, + + { var => 'pg{ansEvalDefaults}{useOldAnswerMacros}', + doc => 'Use older answer checkers', + doc2 => 'During summer 2005, a newer version of the answer checkers was implemented for answers which are functions and numbers. The newer checkers allow more functions in student answers, and behave better in certain cases. Some problems are specifically coded to use new (or old) answer checkers. However, for the bulk of the problems, you can choose what the default will be here.

      Choosing false here means that the newer answer checkers will be used by default, and choosing true means that the old answer checkers will be used by default.', + type => 'boolean'}, + + { var => 'pg{ansEvalDefaults}{defaultDisplayMatrixStyle}', + doc => 'Control string for displaying matricies', + doc2 => 'String of three characters for defining the defaults for displaying matricies. The first and last characters give the left and right delimiters of the matrix, so usually one of ([| for a left delimiter, and one of )]| for the right delimiter. It is also legal to specify "." for no delimiter.

      The middle character indicates how to display vertical lines in a matrix (e.g., for an augmented matrix). This can be s for solid lines and d for dashed lines. While you can specify the defaults, individual problems may override these values.', + type => 'text'}, + + { var => 'pg{ansEvalDefaults}{numRelPercentTolDefault}', + doc => 'Allowed error, as a percentage, for numerical comparisons', + doc2 => "When numerical answers are checked, most test if the student's answer + is close enough to the programmed answer be computing the error as a percentage of + the correct answer. This value controls the default for how close the student answer + has to be in order to be marked correct. +

      +A value such as 0.1 means 0.1 percent error is allowed.", + type => 'number'}, + { var => 'pg{ansEvalDefaults}{reducedScoringPeriod}', + doc => 'Length of Reduced Credit Period in minutes', + doc2 => 'The Reduced Credit Period (formally called the Reduced Scoring Period) is a period before the due date during which + all additional work done by the student counts at a reduced rate. Here is where + you set the length of this period in minutes. If this value is greater than 0, a + message like "This assignment has a Reduced Credit Period that begins 11/08/2009 + at 06:17pm EST and ends on the due date, 11/10/2009 at 06:17pm EST. During this + period all additional work done counts 50% of the original." will be displayed.

      + To use this, you also have to enable Reduced Credit for individual assignments by + editing the set data using the Hmwk Sets Editor.

      + This works with the avg_problem_grader (which is the the default grader) and the + std_problem_grader (the all or nothing grader). It will work with custom graders + if they are written appropriately.' , + type => 'number'}, + { var => 'pg{ansEvalDefaults}{reducedScoringValue}', + doc => 'Value of work done in Reduced Credit Period' , + doc2 => 'The Reduced Credit Period (formally called the Reduced Scoring Period) is a period before the due date during which + all additional work done by the student counts at a reduced rate. Here is where + you set the reduced rate which must be a number in the interval [0,1]. 1 means no + reduction. For example if this value is .5 and a student views a problem during the + Reduced Credit Period, they will see the message "You are in the Reduced Credit + Period: All additional work done counts 50% of the original."

      + To use this, you also have to enable Reduced Credit for individual assignments by + editing the set data using the Hmwk Sets Editor.

      + This works with the avg_problem_grader (which is the the default grader) and the + std_problem_grader (the all or nothing grader). It will work with custom graders + if they are written appropriately.' , + type => 'number'}, + ], + ['E-Mail', + { var => 'mail{feedbackSubjectFormat}', + doc => 'Format for the subject line in feedback e-mails', + doc2 => 'When students click the Email Instructor button + to send feedback, WeBWorK fills in the subject line. Here you can set the + subject line. In it, you can have various bits of information filled in + with the following escape sequences. +

      +

        +
      • %c = course ID +
      • %u = user ID +
      • %s = set ID +
      • %p = problem ID +
      • %x = section +
      • %r = recitation +
      • %% = literal percent sign +
      ', + width => 45, + type => 'text'}, + { var => 'mail{feedbackVerbosity}', + doc => 'E-mail verbosity level', + doc2 => 'The e-mail verbosity level controls how much information is + automatically added to feedback e-mails. Levels are +
        +
      1. send only the feedback comment and context link +
      2. as in 0, plus user, set, problem, and PG data +
      3. as in 1, plus the problem environment (debugging data) +
      ', + type => 'number' + }, + { var => 'mail{allowedRecipients}', + doc => 'E-mail addresses which can receive e-mail from a pg problem', + doc2 => 'List of e-mail addresses to which e-mail can be sent by a problem. Professors need to be added to this list if questionaires are used, or other WeBWorK problems which send e-mail as part of their answer mechanism.', + type => 'list'}, + { var => 'permissionLevels{receive_feedback}', + doc => 'E-mail feedback from students automatically sent to this permission level and higher:', + doc2 => 'Users with this permssion level or greater will automatically be sent feedback from students (generated when they use the "Contact instructor" button on any problem page). In addition the feedback message will be sent to addresses listed below. To send ONLY to addresses listed below set permission level to "nobody".', + type => 'permission'}, + + { var => 'mail{feedbackRecipients}', + doc => 'Additional addresses for receiving feedback e-mail.', + doc2 => 'By default, feeback is sent to all users above who have permission to receive feedback. Feedback is also sent to any addresses specified in this blank. Separate email address entries by commas.', + type => 'list'}, + ] +]; -use constant SECRET => 'fkjOPIiSUfeT6dm5pevSrM1xgFmsex7.Z/.6Wjcxqb9Pi4Zm9JUGygwv^FdG8^yth^*KbDFWMXiLtNDggWNA370llFj68JxNKMCyCeSJxCHRfU2P6br10HtPS!NvcaJ7'; +1; diff --git a/lib/WeBWorK/ContentGenerator.pm b/lib/WeBWorK/ContentGenerator.pm index 3db263a975..13ccbb135d 100644 --- a/lib/WeBWorK/ContentGenerator.pm +++ b/lib/WeBWorK/ContentGenerator.pm @@ -1,7 +1,7 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator.pm,v 1.196 2009/06/04 01:33:15 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 @@ -20,665 +20,2062 @@ package WeBWorK::ContentGenerator; WeBWorK::ContentGenerator - base class for modules that generate page content. +=head1 SYNOPSIS + + # start with a WeBWorK::Request object: $r + + use WeBWorK::ContentGenerator::SomeSubclass; + + my $cg = WeBWorK::ContentGenerator::SomeSubclass->new($r); + my $result = $cg->go(); + +=head1 DESCRIPTION + +WeBWorK::ContentGenerator provides the framework for generating page content. +"Content generators" are subclasses of this class which provide content for +particular parts of the system. + +Default versions of methods used by the templating system are provided. Several +useful methods are provided for rendering common output idioms and some +miscellaneous utilities are provided. + =cut use strict; use warnings; -use Apache::Constants qw(:common); -use CGI qw(); +use Carp; +#use CGI qw(-nosticky *ul *li escapeHTML); +use WeBWorK::CGI; +use WeBWorK::File::Scoring qw/parse_scoring_file/; +use Date::Format; use URI::Escape; -use WeBWorK::Authz; -use WeBWorK::DB; -use WeBWorK::Utils qw(readFile); +use WeBWorK::Debug; +use WeBWorK::PG; +use MIME::Base64; +use WeBWorK::Template qw(template); +use WeBWorK::Localize; +use mod_perl; +use constant MP2 => ( exists $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ); -################################################################################ -# This is a very unruly file, so I'm going to use very large comments to divide -# it into logical sections. -################################################################################ -# new(Apache::Request, WeBWorK::CourseEnvironment, WeBWorK::DB) - create a new -# instance of a content generator. Usually only called by the dispatcher, although -# one might be able to use it for things like "sub-requests". Uh... uh... I have -# to think about that one. The dispatcher uses this idiom: -# -# WeBWorK::ContentGenerator::WHATEVER->new($r, $ce, $db)->go(@whatever); -# -# and throws away the result ;) -# + + +BEGIN { + if (MP2) { + require Apache2::Const; + Apache2::Const->import(-compile => qw/OK NOT_FOUND FORBIDDEN SERVER_ERROR REDIRECT/); + } else { + require Apache::Constants; + Apache::Constants->import(qw/OK NOT_FOUND FORBIDDEN SERVER_ERROR REDIRECT/); + } +} + +############################################################################### + +=head1 CONSTRUCTOR + +=over + +=item new($r) + +Creates a new instance of a content generator. Supply a WeBWorK::Request object +$r. + +=cut + sub new { - my ($invocant, $r, $ce, $db) = @_; + my ($invocant, $r) = @_; my $class = ref($invocant) || $invocant; my $self = { - r => $r, - ce => $ce, - db => $db, - authz => WeBWorK::Authz->new($r, $ce, $db), - noContent => undef, + r => $r, # this is now a WeBWorK::Request + ce => $r->ce(), # these three are here for + db => $r->db(), # backward-compatability + authz => $r->authz(), # with unconverted CGs + noContent => undef, # FIXME this should get clobbered at some point }; bless $self, $class; return $self; } -################################################################################ -# Invocation and template processing +=back + +=cut + ################################################################################ -# go(@otherArguments) - render a page, using methods from the particular -# subclass of ContentGenerator. @otherArguments is passed to each method, so -# that the dispatcher can pass CG-specific data. The order of calls looks like -# this: -# -# * &pre_header_initialize - give subclasses a chance to do initialization -# necessary for generating the HTTP header. -# * &header - this class provides a standard HTTP header with Content-Type -# text/html. Subclasses are welcome to overload this for things like -# an image-creation content generator or a PDF generator. -# In addition, if &header returns a value, that will be the value -# returned by the entire PerlHandler. -# * &initialize - let subclasses do post-header initialization. -# * any "template escapes" defined in the system template and supported by -# the subclass. -# (if &content exists on a content generator, it is called -# and no template processing occurs.) -# -# If &pre_header_initialize or &header sets $self->{noContent} to a true value, -# &initialize will not be run and the content or template processing code -# will not be executed. This is probably only desirable if a redirect has been -# issued. +=head1 INVOCATION + +=over + +=item go() + +Generates a page, using methods from the particular subclass of ContentGenerator +that is instantiated. Generatoion is broken up into several steps, to give +subclasses ample control over the process. + +=over + +=item 1 + +go() will attempt to call the method pre_header_initialize(). This method may be +implemented in subclasses which must do processing before the HTTP header is +emitted. + +=item 2 + +go() will attempt to call the method header(). This method emits the HTTP +header. It is defined in this class (see below), but may be overridden in +subclasses which need to send different header information. For some reason, the +return value of header() will be used as the result of this function, if it is +defined. + +FIXME: figure out what the deal is with the return value of header(). If we sent +a header, it's too late to set the status by returning. If we didn't, header() +didn't perform its function! + +=item 3 + +At this point, go() will terminate if the request is a HEAD request or if the +field $self->{noContent} contains a true value. + +FIXME: I don't think we'll need noContent after reply_with_redirect() is +adopted by all modules. + +=item 4 + +go() then attempts to call the method initialize(). This method may be +implemented in subclasses which must do processing after the HTTP header is sent +but before any content is sent. + +=item 6 + +The method content() is called to send the page content to client. + +=back + +=cut + sub go { - my $self = shift; + my ($self) = @_; + my $r = $self->r; + my $ce = $r->ce; + + # check to verify if there are set-level problems with running + # this content generator (individual content generators must + # check $self->{invalidSet} and react correctly) + my $authz = $r->authz; + $self->{invalidSet} = $authz->checkSet(); - my $r = $self->{r}; - my $ce = $self->{ce}; - my $returnValue = OK; + my $returnValue = MP2 ? Apache2::Const::OK : Apache::Constants::OK; + # We only write to the activity log if it has been defined and if + # we are in a specific course. The latter check is to prevent attempts + # to write to a course log file when viewing the top-level list of + # courses page. + WeBWorK::Utils::writeCourseLog($ce, 'activity_log', + $self->prepare_activity_entry) if ( $r->urlpath->arg("courseID") and + $r->ce->{courseFiles}->{logs}->{activity_log}); + $self->pre_header_initialize(@_) if $self->can("pre_header_initialize"); + + # send a file instead of a normal reply (reply_with_file() sets this field) + defined $self->{reply_with_file} and do { + return $self->do_reply_with_file($self->{reply_with_file}); + }; + + # send a Location: header instead of a normal reply (reply_with_redirect() sets this field) + defined $self->{reply_with_redirect} and do { + return $self->do_reply_with_redirect($self->{reply_with_redirect}); + }; + my $headerReturn = $self->header(@_); $returnValue = $headerReturn if defined $headerReturn; + # FIXME: we won't need noContent after reply_with_redirect() is adopted return $returnValue if $r->header_only or $self->{noContent}; - # if the sendFile flag is set, send the file and exit; - if ($self->{sendFile}) { - return $self->sendFile; - } - - $self->initialize(@_) if $self->can("initialize"); + $self->initialize() if $self->can("initialize"); - # A content generator will have a "content" method if it does not - # wish to be passed through template processing, but wishes to be - # completely responsible for it's own output. - if ($self->can("content")) { - $self->content(@_); - } else { - # if the content generator specifies a custom template name, use that - # field in the $ce->{templates} hash instead of "system" if it exists. - my $templateName; - if ($self->can("templateName")) { - $templateName = $self->templateName; - } else { - $templateName = "system"; - } - $templateName = "system" unless exists $ce->{templates}->{$templateName}; - $self->template($ce->{templates}->{$templateName}, @_); - } + $self->content(); return $returnValue; } -sub sendFile { +=item r() + +Returns a reference to the WeBWorK::Request object associated with this +instance. + +=cut + +sub r { my ($self) = @_; - my $file = $self->{sendFile}->{source}; - - return NOT_FOUND unless -e $file; - return FORBIDDEN unless -r $file; - - open my $fh, "<", $file - or return SERVER_ERROR; - while (<$fh>) { - print $_; - } - close $fh; - - return OK; + return $self->{r}; } -# template(STRING, @otherArguments) - parse a template, looking for escapes of -# the form and calling a member function NAME -# (if available) for each NAME. The escapes are called like: -# -# $self->NAME(@otherArguments, \%escapeArguments) -# -# where @otherArguments originates in the dispatcher and %escapeArguments is -# parsed out of the escape itself (i.e. ARG1 => FOO, ARG2 => BAR) -# -sub template { - my ($self, $templateFile) = (shift, shift); - my $r = $self->{r}; - my $courseEnvironment = $self->{ce}; - my @ifstack = (1); # Start off in printing mode - # say $ifstack[-1] to get the result of the last <#!--if--> - - # so even though the variable $/ APPEARS to contain a newline, - #
      Method$method
      URI$uri