File:  [LON-CAPA] / loncom / homework / matchresponse.pm
Revision 1.92: download - view: text, annotated - select for diffs
Mon Jan 19 15:35:53 2015 UTC (9 years, 3 months ago) by goltermann
Branches: MAIN
CVS tags: version_2_11_X, version_2_11_2_uiuc, version_2_11_2_educog, version_2_11_2, HEAD
authoring space overhaul
this update tries to improve the user experience of the authoring space.

added codemirror for xml editor and script tags in colorful editor
added possibility to deactivate codemirror in author settings
added dropdown menu to insert problem templates into xml editor (thanks to tobias reinhardt)
added feature of saving current scrollposition on save when editing problems
added possibility to fold blocks in colorful editor, this state will be saved and restored
added shortcuts to create empty problems, html files and directories

and other smaller features and bugfixes

# The LearningOnline Network with CAPA
# Full matching style response
#
# $Id: matchresponse.pm,v 1.92 2015/01/19 15:35:53 goltermann Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA 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.
#
# LON-CAPA 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 for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::matchresponse;
use strict;
use HTML::Entities();
use Math::Random();
use Apache::optionresponse();
use Apache::lonlocal;
use Apache::lonnet;
use Apache::lonxml;
use POSIX qw(ceil);

BEGIN {
    &Apache::lonxml::register('Apache::matchresponse',('matchresponse'));
}

sub start_matchresponse {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;
    #when in a matchresponse use these
    &Apache::lonxml::register('Apache::matchresponse',
			      ('foilgroup','foil','conceptgroup','item',
			       'itemgroup'));
    push (@Apache::lonxml::namespace,'matchresponse');
    my $id = &Apache::response::start_response($parstack,$safeeval);
    %Apache::hint::match=();
    undef(%Apache::response::foilnames);
    if ($target eq 'meta') {
	$result=&Apache::response::meta_package_write('matchresponse');
    } elsif ($target eq 'edit' ) {
	$result.=&Apache::edit::start_table($token).
	    '<tr><td>'.&Apache::loncommon::insert_folding_button().&Apache::lonxml::description($token).'</td>'
           .'<td><span class="LC_nobreak">'.&mt('Delete?').' '
	   .&Apache::edit::deletelist($target,$token)
           .'</span></td>'
           .'<td>&nbsp;'.&Apache::edit::end_row()
	   .&Apache::edit::start_spanning_row();
	
	$result.=
	    &Apache::edit::text_arg('Max Number Of Shown Foils:','max',$token,'4').
	    &Apache::edit::select_arg('Randomize Foil Order:','randomize',
				      ['yes','no'],$token).
	    &Apache::edit::end_row().&Apache::edit::start_spanning_row()."\n";
    } elsif ($target eq 'modified') {
	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
						     $safeeval,'max',
						     'randomize');
	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
    } elsif ($target eq 'analyze') {
	my $part_id="$Apache::inputtags::part.$id";
        $Apache::lonhomework::analyze{"$part_id.type"} = 'matchresponse';
	push (@{ $Apache::lonhomework::analyze{"parts"} },$part_id);
	push (@{ $Apache::lonhomework::analyze{"$part_id.bubble_lines"} },
	      1);
    }
    return $result;
}

sub end_matchresponse {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;
    if ($target eq 'edit') { $result=&Apache::edit::end_table(); }
    &Apache::response::end_response;
    pop @Apache::lonxml::namespace;
    &Apache::lonxml::deregister('Apache::matchresponse',
				('foilgroup','foil','conceptgroup'));
    undef(%Apache::response::foilnames);
    return $result;
}

%Apache::response::itemgroup=();
sub start_itemgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;
    %Apache::response::itemgroup=();
    %Apache::matchresponse::itemtable=();

    if ($target eq 'edit') {
	$result=&Apache::edit::tag_start($target,$token);
	$result.=&Apache::edit::select_arg('Randomize Order:','randomize',
					   ['yes','no'],$token);
	$result.=&Apache::edit::select_arg('Items Display Location:',
					   'location',
					   ['top','bottom','left','right'],
					   $token);
	$result.=&Apache::edit::select_arg('Items Display Direction:',
					   'direction',
					   ['vertical','horizontal'],
					   $token);
        $result.=&Apache::edit::select_arg('Items Columns:',
                                           'columns',
                                           [['','default'],'1','2','3','4'],
                                            $token);
	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
    } elsif ($target eq 'modified') {
	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
						     $safeeval,'randomize',
						     'location','direction',
                                                     'columns');
	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
    } elsif ($target eq 'web' or $target eq 'tex') {
	$Apache::matchresponse::itemtable{'location'}=
	    &Apache::lonxml::get_param('location',$parstack,$safeeval);
	$Apache::matchresponse::TeXitemgroupwidth=&Apache::lonxml::get_param('TeXitemgroupwidth',$parstack,$safeeval,undef,0);
    }
    return $result;
}

