require("./../rur.js");
require("./../translator.js");
require("./../recorder/record_frame.js");
RUR.output = {};
RUR.output.write = function () {
var output_string = '';
RUR.state.sound_id = "#write-sound";
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "string") {
output_string += arguments[i];
} else {
output_string += JSON.stringify(arguments[i]);
}
}
RUR.print_cache += output_string;
output_string = output_string.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
RUR.record_frame("stdout", {"element": "#stdout", "message": output_string});
};
RUR.output.write_ln = function () {
var output_string = '';
RUR.state.sound_id = "#write-sound";
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "string") {
output_string += arguments[i];
} else {
output_string += JSON.stringify(arguments[i]);
}
}
RUR.print_cache += output_string + "\n";
output_string = output_string.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
output_string += "\n";
RUR.record_frame("stdout", {"element": "#stdout", "message": output_string});
};
RUR.output.clear_print = function () {
RUR.record_frame("stdout", {"element": "#stdout", "clear": true});
};
RUR.output.print_html = function (arg, replace) {
if (replace) {
RUR.record_frame("print_html", {"element": "#print-html", "message": arg});
} else {
RUR.record_frame("print_html", {"element": "#print-html", "message": arg, "append": true});
}
};
RUR.output.watch_variables = function (arg) {
RUR.record_frame("watch_variables", {"element": "#watch-variables", "message": arg});
};
/** @function get_print
* @memberof RUR
* @instance
* @summary This function returns the content of the standard output,
* produced by `print` in Python, and either `write` or `writeln` in
* Javascript.
* @return {string} The content of the print output.
*/
RUR.get_print = function () {
return RUR.print_cache;
};
/** @function show_diff
* @memberof RUR
* @instance
* @summary This function compares two strings and returns a single html string
* with any differences highlighted. Newlines are converted to ⏎ followed
* by `<br>`. The typical use case is to compare an expected result to the
* one actually observed.
*
* **Note**: if you or one of your students find the color differences between
* the inserted text and the deleted text too hard to distinguish, please
* get in touch with me so that I can make this work better.
*
* @param {string} expected The expected string
* @param {string} actual The string that was obtained (from the student code)
* @param {boolean} [semantic] If set to `true`, a semantic comparison (word by word)
* is attempted rather than a character by character comparison.
* This may give a more readable output in some cases.
*
* @return {string} The content of the print output as a specially formatted html string.
*/
RUR.show_diff = function(expected, actual, semantic) {
// adapted from diff_match_patch's diff_prettyHtml
// See https://code.google.com/archive/p/google-diff-match-patch/wikis/API.wiki
var diff_object = new diff_match_patch();
var diffs = diff_object.diff_main(expected, actual);
if (semantic) {
diff_object.diff_cleanupSemantic(diffs);
}
var html = [];
var pattern_amp = /&/g;
var pattern_lt = /</g;
var pattern_gt = />/g;
var pattern_newline = /\n/g;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0]; // Operation (insert, delete, equal)
var data = diffs[x][1]; // Text of change.
var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<')
.replace(pattern_gt, '>').replace(pattern_newline, '⏎<br>');
switch (op) {
case DIFF_INSERT:
html[x] = '<ins style="background:#e6ffe6;">' + text + '</ins>';
break;
case DIFF_DELETE:
html[x] = '<del style="background:#ffe6e6;">' + text + '</del>';
break;
case DIFF_EQUAL:
html[x] = '<span>' + text + '</span>';
break;
}
}
return html.join('');
};
/** @function show_detailed_diff
* @memberof RUR
* @instance
* @summary This function compares two strings and returns a summary of what
* was expected, followed by an output showing the differences as highlighted.
* This can be used, for example, to compare the output of a print statement
* written by a student to an expected output. If one wants to compare the
* result of a function returning a string, an additional option is to
* also show the actual output.
*
* **Note**: if you or one of your students find the color differences between
* the inserted text and the deleted text too hard to distinguish, please
* get in touch with me so that I can make this work better.
*
* @param {string} expected The expected string
* @param {string} actual The string that was obtained (from the student code)
* @param {Object} [options] A Javascript object (Python dict) containing some
* additional optional options.
* @param {boolean} [options.semantic] If set to `true`, a semantic comparison
* (word by word)
* is attempted rather than a character by character comparison.
* This may give a more readable output in some cases.
* @param {string} [options.expected_heading] A string to use as the heading
* for the expected result.
* @param {string} [options.differences] A string to use as the heading
* for the highlighted differences.
* @param {boolean} [options.show_actual] If we wish to also display the actual
* result.
* @param {string} [options.actual_heading] A string to use as the heading
* for the actual result. This will be ignored if `options.show_actual` does
* not evaluate to `true`.
*
* @return {string} A formatted html string.
*/
RUR.show_detailed_diff = function (expected, actual, options) {
"use strict";
var diff, expected_heading, differences_heading, actual_heading,
div_begin,
expected_section, differences_section, actual_section='';
div_begin = "<div style='margin-left:2em;'>";
if (options && options.semantic) {
diff = RUR.show_diff(expected, actual, true);
} else {
diff = RUR.show_diff(expected, actual);
}
if (options && options.expected_heading) {
expected_heading = "<h3>" + options.expected_heading + "</h3>";
} else {
expected_heading = "<h3>" + RUR.translate("Expected result") + "</h3>";
}
expected_section = expected_heading + div_begin + expected + "</div>";
if (options && options.differences_heading) {
differences_heading = "<h3>" + options.differences_heading + "</h3>";
} else {
differences_heading = "<h3>" + RUR.translate("Differences highlighted") + "</h3>";
}
differences_section = differences_heading + div_begin + diff + "</div>";
if (options && options.show_actual) {
if (options.actual_heading) {
actual_heading = "<h3>" + options.actual_heading + "</h3>";
} else {
actual_heading = "<h3>" + RUR.translate("Actual result") + "</h3>";
}
actual_section = actual_heading + div_begin + actual + "</div>";
}
return expected_section + actual_section + differences_section;
};