Merge pull request #47 from FreeCodeCamp/bonfire-challenge-framework

Bonfire challenge framework
This commit is contained in:
Free Code Camp
2015-01-23 00:11:37 -08:00
19 changed files with 1096 additions and 853 deletions

View File

@ -56,7 +56,7 @@ LINKEDIN_SECRET='LINKEDIN APP SECRET'
```bash ```bash
# Seed your database with the challenges # Seed your database with the challenges
node seed_data/seed_challenges.js node seed_data/seed.js
# start the application # start the application
gulp gulp

8
app.js
View File

@ -250,10 +250,10 @@ app.get(
app.all('/account', passportConf.isAuthenticated); app.all('/account', passportConf.isAuthenticated);
app.get('/account/api', userController.getAccountAngular); app.get('/account/api', userController.getAccountAngular);
app.get('/bonfire', bonfireController.index); app.get('/bonfire', bonfireController.index);
//app.get( app.get(
// '/bonfire/:bonfireNumber', '/bonfire/:bonfireNumber',
// bonfireController.returnBonfire bonfireController.returnBonfire
//); );
// Unique Check API route // Unique Check API route
app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername);

View File

@ -1,68 +1,78 @@
var _ = require('lodash'), var _ = require('lodash'),
debug = require('debug')('freecc:cntr:bonfires'); debug = require('debug')('freecc:cntr:bonfires'),
// bonfire = require('./../models/Bonfire'); Bonfire = require('./../models/Bonfire');
/** /**
* Bonfire controller * Bonfire controller
*/ */
var highestBonfireNumber = 10;
exports.index = function(req, res) { exports.index = function(req, res) {
res.render('bonfire/bonfire.jade', { res.render('bonfire/bonfire.jade', {
title: 'Learn to code with Bonfire' title: 'Learn to code with Bonfire'
}); });
//Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) {
// if (err) { Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) {
// debug('bonfire err: ', err); if (err) {
// next(err); debug('bonfire err: ', err);
// } next(err);
//}); }
});
}; };
//exports.returnBonfire = function(req, res, next) { exports.returnBonfire = function(req, res, next) {
// var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; var bonfireNumber = parseInt(req.params.bonfireNumber) || 0;
// var verbs = [ var verbs = [
// 'ACED', 'ACED',
// 'NAILED', 'NAILED',
// 'ROCKED', 'ROCKED',
// 'SCORCHED', 'SCORCHED',
// 'DEVASTATED', 'DEVASTATED',
// 'OWNED', 'OWNED',
// 'CRUSHED', 'CRUSHED',
// 'CONQUERED', 'CONQUERED',
// 'KILLED', 'KILLED',
// 'SHREDDED', 'SHREDDED',
// 'ANNIHILATED', 'ANNIHILATED',
// 'NUKED' 'NUKED'
// ]; ];
// var phrases = [ var phrases = [
// "Shout it from on top of a mountain", "Shout it from on top of a mountain",
// "Tell everyone and their dogs", "Tell everyone and their dogs",
// "Show them. Show them all!", "Show them. Show them all!",
// "Inspire your friends", "Inspire your friends",
// "Tell the world of your greatness", "Tell the world of your greatness",
// "Look accomplished on social media", "Look accomplished on social media",
// "Share news of your grand endeavor", "Share news of your grand endeavor",
// "Establish your alibi for the past two hours", "Establish your alibi for the past two hours",
// "Prove to mom that computers aren't just for games" "Prove to mom that computers aren't just for games"
// ]; ];
// if (bonfireNumber > 53) { bonfireNumber = 0; }
// Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) { if (bonfireNumber > highestBonfireNumber) { bonfireNumber = 0; }
// if (err) { Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) {
// debug('bonfire err: ', err); debug(bonfire[bonfireNumber].challengeEntryPoint);
// next(err); if (err) {
// } debug('bonfire err: ', err);
// res.render('bonfires/show', { next(err);
// //title: 'bonfire: ' + c[bonfireNumber].name, }
// //name: c[bonfireNumber].name, res.render('bonfire/show', {
// //video: c[bonfireNumber].video, title: bonfire[bonfireNumber].name,
// //time: c[bonfireNumber].time, name: bonfire[bonfireNumber].name,
// //steps: c[bonfireNumber].steps, number: bonfire[bonfireNumber].bonfireNumber,
// //number: bonfireNumber, difficulty: bonfire[bonfireNumber].difficulty,
// //categories: c[bonfireNumber].category, description: bonfire[bonfireNumber].description,
// //cc: req.user ? req.user.bonfiresHash : undefined, publicTests: bonfire[bonfireNumber].publicTests,
// //points: req.user ? req.user.points : undefined, privateTests: bonfire[bonfireNumber].privateTests,
// //verb: verbs[Math.floor(Math.random() * verbs.length)], challengeSeed: bonfire[bonfireNumber].challengeSeed,
// //phrase: phrases[Math.floor(Math.random() * phrases.length)], challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint,
// //bonfires: c challengeEntryPointNegate: bonfire[bonfireNumber].challengeEntryPointNegate,
// });
// }); //cc: req.user ? req.user.bonfiresHash : undefined,
//}; //points: req.user ? req.user.points : undefined,
//verb: verbs[Math.floor(Math.random() * verbs.length)],
//phrase: phrases[Math.floor(Math.random() * phrases.length)],
bonfires: bonfire
});
});
};

