diff --git a/public/js/lib/coursewares/coursewaresFramework.js b/public/js/lib/coursewares/coursewaresFramework.js new file mode 100644 index 0000000000..344d1ac335 --- /dev/null +++ b/public/js/lib/coursewares/coursewaresFramework.js @@ -0,0 +1,232 @@ +/** + * Created by nathanleniz on 2/2/15. + */ + +var widgets = []; +var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { + lineNumbers: true, + mode: "html-mixed", + theme: 'monokai', + runnable: true, + lint: true, + matchBrackets: true, + autoCloseBrackets: true, + scrollbarStyle: 'null', + lineWrapping: true, + gutters: ["CodeMirror-lint-markers"], + onKeyEvent: doLinting, + extraKeys : { + "Ctrl-Enter" : function() { + bonfireExecute(); + return false; + } + } +}); +var editor = myCodeMirror; +editor.setSize("100%", "auto"); + + +// Default value for editor if one isn't provided in (i.e. a challenge) +var nonChallengeValue = '/*Welcome to Bonfire, Free Code Camp\'s future CoderByte replacement.\n' + + 'Please feel free to use Bonfire as an in-browser playground and linting tool.\n' + + 'Note that you can also write tests using Chai.js by using the keywords assert and expect */\n\n' + + 'function test() {\n' + + ' assert(2 !== 3, "2 is not equal to 3");\n' + + ' return [1,2,3].map(function(elem) {\n' + + ' return elem * elem;\n' + + ' });\n' + + '}\n' + + 'expect(test()).to.be.a("array");\n\n' + + 'assert.deepEqual(test(), [1,4,9]);\n\n' + + 'var foo = test();\n' + + 'foo.should.be.a("array");\n\n' + + 'test();\n'; + +var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { + lineNumbers: false, + mode: "text", + theme: 'monokai', + readOnly: 'nocursor', + lineWrapping: true +}); +codeOutput.setValue('/**\n' + +' * Your output will go here.\n' + ' * Console.log() -type statements\n' + +' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' + +' */'); +codeOutput.setSize("100%", "100%"); +var info = editor.getScrollInfo(); +var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top; +if (info.top + info.clientHeight < after) + editor.scrollTo(null, after - info.clientHeight + 3); + +var editorValue; + + +var challengeSeed = challengeSeed || null; +var tests = tests || []; +var challengeEntryPoint = challengeEntryPoint || null; + + +if (challengeSeed !== null) { + editorValue = challengeSeed + '\n\n' + challengeEntryPoint; +} else { + editorValue = nonChallengeValue; +} + + +myCodeMirror.setValue(editorValue); + +function doLinting () { + editor.operation(function () { + for (var i = 0; i < widgets.length; ++i) + editor.removeLineWidget(widgets[i]); + widgets.length = 0; + JSHINT(editor.getValue()); + for (var i = 0; i < JSHINT.errors.length; ++i) { + var err = JSHINT.errors[i]; + if (!err) continue; + var msg = document.createElement("div"); + var icon = msg.appendChild(document.createElement("span")); + icon.innerHTML = "!!"; + icon.className = "lint-error-icon"; + msg.appendChild(document.createTextNode(err.reason)); + msg.className = "lint-error"; + widgets.push(editor.addLineWidget(err.line - 1, msg, { + coverGutter: false, + noHScroll: true + })); + } + }); +}; + +$('#submitButton').on('click', function () { + bonfireExecute(); +}); + +function bonfireExecute() { + userTests= null; + $('#codeOutput').empty(); + var userJavaScript = myCodeMirror.getValue(); + userJavaScript = removeComments(userJavaScript); + userJavaScript = scrapeTests(userJavaScript); + // simple fix in case the user forgets to invoke their function + if (challengeEntryPoint) { + userJavaScript = challengeEntryPoint + ' ' + userJavaScript; + } + submit(userJavaScript, function(cls, message) { + if (cls) { + codeOutput.setValue(message.error); + runTests('Error', null); + } else { + codeOutput.setValue(message.output); + message.input = removeLogs(message.input); + runTests(null, message); + } + }); +} + + +var userTests; +var testSalt = Math.random(); + + +var scrapeTests = function(userJavaScript) { + + // insert tests from mongo + for (var i = 0; i < tests.length; i++) { + userJavaScript += '\n' + tests[i]; + } + + var counter = 0; + var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/); + var match = regex.exec(userJavaScript); + while (match != null) { + var replacement = '//' + counter + testSalt; + userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length); + + if (!userTests) { + userTests= []; + } + userTests.push({"text": match[0], "line": counter, "err": null}); + counter++; + match = regex.exec(userJavaScript); + } + + return userJavaScript; +}; + +function removeComments(userJavaScript) { + var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); + return userJavaScript.replace(regex, ''); +} + +function removeLogs(userJavaScript) { + return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); +} + +var pushed = false; +var createTestDisplay = function() { + if (pushed) { + userTests.pop(); + } + for (var i = 0; i < userTests.length;i++) { + var test = userTests[i]; + var testDoc = document.createElement("div"); + if (test.err != null) { + $(testDoc) + .html("