sub end_itemgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;

    if ($target eq 'edit') { $result=&Apache::edit::end_table(); }
    if (ref($Apache::response::itemgroup{'names'}) eq 'ARRAY') {
        if(!@{ $Apache::response::itemgroup{'names'} }) { return; }
    } else {
        return;
    }
    my @names=@{ $Apache::response::itemgroup{'names'} };
    my $randomize =&Apache::lonxml::get_param('randomize',$parstack,$safeeval);
    if ($randomize ne 'no' ) {
	@names=&Apache::response::whichorder($#names+1,$randomize,0,
					 \%Apache::response::itemgroup);
    }
    if ($target eq 'analyze') {
	my $partid="$Apache::inputtags::part.$Apache::inputtags::response[-1]";
	push (@{ $Apache::lonhomework::analyze{"$partid.items"} }, @names);
    }
    my %letter_name_map;
    my %name_letter_map;
    my @alphabet=('A'..'Z');
    my $i=0;
    foreach my $name (@names) {
	$letter_name_map{$alphabet[$i]}=$name;
	$name_letter_map{$name}=$alphabet[$i];
	$i++;
    }
    $Apache::response::itemgroup{'letter_name_map'}=\%letter_name_map;
    $Apache::response::itemgroup{'name_letter_map'}=\%name_letter_map;
    my $direction=&Apache::lonxml::get_param('direction',$parstack,$safeeval);
    my $columns=&Apache::lonxml::get_param('columns',$parstack,$safeeval);
    
    unless ($columns =~ /^\d+$/) {
        undef($columns);
    }
    if ($target eq 'web') {
	
	my $table='<br /><table>'; # extra space to match what latex does.
        if ((!$columns) || ($columns < 0)) {
            if ($direction eq 'horizontal') {
                if (@names > 0) { 
                    $columns = scalar(@names);
                } else {
                    $columns = 1;
                }
            } else {
                $columns = 1;
            }
        }
        my $rows=ceil(scalar(@names)/$columns);
        my $endloop = $columns*$rows;
        for (my $i=0; $i<$endloop; $i++) {
            my $label = '&nbsp;';
            my $item = '&nbsp;';
            my $index;
            if ($direction eq 'horizontal') {
                $index = $i;
            } else {
                $index = ($i % $columns)*$rows+int($i/$columns);
            }
            if ($index < scalar(@names)) {
                $label = $alphabet[$index];
                $item = $Apache::response::itemgroup{$names[$index].'.text'};
            }
            if ($i % $columns == 0) {
                $table.='<tr>';
            }
            $table.= '<td>'.$label.'</td><td>'.$item.'</td>';
            if ($columns > 1) {
                $table .= '<td>&nbsp;</td>';
            }
            if ( ! (($i+1) % $columns) ) {
                $table.='</tr>';
            }
        }
	$table.='</table>';
	$Apache::matchresponse::itemtable{'display'}=$table;
	$Apache::lonxml::post_evaluate=0;
    } elsif ($target eq 'tex') {
	my $table=' \begin{description}\setlength{\leftmargin}{2em}\setlength{\labelwidth}{1em}\setlength{\itemsep}{0.5pt plus1pt minus2pt}\setlength{\listparindent}{0em} ';
	my $i=0;
        my ($numrows,$bubbles_per_row);
        if ($Apache::lonhomework::type eq 'exam') {
            ($numrows,$bubbles_per_row) = 
                &Apache::optionresponse::getnumrows(scalar(@names)); 
        } else {
            $numrows = 1;
        }
	foreach my $name (@names) {
	    # $Apache::response::itemgroup{$name.'.text'}=~s/\$\$/\$/g;
            my $item;
            if (($numrows > 1) && ($bubbles_per_row > 0)) {
                my $num = 1+int($i/$bubbles_per_row);
                my $idx = int($i % $bubbles_per_row);
                if ($idx == 0) {
                    if ($num == 1) {
                        $table .= '\item[\footnotesize {'.&mt('(first line)').'}]';
                    } else {
                        $table .= '\item[\footnotesize {'.&mt('(next line)').'}]';
                    }
                }
                $item = $alphabet[$idx];
            } else {
                $item = $alphabet[$i];
            }
	    $table.='\item['.$item.'] '.
		$Apache::response::itemgroup{$name.'.text'};
	    $i++;
	}
	$table.=' \end{description}  \strut ';
	if ($Apache::lonhomework::type eq 'exam')  {$table.='\vskip -13 mm \strut ';}
	$Apache::matchresponse::itemtable{'display'}=$table;
	$Apache::lonxml::post_evaluate=0;
    }
    return $result;
}

