Merge pull request #319 from terakilobyte/ux-improvements

Ux improvements
pull/320/head
Quincy Larson 2015-04-16 23:00:58 -07:00
commit 8afb3e1199
25 changed files with 1146 additions and 410 deletions

369
app.js
View File

@ -15,22 +15,23 @@ process.on('uncaughtException', function (err) {
});
var express = require('express'),
//accepts = require('accepts'),
cookieParser = require('cookie-parser'),
compress = require('compression'),
session = require('express-session'),
logger = require('morgan'),
errorHandler = require('errorhandler'),
methodOverride = require('method-override'),
bodyParser = require('body-parser'),
helmet = require('helmet'),
MongoStore = require('connect-mongo')(session),
flash = require('express-flash'),
path = require('path'),
mongoose = require('mongoose'),
passport = require('passport'),
expressValidator = require('express-validator'),
connectAssets = require('connect-assets'),
accepts = require('accepts'),
cookieParser = require('cookie-parser'),
compress = require('compression'),
session = require('express-session'),
logger = require('morgan'),
errorHandler = require('errorhandler'),
methodOverride = require('method-override'),
bodyParser = require('body-parser'),
helmet = require('helmet'),
MongoStore = require('connect-mongo')(session),
flash = require('express-flash'),
path = require('path'),
mongoose = require('mongoose'),
passport = require('passport'),
expressValidator = require('express-validator'),
connectAssets = require('connect-assets'),
request = require('request'),
/**
* Controllers (route handlers).
@ -45,11 +46,10 @@ var express = require('express'),
fieldGuideController = require('./controllers/fieldGuide'),
challengeMapController = require('./controllers/challengeMap'),
/**
/**
* Stories
*/
storyController = require('./controllers/story'),
storyController = require('./controllers/story');
/**
* API keys and Passport configuration.
@ -103,13 +103,13 @@ app.use(expressValidator({
app.use(methodOverride());
app.use(cookieParser());
app.use(session({
resave: true,
saveUninitialized: true,
secret: secrets.sessionSecret,
store: new MongoStore({
url: secrets.db,
'autoReconnect': true
})
resave: true,
saveUninitialized: true,
secret: secrets.sessionSecret,
store: new MongoStore({
url: secrets.db,
'auto_reconnect': true
})
}));
app.use(passport.initialize());
app.use(passport.session());
@ -126,40 +126,42 @@ app.use(function(req, res, next) {
});
var trusted = [
"'self'",
'*.freecodecamp.com',
'*.gstatic.com',
'*.google-analytics.com',
'*.googleapis.com',
'*.google.com',
'*.gstatic.com',
'*.doubleclick.net',
'*.twitter.com',
'*.twitch.tv',
'*.twimg.com',
"'unsafe-eval'",
"'unsafe-inline'",
'*.rafflecopter.com',
'*.bootstrapcdn.com',
'*.cloudflare.com',
'https://*.cloudflare.com',
'localhost:3001',
'ws://localhost:3001/',
'http://localhost:3001',
'localhost:3000',
'ws://localhost:3000/',
'http://localhost:3000',
'*.ionicframework.com',
'https://syndication.twitter.com',
'*.youtube.com',
'*.jsdelivr.net',
'https://*.jsdelivr.net',
'*.togetherjs.com',
'https://*.togetherjs.com',
'wss://hub.togetherjs.com',
'*.ytimg.com',
'wss://fcctogether.herokuapp.com',
'*.bitly.com'
"'self'",
'*.freecodecamp.com',
'*.gstatic.com',
'*.google-analytics.com',
'*.googleapis.com',
'*.google.com',
'*.gstatic.com',
'*.doubleclick.net',
'*.twitter.com',
'*.twitch.tv',
'*.twimg.com',
"'unsafe-eval'",
"'unsafe-inline'",
'*.rafflecopter.com',
'*.bootstrapcdn.com',
'*.cloudflare.com',
'https://*.cloudflare.com',
'localhost:3001',
'ws://localhost:3001/',
'http://localhost:3001',
'localhost:3000',
'ws://localhost:3000/',
'http://localhost:3000',
'*.ionicframework.com',
'https://syndication.twitter.com',
'*.youtube.com',
'*.jsdelivr.net',
'https://*.jsdelivr.net',
'*.togetherjs.com',
'https://*.togetherjs.com',
'wss://hub.togetherjs.com',
'*.ytimg.com',
'wss://fcctogether.herokuapp.com',
'*.bitly.com',
'http://cdn.inspectlet.com/',
'http://hn.inspectlet.com/'
];
app.use(helmet.contentSecurityPolicy({
@ -208,9 +210,9 @@ app.use(helmet.contentSecurityPolicy({
}));
app.use(function (req, res, next) {
// Make user object available in templates.
res.locals.user = req.user;
next();
// Make user object available in templates.
res.locals.user = req.user;
next();
});
app.use(function (req, res, next) {
@ -226,7 +228,7 @@ app.use(function (req, res, next) {
});
app.use(
express.static(path.join(__dirname, 'public'), {maxAge: 31557600000})
express.static(path.join(__dirname, 'public'), {maxAge: 31557600000})
);
app.use(express.static(__dirname + '/public', { maxAge: 86400000 }));
@ -245,8 +247,6 @@ app.get('/nonprofit-project-instructions', function(req, res) {
res.redirect(301, "/field-guide/free-code-camp's-privacy-policy");
});
app.get('/jquery-exercises', resourcesController.jqueryExercises);
app.get('/chat', resourcesController.chat);
app.get('/twitch', resourcesController.twitch);
@ -281,24 +281,18 @@ app.get('/nodeschool-challenges', function(req, res) {
res.redirect(301, '/field-guide/nodeschool-challenges');
});
app.get('/stats', function(req, res) {
res.redirect(301, '/learn-to-code');
});
app.get('/about', function(req, res) {
res.redirect(301, '/learn-to-code');
});
app.get('/learn-to-code', resourcesController.about);
app.get('/news', function(req, res) {
res.redirect(301, '/stories/hot');
res.redirect(301, '/stories/hot');
});
app.get('/learn-to-code', resourcesController.about);
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');
res.redirect(301, '/signin');
});
app.post('/signin', userController.postSignin);
@ -306,7 +300,7 @@ app.post('/signin', userController.postSignin);
app.get('/signout', userController.signout);
app.get('/logout', function(req, res) {
res.redirect(301, '/signout');
res.redirect(301, '/signout');
});
app.get('/forgot', userController.getForgot);
@ -324,6 +318,8 @@ app.get('/email-signin', userController.getEmailSignin);
app.post('/email-signup', userController.postEmailSignup);
app.post('/email-signin', userController.postSignin);
app.get('/nonprofits', contactController.getNonprofitsForm);
app.post('/nonprofits', contactController.postNonprofitsForm);
/**
* Nonprofit Project routes.
@ -374,96 +370,155 @@ app.post(
passportConf.isAuthenticated,
contactController.postDoneWithFirst100Hours
);
//app.get(
// '/nonprofit-project-instructions',
// passportConf.isAuthenticated,
// resourcesController.nonprofitProjectInstructions
//);
app.post(
'/update-progress',
passportConf.isAuthenticated,
userController.updateProgress
'/update-progress',
passportConf.isAuthenticated,
userController.updateProgress
);
app.get('/api/slack', function(req, res) {
if (req.user) {
if (req.user.email) {
var invite = {
'email': req.user.email,
'token': process.env.SLACK_KEY,
'set_active': true
};
var headers = {
'User-Agent': 'Node Browser/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
};
var options = {
url: 'https://freecode.slack.com/api/users.admin.invite',
method: 'POST',
headers: headers,
form: invite
};
request(options, function (error, response, body) {
if (!error && response.statusCode === 200) {
req.flash('success', {
msg: "We've successfully requested an invite for you. Please check your email and follow the instructions from Slack."
});
req.user.sentSlackInvite = true;
req.user.save(function(err, user) {
if (err) {
next(err);
}
return res.redirect('back');
});
} else {
req.flash('errors', {
msg: "The invitation email did not go through for some reason. Please try again or <a href='mailto:team@freecodecamp.com?subject=slack%20invite%20failed%20to%20send>email us</a>."
});
return res.redirect('back');
}
})
} else {
req.flash('notice', {
msg: "Before we can send your Slack invite, we need your email address. Please update your profile information here."
});
return res.redirect('/account');
}
} else {
req.flash('notice', {
msg: "You need to sign in to Free Code Camp before we can send you a Slack invite."
});
return res.redirect('/account');
}
});
/**
* Camper News routes.
*/
app.get(
'/stories/hotStories',
storyController.hotJSON
'/stories/hotStories',
storyController.hotJSON
);
app.get(
'/stories/recentStories',
storyController.recentJSON
'/stories/recentStories',
storyController.recentJSON
);
app.get(
'/stories/',
function(req, res) {
res.redirect(302, '/stories/hot');
}
'/stories/',
function(req, res) {
res.redirect(302, '/stories/hot');
}
);
app.get(
'/stories/comments/:id',
storyController.comments
'/stories/comments/:id',
storyController.comments
);
app.post(
'/stories/comment/',
storyController.commentSubmit
'/stories/comment/',
storyController.commentSubmit
);
app.post(
'/stories/comment/:id/comment',
storyController.commentOnCommentSubmit
'/stories/comment/:id/comment',
storyController.commentOnCommentSubmit
);
app.get(
'/stories/submit',
storyController.submitNew
'/stories/submit',
storyController.submitNew
);
app.get(
'/stories/submit/new-story',
storyController.preSubmit
'/stories/submit/new-story',
storyController.preSubmit
);
app.post(
'/stories/preliminary',
storyController.newStory
'/stories/preliminary',
storyController.newStory
);
app.post(
'/stories/',
storyController.storySubmission
'/stories/',
storyController.storySubmission
);
app.get(
'/stories/hot',
storyController.hot
'/stories/hot',
storyController.hot
);
app.get(
'/stories/recent',
storyController.recent
'/stories/recent',
storyController.recent
);
app.get(
'/stories/search',
storyController.search
'/stories/search',
storyController.search
);
app.post(
'/stories/search',
storyController.getStories
'/stories/search',
storyController.getStories
);
app.get(
'/stories/:storyName',
storyController.returnIndividualStory
'/stories/:storyName',
storyController.returnIndividualStory
);
app.post(
'/stories/upvote/',
storyController.upvote
'/stories/upvote/',
storyController.upvote
);
app.all('/account', passportConf.isAuthenticated);
@ -499,12 +554,12 @@ app.get('/bonfire-challenge-generator', bonfireController.publicGenerator);
app.post('/bonfire-challenge-generator', bonfireController.testBonfire);
app.get(
'/bonfires/:bonfireName',
bonfireController.returnIndividualBonfire
'/bonfires/:bonfireName',
bonfireController.returnIndividualBonfire
);
app.get('/bonfire', function(req, res) {
res.redirect(301, '/playground');
res.redirect(301, '/playground');
});
app.post('/completed-bonfire/', bonfireController.completedBonfire);
@ -561,69 +616,69 @@ app.get('/sitemap.xml', resourcesController.sitemap);
*/
var passportOptions = {
successRedirect: '/',
failureRedirect: '/login'
successRedirect: '/',
failureRedirect: '/login'
};
app.get('/auth/twitter', passport.authenticate('twitter'));
app.get(
'/auth/twitter/callback',
passport.authenticate('twitter', {
successRedirect: '/',
failureRedirect: '/login'
})
'/auth/twitter/callback',
passport.authenticate('twitter', {
successRedirect: '/',
failureRedirect: '/login'
})
);
app.get(
'/auth/linkedin',
passport.authenticate('linkedin', {
state: 'SOME STATE'
})
'/auth/linkedin',
passport.authenticate('linkedin', {
state: 'SOME STATE'
})
);
app.get(
'/auth/linkedin/callback',
passport.authenticate('linkedin', passportOptions)
'/auth/linkedin/callback',
passport.authenticate('linkedin', passportOptions)
);
app.get(
'/auth/facebook',
passport.authenticate('facebook', {scope: ['email', 'user_location']})
'/auth/facebook',
passport.authenticate('facebook', {scope: ['email', 'user_location']})
);
app.get(
'/auth/facebook/callback',
passport.authenticate('facebook', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
'/auth/facebook/callback',
passport.authenticate('facebook', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
);
app.get('/auth/github', passport.authenticate('github'));
app.get(
'/auth/github/callback',
passport.authenticate('github', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
'/auth/github/callback',
passport.authenticate('github', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
);
app.get(
'/auth/google',
passport.authenticate('google', {scope: 'profile email'})
'/auth/google',
passport.authenticate('google', {scope: 'profile email'})
);
app.get(
'/auth/google/callback',
passport.authenticate('google', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
'/auth/google/callback',
passport.authenticate('google', passportOptions), function (req, res) {
res.redirect(req.session.returnTo || '/');
}
);
// put this route last
app.get(
'/:username',
userController.returnUser
'/:username',
userController.returnUser
);
/**
@ -653,11 +708,11 @@ if (process.env.NODE_ENV === 'development') {
if (type === 'html') {
req.flash('errors', { msg: message });
return res.redirect('/');
// json
// json
} else if (type === 'json') {
res.setHeader('Content-Type', 'application/json');
return res.send({ message: message });
// plain text
// plain text
} else {
res.setHeader('Content-Type', 'text/plain');
return res.send(message);
@ -669,11 +724,11 @@ if (process.env.NODE_ENV === 'development') {
* Start Express server.
*/
app.listen(app.get('port'), function () {
console.log(
'FreeCodeCamp server listening on port %d in %s mode',
app.get('port'),
app.get('env')
);
console.log(
'FreeCodeCamp server listening on port %d in %s mode',
app.get('port'),
app.get('env')
);
});
module.exports = app;

View File

@ -82,6 +82,7 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r
User.findOne({ facebook: profile.id }, function(err, existingUser) {
if (existingUser) return done(null, existingUser);
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
if (err) { return done(err); }
if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
done();
@ -96,29 +97,29 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r
user.profile.location = (profile._json.location) ? profile._json.location.name : '';
user.save(function(err) {
done(err, user);
});
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
user: secrets.mandrill.user,
pass: secrets.mandrill.password
}
});
var mailOptions = {
to: user.email,
from: 'Team@freecodecamp.com',
subject: 'Welcome to Free Code Camp!',
text: [
'Greetings from San Francisco!\n\n',
'Thank you for joining our community.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
'Good luck with the challenges!\n\n',
'- the Volunteer Camp Counselor Team'
].join('')
};
transporter.sendMail(mailOptions, function(err) {
if (err) { return err; }
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
user: secrets.mandrill.user,
pass: secrets.mandrill.password
}
});
var mailOptions = {
to: user.email,
from: 'Team@freecodecamp.com',
subject: 'Welcome to Free Code Camp!',
text: [
'Greetings from San Francisco!\n\n',
'Thank you for joining our community.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
'Good luck with the challenges!\n\n',
'- Our All-Volunteer Team'
].join('')
};
transporter.sendMail(mailOptions, function(err) {
if (err) { return err; }
});
});
}
});

