Merge branch 'master' into refactor

Conflicts:
	app.js
	controllers/api.js
	controllers/challenges.js
	gulpfile.js
This commit is contained in:
Michael Q Larson
2014-12-23 12:34:09 -08:00
9 changed files with 243 additions and 478 deletions

38
app.js
View File

@ -1,19 +1,19 @@
require('newrelic');
require('dotenv').load();
/**
* Module dependencies.
*/
var express = require('express'),
debug = require('debug')('freecc:server'),
cookieParser = require('cookie-parser'),
compress = require('compression'),
session = require('express-session'),
bodyParser = require('body-parser'),
logger = require('morgan'),
errorHandler = require('errorhandler'),
methodOverride = require('method-override'),
bodyParser = require('body-parser'),
helmet = require('helmet'),
_ = require('lodash'),
MongoStore = require('connect-mongo')(session),
flash = require('express-flash'),
@ -99,15 +99,19 @@ app.use(helmet.xframe());
var trusted = [
'"self"',
'*.freecodecamp.com',
'*.google-analytics.com',
'*.googleapis.com',
'*.gstatic.com',
'*.doubleclick.net',
'*.twitter.com',
"*.google-analytics.com",
"*.googleapis.com",
"*.google.com",
"*.gstatic.com",
"*.doubleclick.net",
"*.twitter.com",
'*.twimg.com',
'*.githubusercontent.com',
'"unsafe-eval"',
'"unsafe-inline"'
"*.githubusercontent.com",
"'unsafe-eval'",
"'unsafe-inline'",
"*.rafflecopter.com",
"localhost:3001"
];
//TODO(Berks): conditionally add localhost domains to csp;
/*var connectSrc;
@ -123,14 +127,12 @@ debug(trusted);
app.use(helmet.contentSecurityPolicy({
defaultSrc: trusted,
scriptSrc: ['*.optimizely.com'].concat(trusted),
'connect-src': ['ws://localhost:3001/', 'http://localhost:3001/'],
'connect-src': ['ws://*.rafflecopter.com', 'wss://*.rafflecopter.com','https://*.rafflecopter.com', 'ws://www.freecodecamp.com', 'ws://localhost:3001/', 'http://localhost:3001', 'http://www.freecodecamp.com'],
styleSrc: trusted,
imgSrc: ['*.evernote.com', '*.amazonaws.com', 'data:'].concat(trusted),
fontSrc: ['"self"', '*.googleapis.com'].concat(trusted),
imgSrc: ['*.evernote.com', '*.amazonaws.com', "data:", '*.licdn.com', '*.gravatar.com', '*.youtube.com'].concat(trusted),
fontSrc: ["'self", '*.googleapis.com'].concat(trusted),
mediaSrc: ['*.amazonaws.com', '*.twitter.com'],
frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com'],
// sandbox: ['allow-forms', 'allow-scripts'],
// reportUri: '/report-violation',
frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com', '*.rafflecopter.com'],
reportOnly: false, // set to true if you only want to report errors
setAllHeaders: false, // set to true if you want to set all headers
safari5: false // set to true if you want to force buggy CSP in Safari 5
@ -207,11 +209,9 @@ app.post(
);
app.get(
'/challenges/:challengeNumber',
passportConf.isAuthenticated,
challengesController.returnChallenge
'/challenges/:challengeNumber',
challengesController.returnChallenge
);
app.all('/account', passportConf.isAuthenticated);
app.get('/account', userController.getAccount);
app.post('/account/profile', userController.postUpdateProfile);

137
controllers/api.js Normal file
View File

@ -0,0 +1,137 @@
var secrets = require('../config/secrets');
var User = require('../models/User');
var querystring = require('querystring');
var validator = require('validator');
var async = require('async');
var cheerio = require('cheerio');
var request = require('request');
var graph = require('fbgraph');
var Github = require('github-api');
var Twit = require('twit');
var Linkedin = require('node-linkedin')(secrets.linkedin.clientID, secrets.linkedin.clientSecret, secrets.linkedin.callbackURL);
var Y = require('yui/yql');
var _ = require('lodash');
/**
* GET /api
* List of API examples.
*/
exports.getApi = function(req, res) {
res.render('api/index', {
title: 'API Examples'
});
};
/**
* GET /api/facebook
* Facebook API example.
*/
exports.getFacebook = function(req, res, next) {
var token = _.find(req.user.tokens, { kind: 'facebook' });
graph.setAccessToken(token.accessToken);
async.parallel({
getMe: function(done) {
graph.get(req.user.facebook, function(err, me) {
done(err, me);
});
},
getMyFriends: function(done) {
graph.get(req.user.facebook + '/friends', function(err, friends) {
done(err, friends.data);
});
}
},
function(err, results) {
if (err) return next(err);
res.render('api/facebook', {
title: 'Facebook API',
me: results.getMe,
friends: results.getMyFriends
});
});
};
/**
* GET /api/github
* GitHub API Example.
*/
exports.getGithub = function(req, res, next) {
var token = _.find(req.user.tokens, { kind: 'github' });
var github = new Github({ token: token.accessToken });
var repo = github.getRepo('sahat', 'requirejs-library');
repo.show(function(err, repo) {
if (err) return next(err);
res.render('api/github', {
title: 'GitHub API',
repo: repo
});
});
};
/**
* GET /api/twitter
* Twiter API example.
*/
exports.getTwitter = function(req, res, next) {
var token = _.find(req.user.tokens, { kind: 'twitter' });
var T = new Twit({
consumer_key: secrets.twitter.consumerKey,
consumer_secret: secrets.twitter.consumerSecret,
access_token: token.accessToken,
access_token_secret: token.tokenSecret
});
T.get('search/tweets', { q: 'nodejs since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 10 }, function(err, reply) {
if (err) return next(err);
res.render('api/twitter', {
title: 'Twitter API',
tweets: reply.statuses
});
});
};
/**
* POST /api/twitter
* Post a tweet.
*/
exports.postTwitter = function(req, res, next) {
req.assert('tweet', 'Tweet cannot be empty.').notEmpty();
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/api/twitter');
}
var token = _.find(req.user.tokens, { kind: 'twitter' });
var T = new Twit({
consumer_key: secrets.twitter.consumerKey,
consumer_secret: secrets.twitter.consumerSecret,
access_token: token.accessToken,
access_token_secret: token.tokenSecret
});
T.post('statuses/update', { status: req.body.tweet }, function(err, data, response) {
if (err) return next(err);
req.flash('success', { msg: 'Tweet has been posted.'});
res.redirect('/api/twitter');
});
};
/**
* GET /api/linkedin
* LinkedIn API example.
*/
exports.getLinkedin = function(req, res, next) {
var token = _.find(req.user.tokens, { kind: 'linkedin' });
var linkedin = Linkedin.init(token.accessToken);
linkedin.people.me(function(err, $in) {
if (err) return next(err);
res.render('api/linkedin', {
title: 'LinkedIn API',
profile: $in
});
});
};

