subanswer1 =subquestion2 -> subanswer2 =subquestion3 -> subanswer3 } Match the following countries with their corresponding capitals. { =Canada -> Ottawa =Italy -> Rome =Japan -> Tokyo =India -> New Delhi } // ===Numerical=== When was Ulysses S. Grant born? {#1822} What is the value of pi (to 3 decimal places)? {#3.1415:0.0005}. What is the value of pi (to 3 decimal places)? {#3.141..3.142}. When was Ulysses S. Grant born? {# =1822:0 =%50%1822:2} // OPTIONS // ===Line Comments=== // Subheading: Numerical questions below What's 2 plus 2? {#4} // ===Question Name=== ::Kanji Origins::Japanese characters originally came from what country? {=China} ::Thanksgiving Date::The American holiday of Thanksgiving is celebrated on the {~second ~third =fourth} Thursday of November. // ===Feedback=== What's the answer to this multiple-choice question?{ ~wrong answer#feedback comment on the wrong answer ~another wrong answer#feedback comment on this wrong answer =right answer#Very good!} Who's buried in Grant's tomb?{ =no one#excellent answer! =nobody#excellent answer!} // ===Specify text format=== [markdown]Who's buried in **Grant's tomb**?{ =no one#excellent answer! =nobody#excellent answer!} // ===Percentage Answer Weights=== Grant is buried in Grant's tomb.{FALSE#No one is buried in Grant's tomb.} Difficult question.{~wrong answer ~%50%half credit answer =full credit answer} ::Jesus' hometown::Jesus Christ was from { ~Jerusalem#This was an important city, but the wrong answer. ~%25%Bethlehem#He was born here, but not raised here. ~%50%Galilee#You need to be more specific. =Nazareth#Yes! That's right!}. ::Jesus' hometown:: Jesus Christ was from { =Nazareth#Yes! That's right! =%75%Nazereth#Right, but misspelled. =%25%Bethlehem#He was born here, but not raised here.} // ===Multiple Answers=== What two people are entombed in Grant's tomb? { ~No one ~%50%Grant ~%50%Grant's wife ~Grant's father } What two people are entombed in Grant's tomb? { ~%-50%No one ~%50%Grant ~%50%Grant's wife ~%-50%Grant's father } // ===Special Characters=== Which answer equals 5? { ~ \= 2 + 2 = \= 2 + 3 ~ \= 2 + 4 } ::GIFT Control Characters:: Which of the following is NOT a control character for the GIFT import format? { ~ \~ # \~ is a control character. ~ \= # \= is a control character. ~ \# # \# is a control character. ~ \{ # \{ is a control character. ~ \} # \} is a control character. = \\ # Correct! \\ (backslash) is not a control character. BUT, it is used to escape the control characters. So, to specify a literal backslash, you must escape it with a backslash (as shown in this example). } //-----------------------------------------// // Examples from gift/format.php. //-----------------------------------------// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} Grant is {~buried =entombed ~living} in Grant's tomb. Grant is buried in Grant's tomb.{FALSE} Who's buried in Grant's tomb?{=no one =nobody} When was Ulysses S. Grant born?{#1822:5} Match the following countries with their corresponding capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo} //-----------------------------------------// // More complicated examples. //-----------------------------------------// ::Grant's Tomb::Grant is { ~buried#No one is buried there. =entombed#Right answer! ~living#We hope not! } in Grant's tomb. Difficult multiple choice question.{ ~wrong answer #comment on wrong answer ~%50%half credit answer #comment on answer =full credit answer #well done!} ::Jesus' hometown (Short answer ex.):: Jesus Christ was from { =Nazareth#Yes! That's right! =%75%Nazereth#Right, but misspelled. =%25%Bethlehem#He was born here, but not raised here. }. //this comment will be ignored by the filter ::Numerical example:: When was Ulysses S. Grant born? {# =1822:0 #Correct! 100% credit =%50%1822:2 #He was born in 1822. You get 50% credit for being close. }"; class GIFT { public $text; public $questions; public function __construct($input) { $this->text = $input; $this->questions = $this->parseQuiz($input); } public function fixscape($str) { return strtr($str,array( '&126;' => '~', '&61;' => '=', '&35;' => '#', '&123;' => '{', '&125;' => '}', '&92;' => '\\', '[code]' => '
',
		'[br]'	=> '
