Merge pull request #320 from FreeCodeCamp/ux-improvements

Pulling Ux improvements the rest of the way into master
This commit is contained in:
Quincy Larson
2015-04-16 23:07:10 -07:00
96 changed files with 5915 additions and 5986 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[package.json]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

397
app.js
View File

@ -33,28 +33,29 @@ var express = require('express'),
connectAssets = require('connect-assets'),
request = require('request'),
/**
* Controllers (route handlers).
*/
homeController = require('./controllers/home'),
resourcesController = require('./controllers/resources'),
userController = require('./controllers/user'),
contactController = require('./controllers/contact'),
nonprofitController = require('./controllers/nonprofits'),
bonfireController = require('./controllers/bonfire'),
coursewareController = require('./controllers/courseware'),
fieldGuideController = require('./controllers/fieldGuide'),
challengeMapController = require('./controllers/challengeMap'),
/**
* Controllers (route handlers).
*/
homeController = require('./controllers/home'),
challengesController = require('./controllers/challenges'),
resourcesController = require('./controllers/resources'),
userController = require('./controllers/user'),
contactController = require('./controllers/contact'),
bonfireController = require('./controllers/bonfire'),
coursewareController = require('./controllers/courseware'),
/**
* Stories
*/
storyController = require('./controllers/story');
/**
* Stories
*/
storyController = require('./controllers/story'),
/**
* API keys and Passport configuration.
*/
secrets = require('./config/secrets'),
passportConf = require('./config/passport');
/**
* API keys and Passport configuration.
*/
secrets = require('./config/secrets'),
passportConf = require('./config/passport');
/**
* Create Express server.
@ -66,9 +67,9 @@ var app = express();
*/
mongoose.connect(secrets.db);
mongoose.connection.on('error', function () {
console.error(
'MongoDB Connection Error. Please make sure that MongoDB is running.'
);
console.error(
'MongoDB Connection Error. Please make sure that MongoDB is running.'
);
});
/**
@ -83,21 +84,21 @@ app.use(compress());
var oneYear = 31557600000;
app.use(express.static(__dirname + '/public', {maxAge: oneYear}));
app.use(connectAssets({
paths: [
path.join(__dirname, 'public/css'),
path.join(__dirname, 'public/js')
],
helperContext: app.locals
paths: [
path.join(__dirname, 'public/css'),
path.join(__dirname, 'public/js')
],
helperContext: app.locals
}));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressValidator({
customValidators: {
matchRegex: function (param, regex) {
return regex.test(param);
customValidators: {
matchRegex: function (param, regex) {
return regex.test(param);
}
}
}
}));
app.use(methodOverride());
app.use(cookieParser());
@ -119,9 +120,9 @@ app.use(helmet.xssFilter());
app.use(helmet.noSniff());
app.use(helmet.xframe());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
var trusted = [
@ -164,44 +165,48 @@ var trusted = [
];
app.use(helmet.contentSecurityPolicy({
defaultSrc: trusted,
scriptSrc: ['*.optimizely.com', '*.aspnetcdn.com'].concat(trusted),
'connect-src': [
'ws://*.rafflecopter.com',
'wss://*.rafflecopter.com',
'https://*.rafflecopter.com',
'ws://www.freecodecamp.com',
'http://www.freecodecamp.com'
].concat(trusted),
styleSrc: trusted,
imgSrc: [
'*.evernote.com',
'*.amazonaws.com',
'data:',
'*.licdn.com',
'*.gravatar.com',
'*.akamaihd.net',
'graph.facebook.com',
'*.githubusercontent.com',
'*.googleusercontent.com',
'*' /* allow all input since we have user submitted images for public profile*/
].concat(trusted),
fontSrc: ['*.googleapis.com'].concat(trusted),
mediaSrc: [
'*.amazonaws.com',
'*.twitter.com'
].concat(trusted),
frameSrc: [
'*.gitter.im',
'*.gitter.im https:',
'*.vimeo.com',
'*.twitter.com',
'*.rafflecopter.com',
'*.ghbtns.com'
].concat(trusted),
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
defaultSrc: trusted,
scriptSrc: [
'*.optimizely.com',
'*.aspnetcdn.com',
'*.d3js.org',
].concat(trusted),
'connect-src': [
'ws://*.rafflecopter.com',
'wss://*.rafflecopter.com',
'https://*.rafflecopter.com',
'ws://www.freecodecamp.com',
'http://www.freecodecamp.com'
].concat(trusted),
styleSrc: trusted,
imgSrc: [
'*.evernote.com',
'*.amazonaws.com',
'data:',
'*.licdn.com',
'*.gravatar.com',
'*.akamaihd.net',
'graph.facebook.com',
'*.githubusercontent.com',
'*.googleusercontent.com',
'*' /* allow all input since we have user submitted images for public profile*/
].concat(trusted),
fontSrc: ['*.googleapis.com'].concat(trusted),
mediaSrc: [
'*.amazonaws.com',
'*.twitter.com'
].concat(trusted),
frameSrc: [
'*.gitter.im',
'*.gitter.im https:',
'*.vimeo.com',
'*.twitter.com',
'*.rafflecopter.com',
'*.ghbtns.com'
].concat(trusted),
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
}));
app.use(function (req, res, next) {
@ -211,13 +216,15 @@ app.use(function (req, res, next) {
});
app.use(function (req, res, next) {
// Remember original destination before login.
var path = req.path.split('/')[1];
if (/auth|login|logout|signup|fonts|favicon/i.test(path)) {
return next();
}
req.session.returnTo = req.path;
next();
// Remember original destination before login.
var path = req.path.split('/')[1];
if (/auth|login|logout|signin|signup|fonts|favicon/i.test(path)) {
return next();
} else if (/\/stories\/comments\/\w+/i.test(req.path)) {
return next();
}
req.session.returnTo = req.path;
next();
});
app.use(
@ -231,23 +238,50 @@ app.use(express.static(__dirname + '/public', { maxAge: 86400000 }));
*/
app.get('/', homeController.index);
app.get('/privacy', resourcesController.privacy);
app.get('/jquery-exercises', resourcesController.jqueryExercises);
app.get('/chat', resourcesController.chat);
app.get('/live-pair-programming', resourcesController.livePairProgramming);
app.get('/install-screenhero', resourcesController.installScreenHero);
app.get('/javascript-in-your-inbox', resourcesController.javaScriptInYourInbox);
app.get('/guide-to-our-nonprofit-projects', resourcesController.guideToOurNonprofitProjects);
app.get('/chromebook', resourcesController.chromebook);
app.get('/styleguide', resourcesController.styleguide);
app.get('/deploy-a-website', resourcesController.deployAWebsite);
app.get('/gmail-shortcuts', resourcesController.gmailShortcuts);
app.get('/control-shortcuts', resourcesController.controlShortcuts);
app.get('/control-shortcuts', resourcesController.deployAWebsite);
app.get('/nodeschool-challenges', resourcesController.nodeSchoolChallenges);
app.get('/stats', function(req, res) {
res.redirect(301, '/learn-to-code');
app.get('/privacy', function(req, res) {
res.redirect(301, "/field-guide/free-code-camp's-privacy-policy");
});
app.get('/nonprofit-project-instructions', function(req, res) {
res.redirect(301, "/field-guide/free-code-camp's-privacy-policy");
});
app.get('/chat', resourcesController.chat);
app.get('/twitch', resourcesController.twitch);
app.get('/map', challengeMapController.challengeMap);
app.get('/live-pair-programming', function(req, res) {
res.redirect(301, '/field-guide/live-stream-pair-programming-on-twitch.tv');
});
app.get('/install-screenhero', function(req, res) {
res.redirect(301, '/field-guide/install-screenhero');
});
app.get('/guide-to-our-nonprofit-projects', function(req, res) {
res.redirect(301, '/field-guide/a-guide-to-our-nonprofit-projects');
});
app.get('/chromebook', function(req, res) {
res.redirect(301, '/field-guide/chromebook');
});
app.get('/deploy-a-website', function(req, res) {
res.redirect(301, '/field-guide/deploy-a-website');
});
app.get('/gmail-shortcuts', function(req, res) {
res.redirect(301, '/field-guide/gmail-shortcuts');
});
app.get('/nodeschool-challenges', function(req, res) {
res.redirect(301, '/field-guide/nodeschool-challenges');
});
app.get('/news', function(req, res) {
res.redirect(301, '/stories/hot');
});
@ -256,25 +290,76 @@ app.get('/about', function(req, res) {
res.redirect(301, '/learn-to-code');
});
app.get('/signin', userController.getSignin);
app.get('/login', function(req, res) {
res.redirect(301, '/signin');
});
app.post('/signin', userController.postSignin);
app.get('/signout', userController.signout);
app.get('/logout', function(req, res) {
res.redirect(301, '/signout');
});
app.get('/forgot', userController.getForgot);
app.post('/forgot', userController.postForgot);
app.get('/reset/:token', userController.getReset);
app.post('/reset/:token', userController.postReset);
app.get('/email-signup', userController.getEmailSignup);
app.get('/email-signin', userController.getEmailSignin);
app.post('/email-signup', userController.postEmailSignup);
app.post('/email-signin', userController.postSignin);
app.get('/nonprofits', contactController.getNonprofitsForm);
app.post('/nonprofits', contactController.postNonprofitsForm);
/**
* Nonprofit Project routes.
*/
app.get('/nonprofits', nonprofitController.nonprofitsHome);
app.get('/nonprofits/directory', nonprofitController.nonprofitsDirectory);
app.get('/nonprofits/are-you-with-a-registered-nonprofit', nonprofitController.areYouWithARegisteredNonprofit);
app.get('/nonprofits/are-there-people-that-are-already-benefiting-from-your-services', nonprofitController.areTherePeopleThatAreAlreadyBenefitingFromYourServices);
app.get('/nonprofits/in-exchange-we-ask', nonprofitController.inExchangeWeAsk);
app.get('/nonprofits/ok-with-javascript', nonprofitController.okWithJavaScript);
app.get('/nonprofits/how-can-free-code-camp-help-you', nonprofitController.howCanFreeCodeCampHelpYou);
app.get('/nonprofits/what-does-your-nonprofit-do', nonprofitController.whatDoesYourNonprofitDo);
app.get('/nonprofits/link-us-to-your-website', nonprofitController.linkUsToYourWebsite);
app.get('/nonprofits/tell-us-your-name', nonprofitController.tellUsYourName);
app.get('/nonprofits/tell-us-your-email', nonprofitController.tellUsYourEmail);
app.get('/nonprofits/your-nonprofit-project-application-has-been-submitted', nonprofitController.yourNonprofitProjectApplicationHasBeenSubmitted);
app.get('/nonprofits/other-solutions', nonprofitController.otherSolutions);
app.get('/nonprofits/getNonprofitList', nonprofitController.showAllNonprofits);
app.get('/nonprofits/interested-in-nonprofit/:nonprofitName', nonprofitController.interestedInNonprofit);
app.get(
'/nonprofits/:nonprofitName',
nonprofitController.returnIndividualNonprofit
);
app.get(
'/done-with-first-100-hours',
passportConf.isAuthenticated,
@ -285,11 +370,11 @@ app.post(
passportConf.isAuthenticated,
contactController.postDoneWithFirst100Hours
);
app.get(
'/nonprofit-project-instructions',
passportConf.isAuthenticated,
resourcesController.nonprofitProjectInstructions
);
//app.get(
// '/nonprofit-project-instructions',
// passportConf.isAuthenticated,
// resourcesController.nonprofitProjectInstructions
//);
app.post(
'/update-progress',
passportConf.isAuthenticated,
@ -351,7 +436,7 @@ app.get('/api/slack', function(req, res) {
});
/**
* Main routes.
* Camper News routes.
*/
app.get(
'/stories/hotStories',
@ -436,19 +521,8 @@ app.post(
storyController.upvote
);
/**
* Challenge related routes
*/
app.get(
'/challenges/',
challengesController.returnNextChallenge
);
app.get(
'/challenges/:challengeNumber',
challengesController.returnChallenge
);
app.all('/account', passportConf.isAuthenticated);
app.get('/account/api', userController.getAccountAngular);
/**
@ -456,72 +530,86 @@ app.get('/account/api', userController.getAccountAngular);
*/
app.get('/api/github', resourcesController.githubCalls);
app.get('/api/blogger', resourcesController.bloggerCalls);
app.get('/api/trello', resourcesController.trelloCalls);
/**
* Bonfire related routes
*/
app.get('/field-guide/getFieldGuideList', fieldGuideController.showAllFieldGuides);
app.get('/playground', bonfireController.index);
app.get('/bonfires', bonfireController.returnNextBonfire);
app.get('/bonfire-json-generator', bonfireController.returnGenerator);
app.post('/bonfire-json-generator', bonfireController.generateChallenge);
app.get('/bonfire-challenge-generator', bonfireController.publicGenerator);
app.post('/bonfire-challenge-generator', bonfireController.testBonfire)
app.post('/bonfire-challenge-generator', bonfireController.testBonfire);
app.get(
'/bonfires/:bonfireName',
bonfireController.returnIndividualBonfire
);
app.get('/bonfire', function(req, res) {
res.redirect(301, '/playground');
});
app.post('/completed-bonfire/', bonfireController.completedBonfire);
/**
* Field Guide related routes
*/
app.get('/field-guide/:fieldGuideName', fieldGuideController.returnIndividualFieldGuide);
app.get('/field-guide', fieldGuideController.returnNextFieldGuide);
app.post('/completed-field-guide/', fieldGuideController.completedFieldGuide);
/**
* Courseware related routes
*/
app.get('/coursewares/', coursewareController.returnNextCourseware);
app.get('/challenges/', coursewareController.returnNextCourseware);
app.get(
'/coursewares/:coursewareName',
coursewareController.returnIndividualCourseware
'/challenges/:coursewareName',
coursewareController.returnIndividualCourseware
);
app.post('/completed-courseware/', coursewareController.completedCourseware);
app.post('/completed-zipline-or-basejump',
coursewareController.completedZiplineOrBasejump);
// Unique Check API route
app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername);
app.get('/api/checkExistingUsername/:username', userController.checkExistingUsername);
app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail);
app.get('/account', userController.getAccount);
app.post('/account/profile', userController.postUpdateProfile);
app.post('/account/password', userController.postUpdatePassword);
app.post('/account/delete', userController.postDeleteAccount);
app.get('/account/unlink/:provider', userController.getOauthUnlink);
app.get('/sitemap.xml', resourcesController.sitemap);
/**
* API examples routes.
* accepts a post request. the challenge id req.body.challengeNumber
* and updates user.challengesHash & user.challengesCompleted
*
*/
app.post('/completed-challenge', function (req, res, done) {
req.user.challengesHash[parseInt(req.body.challengeNumber)] =
Math.round(+new Date() / 1000);
var timestamp = req.user.challengesHash;
var points = 0;
for (var key in timestamp) {
if (timestamp[key] > 0 && req.body.challengeNumber < 54) {
points += 1;
}
}
req.user.points = points;
req.user.save(function(err) {
if (err) { return done(err); }
res.status(200).send({ msg: 'progress saved' });
});
});
app.get('/api/checkExistingUsername/:username', userController.checkExistingUsername);
app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail);
app.get('/account', userController.getAccount);
app.post('/account/profile', userController.postUpdateProfile);
app.post('/account/password', userController.postUpdatePassword);
app.post('/account/delete', userController.postDeleteAccount);
app.get('/account/unlink/:provider', userController.getOauthUnlink);
app.get('/sitemap.xml', resourcesController.sitemap);
/**
* OAuth sign-in routes.
@ -533,6 +621,7 @@ var passportOptions = {
};
app.get('/auth/twitter', passport.authenticate('twitter'));
app.get(
'/auth/twitter/callback',
passport.authenticate('twitter', {
@ -566,6 +655,7 @@ app.get(
);
app.get('/auth/github', passport.authenticate('github'));
app.get(
'/auth/github/callback',
passport.authenticate('github', passportOptions), function (req, res) {
@ -577,6 +667,7 @@ app.get(
'/auth/google',
passport.authenticate('google', {scope: 'profile email'})
);
app.get(
'/auth/google/callback',
passport.authenticate('google', passportOptions), function (req, res) {
@ -584,10 +675,6 @@ app.get(
}
);
app.get('/induce-vomiting', function(req, res, next) {
next(new Error('vomiting induced'));
});
// put this route last
app.get(
'/:username',
@ -617,11 +704,7 @@ if (process.env.NODE_ENV === 'development') {
var accept = accepts(req);
var type = accept.type('html', 'json', 'text');
var message = 'oops! Something went wrong. Please try again later.' +
' Twitter authentication is currently unavailable for FreeCodeCamp.' +
' We are working with Twitter to restore functionality' +
' as soon as possible.';
console.log('ERROR!!', err);
var message = 'opps! Something went wrong. Please try again later';
if (type === 'html') {
req.flash('errors', { msg: message });
return res.redirect('/');

View File

@ -1,138 +1,148 @@
var _ = require('lodash'),
debug = require('debug')('freecc:cntr:bonfires'),
Bonfire = require('./../models/Bonfire'),
User = require('./../models/User'),
resources = require('./resources'),
MDNlinks = require('./../seed_data/bonfireMDNlinks');
debug = require('debug')('freecc:cntr:bonfires'),
Bonfire = require('./../models/Bonfire'),
User = require('./../models/User'),
resources = require('./resources'),
R = require('ramda');
MDNlinks = require('./../seed_data/bonfireMDNlinks');
/**
* Bonfire controller
*/
exports.bonfireNames = function(req, res) {
res.render('bonfires/showList', {
bonfireList: resources.allBonfireNames()
});
exports.showAllBonfires = function(req, res) {
var completedBonfires = [];
if(req.user) {
completedBonfires = req.user.completedBonfires.map(function (elem) {
return elem._id;
});
}
var noDuplicateBonfires = R.uniq(completedBonfires);
var data = {};
data.bonfireList = resources.allBonfireNames();
data.completedList = noDuplicateBonfires;
res.send(data);
};
exports.index = function(req, res) {
res.render('bonfire/show.jade', {
completedWith: null,
title: 'Bonfire Playground',
name: 'Bonfire Playground',
difficulty: 0,
brief: 'Feel free to play around!',
details: '',
tests: [],
challengeSeed: '',
cc: req.user ? req.user.bonfiresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliments: resources.randomCompliment(),
bonfires: [],
bonfireHash: 'test'
res.render('bonfire/show.jade', {
completedWith: null,
title: 'Bonfire Playground',
name: 'Bonfire Playground',
difficulty: 0,
brief: 'Feel free to play around!',
details: '',
tests: [],
challengeSeed: '',
cc: req.user ? req.user.bonfiresHash : undefined,
progressTimestamps: req.user ? req.user.progressTimestamps : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliments: resources.randomCompliment(),
bonfires: [],
bonfireHash: 'test'
});
});
};
exports.returnNextBonfire = function(req, res, next) {
if (!req.user) {
return res.redirect('../bonfires/meet-bonfire');
}
var completed = req.user.completedBonfires.map(function (elem) {
return elem._id;
});
if (!req.user) {
return res.redirect('../bonfires/meet-bonfire');
}
var completed = req.user.completedBonfires.map(function (elem) {
return elem._id;
});
req.user.uncompletedBonfires = resources.allBonfireIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
});
req.user.save(function(err) {
if (err) return next(err);
var uncompletedBonfires = req.user.uncompletedBonfires;
var displayedBonfires = Bonfire.find({'_id': uncompletedBonfires[0]});
displayedBonfires.exec(function(err, bonfire) {
if (err) {
return next(err);
}
bonfire = bonfire.pop();
if (bonfire === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the bonfires we have available. Good job!"
});
return res.redirect('../bonfires/meet-bonfire');
}
nameString = bonfire.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../bonfires/' + nameString);
});
});
req.user.uncompletedBonfires = resources.allBonfireIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
});
req.user.save();
var uncompletedBonfires = req.user.uncompletedBonfires;
var displayedBonfires = Bonfire.find({'_id': uncompletedBonfires[0]});
displayedBonfires.exec(function(err, bonfire) {
if (err) {
return next(err);
}
bonfire = bonfire.pop();
if (bonfire === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the bonfires we have available. Good job!"
});
return res.redirect('../bonfires/meet-bonfire');
}
var nameString = bonfire.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../bonfires/' + nameString);
});
};
exports.returnIndividualBonfire = function(req, res, next) {
var dashedName = req.params.bonfireName;
var dashedName = req.params.bonfireName;
bonfireName = dashedName.replace(/\-/g, ' ');
var bonfireName = dashedName.replace(/\-/g, ' ');
Bonfire.find({"name" : new RegExp(bonfireName, 'i')}, function(err, bonfire) {
if (err) {
return next(err);
}
Bonfire.find({'name': new RegExp(bonfireName, 'i')}, function(err, bonfire) {
if (err) {
next(err);
}
if (bonfire.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a bonfire with that name. Please double check the name."
});
if (bonfire.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a bonfire with that name. Please double check the name."
});
return res.redirect('/bonfires');
}
return res.redirect('/bonfires');
}
bonfire = bonfire.pop();
var dashedNameFull = bonfire.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../bonfires/' + dashedNameFull);
}
res.render('bonfire/show', {
completedWith: null,
title: bonfire.name,
dashedName: dashedName,
name: bonfire.name,
difficulty: Math.floor(+bonfire.difficulty),
brief: bonfire.description[0],
details: bonfire.description.slice(1),
tests: bonfire.tests,
challengeSeed: bonfire.challengeSeed,
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
bonfires: bonfire,
bonfireHash: bonfire._id,
MDNkeys: bonfire.MDNlinks,
MDNlinks: getMDNlinks(bonfire.MDNlinks)
});
bonfire = bonfire.pop();
var dashedNameFull = bonfire.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../bonfires/' + dashedNameFull);
}
res.render('bonfire/show', {
completedWith: null,
title: bonfire.name,
dashedName: dashedName,
name: bonfire.name,
difficulty: Math.floor(+bonfire.difficulty),
brief: bonfire.description[0],
details: bonfire.description.slice(1),
tests: bonfire.tests,
challengeSeed: bonfire.challengeSeed,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
bonfires: bonfire,
bonfireHash: bonfire._id,
MDNkeys: bonfire.MDNlinks,
MDNlinks: getMDNlinks(bonfire.MDNlinks)
});
});
};
/**
* Bonfire generator
* Bonfire Generator
* @param req Request Object
* @param res Response Object
* @returns void
*/
exports.returnGenerator = function(req, res) {
res.render('bonfire/generator', {
title: null,
name: null,
difficulty: null,
brief: null,
details: null,
tests: null,
challengeSeed: null,
bonfireHash: randomString()
});
res.render('bonfire/generator', {
title: null,
name: null,
difficulty: null,
brief: null,
details: null,
tests: null,
challengeSeed: null,
bonfireHash: randomString()
});
};
/**
@ -140,83 +150,84 @@ exports.returnGenerator = function(req, res) {
*/
function randomString() {
var chars = "0123456789abcdef";
var string_length = 23;
var randomstring = 'a';
for (var i=0; i<string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return randomstring;
};
var chars = '0123456789abcdef';
var string_length = 23;
var randomstring = 'a';
for (var i = 0; i < string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}
/**
* Helper function to populate the MDN links array.
*/
*/
function getMDNlinks(links) {
// takes in an array of links, which are strings
var populatedLinks = [];
// takes in an array of links, which are strings
var populatedLinks = [];
// for each key value, push the corresponding link from the MDNlinks object into a new array
links.forEach(function(value, index) {
populatedLinks.push(MDNlinks[value]);
});
// for each key value, push the corresponding link from the MDNlinks object into a new array
links.forEach(function(value, index) {
populatedLinks.push(MDNlinks[value]);
});
return populatedLinks;
return populatedLinks;
};
}
/**
*
*/
exports.testBonfire = function(req, res) {
var bonfireName = req.body.name,
bonfireTests = req.body.tests,
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireChallengeSeed = req.body.challengeSeed;
var bonfireName = req.body.name,
bonfireTests = req.body.tests,
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', '');
res.render('bonfire/show', {
completedWith: null,
title: bonfireName,
name: bonfireName,
difficulty: +bonfireDifficulty,
brief: bonfireDescription[0],
details: bonfireDescription.slice(1),
tests: bonfireTests,
challengeSeed: bonfireChallengeSeed,
cc: req.user ? req.user.bonfiresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
bonfires: [],
bonfireHash: "test"
});
res.render('bonfire/show', {
completedWith: null,
title: bonfireName,
name: bonfireName,
difficulty: +bonfireDifficulty,
brief: bonfireDescription[0],
details: bonfireDescription.slice(1),
tests: bonfireTests,
challengeSeed: bonfireChallengeSeed,
cc: req.user ? req.user.bonfiresHash : undefined,
progressTimestamps: req.user ? req.user.progressTimestamps : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
bonfires: [],
bonfireHash: 'test'
});
};
function getRidOfEmpties(elem) {
if (elem.length > 0) {
return elem;
}
};
if (elem.length > 0) {
return elem;
}
}
exports.publicGenerator = function(req, res) {
res.render('bonfire/public-generator');
res.render('bonfire/public-generator');
};
exports.generateChallenge = function(req, res) {
var bonfireName = req.body.name,
bonfireTests = req.body.tests,
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireChallengeSeed = req.body.challengeSeed;
var bonfireName = req.body.name,
bonfireTests = req.body.tests,
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);
@ -224,95 +235,100 @@ exports.generateChallenge = function(req, res) {
bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', '');
var response = {
_id: randomString(),
name: bonfireName,
difficulty: bonfireDifficulty,
description: bonfireDescription,
challengeSeed: bonfireChallengeSeed,
tests: bonfireTests
};
res.send(response);
var response = {
_id: randomString(),
name: bonfireName,
difficulty: bonfireDifficulty,
description: bonfireDescription,
challengeSeed: bonfireChallengeSeed,
tests: bonfireTests
};
res.send(response);
};
exports.completedBonfire = function (req, res, next) {
var isCompletedWith = req.body.bonfireInfo.completedWith || undefined;
var isCompletedDate = Math.round(+new Date() / 1000);
var bonfireHash = req.body.bonfireInfo.bonfireHash;
var isSolution = req.body.bonfireInfo.solution;
if (isCompletedWith) {
var paired = User.find({"profile.username": isCompletedWith.toLowerCase()}).limit(1);
paired.exec(function (err, pairedWith) {
if (err) {
return next(err);
} else {
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.points++;
req.user.uncompletedBonfires.splice(index, 1)
}
pairedWith = pairedWith.pop();
index = pairedWith.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
pairedWith.points++;
pairedWith.uncompletedBonfires.splice(index, 1);
}
pairedWith.completedBonfires.push({
_id: bonfireHash,
completedWith: req.user._id,
completedDate: isCompletedDate,
solution: isSolution
});
req.user.completedBonfires.push({
_id: bonfireHash,
completedWith: pairedWith._id,
completedDate: isCompletedDate,
solution: isSolution
})
req.user.save(function (err, user) {
if (err) {
return next(err);
}
pairedWith.save(function (err, paired) {
if (err) {
return next(err);
}
if (user && paired) {
res.send(true);
}
})
});
}
})
} else {
req.user.completedBonfires.push({
_id: bonfireHash,
completedWith: null,
completedDate: isCompletedDate,
solution: isSolution
});
var isCompletedWith = req.body.bonfireInfo.completedWith || '';
var isCompletedDate = Math.round(+new Date());
var bonfireHash = req.body.bonfireInfo.bonfireHash;
var isSolution = req.body.bonfireInfo.solution;
var bonfireName = req.body.bonfireInfo.bonfireName;
if (isCompletedWith) {
var paired = User.find({'profile.username': isCompletedWith
.toLowerCase()}).limit(1);
paired.exec(function (err, pairedWith) {
if (err) {
return next(err);
} else {
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.points++;
req.user.uncompletedBonfires.splice(index, 1)
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1);
}
pairedWith = pairedWith.pop();
index = pairedWith.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
pairedWith.progressTimestamps.push(Date.now() || 0);
pairedWith.uncompletedBonfires.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
debug('Saving user');
res.send(true)
}
pairedWith.completedBonfires.push({
_id: bonfireHash,
name: bonfireName,
completedWith: req.user._id,
completedDate: isCompletedDate,
solution: isSolution
});
req.user.completedBonfires.push({
_id: bonfireHash,
name: bonfireName,
completedWith: pairedWith._id,
completedDate: isCompletedDate,
solution: isSolution
});
req.user.save(function (err, user) {
if (err) {
return next(err);
}
pairedWith.save(function (err, paired) {
if (err) {
return next(err);
}
if (user && paired) {
res.send(true);
}
});
});
}
});
} else {
req.user.completedBonfires.push({
_id: bonfireHash,
name: bonfireName,
completedWith: null,
completedDate: isCompletedDate,
solution: isSolution
});
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
debug('Saving user');
res.send(true);
}
});
}
};

View File

@ -0,0 +1,65 @@
var async = require('async'),
User = require('../models/User'),
Bonfire = require('./../models/Bonfire'),
Story = require('./../models/Story'),
Nonprofit = require('./../models/Nonprofit'),
Comment = require('./../models/Comment'),
Courseware = require('./../models/Courseware'),
resources = require('./resources'),
steps = resources.steps,
secrets = require('./../config/secrets'),
bonfires = require('../seed_data/bonfires.json'),
nonprofits = require('../seed_data/nonprofits.json'),
coursewares = require('../seed_data/coursewares.json'),
moment = require('moment'),
https = require('https'),
debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'),
R = require('ramda');
module.exports = {
challengeMap: function challengeMap(req, res) {
var completedBonfires = [];
var completedList = [];
if (req.user) {
completedBonfires = req.user.completedBonfires.map(function (elem) {
return elem._id;
});
}
if (req.user) {
completedList = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
}
var noDuplicateBonfires = R.uniq(completedBonfires);
var noDuplicatedCoursewares = R.uniq(completedList);
bonfireList = resources.allBonfireNames();
completedBonfireList = noDuplicateBonfires;
coursewareList = resources.allCoursewareNames();
completedCoursewareList = noDuplicatedCoursewares;
waypoints = coursewareList.filter(function(challenge) {
if (challenge.challengeType === 2) { return challenge }
});
ziplines = coursewareList.filter(function(challenge) {
if (challenge.challengeType === 3) { return challenge }
});
basejumps = coursewareList.filter(function(challenge) {
if (challenge.challengeType === 4) { return challenge }
});
res.render('challengeMap/show', {
title: "A map of all Free Code Camp's Challenges",
bonfires: bonfireList,
waypoints: waypoints,
ziplines: ziplines,
basejumps: basejumps,
completedBonfireList: completedBonfireList,
completedCoursewareList: completedCoursewareList
});
}
};

View File