View File

@ -6,6 +6,8 @@ var _ = require('lodash'),
debug = require('debug')('freecc:cntr:challenges'), debug = require('debug')('freecc:cntr:challenges'),
Challenge = require('./../models/Challenge'); Challenge = require('./../models/Challenge');
var highestChallengeNumber = 53;
exports.returnChallenge = function(req, res, next) { exports.returnChallenge = function(req, res, next) {
var challengeNumber = parseInt(req.params.challengeNumber) || 0; var challengeNumber = parseInt(req.params.challengeNumber) || 0;
var verbs = [ var verbs = [
@ -33,7 +35,7 @@ exports.returnChallenge = function(req, res, next) {
"Establish your alibi for the past two hours", "Establish your alibi for the past two hours",
"Prove to mom that computers aren't just for games" "Prove to mom that computers aren't just for games"
]; ];
if (challengeNumber > 53) { challengeNumber = 0; } if (challengeNumber > highestChallengeNumber) { challengeNumber = 0; }
Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) { Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) {
if (err) { if (err) {
debug('Challenge err: ', err); debug('Challenge err: ', err);

View File

@ -1,14 +1,27 @@
var mongoose = require('mongoose'); var mongoose = require('mongoose');
var secrets = require('../config/secrets'); var secrets = require('../config/secrets');
/**
*
* @type {exports.Schema}
*/
var bonfireSchema = new mongoose.Schema({ var bonfireSchema = new mongoose.Schema({
name: { name: {
type: String, type: String,
unique: true unique: true
}, },
link: String, difficulty: Number,
time: String, description: Array,
bonfireNumber: Number publicTests: Array,
privateTests: Array,
challengeSeed: String,
bonfireNumber: Number,
challengeEntryPoint: String,
challengeEntryPointNegate: String
}); });
module.exports = mongoose.model('Bonfire', bonfireSchema); module.exports = mongoose.model('Bonfire', bonfireSchema);

View File

@ -0,0 +1,14 @@
var mongoose = require('mongoose');
var secrets = require('../config/secrets');
var bonfireCompletionSchema = new mongoose.Schema({
dateCompleted: Number,
completedWith: String,
bonfireNumber: {
bonfireNumber: Number,
bonfireId: String
},
solution: String
});
module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema);

View File

@ -352,7 +352,8 @@ var userSchema = new mongoose.Schema({
} }
}, },
resetPasswordToken: String, resetPasswordToken: String,
resetPasswordExpires: Date resetPasswordExpires: Date,
bonfires: Array
}); });
/** /**

View File

@ -569,6 +569,13 @@ div.CodeMirror-scroll {
text-size: 250px; text-size: 250px;
} }
.bonfire-instructions p {
padding: 0;
}
.bonfire-instructions {
margin-bottom: 2px;
}
//uncomment this to see the dimensions of all elements outlined in red //uncomment this to see the dimensions of all elements outlined in red
//* { //* {
// border-color: red; // border-color: red;

View File

@ -0,0 +1,228 @@
var widgets = [];
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
lineNumbers: true,
mode: "javascript",
theme: 'monokai',
runnable: true,
lint: true,
matchBrackets: true,
autoCloseBrackets: true,
cursorHeight: 1,
scrollbarStyle: 'null',
lineWrapping: true,
gutters: ["CodeMirror-lint-markers"],
onKeyEvent: doLinting,
extraKeys : {
"Ctrl-Enter" : function() {
bonfireExecute();
return false;
}
}
});
var editor = myCodeMirror;
// 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\n' +
' 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' +
'test();';
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. Console.log() -type statements\n' +
' * will appear in your browser\'s 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 publicTests = publicTests || [];
var privateTests = privateTests || [];
var challengeEntryPoint = challengeEntryPoint || null;
var challengeEntryPointNegate = challengeEntryPointNegate || 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() {
tests = null;
$('#codeOutput').empty();
var userJavaScript = myCodeMirror.getValue();
userJavaScript = removeComments(userJavaScript);
userJavaScript = scrapeTests(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 replaceQuotesInTests = function() {
tests.forEach(function(elt, ix, arr) {
arr[ix].text = arr[ix].text.replace(/\"/g,'\'');
});
};
var tests;
var testSalt = Math.random();
var scrapeTests = function(userJavaScript) {
for (var i = 0; i < publicTests.length; i++) {
userJavaScript += '\n' + publicTests[i];
}
for (var i = 0; i < privateTests.length; i++) {
userJavaScript += '\n' + privateTests[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 (!tests) tests = [];
tests.push({"text": match[0], "line": counter, "err": null});
counter++;
match = regex.exec(userJavaScript);
}
if (tests) replaceQuotesInTests();
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, '');
return userJavaScript;
}
var pushed = false;
var createTestDisplay = function() {
if (pushed) {
tests.pop();
}
for (var i = 0; i < tests.length;i++) {
var test = tests[i];
var testDoc = document.createElement("li");
$(testDoc)
.addClass('list-group-item')
.addClass('well img-rounded')
.addClass('well-sm')
if (test.err != null) {
$(testDoc)
.html(test.text + "\n" + test.err)
.css("background-color", 'rgba(255,0,0,.2)')
.prependTo($('#testSuite'));
} else {
$(testDoc)
.html(test.text)
.css('background-color', 'rgba(0,255,0,.2)')
.appendTo($('#testSuite'));
}
};
};
var assert = chai.assert;
var expect = chai.expect;
var should = chai.should();
var reassembleTest = function(test, data) {
var lineNum = test.line;
var regexp = new RegExp("\/\/" + lineNum + testSalt);
return data.input.replace(regexp, test.text);
};
var runTests = function(err, data) {
pushed = false;
$('#testSuite').children().remove();
if (err && tests.length > 0) {
tests = [{text:"Program Execution Failure", err: "No tests were run."}];
createTestDisplay();
} else if (tests) {
tests.push(false);
pushed = true;
tests.forEach(function(test, ix, arr){
try {
if (test) {
var output = eval(reassembleTest(test, data));
}
} catch(error) {
arr[ix].err = error.name + ":" + error.message;
} finally {
if (!test) {
createTestDisplay();
}
}
});
}
};

View File

@ -46,6 +46,7 @@ var api = {
reset(); reset();
} else { } else {
print(null, data); print(null, data);
reset();
} }
} }
}; };
@ -66,18 +67,20 @@ var requests;
// (re)initializes the plugin // (re)initializes the plugin
var reset = function() { var reset = function() {
requests = 0; requests = 0;
plugin = new jailed.Plugin(path+'plugin_v0.1.1.js', api); plugin = new jailed.Plugin(path+'plugin_v0.1.2.js', api);
plugin.whenDisconnected( function() { plugin.whenDisconnected( function() {
// give some time to handle the last responce // give some time to handle the last responce
setTimeout( function() { setTimeout( function() {
endLoading(); endLoading();
codeOutput.setValue('Infinite loop detected!'); console.log("resetting on fatal plugin error");
codeOutput.setValue('Infinite loop or fatal error!');
reset(); reset();
}, 10); }, 10);
}); });
}; };
// initialize everything // initialize everything
var plugin = null; var plugin = null;

View File

@ -1,378 +0,0 @@
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,
scrollbarStyle: 'null',
lineWrapping: true,
gutters: ["CodeMirror-lint-markers"],
onKeyEvent: doLinting,
extraKeys : {
"Ctrl-Enter" : function() {
bonfireExecute();
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' +
'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' +
'test();');
//myCodeMirror.setSize("100%", "100%");
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. Console.log() -type statements\n' +
' * will appear in your browser\'s 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 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
}));
}
});
};
var replaceQuotesInTests = function() {
tests.forEach(function(elt, ix, arr) {
arr[ix].text = arr[ix].text.replace(/\"/g,'\'');
});
};
var tests;
var testSalt = Math.random();
var scrapeTests = function(userJavaScript) {
var counter = 0;
var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)/);
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 (!tests) tests = [];
tests.push({"text": match[0], "line": counter, "err": null});
counter++;
match = regex.exec(userJavaScript);
}
replaceQuotesInTests();
return userJavaScript;
};
$('#submitButton').on('click', function () {
bonfireExecute();
});
function bonfireExecute() {
tests = undefined;
$('#codeOutput').empty();
var userJavaScript = myCodeMirror.getValue();
userJavaScript = scrapeTests(userJavaScript);
submit(userJavaScript, function(cls, message) {
if (cls) {
codeOutput.setValue(message.error);
runTests('Error', null);
} else {
codeOutput.setValue(message.output);
runTests(null, message);
}
});
}
var pushed = false;
var createTestDisplay = function() {
if (pushed) {
tests.pop();
}
for (var i = 0; i < tests.length;i++) {
var test = tests[i];
var testDoc = document.createElement("li");
$(testDoc)
.addClass('list-group-item')
.addClass('well img-rounded')
.addClass('well-sm')
if (test.err != null) {
$(testDoc)
.html(test.text + "\n" + test.err)
.css("background-color", 'rgba(255,0,0,.2)')
.prependTo($('#testSuite'));
} else {
$(testDoc)
.html(test.text)
.css('background-color', 'rgba(0,255,0,.2)')
.appendTo($('#testSuite'));
}
};
};
var assert = chai.assert;
var expect = chai.expect;
var reassembleTest = function(test, data) {
var lineNum = test.line;
var regexp = new RegExp("\/\/" + lineNum + testSalt);
return data.input.replace(regexp, test.text);
};
var runTests = function(err, data) {
pushed = false;
$('#testSuite').children().remove();
if (err && tests) {
tests = [{text:"Program Execution Failure", err: "No tests were run."}];
createTestDisplay();
} else if (tests) {
tests.push(false);
pushed = true;
tests.forEach(function(test, ix, arr){
try {
if (test) {
var output = eval(reassembleTest(test, data));
}
} catch(error) {
console.log(error);
arr[ix].err = error.name + ":" + error.message;
console.log(arr);
} finally {
if (!test) {
//window.setTimeout(function() {createTestDisplay()},2000);
createTestDisplay();
}
}
});
}
};
// 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")
// }
// });

60
seed_data/bonfires.json Normal file
View File

@ -0,0 +1,60 @@
[
{
"name": "Palindrome Tester",
"difficulty": 1,
"description": [
"Your job is to determine if a provided string is a palindrome.",
"The definition of a palindrome can be found at http://en.wikipedia.org/wiki/Palindrome.",
"Strings will be passed in with varying formats, such as \"racecar\", \"RaceCar\", and \"race CAR\" among others.",
"Return true if the string is a palindrome, otherwise false"
],
"publicTests": [
"expect(palindrome(\"eye\")).to.be.a(\"boolean\");",
"assert.deepEqual(palindrome(\"eye\"), true);",
"assert.deepEqual(palindrome(\"race car\"), true);",
"assert.deepEqual(palindrome(\"not a palindrome\"), false);"
],
"privateTests": [
"assert.deepEqual(palindrome(\"A man, a plan, a canal. Panama\"), true);",
"assert.deepEqual(palindrome(\"never odd or even\"), true);",
"assert.deepEqual(palindrome(\"nope\"), false);"
],
"challengeSeed": "function palindrome(str) {\n // Good luck!\n return true;\n}\n\n",
"challengeEntryPoint": "palindrome(\"eye\");",
"bonfireNumber": 1,
"challengeEntryPointNegate" : "palindrome\\([^str].*\\;"
},
{
"name": "Validate US Telephone Numbers",
"difficulty": 3,
"description": [
"The user may fill out the form field any way they choose as long as it is a valid US number. The following are all valid formats for US numbers:",
"555-555-5555, (555)555-5555, (555) 555-5555, 555 555 5555, 5555555555, 1 555 555 5555",
"For this challenge you will be presented with a string such as \"800-692-7753\" or \"8oo-six427676;laskdjf\". Your job is to validate or reject the US phone number based on any combination of the formats provided above. The area code is required. If the country code code is provided, you must confirm that the country code is \"1\". Return true if the string is a valid US phone number; otherwise false."
],
"publicTests": [
"expect(telephoneCheck(\"555-555-5555\")).to.be.a(\"boolean\");",
"assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);",
"assert.deepEqual(telephoneCheck(\"1 456 789 4444\"), true);",
"assert.deepEqual(telephoneCheck(\"123**&!!asdf#\"), false);"
],
"privateTests": [
"assert.deepEqual(telephoneCheck(\"55555555\"), false);",
"assert.deepEqual(telephoneCheck(\"(6505552368)\"), false);",
"assert.deepEqual(telephoneCheck(\"2 (757) 622-7382\"), false);",
"assert.deepEqual(telephoneCheck(\"0 (757) 622-7382\"), false);",
"assert.deepEqual(telephoneCheck(\"-1 (757) 622-7382\"), false);",
"assert.deepEqual(telephoneCheck(\"2 757 622-7382\"), false);",
"assert.deepEqual(telephoneCheck(\"10 (757) 622-7382\"), false);",
"assert.deepEqual(telephoneCheck(\"27576227382\"), false);",
"assert.deepEqual(telephoneCheck(\"(275)76227382\"), false);",
"assert.deepEqual(telephoneCheck(\"2(757)6227382\"), false);",
"assert.deepEqual(telephoneCheck(\"2(757)622-7382\"), false);"
],
"challengeSeed": "function telephoneCheck(str) {\n // Good luck!\n return true;\n}\n\n",
"challengeEntryPoint": "telephoneCheck(\"555-555-5555\");",
"bonfireNumber": 2,
"challengeEntryPointNegate" : "palindrome\\([^str].*\\;"
}
]

202
seed_data/challenge-hashes Normal file
View File

@ -0,0 +1,202 @@
/*
"aaa48de84e1ecc7c742e1124"
"ff0395860f5d3034dc0bfc94"
"7123c8c441eddfaeb5bdef0d"
"c3a4d278b9e760a0ffe8321f"
"aceca143b92049a4392a859e"
"ce9394f67d413734758e27e4"
"1369953ef6f03098cb60e2f7"
"fa229986db0716662e63163a"
"0a6207dfc9ac12f223b4e686"
"2e538b339cbcb7437e61d71f"
"21de1104b8ea7c6a382442d9"
"0aa3497514cb1d8a0ed04933"
"3a45068415f5a20a5d4a2039"
"6a73ec9b86f0e742f6c6ea76"
"06dc21940748badcdb29561e"
"0f2341ef36740b5c2b9d830c"
"dc4cc7ce1fd0418859b10e0b"
"1fcda9b5e0bf502c8b770374"
"482879c42188cde1f9ad3d8f"
"3d680bd03fd291028af57f4f"
"6b444fe5aa97359238bd3c0d"
"72d6ad274e72ffcd399e05cb"
"e864a710647bb6883c9ec617"
"024dcd8cc0178de7daa4f59f"
"ded24fb937a3fd60dc624677"
"9ad4c3b2aebe7282e973e5df"
"d16c66ecc656c82899a2d427"
"ee3e4bf39b840609dd88f5af"
"9a10d27eb6ab6f5030bdbcd9"
"b4cca5ecdeca1eebc0dc4183"
"ccdf1ef407864df62b03331e"
"1baa0329f02e41a5517b11a6"
"b5b33e5ecf6c02a5780ac419"
"84b690f53cd6cbbbf4a361f5"
"4e3b1b9d41ff11bdf3722ffe"
"1fd45e64c43cd640abcf8a76"
"c8bd32cdd943c88fb8a74934"
"9e5440269d09b71f07002333"
"5080016b8b68f40f7476c341"
"f762d45cc870d9f18c6b7024"
"cf21358ab1245c7033504b42"
"41eff7cb0f2fd8ec9e0cbf68"
"a344c63de6a00feea5c541e0"
"7ab3c18c0815d7b1f02e8768"
"a6f36e4387e2e1e25af4e000"
"9064af1431c1ff3aaa15a0d6"
"50946066aaba43a294155ab5"
"70e82e77857317d6936ecdcc"
"dc644dc20d5b02ffd7c8cc94"
"92af1e9f1813619d820dec4d"
"9d4a077fdaaa24b78a54cee9"
"cfd5fa7aea14fb294bd4f5ff"
"068541b6c5130ca541d6c05c"
"39f4d1f12142d583b0318265"
"4a201b8fa69d11cb669dca8c"
"6fa6bba2e334dd2c9e5a6911"
"30ff082b6a28558f16743fea"
"afed31c4dc2ddf056e263acb"
"b2431ecf36f0b780959aa03d"
"54e42938ab6fa923a5fbfbff"
"48d50e3ff5e96f152d912ebf"
"6c7aad2ee996132f76630c05"
"2705f0482774977fa9b5c7bc"
"c09d6682c7a144d9afd4bfab"
"1a0a30c55dd9bc2fff4e2aac"
"30c22b502e80afd144d79120"
"8f882989bbc98c68b6c7347e"
"888bf771b63a5f64e6b696ed"
"959aa13a2e6439fd5d29c5fe"
"d03d311ca64bf59a2404dcd0"
"7a650f3e4308a097d7883b1f"
"54c207808ac349fa34ed24a9"
"3cb12e3ea31fe15ba9d234e5"
"0d9f8123e52f791ae172f627"
"387518b1bb96bd918e7d0b91"
"27198d8918c9802ac0b06a58"
"7a519a552f723c6b1fdc9389"
"4240096651a50f888765ed60"
"8ba94f78cc44cfb82b3678d2"
"1cf7c3bda6ec7952923bc90f"
"cd54811e976edbc798a87c24"
"e4ee17912f5d7d80d41dc52d"
"572346dd1874a0c749c3a031"
"cf700031502680580ff25980"
"7054518832e5c7efde04a58c"
"7716c6b6a3dcc9c5df79a262"
"f4d1084c692f5a07c25a1b47"
"817b80892a78fc6f83eab4eb"
"ae0e0adda6d063d3fd2c9499"
"33184c427ecaab77b5332521"
"c6c4da707e3eed987eb65ba6"
"40f9801913c0cb9800960bf7"
"af11cf78e8340f172ba76bbe"
"c1daea28b5996b8e8800e300"
"7312b3234b5f2f50887597b9"
"3e4361c5356b30f8ce833268"
"e6fc8827b9aa96fc91035ac2"
"11658fe51b23319533c72106"
"34207fe236870fd855392ef6"
"dc970a2b17aa1432d180d078"
"2f1686ad711b5ac3d98905b5"
"ed20f4bf782e10ec26c7b3b1"
"cf4d6241df36b0bbcea401e2"
"97e414eb00338bd7bd5d1b31"
"bcfebb743729d4fc51b0db49"
"52782c1eaa15e050ac748b18"
"57f65a6cd2eb0a2745fc90ee"
"fcc569e80808c6cd84c839cc"
"2447f8e0e3ccfd895a619695"
"600687fe6332b2e2b20d6a84"
"0f8a972361baddb3ac64b6d9"
"f768594ab352cf8e2282b615"
"3163b25e25b0d6859df17751"
"c73d68f0cf77aa113337fcde"
"cc16163137f13f70decbcb5c"
"4adbf6c7ac2004ee8ae20f55"
"c2779f78a0e57dcee471cefd"
"bf44903cd2fca4ae4daf0818"
"ca8013372d58365e21bd0428"
"e308901ca272ad447dca965a"
"ec8cefe29832cd726185119f"
"36bf51da3f32b1e9550f3b15"
"aefff6dffdf534a6c9601f70"
"ce237971a71127f9ce747232"
"99f4d97fae16ccbeb8c26d46"
"c9f6480328ec4b63b1034826"
"ce10bb6e80b09533cb21c9a5"
"311a54aaf062b29422ac0b02"
"b2073bec873b295c04e9fb7e"
"7b7d94035d63c9c0598b242b"
"e19e9a0487f1bd3eabdb777c"
"eeafe5169a2afd9ce3979698"
"96c7a1f9fb73468fdb770034"
"77e607be2498160d3ceeff60"
"c784fa6b1fd7d9aedb05b607"
"a76319bca87e0b57bc79064f"
"a08db36785c51e5b92a6e92f"
"0933ab4a366d57fd1f1c0a48"
"17663f17a835cfe0bca64f4a"
"aee8323d1de634fc42c11d90"
"682044caf09a068e26c11cab"
"1346fdbe3c6fcb808b99cdf6"
"26fd8f47fe4791e2dd07b2e0"
"b1da27dd129216c2ead2e162"
"618adebe192e896eb1501708"
"2628d9eeb38db68c0b255a75"
"1fb78eda5deb8972cc357a9b"
"2fdebb85a401de75bb580a02"
"c59eae24bee36f63c938723d"
"fb66b55d5617e27ca64836a3"
"55994022872e01fd41d8ec7b"
"53ae83b5490e1cf55625ff3d"
"c44975deeb67b96e6cc37eb5"
"94c64d5ff48ca6487f5e0b07"
"e34e7c1a3d70678955a5df8e"
"6426d30a923dec6377132c47"
"d67811849354ded9d34c9ece"
"01e7ae81207cfd230cb02747"
"e24558477f94bcdd82f16ebe"
"4370f6dcc2f53bd5abed7899"
"679b1145cd3072fbcd513bcb"
"4dafd3fd84e7a337ef3e9933"
"2aca3d41cb148603b4a2d70b"
"5e5aa8c960d024109543c577"
"c5f5f313c8a7056bbcdb7639"
"a7b2aaa4cf85fce98cdeb314"
"e556c0d14bc0ce9cc7bde0ca"
"922a8284880a7c879d846f90"
"b146cf2fbf206ca48687e9b6"
"fbdc52bb7c7620bd4508e5a3"
"23d79a262a81459e75576cc2"
"a26721706d3b9f971cef3ce6"
"fb3801ed1b056a3ab4bbfdc1"
"d5cafae617f1485826de335c"
"e9650dfc8b570f9d33381012"
"453c804c7ce82e83305cd751"
"3eaab01bdd1393be8d461777"
"2b6d9ddd4754b71d92ed31b7"
"2338e9af6acab9fcf5327f6a"
"588e7405c30c346ed2ea5e44"
"eec8837994054fb2043b6166"
"0877406795956ca9eb13fea1"
"1dea30e852067eec48a24035"
"ee716ce45ea812167ed2497a"
"9621d5aeaf8de9b2f58977bd"
"e9bb838723083bac7f83d602"
"ef64da08bbe787891d47c31b"
"e01b6616cc6cf7819172f7f3"
"17c821c34fca587daec981fc"
"d0c4b7153dcb3e6af47bcd98"
"5188eb00680051c35cd6e0f0"
"9fc7b68e736a1f4931226ad8"
"b5eac897471f125224a3e594"
"c1d0f890a02c85a9320f96d5"
"7e3b1ca1b1b1e990367f0cc2"
"bff5ff77174c43f7c681e0bd"
"ddeb794121937d07a887c9e6"
"9db902e930f25fbacc125087"
"4f82d277b47ae9c98095f068"
"24f999491fa46cd194c7fd35"
*/