', '[/code]' => '
', )); } public function parseQuiz($input) { $escape = array( '\\'.'~' => '&126;', '\\'.'=' => '&61;', '\\'.'#' => '&35;', '\\'.'{' => '&123;', '\\'.'}' => '&125;', '\\'.'\\' => '&92;', ); $unescape = array_flip($escape); //echo "
";

	//Break up each question chunk
	$input = explode("

",$input);

	$result = array();

	//Go over each question chunk
	foreach ($input as $line) {
	
		//Break it up line by line
		$line = explode("
",trim($line));
	
		//Kill comment lines
		$kill = "";
		if (count($line) > 0) foreach ($line as $l) {
	
			if (substr($l,0,2) != "//") $kill .= " ".trim($l);
		
		}
	
		if (trim($kill)=="") continue;
	
		$line = $kill;
	
		//Escape all special characters
		$line = strtr($line, $escape);
	
		//Create the new question array
		$question = array();
	
		//Find format
		if ($line[0] == "[") {
	
			$line = explode("]",$line,2);
		
			$format = substr($line[0],1);
			$format = trim($format);
			$format = strtolower($format);
		
			if (!in_array($format,array('moodle','html','markdown','plain'))) {
				$question["type"] = "error";
				$question["reason"] = 'Unknown format';
				$question["raw"] = $kill;
				$result[] = $question;
				continue;
			}
		
			$question["format"] = $format;
		
			$line = $line[1];
	
		}
	
		//Find the name
		if ($line[0].$line[1] == "::") {
	
			$line = explode("::",$line);
		
			//If there's an error
			if (count($line) != 3) {
				$question["type"] = "error";
				$question["reason"] = 'Improperly formed name.';
				$question["raw"] = $kill;
				$result[] = $question;
				continue;
			}
		
			$question["name"] = $line[1];
		
			$line = $line[2];
	
		}
	
		//Find the question block.
		$line = explode("{",$line,2);
	
		//If there is no question block, it's a comment.
		if (count($line) == 1) {
			$question["type"] = 'comment';
			$question["comment"] = $line[0];
			$result[] = $question;
			continue;
		}
	
		$before = trim($line[0]);
		$line = explode("}",$line[1]);
	
		//If there's an error
		if (count($line) > 2) {
			$question["type"] = "error";
			$question["reason"] = 'Improperly formed question block.';
			$question["raw"] = $kill;
			$result[] = $question;
			continue;
		}
	
		$choices = trim($line[0]);
		$after = trim($line[1]);
	
		$question["before"] = $before;//strtr($before,$unescape);
		$question["choices"] = $choices;
		$question["after"] = $after;///strtr($after,$unescape);
	
		$type = "unknown";
	
		//Determine question type
		//Essay/short answer
		if(trim($choices) == "") {
			$type = 'essay';
		}
	
		//Numerical
		elseif ($choices[0] == "#") {
			$type = "numerical";
			$choices = trim(substr($choices,1));
		
			//If there are multiple correct or partial answers
			if ($choices[0] == "=") {
				$choices = explode("=",substr($choices,1));
			}
			else $choices = array($choices);
		
			foreach ($choices as $c) {
				$c = trim($c);
			
				$credit = 100;
			
				//If there's partial credit
				if ($c[0] == "%") {
					$c = explode("%",substr($c,1),2);
					$credit = $c[0];
					$c = $c[1];
				}
			
				//Snap off any comments
				$comment = "";
				$c = explode("#",$c,2);
				if (count($c) > 1) {
					$comment = trim($c[1]);
					$comment = $comment;//strtr($comment, $unescape);
				}
				$c = $c[0];
			
				//Check for ranges
			
				//Plus/minus
				$plusminus = "";
				$c = explode(":",$c,2);
				if (count($c) > 1) {
					$plusminus = trim($c[1]);
				}
				$c = $c[0];
			
				//From/to
				$fromto = "";
				$c = explode("..",$c,2);
				if (count($c) > 1) {
					$fromto = trim($c[1]);
				}
				$c = $c[0];
			
				//Store it
				$question["answers"][] = array(
					'value' => $c,
					'credit' => $credit,
					'plusminus' => $plusminus,
					'fromto' => $fromto,
					'comment' => $comment,
				);
			
			}		
		
		}
	
		//True/false
		elseif ( 	in_array($choices,array("T","F","TRUE","FALSE"))
					|| substr($choices,0,2) == "T#" 
					|| substr($choices,0,2) == "F#" 
					|| substr($choices,0,5) == "TRUE#" 
					|| substr($choices,0,6) == "FALSE#" 
		) {
			$type = "truefalse";
		
			//Snap off any comments
			$comment = "";
			$choices = explode("#",$choices,2);
			if (count($choices) > 1) {
				$comment = trim($choices[1]);
				$comment = $comment;//strtr($comment, $unescape);
			}
			$choices = $choices[0];
		
			$question["answer"] = $choices[0];
			$question["comment"] = $comment;
		}
	
		//Different choices...
		elseif ($choices[0] == "~" || $choices[0] == "=") {
			$tildes = substr_count($choices,"~");
			$equals = substr_count($choices,"=");
			$darts = substr_count($choices,"->");
		
			//Multiple choice, one answer
			if ($equals == 1 && $tildes >= 1) {
				$type = "multichoice";
			}
			//Multiple choice, multi-select
			elseif ($equals == 0 && $tildes >= 1) {
				$type = "multianswer";
			}
			//Fill in the blank
			elseif ($equals >= 1 && $tildes == 0 && $darts == 0) {
				$type = "fillblank";
			}
			//Matching
			elseif ($equals >= 1 && $tildes == 0 && $darts==$equals) {
				$type = "matching";
			
				$choices = explode("=",substr($choices,1));
		
				foreach ($choices as $c) {
					$c = trim($c);
				
					//Break apart the darts
					$comment = "";
					$c = explode("->",$c,2);
				
					$c[0] = strtr($c[0]);//trim(strtr($c[0],$unescape));
					$c[1] = strtr($c[1]);//trim(strtr($c[1],$unescape));
			
					//Store it
					$question["answers"][] = array(
						'value' => $c[0],
						'match' => $c[1]
					);
			
				}		
			}
		
		
			//If it's not matching, chunk it
			if (in_array($type,array("multichoice","multianswer","fillblank"))) {
			
				$choices = preg_split( "/(~|=)/", $choices, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
			
				for ($i=0; $i 1) {
						$comment = trim($c[1]);
						$comment = $comment;//strtr($comment, $unescape);
					}
					$c = $c[0];
				
					//Store it
					$question["answers"][] = array(
						'value' => $c,
						'credit' => $credit,
						'comment' => $comment,
					);
				}
		
			}
		}
	
		$question["type"] = $type;
	
		//Unescape everything
		$question["before"] = $this->fixscape($question["before"]);
		$question["after"] = $this->fixscape($question["after"]);
	
		if (isset($question["answers"])) if (count($question["answers"]) > 0) {
		
			foreach ($question["answers"] as $a => $r) {
			
				$question["answers"][$a]['value'] = $this->fixscape($question["answers"][$a]['value']);
			
				if (isset($question["answers"][$a]['comment']))
		
					$question["answers"][$a]['comment'] = $this->fixscape($question["answers"][$a]['comment']);
		
			}
	
		}
	
		//Add it
		$result[] = $question;
	
	}

	return $result;

}


public function printQuiz() {
	
	$q = $this->questions;
	
	foreach ($q as $num => $r) {
	
		//echo "
";
		//print_r($r);
		//echo "
"; //Multiple choice if ($r["type"] == "multichoice" || $r["type"] == "multianswer") { echo '

'; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '

'; echo '

'; //Print out the answers foreach ($r["answers"] as $an => $answer) { echo '

'; if ($r["type"] == "multichoice") echo ''; else echo ''; echo ' '; echo $answer["value"]; echo '
'; } echo '

'; } //True/false elseif ($r["type"] == "truefalse") { echo '

'; echo ($num+1).') '; echo $r["before"]; echo '

'; echo '

'; echo '

'; echo ' True'; echo '
'; echo '
'; echo ' False'; echo '
'; echo '

'; } //Numerical or fill in the blank elseif ($r["type"] == "numerical" || $r["type"] == "fillblank") { echo '

'; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '

'; echo '

'; echo '

'; echo ''; echo '
'; echo '

'; } //Essay elseif ($r["type"] == "essay") { echo '

'; echo ($num+1).') '; echo $r["before"]; echo '

'; echo '

'; echo ''; echo '
'; echo '

'; } //Matching elseif ($r["type"] == "matching") { echo '

'; echo ($num+1).') '; echo $r["before"]; //Gather answers $answers = array(); foreach ($r["answers"] as $k => $a) { $answers[] = array("key" => $k, "match" => $a["match"]); } shuffle($answers); echo '

