Skip to content

Commit

Permalink
Merge pull request #16 from vkaravir/codeline-refactor
Browse files Browse the repository at this point in the history
Codeline refactor
  • Loading branch information
vkaravir committed Jun 12, 2014
2 parents 9ecd40c + b94a307 commit 7f89337
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 84 deletions.
3 changes: 1 addition & 2 deletions examples/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ <h2>Simple js-parsons example assignment</h2>
' return True\n' +
' return False\n' +
' return true #distractor\n'
var parson;

function displayErrors(fb) {
if(fb.errors.length > 0) {
Expand All @@ -43,7 +42,7 @@ <h2>Simple js-parsons example assignment</h2>
}

$(document).ready(function(){
parson = new ParsonsWidget({
var parson = new ParsonsWidget({
'sortableId': 'sortable',
'trashId': 'sortableTrash',
'max_wrong_lines': 1,
Expand Down
5 changes: 2 additions & 3 deletions examples/pseudocode-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ <h2>Simple js-parsons pseudocode example assignment</h2>
<script src="../parsons.js"></script>
<script src="../lib/skulpt.js"></script>
<script src="../lib/skulpt-stdlib.js"></script>
<script src="../lib/prettify.js"></script>
<script>
var initial = 'IF a < b THEN\n min := a\nELSE\n min := b\nENDIF';
var initial = 'IF a $$toggle::<::>$$ b THEN\n min := a\nELSE\n min := b\nENDIF';
var parson;

$(document).ready(function(){
Expand All @@ -42,7 +41,7 @@ <h2>Simple js-parsons pseudocode example assignment</h2>
{initcode: "min = None\na = 7\nb = 4\n", code: "", message: "Testing with a = 7 ja b = 4",
variables: {min: 4}}],
'grader': ParsonsWidget._graders.LanguageTranslationGrader,
'executable_code': "if a < b:\n" +
'executable_code': "if a $$toggle$$ b:\n" +
"min = a\n" +
"else:\n" +
"min = b\n pass",
Expand Down
2 changes: 1 addition & 1 deletion examples/toggle-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ <h2>Example of js-parsons toggle-exercises</h2>
toggleTypeHandlers: {abc: ["a", "b", "c"]}
});
parson.init(initial);
parson.shuffleLines();
parson.shuffleLines();
$("#newInstanceLink").click(function(event){
event.preventDefault();
parson.shuffleLines();
Expand Down
14 changes: 6 additions & 8 deletions examples/toggle-variable-grader-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,22 @@ <h2>Variable Swap</h2>
<script src="../lib/skulpt.js"></script>
<script src="../lib/skulpt-stdlib.js"></script>
<script>
var initial = "<span class='jsparson-toggle' data-type='xytmp'></span> = " +
"<span class='jsparson-toggle' data-type='xytmp'></span>\n" +
"<span class='jsparson-toggle' data-type='xytmp'></span> = " +
"<span class='jsparson-toggle' data-type='xytmp'></span>\n" +
"<span class='jsparson-toggle' data-type='xytmp'></span> = " +
"<span class='jsparson-toggle' data-type='xytmp'></span>\n";
var initial = "$$toggle::x::y::tmp$$ = " +
"$$toggle::x::y::tmp$$\n" +
"$$toggle::x::y::tmp$$ = " +
"$$toggle::x::y::tmp$$\n" +
"$$toggle::x::y::tmp$$ = " +
"$$toggle::x::y::tmp$$\n";
$(document).ready(function(){
var parson = new ParsonsWidget({
'sortableId': 'sortable',
'trashId': 'sortableTrash',
'vartests': [{initcode: "x = 0\ny=2", code: "", message: "x = 0 ja y = 2", variables: {x: 2}},
{initcode: "x = 3\ny=4\n", code: "", message: "x=3 ja y=4", variables: {x: 4, y: 3}}],
toggleTypeHandlers: {xytmp: ["x", "y", "tmp"]},
lang: "en"
});
parson.init(initial);
parson.shuffleLines();

$("#newInstanceLink").click(function(event){
event.preventDefault();
parson.shuffleLines();
Expand Down
197 changes: 127 additions & 70 deletions parsons.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,23 @@
if (typeof executableCode === "string") {
executableCode = executableCode.split("\n");
}
// TODO: handle toggle elements
// replace each line with in solution with the corresponding line in executable code
$.each(student_code, function(index, item) {
var ind = parseInt(item.id.replace(parson.id_prefix, ''), 10);
executableCodeString += python_indents[item.indent] + executableCode[ind] + "\n";

// Handle toggle elements. Expects the toggle areas in executable code to be marked
// with $$toggle$$ and there to be as many toggles in executable code than in the
// code shown to learner.
var toggleRegexp = /\$\$toggle\$\$/g;
var execline = executableCode[ind];
var toggles = execline.match(toggleRegexp);
if (toggles) {
for (var i = 0; i < toggles.length; i++) {
execline = execline.replace(toggles[i], item.toggleValue(i));
}
}
// add the modified codeline to the executable code
executableCodeString += python_indents[item.indent] + execline + "\n";
});
return executableCodeString;
};
Expand All @@ -503,7 +516,7 @@
// Find the line objects for the student's code
for (i = 0; i < student_code.length; i++) {
studentCodeLineObjects.push($.extend(true,
{domElementId: student_code[i].id},
{},
parson.getLineById(student_code[i].id)));
}

Expand Down Expand Up @@ -533,7 +546,7 @@
// it must be a distractor
// => add to feedback, log, and ignore in LIS computation
wrong_order = true;
$("#" + lineObject.id).addClass("incorrectPosition");
lineObject.markIncorrectPosition();
incorrectLines.push(lineObject.orig);
lineObject.lisIgnore = true;
} else {
Expand Down Expand Up @@ -562,8 +575,8 @@
.map(function (lineObject) { return lineObject.position; }));
$.each(inv, function(_index, lineObjectIndex) {
// Highlight the lines that could be moved to fix code as defined by the LIS computation
$("#" + lisStudentCodeLineObjects[lineObjectIndex].domElementId).addClass("incorrectPosition");
incorrectLines.push(parseInt(lisStudentCodeLineObjects[lineObjectIndex].domElementId.replace(this.id_prefix, ""), 10));
lisStudentCodeLineObjects[lineObjectIndex].markIncorrectPosition();
incorrectLines.push(lisStudentCodeLineObjects[lineObjectIndex].orig);
});
if (inv.length > 0 || errors.length > 0) {
wrong_order = true;
Expand Down Expand Up @@ -592,14 +605,14 @@
var model_line = parson.model_solution[i];
if (code_line.indent !== model_line.indent &&
((!parson.options.first_error_only) || errors.length === 0)) {
$("#" + code_line.id).addClass("incorrectIndent");
code_line.markIncorrectIndent();
errors.push(parson.translations.block_structure(i+1));
log_errors.push({type: "incorrectIndent", line: (i+1)});
}
if (code_line.code == model_line.code &&
code_line.indent == model_line.indent &&
errors.length === 0) {
$("#" + code_line.id).addClass("correctPosition");
code_line.markCorrect();
}
}
}
Expand All @@ -621,9 +634,9 @@
mathop: ["+", "-", "*", "/"],
boolop: ["and", "or"],
range: function($item) {
var min = parseFloat($item.data("min") || "0", 10),
max = parseFloat($item.data("max") || "10", 10),
step = parseFloat($item.data("step") || "1", 10),
var min = parseFloat($item.data("min") || "0"),
max = parseFloat($item.data("max") || "10"),
step = parseFloat($item.data("step") || "1"),
opts = [],
curr = min;
while (curr <= max) {
Expand All @@ -634,6 +647,9 @@
}
};
var addToggleableElements = function(widget) {
for (var i = 0; i < widget.modified_lines.length; i++) {
widget.modified_lines[i]._addToggles();
}
// toggleable elements are only enabled for unit tests
if (!widget.options.unittests && !widget.options.vartests) { return; }
var handlers = $.extend(defaultToggleTypeHandlers, widget.options.toggleTypeHandlers),
Expand Down Expand Up @@ -668,7 +684,66 @@
});
};

// Creates a parsons widget. Init must be called after creating an object.
// Create a line object skeleton with only code and indentation from
// a code string of an assignment definition string (see parseCode)
var ParsonsCodeline = function(codestring, widget) {
this.widget = widget;
this.code = "";
this.indent = 0;
this._toggles = [];
if (codestring) {
// Consecutive lines to be dragged as a single block of code have strings "\\n" to
// represent newlines => replace them with actual new line characters "\n"
this.code = codestring.replace(/#distractor\s*$/, "").replace(trimRegexp, "$1").replace(/\\n/g, "\n");
this.indent = codestring.length - codestring.replace(/^\s+/, "").length;
}
};
ParsonsCodeline.prototype.elem = function() {
// the element will change on shuffle, so we should re-fetch it every time
return $("#" + this.id);
};
ParsonsCodeline.prototype.markCorrect = function() {
this.elem().addClass(this.widget.FEEDBACK_STYLES.correctPosition);
};
ParsonsCodeline.prototype.markIncorrectPosition = function() {
this.elem().addClass(this.widget.FEEDBACK_STYLES.incorrectPosition);
};
ParsonsCodeline.prototype.markIncorrectIndent = function() {
this.elem().addClass(this.widget.FEEDBACK_STYLES.incorrectIndent);
};
//
ParsonsCodeline.prototype._addToggles = function() {
var toggleRegexp = /\$\$toggle::.*?\$\$/g;
var toggles = this.code.match(toggleRegexp);
var that = this;
this._toggles = [];
if (toggles) {
var html = this.code;
for (var i = 0; i < toggles.length; i++) {
var opts = toggles[i].substring(10, toggles[i].length - 2).split("::");
html = html.replace(toggles[i], "<span class='jsparson-toggle' data-jsp-options='" +
JSON.stringify(opts).replace("<", "&lt;") + "'></span>");

}
this.elem().html(html);
this.elem().find(".jsparson-toggle").each(function(index, item) {
that._toggles.push(item);
});
}
};
// Returns the number of toggleable elements in this code block
ParsonsCodeline.prototype.toggleCount = function() {
return this._toggles.length;
};
// Returns the value of the toggleable element at the given index (0-based)
ParsonsCodeline.prototype.toggleValue = function(index) {
if (index < 0 || index >= this._toggles.length) { return undefined; }
return this._toggles[index].textContent;
};
// expose the type for testing, extending etc
window.ParsonsCodeline = ParsonsCodeline;

// Creates a parsons widget. Init must be called after creating an object.
var ParsonsWidget = function(options) {
// Contains line objects of the user-draggable code.
// The order is not meaningful (unchanged from the initial state) but
Expand Down Expand Up @@ -735,17 +810,6 @@

////Public methods

// Create a line object skeleton with only code and indentation from
// a code string of an assignment definition string (see parseCode)
ParsonsWidget.prototype.parseLine = function(spacePrefixedLine) {
return {
// Consecutive lines to be dragged as a single block of code have strings "\\n" to
// represent newlines => replace them with actual new line characters "\n"
code: spacePrefixedLine.replace(trimRegexp, "$1").replace(/\\n/g,"\n"),
indent: spacePrefixedLine.length - spacePrefixedLine.replace(/^\s+/,"").length
};
};

// Parses an assignment definition given as a string and returns and
// transforms this into an object defining the assignment with line objects.
//
Expand All @@ -771,32 +835,26 @@
// for distractors this is not meaningful but for lines belonging to the
// solution, this is their expected position
$.each(lines, function(index, item) {
if (item.search(/#distractor\s*$/) >= 0) {
// This line is a distractor
lineObject = {
// Consecutive lines to be dragged as a single block of code have strings "\\n" to
// represent newlines => replace them with actual new line characters "\n"
code: item.replace(/#distractor\s*$/,"").replace(trimRegexp, "$1").replace(/\\n/,"\n"),
indent: -1,
distractor: true,
orig: index
};
if (lineObject.code.length > 0) {
// The line is non-empty, not just whitespace
distractors.push(lineObject);
}
} else {
// This line is part of the solution
// Initialize line object with code and indentation properties
lineObject = that.parseLine(item);
if (lineObject.code.length > 0) {
// The line is non-empty, not just whitespace
lineObject.distractor = false;
lineObject.orig = index;
indented.push(lineObject);
}
}
});
lineObject = new ParsonsCodeline(item, that);
lineObject.orig = index;
if (item.search(/#distractor\s*$/) >= 0) {
// This line is a distractor
lineObject.indent = -1;
lineObject.distractor = true;
if (lineObject.code.length > 0) {
// The line is non-empty, not just whitespace
distractors.push(lineObject);
}
} else {
// This line is part of the solution
// Initialize line object with code and indentation properties
if (lineObject.code.length > 0) {
// The line is non-empty, not just whitespace
lineObject.distractor = false;
indented.push(lineObject);
}
}
});

var normalized = this.normalizeIndents(indented);

Expand Down Expand Up @@ -830,27 +888,27 @@
};

ParsonsWidget.prototype.init = function(text) {
// TODO: Error handling, parseCode may return errors in an array in property named errors.
// TODO: Error handling, parseCode may return errors in an array in property named errors.
var initial_structures = this.parseCode(text.split("\n"), this.options.max_wrong_lines);
this.model_solution = initial_structures.solution;
this.extra_lines = initial_structures.distractors;
this.modified_lines = initial_structures.widgetInitial;
var that = this;
var id_prefix = this.id_prefix;

// Add ids to the line objects in the user-draggable lines
$.each(this.modified_lines, function(index, item) {
item.id = that.id_prefix + index;
item.indent = 0;
});

// Add ids to the line objects in the user-draggable lines
$.each(this.modified_lines, function(index, item) {
item.id = id_prefix + index;
item.indent = 0;
});
};

ParsonsWidget.prototype.getHash = function(searchString) {
var ids = [];
var hash = [];
ids = $(searchString).sortable('toArray');
var hash = [],
ids = $(searchString).sortable('toArray'),
line;
for (var i = 0; i < ids.length; i++) {
hash.push(ids[i].replace(this.id_prefix, "") + "_" + this.getLineById(ids[i]).indent);
line = this.getLineById(ids[i]);
hash.push(line.orig + "_" + line.indent);
}
//prefix with something to handle empty output situations
if (hash.length === 0) {
Expand Down Expand Up @@ -1033,11 +1091,12 @@
ParsonsWidget.prototype.getModifiedCode = function(search_string) {
//ids of the the modified code
var lines_to_return = [],
that = this;
$(search_string).find("li").each(function(index, item) {
lines_to_return.push({id: $(item).attr("id"),
indent: parseInt($(item).css("margin-left"), 10)/that.options.x_indent});
});
solution_ids = $(search_string).sortable('toArray'),
i, item;
for (i = 0; i < solution_ids.length; i++) {
item = this.getLineById(solution_ids[i]);
lines_to_return.push($.extend(new ParsonsCodeline(), item));
}
return lines_to_return;
};

Expand All @@ -1062,9 +1121,7 @@
};

ParsonsWidget.prototype.updateIndentsFromHash = function(hash) {
var lines = [];
var lineValues;
var lineObject;
var h;

if (hash === "-" || hash === "" || hash === null) {
Expand Down Expand Up @@ -1155,7 +1212,7 @@


ParsonsWidget.prototype.shuffleLines = function() {
var permutation = this.getRandomPermutation(this.modified_lines.length);
var permutation = (this.options.permutation?this.options.permutation:this.getRandomPermutation)(this.modified_lines.length);
var idlist = [];
for(var i in permutation) {
idlist.push(this.modified_lines[permutation[i]].id);
Expand All @@ -1171,7 +1228,7 @@
ParsonsWidget.prototype.createHTMLFromHashes = function(solutionHash, trashHash) {
var solution = this.hashToIDList(solutionHash);
var trash = this.hashToIDList(trashHash);
this.createHTMLFromLists(solution,trash);
this.createHTMLFromLists(solution, trash);
this.updateIndentsFromHash(solutionHash);
};

Expand Down

0 comments on commit 7f89337

Please sign in to comment.