diff --git a/app.js b/app.js index 165061db3d..b1f65d4e37 100755 --- a/app.js +++ b/app.js @@ -36,25 +36,26 @@ var express = require('express'), /** * Controllers (route handlers). */ - homeController = require('./controllers/home'), - resourcesController = require('./controllers/resources'), - userController = require('./controllers/user'), - nonprofitController = require('./controllers/nonprofits'), - bonfireController = require('./controllers/bonfire'), - coursewareController = require('./controllers/courseware'), - fieldGuideController = require('./controllers/fieldGuide'), - challengeMapController = require('./controllers/challengeMap'), + homeController = require('./controllers/home'), + resourcesController = require('./controllers/resources'), + userController = require('./controllers/user'), + nonprofitController = require('./controllers/nonprofits'), + bonfireController = require('./controllers/bonfire'), + coursewareController = require('./controllers/courseware'), + fieldGuideController = require('./controllers/fieldGuide'), + challengeMapController = require('./controllers/challengeMap'), + challengeController = require('./controllers/challenge'), /** * Stories */ - storyController = require('./controllers/story'), + storyController = require('./controllers/story'), - /** - * API keys and Passport configuration. - */ - secrets = require('./config/secrets'), - passportConf = require('./config/passport'); + /** + * API keys and Passport configuration. + */ + secrets = require('./config/secrets'), + passportConf = require('./config/passport'); /** * Create Express server. @@ -537,17 +538,24 @@ app.post('/completed-field-guide/', fieldGuideController.completedFieldGuide); * Courseware related routes */ -app.get('/challenges/', coursewareController.returnNextCourseware); +app.get('/getstuff', challengeController.getStuff); + + +app.get('/challenges/next-challenge', challengeController.returnNextChallenge); app.get( - '/challenges/:coursewareName', - coursewareController.returnIndividualCourseware + '/challenges/:challengeName', + challengeController.returnIndividualChallenge ); -app.post('/completed-courseware/', coursewareController.completedCourseware); +app.get('/challenges/', challengeController.returnCurrentChallenge); +// todo refactor these routes +app.post('/completed-challenge/', challengeController.completedChallenge); app.post('/completed-zipline-or-basejump', - coursewareController.completedZiplineOrBasejump); + challengeController.completedZiplineOrBasejump); + +app.post('/completed-bonfire', challengeController.completedBonfire); // Unique Check API route app.get('/api/checkUniqueUsername/:username', diff --git a/controllers/challenge.js b/controllers/challenge.js index 9062945916..3894006e96 100644 --- a/controllers/challenge.js +++ b/controllers/challenge.js @@ -40,42 +40,107 @@ exports.showAllChallenges = function(req, res) { res.send(data); }; +exports.getStuff = function(req, res, next) { + res.send({withNames: challengeMapWithNames, withIds: challengeMapWithIds}); +}; + exports.returnNextChallenge = function(req, res, next) { if (!req.user) { return res.redirect('../challenges/learn-how-free-code-camp-works'); } + var completed = req.user.completedChallenges.map(function (elem) { + return elem._id; + }); + + req.user.uncompletedChallenges = resources.allChallengeIds() + .filter(function (elem) { + if (completed.indexOf(elem) === -1) { + return elem; + } + }); + req.user.save(); // find the user's current challenge and block // look in that block and find the index of their current challenge // if index + 1 <= block.challenges.length - 1 // serve index + 1 challenge // otherwise increment block key and serve the first challenge in that block var nextChallengeName; + var nextChallengeId; + var nextChallengeBlock; + var challengeId = req.user.currentChallenge.challengeId; var challengeBlock = req.user.currentChallenge.challengeBlock; + debug('this is the user challenge block', challengeBlock); var indexOfChallenge = challengeMapWithIds[challengeBlock] .indexOf(challengeId); - if (indexOfChallenge - <= challengeMapWithIds[challengeBlock].length - 1) { + debug('Logging first two challenge blocks for sanity', challengeMapWithIds[0], challengeMapWithIds[1]); + + + debug('these are the damn keys on challengeMapWithIds...', Object.keys(challengeMapWithIds)); + if (indexOfChallenge + 1 + < challengeMapWithIds[challengeBlock].length) { + debug('advancing to next challange in current block'); nextChallengeName = challengeMapWithNames[challengeBlock][indexOfChallenge + 1]; - } else if (typeof challengeMapWithIds[challengeBlock + 1] !== 'undefined') { - nextChallengeName = R.head(challengeMapWithNames[challengeBlock + 1]); + nextChallengeId = challengeMapWithIds[challengeBlock][indexOfChallenge + 1]; + nextChallengeBlock = challengeBlock; + } else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') { + debug('Advancing to next block'); + nextChallengeName = R.head(challengeMapWithNames[challengeBlock]); + nextChallengeId = R.head(challengeMapWithNames[challengeBlock]); + nextChallengeBlock = challengeBlock; } else { + debug('completed all challenges'); req.flash('errors', { msg: 'It looks like you have finished all of our challenges.' + ' Great job! Now on to helping nonprofits!' }); - nextChallengeName = R.head(challengeMapWithNames['0'].challenges); + nextChallengeName = R.head(challengeMapWithNames[0].challenges); + nextChallengeId = R.head(challengeMapWithNames[0].challenges); + nextChallengeBlock = 0; } - var nameString = nextChallengeName.toLowerCase().replace(/\s/g, '-'); - return res.redirect('../challenges' + nameString); + req.user.currentChallenge = { + challengeId: nextChallengeId, + challengeName: nextChallengeName, + challengeBlock: nextChallengeBlock + }; + req.user.save(); + var nameString = nextChallengeName.trim() + .toLowerCase() + .replace(/[^\w\s]/g, '') + .replace(/\s/g, '-') + debug('this is the namestring we\'re going to look up', nameString); + return res.redirect('../challenges/' + nameString); +}; + +exports.returnCurrentChallenge = function(req, res, next) { + debug('these are the damn keys on challengeMapWithIds...', Object.keys(challengeMapWithIds)); + debug('sanity check', challengeMapWithIds[0], challengeMapWithIds[1]); + if (!req.user) { + return res.redirect('../challenges/learn-how-free-code-camp-works'); + } + if (!req.user.currentChallenge) { + req.user.currentChallenge = {}; + req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0]; + req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0]; + req.user.currentChallenge.challengeBlock = '0'; + req.user.save(); + return res.redirect('../challenges/learn-how-free-code-camp-works'); + } + var nameString = req.user.currentChallenge.challengeName.trim() + .toLowerCase() + .replace(/[^\w\s]/g, '') + .replace(/\s/g, '-'); + debug('this is the namestring we\'re going to look up', nameString); + return res.redirect('../challenges/' + nameString); }; exports.returnIndividualChallenge = function(req, res, next) { var dashedName = req.params.challengeName; var challengeName = dashedName.replace(/\-/g, ' '); + debug('looking for %s', challengeName); Challenge.find({'name': new RegExp(challengeName, 'i')}, function(err, challengeFromMongo) { @@ -84,6 +149,7 @@ exports.returnIndividualChallenge = function(req, res, next) { } // Handle not found if (challengeFromMongo.length < 1) { + debug(challengeFromMongo); req.flash('errors', { msg: '404: We couldn\'t find a challenge with that name. ' + 'Please double check the name.' @@ -91,6 +157,7 @@ exports.returnIndividualChallenge = function(req, res, next) { return res.redirect('/challenges'); } var challenge = challengeFromMongo.pop(); + debug(challenge); // Redirect to full name if the user only entered a partial var dashedNameFull = challenge.name.toLowerCase().replace(/\s/g, '-'); @@ -186,6 +253,148 @@ exports.returnIndividualChallenge = function(req, res, next) { }); }; +exports.completedBonfire = function(req, res, next) { + +}; + +exports.completedChallenge = function (req, res, next) { + + var isCompletedDate = Math.round(+new Date()); + var challengeId = req.body.challengeInfo.challengeId; + + + req.user.completedChallenges.push({ + _id: challengeId, + completedDate: isCompletedDate, + name: req.body.challengeInfo.challengeName, + solution: null, + githubLink: null, + verified: true + }); + var index = req.user.uncompletedChallenges.indexOf(challengeId); + + if (index > -1) { + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedChallenges.splice(index, 1); + } + + req.user.save(function (err, user) { + if (err) { + return next(err); + } + if (user) { + res.sendStatus(200); + } + }); +}; + +exports.completedZiplineOrBasejump = function (req, res, next) { + debug('Inside controller for completed zipline or basejump with data %s', + req.body.coursewareInfo); + var isCompletedWith = req.body.coursewareInfo.completedWith || false; + var isCompletedDate = Math.round(+new Date()); + var coursewareHash = req.body.coursewareInfo.coursewareHash; + var solutionLink = req.body.coursewareInfo.publicURL; + var githubLink = req.body.coursewareInfo.challengeType === '4' + ? req.body.coursewareInfo.githubURL : true; + if (!solutionLink || !githubLink) { + req.flash('errors', { + msg: 'You haven\'t supplied the necessary URLs for us to inspect ' + + 'your work.' + }); + return res.sendStatus(403); + } + + if (isCompletedWith) { + var paired = User.find({'profile.username': isCompletedWith.toLowerCase()}).limit(1); + paired.exec(function (err, pairedWithFromMongo) { + if (err) { + return next(err); + } else { + var index = req.user.uncompletedCoursewares.indexOf(coursewareHash); + if (index > -1) { + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedCoursewares.splice(index, 1); + } + var pairedWith = pairedWithFromMongo.pop(); + + req.user.completedCoursewares.push({ + _id: coursewareHash, + name: req.body.coursewareInfo.coursewareName, + completedWith: pairedWith._id, + completedDate: isCompletedDate, + solution: solutionLink, + githubLink: githubLink, + verified: false + }); + + req.user.save(function (err, user) { + if (err) { + return next(err); + } + debug('this is the user object returned %s,' + + ' this is the req.user._id %s, ' + + 'this is the pairedWith._id %s', user, req.user._id, pairedWith._id); + debug(req.user._id.toString() === pairedWith._id.toString()); + if (req.user._id.toString() === pairedWith._id.toString()) { + return res.sendStatus(200); + } + index = pairedWith.uncompletedCoursewares.indexOf(coursewareHash); + if (index > -1) { + pairedWith.progressTimestamps.push(Date.now() || 0); + pairedWith.uncompletedCoursewares.splice(index, 1); + + } + + pairedWith.completedCoursewares.push({ + _id: coursewareHash, + name: req.body.coursewareInfo.coursewareName, + completedWith: req.user._id, + completedDate: isCompletedDate, + solution: solutionLink, + githubLink: githubLink, + verified: false + }); + pairedWith.save(function (err, paired) { + if (err) { + return next(err); + } + if (user && paired) { + return res.sendStatus(200); + } + }); + }); + } + }); + } else { + + req.user.completedCoursewares.push({ + _id: coursewareHash, + name: req.body.coursewareInfo.coursewareName, + completedWith: null, + completedDate: isCompletedDate, + solution: solutionLink, + githubLink: githubLink, + verified: false + }); + + var index = req.user.uncompletedCoursewares.indexOf(coursewareHash); + if (index > -1) { + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedCoursewares.splice(index, 1); + } + + req.user.save(function (err, user) { + if (err) { + return next(err); + } + if (user) { + return res.sendStatus(200); + } + }); + } +}; + /* challengeBlock { 0: { diff --git a/controllers/resources.js b/controllers/resources.js index b286a394ec..21a6d20f16 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -28,7 +28,8 @@ var async = require('async'), */ var allBonfireIds, allBonfireNames, allCoursewareIds, allCoursewareNames, allFieldGuideIds, allFieldGuideNames, allNonprofitNames, - allBonfireIndexesAndNames, challengeMap; + allBonfireIndexesAndNames, challengeMap, challengeMapWithIds, + challengeMapWithNames, allChallengeIds; /** * GET / @@ -46,55 +47,73 @@ Array.zip = function(left, right, combinerFunction) { return results; }; -buildChallengeMap = function() { - challengeMap = {}; - fs.readdir(__dirname + '../seed_data/challenges', function(err, files) { - if (err) { - debug(err); - } else { - var keyCounter = 0; - files = files.sort(function(a, b) { - return a.order < b.order ? a : b; - }); - files.forEach(function(file) { - challengeMap[keyCounter++] = file; - }); - } - }); -}; +(function() { + if (!challengeMap) { + var localChallengeMap = {}; + var files = fs.readdirSync(__dirname + '/../seed_data/challenges'); + var keyCounter = 0; + files = files.map(function (file) { + return require(__dirname + + '/../seed_data/challenges/' + file); + }); + files = files.sort(function (a, b) { + return a.order - b.order; + }); + files.forEach(function (file) { + localChallengeMap[keyCounter++] = file; + }); + challengeMap = _.cloneDeep(localChallengeMap); + } +})(); + module.exports = { getChallengeMapWithIds: function() { - // TODO finish this - if (challengeMap === null) { - buildChallengeMap(); - } - var challengeMapWithIds = {}; - Object.keys(challengeMap). - forEach(function(key) { - var onlyIds = challengeMap[key].challenges.map(function(elem) { - return elem.challengeId; + if (challengeMapWithIds) { + return challengeMapWithIds; + } else { + challengeMapWithIds = {}; + Object.keys(challengeMap).forEach(function (key) { + var onlyIds = challengeMap[key].challenges.map(function (elem) { + return elem._id; }); challengeMapWithIds[key] = onlyIds; }); - return challengeMapWithIds; + return challengeMapWithIds; + } + }, + + allChallengeIds: function() { + + if (allChallengeIds) { + return allChallengeIds; + } else { + allChallengeIds = []; + Object.keys(challengeMapWithIds).forEach(function(key) { + allChallengeIds.push(challengeMapWithIds[key].challenges); + }); + allChallengeIds = R.flatten(allChallengeIds); + } + return allChallengeIds; }, getChallengeMapWithNames: function() { - var challengeMapWithNames = {}; - Object.keys(challengeMap). - forEach(function(key) { - var onlyNames = challengeMap[key].challenges.map(function(elem) { - return elem.challengeName; + if (challengeMapWithNames) { + return challengeMapWithNames; + } else { + challengeMapWithNames = {}; + Object.keys(challengeMap). + forEach(function (key) { + var onlyNames = challengeMap[key].challenges.map(function (elem) { + return elem.name; + }); + challengeMapWithNames[key] = onlyNames; }); - challengeMapWithNames[key] = onlyNames; - }); - return challengeMapWithNames; + return challengeMapWithNames; + } }, - - sitemap: function sitemap(req, res, next) { var appUrl = 'http://www.freecodecamp.com'; var now = moment(new Date()).format('YYYY-MM-DD'); diff --git a/models/User.js b/models/User.js index 686468c8d7..6ddc3f7112 100644 --- a/models/User.js +++ b/models/User.js @@ -150,7 +150,19 @@ var userSchema = new mongoose.Schema({ finishedWaypoints: { type: Boolean, default: false }, sendMonthlyEmail: { type: Boolean, default: true }, challengesHash: {}, - currentChallenge: {} + currentChallenge: {}, + completedChallenges: [ + { + completedDate: Long, + _id: String, + name: String, + completedWith: String, + solution: String, + githubLink: String, + verified: Boolean + } + ], + uncompletedChallenges: Array }); /** diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.1.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.1.js index d32e54428e..2534a2456a 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.1.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.1.js @@ -1,69 +1,69 @@ var widgets = []; var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { - lineNumbers: true, - mode: "javascript", - theme: 'monokai', - runnable: true, - lint: true, - matchBrackets: true, - autoCloseBrackets: true, - scrollbarStyle: 'null', - lineWrapping: true, - gutters: ["CodeMirror-lint-markers"], - onKeyEvent: doLinting + lineNumbers: true, + mode: "javascript", + theme: 'monokai', + runnable: true, + lint: true, + matchBrackets: true, + autoCloseBrackets: true, + scrollbarStyle: 'null', + lineWrapping: true, + gutters: ["CodeMirror-lint-markers"], + onKeyEvent: doLinting }); var editor = myCodeMirror; editor.setSize("100%", "auto"); // Hijack tab key to enter two spaces intead editor.setOption("extraKeys", { - Tab: function(cm) { - if (cm.somethingSelected()){ - cm.indentSelection("add"); - } else { - var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); - cm.replaceSelection(spaces); - } - }, - "Shift-Tab": function(cm) { - if (cm.somethingSelected()){ - cm.indentSelection("subtract"); - } else { - var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); - cm.replaceSelection(spaces); - } - }, - "Ctrl-Enter": function() { - bonfireExecute(); - return false; + Tab: function(cm) { + if (cm.somethingSelected()){ + cm.indentSelection("add"); + } else { + var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); + cm.replaceSelection(spaces); } + }, + "Shift-Tab": function(cm) { + if (cm.somethingSelected()){ + cm.indentSelection("subtract"); + } else { + var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); + cm.replaceSelection(spaces); + } + }, + "Ctrl-Enter": function() { + bonfireExecute(); + return false; + } }); var attempts = 0; if (attempts) { - attempts = 0; + attempts = 0; } var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { - lineNumbers: false, - mode: "text", - theme: 'monokai', - readOnly: 'nocursor', - lineWrapping: true + lineNumbers: false, + mode: "text", + theme: 'monokai', + readOnly: 'nocursor', + lineWrapping: true }); codeOutput.setValue('/**\n' + -' * Your output will go here.\n' + ' * Console.log() -type statements\n' + -' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' + -' */'); + ' * Your output will go here.\n' + ' * Console.log() -type statements\n' + + ' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' + + ' */'); codeOutput.setSize("100%", "100%"); var info = editor.getScrollInfo(); var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top; if (info.top + info.clientHeight < after) - editor.scrollTo(null, after - info.clientHeight + 3); + editor.scrollTo(null, after - info.clientHeight + 3); var editorValue; @@ -73,9 +73,9 @@ var tests = tests || []; var allSeeds = ''; (function() { - challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; - }); + challengeSeed.forEach(function(elem) { + allSeeds += elem + '\n'; + }); })(); editorValue = allSeeds; @@ -84,52 +84,52 @@ editorValue = allSeeds; 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 - })); - } - }); + 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(); + bonfireExecute(); }); function bonfireExecute() { - attempts++; - ga('send', 'event', 'Challenge', 'ran-code', challengeName); - userTests= null; - $('#codeOutput').empty(); - var userJavaScript = myCodeMirror.getValue(); - userJavaScript = removeComments(userJavaScript); - userJavaScript = scrapeTests(userJavaScript); - // simple fix in case the user forgets to invoke their function + attempts++; + ga('send', 'event', 'Challenge', 'ran-code', challengeName); + userTests= null; + $('#codeOutput').empty(); + var userJavaScript = myCodeMirror.getValue(); + userJavaScript = removeComments(userJavaScript); + userJavaScript = scrapeTests(userJavaScript); + // simple fix in case the user forgets to invoke their function - 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); - } - }); + 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); + } + }); } @@ -139,108 +139,108 @@ var testSalt = Math.random(); var scrapeTests = function(userJavaScript) { - // insert tests from mongo - for (var i = 0; i < tests.length; i++) { - userJavaScript += '\n' + tests[i]; + // insert tests from mongo + for (var i = 0; i < tests.length; i++) { + userJavaScript += '\n' + tests[i]; + } + + var counter = 0; + var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/); + var match = regex.exec(userJavaScript); + while (match != null) { + var replacement = '//' + counter + testSalt; + userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length); + + if (!userTests) { + userTests= []; } + userTests.push({"text": match[0], "line": counter, "err": null}); + counter++; + match = regex.exec(userJavaScript); + } - var counter = 0; - var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/); - var match = regex.exec(userJavaScript); - while (match != null) { - var replacement = '//' + counter + testSalt; - userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length); - - if (!userTests) { - userTests= []; - } - userTests.push({"text": match[0], "line": counter, "err": null}); - counter++; - match = regex.exec(userJavaScript); - } - - return userJavaScript; + return userJavaScript; }; function removeComments(userJavaScript) { - var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); - return userJavaScript.replace(regex, ''); + var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); + return userJavaScript.replace(regex, ''); } function removeLogs(userJavaScript) { - return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); + return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); } var pushed = false; var createTestDisplay = function() { - if (pushed) { - userTests.pop(); + if (pushed) { + userTests.pop(); + } + for (var i = 0; i < userTests.length;i++) { + var test = userTests[i]; + var testDoc = document.createElement("div"); + if (test.err != null) { + console.log('Should be displaying bad tests'); + $(testDoc) + .html("
" + test.text + "
" + test.err + "
") + .prependTo($('#testSuite')) + } else { + $(testDoc) + .html("
" + test.text + "
") + .appendTo($('#testSuite')); } - for (var i = 0; i < userTests.length;i++) { - var test = userTests[i]; - var testDoc = document.createElement("div"); - if (test.err != null) { - console.log('Should be displaying bad tests'); - $(testDoc) - .html("
" + test.text + "
" + test.err + "
") - .prependTo($('#testSuite')) - } else { - $(testDoc) - .html("
" + test.text + "
") - .appendTo($('#testSuite')); - } - }; + }; }; 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 lineNum = test.line; + var regexp = new RegExp("\/\/" + lineNum + testSalt); + return data.input.replace(regexp, test.text); }; var runTests = function(err, data) { - var allTestsPassed = true; - pushed = false; - $('#testSuite').children().remove(); - if (err && userTests.length > 0) { - userTests= [{text:"Program Execution Failure", err: "No user tests were run."}]; - createTestDisplay(); - } else if (userTests) { - userTests.push(false); - pushed = true; - userTests.forEach(function(test, ix, arr){ - try { - if (test) { - var output = eval(reassembleTest(test, data)); - } - } catch(error) { - allTestsPassed = false; - arr[ix].err = error.message; - } finally { - if (!test) { - createTestDisplay(); - } - } - }); - - if (allTestsPassed) { - allTestsPassed = false; - showCompletion(); + var allTestsPassed = true; + pushed = false; + $('#testSuite').children().remove(); + if (err && userTests.length > 0) { + userTests= [{text:"Program Execution Failure", err: "No user tests were run."}]; + createTestDisplay(); + } else if (userTests) { + userTests.push(false); + pushed = true; + userTests.forEach(function(test, ix, arr){ + try { + if (test) { + var output = eval(reassembleTest(test, data)); } + } catch(error) { + allTestsPassed = false; + arr[ix].err = error.message; + } finally { + if (!test) { + createTestDisplay(); + } + } + }); + if (allTestsPassed) { + allTestsPassed = false; + showCompletion(); } + + } }; function showCompletion() { - var time = Math.floor(Date.now()) - started; - ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time +', Attempts: ' + attempts); - $('#complete-courseware-dialog').modal('show'); - $('#complete-courseware-dialog').keydown(function(e) { - if (e.ctrlKey && e.keyCode == 13) { - $('#next-courseware-button').click(); - } - }); + var time = Math.floor(Date.now()) - started; + ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time +', Attempts: ' + attempts); + $('#complete-courseware-dialog').modal('show'); + $('#complete-courseware-dialog').keydown(function(e) { + if (e.ctrlKey && e.keyCode == 13) { + $('#next-courseware-button').click(); + } + }); } diff --git a/public/js/main.js b/public/js/main.js index d763e733a2..05136f74ca 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -127,16 +127,16 @@ $(document).ready(function() { case 1: case 2: $.post( - '/completed-courseware/', + '/completed-challenge/', { - coursewareInfo: { - coursewareHash: passedCoursewareHash, - coursewareName: passedCoursewareName + challengeInfo: { + challengeId: challengeId, + challengeName: challengeName } }).success( function(res) { if (res) { - window.location.href = '/challenges'; + window.location.href = '/challenges/next-challenge'; } } ); @@ -147,16 +147,16 @@ $(document).ready(function() { $.post( '/completed-zipline-or-basejump/', { - coursewareInfo: { - coursewareHash: passedCoursewareHash, - coursewareName: passedCoursewareName, + challengeInfo: { + challengeId: challengeId, + challengeName: challengeName, completedWith: didCompleteWith, publicURL: publicURL, challengeType: challengeType } }).success( function() { - window.location.href = '/challenges'; + window.location.href = '/challenges/next-challenge'; }).fail( function() { window.location.href = '/challenges'; @@ -169,9 +169,9 @@ $(document).ready(function() { $.post( '/completed-zipline-or-basejump/', { - coursewareInfo: { - coursewareHash: passedCoursewareHash, - coursewareName: passedCoursewareName, + challengeInfo: { + challengeId: challengeId, + challengeName: challengeName, completedWith: didCompleteWith, publicURL: publicURL, githubURL: githubURL, @@ -179,7 +179,7 @@ $(document).ready(function() { verified: false } }).success(function() { - window.location.href = '/challenges'; + window.location.href = '/challenges/next-challenge'; }).fail(function() { window.location.replace(window.location.href); }); diff --git a/seed_data/challenges/basic-html5-and-css.json b/seed_data/challenges/basic-html5-and-css.json index 274d64485b..0e30ebd275 100644 --- a/seed_data/challenges/basic-html5-and-css.json +++ b/seed_data/challenges/basic-html5-and-css.json @@ -20,7 +20,8 @@ ], "challengeSeed": [ "