@ -2,236 +2,380 @@ var _ = require('lodash'),
debug = require('debug')('freecc:cntr:courseware'),
Courseware = require('./../models/Courseware'),
User = require('./../models/User'),
resources = require('./resources');
resources = require('./resources'),
R = require('ramda'),
moment = require('moment');
/**
* Courseware controller
*/
exports.coursewareNames = function(req, res) {
res.render('coursewares/showList', {
coursewareList: resources.allCoursewareNames()
exports.showAllCoursewares = function(req, res) {
var completedList = [];
if(req.user) {
completedList = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
}
var noDuplicatedCoursewares = R.uniq(completedList);
var data = {};
data.coursewareList = resources.allCoursewareNames();
data.completedList = noDuplicatedCoursewares;
res.send(data);
};
exports.returnNextCourseware = function(req, res, next) {
if (!req.user) {
return res.redirect('../coursewares/start-our-challenges');
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
var completed = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
req.user.uncompletedCoursewares = resources.allCoursewareIds()
.filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
var completed = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
});
req.user.save();
req.user.uncompletedCoursewares = resources.allCoursewareIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
});
req.user.save(function(err) {
if (err) {
return next(err);
}
var uncompletedCoursewares = req.user.uncompletedCoursewares;
var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares[0]});
displayedCoursewares.exec(function(err, courseware) {
if (err) {
return next(err);
}
courseware = courseware.pop();
if (courseware === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the courses we have available. Good job!"
})
return res.redirect('../coursewares/start-our-challenges');
}
nameString = courseware.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../coursewares/' + nameString);
});
});
var uncompletedCoursewares = req.user.uncompletedCoursewares.shift();
var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares});
displayedCoursewares.exec(function(err, courseware) {
if (err) {
return next(err);
}
courseware = courseware.pop();
if (courseware === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the courses we have " +
"available. Good job!"
});
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
nameString = courseware.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../challenges/' + nameString);
});
};
exports.returnIndividualCourseware = function(req, res, next) {
var dashedName = req.params.coursewareName;
var dashedName = req.params.coursewareName;
coursewareName = dashedName.replace(/\-/g, ' ');
var coursewareName = dashedName.replace(/\-/g, ' ');
Courseware.find({"name" : new RegExp(coursewareName, 'i')}, function(err, courseware) {
if (err) {
return next(err);
}
// Handle not found
if (courseware.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a challenge with that name. Please double check the name."
Courseware.find({'name': new RegExp(coursewareName, 'i')},
function(err, courseware) {
if (err) {
next(err);
}
// Handle not found
if (courseware.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a challenge with that name. " +
"Please double check the name."
});
return res.redirect('/challenges');
}
courseware = courseware.pop();
// Redirect to full name if the user only entered a partial
var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../challenges/' + dashedNameFull);
}
var challengeType = {
0: function() {
res.render('coursewares/showHTML', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment(),
challengeType: courseware.challengeType
});
return res.redirect('/coursewares')
},
1: function() {
res.render('coursewares/showJS', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: courseware.challengeType
});
},
2: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: courseware.challengeType
});
},
3: function() {
res.render('coursewares/showZiplineOrBasejump', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: courseware.challengeType
});
},
4: function() {
res.render('coursewares/showZiplineOrBasejump', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: courseware.challengeType
});
}
courseware = courseware.pop();
};
// Redirect to full name if the user only entered a partial
var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../coursewares/' + dashedNameFull);
}
return challengeType[courseware.challengeType]();
var challengeType = {
0 : function() {
res.render('coursewares/showHTML', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
},
1 : function() {
res.render('coursewares/showJS', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
},
2: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
}
};
return challengeType[courseware.challengeType]();
});
});
};
exports.testCourseware = function(req, res) {
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
res.render('courseware/show', {
completedWith: null,
title: coursewareName,
name: coursewareName,
difficulty: +coursewareDifficulty,
brief: coursewareDescription[0],
details: coursewareDescription.slice(1),
tests: coursewareTests,
challengeSeed: coursewareChallengeSeed,
challengeEntryPoint: coursewareEntryPoint,
cc: req.user ? req.user.coursewaresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewares: [],
coursewareHash: "test"
});
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
res.render('courseware/show', {
completedWith: null,
title: coursewareName,
name: coursewareName,
difficulty: +coursewareDifficulty,
brief: coursewareDescription[0],
details: coursewareDescription.slice(1),
tests: coursewareTests,
challengeSeed: coursewareChallengeSeed,
challengeEntryPoint: coursewareEntryPoint,
cc: req.user ? req.user.coursewaresHash : undefined,
progressTimestamps: req.user ? req.user.progressTimestamps : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewares: [],
coursewareHash: 'test'
});
};
function getRidOfEmpties(elem) {
if (elem.length > 0) {
return elem;
}
};
if (elem.length > 0) {
return elem;
}
}
exports.publicGenerator = function(req, res) {
res.render('courseware/public-generator');
res.render('courseware/public-generator');
};
exports.generateChallenge = function(req, res) {
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
var response = {
_id: randomString(),
name: coursewareName,
difficulty: coursewareDifficulty,
description: coursewareDescription,
challengeEntryPoint: coursewareEntryPoint,
challengeSeed: coursewareChallengeSeed,
tests: coursewareTests
};
res.send(response);
var response = {
_id: randomString(),
name: coursewareName,
difficulty: coursewareDifficulty,
description: coursewareDescription,
challengeEntryPoint: coursewareEntryPoint,
challengeSeed: coursewareChallengeSeed,
tests: coursewareTests
};
res.send(response);
};
exports.completedCourseware = function (req, res, next) {
var isCompletedDate = Math.round(+new Date() / 1000);
var coursewareHash = req.body.coursewareInfo.coursewareHash;
var isCompletedDate = Math.round(+new Date());
var coursewareHash = req.body.coursewareInfo.coursewareHash;
req.user.completedCoursewares.push({
_id: coursewareHash,
completedDate: isCompletedDate,
name: req.body.coursewareInfo.coursewareName,
solution: null,
githubLink: null,
verified: true
});
var index = req.user.completedCoursewares.indexOf(coursewareHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
res.sendStatus(200);
}
});
};
exports.completedZiplineOrBasejump = function (req, res, next) {
debug('Inside controller for completed zipline or basejump with data %s',
req.body.coursewareInfo);
var isCompletedWith = req.body.coursewareInfo.completedWith || false;
var isCompletedDate = Math.round(+new Date());
var coursewareHash = req.body.coursewareInfo.coursewareHash;
var solutionLink = req.body.coursewareInfo.publicURL;
var githubLink = req.body.coursewareInfo.challengeType === '4'
? req.body.coursewareInfo.githubURL : true;
if (!solutionLink || !githubLink) {
req.flash('errors', {
msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
'your work.'
});
return res.sendStatus(403);
}
if (isCompletedWith) {
var paired = User.find({'profile.username': isCompletedWith.toLowerCase()}).limit(1);
paired.exec(function (err, pairedWith) {
if (err) {
return next(err);
} else {
var index = req.user.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
pairedWith = pairedWith.pop();
req.user.completedCoursewares.push({
_id: coursewareHash,
name: req.body.coursewareInfo.coursewareName,
completedWith: pairedWith._id,
completedDate: isCompletedDate,
solution: solutionLink,
githubLink: githubLink,
verified: false
});
req.user.save(function (err, user) {
if (err) {
return next(err);
}
debug('this is the user object returned %s,' +
' this is the req.user._id %s, ' +
'this is the pairedWith._id %s', user, req.user._id, pairedWith._id);
debug(req.user._id.toString() === pairedWith._id.toString());
if (req.user._id.toString() === pairedWith._id.toString()) {
return res.sendStatus(200);
}
index = pairedWith.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
pairedWith.progressTimestamps.push(Date.now() || 0);
pairedWith.uncompletedCoursewares.splice(index, 1);
}
pairedWith.completedCoursewares.push({
_id: coursewareHash,
name: req.body.coursewareInfo.coursewareName,
completedWith: req.user._id,
completedDate: isCompletedDate,
solution: solutionLink,
githubLink: githubLink,
verified: false
});
pairedWith.save(function (err, paired) {
if (err) {
return next(err);
}
if (user && paired) {
return res.sendStatus(200);
}
});
});
}
});
} else {
req.user.completedCoursewares.push({
_id: coursewareHash,
completedDate: isCompletedDate
_id: coursewareHash,
name: req.body.coursewareInfo.coursewareName,
completedWith: null,
completedDate: isCompletedDate,
solution: solutionLink,
githubLink: githubLink,
verified: false
});
var index = req.user.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
req.user.points++;
req.user.uncompletedCoursewares.splice(index, 1)
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
res.send(true)
}
if (err) {
return next(err);
}
if (user) {
return res.sendStatus(200);
}
});
}
};

99
controllers/fieldGuide.js Normal file
View File

@ -0,0 +1,99 @@
var _ = require('lodash'),
debug = require('debug')('freecc:cntr:fieldGuide'),
FieldGuide = require('./../models/FieldGuide'),
resources = require('./resources'),
R = require('ramda');
exports.returnIndividualFieldGuide = function(req, res, next) {
var dashedName = req.params.fieldGuideName;
var fieldGuideName = dashedName.replace(/\-/g, ' ');
FieldGuide.find({'name': new RegExp(fieldGuideName, 'i')}, function(err, fieldGuide) {
if (err) {
next(err);
}
if (fieldGuide.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a field guide entry with that name. Please double check the name."
});
return res.redirect('/field-guide');
}
fieldGuide = fieldGuide.pop();
var dashedNameFull = fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
if (dashedNameFull != dashedName) {
return res.redirect('../field-guide/' + dashedNameFull);
}
res.render('field-guide/show', {
title: fieldGuide.name,
fieldGuideId: fieldGuide._id,
description: fieldGuide.description.join('')
});
});
};
exports.showAllFieldGuides = function(req, res) {
var data = {};
data.fieldGuideList = resources.allFieldGuideNames();
data.fieldGuideIds = resources.allFieldGuideIds();
data.completedFieldGuides = req.user.completedFieldGuides;
res.send(data);
};
exports.returnNextFieldGuide = function(req, res, next) {
if (!req.user) {
return res.redirect('../field-guide/how-do-i-use-this-guide?');
}
var completed = req.user.completedFieldGuides;
req.user.uncompletedFieldGuides = resources.allFieldGuideIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
});
req.user.save();
var uncompletedFieldGuides = req.user.uncompletedFieldGuides;
var displayedFieldGuides = FieldGuide.find({'_id': uncompletedFieldGuides[0]});
displayedFieldGuides.exec(function(err, fieldGuide) {
if (err) {
return next(err);
}
fieldGuide = fieldGuide.pop();
if (fieldGuide === undefined) {
req.flash('success', {
msg: "You've read all our current Field Guide entries. You can contribute to our Field Guide <a href='https://github.com/FreeCodeCamp/freecodecamp/blob/nonprofit-show/seed_data/fieldGuides.json'>here</a>."
});
return res.redirect('../field-guide/how-do-i-use-this-guide?');
}
var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../field-guide/' + nameString);
});
};
exports.completedFieldGuide = function (req, res, next) {
debug('params in completedFieldGuide', req.params);
var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
req.user.completedFieldGuides.push(fieldGuideId);
var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedFieldGuides.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
res.send(true);
}
});
};

188
controllers/nonprofits.js Normal file
View File

@ -0,0 +1,188 @@
var async = require('async'),
Nonprofit = require('./../models/Nonprofit'),
resources = require('./resources'),
secrets = require('./../config/secrets'),
moment = require('moment'),
debug = require('debug')('freecc:cntr:nonprofits'),
R = require('ramda');
exports.nonprofitsHome = function(req, res) {
res.render('nonprofits/home', {
title: 'A guide to our Nonprofit Projects'
});
};
exports.nonprofitsDirectory = function(req, res) {
Nonprofit.find({estimatedHours: { $gt: 0 } }, function(err, nonprofits) {
if (err) {
next(err);
}
res.render('nonprofits/directory', {
title: 'Nonprofits we help',
nonprofits: nonprofits
});
});
};
exports.areYouWithARegisteredNonprofit = function(req, res) {
res.render('nonprofits/are-you-with-a-registered-nonprofit', {
title: 'Are you with a with a registered nonprofit',
step: 1
});
};
exports.areTherePeopleThatAreAlreadyBenefitingFromYourServices = function(req, res) {
res.render('nonprofits/are-there-people-that-are-already-benefiting-from-your-services', {
title: 'Are there people already benefiting from your services',
step: 2
});
};
exports.okWithJavaScript = function(req, res) {
res.render('nonprofits/ok-with-javascript', {
title: 'Are you OK with us using JavaScript',
step: 3
});
};
exports.inExchangeWeAsk = function(req, res) {
res.render('nonprofits/in-exchange-we-ask', {
title: 'In exchange we ask that you ...',
step: 4
});
};
exports.howCanFreeCodeCampHelpYou = function(req, res) {
res.render('nonprofits/how-can-free-code-camp-help-you', {
title: 'Are you with a with a registered nonprofit',
step: 5
});
};
exports.whatDoesYourNonprofitDo = function(req, res) {
res.render('nonprofits/what-does-your-nonprofit-do', {
existingParams: req.params,
title: 'What does your nonprofit do?',
step: 6
});
};
exports.linkUsToYourWebsite = function(req, res) {
res.render('nonprofits/link-us-to-your-website', {
title: 'Link us to your website',
step: 7
});
};
exports.tellUsYourEmail = function(req, res) {
res.render('nonprofits/tell-us-your-email', {
title: 'Tell us your email',
step: 8
});
};
exports.tellUsYourName = function(req, res) {
res.render('nonprofits/tell-us-your-name', {
title: 'Tell us your name',
step: 9
});
};
exports.yourNonprofitProjectApplicationHasBeenSubmitted = function(req, res) {
res.render('nonprofits/your-nonprofit-project-application-has-been-submitted', {
title: 'Your Nonprofit Project application has been submitted!',
step: 10,
getBackDay: moment().weekday(5).format('dddd')
});
};
exports.otherSolutions = function(req, res) {
res.render('nonprofits/other-solutions', {
title: 'Here are some other possible solutions for you'
});
};
exports.returnIndividualNonprofit = function(req, res, next) {
var dashedName = req.params.nonprofitName;
var nonprofitName = dashedName.replace(/\-/g, ' ');
Nonprofit.find({'name': new RegExp(nonprofitName, 'i')}, function(err, nonprofit) {
if (err) {
next(err);
}
if (nonprofit.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a nonprofit with that name. Please double check the name."
});
return res.redirect('/nonprofits');
}
nonprofit = nonprofit.pop();
var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../nonprofit/' + dashedNameFull);
}
var buttonActive = false;
if (req.user) {
if (req.user.uncompletedBonfires.length === 0) {
if (req.user.completedCoursewares.length > 63) {
var hasShownInterest = nonprofit.interestedCampers.filter(function ( obj ) {
return obj.username === req.user.profile.username;
});
console.log(hasShownInterest);
if (hasShownInterest.length === 0) {
buttonActive = true;
}
}
}
}
res.render('nonprofits/show', {
dashedName: dashedNameFull,
title: nonprofit.name,
logoUrl: nonprofit.logoUrl,
estimatedHours: nonprofit.estimatedHours,
projectDescription: nonprofit.projectDescription,
approvedOther: nonprofit.approvedDeliverables.indexOf('other') > -1,
approvedWebsite: nonprofit.approvedDeliverables.indexOf('website') > -1,
approvedDonor: nonprofit.approvedDeliverables.indexOf('donor') > -1,
approvedInventory: nonprofit.approvedDeliverables.indexOf('inventory') > -1,
approvedVolunteer: nonprofit.approvedDeliverables.indexOf('volunteer') > -1,
approvedForm: nonprofit.approvedDeliverables.indexOf('form') > -1,
approvedCommunity: nonprofit.approvedDeliverables.indexOf('community') > -1,
approvedELearning: nonprofit.approvedDeliverables.indexOf('eLearning') > -1,
websiteLink: nonprofit.websiteLink,
imageUrl: nonprofit.imageUrl,
whatDoesNonprofitDo: nonprofit.whatDoesNonprofitDo,
interestedCampers: nonprofit.interestedCampers,
assignedCampers: nonprofit.assignedCampers,
buttonActive: buttonActive,
currentStatus: nonprofit.currentStatus
});
});
};
exports.showAllNonprofits = function(req, res) {
var data = {};
data.nonprofitsList = resources.allNonprofitNames();
res.send(data);
};
exports.interestedInNonprofit = function(req, res) {
if (req.user) {
Nonprofit.findOne({name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i')}, function(err, nonprofit) {
if (err) { return next(err); }
nonprofit.interestedCampers.push({"username": req.user.profile.username,
"picture": req.user.profile.picture,
"timeOfInterest": Date.now()
});
nonprofit.save(function(err) {
if (err) { return done(err); }
req.flash('success', { msg: "Thanks for expressing interest in this nonprofit project! We've added you to this project as an interested camper!" });
res.redirect('back');
});
});
}
};

View File

@ -1,364 +1,367 @@
var async = require('async'),
User = require('../models/User'),
Challenge = require('./../models/Challenge'),
Bonfire = require('./../models/Bonfire'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
resources = require('./resources.json'),
steps = resources.steps,
secrets = require('./../config/secrets'),
bonfires = require('../seed_data/bonfires.json'),
coursewares = require('../seed_data/coursewares.json'),
moment = require('moment'),
https = require('https'),
debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'),
R = require('ramda');
User = require('../models/User'),
Challenge = require('./../models/Challenge'),
Bonfire = require('./../models/Bonfire'),
Story = require('./../models/Story'),
FieldGuide = require('./../models/FieldGuide'),
Nonprofit = require('./../models/Nonprofit'),
Comment = require('./../models/Comment'),
resources = require('./resources.json'),
steps = resources.steps,
secrets = require('./../config/secrets'),
bonfires = require('../seed_data/bonfires.json'),
nonprofits = require('../seed_data/nonprofits.json'),
coursewares = require('../seed_data/coursewares.json'),
fieldGuides = require('../seed_data/field-guides.json'),
moment = require('moment'),
https = require('https'),
debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'),
R = require('ramda');
/**
* GET /
* Resources.
*/
module.exports = {
privacy: function privacy(req, res) {
res.render('resources/privacy', {
title: 'Privacy'
});
},
Array.zip = function(left, right, combinerFunction) {
var counter,
results = [];
sitemap: function sitemap(req, res, next) {
var appUrl = 'http://www.freecodecamp.com';
var now = moment(new Date()).format('YYYY-MM-DD');
for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
results.push(combinerFunction(left[counter], right[counter]));
}
User.find({'profile.username': {'$ne': '' }}, function(err, users) {
if (err) {
debug('User err: ', err);
return next(err);
}
Challenge.find({}, function (err, challenges) {
if (err) {
debug('User err: ', err);
return next(err);
}
Bonfire.find({}, function (err, bonfires) {
if (err) {
debug('User err: ', err);
return next(err);
}
Story.find({}, function (err, stories) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', {
appUrl: appUrl,
now: now,
users: users,
challenges: challenges,
bonfires: bonfires,
stories: stories
});
});
});
});
});
},
deployAWebsite: function deployAWebsite(req, res) {
res.render('resources/deploy-a-website', {
title: 'Deploy a Dynamic Website in 7 Minutes'
});
},
chat: function chat(req, res) {
if (req.user && req.user.sentSlackInvite) {
res.redirect('https://freecode.slack.com/messages/general/');
} else {
res.render('resources/chat', {
title: "Join our chat room"
});
}
},
nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) {
res.render('resources/nonprofit-project-instructions', {
title: 'Nonprofit Project Instructions'
});
},
gmailShortcuts: function gmailShortcuts(req, res) {
res.render('resources/gmail-shortcuts', {
title: 'These Gmail Shortcuts will save you Hours'
});
},
guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) {
res.render('resources/guide-to-our-nonprofit-projects', {
title: 'A guide to our Nonprofit Projects'
});
},
controlShortcuts: function controlShortcuts(req, res) {
res.render('resources/control-shortcuts', {
title: 'These Control Shortcuts will save you Hours'
});
},
chromebook: function chromebook(req, res) {
res.render('resources/chromebook', {
title: 'Win a Chromebook'
});
},
styleguide: function styleguide(req, res) {
res.render('resources/styleguide', {
title: 'A Styleguide for Contributing to our Bonfires'
});
},
jqueryExercises: function jqueryExercises(req, res) {
res.render('resources/jquery-exercises', {
title: 'jQuery Exercises'
});
},
livePairProgramming: function(req, res) {
res.render('resources/live-pair-programming', {
title: 'Live Pair Programming'
});
},
installScreenHero: function(req, res) {
res.render('resources/install-screenhero', {
title: 'Install ScreenHero'
});
},
javaScriptInYourInbox: function(req, res) {
res.render('resources/javascript-in-your-inbox', {
title: 'JavaScript in your Inbox'
});
},
nodeSchoolChallenges: function(req, res) {
res.render('resources/nodeschool-challenges', {
title: 'NodeSchool Challenges'
});
},
githubCalls: function(req, res) {
var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 };
request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) {
pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github";
request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) {
issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub";
res.send({"issues": issues, "pulls" : pulls});
});
});
},
trelloCalls: function(req, res, next) {
request('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(err, status, trello) {
if (err) { return next(err); }
trello = (status && status.statusCode == 200) ? (JSON.parse(trello)) : "Can't connect to to Trello";
res.end(JSON.stringify(trello));
});
},
bloggerCalls: function(req, res, next) {
request('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (err, status, blog) {
if (err) { return next(err); }
blog = (status && status.statusCode == 200) ? JSON.parse(blog) : "Can't connect to Blogger";
res.end(JSON.stringify(blog));
});
},
about: function(req, res, next) {
if (req.user) {
if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") {
req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png";
req.user.save(function(err) {
if (err) return next(err);
});
}
}
var date1 = new Date("10/15/2014");
var date2 = new Date();
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
var announcements = resources.announcements;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
User.count({}, function (err, c3) {
if (err) {
debug('User err: ', err);
return next(err);
}
User.count({'points': {'$gt': 53}}, function (err, all) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.render('resources/learn-to-code', {
title: 'About Free Code Camp and Our Team of Volunteers',
daysRunning: daysRunning,
c3: numberWithCommas(c3),
all: all,
announcements: announcements
});
});
});
},
randomPhrase: function() {
var phrases = resources.phrases;
return phrases[Math.floor(Math.random() * phrases.length)];
},
randomVerb: function() {
var verbs = resources.verbs;
return verbs[Math.floor(Math.random() * verbs.length)];
},
randomCompliment: function() {
var compliments = resources.compliments;
return compliments[Math.floor(Math.random() * compliments.length)];
},
allBonfireIds: function() {
return bonfires.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allBonfireNames: function() {
return bonfires.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem.name;
});
},
allCoursewareIds: function() {
return coursewares.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allCoursewareNames: function() {
return coursewares.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem.name;
});
},
whichEnvironment: function() {
return process.env.NODE_ENV;
},
getURLTitle: function(url, callback) {
debug('got url in meta scraping function', url);
(function () {
var result = {title: '', image: '', url: '', description: ''};
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var $ = cheerio.load(body);
var metaDescription = $("meta[name='description']");
var metaImage = $("meta[property='og:image']");
var urlImage = metaImage.attr('content') ? metaImage.attr('content') : '';
var description = metaDescription.attr('content') ? metaDescription.attr('content') : '';
result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ...";
result.image = urlImage;
result.description = description;
callback(null, result);
} else {
callback('failed');
}
});
})();
},
updateUserStoryPictures: function(userId, picture, username, cb) {
var counter = 0,
foundStories,
foundComments;
Story.find({'author.userId': userId}, function(err, stories) {
if (err) {
return cb(err);
}
foundStories = stories;
counter++;
saveStoriesAndComments();
});
Comment.find({'author.userId': userId}, function(err, comments) {
if (err) {
return cb(err);
}
foundComments = comments;
counter++;
saveStoriesAndComments();
});
function saveStoriesAndComments() {
if (counter !== 2) {
return;
}
var tasks = [];
R.forEach(function(comment) {
comment.author.picture = picture;
comment.author.username = username;
comment.markModified('author');
tasks.push(function(cb) {
comment.save(cb);
});
}, foundComments);
R.forEach(function(story) {
story.author.picture = picture;
story.author.username = username;
story.markModified('author');
tasks.push(function(cb) {
story.save(cb);
});
}, foundStories);
async.parallel(tasks, function(err) {
if (err) { return cb(err); }
cb();
});
}
}
return results;
};
module.exports = {
privacy: function privacy(req, res) {
res.render('resources/privacy', {
title: 'Privacy'
});
},
sitemap: function sitemap(req, res, next) {
var appUrl = 'http://www.freecodecamp.com';
var now = moment(new Date()).format('YYYY-MM-DD');
User.find({'profile.username': {'$ne': '' }}, function(err, users) {
if (err) {
debug('User err: ', err);
return next(err);
}
Courseware.find({}, function (err, challenges) {
if (err) {
debug('User err: ', err);
return next(err);
}
Bonfire.find({}, function (err, bonfires) {
if (err) {
debug('User err: ', err);
return next(err);
}
Story.find({}, function (err, stories) {
if (err) {
debug('User err: ', err);
return next(err);
}
Nonprofit.find({}, function (err, nonprofits) {
if (err) {
debug('User err: ', err);
return next(err);
}
FieldGuide.find({}, function (err, fieldGuides) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', {
appUrl: appUrl,
now: now,
users: users,
challenges: challenges,
bonfires: bonfires,
stories: stories,
nonprofits: nonprofits,
fieldGuides: fieldGuides
});
});
});
});
});
});
});
},
chat: function chat(req, res) {
if (req.user && req.user.progressTimestamps.length > 5) {
res.redirect('http://freecode.slack.com');
} else {
res.render('resources/chat', {
title: "Watch us code live on Twitch.tv"
});
}
},
twitch: function twitch(req, res) {
res.render('resources/twitch', {
title: "Enter Free Code Camp's Chat Rooms"
});
},
githubCalls: function(req, res) {
var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 };
request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) {
pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github";
request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) {
issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub";
res.send({"issues": issues, "pulls" : pulls});
});
});
},
trelloCalls: function(req, res, next) {
request('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(err, status, trello) {
if (err) { return next(err); }
trello = (status && status.statusCode === 200) ? (JSON.parse(trello)) : "Can't connect to to Trello";
res.end(JSON.stringify(trello));
});
},
bloggerCalls: function(req, res, next) {
request('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (err, status, blog) {
if (err) { return next(err); }
blog = (status && status.statusCode === 200) ? JSON.parse(blog) : "Can't connect to Blogger";
res.end(JSON.stringify(blog));
});
},
about: function(req, res, next) {
if (req.user) {
if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") {
req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png";
req.user.save();
}
}
var date1 = new Date("10/15/2014");
var date2 = new Date();
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
var announcements = resources.announcements;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
User.count({}, function (err, c3) {
if (err) {
debug('User err: ', err);
return next(err);
}
User.count({'points': {'$gt': 53}}, function (err, all) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.render('resources/learn-to-code', {
title: 'About Free Code Camp',
daysRunning: daysRunning,
c3: numberWithCommas(c3),
announcements: announcements
});
});
});
},
randomPhrase: function() {
var phrases = resources.phrases;
return phrases[Math.floor(Math.random() * phrases.length)];
},
randomVerb: function() {
var verbs = resources.verbs;
return verbs[Math.floor(Math.random() * verbs.length)];
},
randomCompliment: function() {
var compliments = resources.compliments;
return compliments[Math.floor(Math.random() * compliments.length)];
},
allBonfireIds: function() {
return bonfires.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allFieldGuideIds: function() {
return fieldGuides.map(function(elem) {
return {
_id: elem._id,
}
})
.map(function(elem) {
return elem._id;
});
},
allBonfireNames: function() {
return bonfires.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name : elem.name,
_id: elem._id
}
});
},
allFieldGuideNames: function() {
return fieldGuides.map(function(elem) {
return {
name: elem.name
}
})
},
allNonprofitNames: function() {
return nonprofits.map(function(elem) {
return {
name: elem.name
}
})
},
allCoursewareIds: function() {
return coursewares.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allCoursewareNames: function() {
return coursewares.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
challengeType: elem.challengeType,
_id: elem._id
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name: elem.name,
challengeType: elem.challengeType,
_id: elem._id
};
});
},
whichEnvironment: function() {
return process.env.NODE_ENV;
},
getURLTitle: function(url, callback) {
(function () {
var result = {title: '', image: '', url: '', description: ''};
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var $ = cheerio.load(body);
var metaDescription = $("meta[name='description']");
var metaImage = $("meta[property='og:image']");
var urlImage = metaImage.attr('content') ? metaImage.attr('content') : '';
var description = metaDescription.attr('content') ? metaDescription.attr('content') : '';
result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ...";
result.image = urlImage;
result.description = description;
callback(null, result);
} else {
callback('failed');
}
});
})();
},
updateUserStoryPictures: function(userId, picture, username, cb) {
var counter = 0,
foundStories,
foundComments;
Story.find({'author.userId': userId}, function(err, stories) {
if (err) {
return cb(err);
}
foundStories = stories;
counter++;
saveStoriesAndComments();
});
Comment.find({'author.userId': userId}, function(err, comments) {
if (err) {
return cb(err);
}
foundComments = comments;
counter++;
saveStoriesAndComments();
});
function saveStoriesAndComments() {
if (counter !== 2) {
return;
}
var tasks = [];
R.forEach(function(comment) {
comment.author.picture = picture;
comment.author.username = username;
comment.markModified('author');
tasks.push(function(cb) {
comment.save(cb);
});
}, foundComments);
R.forEach(function(story) {
story.author.picture = picture;
story.author.username = username;
story.markModified('author');
tasks.push(function(cb) {
story.save(cb);
});
}, foundStories);
async.parallel(tasks, function(err) {
if (err) { return cb(err); }
cb();
});
}
}
};

View File