sub start_item {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result='';
    if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
	&Apache::lonxml::startredirection;
    } elsif ($target eq 'edit') {
	my $randomize=&Apache::lonxml::get_param('randomize',$parstack,
						 $safeeval,'-2');
	$result=&Apache::edit::tag_start($target,$token,"Item");
	$result.=&Apache::edit::text_arg('Name:','name',$token);
	if ($randomize ne 'no') {
	    $result.=
                     &Apache::edit::select_arg('Location:','location',
					       ['random','top','bottom'],
					       $token);
	}
	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
    } elsif ($target eq 'modified') {
	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
						     $safeeval,'name',
						     'location');
	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
    }
    return $result;
}

sub end_item {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $text ='';
    my $result = '';
    if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
	$text=&Apache::lonxml::endredirection;
    }
    if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ||
	$target eq 'edit' || $target eq 'tex' || $target eq 'analyze') {
	my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
	my $location=&Apache::lonxml::get_param('location',$parstack,
						$safeeval);
	&Apache::lonxml::debug("Got a name of :$name:");
	if ($name eq "") { $name=$Apache::lonxml::curdepth; }
	&Apache::lonxml::debug("Using a name of :$name:");
	push @{ $Apache::response::itemgroup{'names'} }, $name;
	$Apache::response::itemgroup{"$name.text"} = $text;
	$Apache::response::itemgroup{"$name.location"} = $location;
    }
    if ($target eq 'edit') {
	$result.= &Apache::edit::tag_end($target,$token,'');
    }
    return $result;
}

sub insert_item {
    return '
<item name="">
<startouttext />
<endouttext />
</item>';
}

%Apache::response::foilgroup=();
sub start_foilgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;
    %Apache::response::foilgroup=();
    $Apache::matchresponse::conceptgroup=0;
    &Apache::response::pushrandomnumber(undef,$target);
    if ($target eq 'edit') {
	$result.=&Apache::edit::start_table($token)
	    .'<tr><td>'.&Apache::loncommon::insert_folding_button()
            .&mt('Collection Of Foils').'</td>'
            .'<td><span class="LC_nobreak">'.&mt('Delete?')
	    .&Apache::edit::deletelist($target,$token)
	    .'</span></td>'
            .'<td>&nbsp;'.&Apache::edit::end_row()
            .&Apache::edit::start_spanning_row()."\n";
    }
    return $result;
}

sub end_foilgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result;
    my $part        = $Apache::inputtags::part;
    my $response_id = $Apache::inputtags::response[-1];

    if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || $target eq 'tex' || $target eq 'analyze') {
	my $max = &Apache::lonxml::get_param('max',$parstack,$safeeval,'-2');
	my $randomize = &Apache::lonxml::get_param('randomize',$parstack,
						   $safeeval,'-2');
	if ($target eq 'web' || $target eq 'tex') {
	    $result=&displayfoils($target,$max,$randomize);
	    $Apache::lonxml::post_evaluate=0;
	} elsif ($target eq 'answer' ) {
	    $result=&displayanswers($max,$randomize);
	} elsif ( $target eq 'grade') {
	    &grade_response($max,$randomize);
	} elsif ( $target eq 'analyze') {
	    my @shown=&whichfoils($max,$randomize);
	    &Apache::response::analyze_store_foilgroup(\@shown,
						 ['text','value','location']);
	    #FIXME need to store options in some way
	}
        my ($numrows,$bubbles_per_row);
        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
            my $numitems;
            if (ref($Apache::response::itemgroup{'names'}) eq 'ARRAY') {
                $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
                ($numrows,$bubbles_per_row) =
                    &Apache::optionresponse::getnumrows($numitems);
            }
        }
        if ($numrows < 1) {
            $numrows = 1;
        }
        my $increment = &getfoilcounts($max) * $numrows;
	&Apache::lonxml::increment_counter($increment,"$part.$response_id");
	if ($target eq 'analyze') {
	    &Apache::lonhomework::set_bubble_lines();
	}

    } elsif ($target eq 'edit') {
	$result=&Apache::edit::end_table();
    }
    &Apache::response::poprandomnumber();
    return $result;
}

sub whichfoils {
    my ($max,$randomize)=@_;
    return &Apache::response::whichorder(&getfoilcounts($max),
					 $randomize,
					 &Apache::response::showallfoils(),
					 \%Apache::response::foilgroup);
}