Hello

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf0887a", @@ -38,7 +39,8 @@ ], "challengeSeed": [ "

Hello World

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08801", @@ -55,7 +57,8 @@ "challengeSeed": [ "

Hello World

", "

CatPhotoApp

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aeaf08801", @@ -74,7 +77,8 @@ "

Hello World

", "

CatPhotoApp

", "

Hello Paragraph

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08802", @@ -101,7 +105,8 @@ "", "

Hello Paragraph

", "-->" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08804", @@ -127,7 +132,8 @@ "", "

Hello Paragraph

", "-->" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08833", @@ -151,7 +157,8 @@ "
", "", "

Hello Paragraph

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fed1348bd9aedf08833", @@ -176,7 +183,8 @@ "
", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08803", @@ -195,7 +203,8 @@ "

CatPhotoApp

", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08805", @@ -218,7 +227,8 @@ "

CatPhotoApp

", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aecf08806", @@ -248,7 +258,8 @@ "

CatPhotoApp

", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aefe08806", @@ -274,7 +285,8 @@ "

CatPhotoApp

", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08806", @@ -300,7 +312,8 @@ "

CatPhotoApp

", "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aede08807", @@ -329,7 +342,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08807", @@ -361,7 +375,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08808", @@ -399,7 +414,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08809", @@ -440,7 +456,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { @@ -478,7 +495,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9acdf08812", @@ -516,7 +534,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9bedf08813", @@ -559,7 +578,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08814", @@ -607,7 +627,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08815", @@ -654,7 +675,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08816", @@ -704,7 +726,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aede08817", @@ -757,7 +780,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { @@ -809,7 +833,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08820", @@ -861,7 +886,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { "_id": "bad87fee1348bd9aedf08818", @@ -913,7 +939,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { @@ -966,7 +993,8 @@ "", "

Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.

", "

Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.

" - ] + ], + "challengeType": 0 }, { @@ -1025,7 +1053,8 @@ "
  • laser pointers
  • ", "
  • lasagna
  • ", "" - ] + ], + "challengeType": 0 }, { @@ -1086,7 +1115,8 @@ "
  • thunder
  • ", "
  • other cats
  • ", "" - ] + ], + "challengeType": 0 }, { @@ -1148,7 +1178,8 @@ "
  • other cats
  • ", "", "" - ] + ], + "challengeType": 0 }, { @@ -1211,7 +1242,8 @@ "
  • other cats
  • ", "", "" - ] + ], + "challengeType": 0 }, { @@ -1275,7 +1307,8 @@ "
    ", " ", "
    " - ] + ], + "challengeType": 0 }, { @@ -1411,7 +1444,8 @@ " ", " ", "" - ] + ], + "challengeType": 0 }, { @@ -1479,7 +1513,8 @@ " ", " ", "" - ] + ], + "challengeType" : 0 }, { @@ -1548,7 +1583,8 @@ " ", " ", "" - ] + ], + "challengeType" : 0 }, { @@ -1620,7 +1656,8 @@ " ", " ", "" - ] + ], + "challengeType" : 0 } ] } diff --git a/seed_data/seed.js b/seed_data/seed.js index fc87f56d02..600ea5e77a 100644 --- a/seed_data/seed.js +++ b/seed_data/seed.js @@ -32,11 +32,12 @@ Challenge.remove({}, function(err, data) { console.log('Deleted ', data); } challenges.forEach(function (file) { - Challenge.create(require('./challenges/' + file), function (err, data) { + Challenge.create(require('./challenges/' + file).challenges, function (err, data) { if (err) { console.log(err); } else { console.log('Successfully parsed %s', file); + console.log(data); } }); }); diff --git a/views/coursewares/showVideo.jade b/views/coursewares/showVideo.jade index 053d22e12e..73ece748e7 100644 --- a/views/coursewares/showVideo.jade +++ b/views/coursewares/showVideo.jade @@ -29,9 +29,8 @@ block content .button-spacer script(type="text/javascript"). var tests = !{JSON.stringify(tests)}; - var passedCoursewareHash = !{JSON.stringify(coursewareHash)}; + var challengeId = !{JSON.stringify(coursewareHash)}; var challengeName = !{JSON.stringify(name)}; - var passedCoursewareName = challengeName; var started = Math.floor(Date.now()); var challengeType = !{JSON.stringify(challengeType)}; var controlEnterHandler = function(e) { diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 7f09909b4b..957ef9f666 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -15,7 +15,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height if user li - a(href='/challenges') Next Challenge + a(href='/challenges') Current Challenge li a(href='/map') Map if (user && user.sentSlackInvite)