@ -1,440 +1,486 @@
/* eslint-disable no-catch-shadow, no-unused-vars */
var R = require('ramda'),
debug = require('debug')('freecc:cntr:story'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
User = require('./../models/User'),
moment = require('../public/js/lib/moment/moment.js'),
resources = require('./resources'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
secrets = require('../config/secrets'),
sanitizeHtml = require('sanitize-html');
debug = require('debug')('freecc:cntr:story'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
User = require('./../models/User'),
moment = require('../public/js/lib/moment/moment.js'),
resources = require('./resources'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
secrets = require('../config/secrets'),
nodemailer = require('nodemailer'),
sanitizeHtml = require('sanitize-html');
function hotRank(timeValue, rank) {
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
* tMS = postedOnDate - foundationTime;
* Ranking...
* f(ts, 1, rank) = log(10)z + (ts)/45000;
*/
var hotness;
var z = Math.log(rank) / Math.log(10);
hotness = z + (timeValue / 115200000);
return hotness;
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
* tMS = postedOnDate - foundationTime;
* Ranking...
* f(ts, 1, rank) = log(10)z + (ts)/45000;
*/
var hotness;
var z = Math.log(rank) / Math.log(10);
hotness = z + (timeValue / 115200000);
return hotness;
}
exports.hotJSON = function(req, res) {
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
story.exec(function(err, stories) {
if (err) {
res.send(500);
return next(err);
}
exports.hotJSON = function(req, res, next) {
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
story.exec(function(err, stories) {
if (err) {
return next(err);
}
var foundationDate = 1413298800000;
var foundationDate = 1413298800000;
var sliceVal = stories.length >= 100 ? 100 : stories.length;
return res.json(stories.map(function(elem) {
return elem;
}).sort(function(a, b) {
return hotRank(b.timePosted - foundationDate, b.rank, b.headline) - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
}).slice(0, sliceVal));
var sliceVal = stories.length >= 100 ? 100 : stories.length;
return res.json(stories.map(function(elem) {
return elem;
}).sort(function(a, b) {
return hotRank(b.timePosted - foundationDate, b.rank, b.headline)
- hotRank(a.timePosted - foundationDate, a.rank, a.headline);
}).slice(0, sliceVal));
});
});
};
exports.recentJSON = function(req, res, next) {
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
story.exec(function(err, stories) {
if (err) {
res.status(500);
return next(err);
}
res.json(stories);
});
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
story.exec(function(err, stories) {
if (err) {
return next(err);
}
return res.json(stories);
});
};
exports.hot = function(req, res) {
res.render('stories/index', {
title: 'Hot stories currently trending on Camper News',
page: 'hot'
});
return res.render('stories/index', {
title: 'Hot stories currently trending on Camper News',
page: 'hot'
});
};
exports.submitNew = function(req, res) {
res.render('stories/index', {
title: 'Submit a new story to Camper News',
page: 'submit'
});
return res.render('stories/index', {
title: 'Submit a new story to Camper News',
page: 'submit'
});
};
exports.search = function(req, res) {
res.render('stories/index', {
title: 'Search the archives of Camper News',
page: 'search'
});
return res.render('stories/index', {
title: 'Search the archives of Camper News',
page: 'search'
});
};
exports.recent = function(req, res) {
res.render('stories/index', {
title: 'Recently submitted stories on Camper News',
page: 'recent'
});
return res.render('stories/index', {
title: 'Recently submitted stories on Camper News',
page: 'recent'
});
};
exports.preSubmit = function(req, res) {
var data = req.query;
var cleanData = sanitizeHtml(data.url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;;/g, '"');
if (data.url.replace(/&/g, '&amp;') !== cleanData) {
var data = req.query;
var cleanData = sanitizeHtml(data.url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;;/g, '"');
if (data.url.replace(/&/g, '&amp;') !== cleanData) {
req.flash('errors', {
msg: 'The data for this post is malformed'
});
return res.render('stories/index', {
page: 'stories/submit'
});
}
var title = data.title || '';
var image = data.image || '';
var description = data.description || '';
return res.render('stories/index', {
title: "Confirm your Camper News story submission",
page: 'storySubmission',
storyURL: data.url,
storyTitle: title,
storyImage: image,
storyMetaDescription: description
req.flash('errors', {
msg: 'The data for this post is malformed'
});
return res.render('stories/index', {
page: 'stories/submit'
});
}
var title = data.title || '';
var image = data.image || '';
var description = data.description || '';
return res.render('stories/index', {
title: 'Confirm your Camper News story submission',
page: 'storySubmission',
storyURL: data.url,
storyTitle: title,
storyImage: image,
storyMetaDescription: description
});
};
exports.returnIndividualStory = function(req, res, next) {
var dashedName = req.params.storyName;
var dashedName = req.params.storyName;
var storyName = dashedName.replace(/\-/g, ' ');
var storyName = dashedName.replace(/\-/g, ' ');
Story.findOne({'storyLink' : new RegExp(storyName, 'i')}, function(err, story) {
if (err) {
next(err);
}
Story.find({'storyLink': new RegExp(storyName, 'i')}, function(err, story) {
if (err) {
return next(err);
}
if (story == null) {
req.flash('errors', {
msg: "404: We couldn't find a story with that name. Please double check the name."
});
if (story.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a story with that name. Please double check the name."
});
return res.redirect('/stories/');
}
return res.redirect('/stories/');
}
var dashedNameFull = story.storyLink.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull !== dashedName) {
return res.redirect('../stories/' + dashedNameFull);
}
story = story.pop();
var dashedNameFull = story.storyLink.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull !== dashedName) {
return res.redirect('../stories/' + dashedNameFull);
}
var userVoted = false;
try {
var votedObj = story.upVotes.filter(function(a){
return a['upVotedByUsername'] === req.user['profile']['username'];
});
if (votedObj.length > 0){
userVoted = true;
}
} catch(err){
userVoted = false;
}
res.render('stories/index', {
title: story.headline,
link: story.link,
author: story.author,
description: story.description,
rank: story.upVotes.length,
upVotes: story.upVotes,
comments: story.comments,
id: story._id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
page: 'show',
storyMetaDescription: story.metaDescription,
hasUserVoted: userVoted
});
var userVoted = false;
try {
var votedObj = story.upVotes.filter(function(a) {
return a['upVotedByUsername'] === req.user['profile']['username'];
});
if (votedObj.length > 0) {
userVoted = true;
}
} catch(err) {
userVoted = false;
}
res.render('stories/index', {
title: story.headline,
link: story.link,
originalStoryLink: dashedName,
originalStoryAuthorEmail: story.author.email || "",
author: story.author,
description: story.description,
rank: story.upVotes.length,
upVotes: story.upVotes,
comments: story.comments,
id: story._id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
page: 'show',
storyMetaDescription: story.metaDescription,
hasUserVoted: userVoted
});
});
};
exports.getStories = function(req, res) {
MongoClient.connect(secrets.db, function(err, database) {
database.collection('stories').find({
"$text": {
"$search": req.body.data.searchValue
}
}, {
headline: 1,
timePosted: 1,
link: 1,
description: 1,
rank: 1,
upVotes: 1,
author: 1,
comments: 1,
image: 1,
storyLink: 1,
metaDescription: 1,
textScore: {
$meta: "textScore"
}
}, {
sort: {
textScore: {
$meta: "textScore"
}
}
}).toArray(function(err, items) {
if (items !== null && items.length !== 0) {
return res.json(items);
}
return res.status(404);
});
exports.getStories = function(req, res, next) {
MongoClient.connect(secrets.db, function(err, database) {
if (err) {
return next(err);
}
database.collection('stories').find({
'$text': {
'$search': req.body.data.searchValue
}
}, {
headline: 1,
timePosted: 1,
link: 1,
description: 1,
rank: 1,
upVotes: 1,
author: 1,
comments: 1,
image: 1,
storyLink: 1,
metaDescription: 1,
textScore: {
$meta: 'textScore'
}
}, {
sort: {
textScore: {
$meta: 'textScore'
}
}
}).toArray(function(err, items) {
if (err) {
return next(err);
}
if (items !== null && items.length !== 0) {
return res.json(items);
}
return res.sendStatus(404);
});
});
};
exports.upvote = function(req, res, next) {
var data = req.body.data;
Story.find({'_id': data.id}, function(err, story) {
if (err) {
res.status(500);
return next(err);
}
story = story.pop();
story.rank++;
story.upVotes.push(
{
upVotedBy: data.upVoter._id,
upVotedByUsername: data.upVoter.profile.username
}
);
story.markModified('rank');
story.save();
return res.send(story);
var data = req.body.data;
Story.find({'_id': data.id}, function(err, story) {
if (err) {
return next(err);
}
story = story.pop();
story.rank++;
story.upVotes.push(
{
upVotedBy: data.upVoter._id,
upVotedByUsername: data.upVoter.profile.username
}
);
story.markModified('rank');
story.save();
User.find({'_id': story.author.userId}, function(err, user) {
'use strict';
if (err) {
return next(err);
}
user = user.pop();
user.progressTimestamps.push(Date.now());
user.save();
});
return res.send(story);
});
};
exports.comments = function(req, res, next) {
var data = req.params.id;
Comment.find({'_id': data}, function(err, comment) {
var data = req.params.id;
Comment.find({'_id': data}, function(err, comment) {
if (err) {
return next(err);
}
comment = comment.pop();
return res.send(comment);
});
};
exports.newStory = function(req, res, next) {
if (!req.user) {
return next(new Error('Must be logged in'));
}
var url = req.body.data.url;
var cleanURL = sanitizeHtml(url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (cleanURL !== url) {
req.flash('errors', {
msg: "The URL you submitted doesn't appear valid"
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/submit'
});
}
if (url.search(/^https?:\/\//g) === -1) {
url = 'http://' + url;
}
Story.find({'link': url}, function(err, story) {
if (err) {
return next(err);
}
if (story.length) {
req.flash('errors', {
msg: "Someone's already posted that link. Here's the discussion."
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/' + story.pop().storyLink
});
}
resources.getURLTitle(url, processResponse);
});
function processResponse(err, story) {
if (err) {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: '',
storyImage: '',
storyMetaDescription: ''
});
} else {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: story.title,
storyImage: story.image,
storyMetaDescription: story.description
});
}
}
};
exports.storySubmission = function(req, res, next) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var storyLink = data.headline
.replace(/\'/g, '')
.replace(/\"/g, '')
.replace(/,/g, '')
.replace(/[^a-z0-9]/gi, ' ')
.replace(/\s+/g, ' ')
.toLowerCase();
var link = data.link;
if (link.search(/^https?:\/\//g) === -1) {
link = 'http://' + link;
}
var story = new Story({
headline: sanitizeHtml(data.headline, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
timePosted: Date.now(),
link: link,
description: sanitizeHtml(data.description, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
rank: 1,
upVotes: data.upVotes,
author: data.author,
comments: [],
image: data.image,
storyLink: storyLink,
metaDescription: data.storyMetaDescription,
originalStoryAuthorEmail: req.user.email
});
story.save(function(err) {
if (err) {
res.status(500);
return next(err);
return res.status(500);
}
comment = comment.pop();
return res.send(comment);
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
};
exports.newStory = function(req, res) {
if (!req.user) {
return res.status(500);
}
var url = req.body.data.url;
var cleanURL = sanitizeHtml(url, {
exports.commentSubmit = function(req, res, next) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (cleanURL !== url) {
req.flash('errors', {
msg: "The URL you submitted doesn't appear valid"
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/submit'
});
}
if (url.search(/^https?:\/\//g) === -1) {
url = 'http://' + url;
}
Story.find({'link': url}, function(err, story) {
if (err) {
return res.status(500);
}
if (story.length) {
req.flash('errors', {
msg: "Someone's already posted that link. Here's the discussion."
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/' + story.pop().storyLink
});
}
resources.getURLTitle(url, processResponse);
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
originalStoryLink: data.originalStoryLink,
originalStoryAuthorEmail: data.originalStoryAuthorEmail,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: true,
commentOn: Date.now()
});
function processResponse(err, story) {
if (err) {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: '',
storyImage: '',
storyMetaDescription: ''
});
} else {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: story.title,
storyImage: story.image,
storyMetaDescription: story.description
});
}
}
commentSave(comment, Story, res, next);
};
exports.storySubmission = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
exports.commentOnCommentSubmit = function(req, res, next) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
originalStoryLink: data.originalStoryLink,
originalStoryAuthorEmail: data.originalStoryAuthorEmail,
author: data.author,
comments: [],
topLevel: false,
commentOn: Date.now()
});
commentSave(comment, Comment, res, next);
};
function commentSave(comment, Context, res, next) {
comment.save(function(err, data) {
if (err) {
return next(err);
}
var storyLink = data.headline
.replace(/\'/g, '')
.replace(/\"/g, '')
.replace(/,/g, '')
.replace(/[^a-z0-9]/gi, ' ')
.replace(/\s+/g, ' ')
.toLowerCase();
Story.count({'storyLink': storyLink}, function(err, storyCount) {
try {
Context.find({'_id': comment.associatedPost}, function (err, associatedStory) {
if (err) {
return res.status(500);
return next(err);
}
// if duplicate storyLink add unique number
storyLink = (storyCount === 0) ? storyLink : storyLink + ' ' + storyCount;
var link = data.link;
if (link.search(/^https?:\/\//g) === -1) {
link = 'http://' + link;
}
var story = new Story({
headline: sanitizeHtml(data.headline, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
timePosted: Date.now(),
link: link,
description: sanitizeHtml(data.description, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
rank: 1,
upVotes: data.upVotes,
author: data.author,
comments: [],
image: data.image,
storyLink: storyLink,
metaDescription: data.storyMetaDescription
});
story.save(function(err) {
associatedStory = associatedStory.pop();
if (associatedStory) {
associatedStory.comments.push(data._id);
associatedStory.save(function (err) {
if (err) {
return res.status(500);
return next(err);
}
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
});
};
exports.commentSubmit = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: true,
commentOn: Date.now()
});
commentSave(comment, Story, res);
};
exports.commentOnCommentSubmit = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: false,
commentOn: Date.now()
});
commentSave(comment, Comment, res);
};
function commentSave(comment, Context, res) {
comment.save(function(err, data) {
if (err) {
return res.status(500);
res.send(true);
});
}
try {
Context.find({'_id': comment.associatedPost}, function (err, associatedStory) {
if (err) {
return res.status(500);
}
associatedStory = associatedStory.pop();
if (associatedStory) {
associatedStory.comments.push(data._id);
associatedStory.save(function (err) {
if (err) {
res.status(500);
}
res.send(true);
});
}
User.findOne({'profile.username': associatedStory.author.username}, function(err, recipient) {
if (err) {
return next(err);
}
var recipients = '';
if (data.originalStoryAuthorEmail && (data.originalStoryAuthorEmail !== recipient.email)) {
recipients = data.originalStoryAuthorEmail + ',' + recipient.email;
} else {
recipients = recipient.email;
}
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
user: secrets.mandrill.user,
pass: secrets.mandrill.password
}
});
} catch (e) {
// delete comment
return res.status(500);
}
});
var mailOptions = {
to: recipients,
from: 'Team@freecodecamp.com',
subject: associatedStory.author.username + " replied to your post on Camper News",
text: [
"Just a quick heads-up: " + associatedStory.author.username + " replied to you on Camper News.",
"You can keep this conversation going.",
"Just head back to the discussion here: http://freecodecamp.com/stories/" + comment.originalStoryLink,
'- the Free Code Camp Volunteer Team'
].join('\n')
};
transporter.sendMail(mailOptions, function (err) {
if (err) {
return err;
}
});
});
});
} catch (e) {
// delete comment
return next(err);
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@ var gulp = require('gulp'),
sync = require('browser-sync'),
reload = sync.reload,
inject = require('gulp-inject'),
reloadDelay = 1000;
reloadDelay = 1000,
eslint = require('gulp-eslint');
var paths = {
server: './app.js',
@ -54,11 +55,17 @@ gulp.task('sync', ['serve'], function() {
sync.init(null, {
proxy: 'http://localhost:3000',
logLeval: 'debug',
files: ['public/**/*'],
files: ['public/js/lib/*/*.{js, jsx}'],
port: 3001,
open: true,
reloadDelay: reloadDelay
});
});
gulp.task('lint', function() {
return gulp.src(['public/js/lib/**/*'])
.pipe(eslint())
.pipe(eslint.format());
});
gulp.task('default', ['serve', 'sync']);

View File

@ -1,11 +0,0 @@
var mongoose = require('mongoose');
var secrets = require('../config/secrets');
var bonfireCompletionSchema = new mongoose.Schema({
dateCompleted: Number,
completedWith: ObjectId,
bonfireHash: ObjectId,
solution: String
});
module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema);

View File

@ -6,6 +6,14 @@ var commentSchema = new mongoose.Schema({
type: String,
required: true
},
originalStoryLink: {
type: String,
default: ''
},
originalStoryAuthorEmail: {
type: String,
default: ''
},
body: {
type: String,
default: ''
@ -36,4 +44,4 @@ module.exports = mongoose.model('Comment', commentSchema);
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
*/
*/

View File

@ -16,7 +16,7 @@ var coursewareSchema = new mongoose.Schema({
tests: Array,
challengeSeed: Array,
completionMessage: String, // Congratulations! You've finished our HTML and CSS track!
challengeType: Number // 0 = html, 1 = javascript only, 2 = video
challengeType: Number // 0 = html, 1 = javascript only, 2 = video, 3 = zipline, 4 = basejump
});
module.exports = mongoose.model('Courseware', coursewareSchema);
module.exports = mongoose.model('Courseware', coursewareSchema);

15
models/FieldGuide.js Normal file
View File

@ -0,0 +1,15 @@
var mongoose = require('mongoose');
var secrets = require('../config/secrets');
var fieldGuideSchema = new mongoose.Schema({
name: {
type: String,
unique: false
},
description: {
type: Array,
unique: false
}
});
module.exports = mongoose.model('FieldGuide', fieldGuideSchema);

28
models/Nonprofit.js Normal file
View File

@ -0,0 +1,28 @@
var mongoose = require('mongoose');
var secrets = require('../config/secrets');
/**
*
* @type {exports.Schema}
*/
var Long = mongoose.Types.Long;
var nonprofitSchema = new mongoose.Schema({
name: String,
requestedDeliverables: Array,
whatDoesNonprofitDo: String,
websiteLink: String,
stakeholderName: String,
stakeholderEmail: String,
endUser: String,
approvedDeliverables: Array,
projectDescription: String,
logoUrl: String,
imageUrl: String,
estimatedHours: 0,
interestedCampers: [],
confirmedCampers: [],
currentStatus: String // "confirmed", "started", "completed", "aborted"
});
module.exports = mongoose.model('Nonprofit', nonprofitSchema);

View File

@ -23,6 +23,10 @@ var storySchema = new mongoose.Schema({
type: String,
unique: false
},
originalStoryAuthorEmail: {
type: String,
default: ''
},
rank: {
type: Number,
default: -Infinity
@ -46,4 +50,4 @@ var storySchema = new mongoose.Schema({
}
});
module.exports = mongoose.model('Story', storySchema);
module.exports = mongoose.model('Story', storySchema);

View File

@ -1,12 +1,13 @@
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var mongoose = require('mongoose');
require('mongoose-long')(mongoose);
var Long = mongoose.Types.Long;
var userSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
trim: true,
sparse: true
},
@ -17,257 +18,10 @@ var userSchema = new mongoose.Schema({
github: String,
linkedin: String,
tokens: Array,
points: {
type: Number,
default: 0
},
challengesCompleted: { type: Array, default: [] },
challengesHash: {
0: {
type: Number,
default: 0
},
1: {
type: Number,
default: 0
},
2: {
type: Number,
default: 0
},
3: {
type: Number,
default: 0
},
4: {
type: Number,
default: 0
},
5: {
type: Number,
default: 0
},
6: {
type: Number,
default: 0
},
7: {
type: Number,
default: 0
},
8: {
type: Number,
default: 0
},
9: {
type: Number,
default: 0
},
10: {
type: Number,
default: 0
},
11: {
type: Number,
default: 0
},
12: {
type: Number,
default: 0
},
13: {
type: Number,
default: 0
},
14: {
type: Number,
default: 0
},
15: {
type: Number,
default: 0
},
16: {
type: Number,
default: 0
},
17: {
type: Number,
default: 0
},
18: {
type: Number,
default: 0
},
19: {
type: Number,
default: 0
},
20: {
type: Number,
default: 0
},
21: {
type: Number,
default: 0
},
22: {
type: Number,
default: 0
},
23: {
type: Number,
default: 0
},
24: {
type: Number,
default: 0
},
25: {
type: Number,
default: 0
},
26: {
type: Number,
default: 0
},
27: {
type: Number,
default: 0
},
28: {
type: Number,
default: 0
},
29: {
type: Number,
default: 0
},
30: {
type: Number,
default: 0
},
31: {
type: Number,
default: 0
},
32: {
type: Number,
default: 0
},
33: {
type: Number,
default: 0
},
34: {
type: Number,
default: 0
},
35: {
type: Number,
default: 0
},
36: {
type: Number,
default: 0
},
37: {
type: Number,
default: 0
},
38: {
type: Number,
default: 0
},
39: {
type: Number,
default: 0
},
40: {
type: Number,
default: 0
},
41: {
type: Number,
default: 0
},
42: {
type: Number,
default: 0
},
43: {
type: Number,
default: 0
},
44: {
type: Number,
default: 0
},
45: {
type: Number,
default: 0
},
46: {
type: Number,
default: 0
},
47: {
type: Number,
default: 0
},
48: {
type: Number,
default: 0
},
49: {
type: Number,
default: 0
},
50: {
type: Number,
default: 0
},
51: {
type: Number,
default: 0
},
52: {
type: Number,
default: 0
},
53: {
type: Number,
default: 0
},
54: {
type: Number,
default: 0
},
55: {
type: Number,
default: 0
},
56: {
type: Number,
default: 0
},
57: {
type: Number,
default: 0
},
58: {
type: Number,
default: 0
},
59: {
type: Number,
default: 0
}
},
progressTimestamps: [],
profile: {
username: {
type: String,
unique: true,
sparse: true,
lowercase: true,
trim: true
@ -309,6 +63,7 @@ var userSchema = new mongoose.Schema({
default: ''
}
},
challengesHash: {},
portfolio: {
website1Link: {
type: String,
@ -351,9 +106,38 @@ var userSchema = new mongoose.Schema({
sentSlackInvite: false,
resetPasswordExpires: Date,
uncompletedBonfires: Array,
completedBonfires: Array,
completedBonfires: [
{
_id: String,
name: String,
completedWith: String,
completedDate: Long,
solution: String
}
],
uncompletedCoursewares: Array,
completedCoursewares: Array
completedCoursewares: [
{
completedDate: Long,
_id: String,
name: String,
completedWith: String,
solution: String,
githubLink: String,
verified: Boolean
}
],
completedFieldGuides: [],
uncompletedFieldGuides: [],
currentStreak: {
type: Number,
default: 0
},
longestStreak: {
type: Number,
default: 0
},
needsMigration: { type: Boolean, default: true }
});
/**
@ -387,23 +171,4 @@ userSchema.methods.comparePassword = function(candidatePassword, cb) {
});
};
/**
* Helper method for getting user's gravatar.
*/
userSchema.methods.gravatar = function(size) {
if (!size) { size = 200; }
if (!this.email) {
return 'https://gravatar.com/avatar/?s=' + size + '&d=retro';
}
var md5 = crypto
.createHash('md5')
.update(this.email)
.digest('hex');
return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro';
};
module.exports = mongoose.model('User', userSchema);

View File

@ -47,11 +47,10 @@
"method-override": "^2.3.0",
"moment": "^2.8.4",
"mongodb": "^1.4.33",
"mongoose": "^3.8.19",
"mongoose-text-search": "0.0.2",
"mongoose": "^4.0.1",
"mongoose-long": "0.0.2",
"morgan": "^1.5.0",
"newrelic": "^1.13.3",
"node": "0.0.0",
"nodemailer": "^1.3.0",
"passport": "^0.2.1",
"passport-facebook": "^1.0.3",
@ -75,8 +74,8 @@
"browser-sync": "^1.8.1",
"chai": "^1.10.0",
"gulp": "^3.8.8",
"gulp-eslint": "^0.9.0",
"gulp-inject": "^1.0.2",
"gulp-minify-css": "^0.5.1",
"gulp-nodemon": "^1.0.4",
"mocha": "^2.0.1",
"multiline": "^1.0.1",

View File

@ -16,7 +16,7 @@
@brand-primary: #215f1e;
@brand-success: #457E86;
@brand-info: #5bc0de;
@brand-info: #4A2B0F;
@brand-warning: #f0ad4e;
@brand-danger: #d9534f;

View File

@ -335,6 +335,10 @@ ul {
margin-bottom: -10px;
}
.nonprofit-landing {
font-size: 50px;
}
.big-text {
font-size: 63px;
}
@ -585,6 +589,17 @@ thead {
padding-left: 50px;
}
.all-list-header {
background-color: #4A2B0F;
color: #eee;
font-size: 36px;
text-align: center;
margin-bottom: -30px;
border-radius: 5px 5px 0px 0px;
padding-left: 50px;
}
.closing-x {
color: #eee;
font-size: 50px;
@ -593,16 +608,23 @@ thead {
.fcc-footer {
width: 100%;
height: 50px;
text-align: center;
background-color: #4a2b0f;
height: 40px;
padding: 12px;
bottom: 0;
left: 0;
position: absolute;
a {
font-size: 28px;
font-size: 20px;
color: #eee;
margin-left: 0px;
margin-right: 0px;
padding-left: 10px;
padding-right: 10px;
&:hover {
padding-top: 14px;
padding-bottom: 14px;
color: #4a2b0f;
background-color: #eee;
text-decoration: none;
@ -720,6 +742,11 @@ iframe.iphone {
}
}
.nonprofit-help-select-text-height {
font-size: 40px;
padding-top: 20px;
}
// To adjust right margin, negative values bring the image closer to the edge of the screen
.iphone-position {
position: absolute;
@ -816,6 +843,10 @@ iframe.iphone {
max-height: 110px;
}
.button-spacer {
padding: 3px 0 2px 0;
}
.spacer {
padding: 15px 0 15px 0;
}
@ -852,6 +883,39 @@ iframe.iphone {
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0);
}
#cal-heatmap {
width: 361px;
}
.cal-heatmap-container {
background-color: #EEEEEE;
}
.checkbox-table label {
margin-left: 10px;
}
.interested-camper-image {
height: 50px;
width: 50px;
padding: 5px;
}
.svg-challenge-map {
fill: #333;
height: 40px;
}
.alert a {
text-decoration: underline;
}
.step-text {
margin-left: -10px;
line-height: 120%;
padding-bottom: 10px;
}
//uncomment this to see the dimensions of all elements outlined in red
//* {
// border-color: red;

View File

@ -16,4 +16,4 @@
//= require lib/jquery-2.1.1.min
//= require lib/bootstrap.min
//= require lib/moment/moment
//= require main
//= require main

View File

@ -250,7 +250,7 @@ var runTests = function(err, data) {
};
function showCompletion() {
var time = Math.floor(Date.now() / 1000) - started;
var time = Math.floor(Date.now()) - started;
ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time +', Attempts: ' + attempts);
$('#complete-bonfire-dialog').modal('show');
$('#complete-bonfire-dialog').keydown(function(e) {

View File

@ -3,12 +3,11 @@
*/
var widgets = [];
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
var editor = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
lineNumbers: true,
mode: "text/html",
theme: 'monokai',
runnable: true,
//lint: true,
matchBrackets: true,
autoCloseBrackets: true,
scrollbarStyle: 'null',
@ -16,7 +15,6 @@ var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor")
gutters: ["CodeMirror-lint-markers"],
onKeyEvent: doLinting
});
var editor = myCodeMirror;
// Hijack tab key to insert two spaces instead
@ -36,10 +34,6 @@ editor.setOption("extraKeys", {
var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
cm.replaceSelection(spaces);
}
},
"Ctrl-Enter": function() {
bonfireExecute();
return false;
}
});
@ -117,7 +111,7 @@ var allSeeds = '';
});
})();
myCodeMirror.setValue(allSeeds);
editor.setValue(allSeeds);
function doLinting () {
editor.operation(function () {
@ -144,7 +138,7 @@ function doLinting () {
//$('#testSuite').empty();
function showCompletion() {
var time = Math.floor(Date.now() / 1000) - started;
var time = Math.floor(Date.now()) - started;
ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time);
$('#next-courseware-button').removeAttr('disabled');
$('#next-courseware-button').addClass('animated tada');

View File

@ -235,12 +235,12 @@ var runTests = function(err, data) {
};
function showCompletion() {
var time = Math.floor(Date.now() / 1000) - started;
var time = Math.floor(Date.now()) - started;
ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time +', Attempts: ' + attempts);
$('#complete-courseware-dialog').modal('show');
$('#complete-courseware-dialog').keydown(function(e) {
if (e.ctrlKey && e.keyCode == 13) {
$('.next-bonfire-button').click();
$('#next-courseware-button').click();
}
});
}

View File

@ -1,358 +1,437 @@
$(document).ready(function() {
var challengeName = typeof challengeName !== undefined ? challengeName : 'Untitled';
if (challengeName) {
ga('send', 'event', 'Challenge', 'load', challengeName);
}
var challengeName = typeof challengeName !== undefined ? challengeName : 'Untitled';
if (challengeName) {
ga('send', 'event', 'Challenge', 'load', challengeName);
}
// When introducing a new announcement, change the localStorage attribute
// and the HTML located in the footer
if (!localStorage || !localStorage.nodeSchoolAnnouncement) {
$('#announcementModal').modal('show');
localStorage.fccShowAnnouncement = "true";
}
// When introducing a new announcement, change the localStorage attribute
// and the HTML located in the footer
if (!localStorage || !localStorage.nodeSchoolAnnouncement) {
$('#announcementModal').modal('show');
localStorage.fccShowAnnouncement = "true";
}
var CSRF_HEADER = 'X-CSRF-Token';
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('.start-challenge').on('click', function() {
$(this).parent().remove();
$('.challenge-content')
.removeClass('hidden-element')
.addClass('animated fadeInDown');
});
$('.completed-challenge').on('click', function() {
$('#complete-challenge-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
l = location.pathname.split('/');
cn = l[l.length - 1];
$.ajax({
type: 'POST',
data: {challengeNumber: cn},
url: '/completed-challenge/'
});
}
});
function completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash) {
$('#complete-bonfire-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
$.post(
'/completed-bonfire',
{
bonfireInfo: {
completedWith : didCompleteWith,
solution: bonfireSolution,
bonfireHash: thisBonfireHash
}
},
function(res) {
if (res) {
window.location.href = '/bonfires'
}
});
}
}
$('.next-bonfire-button').on('click', function() {
var bonfireSolution = myCodeMirror.getValue();
var thisBonfireHash = passedBonfireHash || null;
var didCompleteWith = $('#completed-with').val() || null;
completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash);
});
$('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show');
});
$('#complete-courseware-dialog').on('keypress', function(e) {
if (e.which === 13 || e === 13) {
$('#next-courseware-button').click();
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
});
};
$('#complete-bonfire-dialog').on('hidden.bs.modal', function() {
editor.focus();
});
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
editor.focus();
});
$('#next-courseware-button').on('click', function() {
if ($('.signup-btn-nav').length < 1) {
$.post(
'/completed-courseware',
{
coursewareInfo: {
coursewareHash: passedCoursewareHash
}
},
function(res) {
if (res) {
window.location.href = '/coursewares'
}
})
}
})
$('.start-challenge').on('click', function() {
$(this).parent().remove();
$('.challenge-content')
.removeClass('hidden-element')
.addClass('animated fadeInDown');
});
$('.all-challenges').on('click', function() {
$('#all-challenges-dialog').modal('show');
});
$('.challenge-list-checkbox').on('change', function() {
if ($(this).is(":checked")) {
$(this).parent().parent().children('.step-text').addClass('strikethrough text-primary');
}
if (!$(this).is(":checked")) {
$(this).parent().parent().children('.step-text').removeClass('strikethrough text-primary');
}
});
$('.all-bonfires').on('click', function() {
$('#all-bonfires-dialog').modal('show');
});
function completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash, bonfireName) {
$('#complete-bonfire-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
$.post(
'/completed-bonfire',
{
bonfireInfo: {
completedWith: didCompleteWith,
solution: bonfireSolution,
bonfireHash: thisBonfireHash,
bonfireName: bonfireName
}
},
function(res) {
if (res) {
window.location.href = '/bonfires'
}
});
}
}
$('.next-challenge-button').on('click', function() {
l = location.pathname.split('/');
window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
});
function completedFieldGuide(fieldGuideId) {
if ($('.signup-btn-nav').length < 1) {
$.post(
'/completed-field-guide',
{
fieldGuideInfo: {
fieldGuideId: fieldGuideId
}
},
function(res) {
if (res) {
window.location.href = '/field-guide'
}
});
}
}
$('.next-bonfire-button').on('click', function() {
var bonfireSolution = myCodeMirror.getValue();
var thisBonfireHash = passedBonfireHash || null;
var bonfireName = $('#bonfire-name').text();
var didCompleteWith = $('#completed-with').val() || null;
completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash, bonfireName);
// Bonfire instructions functions
$('#more-info').on('click', function() {
ga('send', 'event', 'Challenge', 'more-info', challengeName);
$('#brief-instructions').hide();
$('#long-instructions').show().removeClass('hide');
});
});
$('#less-info').on('click', function() {
$('#brief-instructions').show();
$('#long-instructions').hide();
});
$('.next-field-guide-button').on('click', function() {
console.log('click');
var fieldGuideId = $('#fieldGuideId').text();
completedFieldGuide(fieldGuideId);
});
var upvoteHandler = function () {
var _id = storyId;
$('#upvote').unbind('click');
var alreadyUpvoted = false;
for (var i = 0; i < upVotes.length; i++) {
if (upVotes[i].upVotedBy === user._id) {
alreadyUpvoted = true;
break;
$("img").error(function () {
$(this).unbind("error").attr("src", "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png");
});
$('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show');
});
$('#completed-zipline-or-basejump').on('click', function() {
$('#complete-zipline-or-basejump-dialog').modal('show');
});
$('#complete-bonfire-dialog').on('hidden.bs.modal', function() {
editor.focus();
});
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
editor.focus();
});
$('#next-courseware-button').on('click', function() {
console.log(passedCoursewareHash);
if ($('.signup-btn-nav').length < 1) {
switch (challengeType) {
case 0:
case 1:
case 2:
$.post(
'/completed-courseware/',
{
coursewareInfo: {
coursewareHash: passedCoursewareHash,
coursewareName: passedCoursewareName
}
}).success(
function(res) {
if (res) {
window.location.href = '/challenges';
}
}
}
if (!alreadyUpvoted) {
$.post('/stories/upvote',
{
data: {
id: _id,
upVoter: user
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#upvote').bind('click', upvoteHandler);
})
.done(function (data, textStatus, xhr) {
$('#upvote').text('Upvoted!').addClass('disabled');
$('#storyRank').text(data.rank + " points");
});
}
};
$('#upvote').on('click', upvoteHandler);
var storySubmitButtonHandler = function storySubmitButtonHandler() {
var link = $('#story-url').val();
var headline = $('#story-title').val();
var description = $('#description-box').val();
var userDataForUpvote = {
upVotedBy: user._id,
upVotedByUsername: user.profile.username
};
$('#story-submit').unbind('click');
$.post('/stories/',
);
break;
case 3:
var didCompleteWith = $('#completed-with').val() || null;
var publicURL = $('#public-url').val() || null;
$.post(
'/completed-zipline-or-basejump/',
{
data: {
link: link,
headline: headline,
timePosted: Date.now(),
description: description,
storyMetaDescription: storyMetaDescription,
rank: 1,
upVotes: [userDataForUpvote],
author: {
picture: user.profile.picture,
userId: user._id,
username: user.profile.username
},
comments: [],
image: storyImage
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#story-submit').bind('click', storySubmitButtonHandler);
})
.done(function (data, textStatus, xhr) {
window.location = '/stories/' + JSON.parse(data).storyLink;
coursewareInfo: {
coursewareHash: passedCoursewareHash,
coursewareName: passedCoursewareName,
completedWith: didCompleteWith,
publicURL: publicURL,
challengeType: challengeType
}
}).success(
function() {
window.location.href = '/challenges';
}).fail(
function() {
window.location.href = '/challenges';
});
};
$('#story-submit').on('click', storySubmitButtonHandler);
var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
$('comment-button').unbind('click');
var data = $('#comment-box').val();
$('#comment-button').attr('disabled', 'disabled');
$.post('/stories/comment/',
break;
case 4:
var didCompleteWith = $('#completed-with').val() || null;
var publicURL = $('#public-url').val() || null;
var githubURL = $('#github-url').val() || null;
$.post(
'/completed-zipline-or-basejump/',
{
data: {
associatedPost: storyId,
body: data,
author: {
picture: user.profile.picture,
userId: user._id,
username: user.profile.username
}
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#comment-button').attr('disabled', false);
})
.done(function (data, textStatus, xhr) {
window.location.reload();
coursewareInfo: {
coursewareHash: passedCoursewareHash,
coursewareName: passedCoursewareName,
completedWith: didCompleteWith,
publicURL: publicURL,
githubURL: githubURL,
challengeType: challengeType,
verified: false
}
}).success(function() {
window.location.href = '/challenges';
}).fail(function() {
window.location.replace(window.location.href);
});
break;
default:
break;
}
}
});
$('.all-challenges').on('click', function() {
$('#show-all-dialog').modal('show');
});
$('#showAllButton').on('click', function() {
$('#show-all-dialog').modal('show');
});
$('.next-challenge-button').on('click', function() {
l = location.pathname.split('/');
window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
});
// Bonfire instructions functions
$('#more-info').on('click', function() {
ga('send', 'event', 'Challenge', 'more-info', challengeName);
$('#brief-instructions').hide();
$('#long-instructions').show().removeClass('hide');
});
$('#less-info').on('click', function() {
$('#brief-instructions').show();
$('#long-instructions').hide();
});
var upvoteHandler = function () {
var _id = storyId;
$('#upvote').unbind('click');
var alreadyUpvoted = false;
for (var i = 0; i < upVotes.length; i++) {
if (upVotes[i].upVotedBy === user._id) {
alreadyUpvoted = true;
break;
}
}
if (!alreadyUpvoted) {
$.post('/stories/upvote',
{
data: {
id: _id,
upVoter: user
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#upvote').bind('click', upvoteHandler);
})
.done(function (data, textStatus, xhr) {
$('#upvote').text('Upvoted!').addClass('disabled');
$('#storyRank').text(data.rank + " points");
});
}
};
$('#upvote').on('click', upvoteHandler);
var storySubmitButtonHandler = function storySubmitButtonHandler() {
var link = $('#story-url').val();
var headline = $('#story-title').val();
var description = $('#description-box').val();
var userDataForUpvote = {
upVotedBy: user._id,
upVotedByUsername: user.profile.username
};
$('#story-submit').unbind('click');
$.post('/stories/',
{
data: {
link: link,
headline: headline,
timePosted: Date.now(),
description: description,
storyMetaDescription: storyMetaDescription,
originalStoryAuthorEmail: user.email,
rank: 1,
upVotes: [userDataForUpvote],
author: {
picture: user.profile.picture,
email: user.email,
userId: user._id,
username: user.profile.username
},
comments: [],
image: storyImage
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#story-submit').bind('click', storySubmitButtonHandler);
})
.done(function (data, textStatus, xhr) {
window.location = '/stories/' + JSON.parse(data).storyLink;
});
$('#comment-button').on('click', commentSubmitButtonHandler);
};
$('#story-submit').on('click', storySubmitButtonHandler);
var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
$('comment-button').unbind('click');
var data = $('#comment-box').val();
$('#comment-button').attr('disabled', 'disabled');
$.post('/stories/comment/',
{
data: {
associatedPost: storyId,
originalStoryLink: originalStoryLink,
originalStoryAuthorEmail: originalStoryAuthorEmail,
body: data,
author: {
picture: user.profile.picture,
userId: user._id,
username: user.profile.username,
email: user.email
}
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#comment-button').attr('disabled', false);
})
.done(function (data, textStatus, xhr) {
window.location.reload();
});
};
$('#comment-button').on('click', commentSubmitButtonHandler);
});
var profileValidation = angular.module('profileValidation',['ui.bootstrap']);
profileValidation.controller('profileValidationController', ['$scope', '$http',
function($scope, $http) {
$http.get('/account/api').success(function(data) {
$scope.user = data.user;
$scope.user.profile.username = $scope.user.profile.username ? $scope.user.profile.username.toLowerCase() : undefined;
$scope.storedUsername = data.user.profile.username;
$scope.storedEmail = data.user.email;
$scope.user.email = $scope.user.email ? $scope.user.email.toLowerCase() : undefined;
$scope.user.profile.twitterHandle = $scope.user.profile.twitterHandle ? $scope.user.profile.twitterHandle.toLowerCase() : undefined;
$scope.asyncComplete = true;
});
}
function($scope, $http) {
$http.get('/account/api').success(function(data) {
$scope.user = data.user;
$scope.user.profile.username = $scope.user.profile.username ? $scope.user.profile.username.toLowerCase() : undefined;
$scope.storedUsername = data.user.profile.username;
$scope.storedEmail = data.user.email;
$scope.user.email = $scope.user.email ? $scope.user.email.toLowerCase() : undefined;
$scope.user.profile.twitterHandle = $scope.user.profile.twitterHandle ? $scope.user.profile.twitterHandle.toLowerCase() : undefined;
$scope.asyncComplete = true;
});
}
]);
profileValidation.controller('pairedWithController', ['$scope',
function($scope) {
$scope.existingUser = null;
}
function($scope) {
$scope.existingUser = null;
}
]);
profileValidation.controller('emailSignUpController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.controller('emailSignInController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.controller('URLSubmitController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.controller('nonprofitFormController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.controller('doneWithFirst100HoursFormController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.controller('submitStoryController', ['$scope',
function($scope) {
function($scope) {
}
}
]);
profileValidation.directive('uniqueUsername',['$http',function($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
ngModel.$setValidity('unique', true);
if (element.val()) {
$http.get("/api/checkUniqueUsername/" + element.val()).success(function (data) {
if (element.val() == scope.storedUsername) {
ngModel.$setValidity('unique', true);
} else if (data) {
ngModel.$setValidity('unique', false);
}
});
}
});
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
ngModel.$setValidity('unique', true);
if (element.val()) {
$http.get("/api/checkUniqueUsername/" + element.val()).success(function (data) {
if (element.val() == scope.storedUsername) {
ngModel.$setValidity('unique', true);
} else if (data) {
ngModel.$setValidity('unique', false);
}
});
}
});
}
}
}]);
profileValidation.directive('existingUsername', ['$http', function($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
if (element.val().length > 0) {
ngModel.$setValidity('exists', false);
} else {
element.removeClass('ng-dirty');
ngModel.$setPristine();
}
if (element.val()) {
$http
.get("/api/checkExistingUsername/" + element.val())
.success(function (data) {
ngModel.$setValidity('exists', data);
});
}
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
if (element.val().length > 0) {
ngModel.$setValidity('exists', false);
} else {
element.removeClass('ng-dirty');
ngModel.$setPristine();
}
if (element.val()) {
$http
.get("/api/checkExistingUsername/" + element.val())
.success(function (data) {
ngModel.$setValidity('exists', data);
});
}
});
}
}
}]);
profileValidation.directive('uniqueEmail', ['$http', function($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function getUnique (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
ngModel.$setValidity('unique', true);
if (element.val()) {
$http.get("/api/checkUniqueEmail/" + encodeURIComponent(element.val())).success(function (data) {
if (element.val() == scope.storedEmail) {
ngModel.$setValidity('unique', true);
} else if (data) {
ngModel.$setValidity('unique', false);
}
});
};
});
}
return {
restrict: 'A',
require: 'ngModel',
link: function getUnique (scope, element, attrs, ngModel) {
element.bind("keyup", function (event) {
ngModel.$setValidity('unique', true);
if (element.val()) {
$http.get("/api/checkUniqueEmail/" + encodeURIComponent(element.val())).success(function (data) {
if (element.val() == scope.storedEmail) {
ngModel.$setValidity('unique', true);
} else if (data) {
ngModel.$setValidity('unique', false);
}
});
};
});
}
}
}]);

View File

@ -17,6 +17,10 @@ var links =
"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
"parseInt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt",
// ========= PROPERTIES/MISC
"String.length" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length",
@ -63,7 +67,7 @@ var links =
"Array.sort()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort",
"Array.splice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice",
"Array.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString",
// ======== MATH METHODS
"Math.max()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max",
"Math.min()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min",

View File

@ -50,7 +50,7 @@
"Factorials are often represented with the shorthand notation n!",
"For example: 5! = 1 * 2 * 3 * 4 * 5 = 120f"
],
"challengeSeed": "function factorialize(num) {\n return num;\r\n}\n\nfactorialize(5);",
"challengeSeed": "function factorialize(num) {\n return num;\r\n}\n\nfactorialize(5);",
"MDNlinks" : ["Arithmetic Operators"]
},
{
@ -253,7 +253,7 @@
"assert.deepEqual(where([{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }], { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }], 'should return with multiples');"
],
"MDNlinks" : ["Global Object", "Object.hasOwnProperty()", "Object.keys()"]
},
},
{
"_id":"a39963a4c10bc8b4d4f06d7e",
"name":"Seek and Destroy",
@ -318,6 +318,80 @@
"assert.deepEqual(diff([], ['snuffleupagus', 'cookie monster', 'elmo']), ['snuffleupagus', 'cookie monster', 'elmo'], 'empty array');"
]
},
{
"_id": "a7f4d8f2483413a6ce226cac",
"name": "Roman Numeral Converter",
"tests": [
"expect(convert(12)).to.equal(\"XII\");",
"expect(convert(5)).to.equal(\"V\");",
"expect(convert(9)).to.equal(\"IX\");",
"expect(convert(29)).to.equal(\"XXIX\");",
"expect(convert(16)).to.equal(\"XVI\");"
],
"difficulty": "2.02",
"description": [
"Convert the number be a roman numeral.",
"All <a href=\"http://www.mathsisfun.com/roman-numerals.html\">roman numerals</a> answers should be provided in upper-case."
],
"challengeSeed": "function convert(num) {\n return num;\r\n}\n\nconvert(36);",
"MDNlinks" : ["Array.splice()", "Array.indexOf()", "Array.join()"]
},
{
"_id": "a0b5010f579e69b815e7c5d6",
"name": "Search and Replace",
"tests": [
"expect(replace(\"Let us go to the store\", \"store\", \"mall\")).to.equal(\"Let us go to the mall\");",
"expect(replace(\"He is Sleeping on the couch\", \"Sleeping\", \"sitting\")).to.equal(\"He is Sitting on the couch\");",
"expect(replace(\"This has a spellngi error\", \"spellngi\", \"spelling\")).to.equal(\"This has a spelling error\");",
"expect(replace(\"His name is Tom\", \"Tom\", \"john\")).to.equal(\"His name is John\");",
"expect(replace(\"Let us get back to more Coding\", \"Coding\", \"bonfires\")).to.equal(\"Let us get back to more Bonfires\");"
],
"difficulty": "2.03",
"description": [
"Perform a search and replace on the sentence using the arguments provided and return the new sentence.",
"First argument is the sentence the perform the search and replace on.",
"Second argument is the word that you will be replacing (before).",
"Third argument is what you will be replacing the second argument with (after)."
],
"challengeSeed": "function replace(str, before, after) {\n return str;\r\n}\n\nreplace(\"A quick brown fox jumped over the lazy dog\", \"jumped\", \"leaped\");",
"MDNlinks" : ["Array.splice()", "String.replace()", "Array.join()"]
},
{
"_id": "aa7697ea2477d1316795783b",
"name": "Pig Latin",
"tests": [
"expect(translate(\"california\")).to.equal(\"aliforniacay\");",
"expect(translate(\"paragraphs\")).to.equal(\"aragraphspay\");",
"expect(translate(\"glove\")).to.equal(\"oveglay\");",
"expect(translate(\"algorithm\")).to.equal(\"algorithmway\");",
"expect(translate(\"eight\")).to.equal(\"eightway\");"
],
"difficulty": "2.04",
"description": [
"Translate the provided string to pig latin.",
"<a href=\"http://en.wikipedia.org/wiki/Pig_Latin\">Pig Latin</a> takes the first consonant (or consonant cluster) of an English word, moves it to the end of the word and suffixes an \"ay\".",
"If a word begins with a vowel you just add \"way\" to the end."
],
"challengeSeed": "function translate(str) {\n return str;\r\n}\n\ntranslate(\"consonant\");",
"MDNlinks" : ["Array.indexOf()", "Array.push()", "Array.join()", "String.substr()", "String.split()"]
},
{
"_id": "afd15382cdfb22c9efe8b7de",
"name": "DNA Pairing",
"tests": [
"assert.deepEqual(pair(\"ATCGA\"),[['A','T'],['T','A'],['C','G'],['G','C'],['A','T']], 'should return the dna pair');",
"assert.deepEqual(pair(\"TTGAG\"),[['T','A'],['T','A'],['G','C'],['A','T'],['G','C']], 'should return the dna pair');",
"assert.deepEqual(pair(\"CTCTA\"),[['C','G'],['T','A'],['C','G'],['T','A'],['A','T']], 'should return the dna pair');"
],
"difficulty": "2.05",
"description": [
"The DNA strand is missing the pairing element. Match each character with the missing element and return the results as a 2d array.",
"<a href=\"http://en.wikipedia.org/wiki/Base_pair\">Base pairs</a> are a pair of AT and CG. Match the missing element to the provided character.",
"Return the provided character as the first element in each array."
],
"challengeSeed": "function pair(str) {\n return str;\r\n}\n\npair(\"GCG\");",
"MDNlinks" : ["Array.push()", "String.split()"]
},
{
"_id": "af7588ade1100bde429baf20",
"name" : "Missing letters",
@ -495,7 +569,7 @@
"assert.deepEqual(steamroller([[['a']], [['b']]]), ['a', 'b'], 'should flatten nested arrays');",
"assert.deepEqual(steamroller([1, [2], [3, [[4]]]]), [1, 2, 3, 4], 'should flatten nested arrays');",
"assert.deepEqual(steamroller([1, [], [3, [[4]]]]), [1, 3, 4], 'should work with empty arrays');"
],
],
"MDNlinks" : ["Array.isArray()"]
},
{
@ -519,12 +593,12 @@
"difficulty": "2.16",
"description": [
"Return the sum of all indices of elements of 'arr' that can be paired with one other element to form a sum that equals the value in the second argument 'arg'. If multiple sums are possible, return the smallest sum. Once an element has been used, it cannot be reused to pair with another.",
"For example, pairwise([1, 4, 2, 3, 0, 5], 5) should return 15 because every element can be paired with another element to equal 5.",
"For example, pairwise([1, 4, 2, 3, 0, 5], 7) should return 11 because 4, 2, 3 and 5 can be paired with each other to equal 7.",
"pairwise([1, 3, 2, 4], 4) would only equal 1, because only the first two elements can be paired to equal 4, and the first element has an index of 0!"
],
"challengeSeed": "function pairwise(arr, arg) {\n return arg;\n}\n\npairwise([1,4,2,3,0,5], 5);",
"challengeSeed": "function pairwise(arr, arg) {\n return arg;\n}\n\npairwise([1,4,2,3,0,5], 7);",
"tests": [
"expect(pairwise([1, 4, 2, 3, 0, 5], 5)).to.equal(15);",
"expect(pairwise([1, 4, 2, 3, 0, 5], 7)).to.equal(11);",
"expect(pairwise([1, 3, 2, 4], 4)).to.equal(1);",
"expect(pairwise([1,1,1], 2)).to.equal(1);",
"expect(pairwise([0, 0, 0, 0, 1, 1], 1)).to.equal(10);",
@ -714,5 +788,27 @@
"expect(permAlone('abfdefa')).to.equal(2640);",
"expect(permAlone('zzzzzzzz')).to.equal(0);"
]
},
{
"_id": "a19f0fbe1872186acd434d5a",
"name": "Friendly Date Ranges",
"difficulty": "4.05",
"description": [
"Implement a way of converting two dates into a more friendly date range that could be presented to a user.",
"It must not show any redundant information in the date range.",
"For example, if the year and month are the same then only the day range should be displayed.",
"Secondly, if the starting year is the current year, and the ending year can be inferred by the reader, the year should be omitted.",
"Input date is formatted as YYYY-MM-DD"
],
"challengeSeed": "function friendly(str) {\n return str;\n}\n\nfriendly(['2015-07-01', '2015-07-04']);",
"tests": [
"assert.deepEqual(friendly(['2015-07-01', '2015-07-04']), ['July 1st','4th'], 'ending month should be omitted since it is already mentioned');",
"assert.deepEqual(friendly(['2015-12-01', '2016-02-03']), ['December 1st','February 3rd'], 'one month apart can be inferred it is the next year');",
"assert.deepEqual(friendly(['2015-12-01', '2017-02-03']), ['December 1st, 2015','February 3rd, 2017']);",
"assert.deepEqual(friendly(['2016-03-01', '2016-05-05']), ['March 1st','May 5th, 2016']);",
"assert.deepEqual(friendly(['2017-01-01', '2017-01-01']), ['January 1st, 2017'], 'since we do not duplicate only return once');",
"assert.deepEqual(friendly(['2022-09-05', '2023-09-04']), ['September 5th, 2022','September 4th, 2023']);"
],
"MDNlinks" : ["String.split()", "String.substr()", "parseInt()"]
}
]

View File

@ -1,201 +0,0 @@
/*
"aceca143b92049a4392a859e"
"ce9394f67d413734758e27e4"
"1369953ef6f03098cb60e2f7"
"fa229986db0716662e63163a"
"0a6207dfc9ac12f223b4e686"
"2e538b339cbcb7437e61d71f"
"21de1104b8ea7c6a382442d9"
"0aa3497514cb1d8a0ed04933"
"3a45068415f5a20a5d4a2039"
"6a73ec9b86f0e742f6c6ea76"
"06dc21940748badcdb29561e"
"0f2341ef36740b5c2b9d830c"
"dc4cc7ce1fd0418859b10e0b"
"1fcda9b5e0bf502c8b770374"
"482879c42188cde1f9ad3d8f"
"3d680bd03fd291028af57f4f"
"6b444fe5aa97359238bd3c0d"
"72d6ad274e72ffcd399e05cb"
"e864a710647bb6883c9ec617"
"024dcd8cc0178de7daa4f59f"
"ded24fb937a3fd60dc624677"
"9ad4c3b2aebe7282e973e5df"
"d16c66ecc656c82899a2d427"
"ee3e4bf39b840609dd88f5af"
"9a10d27eb6ab6f5030bdbcd9"
"b4cca5ecdeca1eebc0dc4183"
"ccdf1ef407864df62b03331e"
"1baa0329f02e41a5517b11a6"
"b5b33e5ecf6c02a5780ac419"
"84b690f53cd6cbbbf4a361f5"
"4e3b1b9d41ff11bdf3722ffe"
"1fd45e64c43cd640abcf8a76"
"c8bd32cdd943c88fb8a74934"
"9e5440269d09b71f07002333"
"5080016b8b68f40f7476c341"
"f762d45cc870d9f18c6b7024"
"cf21358ab1245c7033504b42"
"41eff7cb0f2fd8ec9e0cbf68"
"a344c63de6a00feea5c541e0"
"7ab3c18c0815d7b1f02e8768"
"a6f36e4387e2e1e25af4e000"
"9064af1431c1ff3aaa15a0d6"
"50946066aaba43a294155ab5"
"70e82e77857317d6936ecdcc"
"dc644dc20d5b02ffd7c8cc94"
"92af1e9f1813619d820dec4d"
"9d4a077fdaaa24b78a54cee9"
"cfd5fa7aea14fb294bd4f5ff"
"068541b6c5130ca541d6c05c"
"39f4d1f12142d583b0318265"
"4a201b8fa69d11cb669dca8c"
"6fa6bba2e334dd2c9e5a6911"
"30ff082b6a28558f16743fea"
"afed31c4dc2ddf056e263acb"
"b2431ecf36f0b780959aa03d"
"54e42938ab6fa923a5fbfbff"
"48d50e3ff5e96f152d912ebf"
"6c7aad2ee996132f76630c05"
"2705f0482774977fa9b5c7bc"
"c09d6682c7a144d9afd4bfab"
"1a0a30c55dd9bc2fff4e2aac"
"30c22b502e80afd144d79120"
"8f882989bbc98c68b6c7347e"
"888bf771b63a5f64e6b696ed"
"959aa13a2e6439fd5d29c5fe"
"d03d311ca64bf59a2404dcd0"
"7a650f3e4308a097d7883b1f"
"54c207808ac349fa34ed24a9"
"3cb12e3ea31fe15ba9d234e5"
"0d9f8123e52f791ae172f627"
"387518b1bb96bd918e7d0b91"
"27198d8918c9802ac0b06a58"
"7a519a552f723c6b1fdc9389"
"4240096651a50f888765ed60"
"8ba94f78cc44cfb82b3678d2"
"1cf7c3bda6ec7952923bc90f"
"cd54811e976edbc798a87c24"
"e4ee17912f5d7d80d41dc52d"
"572346dd1874a0c749c3a031"
"cf700031502680580ff25980"
"7054518832e5c7efde04a58c"
"7716c6b6a3dcc9c5df79a262"
"f4d1084c692f5a07c25a1b47"
"817b80892a78fc6f83eab4eb"
"ae0e0adda6d063d3fd2c9499"
"33184c427ecaab77b5332521"
"c6c4da707e3eed987eb65ba6"
"40f9801913c0cb9800960bf7"
"af11cf78e8340f172ba76bbe"
"c1daea28b5996b8e8800e300"
"7312b3234b5f2f50887597b9"
"3e4361c5356b30f8ce833268"
"e6fc8827b9aa96fc91035ac2"
"11658fe51b23319533c72106"
"34207fe236870fd855392ef6"
"dc970a2b17aa1432d180d078"
"2f1686ad711b5ac3d98905b5"
"ed20f4bf782e10ec26c7b3b1"
"cf4d6241df36b0bbcea401e2"
"97e414eb00338bd7bd5d1b31"
"bcfebb743729d4fc51b0db49"
"52782c1eaa15e050ac748b18"
"57f65a6cd2eb0a2745fc90ee"
"fcc569e80808c6cd84c839cc"
"2447f8e0e3ccfd895a619695"
"600687fe6332b2e2b20d6a84"
"0f8a972361baddb3ac64b6d9"
"f768594ab352cf8e2282b615"
"3163b25e25b0d6859df17751"
"c73d68f0cf77aa113337fcde"
"cc16163137f13f70decbcb5c"
"4adbf6c7ac2004ee8ae20f55"
"c2779f78a0e57dcee471cefd"
"bf44903cd2fca4ae4daf0818"
"ca8013372d58365e21bd0428"
"e308901ca272ad447dca965a"
"ec8cefe29832cd726185119f"
"36bf51da3f32b1e9550f3b15"
"aefff6dffdf534a6c9601f70"
"ce237971a71127f9ce747232"
"99f4d97fae16ccbeb8c26d46"
"c9f6480328ec4b63b1034826"
"ce10bb6e80b09533cb21c9a5"
"311a54aaf062b29422ac0b02"
"b2073bec873b295c04e9fb7e"
"7b7d94035d63c9c0598b242b"
"e19e9a0487f1bd3eabdb777c"
"eeafe5169a2afd9ce3979698"
"96c7a1f9fb73468fdb770034"
"77e607be2498160d3ceeff60"
"c784fa6b1fd7d9aedb05b607"
"a76319bca87e0b57bc79064f"
"a08db36785c51e5b92a6e92f"
"0933ab4a366d57fd1f1c0a48"
"17663f17a835cfe0bca64f4a"
"aee8323d1de634fc42c11d90"
"682044caf09a068e26c11cab"
"1346fdbe3c6fcb808b99cdf6"
"26fd8f47fe4791e2dd07b2e0"
"b1da27dd129216c2ead2e162"
"618adebe192e896eb1501708"
"2628d9eeb38db68c0b255a75"
"1fb78eda5deb8972cc357a9b"
"2fdebb85a401de75bb580a02"
"c59eae24bee36f63c938723d"
"fb66b55d5617e27ca64836a3"
"55994022872e01fd41d8ec7b"
"53ae83b5490e1cf55625ff3d"
"c44975deeb67b96e6cc37eb5"
"94c64d5ff48ca6487f5e0b07"
"e34e7c1a3d70678955a5df8e"
"6426d30a923dec6377132c47"
"d67811849354ded9d34c9ece"
"01e7ae81207cfd230cb02747"
"e24558477f94bcdd82f16ebe"
"4370f6dcc2f53bd5abed7899"
"679b1145cd3072fbcd513bcb"
"4dafd3fd84e7a337ef3e9933"
"2aca3d41cb148603b4a2d70b"
"5e5aa8c960d024109543c577"
"c5f5f313c8a7056bbcdb7639"
"a7b2aaa4cf85fce98cdeb314"
"e556c0d14bc0ce9cc7bde0ca"
"922a8284880a7c879d846f90"
"b146cf2fbf206ca48687e9b6"
"fbdc52bb7c7620bd4508e5a3"
"23d79a262a81459e75576cc2"
"a26721706d3b9f971cef3ce6"
"fb3801ed1b056a3ab4bbfdc1"
"d5cafae617f1485826de335c"
"e9650dfc8b570f9d33381012"
"453c804c7ce82e83305cd751"
"3eaab01bdd1393be8d461777"
"2b6d9ddd4754b71d92ed31b7"
"2338e9af6acab9fcf5327f6a"
"588e7405c30c346ed2ea5e44"
"eec8837994054fb2043b6166"
"0877406795956ca9eb13fea1"
"1dea30e852067eec48a24035"
"ee716ce45ea812167ed2497a"
"9621d5aeaf8de9b2f58977bd"
"e9bb838723083bac7f83d602"
"ef64da08bbe787891d47c31b"
"e01b6616cc6cf7819172f7f3"
"17c821c34fca587daec981fc"
"d0c4b7153dcb3e6af47bcd98"
"5188eb00680051c35cd6e0f0"
"9fc7b68e736a1f4931226ad8"
"b5eac897471f125224a3e594"
"c1d0f890a02c85a9320f96d5"
"7e3b1ca1b1b1e990367f0cc2"
"bff5ff77174c43f7c681e0bd"
"ddeb794121937d07a887c9e6"
"9db902e930f25fbacc125087"
"4f82d277b47ae9c98095f068"
"24f999491fa46cd194c7fd35"
*/

View File

@ -0,0 +1,58 @@
require('dotenv').load();
var bonfires = require('./bonfires.json'),
app = require('../server/server'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
User = app.models.User,
UserIdentity = app.models.userIdentity,
oldUri='mongodb://localhost:27017/app30893198',
coursewares = require('./coursewares.json');
var counter = 0;
var offerings = 2;
var CompletionMonitor = function() {
counter++;
console.log('call ' + counter);
if (counter < offerings) {
return;
} else {
process.exit(0);
}
};
MongoClient.connect(oldUri, function(err, database) {
database.collection('users').find({}).batchSize(20).toArray(function(err, users) {
if (users !== null && users.length !== 0) {
var mappedUserArray = users.map(function(user) {
Object.keys(user.profile).forEach(function(prop) {
user[prop] = user.profile[prop];
});
Object.keys(user.portfolio).forEach(function(prop) {
user[prop] = user.portfolio[prop];
});
user.completedCoursewares = Object.keys(user.challengesHash)
.filter(function(key) {
return user.challengesHash[key] !== 0;
})
.map(function(key) {
return({
_id: coursewares[key].id,
completedDate: user.challengesHash[key]
});
});
return user;
});
User.create(mappedUserArray, function(err) {
if (err) {
console.log(err);
}
console.log("a batch finished");
});
}
});
});

View File

@ -0,0 +1,226 @@
[
{
"oldNumber": "0",
"newId": "bd7124d8c441eddfaeb5bdef"
},
{
"oldNumber": "1",
"newId": "bd7125d8c441eddfaeb5bd0f"
},
{
"oldNumber": "2",
"newId": ""
},
{
"oldNumber": "3",
"newId": "bd7127d8c441eddfaeb5bdef"
},
{
"oldNumber": "4",
"newId": "bd7128d8c441eddfaeb5bdef"
},
{
"oldNumber": "5",
"newId": "bd8129d8c441eddfaeb5bdef"
},
{
"oldNumber": "6",
"newId": ""
},
{
"oldNumber": "7",
"newId": ""
},
{
"oldNumber": "8",
"newId": "bd7112d8c441eddfaeb5bdef"
},
{
"oldNumber": "9",
"newId": "bd7113d8c441eddfaeb5bdef"
},
{
"oldNumber": "10",
"newId": "bd7114d8c441eddfaeb5bdef"
},
{
"oldNumber": "11",
"newId": "bd7115d8c441eddfaeb5bdef"
},
{
"oldNumber": "12",
"newId": "bd7116d8c441eddfaeb5bdef"
},
{
"oldNumber": "13",
"newId": "bd7117d8c441eddfaeb5bdef"
},
{
"oldNumber": "14",
"newId": "bd7118d8c441eddfaeb5bdef"
},
{
"oldNumber": "15",
"newId": ""
},
{
"oldNumber": "16",
"newId": ""
},
{
"oldNumber": "17",
"newId": ""
},
{
"oldNumber": "18",
"newId": ""
},
{
"oldNumber": "19",
"newId": "bd7123d8c441eddfaeb5bdef"
},
{
"oldNumber": "20",
"newId": "bd8124d8c441eddfaeb5bdef"
},
{
"oldNumber": "21",
"newId": "bd8126d8c441eddfaeb5bdef"
},
{
"oldNumber": "22",
"newId": "bd8127d8c441eddfaeb5bdef"
},
{
"oldNumber": "23",
"newId": "bd8128d8c441eddfaeb5bdef"
},
{
"oldNumber": "24",
"newId": "bd7129d8c441eddfaeb5bdef"
},
{
"oldNumber": "25",
"newId": "bd7130d8c441eddfaeb5bdef"
},
{
"oldNumber": "26",
"newId": "bd7131d8c441eddfaeb5bdef"
},
{
"oldNumber": "27",
"newId": "bd7132d8c441eddfaeb5bdef"
},
{
"oldNumber": "28",
"newId": "bd7133d8c441eddfaeb5bdef"
},
{
"oldNumber": "29",
"newId": "bd7134d8c441eddfaeb5bdef"
},
{
"oldNumber": "30",
"newId": "bd7135d8c441eddfaeb5bdef"
},
{
"oldNumber": "31",
"newId": "bd7136d8c441eddfaeb5bdef"
},
{
"oldNumber": "32",
"newId": ""
},
{
"oldNumber": "33",
"newId": "bd7138d8c441eddfaeb5bdef"
},
{
"oldNumber": "34",
"newId": "bd7137d8c441eddfaeb5bdef"
},
{
"oldNumber": "35",
"newId": "bd7140d8c441eddfaeb5bdef"
},
{
"oldNumber": "36",
"newId": ""
},
{
"oldNumber": "37",
"newId": ""
},
{
"oldNumber": "38",
"newId": ""
},
{
"oldNumber": "39",
"newId": ""
},
{
"oldNumber": "40",
"newId": ""
},
{
"oldNumber": "41",
"newId": ""
},
{
"oldNumber": "42",
"newId": ""
},
{
"oldNumber": "43",
"newId": ""
},
{
"oldNumber": "44",
"newId": ""
},
{
"oldNumber": "45",
"newId": ""
},
{
"oldNumber": "46",
"newId": ""
},
{
"oldNumber": "47",
"newId": ""
},
{
"oldNumber": "48",
"newId": "bd7153d8c441eddfaeb5bd2f"
},
{
"oldNumber": "49",
"newId": "bd7154d8c441eddfaeb5bdef"
},
{
"oldNumber": "50",
"newId": "bd7155d8c441eddfaeb5bdef"
},
{
"oldNumber": "51",
"newId": "bd7156d8c441eddfaeb5bdef"
},
{
"oldNumber": "52",
"newId": "bd7157d8c441eddfaeb5bdef"
},
{
"oldNumber": "53",
"newId": "bd7158d8c441eddfaeb5bdef"
},
{
"oldNumber": "54",
"newId": ""
},
{
"oldNumber": "55",
"newId": ""
}
]

View File

@ -1,3 +0,0 @@
[
]

File diff suppressed because it is too large Load Diff

502
seed_data/field-guides.json Normal file
View File

@ -0,0 +1,502 @@
[
{
"_id": "bd7158d9c441eddfaeb5bdef",
"name": "How do I use this guide?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h2 class='text-left'>This guide will answer many of your questions about learning to code and getting a coding job.</h2>",
"<h3 class='text-left'>You can click the \"Next article\" button to go to your next article.</h3>",
"<h3 class='text-left'>You can also click the \"Show me all articles\" button to browse all the questions we answer.</h3>",
"</div>"
]
},
{
"_id": "bd7158d9c441eddfaeb5bdff",
"name": "What is Free Code Camp?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h2 class='text-left'>We're a community of busy adults who learn to code by building projects for nonprofits.</h2>",
"<h3 class='text-left'>We help our campers (students):</h3>",
"<h3>",
" <ol>",
" <li>Learn full stack JavaScript</li>",
" <li>Build a portfolio of real apps that real people are using</li>",
" <li>Get a coding job</li>",
" </ol>",
"</h3>",
"</div>"
]
},
{
"_id": "bd7158d9c441eddfaeb5bd1f",
"name": "Why do I need Free Code Camp?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h2 class='text-left'>Learning to code is hard.</h2>",
"<h3 class='text-left'>Most people who successfully learn to code:</h3>",
"<h3>",
" <ol>",
" <li>Code every day</li>",
" <li>Have lots of friends who code</li>",
" </ol>",
"</h3>",
"<h3>We give you the structure and the community you need so you can successfully learn to code.</h3>",
"</div>"
]
},
{
"_id": "bd7158d9c441eddfaeb5bd2f",
"name": "How does Free Code Camp work?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<img class='img-center img-responsive' alt='First you'll do about 100 hours of lessons.' src='https://s3.amazonaws.com/freecodecamp/100-hours-of-lessons.jpg'/>",
"<div class='spacer'></div>",
"<img class='img-center img-responsive' alt='Next you'll write algorithms and build practice projects for 100 hours.' src='https://s3.amazonaws.com/freecodecamp/100-hours-of-practice.jpg'/>",
"<div class='spacer'></div>",
"<img class='img-center img-responsive' alt='Finally you build full stack JavaScript apps for nonprofits for 800 hours.' src='https://s3.amazonaws.com/freecodecamp/800-hours-of-nonprofit-projects.jpg'/>",
"<div class='spacer'></div>",
"<img class='img-center img-responsive' alt='This gives you a total of 1,000 hours of coding experience.' src='https://s3.amazonaws.com/freecodecamp/1000-hours-of-coding.jpg'/>",
"</div>"
]
},
{
"_id": "bd7158d9c441eddfaeb5bd3f",
"name": "What are the advantages of Free Code Camp?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h3>",
" <ul>",
" <li>&bull; We're free</li>",
" <li>&bull; We're self-paced</li>",
" <li>&bull; We're browser-based</li>",
" <li>&bull; You'll code the entire time</li>",
" </ol>",
"</h3>",
"</div>"
]
},
{
"_id": "bd7158d9c441eddfaeb5bd4f",
"name": "Will I really be able to get a job after Free Code Camp?",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h2 class='text-left'>If you complete this program, you <b>will</b> be able to get a coding job.</h2>",
"<img class='img-center img-responsive' src=\"https://www.evernote.com/shard/s116/sh/55c128c7-5d99-41cc-b03d-b3de22611c8d/b43e467b3889f646fec34bb4c161e2a2/deep/0/What's-wrong-with-this-picture----Code.org.png\"/>",
"<h3>Here are the facts:</h3>",
"<h3>",
" <ul>",
" <li>&bull; There are hundreds of thousands of unfilled coding jobs.</li>",
" <li>&bull; Employers and the US government have joined together to promote nontraditional coding programs like Free Code Camp.</li>",
" <li>&bull; Full stack JavaScript is one of best paying skill sets, and has a ton of job openings.</li>",
" <li>&bull; The best proof that you're an employable developer is a portfolio filled with real apps that real people are using.</li>",
" </ul>",
"</h3>",
"</div>"
]
},
{
"_id": "bd7158d9c440eddfaeb5bdef",
"name": "What will I learn, and in what sequence?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\">First, you'll learn basic web design tools like:",
" <ul>",
" <li class=\"landing-p\">&#8226 HTML - the structure of web pages</li>",
" <li class=\"landing-p\">&#8226 CSS - the visual style of web pages</li>",
" <li class=\"landing-p\">&#8226 Bootstrap - a \"responsive design\" tool that helps your websites look great on tablets and phones</li>",
" <li class=\"landing-p\">&#8226 jQuery - an easy tool for controlling content in the browser</li>",
" <li class=\"landing-p\">&#8226 Chrome DevTools - a tool for understanding and debugging websites, right in your browser</li>",
" </ul>",
" </p>",
" <p class=\"landing-p\">Then you'll learn computer science and the art of programming:",
" <ul>",
" <li class=\"landing-p\">&#8226 JavaScript - the one programming language that all web browsers use</li>",
" <li class=\"landing-p\">&#8226 Algorithms - step-by-step recipes for getting things done</li>",
" <li class=\"landing-p\">&#8226 Automated Testing - write tests to test the limits of your code</li>",
" </ul>",
" </p>",
" <p class=\"landing-p\">Finally you'll learn Agile Methodologies and Full Stack JavaScript to build projects for nonprofits:",
" <ul>",
" <li class=\"landing-p\">&#8226 Agile - a set of software development principles that focus the design and production of a project on the needs of its users</li>",
" <li class=\"landing-p\">&#8226 Git - a version control system for saving and sharing your projects</li>",
" <li class=\"landing-p\">&#8226 MongoDB - a popular non-relational database</li>",
" <li class=\"landing-p\">&#8226 Angular.js - a tool for making exciting web interfaces</li>",
" <li class=\"landing-p\">&#8226 Express.js - a powerful web development framework</li>",
" <li class=\"landing-p\">&#8226 Node.js - a JavaScript-based web server</li>",
" </ul>",
" </p>",
"</div>"
]
},
{
"_id": "bd7158d9c434eddfaeb5bdef",
"name": "How long does Free Code Camp take?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\">It takes about 1,000 hours of coding to develop the skills you'll need to get an entry level software engineering job. Most coding bootcamps try to jam all this into 12 weeks of intensive study. Free Code Camp is fully online, and there will always be other people at your skill level that you can pair program with, so you can learn at your own pace. Here are some example coding schedules:</p>",
" <table class=\"table\">",
" <thead>",
" <th>Time budgeted</th>",
" <th class=\"hidden-xs\">Hours per week</th>",
" <th>Weeks to complete</th>",
" </thead>",
" <tr class=\"info\">",
" <td>Weekends</td>",
" <td class=\"hidden-xs\">10 hours/week</td>",
" <td>100 weeks (2 years)</td>",
" </tr>",
" <tr class=\"success\">",
" <td>Nights and Weekends</td>",
" <td class=\"hidden-xs\">20 hours/week</td>",
" <td>50 weeks (1 year)</td>",
" </tr>",
" <tr class=\"warning\">",
" <td>Full time</td>",
" <td class=\"hidden-xs\">40 hours/week</td>",
" <td>25 weeks (6 months)</td>",
" </tr>",
" <tr class=\"danger\">",
" <td>Traditional Bootcamp Pacing</td>",
" <td class=\"hidden-xs\">80 hours/week</td>",
" <td>12 weeks (3 months)</td>",
" </tr>",
" </table>",
"<br>",
"</div>"
]
},
{
"_id": "bd7158d9c438eddfaeb5bdef",
"name": "Why does Free Code Camp use JavaScript instead of Ruby or Python?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\">Like JavaScript, Ruby and Python are high-level scripting languages that can be used for full stack web development. But even if you learned these languages, you'd still need to learn JavaScript. That's because JavaScript is the only language that runs in web browsers. JavaScript has been around for 20 years, and it is still growing in popularity. Because of this, JavaScript has more tools and online learning resources than any other language.</p>",
" <img src=\"https://s3.amazonaws.com/freecodecamp/github-repo-growth.png\" style=\"max-height: 355px;\" alt=\"A chart showing the volume of new GitHub repositories by year, with JavaScript growing and most languages declining.\" class=\"img-center img-responsive\"/>",
"<br>",
"</div>"
]
},
{
"_id": "bd7158d9c437eddfaeb5bdef",
"name": "What's pair programming, and what's so special about it?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\">Pair programming is where two people code together on one computer. You discuss different approaches to solving problems, and keep each other motivated. The result is better code than either of you could have written by yourselves. Because of its benefits, many engineers pair program full time. And it's the best way to learn coding. Thanks to tools that allow two people to share mouse and keyboard inputs, you can pair program with a friend without needing to be in the same room.</p>",
" <p class=\"landing-p\">By pair programming with other Free Code Camp students on our coding challenges. Eventually, you'll work with people at nonprofits to build real-life software solutions.</p>",
"</div>"
]
},
{
"_id": "bd7158d9c436eddfaeb5bdef",
"name": "If Free Code Camp is free, how does it make money?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\"> We are completely free for both students and for nonprofits.</p>",
" <p class=\"landing-p\">Our name is Free Code Camp. We are a free code camp. If you had to pay us (or sign over future earnings), we'd have to change our name. And we are not going to do that.</p>",
" <p class=\"landing-p\"> We don't make any money at all. Everyone working on our community and our open source projects is a volunteer.</p>",
" <p class=\"landing-p\"> We plan to eventually cover our operational costs by earning job placement bonuses from companies who hire our graduates. Note that we will not actually garnish any wages from our graduates - employers are already paying recruiters thousands of dollars to find successful candidates. Employers will simply pay the recruitment bonus to Free Code Camp instead of paying a recruiter.</p>",
"</div>"
]
},
{
"_id": "bd7158d9c435eddfaeb5bdef",
"name": "Does Free Code Camp have an application process?",
"description": [
"<div class=\"text-left col-xs-12 col-md-10 col-md-offset-1\">",
" <p class=\"landing-p\">Unlike coding bootcamps, anyone can study at Free Code Camp. We're not going to tell you that you can't become a software engineer. We believe the only person who should be able to tell you that is you.</p>",
" <p class=\"landing-p\">If you persevere, and keep working through our challenges and nonprofit projects, you will become an employable software engineer.</p>",
"</div>"
]
},
{
"_id": "bd7158d9c442eddfaeb5bdef",
"name": "Global Control Shortcuts for Mac",
"description": [
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
"<h2 class='text-center'>These Global Control Shortcuts for Mac will save you hours by speeding up your typing</h2>",
"<div class=\"embed-responsive embed-responsive-16by9\"><iframe src=\"//player.vimeo.com/video/107073108\" class=\"embed-responsive-item\"></iframe></div>",
"<div class=\"text-left\">",
" <h3>These global shortcuts work everywhere on a Mac:",
" <ul>",
" <li>Control + F = Forward</li>",
" <li>Control + B = Backward</li>",
" <li>Control + N = Next Line</li>",
" <li>Control + P = Previous Line</li>",
" <li>Control + H = Backspace</li>",
" <li>Control + D = Delete</li>",
" <li>Control + A = Beginning of Line</li>",
" <li>Control + E = End of Line</li>",
" <li>Control + K = Kill line</li>",
" </ul>",
"</div>"
]
},
{
"_id": "bd7158d9c445eddfaeb5bdef",
"name": "Gmail Zero Inbox Shortcuts",
"description": [
"<h2 class='text-center'>These Gmail Shortcuts will save you hours and help you get to Zero Inbox</h2><br/>",
"<div class=\"embed-responsive embed-responsive-16by9\">",
" <iframe src=\"//player.vimeo.com/video/115194016\" class=\"embed-responsive-item\"></iframe>",
"</div>",
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1 text-left\">",
" <h3>The shortcuts:",
" <ul>",
" <li>j - move down</li>",
" <li>k - move up</li>",
" <li>o - open</li>",
" <li>r - reply</li>",
" <li>a - reply all</li>",
" <li>f - forward</li>",
" <li>c - compose</li>",
" <li>x - select</li>",
" <li>e - archive</li>",
" <li>! - mark spam</li>",
" <li>z - undo</li>",
" </ul>",
" </h3>",
"</div>"
]
},
{
"_id": "bd7158d9c446eddfaeb5bdef",
"name": "A Guide to our Nonprofit Projects",
"description": [
"<div class='col-xs-12 col-sm-10 col-sm-offset-1'>",
"<h2>Building nonprofit projects is the main way that our campers learn full stack JavaScript and agile software development. Once you complete the Free Code Camp challenges and Bonfire challenges, you'll begin this process.</p>",
"<p>Once you've finished all the challenges, click the \"I'm done with all the challenges\" button, which will become enabled. We will prompt you for your email address, then give you further instructions on our &thinsp;<a href=\"http://www.freecodecamp.com/nonprofit-project-instructions\">Nonprofit Projects Instructions page</a>.</p>",
"<p>We will add you to our &thinsp;<a href=\"https://trello.com/b/BA3xVpz9/nonprofit-projects\">Nonprofit Project Trello board</a>.</p>",
"<h2>Starting with the end in mind</h2>",
"<p>Our goal at Free Code Camp is to help you land a job as a junior software developer (or, if you prefer, a 'pivot job' that leads your current career in a more technical direction).</p>",
"<p>You'll continue to work on nonprofit projects until you've built a sufficiently impressive portfolio and references to start your job search. Your portfolio will ultimately have three to five nonprofit projects. We estimate that the 900 hours of nonprofit projects you're going to complete, in addition to the 100 hours of challenges you've already completed, will be enough to qualify you for your first coding job. This will produce a much broader portfolio than a traditional coding bootcamp, which generally only has one or two capstone projects.</p>",
"<h2>Choosing your first Nonprofit Project</h2>",
"<p>We've categorized all the nonprofit projects by estimated time investment per camper: 100 hours, 200 hours, and 300 hours. These are only rough estimates.</p>",
"<p>Example: if you and the camper you're paired up with (your pair) each stated you could work 20 hours per week (on the &thinsp;<a href=\"http://goo.gl/forms/f61dLt67t8\">form you filled out</a>). If the project is a 100 hour per camper project, you should be able to complete it in about 5 weeks.</p>",
"<p>Our team of nonprofit project camp counselors will match you and your pair based on:",
" <ol>",
" <li>Your estimated time commitment (10, 20 or 40 hours per week)</li>",
" <li>Your time zone</li>",
" <li>The nonprofit projects you've chosen</li>",
" <li>Prior coding experience (we'd like both campers to be able to contribute equally)</li>",
" </ol>",
"</p>",
"<p>We won't take age or gender into account. This will provide you with valuable experience in meshing with diverse teams, which is a reality of the contemporary workplace.</p>",
"<p>You'll only work on one project at a time. Once you start a nonprofit project, we'll remove you from all other nonprofit project Trello cards. There's a good chance those projects will no longer be available when you finish your current project, anyway. Don't worry, though - we get new nonprofit project requests every day, so there will be plenty more projects for you to consider after you finish your current one.</p>",
"<h2>Finalizing the Project</h2>",
"<p>Before you can start working on the project, our team of Nonprofit Project Coordinators will go through the following process:",
" <ol>",
" <li>We'll wait until there are two campers who have chosen the same project and look like they're a good match for one another based on the factors mentioned above.</li>",
" <li>We'll call the stakeholder to confirm once again that he or she agrees with our &thinsp;<a href=\"freecodecamp.com/nonprofits\">terms &thinsp;</a>and has signed our &thinsp;<a href=\"http://goo.gl/forms/0YKkd9bpcR\">Nonprofit Project Stakeholder Pledge</a>.</li>",
" <li>We'll set an initial meeting with representatives from Free Code Camp, the two campers, and the stakeholder.</li>",
" <li>If the stakeholder and both campers shows up promptly, and seem enthusiastic and professional, we'll start the project.</li>",
" </ol>",
"</p>",
"<p>This lengthy process serves an important purpose: it reduces the likelihood that any of our campers or stakeholders will waste their precious time.</p>",
"<h2>Nonprofit Stakeholders</h2>",
"<p>Each nonprofit project was submitted by a nonprofit. A representative from this nonprofit has agreed to serve as a \"stakeholder\" - an authorative person who understands the organization and its needs for this particular project.</p>",
"<p>Stakeholders have a deep understanding of their organizations' needs. Campers will work with them to figure out the best solutions to these needs.</p>",
"<p>When you and your pair first speak with your nonprofit stakeholder, you'll:",
" <ul>",
" <li>talk at length to better understand their needs.</li>",
" <li>create a new Trello board and use it to prioritize what needs to be built.</li>",
" <li>and establish deadlines based on your weekly time commitment, and how long you think each task will take.</li>",
" </ul>",
"</p>",
"<p>It's notoriously difficult to estimate how long building software projects will take, so feel free to ask camp counselors for help.</p>",
"<p>You'll continue to meet with your stakeholder at least twice a month in your project's Gitter channel.</p>",
"<p>You should also ask questions in your project's Gitter channel as they come up throughout the week, and your stakeholder can answer them asynchronously.</p>",
"<p>Getting \"blocked\" on a task can take away your sense of forward momentum, so be sure to proactively seek answers to any ambiguities you encounter.</p>",
"<p>Ultimately, the project will be considered complete once both the stakeholder's needs have been met, and you and your pair are happy with the project. Then you can add it to your portfolio!</p>",
"<h2>Working with your Pair</h2>",
"<p>You and your pair will pair program (code together on the same computer virtually) about half of the time, and work independently the other half of the time.</p>",
"<p>Here are our recommended ways of collaborating:",
" <ul>",
" <li>&#8226; Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.</li>",
" <li>&#8226; Trello is great for managing projects. Work with your stakeholder to create Trello cards, and update these cards regularly as you make progress on them.</li>",
" <li>&#8226; Screen Hero or Team Viewer - These are the ideal way to pair program. Tools like TMUX are good, but difficult to use. We discourage you from using screen sharing tools where only one person has control of the keyboard and mouse - that isn't real pair programming.</li>",
" <li>&#8226; Write clear and readable code, commit messages, branch names, and pull request messages.</li>",
" </ul>",
"</p>",
"<h2>Setting up your Development Environment</h2>",
"<p>We've created a custom virtual machine image with Ubuntu Linux, Git, Team Viewer, the MEAN Stack and all its dependencies. You can run this virtual image on any computer with at least 2 gigabytes of RAM and 16 gigabytes of hard drive space.</p>",
"<p>The benefits of using this virtual machine are as follows:</p>",
"<ul>",
" <li>&#8226; Everyone else on Free Code Camp is using this image, so we can all help you troubleshoot various problems that may arise.</li>",
" <li>&#8226; When you pair program, you and your pair will have the exact same environment, which means you will both feel comfortable on each other's machines.</li>",
" <li>&#8226; You can install the image on any computer without worrying about messing up the computer's original data or configuration.</li>",
" <li>&#8226; Even if you end up using Windows or Mac OSX for development later, your server will almost certainly run Linux, so it's worth getting used to Linux.</li>",
" <li>&#8226; Even experienced developers encounter hangups when setting up a development environment. This virtual machine image will remove this tedious process.</li>",
"</ul>",
"<p>Install a bit torrent client, then &thinsp;<a href=\"http://mgnet.me/ZOQk0rd\">download our virtual machine image</a>.</p>",
"<p>Please note that even though Bit Torrent is often used to download content illegally, all the content on our image is open source and perfectly legal to redistribute.</p>",
"<p>Once you've downloaded the file, &thinsp;<a href=\"https://www.virtualbox.org/field-guide/Downloads\">download VirtualBox &thinsp;</a>and follow &thinsp;<a href=\"http://techathlon.com/how-to-run-a-vmdk-file-in-oracle-virtualbox/\">this tutorial &thinsp;</a>to open the image in VirtualBox. You'll want to assign the virtual machine at least two gigabytes of ram.</p>",
"<p>Now you have your own Linux development environment. You can shut it down when you're not using it and it will save its state. Please continue to seed the file in bit torrent so that other campers can download it as well. Enjoy!</p>",
"<h2>Hosting Apps</h2>",
"<p>Unless your stakeholder has an existing modern host (AWS, Digital Ocean), you'll need to transition them over to a new platform. We believe Heroku is the best choice for a vast majority of web projects. It's free, easy to use, and has both browser and command line interfaces. It's owned by Salesforce and used by a ton of companies, so it's accountable and unlikely to go away.</p>",
"<p>If you need help convincing your stakeholder that Heroku is the ideal platform, we'll be happy to talk with them.</p>",
"<h2>Maintaining Apps</h2>",
"<p>Once you complete a nonprofit project, your obligation to its stakeholder is finished. You goal is to leave behind a well documented solution that can be easily maintained by a contract JavaScript developer (or even a less-technical \"super user\").</p>",
"<p>While you will no longer need to help with feature development, we encourage you to consider helping your stakeholder with occasional patches down the road. After all, this project will be an important piece of your portfolio, and you'll want it to remain in good shape for curious future employers.</p>",
"<h2>Office Hours</h2>",
"<p>Quincy Larson and/or Michael Johnson will be in the &thinsp;<a href=\"https://gitter.im/FreeCodeCamp/NonprofitProjects\">Gitter Nonprofit Project Channel &thinsp;</a>every Monday and Thursday from 9 - 10 p.m. EST.</p>",
"<p>Our goal is to make the discussion as public as possible so all our campers can benefit from each campers questions.</p>",
"<p>If necessary, we can also hop on Screen Hero with you to help you with issues more specific to your project.</p>",
"<h2>Pledging to finish the project</h2>",
"<p>Your nonprofit stakeholder, your pair, and the volunteer camp counselor team are all counting on you to finish your nonprofit project. If you walk away from an unfinished nonprofit project, you'll become ineligible to ever be assigned another one.</p>",
"<p>To confirm that you understand the seriousness of this commitment, we require that all campers &thinsp;<a href=\"http://goo.gl/forms/ZMn96z2QqY\">sign this pledge &thinsp;</a>before starting on their nonprofit projects.</p>",
"<p>There will likely be times of confusion or frustration. This is normal in software development. The most important thing is that you do not give up and instead persevere through these setbacks. As Steve Jobs famously said, \"Real artists ship.\" And you are going to ship one successful nonprofit project after another until you feel ready to take the next step in your promising career.</p>",
"</div>"
]
},
{
"_id": "bd7158d9c447eddfaeb5bdef",
"name": "Install Screenhero",
"description": [
"<div class='text-center'>",
"<h2><a href=\"http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=\">Download for Mac</a></h2>",
"<h2><a href=\"http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9\"> Download for Windows</a></h2>",
"<p>You can learn more about using Screen Hero by taking &thinsp;<a href=\"http://www.freecodecamp.com/challenges/34\">Challenge 34.</a></p>",
"<p>Screen Hero was recently acquired by a collaboration tool called Slack.</p>",
"It's still available and free, but will go away in the indefinite future. We'll replace it.</p>",
"</div>"
]
},
{
"_id": "bd7158d9c448eddfaeb5bdef",
"name": "Live Stream Pair Programming on Twitch.tv",
"description": [
"<div class=\"text-center\">",
" <h1>Live Pair Programming</h1>",
" <h2 id=\"next-session\"></h2>",
" <h2>Watch the live stream below or on our &thinsp;<a href=\"http://twitch.tv/freecodecamp\" target=\"_blank\">Twitch.tv channel</a>.</h2>",
" <div class=\"row\">",
" <div class=\"col-md-8 col-xs-12\">",
" <div class=\"embed-responsive embed-responsive-16by9\">",
" <iframe src=\"http://www.twitch.tv/freecodecamp/embed\" frameborder=\"0\" scrolling=\"no\"></iframe>",
" </div>",
" </div>",
" <div class=\"col-md-4 col-xs-12\">",
" <div class=\"visible-sm visible-xs\">",
" <div class=\"embed-responsive embed-responsive-16by9\">",
" <iframe src=\"http://www.twitch.tv/freecodecamp/chat?popout=\" frameborder=\"0\" scrolling=\"no\"></iframe>",
" </div>",
" </div>",
" <div class=\"visible-md visible-lg\">",
" <div class=\"embed-responsive embed-responsive-twitch-chat\">",
" <iframe src=\"http://www.twitch.tv/freecodecamp/chat?popout=\" frameborder=\"0\" scrolling=\"no\"></iframe>",
" </div>",
" </div>",
" </div>",
" </div>",
" <h1>Previous Live Pair Programming Sessions</h1>",
" <div class=\"col-xs-12\">",
" <div class=\"embed-responsive embed-responsive-16by9 big-break\">",
" <iframe src=\"//www.youtube.com/embed/_BErpDdmBOw\" class=\"embed-responsive-item\"></iframe>",
" </div>",
" <h3 class=\"wrappable\">link: &thinsp;<a href=\"http://www.youtube.com/watch/_BErpDdmBOw\">http://www.youtube.com/watch/_BErpDdmBOw</a></h3>",
" <div class=\"embed-responsive embed-responsive-16by9 big-break\">",
" <iframe src=\"//www.youtube.com/embed/Fn9HMn79KH0\" class=\"embed-responsive-item\"></iframe>",
" </div>",
" <h3 class=\"wrappable\">link: &thinsp;<a href=\"http://www.youtube.com/watch/Fn9HMn79KH0\">http://www.youtube.com/watch/Fn9HMn79KH0</a></h3>",
" <div class=\"embed-responsive embed-responsive-16by9 big-break\">",
" <iframe src=\"//www.youtube.com/embed/S7iRBZJwOAs\" class=\"embed-responsive-item\"></iframe>",
" </div>",
" <h3 class=\"wrappable\">link: &thinsp;<a href=\"http://www.youtube.com/watch/S7iRBZJwOAs\">http://www.youtube.com/watch/S7iRBZJwOAs</a></h3>",
" <div class=\"embed-responsive embed-responsive-16by9 big-break\">",
" <iframe src=\"//www.youtube.com/embed/BHNRg39ZblE\" class=\"embed-responsive-item\"></iframe>",
" </div>",
" <h3 class=\"wrappable\">link: &thinsp;<a href=\"http://www.youtube.com/watch/BHNRg39ZblE\">http://www.youtube.com/watch/BHNRg39ZblE</a></h3>",
" <div class=\"embed-responsive embed-responsive-16by9 big-break\">",
" <iframe src=\"//www.youtube.com/embed/YDfkHlDmehA\" class=\"embed-responsive-item\"></iframe>",
" </div>",
" <h3 class=\"wrappable\">link: &thinsp;<a href=\"http://www.youtube.com/watch/YDfkHlDmehA\">http://www.youtube.com/watch/YDfkHlDmehA</a></h3>",
" </div>",
" <script src=\"/js/lib/moment/moment.js\"></script>",
" <script src=\"/js/lib/moment/nextTuesday.js\"></script>",
" <script>$('#next-session').text(nextSession());</script>",
"</div>"
]
},
{
"_id": "bd7158d9c449eddfaeb5bdef",
"name": "Nodeschool Challenges",
"description": [
"<h2 class='text-center'>Learn Node.js, NPM, Express.js, and advanced JavaScript like Functional Programming and Promises</h2><br/>",
"<div class=\"embed-responsive embed-responsive-16by9\">",
" <iframe src=\"//player.vimeo.com/video/122719685\" class=\"embed-responsive-item\"></iframe>",
"</div>",
"<div class=\"text-left col-xs-12 col-sm-10 col-sm-offset-1\">",
" <h3>Here are the NodeSchool courses you should complete:",
" <ol>",
" <li><a href=\"http://runnable.com/VQuO_Y4BbkhaOOsV/nodeschool-io-s-learnyounode-course-available-in-your-browser-for-node-js-and-freecodecamp\" target=\"_blank\">NodeSchool.io's LearnYouNode course</a></li>",
" <li><a href=\"http://runnable.com/VQt7deuMe6RZ3Gcl/nodeschool-io-s-learn-to-npm-course-running-in-your-browser-for-node-js-and-hello-world\" target=\"_blank\">NodeSchool.io's Learn-to-NPM course</a></li>",
" <li><a href=\"http://runnable.com/VQufnaRAlaNc9JuM/nodeschool-io-s-express-js-course-available-in-your-browser-for-node-js-and-freecodecamp\" target=\"_blank\">NodeSchool.io's Express.js course</a></li>",
" <li><a href=\"http://runnable.com/VQuZjvia8Gxcqkpy/nodeschool-io-s-functional-programming-in-javascript-course-available-in-your-browser-for-node-js-and-freecodecamp\" target=\"_blank\">NodeSchool.io's Functional Programming in JavaScript course</a></li>",
" <li><a href=\"http://runnable.com/VQunH26qdytcbLBg/nodeschool-io-s-promise-it-won-t-hurt-promises-course-available-in-your-browser-for-node-js-javascript-and-freecodecamp\" target=\"_blank\">NodeSchool.io's Promise It Won't Hurt Promises course</a></li>",
" </ol>",
" </h3>",
"</div><br/>"
]
},
{
"_id": "bd7158d9c450eddfaeb5bdef",
"name": "Nonprofit Project Instructions",
"description": [
"<div class='col-xs-12 col-sm-10 col-sm-offset-1'>",
"<h3>It's time to apply what you've learned here at Free Code Camp.</h3>",
"<h3>By the end of this process, you'll have a portfolio of live apps being used by real people.</h3>",
"<h3>Please do the following immediately:</h3>",
"<h4>",
" <ol>",
" <li>Complete this form: &thinsp;<a href=\"http://goo.gl/forms/f61dLt67t8\" target=\"_blank\">http://goo.gl/forms/f61dLt67t8</a>.</li>",
" <li>Read this document, which will answer many questions you may have about our nonprofit projects: &thinsp;<a href=\"/field-guide/guide-to-our-nonprofit-projects\" target=\"_blank\">http://freecodecamp.com/field-guide/guide-to-our-nonprofit-projects</a>.</li>",
" <li>We'll send you an invite to our Nonprofit Projects Trello board. Once we do, go there and add yourself to at least 3 nonprofit projects that interest you.</li>",
" <li>Finish any unfinished Bonfire challenges. These challenges serve as the Free Code Camp \"exit test\". You must complete these before you can start working on nonprofit projects. If you completed CoderByte or CodeWars challenges instead of Bonfire, email us and we'll take a look:&thinsp;<a href=\"mailto:team@freecodecamp.com\">team@freecodecamp.com</a>.</li>",
" </ol>",
" <h4>Please email us if you have further questions: &thinsp;<a href=\"mailto:team@freecodecamp.com\">team@freecodecamp.com</a>.</h4>",
"</h4>",
"</div>"
]
},
{
"_id": "bd7158d9c451eddfaeb5bdef",
"name": "Free Code Camp's Privacy Policy",
"description": [
"<div class='col-xs-12 col-sm-10 col-sm-offset-1'>",
"<p>Free Code Camp is committed to respecting the privacy of visitors to our web sites and web applications. The guidelines below explain how we protect the privacy of visitors to FreeCodeCamp.com and its features.</p>",
"<h3>Personally Identifiable Information</h3>",
"<p>Free Code Camp protects the identity of visitors to FreeCodeCamp.com by limiting the collection of personally identifiable information.</p>",
"<p>Free Code Camp does not knowingly collect or solicit personally identifiable information from or about children under 13, except as permitted by law. If we discover we have received any information from a child under 13 in violation of this policy, we will delete that information immediately. If you believe Free Code Camp has any information from or about anyone under 13, please e-mail us at <a href=\"mailto:team@freecodecamp.com\" target=\"_blank\">team@freecodecamp.com</a>.</p>",
"<p>All personally identifiable information you provide to us is used by Free Code Camp and its team to process and manage your account, analyze the demographic of our users, or to deliver services through the site. </p>",
"<p>If you choose to provide personally identifiable information to us, you may receive occasional e-mails from us that are relevant to Free Code Camp, getting a job, or learning to code in general.</p>",
"<p>Free Code Camp may also use other third-party providers to facilitate the delivery of the services described above, and these third-party providers may be supplied with or have access to personally identifiable information for the sole purpose of providing these services, to you on behalf of Free Code Camp.</p>",
"<p>Free Code Camp may also disclose personally identifiable information in special legal circumstances. For instance, such information may be used where it is necessary to protect our copyright or intellectual property rights, or if the law requires us to do so.</p>",
"<h3>Anonymous Information</h3>",
"<p>Anonymous aggregated data may be provided to other organizations we associate with for statistical purposes. For example, we may report to an organization that a certain percentage of our site's visitors are adults between the ages of 25 and 35.</p>",
"<h3>Cookies and Beacons—Use by Free Code Camp; Opting Out</h3>",
"<p>We use cookies and software logs to monitor the use of FreeCodeCamp.com and to gather non-personal information about visitors to the site. Cookies are small files that Free Code Camp transfers to the hard drives of visitors for record-keeping purposes. These monitoring systems allow us to track general information about our visitors, such as the type of browsers (for example, Firefox or Internet Explorer), the operating systems (for instance, Windows or Macintosh), or the Internet providers (for instance, Comcast) they use. This information is used for statistical and market research purposes to tailor content to usage patterns and to provide services requested by our customers. To delete these cookies, please see your browser's privacy settings.</p>",
"<p>A beacon is an electronic file object (typically a transparent image) placed in the code of a Web page. We use third party beacons to monitor the traffic patterns of visitors from one Free Code Camp.com page to another and to improve site performance.</p>",
"<p>None of the information we gather in this way can be used to identify any individual who visits our site.</p>",
"<h3>Security</h3>",
"<p>Any personally identifiable information collected through this site is stored on limited-access servers. We will maintain safeguards to protect these servers and the information they store.</p>",
"<h3>Surveys</h3>",
"<p>We may occasionally conduct on-line surveys. All surveys are voluntary and you may decline to participate.</p>",
"<h3>Copyright</h3>",
"<p>All of the content on FreeCodeCamp.com is copyrighted by Free Code Camp. If you'd like to redistribute it beyond simply sharing it through social media, please contact us at <a href=\"mailto:team@freecodecamp.com\" target=\"_blank\">team@freecodecamp.com</a>.</p>",
"<h3>Contacting Us</h3>",
"<p>If you have questions about Free Code Camp, or to correct, update, or remove personally identifiable information, please email us at <a href=\"mailto:team@freecodecamp.com\" target=\"_blank\">team@freecodecamp.com</a>.</p>",
"<h3>Links to Other Web sites</h3>",
"<p>Free Code Camp's sites each contain links to other Web sites. Free Code Camp is not responsible for the privacy practices or content of these third-party Web sites. We urge all FreeCodeCamp.com visitors to follow safe Internet practices: Do not supply Personally Identifiable Information to these Web sites unless you have verified their security and privacy policies.</p>",
"<h3>Data Retention</h3>",
"<p>We retain your information for as long as necessary to permit us to use it for the purposes that we have communicated to you and comply with applicable law or regulations.</p>",
"<h3>Business Transfers</h3>",
"<p>As we continue to develop our business, we might sell or buy subsidiaries, or business units. In such transactions, customer information generally is one of the transferred business assets but remains subject to the promises made in any pre-existing Privacy Policy (unless, of course, the customer consents otherwise). Also, in the unlikely event that Free Code Camp, or substantially all of its assets are acquired, customer information will be one of the transferred assets, and will remain subject to our Privacy Policy.</p>",
"<h3>Your California Privacy Rights</h3>",
"<p>If you are a California resident, you are entitled to prevent sharing of your personal information with third parties for their own marketing purposes through a cost-free means. If you send a request to the address above, Free Code Camp will provide you with a California Customer Choice Notice that you may use to opt-out of such information sharing. To receive this notice, submit a written request to <a href=\"mailto:team@freecodecamp.com\" target=\"_blank\">team@freecodecamp.com</a>, specifying that you seek your &quot;California Customer Choice Notice.&quot; Please allow at least thirty (30) days for a response.</p>",
"<h3>Acceptance of Privacy Policy Terms and Conditions</h3>",
"<p>By using this site, you signify your agreement to the terms and conditions of this FreeCodeCamp.com Privacy Policy. If you do not agree to these terms, please do not use this site. We reserve the right, at our sole discretion, to change, modify, add, or remove portions of this policy at any time. All amended terms automatically take effect 30 days after they are initially posted on the site. Please check this page periodically for any modifications. Your continued use of FreeCodeCamp.com following the posting of any changes to these terms shall mean that you have accepted those changes.</p>",
"<p>If you have any questions or concerns, please send an e-mail to <a href=\"mailto:team@freecodecamp.com\" target=\"_blank\">team@freecodecamp.com</a>.</p>",
"</div>"
]
}
]

