-
Notifications
You must be signed in to change notification settings - Fork 183
How the live editor works
The live editor consists of two main components:
The code editor is on the left hand side, and by default, it is powered by the ACE editor. Other editors can also be plugged in (like the structured-blocks editor).
The output is on the right hand side in an iframe, and it executes code (either ProcessingJS code, HTML/CSS/JS, or SQL) . On Khan Academy, that iframe is on a separate domain so that users can't access Khan Academy user credentials. Be aware of that if running this on an authenticated domain.
Everything is kicked off by instantiating a new LiveEditor()
on a page, giving it a div to live inside, and specifying paths if necessary:
var options = {
el: $("#sample-live-editor"),
execDir: "../../js/exec/",
execFile: "../../exec.html",
externalsDir: "../../external/",
imagesDir: "../../images/"
};
window.liveEditor = new LiveEditor(options);
This is the life cycle of a code edit for the ProcessingJS environment, there are some differences in HTML/CSS and SQL:
- When the user types in code, that triggers the ACE editor's "change" event. See
LiveEditor.bind
in live-editor.js - LiveEditor posts a message to the exec iframe with the current code. See
LiveEditor.runCode
in live-editor.js - When the exec iframe receives a message with code in it, it will send it through a series of steps to check for errors. See
Output.runCode
in output.js - It will first send the code to the JSHint worker. That worker will respond back with any encountered errors.
- It will then send the code through BabyHint (not in a worker, as it's not super intensive), and will merge together JSHint errors and BabyHint errors.
- It will then send the code and any associated tests (like for coding challenges) to the test worker.
- If there are hint-related errors, it will display them using
Output.handleError
. Otherwise, it will proceed to sending it to the associated output mechanism. - When the code gets sent to
PJSOutput
, it goes through a ProcessingJS-specific process. SeePJSOutput.runCode
in output.js - It first caches any images found in the code using the PJSResourceCache and executes the code using
PJSOutput.injectCode
- The CodeInjector should have wrapped LoopProtector around any loops, so it will pop up a runtime error after a few seconds if it perceives long-running loops.
See js/workers/pjs/jshint-worker.js, external/jshint/jshint.js, external/es5-shim/es5-shim.js
The JSHint worker delay loads its dependencies (ES5-shim and JSHint), due to the need on Khan Academy to communicate the user's language. Our JSHint is a custom fork both because it has translated message strings and has more friendly, helpful messages.
The BabyHint library does an additional series of checks that JSHint does not do:
- It checks for misspellings of functions/variable names, and throws an error suggesting the correct spelling based on ProcessingJS keywords and program variables.
- It throws an error if the user from using banned properties like document and location.
- It throws an error if the user declares a function in the
function name(){}
style instead ofvar name=function(){}
, as we only support the latter. - It checks for trailing equal signs, a common error.
- It checks for no whitespace after
var
and before a variable name, a common error.
See: js/workers/pjs/test-worker.js, js/output/shared/output-tester.js, external/structuredjs/structured.js
This worker is used for checking user code against specified tests. This is used in the coding challenges in the Khan Academy curriculum, to help the student practice what they've learnt and get feedback about how well they're doing.
The tests are written using an interface defined by OutputTester
in output-tester.js, and detailed in this guide for challenge creators. Basically, they look for particular structures in the code using the StructuredJS library, and decide to either accept the code or give a hint about what's wrong with the particular structure.
Once the worker gets the results from OutputTester
, it will return an array of test results and any errors encountered along the way.
See: js/output/pjs/pjs-output.js
Once the code has gone through all the checks and is error-free, it's ready to be executed by Output.injectCode
. If it's the first time the code has been injected, it's just executed normally. If there's a code change and another call to Output.injectCode
, then the function decides what aspects of the code to dynamically inject. That way, users can change the contents of the draw function and just see the effect of that on the current state of animation.
For more details, watch this talk from EmpireJS or read the extensive comments in Output.injectCode
.