View File

@ -10,7 +10,11 @@ module.exports = {
},
blogger: {
key: process.env.BLOGGER_KEY,
key: process.env.BLOGGER_KEY
},
slack: {
key: process.env.SLACK_KEY
},
mandrill: {

View File

@ -175,7 +175,7 @@ function getMDNlinks(links) {
return populatedLinks;
};
}
/**
*
@ -187,11 +187,11 @@ exports.testBonfire = function(req, res) {
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireChallengeSeed = req.body.challengeSeed;
bonfireTests = bonfireTests.split('\r\n');
bonfireDescription = bonfireDescription.split('\r\n');
bonfireTests.filter(getRidOfEmpties);
bonfireDescription.filter(getRidOfEmpties);
bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', '');
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,
@ -228,11 +228,11 @@ exports.generateChallenge = function(req, res) {
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireChallengeSeed = req.body.challengeSeed;
bonfireTests = bonfireTests.split('\r\n');
bonfireDescription = bonfireDescription.split('\r\n');
bonfireTests.filter(getRidOfEmpties);
bonfireDescription.filter(getRidOfEmpties);
bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', '');
bonfireTests = bonfireTests.split('\r\n');
bonfireDescription = bonfireDescription.split('\r\n');
bonfireTests.filter(getRidOfEmpties);
bonfireDescription.filter(getRidOfEmpties);
bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', '');
var response = {
@ -306,7 +306,6 @@ exports.completedBonfire = function (req, res, next) {
}
});
} else {
console.log('look here!', bonfireName);
req.user.completedBonfires.push({
_id: bonfireHash,
name: bonfireName,

View File

@ -27,24 +27,8 @@ exports.returnNextChallenge = function(req, res) {
}
};
exports.returnChallenge = function(req, res) {
exports.returnChallenge = function(req, res, next) {
var challengeNumber = parseInt(req.params.challengeNumber) || 0;
if (challengeNumber === 2) {
req.user.challengesHash[challengeNumber] = Math.round(+new Date() / 1000);
var timestamp = req.user.challengesHash;
var points = 0;
for (var key in timestamp) {
if (timestamp[key] > 0 && req.body.challengeNumber < 54) {
points += 1;
}
}
req.user.points = points;
req.user.save(function(err) {
if (err) { return done(err); }
});
return res.redirect('../challenges/3');
}
if (challengeNumber > highestChallengeNumber) {
req.flash('errors', {
@ -55,7 +39,7 @@ exports.returnChallenge = function(req, res) {
Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) {
if (err) {
debug('Challenge err: ', err);
next(err);
return next(err);
}
res.render('challenges/show', {
title: 'Challenge: ' + c[challengeNumber].name,

View File

@ -258,7 +258,7 @@ exports.completedCourseware = function (req, res, next) {
});
var index = req.user.completedCoursewares.indexOf(coursewareHash);
if (index === -1) {
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
@ -364,7 +364,6 @@ exports.completedZiplineOrBasejump = function (req, res, next) {
});
var index = req.user.uncompletedCoursewares.indexOf(coursewareHash);
console.log('index here', index)
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);

View File

@ -98,7 +98,7 @@ module.exports = {
chat: function chat(req, res) {
if (req.user && req.user.progressTimestamps.length > 5) {
res.redirect('http://gitter.im/freecodecamp/freecodecamp');
res.redirect('http://freecode.slack.com');
} else {
res.render('resources/chat', {
title: "Watch us code live on Twitch.tv"
@ -106,12 +106,6 @@ module.exports = {
}
},
jqueryExercises: function jqueryExercises(req, res) {
res.render('resources/jquery-exercises', {
title: 'jQuery Exercises'
});
},
twitch: function twitch(req, res) {
res.render('resources/twitch', {
title: "Enter Free Code Camp's Chat Rooms"
@ -155,52 +149,6 @@ module.exports = {
var date1 = new Date("10/15/2014");
var date2 = new Date();
var progressTimestamps = req.user.progressTimestamps;
var now = Date.now() || 0;
//if (req.user.pointsNeedMigration) {
// var challengesHash = req.user.challengesHash;
// for (var key in challengesHash) {
// if (challengesHash[key] > 0) {
// req.user.progressTimestamps.push(challengesHash[key]);
// }
// }
//
// var oldChallengeKeys = R.keys(req.user.challengesHash);
//
// var updatedTimesFromOldChallenges = oldChallengeKeys.map(function(timeStamp) {
// if (timeStamp.toString().length !== 13) {
// timeStamp *= 1000;
// }
// return timeStamp;
// });
//
// var newTimeStamps = R.map(function(timeStamp) {
// if (timeStamp.toString().length !== 13) {
// timeStamp *= 1000;
// }
// return timeStamp;
// }, req.user.progressTimestamps);
//
// req.user.progressTimestamps = newTimeStamps;
//
// req.user.completedCoursewares = Array.zip(updatedTimesFromOldChallenges, coursewares,
// function(left, right) {
// return ({
// completedDate: left.timeStamp,
// _id: right._id,
// name: right.name
// });
// }).filter(function(elem) {
// return elem.completedDate !== 0;
// });
// req.user.pointsNeedMigration = false;
// req.user.save();
//}
if (progressTimestamps[progressTimestamps.length - 1] <= (now - 43200)) {
req.user.progressTimestamps.push(now);
}
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
var announcements = resources.announcements;
@ -222,7 +170,6 @@ module.exports = {
title: 'About Free Code Camp',
daysRunning: daysRunning,
c3: numberWithCommas(c3),
all: all,
announcements: announcements
});
});

View File

@ -349,17 +349,14 @@ exports.storySubmission = function(req, res, next) {
originalStoryAuthorEmail: req.user.email
});
req.user.progressTimestamps.push(Date.now());
req.user.save();
story.save(function(err) {
if (err) {
return next(err);
}
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
story.save(function(err) {
if (err) {
return res.status(500);
}
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
};
exports.commentSubmit = function(req, res, next) {

View File

@ -196,6 +196,10 @@ exports.postEmailSignup = function(req, res, next) {
});
};
/**
* GET /account
* Profile page.
*/
exports.getAccount = function(req, res) {
res.render('account/account', {
@ -217,41 +221,44 @@ exports.getAccountAngular = function(req, res) {
* Unique username check API Call
*/
exports.checkUniqueUsername = function(req, res) {
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
if (data == 1) {
return res.send(true);
} else {
return res.send(false);
}
});
exports.checkUniqueUsername = function(req, res, next) {
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
if (err) { return next(err); }
if (data == 1) {
return res.send(true);
} else {
return res.send(false);
}
});
};
/**
* 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(true);
} else {
return res.send(false);
}
});
exports.checkExistingUsername = function(req, res, next) {
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
if (err) { return next(err); }
if (data === 1) {
return res.send(true);
} else {
return res.send(false);
}
});
};
/**
* Unique email check API Call
*/
exports.checkUniqueEmail = function(req, res) {
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
if (data === 1) {
return res.send(true);
} else {
return res.send(false);
}
});
exports.checkUniqueEmail = function(req, res, next) {
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
if (err) { return next(err); }
if (data == 1) {
return res.send(true);
} else {
return res.send(false);
}
});
};