'; foreach ($r["answers"] as $k => $a) { echo '

'; echo $a["value"]; echo ' '; echo ''; echo '
'; } echo '

'; } echo "\n\n"; } } //Grade a test and diplay it, graded //"return" return graded test in html //"print" print results and return stats //"stats" return stats public function gradeQuiz($a, $action = "print") { $q = $this->questions; $correct = 0; $ungradable = 0; $e = ""; ob_start(); foreach ($q as $num => $r) { if (!isset($a[$num])) continue; //echo "
";
		//print_r($r);
		//echo "
"; $score = 0; //Multiple choice if ($r["type"] == "multichoice" || $r["type"] == "multianswer") { echo '

'; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '

'; echo '

'; //Print out the answers foreach ($r["answers"] as $an => $answer) { echo '

'; if ($r["type"] == "multichoice") { $style = 'color:#000;'; $comment = ""; //If it's correct if ($answer["credit"] == "100") { //If they chose it if ($an == $a[$num]) { $style = 'color:#0C0; font-weight:bold;'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000; font-weight:bold; text-decoration:underline;'; } } //It's incorrect or partial credit else { //If they chose it if ($an == $a[$num]) { $style = 'color:#C00; text-decoration:line-through'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000;'; } } echo ''; echo $answer["value"]; echo ''; if ($comment != "") echo ' ('.$comment.')'; } else {//multichoice $style = 'color:#000;'; $comment = ""; //If it's correct if ($answer["credit"] > 0) { //If they chose it if (in_array($an,$a[$num])) { $style = 'color:#0C0; font-weight:bold;'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000; font-weight:bold; text-decoration:underline;'; } } //It's incorrect or partial credit else { //If they chose it if (in_array($an,$a[$num])) { $style = 'color:#C00; text-decoration:line-through'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000;'; } } echo ''; echo $answer["value"]; echo ''; if ($comment != "") echo ' ('.$comment.')'; } echo '
'; } echo '

