replay.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>BlockPy Replay</title>
  6. <link rel="stylesheet" href="libs/bootstrap.min.css">
  7. <link rel="stylesheet" href="libs/codemirror/codemirror.css">
  8. <link rel="stylesheet" href="libs/font-awesome.min.css">
  9. <link rel="stylesheet" href="libs/summernote/summernote.css">
  10. <link rel="stylesheet" href="libs/multi-select.css">
  11. <link rel="stylesheet" href="src/blockpy.css">
  12. <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">-->
  13. <!-- JQuery, D3, Math.js, Bootstrap -->
  14. <script type="text/javascript" src="libs/jquery.js"></script>
  15. <script type="text/javascript" src="libs/jquery-ui.min.js"></script>
  16. <script type="text/javascript" src="libs/jquery.hotkeys.js"></script>
  17. <script type="text/javascript" src="libs/jquery.multi-select.js"></script>
  18. <script type="text/javascript" src="libs/d3.min.js"></script>
  19. <script type="text/javascript" src="libs/math.0.19.0.min.js"></script>
  20. <script type="text/javascript" src="libs/bootstrap.min.js"></script>
  21. <script type="text/javascript" src="libs/bootstrap-wysiwyg.js"></script>
  22. <script type="text/javascript" src="libs/mindmup-editabletable.js"></script>
  23. <script type="text/javascript" src="libs/codemirror/codemirror.js"></script>
  24. <script type="text/javascript" src="libs/codemirror/python.js"></script>
  25. <script type="text/javascript" src="libs/codemirror/htmlmixed.js"></script>
  26. <script type="text/javascript" src="libs/codemirror/xml.js"></script>
  27. <script type="text/javascript" src="libs/knockout-3.4.0.js"></script>
  28. <!-- Summernote, a rich text editor -->
  29. <script type="text/javascript" src="libs/summernote/summernote.min.js"></script>
  30. <script type="text/javascript" src="libs/summernote/summernote-ext-hint.js"></script>
  31. <script type="text/javascript" src="libs/summernote/summernote-ext-video.js"></script>
  32. <!-- Blockly -->
  33. <script type="text/javascript" src="blockly/blockly_uncompressed.js"></script>
  34. <script type="text/javascript" src="blockly/blocks_compressed.js"></script>
  35. <script type="text/javascript" src="blockly/python_compressed.js"></script>
  36. <script type="text/javascript" src="blockly/pseudo_compressed.js"></script>
  37. <script type="text/javascript" src="blockly/javascript_compressed.js"></script>
  38. <script type="text/javascript" src="blockly/msg/js/en.js"></script>
  39. <!-- Skulpt -->
  40. <script type="text/javascript" src="skulpt/dist/skulpt.min.js"></script>
  41. <script type="text/javascript" src="skulpt/dist/skulpt-stdlib.js"></script>
  42. <!-- Source Code -->
  43. <script type="text/javascript" src="src/utilities.js"></script>
  44. <script type="text/javascript" src="src/python_errors.js"></script>
  45. <script type="text/javascript" src="src/ast_node_visitor.js"></script>
  46. <script type="text/javascript" src="src/abstract_interpreter.js"></script>
  47. <script type="text/javascript" src="src/abstract_interpreter_definitions.js"></script>
  48. <script type="text/javascript" src="src/abstract_interpreter_tests.js"></script>
  49. <script type="text/javascript" src="src/python_to_blockly.js"></script>
  50. <script type="text/javascript" src="src/imported.js"></script>
  51. <script type="text/javascript" src="src/blockly_blocks/class.js"></script>
  52. <script type="text/javascript" src="src/blockly_blocks/comment.js"></script>
  53. <script type="text/javascript" src="src/blockly_blocks/comprehensions.js"></script>
  54. <script type="text/javascript" src="src/blockly_blocks/dict.js"></script>
  55. <script type="text/javascript" src="src/blockly_blocks/if.js"></script>
  56. <script type="text/javascript" src="src/blockly_blocks/io.js"></script>
  57. <script type="text/javascript" src="src/blockly_blocks/lists.js"></script>
  58. <script type="text/javascript" src="src/blockly_blocks/sets.js"></script>
  59. <script type="text/javascript" src="src/blockly_blocks/loops.js"></script>
  60. <script type="text/javascript" src="src/blockly_blocks/parking.js"></script>
  61. <script type="text/javascript" src="src/blockly_blocks/tuple.js"></script>
  62. <script type="text/javascript" src="src/blockly_blocks/turtles.js"></script>
  63. <script type="text/javascript" src="src/blockly_blocks/text.js"></script>
  64. <script type="text/javascript" src="src/dialog.js"></script>
  65. <script type="text/javascript" src="src/storage.js"></script>
  66. <script type="text/javascript" src="src/printer.js"></script>
  67. <script type="text/javascript" src="src/interface.js"></script>
  68. <script type="text/javascript" src="src/server.js"></script>
  69. <script type="text/javascript" src="src/english.js"></script>
  70. <script type="text/javascript" src="src/corgis.js"></script>
  71. <script type="text/javascript" src="src/history.js"></script>
  72. <script type="text/javascript" src="src/presentation.js"></script>
  73. <script type="text/javascript" src="src/editor.js"></script>
  74. <script type="text/javascript" src="src/feedback.js"></script>
  75. <script type="text/javascript" src="src/toolbar.js"></script>
  76. <script type="text/javascript" src="src/treeMatching.js"></script>
  77. <script type="text/javascript" src="src/sk_mod_instructor_extended.js"></script>
  78. <script type="text/javascript" src="src/sk_mod_instructor.js"></script>
  79. <script type="text/javascript" src="src/engine.js"></script>
  80. <script type="text/javascript" src="src/main.js"></script>
  81. <!--<script type="text/javascript" src="test_corgis/blockpy/tate/tate_blockly.js"></script>
  82. <script type="text/javascript" src="test_corgis/blockpy/tate/tate_skulpt.js"></script>
  83. <script type="text/javascript" src="test_corgis/blockpy/tate/tate_dataset.js"></script>-->
  84. <style>
  85. </style>
  86. <script>
  87. $(document).ready(function() {
  88. blockpy = new BlockPy({
  89. 'blocklyPath': "blockly/",
  90. 'attachmentPoint': document.getElementById('blockpy-div'),
  91. 'instructor': true,
  92. 'developer': true,
  93. 'editor': 'Text',
  94. 'urls': {
  95. 'import_datasets': 'https://think.cs.vt.edu/blockpy/load_corgis/',
  96. //'walk_old_code': 'http://localhost:8000/walk_code/',
  97. //'log_event': 'http://localhost:8000/save_events/'
  98. /*'save_code': 'http://localhost:8000/save_code/',
  99. 'save_success': 'http://localhost:8000/submit_grade/',
  100. 'log_event': 'http://localhost:8000/save_events/'*/
  101. }
  102. });
  103. blockpy.setAssignment(
  104. // settings
  105. {
  106. 'editor': 'Text',
  107. 'read_only': false,
  108. 'disable_semantic_errors': false,
  109. },
  110. // assignment
  111. {
  112. 'introduction': "Welcome to BlockPy. Try running the code below.",
  113. 'name': "#1.2",
  114. 'give_feedback': 'import instructor_histogram as ins_hist\nimport instructor_append as ins_app\nimport instructor_iteration as ins_iter\nimport instructor_filter as ins_filt\nimport iteration_context as ins_cont\nins_app.missing_append_in_iteration()\nins_app.wrong_not_append_to_list()\nins_app.missing_append_list_initialization()\nins_app.wrong_append_list_initiatization()\nins_app.append_list_wrong_slot()\n',
  115. 'parsons': false,
  116. 'starting_code': '',
  117. 'importable': true,
  118. 'initial_view': 'Blocks',
  119. 'modules': {
  120. 'added': ['Functions', 'Data - Weather'],
  121. 'removed': []
  122. }
  123. },
  124. // programs
  125. {
  126. '__main__': 'import matplotlib.pyplot as plt\nquakes = [1,2,3,4]\nquakes_in_miles = []\nfor quake in quakes:\n quakes_in_miles.append(quake * 0.62)\nplt.hist(quakes_in_miles)\nplt.xlabel("Depth in Miles")\nplt.ylabel("Number of Earthquakes")\nplt.title("Distribution of Depth in Miles of Earthquakes")\nplt.show()\n'
  127. }
  128. )
  129. function inIframe () {
  130. try {
  131. return window.self !== window.top;
  132. } catch (e) {
  133. return true;
  134. }
  135. }
  136. if (inIframe()) {
  137. $("#blockpy-container").width("100%");
  138. }
  139. function updatePauseButton(){
  140. if(global_isPaused){
  141. document.getElementById("pause-button").innerHTML = "Play";
  142. }else{
  143. document.getElementById("pause-button").innerHTML = "Pause";
  144. }
  145. };
  146. var global_isPaused = false;
  147. const FORWARD_SCRUB = "forward";
  148. const BACKWARD_SCRUB = "backward";
  149. const STOP_SCRUB = "stop"
  150. var global_scrubing = STOP_SCRUB;
  151. var speed = 50;
  152. document.onkeydown = navigationStroke;
  153. function pauseEvent(){
  154. global_isPaused = !global_isPaused;
  155. updatePauseButton();
  156. }
  157. function forwardEvent(){
  158. global_isPaused = true;
  159. global_scrubing = FORWARD_SCRUB;
  160. updatePauseButton();
  161. }
  162. function backEvent(){
  163. global_isPaused = true;
  164. global_scrubing = BACKWARD_SCRUB;
  165. updatePauseButton();
  166. }
  167. function navigationStroke(key){
  168. var KeyNavigationEnabled = document.getElementById("myCheck").checked;
  169. if(KeyNavigationEnabled == true){
  170. key = key || window.event;
  171. if (key.keyCode == '80') {
  172. // p is pressed
  173. pauseEvent();
  174. }
  175. else if (key.keyCode == '37') {
  176. // left arrow
  177. backEvent();
  178. }
  179. else if (key.keyCode == '39') {
  180. // right arrow
  181. forwardEvent();
  182. }
  183. }
  184. }
  185. $('#speed-slider').change(function() {
  186. var sliderValue = document.getElementById("speed-slider").value;
  187. speed = 100 - document.getElementById("speed-slider").value;
  188. document.getElementById("sliderValue").innerHTML = sliderValue;
  189. });
  190. $('#pause-button').click(function() {
  191. global_isPaused = !global_isPaused;
  192. updatePauseButton();
  193. });
  194. $('#forward-button').click(function() {
  195. forwardEvent();
  196. });
  197. $('#backward-button').click(function() {
  198. backEvent();
  199. });
  200. function handleCodeEvents(record){
  201. switch (record.action.toLowerCase()) {
  202. case "set":
  203. blockpy.model.programs['__main__'](record.body);
  204. blockpy.components.editor.updateBlocks();
  205. break;
  206. }
  207. }
  208. function executeLastCodeEvent(pastRecords){
  209. var codeEvent = null;
  210. for(i = pastRecords.length-1; i >= 0; i--){
  211. var rawRecord = pastRecords[i];
  212. record = JSON.parse(rawRecord);
  213. if(record.event.toLowerCase() == "code"){
  214. codeEvent = record;
  215. break;
  216. }
  217. }
  218. if(codeEvent != null){
  219. handleCodeEvents(codeEvent);
  220. }
  221. }
  222. function handleFeedbackEvents(record){
  223. var messages = record.body.split("\n");
  224. var detailError = messages[0];
  225. var bodyMessage = "";
  226. var regex = /line (\d{1,})/i;
  227. for(i = 1; i < messages.length; i++){
  228. bodyMessage += messages[i];
  229. }
  230. var results = regex.exec(bodyMessage);
  231. var line = results != null && results.length > 1 ? results[1] : null;
  232. try{
  233. switch (record.action.toLowerCase()) {
  234. case "success":
  235. blockpy.components.feedback.complete(detailError, bodyMessage, line);
  236. break;
  237. case "editor error":
  238. blockpy.components.feedback.editorError(detailError, bodyMessage, line);
  239. break;
  240. case "no errors":
  241. blockpy.components.feedback.noErrors(detailError, bodyMessage, line);
  242. break;
  243. case "syntax error":
  244. blockpy.components.feedback.syntaxError(detailError, bodyMessage, line);
  245. break;
  246. case "semantic error":
  247. blockpy.components.feedback.semanticError(detailError, bodyMessage, line);
  248. break;
  249. case "internal error":
  250. blockpy.components.feedback.internalError(detailError, bodyMessage, line);
  251. break;
  252. case "instructor feedback":
  253. blockpy.components.feedback.instructorFeedback(detailError, bodyMessage, line);
  254. break;
  255. case "empty program":
  256. blockpy.components.feedback.emptyProgram(detailError, bodyMessage, line);
  257. break;
  258. default:
  259. console.log(record.action.toLowerCase());
  260. break;
  261. // ...
  262. }
  263. }catch(err){
  264. console.error(err);
  265. }
  266. };
  267. $('#replay-button').click(function() {
  268. var records = $('#replay-data').val().split(/\r?\n/);
  269. var previousTime = null;
  270. var pastRecords = [];
  271. var goingBackwards = false;
  272. var clearRunFlag = false;
  273. function processAnother(recordsLeft) {
  274. var gap = 100;
  275. var moveForward = true;
  276. if(global_isPaused){
  277. switch(global_scrubing){
  278. case BACKWARD_SCRUB:
  279. if(pastRecords.length > 1){
  280. blockpy.components.feedback.clearEditorErrors();
  281. blockpy.components.feedback.clear();
  282. recordsLeft.unshift(pastRecords.pop());
  283. recordsLeft.unshift(pastRecords.pop());
  284. goingBackwards = true;
  285. //TODO: Handle when going backwards to a feedback event
  286. }else{
  287. moveForward = false;
  288. }
  289. //NOTE: This cascades into FORWARD_SCRUB
  290. case FORWARD_SCRUB:
  291. global_scrubing = STOP_SCRUB;
  292. previousTime = null;
  293. break;
  294. default:
  295. moveForward = false;
  296. break;
  297. };
  298. }
  299. if(moveForward && recordsLeft.length > 0){
  300. blockpy.components.feedback.clearEditorErrors();
  301. blockpy.components.feedback.clear();
  302. var rawRecord = recordsLeft.shift();
  303. var record = JSON.parse(rawRecord);
  304. pastRecords.push(rawRecord);
  305. switch (record.event.toLowerCase()) {
  306. case "code":
  307. handleCodeEvents(record);
  308. break;
  309. case "feedback":
  310. if(goingBackwards){
  311. executeLastCodeEvent(pastRecords);
  312. }
  313. handleFeedbackEvents(record);
  314. break;
  315. }
  316. var cur_time = new Date(record.time*1000);
  317. if(!global_isPaused){
  318. gap = previousTime != null ? record.time - previousTime : 0;
  319. gap = (gap > 2000 ? 2000 : gap)*speed;
  320. previousTime = record.time;
  321. //var cur_time = new Date(record.time*1000);
  322. //var cur_time = record.time;
  323. //cur_time = cur_time.getHours();
  324. console.log(gap + ": " + cur_time.getHours() + ":" + cur_time.getMinutes() + ":" + cur_time.getSeconds() + ":" + cur_time.getMilliseconds() + "/" + record.time + ", ass: " + record.assignment + ", user: " + record.user);
  325. }else{
  326. console.log("freeze frame" + ": " + cur_time.getHours() + ":" + cur_time.getMinutes() + ":" + cur_time.getSeconds() + ":" + cur_time.getMilliseconds() + "/" + record.time + ", ass:" + record.assignment + ", user: " + record.user);
  327. }
  328. //blockpy.components.editor.getPngFromBlocks
  329. }
  330. if(!clearRunFlag){
  331. setTimeout(function() {processAnother(recordsLeft)}, gap);
  332. }
  333. }
  334. processAnother(records);
  335. });
  336. });
  337. </script>
  338. </head>
  339. <body>
  340. <div style="width:900px; margin:0 auto;" id='blockpy-container'> <!-- 900px -->
  341. <!-- Replay stuff -->
  342. <textarea class="form-control" rows="3" id='replay-data'></textarea>
  343. <button type='button' class='btn btn-submit' id='replay-button'>Replay</button>
  344. <button type='button' class='btn btn-submit' id='pause-button'>Pause</button>
  345. <button type='button' class='btn btn-submit' id='backward-button'>Backward</button>
  346. <button type='button' class='btn btn-submit' id='forward-button'>Forward</button>
  347. KeyNavigation: <input type="checkbox" id="myCheck" checked="check">
  348. <input type="range" id="speed-slider" value="50" min="1" max="100">
  349. <p id="sliderValue" value="50"></p>
  350. <div id="blockpy-div" style='height:100%'></div>
  351. </div>
  352. <script>
  353. </script>
  354. </body>
  355. </html>