170
seed_data/nonprofits.json Normal file
View File

@ -0,0 +1,170 @@
[
{
"id": "bd7157d8c441cbafaeb5bdef",
"requestedDeliverables": [
"website",
"donor",
"inventory",
"volunteer",
"form"
],
"whatDoesNonprofitDo": "We help the many less-fortunate Jewish families in our community, by providing them with nutritious food and energy to grow, learn, work, and give them hope for a better and brighter future.",
"websiteLink": "http://chasdeikaduri.org/",
"stakeholderName": "Jonathan Tebeka",
"stakeholderEmail": "jonathan@chasdeikaduri.org",
"name": "Chasdei Kaduri",
"endUser": "Clients, donors, and admin.",
"approvedDeliverables": [
"website",
"donor",
"inventory",
"volunteer",
"form"
],
"projectDescription": "Campers will create a system will integrate the food inventory, donor and delivery driver management systems as well as replace the current application system with a custom form solution. System will include a more streamlined operations management, with user printable lists of inventory, drivers, and deliveries.",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c7e02f2c173c37015b2f36/604x309/00580a0567a4b3afda29d52b09e7e829/rQQ6zwq31Uya8ie9QHC-MlvfXxqftm9UPPe524JUhmwSEaZjQ7oL7U1tVoHLUj-gVUwM-7uzBGFsAXD_A_cx_JyAZP4Td-GMBJ-AebJNRAQP0m0v253eKMkURp63aG4%3Ds0-d-e1-ft.png",
"imageUrl": "http://chasdeikaduri.org/images/523455_516325865106850_1885515210_n.jpg",
"estimatedHours": 200,
"currentStatus": "started"
},
{
"id": "bd7158d8c464cbafaeb4bdef",
"requestedDeliverables": [
"other"
],
"whatDoesNonprofitDo": "We connect simple technology with last mile communities to reduce poverty.",
"websiteLink": "http://kopernik.info/",
"stakeholderName": "Amber Gregory",
"stakeholderEmail": "amber.gregory@kopernik.info",
"name": "Kopernik",
"endUser": "Women in rural Indonesia.",
"approvedDeliverables": [
"other"
],
"projectDescription": "Campers will create a Chrome browser extension to preserve sales data from a form, and upload in batches as the internet connection allows.",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d29f1e4c726fd765fa87ef/54d29f6388812dd367a243ab/x/018d9d3be5439870c56cccba5b3aa8bf/kopernik-logo-global.png",
"imageUrl": "http://kopernik.info/sites/default/files/updates/Presenting_the_low_carbon_t.jpg",
"estimatedHours": 100,
"currentStatus": "completed"
},
{
"id": "bd1326d9c245cbafaeb4bdef",
"requestedDeliverables": [
"website"
],
"whatDoesNonprofitDo": "We distribute biodegradable toothbrushes globally to children in need.",
"websiteLink": "http://www.operationbrush.org/",
"stakeholderName": "Dane Jonas",
"stakeholderEmail": "DaneJonas@operationbrush.org",
"name": "Operation Brush",
"endUser": "Donors",
"approvedDeliverables": [
"website"
],
"projectDescription": "Campers will create a mobile responsive website for the organization, with donation capabilities.",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d9810307b159a4d9027aa2/54d981bfe5eb145560fbb769/x/cf7f318bfe4aee631b0d0eeef272225c/logo.png",
"imageUrl": "http://www.operationbrush.org/images/temp/hands1.png",
"estimatedHours": 100,
"currentStatus": "started"
},
{
"id": "bd1325d8c464cbafaeb5bdef",
"requestedDeliverables": [
"community"
],
"whatDoesNonprofitDo": "We are the largest roller derby league in the world with around 250 adults and 150 junior skater members plus 500+ volunteers.",
"websiteLink": "http://www.rosecityrollers.com/about/our-charities/",
"stakeholderName": "Charity Kuahiwinui",
"stakeholderEmail": "insurance@rosecityrollers.com",
"name": "Rose City Rollers",
"endUser": "Administrators, Coaches, and Volunteers",
"approvedDeliverables": [
"community"
],
"projectDescription": "Campers will create a volunteer management system with multi-user access and reporting capabilities.",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c1daf2d72d8eb868910b60/54c1dd4ecffcb09fc52b68a1/x/a8148f08769b449217e433bab8f39ddd/RCR-color.jpg",
"imageUrl": "http://www.rosecityrollers.com/wp-content/uploads/2015/01/BZ7_5923-X3-675x375.jpg",
"estimatedHours": 200,
"currentStatus": "started"
},
{
"id": "bd1325d8c464cbafaeb6bdef",
"requestedDeliverables": [
"website"
],
"whatDoesNonprofitDo": "Save a Child's Heart provides urgently needed pediatric heart surgery and follow-up care for indigent children from developing countries",
"websiteLink": "http://www.saveachildsheart.com/global/young-leadership-program/",
"stakeholderName": "Shier Ziser",
"stakeholderEmail": "Shier_z@hotmail.com",
"name": "Save a Child's Heart",
"endUser": "Donors",
"approvedDeliverables": [
"website"
],
"projectDescription": "Campers will create a single page fundraising website. In exchange for a donation, a user can customize a graphical 'heart' in someone's name or anonymously. The page will display all of the hearts on a 'wall of hearts.'",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/548b36629137780091a973cc/666x666/6c7a366ffb659649f6377d4a431687cd/country-logos-1-300dpi.jpg",
"imageUrl": "http://www.saveachildsheart.com/wp-content/uploads/2013/10/7.2.5_Internation_Photograohy_Exhibition.jpg",
"estimatedHours": 100,
"currentStatus": "completed"
},
{
"id": "bd1325d8c464cbafaeb4bdef",
"requestedDeliverables": [
"website"
],
"whatDoesNonprofitDo": "Savvy Cyber Kids enables youth to be empowered with technology by providing age appropriate resources and education.",
"websiteLink": "http://savvycyberkids.org/",
"stakeholderName": "Ben Halpert",
"stakeholderEmail": "info@savvycyberkids.org ",
"name": "Savvy Cyber Kids",
"endUser": "Donors",
"approvedDeliverables": [
"website"
],
"projectDescription": "Campers will create a website where potential donors can view which schools already have the Savvy Cyber Kids books, and donate books to those schools that do not.",
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54ee3c7bf205562680177b59/218x190/1dc460de4edc9fdd4b481b24e93cfb23/logo.png",
"imageUrl": "http://www.privatewifi.com/wp-content/uploads/2014/10/Halpert.jpg",
"estimatedHours": 200,
"currentStatus": "started"
},
{
"id": "bd1325d8c464cbafaeb7bdef",
"requestedDeliverables": [
"other"
],
"whatDoesNonprofitDo": "Transcendent Pathways",
"websiteLink": "http://transcendentpathways.org/",
"stakeholderName": "Mark Ackerley",
"stakeholderEmail": "mackerley.music@gmail.com",
"name": "Transcendent Pathways",
"endUser": "Medical Facilities, Musicians",
"approvedDeliverables": [
"other"
],
"projectDescription": "Campers will build a website where medical facilities can list music therapy time slots, and musicians can sign up to fill these slots.",
"logoUrl": "http://static1.squarespace.com/static/521b8957e4b024f66a58b214/t/521b8e9de4b093a8696eb9b8/1398718364447/?format=750w",
"imageUrl": "https://trello-attachments.s3.amazonaws.com/54fdb0328917ca64e9e8a79f/54fdc3b710f67caf6da14719/x/49fbe0012179bf254928f3f2a44810b4/Screen_2BShot_2B2013-08-26_2Bat_2B1.32.35_2BPM.png",
"estimatedHours": 200,
"currentStatus": "started"
},
{
"id": "bd1325d8c464cbafaeb8bdef",
"requestedDeliverables": [
"other"
],
"whatDoesNonprofitDo": "Timeraiser is a volunteer matching fair, a silent art auction, and a night out on the town. The big Timeraiser twist is rather than bid money on artwork, participants bid volunteer hours. ",
"websiteLink": "http://www.timeraiser.ca/",
"stakeholderName": "Stephanie McAllister",
"stakeholderEmail": "stephanie@timeraiser.ca",
"name": "Timeraiser",
"endUser": "Eventgoers",
"approvedDeliverables": [
"other"
],
"projectDescription": "Campers will build a mobile responsive web form to allow Timeraiser eventgoers to select which nonprofit organizations they're interested in volunteering with. System will have Salesforce integration and reporting capabilities.",
"logoUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/1277176.png?480",
"imageUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/______________4571248_orig.png",
"estimatedHours": 100,
"currentStatus": "started"
}
]