'; } //True/false elseif ($r["type"] == "truefalse") { echo '

'; echo ($num+1).') '; echo $r["before"]; echo '

'; echo '

'; echo '

'; $style = "color:#C00; text-decoration:line-through;"; //If correct if ($r["answer"] == $a[$num]) { $style = "color:#0C0; font-weight:bold;"; $score = 100; } //If wrong else { $score = 0; } echo ''.($r["answer"]=="T"?"True":"False").''; if ($comment != "") echo ' ('.$comment.')'; echo '
'; echo '

'; } //Fill in the blank elseif ($r["type"] == "fillblank") { echo '

'; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '

'; echo '

'; echo '

'; $good_answers = array(); $style = "color:#C00; text-decoration:line-through;"; //Check answers foreach ($r["answers"] as $an => $answer) { if (strtolower($a[$num]) == strtolower($answer["value"])) { $style = "color:#0C0; font-weight:bold;"; $score = 100; } else { $good_answers[] = ''.$answer["value"].''; } } echo ''.htmlspecialchars($a[$num]).''; if ($score != 100) { echo '
'.implode(", ",$good_answers); } echo '
'; echo '

'; } //Numerical elseif ($r["type"] == "numerical") { echo '

'; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '

'; echo '

'; echo '

'; $style = "color:#C00; text-decoration:line-through;"; //Iterate over each possibility foreach ($r["answers"] as $cor => $subanswer) { //Calculate answer $rightAnswer = floatVal($subanswer["value"]); $minAnswer = $rightAnswer; $maxAnswer = $rightAnswer; if ($subanswer["plusminus"] != "") { $minAnswer -= floatVal($subanswer["plusminus"]); $maxAnswer += floatVal($subanswer["plusminus"]); } elseif ($subanswer["fromto"] != "") { $maxAnswer += floatVal($subanswer["fromto"]); } //If it's correct if ( ($a[$num] >= $minAnswer) & ($a[$num] <= $maxAnswer) ) { $score = $subanswer["credit"]; $comment = $comment = $r["answers"][$cor]["comment"]; break; } } if ($score == 100) { $style = "color:#0C0; font-weight:bold;"; } echo ''.$a[$num].''; if ($comment != "") echo ' ('.$comment.')'; if ($score == 0) { echo '
'; echo ''; echo $r["answers"][0]["value"]; if ($r["answers"][0]["plusminus"]) echo ' +/- '.$r["answers"][0]["plusminus"]; if ($r["answers"][0]["fromto"]) echo ' to '.$r["answers"][0]["fromto"]; echo ''; } echo '
'; echo '

'; } //Essay elseif ($r["type"] == "essay") { $ungradable++; echo '

'; echo ($num+1).') '; echo $r["before"]. " (Essays are graded separately.)"; echo '