sub displayanswers {
    my ($max,$randomize,@opt)=@_;
    if (ref($Apache::response::foilgroup{'names'}) eq 'ARRAY') {
        if (!@{ $Apache::response::foilgroup{'names'} }) { return; }
    } else {
        return;
    }
    my @names = @{ $Apache::response::foilgroup{'names'} };
    my @whichfoils = &Apache::response::whichorder(&getfoilcounts($max),
						   $randomize,
					  &Apache::response::showallfoils(),
					 \%Apache::response::foilgroup);
    my %name_letter_map;
    if (ref($Apache::response::itemgroup{'name_letter_map'}) eq 'HASH') {
        if (%{$Apache::response::itemgroup{'name_letter_map'}}) {
	    %name_letter_map=
	        %{ $Apache::response::itemgroup{'name_letter_map'} };
        }
    }
    my $result;
    if ($Apache::lonhomework::type eq 'exam') {
	my $i=0;
	foreach my $name (@whichfoils) {
	    $result.=&Apache::response::answer_header('matchresponse',$i++);
	    my $value_name=$Apache::response::foilgroup{$name.'.value'};
	    my $letter=$name_letter_map{$value_name};
	    $result.=&Apache::response::answer_part('matchresponse',$letter);
	    $result.=&Apache::response::answer_footer('matchresponse');
	}
    } else {
	$result=&Apache::response::answer_header('matchresponse');
	foreach my $name (@whichfoils) {
	    my $value_name=$Apache::response::foilgroup{$name.'.value'};
	    my $letter=$name_letter_map{$value_name};
	    $result.=&Apache::response::answer_part('matchresponse',$letter);
	}
	$result.=&Apache::response::answer_footer('matchresponse');
    }
    return $result;
}


sub grade_response {
    my ($max,$randomize)=@_;
    my (@whichfoils)=&Apache::response::whichorder(&getfoilcounts($max),
						   $randomize,
				            &Apache::response::showallfoils(),
				           \%Apache::response::foilgroup);
    if (!&Apache::response::submitted()) { return; }
    my %responsehash;
    my %grade;
    my %answerhash;
    my ($temp,$right,$wrong,$ignored)=(1,0,0,0);
    my %letter_name_map;
    if (ref($Apache::response::itemgroup{'letter_name_map'}) eq 'HASH') {
        if (%{$Apache::response::itemgroup{'letter_name_map'}}) {
	    %letter_name_map=
	        %{ $Apache::response::itemgroup{'letter_name_map'} };
        }
    }
    my @items;
    my $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
    my ($numrows,$bubbles_per_row);
    if ($Apache::lonhomework::scantronmode) {
        my $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
        ($numrows,$bubbles_per_row) =
            &Apache::optionresponse::getnumrows($numitems);
    }
    if ($numrows < 1) {
        $numrows = 1;
    }
    my @alphabet=('A'..'Z');
    my %nums_from_letters;
    for (my $i=0; $i<@alphabet; $i++) {
        $nums_from_letters{$alphabet[$i]} = $i;
    }
    foreach my $name (@whichfoils) {
        my $response;
        if ($numrows > 1) {
            my $num = $temp;
            my $totalnum;
            for (my $i=0; $i<$numrows; $i++) {
                my $item = &Apache::response::getresponse($num,'letter');
                if ($item =~ /^\w$/) {
                    $totalnum = $i*$bubbles_per_row + $nums_from_letters{$item};
                }
                $num ++;
            }
            if ($totalnum =~ /^\d+$/) {
                $response = $alphabet[$totalnum];
            }
            $temp += $numrows;
        } else {
	    $response = &Apache::response::getresponse($temp,'letter');
            $temp ++;
        }
	push(@items,$response);
	my $responsename = $letter_name_map{$response};
	my $value=$Apache::response::foilgroup{$name.'.value'};
        if ($Apache::lonhomework::type eq 'randomizetry') {
            $answerhash{$name} = $value;
        }
	if ( $response =~ /[^\s]/) {
	    $responsehash{$name}=$responsename;
	    &Apache::lonxml::debug("submitted a $response($responsename) for $value<br />\n");
	    if ($value eq $responsename) {
		$grade{$name}='1'; $right++;
	    } else {
		$grade{$name}='0'; $wrong++;
	    }
	} else {
	    $ignored++;
	}
    }
    my $part=$Apache::inputtags::part;
    my $nonlenient=&Apache::optionresponse::grading_is_nonlenient($part);
    my $id = $Apache::inputtags::response['-1'];
    my $responsestr=&Apache::lonnet::hash2str(%responsehash);
    my $itemstr    =&Apache::lonnet::array2str(@items);
    my $gradestr   =&Apache::lonnet::hash2str(%grade);
    my %previous=&Apache::response::check_for_previous($responsestr,$part,$id);
    &Apache::lonxml::debug("Got $right right and $wrong wrong, and $ignored were ignored ");
    $Apache::lonhomework::results{"resource.$part.$id.submission"}=
	$responsestr;
    $Apache::lonhomework::results{"resource.$part.$id.submissionitems"}=
	$itemstr;
    $Apache::lonhomework::results{"resource.$part.$id.submissiongrading"}=
	$gradestr;
    if ($Apache::lonhomework::type eq 'randomizetry') {
        $Apache::lonhomework::results{"resource.$part.$id.foilorder"} = &Apache::lonnet::array2str(@whichfoils);
    }
    if (($Apache::lonhomework::type eq 'survey') ||
        ($Apache::lonhomework::type eq 'surveycred') ||
        ($Apache::lonhomework::type eq 'anonsurvey') ||
        ($Apache::lonhomework::type eq 'anonsurveycred')) {
	if ($ignored == 0) {
	    my $ad;
            if ($Apache::lonhomework::type eq 'anonsurvey') {
                $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='ANONYMOUS';
            } elsif ($Apache::lonhomework::type eq 'anonsurveycred') {
                $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='ANONYMOUS_CREDIT';
            } elsif ($Apache::lonhomework::type eq 'surveycred') {
                $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='SUBMITTED_CREDIT';
            } else {
                $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='SUBMITTED';
            }
	    &Apache::response::handle_previous(\%previous,$ad);
	} elsif ($wrong==0 && $right==0) {
	} else {
	    my $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='MISSING_ANSWER';
	    &Apache::response::handle_previous(\%previous,$ad);
	}
    } elsif ($nonlenient) {
        my $ad;
        if ($wrong==0 && $ignored==0) {
            $ad='EXACT_ANS';
        } elsif ($wrong==0 && $right==0) {
            #nothing submitted
        } else {
            if ($ignored==0) {
                $ad='INCORRECT';
            } else {
                $ad='MISSING_ANSWER';
            }
        }
        $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad;
        &Apache::response::handle_previous(\%previous,$ad);
    } else {
        my $ad;
        if ($wrong==0 && $right==0) {
            #nothing submitted only assign a score if we
            #need to override a previous grade
            if (defined($Apache::lonhomework::history{"resource.$part.$id.awarddetail"})) {
                $ad='ASSIGNED_SCORE';
            }
        } else {
            $ad='ASSIGNED_SCORE';
        }
        $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad;
        $Apache::lonhomework::results{"resource.$part.$id.awarded"}=
            $right/(scalar(@whichfoils));
        $Apache::lonhomework::results{"resource.$part.$id.numfoils"}=
            scalar(@whichfoils);
    }
}

