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 5b32f9b1af..bbae2fd4ba 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 cf7f80eb4e..958663f6e4 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 461e663d77..dd0cff31d7 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 671c7ff4d9..eb4249400a 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 582bd551e5..81a33ef72d 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 4311a7fa09..fe844286de 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 ced2575987..60cd47f011 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 79f5f34bfb..0a5dedfe97 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 8b6ddfa36d..a03c038d90 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 6b51fe72f1..9c420e283b 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 36d9dc8e0d..685f9d2bc9 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 1adbb74b8d..cabd9ecf93 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 bbae2fd4ba..824cbde19b 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 958663f6e4..580e47e71f 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 81a33ef72d..84cd18d600 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 fe844286de..aea21a58ea 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 60cd47f011..cbe0fca6c7 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 9c420e283b..f91b79d695 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 685f9d2bc9..aa1bd9960f 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 824cbde19b..8f572959e5 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 97ea084550..e0e9ae3d7f 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 cbe0fca6c7..4c812dae1d 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 f91b79d695..72286d661b 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 e0e9ae3d7f..07edf0e9f5 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 4c812dae1d..c817b94a0d 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 72286d661b..2145bac009 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 8f572959e5..d15af73684 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 580e47e71f..866fdc87cb 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 c817b94a0d..5c8b5c6c3e 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 d15af73684..18a6516ce4 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 866fdc87cb..34311cb3a3 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 5c8b5c6c3e..b333e61ad6 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 6a026ed505..e42d08e94c 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 ecc99cd09b..9966d52262 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 34311cb3a3..495a27e1d2 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 84cd18d600..f4a4faacce 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 0e1ccbe8a3..f4d23dd17d 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 9966d52262..1b00f76de3 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 aea21a58ea..adb0f362ef 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 8fcf04a9ed..62894f1630 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 f4d23dd17d..8651171bf6 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 e42d08e94c..e6ca1aba4f 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 2145bac009..39bf14d496 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 39bf14d496..9100cd6575 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 39bf14d496..0ead2ad007 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 aa1bd9960f..3111eb1edd 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 b333e61ad6..c04ee6e31c 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 495a27e1d2..d88a9d4c45 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 0630a4e272..2938ac72dd 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 c04ee6e31c..bd96ace1ac 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 9100cd6575..51d88fe64e 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 d88a9d4c45..25dbcdab1b 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 9d3b325df6..401940ca29 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 098f41fa46..adae5097a6 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 2938ac72dd..bbf1a63c24 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 0a5dedfe97..db847aecea 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 a03c038d90..ca100cb520 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 eaa1c21170..88d28655b9 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 3111eb1edd..ddb2fa6dce 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 25dbcdab1b..b669452ab0 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 401940ca29..6341ce520f 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 f073188bca..5eea62da79 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 88d28655b9..fac141d818 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 adb0f362ef..4f19af70e4 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 88d28655b9..3f9df3e977 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 de6f552728..d44dfea030 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 3f9df3e977..d9965075dc 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 bbf1a63c24..01adb80a6d 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 4f19af70e4..ea832a18d0 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 fb5377729d..70c59bf1fe 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 01adb80a6d..a69c239782 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 ea832a18d0..02f9230f13 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 d9965075dc..0224965ce0 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 a69c239782..23fcc5c631 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 02f9230f13..45ac46ca0e 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 0224965ce0..23a3404d68 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 e6ca1aba4f..0cbfe2dc68 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 1b00f76de3..24b87bf777 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 b669452ab0..264c688281 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 23fcc5c631..d9c9cd4ab0 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 45ac46ca0e..8d8705cf0c 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 db847aecea..42778c1e5e 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 ca100cb520..64932960ca 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 0000000000..bc150870a1 --- /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 8d8705cf0c..d5dc8860af 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 42778c1e5e..6b04e83164 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 bc150870a1..861b8ffee9 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 24b87bf777..279c24b74f 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 264c688281..079aad96b1 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 dd0cff31d7..5cb531547e 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 d5dc8860af..3d9f68b6b5 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 6b04e83164..bf8412d639 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 62894f1630..ff07aee175 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 0000000000..13750a9b4d --- /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 279c24b74f..0fbff67ef6 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 079aad96b1..e52a2d19fb 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 3d9f68b6b5..5514d1d798 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 bf8412d639..920824fa2c 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 23a3404d68..bc1017d59b 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 d9c9cd4ab0..b215b8a0f8 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 ff07aee175..498f7d3a23 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 bc1017d59b..6432e175f2 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 0000000000..63fb20c1d7 --- /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 d44dfea030..dfddef33b4 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 0000000000..b8ff7267f9 --- /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 0000000000..96165a030d --- /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 0cbfe2dc68..8388ea2ba6 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 b6c3a18727..2236b439a2 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 0fbff67ef6..d4a412bb4e 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 e52a2d19fb..e9cefa2bc4 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 5eea62da79..b6b4f99aa5 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 2421bdcbed..7c4a9dc77a 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 2236b439a2..92a911cc69 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 d4a412bb4e..bc4b44614b 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 e9cefa2bc4..b820911239 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 bc4b44614b..9c24c7240f 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 b820911239..88d4fe6192 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 6642e70a90..ce9c0160e4 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 906310f63b..d35020b0ad 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 bd96ace1ac..5eb045dab9 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 9c24c7240f..2d089000db 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 88d4fe6192..3f7a655453 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 ce9c0160e4..61becf46a5 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 5eb045dab9..832f54aecb 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 832f54aecb..f9ea38d6ee 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 61becf46a5..aefafe02c0 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 6c49acfffc..521a054722 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 2d089000db..c9e59393f1 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 3f7a655453..5eea2483ba 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 f9ea38d6ee..99e2ff1c03 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 adae5097a6..0c9427e5bb 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 b215b8a0f8..fe5cd6676f 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 521a054722..24e5405698 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 63fb20c1d7..0bed8cac5f 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 c9e59393f1..16d5307f2b 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 fe5cd6676f..78b4a42b08 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 2aa742c4ca..8ac80e7ac2 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 861b8ffee9..63f53f7be1 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 13750a9b4d..c3761fe674 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 24e5405698..259634d536 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 16d5307f2b..e5503ad7df 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 5eea2483ba..e36285a199 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) {