'; echo '

'; if (strlen(trim($a[$num])) > 0) echo htmlspecialchars($a[$num]); else echo '[No entry.]'; echo '
'; echo '

'; } //Matching elseif ($r["type"] == "matching") { echo '

'; echo ($num+1).') '; echo $r["before"]; $numRight = 0; echo '

'; foreach ($r["answers"] as $k => $ma) { echo '

'; echo $ma["value"]; echo ' '; echo '-->'; //Correct! if ($a[$num][$k] == $k) { echo ' '; echo $ma["match"]; echo ''; $numRight++; } else { echo ' '; echo $r["answers"][$a[$num][$k]]["match"]; echo ''; echo ' '; echo $ma["match"]; echo ''; } echo '
'; } echo '

'; $score = $numRight/count($r["answers"]) * 100; } echo "\n\n"; $correct += $score/100; } $results = array( "score" => $correct, "outof" => count($q), "pending" => $ungradable, "percentage" => count($q) == 0 ? 0 : $correct/count($q), "final" => ($ungradable > 0)?true:false ); //"return" return graded test in html //"print" print results and return stats //"stats" return stats if ($action == "return") { return ob_get_clean(); } elseif ($action == "stats") { ob_end_clean(); return $results; } ob_end_flush(); return $results; } }//end class /* $GIFT = new GIFT($input); if (isset($_REQUEST["GIFTquestion"])) { $answers = serialize($_REQUEST["GIFTquestion"]); $answers = unserialize($answers); print_r($GIFT->grade($answers)); echo "

Responses:". serialize($_REQUEST["GIFTquestion"]).'

'; } else { echo '
'; $GIFT->print(); echo ''; echo '
'; }*/ ?>