View File

@ -1,17 +1,19 @@
require('dotenv').load();
var Challenge = require('../models/Challenge.js'),
Bonfire = require('../models/Bonfire.js'),
var Bonfire = require('../models/Bonfire.js'),
Courseware = require('../models/Courseware.js'),
FieldGuide = require('../models/FieldGuide.js'),
Nonprofit = require('../models/Nonprofit.js'),
mongoose = require('mongoose'),
secrets = require('../config/secrets'),
challenges = require('./challenges.json'),
coursewares = require('./coursewares.json'),
fieldGuides = require('./field-guides.json'),
nonprofits = require('./nonprofits.json'),
bonfires = require('./bonfires.json');
mongoose.connect(secrets.db);
var counter = 0;
var offerings = 3;
var offerings = 4;
var CompletionMonitor = function() {
counter++;
@ -22,24 +24,8 @@ var CompletionMonitor = function() {
} else {
process.exit(0);
}
}
};
Challenge.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
Challenge.create(challenges, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
CompletionMonitor();
});
console.log('challenges');
});
Bonfire.remove({}, function(err, data) {
if (err) {
@ -74,3 +60,37 @@ Courseware.remove({}, function(err, data) {
});
console.log('coursewares');
});
FieldGuide.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
FieldGuide.create(fieldGuides, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
CompletionMonitor();
});
console.log('field guides');
});
Nonprofit.remove({}, function(err, data) {
if (err) {
console.error(err);
} else {
console.log('Deleted ', data);
}
Nonprofit.create(nonprofits, function(err, data) {
if (err) {
console.log(err);
} else {
console.log('Saved ', data);
}
CompletionMonitor();
});
console.log('nonprofits');
});