View File

@ -68,4 +68,4 @@ gulp.task('lint', function() {
.pipe(eslint.format());
});
gulp.task('default', ['lint', 'serve', 'sync']);
gulp.task('default', ['serve', 'sync']);

View File

@ -103,6 +103,7 @@ var userSchema = new mongoose.Schema({
}
},
resetPasswordToken: String,
sentSlackInvite: false,
resetPasswordExpires: Date,
uncompletedBonfires: Array,
completedBonfires: [

View File

@ -67,7 +67,7 @@ var requests;
// (re)initializes the plugin
var reset = function() {
requests = 0;
plugin = new jailed.Plugin(path+'plugin_v0.1.4.js', api);
plugin = new jailed.Plugin(path+'plugin_v0.1.5.js', api);
plugin.whenDisconnected( function() {
// give some time to handle the last responce
setTimeout( function() {

View File

@ -1,82 +0,0 @@
// executes the given code and handles the result
var run = function(code) {
var result = {
input: code,
output: null,
error: null,
type: null
};
try {
var codeExec = runHidden(code);
result.type = typeof codeExec;
result.output = stringify(codeExec);
} catch(e) {
result.error = e.message;
}
application.remote.output(result);
self.close();
};
// protects even the worker scope from being accessed
var runHidden = function(code) {
var importScript = function(url) {
var error = null;
try {
importScripts(url);
} catch (e) {
error = e;
console.log('Unable to load ramda!');
}
};
var indexedDB = null;
var location = null;
var navigator = null;
var onerror = null;
var onmessage = null;
var performance = null;
var self = null;
var webkitIndexedDB = null;
var postMessage = null;
var close = null;
var openDatabase = null;
var openDatabaseSync = null;
var webkitRequestFileSystem = null;
var webkitRequestFileSystemSync = null;
var webkitResolveLocalFileSystemSyncURL = null;
var webkitResolveLocalFileSystemURL = null;
var addEventListener = null;
var dispatchEvent = null;
var removeEventListener = null;
var dump = null;
var onoffline = null;
var ononline = null;
importScript("https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js");
var _ = R;
return eval(code);
}
// converts the output into a string
var stringify = function(output) {
var result;
if (typeof output == 'undefined') {
result = 'undefined';
} else if (output === null) {
result = 'null';
} else {
result = JSON.stringify(output) || output.toString();
}
return result;
}
application.setInterface({run:run});

View File

@ -0,0 +1,88 @@
// executes the given code and handles the result
var run = function(code) {
var result = {
input: code,
output: null,
error: null,
type: null
};
try {
var codeExec = runHidden(code);
result.type = typeof codeExec;
result.output = stringify(codeExec);
} catch(e) {
result.error = e.message;
}
application.remote.output(result);
self.close();
};
// protects even the worker scope from being accessed
var runHidden = function(code) {
var importScript = function(url) {
var error = null;
try {
importScripts(url);
} catch (e) {
error = e;
console.log('Unable to load %s!', url);
}
};
var indexedDB = null;
var location = null;
var navigator = null;
var onerror = null;
var onmessage = null;
var performance = null;
var self = null;
var webkitIndexedDB = null;
var postMessage = null;
var close = null;
var openDatabase = null;
var openDatabaseSync = null;
var webkitRequestFileSystem = null;
var webkitRequestFileSystemSync = null;
var webkitResolveLocalFileSystemSyncURL = null;
var webkitResolveLocalFileSystemURL = null;
var addEventListener = null;
var dispatchEvent = null;
var removeEventListener = null;
var dump = null;
var onoffline = null;
var ononline = null;
importScripts(
"https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js");
importScripts(
"https://cdnjs.cloudflare.com/ajax/libs/chai/2.2.0/chai.min.js"
)
var expect = chai.expect;
var assert = chai.assert;
return eval(code);
}
// converts the output into a string
var stringify = function(output) {
var result;
if (typeof output == 'undefined') {
result = 'undefined';
} else if (output === null) {
result = 'null';
} else {
result = JSON.stringify(output) || output.toString();
}
return result;
}
application.setInterface({run:run});

View File

@ -14,6 +14,7 @@ var links =
"Global String Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String",
"Boolean Objects" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean",
"RegExp" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp",
"Global Function Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function",
"Arguments object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments",
// ========= GLOBAL OBJECT METHODS

View File

@ -636,6 +636,23 @@
],
"MDNlinks" : ["Object.hasOwnProperty()", "Object.getOwnPropertyNames()"]
},
{
"_id": "a97fd23d9b809dac9921074f",
"name": "Arguments Optional",
"difficulty": "2.22",
"description": [
"Create a function that sums two arguments together. If only one argument is provided, return a function that expects one additional argument and will return the sum.",
"For example, add(2, 3) should return 5, and add(2) should return a function that is waiting for an argument so that <code>var sum2And = add(2); return sum2And(3); // 5</code>",
"If either argument isn't a valid numbers, return undefined"
],
"challengeSeed": "function add() {\n return false;\n}\n\nadd(2,3);",
"tests": [
"expect(add(2, 3)).to.equal(5);",
"expect(add(2)(3)).to.equal(5);",
"expect(add('http://bit.ly/IqT6zt')).to.be.undefined;"
],
"MDNlinks": ["Global Function Object", "Arguments object"]
},
{
"_id": "a2f1d72d9b908d0bd72bb9f6",
"name": "Make a Person",

662
seed_data/challenges.json Normal file
View File

@ -0,0 +1,662 @@
[
{
"name": "Learn how Free Code Camp Works",
"time": 2,
"video": "114486344",
"challengeNumber": 0,
"steps": [
"Watch this 1-minute video, or simply read this summary.",
"Welcome to Free Code Camp. We're a community of busy people learning to code.",
"We built this community because learning to code is hard. But anyone who can stay motivated can learn to code. And the best way to stay motivated is to code with friends.",
"To maximize accessibility, all our challenges are self-paced, browser-based, and free.",
"All of us start with the same 100 hours of interactive coding challenges. These cover Computer Science and databases. They also cover in-demand JavaScript tools like jQuery, Node.js and MongoDB.",
"Once we have a basic understanding of web development, we'll spend another 900 hours putting that theory into practice. We'll build full stack solutions for nonprofits.",
"By the end of this process, we'll be good at coding. We'll have a portfolio of apps with happy users to prove it. We'll also have an alumni network of fellow coders and nonprofits ready to serve as references.",
"If you make it through Free Code Camp, you will be able to get a coding job. There are far more job openings out there than there are qualified coders to fill them.",
"Also, for every pure coding job, there are at least 5 additional jobs that require some coding skills. So even if you decide not to pursue coding as a career, you'll still walk away with a valuable job skill.",
"There are 3 keys to succeeding in our community: do the challenges, make friends, and find a routine.",
"Now it's time to join our chatroom. Click the \"I've completed this challenge\" button to move on to your next challenge."
]
},
{
"name": "Join Our Chat Room",
"time": 5,
"video": "124555254",
"challengeNumber": 1,
"steps": [
"Now we're going to join the Free Code Camp chat room. You can come here any time of day to hang out, ask questions, or find another camper to pair program with.",
"Make sure your Free Code Camp account includes your email address. You can do this here: <a href='/account' target='_blank'>http://freecodecamp.com/account</a>.",
"Click this link, which will email you an invite to Free Code Camp's Slack chat room: <a href='/api/slack' target='_blank'>http://freecodecamp.com/api/slack</a>.",
"Now check your email and click the link in the email from Slack",
"Complete the sign up process, then update your biographical information and upload an image. A picture of your face works best. This is how people will see you in the chat room, so put your best foot forward.",
"Now enter the general chat room and introduce yourself to our chat room by typing: \"hello world!\".",
"Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.",
"Keep the chat room open while you work through the other challenges. That way you ask for help if you get stuck on a challenge. You can also socialize when you feel like taking a break.",
"You can also access this chat room by clicking the \"Chat\" button in the upper right hand corner."
]
},
{
"name": "Check out Camper News",
"time": 5,
"video": "124553410",
"challengeNumber": 2,
"steps": [
"Camper News is the best place for our campers to share and discuss helpful links.",
"Click \"News\" in the upper right hand corner.",
"You'll see a variety of links that have been submitted. Click on the \"Discuss\" button under one of them.",
"You can upvote links. This will push the link up the rankings of hot links.",
"You an also comment on a link. If someone responds to your comment, you'll get an email notification so you can come back and respond to them.",
"You can also submit links. You can modify the link's headline and also leave an initial comment about the link.",
"You can view the portfolio pages of any camper who has posted links or comments on Camper News. Just click on their photo.",
"When you submit a link, you'll get a point. You will also get a point each time someone upvotes your link."
]
},
{
"name": "Build a Personal Website",
"time": 60,
"video": "114627406",
"challengeNumber": 3,
"steps": [
"There are tons of interactive HTML and CSS tutorials out there, but Nathan Bashaw and Christine Bower's Dash tutorials - which they built for General Assembly - are our favorite.",
"Go to <a href='https://dash.generalassemb.ly/projects/annas-website-1' target='_blank'>https://dash.generalassemb.ly/projects/annas-website-1</a> and get started with your first project."]
},
{
"name": "Build a Responsive Blog Theme",
"time": 60,
"video": "114578441",
"challengeNumber": 4,
"steps": [
"Next, let's learn about responsive web design and continue learning about HTML and CSS.",
"A responsive website will automatically adapt to changes in your browser's width. This means that you can make one version of a website that will look good on desktop, tablet and phone.",
"Later, we'll use Twitter's Bootstrap CSS framework to build responsive websites.",
"You can check it out here: <a href='http://getbootstrap.com/' target='_blank'>http://getbootstrap.com/</a>.",
"Go to <a href='https://dash.generalassemb.ly/projects/jeffs-blog-1' target='_blank'>https://dash.generalassemb.ly/projects/jeffs-blog-1</a> and complete the second project."
]
},
{
"name": "Build a Small Business Website",
"time": 60,
"video": "114578438",
"challengeNumber": 5,
"steps": [
"Ready for some more HTML and CSS fundamentals?",
"Go to <a href='https://dash.generalassemb.ly/projects/eshas-restaurant-1' target='_blank'>https://dash.generalassemb.ly/projects/eshas-restaurant-1</a> and complete the third project."]
},
{
"name": "Tweak HTML and CSS in CodePen",
"time": 10,
"video": "110752744",
"challengeNumber": 6,
"steps": [
"Now we're going to learn how to use a tool called CodePen, which lets you experiment with HTML and CSS, and even create single-page web applications, right in your browser!",
"Go to <a href='http://www.newsweek.com/' target='_blank'>http://www.newsweek.com/</a>",
"Change the window size. Note that Newsweek.com is using <strong>Responsive Design</strong>.",
"Right-click an area of the page that doesn't have any HTML elements on it, then choose 'view page source'.",
"Select all the text, then copy it.",
"Go to <a href='http://codepen.io/pen/' target='_blank'>http://codepen.io/pen/</a>",
"Paste the HTML you copied from Newsweek.com into the HTML field of CodePen.",
"You now have your own customizable version of the Newsweek.com website. See if you can change some of the text and images."
]
},
{
"name": "Build a CSS Robot",
"time": 60,
"video": "114578436",
"challengeNumber": 7,
"steps": [
"Now let's learn some more CSS, and a small amount of a JavaScript-based tool called jQuery.",
"Go to <a href='https://dash.generalassemb.ly/projects/cotbots-1' target='_blank'>https://dash.generalassemb.ly/projects/cotbots-1</a> and complete the fourth project."]
},
{
"name": "Get Started with jQuery",
"time": 30,
"video": "114578435",
"challengeNumber": 8,
"steps": [
"jQuery is a powerful tool for manipulating HTML elements.",
"It's a lot easier to use than JavaScript itself, so we'll learn it first.",
"It's also extremely popular with employers, so we're going to learn it well.",
"Code School has an excellent free course that will walk us through the basics of jQuery.",
"Go to <a href='http://try.jquery.com/levels/1/challenges/1' target='_blank'>http://try.jquery.com/levels/1/challenges/1</a> and complete the first section."
]
},
{
"name": "Traverse the DOM",
"time": 30,
"video": "114591805",
"challengeNumber": 9,
"steps": [
"Now let's learn more about DOM traversal - that is, moving from one HTML element to the next.",
"Go to <a href='http://try.jquery.com/levels/2/challenges/1' target='_blank'>http://try.jquery.com/levels/2/challenges/1</a> and complete the second section."
]
},
{
"name": "Work with the DOM",
"time": 30,
"video": "114591804",
"challengeNumber": 10,
"steps": [
"Let's learn some more advanced ways to use jQuery to manipulate the DOM.",
"Go to <a href='http://try.jquery.com/levels/3/challenges/1' target='_blank'>http://try.jquery.com/levels/3/challenges/1</a> and complete the third section."
]
},
{
"name": "Listen for DOM Events",
"time": 30,
"video": "114591802",
"challengeNumber": 11,
"steps": [
"Now let's learn how to use jQuery Listeners. These will \"listen\" for something to happen, and then trigger a subsequent event",
"Go to <a href='http://try.jquery.com/levels/4/challenges/1' target='_blank'>http://try.jquery.com/levels/4/challenges/1</a> and complete the fourth section."
]
},
{
"name": "Use jQuery for Styling",
"time": 30,
"video": "114591801",
"challengeNumber": 12,
"steps": [
"Finally, let's use jQuery to manipulate the way websites look by changing the CSS of elements.",
"Go to <a href='http://try.jquery.com/levels/5/challenges/1' target='_blank'>http://try.jquery.com/levels/5/challenges/1</a> and complete the fifth section."
]
},
{
"name": "Build a MadLibs Game",
"time": 60,
"video": "114591799",
"challengeNumber": 13,
"steps": [
"Now that we've built a foundation in jQuery, let's go back to Dash and do its last challenge.",
"If you aren't familiar with Mad Libs, they basically involve inserting random nouns, adjectives and verbs into stories. The stories that result are often hilarious.",
"Go to <a href='https://dash.generalassemb.ly/projects/mad-libs-1' target='_blank'>https://dash.generalassemb.ly/projects/mad-libs-1</a> and complete the fifth project."
]
},
{
"name": "Discover Chrome's DevTools",
"time": 90,
"video": "110752743",
"challengeNumber": 14,
"steps": [
"It's time to learn the most powerful tool your browser has - the Development Tools!",
"If you aren't already using Chrome, you'll want to download it here: <a href='http://www.google.com/chrome/' target='_blank'>http://www.google.com/chrome/</a>. While it's true that Firefox has a tool called Firebug that is very similar to Chrome's DevTools, we will use Chrome for this challenge.",
"Note that this course, jointly produced by Google and Code School, is technologically impressive, but occasionally buggy. If you encounter a bug, just ignore it and keep going.",
"Go to <a href='http://discover-devtools.codeschool.com' target='_blank'>http://discover-devtools.codeschool.com</a> and complete this short course."
]
},
{
"name": "Tackle jQuery Exercises",
"time": 60,
"video": "113173612",
"challengeNumber": 15,
"steps": [
"We've built some special jQuery challenges to help you reinforce your knowledge of this fundamental skill.",
"There are many correct ways to solve each of these exercises. After you complete the challenge, you can compare your solution with our solution by pressing the \"#solution-button\" button.",
"Go to <a href='http://freecodecamp.com/jquery-exercises' target='_blank'>http://freecodecamp.com/jquery-exercises</a> and complete the exercises."
]
},
{
"name": "Customize Bootstrap",
"time": 15,
"video": "110752741",
"challengeNumber": 16,
"steps": [
"Let's learn a little more about Twitter's responsive CSS framework, Bootstrap, and how we can add some custom themes to it.",
"Go to <a href='http://getbootstrap.com/components/' target='_blank'>http://getbootstrap.com/components/</a>",
"Right-click an area of the page that doesn't have any HTML elements on it, then choose 'view page source'.",
"Select all the text, then copy it.",
"Go to <a href='http://codepen.io/pen/' target='_blank'>http://codepen.io/pen/</a>",
"Paste the HTML you copied from GetBootStrap.com into the HTML field of CodePen.",
"Go to <a href='http://bootswatch.com/' target='_blank'>http://bootswatch.com/</a>",
"Decide which theme you want to use.",
"Click the down arrow next to the download button and choose 'bootstrap.css'.",
"Select all the text, then copy it.",
"Go back to CodePen and paste the CSS you copied from Bootswatch.com into the CSS field of CodePen.",
"Your Bootswatch CSS should now be applied to the HTML from the GetBootStrap page.",
"This page is currently using a two-column layout, with the main content on the left and additional navigation on the right. See if you can make it a one-column layout."
]
},
{
"name": "Inject Animation into CSS",
"time": 15,
"video": "110752740",
"challengeNumber": 17,
"steps": [
"You may have noticed some sites have cool animations. Actually, animating DOM elements is pretty straightforward if you use a CSS library called Animate.css.",
"Go to <a href='http://daneden.github.io/animate.css/' target='_blank'>http://daneden.github.io/animate.css/</a> and try out some of the CSS animations.",
"Go to <a href='http://codepen.io/ossia/pen/bGegt' target='_blank'>http://codepen.io/ossia/pen/bGegt</a>.",
"Press the \"Fork\" button. This will fork, meaning create a copy of, the CodePen.",
"Click the gear in the CSS column.",
"Click \"Add another resource\" and start typing \"animate.css\". Click on the dropdown results to autocomplete it.",
"Now that you have Animate.css enabled, use jQuery and the \"toggleClass\" method to add an animated class to all h1 elements when you click the \"Press Me\" button."
]
},
{
"name": "Learn Basic Computer Science",
"time": 120,
"video": "114628241",
"challengeNumber": 18,
"steps": [
"Stanford has an excellent free online Computer Science curriculum. This interactive course uses a modified version of JavaScript. It will cover a lot of concepts quickly.",
"Note that Harvard also has an excellent introduction to computer science course called CS50, but it takes more than 100 hours to complete, and doesn't use JavaScript.",
"Despite being completely self-paced, Stanford's CS101 course is broken up into weeks. Each of the following challenges will address one of those weeks.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z54/z1/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z54/z1/</a> and complete the first week's course work."
]
},
{
"name": "Learn Loops",
"time": 120,
"video": "114597348",
"challengeNumber": 19,
"steps": [
"Now let's tackle week 2 of Stanford's Intro to Computer Science course.",
"This will introduce us to loops, a fundamental feature of every programming language.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z100/a7a70ce6e4724c58862ee6007284face/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z100/a7a70ce6e4724c58862ee6007284face/</a> and complete Week 2."
]
},
{
"name": "Learn Computer Hardware",
"time": 120,
"video": "114597347",
"challengeNumber": 20,
"steps": [
"Week 3 of Stanford's Intro to Computer Science covers computer hardware and explains Moore's law of exponential growth in the price-performance of processors.",
"This challenge will also give you an understanding of how bits and bytes work.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z143/z101/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z143/z101/</a> and complete Week 3."
]
},
{
"name": "Learn Computer Networking",
"time": 120,
"video": "114604811",
"challengeNumber": 21,
"steps": [
"Now that you've learned about computer hardware, it's time to learn about the software that runs on top of it.",
"Particularly important, you will learn about networks and TCP/IP - the protocol that powers the internet.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z187/z144/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z187/z144/</a> and complete Week 4."
]
},
{
"name": "Learn Boolean Logic",
"time": 120,
"video": "114604812",
"challengeNumber": 22,
"steps": [
"Now we'll do some more table exercises and learn boolean logic.",
"We'll also learn the difference between digital data and analog data.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z208/z188/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z208/z188/</a> and complete Week 5."
]
},
{
"name": "Learn Computer Security",
"time": 120,
"video": "114604813",
"challengeNumber": 23,
"steps": [
"We're almost done with Stanford's Introduction to Computer Science course!",
"We'll learn about one of the most important inventions of the 20th century - spreadsheets.",
"We'll also learn about Computer Security and some of the more common vulnerabilities software systems have.",
"Go to <a href='https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z229/z213/' target='_blank'>https://class.stanford.edu/courses/Engineering/CS101/Summer2014/courseware/z229/z213/</a> and complete Week 6, the final week of the course."
]
},
{
"name": "Build an Adventure Game",
"time": 60,
"video": "114604814",
"challengeNumber": 24,
"steps": [
"Now that you understand some Computer Science fundamentals, let's focus on programming JavaScript!",
"We're going to work through Codecademy's famous interactive JavaScript course.",
"This course will teach us some JavaScript fundamentals while guiding us through the process of building interesting web apps, all within Codecademy's learner-friendly environment!",
"Go to <a href='http://www.codecademy.com/courses/getting-started-v2/0/1' target='_blank'>http://www.codecademy.com/courses/getting-started-v2/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-x9DnD/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-x9DnD/0/1</a>."
]
},
{
"name": "Build Rock Paper Scissors",
"time": 60,
"video": "114604815",
"challengeNumber": 25,
"steps": [
"Now we'll learn how JavaScript functions work, and use them to build a simple Rock Paper Scissors game.",
"Go to <a href='http://www.codecademy.com/courses/javascript-beginner-en-6LzGd/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-6LzGd/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-Bthev-mskY8/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-Bthev-mskY8/0/1</a>."
]
},
{
"name": "Learn JavaScript For Loops",
"time": 60,
"video": "114614220",
"challengeNumber": 26,
"steps": [
"Let's learn more about the loops that make virtually all programs possible - the \"For Loop\" and \"While Loop\". First, we'll learn the For Loop.",
"Go to <a href='http://www.codecademy.com/courses/javascript-beginner-en-NhsaT/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-NhsaT/0/1web</a> and complete both the both For and While loop section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-XEDZA/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-XEDZA/0/1</a>."
]
},
{
"name": "Learn JavaScript While Loops",
"time": 60,
"video": "114612889",
"challengeNumber": 27,
"steps": [
"Go to <a href='http://www.codecademy.com/courses/javascript-beginner-en-ASGIv/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-ASGIv/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-mrTNH-6VIZ9/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-mrTNH-6VIZ9/0/1</a>."
]
},
{
"name": "Learn Control Flow",
"time": 60,
"video": "114612888",
"challengeNumber": 28,
"steps": [
"Much of human reasoning can be broken down into what we call Boolean Logic. Lucky for us, computers can think the same way! Let's learn how to instruct our computers by writing \"If Statements\" and \"Else Statements\".",
"We'll also learn some advanced \"Control Flow\" principals, such as ways we can exit loops early.",
"Go to <a href='http://www.codecademy.com/courses/javascript-beginner-en-qDwp0/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-qDwp0/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-ZA2rb/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-ZA2rb/0/1</a>."
]
},
{
"name": "Build a Contact List",
"time": 60,
"video": "114612887",
"challengeNumber": 29,
"steps": [
"Up to this point, you've been working mostly with strings and numbers. Now we're going to learn more complicated data structures, like \"Arrays\" and \"Objects\".",
"Go to <a href='http://www.codecademy.com/courses/javascript-beginner-en-9Sgpi/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-9Sgpi/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/javascript-beginner-en-3bmfN/0/1' target='_blank'>http://www.codecademy.com/courses/javascript-beginner-en-3bmfN/0/1</a>."
]
},
{
"name": "Build an Address Book",
"time": 60,
"video": "114612885",
"challengeNumber": 30,
"steps": [
"Let's learn more about objects.",
"Go to <a href='http://www.codecademy.com/courses/spencer-sandbox/0/1' target='_blank'>http://www.codecademy.com/courses/spencer-sandbox/0/1</a> and complete the section.",
"Be sure to also complete this section: <a href='http://www.codecademy.com/courses/building-an-address-book/0/1?curriculum_id=506324b3a7dffd00020bf661' target='_blank'>http://www.codecademy.com/courses/building-an-address-book/0/1?curriculum_id=506324b3a7dffd00020bf661</a>."
]
},
{
"name": "Build a Cash Register",
"time": 60,
"video": "114612882",
"challengeNumber": 31,
"steps": [
"In this final Codecademy section, we'll learn even more about JavaScript objects.",
"Go to <a href='http://www.codecademy.com/courses/objects-ii/0/1' target='_blank'>http://www.codecademy.com/courses/objects-ii/0/1</a> and complete this section.",
"Be sure to also complete the final section: <a href='http://www.codecademy.com/courses/close-the-super-makert/0/1' target='_blank'>http://www.codecademy.com/courses/close-the-super-makert/0/1</a>."
]
},
{
"name": "Get Help the Hacker Way",
"time": 30,
"video": "111500801",
"challengeNumber": 32,
"steps": [
"Watch the video to learn the RSAP (Read, Search, Ask, Post) methodology for getting help.",
"Try an intelligent Google query that involves JavaScript and filters for this year (since JavaScript changes).",
"Go to <a href='http://stackoverflow.com/' target='_blank'>http://stackoverflow.com/</a> and view the recent questions.",
"Go to <a href='http://webchat.freenode.net/' target='_blank'>http://webchat.freenode.net/</a> and create an IRC account.",
"Join the #LearnJavaScript chat room and introduce yourself as a Free Code Camp student.",
"Finally, we have a special chat room specifically for getting help with tools you learn through Free Code Camp Challenges. Go to <a href='https://gitter.im/FreeCodeCamp/Help' target='_blank'>https://gitter.im/FreeCodeCamp/Help</a>. Keep this chat open while you work on the remaining challenges.",
"Now you have several ways of getting help when you're stuck."
]
},
{
"name": "Learn Regular Expressions",
"time": 60,
"video": "112547802",
"challengeNumber": 33,
"steps": [
"You can use a Regular Expression, or \"Regex\", to select specific types of characters in text.",
"Check out <a href='http://www.regexr.com' target='_blank'>http://www.regexr.com</a>. It's a Regular Expression Sandbox.",
"Now go to <a href='http://www.regexone.com' target='_blank'>http://www.regexone.com</a> and complete the tutorial and exercises 1 - 6.",
"Note that you can click \"continue\" to move on to the next step as soon as all the tasks have green check marks beside them. You can often do this just by using the wildcard \"dot\" operator, but try to use the techniques that each lesson recommends."
]
},
{
"name": "Pair Program on Bonfires",
"time": 60,
"video": "119657641",
"challengeNumber": 34,
"steps": [
"OK, we're finally ready to start pair programming!",
"Pair Programming is where two people code together on the same computer. It is an efficient way to collaborate, and widely practiced at software companies. Pair Programming is one of the core concepts of \"Agile\" Software Development, which you will hear more about later.",
"Many people use Skype or Google Hangouts to pair program, but if you talk with professional software engineers, they will tell you that it's not really pair programming unless both people have the ability to use the keyboard and mouse.",
"The most popular tool for pair programming is Screen Hero. You can download Screen Hero for <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=' target='_blank'>Mac</a> or <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9' target='_blank'>Windows</a>. Create your new user account from within the app.",
"We have a special chat room for people ready to pair program. Go to <a href='https://gitter.im/FreeCodeCamp/LetsPair' target='_blank'>https://gitter.im/FreeCodeCamp/LetsPair</a> and type \"Hello Pair Programmers!\"",
"If someone is available, they will be your \"pair\" - the person you pair programming with.",
"If no one gets back to you in the first few minutes, don't worry. There will be lots of opportunities to pair program in the future.",
"If someone does get back to you, private message them and ask for the email address they used to register Screen Hero.",
"Add them as a new contact in Screen Hero, then click the monitor-looking button to attempt to share your screen with them.",
"Once the Screen Hero session starts, your screen's margins will glow orange. You are now sharing your screen.",
"Your pair will have their own cursor, and will be able to type text on his or her and keyboard.",
"Now it's time to tackle our Bonfires.",
"Go to <a href='http://freecodecamp.com/bonfires' target='_blank'>http://freecodecamp.com/bonfires</a> and start working through our Bonfire challenges.",
"Once you you finish pair programming, end the session in Screen Hero session.",
"Congratulations! You have completed your first pair programming session.",
"Try to pair program with different campers until you've completed all the Bonfire challenges. This is a big time investment, but the JavaScript practice you'll get, along with the scripting and algorithm experience, are well worth it!",
"You can complete Bonfire challenges while you continue to work through Free Code Camp's challenges. Take your time.",
"Mark this challenge as complete and move on."
]
},
{
"name": "Manage Source Code with Git",
"time": 30,
"video": "114635309",
"challengeNumber": 35,
"steps": [
"Revision Control Systems like Git ensure that, no matter how you experiment with your code, you can always roll back your app to a stable previous state.",
"Git is also a great way to share and contribute to open source software.",
"Go to <a href='https://www.codeschool.com/courses/try-git' target='_blank'>https://www.codeschool.com/courses/try-git</a> and complete this short interactive course."
]
},
{
"name": "Get Started with Node.js",
"time": 45,
"video": "114686471",
"challengeNumber": 36,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Now that we understand some Computer Science and JavaScript programming, you're ready to move on to Full-stack JavaScript!",
"The first step is to familiarize ourselves Node.js, the JavaScript-based web server that most full-stack JavaScript apps use.",
"When you're ready, go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/1/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/1/video/1</a> and complete the first chapter."
]
},
{
"name": "Try Node.js Events",
"time": 45,
"video": "114684206",
"challengeNumber": 37,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"One of the reasons Node.js is so fast is that it is \"evented.\" It processes events in an asynchronous manner.",
"As a result, Node.js relies on asynchronous callbacks.",
"We'll learn more about how events and callbacks work in this exciting Code School lesson.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/2/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/2/video/1</a> and complete the section."
]
},
{
"name": "Try Node.js Streams",
"time": 45,
"video": "114684209",
"challengeNumber": 38,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"In this Code School lesson, we'll learn about streaming data back and forth between the client to the server.",
"We'll also learn about FS, or File System, an important Node.js module for streaming data.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/3/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/3/video/1</a> and complete the section."
]
},
{
"name": "Learn how Node.js Modules Work",
"time": 45,
"video": "114684213",
"challengeNumber": 39,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"One of the most exciting features of Node.js is NPM - Node Package Manager",
"With NPM, you quickly install any of thousands of Node.js modules into your app, and it will automatically handle the other modules that each module dependends upon downstream.",
"In this lesson, we'll learn how to include these modules in our Node.js app by requiring them as variables.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/4/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/4/video/1</a> and complete the section."
]
},
{
"name": "Start an Express.js Server",
"time": 45,
"video": "114684247",
"challengeNumber": 40,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"We'll complete Code School's Express.js course shortly after completing this course, but this challenge will give you a quick tour of the Express.js framework.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/5/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/5/video/1</a> and complete the section."
]
},
{
"name": "Use Socket.io",
"time": 45,
"video": "114684530",
"challengeNumber": 41,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/6/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/6/video/1</a> and complete the section."
]
},
{
"name": "Use Redis to Persist Data",
"time": 45,
"video": "114684532",
"challengeNumber": 42,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Redis is a key-value store, which is a type of non-relational database. It's one of the fastest and easiest ways to persist data.",
"Even though we'll ultimately use MongoDB and other technologies to persist data, Redis is quite easy to learn and is still worth learning.",
"Go to <a href='http://campus.codeschool.com/courses/real-time-web-with-node-js/level/7/video/1' target='_blank'>http://campus.codeschool.com/courses/real-time-web-with-node-js/level/7/video/1</a> and complete the section."
]
},
{
"name": "Dive Deeper into Express.js",
"time": 45,
"video": "114684533",
"challengeNumber": 43,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Go to <a href='http://campus.codeschool.com/courses/building-blocks-of-express-js/level/1/video/1' target='_blank'>http://campus.codeschool.com/courses/building-blocks-of-express-js/level/1/video/1</a> and complete the section."
]
},
{
"name": "Setup Express.js Middleware",
"time": 45,
"video": "114684535",
"challengeNumber": 44,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Express.js makes extensive use of middleware - a stack of functions that run sequentially in response to a specific event.",
"Let's learn how to incorporate modules and middleware into our Express.js app.",
"Go to <a href='http://campus.codeschool.com/courses/building-blocks-of-express-js/level/2/video/1' target='_blank'>http://campus.codeschool.com/courses/building-blocks-of-express-js/level/2/video/1</a> and complete the section."
]
},
{
"name": "Take Advantage of Parameters",
"time": 45,
"video": "114684537",
"challengeNumber": 45,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Have you ever noticed a question mark in your browser's address bar, followed by a series of strings? Those are parameters. Parameters are an efficient way to pass information to the server between page loads.",
"We'll learn about parameters, along with a powerful Express.js feature called Dynamic Routing, in this exciting Code School lesson.",
"Go to <a href='http://campus.codeschool.com/courses/building-blocks-of-express-js/level/3/video/1' target='_blank'>http://campus.codeschool.com/courses/building-blocks-of-express-js/level/3/video/1</a> and complete the section."
]
},
{
"name": "Add the Body Parser",
"time": 45,
"video": "114684720",
"challengeNumber": 46,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"Now we'll add the Body Parser module to Express.js. Body Parser is a powerful middleware that helps with routing.",
"We'll also learn more about HTTP Requests, such as Post and Delete.",
"Go to <a href='http://campus.codeschool.com/courses/building-blocks-of-express-js/level/4/video/1' target='_blank'>http://campus.codeschool.com/courses/building-blocks-of-express-js/level/4/video/1</a> and complete the section."
]
},
{
"name": "Configure Routes in Express.js",
"time": 45,
"video": "114684724",
"challengeNumber": 47,
"steps": [
"Note that this Code School course is no longer free. We have free alternatives to this course <a href='/nodeschool-challenges' target='_blank'>here</a>.",
"For this last Code School Express.js challenge, we'll refactor our routes.",
"Go to <a href='http://campus.codeschool.com/courses/building-blocks-of-express-js/level/5/video/1' target='_blank'>http://campus.codeschool.com/courses/building-blocks-of-express-js/level/5/video/1</a> and complete the section."
]
},
{
"name": "Try MongoDB",
"time": 30,
"video": "114685061",
"challengeNumber": 48,
"steps": [
"MongoDB is a popular NoSQL (Not Only SQL) database used by many JavaScript apps.",
"Go to <a href='http://try.mongodb.org/' target='_blank'>http://try.mongodb.org/</a> and work through their interactive MongoDB tutorial."
]
},
{
"name": "Get Started with Angular.js",
"time": 45,
"video": "114684726",
"challengeNumber": 49,
"steps": [
"Code School has a short, free Angular.js course. This will give us a quick tour of Angular.js's mechanics and features.",
"In this course, we'll build a virtual shop entirely in Angular.js.",
"Go to <a href='http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/1/section/1/video/1' target='_blank'>http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/1/section/1/video/1</a> and complete the section."
]
},
{
"name": "Apply Angular.js Directives",
"time": 45,
"video": "114684727",
"challengeNumber": 50,
"steps": [
"Directives serve as markers in your HTML. When Angular.js compiles your HTML, it will can alter the behavior of DOM elements based on the directives you've used.",
"Let's learn how these powerful directives work, and how to use them to make your web apps more dynamic",
"Go to <a href='http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/2/section/1/video/1' target='_blank'>http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/2/section/1/video/1</a> and complete the section."
]
},
{
"name": "Power Forms with Angular.js",
"time": 45,
"video": "114684729",
"challengeNumber": 51,
"steps": [
"One area where Angular.js really shines is its powerful web forms.",
"Learn how to create reactive Angular.js forms, including real-time form validation.",
"Go to <a href='http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/3/section/1/video/1' target='_blank'>http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/3/section/1/video/1</a> and complete the section."
]
},
{
"name": "Customize Angular.js Directives",
"time": 45,
"video": "114685062",
"challengeNumber": 52,
"steps": [
"Now we'll learn how to modify existing Angular.js directives, and even build directives of your own.",
"Go to <a href='http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/4/section/1/video/1' target='_blank'>http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/4/section/1/video/1</a> and complete the section."
]
},
{
"name": "Create Angular.js Services",
"time": 45,
"video": "114685060",
"challengeNumber": 53,
"steps": [
"Services are functions that you can use and reuse throughout your Angular.js app to get things done.",
"We'll learn how to use services in this final Code School Angular.js challenge.",
"Go to <a href='http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/5/section/1/video/1' target='_blank'>http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/5/section/1/video/1</a> and complete the section."
]
}
]