sub format_prior_answer {
    my ($mode,$answer,$other_data) = @_;
    my %answer      =&Apache::lonnet::str2hash($answer);
    my $foil_order  =$other_data->[0];
    my %grading     =&Apache::lonnet::str2hash($other_data->[1]);
    my @items       =&Apache::lonnet::str2array($other_data->[2]);
    my $output;
    
    foreach my $name (@{ $foil_order }) {
	my $item = &HTML::Entities::encode(shift(@items),'<>&"');
        if ($item eq '') {
            $item = '&nbsp;';
        }
	$output .= '<tr><td>'.$item.'</td></tr>';
    }
    return if (!defined($output));

    $output = '<table class="LC_prior_match">'.$output.'</table>';
    return $output;
}

sub itemdisplay {
    my ($location)=@_;
    if ($location eq 'top' &&
	!defined($Apache::matchresponse::itemtable{'location'})) {
	return $Apache::matchresponse::itemtable{'display'};
    }
    if ($Apache::matchresponse::itemtable{'location'} eq $location) {
	return $Apache::matchresponse::itemtable{'display'};
    }
    return undef;
}

sub displayfoils {
    my ($target,$max,$randomize)=@_;
    my ($tabsize, $lefttabsize, $righttabsize);

    my $result;
    my $question;
    my (@whichfoils)=&Apache::response::whichorder(&getfoilcounts($max),
						   $randomize,
				            &Apache::response::showallfoils(),
				           \%Apache::response::foilgroup);
    my $part=$Apache::inputtags::part;
    my $solved=$Apache::lonhomework::history{"resource.$part.solved"};
    my %letter_name_map;
    if (ref($Apache::response::itemgroup{'letter_name_map'}) eq 'HASH') {
        if (%{$Apache::response::itemgroup{'letter_name_map'}}) {
	    %letter_name_map=
	        %{ $Apache::response::itemgroup{'letter_name_map'} };
        }
    }
    my %name_letter_map;
    if (ref($Apache::response::itemgroup{'name_letter_map'}) eq 'HASH') {
        if (%{$Apache::response::itemgroup{'name_letter_map'}}) {
	    %name_letter_map=
	        %{ $Apache::response::itemgroup{'name_letter_map'} };
        }
    }
    if ( &Apache::response::show_answer() && ($target ne 'tex')) {
	foreach my $name (@whichfoils) {
	    my $text=$Apache::response::foilgroup{$name.'.text'};
	    my $value=$Apache::response::foilgroup{$name.'.value'};
	    my $letter=$name_letter_map{$value};
	    if ($target eq 'tex') {
		$question.=' \\\\ '.$letter.':'.$text;
	    } else {
		$question.='<br /><b>'.$letter.':</b> '.$text;
	    }
	}
    } else {
	my $i = 0;
	my $temp=1;
	my $id=$Apache::inputtags::response[-1];
	my $part=$Apache::inputtags::part;
        my ($lastresponse,$newvariation);
        if ((($Apache::lonhomework::history{"resource.$part.type"} eq 'randomizetry') ||
            ($Apache::lonhomework::type eq 'randomizetry')) &&
            ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
            if ($env{'form.'.$part.'.rndseed'} ne
                $Apache::lonhomework::history{"resource.$part.rndseed"}) {
                $newvariation = 1;
            }
        }
        unless ($newvariation) {
            if ((($env{'form.grade_username'} eq '') && ($env{'form.grade_domain'} eq '')) ||
                (($env{'form.grade_username'} eq $env{'user.name'}) &&
                 ($env{'form.grade_domain'} eq $env{'user.domain'}))) {
                $lastresponse=$Apache::lonhomework::history{"resource.$part.$id.submission"};
            } else {
                unless (($Apache::lonhomework::history{"resource.$part.type"} eq 'anonsurvey') ||
                        ($Apache::lonhomework::history{"resource.$part.type"} eq 'anonsurveycred')) {
                    $lastresponse=$Apache::lonhomework::history{"resource.$part.$id.submission"};
                }
            }
        }

	my %lastresponse=&Apache::lonnet::str2hash($lastresponse);    
	my @alphabet=('A'..'Z');
	my @used_letters=sort(keys(%letter_name_map));
	my $internal_counter=$Apache::lonxml::counter;
        my ($numrows,$bubbles_per_row);
        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
            ($numrows,$bubbles_per_row) = 
                &Apache::optionresponse::getnumrows(scalar(@used_letters));
        } else {
            $numrows = 1;
        }
	foreach my $name (@whichfoils) {
	    my $lastopt=$lastresponse{$name};
	    my $last_letter=$name_letter_map{$lastopt};
	    my $optionlist = '';
	    if ($target ne 'tex') {
		$optionlist="<option></option>\n";
	    } else {
		if ($Apache::lonhomework::type ne 'exam') {
                    if($env{'form.pdfFormFields'} eq 'yes'
                       && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER') {
                        my $fieldname = $env{'request.symb'} . '&part_'. $Apache::inputtags::part .'&matchresponse'. '&HWVAL_' . $Apache::inputtags::response['-1'] . ':' . $temp . '&submit_' . $Apache::inputtags::part . '&';
                        $optionlist = &Apache::lonxml::print_pdf_start_combobox($fieldname);
                    } else {

                        $optionlist='\framebox[10 mm][s]{\tiny\strut}';
                    }
                }
	    }
	    my $option;
	    foreach $option (@used_letters) {
		if ($option eq $last_letter) {
		    if ($target ne 'tex') {
                        $optionlist.="<option selected=\"selected\">$option</option>\n";
                    } elsif ($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes'
                             && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER'
                             && $Apache::lonhomework::type ne 'exam') {
                        $optionlist .= &Apache::lonxml::print_pdf_add_combobox_option($option);
                    }
		} else {
		    if ($target ne 'tex') {
                        $optionlist.="<option>$option</option>\n";
                    } elsif ($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes'
                             && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER'
                             && $Apache::lonhomework::type ne 'exam') {
                        $optionlist .= &Apache::lonxml::print_pdf_add_combobox_option($option);
                    }
		}
	    }
	    if ($target ne 'tex' && $Apache::lonhomework::type ne 'exam') {
	        $optionlist='<select onchange="javascript:setSubmittedPart(\''.
		  $part.'\');" name="HWVAL_'.
		    $Apache::inputtags::response[-1].':'.$temp.'">'.
		        $optionlist."</select>\n";
	    }
	    my $text=$Apache::response::foilgroup{$name.'.text'};
	    #
            #  Factor out the tex computations of the left/right 1/2 minipage
	    #  widths for left or right positioning.  This allows us
	    #  to, if necessary constrain the bubble widths:
	    #
	    if ($target eq 'tex'   &&
		(&itemdisplay('left') || &itemdisplay('right'))) {
		$tabsize=&Apache::londefdef::recalc($env{'form.textwidth'});
		($lefttabsize,$righttabsize)=(0,0);
		if ($Apache::matchresponse::TeXitemgroupwidth ne '') {
		    $Apache::matchresponse::TeXitemgroupwidth=~/(\d*.?\d*)/;
		    $lefttabsize=$tabsize*$1/100;	
		    $righttabsize=0.95*($tabsize-$lefttabsize);
		} else {
		    $tabsize=~/(\d+\.?\d*)/;
		    $lefttabsize=$1/2.1;
		    $righttabsize=0.95*($1-$lefttabsize);
		}
	    }
	    if ($target ne 'tex') {
		if ($Apache::lonhomework::type ne 'exam') {
		    $question.="<br />\n".$optionlist.$text;
		} else {
		    $question.="<br />\n".$text;
		}
		if ($Apache::lonhomework::type eq 'exam') {
		    my @blank;
		    $question.=&Apache::optionresponse::webbubbles(\@used_letters,\@blank,$temp,$last_letter);
		}
	    } else {		
		# Note that if left or right positioned, we must
		# confine the bubbles to righttabsize:
		#
		if ($Apache::lonhomework::type eq 'exam') {
		    $question.=' '.$optionlist.$text."\n";
		    my @emptyItems = ();
		    for (my $i=0;$i<=$#used_letters;$i++) {push @emptyItems, ' ';}
		    $question.='\vskip -1 mm\noindent\begin{list}{}{\setlength{\listparindent}{0mm}\setlength{\leftmargin}{2mm}}'
			.'\item \hskip -3mm ';
                    if ($numrows == 1) {
                        $question .= '\textbf{'.$internal_counter.'}';
                    } else {
                        my $linetext;
                        for (my $i=0; $i<$numrows; $i++) {
                            $linetext .= $internal_counter+$i.', ';
                        }
                        $linetext =~ s/,\s$//;
                        $question .= '\small {\textbf{'.$linetext.'}} '.
                                     '\hskip 2 mm {\footnotesize '.
                                     &mt('(Bubble once in [_1] lines)',$numrows).
                                     '} \vskip 3 mm';
                    }
                    my $max_width;
		    if (&itemdisplay('left') || &itemdisplay('right')) {
                        $max_width = $righttabsize;
                    }
		    $question .= '\vskip -4 mm' . &Apache::optionresponse::bubbles(\@used_letters,\@emptyItems,'',$max_width,$numrows,$bubbles_per_row,$internal_counter);
		    $question .= '\end{list} \vskip -8 mm \strut ';
		    $internal_counter += $numrows;
	        } else {
                    if($env{'form.pdfFormFields'} eq 'yes' 
                            && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER') {
                        $question .= " $optionlist ". &Apache::lonxml::print_pdf_end_combobox($text).'\strut';
                    } else {
                        $question.=' '.$optionlist.$text.'\strut\\\\\strut '."\n";
                    }
		}
            } 
	    $temp++;
	}
    }
    if ($result=&itemdisplay('top')) {
	$result.=$question;
    } elsif ($result=&itemdisplay('bottom')) {
	$result=$question.$result;
    } elsif ($result=&itemdisplay('right')) {
	if ($target ne 'tex') {
	    #remove the first <br />
	    $question=~s|<br />||;
	    $result='<table><tr><td valign="top">'.$question.
		'</td><td valign="top">'.$result.'</td></tr></table>';
	} else {
	    $lefttabsize.=' mm ';
	    $righttabsize.=' mm ';
	    $result='\setlength{\tabcolsep}{1 mm}\begin{tabular}{p{'.$righttabsize.'}p{'.$lefttabsize.'}}\begin{minipage}{'.$righttabsize.'}'.$question.'\end{minipage}&\begin{minipage}{'.$lefttabsize.'}'.$result.'\end{minipage}\end{tabular}';
	}
    } elsif ($result=&itemdisplay('left')) {
	if ($target ne 'tex') {
	    #remove the first <br />
	    $question=~s|<br />||;
	    $result='<table><tr><td valign="top">'.$result.
		'</td><td valign="top">'.$question.'</td></tr></table>';
	} else {
	    $lefttabsize.=' mm ';
	    $righttabsize.=' mm ';
	    $result='\setlength{\tabcolsep}{1 mm}\begin{tabular}{p{'.$lefttabsize.'}p{'.$righttabsize.'}}\begin{minipage}{'.$lefttabsize.'}'.$result.'\end{minipage}&\begin{minipage}{'.$righttabsize.'}'.$question.'\end{minipage}\end{tabular}';
	}
    }
    if ($target eq 'web') {
        my $questiontype;
        if ($Apache::lonhomework::type eq 'randomizetry') {
            $questiontype = $Apache::lonhomework::type;
        }
	&Apache::response::setup_prior_tries_hash(\&format_prior_answer,
						  [\@whichfoils,
						   'submissiongrading',
						   'submissionitems'],
                                                  $questiontype);
    }
    if ($target ne 'tex') {$result.="<br />";} else {$result.=' \\\\ ';}
    return $result;
}