108
seed_data/userMigration.js Normal file
View File

@ -0,0 +1,108 @@
require('dotenv').load();
var mongodb = require('mongodb'),
User = require('../models/User.js'),
newChallenges = require('./challengeMapping.json'),
secrets = require('../config/secrets');
mongoose = require('mongoose');
mongoose.connect(secrets.db);
function userModelAssurity(cb) {
console.log('userModelAssurity');
var i = 1;
var stream = User.find({}).skip(0).limit(0).batchSize(20000).stream();
stream.on('data', function (user) {
console.log(i++);
this.pause();
user.needsMigration = true;
user.save(function (err) {
if (err) {
console.log('woops');
}
this.resume();
}.bind(this));
})
.on('error', function (err) {
console.log(err);
}).on('close', function () {
console.log('done with set');
stream.destroy();
cb();
});
}
function migrateIt() {
console.log('migrateIt');
var dones = 0;
var done = function() {
dones++;
if (dones === 2) {
process.exit(0);
}
if (dones === 1) {
userModelMigration(done);
}
};
console.log('calling userModelAssurity');
userModelAssurity(done);
}
function userModelMigration(cb) {
var i = 1;
var stream = User.find({needsMigration: true}).skip(0).limit(0)
.batchSize(20000).stream();
stream.on('data', function (user) {
console.log(i++);
if (user.challengesHash) {
this.pause();
user.needsMigration = false;
var oldChallenges = Object.keys(user.challengesHash).filter(function (key) {
if (user.challengesHash[key]) {
user.progressTimestamps.push(user.challengesHash[key] * 1000);
}
return user.challengesHash[key];
});
newChallenges.forEach(function (challenge) {
if (oldChallenges.indexOf(challenge.oldNumber) !== -1 && challenge.newId) {
user.completedCoursewares.push({
_id: challenge.newId,
completedDate: user.challengesHash[challenge.oldNumber] * 1000
});
}
});
user.completedCoursewares.forEach(function (course) {
var indexOfCourse = user.uncompletedCoursewares.indexOf(course._id) !== -1;
if (indexOfCourse !== -1) {
user.uncompletedCoursewares.splice(indexOfCourse, 1);
}
});
user.completedBonfires.forEach(function (bonfire) {
bonfire.completedDate = bonfire.completedDate * 1000;
user.progressTimestamps.push(bonfire.completedDate);
});
}
var self = this;
user.save(function (err) {
if (err) {
console.log('woops');
}
self.resume();
});
}).on('error', function (err) {
console.log(err);
}).on('close', function () {
console.log('done with set');
stream.destroy();
cb();
});
}
migrateIt();

View File

@ -176,15 +176,11 @@ block content
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='website1Image') Image Link (4:3 ratio)
.col-sm-4
input.form-control(type='url', name='website1Image', id='website1Image', autocomplete="off", ng-model='user.portfolio.website1Image', placeholder='http://www.example.com/image.jpg', ng-pattern="/[\.](jpg|png|jpeg|gif)\s?$/")
input.form-control(type='url', name='website1Image', id='website1Image', autocomplete="off", ng-model='user.portfolio.website1Image', placeholder='http://www.example.com/image.jpg')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Image.$error.url && !profileForm.website1Image.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com/image.jpg).
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Image.$error.pattern")
alert(type='danger')
span.ion-close-circled
| The image URL must end in .jpg, .png, .jpeg or .gif.
.col-sm-4.col-sm-offset-5.flat-top
h3 Second Portfolio Project
@ -209,15 +205,11 @@ block content
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='website2Image') Image Link (4:3 ratio)
.col-sm-4
input.form-control(type='url', name='website2Image', id='website2Image', autocomplete="off", ng-model='user.portfolio.website2Image', placeholder='http://www.example.com/image.jpg', ng-pattern="/[\.](jpg|png|jpeg|gif)\s?$/")
input.form-control(type='url', name='website2Image', id='website2Image', autocomplete="off", ng-model='user.portfolio.website2Image', placeholder='http://www.example.com/image.jpg')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Image.$error.url && !profileForm.website2Image.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com/image.jpg).
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Image.$error.pattern")
alert(type='danger')
span.ion-close-circled
| The image URL must end in .jpg, .png, .jpeg or .gif.
.col-sm-4.col-sm-offset-5.flat-top
h3 Third Portfolio Project
@ -248,10 +240,6 @@ block content
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com/image.jpg).
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Image.$error.pattern")
alert(type='danger')
span.ion-close-circled
| The image URL must end in .jpg, .png, .jpeg or .gif.
.form-group
.col-sm-offset-5.col-sm-4

View File

@ -1,5 +1,7 @@
extends ../layout
block content
script.
var challengeName = 'Profile View';
.col-xs-12.col-sm-12.col-md-12
.panel.panel-info
.panel-heading.text-center
@ -8,7 +10,7 @@ block content
if (user && user.profile.username === username)
.col-xs-12
.text-center
a.btn.btn-big.btn-primary(href="/account") Update my public portfolio or manage my account
a.btn.btn-big.btn-primary(href="/account") Update my portfolio page or manage my account
br
.row
.col-xs-12
@ -39,7 +41,7 @@ block content
.col-xs-12.col-sm-12.col-md-3.text-center
.background-svg.img-center
.points-on-top
= "[ " + points + " ]"
= "[ " + (user ? user.progressTimestamps.length : 0) + " ]"
.row
@ -47,7 +49,7 @@ block content
if (website1Title && website1Link && website1Image)
.row
.col-xs-12.col-md-5
img.img-center.img-responsive.portfolio-image(src=website1Image, alt="@#{username}'s #{website1Title")
img.img-center.img-responsive.portfolio-image(src=website1Image, alt="@#{username}'s #{website1Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website1Title
a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank')
@ -64,7 +66,7 @@ block content
if (website2Title && website2Link && website2Image)
.row
.col-xs-12.col-md-5
img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title")
img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website2Title
a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank')
@ -81,7 +83,7 @@ block content
if (website3Title && website3Link && website3Image)
.row
.col-xs-12.col-md-5
img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title")
img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website3Title
a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank')
@ -94,16 +96,65 @@ block content
i.fa.icon-beaker
| Try it out
- if (ch[0] > 0)
.col-sm-12
table.table.table-striped
thead
tr
th Challenge
th Date Finished
for challenge in challenges
if ch[challenge.challengeNumber] > 0
tr
td= challenges[challenge.challengeNumber].name
td= moment(ch[challenge.challengeNumber], 'X').format("MMM DD, YYYY")
br
.hidden-xs.col-sm-12
script(src="//d3js.org/d3.v3.min.js")
script(src="//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.min.js")
#cal-heatmap.img-center
script.
$(document).ready(function() {
setTimeout(function() {
var cal = new CalHeatMap();
var calendar = !{JSON.stringify(calender)};
cal.init({
itemSelector: "#cal-heatmap",
domain: "month",
subDomain: "day",
data: calendar,
cellSize: 15,
align: 'center',
cellRadius: 3,
cellPadding: 2,
tooltip: true,
range: 4,
start: new Date().setDate(new Date().getDate() - 90),
legendColors: ["#cccccc", "#215f1e"],
legend: [1, 2, 3]
});
}, 300);
});
.row
.hidden-xs.col-sm-12.text-center
.row.text-primary
h4.col-sm-6.text-right Longest Streak: #{longestStreak}
h4.col-sm-6.text-left Current Streak: #{currentStreak}
- if (challenges.length > 0)
.col-sm-12
table.table.table-striped
thead
tr
th.col-xs-4 Challenge
th.col-xs-2 Completed
th.col-xs-6 Link
for challenge in challenges
tr
td.col-xs-4= challenge.name
td.col-xs-2= moment(challenge.completedDate, 'x').format("MMM DD, YYYY")
td.col-xs-6
a(href=challenge.solution) View my solution
br
- if (bonfires.length > 0)
.col-sm-12
table.table.table-striped
thead
tr
th.col-xs-4 Bonfire
th.col-xs-2 Completed
th.col-xs-6 Solution
for bonfire in bonfires
tr
td.col-xs-4= bonfire.name
td.col-xs-2= moment(bonfire.completedDate, 'x').format("MMM DD, YYYY")
td.col-xs-6= bonfire.solution
br

View File

@ -21,7 +21,7 @@ block content
.row
.col-xs-12.col-sm-12.col-md-4.bonfire-top
#testCreatePanel
h1.text-center= name
h1#bonfire-name.text-center= name
h2.text-center
.bonfire-flames Difficulty:&thinsp;
if (difficulty == "0")
@ -80,7 +80,7 @@ block content
#less-info.btn.btn-primary.btn-block.btn-primary-ghost
span.ion-arrow-up-b
| Less information
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
br
form.code
@ -94,7 +94,7 @@ block content
var challengeSeed = !{JSON.stringify(challengeSeed)};
var passedBonfireHash = !{JSON.stringify(bonfireHash)};
var challengeName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
var started = Math.floor(Date.now());
var _ = R;
var dashed = !{JSON.stringify(dashedName)};
@ -116,7 +116,7 @@ block content
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
- if (user)
form.form-horizontal(novalidate='novalidate', name='completedWithForm')
.form-group.text-center
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2.animated.fadeIn
@ -131,7 +131,7 @@ block content
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-bonfire-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next bonfire (ctrl + enter)
- if (points && points > 2)
- if (user.progressTimestamps.length > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
@ -139,19 +139,7 @@ block content
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
$.ajax({
url: 'https://api-ssl.bitly.com/v3/shorten?access_token=75e7931a19befaafcf108021b6d597e554b2c5c3&longUrl=http%3A%2F%2Ffreecodecamp.com%2Fbonfires%2F' + dashed + '&format=txt'
})
.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";
$('.btn-twitter').attr('href', url);
}
);
var MDNlinks = !{JSON.stringify(MDNlinks)};
if (!MDNlinks.length) {
$('#MDN-links').addClass('collapse');
}

View File

@ -0,0 +1,71 @@
extends ../layout
block content
.col-xs-12.col-sm-12.col-md-12
.panel.panel-info
.panel-heading.text-center
h1 Challenge Map
.panel-body
.row
.col-xs-12.col-sm-12.col-md-8.col-md-offset-2
h3 You must complete all of these challenges before you can start working on nonprofit projects.
h3 You should complete these in order from top to bottom.
h2
span.fa.fa-flag
| &nbsp; Waypoints (web development lessons)
.col-xs-12
h3.negative-15
ol
for waypoint in waypoints
if completedCoursewareList.indexOf(waypoint._id) > -1
li.strikethrough
a(href="/challenges/#{waypoint.name}")= waypoint.name
else
li
a(href="/challenges/#{waypoint.name}")= waypoint.name
h2
span.ion-bonfire
| &nbsp; Bonfires (algorithm practice)
.col-xs-12
h3.negative-15
ol
for bonfire in bonfires
if completedBonfireList.indexOf(bonfire._id) > -1
li.strikethrough
a(href="/bonfires/#{bonfire.name}")= bonfire.name
else
li
a(href="/bonfires/#{bonfire.name}")= bonfire.name
h2
span.fa.fa-angle-double-right
| &nbsp; Ziplines (front end development practice)
.col-xs-12
h3.negative-15
ol
for zipline in ziplines
if completedCoursewareList.indexOf(zipline._id) > -1
li.strikethrough
a(href="/challenges/#{zipline.name}")= zipline.name
else
li
a(href="/challenges/#{zipline.name}")= zipline.name
h2
span.fa.fa-level-down
| &nbsp; Basejumps (full stack development practice)
.col-xs-12
h3.negative-15
ol
for basejump in basejumps
if completedCoursewareList.indexOf(basejump._id) > -1
li.strikethrough
a(href="/challenges/#{basejump.name}")= basejump.name
else
li
a(href="/challenges/#{basejump.name}")= basejump.name
h2
span.ion-ios-heart
| &nbsp; Nonprofit Projects
h3.negative-15
ul
li
a(href="/nonprofits/directory") Browse our nonprofit projects

View File

