main.js.html 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: main.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: main.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>/**
  20. * A helper function for extending an array based
  21. * on an "addArray" and "removeArray". Any element
  22. * found in removeArray is removed from the first array
  23. * and all the elements of addArray are added.
  24. * Creates a new array, so is non-destructive.
  25. *
  26. * @param {Array} array - the array to manipulate
  27. * @param {Array} addArray - the elements to add to the array
  28. * @param {Array} removeArray - the elements to remove from the array
  29. * @return {Array} The modified array
  30. */
  31. function expandArray(array, addArray, removeArray) {
  32. var copyArray = array.filter(function(item) {
  33. return removeArray.indexOf(item) === -1;
  34. });
  35. return copyArray.concat(addArray);
  36. }
  37. /**
  38. * Deeply clones a node
  39. * @param {Node} node A node to clone
  40. * @return {Node} A clone of the given node and all its children
  41. */
  42. function cloneNode(node) {
  43. // If the node is a text node, then re-create it rather than clone it
  44. var clone = node.nodeType == 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
  45. // Recurse
  46. var child = node.firstChild;
  47. while(child) {
  48. clone.appendChild(cloneNode(child));
  49. child = child.nextSibling;
  50. }
  51. return clone;
  52. }
  53. /**
  54. * Creates an instance of BlockPy
  55. *
  56. * @constructor
  57. * @this {BlockPy}
  58. * @param {Object} settings - User level settings (e.g., what view mode, whether to mute semantic errors, etc.)
  59. * @param {Object} assignment - Assignment level settings (data about the loaded assignment, user, submission, etc.)
  60. * @param {Object} submission - Unused parameter.
  61. * @param {Object} programs - Includes the source code of any programs to be loaded
  62. */
  63. function BlockPy(settings, assignment, submission, programs) {
  64. this.model = {
  65. // User level settings
  66. "settings": {
  67. // Default mode when you open the screen is text
  68. // 'text', 'blocks'
  69. 'editor': ko.observable(assignment.initial_view),
  70. // Default mode when you open the screen is instructor
  71. // boolean
  72. 'instructor': ko.observable(settings.instructor),
  73. 'instructor_initial': ko.observable(settings.instructor),
  74. // String
  75. 'log_id': ko.observable(null),
  76. // boolean
  77. 'enable_blocks': ko.observable(settings.blocks_enabled),
  78. // boolean
  79. 'read_only': ko.observable(settings.read_only),
  80. // string
  81. 'filename': ko.observable("__main__"),
  82. // string
  83. 'level': ko.observable("level"),
  84. // boolean
  85. 'disable_semantic_errors': ko.observable(settings.disable_semantic_errors ||
  86. assignment.disable_algorithm_errors || false),
  87. // boolean
  88. 'disable_variable_types': ko.observable(settings.disable_variable_types || true),
  89. // boolean
  90. 'disable_timeout': ko.observable(settings.disable_timeout || false),
  91. // boolean
  92. 'auto_upload': ko.observable(true),
  93. // boolean
  94. 'developer': ko.observable(settings.developer || false),
  95. // boolean
  96. 'mute_printer': ko.observable(false)
  97. },
  98. 'execution': {
  99. // 'waiting', 'running'
  100. 'status': ko.observable('waiting'),
  101. // integer
  102. 'step': ko.observable(0),
  103. // integer
  104. 'last_step': ko.observable(0),
  105. // list of string/list of int
  106. 'output': ko.observableArray([]),
  107. // integer
  108. 'line_number': ko.observable(0),
  109. // array of simple objects
  110. 'trace': ko.observableArray([]),
  111. // integer
  112. 'trace_step': ko.observable(0),
  113. // object
  114. 'ast': {},
  115. // boolean
  116. 'show_trace': ko.observable(false),
  117. },
  118. 'status': {
  119. // boolean
  120. 'loaded': ko.observable(false),
  121. 'text': ko.observable("Loading"),
  122. // 'none', 'runtime', 'syntax', 'semantic', 'feedback', 'complete', 'editor'
  123. 'error': ko.observable('none'),
  124. // "Loading", "Saving", "Ready", "Disconnected", "Error"
  125. 'server': ko.observable("Loading"),
  126. // Some message from a server error can go here
  127. 'server_error': ko.observable(''),
  128. // Dataset loading
  129. 'dataset_loading': ko.observableArray()
  130. },
  131. 'constants': {
  132. // string
  133. 'blocklyPath': settings.blocklyPath,
  134. // boolean
  135. 'blocklyScrollbars': true,
  136. // string
  137. 'attachmentPoint': settings.attachmentPoint,
  138. // JQuery object
  139. 'container': null,
  140. // Maps codes ('log_event', 'save_code') to URLs
  141. 'urls': settings.urls
  142. },
  143. // Assignment level settings
  144. "assignment": {
  145. 'modules': ko.observableArray(expandArray(BlockPy.DEFAULT_MODULES, assignment.modules.added || [], assignment.modules.removed || [])),
  146. 'assignment_id': assignment.assignment_id,
  147. 'student_id': assignment.student_id,
  148. 'course_id': assignment.course_id,
  149. 'version': ko.observable(assignment.version),
  150. //'lis_result_sourcedid': assignment.lis_result_sourcedid,
  151. 'name': ko.observable(assignment.name),
  152. 'introduction': ko.observable(assignment.introduction),
  153. "initial_view": ko.observable(assignment.initial_view || 'Blocks'),
  154. 'parsons': ko.observable(assignment.parsons),
  155. 'upload': ko.observable(assignment.initial_view == 'Upload'),
  156. 'importable': ko.observable(assignment.importable || false),
  157. 'disable_algorithm_errors': ko.observable(assignment.disable_algorithm_errors || false)
  158. },
  159. "programs": {
  160. "__main__": ko.observable(programs.__main__),
  161. "starting_code": ko.observable(assignment.starting_code),
  162. "give_feedback": ko.observable(assignment.give_feedback),
  163. "answer": ko.observable(assignment.answer)
  164. }
  165. };
  166. // The code for the current active program file (e.g., "__main__")
  167. this.model.program = ko.computed(function() {
  168. return this.programs[this.settings.filename()]();
  169. }, this.model) //.extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 400 } });
  170. // Whether this URL has been specified
  171. this.model.server_is_connected = function(url) {
  172. return this.constants.urls !== undefined &amp;&amp; this.constants.urls[url] !== undefined;
  173. };
  174. // Helper function to map error statuses to UI elements
  175. this.model.status_feedback_class = ko.computed(function() {
  176. switch (this.status.error()) {
  177. default: case 'none': return ['label-none', ''];
  178. case 'runtime': return ['label-runtime-error', 'Runtime Error'];
  179. case 'syntax': return ['label-syntax-error', 'Syntax Error'];
  180. case 'editor': return ['label-syntax-error', 'Editor Error'];
  181. case 'internal': return ['label-internal-error', 'Internal Error'];
  182. case 'semantic': return ['label-semantic-error', 'Algorithm Error'];
  183. case 'feedback': return ['label-feedback-error', 'Incorrect Answer'];
  184. case 'complete': return ['label-problem-complete', 'Complete'];
  185. case 'no errors': return ['label-no-errors', 'No errors'];
  186. }
  187. }, this.model);
  188. // Helper function to map Server error statuses to UI elements
  189. this.model.status_server_class = ko.computed(function() {
  190. switch (this.status.server()) {
  191. default: case 'Loading': return ['label-default', 'Loading'];
  192. case 'Offline': return ['label-default', 'Offline'];
  193. case 'Out of date': return ['label-danger', 'Out of Date'];
  194. case 'Loaded': return ['label-success', 'Loaded'];
  195. case 'Logging': return ['label-primary', 'Logging'];
  196. case 'Saving': return ['label-primary', 'Saving'];
  197. case 'Saved': return ['label-success', 'Saved'];
  198. case 'Disconnected': return ['label-danger', 'Disconnected'];
  199. case 'Error': return ['label-danger', 'Error'];
  200. }
  201. }, this.model);
  202. // Program trace functions
  203. var execution = this.model.execution;
  204. this.model.moveTraceFirst = function(index) {
  205. execution.trace_step(0); };
  206. this.model.moveTraceBackward = function(index) {
  207. var previous = Math.max(execution.trace_step()-1, 0);
  208. execution.trace_step(previous); };
  209. this.model.moveTraceForward = function(index) {
  210. var next = Math.min(execution.trace_step()+1, execution.last_step());
  211. execution.trace_step(next); };
  212. this.model.moveTraceLast = function(index) {
  213. execution.trace_step(execution.last_step()); };
  214. this.model.current_trace = ko.pureComputed(function() {
  215. return execution.trace()[Math.min(execution.trace().length-1, execution.trace_step())];
  216. });
  217. /**
  218. * Opens a new window to represent the exact value of a Skulpt object.
  219. * Particularly useful for things like lists that can be really, really
  220. * long.
  221. *
  222. * @param {String} type - The type of the value
  223. * @param {Object} exact_value - A Skulpt value to be rendered.
  224. */
  225. this.model.viewExactValue = function(type, exact_value) {
  226. return function() {
  227. if (type == "List") {
  228. var output = exact_value.$r().v;
  229. var result = (window.btoa?'base64,'+btoa(JSON.stringify(output)):JSON.stringify(output));
  230. window.open('data:application/json;' + result);
  231. }
  232. }
  233. }
  234. // For performance reasons, batch notifications for execution handling.
  235. // I'm not even sure these have any value any more.
  236. execution.trace.extend({ rateLimit: { timeout: 20, method: "notifyWhenChangesStop" } });
  237. execution.step.extend({ rateLimit: { timeout: 20, method: "notifyWhenChangesStop" } });
  238. execution.last_step.extend({ rateLimit: { timeout: 20, method: "notifyWhenChangesStop" } });
  239. execution.line_number.extend({ rateLimit: { timeout: 20, method: "notifyWhenChangesStop" } });
  240. this.initMain();
  241. }
  242. /**
  243. * The default modules to make available to the user.
  244. *
  245. * @type Array.&lt;String>
  246. */
  247. BlockPy.DEFAULT_MODULES = ['Properties', 'Decisions',
  248. 'Iteration',
  249. 'Calculation', 'Output',
  250. 'Values',
  251. 'Lists', 'Dictionaries']
  252. /**
  253. * Initializes the BlockPy object by initializing its interface,
  254. * model, and components.
  255. *
  256. */
  257. BlockPy.prototype.initMain = function() {
  258. this.turnOnHacks();
  259. this.initInterface();
  260. this.initModel();
  261. this.initComponents();
  262. if (this.model.settings.developer()) {
  263. this.initDevelopment();
  264. }
  265. }
  266. /**
  267. * Initializes the User Inteface for the instance, by loading in the
  268. * HTML file (which has been manually encoded into a JS string using
  269. * the build.py script). We do this because its a giant hassle to keep
  270. * HTML correct when it's stored in JS strings. We should look into
  271. * more sophisticated templating features, probably.
  272. *
  273. */
  274. BlockPy.prototype.initInterface = function() {
  275. var constants = this.model.constants;
  276. // Refer to interface.js, interface.html, and build.py
  277. constants.container = $(constants.attachmentPoint).html($(BlockPyInterface))
  278. }
  279. /**
  280. * Applys the KnockoutJS bindings to the model, instantiating the values into the
  281. * HTML.
  282. */
  283. BlockPy.prototype.initModel = function() {
  284. ko.applyBindings(this.model);
  285. }
  286. /**
  287. * Initializes each of the relevant components of BlockPy. For more information,
  288. * consult each of the component's relevant JS file in turn.
  289. */
  290. BlockPy.prototype.initComponents = function() {
  291. var container = this.model.constants.container;
  292. this.components = {};
  293. var main = this,
  294. components = this.components;
  295. // Each of these components will take the BlockPy instance, and possibly a
  296. // reference to the relevant HTML location where it will be embedded.
  297. components.dialog = new BlockPyDialog(main, container.find('.blockpy-popup'));
  298. components.toolbar = new BlockPyToolbar(main, container.find('.blockpy-toolbar'));
  299. components.feedback = new BlockPyFeedback(main, container.find('.blockpy-feedback'));
  300. components.editor = new BlockPyEditor(main, container.find('.blockpy-editor'));
  301. components.presentation = new BlockPyPresentation(main, container.find('.blockpy-presentation'));
  302. components.printer = new BlockPyPrinter(main, container.find('.blockpy-printer'));
  303. components.engine = new BlockPyEngine(main);
  304. components.server = new BlockPyServer(main);
  305. components.corgis = new BlockPyCorgis(main);
  306. components.history = new BlockPyHistory(main);
  307. components.english = new BlockPyEnglish(main);
  308. components.editor.setMode();
  309. main.model.status.server('Loaded')
  310. var statusBox = container.find(".blockpy-status-box");
  311. main.model.status.server.subscribe(function(newValue) {
  312. if (newValue == "Error" ||
  313. newValue == "Offline" ||
  314. newValue == "Disconnected") {
  315. if (!statusBox.is(':animated')) {
  316. statusBox.effect("shake");
  317. }
  318. } else if (newValue == "Out of date") {
  319. if (!statusBox.is(':animated')) {
  320. statusBox.effect("shake").effect("shake");
  321. }
  322. }
  323. });
  324. statusBox.tooltip();
  325. }
  326. /**
  327. * Initiailizes certain development data, useful for testing out new modules in
  328. * Skulpt.
  329. */
  330. BlockPy.prototype.initDevelopment = function () {
  331. /*$.get('src/skulpt_ast.js', function(data) {
  332. Sk.builtinFiles['files']['src/lib/ast/__init__.js'] = data;
  333. });*/
  334. }
  335. /**
  336. * Redundant method for reporting an error. If this is used anywhere, it should be
  337. * converted to direct calls to components.feedback.
  338. */
  339. BlockPy.prototype.reportError = function(component, original, message, line) {
  340. if (component == 'editor') {
  341. this.components.feedback.editorError(original, message, line);
  342. } else if (component == 'syntax') {
  343. this.components.feedback.syntaxError(original, message, line);
  344. }
  345. console.error(component, message)
  346. }
  347. /**
  348. * Helper function for setting the current code, optionally in the given filename.
  349. *
  350. * @param {String} code - The new Python source code to set.
  351. * @param {String?} name - An optional filename (e.g,. '__main__') to update. Defaults to the currently selected filename.
  352. * @returns {Boolean} - whether the code was updated (i.e. there was a diff between new and old).
  353. */
  354. BlockPy.prototype.setCode = function(code, name) {
  355. if (name === undefined) {
  356. name = this.model.settings.filename();
  357. }
  358. var original = this.model.programs[name]();
  359. this.model.programs[name](code);
  360. return original != this.model.programs[name]();
  361. }
  362. /**
  363. * Function for running any code that fixes bugs and stuff upstream.
  364. * Not pleasant that this exists, but better to have it isolated than
  365. * just lying about randomly...
  366. *
  367. */
  368. BlockPy.prototype.turnOnHacks = function() {
  369. /*
  370. * jQuery UI shake - Padding disappears
  371. * Courtesy: http://stackoverflow.com/questions/22301972/jquery-ui-shake-padding-disappears
  372. */
  373. if ($.ui) {
  374. (function () {
  375. var oldEffect = $.fn.effect;
  376. $.fn.effect = function (effectName) {
  377. if (effectName === "shake" || effectName.effect == "shake") {
  378. var old = $.effects.createWrapper;
  379. $.effects.createWrapper = function (element) {
  380. var result;
  381. var oldCSS = $.fn.css;
  382. $.fn.css = function (size) {
  383. var _element = this;
  384. var hasOwn = Object.prototype.hasOwnProperty;
  385. return _element === element &amp;&amp; hasOwn.call(size, "width") &amp;&amp; hasOwn.call(size, "height") &amp;&amp; _element || oldCSS.apply(this, arguments);
  386. };
  387. result = old.apply(this, arguments);
  388. $.fn.css = oldCSS;
  389. return result;
  390. };
  391. }
  392. return oldEffect.apply(this, arguments);
  393. };
  394. })();
  395. }
  396. }</code></pre>
  397. </article>
  398. </section>
  399. </div>
  400. <nav>
  401. <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="BlockPy.html">BlockPy</a></li><li><a href="BlockPyCorgis.html">BlockPyCorgis</a></li><li><a href="BlockPyDialog.html">BlockPyDialog</a></li><li><a href="BlockPyEditor.html">BlockPyEditor</a></li><li><a href="BlockPyEngine.html">BlockPyEngine</a></li><li><a href="BlockPyEnglish.html">BlockPyEnglish</a></li><li><a href="BlockPyFeedback.html">BlockPyFeedback</a></li><li><a href="BlockPyHistory.html">BlockPyHistory</a></li><li><a href="BlockPyPresentation.html">BlockPyPresentation</a></li><li><a href="BlockPyPrinter.html">BlockPyPrinter</a></li><li><a href="BlockPyServer.html">BlockPyServer</a></li><li><a href="BlockPyToolbar.html">BlockPyToolbar</a></li><li><a href="LocalStorageWrapper.html">LocalStorageWrapper</a></li><li><a href="PythonToBlocks.html">PythonToBlocks</a></li></ul><h3>Global</h3><ul><li><a href="global.html#BlockPyInterface">BlockPyInterface</a></li><li><a href="global.html#cloneNode">cloneNode</a></li><li><a href="global.html#encodeHTML">encodeHTML</a></li><li><a href="global.html#expandArray">expandArray</a></li><li><a href="global.html#EXTENDED_ERROR_EXPLANATION">EXTENDED_ERROR_EXPLANATION</a></li><li><a href="global.html#indent">indent</a></li><li><a href="global.html#instructor_module">instructor_module</a></li><li><a href="global.html#prettyPrintDateTime">prettyPrintDateTime</a></li><li><a href="global.html#randomInteger">randomInteger</a></li><li><a href="global.html#set_button_loaded">set_button_loaded</a></li><li><a href="global.html#timerGuard">timerGuard</a></li></ul>
  402. </nav>
  403. <br class="clear">
  404. <footer>
  405. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Sun Mar 26 2017 09:45:03 GMT-0400 (Eastern Daylight Time)
  406. </footer>
  407. <script> prettyPrint(); </script>
  408. <script src="scripts/linenumber.js"> </script>
  409. </body>
  410. </html>