diff --git a/.gitignore b/.gitignore index 112d64591e..60d9092f91 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ bower_components main.css bundle.js coverage +.remote-sync.json diff --git a/README.md b/README.md index 0a7e0e9151..9770de3ebc 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ Welcome to Free Code Camp's open source codebase! ======================= #Note -We're currently very close to moving from express to loopback. As such, please keep in mind that the instructions here for setting up and running the project do not directly translate to the staging branch. Additionally, the file structure is quite a bit different. As always, the staging branch is the appropriate place to branch off of to fix/add something! +We're currently very close to moving from Express to Loopback. As such, please keep in mind that the instructions here for setting up and running the project do not directly translate to the staging branch. Additionally, the file structure is quite a bit different. As always, the staging branch is the appropriate place to branch off of to fix/add something! -Free Code Camp is an open-source community of busy people who learn to code, then build projects for nonprofits. +Free Code Camp is an open-source community of busy people who learn to code, then build projects for nonprofits. Our campers (students) start by working through our free, self-paced, browser-based curriculum. Next, they build several practice projects. Finally, we pair two campers together with a stakeholder from a nonprofit organization, and help them build the solution the nonprofit has requested. @@ -17,7 +17,7 @@ Our campers (students) start by working through our free, self-paced, browser-ba 80% of our campers are over 25, and nearly a fifth of our campers are women. -This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Slack](http://freecodecamp.slack.com), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). +This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Slack](http://freecodecamp.slack.com), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). [Join our community](http://www.freecodecamp.com/signin)! @@ -28,7 +28,7 @@ We welcome pull requests from Free Code Camp campers (our students) and seasoned 1. Check our [public Waffle Board](https://waffle.io/freecodecamp/freecodecamp). 2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Slack](http://freecodecamp.slack.com). -3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy. +3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy. 4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)) 5. Name the branch something like `user-xxx` where user is your username and xxx is the issue number you are addressing. 6. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [AirBnB's JavaScript Style Guide](https://github.com/airbnb/javascript). Please do not ignore any linting errors, as they are meant to **help** you. Make sure none of your JavaScript is longer than 80 characters per line. diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000000..b75516ce38 --- /dev/null +++ b/client/README.md @@ -0,0 +1,3 @@ +This is the entry point for the client +Code that should only run on the client should be put here. +NOTE(berks): For react specific stuff this should be the entry point diff --git a/client/index.js b/client/index.js new file mode 100644 index 0000000000..68baa7ec0a --- /dev/null +++ b/client/index.js @@ -0,0 +1,20 @@ +import BrowserHistory from 'react-router/lib/BrowserHistory'; +import debugFactory from 'debug'; +import { Cat } from 'thundercats'; + +import AppFactory from '../common/app/appFactory'; + +const debug = debugFactory('fcc:client'); +const DOMContianer = document.getElemenetById('#fCC'); +const fcc = new Cat(); + +// returns an observable +fcc.render(AppFactory(BrowserHistory), DOMContianer) + .subscribe( + function() { + debug('react rendered'); + }, + function(err) { + debug('an error has occured', err.stack); + } + ); diff --git a/common/app/App.jsx b/common/app/App.jsx new file mode 100644 index 0000000000..f3a77cae51 --- /dev/null +++ b/common/app/App.jsx @@ -0,0 +1,25 @@ +import React, { PropTypes } from 'react'; + +import Nav from './components/Nav'; +import Footer from './components/Footer'; + +export default class extends React.Component { + constructor(props) { + super(props); + } + + static displayName = 'FreeCodeCamp' + static propTypes = { + children: PropTypes.node + } + + render() { + return ( +
img
element should have the class \"thick-green-border\".')",
"assert($('img').hasClass('thick-green-border') && parseInt($('img').css('border-top-width')), 'Give your image a border width of 10px.')",
"assert(new RegExp('solid', 'gi').test(editor), 'Give your image a border style of \"solid\".')",
- "assert($('img').css('border-left-color') === 'rgb(0, 128, 0)', 'Your img
element should be green.')"
+ "assert($('img').css('border-left-color') === 'rgb(0, 128, 0)', 'The border around your img
element should be green.')"
],
"challengeSeed": [
"",
@@ -1165,7 +1165,7 @@
],
"tests": [
"assert((/cat photos/gi).test($('a').text()), 'Your a
element should have the anchor text of \"cat photos\"')",
- "assert($('a').filter(function(index) { return /com/gi.test($('a').attr('href')); }).length > 0, 'You need an a
element that links to http://catphotoapp.com.')",
+ "assert(/http:\\/\\/catphotoapp\\.com/gi.test($('a').attr('href')), 'You need an a
element that links to http://catphotoapp.com.')",
"assert(editor.match(/<\\/a>/g) && editor.match(/<\\/a>/g).length === editor.match(/a
element has a closing tag.')"
],
"challengeSeed": [
diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json
index eeac669f73..9f2b704153 100644
--- a/seed/challenges/bootstrap.json
+++ b/seed/challenges/bootstrap.json
@@ -626,7 +626,7 @@
"The \"row\" class is applied to a div
, and the buttons themselves can be wrapped within it."
],
"tests": [
- "assert($('div.row:has(button)'), 'Your buttons should all be wrapped within the same div
element with the class \"row\".')",
+ "assert($('div.row:has(button)').length > 0, 'Your buttons should all be wrapped within the same div
element with the class \"row\".')",
"assert($('div.col-xs-4:has(button)').length > 2, 'Each of your Bootstrap buttons should be wrapped within its own a div
element with the class \"col-xs-4\".')",
"assert(editor.match(/<\\/button>/g) && editor.match(/
elements has a closing tag.')",
"assert(editor.match(/<\\/div>/g) && editor.match(//g).length === editor.match(/div elements has a closing tag.')"
diff --git a/seed/challenges/ziplines.json b/seed/challenges/ziplines.json
index 3779c06ff7..11b1b15f9f 100644
--- a/seed/challenges/ziplines.json
+++ b/seed/challenges/ziplines.json
@@ -56,7 +56,7 @@
"Hint: Here's an example call to Twitch.tv's JSON API: https://api.twitch.tv/kraken/streams/freecodecamp
.",
"Hint: The relevant documentation about this API call is here: https://github.com/justintv/Twitch-API/blob/master/v3_resources/streams.md#get-streamschannel.",
"Hint: Here's an array of the Twitch.tv usernames of people who regularly stream coding: [\"freecodecamp\", \"storbeck\", \"terakilobyte\", \"habathcx\",\"RobotCaleb\",\"comster404\",\"brunofin\",\"thomasballinger\",\"noobs2ninjas\",\"beohoff\"]
",
- "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
+ "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.",
"If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.
Click here then add your link to your tweet's text"
],
@@ -87,7 +87,7 @@
"Here are the user stories you must enable, and optional bonus user stories:",
"User Story: As a user, I can click a button to show me a new random quote.",
"Bonus User Story: As a user, I can press a button to tweet out a quote.",
- "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
+ "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.",
"If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.
Click here then add your link to your tweet's text"
],
@@ -120,7 +120,7 @@
"Bonus User Story: As a user, I can see an icon depending on the temperature..",
"Bonus User Story: As a user, I see a different background image depending on the temperature (e.g. snowy mountain, hot desert).",
"Bonus User Story: As a user, I can push a button to toggle between Fahrenheit and Celsius.",
- "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
+ "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.",
"If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.
Click here then add your link to your tweet's text"
],
@@ -154,7 +154,7 @@
"User Story: As a user, I can click a link to go directly to the post's discussion page.",
"Bonus User Story: As a user, I can see how many upvotes each story has.",
"Hint: Here's the Camper News Hot Stories API endpoint: http://www.freecodecamp.com/stories/hotStories
.",
- "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
+ "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.",
"If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.
Click here then add your link to your tweet's text"
],
@@ -187,7 +187,7 @@
"Bonus User Story:As a user, I can click a button to see a random Wikipedia entry.",
"Bonus User Story:As a user, when I type in the search box, I can see a dropdown menu with autocomplete options for matching Wikipedia entries.",
"Hint: Here's an entry on using Wikipedia's API: http://www.mediawiki.org/wiki/API:Main_page
.",
- "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
+ "Remember to use RSAP if you get stuck. Try using jQuery's $.getJSON() to consume APIs.",
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.",
"If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.
Click here then add your link to your tweet's text"
],
diff --git a/seed/field-guides.json b/seed/field-guides.json
index 2de429b569..9d3e906d16 100644
--- a/seed/field-guides.json
+++ b/seed/field-guides.json
@@ -863,7 +863,7 @@
"description": [
"",
" Translation is an all-or-nothing proposal.",
- "
We won't be able to add new languages to Free Code Camp until all of our challenges are translated into that langauge.
",
+ " We won't be able to add new languages to Free Code Camp until all of our challenges are translated into that language.
",
" In addition to translating these initially, we'll also need to maintain the translation as the challenges are gradually updated.
",
" If you're able to help us, you can join our Trello board by sending @quincylarson your email address in Slack.
",
""
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index dfa2f98ef2..bddf9118c4 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -32,50 +32,30 @@
var R = require('ramda'),
utils = require('../utils'),
- userMigration = require('../utils/middleware').userMigration,
- MDNlinks = require('../../seed/bonfireMDNlinks');
+ userMigration = require('../utils/middleware').userMigration;
var challengeMapWithNames = utils.getChallengeMapWithNames();
var challengeMapWithIds = utils.getChallengeMapWithIds();
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
-
-function getMDNlinks(links) {
- // 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
- if (links) {
- links.forEach(function (value) {
- populatedLinks.push(MDNlinks[value]);
- });
- }
- return populatedLinks;
-}
+var getMDNLinks = utils.getMDNLinks;
module.exports = function(app) {
var router = app.loopback.Router();
var Challenge = app.models.Challenge;
var User = app.models.User;
- router.get(
- '/challenges/next-challenge',
- userMigration,
- returnNextChallenge
- );
-
- router.get(
- '/challenges/:challengeName',
- userMigration,
- returnIndividualChallenge
- );
-
- router.get('/challenges/', userMigration, returnCurrentChallenge);
router.post('/completed-challenge/', completedChallenge);
router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
router.post('/completed-bonfire', completedBonfire);
+ // the follow routes are covered by userMigration
+ router.use(userMigration);
+ router.get('/challenges/next-challenge', returnNextChallenge);
+ router.get('/challenges/:challengeName', returnIndividualChallenge);
+ router.get('/challenges/', returnCurrentChallenge);
+ router.get('/map', challengeMap);
+
app.use(router);
function returnNextChallenge(req, res, next) {
@@ -295,7 +275,7 @@ module.exports = function(app) {
bonfires: challenge,
challengeId: challenge.id,
MDNkeys: challenge.MDNlinks,
- MDNlinks: getMDNlinks(challenge.MDNlinks),
+ MDNlinks: getMDNLinks(challenge.MDNlinks),
challengeType: challenge.challengeType
});
}
@@ -547,4 +527,51 @@ module.exports = function(app) {
});
}
}
+
+ function challengeMap(req, res, next) {
+ var completedList = [];
+
+ if (req.user) {
+ completedList = req.user.completedChallenges;
+ }
+
+ var noDuplicatedChallenges = R.uniq(completedList);
+
+ var completedChallengeList = noDuplicatedChallenges
+ .map(function(challenge) {
+ // backwards compatibility
+ return (challenge.id || challenge._id);
+ });
+ var challengeList = utils.
+ getChallengeMapForDisplay(completedChallengeList);
+
+ Object.keys(challengeList).forEach(function(key) {
+ challengeList[key].completed = challengeList[key]
+ .challenges.filter(function(elem) {
+ // backwards compatibility hack
+ return completedChallengeList.indexOf(elem.id || elem._id) > -1;
+ });
+ });
+
+ function numberWithCommas(x) {
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+ }
+
+ 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));
+
+ User.count(function(err, camperCount) {
+ if (err) { return next(err); }
+
+ res.render('challengeMap/show', {
+ daysRunning: daysRunning,
+ camperCount: numberWithCommas(camperCount),
+ title: "A map of all Free Code Camp's Challenges",
+ challengeList: challengeList,
+ completedChallengeList: completedChallengeList
+ });
+ });
+ }
};
diff --git a/server/boot/challengeMap.js b/server/boot/challengeMap.js
deleted file mode 100644
index 3b7adf2489..0000000000
--- a/server/boot/challengeMap.js
+++ /dev/null
@@ -1,66 +0,0 @@
-var R = require('ramda'),
- // debug = require('debug')('freecc:cntr:challengeMap'),
- utils = require('./../utils'),
- middleware = require('../utils/middleware');
-
-
-module.exports = function(app) {
- var User = app.models.User;
- var router = app.loopback.Router();
-
- router.get('/map', middleware.userMigration, challengeMap);
- router.get('/learn-to-code', function(req, res) {
- res.redirect(301, '/map');
- });
- router.get('/about', function(req, res) {
- res.redirect(301, '/map');
- });
-
- app.use(router);
-
- function challengeMap(req, res, next) {
- var completedList = [];
-
- if (req.user) {
- completedList = req.user.completedChallenges;
- }
-
- var noDuplicatedChallenges = R.uniq(completedList);
-
- var completedChallengeList = noDuplicatedChallenges
- .map(function(challenge) {
- return (challenge.id || challenge._id); // backwards compatibility
- });
- var challengeList = utils.
- getChallengeMapForDisplay(completedChallengeList);
-
- Object.keys(challengeList).forEach(function(key) {
- challengeList[key].completed = challengeList[key]
- .challenges.filter(function(elem) {
- return completedChallengeList.indexOf(elem.id || elem._id) > -1;
- //backwards compatibility hack
- });
- });
-
- function numberWithCommas(x) {
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
- }
-
- 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));
-
- User.count(function(err, camperCount) {
- if (err) { return next(err); }
-
- res.render('challengeMap/show', {
- daysRunning: daysRunning,
- camperCount: numberWithCommas(camperCount),
- title: "A map of all Free Code Camp's Challenges",
- challengeList: challengeList,
- completedChallengeList: completedChallengeList
- });
- });
- }
-};
diff --git a/server/boot/nonprofits.js b/server/boot/nonprofits.js
index 988ded5f84..1d16e76b74 100644
--- a/server/boot/nonprofits.js
+++ b/server/boot/nonprofits.js
@@ -9,7 +9,7 @@ module.exports = function(app) {
function nonprofitsDirectory(req, res, next) {
Nonprofit.find(
- { where: { estimatedHours: { $gt: 0 } } },
+ { where: { estimatedHours: { gt: 0 } } },
function(err, nonprofits) {
if (err) { return next(err); }
diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js
index 8836f4f638..7dd89b1b4f 100644
--- a/server/boot/randomAPIs.js
+++ b/server/boot/randomAPIs.js
@@ -27,8 +27,8 @@ module.exports = function(app) {
router.post('/get-help', getHelp);
router.post('/get-pair', getPair);
router.get('/chat', chat);
- router.get('/bootcamp-calculator', bootcampCalculator);
- router.get('/bootcamp-calculator.json', bootcampCalculatorJson);
+ router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
+ router.get('/coding-bootcamp-cost-calculator.json', bootcampCalculatorJson);
router.get('/twitch', twitch);
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
diff --git a/server/boot/redirects.js b/server/boot/redirects.js
index 8c622cb501..b07ac3b13e 100644
--- a/server/boot/redirects.js
+++ b/server/boot/redirects.js
@@ -18,5 +18,13 @@ module.exports = function(app) {
);
});
+ router.get('/learn-to-code', function(req, res) {
+ res.redirect(301, '/map');
+ });
+
+ router.get('/about', function(req, res) {
+ res.redirect(301, '/map');
+ });
+
app.use(router);
};
diff --git a/server/datasources.local.js b/server/datasources.local.js
index 03a6648e94..276a4e8dee 100644
--- a/server/datasources.local.js
+++ b/server/datasources.local.js
@@ -3,6 +3,7 @@ var secrets = require('../config/secrets');
module.exports = {
db: {
connector: 'mongodb',
+ connectionTimeout: 15000,
url: process.env.MONGOHQ_URL
},
mail: {
diff --git a/server/passport-providers.js b/server/passport-providers.js
index 3c779a162e..b60ad8f51e 100644
--- a/server/passport-providers.js
+++ b/server/passport-providers.js
@@ -41,6 +41,7 @@ module.exports = {
},
'google-login': {
provider: 'google',
+ authScheme: 'oauth',
module: 'passport-google-oauth2',
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
@@ -54,6 +55,7 @@ module.exports = {
},
'google-link': {
provider: 'google',
+ authScheme: 'oauth',
module: 'passport-google-oauth2',
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
@@ -94,7 +96,7 @@ module.exports = {
},
'linkedin-login': {
provider: 'linkedin',
- authScheme: 'oauth',
+ authScheme: 'oauth2',
module: 'passport-linkedin-oauth2',
authPath: '/auth/linkedin',
callbackURL: '/auth/linkedin/callback',
@@ -103,12 +105,15 @@ module.exports = {
failureRedirect: failureRedirect,
clientID: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
- scope: ['r_fullprofile', 'r_emailaddress'],
+ scope: ['r_basicprofile', 'r_emailaddress'],
+ authOptions: {
+ state: process.env.LINKEDIN_STATE
+ },
failureFlash: true
},
'linkedin-link': {
provider: 'linkedin',
- authScheme: 'oauth',
+ authScheme: 'oauth2',
module: 'passport-linkedin-oauth2',
authPath: '/link/linkedin',
callbackURL: '/link/linkedin/callback',
@@ -117,7 +122,10 @@ module.exports = {
failureRedirect: failureRedirect,
clientID: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
- scope: ['r_fullprofile', 'r_emailaddress'],
+ scope: ['r_basicprofile', 'r_emailaddress'],
+ authOptions: {
+ state: process.env.LINKEDIN_STATE
+ },
failureFlash: true
}
};
diff --git a/server/server.js b/server/server.js
index e950611d24..4d59ccbe7f 100755
--- a/server/server.js
+++ b/server/server.js
@@ -1,10 +1,9 @@
require('dotenv').load();
-require('pmx').init();
+var pmx = require('pmx');
+pmx.init();
// handle uncaught exceptions. Forever will restart process on shutdown
-var https = require('https'),
- sslConfig = require('./ssl-config'),
- R = require('ramda'),
+var R = require('ramda'),
assign = require('lodash').assign,
loopback = require('loopback'),
boot = require('loopback-boot'),
@@ -21,9 +20,7 @@ var https = require('https'),
flash = require('express-flash'),
path = require('path'),
expressValidator = require('express-validator'),
- forceDomain = require('forcedomain'),
lessMiddleware = require('less-middleware'),
- pmx = require('pmx'),
passportProviders = require('./passport-providers'),
/**
@@ -45,20 +42,16 @@ app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
-//if (process.env.NODE_ENV === 'production') {
-// app.use(forceDomain({
-// hostname: 'www.freecodecamp.com'
-// }));
-//}
-
app.use(compress());
app.use(lessMiddleware(path.join(__dirname, '/public')));
app.use(logger('dev'));
app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: true }));
+app.use(bodyParser.urlencoded({
+ extended: true
+}));
app.use(expressValidator({
customValidators: {
- matchRegex: function (param, regex) {
+ matchRegex: function(param, regex) {
return regex.test(param);
}
}
@@ -99,6 +92,8 @@ var trusted = [
'https://freecodecamp.com',
'https://freecodecamp.org',
'*.freecodecamp.org',
+ // NOTE(berks): add the following as the blob above was not covering www
+ 'http://www.freecodecamp.org',
'ws://freecodecamp.com/',
'ws://www.freecodecamp.com/',
'*.gstatic.com',
@@ -133,7 +128,8 @@ var trusted = [
'wss://inspectletws.herokuapp.com/',
'http://hn.inspectlet.com/',
'*.googleapis.com',
- '*.gstatic.com'
+ '*.gstatic.com',
+ 'https://hn.inspectlet.com/'
];
app.use(helmet.csp({
@@ -143,10 +139,10 @@ app.use(helmet.csp({
'*.aspnetcdn.com',
'*.d3js.org',
'https://cdn.inspectlet.com/inspectlet.js',
- 'http://cdn.inspectlet.com/inspectlet.js'
- ].concat(trusted),
- 'connect-src': [
+ 'http://cdn.inspectlet.com/inspectlet.js',
+ 'http://www.freecodecamp.org'
].concat(trusted),
+ 'connect-src': [].concat(trusted),
styleSrc: [
'*.googleapis.com',
'*.gstatic.com'
@@ -180,14 +176,16 @@ app.use(helmet.csp({
passportConfigurator.init();
-app.use(function (req, res, next) {
+app.use(function(req, res, next) {
// Make user object available in templates.
res.locals.user = req.user;
next();
});
app.use(
- loopback.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
+ loopback.static(path.join(__dirname, '../public'), {
+ maxAge: 86400000
+ })
);
boot(app, {
@@ -195,7 +193,7 @@ boot(app, {
dev: process.env.NODE_ENV
});
-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|signin|signup|fonts|favicon/i.test(path)) {
@@ -224,7 +222,8 @@ var passportOptions = {
null;
var username = (profile.username || profile.id);
- username = typeof username === 'string' ? username.toLowerCase() : username;
+ username = typeof username === 'string' ? username.toLowerCase() :
+ username;
var password = generateKey('password');
var userObj = {
username: username,
@@ -255,8 +254,13 @@ R.keys(passportProviders).map(function(strategy) {
* 500 Error Handler.
*/
-if (process.env.NODE_ENV === 'development') {
- app.use(errorHandler({ log: true }));
+// if (process.env.NODE_ENV === 'development') {
+if (true) { // eslint-disable-line
+ // NOTE(berks): adding pmx here for Beta test. Remove for production
+ app.use(pmx.expressErrorHandler());
+ app.use(errorHandler({
+ log: true
+ }));
} else {
app.use(pmx.expressErrorHandler());
// error handling in production disabling eslint due to express parity rules
@@ -279,12 +283,16 @@ if (process.env.NODE_ENV === 'development') {
var message = 'opps! Something went wrong. Please try again later';
if (type === 'html') {
- req.flash('errors', { msg: message });
+ req.flash('errors', {
+ msg: message
+ });
return res.redirect('/');
// json
} else if (type === 'json') {
res.setHeader('Content-Type', 'application/json');
- return res.send({ message: message });
+ return res.send({
+ message: message
+ });
// plain text
} else {
res.setHeader('Content-Type', 'text/plain');
@@ -297,37 +305,16 @@ if (process.env.NODE_ENV === 'development') {
* Start Express server.
*/
-var options = {
- key: sslConfig.privateKey,
- cert: sslConfig.certificate
-};
-if (process.env.NODE_ENV === 'production') {
- app.start = function() {
- var server = https.createServer(options, app);
- server.listen('https://' + process.env.HOST + ':' + app.get('port'), function () {
- console.log(
- 'FreeCodeCamp server listening on port %d in %s mode',
- app.get('port'),
- app.get('env')
- );
- });
- };
-} else {
- app.start = function () {
- app.listen(app.get('port'), function () {
- console.log(
- 'FreeCodeCamp server listening on port %d in %s mode',
- app.get('port'),
- app.get('env')
- );
- });
- };
-}
+app.listen(app.get('port'), function() {
+ console.log(
+ 'FreeCodeCamp server listening on port %d in %s mode',
+ app.get('port'),
+ app.get('env')
+ );
+});
// start the server if `$ node server.js`
-if (require.main === module) {
- app.start();
-}
+
module.exports = app;
diff --git a/server/ssl-config.js b/server/ssl-config.js
index 7ac90dc7cf..6cbf418971 100644
--- a/server/ssl-config.js
+++ b/server/ssl-config.js
@@ -8,9 +8,9 @@ var fs = require('fs');
if (process.env.NODE_ENV === 'production') {
exports.privateKey =
fs.readFileSync(path.join(__dirname,
- '../../private/privatekey.pem')).toString();
+ '../../private/privatekey.pem'));
exports.certificate =
fs.readFileSync(path.join(__dirname,
- '../../private/certificate.pem')).toString();
+ '../../private/certificate.pem'));
}
diff --git a/server/utils/index.js b/server/utils/index.js
index 6b7b342e56..bff016241c 100644
--- a/server/utils/index.js
+++ b/server/utils/index.js
@@ -7,6 +7,7 @@ var path = require('path'),
fs = require('fs'),
+ MDNlinks = require('../../seed/bonfireMDNlinks'),
resources = require('./resources.json'),
nonprofits = require('../../seed/nonprofits.json'),
fieldGuides = require('../../seed/field-guides.json');
@@ -16,7 +17,7 @@ var path = require('path'),
*/
var allFieldGuideIds, allFieldGuideNames, allNonprofitNames,
challengeMap, challengeMapForDisplay, challengeMapWithIds,
- challengeMapWithNames, allChallengeIds, allChallenges,
+ challengeMapWithNames, allChallengeIds,
challengeMapWithDashedNames;
/**
@@ -216,5 +217,18 @@ module.exports = {
}
});
})();
+ },
+
+ getMDNLinks: function(links) {
+ if (!links) {
+ return [];
+ }
+ // takes in an array of links, which are strings
+
+ // for each key value, push the corresponding link
+ // from the MDNlinks object into a new array
+ return links.map(function(value) {
+ return MDNlinks[value];
+ });
}
};
diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade
index d18abe290e..c28f20e322 100644
--- a/server/views/coursewares/showBonfire.jade
+++ b/server/views/coursewares/showBonfire.jade
@@ -84,10 +84,16 @@ block content
label.negative-10.btn.btn-primary.btn-block#submitButton
i.fa.fa-play
| Run code (ctrl + enter)
-
+ #resetButton.btn.btn-danger.btn-big.btn-block(data-toggle='modal', data-target='#reset-modal', data-backdrop='true') Reset Code
if (user && user.sentSlackInvite)
.button-spacer
.btn-group.input-group.btn-group-justified
+ label.btn.btn-success#trigger-help-modal
+ i.fa.fa-refresh
+ | Reset
+ label.btn.btn-success#trigger-help-modal
+ i.fa.fa-refresh
+ | Reset
label.btn.btn-success#trigger-help-modal
i.fa.fa-medkit
| Help
@@ -119,7 +125,7 @@ block content
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true, style='display: none;')
- script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.5.js')
+ script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.6.js')
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
@@ -140,6 +146,15 @@ block content
= phrase
else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
+ #reset-modal.modal(tabindex='-1')
+ .modal-dialog.animated.fadeInUp.fast-animation
+ .modal-content
+ .modal-header.challenge-list-header Clear your code?
+ a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
+ .modal-body
+ h3 This will restore your code editor to its original state.
+ a.btn.btn-lg.btn-info.btn-block#reset-button(href='#', data-dismiss='modal', aria-hidden='true') Clear my code
+ a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
include ../partials/challenge-modals
script.
var MDNlinks = !{JSON.stringify(MDNlinks)};
diff --git a/server/views/partials/footer.jade b/server/views/partials/footer.jade
index 0af2fce328..fe25c7723a 100644
--- a/server/views/partials/footer.jade
+++ b/server/views/partials/footer.jade
@@ -7,7 +7,6 @@
a.ion-social-facebook(href="/field-guide/how-can-i-find-other-free-code-camp-campers-in-my-city") Facebook
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') Twitter
a.ion-locked(href="/privacy") Privacy
- a.ion-android-mail(href="mailto:team@freecodecamp.com") Contact
.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
@@ -23,5 +22,3 @@
span.sr-only Free Code Camp on Twitter
a.ion-locked(href="/privacy")
span.sr-only Free Code Camp's Privacy Policy
- a.ion-android-mail(href="mailto:team@freecodecamp.com")
- span.sr-only Contact Free Code Camp by email
diff --git a/server/views/resources/calculator.jade b/server/views/resources/calculator.jade
index 373100da77..5e25352139 100644
--- a/server/views/resources/calculator.jade
+++ b/server/views/resources/calculator.jade
@@ -1,344 +1,104 @@
-extends ../layout
+extends ../layout-wide
block content
- .panel.panel-info
- .panel-heading.text-center Coding Bootcamp Cost Calculator
- .panel-body
- .row
+ script(src="../../../js/calculator.js")
+ .row
+ .col-xs-12.col-sm-10.col-md-8.col-lg-6.col-sm-offset-1.col-md-offset-2.col-lg-offset-3
+ h3.text-center.text-primary#chosen Coming from _______, and making $_______, your true costs will be:
+ #city-buttons
+ .spacer
+ h2.text-center Where do you live?
+ .spacer
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#atlanta.btn.btn-primary.btn-block.btn-lg Atlanta
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#austin.btn.btn-primary.btn-block.btn-lg Austin
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#brisbane.btn.btn-primary.btn-block.btn-lg Brisbane
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#boulder.btn.btn-primary.btn-block.btn-lg Boulder
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#chicago.btn.btn-primary.btn-block.btn-lg Chicago
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#denver.btn.btn-primary.btn-block.btn-lg Denver
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#hong-kong.btn.btn-primary.btn-block.btn-lg Hong Kong
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#london.btn.btn-primary.btn-block.btn-lg London
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#los-angeles.btn.btn-primary.btn-block.btn-lg Los Angeles
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#manchester.btn.btn-primary.btn-block.btn-lg Manchester
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#melbourne.btn.btn-primary.btn-block.btn-lg Melbourne
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#new-york-city.btn.btn-primary.btn-block.btn-lg New York City
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#portland.btn.btn-primary.btn-block.btn-lg Portland
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#raleigh-durham.btn.btn-primary.btn-block.btn-lg Raleigh-Durham
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#san-francisco.btn.btn-primary.btn-block.btn-lg San Fransisco
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#seattle.btn.btn-primary.btn-block.btn-lg Seattle
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#singapore.btn.btn-primary.btn-block.btn-lg Singapore
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#toronto.btn.btn-primary.btn-block.btn-lg Toronto
+ .col-xs-12.btn-nav
+ button#other.btn.btn-primary.btn-block.btn-lg Other
+ .spacer
+ #income.initially-hidden
+ .spacer
+ h2.text-center How much money did you make last year (in USD)?
+ .spacer
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#0.btn.btn-primary.btn-block.btn-lg(href='#') $0
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#20000.btn.btn-primary.btn-block.btn-lg(href='#') $20,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#30000.btn.btn-primary.btn-block.btn-lg(href='#') $30,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#40000.btn.btn-primary.btn-block.btn-lg(href='#') $40,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#50000.btn.btn-primary.btn-block.btn-lg(href='#') $50,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#60000.btn.btn-primary.btn-block.btn-lg(href='#') $60,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#70000.btn.btn-primary.btn-block.btn-lg(href='#') $70,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#80000.btn.btn-primary.btn-block.btn-lg(href='#') $80,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#90000.btn.btn-primary.btn-block.btn-lg(href='#') $90,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#100000.btn.btn-primary.btn-block.btn-lg(href='#') $100,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#120000.btn.btn-primary.btn-block.btn-lg(href='#') $120,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#140000.btn.btn-primary.btn-block.btn-lg(href='#') $140,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#160000.btn.btn-primary.btn-block.btn-lg(href='#') $160,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#180000.btn.btn-primary.btn-block.btn-lg(href='#') $180,000
+ .col-xs-12.col-sm-12.col-md-4.btn-nav
+ button#200000.btn.btn-primary.btn-block.btn-lg(href='#') $200,000
+ .spacer
+ #chart.initially-hidden
+ .d3-centered
+ svg.chart
+ #explanation.initially-hidden
.col-xs-12.col-sm-10.col-sm-offset-1
- h2.text-primary#chosen
- #chart-controls.initially-hidden
- form
- label
- input(type='radio', name='mode', value='grouped')
- | Grouped
- label
- input(type='radio', name='mode', value='stacked')
- | Stacked
- br
- a(href='/bootcamp-calculator.json') View Data Source JSON
- #city-buttons
- h2 Where do you live?
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#atlanta.btn.btn-primary.btn-block.btn-lg Atlanta
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#austin.btn.btn-primary.btn-block.btn-lg Austin
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#brisbane.btn.btn-primary.btn-block.btn-lg Brisbane
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#boulder.btn.btn-primary.btn-block.btn-lg Boulder
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#chicago.btn.btn-primary.btn-block.btn-lg Chicago
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#denver.btn.btn-primary.btn-block.btn-lg Denver
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#hong-kong.btn.btn-primary.btn-block.btn-lg Hong Kong
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#london.btn.btn-primary.btn-block.btn-lg London
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#los-angeles.btn.btn-primary.btn-block.btn-lg Los Angeles
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#manchester.btn.btn-primary.btn-block.btn-lg Manchester
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#melbourne.btn.btn-primary.btn-block.btn-lg Melbourne
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#new-york-city.btn.btn-primary.btn-block.btn-lg New York City
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#portland.btn.btn-primary.btn-block.btn-lg Portland
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#raleigh-durham.btn.btn-primary.btn-block.btn-lg Raleigh-Durham
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#san-francisco.btn.btn-primary.btn-block.btn-lg San Fransisco
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#seattle.btn.btn-primary.btn-block.btn-lg Seattle
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#singapore.btn.btn-primary.btn-block.btn-lg Singapore
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#toronto.btn.btn-primary.btn-block.btn-lg Toronto
- .col-xs-12.btn-nav
- button#other.btn.btn-primary.btn-block.btn-lg Other
- #income.hidden-by-default
- h2 How much money did you make last year (in USD)?
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#0.btn.btn-primary.btn-block.btn-lg(href='#') $0
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#20000.btn.btn-primary.btn-block.btn-lg(href='#') $20,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#30000.btn.btn-primary.btn-block.btn-lg(href='#') $30,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#40000.btn.btn-primary.btn-block.btn-lg(href='#') $40,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#50000.btn.btn-primary.btn-block.btn-lg(href='#') $50,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#60000.btn.btn-primary.btn-block.btn-lg(href='#') $60,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#70000.btn.btn-primary.btn-block.btn-lg(href='#') $70,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#80000.btn.btn-primary.btn-block.btn-lg(href='#') $80,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#90000.btn.btn-primary.btn-block.btn-lg(href='#') $90,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#100000.btn.btn-primary.btn-block.btn-lg(href='#') $100,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#120000.btn.btn-primary.btn-block.btn-lg(href='#') $120,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#140000.btn.btn-primary.btn-block.btn-lg(href='#') $140,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#160000.btn.btn-primary.btn-block.btn-lg(href='#') $160,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#180000.btn.btn-primary.btn-block.btn-lg(href='#') $180,000
- .col-xs-12.col-sm-6.col-md-4.btn-nav
- button#200000.btn.btn-primary.btn-block.btn-lg(href='#') $200,000
- #chart.hidden-by-default
- svg.chart
-
- script.
- $(document).ready(function () {
- var bootcamps = !{JSON.stringify(bootcampJson)};
- var city = "";
- var cityArray = ["san-fransisco", "los-angeles", "chicago", "austin", "new-york-city", "melbourne", "hong-kong", "seattle", "singapore", "london", "toronto", "portland", "brisbane", "atlanta", "raleigh-durham"];
- $('#city-buttons').on("click", "button", function () {
- city = $(this).attr("id");
- $('#city-buttons').hide();
- $('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}));
- $('#income').css({visibility: 'visible'});
- });
- $('#income').on("click", "button", function() {
- $('#income').hide();
- $('#chart').css({visibility: 'visible'});
- var lastYearsIncome = parseInt($(this).attr("id"));
- $('#chosen').append(' making $' + lastYearsIncome.toString().replace(/0000/, '0,000') + ', your true costs are:');
- var categoryNames = ['Opportunity Cost at Current Wage', 'Financing Cost', 'Housing Cost', 'Tuition / Wage Garnishing'];
- bootcamps.forEach(function (camp) {
- var x0 = 0;
- if (camp.cities.indexOf(city) > -1) {
- weeklyHousing = 0;
- } else {
- weeklyHousing = +camp.housing;
- }
- camp.mapping = [{
- name: camp.name,
- label: 'Opportunity Cost at Current Wage',
- value: +camp.cost,
- x0: x0,
- x1: x0 += +camp.cost
- }, {
- name: camp.name,
- label: 'Financing Cost',
- value: +Math.floor(camp.cost * .09519),
- x0: +camp.cost,
- x1: camp.finance ? x0 += +Math.floor(camp.cost * .09519) : 0
- }, {
- name: camp.name,
- label: 'Housing Cost',
- value: +weeklyHousing * camp.weeks,
- x0: camp.finance ? +Math.floor(camp.cost * 1.09519) : camp.cost,
- x1: x0 += weeklyHousing * camp.weeks
- }, {
- name: camp.name,
- label: 'Tuition / Wage Garnishing',
- value: +(Math.floor(camp.weeks * lastYearsIncome / 50)),
- x0: camp.finance ? +(Math.floor(camp.cost * 1.09519) + weeklyHousing * camp.weeks) : +camp.cost + weeklyHousing * camp.weeks,
- x1: x0 += +(Math.floor(camp.weeks * lastYearsIncome / 50))
- }];
- camp.total = camp.mapping[camp.mapping.length - 1].x1;
- });
- bootcamps.sort(function(a, b) { return a.total - b.total; });
- maxValue = 0;
- bootcamps.forEach(function (camp) {
- camp.mapping.forEach(function (thing) {
- //console.log(thing.value );
- if (thing.value > maxValue) {
- maxValue = thing.value;
- console.log(maxValue);
- }
- });
- });
- var xStackMax = d3.max(bootcamps, function (d) {
- return d.total;
- }), //Scale for Stacked
- xGroupMax = bootcamps.map(function (camp) {
- return camp.mapping.reduce(function (a, b) {
- return a.value > b.value ? a.value : b.value;
- });
- }).reduce(function (a, b) {
- return a > b ? a : b;
- });
- var margin = {
- top: 30,
- right: 60,
- bottom: 50,
- left: 140
- },
- width = 800 - margin.left - margin.right,
- height = 1200 - margin.top - margin.bottom;
- var barHeight = 20;
- var xScale = d3.scale.linear()
- .domain([0, xStackMax])
- .rangeRound([0, width]);
- var y0Scale = d3.scale.ordinal()
- .domain(bootcamps.map(function (d) {
- return d.name;
- }))
- .rangeRoundBands([0, height], .1);
- var y1Scale = d3.scale.ordinal()
- .domain(categoryNames).rangeRoundBands([0, y0Scale.rangeBand()]);
- var color = d3.scale.ordinal()
- .range(["#215f1e", "#5f5c1e", "#1e215f", "#5c1e5f"])
- .domain(categoryNames);
- var svg = d3.select("svg")
- .attr("width", width + margin.left + margin.right)
- .attr("height", height + margin.top + margin.bottom)
- .append("g")
- .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
- var selection = svg.selectAll(".series")
- .data(bootcamps)
- .enter().append("g")
- .attr("class", "series")
- .attr("transform", function (d) {
- return "translate(0," + y0Scale(d.name) + ")";
- });
- var rect = selection.selectAll("rect")
- .data(function (d) {
- return d.mapping;
- })
- .enter().append("rect")
- .attr("x", 0)
- .attr("width", 0)
- .attr("height", y0Scale.rangeBand())
- .style("fill", function (d) {
- return color(d.label);
- })
- .style("stroke", "white")
- .on("mouseover", function (d) {
- showPopover.call(this, d);
- })
- .on("mouseout", function (d) {
- removePopovers();
- });
- rect.transition()
- .delay(function (d, i) {
- return i * 10;
- })
- .attr("x", function (d) {
- return xScale(d.x0);
- })
- .attr("width", function (d) {
- return xScale((d.x1) - (d.x0));
- });
- d3.selectAll("input").on("change", change);
- var timeout= setTimeout(function () {
- d3.select("input[value=\"stacked\"]").property("checked",true).each(change);
- // d3.select("input[value=\"stacked\"]").property("checked",true).each(change);
- }, 4000);
- var timeout= setTimeout(function () {
- d3.select("input[value=\"grouped\"]").property("checked",true).each(change);
- }, 1500);
-
- function change() {
- clearTimeout(timeout);
- if (this.value === "grouped") transitionGrouped();
- else transitionStacked();
- }
-
- function transitionGrouped() {
- xScale.domain = ([0, xGroupMax]);
- rect.transition()
- .duration(500)
- .delay(function (d, i) {
- return i * 10;
- })
- .attr("width", function (d) {
- return xScale((d.x1) - (d.x0));
- })
- .transition()
- .attr("y", function (d) {
- return y1Scale(d.label);
- })
- .attr("x", 0)
- .attr("height", y1Scale.rangeBand())
- }
-
- function transitionStacked() {
- xScale.domain = ([0, xStackMax]);
- rect.transition()
- .duration(500)
- .delay(function (d, i) {
- return i * 10;
- })
- .attr("x", function (d) {
- return xScale(d.x0);
- })
- .transition()
- .attr("y", function (d) {
- return y0Scale(d.label);
- })
- .attr("height", y0Scale.rangeBand())
- }
-
- //axes
- var xAxis = d3.svg.axis()
- .scale(xScale)
- .orient("bottom");
- var yAxis = d3.svg.axis()
- .scale(y0Scale)
- .orient("left");
- svg.append("g")
- .attr("class", "y axis")
- .call(yAxis);
- svg.append("g")
- .attr("class", "x axis")
- .attr("transform", "translate(0," + height + ")")
- .call(xAxis)
- .append("text")
- .attr("x", 300)
- .attr("y", 35)
- .attr("dy", ".35em")
- .style("text-anchor", "middle")
- .text("Cost in $USD");
- //tooltips
- function removePopovers() {
- $('.popover').each(function () {
- $(this).remove();
- });
- }
-
- function showPopover(d) {
- $(this).popover({
- title: d.name,
- placement: 'auto top',
- container: 'body',
- trigger: 'manual',
- html: true,
- content: function () {
- return d.label +
- "
$" +
- d3.format(",")(d.value ? d.value : d.x1 - d.x0);
- }
- });
- $(this).popover('show')
- }
-
- //legends
- var legend = svg.selectAll(".legend")
- .data(categoryNames.slice().reverse())
- .enter().append("g")
- .attr("class", "legend")
- .attr("transform", function (d, i) {
- return "translate(30," + i * y0Scale.rangeBand() * 1.1 + ")";
- });
- legend.append("rect")
- .attr("x", width - y0Scale.rangeBand())
- .attr("width", y0Scale.rangeBand())
- .attr("height", y0Scale.rangeBand())
- .style("fill", color)
- .style("stroke", "white");
- legend.append("text")
- .attr("x", width - y0Scale.rangeBand() * 1.2)
- .attr("y", 12)
- .attr("dy", ".35em")
- .style("text-anchor", "end")
- .text(function (d) {
- return d;
- });
- });
- });
+ .text-center
+ button#transform.btn.btn-primary.btn-lg Transform
+ .button-spacer
+ a(href='/coding-bootcamp-cost-calculator.json') View Data Source JSON
+ span •
+ a(href='/coding-bootcamp-cost-calculator') Recalculate
+ h3 Notes:
+ ol
+ li.large-li For cash-up-front bootcamps, we assumed an APR of 6% and a term of 3 years.
+ li.large-li For wage-garnishing bootcamps, we assume 18% of first year wages at their advertised starting annual salary of around $100,000.
+ li.large-li We assume a cost of living of $500 for cities like San Francisco and New York City, and $400 per week for everywhere else.
+ li.large-li The most substantial cost for most people is lost wages. A 40-hour-per-week job at the US Federal minimum wage would pay at least $15,000 per year. You can read more about economic cost
+ a(href='https://en.wikipedia.org/wiki/Economic_cost' target='_blank') here
+ | .
+ li.large-li Free Code Camp. We don't charge tuition or garnish wages. We're fully online so you don't have to move. We're self-paced so you don't have to quit your job. Thus, your true cost of attending Free Code Camp will be $0.
diff --git a/setup.js b/setup.js
deleted file mode 100644
index 652e4ce228..0000000000
--- a/setup.js
+++ /dev/null
@@ -1,749 +0,0 @@
-var fs = require('fs');
-var os = require('os');
-var blessed = require('blessed');
-var multiline = require('multiline');
-
-if (os.platform() === 'win32') {
- console.log('**************************************************************');
- console.log('Hackathon Starter Generator has been disabled on Windows until');
- console.log('https://github.com/chjj/blessed is fixed or until I find a');
- console.log('better CLI module.');
- console.log('**************************************************************');
- process.exit();
-}
-var screen = blessed.screen({
- autoPadding: true
-});
-
-screen.key('q', function() {
- process.exit(0);
-});
-
-var home = blessed.list({
- parent: screen,
- padding: { top: 2 },
- mouse: true,
- keys: true,
- fg: 'white',
- bg: 'blue',
- selectedFg: 'blue',
- selectedBg: 'white',
- items: [
- '» REMOVE AUTHENTICATION PROVIDER',
- '» CHANGE EMAIL SERVICE',
- '» ADD NODE.JS CLUSTER SUPPORT',
- '» EXIT'
- ]
-});
-
-var homeTitle = blessed.text({
- parent: screen,
- align: 'center',
- fg: 'blue',
- bg: 'white',
- content: 'Hackathon Starter (c) 2014'
-});
-
-var footer = blessed.text({
- parent: screen,
- bottom: 0,
- fg: 'white',
- bg: 'blue',
- tags: true,
- content: ' {cyan-fg}{/cyan-fg} moves |' +
- ' {cyan-fg}{/cyan-fg} selects | {cyan-fg}{/cyan-fg} exits'
-});
-
-var inner = blessed.form({
- top: 'center',
- left: 'center',
- mouse: true,
- keys: true,
- width: 33,
- height: 10,
- border: {
- type: 'line',
- fg: 'white',
- bg: 'red'
- },
- fg: 'white',
- bg: 'red'
-});
-
-var success = blessed.box({
- top: 'center',
- left: 'center',
- mouse: true,
- keys: true,
- tags: true,
- width: '50%',
- height: '40%',
- border: {
- type: 'line',
- fg: 'white',
- bg: 'green'
- },
- fg: 'white',
- bg: 'green',
- padding: 1
-});
-
-success.on('keypress', function() {
- home.focus();
- home.remove(success);
-});
-
-var clusterText = blessed.text({
- top: 'top',
- bg: 'red',
- fg: 'white',
- tags: true,
- content: 'Take advantage of multi-core systems' +
- ' using built-in {underline}cluster{/underline} module.'
-});
-
-
-var enable = blessed.button({
- parent: inner,
- bottom: 0,
- mouse: true,
- shrink: true,
- name: 'enable',
- content: ' ENABLE ',
- border: {
- type: 'line',
- fg: 'white',
- bg: 'red'
- },
- style: {
- fg: 'white',
- bg: 'red',
- focus: {
- fg: 'red',
- bg: 'white'
- }
- }
-});
-
-
-var disable = blessed.button({
- parent: inner,
- bottom: 0,
- left: 10,
- mouse: true,
- shrink: true,
- name: 'disable',
- content: ' DISABLE ',
- border: {
- type: 'line',
- fg: 'white',
- bg: 'red'
- },
- style: {
- fg: 'white',
- bg: 'red',
- focus: {
- fg: 'red',
- bg: 'white'
- }
- }
-});
-
-var cancel = blessed.button({
- parent: inner,
- bottom: 0,
- left: 21,
- mouse: true,
- shrink: true,
- name: 'cancel',
- content: ' CANCEL ',
- border: {
- type: 'line',
- fg: 'white',
- bg: 'red'
- },
- style: {
- fg: 'white',
- bg: 'red',
- focus: {
- fg: 'red',
- bg: 'white'
- }
- }
-});
-
-cancel.on('press', function() {
- home.focus();
- home.remove(inner);
- screen.render();
-
-});
-
-var authForm = blessed.form({
- mouse: true,
- keys: true,
- fg: 'white',
- bg: 'blue',
- padding: { left: 1, right: 1 }
-});
-
-authForm.on('submit', function() {
- var passportConfig = fs.readFileSync('config/passport.js')
- .toString().split(os.EOL);
- var loginTemplate = fs.readFileSync('views/account/login.jade')
- .toString().split(os.EOL);
- var profileTemplate = fs.readFileSync('views/account/profile.jade')
- .toString().split(os.EOL);
- var userModel = fs.readFileSync('models/User.js').toString().split(os.EOL);
- var app = fs.readFileSync('app.js').toString().split(os.EOL);
- var secrets = fs.readFileSync('config/secrets.js').toString().split(os.EOL);
-
- var index = passportConfig
- .indexOf('var FacebookStrategy = require("passport-facebook").Strategy;');
- if (facebookCheckbox.checked && index !== -1) {
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with Facebook.');
- passportConfig.splice(index, 47);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
-
- index = loginTemplate.indexOf(
- ' a.btn.btn-block.btn-facebook.btn-social(href="/auth/facebook")'
- );
- loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
-
- index = profileTemplate.indexOf(' if user.facebook');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade',
- profileTemplate.join(os.EOL)
- );
-
- index = userModel.indexOf(' facebook: String,');
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
-
- index = app.indexOf([
- 'app.get("/auth/facebook"',
- 'passport.authenticate("facebook",',
- ' { scope: ["email", "user_location"] }));'
- ].join(' '));
-
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
- }
-
- index = passportConfig.indexOf(
- "var GitHubStrategy = require('passport-github').Strategy;"
- );
- if (githubCheckbox.checked && index !== -1) {
- console.log(index);
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with GitHub.');
- passportConfig.splice(index, 48);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
-
- index = loginTemplate.indexOf(
- " a.btn.btn-block.btn-github.btn-social(href='/auth/github')"
- );
- loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
-
- index = profileTemplate.indexOf(' if user.github');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade',
- profileTemplate.join(os.EOL)
- );
-
- index = userModel.indexOf(' github: String,');
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
-
- index = app.indexOf(
- 'app.get("/auth/github", passport.authenticate("github"));'
- );
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
- }
-
- index = passportConfig.indexOf(
- 'var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;'
- );
- if (googleCheckbox.checked && index !== -1) {
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with Google.');
- passportConfig.splice(index, 46);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
-
- index = loginTemplate.indexOf(
- " a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')"
- );
- loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
-
- index = profileTemplate.indexOf(' if user.google');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade',
- profileTemplate.join(os.EOL)
- );
-
- index = userModel.indexOf(' google: String,');
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
-
- index = app.indexOf(
- 'app.get("/auth/google",' +
- ' passport.authenticate("google", { scope: "profile email" }));'
- );
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
- }
-
- index = passportConfig.indexOf(
- 'var TwitterStrategy = require("passport-twitter").Strategy;'
- );
- if (twitterCheckbox.checked && index !== -1) {
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with Twitter.');
- passportConfig.splice(index, 43);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
-
- index = loginTemplate.indexOf(
- ' a.btn.btn-block.btn-twitter.btn-social(href="/auth/twitter")'
- );
- loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
-
- index = profileTemplate.indexOf(' if user.twitter');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade', profileTemplate.join(os.EOL)
- );
-
- index = userModel.indexOf(' twitter: String,');
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
-
- index = app.indexOf(
- "app.get('/auth/twitter', passport.authenticate('twitter'));"
- );
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
- }
-
- index = passportConfig.indexOf(
- 'var LinkedInStrategy = require("passport-linkedin-oauth2").Strategy;'
- );
- if (linkedinCheckbox.checked && index !== -1) {
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with LinkedIn.');
- passportConfig.splice(index, 47);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
-
- index = loginTemplate.indexOf(
- ' a.btn.btn-block.btn-linkedin.btn-social(href="/auth/linkedin")'
- );
- loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
-
- index = profileTemplate.indexOf(' if user.linkedin');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade',
- profileTemplate.join(os.EOL)
- );
-
- index = userModel.indexOf(' linkedin: String,');
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
-
- index = app.indexOf(
- 'app.get("/auth/linkedin",',
- ' passport.authenticate("linkedin", { state: "SOME STATE" }));'
- );
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
- }
-
- index = passportConfig.indexOf(
- 'var InstagramStrategy = require("passport-instagram").Strategy;'
- );
- if (instagramCheckbox.checked && index !== -1) {
- passportConfig.splice(index, 1);
- index = passportConfig.indexOf('// Sign in with Instagram.');
- passportConfig.splice(index, 43);
- fs.writeFileSync(
- 'config/passport.js',
- passportConfig.join(os.EOL)
- );
-
- index = loginTemplate.indexOf(
- ' a.btn.btn-block.btn-instagram.btn-social(href="/auth/instagram")'
- );
- loginTemplate.splice(index, 3);
-
- fs.writeFileSync(
- 'views/account/login.jade',
- loginTemplate.join(os.EOL)
- );
-
- index = profileTemplate.indexOf(' if user.instagram');
- profileTemplate.splice(index - 1, 5);
- fs.writeFileSync(
- 'views/account/profile.jade',
- profileTemplate.join(os.EOL)
- );
-
- index = app.indexOf(
- 'app.get("/auth/instagram", passport.authenticate("instagram"));'
- );
- app.splice(index, 4);
- fs.writeFileSync('app.js', app.join(os.EOL));
-
- userModel.splice(index, 1);
- fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- }
-
- home.remove(authForm);
- home.append(success);
- success.setContent(
- 'Selected authentication providers have been removed' +
- 'from passportConfig.js, User.js, server.js, login.jade and profile.jade!'
- );
- success.focus();
- screen.render();
-});
-
-var authText = blessed.text({
- parent: authForm,
- content: 'Selecting a checkbox removes an authentication provider.' +
- ' If authentication provider is already removed, no action will be taken.',
- padding: 1,
- bg: 'magenta',
- fg: 'white'
-});
-
-var facebookCheckbox = blessed.checkbox({
- parent: authForm,
- top: 6,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Facebook'
-});
-
-var githubCheckbox = blessed.checkbox({
- parent: authForm,
- top: 7,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'GitHub'
-});
-
-var googleCheckbox = blessed.checkbox({
- parent: authForm,
- top: 8,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Google'
-});
-
-var twitterCheckbox = blessed.checkbox({
- parent: authForm,
- top: 9,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Twitter'
-});
-
-var linkedinCheckbox = blessed.checkbox({
- parent: authForm,
- top: 10,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'LinkedIn'
-});
-
-var instagramCheckbox = blessed.checkbox({
- parent: authForm,
- top: 11,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Instagram'
-});
-
-var authSubmit = blessed.button({
- parent: authForm,
- top: 13,
- mouse: true,
- shrink: true,
- name: 'submit',
- content: ' SUBMIT ',
- style: {
- fg: 'blue',
- bg: 'white',
- focus: {
- fg: 'white',
- bg: 'red'
- }
- }
-});
-
-authSubmit.on('press', function() {
- authForm.submit();
-});
-
-var authCancel = blessed.button({
- parent: authForm,
- top: 13,
- left: 9,
- mouse: true,
- shrink: true,
- name: 'cancel',
- content: ' CANCEL ',
- style: {
- fg: 'blue',
- bg: 'white',
- focus: {
- fg: 'white',
- bg: 'red'
- }
- }
-});
-
-authCancel.on('press', function() {
- home.focus();
- home.remove(authForm);
- screen.render();
-});
-
-var emailForm = blessed.form({
- mouse: true,
- keys: true,
- fg: 'white',
- bg: 'blue',
- padding: { left: 1, right: 1 }
-});
-
-emailForm.on('submit', function() {
- var contactCtrl = fs.readFileSync('controllers/contact.js')
- .toString().split(os.EOL);
- var userCtrl = fs.readFileSync('controllers/user.js')
- .toString().split(os.EOL);
- var choice = null;
-
- if (sendgridRadio.checked) {
- choice = 'SendGrid';
- } else if (mailgunRadio.checked) {
- choice = 'Mailgun';
- } else if (mandrillRadio.checked) {
- choice = 'Mandrill';
- }
-
- var index = contactCtrl.indexOf(
- 'var transporter = nodemailer.createTransport({'
- );
- contactCtrl.splice(index + 1, 1, " service: '" + choice + "',");
- contactCtrl.splice(
- index + 3,
- 1,
- ' user: secrets.' + choice.toLowerCase() + '.user,'
- );
- contactCtrl.splice(
- index + 4,
- 1,
- ' pass: secrets.' + choice.toLowerCase() + '.password'
- );
- fs.writeFileSync('controllers/contact.js', contactCtrl.join(os.EOL));
-
- index = userCtrl.indexOf(
- ' var transporter = nodemailer.createTransport({'
- );
- userCtrl.splice(index + 1, 1, " service: '" + choice + "',");
- userCtrl.splice(
- index + 3,
- 1,
- ' user: secrets.' + choice.toLowerCase() + '.user,'
- );
- userCtrl.splice(
- index + 4,
- 1,
- ' pass: secrets.' + choice.toLowerCase() + '.password'
- );
-
- index = userCtrl.indexOf(
- ' var transporter = nodemailer.createTransport({',
- (index + 1)
- );
- userCtrl.splice(
- index + 1,
- 1,
- ' service: "' + choice + '",'
- );
-
- userCtrl.splice(
- index + 3,
- 1,
- ' user: secrets.' + choice.toLowerCase() + '.user,'
- );
- userCtrl.splice(
- index + 4,
- 1,
- ' pass: secrets.' + choice.toLowerCase() + '.password'
- );
- fs.writeFileSync('controllers/user.js', userCtrl.join(os.EOL));
-
- home.remove(emailForm);
- home.append(success);
- success.setContent('Email Service has been switched to ' + choice);
- success.focus();
- screen.render();
-});
-
-var emailText = blessed.text({
- parent: emailForm,
- content: 'Select one of the following email service providers ' +
- 'for {underline}contact form{/underline}' +
- ' and {underline}password reset{/underline}.',
- padding: 1,
- bg: 'red',
- fg: 'white',
- tags: true
-});
-
-var sendgridRadio = blessed.radiobutton({
- parent: emailForm,
- top: 5,
- checked: true,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'SendGrid'
-});
-
-var mailgunRadio = blessed.radiobutton({
- parent: emailForm,
- top: 6,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Mailgun'
-});
-
-var mandrillRadio = blessed.radiobutton({
- parent: emailForm,
- top: 7,
- mouse: true,
- fg: 'white',
- bg: 'blue',
- content: 'Mandrill'
-});
-
-var emailSubmit = blessed.button({
- parent: emailForm,
- top: 9,
- mouse: true,
- shrink: true,
- name: 'submit',
- content: ' SUBMIT ',
- style: {
- fg: 'blue',
- bg: 'white',
- focus: {
- fg: 'white',
- bg: 'red'
- }
- }
-});
-
-emailSubmit.on('press', function() {
- emailForm.submit();
-});
-
-var emailCancel = blessed.button({
- parent: emailForm,
- top: 9,
- left: 9,
- mouse: true,
- shrink: true,
- name: 'cancel',
- content: ' CANCEL ',
- style: {
- fg: 'blue',
- bg: 'white',
- focus: {
- fg: 'white',
- bg: 'red'
- }
- }
-});
-
-emailCancel.on('press', function() {
- home.focus();
- home.remove(emailForm);
- screen.render();
-
-});
-
-home.on('select', function(child, index) {
- switch (index) {
- case 0:
- home.append(authForm);
- authForm.focus();
- screen.render();
- break;
- case 1:
- home.append(emailForm);
- emailForm.focus();
- break;
- case 2:
- addClusterSupport();
- home.append(success);
- success.setContent([
- 'New file {underline}cluster_app.js{/underline} has been created.',
- 'Your app is now able to use more than 1 CPU by running',
- '{underline}node cluster_app.js{/underline}, which in turn',
- 'spawns multiple instances of {underline}server.js{/underline}'
- ].join(' '));
- success.focus();
- screen.render();
- break;
- default:
- process.exit(0);
- }
-});
-
-screen.render();
-
-
-function addClusterSupport() {
-
- var fileContents = multiline(function() {
-/*
-var os = require('os');
-var cluster = require('cluster');
-
-cluster.setupMaster({
- exec: 'server.js'
-});
-
-cluster.on('exit', function(worker) {
- console.log('worker ' + worker.id + ' died');
- cluster.fork();
-});
-
-for (var i = 0; i < os.cpus().length; i++) {
- cluster.fork();
-}
-*/
- });
-
- fs.writeFileSync('cluster_app.js', fileContents);
-}