View File

@ -41,16 +41,17 @@
"_id": "bd7125d8c441eddfaeb5bd0f",
"name": "Join Our Chat Room",
"difficulty": 0.03,
"challengeSeed": "114627322",
"challengeSeed": "124555254",
"description": [
"Now we're going to join the Free Code Camp chat room. You can come here any time of day to hang out, ask questions and get help.",
"If you don't already have a GitHub account, create one real quick at <a href='https://www.github.com' target='_blank'>https://www.github.com</a>.",
"Be sure to update your biographical information and upload an image. A picture of your face works best. This is how people will see you in the chat room, so put your best foot forward.",
"Now enter the chat room by going to <a href='https://gitter.im/FreeCodeCamp/FreeCodeCamp' target='_blank'>https://gitter.im/FreeCodeCamp/FreeCodeCamp</a> and clicking the \"sign in with GitHub\" button.",
"Introduce yourself to our chat room by typing: \"hello world!\".",
"Now we're going to join the Free Code Camp chat room. You can come here any time of day to hang out, ask questions, or find another camper to pair program with.",
"Make sure your Free Code Camp account includes your email address. You can do this here: <a href='/account' target='_blank'>http://freecodecamp.com/account</a>.",
"Click this link, which will email you an invite to Free Code Camp's Slack chat room: <a href='/api/slack' target='_blank'>http://freecodecamp.com/api/slack</a>.",
"Now check your email and click the link in the email from Slack",
"Complete the sign up process, then update your biographical information and upload an image. A picture of your face works best. This is how people will see you in the chat room, so put your best foot forward.",
"Now enter the general chat room and introduce yourself to our chat room by typing: \"hello world!\".",
"Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.",
"Keep the chat room open while you work through the other challenges. That way you ask for help if you get stuck on a challenge. You can also socialize when you feel like taking a break.",
"You can also access this chatroom by clicking the \"Chat\" button in the upper right hand corner."
"You can also access this chat room by clicking the \"Chat\" button in the upper right hand corner."
],
"challengeType": 2,
"tests": []