59
seed_data/seed.js Normal file
View File

@ -0,0 +1,59 @@
require('dotenv').load();
var Challenge = require('../models/Challenge.js'),
Bonfire = require('../models/Bonfire.js'),
mongoose = require('mongoose'),
secrets = require('../config/secrets'),
challenges = require('./challenges.json'),
bonfires = require('./bonfires.json');
mongoose.connect(secrets.db);
var counter = 0;
var CompletionMonitor = function() {
counter++;
console.log('call ' + counter);
if (counter < 2) {
return;
} else {
process.exit(0);
}
}
Challenge.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
Challenge.create(challenges, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
CompletionMonitor();
});
console.log('challenges');
});
Bonfire.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
Bonfire.create(bonfires, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
CompletionMonitor();
});
console.log('bonfires');
});

View File

@ -1,23 +0,0 @@
require('dotenv').load();
var Challenge = require('../models/Challenge.js'),
mongoose = require('mongoose'),
secrets = require('../config/secrets'),
challenges = require('./challenges.json');
mongoose.connect(secrets.db);
Challenge.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
Challenge.create(challenges, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
process.exit(0);
});
});

View File

@ -12,8 +12,8 @@ block content
link(rel='stylesheet', href='/js/lib/codemirror/theme/monokai.css') link(rel='stylesheet', href='/js/lib/codemirror/theme/monokai.css')
link(rel="stylesheet", href="http://fonts.googleapis.com/css?family=Ubuntu+Mono") 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/codemirror/mode/javascript/javascript.js')
script(src='js/lib/jailed/jailed.js') script(src='/js/lib/jailed/jailed.js')
script(src='/js/lib/bonfire/bonfire.js') script(src='/js/lib/bonfire/bonfireInit.js')
.row .row
#mainEditorPanel.col-sm-12.col-md-7.col-xs-12 #mainEditorPanel.col-sm-12.col-md-7.col-xs-12
.panel.panel-primary.panel-bonfire .panel.panel-primary.panel-bonfire
@ -36,4 +36,4 @@ block content
br br
ul#testSuite.list-group ul#testSuite.list-group
br br
script(src='/js/lib/bonfire/framework.js') script(src='/js/lib/bonfire/bonfireFramework.js')

