Skip to content

Commit

Permalink
Merge pull request rederly#5 from rederly/add-cdn-draggable-proof
Browse files Browse the repository at this point in the history
add draggableProof macro with nestable via cdn
  • Loading branch information
doombot-exe committed Jan 20, 2021
2 parents d36e37c + 4e9b8f3 commit fd85c5e
Showing 1 changed file with 285 additions and 0 deletions.
285 changes: 285 additions & 0 deletions macros/draggableProof.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
# Done: show possible choices in TeX mode
# To do: display student answers and correct answers in TeX mode properly.
# To do: put jquery.nestable.js in a universal spot on every webwork server.

loadMacros("PGchoicemacros.pl");

sub _draggableProof_init {
PG_restricted_eval("sub DraggableProof {new draggableProof(\@_)}");

$courseHtmlUrl = $envir{htmlURL};

# post global javascript
main::POST_HEADER_TEXT(main::MODES(TeX=>"", HTML=><<" END_SCRIPTS"));
<!-- The next to scripts may need to be included on older versions of WeBWoRK (before 2.9 or so.
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/nestable2/1.6.0/jquery.nestable.min.js" integrity="sha512-7bS2beHr26eBtIOb/82sgllyFc1qMsDcOOkGK3NLrZ34yTbZX8uJi5sE0NNDYFNflwx1TtnDKkEq+k2DCGfb5w==" crossorigin="anonymous"></script>
<script>oh
function _nestableUpdate (e) {
var dd = e.length ? e : \$(e.target);
var ans = dd["0"].getAttribute("data-ans");
var li_tags = dd["0"].getElementsByTagName("li");
var list = [];
for (var i = 0, n = li_tags.length; i < n; i++) {
list.push(li_tags[i].id);
}
\$("#"+ans).val(list.join(","));
}
</script>
END_SCRIPTS
}

package draggableProof;

my $n = 0; # number of nestable lists so far

sub new {
$n++;
my $self = shift; my $class = ref($self) || $self;
my $proof = shift || []; my $extra = shift || [];
my %options = (
SourceLabel => "Choose from these sentences:",
TargetLabel => "Your Proof:",
id => "P$n",
@_
);
my $lines = [@$proof,@$extra];
my $numNeeded = scalar(@$proof);
my $numProvided = scalar(@$lines);
my @order = main::shuffle($numProvided);
my @unorder = main::invert(@order);
$self = bless {
lines => $lines,
numNeeded => $numNeeded, numProvided => $numProvided,
order => \@order, unordered => \@unorder,
proof => "$options{id}-".join(",$options{id}-",@unorder[0..$numNeeded-1]),
%options
}, $class;
$self->AnswerRule;
$self->ScriptAndStyles;
$self->GetAnswer;
return $self;
}

sub lines {my $self = shift; return @{$self->{lines}}}
sub numNeeded {(shift)->{numNeeded}}
sub numProvided {(shift)->{numProvided}}
sub order {my $self = shift; return @{$self->{order}}}
sub unorder {my $self = shift; return @{$self->{unorder}}}

# In principle, the styles (for example, color and size of the tiles)
# could be customized for each draggableProof instance. This is why
# each instance receives its own CSS container class.

sub ScriptAndStyles {
my $self = shift; my $id = $self->{id};

main::POST_HEADER_TEXT(main::MODES(TeX=>"", HTML=><<" SCRIPT_AND_STYLE"));
<script type="text/javascript">
\$(document).ready(function() {
\$("#nestable-$id-source").nestable({
group: "$id",
maxDepth: 1,
scroll: true,
callback: _nestableUpdate
});
\$("#nestable-$id-source").on('lostItem', _nestableUpdate);
\$("#nestable-$id-target").nestable({
group: "$id",
maxDepth: 1,
scroll: true,
callback: _nestableUpdate
});
\$("#nestable-$id-target").on('lostItem', _nestableUpdate);
});
</script>
<style type="text/css">
.nestable-$id-container {
width: 350px;
float: left;
margin: 10px;
padding: 0;
color: #000000;
border:1 px solid #388E8E;
text-align: center;
}
.nestable-label {
margin: 10px 0 10px 0;
}
.dd, .dd-list, .dd-item {
display: block;
position: relative;
list-style: none;
margin: 0;
padding: 0;
min-height: 30px;
}
.dd-empty, .dd-handle, .dd-placeholder {
display: block;
position: relative;
margin: 0 10px 10px 10px;
padding: 4px;
min-height: 30px;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.dd-handle {
background: #F5DEB3;
border: 1px solid #388E8E;
-webkit-border-radius: 5px;
border-radius: 5px;
text-align: center;
}
.dd-handle:hover {
cursor: pointer;
}
.dd-placeholder {
background: #f2fbff;
border: 1px dashed #b6bcbf;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.dd-dragel {
position: absolute;
pointer-events: none;
z-index: 9999;
}
.dd-dragel > .dd-item .dd-handle {
margin-top: 0;
}
.dd-dragel .dd-handle {
-webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
opacity: 0.8;
}
</style>
SCRIPT_AND_STYLE
}

sub AnswerRule {
my $self = shift;
my $rule = main::ans_rule(1);
$self->{tgtAns} = ""; $self->{tgtAns} = $1 if $rule =~ m/id="(.*?)"/;
$self->{srcAns} = $self->{tgtAns}."-src";
main::RECORD_FORM_LABEL($self->{srcAns}); # use this for release 2.13 and comment out for develop
#$main::PG->store_persistent_data; # uncomment this for develop and releases beyond 2.13
my $ext = main::NAMED_ANS_RULE_EXTENSION($self->{srcAns},1,answer_group_name=>$self->{srcAns}.'-src');
main::TEXT( main::MODES(TeX=>"", HTML=>'<div style="display:none" id="Proof">'.$rule.$ext.'</div>'));
}

sub GetAnswer {
my $self = shift; my $previous;

# Retrieve the previous state of the right column.
$previous = $main::inputs_ref->{$self->{tgtAns}} || "";
$previous =~ s/$self->{id}-//g; $self->{previousTarget} = [split(/,/,$previous)];

# Calculate the complement of the right column.
my %prevTarget = map {$_ => 1} @{$self->{previousTarget}};
my @diff = grep {not $prevTarget{$_}} (0..$self->{numProvided}-1);

# If the previous state of the left column has been saved, use it. (This ensures that the tiles
# in the left column are kept in the same order that the user had arranged them). If it has not
# been saved, use the complement of the right column.
$previous = $main::inputs_ref->{$self->{srcAns}} || "$self->{id}-".join(",$self->{id}-",@diff);
$previous =~ s/$self->{id}-//g; $self->{previousSource} = [split(/,/,$previous)];
}

sub Print {
my $self = shift;

if ($main::displayMode ne "TeX") { # HTML mode

return join("\n",
'<div style="min-width:750px;">',
$self->Source,
$self->Target,
'<br clear="all" />',
'</div>',
);

} else { # TeX mode

return join("\n",
$self->Source,
$self->Target,
);

}

}

sub Source {
my $self = shift;
return $self->Bucket("source",$self->{srcAns},$self->{SourceLabel},$self->{previousSource});
}

sub Target {
my $self = shift;
return $self->Bucket("target",$self->{tgtAns},$self->{TargetLabel},$self->{previousTarget});
}

sub Bucket {
my $self = shift; my $id = $self->{id};
my ($name,$ans,$label,$previous) = @_;

if ($main::displayMode ne "TeX") { # HTML mode

my @lines = ();
push(@lines, '<div class="nestable-'.$id.'-container">',
'<div class="nestable-label">'.$label.'</div>',
'<div class="dd" id="nestable-'.$id.'-'.$name.'" data-ans="'.$ans.'">'
);
if (scalar @{$previous} > 0) {
push(@lines, '<ol class="dd-list">');
foreach my $i (@{$previous}) {
push(@lines, '<li class="dd-item" id="'.$id.'-'.$i.'"><div class="dd-handle">'.$self->{lines}[$self->{order}[$i]].'</div></li>');
}
push(@lines, '</ol>');
}
push(@lines,
'</div>',
'</div>'
);
return join("\n",@lines);

} else { # TeX mode

if (@{$previous}) { # array is nonempty
my @lines = ('\\begin{itemize}');
foreach my $i (@{$previous}) {
push(@lines,'\\item '.$self->{lines}[$self->{order}[$i]] )
}
push(@lines,'\\end{itemize}');
return join("\n",@lines);
} else {
return '';
}
}

}

sub cmp {
my $self = shift;
return main::str_cmp($self->{proof})->withPreFilter("erase")->withPostFilter(sub {$self->filter(@_)});
}

sub filter {
my $self = shift; my $ans = shift;
my @line = $self->lines; my @order = $self->order;
my $correct = $ans->{correct_ans}; $correct =~ s/$self->{id}-//g;
my $student = $ans->{student_ans}; $student =~ s/$self->{id}-//g;
my @correct = @line[map {@order[$_]} split(/,/,$correct)];
my @student = @line[map {@order[$_]} split(/,/,$student)];
$ans->{preview_latex_string} = "\\begin{array}{l}\\text{".join("}\\\\\\text{",@student)."}\\end{array}";
$ans->{student_ans} = "(see preview)";
$ans->{correct_ans_latex_string} = "\\begin{array}{l}\\text{".join("}\\\\\\text{",@correct)."}\\end{array}";
$ans->{correct_ans} = join("<br />",@correct);
return $ans;
}

1;

0 comments on commit fd85c5e

Please sign in to comment.