sub getfoilcounts {
    my ($max)=@_;
    # +1 since instructors will count from 1
    my $count = $#{ $Apache::response::foilgroup{'names'} }+1;
    if (&Apache::response::showallfoils()) { $max=$count; }
    if ($count>$max) { $count=$max } 
    &Apache::lonxml::debug("Count is $count from $max");
    return $count;
}


sub start_conceptgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    $Apache::matchresponse::conceptgroup=1;
    %Apache::response::conceptgroup=();
    my $result;
    if ($target eq 'edit') {
	$result.=&Apache::edit::tag_start($target,$token,
					  "Concept Grouped Foils");
	$result.=&Apache::edit::text_arg('Concept:','concept',$token,'50').
	    &Apache::edit::end_row().&Apache::edit::start_spanning_row();
    }
    if ($target eq 'modified') {
	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
						     $safeeval,'concept');
	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
    }
    return $result;
}

sub end_conceptgroup {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    $Apache::matchresponse::conceptgroup=0;
    my $result='';
    if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ||
	$target eq 'tex' || $target eq 'analyze') {
	&Apache::response::pick_foil_for_concept($target,
						 ['value','text','location'],
						 \%Apache::hint::match,
						 $parstack,$safeeval);
    } elsif ($target eq 'edit') {
	$result=&Apache::edit::end_table();
    }
    return $result;
}