View File

@ -7,24 +7,49 @@ var _ = require('lodash'),
Challenge = require('./../models/Challenge');
exports.returnChallenge = function(req, res, next) {
var challengeNumber = parseInt(req.params.challengeNumber) || 0;
if (challengeNumber > 59) {
challengeNumber = 0;
}
Challenge.findOne({ challengeNumber: challengeNumber }, function(err, c) {
if (err) {
debug('Challenge err: ', err);
return next(err);
}
res.render('challenges/show', {
title: 'Challenge: ' + c.name,
name: c.name,
video: c.video,
time: c.time,
steps: c.steps,
cc: req.user.challengesHash
var challengeNumber = parseInt(req.params.challengeNumber) || 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 (challengeNumber > 59) { challengeNumber = 0; }
Challenge.find({}, function (err, c) {
if (err) {
debug('Challenge err: ', err);
next(err);
}
res.render('challenges/show', {
title: 'Challenge: ' + c[challengeNumber].name,
name: c[challengeNumber].name,
video: c[challengeNumber].video,
time: c[challengeNumber].time,
steps: c[challengeNumber].steps,
number: challengeNumber,
cc: req.user ? req.user.challengesHash : undefined,
verb: verbs[Math.floor(Math.random() * verbs.length)],
phrase: phrases[Math.floor(Math.random() * phrases.length)],
challenges: c
});
});
});
};

View File

@ -6,6 +6,7 @@ var passport = require('passport');
var User = require('../models/User');
var secrets = require('../config/secrets');
var moment = require('moment');
var Challenge = require('./../models/Challenge');
//TODO(Berks): Refactor to use module.exports = {} pattern.
/**
@ -133,10 +134,17 @@ exports.postEmailSignup = function(req, res, next) {
*/
exports.getAccount = function(req, res) {
res.render('account/profile', {
title: 'Manage your Free Code Camp Account',
cc: req.user.challengesHash,
moment: moment
Challenge.find({}, function (err, c) {
if (err) {
console.error('Challenge err: ', err);
next(err);
}
res.render('account/profile', {
title: 'Manage your Free Code Camp Account',
cc: c,
ch: req.user.challengesHash,
moment: moment
});
});
};

View File

@ -36,7 +36,7 @@
{
"name": "Join our Forum",
"time": 5,
"video": "114578442",
"video": "115275066",
"challengeNumber": 2,
"steps": [
"Go to Free Code Camp's forum: <a href='http://forum.freecodecamp.com' target='_blank'>http://forum.freecodecamp.com</a>.",
@ -46,7 +46,7 @@
"Click on the \"Introduce yourself here\" discussion.",
"Here you can read through other Free Code Camp community members' self introductions.",
"Go ahead and type a brief self introduction of your own.",
"Click on the \"Categories\" drop-down menu. You should see a category called \"Local Chapters\". Click that. If your city isn't already on the list, you can create a topic for it. Otherwise, you can also introduce yourself to the other Code Campers from your city.",
"Click on the \"Categories\" drop-down menu. You should see a category called \"Local Chapters\". Click that. If your city isn't already on the list, create a topic for it. Otherwise, introduce yourself to the other Code Campers from your city.",
"Come back here daily to ask questions, engage in discussions, and share links to helpful coding tools."
]
},
@ -192,8 +192,8 @@
]
},
{
"name": "Customize Bootstrap with Bootswatch",
"time": 10,
"name": "Customize Bootstrap",
"time": 15,
"video": "110752741",
"challengeNumber": 16,
"steps": [

View File

@ -40,246 +40,12 @@ block content
tr
th Challenge
th Date Finished
if cc[0] > 0
tr
td Learn how Free Code Camp Works
td=moment(cc[0], 'X').format("MMM DD, YYYY")
if cc[1] > 0
tr
td Join Our Chat Room
td=moment(cc[1], 'X').format("MMM DD, YYYY")
if cc[2] > 0
tr
td Subscribe to Our Subreddit
td=moment(cc[2], 'X').format("MMM DD, YYYY")
if cc[3] > 0
tr
td Build a Personal Website
td=moment(cc[3], 'X').format("MMM DD, YYYY")
if cc[4] > 0
tr
td Build a Responsive Blog Theme
td=moment(cc[4], 'X').format("MMM DD, YYYY")
if cc[5] > 0
tr
td Build a Small Business Website
td=moment(cc[5], 'X').format("MMM DD, YYYY")
if cc[6] > 0
tr
td Tweak HTML and CSS in CodePen
td=moment(cc[6], 'X').format("MMM DD, YYYY")
if cc[7] > 0
tr
td Build a CSS Robot
td=moment(cc[7], 'X').format("MMM DD, YYYY")
if cc[8] > 0
tr
td Get Started with jQuery
td=moment(cc[8], 'X').format("MMM DD, YYYY")
if cc[9] > 0
tr
td Traverse the DOM
td=moment(cc[9], 'X').format("MMM DD, YYYY")
if cc[10] > 0
tr
td Work with the DOM
td=moment(cc[10], 'X').format("MMM DD, YYYY")
if cc[11] > 0
tr
td Listen for DOM Events
td=moment(cc[11], 'X').format("MMM DD, YYYY")
if cc[12] > 0
tr
td Use jQuery for Styling
td=moment(cc[12], 'X').format("MMM DD, YYYY")
if cc[13] > 0
tr
td Build a MadLibs Game
td=moment(cc[13], 'X').format("MMM DD, YYYY")
if cc[14] > 0
tr
td Discover Chrome's DevTools
td=moment(cc[14], 'X').format("MMM DD, YYYY")
if cc[15] > 0
tr
td Tackle jQuery Exercises
td=moment(cc[15], 'X').format("MMM DD, YYYY")
if cc[16] > 0
tr
td Customize Bootstrap
td=moment(cc[16], 'X').format("MMM DD, YYYY")
if cc[17] > 0
tr
td Inject Animation into CSS
td=moment(cc[17], 'X').format("MMM DD, YYYY")
if cc[18] > 0
tr
td Learn Basic Computer Science
td=moment(cc[18], 'X').format("MMM DD, YYYY")
if cc[19] > 0
tr
td Learn Loops
td=moment(cc[19], 'X').format("MMM DD, YYYY")
if cc[20] > 0
tr
td Learn Computer Hardware
td=moment(cc[20], 'X').format("MMM DD, YYYY")
if cc[21] > 0
tr
td Learn Computer Networking
td=moment(cc[21], 'X').format("MMM DD, YYYY")
if cc[22] > 0
tr
td Learn Boolean Logic
td=moment(cc[22], 'X').format("MMM DD, YYYY")
if cc[23] > 0
tr
td Learn Computer Security
td=moment(cc[23], 'X').format("MMM DD, YYYY")
if cc[24] > 0
tr
td Build an Adventure Game
td=moment(cc[24], 'X').format("MMM DD, YYYY")
if cc[25] > 0
tr
td Build Rock Paper Scissors
td=moment(cc[25], 'X').format("MMM DD, YYYY")
if cc[26] > 0
tr
td Learn JavaScript For Loops
td=moment(cc[26], 'X').format("MMM DD, YYYY")
if cc[27] > 0
tr
td Learn JavaScript While Loops
td=moment(cc[27], 'X').format("MMM DD, YYYY")
if cc[28] > 0
tr
td Learn Control Flow
td=moment(cc[28], 'X').format("MMM DD, YYYY")
if cc[29] > 0
tr
td Build a Contact List
td=moment(cc[29], 'X').format("MMM DD, YYYY")
if cc[30] > 0
tr
td Build an Address Book
td=moment(cc[30], 'X').format("MMM DD, YYYY")
if cc[31] > 0
tr
td Build a Cash Register
td=moment(cc[31], 'X').format("MMM DD, YYYY")
if cc[32] > 0
tr
td Get Help the Hacker Way
td=moment(cc[32], 'X').format("MMM DD, YYYY")
if cc[33] > 0
tr
td Learn Regular Expressions
td=moment(cc[33], 'X').format("MMM DD, YYYY")
if cc[34] > 0
tr
td Pair Program on CoderByte
td=moment(cc[34], 'X').format("MMM DD, YYYY")
if cc[35] > 0
tr
td Learn Relational Database Theory
td=moment(cc[35], 'X').format("MMM DD, YYYY")
if cc[36] > 0
tr
td Pair Program to Query SQL pt 1
td=moment(cc[36], 'X').format("MMM DD, YYYY")
if cc[37] > 0
tr
td Pair Program to Query SQL pt 2
td=moment(cc[37], 'X').format("MMM DD, YYYY")
if cc[38] > 0
tr
td Pair Program to modify SQL pt 1
td=moment(cc[38], 'X').format("MMM DD, YYYY")
if cc[39] > 0
tr
td Pair Program to modify SQL pt 2
td=moment(cc[39], 'X').format("MMM DD, YYYY")
if cc[40] > 0
tr
td Learn JSON
td=moment(cc[40], 'X').format("MMM DD, YYYY")
if cc[41] > 0
tr
td Manage Source Code with Git
td=moment(cc[41], 'X').format("MMM DD, YYYY")
if cc[42] > 0
tr
td Get Started with Node.js
td=moment(cc[42], 'X').format("MMM DD, YYYY")
if cc[43] > 0
tr
td Try Node.js Events
td=moment(cc[43], 'X').format("MMM DD, YYYY")
if cc[44] > 0
tr
td Try Node.js Streams
td=moment(cc[44], 'X').format("MMM DD, YYYY")
if cc[45] > 0
tr
td Learn how Node.js Modules Work
td=moment(cc[45], 'X').format("MMM DD, YYYY")
if cc[46] > 0
tr
td Start an Express.js Server
td=moment(cc[46], 'X').format("MMM DD, YYYY")
if cc[47] > 0
tr
td Use Socket.io
td=moment(cc[47], 'X').format("MMM DD, YYYY")
if cc[48] > 0
tr
td Use Redis to Persist Data
td=moment(cc[48], 'X').format("MMM DD, YYYY")
if cc[49] > 0
tr
td Dive Deeper into Express.js
td=moment(cc[49], 'X').format("MMM DD, YYYY")
if cc[50] > 0
tr
td Set up Express.js Middleware
td=moment(cc[50], 'X').format("MMM DD, YYYY")
if cc[51] > 0
tr
td Take Advantage of Parameters
td=moment(cc[51], 'X').format("MMM DD, YYYY")
if cc[52] > 0
tr
td Add the Body Parser
td=moment(cc[52], 'X').format("MMM DD, YYYY")
if cc[53] > 0
tr
td Configure Routes in Express.js
td=moment(cc[53], 'X').format("MMM DD, YYYY")
if cc[54] > 0
tr
td Try MongoDB
td=moment(cc[54], 'X').format("MMM DD, YYYY")
if cc[55] > 0
tr
td Get Started with Angular.js
td=moment(cc[55], 'X').format("MMM DD, YYYY")
if cc[56] > 0
tr
td Apply Angular.js Directives
td=moment(cc[56], 'X').format("MMM DD, YYYY")
if cc[57] > 0
tr
td Power Forms with Angular.js
td=moment(cc[57], 'X').format("MMM DD, YYYY")
if cc[58] > 0
tr
td Customize Angular.js Directives
td=moment(cc[58], 'X').format("MMM DD, YYYY")
if cc[59] > 0
tr
td Create Angular.js Services
td=moment(cc[59], 'X').format("MMM DD, YYYY")
for challenge in cc
if ch[challenge.challengeNumber] > 0
tr
td= cc[challenge.challengeNumber].name
td= moment(ch[challenge.challengeNumber], 'X').format("MMM DD, YYYY")
h3 Danger Zone
button.btn.btn-danger.confirm-deletion
span.ion-trash-b

View File

@ -15,7 +15,7 @@ block content
.btn.btn-primary.btn-big.btn-block.completed-challenge I've completed this challenge
.ten-pixel-break
.btn.btn-success.btn-large.btn-block.skip-challenge I want to skip this challenge for now
- if (cc[2] > 0)
- if (cc && cc[2] > 0)
.panel-footer.text-center
span Need a break? Check out our:&nbsp;
a(href="https://gitter.im/FreeCodeCamp/FreeCodeCamp", target="_blank") Chat Room
@ -34,6 +34,11 @@ block content
.animated.zoomInUp.delay-1
span.landing-icon.ion-checkmark-circled.text-primary
a.animated.fadeIn.delay-2.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge
if (cc && cc[2] > 0)
a.animated.fadeIn.delay-2.btn-twitter.btn.btn-lg.btn-info.btn-block(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")
i.fa.fa-twitter &nbsp;
= phrase
#skip-dialog.modal
.modal-dialog.animated.zoomIn.fast-animation
.modal-content

View File

@ -2,185 +2,9 @@
.panel-heading Challenges
.panel-body
ol(start='0')
for challenge in challenges
li
a(href="/challenges/0", class="#{ cc[0] > 0 ? 'strikethrough' : '' }") Learn how Free Code Camp Works
| &nbsp; (2 minutes)
li
a(href="/challenges/1", class="#{ cc[1] > 0 ? 'strikethrough' : '' }") Join Our Chat Room
| &nbsp; (5 minutes)
li
a(href="/challenges/2", class="#{ cc[2] > 0 ? 'strikethrough' : '' }") Subscribe to Our Subreddit
| &nbsp; (5 minutes)
li
a(href="/challenges/3", class="#{ cc[3] > 0 ? 'strikethrough' : '' }") Build a Personal Website
| &nbsp; (60 minutes)
li
a(href="/challenges/4", class="#{ cc[4] > 0 ? 'strikethrough' : '' }") Build a Responsive Blog Theme
| &nbsp; (60 minutes)
li
a(href="/challenges/5", class="#{ cc[5] > 0 ? 'strikethrough' : '' }") Build a Small Business Website
| &nbsp; (60 minutes)
li
a(href="/challenges/6", class="#{ cc[6] > 0 ? 'strikethrough' : '' }") Tweak HTML and CSS in CodePen
| &nbsp; (10 minutes)
li
a(href="/challenges/7", class="#{ cc[7] > 0 ? 'strikethrough' : '' }") Build a CSS Robot
| &nbsp; (60 minutes)
li
a(href="/challenges/8", class="#{ cc[8] > 0 ? 'strikethrough' : '' }") Get Started with jQuery
| &nbsp; (30 minutes)
li
a(href="/challenges/9", class="#{ cc[9] > 0 ? 'strikethrough' : '' }") Traverse the DOM
| &nbsp; (30 minutes)
li
a(href="/challenges/10", class="#{ cc[10] > 0 ? 'strikethrough' : '' }") Work with the DOM
| &nbsp; (30 minutes)
li
a(href="/challenges/11", class="#{ cc[11] > 0 ? 'strikethrough' : '' }") Listen for DOM Events
| &nbsp; (30 minutes)
li
a(href="/challenges/12", class="#{ cc[12] > 0 ? 'strikethrough' : '' }") Use jQuery for Styling
| &nbsp; (30 minutes)
li
a(href="/challenges/13", class="#{ cc[13] > 0 ? 'strikethrough' : '' }") Build a MadLibs Game
| &nbsp; (60 minutes)
li
a(href="/challenges/14", class="#{ cc[14] > 0 ? 'strikethrough' : '' }") Discover Chrome's DevTools
| &nbsp; (90 minutes)
li
a(href="/challenges/15", class="#{ cc[15] > 0 ? 'strikethrough' : '' }") Tackle jQuery Exercises
| &nbsp; (60 minutes)
li
a(href="/challenges/16", class="#{ cc[16] > 0 ? 'strikethrough' : '' }") Customize Bootstrap
| &nbsp; (10 minutes)
li
a(href="/challenges/17", class="#{ cc[17] > 0 ? 'strikethrough' : '' }") Inject Animation into CSS
| &nbsp; (15 minutes)
li
a(href="/challenges/18", class="#{ cc[18] > 0 ? 'strikethrough' : '' }") Learn Basic Computer Science
| &nbsp; (120 minutes)
li
a(href="/challenges/19", class="#{ cc[19] > 0 ? 'strikethrough' : '' }") Learn Loops
| &nbsp; (120 minutes)
li
a(href="/challenges/20", class="#{ cc[20] > 0 ? 'strikethrough' : '' }") Learn Computer Hardware
| &nbsp; (120 minutes)
li
a(href="/challenges/21", class="#{ cc[21] > 0 ? 'strikethrough' : '' }") Learn Computer Networking
| &nbsp; (120 minutes)
li
a(href="/challenges/22", class="#{ cc[22] > 0 ? 'strikethrough' : '' }") Learn Boolean Logic
| &nbsp; (120 minutes)
li
a(href="/challenges/23", class="#{ cc[23] > 0 ? 'strikethrough' : '' }") Learn Computer Security
| &nbsp; (120 minutes)
li
a(href="/challenges/24", class="#{ cc[24] > 0 ? 'strikethrough' : '' }") Build an Adventure Game
| &nbsp; (60 minutes)
li
a(href="/challenges/25", class="#{ cc[25] > 0 ? 'strikethrough' : '' }") Build Rock Paper Scissors
| &nbsp; (60 minutes)
li
a(href="/challenges/26", class="#{ cc[26] > 0 ? 'strikethrough' : '' }") Learn JavaScript For Loops
| &nbsp; (60 minutes)
li
a(href="/challenges/27", class="#{ cc[27] > 0 ? 'strikethrough' : '' }") Learn JavaScript While Loops
| &nbsp; (60 minutes)
li
a(href="/challenges/28", class="#{ cc[28] > 0 ? 'strikethrough' : '' }") Learn Control Flow
| &nbsp; (60 minutes)
li
a(href="/challenges/29", class="#{ cc[29] > 0 ? 'strikethrough' : '' }") Build a Contact List
| &nbsp; (60 minutes)
li
a(href="/challenges/30", class="#{ cc[30] > 0 ? 'strikethrough' : '' }") Build an Address Book
| &nbsp; (60 minutes)
li
a(href="/challenges/31", class="#{ cc[31] > 0 ? 'strikethrough' : '' }") Build a Cash Register
| &nbsp; (60 minutes)
li
a(href="/challenges/32", class="#{ cc[32] > 0 ? 'strikethrough' : '' }") Get Help the Hacker Way
| &nbsp; (30 minutes)
li
a(href="/challenges/33", class="#{ cc[33] > 0 ? 'strikethrough' : '' }") Learn Regular Expressions
| &nbsp; (60 minutes)
li
a(href="/challenges/34", class="#{ cc[34] > 0 ? 'strikethrough' : '' }") Pair Program on CoderByte
| &nbsp; (60 minutes)
li
a(href="/challenges/35", class="#{ cc[35] > 0 ? 'strikethrough' : '' }") Learn Relational Database Theory
| &nbsp; (30 minutes)
li
a(href="/challenges/36", class="#{ cc[36] > 0 ? 'strikethrough' : '' }") Pair Program to Query SQL pt 1
| &nbsp; (90 minutes)
li
a(href="/challenges/37", class="#{ cc[37] > 0 ? 'strikethrough' : '' }") Pair Program to Query SQL pt 2
| &nbsp; (60 minutes)
li
a(href="/challenges/38", class="#{ cc[38] > 0 ? 'strikethrough' : '' }") Pair Program to modify SQL pt 1
| &nbsp; (90 minutes)
li
a(href="/challenges/39", class="#{ cc[39] > 0 ? 'strikethrough' : '' }") Pair Program to modify SQL pt 2
| &nbsp; (60 minutes)
li
a(href="/challenges/40", class="#{ cc[40] > 0 ? 'strikethrough' : '' }") Learn JSON
| &nbsp; (30 minutes)
li
a(href="/challenges/41", class="#{ cc[41] > 0 ? 'strikethrough' : '' }") Manage Source Code with Git
| &nbsp; (30 minutes)
li
a(href="/challenges/42", class="#{ cc[42] > 0 ? 'strikethrough' : '' }") Get Started with Node.js
| &nbsp; (45 minutes)
li
a(href="/challenges/43", class="#{ cc[43] > 0 ? 'strikethrough' : '' }") Try Node.js Events
| &nbsp; (45 minutes)
li
a(href="/challenges/44", class="#{ cc[44] > 0 ? 'strikethrough' : '' }") Try Node.js Streams
| &nbsp; (45 minutes)
li
a(href="/challenges/45", class="#{ cc[45] > 0 ? 'strikethrough' : '' }") Learn how Node.js Modules Work
| &nbsp; (45 minutes)
li
a(href="/challenges/46", class="#{ cc[46] > 0 ? 'strikethrough' : '' }") Start an Express.js Server
| &nbsp; (45 minutes)
li
a(href="/challenges/47", class="#{ cc[47] > 0 ? 'strikethrough' : '' }") Use Socket.io
| &nbsp; (45 minutes)
li
a(href="/challenges/48", class="#{ cc[48] > 0 ? 'strikethrough' : '' }") Use Redis to Persist Data
| &nbsp; (45 minutes)
li
a(href="/challenges/49", class="#{ cc[49] > 0 ? 'strikethrough' : '' }") Dive Deeper into Express.js
| &nbsp; (45 minutes)
li
a(href="/challenges/50", class="#{ cc[50] > 0 ? 'strikethrough' : '' }") Set up Express.js Middleware
| &nbsp; (45 minutes)
li
a(href="/challenges/51", class="#{ cc[51] > 0 ? 'strikethrough' : '' }") Take Advantage of Parameters
| &nbsp; (45 minutes)
li
a(href="/challenges/52", class="#{ cc[52] > 0 ? 'strikethrough' : '' }") Add the Body Parser
| &nbsp; (45 minutes)
li
a(href="/challenges/53", class="#{ cc[53] > 0 ? 'strikethrough' : '' }") Configure Routes in Express.js
| &nbsp; (45 minutes)
li
a(href="/challenges/54", class="#{ cc[54] > 0 ? 'strikethrough' : '' }") Try MongoDB
| &nbsp; (30 minutes)
li
a(href="/challenges/55", class="#{ cc[55] > 0 ? 'strikethrough' : '' }") Get Started with Angular.js
| &nbsp; (45 minutes)
li
a(href="/challenges/56", class="#{ cc[56] > 0 ? 'strikethrough' : '' }") Apply Angular.js Directives
| &nbsp; (45 minutes)
li
a(href="/challenges/57", class="#{ cc[57] > 0 ? 'strikethrough' : '' }") Power Forms with Angular.js
| &nbsp; (45 minutes)
li
a(href="/challenges/58", class="#{ cc[58] > 0 ? 'strikethrough' : '' }") Customize Angular.js Directives
| &nbsp; (45 minutes)
li
a(href="/challenges/59", class="#{ cc[59] > 0 ? 'strikethrough' : '' }") Create Angular.js Services
| &nbsp; (45 minutes)
a(href="/challenges/#{challenge.challengeNumber}", class="#{ (cc && cc[challenge.challengeNumber] > 0) ? 'strikethrough' : '' }") #{challenge.name}
| &nbsp; (#{challenge.time} mins)
a.btn.btn-primary(href="/done-with-first-100-hours", class="#{ cc[59] > 0 ? '' : 'disabled' }") I've finished all Free Code Camp challenges and all the Easy and Medium CoderByte Challenges
a.btn.btn-primary(href="/done-with-first-100-hours", class="#{ ((cc && cc[59] === 0) || (!cc)) ? 'disabled' : '' }") I've finished all Free Code Camp challenges and all the Easy and Medium CoderByte Challenges

View File

@ -27,26 +27,26 @@
a(href='/')
span.ion-map
| My Challenges
- if (cc[1] > 0)
li.hamburger
a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank')
span.ion-coffee
| Chat Room
- else
- if (cc && cc[1] < 1)
li.hamburger.disabled
a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank')
span.ion-coffee
| Chat Room (do Challenge 1 first)
- if (cc[2] > 0)
li.hamburger
a(href='http://forum.freecodecamp.com/', target='_blank')
span.ion-planet
| Forum
- else
li.hamburger
a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank')
span.ion-coffee
| Chat Room
- if (cc && cc[2] < 1)
li.hamburger.disabled
a(href='http://forum.freecodecamp.com/', target='_blank')
span.ion-planet
| Forum (do Challenge 2 first)
- else
li.hamburger
a(href='http://forum.freecodecamp.com/', target='_blank')
span.ion-planet
| Forum
li.hamburger
a(href='/account')
span.ion-person