@ -36,13 +36,7 @@ block content
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
#all-challenges-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.challenge-list-header Challenges
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
include ../partials/challenges
script.
$.ajax({
url: 'https://api-ssl.bitly.com/v3/shorten?access_token=75e7931a19befaafcf108021b6d597e554b2c5c3&longUrl=http%3A%2F%2Ffreecodecamp.com%2Fchallenges%2F' + !{JSON.stringify(number)} + '&format=txt'
@ -53,4 +47,4 @@ block content
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

@ -38,22 +38,19 @@ block content
span.ion-arrow-up-b
| Less information
br
- if (cc)
a.btn.btn-primary.btn-lg.btn-block#next-courseware-button
- if (user)
a.btn.btn-primary.btn-big.btn-block#next-courseware-button
| Go to my next challenge
br
| (ctrl + enter)
script.
var userLoggedIn = true;
- else
a.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
var userLoggedIn = false;
br
script.
var userLoggedIn = false;
.button-spacer
ul#testSuite.list-group
br
script(type="text/javascript").
$('#next-courseware-button').attr('disabled', 'disabled');
@ -61,8 +58,10 @@ block content
var challengeSeed = !{JSON.stringify(challengeSeed)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var passedCoursewareName = challengeName;
var prodOrDev = !{JSON.stringify(environment)};
var started = Math.floor(Date.now() / 1000);
var challengeType = !{JSON.stringify(challengeType)};
var started = Math.floor(Date.now());
.col-xs-12.col-sm-12.col-md-5.col-lg-6
#mainEditorPanel
form.code
@ -72,8 +71,6 @@ block content
.hidden-xs.hidden-sm
img.iphone-position(src="https://s3.amazonaws.com/freecodecamp/iphone6-frame.png")
iframe.iphone#preview
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content

View File

@ -47,7 +47,10 @@ block content
var challengeSeed = !{JSON.stringify(challengeSeed)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
var challengeType = !{JSON.stringify(challengeType)};
var passedCoursewareName = challengeName;
var started = Math.floor(Date.now());
.col-xs-12.col-sm-12.col-md-8
#mainEditorPanel
form.code
@ -63,12 +66,11 @@ block content
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a#next-courseware-button.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-courseware-button(name='_csrf', value=_csrf) Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
- if (user)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress

View File

@ -7,43 +7,53 @@ block content
h4
ol
for step in details
li!= step
.row
li
.col-xs-2
input(type='checkbox' class='challenge-list-checkbox')
.col-xs-10.step-text!= step
.col-xs-12.col-sm-12.col-md-8
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/#{video}')
br
- if (cc)
a.btn.btn-primary.btn-lg.btn-block#next-courseware-button Go to my next challenge (ctrl + enter)
- if (user)
a.btn.btn-primary.btn-big.btn-block#completed-courseware I've completed this challenge (ctrl + enter)
script.
var userLoggedIn = true;
- else
a.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
a.btn.btn-big.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
var userLoggedIn = false;
br
script(type="text/javascript").
.button-spacer
script(type="text/javascript").
var tests = !{JSON.stringify(tests)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
var passedCoursewareName = challengeName;
var started = Math.floor(Date.now());
var challengeType = !{JSON.stringify(challengeType)};
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header= compliment
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body(ng-controller="pairedWithController")
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (points && points > 2)
- if (user)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf) Go to my next challenge (ctrl + enter)
- if (user.progressTimestamps.length > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
h1 #{name}
script.
var challengeName = !{JSON.stringify(name)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
script.
$('body').on('keypress', function(e) {
if (e.ctrlKey && e.keyCode == 13) {
$('#complete-courseware-dialog').modal('show');
}
});

View File

@ -0,0 +1,76 @@
extends ../layout-wide
block content
.row
.col-xs-12.col-sm-12.col-md-4.bonfire-top
h1.text-center= name
.well
h4
ol
for step in details
.row
li
.col-xs-2
input(type='checkbox' class='challenge-list-checkbox')
.col-xs-10.step-text!= step
.col-xs-12.col-sm-12.col-md-8
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/#{video}')
br
- if (user)
a.btn.btn-primary.btn-lg.btn-block#completed-zipline-or-basejump I've completed this challenge (ctrl + enter)
script.
var userLoggedIn = true;
- else
a.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
var userLoggedIn = false;
br
script(type="text/javascript").
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var passedCoursewareName = challengeName;
var started = Math.floor(Date.now());
var challengeType = !{JSON.stringify(challengeType)};
#complete-zipline-or-basejump-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header= compliment
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body(ng-controller="pairedWithController")
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (user)
form.form-horizontal(novalidate='novalidate', name='completedWithForm')
.form-group.text-center
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2.animated.fadeIn
// extra field to distract password tools like lastpass from injecting css into our username field
input.form-control(ng-show="false")
if (challengeType === 3)
input.form-control#public-url(name="codepenUrl", placeholder="http://codepen.io/your-pen-here", autofocus)
else
input.form-control#public-url(name="depoloymentUrl", placeholder="http://yourapp.com", autofocus)
input.form-control#github-url(name="githubUrl", placeholder="http://github.com/camper/project")
input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser")
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-cloak, ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0")
alert(type='danger')
span.ion-close-circled
| Username not found
if (user)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (user.progressTimestamps.length > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
$('body').on('keypress', function(e) {
if (e.ctrlKey && e.keyCode == 13) {
$('#complete-zipline-or-basejump-dialog').modal('show');
}
});

View File

@ -0,0 +1,42 @@
extends ../layout
block content
script.
var challengeName = 'Field Guide View';
.col-xs-12.col-sm-12.col-md-12
.panel.panel-info
.panel-heading.text-center
h1= title
.panel-body
.row
.col-xs-12
div!= description
.spacer
.spacer
.spacer
.col-xs-12.col-sm-6.col-sm-offset-3
.text-center
.next-field-guide-button.btn.btn-primary.btn-big.btn-block Next article (ctrl + enter)
.ten-pixel-break
#showAllButton.btn.btn-info.btn-big.btn-block Show me all articles
.spacer
.row
.col-xs-12.text-center
if !user
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
.spacer
#show-all-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.all-list-header Field Guide Articles
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
include ../partials/field-guide
#fieldGuideId.hidden= fieldGuideId
script.
$(document).ready(function() {
$('body').keydown(function(e) {
if (e.ctrlKey && e.keyCode == 13) {
$('.next-field-guide-button').click();
}
});
});

View File

@ -89,4 +89,4 @@ block content
.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

@ -0,0 +1,12 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 We build solutions for nonprofits who are already serving a need. Are there people who already benefit from your services?
.spacer
.row
.col-xs-6
a.btn.btn-primary.btn-big.btn-block(href='/nonprofits/ok-with-javascript') Yes
.col-xs-6
a.btn.btn-warning.btn-big.btn-block(href='/nonprofits/other-solutions') No

View File

@ -0,0 +1,12 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Do you represent a nonprofit organization that is registered with your government?
.spacer
.row
.col-xs-6
a.btn.btn-primary.btn-big.btn-block(href='/nonprofits/are-there-people-that-are-already-benefiting-from-your-services') Yes
.col-xs-6
a.btn.btn-warning.btn-big.btn-block(href='/nonprofits/other-solutions') No

View File

@ -0,0 +1,19 @@
extends ../layout
block content
script.
var challengeName = 'Nonprofits View';
.col-xs-12.col-sm-12.col-md-12
.panel.panel-info
.panel-heading.text-center Nonprofits We Help
.panel-body
for nonprofit in nonprofits
.spacer
.row
.col-xs-12.col-sm-3
img.img-responsive(src=nonprofit.logoUrl)
.col-xs-12.col-sm-9
h2.negative-15= nonprofit.name
h3.negative-15= nonprofit.whatDoesNonprofitDo
a.text-center.btn.btn-primary.btn-lg(href='/nonprofits/' + nonprofit.name.toLowerCase().replace(/\s/g, '-')) Read more
.spacer
.spacer

View File

@ -0,0 +1,83 @@
extends ../layout
block content
.jumbotron
.text-center
h2.nonprofit-landing.hug-top We'll code for your nonprofit, pro bono
.big-break
h2 Some of our success stories
.row
.col-xs-12.col-sm-12.col-md-4
img.img-responsive.testimonial-image.img-center(src="https://s3.amazonaws.com/freecodecamp/testimonial-jen.jpg", alt="@jenthebest's testimonial image")
.testimonial-copy Getting back on track with Free Code Camp and committing to a new career in 2015!
h3 - @jenbestyoga
.col-xs-12.col-sm-12.col-md-4
img.img-responsive.testimonial-image.img-center(src="https://s3.amazonaws.com/freecodecamp/testimonial-tate.jpg", alt="@TateThurston's testimonial image")
.testimonial-copy Just built my company's website with skills I've learned from Free Code Camp!
h3 - @TateThurston
.col-xs-12.col-sm-12.col-md-4
img.img-responsive.testimonial-image.img-center(src="https://s3.amazonaws.com/freecodecamp/testimonial-cynthia.jpg", alt="@cynthialanel's testimonial image")
.testimonial-copy I'm currently working through Free Code Camp to improve my JavaScript. The community is very welcoming!
h3 - @cynthialanel
.big-break
a.btn.btn-cta.signup-btn(href="/nonprofits/are-you-with-a-registered-nonprofit") Get pro bono help for my nonprofit
.big-break
h2 Our process
.row
.col-xs-12.col-sm-12.col-md-4
h3.nowrap Your idea
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_portfolio.svg.gz', title='Get great references and connections to help you get a job')
p.landing-p You tell us how we can help you.
.col-xs-12.col-sm-12.col-md-4
h3.nowrap Our team
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_nonprofits.svg.gz', title='Build a portfolio of apps for nonprofits')
p.landing-p We'll hand pick developers and a project manager.
.col-xs-12.col-sm-12.col-md-4
h3.nowrap Your solution
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_connect.svg.gz', title='Help nonprofits')
p.landing-p Together we'll set milestones and complete your project.
.big-break
h2 Solutions we can help you build:
.text-center.negative-35
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-android-globe
h2.black-text Websites
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-card
h2.black-text Donation Systems
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-android-calendar
h2.black-text Volunteer Systems
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-ios-box
h2.black-text Inventory Systems
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-university
h2.black-text E-learning Platforms
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-ios-list
h2.black-text Web Forms
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-ios-people
h2.black-text Community Tools
.col-xs-12.col-sm-12.col-md-3
.landing-skill-icon.ion-settings
h2.black-text ...and other tools
br
br
.big-break
h2 Why you should join our community right now:
h3.col-xs-offset-0.col-sm-offset-1
ul.text-left
li.ion-code &thinsp; We're thousands of professionals, all learning to code together
li.ion-code &thinsp; We're building projects for dozens of nonprofits
li.ion-code &thinsp; Our community is 100% free and open source
li.ion-code &thinsp; You'll learn Full Stack JavaScript and become a Software Engineer
li.ion-code &thinsp; You'll work through our focused, interactive courses and tutorials
li.ion-code &thinsp; You'll learn to code at your own pace, in your browser or on your phone
li.ion-code &thinsp; You'll become qualified for thousands of jobs currently going unfilled
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="/nonprofits/are-you-with-a-registered-nonprofit") Get pro bono help for my nonprofit
script.
challengeName = 'Home'

View File

@ -0,0 +1,33 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 How can Free Code Camp help you?
.text-left.form-inline
h3.col-xs-12.col-sm-offset-5.checkbox-table
.col-xs-12
input.checkbox(type='checkbox', id='website')
label.ion-android-globe &nbsp; Websites
.col-xs-12
input.checkbox(type='checkbox', id='donation')
label.ion-card &nbsp; Donation Systems
.col-xs-12
input.checkbox(type='checkbox', id='volunteer')
label.ion-android-calendar &nbsp; Volunteer Systems
.col-xs-12
input.checkbox(type='checkbox', id='inventory')
label.ion-ios-box &nbsp; Inventory Systems
.col-xs-12
input.checkbox(type='checkbox', id='eLearning')
label.ion-university &nbsp; E-learning Platforms
.col-xs-12
input.checkbox(type='checkbox', id='form')
label.ion-ios-list &nbsp; Web Forms
.col-xs-12
input.checkbox(type='checkbox', id='community')
label.ion-ios-people &nbsp; Community Tools
.col-xs-12
input.checkbox(type='checkbox', id='other')
label.ion-settings &nbsp; Other tools
button#next-step.btn.btn-primary.btn-big.btn-block(type='submit') I've selected all that apply and am ready to move on

View File

@ -0,0 +1,17 @@
extends ../layout
block content
.jumbotron
h1.hug-top.text-center Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Great! In exchange for our help, we ask only that you:
h3
ol
li Appoint one principal stakeholder to serve on behalf of your organization.
li Communicate with our campers on a regular basis, to answer questions and provide them with direction.
li Commit to using the solution that our campers build for your nonprofit.
.spacer
.row
.col-xs-6
a.btn.btn-primary.btn-big.btn-block(href='/nonprofits/how-can-free-code-camp-help-you') Sounds good!
.col-xs-6
a.btn.btn-warning.btn-big.btn-block(href='/nonprofits/other-solutions') This might not be for us.

View File

@ -0,0 +1,14 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Link us to the website, blog, or social media page that best represents your organization.
.spacer
form(role='form', method='GET', action="/nonprofits/tell-us-your-email/?" + existingParams)
input(type='hidden', name='_csrf', value=_csrf)
.formgroup
.input-group
input.form-control.big-text-field.field-responsive(type='text', name='link', autocomplete='off', maxlength='500', autofocus='')
span.input-group-btn
button.btn.btn-big.btn-primary.btn-responsive Submit

View File

@ -0,0 +1,12 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Our campers are learning to code using modern full stack JavaScript technologies like Node.js. We do not build or maintain Wordpress, Drupal, or other non-JavaScript based frameworks.
.spacer
.row
.col-xs-6
a.btn.btn-primary.btn-big.btn-block(href='/nonprofits/in-exchange-we-ask') Sounds good!
.col-xs-6
a.btn.btn-warning.btn-big.btn-block(href='/nonprofits/other-solutions') This might not be for us.

View File

@ -0,0 +1,65 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Here are some other solutions we recommend
.spacer
h2 Please note that Free Code Camp is not partnered with, nor do we receive a referral fee from, any of the following providers. We simply want to help guide you towards a solution for your organization.
h3 Skills-based Volunteer Organizations
ul
li
a(href='http://www.volunteermatch.com') Volunteer Match
li
a(href='http://www.catchafire.com') Catchafire
h3 DIY Websites
ul
li
a(href='http://www.wix.com') Wix
li
a(href='http://www.squarespace.com/') Square Space
h3 Donor and Volunteer Management Systems
ul
li
a(href='https://www.thedatabank.com/') The Data Bank
li
a(href='http://www.donorsnap.com/') DonorSnap
li
a(href='http://www.donorperfect.com/') Donor Perfect
li
a(href='https://www.blackbaud.com/fundraising-crm/etapestry-donor-management') eTapestry
li
a(href='http://www.z2systems.com/') NeonCRM
li
a(href='http://www.regpacks.com/volunteer-management/') Regpack
li
a(href='http://sumac.com/') Sumac
li
a(href='http://www.volgistics.com/') Volgistics
h3 Inventory Management Systems
ul
li
a(href='https://www.ezofficeinventory.com/industries/non-profits') EZ Office Inventory
li
a(href='https://www.ordoro.com') Ordoro
li
a(href='http://www.unleashedsoftware.com') Unleashed
h3 E-Learning platforms
ul
li
a(href='http://www.dokeos.com/') Dokeos
li
a(href='http://www.efrontlearning.net/') eFront
li
a(href='https://moodle.org/') Moodle
li
a(href='https://sakaiproject.org/') Sakai
h3 Community Management
ul
li
a(href='https://civicrm.org/') CiviCRM
li
a(href='http://tcmgr.com/') Total Community Manager
h3 Electronic Forms
ul
li
a(href='http://www.google.com/forms') Google Forms

View File

@ -0,0 +1,82 @@
extends ../layout
block content
script.
var challengeName = 'Nonprofits View';
.col-xs-12.col-sm-12.col-md-12
.panel.panel-info
.panel-heading.text-center= title
.panel-body
.row
.col-xs-12.col-sm-10.col-sm-offset-1
.row
.col-xs-12
img.img-center.img-responsive(src=imageUrl)
.spacer
.row
.col-xs-12.col-sm-4
img.img-responsive(src=logoUrl)
.col-xs-12.col-sm-8
.col-xs-12
h4= whatDoesNonprofitDo
h4
a(href=websiteLink)= websiteLink
.spacer
h3 Project Description:
.col-xs-12
h4.negative-15 #{projectDescription} (About #{estimatedHours} hours per camper)
.spacer
h3 This project involves building:
h4.negative-15.col-xs-12
if (approvedWebsite)
.ion-android-globe &nbsp; Website
if (approvedDonor)
.ion-card &nbsp; Donor Management System
if (approvedInventory)
.ion-ios-box &nbsp; Inventory Management System
if (approvedVolunteer)
.ion-android-calendar &nbsp; Volunteer Management System
if (approvedForm)
.ion-ios-list &nbsp; Webform
if (approvedCommunity)
.ion-ios-people &nbsp; Community Management System
if (approvedELearning)
.ion-university &nbsp; E-learning Platform
if (approvedOther)
.ion-settings &nbsp; Other tools
h3 Project Status: #{currentStatus}
if (interestedCampers && interestedCampers.length > 0)
h3 Interested campers:
.col-xs-12.text-left
for interestedCamper in interestedCampers
a(href='/' + interestedCamper.username class="interested-camper-image")
img.profile-picture.float-right(src=interestedCamper.picture)
if (assignedCampers && assignedCampers.length > 0)
h3 Assigned campers:
.col-xs-12.text-left
for assignedCamper in assignedCampers
a(href='/' + assignedCamper.username class="interested-camper-image")
img.profile-picture.float-right(src=assignedCamper.picture)
.spacer
if (!buttonActive)
.col-xs-12.col-sm-8.col-sm-offset-2
.text-center
a.btn.btn-primary.btn-big.btn-block.disabled(href='/nonprofits/interested-in-nonprofit/#{dashedName}') I'm interested in building this project *
p * Complete all our Bonfires, Ziplines, and Basejumps to unlock this.
#showAllButton.btn.btn-info.btn-big.btn-block Show all Nonprofits Projects
if (buttonActive)
.col-xs-12.col-sm-8.col-sm-offset-2
.text-center
a.btn.btn-primary.btn-big.btn-block(href='/nonprofits/interested-in-nonprofit/#{dashedName}') I'm interested in building this project
#showAllButton.btn.btn-info.btn-big.btn-block Show all Nonprofits Projects
.row
.col-xs-12.text-center
if !user
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
.spacer
#show-all-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.all-list-header Nonprofit Projects
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
include ../partials/nonprofits

View File

@ -0,0 +1,14 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Please tell us your email
.spacer
form(role='form', method='GET', novalidate='novalidate', name='nonprofitForm', action="/nonprofits/tell-us-your-name/")
input(type='hidden', name='_csrf', value=_csrf)
.formgroup
.input-group
input.form-control.big-text-field.field-responsive(type='text', name='email', autocomplete='off', maxlength='500', autofocus='')
span.input-group-btn
button.btn.btn-big.btn-primary.btn-responsive Submit

View File

@ -0,0 +1,13 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Please tell us your name
.spacer
form(role='form', method='POST', novalidate='novalidate', name='nonprofitForm', action="/nonprofits/finish-application/")
.formgroup
.input-group
input.form-control.big-text-field.field-responsive(type='text', name='name', autocomplete='off', maxlength='140', autofocus='')
span.input-group-btn
button.btn.btn-big.btn-primary.btn-responsive Submit

View File

@ -0,0 +1,28 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 In 140 characters or less, what does your nonprofit do? For whom?
.spacer
form(role='form', method='GET', action="/nonprofits/link-us-to-your-website/")
input(type='hidden', name='_csrf', value=_csrf)
.formgroup
.input-group
input.form-control.big-text-field.field-responsive#what-does-the-nonprofit-do(type='text', maxlength='140', autofocus='', autocomplete='off', name='mission')
span.input-group-btn
button.btn.btn-big.btn-primary.btn-responsive(href='/nonprofits/link-us-to-your-website') Submit
.text-left
span#what-does-the-nonprofit-do-feedback
script.
var text_max = 140;
$('#what-does-the-nonprofit-do-feedback').html(text_max + ' characters remaining');
$('#what-does-the-nonprofit-do').keyup(function (e) {
if (e.which === 13 || e === 13) {
$('#submit-comment-to-comment').click();
}
var text_length = $('#what-does-the-nonprofit-do').val().length;
var text_remaining = text_max - text_length;
$('#what-does-the-nonprofit-do-feedback').html(text_remaining + ' characters remaining');
});

View File

@ -0,0 +1,6 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Nonprofit Sign Up
include ../partials/nonprofit-application-progress-bar
h2 Thank you for reaching out to us. Well send you an email no later than #{getBackDay}.

View File

@ -1,7 +0,0 @@
h3
ol(start='0')
for bonfire in bonfires
li
a(href="/bonfires/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name}
| &thinsp; (Level #{bonfire.difficulty})
a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((cc && cc[53] === 0) || (!cc)) ? 'disabled' : '' }") I'm done with all the challenges!

View File

@ -1,8 +0,0 @@
h3
ol(start='0')
for challenge in challenges
li
a(href="/challenges/#{challenge.challengeNumber}", class="#{ (cc && cc[challenge.challengeNumber] > 0) ? 'strikethrough' : '' }") #{challenge.name}
| &thinsp; (#{challenge.time} mins)
a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((points && points < 54) || (!points)) ? 'disabled' : '' }") I'm done with all the challenges!

View File

@ -32,4 +32,16 @@ script.
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
else window.addEventListener('load', cb);
var cb = function () {
var l = document.createElement('link');
l.rel = 'stylesheet';
l.href = '//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.css';
var h = document.getElementsByTagName('head')[0];
h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);

View File

@ -1,75 +0,0 @@
.panel.panel-info
.panel-heading.landing-panel-heading.text-center Frequently Asked Questions
.panel-body
.landing-panel-body
.row
.text-left.col-xs-12.col-md-10.col-md-offset-1
h2 What will I learn, and in what sequence?
ul
p.landing-p First, you'll learn basic web design tools like:
ul
li.landing-p &#8226 HTML - the structure of web pages
li.landing-p &#8226 CSS - the visual style of web pages
li.landing-p &#8226 Bootstrap - a "responsive design" tool that helps your websites look great on tablets and phones
li.landing-p &#8226 jQuery - an easy tool for controlling content in the browser
li.landing-p &#8226 Chrome DevTools - a tool for understanding and debugging websites, right in your browser
p.landing-p Then you'll learn computer science and the art of programming:
ul
li.landing-p &#8226 JavaScript - the one programming language that all web browsers use
li.landing-p &#8226 Algorithms - step-by-step recipes for getting things done
li.landing-p &#8226 Automated Testing - write tests to test the limits of your code
p.landing-p You'll spend the last half of Free Code Camp using Agile Methodologies and Full Stack JavaScript to build projects for nonprofits:
ul
li.landing-p &#8226 Agile - a set of software development principles that focus the design and production of a project on the needs of its users
li.landing-p &#8226 Git - a version control system for saving and sharing your projects
li.landing-p &#8226 MongoDB - a popular non-relational database
li.landing-p &#8226 Angular.js - a tool for making exciting web interfaces
li.landing-p &#8226 Express.js - a powerful web development framework
li.landing-p &#8226 Node.js - a JavaScript-based web server
h2 Will I be ready to get a software engineer job after this?
ul
p.landing-p At the end of Free Code Camp, you will have pair programmed around 1,000 hours with dozens of other students, built a portfolio of projects that people are actively using, and a roster of glowing references from nonprofits you've helped. This is more coding than most coding bootcamps provide, and on average, 75% of bootcamp graduates get software engineering jobs within 6 months, and earn an average annual salary of $76,000.
img.img-center.img-responsive(src="https://s3.amazonaws.com/freecodecamp/table-of-earnings.png" alt="A chart showing the average earnings of coding bootcamp graduates")
h2 How long does Free Code Camp take?
ul
p.landing-p It takes about 1,000 hours of coding to develop the skills you'll need to get an entry level software engineering job. Most coding bootcamps try to jam all this into 12 weeks of intensive study. Free Code Camp is fully online, and there will always be other people at your skill level that you can pair program with, so you can learn at your own pace. Here are some example coding schedules:
table.table
thead
th Time budgeted
th.hidden-xs Hours per week
th Weeks to complete
tr.info
td Weekends
td.hidden-xs 10 hours/week
td 100 weeks (2 years)
tr.success
td Nights and Weekends
td.hidden-xs 20 hours/week
td 50 weeks (1 year)
tr.warning
td Full time
td.hidden-xs 40 hours/week
td 25 weeks (6 months)
tr.danger
td Traditional Bootcamp Pacing
td.hidden-xs 80 hours/week
td 12 weeks (3 months)
h2 Why does Free Code Camp use JavaScript instead of Ruby or Python?
ul
p.landing-p Like JavaScript, Ruby and Python are high-level scripting languages that can be used for full stack web development. But even if you learned these languages, you'd still need to learn JavaScript. That's because JavaScript is the only language that runs in web browsers. JavaScript has been around for 20 years, and it is still growing in popularity. Because of this, JavaScript has more tools and online learning resources than any other language.
img.img-center.img-responsive(src="https://s3.amazonaws.com/freecodecamp/github-repo-growth.png", style="max-height: 355px;" alt="A chart showing the volume of new GitHub repositories by year, with JavaScript growing and most languages declining.")
br
h2 How will I learn all this?
ul
p.landing-p By pair programming with other Free Code Camp students on our coding challenges. Eventually, you'll work with people at nonprofits to build real-life software solutions.
h2 What is 'pair programming', and what's so special about it?
ul
p.landing-p Pair programming is where two people code together on one computer. You discuss different approaches to solving problems, and keep each other motivated. The result is better code than either of you could have written by yourselves. Because of its benefits, many engineers pair program full time. And it's the best way to learn coding. Thanks to tools that allow two people to share mouse and keyboard inputs, you can pair program with a friend without needing to be in the same room.
h2 Is this really free? Do you claim part of my first year's salary like some bootcamps do?
ul
p.landing-p Our name is Free Code Camp. We are a free code camp. If you had to pay us (or sign over future earnings), we'd have to change our name. And we are not going to do that.
h2 Does Free Code Camp have an application process?
ul
p.landing-p Unlike coding bootcamps, anyone can study at Free Code Camp. We're not going to tell you that you can't become a software engineer. We believe the only person who should be able to tell you that is you. If you persevere, and keep working through our challenges and nonprofit projects, you will become an employable software engineer.
br
br

View File

@ -0,0 +1,29 @@
h3
ol#fieldGuideList
script(src='/js/lib/ramda/ramda.min.js')
script.
var getLinkedName = function getLinkedName(name) {
// ensure name is a string
name = name + '';
return name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
}
$.ajax({
url: '/field-guide/getFieldGuideList',
type: 'GET'
})
.success(
function(data) {
var docfrag = document.createDocumentFragment();
for (var i = 0; i < data.fieldGuideList.length; i++) {
var li = document.createElement("li");
// strike through field guides previously read
var linkedName = getLinkedName(data.fieldGuideList[i].name);
if (data.completedFieldGuides.indexOf(data.fieldGuideIds[i]) > -1) {
$(li).html("<a class='strikethrough' href='/field-guide/" + linkedName + "'>" + data.fieldGuideList[i].name + "</a></li>");
} else {
$(li).html("<a href='/field-guide/" + linkedName + "'>" + data.fieldGuideList[i].name + "</a></li>");
}
docfrag.appendChild(li);
};
$('#fieldGuideList').append(docfrag);
});

View File

@ -1,18 +1,20 @@
if messages.errors
.alert.alert-danger.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for error in messages.errors
div= error.msg
if messages.info
.alert.alert-info.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for info in messages.info
div= info.msg
if messages.success
.alert.alert-success.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for success in messages.success
div= success.msg
.row
.col-xs-12
if messages.errors
.alert.alert-danger.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for error in messages.errors
div!= error.msg
if messages.info
.alert.alert-info.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for info in messages.info
div!= info.msg
if messages.success
.alert.alert-success.fade.in
button.close(type='button', data-dismiss='alert')
span.ion-close-circled
for success in messages.success
div!= success.msg

View File

@ -1,15 +1,15 @@
.fcc-footer
.col-xs-12.hidden-xs.hidden-sm
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') &thinsp;Blog&thinsp;&thinsp;
a.ion-social-twitch-outline(href="http://www.twitch.tv/freecodecamp", target='_blank') &thinsp;Twitch&thinsp;&thinsp;
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank') &thinsp;Github&thinsp;&thinsp;
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') &thinsp;Twitter&thinsp;&thinsp;
a.ion-information-circled(href="/learn-to-code") &thinsp;About&thinsp;&thinsp;
a.ion-locked(href="/privacy") &thinsp;Privacy&thinsp;&thinsp;
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') &nbsp;Blog&nbsp;&nbsp;
a.ion-social-twitch-outline(href="/twitch") &thinsp;Twitch&nbsp;
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank') &nbsp;Github&nbsp;&nbsp;
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') &nbsp;Twitter&nbsp;&nbsp;
a.ion-information-circled(href="/learn-to-code") &nbsp;About&nbsp;&nbsp;
a.ion-locked(href="/privacy") &nbsp;Privacy&nbsp;&nbsp;
.col-xs-12.visible-xs.visible-sm
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank')
span.sr-only Free Code Camp's Blog
a.ion-social-twitch-outline(href="http://www.twitch.tv/freecodecamp", target='_blank')
a.ion-social-twitch-outline(href="/twitch")
span.sr-only Free Code Camp Live Pair Programming on Twitch.tv
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank')
span.sr-only Free Code Camp on GitHub
@ -18,4 +18,4 @@
a.ion-information-circled(href="/learn-to-code")
span.sr-only About Free Code Camp
a.ion-locked(href="/privacy")
span.sr-only Free Code Camp's Privacy Policy
span.sr-only Free Code Camp's Privacy Policy

View File

@ -21,7 +21,7 @@ meta(name="twitter:creator", content="@freecodecamp")
meta(name="twitter:url", content="http://www.freecodecamp.com")
meta(name="twitter:site", content="@freecodecamp")
meta(name="twitter:card", content="summary_large_image")
meta(name="twitter:image:src", content="https://s3.amazonaws.com/freecodecamp/challenges.png")
meta(name="twitter:image:src", content="https://s3.amazonaws.com/freecodecamp/1000-hours-of-coding.jpg")
meta(name="twitter:title", content="Free Code Camp: a community of busy people learning to code")
meta(name="twitter:description", content="We're a community of busy people learning to code by collaborating on projects for nonprofits. Learn Full-stack JavaScript, build a portfolio, and get great references with our online coding bootcamp.")
meta(content="a40ee5d5dba3bb091ad783ebd2b1383f", name="p:domain_verify")
@ -54,4 +54,4 @@ link(rel="mstile", sizes="310x310", href="https://s3.amazonaws.com/freecodecamp/
link(rel="mstile", sizes="310x150", href="https://s3.amazonaws.com/freecodecamp/favicons/mstile-310x150.png")
link(rel="mstile", sizes="70x70", href="https://s3.amazonaws.com/freecodecamp/favicons/mstile-70x70.png")
link(rel="favicon", href="https://s3.amazonaws.com/freecodecamp/favicons/favicon.ico")
link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicons/favicon.ico')
link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicons/favicon.ico')

View File

@ -11,18 +11,20 @@
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='/challenges') Challenges
a(href='/map') Map
if (user && user.sentSlackInvite)
li
a(href='/chat', target='_blank') Chat
else
li
a(href='/challenges/01') Chat
a(href='/challenges/join-our-chat-room') Chat
li
a(href='/stories/hot') News
li
a(href='/bonfires') Bonfires
a(href='/field-guide') Field Guide
if !user
li &thinsp; &thinsp; &thinsp;
li
@ -30,7 +32,9 @@
else
li
if (user.profile.username)
a(href='/' + user.profile.username) [&thinsp;#{user.points}&thinsp;]
a(href='/' + user.profile.username) [&nbsp;#{user.progressTimestamps.length}&nbsp;]
else
a(href='/account') [&thinsp;#{user.points}&thinsp;]
.hidden-xs
@ -47,4 +51,4 @@
img.profile-picture.float-right(src='#{user.gravatar(60)}')
else
a(href='/account')
img.profile-picture.float-right(src='#{user.gravatar(60)}')
img.profile-picture.float-right(src='#{user.gravatar(60)}')

View File

@ -0,0 +1,39 @@
.spacer
.progress
.progress-bar(role='progressbar', aria-valuenow= (step * 10), aria-valuemin='0', aria-valuemax='100', style="width: #{step * 10}%;")
span.sr-only= step * 10
| % Complete
h3.gray-text.text-center Step #{step} of 10
script.
$('#story-url').on('keypress', function (e) {
if (e.which === 13 || e === 13) {
$('#preliminary-story-submit').click();
}
});
var preliminaryStorySubmit = function preliminaryStorySubmit() {
var storyURL = $('#story-url').val();
$('#preliminary-story-submit').attr('disabled', 'disabled');
$.post('/stories/preliminary',
{
data: {
url: storyURL
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#preliminary-story-submit').attr('disabled', false);
})
.done(function (data, textStatus, xhr) {
if (data.alreadyPosted) {
window.location = data.storyURL;
} else {
window.location = '/stories/submit/new-story?url=' +
encodeURIComponent(data.storyURL) +
'&title=' + encodeURIComponent(data.storyTitle) +
'&image=' + encodeURIComponent(data.storyImage) +
'&description=' + encodeURIComponent(data.storyMetaDescription);
}
});
}
$('#preliminary-story-submit').on('click', preliminaryStorySubmit);

View File

@ -0,0 +1,24 @@
h3
ol#nonprofitsList
script(src='/js/lib/ramda/ramda.min.js')
script.
var getLinkedName = function getLinkedName(name) {
// ensure name is a string
name = name + '';
return name.toLowerCase().replace(/\s/g, '-');
}
$.ajax({
url: '/nonprofits/getNonprofitList',
type: 'GET'
})
.success(
function(data) {
var docfrag = document.createDocumentFragment();
for (var i = 0; i < data.nonprofitsList.length; i++) {
var li = document.createElement("li");
var linkedName = getLinkedName(data.nonprofitsList[i].name);
$(li).html("<a href='/nonprofits/" + linkedName + "'>" + data.nonprofitsList[i].name + "</a></li>");
docfrag.appendChild(li);
};
$('#nonprofitsList').append(docfrag);
});

View File

@ -30,4 +30,4 @@ script#inspectletjs(type='text/javascript').
}
if (window.attachEvent) window.attachEvent('onload', __ldinsp);
else window.addEventListener('load', __ldinsp, false);
})();
})();

View File

@ -1,10 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Win a Chromebook
h2 Thanks to everyone who participated in our Chromebook giveaway.
script(src='//widget-prime.rafflecopter.com/launch.js')
a#rcwidget_a7khonhd.rcptr(href='http://www.rafflecopter.com/rafl/display/d70901b10/', rel='nofollow', data-raflid='d70901b10', data-theme='classic', data-template='') a Rafflecopter giveaway
h2 Ready to learn to code?
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br

View File

@ -1,22 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Global Control Shortcuts for Mac
h2 These Global Control Shortcuts for Mac will save you hours by speeding up your typing
br
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/107073108')
.text-left
h3 These global shortcuts work everywhere on a Mac:
ul
li Control + F = Forward
li Control + B = Backward
li Control + N = Next Line
li Control + P = Previous Line
li Control + H = Backspace
li Control + D = Delete
li Control + A = Beginning of Line
li Control + E = End of Line
li Control + K = Kill line
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br

View File

@ -1,25 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Deploy a Dynamic Website in 7 Minutes
h2 Here's a fast and easy way to deploy a dynamic website to the internet
br
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/115194017')
.text-left
h3 Use these links:
ul
li
a(href='http://www.atom.io' target='_blank') http://www.atom.io
| &thinsp; - free text editor
li
a(href='http://www.startbootstrap.com' target='_blank') http://www.startbootstrap.com
| &thinsp; - free responsive (Bootstrap) templates
li
a(href='http://www.powr.io' target='_blank') http://www.powr.io
| &thinsp; - great plugins
li
a(href='http://www.bitballoon.com' target='_blank') http://www.bitballoon.com
| &thinsp; - drag-and-drop deployment
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br

View File

@ -1,24 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Gmail Shortcuts
h2 These Gmail Shortcuts will save you hours and help you get to Zero Inbox
br
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/115194016')
.text-left
h3 The shortcuts:
ul
li j - move down
li k - move up
li o - open
li r - reply
li a - reply all
li f - forward
li c - compose
li x - select
li e - archive
li ! - mark spam
li z - undo
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br

View File

@ -1,98 +0,0 @@
extends ../layout
block content
.jumbotron
h1.hug-top.text-center A Guide to our Nonprofit Projects
br
p Building nonprofit projects is the main way that our campers learn full stack JavaScript and agile software development. Once you complete the Free Code Camp challenges and Bonfire challenges, you'll begin this process.
p Once you've finished all the challenges, click the "I'm done with all the challenges" button, which will become enabled. We will prompt you for your email address, then give you further instructions on our &thinsp;
a(href="http://www.freecodecamp.com/nonprofit-project-instructions") Nonprofit Projects Instructions page
| .
p We will add you to our &thinsp;
a(href="https://trello.com/b/BA3xVpz9/nonprofit-projects") Nonprofit Project Trello board
| .
h2 Starting with the end in mind
p Our goal at Free Code Camp is to help you land a job as a junior software developer (or, if you prefer, a 'pivot job' that leads your current career in a more technical direction).
p You'll continue to work on nonprofit projects until you've built a sufficiently impressive portfolio and references to start your job search. Your portfolio will ultimately have three to five nonprofit projects. We estimate that the 900 hours of nonprofit projects you're going to complete, in addition to the 100 hours of challenges you've already completed, will be enough to qualify you for your first coding job. This will produce a much broader portfolio than a traditional coding bootcamp, which generally only has one or two capstone projects.
h2 Choosing your first Nonprofit Project
p We've categorized all the nonprofit projects by estimated time investment per camper: 100 hours, 200 hours, and 300 hours. These are only rough estimates.
p Example: if you and the camper you're paired up with (your pair) each stated you could work 20 hours per week (on the &thinsp;
a(href="http://goo.gl/forms/f61dLt67t8") form you filled out
| ). If the project is a 100 hour per camper project, you should be able to complete it in about 5 weeks.
p Our team of nonprofit project camp counselors will match you and your pair based on:
ol
li Your estimated time commitment (10, 20 or 40 hours per week)
li Your time zone
li The nonprofit projects you've chosen
li Prior coding experience (we'd like both campers to be able to contribute equally)
p We won't take age or gender into account. This will provide you with valuable experience in meshing with diverse teams, which is a reality of the contemporary workplace.
p You'll only work on one project at a time. Once you start a nonprofit project, we'll remove you from all other nonprofit project Trello cards. There's a good chance those projects will no longer be available when you finish your current project, anyway. Don't worry, though - we get new nonprofit project requests every day, so there will be plenty more projects for you to consider after you finish your current one.
h2 Finalizing the Project
p Before you can start working on the project, our team of Nonprofit Project Coordinators will go through the following process:
ol
li We'll wait until there are two campers who have chosen the same project and look like they're a good match for one another based on the factors mentioned above.
li We'll call the stakeholder to confirm once again that he or she agrees with our &thinsp;
a(href="freecodecamp.com/nonprofits") terms &thinsp;
| and has signed our &thinsp;
a(href="http://goo.gl/forms/0YKkd9bpcR") Nonprofit Project Stakeholder Pledge
| .
li We'll set an initial meeting with representatives from Free Code Camp, the two campers, and the stakeholder.
li If the stakeholder and both campers shows up promptly, and seem enthusiastic and professional, we'll start the project.
p This lengthy process serves an important purpose: it reduces the likelihood that any of our campers or stakeholders will waste their precious time.
h2 Nonprofit Stakeholders
p Each nonprofit project was submitted by a nonprofit. A representative from this nonprofit has agreed to serve as a "stakeholder" - an authorative person who understands the organization and its needs for this particular project.
p Stakeholders have a deep understanding of their organizations' needs. Campers will work with them to figure out the best solutions to these needs.
p When you and your pair first speak with your nonprofit stakeholder, you'll:
ul
li talk at length to better understand their needs.
li create a new Trello board and use it to prioritize what needs to be built.
li and establish deadlines based on your weekly time commitment, and how long you think each task will take.
p It's notoriously difficult to estimate how long building software projects will take, so feel free to ask camp counselors for help.
p You'll continue to meet with your stakeholder at least twice a month in your project's Gitter channel.
p You should also ask questions in your project's Gitter channel as they come up throughout the week, and your stakeholder can answer them asynchronously.
p Getting "blocked" on a task can take away your sense of forward momentum, so be sure to proactively seek answers to any ambiguities you encounter.
p Ultimately, the project will be considered complete once both the stakeholder's needs have been met, and you and your pair are happy with the project. Then you can add it to your portfolio!
h2 Working with your Pair
p You and your pair will pair program (code together on the same computer virtually) about half of the time, and work independently the other half of the time.
p Here are our recommended ways of collaborating:
ul
li &#8226; Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.
li &#8226; Trello is great for managing projects. Work with your stakeholder to create Trello cards, and update these cards regularly as you make progress on them.
li &#8226; Screen Hero or Team Viewer - These are the ideal way to pair program. Tools like TMUX are good, but difficult to use. We discourage you from using screen sharing tools where only one person has control of the keyboard and mouse - that isn't real pair programming.
li &#8226; Write clear and readable code, commit messages, branch names, and pull request messages.
h2 Setting up your Development Environment
p We've created a custom virtual machine image with Ubuntu Linux, Git, Team Viewer, the MEAN Stack and all its dependencies. You can run this virtual image on any computer with at least 2 gigabytes of RAM and 16 gigabytes of hard drive space.
p The benefits of using this virtual machine are as follows:
ul
li &#8226; Everyone else on Free Code Camp is using this image, so we can all help you troubleshoot various problems that may arise.
li &#8226; When you pair program, you and your pair will have the exact same environment, which means you will both feel comfortable on each other's machines.
li &#8226; You can install the image on any computer without worrying about messing up the computer's original data or configuration.
li &#8226; Even if you end up using Windows or Mac OSX for development later, your server will almost certainly run Linux, so it's worth getting used to Linux.
li &#8226; Even experienced developers encounter hangups when setting up a development environment. This virtual machine image will remove this tedious process.
p Install a bit torrent client, then &thinsp;
a(href="http://mgnet.me/ZOQk0rd") download our virtual machine image
| .
p Please note that even though Bit Torrent is often used to download content illegally, all the content on our image is open source and perfectly legal to redistribute.
p Once you've downloaded the file, &thinsp;
a(href="https://www.virtualbox.org/wiki/Downloads") download VirtualBox &thinsp;
| and follow &thinsp;
a(href="http://techathlon.com/how-to-run-a-vmdk-file-in-oracle-virtualbox/") this tutorial &thinsp;
| to open the image in VirtualBox. You'll want to assign the virtual machine at least two gigabytes of ram.
p Now you have your own Linux development environment. You can shut it down when you're not using it and it will save its state. Please continue to seed the file in bit torrent so that other campers can download it as well. Enjoy!
h2 Hosting Apps
p Unless your stakeholder has an existing modern host (AWS, Digital Ocean), you'll need to transition them over to a new platform. We believe Heroku is the best choice for a vast majority of web projects. It's free, easy to use, and has both browser and command line interfaces. It's owned by Salesforce and used by a ton of companies, so it's accountable and unlikely to go away.
p If you need help convincing your stakeholder that Heroku is the ideal platform, we'll be happy to talk with them.
h2 Maintaining Apps
p Once you complete a nonprofit project, your obligation to its stakeholder is finished. You goal is to leave behind a well documented solution that can be easily maintained by a contract JavaScript developer (or even a less-technical "super user").
p While you will no longer need to help with feature development, we encourage you to consider helping your stakeholder with occasional patches down the road. After all, this project will be an important piece of your portfolio, and you'll want it to remain in good shape for curious future employers.
h2 Office Hours
p Quincy Larson and/or Michael Johnson will be in the &thinsp;
a(href="https://gitter.im/FreeCodeCamp/NonprofitProjects") Gitter Nonprofit Project Channel &thinsp;
| every Monday and Thursday from 9 - 10 p.m. EST.
p Our goal is to make the discussion as public as possible so all our campers can benefit from each campers questions.
p If necessary, we can also hop on Screen Hero with you to help you with issues more specific to your project.
h2 Pledging to finish the project
p Your nonprofit stakeholder, your pair, and the volunteer camp counselor team are all counting on you to finish your nonprofit project. If you walk away from an unfinished nonprofit project, you'll become ineligible to ever be assigned another one.
p To confirm that you understand the seriousness of this commitment, we require that all campers &thinsp;
a(href="http://goo.gl/forms/ZMn96z2QqY") sign this pledge &thinsp;
| before starting on their nonprofit projects.
p There will likely be times of confusion or frustration. This is normal in software development. The most important thing is that you do not give up and instead persevere through these setbacks. As Steve Jobs famously said, "Real artists ship." And you are going to ship one successful nonprofit project after another until you feel ready to take the next step in your promising career.

View File

@ -1,13 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Install ScreenHero
h2
a(href="http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=") Download for Mac
h2
a(href="http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9") Download for Windows
p You can learn more about using Screen Hero by taking &thinsp;
a(href="http://www.freecodecamp.com/challenges/34") Challenge 34.
p Screen Hero was recently acquired by a collaboration tool called Slack. It's still available and free, but will go away in the indefinite future. Discuss alternatives on our &thinsp;
a(href="http://forum.freecodecamp.com/t/replacing-screen-hero/992") Screen Hero replacement thread
| .

View File

@ -1,12 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top Win a Chromebook
h2 Sign up for Inbox.js - JavaScript challenges in your inbox - and enter to win a Chromebook!
img.image-responsive(src="https://s3.amazonaws.com/freecodecamp/chromebook.jpg" alt="HP Chromebook 11")
script(src='//widget-prime.rafflecopter.com/launch.js')
a#rcwidget_a7khonhd.rcptr(href='http://www.rafflecopter.com/rafl/display/d70901b10/', rel='nofollow', data-raflid='d70901b10', data-theme='classic', data-template='') a Rafflecopter giveaway
.animated.zoomInDown.delay-10
p Finished signing up for the giveaway?
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br

View File

@ -1,6 +1,9 @@
extends ../layout-wide
block content
img.img-responsive.img-center(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png')
if (Math.random() > 0.98)
img.img-responsive.img-center(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner-dino.png')
else
img.img-responsive.img-center(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png')
br
.row.text-center
.col-xs-12.col-md-6.col-md-offset-3
@ -40,7 +43,7 @@ block content
script.
var challengeName = 'Learn to code'
.row
.col-xs-12.col-sm-12.col-md-6
.col-xs-12
.panel.panel-info
.panel-heading.landing-panel-heading.text-center Announcements
.panel-body
@ -77,23 +80,21 @@ block content
.col-xs-12.github-and-twitter-button-text
html.
<iframe src="http://ghbtns.com/github-btn.html?user=freecodecamp&repo=freecodecamp&type=watch&count=true&size=large" height="30" width="170" frameborder="0" scrolling="0" style="width:170px; height: 30px;" allowTransparency="true"></iframe>
.col-xs-12.col-sm-12.col-md-6
include ../partials/faq
//#announcementModal.modal(tabindex='-1')
// .modal-dialog
// .modal-content
// .modal-header.challenge-list-header Camp-wide Meeting on Saturday at Noon EST
// a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
// .modal-body
// h3.text-left We'll live-stream some of Free Code Camp's new features, and campers will show what they're building. Live Saturday, March 28 at Noon EST on our &thinsp;
// a(href='http://twitch.tv/freecodecamp', target='_blank') Twitch.tv channel
// | .
// a.btn.btn-lg.btn-info.btn-block(name='_csrf', value=_csrf, aria-hidden='true', href='http://twitch.tv/freecodecamp', target='_blank') Take me to Twitch so I can follow Free Code Camp
// a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Thanks for the heads-up!
//script.
// $(document).ready(function() {
// if (!localStorage || !localStorage.campWideMeeting) {
// $('#announcementModal').modal('show');
// localStorage.campWideMeeting = "true";
// }
// });
#announcementModal.modal(tabindex='-1')
.modal-dialog
.modal-content
.modal-header.challenge-list-header Camp-wide Meeting on Saturday at Noon EST
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
h3.text-left We'll live-stream some of Free Code Camp's new features, and campers will show what they're building. Live Saturday, March 28 at Noon EST on our &thinsp;
a(href='http://twitch.tv/freecodecamp', target='_blank') Twitch.tv channel
| .
a.btn.btn-lg.btn-info.btn-block(name='_csrf', value=_csrf, aria-hidden='true', href='http://twitch.tv/freecodecamp', target='_blank') Take me to Twitch so I can follow Free Code Camp
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Thanks for the heads-up!
script.
$(document).ready(function() {
if (!localStorage || !localStorage.campWideMeeting) {
$('#announcementModal').modal('show');
localStorage.campWideMeeting = "true";
}
});

View File

@ -1,56 +0,0 @@
extends ../layout-wide
block content
.text-center
h1 Live Pair Programming
h2#next-session
h2 Watch the live stream below or on our &thinsp;
a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel
| .
.row
.col-md-8.col-xs-12
.embed-responsive.embed-responsive-16by9
iframe(src='http://www.twitch.tv/freecodecamp/embed', frameborder='0', scrolling='no')
.col-md-4.col-xs-12
.visible-sm.visible-xs
.embed-responsive.embed-responsive-16by9
iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no')
.visible-md.visible-lg
.embed-responsive.embed-responsive-twitch-chat
iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no')
h1 Previous Live Pair Programming Sessions
.col-xs-12
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw')
h3.wrappable link: &thinsp;
a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0')
h3.wrappable link: &thinsp;
a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs')
h3.wrappable link: &thinsp;
a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE')
h3.wrappable link: &thinsp;
a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA')
h3.wrappable link: &thinsp;
a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA
h3 Got 3 minutes? Learn to code with us!
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
br
br
br
br
br
script(src="/js/lib/moment/moment.js")
script(src="/js/lib/moment/nextTuesday.js")
script.
$('#next-session').text(nextSession());

View File

@ -1,22 +0,0 @@
extends ../layout
block content
.jumbotron.text-center
h1.hug-top NodeSchool Challenges
h2 Learn Node.js, NPM, Express.js, and advanced JavaScript like Functional Programming and Promises
br
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/122719685')
.text-left
h3 Here are the NodeSchool courses you should complete:
ol
li
a(href='http://runnable.com/VQuO_Y4BbkhaOOsV/nodeschool-io-s-learnyounode-course-available-in-your-browser-for-node-js-and-freecodecamp' target='_blank') NodeSchool.io's LearnYouNode course
li
a(href='http://runnable.com/VQt7deuMe6RZ3Gcl/nodeschool-io-s-learn-to-npm-course-running-in-your-browser-for-node-js-and-hello-world' target='_blank') NodeSchool.io's Learn-to-NPM course
li
a(href='http://runnable.com/VQufnaRAlaNc9JuM/nodeschool-io-s-express-js-course-available-in-your-browser-for-node-js-and-freecodecamp' target='_blank') NodeSchool.io's Express.js course
li
a(href='http://runnable.com/VQuZjvia8Gxcqkpy/nodeschool-io-s-functional-programming-in-javascript-course-available-in-your-browser-for-node-js-and-freecodecamp' target='_blank') NodeSchool.io's Functional Programming in JavaScript course
li
a(href='http://runnable.com/VQunH26qdytcbLBg/nodeschool-io-s-promise-it-won-t-hurt-promises-course-available-in-your-browser-for-node-js-javascript-and-freecodecamp' target='_blank') NodeSchool.io's Promise It Won't Hurt Promises course
br

View File

@ -1,23 +0,0 @@
extends ../layout
block content
.jumbotron
.col-sm-offset-1
h1 Nonprofit projects
h3 It's time to apply what you've learned here at Free Code Camp.
h3 By the end of this process, you'll have a portfolio of live apps being used by real people.
h3 Please do the following immediately:
h4
ol
li Complete this form: &thinsp;
a(href="http://goo.gl/forms/f61dLt67t8" target="_blank") http://goo.gl/forms/f61dLt67t8
| .
li Read this document, which will answer many questions you may have about our nonprofit projects: &thinsp;
a(href="/guide-to-our-nonprofit-projects" target="_blank") http://freecodecamp.com/guide-to-our-nonprofit-projects
| .
li We'll send you an invite to our Nonprofit Projects Trello board. Once we do, go there and add yourself to at least 3 nonprofit projects that interest you.
li Finish any unfinished Bonfire challenges. These challenges serve as the Free Code Camp "exit test". You must complete these before you can start working on nonprofit projects. If you completed CoderByte or CodeWars challenges instead of Bonfire, email us and we'll take a look:&thinsp;
a(href="mailto:team@freecodecamp.com") team@freecodecamp.com
| .
h4 Please email us if you have further questions: &thinsp;
a(href="mailto:team@freecodecamp.com") team@freecodecamp.com
| .

View File

@ -1,40 +0,0 @@
extends ../layout
block content
.jumbotron
h2.big-text Privacy
html.
<p>Free Code Camp is committed to respecting the privacy of visitors to our web sites and web applications. The guidelines below explain how we protect the privacy of visitors to FreeCodeCamp.com and its features.</p>
<h3>Personally Identifiable Information</h3>
<p>Free Code Camp protects the identity of visitors to FreeCodeCamp.com by limiting the collection of personally identifiable information.</p>
<p>Free Code Camp does not knowingly collect or solicit personally identifiable information from or about children under 13, except as permitted by law. If we discover we have received any information from a child under 13 in violation of this policy, we will delete that information immediately. If you believe Free Code Camp has any information from or about anyone under 13, please e-mail us at <a href="mailto:team@freecodecamp.com" target="_blank">team@freecodecamp.com</a>.</p>
<p>All personally identifiable information you provide to us is used by Free Code Camp and its team to process and manage your account, analyze the demographic of our users, or to deliver services through the site. </p>
<p>If you choose to provide personally identifiable information to us, you may receive occasional e-mails from us that are relevant to Free Code Camp, getting a job, or learning to code in general.</p>
<p>Free Code Camp may also use other third-party providers to facilitate the delivery of the services described above, and these third-party providers may be supplied with or have access to personally identifiable information for the sole purpose of providing these services, to you on behalf of Free Code Camp.</p>
<p>Free Code Camp may also disclose personally identifiable information in special legal circumstances. For instance, such information may be used where it is necessary to protect our copyright or intellectual property rights, or if the law requires us to do so.</p>
<h3>Anonymous Information</h3>
<p>Anonymous aggregated data may be provided to other organizations we associate with for statistical purposes. For example, we may report to an organization that a certain percentage of our site's visitors are adults between the ages of 25 and 35.</p>
<h3>Cookies and Beacons—Use by Free Code Camp; Opting Out</h3>
<p>We use cookies and software logs to monitor the use of FreeCodeCamp.com and to gather non-personal information about visitors to the site. Cookies are small files that Free Code Camp transfers to the hard drives of visitors for record-keeping purposes. These monitoring systems allow us to track general information about our visitors, such as the type of browsers (for example, Firefox or Internet Explorer), the operating systems (for instance, Windows or Macintosh), or the Internet providers (for instance, Comcast) they use. This information is used for statistical and market research purposes to tailor content to usage patterns and to provide services requested by our customers. To delete these cookies, please see your browser's privacy settings.</p>
<p>A beacon is an electronic file object (typically a transparent image) placed in the code of a Web page. We use third party beacons to monitor the traffic patterns of visitors from one Free Code Camp.com page to another and to improve site performance.</p>
<p>None of the information we gather in this way can be used to identify any individual who visits our site.</p>
<h3>Security</h3>
<p>Any personally identifiable information collected through this site is stored on limited-access servers. We will maintain safeguards to protect these servers and the information they store.</p>
<h3>Surveys</h3>
<p>We may occasionally conduct on-line surveys. All surveys are voluntary and you may decline to participate.</p>
<h3>Copyright</h3>
<p>All of the content on FreeCodeCamp.com is copyrighted by Free Code Camp. If you'd like to redistribute it beyond simply sharing it through social media, please contact us at <a href="mailto:team@freecodecamp.com" target="_blank">team@freecodecamp.com</a>.</p>
<h3>Contacting Us</h3>
<p>If you have questions about Free Code Camp, or to correct, update, or remove personally identifiable information, please email us at <a href="mailto:team@freecodecamp.com" target="_blank">team@freecodecamp.com</a>.</p>
<h3>Links to Other Web sites</h3>
<p>Free Code Camp's sites each contain links to other Web sites. Free Code Camp is not responsible for the privacy practices or content of these third-party Web sites. We urge all FreeCodeCamp.com visitors to follow safe Internet practices: Do not supply Personally Identifiable Information to these Web sites unless you have verified their security and privacy policies.</p>
<h3>Data Retention</h3>
<p>We retain your information for as long as necessary to permit us to use it for the purposes that we have communicated to you and comply with applicable law or regulations.</p>
<h3>Business Transfers</h3>
<p>As we continue to develop our business, we might sell or buy subsidiaries, or business units. In such transactions, customer information generally is one of the transferred business assets but remains subject to the promises made in any pre-existing Privacy Policy (unless, of course, the customer consents otherwise). Also, in the unlikely event that Free Code Camp, or substantially all of its assets are acquired, customer information will be one of the transferred assets, and will remain subject to our Privacy Policy.</p>
<h3>Your California Privacy Rights</h3>
<p>If you are a California resident, you are entitled to prevent sharing of your personal information with third parties for their own marketing purposes through a cost-free means. If you send a request to the address above, Free Code Camp will provide you with a California Customer Choice Notice that you may use to opt-out of such information sharing. To receive this notice, submit a written request to <a href="mailto:team@freecodecamp.com" target="_blank">team@freecodecamp.com</a>, specifying that you seek your &quot;California Customer Choice Notice.&quot; Please allow at least thirty (30) days for a response.</p>
<h3>Acceptance of Privacy Policy Terms and Conditions</h3>
<p>By using this site, you signify your agreement to the terms and conditions of this FreeCodeCamp.com Privacy Policy. If you do not agree to these terms, please do not use this site. We reserve the right, at our sole discretion, to change, modify, add, or remove portions of this policy at any time. All amended terms automatically take effect 30 days after they are initially posted on the site. Please check this page periodically for any modifications. Your continued use of FreeCodeCamp.com following the posting of any changes to these terms shall mean that you have accepted those changes.</p>
<p>If you have any questions or concerns, please send an e-mail to <a href="mailto:team@freecodecamp.com" target="_blank">team@freecodecamp.com</a>.</p>
script.
var challengeName = 'Privacy';

View File

@ -1,202 +0,0 @@
extends ../layout
block content
script.
$(document).ready(function () {
var directions = {
0: "To get started, open your Chrome DevTools. The #next-exercise button is disabled below. Try using jQuery's .attr() method to turn the disabled attribute to false.",
1: "Move the .target element from #location1 to #location2.",
2: "Change the background color of .target to red.",
3: "Change the background color of the even-numbered targets to red.",
4: "Change the background color of the target4 to red.",
5: "Clone the target2 in #location1 so that it also exists in #location2.",
6: "Remove the target3 from element from #location1.",
7: "Check the following checkboxes using jQuery.",
8: "Make the text input field read-only.",
9: "Select the target2 option in the select box.",
10: "Add the following css classes to .target: 'animated' and 'hinge'.",
11: "Use jQuery to read the data of .target.",
12: "Use 'length' to count the number of child elements in #location1, then display that value in #location2.",
13: "There's an element hidden in #location1. Show it using jQuery, and then click on it."
};
var hint = {
0: "$('#next-exercise').attr('disabled', false);",
1: "$('.target').appendTo('#location2');",
2: "$('.target').css('background', 'red');",
3: "$('.target:even').css('background', 'red');",
4: "$('.target:nth-child(4)').css('background', 'red');",
5: "$('.target:nth-child(2)').clone().appendTo($('#location2'));",
6: "$('.target:nth-child(3)').remove();",
7: "$('#location1 input').attr('checked', 'true')",
8: "$('#location1 input').attr('readonly', 'true')",
9: "$('#location1 select').val('target2');",
10: "$('.target').addClass('animated hinge');",
11: "$('.target').data();",
12: "$('#location2').text($('#location1').children().length)",
13: "$('#finished-button').show().click();"
};
var elements = {
0: "",
1: "<div class='btn btn-default target'>.target</div>",
2: "<div class='btn btn-default target'>.target</div>",
3: "<div class='btn btn-default target'>target0</div><div class='btn btn-default target'>target1</div><div class='btn btn-default target'>target2</div><div class='btn btn-default target'>target3</div><div class='btn btn-default target'>target4</div>",
4: "<div class='btn btn-default target'>target1</div><div class='btn btn-default target'>target2</div><div class='btn btn-default target'>target3</div><div class='btn btn-default target'>target4</div><div class='btn btn-default target'>target5</div>",
5: "<div class='btn btn-default target'>target1</div><div class='btn btn-default target'>target2</div><div class='btn btn-default target'>target3</div><div class='btn btn-default target'>target4</div><div class='btn btn-default target'>target5</div>",
6: "<div class='btn btn-default target'>target1</div><div class='btn btn-default target'>target2</div><div class='btn btn-default target'>target3</div><div class='btn btn-default target'>target4</div><div class='btn btn-default target'>target5</div>",
7: "<input type='checkbox'>checkbox1</input><br><input type='checkbox'>checkbox2</input>",
8: "<input type='text' value='target'>",
9: "<select><option value='target0'>target0</option><option value='target1'>target1</option><option value='target2'>target2</option><option value='target3'>target3</option></select>",
10: "<div class='btn btn-default target'>.target</div>",
11: "<div class='btn btn-default target' data-lookAtMe='This is some super secret data hidden in the DOM!'>.target</div>",
12: "<div class='btn btn-default target'>target1</div><div class='btn btn-default target'>target2</div><div class='btn btn-default target'>target3</div><div class='btn btn-default target'>target4</div><div class='btn btn-default target'>target5</div>",
13: "<div class='btn btn-default target' id='finished-button'>Finish!</div>"
};
function refreshEverything() {
$('#directions').text("Exercise " + currentExercise + ": " + directions[currentExercise]);
$('#location1').html(elements[currentExercise]);
$('#hint').text(hint[currentExercise]);
handleExerciseTransition();
}
$('#exercise-directory').on('click', 'li', event, function () {
currentExercise = $(this).index();
event.preventDefault();
refreshEverything(event);
});
$('#next-exercise').on('click', event, function () {
++currentExercise;
event.preventDefault();
refreshEverything(event);
});
$('#solution-button').on('click', function () {
$('#hint-modal').modal({backdrop: "static"});
});
$('#location1').on('click', '#finished-button', function () {
$('#finished-modal').modal({backdrop: "static"});
});
function handleExerciseTransition() {
if (currentExercise === 0) {
$('#next-exercise').attr('disabled', true);
} else {
$('#next-exercise').attr('disabled', false);
}
if (currentExercise === 2 || currentExercise === 6) {
$('#location2 .target').remove();
}
if (currentExercise === 13) {
$('#location2').text('');
$('#finished-button').hide();
$('#next-exercise').attr('disabled', true);
}
}
var currentExercise = 0;
refreshEverything(currentExercise);
});
style.
#directions {
text-align: left;
font-size: 15px;
}
.well {
text-align: left;
height: 200px;
}
#exercise-directory {
font-size: 20px;
}
#current-exercise {
text-size: 250px;
}
html.
<div class="container">
<div class="row">
<div class=" col-xs-12 col-sm-8">
<div class="jumbotron">
<div class="row">
<div class="col-xs-12 text-center">
<img src="http://3.bp.blogspot.com/-vgd3mMqeBzk/VGzEmodGqmI/AAAAAAAAAZY/OCcbFvYKzF0/s780/logo4.0LG.png" alt="free code learning at freecodecamp.com" class="img-responsive">
<h1>jQuery Exercises</h1>
</div>
</div>
<br>
<div class="text-primary" id="directions">
</div>
<br>
<div class="row">
<div class="col-xs-6">#location1</div>
<div class="col-xs-6">#location2</div>
</div>
<div class="row">
<div class="col-xs-6 well" id="location1"></div>
<div class="col-xs-6 well" id="location2"></div>
</div>
<div class="btn btn-primary btn-lg btn-block" id="next-exercise">
#next-exercise
</div>
<br>
<button class="btn btn-block btn-lg btn-info" id="solution-button">
#solution-button
</button>
</br>
<div class="text-center">
Created for <a href="http://www.FreeCodeCamp.com">Free Code Camp</a><br/>by <a href="https://twitter.com/ossia">Quincy Larson</a>,
<a href="https://www.twitter.com/terakilobyte">Nathan Leniz</a>, <a href="https://twitter.com/iheartkode"> Mark Howard</a> and <a href="https://twitter.com/ryanmalm">Ryan Malm</a>. Please <a href="http://codepen.io/ossia/pen/raVEgN">fork this.</a>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-4" id="exercise-directory">
<div class="panel panel-primary">
<div class="panel panel-heading">List of Exercises</div>
<div class="panel-body">
<ol start="0">
<li> <a href='#'>Re-enable an element</a></li>
<li> <a href='#'>Change an element's parent</a></li>
<li> <a href='#'>Change an element's CSS</a></li>
<li> <a href='#'>Use jQuery filters to modify even elements</a></li>
<li> <a href='#'>Change nth child</a></li>
<li> <a href='#'>Clone an element</a></li>
<li> <a href='#'>Remove an element</a></li>
<li> <a href='#'>Check checkboxes</a></li>
<li> <a href='#'>Make text read-only</a></li>
<li> <a href='#'>Select an option in a select box</a></li>
<li> <a href='#'>Add a CSS class to an element</a></li>
<li> <a href='#'>Lookup an element's data attribute</a></li>
<li> <a href='#'>Count child elements</a></li>
<li> <a href='#'>Show an element and click on it</a></li>
</ol>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="hint-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Your hint</h4>
</div>
<div class="modal-body">
<div id="hint">
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="finished-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Congratulations! You've finished our jQuery exercises! <a href="http://www.freecodecamp.com/">Go back to Free Code Camp </a> and mark this challenge complete.</h4>
</div>
<div class="modal-body">
<div id="hint"></div>
</div>
</div>
</div>
</div>

View File

@ -105,7 +105,7 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
//- Products
each bonfire in bonfires
url
loc #{appUrl}/#{bonfire.name.replace(/\s/, '-')}
loc #{appUrl}/bonfires/#{bonfire.name.replace(/\s/, '-')}
lastmod= now
changefreq weekly
priority= 0.5
@ -113,7 +113,7 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
//- Challenges
each challenge in challenges
url
loc #{appUrl}/#{challenge.challengeNumber}
loc #{appUrl}/challenges/#{challenge.name.replace(/\s/, '-')}
lastmod= now
changefreq weekly
priority= 0.5
@ -121,15 +121,23 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
//- Stories
each story in stories
url
loc #{appUrl}/#{story.storyLink}
loc #{appUrl}/stories/#{story.storyLink.name.replace(/\s/, '-')}
lastmod= now
changefreq daily
priority= 0.9
//- Nonprofit
//- Nonprofit
each nonprofit in nonprofits
url
loc #{appUrl}/#{nonprofit.nonprofitLink}
lastmod= now
changefreq daily
priority= 0.9
loc #{appUrl}/nonprofits/#{nonprofit.name.replace(/\s/, '-')}
lastmod= now
changefreq daily
priority= 0.9
//- Nonprofit
each fieldGuide in fieldGuides
url
loc #{appUrl}/field-guide/#{fieldGuide.name.replace(/\s/, '-')}
lastmod= now
changefreq daily
priority= 0.9

View File

@ -1,3 +0,0 @@
extends ../layout
block content
include ../partials/stats

View File

@ -0,0 +1,59 @@
extends ../layout
block content
.col-xs-12
.panel.panel-info
.panel-heading.text-center Watch us code on our Twitch.tv channel
.panel-body.text-center
h2 Watch the live stream below or on our &thinsp;
a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel
| .
.spacer
.row
.col-md-8.col-xs-12
.embed-responsive.embed-responsive-16by9
iframe(src='http://www.twitch.tv/freecodecamp/embed', frameborder='0', scrolling='no')
.col-md-4.col-xs-12
.visible-sm.visible-xs
.embed-responsive.embed-responsive-16by9
iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no')
.visible-md.visible-lg
.embed-responsive.embed-responsive-twitch-chat
iframe(src='http://www.twitch.tv/freecodecamp/chat?popout=', frameborder='0', scrolling='no')
.row
.col-xs-12
h2 Check out our scheduled shows. You can add them to your calendar.
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src="https://www.google.com/calendar/embed?src=freecodecamp.com_r06116ile3o6ucpif7s0g281tc%40group.calendar.google.com&ctz=America/New_York&mode=AGENDA" style="border: 0" width="800" height="600" frameborder="0" scrolling="no")
.row
.col-xs-12
h2 Here are some of our previous shows (you can full-screen them)
.row
.col-xs-12.col-sm-12.col-md-6
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw')
p.wrappable.negative-45 link: &thinsp;
a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw
.col-xs-12.col-sm-12.col-md-6
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0')
p.wrappable.negative-45 link: &thinsp;
a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0
.col-xs-12.col-sm-12.col-md-6
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs')
p.wrappable.negative-45 link: &thinsp;
a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs
.col-xs-12.col-sm-12.col-md-6
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE')
p.wrappable.negative-45 link: &thinsp;
a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE
.col-xs-12.col-sm-12.col-md-6
.embed-responsive.embed-responsive-16by9.big-break
iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA')
p.wrappable.negative-45 link: &thinsp;
a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA
.row
.col-xs-12.text-center
if !user
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)

View File

@ -56,7 +56,8 @@
sentinel--;
if (!sentinel) {
$('.comment-a-comment').on('click', 'a', function () {
if (!user) {
if (typeof user == "undefined" || !user) {
window.location.href = '/signin';
return;
}
$(this).unbind('click');
@ -91,15 +92,19 @@
});
var submitCommentToCommentHandler = function submitCommentToCommentHandler() {
$('#submit-comment-to-comment').unbind('click');
console.log('in comments.jade', originalStoryAuthorEmail);
$.post('/stories/comment/' + commentId + '/comment',
{
data: {
associatedPost: commentId,
originalStoryLink: originalStoryLink,
originalStoryAuthorEmail: originalStoryAuthorEmail,
body: $('#comment-to-comment-textinput').val(),
author: {
picture: user.profile.picture,
userId: user._id,
username: user.profile.username
username: user.profile.username,
email: user.email
}
}
})

View File

@ -1,9 +1,14 @@
extends ../layout
block content
script(src='/js/lib/moment/moment.js')
if (user)
script.
var user = !{JSON.stringify(user)};
else
script.
var user = undefined;
script.
var challengeName = 'Camper News';
var user = !{JSON.stringify(user)};
var page = !{JSON.stringify(page)};
.panel.panel-info
.panel-heading.text-center Camper News

View File

@ -44,4 +44,12 @@
}
});
}
$('#preliminary-story-submit').on('click', preliminaryStorySubmit);
$('#preliminary-story-submit').on('click', preliminaryStorySubmit);
arr = $( "h3 input:checked" )
.map(function() {
return this.id;
})
.get()
.join('&');

View File

@ -2,7 +2,7 @@
.spacer
h1.text-center Search is coming soon
.input-group
input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search our stories', autofocus)
input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search our links', autofocus)
span.input-group-btn
button.disabled#searchbutton.btn.btn-big.btn-primary.btn-responsive(type='button') Search
.spacer
@ -70,4 +70,4 @@ script.
}
});
}
}

View File

@ -1,6 +1,8 @@
.spacer
script.
var storyId = !{JSON.stringify(id)};
var originalStoryLink = !{JSON.stringify(originalStoryLink)};
var originalStoryAuthorEmail = !{JSON.stringify(originalStoryAuthorEmail)};
var comments = !{JSON.stringify(comments)};
var upVotes = !{JSON.stringify(upVotes)};
var image = !{JSON.stringify(image)};
@ -56,7 +58,10 @@
$('#image-display').removeClass('hidden-element')
}
$('#reply-to-main-post').on('click', function() {
if (!user) return;
if (typeof user == "undefined" || !user) {
window.location.href = '/signin';
return;
}
$('#initial-comment-submit').removeClass('hidden-element');
$(this).unbind('click');
$('.comment-to-comment-formgroup').empty();