sub insert_conceptgroup {
    my $result="\n\t\t<conceptgroup concept=\"\">".&insert_foil()."\n\t\t</conceptgroup>\n";
    return $result;
}

sub start_foil {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $result='';
    if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
	&Apache::lonxml::startredirection;
	if ($target eq 'analyze') {
	    &Apache::response::check_if_computed($token,$parstack,$safeeval,'value');
	}
    } elsif ($target eq 'edit') {
	$result=&Apache::edit::tag_start($target,$token,"Foil");
	my $level='-2';
	if ($$tagstack[-2] eq 'conceptgroup') { $level = '-3'; }
	$result.=&Apache::edit::text_arg('Name:','name',$token);
	my @names;
	if (ref($Apache::response::itemgroup{'names'}) eq 'ARRAY') {
            if (@{ $Apache::response::itemgroup{'names'} }) {
	        @names=@{ $Apache::response::itemgroup{'names'} };
            }
	}
	$result.= &Apache::edit::select_or_text_arg('Correct Option:','value',['unused',@names],$token,'15');
	my $randomize=&Apache::lonxml::get_param('randomize',$parstack,
						 $safeeval,'-3');
	if ($randomize ne 'no') {
	    $result.=&Apache::edit::select_arg('Location:','location',
					     ['random','top','bottom'],$token);
	}
	$result .=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
    } elsif ($target eq 'modified') {
	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
						     $safeeval,'value',
						     'name','location');
	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
    }
    return $result;
}

