diff --git a/README.md b/README.md index 411224ae9d..46f1d5b0cf 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ LINKEDIN_SECRET='LINKEDIN APP SECRET' ```bash # Seed your database with the challenges -node seed_data/seed_challenges.js +node seed_data/seed.js # start the application gulp diff --git a/app.js b/app.js index eb65edc55a..5b32f9b1af 100644 --- a/app.js +++ b/app.js @@ -250,10 +250,10 @@ app.get( app.all('/account', passportConf.isAuthenticated); app.get('/account/api', userController.getAccountAngular); app.get('/bonfire', bonfireController.index); -//app.get( -// '/bonfire/:bonfireNumber', -// bonfireController.returnBonfire -//); +app.get( + '/bonfire/:bonfireNumber', + bonfireController.returnBonfire +); // Unique Check API route app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index c19f410364..890c149a99 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -1,68 +1,78 @@ var _ = require('lodash'), -debug = require('debug')('freecc:cntr:bonfires'); -// bonfire = require('./../models/Bonfire'); + debug = require('debug')('freecc:cntr:bonfires'), + Bonfire = require('./../models/Bonfire'); /** * Bonfire controller */ + +var highestBonfireNumber = 10; + exports.index = function(req, res) { - res.render('bonfire/bonfire.jade', { - title: 'Learn to code with Bonfire' - }); - //Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) { - // if (err) { - // debug('bonfire err: ', err); - // next(err); - // } - //}); + res.render('bonfire/bonfire.jade', { + title: 'Learn to code with Bonfire' + }); + + Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) { + if (err) { + debug('bonfire err: ', err); + next(err); + } + }); }; -//exports.returnBonfire = function(req, res, next) { -// var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; -// var verbs = [ -// 'ACED', -// 'NAILED', -// 'ROCKED', -// 'SCORCHED', -// 'DEVASTATED', -// 'OWNED', -// 'CRUSHED', -// 'CONQUERED', -// 'KILLED', -// 'SHREDDED', -// 'ANNIHILATED', -// 'NUKED' -// ]; -// var phrases = [ -// "Shout it from on top of a mountain", -// "Tell everyone and their dogs", -// "Show them. Show them all!", -// "Inspire your friends", -// "Tell the world of your greatness", -// "Look accomplished on social media", -// "Share news of your grand endeavor", -// "Establish your alibi for the past two hours", -// "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 (err) { -// debug('bonfire err: ', err); -// next(err); -// } -// res.render('bonfires/show', { -// //title: 'bonfire: ' + c[bonfireNumber].name, -// //name: c[bonfireNumber].name, -// //video: c[bonfireNumber].video, -// //time: c[bonfireNumber].time, -// //steps: c[bonfireNumber].steps, -// //number: bonfireNumber, -// //categories: c[bonfireNumber].category, -// //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: c -// }); -// }); -//}; \ No newline at end of file +exports.returnBonfire = function(req, res, next) { + var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; + var verbs = [ + 'ACED', + 'NAILED', + 'ROCKED', + 'SCORCHED', + 'DEVASTATED', + 'OWNED', + 'CRUSHED', + 'CONQUERED', + 'KILLED', + 'SHREDDED', + 'ANNIHILATED', + 'NUKED' + ]; + var phrases = [ + "Shout it from on top of a mountain", + "Tell everyone and their dogs", + "Show them. Show them all!", + "Inspire your friends", + "Tell the world of your greatness", + "Look accomplished on social media", + "Share news of your grand endeavor", + "Establish your alibi for the past two hours", + "Prove to mom that computers aren't just for games" + ]; + + if (bonfireNumber > highestBonfireNumber) { bonfireNumber = 0; } + Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) { + debug(bonfire[bonfireNumber].challengeEntryPoint); + if (err) { + debug('bonfire err: ', err); + next(err); + } + res.render('bonfire/show', { + title: bonfire[bonfireNumber].name, + name: bonfire[bonfireNumber].name, + number: bonfire[bonfireNumber].bonfireNumber, + difficulty: bonfire[bonfireNumber].difficulty, + description: bonfire[bonfireNumber].description, + publicTests: bonfire[bonfireNumber].publicTests, + privateTests: bonfire[bonfireNumber].privateTests, + challengeSeed: bonfire[bonfireNumber].challengeSeed, + challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + 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 + }); + }); +}; \ No newline at end of file diff --git a/controllers/challenges.js b/controllers/challenges.js index e686fc8b39..96170f433b 100644 --- a/controllers/challenges.js +++ b/controllers/challenges.js @@ -6,6 +6,8 @@ var _ = require('lodash'), debug = require('debug')('freecc:cntr:challenges'), Challenge = require('./../models/Challenge'); +var highestChallengeNumber = 53; + exports.returnChallenge = function(req, res, next) { var challengeNumber = parseInt(req.params.challengeNumber) || 0; var verbs = [ @@ -33,7 +35,7 @@ exports.returnChallenge = function(req, res, next) { "Establish your alibi for the past two hours", "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) { if (err) { debug('Challenge err: ', err); diff --git a/models/Bonfire.js b/models/Bonfire.js index 10360c40c0..461e663d77 100644 --- a/models/Bonfire.js +++ b/models/Bonfire.js @@ -1,14 +1,27 @@ var mongoose = require('mongoose'); var secrets = require('../config/secrets'); +/** + * + * @type {exports.Schema} + */ + + var bonfireSchema = new mongoose.Schema({ + name: { type: String, unique: true }, - link: String, - time: String, - bonfireNumber: Number + difficulty: Number, + description: Array, + publicTests: Array, + privateTests: Array, + challengeSeed: String, + bonfireNumber: Number, + challengeEntryPoint: String, + challengeEntryPointNegate: String + }); module.exports = mongoose.model('Bonfire', bonfireSchema); diff --git a/models/BonfireCompletion.js b/models/BonfireCompletion.js new file mode 100644 index 0000000000..671c7ff4d9 --- /dev/null +++ b/models/BonfireCompletion.js @@ -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); diff --git a/models/User.js b/models/User.js index ab217b2ad7..582bd551e5 100644 --- a/models/User.js +++ b/models/User.js @@ -3,356 +3,357 @@ var crypto = require('crypto'); var mongoose = require('mongoose'); var userSchema = new mongoose.Schema({ - email: { - type: String, - lowercase: true, - unique: true, - trim: true, - sparse: true - }, - password: String, - facebook: String, - twitter: String, - google: String, - github: String, - linkedin: String, - tokens: Array, - points: { - type: Number, - default: 0 - }, - challengesCompleted: { type: Array, default: [] }, - challengesHash: { - 0: { - type: Number, - default: 0 - }, - 1: { - type: Number, - default: 0 - }, - 2: { - type: Number, - default: 0 - }, - 3: { - type: Number, - default: 0 - }, - 4: { - type: Number, - default: 0 - }, - 5: { - type: Number, - default: 0 - }, - 6: { - type: Number, - default: 0 - }, - 7: { - type: Number, - default: 0 - }, - 8: { - type: Number, - default: 0 - }, - 9: { - type: Number, - default: 0 - }, - 10: { - type: Number, - default: 0 - }, - 11: { - type: Number, - default: 0 - }, - 12: { - type: Number, - default: 0 - }, - 13: { - type: Number, - default: 0 - }, - 14: { - type: Number, - default: 0 - }, - 15: { - type: Number, - default: 0 - }, - 16: { - type: Number, - default: 0 - }, - 17: { - type: Number, - default: 0 - }, - 18: { - type: Number, - default: 0 - }, - 19: { - type: Number, - default: 0 - }, - 20: { - type: Number, - default: 0 - }, - 21: { - type: Number, - default: 0 - }, - 22: { - type: Number, - default: 0 - }, - 23: { - type: Number, - default: 0 - }, - 24: { - type: Number, - default: 0 - }, - 25: { - type: Number, - default: 0 - }, - 26: { - type: Number, - default: 0 - }, - 27: { - type: Number, - default: 0 - }, - 28: { - type: Number, - default: 0 - }, - 29: { - type: Number, - default: 0 - }, - 30: { - type: Number, - default: 0 - }, - 31: { - type: Number, - default: 0 - }, - 32: { - type: Number, - default: 0 - }, - 33: { - type: Number, - default: 0 - }, - 34: { - type: Number, - default: 0 - }, - 35: { - type: Number, - default: 0 - }, - 36: { - type: Number, - default: 0 - }, - 37: { - type: Number, - default: 0 - }, - 38: { - type: Number, - default: 0 - }, - 39: { - type: Number, - default: 0 - }, - 40: { - type: Number, - default: 0 - }, - 41: { - type: Number, - default: 0 - }, - 42: { - type: Number, - default: 0 - }, - 43: { - type: Number, - default: 0 - }, - 44: { - type: Number, - default: 0 - }, - 45: { - type: Number, - default: 0 - }, - 46: { - type: Number, - default: 0 - }, - 47: { - type: Number, - default: 0 - }, - 48: { - type: Number, - default: 0 - }, - 49: { - type: Number, - default: 0 - }, - 50: { - type: Number, - default: 0 - }, - 51: { - type: Number, - default: 0 - }, - 52: { - type: Number, - default: 0 - }, - 53: { - type: Number, - default: 0 - }, - 54: { - type: Number, - default: 0 - }, - 55: { - type: Number, - default: 0 - }, - 56: { - type: Number, - default: 0 - }, - 57: { - type: Number, - default: 0 - }, - 58: { - type: Number, - default: 0 - }, - 59: { - type: Number, - default: 0 - } - }, - profile: { - username: { - type: String, - unique: true, - sparse: true, - lowercase: true, - trim: true - }, - bio: { - type: String, - defaults: '' - }, - name: { - type: String, - default: '' - }, - gender: { - type: String, - default: '' - }, - location: { - type: String, - default: '' - }, - picture: { - type: String, - default: '' - }, - linkedinProfile: { - type: String, - default: '' - }, - githubProfile: { - type: String, - default: '' - }, - coderbyteProfile: { - type: String, - default: '' - }, - codepenProfile: { - type: String, - default: '' - }, - twitterHandle: { - type: String, - default: '' - } - }, - portfolio: { - website1Link: { - type: String, - default: '' - }, - website1Title: { - type: String, - default: '' - }, - website1Image: { - type: String, - default: '' - }, - website2Link: { - type: String, - default: '' - }, - website2Title: { - type: String, - default: '' - }, - website2Image: { - type: String, - default: '' - }, - website3Link: { - type: String, - default: '' - }, - website3Title: { - type: String, - default: '' - }, - website3Image: { - type: String, - default: '' - } - }, - resetPasswordToken: String, - resetPasswordExpires: Date + email: { + type: String, + lowercase: true, + unique: true, + trim: true, + sparse: true + }, + password: String, + facebook: String, + twitter: String, + google: String, + github: String, + linkedin: String, + tokens: Array, + points: { + type: Number, + default: 0 + }, + challengesCompleted: { type: Array, default: [] }, + challengesHash: { + 0: { + type: Number, + default: 0 + }, + 1: { + type: Number, + default: 0 + }, + 2: { + type: Number, + default: 0 + }, + 3: { + type: Number, + default: 0 + }, + 4: { + type: Number, + default: 0 + }, + 5: { + type: Number, + default: 0 + }, + 6: { + type: Number, + default: 0 + }, + 7: { + type: Number, + default: 0 + }, + 8: { + type: Number, + default: 0 + }, + 9: { + type: Number, + default: 0 + }, + 10: { + type: Number, + default: 0 + }, + 11: { + type: Number, + default: 0 + }, + 12: { + type: Number, + default: 0 + }, + 13: { + type: Number, + default: 0 + }, + 14: { + type: Number, + default: 0 + }, + 15: { + type: Number, + default: 0 + }, + 16: { + type: Number, + default: 0 + }, + 17: { + type: Number, + default: 0 + }, + 18: { + type: Number, + default: 0 + }, + 19: { + type: Number, + default: 0 + }, + 20: { + type: Number, + default: 0 + }, + 21: { + type: Number, + default: 0 + }, + 22: { + type: Number, + default: 0 + }, + 23: { + type: Number, + default: 0 + }, + 24: { + type: Number, + default: 0 + }, + 25: { + type: Number, + default: 0 + }, + 26: { + type: Number, + default: 0 + }, + 27: { + type: Number, + default: 0 + }, + 28: { + type: Number, + default: 0 + }, + 29: { + type: Number, + default: 0 + }, + 30: { + type: Number, + default: 0 + }, + 31: { + type: Number, + default: 0 + }, + 32: { + type: Number, + default: 0 + }, + 33: { + type: Number, + default: 0 + }, + 34: { + type: Number, + default: 0 + }, + 35: { + type: Number, + default: 0 + }, + 36: { + type: Number, + default: 0 + }, + 37: { + type: Number, + default: 0 + }, + 38: { + type: Number, + default: 0 + }, + 39: { + type: Number, + default: 0 + }, + 40: { + type: Number, + default: 0 + }, + 41: { + type: Number, + default: 0 + }, + 42: { + type: Number, + default: 0 + }, + 43: { + type: Number, + default: 0 + }, + 44: { + type: Number, + default: 0 + }, + 45: { + type: Number, + default: 0 + }, + 46: { + type: Number, + default: 0 + }, + 47: { + type: Number, + default: 0 + }, + 48: { + type: Number, + default: 0 + }, + 49: { + type: Number, + default: 0 + }, + 50: { + type: Number, + default: 0 + }, + 51: { + type: Number, + default: 0 + }, + 52: { + type: Number, + default: 0 + }, + 53: { + type: Number, + default: 0 + }, + 54: { + type: Number, + default: 0 + }, + 55: { + type: Number, + default: 0 + }, + 56: { + type: Number, + default: 0 + }, + 57: { + type: Number, + default: 0 + }, + 58: { + type: Number, + default: 0 + }, + 59: { + type: Number, + default: 0 + } + }, + profile: { + username: { + type: String, + unique: true, + sparse: true, + lowercase: true, + trim: true + }, + bio: { + type: String, + defaults: '' + }, + name: { + type: String, + default: '' + }, + gender: { + type: String, + default: '' + }, + location: { + type: String, + default: '' + }, + picture: { + type: String, + default: '' + }, + linkedinProfile: { + type: String, + default: '' + }, + githubProfile: { + type: String, + default: '' + }, + coderbyteProfile: { + type: String, + default: '' + }, + codepenProfile: { + type: String, + default: '' + }, + twitterHandle: { + type: String, + default: '' + } + }, + portfolio: { + website1Link: { + type: String, + default: '' + }, + website1Title: { + type: String, + default: '' + }, + website1Image: { + type: String, + default: '' + }, + website2Link: { + type: String, + default: '' + }, + website2Title: { + type: String, + default: '' + }, + website2Image: { + type: String, + default: '' + }, + website3Link: { + type: String, + default: '' + }, + website3Title: { + type: String, + default: '' + }, + website3Image: { + type: String, + default: '' + } + }, + resetPasswordToken: String, + resetPasswordExpires: Date, + bonfires: Array }); /** @@ -360,19 +361,19 @@ var userSchema = new mongoose.Schema({ */ userSchema.pre('save', function(next) { - var user = this; + var user = this; - if (!user.isModified('password')) { return next(); } + if (!user.isModified('password')) { return next(); } - bcrypt.genSalt(5, function(err, salt) { - if (err) { return next(err); } + bcrypt.genSalt(5, function(err, salt) { + if (err) { return next(err); } - bcrypt.hash(user.password, salt, null, function(err, hash) { - if (err) { return next(err); } - user.password = hash; - next(); + bcrypt.hash(user.password, salt, null, function(err, hash) { + if (err) { return next(err); } + user.password = hash; + next(); + }); }); - }); }); /** @@ -380,10 +381,10 @@ userSchema.pre('save', function(next) { */ userSchema.methods.comparePassword = function(candidatePassword, cb) { - bcrypt.compare(candidatePassword, this.password, function(err, isMatch) { - if (err) { return cb(err); } - cb(null, isMatch); - }); + bcrypt.compare(candidatePassword, this.password, function(err, isMatch) { + if (err) { return cb(err); } + cb(null, isMatch); + }); }; /** @@ -391,18 +392,18 @@ userSchema.methods.comparePassword = function(candidatePassword, cb) { */ userSchema.methods.gravatar = function(size) { - if (!size) { size = 200; } + if (!size) { size = 200; } - if (!this.email) { - return 'https://gravatar.com/avatar/?s=' + size + '&d=retro'; - } + if (!this.email) { + return 'https://gravatar.com/avatar/?s=' + size + '&d=retro'; + } - var md5 = crypto - .createHash('md5') - .update(this.email) - .digest('hex'); + var md5 = crypto + .createHash('md5') + .update(this.email) + .digest('hex'); - return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro'; + return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro'; }; module.exports = mongoose.model('User', userSchema); diff --git a/public/css/main.less b/public/css/main.less index f31a8e2a59..3331d20ab2 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -569,6 +569,13 @@ div.CodeMirror-scroll { 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 //* { // border-color: red; diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js new file mode 100644 index 0000000000..4311a7fa09 --- /dev/null +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -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(); + } + } + }); + } +}; \ No newline at end of file diff --git a/public/js/lib/bonfire/bonfire.js b/public/js/lib/bonfire/bonfireInit.js similarity index 88% rename from public/js/lib/bonfire/bonfire.js rename to public/js/lib/bonfire/bonfireInit.js index 19be8fb116..ee885599f9 100644 --- a/public/js/lib/bonfire/bonfire.js +++ b/public/js/lib/bonfire/bonfireInit.js @@ -46,6 +46,7 @@ var api = { reset(); } else { print(null, data); + reset(); } } }; @@ -66,18 +67,20 @@ var requests; // (re)initializes the plugin var reset = function() { 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() { // give some time to handle the last responce setTimeout( function() { endLoading(); - codeOutput.setValue('Infinite loop detected!'); + console.log("resetting on fatal plugin error"); + codeOutput.setValue('Infinite loop or fatal error!'); reset(); }, 10); }); }; + // initialize everything var plugin = null; diff --git a/public/js/lib/bonfire/framework.js b/public/js/lib/bonfire/framework.js deleted file mode 100644 index 441cfe9305..0000000000 --- a/public/js/lib/bonfire/framework.js +++ /dev/null @@ -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") -// } -// }); \ No newline at end of file diff --git a/public/js/lib/bonfire/plugin_v0.1.1.js b/public/js/lib/bonfire/plugin_v0.1.2.js similarity index 100% rename from public/js/lib/bonfire/plugin_v0.1.1.js rename to public/js/lib/bonfire/plugin_v0.1.2.js diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json new file mode 100644 index 0000000000..79f5f34bfb --- /dev/null +++ b/seed_data/bonfires.json @@ -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].*\\;" + } +] + diff --git a/seed_data/challenge-hashes b/seed_data/challenge-hashes new file mode 100644 index 0000000000..8b6ddfa36d --- /dev/null +++ b/seed_data/challenge-hashes @@ -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" +*/ \ No newline at end of file diff --git a/seed_data/seed.js b/seed_data/seed.js new file mode 100644 index 0000000000..797f704d5e --- /dev/null +++ b/seed_data/seed.js @@ -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'); + +}); + + diff --git a/seed_data/seed_challenges.js b/seed_data/seed_challenges.js deleted file mode 100644 index 1d0ab0760d..0000000000 --- a/seed_data/seed_challenges.js +++ /dev/null @@ -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); - }); -}); diff --git a/views/bonfire/bonfire.jade b/views/bonfire/bonfire.jade index 2d29a40a04..8fcf04a9ed 100644 --- a/views/bonfire/bonfire.jade +++ b/views/bonfire/bonfire.jade @@ -12,8 +12,8 @@ block content 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') + 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 @@ -36,4 +36,4 @@ block content br ul#testSuite.list-group br - script(src='/js/lib/bonfire/framework.js') \ No newline at end of file + script(src='/js/lib/bonfire/bonfireFramework.js') \ No newline at end of file diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade new file mode 100644 index 0000000000..6b51fe72f1 --- /dev/null +++ b/views/bonfire/show.jade @@ -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') diff --git a/views/partials/bonfires.jade b/views/partials/bonfires.jade index 41f9bbe9dd..1adbb74b8d 100644 --- a/views/partials/bonfires.jade +++ b/views/partials/bonfires.jade @@ -1,6 +1 @@ -h3 - ol(start='0') -for bonfire in bonfires - li - a(href="/bonfire/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name} - |   (#{bonfire.time} mins) \ No newline at end of file +//TODO: STUFF \ No newline at end of file