View File

@ -15,7 +15,7 @@ block content
script(src='/js/lib/codemirror/mode/javascript/javascript.js')
script(src='/js/lib/jailed/jailed.js')
script(src='/js/lib/bonfire/bonfireInit.js')
script(src='/js/lib/ramda/ramda.min.js')
script(src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js")
.row
@ -74,7 +74,7 @@ block content
for sentence in details
p!= sentence
#MDN-links
h4 Here are some helpful links.
p Here are some helpful links:
for link, index in MDNlinks
ul: li: a(href=""+link, target="_blank") !{MDNkeys[index]}
#less-info.btn.btn-primary.btn-block.btn-primary-ghost

View File

@ -44,7 +44,7 @@ block content
.success(
function (data) {
console.log(data);
url = "https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=" + data + "&hashtags=LearnToCode, JavaScript";
url = "https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge:%20#{name}&url=" + data + "&hashtags=LearnToCode, JavaScript";
$('.btn-twitter').attr('href', url);
}
);

View File

@ -22,11 +22,12 @@ block content
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_nonprofits.svg.gz', title='Help nonprofits')
p.landing-p Give nonprofits a boost by empowering them with code.
.big-break
a.btn.btn-cta.signup-btn(href="/login") Start learning to code (it's free)
br
br
a.btn.nonprofit-cta.btn-success(href="/nonprofits") Get pro bono help for my nonprofit
.big-break
.row
.col-xs-12.col-sm-8.col-sm-offset-2
a.btn.btn-cta.signup-btn.btn-block(href="/login") Start learning to code (it's free)
.spacer
a.btn.btn-cta.btn-success.btn-block(href="/nonprofits") My nonprofit needs coding help
.big-break
h2 Campers you'll hang out with:
.row
.col-xs-12.col-sm-12.col-md-4
@ -84,6 +85,8 @@ block content
li.ion-code &thinsp; You can get help in real time from our community chat rooms and forum
li.ion-code &thinsp; We all share one common goal: to boost our careers with code
.big-break
a.btn.btn-cta.signup-btn(href="/login") Learn to code today (it's free)
.row
.col-xs-12.col-sm-8.col-sm-offset-2
a.btn.btn-cta.signup-btn.btn-block(href="/login") Learn to code today (it's free)
script.
challengeName = 'Home'
challengeName = 'Home'

View File

@ -11,10 +11,16 @@
img.img-responsive.nav-logo(src='https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg.gz', alt='learn to code javascript at Free Code Camp logo')
.collapse.navbar-collapse
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
li
a(href='/map') Map
li
a(href='/chat' target='_blank') Chat
if (user && user.sentSlackInvite)
li
a(href='/chat', target='_blank') Chat
else
li
a(href='/challenges/join-our-chat-room') Chat
li
a(href='/stories/hot') News
li

View File

@ -15,3 +15,19 @@ script.
ga('create', 'UA-55446531-1', 'auto');
ga('require', 'displayfeatures');
ga('send', 'pageview');
script#inspectletjs(type='text/javascript').
window.__insp = window.__insp || [];
__insp.push(['wid', 561999918]);
(function() {
function __ldinsp() {
var insp = document.createElement('script');
insp.type = 'text/javascript';
insp.async = true;
insp.id = "inspsync";
insp.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://cdn.inspectlet.com/inspectlet.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(insp, x);
}
if (window.attachEvent) window.attachEvent('onload', __ldinsp);
else window.addEventListener('load', __ldinsp, false);
})();

View File

@ -5,11 +5,15 @@ block content
else
img.img-responsive.img-center(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png')
br
.text-center
if (user)
a.btn.btn-cta.signup-btn.next-challenge-button(href="/challenges") Take me to my next challenge
else
a.btn.btn-cta.signup-btn.next-challenge-button(href="/signin") Start learning to code (it's free)
.row.text-center
.col-xs-12.col-md-6.col-md-offset-3
if (user)
if (!user.sentSlackInvite)
a.btn.btn-cta.signup-btn.next-challenge-button.btn-block(href="/challenges") Take me to my next challenge
.spacer
a.btn.btn-primary.btn-cta.next-challenge-button.btn-block(href="/api/slack") Join our Slack Chat Room
else
a.btn.btn-cta.signup-btn.next-challenge-button.btn-block(href="/signin") Start learning to code (it's free)
br
.row
.col-xs-12.col-sm-12.col-md-4

View File

@ -0,0 +1,26 @@
extends ../layout
block content
.jumbotron.text-left
h1.hug-top.text-center Bonfire Style Guide
h3 Writing Bonfire challenges is a great way to exercise your own problem solving and testing abilities. It is a simple three step process.
h4
ol
li Fill out the generator form and test your challenge:&thinsp;
a(href="http://www.freecodecamp.com/bonfire-challenge-generator") http://www.freecodecamp.com/bonfire-challenge-generator
li Once you have confirmed a working bonfire challenge in the generator, copy and paste the the fields into the JSON generator: http://www.freecodecamp.com/bonfire-json-generator
li Copy the JSON, fork the freecodecamp repository, and submit a pull request with your addition to the bonfires.json:&thinsp;
a(href="https://github.com/FreeCodeCamp/freecodecamp/blob/master/seed_data/bonfires.json") https://github.com/FreeCodeCamp/freecodecamp/blob/master/seed_data/bonfires.json
h3 Name
p Name your challenge
h3 Difficulty
p Attempt to rate difficulty compared against existing bonfire challenges.
h3 Description
p Separate paragraphs with a line break. Only the first paragraph is visible prior to a user clicking the "More information" button.
p All necessary information must be included in the first paragraph. Write this first paragraph as succinct as possible. Subsequent paragraphs should offer hints or details if needed.
p If your subject matter warrants deeper understanding, you may link to Wikipedia.
h3 Challenge Seed
p This is where you set up what will be in the editor when the camper starts the bonfire.
h3 Tests
p These tests are what bring your challenge to life. Without them, we cannot confirm the accuracy of a user's submitted answer. Choose your tests wisely.
p Bonfire tests are written using the Chai.js assertion library. Please use the should and expect syntax for end user readability. As an example of what not do to, many of the original Bonfire challenges are written with assert syntax and many of the test cases are difficult to read.
p If your bonfire question has a lot of edge cases, you will need to write many tests for full coverage. If you find yourself writing more tests than you desire, you may consider simplifying the requirements of your bonfire challenge. For difficulty level 1 through 3, you will generally only need 2 to 4 tests.