50
views/bonfire/show.jade Normal file
View File

@ -0,0 +1,50 @@
extends ../layout
block content
script(src='/js/lib/codemirror/lib/codemirror.js')
script(src='/js/lib/codemirror/addon/edit/closebrackets.js')
script(src='/js/lib/codemirror/addon/edit/matchbrackets.js')
script(src='/js/lib/codemirror/addon/lint/lint.js')
script(src='/js/lib/codemirror/addon/lint/javascript-lint.js')
script(src='//ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js')
script(src='/js/lib/chai/chai.js')
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/bonfireInit.js')
.row
#mainEditorPanel.col-sm-12.col-md-7.col-xs-12
.panel.panel-primary.panel-bonfire
.panel-heading.text-center #{name} (Level #{difficulty} bonfire)
.panel.panel-body
.well
.text-justify.bonfire-instructions
for sentence in description
p.bonfire-instructions!= sentence
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
#testCreatePanel.col-sm-12.col-md-5.col-xs-12
.panel.panel-primary.panel-bonfire
.panel-heading.text-center Output
.panel.panel-body
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
br
form.code
.form-group.codeMirrorView
textarea#codeOutput
br
ul#testSuite.list-group
br
script(type="text/javascript").
var publicTests = !{JSON.stringify(publicTests)};
var privateTests = !{JSON.stringify(privateTests)};
var challengeSeed = !{JSON.stringify(challengeSeed)};
var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)};
var challengeEntryPointNegate = !{JSON.stringify(challengeEntryPointNegate)};
script(src='/js/lib/bonfire/bonfireFramework.js')

View File

@ -1,6 +1 @@
h3 //TODO: STUFF
ol(start='0')
for bonfire in bonfires
li
a(href="/bonfire/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name}
| &nbsp; (#{bonfire.time} mins)