From 6b4475a5b87b358b4a00904ef2e81d2ed0bdd49c Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 00:44:08 -0500 Subject: [PATCH 01/39] Major refactor of bonfireFramework to differentiate tests, refactor tests in Bonfire schema to remove public/private tests and only have tests, fire modal on successful run of all tests in bonfire/show, added bonfiresHash to User schema, and we should have committed a while ago because there is too much too list --- app.js | 14 ++ controllers/bonfire.js | 13 +- models/Bonfire.js | 4 +- models/BonfireCompletion.js | 7 +- models/User.js | 243 +++++++++++++++++++++- public/js/lib/bonfire/bonfireFramework.js | 73 ++++--- public/js/main.js | 86 +++++--- seed_data/bonfires.json | 14 +- seed_data/challenge-hashes | 4 +- views/bonfire/show.jade | 31 ++- views/challenges/show.jade | 2 +- views/partials/bonfires.jade | 8 +- 12 files changed, 401 insertions(+), 98 deletions(-) diff --git a/app.js b/app.js index 5b32f9b1af7..bbae2fd4ba8 100644 --- a/app.js +++ b/app.js @@ -285,6 +285,20 @@ app.post('/completed-challenge', function (req, res) { req.user.save(); }); +app.post('/completed-bonfire/', function (req, res) { + req.user.challengesHash[parseInt(req.body.challengeNumber)] = + Math.round(+new Date() / 1000); + var ch = req.user.challengesHash; + var p = 0; + for (var k in ch) { + if (ch[k] > 0) { + p += 1; + } + } + req.user.points = p; + req.user.save(); +}); + /** * OAuth sign-in routes. */ diff --git a/controllers/bonfire.js b/controllers/bonfire.js index cf7f80eb4e8..958663f6e4e 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -62,16 +62,13 @@ exports.returnBonfire = function(req, res, next) { number: bonfire[bonfireNumber].bonfireNumber, difficulty: bonfire[bonfireNumber].difficulty, description: bonfire[bonfireNumber].description, - publicTests: bonfire[bonfireNumber].publicTests, - privateTests: bonfire[bonfireNumber].privateTests, + tests: bonfire[bonfireNumber].tests, 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)], + 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 }); }); diff --git a/models/Bonfire.js b/models/Bonfire.js index 461e663d772..dd0cff31d7a 100644 --- a/models/Bonfire.js +++ b/models/Bonfire.js @@ -8,15 +8,13 @@ var secrets = require('../config/secrets'); var bonfireSchema = new mongoose.Schema({ - name: { type: String, unique: true }, difficulty: Number, description: Array, - publicTests: Array, - privateTests: Array, + tests: Array, challengeSeed: String, bonfireNumber: Number, challengeEntryPoint: String, diff --git a/models/BonfireCompletion.js b/models/BonfireCompletion.js index 671c7ff4d92..eb4249400ae 100644 --- a/models/BonfireCompletion.js +++ b/models/BonfireCompletion.js @@ -3,11 +3,8 @@ var secrets = require('../config/secrets'); var bonfireCompletionSchema = new mongoose.Schema({ dateCompleted: Number, - completedWith: String, - bonfireNumber: { - bonfireNumber: Number, - bonfireId: String - }, + completedWith: ObjectId, + bonfireHash: ObjectId, solution: String }); diff --git a/models/User.js b/models/User.js index 582bd551e5d..81a33ef72d5 100644 --- a/models/User.js +++ b/models/User.js @@ -353,7 +353,248 @@ var userSchema = new mongoose.Schema({ }, resetPasswordToken: String, resetPasswordExpires: Date, - bonfires: Array + bonfiresHash: { + 0: { + type: Boolean, + default: 0 + }, + 1: { + type: Boolean, + default: 0 + }, + 2: { + type: Boolean, + default: 0 + }, + 3: { + type: Boolean, + default: 0 + }, + 4: { + type: Boolean, + default: 0 + }, + 5: { + type: Boolean, + default: 0 + }, + 6: { + type: Boolean, + default: 0 + }, + 7: { + type: Boolean, + default: 0 + }, + 8: { + type: Boolean, + default: 0 + }, + 9: { + type: Boolean, + default: 0 + }, + 10: { + type: Boolean, + default: 0 + }, + 11: { + type: Boolean, + default: 0 + }, + 12: { + type: Boolean, + default: 0 + }, + 13: { + type: Boolean, + default: 0 + }, + 14: { + type: Boolean, + default: 0 + }, + 15: { + type: Boolean, + default: 0 + }, + 16: { + type: Boolean, + default: 0 + }, + 17: { + type: Boolean, + default: 0 + }, + 18: { + type: Boolean, + default: 0 + }, + 19: { + type: Boolean, + default: 0 + }, + 20: { + type: Boolean, + default: 0 + }, + 21: { + type: Boolean, + default: 0 + }, + 22: { + type: Boolean, + default: 0 + }, + 23: { + type: Boolean, + default: 0 + }, + 24: { + type: Boolean, + default: 0 + }, + 25: { + type: Boolean, + default: 0 + }, + 26: { + type: Boolean, + default: 0 + }, + 27: { + type: Boolean, + default: 0 + }, + 28: { + type: Boolean, + default: 0 + }, + 29: { + type: Boolean, + default: 0 + }, + 30: { + type: Boolean, + default: 0 + }, + 31: { + type: Boolean, + default: 0 + }, + 32: { + type: Boolean, + default: 0 + }, + 33: { + type: Boolean, + default: 0 + }, + 34: { + type: Boolean, + default: 0 + }, + 35: { + type: Boolean, + default: 0 + }, + 36: { + type: Boolean, + default: 0 + }, + 37: { + type: Boolean, + default: 0 + }, + 38: { + type: Boolean, + default: 0 + }, + 39: { + type: Boolean, + default: 0 + }, + 40: { + type: Boolean, + default: 0 + }, + 41: { + type: Boolean, + default: 0 + }, + 42: { + type: Boolean, + default: 0 + }, + 43: { + type: Boolean, + default: 0 + }, + 44: { + type: Boolean, + default: 0 + }, + 45: { + type: Boolean, + default: 0 + }, + 46: { + type: Boolean, + default: 0 + }, + 47: { + type: Boolean, + default: 0 + }, + 48: { + type: Boolean, + default: 0 + }, + 49: { + type: Boolean, + default: 0 + }, + 50: { + type: Boolean, + default: 0 + }, + 51: { + type: Boolean, + default: 0 + }, + 52: { + type: Boolean, + default: 0 + }, + 53: { + type: Boolean, + default: 0 + }, + 54: { + type: Boolean, + default: 0 + }, + 55: { + type: Boolean, + default: 0 + }, + 56: { + type: Boolean, + default: 0 + }, + 57: { + type: Boolean, + default: 0 + }, + 58: { + type: Boolean, + default: 0 + }, + 59: { + type: Boolean, + default: 0 + } + } }); /** diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index 4311a7fa094..fe844286deb 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -45,9 +45,9 @@ var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), lineWrapping: true }); codeOutput.setValue('/**\n' + - ' * Your output will go here. Console.log() -type statements\n' + - ' * will appear in your browser\'s javascript console.\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; @@ -58,10 +58,8 @@ var editorValue; var challengeSeed = challengeSeed || null; -var publicTests = publicTests || []; -var privateTests = privateTests || []; +var tests = tests || []; var challengeEntryPoint = challengeEntryPoint || null; -var challengeEntryPointNegate = challengeEntryPointNegate || null; if (challengeSeed !== null) { @@ -101,7 +99,7 @@ $('#submitButton').on('click', function () { }); function bonfireExecute() { - tests = null; + userTests= null; $('#codeOutput').empty(); var userJavaScript = myCodeMirror.getValue(); userJavaScript = removeComments(userJavaScript); @@ -119,23 +117,20 @@ function bonfireExecute() { } var replaceQuotesInTests = function() { - tests.forEach(function(elt, ix, arr) { + userTests.forEach(function(elt, ix, arr) { arr[ix].text = arr[ix].text.replace(/\"/g,'\''); }); }; -var tests; +var userTests; 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]; + // insert tests from mongo + for (var i = 0; i < tests.length; i++) { + userJavaScript += '\n' + tests[i]; } var counter = 0; @@ -144,16 +139,20 @@ var scrapeTests = function(userJavaScript) { while (match != null) { var replacement = '//' + counter + testSalt; userJavaScript = userJavaScript.substring(0, match.index) - + replacement - + userJavaScript.substring(match.index + match[0].length); + + replacement + + userJavaScript.substring(match.index + match[0].length); - if (!tests) tests = []; - tests.push({"text": match[0], "line": counter, "err": null}); + if (!userTests) { + userTests= []; + } + userTests.push({"text": match[0], "line": counter, "err": null}); counter++; match = regex.exec(userJavaScript); } - if (tests) replaceQuotesInTests(); + if (userTests) { + replaceQuotesInTests(); + } return userJavaScript; }; @@ -170,10 +169,10 @@ function removeLogs(userJavaScript) { var pushed = false; var createTestDisplay = function() { if (pushed) { - tests.pop(); + userTests.pop(); } - for (var i = 0; i < tests.length;i++) { - var test = tests[i]; + for (var i = 0; i < userTests.length;i++) { + var test = userTests[i]; var testDoc = document.createElement("li"); $(testDoc) .addClass('list-group-item') @@ -201,28 +200,40 @@ var reassembleTest = function(test, data) { 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 && tests.length > 0) { - tests = [{text:"Program Execution Failure", err: "No tests were run."}]; + if (err && userTests.length > 0) { + userTests= [{text:"Program Execution Failure", err: "NouserTestswere run."}]; createTestDisplay(); - } else if (tests) { - tests.push(false); + } else if (userTests) { + + userTests.push(false); pushed = true; - tests.forEach(function(test, ix, arr){ + userTests.forEach(function(test, ix, arr){ try { if (test) { var output = eval(reassembleTest(test, data)); } } catch(error) { - + allTestsPassed = false; arr[ix].err = error.name + ":" + error.message; - } finally { + } finally { if (!test) { createTestDisplay(); } } }); } -}; \ No newline at end of file + if (allTestsPassed) { + allTestsPassed = false; + showCompletion(); + } + console.log(allTestsPassed); +}; + +function showCompletion() { + $('#complete-bonfire-dialog').modal('show'); +} \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index ced25759870..60cd47f0112 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,46 +1,64 @@ $(document).ready(function() { - var CSRF_HEADER = 'X-CSRF-Token'; + var CSRF_HEADER = 'X-CSRF-Token'; - var setCSRFToken = function(securityToken) { - jQuery.ajaxPrefilter(function(options, _, xhr) { - if (!xhr.crossDomain) { - xhr.setRequestHeader(CSRF_HEADER, securityToken); - } + var setCSRFToken = function(securityToken) { + jQuery.ajaxPrefilter(function(options, _, xhr) { + if (!xhr.crossDomain) { + xhr.setRequestHeader(CSRF_HEADER, securityToken); + } + }); + }; + + setCSRFToken($('meta[name="csrf-token"]').attr('content')); + + $('.start-challenge').on('click', function() { + $(this).parent().remove(); + $('.challenge-content') + .removeClass('hidden-element') + .addClass('animated fadeInDown'); }); - }; - setCSRFToken($('meta[name="csrf-token"]').attr('content')); + $('.completed-challenge').on('click', function() { + $('#complete-challenge-dialog').modal('show'); + // Only post to server if there is an authenticated user + if ($('.signup-btn-nav').length < 1) { + l = location.pathname.split('/'); + cn = l[l.length - 1]; + $.ajax({ + type: 'POST', + data: {challengeNumber: cn}, + url: '/completed-challenge/' + }); + } + }); - $('.start-challenge').on('click', function() { - $(this).parent().remove(); - $('.challenge-content') - .removeClass('hidden-element') - .addClass('animated fadeInDown'); - }); + $('.completed-bonfire').on('click', function() { + $('#complete-bonfire-dialog').modal('show'); + // Only post to server if there is an authenticated user + if ($('.signup-btn-nav').length < 1) { + l = location.pathname.split('/'); + cn = l[l.length - 1]; + $.ajax({ + type: 'POST', + data: {bonfireHash: cn}, + url: '/completed-bonfire/' + }); + } + }); - $('.completed-challenge').on('click', function() { - $('#complete-dialog').modal('show'); - // Only post to server if there is an authenticated user - if ($('.signup-btn-nav').length < 1) { - l = location.pathname.split('/'); - cn = l[l.length - 1]; - $.ajax({ - type: 'POST', - data: {challengeNumber: cn}, - url: '/completed-challenge/' - }); - } - }); + $('.all-challenges').on('click', function() { + $('#all-challenges-dialog').modal('show'); + }); - $('.all-challenges').on('click', function() { - $('#all-challenges-dialog').modal('show'); - }); + $('.all-bonfires').on('click', function() { + $('#all-bonfires-dialog').modal('show'); + }); - $('.next-button').on('click', function() { - l = location.pathname.split('/'); - window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); - }); + $('.next-button').on('click', function() { + l = location.pathname.split('/'); + window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); + }); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index 79f5f34bfb1..0a5dedfe979 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -1,5 +1,6 @@ [ { + "_id" : "aaa48de84e1ecc7c742e1124", "name": "Palindrome Tester", "difficulty": 1, "description": [ @@ -8,13 +9,11 @@ "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": [ + "tests": [ "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(\"not a palindrome\"), false);", "assert.deepEqual(palindrome(\"A man, a plan, a canal. Panama\"), true);", "assert.deepEqual(palindrome(\"never odd or even\"), true);", "assert.deepEqual(palindrome(\"nope\"), false);" @@ -25,6 +24,7 @@ "challengeEntryPointNegate" : "palindrome\\([^str].*\\;" }, { + "_id" : "ff0395860f5d3034dc0bfc94", "name": "Validate US Telephone Numbers", "difficulty": 3, "description": [ @@ -32,13 +32,11 @@ "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": [ + "tests": [ "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(\"123**&!!asdf#\"), false);", "assert.deepEqual(telephoneCheck(\"55555555\"), false);", "assert.deepEqual(telephoneCheck(\"(6505552368)\"), false);", "assert.deepEqual(telephoneCheck(\"2 (757) 622-7382\"), false);", diff --git a/seed_data/challenge-hashes b/seed_data/challenge-hashes index 8b6ddfa36d1..a03c038d90c 100644 --- a/seed_data/challenge-hashes +++ b/seed_data/challenge-hashes @@ -1,6 +1,6 @@ /* -"aaa48de84e1ecc7c742e1124" -"ff0395860f5d3034dc0bfc94" + + "7123c8c441eddfaeb5bdef0d" "c3a4d278b9e760a0ffe8321f" "aceca143b92049a4392a859e" diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 6b51fe72f1d..9c420e283b2 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -23,7 +23,7 @@ block content .panel-heading.text-center #{name} (Level #{difficulty} bonfire) .panel.panel-body .well - .text-justify.bonfire-instructions + .text-left.bonfire-instructions for sentence in description p.bonfire-instructions!= sentence form.code @@ -42,9 +42,32 @@ block content ul#testSuite.list-group br script(type="text/javascript"). - var publicTests = !{JSON.stringify(publicTests)}; - var privateTests = !{JSON.stringify(privateTests)}; + var tests = !{JSON.stringify(tests)}; var challengeSeed = !{JSON.stringify(challengeSeed)}; var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; - var challengeEntryPointNegate = !{JSON.stringify(challengeEntryPointNegate)}; script(src='/js/lib/bonfire/bonfireFramework.js') + + #complete-bonfire-dialog.modal(tabindex='-1') + .modal-dialog.animated.zoomIn.fast-animation + .modal-content + .modal-header.challenge-list-header Nicely done! + a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × + .modal-body + .text-center + .animated.zoomInDown.delay-half + span.landing-icon.ion-checkmark-circled.text-primary + - if (cc) + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + - if (points && points > 2) + a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank") + i.fa.fa-twitter   + = phrase + - else + a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress + #all-bonfires-dialog.modal(tabindex='-1') + .modal-dialog.animated.fadeInUp.fast-animation + .modal-content + .modal-header.challenge-list-header Challenges + a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × + .modal-body + include ../partials/bonfires diff --git a/views/challenges/show.jade b/views/challenges/show.jade index 36d9dc8e0dc..685f9d2bc99 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -16,7 +16,7 @@ block content .btn.btn-primary.btn-big.btn-block.completed-challenge I've completed this challenge .ten-pixel-break .btn.btn-success.btn-big.btn-block.all-challenges Show me all the challenges - #complete-dialog.modal(tabindex='-1') + #complete-challenge-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation .modal-content .modal-header.challenge-list-header Nicely done! diff --git a/views/partials/bonfires.jade b/views/partials/bonfires.jade index 1adbb74b8d2..cabd9ecf93a 100644 --- a/views/partials/bonfires.jade +++ b/views/partials/bonfires.jade @@ -1 +1,7 @@ -//TODO: STUFF \ No newline at end of file +h3 + ol(start='0') + for bonfire in bonfires + li + a(href="/bonfires/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name} + |   (Level #{bonfire.difficulty}) +a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((cc && cc[53] === 0) || (!cc)) ? 'disabled' : '' }") I'm done with all the challenges! \ No newline at end of file From a3cf6c3ee289092a77fd63c9536e6bee2eb9ebec Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 03:11:01 -0500 Subject: [PATCH 02/39] Persisting completion of bonfire challenges into User model, split routes for bonfires and challenges, refactored class selectors to be challenge type specific --- app.js | 69 ++++-- controllers/bonfire.js | 5 +- models/User.js | 255 ++-------------------- public/js/lib/bonfire/bonfireFramework.js | 1 - public/js/main.js | 26 ++- views/bonfire/show.jade | 12 +- views/challenges/show.jade | 2 +- 7 files changed, 106 insertions(+), 264 deletions(-) diff --git a/app.js b/app.js index bbae2fd4ba8..824cbde19b5 100644 --- a/app.js +++ b/app.js @@ -251,7 +251,7 @@ app.all('/account', passportConf.isAuthenticated); app.get('/account/api', userController.getAccountAngular); app.get('/bonfire', bonfireController.index); app.get( - '/bonfire/:bonfireNumber', + '/bonfires/:bonfireNumber', bonfireController.returnBonfire ); @@ -274,29 +274,68 @@ app.get('/account/unlink/:provider', userController.getOauthUnlink); app.post('/completed-challenge', function (req, res) { req.user.challengesHash[parseInt(req.body.challengeNumber)] = Math.round(+new Date() / 1000); - var ch = req.user.challengesHash; - var p = 0; - for (var k in ch) { - if (ch[k] > 0) { - p += 1; + var timestamp = req.user.challengesHash; + var points = 0; + for (var key in timestamp) { + if (timestamp[key] > 0) { + points += 1; } } - req.user.points = p; + req.user.points = points; req.user.save(); }); app.post('/completed-bonfire/', function (req, res) { - req.user.challengesHash[parseInt(req.body.challengeNumber)] = + debug(req.body, 'In post method'); // TODO: remove debug statement + req.user.bonfiresHash[parseInt(req.body.bonfireNumber)] = Math.round(+new Date() / 1000); - var ch = req.user.challengesHash; - var p = 0; - for (var k in ch) { - if (ch[k] > 0) { - p += 1; + var timestamp = req.user.bonfiresHash; + var points = 0; + for (var key in timestamp) { + if (timestamp[key] > 0) { + points += 1; } } - req.user.points = p; - req.user.save(); + + var isCompletedWith = req.body.bonfireInfo.completedWith || undefined; + var isCompletedDate = Math.round(+new Date() / 1000); + var bonfireHash = req.body.bonfireInfo.bonfireHash; + var isSolution = req.body.bonfireInfo.solution; + req.user.bonfiresHash[bonfireHash] = { + completedWith: isCompletedWith, + completedDate: isCompletedDate, + solution: isSolution + }; + + if (isCompletedWith) { + User.find({"profile.username": isCompletedWith}, function(err, pairedWith) { + if (err) { + return err; + } else { + pairedWith.bonfiresHash[bonfireHash] = { + completedWith: req.user._id, + completedDate: isCompletedDate, + solution: isSolution + }; + req.user.bonfiresHash[bonfireHash] = { + completedWith: pairedWith._id, + completedDate: isCompletedDate, + solution: isSolution + }; + + req.user.save(); + pairedWith.save(); + + } + }) + } else { + req.user.bonfiresHash[bonfireHash] = { + completedWith: null, + completedDate: isCompletedDate, + solution: isSolution + }; + req.user.save(); + } }); /** diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 958663f6e4e..580e47e71fb 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -56,7 +56,9 @@ exports.returnBonfire = function(req, res, next) { debug('bonfire err: ', err); next(err); } + debug(bonfire[bonfireNumber]._id); res.render('bonfire/show', { + completedWith: null, title: bonfire[bonfireNumber].name, name: bonfire[bonfireNumber].name, number: bonfire[bonfireNumber].bonfireNumber, @@ -69,7 +71,8 @@ exports.returnBonfire = function(req, res, next) { 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 + bonfires: bonfire, + bonfireHash: bonfire[bonfireNumber]._id }); }); }; \ No newline at end of file diff --git a/models/User.js b/models/User.js index 81a33ef72d5..84cd18d6002 100644 --- a/models/User.js +++ b/models/User.js @@ -354,247 +354,24 @@ var userSchema = new mongoose.Schema({ resetPasswordToken: String, resetPasswordExpires: Date, bonfiresHash: { - 0: { - type: Boolean, - default: 0 + aaa48de84e1ecc7c742e1124: { + completedWith: String, + completedDate: { + type: Number, + default: 0 + }, + solution: String }, - 1: { - type: Boolean, - default: 0 - }, - 2: { - type: Boolean, - default: 0 - }, - 3: { - type: Boolean, - default: 0 - }, - 4: { - type: Boolean, - default: 0 - }, - 5: { - type: Boolean, - default: 0 - }, - 6: { - type: Boolean, - default: 0 - }, - 7: { - type: Boolean, - default: 0 - }, - 8: { - type: Boolean, - default: 0 - }, - 9: { - type: Boolean, - default: 0 - }, - 10: { - type: Boolean, - default: 0 - }, - 11: { - type: Boolean, - default: 0 - }, - 12: { - type: Boolean, - default: 0 - }, - 13: { - type: Boolean, - default: 0 - }, - 14: { - type: Boolean, - default: 0 - }, - 15: { - type: Boolean, - default: 0 - }, - 16: { - type: Boolean, - default: 0 - }, - 17: { - type: Boolean, - default: 0 - }, - 18: { - type: Boolean, - default: 0 - }, - 19: { - type: Boolean, - default: 0 - }, - 20: { - type: Boolean, - default: 0 - }, - 21: { - type: Boolean, - default: 0 - }, - 22: { - type: Boolean, - default: 0 - }, - 23: { - type: Boolean, - default: 0 - }, - 24: { - type: Boolean, - default: 0 - }, - 25: { - type: Boolean, - default: 0 - }, - 26: { - type: Boolean, - default: 0 - }, - 27: { - type: Boolean, - default: 0 - }, - 28: { - type: Boolean, - default: 0 - }, - 29: { - type: Boolean, - default: 0 - }, - 30: { - type: Boolean, - default: 0 - }, - 31: { - type: Boolean, - default: 0 - }, - 32: { - type: Boolean, - default: 0 - }, - 33: { - type: Boolean, - default: 0 - }, - 34: { - type: Boolean, - default: 0 - }, - 35: { - type: Boolean, - default: 0 - }, - 36: { - type: Boolean, - default: 0 - }, - 37: { - type: Boolean, - default: 0 - }, - 38: { - type: Boolean, - default: 0 - }, - 39: { - type: Boolean, - default: 0 - }, - 40: { - type: Boolean, - default: 0 - }, - 41: { - type: Boolean, - default: 0 - }, - 42: { - type: Boolean, - default: 0 - }, - 43: { - type: Boolean, - default: 0 - }, - 44: { - type: Boolean, - default: 0 - }, - 45: { - type: Boolean, - default: 0 - }, - 46: { - type: Boolean, - default: 0 - }, - 47: { - type: Boolean, - default: 0 - }, - 48: { - type: Boolean, - default: 0 - }, - 49: { - type: Boolean, - default: 0 - }, - 50: { - type: Boolean, - default: 0 - }, - 51: { - type: Boolean, - default: 0 - }, - 52: { - type: Boolean, - default: 0 - }, - 53: { - type: Boolean, - default: 0 - }, - 54: { - type: Boolean, - default: 0 - }, - 55: { - type: Boolean, - default: 0 - }, - 56: { - type: Boolean, - default: 0 - }, - 57: { - type: Boolean, - default: 0 - }, - 58: { - type: Boolean, - default: 0 - }, - 59: { - type: Boolean, - default: 0 + ff0395860f5d3034dc0bfc94: { + completedWith: String, + completedDate: { + type: Number, + default: 0 + }, + solution: String } - } + }, + bonfires: Array }); /** diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index fe844286deb..aea21a58ea3 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -231,7 +231,6 @@ var runTests = function(err, data) { allTestsPassed = false; showCompletion(); } - console.log(allTestsPassed); }; function showCompletion() { diff --git a/public/js/main.js b/public/js/main.js index 60cd47f0112..cbe0fca6c7f 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -33,7 +33,7 @@ $(document).ready(function() { } }); - $('.completed-bonfire').on('click', function() { + function completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash) { $('#complete-bonfire-dialog').modal('show'); // Only post to server if there is an authenticated user if ($('.signup-btn-nav').length < 1) { @@ -41,11 +41,18 @@ $(document).ready(function() { cn = l[l.length - 1]; $.ajax({ type: 'POST', - data: {bonfireHash: cn}, + data: { + bonfireInfo: { + completedWith : didCompleteWith, + solution: bonfireSolution, + bonfireHash: thisBonfireHash + } + }, url: '/completed-bonfire/' }); + console.log(didCompleteWith, bonfireSolution, thisBonfireHash); // TODO: remove debug statement } - }); + } $('.all-challenges').on('click', function() { $('#all-challenges-dialog').modal('show'); @@ -55,10 +62,21 @@ $(document).ready(function() { $('#all-bonfires-dialog').modal('show'); }); - $('.next-button').on('click', function() { + $('.next-challenge-button').on('click', function() { l = location.pathname.split('/'); window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); }); + + // TODO: refactor this to create meaningful variable names, what the heck i l? + $('.next-bonfire-button').on('click', function() { + var bonfireSolution = myCodeMirror.getValue(); + var thisBonfireHash = passedBonfireHash || null; + var didCompleteWith = $('#completed-with').text() || null; + console.log(bonfireSolution, thisBonfireHash); + completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash); + l = location.pathname.split('/'); + window.location = '/bonfires/' + (parseInt(l[l.length - 1]) + 1); + }); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 9c420e283b2..f91b79d695f 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -45,8 +45,11 @@ block content var tests = !{JSON.stringify(tests)}; var challengeSeed = !{JSON.stringify(challengeSeed)}; var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var passedBonfireHash = !{JSON.stringify(bonfireHash)}; + console.log(passedBonfireHash); script(src='/js/lib/bonfire/bonfireFramework.js') + #complete-bonfire-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation .modal-content @@ -57,9 +60,12 @@ block content .animated.zoomInDown.delay-half span.landing-icon.ion-checkmark-circled.text-primary - if (cc) - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + + - if (points && points > 2) - a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank") + a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") i.fa.fa-twitter   = phrase - else @@ -67,7 +73,7 @@ block content #all-bonfires-dialog.modal(tabindex='-1') .modal-dialog.animated.fadeInUp.fast-animation .modal-content - .modal-header.challenge-list-header Challenges + .modal-header.challenge-list-header Bonfires a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × .modal-body include ../partials/bonfires diff --git a/views/challenges/show.jade b/views/challenges/show.jade index 685f9d2bc99..aa1bd9960f7 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -26,7 +26,7 @@ block content .animated.zoomInDown.delay-half span.landing-icon.ion-checkmark-circled.text-primary - if (cc) - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-challenge-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge - if (points && points > 2) a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank") i.fa.fa-twitter   From 022b8daddc441043340225a80bb71c8f94c21129 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 04:14:41 -0500 Subject: [PATCH 03/39] Adding in existing username checking, NOT WORKING AT THE MOMENT --- app.js | 4 +++- controllers/user.js | 14 ++++++++++++++ public/js/main.js | 31 +++++++++++++++++++++++++++++++ views/bonfire/show.jade | 11 +++++++++-- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 824cbde19b5..8f572959e53 100644 --- a/app.js +++ b/app.js @@ -257,6 +257,7 @@ app.get( // Unique Check API route app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); +app.get('/api/checkExistingUsername/:username', userController.checkExistingUsername); app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail); app.get('/account', userController.getAccount); app.post('/account/profile', userController.postUpdateProfile); @@ -286,7 +287,8 @@ app.post('/completed-challenge', function (req, res) { }); app.post('/completed-bonfire/', function (req, res) { - debug(req.body, 'In post method'); // TODO: remove debug statement + debug(req.body, 'In post method' + ); // TODO: remove debug statement req.user.bonfiresHash[parseInt(req.body.bonfireNumber)] = Math.round(+new Date() / 1000); var timestamp = req.user.bonfiresHash; diff --git a/controllers/user.js b/controllers/user.js index 97ea084550f..e0e9ae3d7f1 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -184,6 +184,20 @@ exports.checkUniqueUsername = function(req, res) { } }); }; + +/** + * Existing username check + */ +exports.checkExistingUsername = function(req, res) { + User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) { + if (data == 1) { + return res.send(false); + } else { + return res.send(true); + } + }); +}; + /** * Unique email check API Call */ diff --git a/public/js/main.js b/public/js/main.js index cbe0fca6c7f..4c812dae1dd 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -94,6 +94,13 @@ profileValidation.controller('profileValidationController', ['$scope', '$http', } ]); +profileValidation.controller('pairedWithController', ['$scope', + function($scope) { + + + } +]); + profileValidation.controller('emailSignUpController', ['$scope', function($scope) { @@ -138,6 +145,30 @@ profileValidation.directive('uniqueUsername', function($http) { } } }); +// TODO: FIX THIS +profileValidation.directive('existingUsername', function($http) { + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, element, attrs, ngModel) { + element.bind("keyup", function (event) { + ngModel.$setValidity('exists', true); + if (element.val()) { + $http.get("/api/checkExistingUsername/" + element.val()).success(function (data) { + console.log('in existing username function'); + if (element.val() == scope.existingUsername) { + console.log('matches a username'); + ngModel.$setValidity('exists', true); + } else if (data) { + console.log("doesn't match a username") + ngModel.$setValidity('exists', false); + } + }); + } + }); + } + } +}); profileValidation.directive('uniqueEmail', function($http) { return { diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index f91b79d695f..72286d661ba 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -55,13 +55,20 @@ block content .modal-content .modal-header.challenge-list-header Nicely done! a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × - .modal-body + .modal-body(ng-controller="pairedWithController") + .text-center .animated.zoomInDown.delay-half span.landing-icon.ion-checkmark-circled.text-primary - if (cc) + form.form-horizontal(novalidate='novalidate', name='completedWithForm') - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + input.form-control#completed-with(name="completed-with", placeholder="If you paired with someone, enter their username here", ng-keypress='', existing-username='', ng-model="existingUsername") + .col-sm-4.col-sm-offset-5(ng-show="completedWithForm.existingUsername.$error.unique && !completedWithForm.existingUsername.$pristine") + alert(type='danger') + span.ion-close-circled + | Username not found + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid') Take me to my next challenge - if (points && points > 2) From 4128b3b76523966fea2449dd50d54d95e3c168c6 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 12:51:53 -0500 Subject: [PATCH 04/39] Existing username check now working, error box showing and hiding properly, UI tweaks --- controllers/user.js | 6 ++++-- public/js/main.js | 14 +++++++------- views/bonfire/show.jade | 15 +++++++++------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/controllers/user.js b/controllers/user.js index e0e9ae3d7f1..07edf0e9f51 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -191,9 +191,11 @@ exports.checkUniqueUsername = function(req, res) { exports.checkExistingUsername = function(req, res) { User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) { if (data == 1) { - return res.send(false); - } else { + debug('sending false back') return res.send(true); + } else { + debug('sending true back') + return res.send(false); } }); }; diff --git a/public/js/main.js b/public/js/main.js index 4c812dae1dd..c817b94a0de 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -96,7 +96,7 @@ profileValidation.controller('profileValidationController', ['$scope', '$http', profileValidation.controller('pairedWithController', ['$scope', function($scope) { - + $scope.existingUser = null; } ]); @@ -152,16 +152,16 @@ profileValidation.directive('existingUsername', function($http) { require: 'ngModel', link: function (scope, element, attrs, ngModel) { element.bind("keyup", function (event) { - ngModel.$setValidity('exists', true); + ngModel.$setValidity('exists', false); if (element.val()) { - $http.get("/api/checkExistingUsername/" + element.val()).success(function (data) { + $http.get("/api/checkExistingUsername/" + element.val() + ' ').success(function (data) { console.log('in existing username function'); if (element.val() == scope.existingUsername) { - console.log('matches a username'); - ngModel.$setValidity('exists', true); - } else if (data) { - console.log("doesn't match a username") + console.log("doesn't match a username"); ngModel.$setValidity('exists', false); + } else if (data) { + console.log("matches a username") + ngModel.$setValidity('exists', true); } }); } diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 72286d661ba..2145bac0091 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -62,13 +62,16 @@ block content span.landing-icon.ion-checkmark-circled.text-primary - if (cc) form.form-horizontal(novalidate='novalidate', name='completedWithForm') + .form-group.text-center + label(for="existingUserName") If you paired with someone, enter their username here. + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 + input.form-control#completed-with(name="existingUser", placeholder='Otherwise just leave it blank', existing-username='', ng-model="existingUser") + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine") + alert(type='danger') + span.ion-close-circled + | Username not found - input.form-control#completed-with(name="completed-with", placeholder="If you paired with someone, enter their username here", ng-keypress='', existing-username='', ng-model="existingUsername") - .col-sm-4.col-sm-offset-5(ng-show="completedWithForm.existingUsername.$error.unique && !completedWithForm.existingUsername.$pristine") - alert(type='danger') - span.ion-close-circled - | Username not found - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid') Take me to my next challenge + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid') Take me to my next challenge - if (points && points > 2) From 0ca042a1f00066596afef990335cfa5f8fc609bb Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 13:40:58 -0500 Subject: [PATCH 05/39] Paired with is now properly saved, bonfire challenge framework is almost ready for release --- app.js | 2 +- controllers/bonfire.js | 2 -- public/js/main.js | 3 ++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 8f572959e53..d15af736844 100644 --- a/app.js +++ b/app.js @@ -310,7 +310,7 @@ app.post('/completed-bonfire/', function (req, res) { }; if (isCompletedWith) { - User.find({"profile.username": isCompletedWith}, function(err, pairedWith) { + User.findOne({"profile.username": isCompletedWith}, function(err, pairedWith) { if (err) { return err; } else { diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 580e47e71fb..866fdc87cba 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -53,10 +53,8 @@ exports.returnBonfire = function(req, res, next) { Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) { debug(bonfire[bonfireNumber].challengeEntryPoint); if (err) { - debug('bonfire err: ', err); next(err); } - debug(bonfire[bonfireNumber]._id); res.render('bonfire/show', { completedWith: null, title: bonfire[bonfireNumber].name, diff --git a/public/js/main.js b/public/js/main.js index c817b94a0de..5c8b5c6c3e3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -71,7 +71,8 @@ $(document).ready(function() { $('.next-bonfire-button').on('click', function() { var bonfireSolution = myCodeMirror.getValue(); var thisBonfireHash = passedBonfireHash || null; - var didCompleteWith = $('#completed-with').text() || null; + var didCompleteWith = $('#completed-with').val() || null; + console.log(didCompleteWith); console.log(bonfireSolution, thisBonfireHash); completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash); l = location.pathname.split('/'); From a7f228c410d50acd50c1fa67d924dc3e26a26095 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 14:21:44 -0500 Subject: [PATCH 06/39] Refactor db read/write ops to use find instead of findOne to improve performance --- app.js | 4 +++- controllers/bonfire.js | 2 +- public/js/main.js | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index d15af736844..18a6516ce4d 100644 --- a/app.js +++ b/app.js @@ -310,10 +310,12 @@ app.post('/completed-bonfire/', function (req, res) { }; if (isCompletedWith) { - User.findOne({"profile.username": isCompletedWith}, function(err, pairedWith) { + var paired = User.find({"profile.username": isCompletedWith}).limit(1); + paired.exec(function(err, pairedWith) { if (err) { return err; } else { + pairedWith = pairedWith.pop(); pairedWith.bonfiresHash[bonfireHash] = { completedWith: req.user._id, completedDate: isCompletedDate, diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 866fdc87cba..34311cb3a30 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -50,7 +50,7 @@ exports.returnBonfire = function(req, res, next) { ]; if (bonfireNumber > highestBonfireNumber) { bonfireNumber = 0; } - Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) { + Bonfire.find({}, null, { sort: { difficulty: 1, bonfireNumber: 1 } }, function(err, bonfire) { debug(bonfire[bonfireNumber].challengeEntryPoint); if (err) { next(err); diff --git a/public/js/main.js b/public/js/main.js index 5c8b5c6c3e3..b333e61ad6d 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -50,7 +50,6 @@ $(document).ready(function() { }, url: '/completed-bonfire/' }); - console.log(didCompleteWith, bonfireSolution, thisBonfireHash); // TODO: remove debug statement } } From 1b75a6a7b88ded498ffcdfd2a678237d06686417 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 11:31:56 -0800 Subject: [PATCH 07/39] only pad navbar rightmost element if not authenticated --- views/partials/navbar.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 6a026ed5057..e42d08e94c7 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -30,8 +30,8 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height a(href='http://forum.freecodecamp.com' target='_blank') Forum li a(href='/bonfire') Bonfire - li       if !user + li       li a.btn.signup-btn.signup-btn-nav(href='/login') Sign in else From c4bd0e4dc3447810bc728eb95ebf5e5852f77232 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 14:29:50 -0800 Subject: [PATCH 08/39] make bonfire dynamically direct to best subsequent bonfire after completion --- app.js | 3 +- controllers/bonfire.js | 76 ++++++++++++++++++++++++++++++++++++-- models/User.js | 2 + views/partials/footer.jade | 4 +- 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index ecc99cd09bc..9966d52262a 100644 --- a/app.js +++ b/app.js @@ -258,8 +258,9 @@ app.get('/account/api', userController.getAccountAngular); app.get('/bonfire', bonfireController.index); app.get( '/bonfires/:bonfireNumber', - bonfireController.returnBonfire + bonfireController.returnIndividualBonfire ); +app.get('/bonfires', bonfireController.returnBonfire); // Unique Check API route app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 34311cb3a30..495a27e1d22 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -1,6 +1,7 @@ var _ = require('lodash'), debug = require('debug')('freecc:cntr:bonfires'), - Bonfire = require('./../models/Bonfire'); + Bonfire = require('./../models/Bonfire'), + User = require('./../models/User'); /** * Bonfire controller @@ -49,9 +50,78 @@ exports.returnBonfire = function(req, res, next) { "Prove to mom that computers aren't just for games" ]; + + // This code is in bad need of refactoring + var bonfiresToFind = req.user.bonfiresHash; + var bonfiresArray = _.map(bonfiresToFind, function(value, index) { + return [index, value]; + }); + // Get rid of the first entry, which will be a useless function that causes an error. + bonfiresArray.shift(); + unsolvedBonfires = []; + for (i = 0; i < bonfiresArray.length; i++) { + if (bonfiresArray[i][1]["completedDate"] === 0) { + unsolvedBonfires.push(bonfiresArray[i][0]) + } + } + + //.where('likes').in(['vaporizing', 'talking']) + var displayedBonfires = Bonfire.find({}).where('_id').in(unsolvedBonfires).sort({ difficulty: 1 }); + displayedBonfires.exec(function(err, bonfire) { + if (err) { + next(err); + } + res.render('bonfire/show', { + completedWith: null, + title: bonfire[bonfireNumber].name, + name: bonfire[bonfireNumber].name, + number: bonfire[bonfireNumber].bonfireNumber, + difficulty: bonfire[bonfireNumber].difficulty, + description: bonfire[bonfireNumber].description, + tests: bonfire[bonfireNumber].tests, + challengeSeed: bonfire[bonfireNumber].challengeSeed, + challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + 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, + bonfireHash: bonfire[bonfireNumber]._id + }); + }); +}; + +exports.returnIndividualBonfire = 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: { difficulty: 1, bonfireNumber: 1 } }, function(err, bonfire) { - debug(bonfire[bonfireNumber].challengeEntryPoint); + Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) { if (err) { next(err); } diff --git a/models/User.js b/models/User.js index 84cd18d6002..f4a4faacce0 100644 --- a/models/User.js +++ b/models/User.js @@ -355,6 +355,7 @@ var userSchema = new mongoose.Schema({ resetPasswordExpires: Date, bonfiresHash: { aaa48de84e1ecc7c742e1124: { + hash: String, completedWith: String, completedDate: { type: Number, @@ -363,6 +364,7 @@ var userSchema = new mongoose.Schema({ solution: String }, ff0395860f5d3034dc0bfc94: { + hash: String, completedWith: String, completedDate: { type: Number, diff --git a/views/partials/footer.jade b/views/partials/footer.jade index 0e1ccbe8a37..f4d23dd17d1 100644 --- a/views/partials/footer.jade +++ b/views/partials/footer.jade @@ -15,4 +15,6 @@ |   a.ion-information-circled(title="About Free Code Camp", href="/learn-to-code") |   - a.ion-locked(title="Free Code Camp's Privacy Policy", href="/privacy") \ No newline at end of file + a.ion-locked(title="Free Code Camp's Privacy Policy", href="/privacy") + |   + a.ion-code-working(title="Bonfire Coding Playground", href="/bonfire") \ No newline at end of file From 54c33052813f24d317c34d2dd0b08a3cf9cf5f08 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 14:42:34 -0800 Subject: [PATCH 09/39] change bonfire path to playground and update links, and also add a should example test to playground --- app.js | 2 +- public/js/lib/bonfire/bonfireFramework.js | 6 ++++-- views/bonfire/bonfire.jade | 2 +- views/partials/footer.jade | 2 +- views/partials/navbar.jade | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 9966d52262a..1b00f76de3a 100644 --- a/app.js +++ b/app.js @@ -255,7 +255,7 @@ app.get( ); app.all('/account', passportConf.isAuthenticated); app.get('/account/api', userController.getAccountAngular); -app.get('/bonfire', bonfireController.index); +app.get('/playground', bonfireController.index); app.get( '/bonfires/:bonfireNumber', bonfireController.returnIndividualBonfire diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index aea21a58ea3..adb0f362ef5 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -35,6 +35,8 @@ var nonChallengeValue = '/*Welcome to Bonfire, Free Code Camp\'s future CoderByt '}\n' + 'expect(test()).to.be.a("array");\n\n' + 'assert.deepEqual(test(), [1,4,9]);\n\n' + + 'var foo = test();\n' + + 'foo.should.be.a("array");\n\n' + 'test();'; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { @@ -139,8 +141,8 @@ var scrapeTests = function(userJavaScript) { while (match != null) { var replacement = '//' + counter + testSalt; userJavaScript = userJavaScript.substring(0, match.index) - + replacement - + userJavaScript.substring(match.index + match[0].length); + + replacement + + userJavaScript.substring(match.index + match[0].length); if (!userTests) { userTests= []; diff --git a/views/bonfire/bonfire.jade b/views/bonfire/bonfire.jade index 8fcf04a9ed2..62894f16303 100644 --- a/views/bonfire/bonfire.jade +++ b/views/bonfire/bonfire.jade @@ -17,7 +17,7 @@ block content .row #mainEditorPanel.col-sm-12.col-md-7.col-xs-12 .panel.panel-primary.panel-bonfire - .panel-heading.text-center Bonfire Playground + .panel-heading.text-center Playground .panel.panel-body form.code .form-group.codeMirrorView diff --git a/views/partials/footer.jade b/views/partials/footer.jade index f4d23dd17d1..8651171bf62 100644 --- a/views/partials/footer.jade +++ b/views/partials/footer.jade @@ -17,4 +17,4 @@ |   a.ion-locked(title="Free Code Camp's Privacy Policy", href="/privacy") |   - a.ion-code-working(title="Bonfire Coding Playground", href="/bonfire") \ No newline at end of file + a.ion-code-working(title="Bonfire Coding Playground", href="/playground") \ No newline at end of file diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index e42d08e94c7..e6ca1aba4f3 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -29,7 +29,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height li a(href='http://forum.freecodecamp.com' target='_blank') Forum li - a(href='/bonfire') Bonfire + a(href='/bonfires') Bonfires if !user li       li From 65384b29138e9f7e29cb818b02c48e965e007704 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 15:20:48 -0800 Subject: [PATCH 10/39] add @ sign and autofocus to twitter form --- views/bonfire/show.jade | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 2145bac0091..39bf14d496c 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -65,7 +65,11 @@ block content .form-group.text-center label(for="existingUserName") If you paired with someone, enter their username here. .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 - input.form-control#completed-with(name="existingUser", placeholder='Otherwise just leave it blank', existing-username='', ng-model="existingUser") + .input-group.twitter-input + // extra field to distract password tools like lastpass from injecting css into our username field + input.form-control(ng-show="false") + span.input-group-addon @ + input.form-control#completed-with(name="existingUser", placeholder='Otherwise just leave it blank', existing-username='', ng-model="existingUser", autofocus) .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine") alert(type='danger') span.ion-close-circled From 7502dc0689ca5689b9dfdc6171b1f9044f938d3c Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 18:33:00 -0500 Subject: [PATCH 11/39] Allowing user to delete attempted username input from bonfire challenges completion modal --- views/bonfire/show.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 39bf14d496c..9100cd6575e 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -70,12 +70,12 @@ block content input.form-control(ng-show="false") span.input-group-addon @ input.form-control#completed-with(name="existingUser", placeholder='Otherwise just leave it blank', existing-username='', ng-model="existingUser", autofocus) - .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine") + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0") alert(type='danger') span.ion-close-circled | Username not found - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid') Take me to my next challenge + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Take me to my next challenge - if (points && points > 2) From 0ba1ecd30a3e925f95bdc7559f24fba56ba74cf8 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 15:40:11 -0800 Subject: [PATCH 12/39] add @freecodecamp to generated tweet messages --- views/bonfire/show.jade | 2 +- views/challenges/show.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 39bf14d496c..0ead2ad0074 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -79,7 +79,7 @@ block content - if (points && points > 2) - a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") + a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") i.fa.fa-twitter   = phrase - else diff --git a/views/challenges/show.jade b/views/challenges/show.jade index aa1bd9960f7..3111eb1edd0 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -28,7 +28,7 @@ block content - if (cc) a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-challenge-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge - if (points && points > 2) - a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank") + a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank") i.fa.fa-twitter   = phrase - else From 966eb3d073f48d410fc3f3e08f8433c16ba372f5 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 19:16:28 -0500 Subject: [PATCH 13/39] More cleanup of input field after user error --- public/js/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index b333e61ad6d..c04ee6e31c8 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -97,7 +97,6 @@ profileValidation.controller('profileValidationController', ['$scope', '$http', profileValidation.controller('pairedWithController', ['$scope', function($scope) { $scope.existingUser = null; - } ]); @@ -152,15 +151,16 @@ profileValidation.directive('existingUsername', function($http) { require: 'ngModel', link: function (scope, element, attrs, ngModel) { element.bind("keyup", function (event) { - ngModel.$setValidity('exists', false); + if (element.val().length > 0) { + ngModel.$setValidity('exists', false); + } else { + ngModel.$setPristine(); + } if (element.val()) { $http.get("/api/checkExistingUsername/" + element.val() + ' ').success(function (data) { - console.log('in existing username function'); if (element.val() == scope.existingUsername) { - console.log("doesn't match a username"); ngModel.$setValidity('exists', false); } else if (data) { - console.log("matches a username") ngModel.$setValidity('exists', true); } }); From 3c3dc803c5ff55dcf8cdb1c4d08d0ba3b99d75ea Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 24 Jan 2015 20:49:59 -0500 Subject: [PATCH 14/39] Show long/short form instructions based on user input --- controllers/bonfire.js | 6 ++++-- public/css/main.less | 10 ++++++---- public/js/main.js | 11 +++++++++++ views/bonfire/show.jade | 20 +++++++++++++++++--- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 495a27e1d22..d88a9d4c456 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -77,7 +77,8 @@ exports.returnBonfire = function(req, res, next) { name: bonfire[bonfireNumber].name, number: bonfire[bonfireNumber].bonfireNumber, difficulty: bonfire[bonfireNumber].difficulty, - description: bonfire[bonfireNumber].description, + brief: bonfire[bonfireNumber].description[0], + details: bonfire[bonfireNumber].description.slice(1), tests: bonfire[bonfireNumber].tests, challengeSeed: bonfire[bonfireNumber].challengeSeed, challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, @@ -131,7 +132,8 @@ exports.returnIndividualBonfire = function(req, res, next) { name: bonfire[bonfireNumber].name, number: bonfire[bonfireNumber].bonfireNumber, difficulty: bonfire[bonfireNumber].difficulty, - description: bonfire[bonfireNumber].description, + brief: bonfire[bonfireNumber].description[0], + details: bonfire[bonfireNumber].description.slice(1), tests: bonfire[bonfireNumber].tests, challengeSeed: bonfire[bonfireNumber].challengeSeed, challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, diff --git a/public/css/main.less b/public/css/main.less index 0630a4e272a..2938ac72dd1 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -205,6 +205,11 @@ ul { animation-duration: 0.5s; } +.slow-animation { + -webkit-animation-duration: 1.5s; + animation-duration: 1.5s; +} + .disabled { pointer-events: none; cursor: default; @@ -569,11 +574,8 @@ div.CodeMirror-scroll { text-size: 250px; } -.bonfire-instructions p { - padding: 0; -} .bonfire-instructions { - margin-bottom: 2px; + margin-bottom: 5px; } //uncomment this to see the dimensions of all elements outlined in red diff --git a/public/js/main.js b/public/js/main.js index c04ee6e31c8..bd96ace1aca 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -77,6 +77,17 @@ $(document).ready(function() { l = location.pathname.split('/'); window.location = '/bonfires/' + (parseInt(l[l.length - 1]) + 1); }); + + // Bonfire instructions functions + $('#more-info').on('click', function() { + $('#brief-instructions').hide(); + $('#long-instructions').show().removeClass('hide'); + + }); + $('#less-info').on('click', function() { + $('#brief-instructions').show(); + $('#long-instructions').hide(); + }); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 9100cd6575e..51d88fe64e1 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -23,9 +23,23 @@ block content .panel-heading.text-center #{name} (Level #{difficulty} bonfire) .panel.panel-body .well - .text-left.bonfire-instructions - for sentence in description - p.bonfire-instructions!= sentence + .row.text-center + row.text-center + .col-xs-12 + .bonfire-instructions + = brief + #brief-instructions.col-xs-12 + button#more-info.btn.btn-info + span.ion-help-circled + | More information + #long-instructions.row.text-center.hide + .col-xs-12 + .bonfire-instructions + for sentence in details + p!= sentence + button#less-info.btn.btn-info + span.ion-help-circled + | Less information form.code .form-group.codeMirrorView textarea#codeEditor(autofocus=true) From 0e63521ee0ea0041e76c132687b3d0c413ab9262 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 24 Jan 2015 23:03:25 -0800 Subject: [PATCH 15/39] refactor verbs, compliments and phrases into resources controller and require them where necessary. Create tutorial bonfire. Clean up existing bonfires. Fix minor ui issues. --- controllers/bonfire.js | 35 +++------------ controllers/challenges.js | 51 +++------------------ controllers/resources.json | 91 +++++++++++++++++++++++++++++++++++++- public/css/main.less | 1 + seed_data/bonfires.json | 40 ++++++++++++++--- seed_data/challenge-hashes | 1 - views/bonfire/show.jade | 11 +++-- views/challenges/show.jade | 2 +- 8 files changed, 144 insertions(+), 88 deletions(-) diff --git a/controllers/bonfire.js b/controllers/bonfire.js index d88a9d4c456..25dbcdab1bf 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -1,7 +1,11 @@ var _ = require('lodash'), debug = require('debug')('freecc:cntr:bonfires'), Bonfire = require('./../models/Bonfire'), - User = require('./../models/User'); + User = require('./../models/User'), + resources = require('./resources.json'), + phrases = resources.phrases, + verbs = resources.verbs, + compliments = resources.compliments; /** * Bonfire controller @@ -24,33 +28,6 @@ exports.index = function(req, res) { 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" - ]; - - // This code is in bad need of refactoring var bonfiresToFind = req.user.bonfiresHash; var bonfiresArray = _.map(bonfiresToFind, function(value, index) { @@ -86,6 +63,7 @@ exports.returnBonfire = function(req, res, next) { points: req.user ? req.user.points : undefined, verb: verbs[Math.floor(Math.random() * verbs.length)], phrase: phrases[Math.floor(Math.random() * phrases.length)], + compliments: compliments[Math.floor(Math.random() * phrases.length)], bonfires: bonfire, bonfireHash: bonfire[bonfireNumber]._id }); @@ -141,6 +119,7 @@ exports.returnIndividualBonfire = function(req, res, next) { points: req.user ? req.user.points : undefined, verb: verbs[Math.floor(Math.random() * verbs.length)], phrase: phrases[Math.floor(Math.random() * phrases.length)], + compliment: compliments[Math.floor(Math.random() * phrases.length)], bonfires: bonfire, bonfireHash: bonfire[bonfireNumber]._id }); diff --git a/controllers/challenges.js b/controllers/challenges.js index 9d3b325df64..401940ca291 100644 --- a/controllers/challenges.js +++ b/controllers/challenges.js @@ -4,56 +4,16 @@ */ var _ = require('lodash'), debug = require('debug')('freecc:cntr:challenges'), - Challenge = require('./../models/Challenge'); + Challenge = require('./../models/Challenge'), + resources = require('./resources.json'), + phrases = resources.phrases, + verbs = resources.verbs, + compliments = resources.compliments; var highestChallengeNumber = 53; exports.returnChallenge = function(req, res, next) { var challengeNumber = parseInt(req.params.challengeNumber) || 0; - var verbs = [ - 'ACED', - 'NAILED', - 'ROCKED', - 'SCORCHED', - 'DEVASTATED', - 'OWNED', - 'CRUSHED', - 'CONQUERED', - 'KILLED', - 'SHREDDED', - 'ANNIHILATED', - 'NUKED' - ]; - var compliments = [ - "You've got the power!", - "Nicely done!", - "Rock and roll!", - "High five!", - "Bravo!", - "Encore!", - "Challenge destroyed!", - "Power level 9000!", - "Most efficient!", - "Party on Wayne!", - "You've got the touch!", - "You're on fire!", - "Don't hurt 'em, Hammer!", - "The town is now red", - "To the nines!", - "Nothing but net!", - "Even grumpy cat is impressed" - ]; - 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 (challengeNumber > highestChallengeNumber) { challengeNumber = 0; } Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) { if (err) { @@ -70,6 +30,7 @@ exports.returnChallenge = function(req, res, next) { cc: req.user ? req.user.challengesHash : undefined, points: req.user ? req.user.points : undefined, verb: verbs[Math.floor(Math.random() * verbs.length)], + compliment: compliments[Math.floor(Math.random() * compliments.length)], phrase: phrases[Math.floor(Math.random() * phrases.length)], challenges: c }); diff --git a/controllers/resources.json b/controllers/resources.json index 098f41fa463..adae5097a6f 100644 --- a/controllers/resources.json +++ b/controllers/resources.json @@ -144,5 +144,94 @@ "You can complete CoderByte problems while you continue to work through Free Code Camp's challenges.", "Be sure to pair program on these challenges, and remember to apply the RSAP methodology.", "Click the button below to return to the Pair Programming challenge, then mark it complete." + ], + "verbs": [ + "aced", + "nailed", + "rocked", + "scorched", + "devastated", + "destroyed", + "owned", + "crushed", + "conquered", + "killed", + "shredded", + "annihilated", + "nuked", + "smashed", + "decimated", + "demolished", + "devoured", + "pulvarized", + "banished", + "throttled", + "blew away", + "roundhoused", + "tangoed", + "wrangled", + "shot down", + "scarfed", + "grappled", + "incinerated", + "uppercutted" + ], + "compliments": [ + "You've got the power!", + "Nicely done!", + "Rock and roll!", + "High five!", + "Bravo!", + "Encore!", + "Challenge destroyed!", + "Power level 9000!", + "Most efficient!", + "Party on, Wayne!", + "You've got the touch!", + "You're on fire!", + "Don't hurt 'em, Hammer!", + "The town is now red", + "To the nines!", + "Nothing but net!", + "Grumpy cat approves", + "The world rejoices", + "That's the way it's done!", + "You rock!", + "Woo-hoo!", + "We knew you could do it", + "Hyper Combo Finish!", + "Nothing but net!", + "Boom-shakalaka!", + "You're a shooting star!", + "You're unstoppable!", + "Way cool!", + "You're king of the world!", + "Walk on that sunshine!", + "Keep on trucking!", + "Off the charts!", + "There is no spoon", + "Cranked it up to 11!", + "Escape velocity reached!", + "You make this look easy!", + "Passed with flying colors!", + "One step closer...", + "You've got this!", + "Happy happy joy joy!", + "Tomorrow, the world!", + "Here's looking at you, Code!", + "It's alive. It's alive!" + ], + "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", + "With coding power comes sharing responsibility", + "Have you told your friends of your coding powers?" ] -} +} \ No newline at end of file diff --git a/public/css/main.less b/public/css/main.less index 2938ac72dd1..bbf1a63c241 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -510,6 +510,7 @@ thead { text-align: center; margin-bottom: -30px; border-radius: 5px 5px 0px 0px; + padding-left: 50px; } .closing-x { diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index 0a5dedfe979..db847aecea2 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -1,13 +1,33 @@ [ { - "_id" : "aaa48de84e1ecc7c742e1124", - "name": "Palindrome Tester", + "_id" : "7123c8c441eddfaeb5bdef0d", + "name": "Meet Bonfire", "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" + "Click the button below for further instructions.", + "Your goal is to fix the failing test.", + "First, run all the tests by clickin \"Run code\" or by pressing Control + Enter", + "The failing test is in red. Fix the code so that all tests pass. Then you can move on to the next Bonfire." + ], + "tests": [ + "expect(meetBonfire(\"test\")).to.be.a(\"boolean\");", + "expect(meetBonfire(\"test\")).to.be.true;" + ], + "challengeSeed": "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\nreturn false;\n}\n\n", + "challengeEntryPoint": "meetBonfire(\"You can do this!\");", + "bonfireNumber": 0, + "challengeEntryPointNegate" : "meetBonfire(\"You can do this!\");" + }, + { + "_id" : "aaa48de84e1ecc7c742e1124", + "name": "Check for Palindromes", + "difficulty": 1, + "description": [ + "Return 'true' if a given string is a palindrome.", + "A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation and case.", + "You'll need to remove punctuation and turn everything lower case in order to check for palindromes.", + "We'll pass strings with varying formats, such as \"racecar\", \"RaceCar\", and \"race CAR\" among others.", + "Return true if the string is a palindrome. Otherwise, return false." ], "tests": [ "expect(palindrome(\"eye\")).to.be.a(\"boolean\");", @@ -28,12 +48,20 @@ "name": "Validate US Telephone Numbers", "difficulty": 3, "description": [ + "Return true if the passed string is a valid US phone number", "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." ], "tests": [ "expect(telephoneCheck(\"555-555-5555\")).to.be.a(\"boolean\");", + "assert.deepEqual(telephoneCheck(\"1 555-555-5555\"), true);", + "assert.deepEqual(telephoneCheck(\"1 (555) 555-5555\"), true);", + "assert.deepEqual(telephoneCheck(\"5555555555\"), true);", + "assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);", + "assert.deepEqual(telephoneCheck(\"(555)555-5555\"), true);", + "assert.deepEqual(telephoneCheck(\"1(555)555-5555\"), true);", + "assert.deepEqual(telephoneCheck(\"1 555 555 5555\"), true);", "assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);", "assert.deepEqual(telephoneCheck(\"1 456 789 4444\"), true);", "assert.deepEqual(telephoneCheck(\"123**&!!asdf#\"), false);", diff --git a/seed_data/challenge-hashes b/seed_data/challenge-hashes index a03c038d90c..ca100cb5203 100644 --- a/seed_data/challenge-hashes +++ b/seed_data/challenge-hashes @@ -1,7 +1,6 @@ /* -"7123c8c441eddfaeb5bdef0d" "c3a4d278b9e760a0ffe8321f" "aceca143b92049a4392a859e" "ce9394f67d413734758e27e4" diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index eaa1c211707..88d28655b98 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -67,7 +67,7 @@ block content #complete-bonfire-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation .modal-content - .modal-header.challenge-list-header Nicely done! + .modal-header.challenge-list-header= compliment a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × .modal-body(ng-controller="pairedWithController") @@ -77,13 +77,12 @@ block content - if (cc) form.form-horizontal(novalidate='novalidate', name='completedWithForm') .form-group.text-center - label(for="existingUserName") If you paired with someone, enter their username here. .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 - .input-group.twitter-input + .input-group.twitter-input.animated.fadeIn // extra field to distract password tools like lastpass from injecting css into our username field input.form-control(ng-show="false") span.input-group-addon @ - input.form-control#completed-with(name="existingUser", placeholder='Otherwise just leave it blank', existing-username='', ng-model="existingUser", autofocus) + input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus) .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0") alert(type='danger') span.ion-close-circled @@ -94,8 +93,8 @@ block content - if (points && points > 2) a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") - i.fa.fa-twitter   - = phrase + i.fa.fa-twitter   + = phrase - else a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress #all-bonfires-dialog.modal(tabindex='-1') diff --git a/views/challenges/show.jade b/views/challenges/show.jade index 3111eb1edd0..ddb2fa6dced 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -19,7 +19,7 @@ block content #complete-challenge-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation .modal-content - .modal-header.challenge-list-header Nicely done! + .modal-header.challenge-list-header= compliment a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × .modal-body .text-center From d886a778581f2e1b7f741bd3c3a43e5167275b56 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 25 Jan 2015 10:02:41 -0500 Subject: [PATCH 16/39] Refactor random verbs, phrases, and compliments so that only the resources controller is concerned with how to fetch them and how to make them randomly. Remove debug statement from bonfire/show.jade --- controllers/bonfire.js | 45 +++++++-------------------------------- controllers/challenges.js | 11 ++++------ controllers/resources.js | 17 ++++++++++++++- views/bonfire/show.jade | 1 - 4 files changed, 28 insertions(+), 46 deletions(-) diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 25dbcdab1bf..b669452ab08 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -2,16 +2,13 @@ var _ = require('lodash'), debug = require('debug')('freecc:cntr:bonfires'), Bonfire = require('./../models/Bonfire'), User = require('./../models/User'), - resources = require('./resources.json'), - phrases = resources.phrases, - verbs = resources.verbs, - compliments = resources.compliments; + resources = require('./resources'); /** * Bonfire controller */ -var highestBonfireNumber = 1; +var highestBonfireNumber = 2; exports.index = function(req, res) { res.render('bonfire/bonfire.jade', { @@ -61,9 +58,9 @@ exports.returnBonfire = function(req, res, next) { challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, 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)], - compliments: compliments[Math.floor(Math.random() * phrases.length)], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliments: resources.randomCompliment(), bonfires: bonfire, bonfireHash: bonfire[bonfireNumber]._id }); @@ -72,32 +69,6 @@ exports.returnBonfire = function(req, res, next) { exports.returnIndividualBonfire = 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) { @@ -117,9 +88,9 @@ exports.returnIndividualBonfire = function(req, res, next) { challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, 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)], - compliment: compliments[Math.floor(Math.random() * phrases.length)], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), bonfires: bonfire, bonfireHash: bonfire[bonfireNumber]._id }); diff --git a/controllers/challenges.js b/controllers/challenges.js index 401940ca291..6341ce520f3 100644 --- a/controllers/challenges.js +++ b/controllers/challenges.js @@ -5,10 +5,7 @@ var _ = require('lodash'), debug = require('debug')('freecc:cntr:challenges'), Challenge = require('./../models/Challenge'), - resources = require('./resources.json'), - phrases = resources.phrases, - verbs = resources.verbs, - compliments = resources.compliments; + resources = require('./resources'); var highestChallengeNumber = 53; @@ -29,9 +26,9 @@ exports.returnChallenge = function(req, res, next) { number: challengeNumber, cc: req.user ? req.user.challengesHash : undefined, points: req.user ? req.user.points : undefined, - verb: verbs[Math.floor(Math.random() * verbs.length)], - compliment: compliments[Math.floor(Math.random() * compliments.length)], - phrase: phrases[Math.floor(Math.random() * phrases.length)], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), challenges: c }); }); diff --git a/controllers/resources.js b/controllers/resources.js index f073188bcac..5eea62da79e 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -167,7 +167,22 @@ module.exports = { }); }); }); - } + }, + + randomPhrase: function() { + var phrases = resources.phrases; + return phrases[Math.floor(Math.random() * phrases.length)]; + }, + + randomVerb: function() { + var verbs = resources.verbs; + return verbs[Math.floor(Math.random() * verbs.length)]; + }, + + randomCompliment: function() { + var compliments = resources.compliments; + return compliments[Math.floor(Math.random() * compliments.length)]; + } }; diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 88d28655b98..fac141d818c 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -60,7 +60,6 @@ block content var challengeSeed = !{JSON.stringify(challengeSeed)}; var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - console.log(passedBonfireHash); script(src='/js/lib/bonfire/bonfireFramework.js') From f593ebbbf2d66ba388f4983c57eec28220dff0ac Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 09:06:30 -0800 Subject: [PATCH 17/39] start widescreen refactor --- public/js/lib/bonfire/bonfireFramework.js | 10 +-- views/bonfire/show.jade | 80 ++++++++++++----------- views/layout.jade | 6 +- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index adb0f362ef5..4f19af70e4c 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -177,18 +177,18 @@ var createTestDisplay = function() { var test = userTests[i]; var testDoc = document.createElement("li"); $(testDoc) - .addClass('list-group-item') - .addClass('well img-rounded') - .addClass('well-sm') + //.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)') + .css("text-color", 'rgba(255,0,0,.2)') .prependTo($('#testSuite')); } else { $(testDoc) .html(test.text) - .css('background-color', 'rgba(0,255,0,.2)') + .css('text-color', 'rgba(0,255,0,.2)') .appendTo($('#testSuite')); } }; diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 88d28655b98..3f9df3e977e 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -18,7 +18,7 @@ block content .row - #mainEditorPanel.col-sm-12.col-md-7.col-xs-12 + #mainEditorPanel.col-xs-12.col-sm-12.col-md-8 .panel.panel-primary.panel-bonfire .panel-heading.text-center #{name} (Level #{difficulty} bonfire) .panel.panel-body @@ -43,7 +43,8 @@ block content form.code .form-group.codeMirrorView textarea#codeEditor(autofocus=true) - #testCreatePanel.col-sm-12.col-md-5.col-xs-12 + + #testCreatePanel.col-xs-12.col-sm-12.col-md-4 .panel.panel-primary.panel-bonfire .panel-heading.text-center Output .panel.panel-body @@ -60,47 +61,48 @@ block content var challengeSeed = !{JSON.stringify(challengeSeed)}; var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - console.log(passedBonfireHash); script(src='/js/lib/bonfire/bonfireFramework.js') - #complete-bonfire-dialog.modal(tabindex='-1') - .modal-dialog.animated.zoomIn.fast-animation - .modal-content - .modal-header.challenge-list-header= compliment - a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × - .modal-body(ng-controller="pairedWithController") - .text-center - .animated.zoomInDown.delay-half - span.landing-icon.ion-checkmark-circled.text-primary - - if (cc) - form.form-horizontal(novalidate='novalidate', name='completedWithForm') - .form-group.text-center - .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 - .input-group.twitter-input.animated.fadeIn - // extra field to distract password tools like lastpass from injecting css into our username field - input.form-control(ng-show="false") - span.input-group-addon @ - input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus) - .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0") - alert(type='danger') - span.ion-close-circled - | Username not found + #complete-bonfire-dialog.modal(tabindex='-1') + .modal-dialog.animated.zoomIn.fast-animation + .modal-content + .modal-header.challenge-list-header= compliment + a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × + .modal-body(ng-controller="pairedWithController") - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Take me to my next challenge + .text-center + .animated.zoomInDown.delay-half + span.landing-icon.ion-checkmark-circled.text-primary + - if (cc) + form.form-horizontal(novalidate='novalidate', name='completedWithForm') + .form-group.text-center + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 + .input-group.twitter-input.animated.fadeIn + // extra field to distract password tools like lastpass from injecting css into our username field + input.form-control(ng-show="false") + span.input-group-addon @ + input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus) + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0") + alert(type='danger') + span.ion-close-circled + | Username not found + + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Take me to my next challenge - - if (points && points > 2) - a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") - i.fa.fa-twitter   - = phrase - - else - a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress - #all-bonfires-dialog.modal(tabindex='-1') - .modal-dialog.animated.fadeInUp.fast-animation - .modal-content - .modal-header.challenge-list-header Bonfires - a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × - .modal-body - include ../partials/bonfires + - if (points && points > 2) + a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{number}&hashtags=learntocode, javascript" target="_blank") + i.fa.fa-twitter   + = phrase + - else + a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress + + #all-bonfires-dialog.modal(tabindex='-1') + .modal-dialog.animated.fadeInUp.fast-animation + .modal-content + .modal-header.challenge-list-header Bonfires + a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × + .modal-body + include ../partials/bonfires diff --git a/views/layout.jade b/views/layout.jade index de6f5527280..d44dfea0306 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -18,10 +18,8 @@ html(ng-app='profileValidation', lang='en') body include partials/navbar - - .container - include partials/flash - block content + include partials/flash + block content include partials/footer != js('application') script. From 17312f43f5226c1e6dcd9839d485ae4413391f02 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 09:41:07 -0800 Subject: [PATCH 18/39] moved components to correct sides of the view and cleaned up pair name entry --- views/bonfire/show.jade | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 3f9df3e977e..d9965075dcf 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -18,9 +18,10 @@ block content .row - #mainEditorPanel.col-xs-12.col-sm-12.col-md-8 + + #testCreatePanel.col-xs-12.col-sm-12.col-md-4 .panel.panel-primary.panel-bonfire - .panel-heading.text-center #{name} (Level #{difficulty} bonfire) + .panel-heading.text-center Output .panel.panel-body .well .row.text-center @@ -40,14 +41,6 @@ block content button#less-info.btn.btn-info span.ion-help-circled | Less information - form.code - .form-group.codeMirrorView - textarea#codeEditor(autofocus=true) - - #testCreatePanel.col-xs-12.col-sm-12.col-md-4 - .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 @@ -61,8 +54,15 @@ block content var challengeSeed = !{JSON.stringify(challengeSeed)}; var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - script(src='/js/lib/bonfire/bonfireFramework.js') + #mainEditorPanel.col-xs-12.col-sm-12.col-md-8 + .panel.panel-primary.panel-bonfire + .panel-heading.text-center #{name} (Level #{difficulty} bonfire) + .panel.panel-body + form.code + .form-group.codeMirrorView + textarea#codeEditor(autofocus=true) + script(src='/js/lib/bonfire/bonfireFramework.js') #complete-bonfire-dialog.modal(tabindex='-1') @@ -78,12 +78,10 @@ block content - if (cc) form.form-horizontal(novalidate='novalidate', name='completedWithForm') .form-group.text-center - .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2 - .input-group.twitter-input.animated.fadeIn - // extra field to distract password tools like lastpass from injecting css into our username field - input.form-control(ng-show="false") - span.input-group-addon @ - input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus) + .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2.animated.fadeIn + // extra field to distract password tools like lastpass from injecting css into our username field + input.form-control(ng-show="false") + input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus) .col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0") alert(type='danger') span.ion-close-circled From fa5a1a86185906e26df86c1e6615ab1cf3aab83d Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 10:35:41 -0800 Subject: [PATCH 19/39] make test results look better --- public/css/main.less | 22 +++++++++++++++++++++- public/js/lib/bonfire/bonfireFramework.js | 14 ++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index bbf1a63c241..01adb80a6d6 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -2,8 +2,9 @@ @import "lib/bootstrap-social/bootstrap-social"; @import "lib/ionicons/ionicons"; @import "lib/animate.min.less"; +@import "lib/bootstrap/variables"; -@import url(http://fonts.googleapis.com/css?family=Lato:300); +//fonts.googleapis.com/css?family=Lato:300); @import url(http://fonts.googleapis.com/css?family=Lato:400); @import url(http://fonts.googleapis.com/css?family=Inconsolata); @@ -545,6 +546,21 @@ form.code span { height: auto; } +.big-error-icon { + font-size: 32px; + color: @brand-danger; +} + +.big-success-icon { + font-size: 32px; + color: @brand-primary; +} + +.test-output { + font-size: 14px; + font-family: "Ubuntu Mono"; +} + #mainEditorPanel .panel-body { padding-bottom: 0px; } @@ -579,6 +595,10 @@ div.CodeMirror-scroll { margin-bottom: 5px; } +.test-vertical-center { + margin: 8px; +} + //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 index 4f19af70e4c..ea832a18d08 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -176,19 +176,13 @@ var createTestDisplay = function() { for (var i = 0; i < userTests.length;i++) { var test = userTests[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("text-color", 'rgba(255,0,0,.2)') - .prependTo($('#testSuite')); + .html("" + test.text + "\n" + test.err).addClass('test-output col-xs-10') + .prependTo($('#testSuite')) } else { $(testDoc) - .html(test.text) - .css('text-color', 'rgba(0,255,0,.2)') + .html("
" + test.text + "
").addClass('test-output col-xs-10') .appendTo($('#testSuite')); } }; @@ -208,7 +202,7 @@ var runTests = function(err, data) { pushed = false; $('#testSuite').children().remove(); if (err && userTests.length > 0) { - userTests= [{text:"Program Execution Failure", err: "NouserTestswere run."}]; + userTests= [{text:"Program Execution Failure", err: "No user tests were run."}]; createTestDisplay(); } else if (userTests) { From 00dfdb2e1e95a319ddf180864d9fb5e5a82b3585 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 25 Jan 2015 15:22:21 -0500 Subject: [PATCH 20/39] More layout improvements, adding in contextual icons for test success/failure and styling font of test output --- gulpfile.js | 2 +- public/css/main.less | 70 ++++++++++++----------- public/js/lib/bonfire/bonfireFramework.js | 6 +- views/bonfire/show.jade | 2 +- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index fb5377729d4..70c59bf1fe4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,7 +5,7 @@ var gulp = require('gulp'), sync = require('browser-sync'), reload = sync.reload, inject = require('gulp-inject'), - reloadDelay = 3000; + reloadDelay = 1000; var paths = { server: './app.js', diff --git a/public/css/main.less b/public/css/main.less index 01adb80a6d6..a69c2397829 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -538,37 +538,6 @@ thead { } } -form.code span { - font-size: 14px; - font-family: "Ubuntu Mono"; - padding-bottom: 0px; - margin-bottom: 0px; - height: auto; -} - -.big-error-icon { - font-size: 32px; - color: @brand-danger; -} - -.big-success-icon { - font-size: 32px; - color: @brand-primary; -} - -.test-output { - font-size: 14px; - font-family: "Ubuntu Mono"; -} - -#mainEditorPanel .panel-body { - padding-bottom: 0px; -} - -div.CodeMirror-scroll { - padding-bottom: 100px; -} - .embed-responsive-twitch-chat { padding-bottom: 117%; } @@ -595,8 +564,45 @@ div.CodeMirror-scroll { margin-bottom: 5px; } + + +/** + * Bonfire styling + */ + +form.code span { + font-size: 14px; + font-family: "Ubuntu Mono"; + padding-bottom: 0px; + margin-bottom: 0px; + height: auto; +} + +.big-error-icon { + font-size: 40px; + color: @brand-danger; +} + +.big-success-icon { + font-size: 40px; + color: @brand-primary; +} + +.test-output { + font-size: 15px; + font-family: "Ubuntu Mono"; +} + +#mainEditorPanel .panel-body { + padding-bottom: 0px; +} + +div.CodeMirror-scroll { + padding-bottom: 100px; +} + .test-vertical-center { - margin: 8px; + margin-top: 15px; } //uncomment this to see the dimensions of all elements outlined in red diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index ea832a18d08..02f9230f13a 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -175,14 +175,14 @@ var createTestDisplay = function() { } for (var i = 0; i < userTests.length;i++) { var test = userTests[i]; - var testDoc = document.createElement("li"); + var testDoc = document.createElement("div"); if (test.err != null) { $(testDoc) - .html("" + test.text + "\n" + test.err).addClass('test-output col-xs-10') + .html("
" + test.text + "
" + test.err + "
") .prependTo($('#testSuite')) } else { $(testDoc) - .html("
" + test.text + "
").addClass('test-output col-xs-10') + .html("
" + test.text + "
") .appendTo($('#testSuite')); } }; diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index d9965075dcf..0224965ce07 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -47,7 +47,7 @@ block content .form-group.codeMirrorView textarea#codeOutput br - ul#testSuite.list-group + #testSuite br script(type="text/javascript"). var tests = !{JSON.stringify(tests)}; From 413d89cf184d6840ea7c1121ebe09681efd38422 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 14:05:56 -0800 Subject: [PATCH 21/39] minor improvements to the navbar and layout in general --- public/css/main.less | 14 ++- public/js/lib/bonfire/bonfireFramework.js | 8 +- views/bonfire/show.jade | 125 ++++++++++++++-------- views/partials/navbar.jade | 1 - 4 files changed, 95 insertions(+), 53 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index a69c2397829..23fcc5c6318 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -30,8 +30,8 @@ html { } body { - padding-top: 80px; - margin-bottom: 60px; + padding-top: 50px; +// margin-bottom: 60px; } h1, h2 { @@ -253,6 +253,10 @@ ul { .navbar { white-space: nowrap; border: none; + @media (min-width: 767px) { + padding-left: 30px; + padding-right: 30px; + } } .panel-body { @@ -571,7 +575,7 @@ thead { */ form.code span { - font-size: 14px; + font-size: 18px; font-family: "Ubuntu Mono"; padding-bottom: 0px; margin-bottom: 0px; @@ -597,6 +601,10 @@ form.code span { padding-bottom: 0px; } +.panel-bonfire { + height: 100% +} + div.CodeMirror-scroll { padding-bottom: 100px; } diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index 02f9230f13a..45ac46ca0e4 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -47,8 +47,8 @@ var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), lineWrapping: true }); codeOutput.setValue('/**\n' + -' * Your output will go here. Console.log() -type statements\n' + -' * will appear in your browser\'s 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(); @@ -178,11 +178,11 @@ var createTestDisplay = function() { var testDoc = document.createElement("div"); if (test.err != null) { $(testDoc) - .html("
" + test.text + "
" + test.err + "
") + .html("
" + test.text + "
" + test.err + "
") .prependTo($('#testSuite')) } else { $(testDoc) - .html("
" + test.text + "
") + .html("
" + test.text + "
") .appendTo($('#testSuite')); } }; diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 0224965ce07..23a3404d68b 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -17,52 +17,87 @@ block content script(src='/js/lib/bonfire/bonfireInit.js') - .row + .panel.panel-primary.panel-bonfire + .panel.panel-body + .row + .col-xs-12.col-sm-12.col-md-3 + #testCreatePanel + h2.text-center #{name} + Difficulty:   + if (difficulty == "0") + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "1") + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "2") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "3") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "4") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + if (difficulty == "5") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + .well + .row.text-center + row.text-center + .col-xs-12 + .bonfire-instructions + = brief + #brief-instructions.col-xs-12 + button#more-info.btn.btn-info + span.ion-help-circled + | More information + #long-instructions.row.text-center.hide + .col-xs-12 + .bonfire-instructions + for sentence in details + p!= sentence + button#less-info.btn.btn-info + span.ion-help-circled + | Less information + #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) + br + form.code + .form-group.codeMirrorView + textarea#codeOutput + br + #testSuite + br + script(type="text/javascript"). + var tests = !{JSON.stringify(tests)}; + var challengeSeed = !{JSON.stringify(challengeSeed)}; + var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var passedBonfireHash = !{JSON.stringify(bonfireHash)}; + .col-xs-12.col-sm-12.col-md-9 + #mainEditorPanel + form.code + .form-group.codeMirrorView + textarea#codeEditor(autofocus=true) + script(src='/js/lib/bonfire/bonfireFramework.js') - #testCreatePanel.col-xs-12.col-sm-12.col-md-4 - .panel.panel-primary.panel-bonfire - .panel-heading.text-center Output - .panel.panel-body - .well - .row.text-center - row.text-center - .col-xs-12 - .bonfire-instructions - = brief - #brief-instructions.col-xs-12 - button#more-info.btn.btn-info - span.ion-help-circled - | More information - #long-instructions.row.text-center.hide - .col-xs-12 - .bonfire-instructions - for sentence in details - p!= sentence - button#less-info.btn.btn-info - span.ion-help-circled - | Less information - #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) - br - form.code - .form-group.codeMirrorView - textarea#codeOutput - br - #testSuite - br - script(type="text/javascript"). - var tests = !{JSON.stringify(tests)}; - var challengeSeed = !{JSON.stringify(challengeSeed)}; - var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; - var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - - #mainEditorPanel.col-xs-12.col-sm-12.col-md-8 - .panel.panel-primary.panel-bonfire - .panel-heading.text-center #{name} (Level #{difficulty} bonfire) - .panel.panel-body - form.code - .form-group.codeMirrorView - textarea#codeEditor(autofocus=true) - script(src='/js/lib/bonfire/bonfireFramework.js') #complete-bonfire-dialog.modal(tabindex='-1') diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index e6ca1aba4f3..0cbfe2dc687 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -1,5 +1,4 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height - .container .navbar-header button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse') span.sr-only Toggle navigation From 26c041826bb1819c6cad880e45bad0b1c87868cd Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 25 Jan 2015 23:56:04 -0500 Subject: [PATCH 22/39] Fixed bonfire to not overwrite user entry point but still allow user error by not defining entry point, create web form to give challenge json back, improved layout of bonfire even more --- app.js | 2 + controllers/bonfire.js | 94 ++++++++++++++++++++++- public/css/main.less | 15 +++- public/js/lib/bonfire/bonfireFramework.js | 7 +- seed_data/bonfires.json | 9 +-- seed_data/challenge-hashes | 2 +- views/bonfire/generator.jade | 45 +++++++++++ 7 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 views/bonfire/generator.jade diff --git a/app.js b/app.js index 1b00f76de3a..24b87bf7775 100644 --- a/app.js +++ b/app.js @@ -261,6 +261,8 @@ app.get( bonfireController.returnIndividualBonfire ); app.get('/bonfires', bonfireController.returnBonfire); +app.get('/bonfire/generator', bonfireController.returnGenerator); +app.post('/bonfire/generator', bonfireController.generateChallenge); // Unique Check API route app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index b669452ab08..264c6882810 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -95,4 +95,96 @@ exports.returnIndividualBonfire = function(req, res, next) { bonfireHash: bonfire[bonfireNumber]._id }); }); -}; \ No newline at end of file +}; + +/** + * Bonfire generator + */ +exports.returnGenerator = function(req, res) { + res.render('bonfire/generator', { + title: null, + name: null, + difficulty: null, + brief: null, + details: null, + tests: null, + challengeSeed: null, + challengeEntryPoint: null, + bonfireHash: randomString() + }); +}; + +/** + * Post for bonfire generation + */ + +function randomString() { + var chars = "0123456789abcdef"; + var string_length = 24; + var randomstring = ''; + for (var i=0; i 0) { + return elem; + } +} + +exports.generateChallenge = function(req, res) { + var bonfireName = req.body.name, + bonfireTests = req.body.tests, + bonfireDifficulty = req.body.difficulty, + bonfireDescription = req.body.description, + bonfireEntryPoint = req.body.challengeEntryPoint, + bonfireChallengeSeed = req.body.challengeSeed; + bonfireTests = bonfireTests.split('\r\n'); + bonfireDescription = bonfireDescription.split('\r\n'); + bonfireTests.filter(getRidOfEmpties); + bonfireDescription.filter(getRidOfEmpties); + bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', ''); + + + var response = { + name: bonfireName, + tests: bonfireTests, + difficulty: bonfireDifficulty, + description: bonfireDescription, + challengeEntryPoint: bonfireEntryPoint, + challengeSeed: bonfireChallengeSeed, + bonfireNumber: 0, + _id: randomString() + }; + res.send(response); +} diff --git a/public/css/main.less b/public/css/main.less index 23fcc5c6318..d9c9cd4ab04 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -579,7 +579,16 @@ form.code span { font-family: "Ubuntu Mono"; padding-bottom: 0px; margin-bottom: 0px; - height: auto; + height: 100%; +} + +.CodeMirror-linenumber { + font-size: 18px; + font-family: "Ubuntu Mono"; +} + +#mainEditorPanel { + height: 100%; } .big-error-icon { @@ -606,13 +615,15 @@ form.code span { } div.CodeMirror-scroll { - padding-bottom: 100px; + padding-bottom: 40px; } .test-vertical-center { margin-top: 15px; } + + //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 index 45ac46ca0e4..8d8705cf0c5 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -7,7 +7,6 @@ var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor") lint: true, matchBrackets: true, autoCloseBrackets: true, - cursorHeight: 1, scrollbarStyle: 'null', lineWrapping: true, gutters: ["CodeMirror-lint-markers"], @@ -20,6 +19,7 @@ var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor") } }); var editor = myCodeMirror; +editor.setSize("100%", "auto"); // Default value for editor if one isn't provided in (i.e. a challenge) @@ -37,7 +37,8 @@ var nonChallengeValue = '/*Welcome to Bonfire, Free Code Camp\'s future CoderByt 'assert.deepEqual(test(), [1,4,9]);\n\n' + 'var foo = test();\n' + 'foo.should.be.a("array");\n\n' + - 'test();'; + 'test();\n' + + 'function test(str) {\r\n return str;\r\n}'; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { lineNumbers: false, @@ -106,6 +107,8 @@ function bonfireExecute() { var userJavaScript = myCodeMirror.getValue(); userJavaScript = removeComments(userJavaScript); userJavaScript = scrapeTests(userJavaScript); + // simple fix in case the user forgets to invoke their function + userJavaScript = challengeEntryPoint + ' ' + userJavaScript; submit(userJavaScript, function(cls, message) { if (cls) { codeOutput.setValue(message.error); diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index db847aecea2..42778c1e5e1 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -15,8 +15,7 @@ ], "challengeSeed": "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\nreturn false;\n}\n\n", "challengeEntryPoint": "meetBonfire(\"You can do this!\");", - "bonfireNumber": 0, - "challengeEntryPointNegate" : "meetBonfire(\"You can do this!\");" + "bonfireNumber": 0 }, { "_id" : "aaa48de84e1ecc7c742e1124", @@ -40,8 +39,7 @@ ], "challengeSeed": "function palindrome(str) {\n // Good luck!\n return true;\n}\n\n", "challengeEntryPoint": "palindrome(\"eye\");", - "bonfireNumber": 1, - "challengeEntryPointNegate" : "palindrome\\([^str].*\\;" + "bonfireNumber": 1 }, { "_id" : "ff0395860f5d3034dc0bfc94", @@ -79,8 +77,7 @@ ], "challengeSeed": "function telephoneCheck(str) {\n // Good luck!\n return true;\n}\n\n", "challengeEntryPoint": "telephoneCheck(\"555-555-5555\");", - "bonfireNumber": 2, - "challengeEntryPointNegate" : "palindrome\\([^str].*\\;" + "bonfireNumber": 2 } ] diff --git a/seed_data/challenge-hashes b/seed_data/challenge-hashes index ca100cb5203..64932960cae 100644 --- a/seed_data/challenge-hashes +++ b/seed_data/challenge-hashes @@ -1,7 +1,7 @@ /* -"c3a4d278b9e760a0ffe8321f" + "aceca143b92049a4392a859e" "ce9394f67d413734758e27e4" "1369953ef6f03098cb60e2f7" diff --git a/views/bonfire/generator.jade b/views/bonfire/generator.jade new file mode 100644 index 00000000000..bc150870a10 --- /dev/null +++ b/views/bonfire/generator.jade @@ -0,0 +1,45 @@ +extends ../layout +block content + .row + .col-md-offset-2.col-md-8.col-lg-offset-2.col-lg-8.text-center + + h1 JSON generator for bonfire challenges - just fill the form out and get the correct format back + .col-xs-12.col-sm-12.col-md-offset-3.col-md-6.col-lg-offset-3-col-lg-6 + .panel + form.form-horizontal(method="POST", action="/bonfire/generator", name="bonfireInfo") + .form-group + label.col-sm-2.control-label(for='name') name: + .col-sm-10 + input#name.form-control(type='text', placeholder='name', name="name") + .form-group + label.col-sm-2.control-label(for='difficultyField') difficulty: + .col-sm-10 + label.radio-inline 1 + input#inlineRadio1(type='radio', name='difficulty', value='1') + label.radio-inline 2 + input#inlineRadio2(type='radio', name='difficulty', value='2') + label.radio-inline 3 + input#inlineRadio3(type='radio', name='difficulty', value='3') + label.radio-inline 4 + input#inlineRadio4(type='radio', name='difficulty', value='4') + label.radio-inline 5 + input#inlineRadio5(type='radio', name='difficulty', value='5') + .form-group + label.col-sm-2.control-label.wrappable(for='description') description: + .col-sm-10 + textarea#description.form-control(name="description", placeholder="Separate sentences by exactly one space only. Do not add in line breaks.") + .form-group + label.col-sm-2.control-label.wrappable(for='tests') tests: + .col-sm-10 + textarea#tests.form-control(name="tests", rows=5, placeholder="Separate tests by a newline.") + .form-group + label.col-sm-2.control-label.wrappable(for='challengeSeed') challengeSeed: + .col-sm-10 + textarea#challengeSeed.form-control(name="challengeSeed", rows=5) + .form-group + label.col-sm-2.control-label.wrappable(for='challengeEntryPoint') challenge entrypoint: + .col-sm-10 + textarea#name.form-control(name="challengeEntryPoint", rows=1, type='text', placeholder="palindrome(\"eye\");") + .form-group + .col-sm-offset-2.col-sm-10 + input.btn.btn-default(type='submit', value="submit") From f8eb5971b86b6c44db62cf2a7426d7d88207b983 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Mon, 26 Jan 2015 00:15:46 -0500 Subject: [PATCH 23/39] Fixing playground for to not include a null symbol --- public/js/lib/bonfire/bonfireFramework.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index 8d8705cf0c5..d5dc8860af2 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -108,7 +108,9 @@ function bonfireExecute() { userJavaScript = removeComments(userJavaScript); userJavaScript = scrapeTests(userJavaScript); // simple fix in case the user forgets to invoke their function - userJavaScript = challengeEntryPoint + ' ' + userJavaScript; + if (challengeEntryPoint) { + userJavaScript = challengeEntryPoint + ' ' + userJavaScript; + } submit(userJavaScript, function(cls, message) { if (cls) { codeOutput.setValue(message.error); From 3f6ee4424863070e42b8a8dc71c5b1f3efa1702b Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 21:16:44 -0800 Subject: [PATCH 24/39] add the first new bonfire and resequence bonfire form slightly --- seed_data/bonfires.json | 71 +++++++++++++++++++++++++++++++++++- views/bonfire/generator.jade | 8 ++-- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index 42778c1e5e1..6b04e831641 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -78,6 +78,75 @@ "challengeSeed": "function telephoneCheck(str) {\n // Good luck!\n return true;\n}\n\n", "challengeEntryPoint": "telephoneCheck(\"555-555-5555\");", "bonfireNumber": 2 - } + }, + { + "name": "Reverse a String", + "tests": [ + "expect(reverseString('hello')).to.be.a('String');", + "expect(reverseString('hello')).to.equal('olleh');", + "expect(reverseString('Howdy')).to.equal('ydwoH');", + "expect(reverseString('Greetings from Earth')).to.equal('htraE morf sgniteerG');" + ], + "difficulty": "1", + "description": [ + "Reverse the provided string. ", + "You may need to turn the string into an array before you can reverse it.", + "Your result must be a string." + ], + "challengeEntryPoint": "reverseString('hello');", + "challengeSeed": "function reverseString(str) {\n return str;\r\n}", + "bonfireNumber": 0, + "_id": "202eed8fc186c8434cb6d618" + }, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ] diff --git a/views/bonfire/generator.jade b/views/bonfire/generator.jade index bc150870a10..861b8ffee9d 100644 --- a/views/bonfire/generator.jade +++ b/views/bonfire/generator.jade @@ -28,10 +28,6 @@ block content label.col-sm-2.control-label.wrappable(for='description') description: .col-sm-10 textarea#description.form-control(name="description", placeholder="Separate sentences by exactly one space only. Do not add in line breaks.") - .form-group - label.col-sm-2.control-label.wrappable(for='tests') tests: - .col-sm-10 - textarea#tests.form-control(name="tests", rows=5, placeholder="Separate tests by a newline.") .form-group label.col-sm-2.control-label.wrappable(for='challengeSeed') challengeSeed: .col-sm-10 @@ -40,6 +36,10 @@ block content label.col-sm-2.control-label.wrappable(for='challengeEntryPoint') challenge entrypoint: .col-sm-10 textarea#name.form-control(name="challengeEntryPoint", rows=1, type='text', placeholder="palindrome(\"eye\");") + .form-group + label.col-sm-2.control-label.wrappable(for='tests') tests: + .col-sm-10 + textarea#tests.form-control(name="tests", rows=5, placeholder="Separate tests by a newline.") .form-group .col-sm-offset-2.col-sm-10 input.btn.btn-default(type='submit', value="submit") From c20aa1697c663109de4c7c47b3d153f580fead84 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 25 Jan 2015 23:10:05 -0800 Subject: [PATCH 25/39] generator now publishes directly to a view where you can interact with your new bonfire --- app.js | 2 + controllers/bonfire.js | 65 +++++----- models/Bonfire.js | 4 +- public/js/lib/bonfire/bonfireFramework.js | 3 +- seed_data/bonfires.json | 138 ++++++++++++---------- views/bonfire/bonfire.jade | 6 + views/bonfire/public-generator.jade | 44 +++++++ 7 files changed, 170 insertions(+), 92 deletions(-) create mode 100644 views/bonfire/public-generator.jade diff --git a/app.js b/app.js index 24b87bf7775..279c24b74f1 100644 --- a/app.js +++ b/app.js @@ -263,6 +263,8 @@ app.get( app.get('/bonfires', bonfireController.returnBonfire); app.get('/bonfire/generator', bonfireController.returnGenerator); app.post('/bonfire/generator', bonfireController.generateChallenge); +app.get('/bonfire/public-generator', bonfireController.publicGenerator); +app.post('/bonfire/public-generator', bonfireController.testBonfire) // Unique Check API route app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 264c6882810..079aad96b15 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -15,7 +15,7 @@ exports.index = function(req, res) { title: 'Learn to code with Bonfire' }); - Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, c) { + Bonfire.find({}, null, { sort: { difficulty: 1 } }, function(err, c) { if (err) { debug('bonfire err: ', err); next(err); @@ -49,8 +49,7 @@ exports.returnBonfire = function(req, res, next) { completedWith: null, title: bonfire[bonfireNumber].name, name: bonfire[bonfireNumber].name, - number: bonfire[bonfireNumber].bonfireNumber, - difficulty: bonfire[bonfireNumber].difficulty, + difficulty: +bonfire[bonfireNumber].difficulty, brief: bonfire[bonfireNumber].description[0], details: bonfire[bonfireNumber].description.slice(1), tests: bonfire[bonfireNumber].tests, @@ -71,7 +70,7 @@ exports.returnIndividualBonfire = function(req, res, next) { var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; if (bonfireNumber > highestBonfireNumber) { bonfireNumber = 0; } - Bonfire.find({}, null, { sort: { bonfireNumber: 1 } }, function(err, bonfire) { + Bonfire.find({}, null, { sort: { difficulty: 1 } }, function(err, bonfire) { if (err) { next(err); } @@ -79,8 +78,7 @@ exports.returnIndividualBonfire = function(req, res, next) { completedWith: null, title: bonfire[bonfireNumber].name, name: bonfire[bonfireNumber].name, - number: bonfire[bonfireNumber].bonfireNumber, - difficulty: bonfire[bonfireNumber].difficulty, + difficulty: +bonfire[bonfireNumber].difficulty, brief: bonfire[bonfireNumber].description[0], details: bonfire[bonfireNumber].description.slice(1), tests: bonfire[bonfireNumber].tests, @@ -134,25 +132,33 @@ function randomString() { */ exports.testBonfire = function(req, res) { - // TODO: Add stuff here to parse posted variables and feed them back to bonfire playground - var candidateTitle = req.body.bonfireInfo.title; - var candidateName = req.body.bonfireInfo.name; - var candidateDifficulty = req.body.bonfireInfo.difficulty; - var candidateBrief = req.body.bonfireInfo.brief; - var candidateDetails = req.body.bonfireInfo.details; - var candidateTests = req.body.bonfireInfo.tests; - var candidateChallengeSeed = req.body.bonfireInfo.challengeSeed; - var candidateChallengeEntryPoint = req.body.bonfireInfo.challengeSeed; - res.render('bonfire/bonfire', { - title: candidateTitle, - name: candidateName, - difficulty: candidateDifficulty, - brief: candidateBrief, - details: candidateDetails, - tests: candidateTests, - challengeSeed: candidateChallengeSeed, - challengeEntryPoint: candidateChallengeEntryPoint, - bonfireHash: bonfire[bonfireNumber]._id + var bonfireName = req.body.name, + bonfireTests = req.body.tests, + bonfireDifficulty = req.body.difficulty, + bonfireDescription = req.body.description, + bonfireEntryPoint = req.body.challengeEntryPoint, + bonfireChallengeSeed = req.body.challengeSeed; + bonfireTests = bonfireTests.split('\r\n'); + bonfireDescription = bonfireDescription.split('\r\n'); + bonfireTests.filter(getRidOfEmpties); + bonfireDescription.filter(getRidOfEmpties); + bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', ''); + res.render('bonfire/show', { + completedWith: null, + title: bonfireName, + difficulty: +bonfireDifficulty, + brief: bonfireDescription[0], + details: bonfireDescription.slice(1), + tests: bonfireTests, + challengeSeed: bonfireChallengeSeed, + challengeEntryPoint: bonfireEntryPoint, + cc: req.user ? req.user.bonfiresHash : undefined, + points: req.user ? req.user.points : undefined, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + bonfires: [], + bonfireHash: "test" }); }; @@ -162,6 +168,10 @@ function getRidOfEmpties(elem) { } } +exports.publicGenerator = function(req, res) { + res.render('bonfire/public-generator'); +} + exports.generateChallenge = function(req, res) { var bonfireName = req.body.name, bonfireTests = req.body.tests, @@ -177,14 +187,13 @@ exports.generateChallenge = function(req, res) { var response = { + _id: randomString(), name: bonfireName, - tests: bonfireTests, difficulty: bonfireDifficulty, description: bonfireDescription, challengeEntryPoint: bonfireEntryPoint, challengeSeed: bonfireChallengeSeed, - bonfireNumber: 0, - _id: randomString() + tests: bonfireTests }; res.send(response); } diff --git a/models/Bonfire.js b/models/Bonfire.js index dd0cff31d7a..5cb531547e7 100644 --- a/models/Bonfire.js +++ b/models/Bonfire.js @@ -12,14 +12,12 @@ var bonfireSchema = new mongoose.Schema({ type: String, unique: true }, - difficulty: Number, + difficulty: String, description: Array, tests: Array, challengeSeed: String, - bonfireNumber: Number, challengeEntryPoint: String, challengeEntryPointNegate: String - }); module.exports = mongoose.model('Bonfire', bonfireSchema); diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index d5dc8860af2..3d9f68b6b5b 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -216,7 +216,8 @@ var runTests = function(err, data) { userTests.forEach(function(test, ix, arr){ try { if (test) { - var output = eval(reassembleTest(test, data)); + var test = JSON.stringify(reassembleTest(test, data)); + var output = eval(test); } } catch(error) { allTestsPassed = false; diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index 6b04e831641..bf8412d6394 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -2,7 +2,7 @@ { "_id" : "7123c8c441eddfaeb5bdef0d", "name": "Meet Bonfire", - "difficulty": 1, + "difficulty": "0", "description": [ "Click the button below for further instructions.", "Your goal is to fix the failing test.", @@ -15,12 +15,11 @@ ], "challengeSeed": "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\nreturn false;\n}\n\n", "challengeEntryPoint": "meetBonfire(\"You can do this!\");", - "bonfireNumber": 0 }, { "_id" : "aaa48de84e1ecc7c742e1124", "name": "Check for Palindromes", - "difficulty": 1, + "difficulty": "1", "description": [ "Return 'true' if a given string is a palindrome.", "A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation and case.", @@ -39,12 +38,11 @@ ], "challengeSeed": "function palindrome(str) {\n // Good luck!\n return true;\n}\n\n", "challengeEntryPoint": "palindrome(\"eye\");", - "bonfireNumber": 1 }, { "_id" : "ff0395860f5d3034dc0bfc94", "name": "Validate US Telephone Numbers", - "difficulty": 3, + "difficulty": "3", "description": [ "Return true if the passed string is a valid US phone number", "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:", @@ -77,76 +75,96 @@ ], "challengeSeed": "function telephoneCheck(str) {\n // Good luck!\n return true;\n}\n\n", "challengeEntryPoint": "telephoneCheck(\"555-555-5555\");", - "bonfireNumber": 2 }, { + "_id": "202eed8fc186c8434cb6d618", "name": "Reverse a String", + "difficulty": "1", "tests": [ "expect(reverseString('hello')).to.be.a('String');", "expect(reverseString('hello')).to.equal('olleh');", "expect(reverseString('Howdy')).to.equal('ydwoH');", "expect(reverseString('Greetings from Earth')).to.equal('htraE morf sgniteerG');" ], - "difficulty": "1", "description": [ - "Reverse the provided string. ", + "Reverse the provided string.", "You may need to turn the string into an array before you can reverse it.", "Your result must be a string." ], "challengeEntryPoint": "reverseString('hello');", "challengeSeed": "function reverseString(str) {\n return str;\r\n}", - "bonfireNumber": 0, - "_id": "202eed8fc186c8434cb6d618" }, - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + { + "_id": "302f7aae1aa3152a5b413bca", + "name": "Factorialize a Number", + "tests": [ + "expect(factorialize(5)).to.be.a(\"Number\");", + "expect(factorialize(5)).to.equal(120);", + "expect(factorialize(10)).to.equal(3628800);", + "expect(factorialize(20)).to.equal(2432902008176640000);" + ], + "difficulty": "1", + "description": [ + "Return the factorial of the provided integer.", + "If the integer is represented with the letter n, a factorial is the product of all positive integers less than or equal to n.", + "Factorials are often represented with the shorthand notation n!", + "For example: 5! = 1 * 2 * 3 * 4 * 5 = 120f" + ], + "challengeSeed": "function factorialize(num) {\n return num;\r\n}", + "challengeEntryPoint": "factorialize(5);" + }, + { + "_id": "a26cbbe9ad8655a977e1ceb5", + "name": "Find the Longest Word in a String", + "difficulty": "1", + "description": [ + "Return the length of the longest word in the provided sentence.", + "Your response should be a number." + ], + "challengeEntryPoint": "findLongestWord('The quick brown fox jumped over the lazy dog');", + "challengeSeed": "function findLongestWord(str) {\n return str.length;\r\n}", + "tests": [ + "expect(findLongestWord('The quick brown fox jumped over the lazy dog')).to.be.a('Number');", + "expect(findLongestWord('The quick brown fox jumped over the lazy dog')).to.equal(6);", + "expect(findLongestWord('May the force be with you')).to.equal(5);", + "expect(findLongestWord('Google do a barrel roll')).to.equal(6);", + "expect(findLongestWord('What is the average airspeed velocity of an unladen swallow')).to.equal(8);" + ] + }, + { + "_id": "3566b1109230028080c9345d", + "name": "Sum All Numbers in a Range", + "difficulty": "2", + "description": [ + "We'll pass you an array of two numbers. Return the sum those two numbers and all numbers between them.", + "The lowest number will not always come first." + ], + "challengeEntryPoint": "sumAll([1, 4]);", + "challengeSeed": "function sumAll(arr) {\n return(1);\r\n}", + "tests": [ + "expect(sumAll([1, 4])).to.be.a('Number');", + "expect(sumAll([1, 4])).to.equal(10);", + "expect(sumAll([4, 1])).to.equal(10);", + "expect(sumAll([5, 10])).to.equal(45);", + "expect(sumAll([10, 5])).to.equal(45);" + ] + }, + { + "_id": "fb6137d4e35944e21037b769", + "name": "Title Case a Sentence", + "difficulty": "1", + "description": [ + "Return the provided string with the first letter of each word capitalized.", + "For the purpose of this exercise, you should also capitalize connecting words like 'the' and 'of'." + ], + "challengeEntryPoint": "titleCase(\"I'm a little tea pot\")", + "challengeSeed": "function titleCase(str) {\n return str;\r\n}", + "tests": [ + "expect(titleCase(\"I'm a little tea pot\")).to.be.a('String');", + "expect(titleCase(\"I'm a little tea pot\")).to.equal(\"I'm A Little Tea Pot\");", + "expect(titleCase(\"sHoRt AnD sToUt\")).to.equal(\"Short And Stout\");", + "expect(titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\")).to.equal(\"Here Is My Handle Here Is My Spout\");" + ] + } ] diff --git a/views/bonfire/bonfire.jade b/views/bonfire/bonfire.jade index 62894f16303..ff07aee1752 100644 --- a/views/bonfire/bonfire.jade +++ b/views/bonfire/bonfire.jade @@ -15,6 +15,12 @@ block content script(src='/js/lib/jailed/jailed.js') script(src='/js/lib/bonfire/bonfireInit.js') .row + script(type="text/javascript"). + var tests = !{JSON.stringify(tests)}; + var challengeSeed = !{JSON.stringify(challengeSeed)}; + var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var title = !{JSON.stringify(title)}; + #mainEditorPanel.col-sm-12.col-md-7.col-xs-12 .panel.panel-primary.panel-bonfire .panel-heading.text-center Playground diff --git a/views/bonfire/public-generator.jade b/views/bonfire/public-generator.jade new file mode 100644 index 00000000000..13750a9b4d9 --- /dev/null +++ b/views/bonfire/public-generator.jade @@ -0,0 +1,44 @@ +extends ../layout +block content + .row + .col-md-offset-2.col-md-8.col-lg-offset-2.col-lg-8.text-center + h1 JSON generator for bonfire challenges - just fill the form out and get the correct format back + .col-xs-12.col-sm-12.col-md-offset-3.col-md-6.col-lg-offset-3-col-lg-6 + .panel + form.form-horizontal(method="POST", action="/bonfire/public-generator", name="bonfireInfo") + .form-group + label.col-sm-2.control-label(for='name') name: + .col-sm-10 + input#name.form-control(type='text', placeholder='name', name="name") + .form-group + label.col-sm-2.control-label(for='difficultyField') difficulty: + .col-sm-10 + label.radio-inline 1 + input#inlineRadio1(type='radio', name='difficulty', value='1') + label.radio-inline 2 + input#inlineRadio2(type='radio', name='difficulty', value='2') + label.radio-inline 3 + input#inlineRadio3(type='radio', name='difficulty', value='3') + label.radio-inline 4 + input#inlineRadio4(type='radio', name='difficulty', value='4') + label.radio-inline 5 + input#inlineRadio5(type='radio', name='difficulty', value='5') + .form-group + label.col-sm-2.control-label.wrappable(for='description') description: + .col-sm-10 + textarea#description.form-control(name="description", placeholder="Separate sentences by exactly one space only. Do not add in line breaks.") + .form-group + label.col-sm-2.control-label.wrappable(for='challengeSeed') challengeSeed: + .col-sm-10 + textarea#challengeSeed.form-control(name="challengeSeed", rows=5) + .form-group + label.col-sm-2.control-label.wrappable(for='challengeEntryPoint') challenge entrypoint: + .col-sm-10 + textarea#name.form-control(name="challengeEntryPoint", rows=1, type='text', placeholder="palindrome(\"eye\");") + .form-group + label.col-sm-2.control-label.wrappable(for='tests') tests: + .col-sm-10 + textarea#tests.form-control(name="tests", rows=5, placeholder="Separate tests by a newline.") + .form-group + .col-sm-offset-2.col-sm-10 + input.btn.btn-default(type='submit', value="submit") From ead9fe56d1de6c0d087540ac687790a2a810e92f Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Mon, 26 Jan 2015 12:30:04 -0500 Subject: [PATCH 26/39] Fixed bonfire to allow users to enter contractions --- app.js | 3 +++ controllers/bonfire.js | 18 +++++++++++++++++- public/js/lib/bonfire/bonfireFramework.js | 18 +++--------------- seed_data/bonfires.json | 8 ++++---- views/bonfire/show.jade | 2 +- 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index 279c24b74f1..0fbff67ef6d 100644 --- a/app.js +++ b/app.js @@ -260,6 +260,9 @@ app.get( '/bonfires/:bonfireNumber', bonfireController.returnIndividualBonfire ); +app.get('/bonfire', function(req, res) { + res.redirect(301, '/playground'); +}); app.get('/bonfires', bonfireController.returnBonfire); app.get('/bonfire/generator', bonfireController.returnGenerator); app.post('/bonfire/generator', bonfireController.generateChallenge); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 079aad96b15..e52a2d19fbb 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -12,7 +12,23 @@ var highestBonfireNumber = 2; exports.index = function(req, res) { res.render('bonfire/bonfire.jade', { - title: 'Learn to code with Bonfire' + title: 'Learn to code with Bonfire', + completedWith: null, + title: null, + difficulty: null, + brief: null, + details: null, + tests: null, + challengeSeed: null, + challengeEntryPoint: null, + cc: req.user ? req.user.bonfiresHash : undefined, + points: req.user ? req.user.points : undefined, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + bonfires: [], + bonfireHash: "test" + }); Bonfire.find({}, null, { sort: { difficulty: 1 } }, function(err, c) { diff --git a/public/js/lib/bonfire/bonfireFramework.js b/public/js/lib/bonfire/bonfireFramework.js index 3d9f68b6b5b..5514d1d7985 100644 --- a/public/js/lib/bonfire/bonfireFramework.js +++ b/public/js/lib/bonfire/bonfireFramework.js @@ -123,11 +123,6 @@ function bonfireExecute() { }); } -var replaceQuotesInTests = function() { - userTests.forEach(function(elt, ix, arr) { - arr[ix].text = arr[ix].text.replace(/\"/g,'\''); - }); -}; var userTests; var testSalt = Math.random(); @@ -145,9 +140,7 @@ var scrapeTests = function(userJavaScript) { 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); + userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length); if (!userTests) { userTests= []; @@ -157,9 +150,6 @@ var scrapeTests = function(userJavaScript) { match = regex.exec(userJavaScript); } - if (userTests) { - replaceQuotesInTests(); - } return userJavaScript; }; @@ -170,7 +160,6 @@ function removeComments(userJavaScript) { function removeLogs(userJavaScript) { return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); - return userJavaScript; } var pushed = false; @@ -210,14 +199,13 @@ var runTests = function(err, data) { 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 test = JSON.stringify(reassembleTest(test, data)); - var output = eval(test); + console.log(); + var output = eval(reassembleTest(test, data)); } } catch(error) { allTestsPassed = false; diff --git a/seed_data/bonfires.json b/seed_data/bonfires.json index bf8412d6394..920824fa2c0 100644 --- a/seed_data/bonfires.json +++ b/seed_data/bonfires.json @@ -14,7 +14,7 @@ "expect(meetBonfire(\"test\")).to.be.true;" ], "challengeSeed": "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\nreturn false;\n}\n\n", - "challengeEntryPoint": "meetBonfire(\"You can do this!\");", + "challengeEntryPoint": "meetBonfire(\"You can do this!\");" }, { "_id" : "aaa48de84e1ecc7c742e1124", @@ -37,7 +37,7 @@ "assert.deepEqual(palindrome(\"nope\"), false);" ], "challengeSeed": "function palindrome(str) {\n // Good luck!\n return true;\n}\n\n", - "challengeEntryPoint": "palindrome(\"eye\");", + "challengeEntryPoint": "palindrome(\"eye\");" }, { "_id" : "ff0395860f5d3034dc0bfc94", @@ -74,7 +74,7 @@ "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\");", + "challengeEntryPoint": "telephoneCheck(\"555-555-5555\");" }, { "_id": "202eed8fc186c8434cb6d618", @@ -92,7 +92,7 @@ "Your result must be a string." ], "challengeEntryPoint": "reverseString('hello');", - "challengeSeed": "function reverseString(str) {\n return str;\r\n}", + "challengeSeed": "function reverseString(str) {\n return str;\r\n}" }, { "_id": "302f7aae1aa3152a5b413bca", diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 23a3404d68b..bc1017d59b8 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -23,7 +23,7 @@ block content .col-xs-12.col-sm-12.col-md-3 #testCreatePanel h2.text-center #{name} - Difficulty:   + Difficulty if (difficulty == "0") i.ion-ios-flame-outline i.ion-ios-flame-outline From 48e535b9c5c22862f2d330314715e658a08c65ba Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Mon, 26 Jan 2015 11:02:22 -0800 Subject: [PATCH 27/39] now have both full width layout and normal layout --- public/css/main.less | 9 +- views/bonfire/bonfire.jade | 2 +- views/bonfire/show.jade | 2 +- views/layout-wide.jade | 33 +++++ views/layout.jade | 11 +- views/partials/navbar-narrow.jade | 3 + views/partials/navbar-wide.jade | 2 + views/partials/navbar.jade | 59 +++++---- views/resources/live-pair-programming.jade | 145 ++++++++------------- 9 files changed, 137 insertions(+), 129 deletions(-) create mode 100644 views/layout-wide.jade create mode 100644 views/partials/navbar-narrow.jade create mode 100644 views/partials/navbar-wide.jade diff --git a/public/css/main.less b/public/css/main.less index d9c9cd4ab04..b215b8a0f81 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -29,9 +29,14 @@ html { min-height: 100%; } -body { +body.top-and-bottom-margins { + padding-top: 80px; + margin-bottom: 60px; +} + +body.no-top-and-bottom-margins { padding-top: 50px; -// margin-bottom: 60px; + margin-bottom: 40px; } h1, h2 { diff --git a/views/bonfire/bonfire.jade b/views/bonfire/bonfire.jade index ff07aee1752..498f7d3a239 100644 --- a/views/bonfire/bonfire.jade +++ b/views/bonfire/bonfire.jade @@ -1,4 +1,4 @@ -extends ../layout +extends ../layout-wide block content script(src='/js/lib/codemirror/lib/codemirror.js') script(src='/js/lib/codemirror/addon/edit/closebrackets.js') diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index bc1017d59b8..6432e175f23 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -1,4 +1,4 @@ -extends ../layout +extends ../layout-wide block content script(src='/js/lib/codemirror/lib/codemirror.js') diff --git a/views/layout-wide.jade b/views/layout-wide.jade new file mode 100644 index 00000000000..63fb20c1d79 --- /dev/null +++ b/views/layout-wide.jade @@ -0,0 +1,33 @@ +doctype html +html(ng-app='profileValidation', lang='en') + head + script(src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js") + script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js") + script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js") + link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicon.ico') + link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css') + link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css') + link(rel='stylesheet', href='//code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css') + include partials/meta + title #{title} | Free Code Camp + meta(charset='utf-8') + meta(http-equiv='X-UA-Compatible', content='IE=edge') + meta(name='viewport', content='width=device-width, initial-scale=1.0') + meta(name='csrf-token', content=_csrf) + != css('main') + + body.no-top-and-bottom-margins + include partials/navbar-wide + include partials/flash + block content + include partials/footer + != js('application') +script. + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + ga('create', 'UA-55446531-1', 'auto'); + ga('require', 'displayfeatures'); + ga('send', 'pageview'); +script(src="//cdn.optimizely.com/js/999692993.js") \ No newline at end of file diff --git a/views/layout.jade b/views/layout.jade index d44dfea0306..dfddef33b4d 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -16,12 +16,13 @@ html(ng-app='profileValidation', lang='en') meta(name='csrf-token', content=_csrf) != css('main') - body - include partials/navbar - include partials/flash - block content + body.top-and-bottom-margins + include partials/navbar-narrow + .container + include partials/flash + block content include partials/footer - != js('application') + != js('application') script. (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), diff --git a/views/partials/navbar-narrow.jade b/views/partials/navbar-narrow.jade new file mode 100644 index 00000000000..b8ff7267f92 --- /dev/null +++ b/views/partials/navbar-narrow.jade @@ -0,0 +1,3 @@ +nav.navbar.navbar-default.navbar-fixed-top.nav-height + .container + include ./navbar \ No newline at end of file diff --git a/views/partials/navbar-wide.jade b/views/partials/navbar-wide.jade new file mode 100644 index 00000000000..96165a030db --- /dev/null +++ b/views/partials/navbar-wide.jade @@ -0,0 +1,2 @@ +nav.navbar.navbar-default.navbar-fixed-top.nav-height + include ./navbar \ No newline at end of file diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 0cbfe2dc687..8388ea2ba65 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -1,45 +1,44 @@ -nav.navbar.navbar-default.navbar-fixed-top.nav-height - .navbar-header - button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse') +.navbar-header + button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar - a.navbar-brand(href='/') + a.navbar-brand(href='/') img.img-responsive.nav-logo(src='https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg', alt='learn to code javascript at Free Code Camp logo') - .collapse.navbar-collapse - ul.nav.navbar-nav.navbar-right.hamburger-dropdown +.collapse.navbar-collapse + ul.nav.navbar-nav.navbar-right.hamburger-dropdown - if (!cc) - li - a(href='/challenges/0') Challenges + li + a(href='/challenges/0') Challenges - else - li - a(href='/') Challenges + li + a(href='/') Challenges - if (!cc || (cc && cc[1] < 1)) - li - a(href='/challenges/1') Chat + li + a(href='/challenges/1') Chat - else - li - a(href='http://chat.freecodecamp.com' target='_blank') Chat + li + a(href='http://chat.freecodecamp.com' target='_blank') Chat - if (!cc || (cc && cc[2] < 1)) - li - a(href='/challenges/2') Forum + li + a(href='/challenges/2') Forum - else - li - a(href='http://forum.freecodecamp.com' target='_blank') Forum + li + a(href='http://forum.freecodecamp.com' target='_blank') Forum li - a(href='/bonfires') Bonfires + a(href='/bonfires') Bonfires if !user - li       - li - a.btn.signup-btn.signup-btn-nav(href='/login') Sign in + li       + li + a.btn.signup-btn.signup-btn-nav(href='/login') Sign in else - li - a(href='/account') [ #{user.points} ] + li + a(href='/account') [ #{user.points} ] .hidden-xs - if user.profile.picture - a(href='/account') - img.profile-picture.float-right(src='#{user.profile.picture}') - else - a(href='/account') - img.profile-picture.float-right(src='#{user.gravatar(60)}') \ No newline at end of file + if user.profile.picture + a(href='/account') + img.profile-picture.float-right(src='#{user.profile.picture}') + else + a(href='/account') + img.profile-picture.float-right(src='#{user.gravatar(60)}') \ No newline at end of file diff --git a/views/resources/live-pair-programming.jade b/views/resources/live-pair-programming.jade index b6c3a187272..2236b439a29 100644 --- a/views/resources/live-pair-programming.jade +++ b/views/resources/live-pair-programming.jade @@ -1,91 +1,56 @@ -doctype html -html(ng-app='profileValidation', lang='en') - head - script(src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js") - script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js") - script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js") - link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicon.ico') - link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css') - link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css') - link(rel='stylesheet', href='//code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css') - include ../partials/meta - title #{title} | Free Code Camp - meta(charset='utf-8') - meta(http-equiv='X-UA-Compatible', content='IE=edge') - meta(name='viewport', content='width=device-width, initial-scale=1.0') - meta(name='csrf-token', content=_csrf) - != css('main') - body - block content - include ../partials/navbar - .panel.panel-primary - .panel-heading.landing-panel-heading.text-center Live Pair Programming - .panel-body - .landing-panel-body.text-center - h2 We live pair program every Tuesday from 9 pm to 10 pm EST (Eastern Standard Time). - h2 Our next session will be January 27th, 2015 at 9 p.m. EST! - h2 Join the discussion in our   - a(href="chat.freecodecamp.com", target="_blank") FreeCodeCamp chat room. - h2 Watch the live stream below or on our   - a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel - | . - .row - .col-md-8.col-xs-12 - .embed-responsive.embed-responsive-16by9 - iframe(src='http://www.twitch.tv/freecodecamp/embed', frameborder='0', scrolling='no') - .col-md-4.col-xs-12 - .visible-sm.visible-xs - .embed-responsive.embed-responsive-16by9 - iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no') - .visible-md.visible-lg - .embed-responsive.embed-responsive-twitch-chat - iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no') +extends ../layout-wide +block content + include ../partials/navbar-wide + .panel.panel-primary + .panel-heading.landing-panel-heading.text-center Live Pair Programming + .panel-body + .landing-panel-body.text-center + h2 We live pair program every Tuesday from 9 pm to 10 pm EST (Eastern Standard Time). + h2 Our next session will be January 27th, 2015 at 9 p.m. EST! + h2 Join the discussion in our   + a(href="chat.freecodecamp.com", target="_blank") FreeCodeCamp chat room. + h2 Watch the live stream below or on our   + a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel + | . + .row + .col-md-8.col-xs-12 + .embed-responsive.embed-responsive-16by9 + iframe(src='http://www.twitch.tv/freecodecamp/embed', frameborder='0', scrolling='no') + .col-md-4.col-xs-12 + .visible-sm.visible-xs + .embed-responsive.embed-responsive-16by9 + iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no') + .visible-md.visible-lg + .embed-responsive.embed-responsive-twitch-chat + iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no') - br - .panel.panel-primary - .panel-heading.landing-panel-heading.text-center Previous Live Pair Programming Sessions - .panel-body - .landing-panel-body.text-center - .col-xs-12 - .embed-responsive.embed-responsive-16by9.big-break - iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw') - h3.wrappable link:   - a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw - .embed-responsive.embed-responsive-16by9.big-break - iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0') - h3.wrappable link:   - a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0 - .embed-responsive.embed-responsive-16by9.big-break - iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs') - h3.wrappable link:   - a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs - .embed-responsive.embed-responsive-16by9.big-break - iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE') - h3.wrappable link:   - a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE - .embed-responsive.embed-responsive-16by9.big-break - iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA') - h3.wrappable link:   - a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA - h3 Got 3 minutes? Learn to code with us! - a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free) - br - br - include ../partials/footer - != js('application') - script. - (function (i, s, o, g, r, a, m) { - i['GoogleAnalyticsObject'] = r; - i[r] = i[r] || function () { - (i[r].q = i[r].q || []).push(arguments) - }, i[r].l = 1 * new Date(); - a = s.createElement(o), - m = s.getElementsByTagName(o)[0]; - a.async = 1; - a.src = g; - m.parentNode.insertBefore(a, m) - })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); - ga('create', 'UA-55446531-1', 'auto'); - ga('require', 'displayfeatures'); - ga('send', 'pageview'); - script(src="//cdn.optimizely.com/js/999692993.js") + br + .panel.panel-primary + .panel-heading.landing-panel-heading.text-center Previous Live Pair Programming Sessions + .panel-body + .landing-panel-body.text-center + .col-xs-12 + .embed-responsive.embed-responsive-16by9.big-break + iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw') + h3.wrappable link:   + a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw + .embed-responsive.embed-responsive-16by9.big-break + iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0') + h3.wrappable link:   + a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0 + .embed-responsive.embed-responsive-16by9.big-break + iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs') + h3.wrappable link:   + a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs + .embed-responsive.embed-responsive-16by9.big-break + iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE') + h3.wrappable link:   + a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE + .embed-responsive.embed-responsive-16by9.big-break + iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA') + h3.wrappable link:   + a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA + h3 Got 3 minutes? Learn to code with us! + a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free) + br + br \ No newline at end of file From f8343d4c13e6bd80e8f5974d6e7b0801a4ef5830 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Mon, 26 Jan 2015 11:38:19 -0800 Subject: [PATCH 28/39] full layout now works. refactor files and routes to have more conventional names and redirects where necessary --- app.js | 22 ++++++++++++----- controllers/bonfire.js | 5 +++- controllers/resources.js | 2 +- controllers/user.js | 24 +++++++++---------- views/account/{profile.jade => account.jade} | 0 views/account/{login.jade => signin.jade} | 0 .../{about.jade => learn-to-code.jade} | 0 views/resources/live-pair-programming.jade | 12 +++------- 8 files changed, 36 insertions(+), 29 deletions(-) rename views/account/{profile.jade => account.jade} (100%) rename views/account/{login.jade => signin.jade} (100%) rename views/resources/{about.jade => learn-to-code.jade} (100%) diff --git a/app.js b/app.js index 0fbff67ef6d..d4a412bb4e8 100644 --- a/app.js +++ b/app.js @@ -208,16 +208,26 @@ app.get('/deploy-a-website', resourcesController.deployAWebsite); app.get('/gmail-shortcuts', resourcesController.gmailShortcuts); app.get('/control-shortcuts', resourcesController.controlShortcuts); app.get('/control-shortcuts', resourcesController.deployAWebsite); -app.get('/stats', resourcesController.stats); +app.get('/stats', function(req, res) { + res.redirect(301, '/learn-to-code'); +}); app.get( '/pair-program-with-team-viewer', resourcesController.pairProgramWithTeamViewer ); app.get('/learn-to-code', resourcesController.about); -app.get('/about', resourcesController.about); -app.get('/login', userController.getLogin); -app.post('/login', userController.postLogin); -app.get('/logout', userController.logout); +app.get('/about', function(req, res) { + res.redirect(301, '/learn-to-code'); +}); +app.get('/signin', userController.getSignin); +app.get('/login', function(req, res) { + res.redirect(301, '/signin'); +}); +app.post('/signin', userController.postSignin); +app.get('/signout', userController.signout); +app.get('/logout', function(req, res) { + res.redirect(301, '/signout'); +}); app.get('/forgot', userController.getForgot); app.post('/forgot', userController.postForgot); app.get('/reset/:token', userController.getReset); @@ -225,7 +235,7 @@ app.post('/reset/:token', userController.postReset); app.get('/email-signup', userController.getEmailSignup); app.get('/email-signin', userController.getEmailSignin); app.post('/email-signup', userController.postEmailSignup); -app.post('/email-signin', userController.postLogin); +app.post('/email-signin', userController.postSignin); app.get('/nonprofits', contactController.getNonprofitsForm); app.post('/nonprofits', contactController.postNonprofitsForm); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index e52a2d19fbb..e9cefa2bc4b 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -40,9 +40,12 @@ exports.index = function(req, res) { }; exports.returnBonfire = function(req, res, next) { + if (!req.user) { + req.user = new User(); + } var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; // This code is in bad need of refactoring - var bonfiresToFind = req.user.bonfiresHash; + var bonfiresToFind = req.user ? req.user.bonfiresHash : []; var bonfiresArray = _.map(bonfiresToFind, function(value, index) { return [index, value]; }); diff --git a/controllers/resources.js b/controllers/resources.js index 5eea62da79e..b6b4f99aa5a 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -142,7 +142,7 @@ module.exports = { debug('User err: ', err); next(err); } - res.render('resources/about', { + res.render('resources/learn-to-code', { title: 'About Free Code Camp and Our Team of Volunteers', daysRunning: daysRunning, nonprofitProjects: nonprofitProjects, diff --git a/controllers/user.js b/controllers/user.js index 2421bdcbedd..7c4a9dc77a7 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -12,23 +12,23 @@ var _ = require('lodash'), //TODO(Berks): Refactor to use module.exports = {} pattern. /** - * GET /login - * Login page. + * GET /signin + * Siginin page. */ -exports.getLogin = function(req, res) { +exports.getSignin = function(req, res) { if (req.user) return res.redirect('/'); - res.render('account/login', { + res.render('account/signin', { title: 'Free Code Camp Login' }); }; /** - * POST /login + * POST /signin * Sign in using email and password. */ -exports.postLogin = function(req, res, next) { +exports.postSignin = function(req, res, next) { req.assert('email', 'Email is not valid').isEmail(); req.assert('password', 'Password cannot be blank').notEmpty(); @@ -36,14 +36,14 @@ exports.postLogin = function(req, res, next) { if (errors) { req.flash('errors', errors); - return res.redirect('/login'); + return res.redirect('/signin'); } passport.authenticate('local', function(err, user, info) { if (err) return next(err); if (!user) { req.flash('errors', { msg: info.message }); - return res.redirect('/login'); + return res.redirect('/signin'); } req.logIn(user, function(err) { if (err) return next(err); @@ -54,11 +54,11 @@ exports.postLogin = function(req, res, next) { }; /** - * GET /logout + * GET /signout * Log out. */ -exports.logout = function(req, res) { +exports.signout = function(req, res) { req.logout(); res.redirect('/'); }; @@ -76,7 +76,7 @@ exports.getEmailSignin = function(req, res) { }; /** - * GET /email-signin + * GET /signin * Signup page. */ @@ -146,7 +146,7 @@ exports.getAccount = function(req, res) { console.error('Challenge err: ', err); next(err); } - res.render('account/profile', { + res.render('account/account', { title: 'Manage your Free Code Camp Account', challenges: c, ch: req.user.challengesHash, diff --git a/views/account/profile.jade b/views/account/account.jade similarity index 100% rename from views/account/profile.jade rename to views/account/account.jade diff --git a/views/account/login.jade b/views/account/signin.jade similarity index 100% rename from views/account/login.jade rename to views/account/signin.jade diff --git a/views/resources/about.jade b/views/resources/learn-to-code.jade similarity index 100% rename from views/resources/about.jade rename to views/resources/learn-to-code.jade diff --git a/views/resources/live-pair-programming.jade b/views/resources/live-pair-programming.jade index 2236b439a29..92a911cc695 100644 --- a/views/resources/live-pair-programming.jade +++ b/views/resources/live-pair-programming.jade @@ -1,14 +1,12 @@ extends ../layout-wide block content include ../partials/navbar-wide - .panel.panel-primary - .panel-heading.landing-panel-heading.text-center Live Pair Programming + .panel .panel-body .landing-panel-body.text-center + h1 Live Pair Programming h2 We live pair program every Tuesday from 9 pm to 10 pm EST (Eastern Standard Time). h2 Our next session will be January 27th, 2015 at 9 p.m. EST! - h2 Join the discussion in our   - a(href="chat.freecodecamp.com", target="_blank") FreeCodeCamp chat room. h2 Watch the live stream below or on our   a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel | . @@ -24,11 +22,7 @@ block content .embed-responsive.embed-responsive-twitch-chat iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no') - br - .panel.panel-primary - .panel-heading.landing-panel-heading.text-center Previous Live Pair Programming Sessions - .panel-body - .landing-panel-body.text-center + h1 Previous Live Pair Programming Sessions .col-xs-12 .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw') From 2e5dd6ea7370af9cd31e36f2447f2017f9de58bc Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Mon, 26 Jan 2015 18:28:14 -0500 Subject: [PATCH 29/39] Strange user behavior, model not updating correctly --- app.js | 7 +- controllers/bonfire.js | 96 ++++++++++++++--------- controllers/resources.js | 5 ++ models/User.js | 56 ++++++++++++- public/js/lib/bonfire/bonfireFramework.js | 6 +- seed_data/bonfires.json | 12 +-- views/bonfire/show.jade | 74 ++++++++--------- 7 files changed, 165 insertions(+), 91 deletions(-) diff --git a/app.js b/app.js index d4a412bb4e8..bc4b44614b7 100644 --- a/app.js +++ b/app.js @@ -267,13 +267,13 @@ app.all('/account', passportConf.isAuthenticated); app.get('/account/api', userController.getAccountAngular); app.get('/playground', bonfireController.index); app.get( - '/bonfires/:bonfireNumber', + '/bonfires/:bonfireName', bonfireController.returnIndividualBonfire ); app.get('/bonfire', function(req, res) { res.redirect(301, '/playground'); }); -app.get('/bonfires', bonfireController.returnBonfire); +app.get('/bonfires', bonfireController.returnNextBonfire); app.get('/bonfire/generator', bonfireController.returnGenerator); app.post('/bonfire/generator', bonfireController.generateChallenge); app.get('/bonfire/public-generator', bonfireController.publicGenerator); @@ -311,8 +311,6 @@ app.post('/completed-challenge', function (req, res) { }); app.post('/completed-bonfire/', function (req, res) { - debug(req.body, 'In post method' - ); // TODO: remove debug statement req.user.bonfiresHash[parseInt(req.body.bonfireNumber)] = Math.round(+new Date() / 1000); var timestamp = req.user.bonfiresHash; @@ -364,6 +362,7 @@ app.post('/completed-bonfire/', function (req, res) { }; req.user.save(); } + bonfireController.returnNextBonfire(req, res); }); /** diff --git a/controllers/bonfire.js b/controllers/bonfire.js index e9cefa2bc4b..b8209112399 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -8,26 +8,26 @@ var _ = require('lodash'), * Bonfire controller */ -var highestBonfireNumber = 2; +var highestBonfireNumber = resources.numberOfBonfires(); exports.index = function(req, res) { - res.render('bonfire/bonfire.jade', { - title: 'Learn to code with Bonfire', + res.render('bonfire/show.jade', { completedWith: null, - title: null, - difficulty: null, - brief: null, - details: null, - tests: null, - challengeSeed: null, - challengeEntryPoint: null, + title: 'Bonfire Playground', + name: 'Bonfire Playground', + difficulty: 0, + brief: 'Feel free to play around!', + details: '', + tests: [], + challengeSeed: '', + challengeEntryPoint: '', cc: req.user ? req.user.bonfiresHash : undefined, points: req.user ? req.user.points : undefined, verb: resources.randomVerb(), phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), + compliments: resources.randomCompliment(), bonfires: [], - bonfireHash: "test" + bonfireHash: 'test' }); @@ -39,11 +39,12 @@ exports.index = function(req, res) { }); }; -exports.returnBonfire = function(req, res, next) { +exports.returnNextBonfire = function(req, res, next) { + var bonfireNumber; if (!req.user) { req.user = new User(); + //return res.redirect('/bonfires/meet-bonfire'); } - var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; // This code is in bad need of refactoring var bonfiresToFind = req.user ? req.user.bonfiresHash : []; var bonfiresArray = _.map(bonfiresToFind, function(value, index) { @@ -51,48 +52,65 @@ exports.returnBonfire = function(req, res, next) { }); // Get rid of the first entry, which will be a useless function that causes an error. bonfiresArray.shift(); - unsolvedBonfires = []; + var unsolvedBonfires = []; for (i = 0; i < bonfiresArray.length; i++) { if (bonfiresArray[i][1]["completedDate"] === 0) { unsolvedBonfires.push(bonfiresArray[i][0]) } } - //.where('likes').in(['vaporizing', 'talking']) + var displayedBonfires = Bonfire.find({}).where('_id').in(unsolvedBonfires).sort({ difficulty: 1 }); displayedBonfires.exec(function(err, bonfire) { if (err) { next(err); } - res.render('bonfire/show', { - completedWith: null, - title: bonfire[bonfireNumber].name, - name: bonfire[bonfireNumber].name, - difficulty: +bonfire[bonfireNumber].difficulty, - brief: bonfire[bonfireNumber].description[0], - details: bonfire[bonfireNumber].description.slice(1), - tests: bonfire[bonfireNumber].tests, - challengeSeed: bonfire[bonfireNumber].challengeSeed, - challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, - cc: req.user ? req.user.bonfiresHash : undefined, - points: req.user ? req.user.points : undefined, - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliments: resources.randomCompliment(), - bonfires: bonfire, - bonfireHash: bonfire[bonfireNumber]._id - }); + debug('Finding next bonfire for user', bonfire); + nameString = bonfire[0].name.toLowerCase().replace(/\s/g, '-'); + return res.redirect('/bonfires/' + nameString); + //res.render('bonfire/show', { + // completedWith: null, + // title: bonfire[bonfireNumber].name, + // name: bonfire[bonfireNumber].name, + // difficulty: +bonfire[bonfireNumber].difficulty, + // brief: bonfire[bonfireNumber].description[0], + // details: bonfire[bonfireNumber].description.slice(1), + // tests: bonfire[bonfireNumber].tests, + // challengeSeed: bonfire[bonfireNumber].challengeSeed, + // challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + // cc: req.user ? req.user.bonfiresHash : undefined, + // points: req.user ? req.user.points : undefined, + // verb: resources.randomVerb(), + // phrase: resources.randomPhrase(), + // compliments: resources.randomCompliment(), + // bonfires: bonfire, + // bonfireHash: bonfire[bonfireNumber]._id + //}); }); }; exports.returnIndividualBonfire = function(req, res, next) { - var bonfireNumber = parseInt(req.params.bonfireNumber) || 0; + var bonfireName = req.params.bonfireName; + debug('Getting this as argument for bonfireName', bonfireName); + //if (!/[a-z\-]+/i.test(bonfireName)) { + // bonfireName = 'meet bonfire'; + //} - if (bonfireNumber > highestBonfireNumber) { bonfireNumber = 0; } - Bonfire.find({}, null, { sort: { difficulty: 1 } }, function(err, bonfire) { + bonfireName = bonfireName.replace(/\-/g, ' '); + debug('Checking sanity of name of bonfire after replacing "-" characters', bonfireName); + var bonfireNumber = 0; + + Bonfire.find({"name" : new RegExp(bonfireName, 'i')}, function(err, bonfire) { if (err) { next(err); } + if (bonfire.length < 1) { + debug('Full Bonfire', bonfire); + req.flash('errors', { + msg: "404: We couldn't find a bonfire with that name. Please double check the name." + }); + return res.redirect('/bonfires/meet-bonfire') + } res.render('bonfire/show', { completedWith: null, title: bonfire[bonfireNumber].name, @@ -137,8 +155,8 @@ exports.returnGenerator = function(req, res) { function randomString() { var chars = "0123456789abcdef"; - var string_length = 24; - var randomstring = ''; + var string_length = 23; + var randomstring = 'a'; for (var i=0; i Date: Tue, 27 Jan 2015 01:22:02 -0500 Subject: [PATCH 30/39] massive redesign of user model and the way it interacts with bonfires, successfully returning next bonfire for the user, showing meet bonfire to unauthenticated users --- app.js | 51 ++++++++++++++----------- controllers/bonfire.js | 70 +++++++++++++++++----------------- controllers/resources.js | 6 +++ models/User.js | 81 +++------------------------------------- public/js/main.js | 3 +- 5 files changed, 77 insertions(+), 134 deletions(-) diff --git a/app.js b/app.js index bc4b44614b7..9c24c7240f9 100644 --- a/app.js +++ b/app.js @@ -311,25 +311,14 @@ app.post('/completed-challenge', function (req, res) { }); app.post('/completed-bonfire/', function (req, res) { - req.user.bonfiresHash[parseInt(req.body.bonfireNumber)] = - Math.round(+new Date() / 1000); - var timestamp = req.user.bonfiresHash; - var points = 0; - for (var key in timestamp) { - if (timestamp[key] > 0) { - points += 1; - } - } + + // TODO: remove debug statement + debug(req.body.bonfireInfo, 'This is bonfire info we got from posted'); var isCompletedWith = req.body.bonfireInfo.completedWith || undefined; var isCompletedDate = Math.round(+new Date() / 1000); var bonfireHash = req.body.bonfireInfo.bonfireHash; var isSolution = req.body.bonfireInfo.solution; - req.user.bonfiresHash[bonfireHash] = { - completedWith: isCompletedWith, - completedDate: isCompletedDate, - solution: isSolution - }; if (isCompletedWith) { var paired = User.find({"profile.username": isCompletedWith}).limit(1); @@ -338,16 +327,30 @@ app.post('/completed-bonfire/', function (req, res) { return err; } else { pairedWith = pairedWith.pop(); - pairedWith.bonfiresHash[bonfireHash] = { + pairedWith.completedBonfires.push({ + _id: bonfireHash, completedWith: req.user._id, completedDate: isCompletedDate, solution: isSolution - }; - req.user.bonfiresHash[bonfireHash] = { + }) + + req.user.completedBonfires.push({ + _id: bonfireHash, completedWith: pairedWith._id, completedDate: isCompletedDate, solution: isSolution - }; + }) + + var index = req.user.uncompletedBonfires.indexOf(bonfireHash); + + if (index > -1) { + req.user.uncompletedBonfires.splice(index,1) + } + + index = pairedWith.uncompletedBonfires.indexOf(bonfireHash); + if (index > -1) { + req.user.uncompletedBonfires.splice(index,1) + } req.user.save(); pairedWith.save(); @@ -355,14 +358,20 @@ app.post('/completed-bonfire/', function (req, res) { } }) } else { - req.user.bonfiresHash[bonfireHash] = { + req.user.completedBonfires.push({ + _id: bonfireHash, completedWith: null, completedDate: isCompletedDate, solution: isSolution - }; + }) + + var index = req.user.uncompletedBonfires.indexOf(bonfireHash); + + if (index > -1) { + req.user.uncompletedBonfires.splice(index,1) + } req.user.save(); } - bonfireController.returnNextBonfire(req, res); }); /** diff --git a/controllers/bonfire.js b/controllers/bonfire.js index b8209112399..88d4fe61921 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -45,22 +45,23 @@ exports.returnNextBonfire = function(req, res, next) { req.user = new User(); //return res.redirect('/bonfires/meet-bonfire'); } - // This code is in bad need of refactoring - var bonfiresToFind = req.user ? req.user.bonfiresHash : []; - var bonfiresArray = _.map(bonfiresToFind, function(value, index) { - return [index, value]; - }); - // Get rid of the first entry, which will be a useless function that causes an error. - bonfiresArray.shift(); - var unsolvedBonfires = []; - for (i = 0; i < bonfiresArray.length; i++) { - if (bonfiresArray[i][1]["completedDate"] === 0) { - unsolvedBonfires.push(bonfiresArray[i][0]) - } + var currentTime = parseInt(+new Date() / 1000) + if (currentTime - req.user.lastContentSync > 86400) { + req.user.lastContentSync = currentTime; + var completed = req.user.completedBonfires; + // TODO : remove debug statement + debug(req.user, 'this is the user'); + req.user.uncompletedBonfires = resources.allBonfireIds().filter(function(elem) { + if (completed.indexOf(elem) === -1) { + return elem; + } + }); } + var uncompletedBonfires = req.user.uncompletedBonfires; - var displayedBonfires = Bonfire.find({}).where('_id').in(unsolvedBonfires).sort({ difficulty: 1 }); + + var displayedBonfires = Bonfire.find({'_id': uncompletedBonfires[0]}); displayedBonfires.exec(function(err, bonfire) { if (err) { next(err); @@ -91,13 +92,8 @@ exports.returnNextBonfire = function(req, res, next) { exports.returnIndividualBonfire = function(req, res, next) { var bonfireName = req.params.bonfireName; - debug('Getting this as argument for bonfireName', bonfireName); - //if (!/[a-z\-]+/i.test(bonfireName)) { - // bonfireName = 'meet bonfire'; - //} bonfireName = bonfireName.replace(/\-/g, ' '); - debug('Checking sanity of name of bonfire after replacing "-" characters', bonfireName); var bonfireNumber = 0; Bonfire.find({"name" : new RegExp(bonfireName, 'i')}, function(err, bonfire) { @@ -110,25 +106,27 @@ exports.returnIndividualBonfire = function(req, res, next) { msg: "404: We couldn't find a bonfire with that name. Please double check the name." }); return res.redirect('/bonfires/meet-bonfire') + } else { + res.render('bonfire/show', { + completedWith: null, + title: bonfire[bonfireNumber].name, + name: bonfire[bonfireNumber].name, + difficulty: +bonfire[bonfireNumber].difficulty, + brief: bonfire[bonfireNumber].description[0], + details: bonfire[bonfireNumber].description.slice(1), + tests: bonfire[bonfireNumber].tests, + challengeSeed: bonfire[bonfireNumber].challengeSeed, + challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + cc: !!req.user, + points: req.user ? req.user.points : undefined, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + bonfires: bonfire, + bonfireHash: bonfire[bonfireNumber]._id + + }); } - res.render('bonfire/show', { - completedWith: null, - title: bonfire[bonfireNumber].name, - name: bonfire[bonfireNumber].name, - difficulty: +bonfire[bonfireNumber].difficulty, - brief: bonfire[bonfireNumber].description[0], - details: bonfire[bonfireNumber].description.slice(1), - tests: bonfire[bonfireNumber].tests, - challengeSeed: bonfire[bonfireNumber].challengeSeed, - challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, - cc: req.user ? req.user.bonfiresHash : undefined, - points: req.user ? req.user.points : undefined, - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - bonfires: bonfire, - bonfireHash: bonfire[bonfireNumber]._id - }); }); }; diff --git a/controllers/resources.js b/controllers/resources.js index 6642e70a906..ce9c0160e46 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -187,6 +187,12 @@ module.exports = { numberOfBonfires: function() { return bonfires.length - 1; + }, + + allBonfireIds: function() { + return bonfires.map(function(elem) { + return elem._id; + }) } }; diff --git a/models/User.js b/models/User.js index 906310f63bd..d35020b0ad1 100644 --- a/models/User.js +++ b/models/User.js @@ -353,81 +353,12 @@ var userSchema = new mongoose.Schema({ }, resetPasswordToken: String, resetPasswordExpires: Date, - bonfiresHash: { - ab6137d4e35944e21037b769: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - a3566b1109230028080c9345: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - a26cbbe9ad8655a977e1ceb5: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - a302f7aae1aa3152a5b413bc: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - a202eed8fc186c8434cb6d61: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - aff0395860f5d3034dc0bfc9: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - aaa48de84e1ecc7c742e1124: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - }, - ad7123c8c441eddfaeb5bdef: { - hash: String, - completedWith: String, - completedDate: { - type: Number, - default: 0 - }, - solution: String - } - }, - bonfires: Array + uncompletedBonfires: Array, + completedBonfires: Array, + lastContentSync: { + type: Number, + default: 0 + } }); /** diff --git a/public/js/main.js b/public/js/main.js index bd96ace1aca..5eb045dab92 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -74,8 +74,7 @@ $(document).ready(function() { console.log(didCompleteWith); console.log(bonfireSolution, thisBonfireHash); completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash); - l = location.pathname.split('/'); - window.location = '/bonfires/' + (parseInt(l[l.length - 1]) + 1); + window.location = '/bonfires/'; }); // Bonfire instructions functions From be0cf978f5d611a5c06a872381f17a29392c7ba6 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 02:51:59 -0500 Subject: [PATCH 31/39] Correctly sorting unsolved bonfires --- app.js | 23 +-- controllers/bonfire.js | 18 +-- controllers/resources.js | 303 ++++++++++++++++++++------------------- public/js/main.js | 12 +- 4 files changed, 179 insertions(+), 177 deletions(-) diff --git a/app.js b/app.js index 9c24c7240f9..2d089000dba 100644 --- a/app.js +++ b/app.js @@ -326,6 +326,17 @@ app.post('/completed-bonfire/', function (req, res) { if (err) { return err; } else { + var index = req.user.uncompletedBonfires.indexOf(bonfireHash); + + if (index > -1) { + req.user.uncompletedBonfires.splice(index,1) + } + + index = pairedWith.uncompletedBonfires.indexOf(bonfireHash); + if (index > -1) { + pairedWith.uncompletedBonfires.splice(index,1) + } + pairedWith = pairedWith.pop(); pairedWith.completedBonfires.push({ _id: bonfireHash, @@ -341,17 +352,8 @@ app.post('/completed-bonfire/', function (req, res) { solution: isSolution }) - var index = req.user.uncompletedBonfires.indexOf(bonfireHash); - - if (index > -1) { - req.user.uncompletedBonfires.splice(index,1) - } - - index = pairedWith.uncompletedBonfires.indexOf(bonfireHash); - if (index > -1) { - req.user.uncompletedBonfires.splice(index,1) - } + debug('saving user with a pair'); req.user.save(); pairedWith.save(); @@ -370,6 +372,7 @@ app.post('/completed-bonfire/', function (req, res) { if (index > -1) { req.user.uncompletedBonfires.splice(index,1) } + debug("Saving user without a pair"); req.user.save(); } }); diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 88d4fe61921..3f7a6554535 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -30,25 +30,19 @@ exports.index = function(req, res) { bonfireHash: 'test' }); - - Bonfire.find({}, null, { sort: { difficulty: 1 } }, function(err, c) { - if (err) { - debug('bonfire err: ', err); - next(err); - } - }); }; exports.returnNextBonfire = function(req, res, next) { - var bonfireNumber; + if (!req.user) { req.user = new User(); - //return res.redirect('/bonfires/meet-bonfire'); } var currentTime = parseInt(+new Date() / 1000) if (currentTime - req.user.lastContentSync > 86400) { req.user.lastContentSync = currentTime; - var completed = req.user.completedBonfires; + var completed = req.user.completedBonfires.map(function(elem) { + return elem._id; + }); // TODO : remove debug statement debug(req.user, 'this is the user'); req.user.uncompletedBonfires = resources.allBonfireIds().filter(function(elem) { @@ -57,6 +51,8 @@ exports.returnNextBonfire = function(req, res, next) { } }); } + debug('These are completed bonfires', completed); + debug('These are uncompleted bonfires', req.user.uncompletedBonfires); var uncompletedBonfires = req.user.uncompletedBonfires; @@ -66,7 +62,7 @@ exports.returnNextBonfire = function(req, res, next) { if (err) { next(err); } - debug('Finding next bonfire for user', bonfire); + nameString = bonfire[0].name.toLowerCase().replace(/\s/g, '-'); return res.redirect('/bonfires/' + nameString); //res.render('bonfire/show', { diff --git a/controllers/resources.js b/controllers/resources.js index ce9c0160e46..61becf46a5e 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -6,7 +6,8 @@ var User = require('../models/User'), Challenge = require('./../models/Challenge'), bonfires = require('../seed_data/bonfires.json'); Client = require('node-rest-client').Client, - client = new Client(); + client = new Client(), + debug = require('debug')('freecc:cntr:bonfires'); /** * GET / @@ -14,161 +15,161 @@ var User = require('../models/User'), */ module.exports = { - privacy: function privacy(req, res) { - res.render('resources/privacy', { - title: 'Privacy' - }); - }, - - stats: function stats(req, res) { - var date1 = new Date("10/15/2014"); - var date2 = new Date(); - var timeDiff = Math.abs(date2.getTime() - date1.getTime()); - var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); - client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, response) { - var nonprofitProjects = (trello && trello.length) || 15; - User.count({'points': {'$gt': 2}}, function(err, c3) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 9}}, function(err, c10) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 29}}, function(err, c30) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 53}}, function(err, all) { if (err) { debug('User err: ', err); next(err); } - res.render('resources/stats', { - title: 'Free Code Camp Stats:', - daysRunning: daysRunning, - nonprofitProjects: nonprofitProjects, - c3: c3, - c10: c10, - c30: c30, - all: all - }); - }); - }); + privacy: function privacy(req, res) { + res.render('resources/privacy', { + title: 'Privacy' }); - }); - }); - }, + }, - deployAWebsite: function deployAWebsite(req, res) { - res.render('resources/deploy-a-website', { - title: 'Deploy a Dynamic Website in 7 Minutes' - }); - }, - - nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) { - res.render('resources/nonprofit-project-instructions', { - title: 'Nonprofit Project Instructions' - }); - }, - - gmailShortcuts: function gmailShortcuts(req, res) { - res.render('resources/gmail-shortcuts', { - title: 'These Gmail Shortcuts will save you Hours' - }); - }, - - controlShortcuts: function controlShortcuts(req, res) { - res.render('resources/control-shortcuts', { - title: 'These Control Shortcuts will save you Hours' - }); - }, - - chromebook: function chromebook(req, res) { - res.render('resources/chromebook', { - title: 'Win a Chromebook' - }); - }, - - jqueryExercises: function jqueryExercises(req, res) { - res.render('resources/jquery-exercises', { - title: 'jQuery Exercises' - }); - }, - - livePairProgramming: function(req, res) { - res.render('resources/live-pair-programming', { - title: 'Live Pair Programming' - }); - }, - - javaScriptInYourInbox: function(req, res) { - res.render('resources/javascript-in-your-inbox', { - title: 'JavaScript in your Inbox' - }); - }, - - pairProgramWithTeamViewer: function(req, res) { - Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) { - if (err) { - debug('Challenge err: ', err); - next(err); - } - res.render('resources/pair-program-with-team-viewer', { - title: 'Challenge: Pair Program with Team Viewer', - name: 'Pair Program with Team Viewer', - video: '', - time: 30, - steps: steps, - cc: req.user ? req.user.challengesHash : undefined, - points: req.user ? req.user.points : undefined, - challenges: c - }); - }); - }, - - about: function(req, res) { - var date1 = new Date("10/15/2014"); - var date2 = new Date(); - var timeDiff = Math.abs(date2.getTime() - date1.getTime()); - var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); - client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, res2) { - client.get('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function(blogger, res3) { - var nonprofitProjects = trello.length || 15; - var blog = JSON.parse(blogger); - User.count({'points': {'$gt': 2}}, function (err, c3) { - if (err) { - debug('User err: ', err); - next(err); - } - User.count({'points': {'$gt': 9}}, function (err, c10) { - if (err) { - debug('User err: ', err); - next(err); - } - User.count({'points': {'$gt': 29}}, function (err, c30) { - if (err) { - debug('User err: ', err); - next(err); - } - User.count({'points': {'$gt': 53}}, function (err, all) { - if (err) { - debug('User err: ', err); - next(err); - } - res.render('resources/learn-to-code', { - title: 'About Free Code Camp and Our Team of Volunteers', - daysRunning: daysRunning, - nonprofitProjects: nonprofitProjects, - c3: c3, - c10: c10, - c30: c30, - all: all, - blog1Title: blog["items"][0]["title"], - blog1Link: blog["items"][0]["url"], - blog2Title: blog["items"][1]["title"], - blog2Link: blog["items"][1]["url"], - blog3Title: blog["items"][2]["title"], - blog3Link: blog["items"][2]["url"], - blog4Title: blog["items"][3]["title"], - blog4Link: blog["items"][3]["url"], - blog5Title: blog["items"][4]["title"], - blog5Link: blog["items"][4]["url"] + stats: function stats(req, res) { + var date1 = new Date("10/15/2014"); + var date2 = new Date(); + var timeDiff = Math.abs(date2.getTime() - date1.getTime()); + var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); + client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, response) { + var nonprofitProjects = (trello && trello.length) || 15; + User.count({'points': {'$gt': 2}}, function(err, c3) { if (err) { debug('User err: ', err); next(err); } + User.count({'points': {'$gt': 9}}, function(err, c10) { if (err) { debug('User err: ', err); next(err); } + User.count({'points': {'$gt': 29}}, function(err, c30) { if (err) { debug('User err: ', err); next(err); } + User.count({'points': {'$gt': 53}}, function(err, all) { if (err) { debug('User err: ', err); next(err); } + res.render('resources/stats', { + title: 'Free Code Camp Stats:', + daysRunning: daysRunning, + nonprofitProjects: nonprofitProjects, + c3: c3, + c10: c10, + c30: c30, + all: all + }); + }); + }); }); - }); }); - }); }); - }); - }); - }, + }, + + deployAWebsite: function deployAWebsite(req, res) { + res.render('resources/deploy-a-website', { + title: 'Deploy a Dynamic Website in 7 Minutes' + }); + }, + + nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) { + res.render('resources/nonprofit-project-instructions', { + title: 'Nonprofit Project Instructions' + }); + }, + + gmailShortcuts: function gmailShortcuts(req, res) { + res.render('resources/gmail-shortcuts', { + title: 'These Gmail Shortcuts will save you Hours' + }); + }, + + controlShortcuts: function controlShortcuts(req, res) { + res.render('resources/control-shortcuts', { + title: 'These Control Shortcuts will save you Hours' + }); + }, + + chromebook: function chromebook(req, res) { + res.render('resources/chromebook', { + title: 'Win a Chromebook' + }); + }, + + jqueryExercises: function jqueryExercises(req, res) { + res.render('resources/jquery-exercises', { + title: 'jQuery Exercises' + }); + }, + + livePairProgramming: function(req, res) { + res.render('resources/live-pair-programming', { + title: 'Live Pair Programming' + }); + }, + + javaScriptInYourInbox: function(req, res) { + res.render('resources/javascript-in-your-inbox', { + title: 'JavaScript in your Inbox' + }); + }, + + pairProgramWithTeamViewer: function(req, res) { + Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) { + if (err) { + debug('Challenge err: ', err); + next(err); + } + res.render('resources/pair-program-with-team-viewer', { + title: 'Challenge: Pair Program with Team Viewer', + name: 'Pair Program with Team Viewer', + video: '', + time: 30, + steps: steps, + cc: req.user ? req.user.challengesHash : undefined, + points: req.user ? req.user.points : undefined, + challenges: c + }); + }); + }, + + about: function(req, res) { + var date1 = new Date("10/15/2014"); + var date2 = new Date(); + var timeDiff = Math.abs(date2.getTime() - date1.getTime()); + var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); + client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, res2) { + client.get('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function(blogger, res3) { + var nonprofitProjects = trello.length || 15; + var blog = JSON.parse(blogger); + User.count({'points': {'$gt': 2}}, function (err, c3) { + if (err) { + debug('User err: ', err); + next(err); + } + User.count({'points': {'$gt': 9}}, function (err, c10) { + if (err) { + debug('User err: ', err); + next(err); + } + User.count({'points': {'$gt': 29}}, function (err, c30) { + if (err) { + debug('User err: ', err); + next(err); + } + User.count({'points': {'$gt': 53}}, function (err, all) { + if (err) { + debug('User err: ', err); + next(err); + } + res.render('resources/learn-to-code', { + title: 'About Free Code Camp and Our Team of Volunteers', + daysRunning: daysRunning, + nonprofitProjects: nonprofitProjects, + c3: c3, + c10: c10, + c30: c30, + all: all, + blog1Title: blog["items"][0]["title"], + blog1Link: blog["items"][0]["url"], + blog2Title: blog["items"][1]["title"], + blog2Link: blog["items"][1]["url"], + blog3Title: blog["items"][2]["title"], + blog3Link: blog["items"][2]["url"], + blog4Title: blog["items"][3]["title"], + blog4Link: blog["items"][3]["url"], + blog5Title: blog["items"][4]["title"], + blog5Link: blog["items"][4]["url"] + }); + }); + }); + }); + }); + }); + }); + }, randomPhrase: function() { var phrases = resources.phrases; diff --git a/public/js/main.js b/public/js/main.js index 5eb045dab92..832f54aecb1 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -33,12 +33,15 @@ $(document).ready(function() { } }); + + function completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash) { $('#complete-bonfire-dialog').modal('show'); // Only post to server if there is an authenticated user if ($('.signup-btn-nav').length < 1) { l = location.pathname.split('/'); cn = l[l.length - 1]; + $.ajax({ type: 'POST', data: { @@ -49,7 +52,7 @@ $(document).ready(function() { } }, url: '/completed-bonfire/' - }); + }) } } @@ -66,15 +69,14 @@ $(document).ready(function() { window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); }); - // TODO: refactor this to create meaningful variable names, what the heck i l? $('.next-bonfire-button').on('click', function() { var bonfireSolution = myCodeMirror.getValue(); var thisBonfireHash = passedBonfireHash || null; var didCompleteWith = $('#completed-with').val() || null; - console.log(didCompleteWith); - console.log(bonfireSolution, thisBonfireHash); + completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash); - window.location = '/bonfires/'; + window.location('/bonfires'); + }); // Bonfire instructions functions From 1988101ba22cfa94aed13841d88ace16a96cdb25 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 02:55:41 -0500 Subject: [PATCH 32/39] Server successfully redirects, still need to work on challenge ordering --- public/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/main.js b/public/js/main.js index 832f54aecb1..f9ea38d6ee8 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -75,7 +75,7 @@ $(document).ready(function() { var didCompleteWith = $('#completed-with').val() || null; completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash); - window.location('/bonfires'); + window.location = '/bonfires'; }); From f97350ecf376b5396035d032c9840496b85069a2 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 03:29:17 -0500 Subject: [PATCH 33/39] Fixed challenge ordering --- controllers/resources.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/controllers/resources.js b/controllers/resources.js index 61becf46a5e..aefafe02c01 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -192,6 +192,15 @@ module.exports = { allBonfireIds: function() { return bonfires.map(function(elem) { + return { + _id: elem._id, + difficulty: elem.difficulty + } + }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map(function(elem) { return elem._id; }) } From 504838083ed5408bbbe95b3a2e17c2413eca4ed7 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 03:31:59 -0500 Subject: [PATCH 34/39] Fixed display issue of difficulty iconography --- views/bonfire/show.jade | 74 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 6c49acfffc8..521a0547222 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -23,43 +23,43 @@ block content .col-xs-12.col-sm-12.col-md-3 #testCreatePanel h2.text-center #{name} - Difficulty - if (difficulty == "0") - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "1") - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "2") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "3") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "4") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - if (difficulty == "5") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame + h2.text-center + if (difficulty == "0") + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "1") + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "2") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "3") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "4") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + if (difficulty == "5") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame .well .row.text-center row.text-center From 01bdbd8f4d7bbcbf2802ff73edfc9a6338b9a706 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 17:39:53 -0500 Subject: [PATCH 35/39] Finishing up pathing issues, writing user object if they haven't visited in a day --- app.js | 7 +++---- controllers/bonfire.js | 7 +++---- public/js/main.js | 10 ++++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index 2d089000dba..c9e59393f19 100644 --- a/app.js +++ b/app.js @@ -352,11 +352,9 @@ app.post('/completed-bonfire/', function (req, res) { solution: isSolution }) - - debug('saving user with a pair'); req.user.save(); pairedWith.save(); - + res.redirect('/bonfires'); } }) } else { @@ -372,9 +370,10 @@ app.post('/completed-bonfire/', function (req, res) { if (index > -1) { req.user.uncompletedBonfires.splice(index,1) } - debug("Saving user without a pair"); req.user.save(); + res.redirect('/bonfires'); } + }); /** diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 3f7a6554535..5eea2483ba0 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -43,16 +43,15 @@ exports.returnNextBonfire = function(req, res, next) { var completed = req.user.completedBonfires.map(function(elem) { return elem._id; }); - // TODO : remove debug statement - debug(req.user, 'this is the user'); + req.user.uncompletedBonfires = resources.allBonfireIds().filter(function(elem) { if (completed.indexOf(elem) === -1) { return elem; } }); + req.user.save(); } - debug('These are completed bonfires', completed); - debug('These are uncompleted bonfires', req.user.uncompletedBonfires); + var uncompletedBonfires = req.user.uncompletedBonfires; diff --git a/public/js/main.js b/public/js/main.js index f9ea38d6ee8..99e2ff1c031 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -39,9 +39,6 @@ $(document).ready(function() { $('#complete-bonfire-dialog').modal('show'); // Only post to server if there is an authenticated user if ($('.signup-btn-nav').length < 1) { - l = location.pathname.split('/'); - cn = l[l.length - 1]; - $.ajax({ type: 'POST', data: { @@ -52,7 +49,12 @@ $(document).ready(function() { } }, url: '/completed-bonfire/' - }) + + }); + + //$.post( '/completed-bonfire', function( data ) { + // window.location = '/bonfires'; + //}); } } From 2f0415ecf99f89dc1974e26d7cb0cf12c99c5000 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 27 Jan 2015 14:46:05 -0800 Subject: [PATCH 36/39] update the congratulatory text a bit --- controllers/resources.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/controllers/resources.json b/controllers/resources.json index adae5097a6f..0c9427e5bb7 100644 --- a/controllers/resources.json +++ b/controllers/resources.json @@ -173,8 +173,7 @@ "shot down", "scarfed", "grappled", - "incinerated", - "uppercutted" + "incinerated" ], "compliments": [ "You've got the power!", @@ -182,6 +181,14 @@ "Rock and roll!", "High five!", "Bravo!", + "Yes!", + "Swoosh!", + "There is no try", + "Done and done!", + "You make this look easy", + "Terminated", + "We have liftoff!", + "To infinity and beyond!", "Encore!", "Challenge destroyed!", "Power level 9000!", From 547b9757b1617833b862cdbad39f0912817cd782 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 27 Jan 2015 15:05:51 -0800 Subject: [PATCH 37/39] set wide layout background to white --- public/css/main.less | 4 + views/bonfire/show.jade | 158 ++++++++++++++++++++-------------------- views/layout-wide.jade | 2 +- 3 files changed, 83 insertions(+), 81 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index b215b8a0f81..fe5cd6676fa 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -29,6 +29,10 @@ html { min-height: 100%; } +body.full-screen-body-background { + background-color: #eeeeee; +} + body.top-and-bottom-margins { padding-top: 80px; margin-bottom: 60px; diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 521a0547222..24e5405698e 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -17,86 +17,84 @@ block content script(src='/js/lib/bonfire/bonfireInit.js') - .panel.panel-primary.panel-bonfire - .panel.panel-body - .row - .col-xs-12.col-sm-12.col-md-3 - #testCreatePanel - h2.text-center #{name} - h2.text-center - if (difficulty == "0") - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "1") - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "2") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "3") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "4") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - if (difficulty == "5") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - .well - .row.text-center - row.text-center - .col-xs-12 - .bonfire-instructions - = brief - #brief-instructions.col-xs-12 - button#more-info.btn.btn-info - span.ion-help-circled - | More information - #long-instructions.row.text-center.hide - .col-xs-12 - .bonfire-instructions - for sentence in details - p!= sentence - button#less-info.btn.btn-info - span.ion-help-circled - | Less information - #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) - br - form.code - .form-group.codeMirrorView - textarea#codeOutput - br - #testSuite - br - script(type="text/javascript"). - var tests = !{JSON.stringify(tests)}; - var challengeSeed = !{JSON.stringify(challengeSeed)}; - var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; - var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - .col-xs-12.col-sm-12.col-md-9 - #mainEditorPanel - form.code - .form-group.codeMirrorView - textarea#codeEditor(autofocus=true) - script(src='/js/lib/bonfire/bonfireFramework.js') + .row + .col-xs-12.col-sm-12.col-md-3 + #testCreatePanel + h2.text-center #{name} + h2.text-center + if (difficulty == "0") + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "1") + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "2") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "3") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "4") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + if (difficulty == "5") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + .well + .row.text-center + row.text-center + .col-xs-12 + .bonfire-instructions + = brief + #brief-instructions.col-xs-12 + button#more-info.btn.btn-info + span.ion-help-circled + | More information + #long-instructions.row.text-center.hide + .col-xs-12 + .bonfire-instructions + for sentence in details + p!= sentence + button#less-info.btn.btn-info + span.ion-help-circled + | Less information + #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) + br + form.code + .form-group.codeMirrorView + textarea#codeOutput + br + #testSuite + br + script(type="text/javascript"). + var tests = !{JSON.stringify(tests)}; + var challengeSeed = !{JSON.stringify(challengeSeed)}; + var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var passedBonfireHash = !{JSON.stringify(bonfireHash)}; + .col-xs-12.col-sm-12.col-md-9 + #mainEditorPanel + form.code + .form-group.codeMirrorView + textarea#codeEditor(autofocus=true) + script(src='/js/lib/bonfire/bonfireFramework.js') diff --git a/views/layout-wide.jade b/views/layout-wide.jade index 63fb20c1d79..0bed8cac5f8 100644 --- a/views/layout-wide.jade +++ b/views/layout-wide.jade @@ -16,7 +16,7 @@ html(ng-app='profileValidation', lang='en') meta(name='csrf-token', content=_csrf) != css('main') - body.no-top-and-bottom-margins + body.no-top-and-bottom-margins.full-screen-body-background include partials/navbar-wide include partials/flash block content From 38445426515d503a0f6f1f5355ff10c97aa188b5 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 20:12:51 -0500 Subject: [PATCH 38/39] Refactor routing, minor display tweaks --- app.js | 80 ++++++------ public/css/main.less | 17 ++- public/js/lib/codemirror/theme/monokai.css | 2 +- views/bonfire/generator.jade | 4 +- views/bonfire/public-generator.jade | 6 +- views/bonfire/show.jade | 143 +++++++++++---------- 6 files changed, 132 insertions(+), 120 deletions(-) diff --git a/app.js b/app.js index c9e59393f19..16d5307f2be 100644 --- a/app.js +++ b/app.js @@ -265,7 +265,16 @@ app.get( ); app.all('/account', passportConf.isAuthenticated); app.get('/account/api', userController.getAccountAngular); + +/** + * Bonfire related routes + */ app.get('/playground', bonfireController.index); +app.get('/bonfires', bonfireController.returnNextBonfire); +app.get('/bonfire-json-generator', bonfireController.returnGenerator); +app.post('/bonfire-json-generator', bonfireController.generateChallenge); +app.get('/bonfire-challenge-generator', bonfireController.publicGenerator); +app.post('/bonfire-challenge-generator', bonfireController.testBonfire) app.get( '/bonfires/:bonfireName', bonfireController.returnIndividualBonfire @@ -273,48 +282,8 @@ app.get( app.get('/bonfire', function(req, res) { res.redirect(301, '/playground'); }); -app.get('/bonfires', bonfireController.returnNextBonfire); -app.get('/bonfire/generator', bonfireController.returnGenerator); -app.post('/bonfire/generator', bonfireController.generateChallenge); -app.get('/bonfire/public-generator', bonfireController.publicGenerator); -app.post('/bonfire/public-generator', bonfireController.testBonfire) - -// Unique Check API route -app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); -app.get('/api/checkExistingUsername/:username', userController.checkExistingUsername); -app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail); -app.get('/account', userController.getAccount); -app.post('/account/profile', userController.postUpdateProfile); -app.post('/account/password', userController.postUpdatePassword); -app.post('/account/delete', userController.postDeleteAccount); -app.get('/account/unlink/:provider', userController.getOauthUnlink); - - -/** - * API examples routes. - * accepts a post request. the challenge id req.body.challengeNumber - * and updates user.challengesHash & user.challengesCompleted - * - */ -app.post('/completed-challenge', function (req, res) { - req.user.challengesHash[parseInt(req.body.challengeNumber)] = - Math.round(+new Date() / 1000); - var timestamp = req.user.challengesHash; - var points = 0; - for (var key in timestamp) { - if (timestamp[key] > 0) { - points += 1; - } - } - req.user.points = points; - req.user.save(); -}); app.post('/completed-bonfire/', function (req, res) { - - // TODO: remove debug statement - debug(req.body.bonfireInfo, 'This is bonfire info we got from posted'); - var isCompletedWith = req.body.bonfireInfo.completedWith || undefined; var isCompletedDate = Math.round(+new Date() / 1000); var bonfireHash = req.body.bonfireInfo.bonfireHash; @@ -376,6 +345,37 @@ app.post('/completed-bonfire/', function (req, res) { }); +// Unique Check API route +app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername); +app.get('/api/checkExistingUsername/:username', userController.checkExistingUsername); +app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail); +app.get('/account', userController.getAccount); +app.post('/account/profile', userController.postUpdateProfile); +app.post('/account/password', userController.postUpdatePassword); +app.post('/account/delete', userController.postDeleteAccount); +app.get('/account/unlink/:provider', userController.getOauthUnlink); + + +/** + * API examples routes. + * accepts a post request. the challenge id req.body.challengeNumber + * and updates user.challengesHash & user.challengesCompleted + * + */ +app.post('/completed-challenge', function (req, res) { + req.user.challengesHash[parseInt(req.body.challengeNumber)] = + Math.round(+new Date() / 1000); + var timestamp = req.user.challengesHash; + var points = 0; + for (var key in timestamp) { + if (timestamp[key] > 0) { + points += 1; + } + } + req.user.points = points; + req.user.save(); +}); + /** * OAuth sign-in routes. */ diff --git a/public/css/main.less b/public/css/main.less index fe5cd6676fa..78b4a42b08c 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -39,8 +39,7 @@ body.top-and-bottom-margins { } body.no-top-and-bottom-margins { - padding-top: 50px; - margin-bottom: 40px; + margin: 70px 20px 50px 20px; } h1, h2 { @@ -624,13 +623,25 @@ form.code span { } div.CodeMirror-scroll { - padding-bottom: 40px; + padding-bottom: 30px; } .test-vertical-center { margin-top: 15px; } +.cm-s-monokai.CodeMirror { + border-radius: 5px; +} +.bonfire-flames { + margin-top: -20px; + margin-bottom: -2px; +} + +.bonfire-top { + margin-top: -30px; +} + //uncomment this to see the dimensions of all elements outlined in red diff --git a/public/js/lib/codemirror/theme/monokai.css b/public/js/lib/codemirror/theme/monokai.css index 2aa742c4ca5..8ac80e7ac20 100644 --- a/public/js/lib/codemirror/theme/monokai.css +++ b/public/js/lib/codemirror/theme/monokai.css @@ -1,6 +1,6 @@ /* Based on Sublime Text's Monokai theme */ -.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2; border-radius: 5px; height: auto;} +.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2; height: auto;} .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} .cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} .cm-s-monokai .CodeMirror-guttermarker { color: white; } diff --git a/views/bonfire/generator.jade b/views/bonfire/generator.jade index 861b8ffee9d..63f53f7be10 100644 --- a/views/bonfire/generator.jade +++ b/views/bonfire/generator.jade @@ -4,9 +4,9 @@ block content .col-md-offset-2.col-md-8.col-lg-offset-2.col-lg-8.text-center h1 JSON generator for bonfire challenges - just fill the form out and get the correct format back - .col-xs-12.col-sm-12.col-md-offset-3.col-md-6.col-lg-offset-3-col-lg-6 + .col-xs-12.col-sm-12.col-md-offset-1.col-md-10.col-lg-offset-1-col-lg-10 .panel - form.form-horizontal(method="POST", action="/bonfire/generator", name="bonfireInfo") + form.form-horizontal(method="POST", action="/bonfire-json-generator", name="bonfireInfo") .form-group label.col-sm-2.control-label(for='name') name: .col-sm-10 diff --git a/views/bonfire/public-generator.jade b/views/bonfire/public-generator.jade index 13750a9b4d9..c3761fe674c 100644 --- a/views/bonfire/public-generator.jade +++ b/views/bonfire/public-generator.jade @@ -2,10 +2,10 @@ extends ../layout block content .row .col-md-offset-2.col-md-8.col-lg-offset-2.col-lg-8.text-center - h1 JSON generator for bonfire challenges - just fill the form out and get the correct format back - .col-xs-12.col-sm-12.col-md-offset-3.col-md-6.col-lg-offset-3-col-lg-6 + h1 Welcome to the challenge generator. Test your ideas out and see them live in bonfire! + .col-xs-12.col-sm-12.col-md-offset-1.col-md-10.col-lg-offset-1-col-lg-10 .panel - form.form-horizontal(method="POST", action="/bonfire/public-generator", name="bonfireInfo") + form.form-horizontal(method="POST", action="/bonfire-challenge-generator", name="bonfireInfo") .form-group label.col-sm-2.control-label(for='name') name: .col-sm-10 diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 24e5405698e..259634d5360 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -18,78 +18,79 @@ block content .row - .col-xs-12.col-sm-12.col-md-3 + .col-xs-12.col-sm-12.col-md-4.bonfire-top #testCreatePanel - h2.text-center #{name} - h2.text-center - if (difficulty == "0") - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "1") - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "2") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "3") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - i.ion-ios-flame-outline - if (difficulty == "4") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame-outline - if (difficulty == "5") - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - i.ion-ios-flame - .well - .row.text-center - row.text-center - .col-xs-12 - .bonfire-instructions - = brief - #brief-instructions.col-xs-12 - button#more-info.btn.btn-info - span.ion-help-circled - | More information - #long-instructions.row.text-center.hide - .col-xs-12 - .bonfire-instructions - for sentence in details - p!= sentence - button#less-info.btn.btn-info - span.ion-help-circled - | Less information - #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) - br - form.code - .form-group.codeMirrorView - textarea#codeOutput - br - #testSuite - br - script(type="text/javascript"). - var tests = !{JSON.stringify(tests)}; - var challengeSeed = !{JSON.stringify(challengeSeed)}; - var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; - var passedBonfireHash = !{JSON.stringify(bonfireHash)}; - .col-xs-12.col-sm-12.col-md-9 + + h2.text-center= name + h2.text-center.bonfire-flames + if (difficulty == "0") + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "1") + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "2") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "3") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + i.ion-ios-flame-outline + if (difficulty == "4") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame-outline + if (difficulty == "5") + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + i.ion-ios-flame + .well + .row.text-center + row.text-center + .col-xs-12 + .bonfire-instructions + = brief + #brief-instructions.col-xs-12 + button#more-info.btn.btn-info + span.ion-help-circled + | More information + #long-instructions.row.text-center.hide + .col-xs-12 + .bonfire-instructions + for sentence in details + p!= sentence + button#less-info.btn.btn-info + span.ion-help-circled + | Less information + #submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter) + br + form.code + .form-group.codeMirrorView + textarea#codeOutput + br + #testSuite + br + script(type="text/javascript"). + var tests = !{JSON.stringify(tests)}; + var challengeSeed = !{JSON.stringify(challengeSeed)}; + var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var passedBonfireHash = !{JSON.stringify(bonfireHash)}; + .col-xs-12.col-sm-12.col-md-8 #mainEditorPanel form.code .form-group.codeMirrorView From 5294462e4c55a1bb97efb147edd10e2c4417edf4 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 27 Jan 2015 22:25:48 -0500 Subject: [PATCH 39/39] Resolving issue with saving to "paired with" user profile --- app.js | 6 +++++- controllers/bonfire.js | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 16d5307f2be..e5503ad7df6 100644 --- a/app.js +++ b/app.js @@ -288,6 +288,8 @@ app.post('/completed-bonfire/', function (req, res) { var isCompletedDate = Math.round(+new Date() / 1000); var bonfireHash = req.body.bonfireInfo.bonfireHash; var isSolution = req.body.bonfireInfo.solution; + // TODO + debug(isCompletedWith, 'Is completed with'); if (isCompletedWith) { var paired = User.find({"profile.username": isCompletedWith}).limit(1); @@ -300,13 +302,15 @@ app.post('/completed-bonfire/', function (req, res) { if (index > -1) { req.user.uncompletedBonfires.splice(index,1) } + pairedWith = pairedWith.pop(); + //debug('This is paired with', Object.keys(pairedWith)); + debug('This is paired with\'s uncompleted bonfires array', pairedWith.uncompletedBonfires); index = pairedWith.uncompletedBonfires.indexOf(bonfireHash); if (index > -1) { pairedWith.uncompletedBonfires.splice(index,1) } - pairedWith = pairedWith.pop(); pairedWith.completedBonfires.push({ _id: bonfireHash, completedWith: req.user._id, diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 5eea2483ba0..e36285a1990 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -33,9 +33,12 @@ exports.index = function(req, res) { }; exports.returnNextBonfire = function(req, res, next) { - + // TODO + //var tempUser = false; if (!req.user) { - req.user = new User(); + res.redirect('bonfires/meet-bonfire'); + //tempUser = true; + //req.user = new User(); } var currentTime = parseInt(+new Date() / 1000) if (currentTime - req.user.lastContentSync > 86400) {