sub end_foil {
    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
    my $text ='';
    my $result = '';
    if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
	$text=&Apache::lonxml::endredirection;
    }

    if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ||
	$target eq 'tex' || $target eq 'analyze') {
	if ($target eq 'tex' && $Apache::lonhomework::type eq 'exam') {
	    $text='\vskip 5mm $\triangleright$ '.$text;
	}
	my $value = &Apache::lonxml::get_param('value',$parstack,$safeeval);
	if ($value ne 'unused') {
	    my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
	    &Apache::lonxml::debug("Got a name of :$name:");
	    if (!$name) {
		&Apache::lonxml::warning(&mt('Foils without names exist. This can cause problems to malfunction.'));
		$name=$Apache::lonxml::curdepth;
	    }
	    &Apache::lonxml::debug("Using a name of :$name:");
	    if (defined($Apache::response::foilnames{$name})) {
		&Apache::lonxml::error(&mt("Foil name [_1] appears more than once. Foil names need to be unique.",'<b><tt>'.$name.'</tt></b>'));
	    }
	    $Apache::response::foilnames{$name}++;
	    my $location =&Apache::lonxml::get_param('location',$parstack,
						     $safeeval);
	    if ( $Apache::matchresponse::conceptgroup
		 && !&Apache::response::showallfoils() ) {

		push @{ $Apache::response::conceptgroup{'names'} }, $name;
		$Apache::response::conceptgroup{"$name.value"} = $value;
		$Apache::response::conceptgroup{"$name.text"} = $text;
		$Apache::response::conceptgroup{"$name.location"} = $location;
	    } else {

		push @{ $Apache::response::foilgroup{'names'} }, $name;
		$Apache::response::foilgroup{"$name.value"} = $value;
		$Apache::response::foilgroup{"$name.text"} = $text;
		$Apache::response::foilgroup{"$name.location"} = $location;
	    }
	}
    }
    if ($target eq 'edit') {
	$result.= &Apache::edit::tag_end($target,$token,'');
    }
    return $result;
}

sub insert_foil {
    return '
<foil name="" value="unused">
<startouttext />
<endouttext />
</foil>';
}

1;
__END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>