Merge pull request #320 from FreeCodeCamp/ux-improvements
Pulling Ux improvements the rest of the way into master
This commit is contained in:
15
.editorconfig
Normal file
15
.editorconfig
Normal 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
397
app.js
@ -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('/');
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
65
controllers/challengeMap.js
Normal file
65
controllers/challengeMap.js
Normal 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
|
||||
});
|
||||
}
|
||||
};
|
@ -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
99
controllers/fieldGuide.js
Normal 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
188
controllers/nonprofits.js
Normal 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');
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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(/";/g, '"');
|
||||
if (data.url.replace(/&/g, '&') !== cleanData) {
|
||||
var data = req.query;
|
||||
var cleanData = sanitizeHtml(data.url, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
}).replace(/";/g, '"');
|
||||
if (data.url.replace(/&/g, '&') !== 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(/"/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(/"/g, '"'),
|
||||
timePosted: Date.now(),
|
||||
link: link,
|
||||
description: sanitizeHtml(data.description, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
}).replace(/"/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(/"/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(/"/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(/"/g, '"'),
|
||||
timePosted: Date.now(),
|
||||
link: link,
|
||||
description: sanitizeHtml(data.description, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
}).replace(/"/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(/"/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(/"/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
11
gulpfile.js
11
gulpfile.js
@ -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']);
|
||||
|
@ -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);
|
@ -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'
|
||||
},
|
||||
*/
|
||||
*/
|
||||
|
@ -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
15
models/FieldGuide.js
Normal 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
28
models/Nonprofit.js
Normal 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);
|
@ -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);
|
||||
|
305
models/User.js
305
models/User.js
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
@brand-primary: #215f1e;
|
||||
@brand-success: #457E86;
|
||||
@brand-info: #5bc0de;
|
||||
@brand-info: #4A2B0F;
|
||||
@brand-warning: #f0ad4e;
|
||||
@brand-danger: #d9534f;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -16,4 +16,4 @@
|
||||
//= require lib/jquery-2.1.1.min
|
||||
//= require lib/bootstrap.min
|
||||
//= require lib/moment/moment
|
||||
//= require main
|
||||
//= require main
|
@ -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) {
|
||||
|
@ -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');
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
@ -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",
|
||||
|
@ -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()"]
|
||||
}
|
||||
]
|
||||
|
@ -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"
|
||||
*/
|
58
seed_data/challenge-migration.js
Normal file
58
seed_data/challenge-migration.js
Normal 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");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
226
seed_data/challengeMapping.json
Normal file
226
seed_data/challengeMapping.json
Normal 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": ""
|
||||
}
|
||||
]
|
@ -1,3 +0,0 @@
|
||||
[
|
||||
|
||||
]
|
File diff suppressed because it is too large
Load Diff
502
seed_data/field-guides.json
Normal file
502
seed_data/field-guides.json
Normal 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>• We're free</li>",
|
||||
" <li>• We're self-paced</li>",
|
||||
" <li>• We're browser-based</li>",
|
||||
" <li>• 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>• There are hundreds of thousands of unfilled coding jobs.</li>",
|
||||
" <li>• Employers and the US government have joined together to promote nontraditional coding programs like Free Code Camp.</li>",
|
||||
" <li>• Full stack JavaScript is one of best paying skill sets, and has a ton of job openings.</li>",
|
||||
" <li>• 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\">• HTML - the structure of web pages</li>",
|
||||
" <li class=\"landing-p\">• CSS - the visual style of web pages</li>",
|
||||
" <li class=\"landing-p\">• Bootstrap - a \"responsive design\" tool that helps your websites look great on tablets and phones</li>",
|
||||
" <li class=\"landing-p\">• jQuery - an easy tool for controlling content in the browser</li>",
|
||||
" <li class=\"landing-p\">• 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\">• JavaScript - the one programming language that all web browsers use</li>",
|
||||
" <li class=\"landing-p\">• Algorithms - step-by-step recipes for getting things done</li>",
|
||||
" <li class=\"landing-p\">• 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\">• 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\">• Git - a version control system for saving and sharing your projects</li>",
|
||||
" <li class=\"landing-p\">• MongoDB - a popular non-relational database</li>",
|
||||
" <li class=\"landing-p\">• Angular.js - a tool for making exciting web interfaces</li>",
|
||||
" <li class=\"landing-p\">• Express.js - a powerful web development framework</li>",
|
||||
" <li class=\"landing-p\">• 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  <a href=\"http://www.freecodecamp.com/nonprofit-project-instructions\">Nonprofit Projects Instructions page</a>.</p>",
|
||||
"<p>We will add you to our  <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  <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  <a href=\"freecodecamp.com/nonprofits\">terms  </a>and has signed our  <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>• Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.</li>",
|
||||
" <li>• 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>• 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>• 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>• Everyone else on Free Code Camp is using this image, so we can all help you troubleshoot various problems that may arise.</li>",
|
||||
" <li>• 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>• You can install the image on any computer without worrying about messing up the computer's original data or configuration.</li>",
|
||||
" <li>• 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>• 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  <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,  <a href=\"https://www.virtualbox.org/field-guide/Downloads\">download VirtualBox  </a>and follow  <a href=\"http://techathlon.com/how-to-run-a-vmdk-file-in-oracle-virtualbox/\">this tutorial  </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  <a href=\"https://gitter.im/FreeCodeCamp/NonprofitProjects\">Gitter Nonprofit Project Channel  </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 camper’s 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  <a href=\"http://goo.gl/forms/ZMn96z2QqY\">sign this pledge  </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  <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  <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:  <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:  <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:  <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:  <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:  <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:  <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:  <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: <a href=\"mailto:team@freecodecamp.com\">team@freecodecamp.com</a>.</li>",
|
||||
" </ol>",
|
||||
" <h4>Please email us if you have further questions:  <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 "California Customer Choice Notice." 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
170
seed_data/nonprofits.json
Normal 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"
|
||||
}
|
||||
]
|
@ -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
108
seed_data/userMigration.js
Normal 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();
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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: 
|
||||
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  
|
||||
= 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');
|
||||
}
|
||||
|
||||
|
||||
|
71
views/challengeMap/show.jade
Normal file
71
views/challengeMap/show.jade
Normal 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
|
||||
| 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
|
||||
| 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
|
||||
| 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
|
||||
| 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
|
||||
| Nonprofit Projects
|
||||
h3.negative-15
|
||||
ul
|
||||
li
|
||||
a(href="/nonprofits/directory") Browse our nonprofit projects
|
@ -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);
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -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
|
||||
|
@ -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  
|
||||
= 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  
|
||||
= phrase
|
||||
- else
|
||||
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
|
||||
|
@ -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  
|
||||
= 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');
|
||||
}
|
||||
});
|
||||
|
76
views/coursewares/showZiplineOrBasejump.jade
Normal file
76
views/coursewares/showZiplineOrBasejump.jade
Normal 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  
|
||||
= 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');
|
||||
}
|
||||
});
|
42
views/field-guide/show.jade
Normal file
42
views/field-guide/show.jade
Normal 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();
|
||||
}
|
||||
});
|
||||
});
|
@ -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'
|
||||
|
@ -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
|
12
views/nonprofits/are-you-with-a-registered-nonprofit.jade
Normal file
12
views/nonprofits/are-you-with-a-registered-nonprofit.jade
Normal 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
|
19
views/nonprofits/directory.jade
Normal file
19
views/nonprofits/directory.jade
Normal 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
|
83
views/nonprofits/home.jade
Normal file
83
views/nonprofits/home.jade
Normal 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   We're thousands of professionals, all learning to code together
|
||||
li.ion-code   We're building projects for dozens of nonprofits
|
||||
li.ion-code   Our community is 100% free and open source
|
||||
li.ion-code   You'll learn Full Stack JavaScript and become a Software Engineer
|
||||
li.ion-code   You'll work through our focused, interactive courses and tutorials
|
||||
li.ion-code   You'll learn to code at your own pace, in your browser or on your phone
|
||||
li.ion-code   You'll become qualified for thousands of jobs currently going unfilled
|
||||
li.ion-code   You can get help in real time from our community chat rooms and forum
|
||||
li.ion-code   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'
|
33
views/nonprofits/how-can-free-code-camp-help-you.jade
Normal file
33
views/nonprofits/how-can-free-code-camp-help-you.jade
Normal 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 Websites
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='donation')
|
||||
label.ion-card Donation Systems
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='volunteer')
|
||||
label.ion-android-calendar Volunteer Systems
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='inventory')
|
||||
label.ion-ios-box Inventory Systems
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='eLearning')
|
||||
label.ion-university E-learning Platforms
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='form')
|
||||
label.ion-ios-list Web Forms
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='community')
|
||||
label.ion-ios-people Community Tools
|
||||
.col-xs-12
|
||||
input.checkbox(type='checkbox', id='other')
|
||||
label.ion-settings 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
|
17
views/nonprofits/in-exchange-we-ask.jade
Normal file
17
views/nonprofits/in-exchange-we-ask.jade
Normal 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.
|
14
views/nonprofits/link-us-to-your-website.jade
Normal file
14
views/nonprofits/link-us-to-your-website.jade
Normal 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
|
12
views/nonprofits/ok-with-javascript.jade
Normal file
12
views/nonprofits/ok-with-javascript.jade
Normal 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.
|
65
views/nonprofits/other-solutions.jade
Normal file
65
views/nonprofits/other-solutions.jade
Normal 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
|
82
views/nonprofits/show.jade
Normal file
82
views/nonprofits/show.jade
Normal 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 Website
|
||||
if (approvedDonor)
|
||||
.ion-card Donor Management System
|
||||
if (approvedInventory)
|
||||
.ion-ios-box Inventory Management System
|
||||
if (approvedVolunteer)
|
||||
.ion-android-calendar Volunteer Management System
|
||||
if (approvedForm)
|
||||
.ion-ios-list Webform
|
||||
if (approvedCommunity)
|
||||
.ion-ios-people Community Management System
|
||||
if (approvedELearning)
|
||||
.ion-university E-learning Platform
|
||||
if (approvedOther)
|
||||
.ion-settings 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
|
14
views/nonprofits/tell-us-your-email.jade
Normal file
14
views/nonprofits/tell-us-your-email.jade
Normal 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
|
13
views/nonprofits/tell-us-your-name.jade
Normal file
13
views/nonprofits/tell-us-your-name.jade
Normal 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
|
28
views/nonprofits/what-does-your-nonprofit-do.jade
Normal file
28
views/nonprofits/what-does-your-nonprofit-do.jade
Normal 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');
|
||||
});
|
@ -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. We’ll send you an email no later than #{getBackDay}.
|
@ -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}
|
||||
|   (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!
|
@ -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}
|
||||
|   (#{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!
|
@ -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);
|
||||
|
@ -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 • HTML - the structure of web pages
|
||||
li.landing-p • CSS - the visual style of web pages
|
||||
li.landing-p • Bootstrap - a "responsive design" tool that helps your websites look great on tablets and phones
|
||||
li.landing-p • jQuery - an easy tool for controlling content in the browser
|
||||
li.landing-p • 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 • JavaScript - the one programming language that all web browsers use
|
||||
li.landing-p • Algorithms - step-by-step recipes for getting things done
|
||||
li.landing-p • 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 • 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 • Git - a version control system for saving and sharing your projects
|
||||
li.landing-p • MongoDB - a popular non-relational database
|
||||
li.landing-p • Angular.js - a tool for making exciting web interfaces
|
||||
li.landing-p • Express.js - a powerful web development framework
|
||||
li.landing-p • 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
|
29
views/partials/field-guide.jade
Normal file
29
views/partials/field-guide.jade
Normal 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);
|
||||
});
|
@ -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
|
||||
|
@ -1,15 +1,15 @@
|
||||
.fcc-footer
|
||||
.col-xs-12.hidden-xs.hidden-sm
|
||||
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank')  Blog  
|
||||
a.ion-social-twitch-outline(href="http://www.twitch.tv/freecodecamp", target='_blank')  Twitch  
|
||||
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank')  Github  
|
||||
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank')  Twitter  
|
||||
a.ion-information-circled(href="/learn-to-code")  About  
|
||||
a.ion-locked(href="/privacy")  Privacy  
|
||||
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') Blog
|
||||
a.ion-social-twitch-outline(href="/twitch")  Twitch
|
||||
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank') Github
|
||||
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') Twitter
|
||||
a.ion-information-circled(href="/learn-to-code") About
|
||||
a.ion-locked(href="/privacy") Privacy
|
||||
.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
|
||||
|
@ -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')
|
||||
|
@ -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      
|
||||
li
|
||||
@ -30,7 +32,9 @@
|
||||
else
|
||||
li
|
||||
if (user.profile.username)
|
||||
a(href='/' + user.profile.username) [ #{user.points} ]
|
||||
|
||||
a(href='/' + user.profile.username) [ #{user.progressTimestamps.length} ]
|
||||
|
||||
else
|
||||
a(href='/account') [ #{user.points} ]
|
||||
.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)}')
|
||||
|
39
views/partials/nonprofit-application-progress-bar.jade
Normal file
39
views/partials/nonprofit-application-progress-bar.jade
Normal 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);
|
24
views/partials/nonprofits.jade
Normal file
24
views/partials/nonprofits.jade
Normal 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);
|
||||
});
|
@ -30,4 +30,4 @@ script#inspectletjs(type='text/javascript').
|
||||
}
|
||||
if (window.attachEvent) window.attachEvent('onload', __ldinsp);
|
||||
else window.addEventListener('load', __ldinsp, false);
|
||||
})();
|
||||
})();
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|   - free text editor
|
||||
li
|
||||
a(href='http://www.startbootstrap.com' target='_blank') http://www.startbootstrap.com
|
||||
|   - free responsive (Bootstrap) templates
|
||||
li
|
||||
a(href='http://www.powr.io' target='_blank') http://www.powr.io
|
||||
|   - great plugins
|
||||
li
|
||||
a(href='http://www.bitballoon.com' target='_blank') http://www.bitballoon.com
|
||||
|   - drag-and-drop deployment
|
||||
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
|
||||
br
|
@ -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
|
@ -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  
|
||||
a(href="http://www.freecodecamp.com/nonprofit-project-instructions") Nonprofit Projects Instructions page
|
||||
| .
|
||||
p We will add you to our  
|
||||
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  
|
||||
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  
|
||||
a(href="freecodecamp.com/nonprofits") terms  
|
||||
| and has signed our  
|
||||
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 • Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.
|
||||
li • 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 • 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 • 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 • Everyone else on Free Code Camp is using this image, so we can all help you troubleshoot various problems that may arise.
|
||||
li • 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 • You can install the image on any computer without worrying about messing up the computer's original data or configuration.
|
||||
li • 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 • 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  
|
||||
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,  
|
||||
a(href="https://www.virtualbox.org/wiki/Downloads") download VirtualBox  
|
||||
| and follow  
|
||||
a(href="http://techathlon.com/how-to-run-a-vmdk-file-in-oracle-virtualbox/") this tutorial  
|
||||
| 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  
|
||||
a(href="https://gitter.im/FreeCodeCamp/NonprofitProjects") Gitter Nonprofit Project Channel  
|
||||
| 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 camper’s 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  
|
||||
a(href="http://goo.gl/forms/ZMn96z2QqY") sign this pledge  
|
||||
| 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.
|
@ -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  
|
||||
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  
|
||||
a(href="http://forum.freecodecamp.com/t/replacing-screen-hero/992") Screen Hero replacement thread
|
||||
| .
|
@ -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
|
@ -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  
|
||||
// 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  
|
||||
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";
|
||||
}
|
||||
});
|
||||
|
@ -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  
|
||||
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:  
|
||||
a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw
|
||||
.embed-responsive.embed-responsive-16by9.big-break
|
||||
iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0')
|
||||
h3.wrappable link:  
|
||||
a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0
|
||||
.embed-responsive.embed-responsive-16by9.big-break
|
||||
iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs')
|
||||
h3.wrappable link:  
|
||||
a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs
|
||||
.embed-responsive.embed-responsive-16by9.big-break
|
||||
iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE')
|
||||
h3.wrappable link:  
|
||||
a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE
|
||||
.embed-responsive.embed-responsive-16by9.big-break
|
||||
iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA')
|
||||
h3.wrappable link:  
|
||||
a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA
|
||||
h3 Got 3 minutes? Learn to code with us!
|
||||
a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free)
|
||||
br
|
||||
br
|
||||
br
|
||||
br
|
||||
br
|
||||
script(src="/js/lib/moment/moment.js")
|
||||
script(src="/js/lib/moment/nextTuesday.js")
|
||||
script.
|
||||
$('#next-session').text(nextSession());
|
@ -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
|
@ -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:  
|
||||
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:  
|
||||
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: 
|
||||
a(href="mailto:team@freecodecamp.com") team@freecodecamp.com
|
||||
| .
|
||||
h4 Please email us if you have further questions:  
|
||||
a(href="mailto:team@freecodecamp.com") team@freecodecamp.com
|
||||
| .
|
@ -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 "California Customer Choice Notice." 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';
|
@ -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">×</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">×</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>
|
@ -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
|
||||
|
@ -1,3 +0,0 @@
|
||||
extends ../layout
|
||||
block content
|
||||
include ../partials/stats
|
59
views/resources/twitch.jade
Normal file
59
views/resources/twitch.jade
Normal 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  
|
||||
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:  
|
||||
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:  
|
||||
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:  
|
||||
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:  
|
||||
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:  
|
||||
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)
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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('&');
|
@ -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.
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user