From 7e46af3d4225f54069558054c2f4b705f719d268 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 20 Jan 2015 15:21:11 -0500 Subject: [PATCH] Major refactor of Bonfire.jade to pull out all JS and put into framework.js, resizing font to 14px and setting to Ubuntu Mono, permanently change layout of bonfire.jade to 2 column responsive --- public/css/main.less | 18 +- public/js/lib/bonfire/framework.js | 265 +++++++++++++++++++++ public/js/lib/codemirror/theme/monokai.css | 2 +- views/bonfire/bonfire.jade | 260 +------------------- 4 files changed, 275 insertions(+), 270 deletions(-) create mode 100644 public/js/lib/bonfire/framework.js diff --git a/public/css/main.less b/public/css/main.less index b8b623428c..226d752449 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -525,15 +525,11 @@ thead { } } -form.code div { - border-radius: 5px; -} form.code span { - font-size: 16px; - font-family: "Courier New", "Lucida Console", monospace; + font-size: 14px; + font-family: "Ubuntu Mono"; padding-bottom: 0px; margin-bottom: 0px; - } #mainEditorPanel .panel-body { @@ -545,8 +541,8 @@ form.code span { } //uncomment this to see the dimensions of all elements outlined in red -//* { -// border-color: red; -// border-width: 1px; -// border-style: solid; -//} +* { + border-color: red; + border-width: 1px; + border-style: solid; +} diff --git a/public/js/lib/bonfire/framework.js b/public/js/lib/bonfire/framework.js new file mode 100644 index 0000000000..86bbefffc4 --- /dev/null +++ b/public/js/lib/bonfire/framework.js @@ -0,0 +1,265 @@ +var widgets = []; +var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { + lineNumbers: true, + mode: "javascript", + theme: 'monokai', + runnable: true, + lint: true, + matchBrackets: true, + autoCloseBrackets: true, + cursorHeight: 0.85, + lineWrapping: true, + gutters: ["CodeMirror-lint-markers"], + onKeyEvent: doLinting, + extraKeys : { + "Ctrl-Enter" : function() { + submit(myCodeMirror.getValue()); + return false; + } + } +}); +var editor = myCodeMirror; +myCodeMirror.setValue('/*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\n\n' + +'function test() {\n' + +' return [1,2,3].map(function(elem) {\n' + +' return elem * elem;\n' + +' });\n' + +'}\n\n' + +'test();'); +myCodeMirror.setSize("100%", 500); + +var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { + lineNumbers: false, + mode: "text", + theme: 'monokai', + readOnly: 'nocursor', + lineWrapping: true +}); +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 doLinting = function () { + 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 () { + $('#codeOutput').empty(); + var js = myCodeMirror.getValue(); + submit(js); +}); + +var assert = chai.assert; +var testResults = []; +$('#runTests').on('click', function () { + clearTestOutput(); + var testCaseList = [], + jsCode = myCodeMirror.getValue(); + getTestSuite().each(function () { + testCaseList.push([$(this).data("input"), $(this).data("output"), $(this)]); + }); + testCaseList.forEach(function (input) { + var testCode = jsCode + "\n\n" + input[0] + ";"; + //TODO use plugin for this with the rest as a callback? + var output = eval(testCode); + testEquality(output, input); + }); + // some timeout here? + if (testResults.length === testCaseList.length) { + var sum = testResults.reduce(function (a, b) { + return a + b + }); + prependTestOutput("======Testing========\n" + Math.round(100 * sum / testResults.length) + "% tests passed\n"); + } +}); +var testEquality = function (output, input) { + try { + switch (typeof output) { + case 'object': + assert.deepEqual(output, input[1]); + break; + case 'string': + assert(output.localeCompare(input[1])); + break + default: + assert.equal(output, input[1]); + } + appendTestOutput("\n" + createTestString(input[0], input[1]) + "\nTest passed!\n"); + input[2].css("background-color", "rgba(0,255,0,.2)"); + testResults.push(1); + } catch (err) { + input[2].css("background-color", "rgba(255,0,0,.2)"); + appendTestOutput(createTestString(input[0], input[1])); + appendTestOutput("Test failed: \nOutput was: " + output + "\nType of output was: " + (typeof output)); + testResults.push(0); + } +}; +//$('#sideBySide').on('click', function () { +// var main = $('#mainEditorPanel'); +// if (main.hasClass('col-md-12')) { +// replaceColClz(main, 'md', 12, 6); +// replaceColClz(main, 'sm', 12, 6); +// $(this).text("Original Layout") +// } else { +// replaceColClz(main, 'md', 6, 12); +// replaceColClz(main, 'sm', 6, 12); +// $(this).text("Tests side by side") +// } +//}); +//var replaceColClz = function (elt, size, oldVal, newVal) { +// elt.removeClass('col-' + size + '-' + oldVal); +// elt.addClass('col-' + size + '-' + newVal); +//}; +//var getTestSuite = function () { +// return $('#testSuite').find('li'); +//}; +//var clearTestOutput = function () { +// testOutput.setValue(""); +//}; +var appendTestOutput = function (msg) { + writeToTest(msg, CodeMirror.Pos(editor.lastLine())); +}; +var prependTestOutput = function (msg) { + writeToTest(msg, CodeMirror.Pos(editor.firstLine())); +}; +var writeToTest = function (msg, location) { + testOutput.replaceRange("\n" + msg, location); +}; +//$('#addTest').on('click', function () { +// var functionName = $('#testFunctionName option:selected').text(); +// var inputs = []; +// var output; +// $('#testInputs').find('input').each(function () { +// if ($(this).val() != null && $(this).val().length !== 0) { +// inputs.push($(this).val()); +// } else { +// //var keepGoing = prompt("You have submitted a test with empty input. Enter yes to continue."); +// if (/yes/.test(keepGoing.toLowerCase())) { +// inputs.push($(this).val()); +// } else { +// return; +// } +// } +// }); +// output = $('#testOutputs').find('input').val(); +// var functionCall = functionName + "(" + inputs.join(",") + ")"; +// var test = document.createElement("li"); +// $(test) +// .addClass('list-group-item') +// .addClass('well') +// .addClass('well-sm') +// .attr({"data-input": functionCall, "data-output": output}) +// .html(createTestString(functionCall, output)) +// .appendTo($('#testSuite')); +// var closeLink = document.createElement('i'); +// var closeSpan = document.createElement('span'); +// $(closeSpan) +// .addClass("glyphicon glyphicon-remove-sign") +// //.css("float", "right") +// .click(function () { +// //var input = prompt("This will remove the test permanently.\n If you want to do this, type delete"); +// if (/delete/.test(input.toLowerCase())) { +// $(this).parent().remove(); +// } +// }).appendTo($(test)); +// //blank out the form +// $("#testCreateForm").find("input[type=text]").val(""); +//}); +//var createTestString = function (inputs, output) { +// return "Input: " + inputs + "\nExpect:" + output; +//}; +//var testOutput = CodeMirror.fromTextArea(document.getElementById("testOutput"), { +// lineNumbers: false, +// mode: "javascript", +// theme: 'monokai', +// readOnly: 'nocursor' +//}); +//testOutput.setSize("100%", 200); +//var createOptions = function (re, code) { +// var m = re.exec(code); +// while (m != null) { +// var functionName = m[1]; +// if (functionName !== undefined) { +// var option = document.createElement('option'); +// $(option) +// .html(functionName) +// .attr({"data-args": m[2]}) +// .appendTo($('#testFunctionName')); +// } +// m = re.exec(code); +// } +//}; +$('#testFunctionName').on('change', function () { + $('#testInputs').children().remove(); + $('#testOutputs').children().remove(); + var args = $('#testFunctionName option:selected').data("args"); + var argArray = args.split(","); + argArray.forEach(function (arg) { + if (arg.length > 0) { + createInputField('#testInputs', arg); + } + }); + createInputField('#testOutputs', 'Expected output'); +}); +var createInputField = function (className, arg) { + var inputDiv = document.createElement('div'); + $(inputDiv) + .addClass("control-group") + .appendTo($(className)); + var inputLabel = document.createElement('label'); + $(inputLabel) + .attr("for", "inputs") + .html(arg) + .addClass("col-xs-4 control-label") + .appendTo($(inputDiv)); + var textDiv = document.createElement('div'); + $(textDiv) + .addClass("col-xs-8 controls") + .appendTo($(inputDiv)); + var inputArea = document.createElement('input'); + $(inputArea) + .attr("type", "text") + .addClass("form-control") + .appendTo($(inputDiv)); + $(document.createElement("br")).appendTo($(textDiv)); +}; +$('#testFunctionName').on('focus', function () { + $('#testFunctionName').children().remove(); + var blankOpt = document.createElement("option"); + $(blankOpt).addClass("selected").appendTo($('#testFunctionName')); + var re = /function\s+(\w+)\s*\(([\w\s,]*)\)/g; + var code = myCodeMirror.getValue(); + createOptions(re, code); + re = /var (\w+)\s*=\s*function\s*\(([\s\w,]*)\)/g; + createOptions(re, code); +}); +$('#hideTestCreate').on('click', function () { + var testForm = $("#testCreateForm"); + if (testForm.is(":visible")) { + testForm.hide(); + $(this).text("Create more tests"); + } else { + testForm.show(); + $(this).text("Hide test creation dialogue") + } +}); \ No newline at end of file diff --git a/public/js/lib/codemirror/theme/monokai.css b/public/js/lib/codemirror/theme/monokai.css index 548d2dfff6..74c3d2d903 100644 --- a/public/js/lib/codemirror/theme/monokai.css +++ b/public/js/lib/codemirror/theme/monokai.css @@ -1,6 +1,6 @@ /* Based on Sublime Text's Monokai theme */ -.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2;} +.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2; border-radius: 5px;} .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} .cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} .cm-s-monokai .CodeMirror-guttermarker { color: white; } diff --git a/views/bonfire/bonfire.jade b/views/bonfire/bonfire.jade index 84f9d3c425..4463d7656d 100644 --- a/views/bonfire/bonfire.jade +++ b/views/bonfire/bonfire.jade @@ -10,6 +10,7 @@ block content link(rel='stylesheet', href='/js/lib/codemirror/lib/codemirror.css') link(rel='stylesheet', href='/js/lib/codemirror/addon/lint/lint.css') link(rel='stylesheet', href='/js/lib/codemirror/theme/monokai.css') + link(rel="stylesheet", href="http://fonts.googleapis.com/css?family=Ubuntu+Mono") script(src='/js/lib/codemirror/mode/javascript/javascript.js') script(src='js/lib/jailed/jailed.js') script(src='/js/lib/bonfire/bonfire.js') @@ -39,261 +40,4 @@ block content #testOutputs.form-group br - script. - var widgets = []; - var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { - lineNumbers: true, - mode: "javascript", - theme: 'monokai', - runnable: true, - lint: true, - matchBrackets: true, - autoCloseBrackets: true, - cursorHeight: 0.85, - lineWrapping: true, - gutters: ["CodeMirror-lint-markers"], - onKeyEvent: doLinting - }); - var editor = myCodeMirror; - myCodeMirror.setValue('/*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\n\n' + - 'function test() {\n' + - ' return [1,2,3].map(function(elem) {\n' + - ' return elem * elem;\n' + - ' });\n' + - '}\n\n' + - 'test();'); - myCodeMirror.setSize("100%", 500); - - var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { - lineNumbers: false, - mode: "text", - theme: 'monokai', - readOnly: 'nocursor' - }); - 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 doLinting = function () { - 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 () { - $('#codeOutput').empty(); - var js = myCodeMirror.getValue(); - submit(js); - }); - var assert = chai.assert; - var testResults = []; - $('#runTests').on('click', function () { - clearTestOutput(); - var testCaseList = [], - jsCode = myCodeMirror.getValue(); - getTestSuite().each(function () { - testCaseList.push([$(this).data("input"), $(this).data("output"), $(this)]); - }); - testCaseList.forEach(function (input) { - var testCode = jsCode + "\n\n" + input[0] + ";"; - //TODO use plugin for this with the rest as a callback? - var output = eval(testCode); - testEquality(output, input); - }); - // some timeout here? - if (testResults.length === testCaseList.length) { - var sum = testResults.reduce(function (a, b) { - return a + b - }); - prependTestOutput("======Testing========\n" + Math.round(100 * sum / testResults.length) + "% tests passed\n"); - } - }); - var testEquality = function (output, input) { - try { - switch (typeof output) { - case 'object': - assert.deepEqual(output, input[1]); - break; - case 'string': - assert(output.localeCompare(input[1])); - break - default: - assert.equal(output, input[1]); - } - appendTestOutput("\n" + createTestString(input[0], input[1]) + "\nTest passed!\n"); - input[2].css("background-color", "rgba(0,255,0,.2)"); - testResults.push(1); - } catch (err) { - input[2].css("background-color", "rgba(255,0,0,.2)"); - appendTestOutput(createTestString(input[0], input[1])); - appendTestOutput("Test failed: \nOutput was: " + output + "\nType of output was: " + (typeof output)); - testResults.push(0); - } - }; - $('#sideBySide').on('click', function () { - var main = $('#mainEditorPanel'); - if (main.hasClass('col-md-12')) { - replaceColClz(main, 'md', 12, 6); - replaceColClz(main, 'sm', 12, 6); - $(this).text("Original Layout") - } else { - replaceColClz(main, 'md', 6, 12); - replaceColClz(main, 'sm', 6, 12); - $(this).text("Tests side by side") - } - }); - var replaceColClz = function (elt, size, oldVal, newVal) { - elt.removeClass('col-' + size + '-' + oldVal); - elt.addClass('col-' + size + '-' + newVal); - }; - var getTestSuite = function () { - return $('#testSuite').find('li'); - }; - var clearTestOutput = function () { - testOutput.setValue(""); - }; - var appendTestOutput = function (msg) { - writeToTest(msg, CodeMirror.Pos(editor.lastLine())); - }; - var prependTestOutput = function (msg) { - writeToTest(msg, CodeMirror.Pos(editor.firstLine())); - }; - var writeToTest = function (msg, location) { - testOutput.replaceRange("\n" + msg, location); - }; - $('#addTest').on('click', function () { - var functionName = $('#testFunctionName option:selected').text(); - var inputs = []; - var output; - $('#testInputs').find('input').each(function () { - if ($(this).val() != null && $(this).val().length !== 0) { - inputs.push($(this).val()); - } else { - //var keepGoing = prompt("You have submitted a test with empty input. Enter yes to continue."); - if (/yes/.test(keepGoing.toLowerCase())) { - inputs.push($(this).val()); - } else { - return; - } - } - }); - output = $('#testOutputs').find('input').val(); - var functionCall = functionName + "(" + inputs.join(",") + ")"; - var test = document.createElement("li"); - $(test) - .addClass('list-group-item') - .addClass('well') - .addClass('well-sm') - .attr({"data-input": functionCall, "data-output": output}) - .html(createTestString(functionCall, output)) - .appendTo($('#testSuite')); - var closeLink = document.createElement('i'); - var closeSpan = document.createElement('span'); - $(closeSpan) - .addClass("glyphicon glyphicon-remove-sign") - //.css("float", "right") - .click(function () { - //var input = prompt("This will remove the test permanently.\n If you want to do this, type delete"); - if (/delete/.test(input.toLowerCase())) { - $(this).parent().remove(); - } - }).appendTo($(test)); - //blank out the form - $("#testCreateForm").find("input[type=text]").val(""); - }); - var createTestString = function (inputs, output) { - return "Input: " + inputs + "\nExpect:" + output; - }; - var testOutput = CodeMirror.fromTextArea(document.getElementById("testOutput"), { - lineNumbers: false, - mode: "javascript", - theme: 'monokai', - readOnly: 'nocursor' - }); - testOutput.setSize("100%", 200); - var createOptions = function (re, code) { - var m = re.exec(code); - while (m != null) { - var functionName = m[1]; - if (functionName !== undefined) { - var option = document.createElement('option'); - $(option) - .html(functionName) - .attr({"data-args": m[2]}) - .appendTo($('#testFunctionName')); - } - m = re.exec(code); - } - }; - $('#testFunctionName').on('change', function () { - $('#testInputs').children().remove(); - $('#testOutputs').children().remove(); - var args = $('#testFunctionName option:selected').data("args"); - var argArray = args.split(","); - argArray.forEach(function (arg) { - if (arg.length > 0) { - createInputField('#testInputs', arg); - } - }); - createInputField('#testOutputs', 'Expected output'); - }); - var createInputField = function (className, arg) { - var inputDiv = document.createElement('div'); - $(inputDiv) - .addClass("control-group") - .appendTo($(className)); - var inputLabel = document.createElement('label'); - $(inputLabel) - .attr("for", "inputs") - .html(arg) - .addClass("col-xs-4 control-label") - .appendTo($(inputDiv)); - var textDiv = document.createElement('div'); - $(textDiv) - .addClass("col-xs-8 controls") - .appendTo($(inputDiv)); - var inputArea = document.createElement('input'); - $(inputArea) - .attr("type", "text") - .addClass("form-control") - .appendTo($(inputDiv)); - $(document.createElement("br")).appendTo($(textDiv)); - }; - $('#testFunctionName').on('focus', function () { - $('#testFunctionName').children().remove(); - var blankOpt = document.createElement("option"); - $(blankOpt).addClass("selected").appendTo($('#testFunctionName')); - var re = /function\s+(\w+)\s*\(([\w\s,]*)\)/g; - var code = myCodeMirror.getValue(); - createOptions(re, code); - re = /var (\w+)\s*=\s*function\s*\(([\s\w,]*)\)/g; - createOptions(re, code); - }); - $('#hideTestCreate').on('click', function () { - var testForm = $("#testCreateForm"); - if (testForm.is(":visible")) { - testForm.hide(); - $(this).text("Create more tests"); - } else { - testForm.show(); - $(this).text("Hide test creation dialogue") - } - }); \ No newline at end of file + script(src='/js/lib/bonfire/framework.js')