Merge branch 'master' into courseware

Conflicts:
	controllers/bonfire.js
	views/layout.jade
This commit is contained in:
Michael Q Larson
2015-02-02 23:32:46 -08:00
27 changed files with 360 additions and 186 deletions

View File

@ -73,6 +73,10 @@ exports.returnIndividualBonfire = function(req, res, next) {
if (err) {
next(err);
}
var dashedNameFull = bonfire[bonfireNumber].name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('/bonfires/' + dashedNameFull);
}
if (bonfire.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a bonfire with that name. Please double check the name."

View File

@ -220,7 +220,10 @@
"Most triumphant!",
"One loop to rule them all!",
"Ain't got time to bleed!",
"By the power of Grayskull!"
"By the power of Grayskull!",
"You did it!",
"Storm that castle!",
"Face-melting guitar Solo!"
],
"phrases": [
"Shout it from on top of a mountain",

View File

@ -93,25 +93,19 @@ exports.getEmailSignup = function(req, res) {
*/
exports.postEmailSignup = function(req, res, next) {
console.log('post email signup called');
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
req.assert('confirmPassword', 'Passwords do not match')
.equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/email-signup');
console.log(errors);
debug(errors);
}
var user = new User({
email: req.body.email,
email: req.body.email.trim(),
password: req.body.password,
profile : {
username: req.body.username
username: req.body.username.trim()
}
});
@ -132,6 +126,29 @@ exports.postEmailSignup = function(req, res, next) {
res.redirect('/email-signup');
});
});
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
user: secrets.mandrill.user,
pass: secrets.mandrill.password
}
});
var mailOptions = {
to: user.email,
from: 'Team@freecodecamp.com',
subject: 'Welcome to Free Code Camp!',
text: [
'Greetings from San Francisco!\n\n',
'Thank you for joining our community.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
'Good luck with the challenges!\n\n',
'- the Volunteer Camp Counselor Team'
].join('')
};
transporter.sendMail(mailOptions, function(err) {
if (err) { return err; }
});
});
};
@ -141,17 +158,8 @@ exports.postEmailSignup = function(req, res, next) {
*/
exports.getAccount = function(req, res) {
Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) {
if (err) {
console.error('Challenge err: ', err);
next(err);
}
res.render('account/account', {
title: 'Manage your Free Code Camp Account',
challenges: c,
ch: req.user.challengesHash,
moment: moment
});
title: 'Manage your Free Code Camp Account'
});
};
@ -160,15 +168,9 @@ exports.getAccount = function(req, res) {
*/
exports.getAccountAngular = function(req, res) {
Challenge.find({}, null, { sort: { challengeNumber: 1 } }, function(err, c) {
if (err) {
console.error('Challenge err: ', err);
next(err);
}
res.json({
user: req.user
});
});
};
/**
@ -322,26 +324,26 @@ exports.postUpdateProfile = function(req, res, next) {
return res.redirect('/account');
}
var user = req.user;
user.email = req.body.email || '';
user.profile.name = req.body.name || '';
user.profile.username = req.body.username || '';
user.profile.location = req.body.location || '';
user.profile.githubProfile = req.body.githubProfile || '';
user.profile.coderbyteProfile = req.body.coderbyteProfile || '';
user.profile.linkedinProfile = req.body.linkedinProfile || '';
user.profile.codepenProfile = req.body.codepenProfile || '';
user.profile.twitterHandle = req.body.twitterHandle || '';
user.profile.bio = req.body.bio || '';
user.profile.picture = req.body.picture || '';
user.portfolio.website1Title = req.body.website1Title || '';
user.portfolio.website1Link = req.body.website1Link || '';
user.portfolio.website1Image = req.body.website1Image || '';
user.portfolio.website2Title = req.body.website2Title || '';
user.portfolio.website2Link = req.body.website2Link || '';
user.portfolio.website2Image = req.body.website2Image || '';
user.portfolio.website3Title = req.body.website3Title || '';
user.portfolio.website3Link = req.body.website3Link || '';
user.portfolio.website3Image = req.body.website3Image || '';
user.email = req.body.email.trim() || '';
user.profile.name = req.body.name.trim() || '';
user.profile.username = req.body.username.trim() || '';
user.profile.location = req.body.location.trim() || '';
user.profile.githubProfile = req.body.githubProfile.trim() || '';
user.profile.coderbyteProfile = req.body.coderbyteProfile.trim() || '';
user.profile.linkedinProfile = req.body.linkedinProfile.trim() || '';
user.profile.codepenProfile = req.body.codepenProfile.trim() || '';
user.profile.twitterHandle = req.body.twitterHandle.trim() || '';
user.profile.bio = req.body.bio.trim() || '';
user.profile.picture = req.body.picture.trim() || '';
user.portfolio.website1Title = req.body.website1Title.trim() || '';
user.portfolio.website1Link = req.body.website1Link.trim() || '';
user.portfolio.website1Image = req.body.website1Image.trim() || '';
user.portfolio.website2Title = req.body.website2Title.trim() || '';
user.portfolio.website2Link = req.body.website2Link.trim() || '';
user.portfolio.website2Image = req.body.website2Image.trim() || '';
user.portfolio.website3Title = req.body.website3Title.trim() || '';
user.portfolio.website3Link = req.body.website3Link.trim() || '';
user.portfolio.website3Image = req.body.website3Image.trim() || '';
user.save(function (err) {
@ -456,9 +458,6 @@ exports.getReset = function(req, res) {
*/
exports.postReset = function(req, res, next) {
req.assert('password', 'Password must be at least 4 characters long.').len(4);
req.assert('confirm', 'Passwords must match.').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
@ -547,8 +546,6 @@ exports.getForgot = function(req, res) {
*/
exports.postForgot = function(req, res, next) {
req.assert('email', 'Please enter a valid email address.').isEmail();
var errors = req.validationErrors();
if (errors) {

View File

@ -11,7 +11,8 @@
},
"scripts": {
"start": "node app.js",
"test": "mocha"
"test": "mocha",
"prepublish": "node seed_data/seed.js"
},
"dependencies": {
"async": "^0.9.0",
@ -32,12 +33,9 @@
"express-flash": "^0.0.2",
"express-session": "^1.9.2",
"express-validator": "^2.8.0",
"fbgraph": "^0.3.0",
"github-api": "^0.7.0",
"helmet": "^0.5.3",
"instagram-node": "^0.5.1",
"jade": "^1.8.0",
"lastfm": "^0.9.2",
"less": "^1.7.5",
"lodash": "^2.4.1",
"lusca": "^1.0.2",
@ -47,25 +45,18 @@
"morgan": "^1.5.0",
"newrelic": "^1.13.3",
"node": "0.0.0",
"node-foursquare": "^0.2.1",
"node-linkedin": "^0.3.4",
"node-rest-client": "^1.4.3",
"nodemailer": "^1.3.0",
"passport": "^0.2.1",
"passport-facebook": "^1.0.3",
"passport-github": "^0.1.5",
"passport-google-oauth": "^0.1.5",
"passport-instagram": "^0.1.2",
"passport-linkedin-oauth2": "^1.2.1",
"passport-local": "^1.0.0",
"passport-oauth": "^1.0.0",
"passport-twitter": "^1.0.2",
"request": "^2.49.0",
"sitemap": "^0.7.4",
"stripe": "^3.0.2",
"tumblr.js": "^0.0.4",
"twilio": "^1.9.0",
"twit": "^1.1.18",
"uglify-js": "^2.4.15",
"validator": "^3.22.1",
"yui": "^3.18.1"

12
public/browserconfig.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png"/>
<square310x310logo src="/mstile-310x310.png"/>
<wide310x150logo src="/mstile-310x150.png"/>
<TileColor>#492c14</TileColor>
</tile>
</msapplication>
</browserconfig>

View File

@ -154,6 +154,11 @@ ul {
}
.landing-icon {
height: 200px;
width: 200px;
}
.completion-icon{
font-size: 150px;
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

17
public/favicons.html Normal file
View File

@ -0,0 +1,17 @@
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/android-chrome-manifest.json">
<meta name="msapplication-TileColor" content="#492c14">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<meta name="theme-color" content="#ffffff">

View File

@ -22,6 +22,11 @@ var editor = myCodeMirror;
editor.setSize("100%", "auto");
var attempts = 0;
if (attempts) {
attempts = 0;
}
// Default value for editor if one isn't provided in (i.e. a challenge)
var nonChallengeValue = '/*Welcome to Bonfire, Free Code Camp\'s future CoderByte replacement.\n' +
'Please feel free to use Bonfire as an in-browser playground and linting tool.\n' +
@ -100,15 +105,18 @@ $('#submitButton').on('click', function () {
});
function bonfireExecute() {
attempts++;
ga('send', 'event', 'Bonfire', 'ran-code', bonfireName);
userTests= null;
$('#codeOutput').empty();
var userJavaScript = myCodeMirror.getValue();
userJavaScript = removeComments(userJavaScript);
userJavaScript = scrapeTests(userJavaScript);
// simple fix in case the user forgets to invoke their function
if (challengeEntryPoint) {
if (challengeEntryPoint && challengeSeed) {
userJavaScript = challengeEntryPoint + ' ' + userJavaScript;
}
console.log(userJavaScript);
submit(userJavaScript, function(cls, message) {
if (cls) {
codeOutput.setValue(message.error);
@ -224,5 +232,7 @@ var runTests = function(err, data) {
};
function showCompletion() {
ga('send', 'event', 'Bonfire', 'solved', bonfireName + ', Time: ' + (Math.floor(Date.now() / 1000) - started) +', Attempts: ' + attempts);
$('#complete-bonfire-dialog').modal('show');
}

View File

@ -1,4 +1,7 @@
$(document).ready(function() {
if (bonfireName !== undefined) {
ga('send', 'event', 'Bonfire', 'load', bonfireName + ':' + Math.floor(Date.now() / 1000));
}
var CSRF_HEADER = 'X-CSRF-Token';
@ -61,7 +64,6 @@ $(document).ready(function() {
var bonfireSolution = myCodeMirror.getValue();
var thisBonfireHash = passedBonfireHash || null;
var didCompleteWith = $('#completed-with').val() || null;
completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash);
});
@ -83,6 +85,7 @@ $(document).ready(function() {
// Bonfire instructions functions
$('#more-info').on('click', function() {
ga('send', 'event', 'Bonfire', 'more-info', bonfireName);
$('#brief-instructions').hide();
$('#long-instructions').show().removeClass('hide');

View File

@ -1,7 +1,7 @@
[
{
"_id" : "ad7123c8c441eddfaeb5bdef",
"name": "Meet Bonfire",
"name": "Meet Bonfire!",
"difficulty": "0",
"description": [
"Click the button below for further instructions.",
@ -255,6 +255,34 @@
"assert.deepEqual(inventory([], [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']]), [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']]);",
"assert.deepEqual(inventory([[0, 'Bowling Ball'], [0, 'Dirty Sock'], [0, 'Hair pin'], [0, 'Microphone']], [[1, 'Hair Pin'], [1, 'Half-Eaten Apple'], [1, 'Bowling Ball'], [1, 'Toothpaste']]), [[1, 'Bowling Ball'], [1, 'Dirty Sock'], [1, 'Hair pin'], [1, 'Half-Eaten Apple'], [1, 'Microphone'], [1, 'Toothpaste']]);"
]
},
{
"_id": "a2f1d72d9b908d0bd72bb9f6",
"name": "Make a Person",
"difficulty": "3.12",
"description": [
"Fill in the object constructor with the methods specified in the tests.",
"Those methods are getFirstName(), getLastName(), getFullName(), setFirstName(), setLastName(), and setFullName().",
"These methods must be the only available means for interacting with the object.",
"There will be some linting errors on the tests, you may safely ignore them. You should see undefined in the console output."
],
"challengeEntryPoint": "var bob = new Person('Bob Ross');",
"challengeSeed": "var Person = function(firstAndLast) {\n return firstAndLast;\r\n};",
"tests": [
"expect(Object.keys(bob).length).to.eql(6);",
"expect(bob instanceof Person).to.be.true;",
"expect(bob.firstName).to.be.undefined();",
"expect(bob.lastName).to.be.undefined();",
"expect(bob.getFirstName()).to.eql('Bob');",
"expect(bob.getLastName()).to.eql('Ross');",
"expect(bob.getFullName()).to.eql('Bob Ross');",
"bob.setFirstName('Happy');",
"expect(bob.getFirstName()).to.eql('Happy');",
"bob.setLastName('Trees');",
"expect(bob.getLastName()).to.eql('Trees');",
"bob.setFullName('George Carlin');",
"expect(bob.getFullName()).to.eql('George Carlin');"
]
}
]

View File

@ -420,8 +420,8 @@
"OK, we're finally ready to start pair programming!",
"Pair Programming is where two people code together on the same computer. It is an efficient way to collaborate, and widely practiced at software companies. Pair Programming is one of the core concepts of \"Agile\" Software Development, which you will hear more about later.",
"Many people use Skype or Google Hangouts to pair program, but if you talk with professional software engineers, they will tell you that it's not really pair programming unless both people have the ability to use the keyboard and mouse.",
"The most popular tool for pair programming is Screen Hero. Note that Screen Hero isn't free, and isn't yet available for Linux. Screen Hero does come with a 14 day free trial, is free for students, and you can also use it for free if your pair has a 14-day trial or a paid subscription to it. Download Screen Hero here: <a href='https://screenhero.com/download.html' target='_blank'>https://screenhero.com/download.html</a>.",
"If you are using Linux, or if your 14 day free trial for Screen Hero has expired and you don't want to pay $10 per month for it, go to <a href='http://www.freecodecamp.com/pair-program-with-team-viewer' target='_blank'>http://www.freecodecamp.com/pair-program-with-team-viewer</a> to learn how to use an alternative (but inferior) tool called Team Viewer.",
"The most popular tool for pair programming is Screen Hero. You can download Screen Hero for <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=' target='_blank'>Mac</a> or <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9' target='_blank'>Windows</a>. Create your new user account from within the app.",
"If you are using Linux, go to <a href='http://www.freecodecamp.com/pair-program-with-team-viewer' target='_blank'>http://www.freecodecamp.com/pair-program-with-team-viewer</a> to learn how to use an alternative (but inferior) tool called Team Viewer.",
"We have a special chat room for people ready to pair program. Go to <a href='https://gitter.im/FreeCodeCamp/LetsPair' target='_blank'>https://gitter.im/FreeCodeCamp/LetsPair</a> and type \"Hello Pair Programmers!\"",
"If someone is available, they will be your \"pair\" - the person you pair programming with.",
"Private message your pair and ask for the email address he or she used to register Screen Hero.",

View File

@ -14,15 +14,15 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='name') Name *
.col-sm-4
input.form-control(type='text', placeholder='Name', name='name', autocomplete="off", ng-model='user.profile.name', ng-minlength='3', ng-maxlength='50', required='required', id='name')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.name.$invalid && profileForm.name.$error.required")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.name.$invalid && profileForm.name.$error.required")
alert(type='danger')
span.ion-close-circled(id='#name-error')
| Your name is required.
.col-sm-4.col-sm-offset-5(ng-show='profileForm.name.$error.minlength && !profileForm.name.$pristine')
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.name.$error.minlength && !profileForm.name.$pristine')
alert(type='danger')
span.ion-close-circled
| Your name must be at least 3 characters.
.col-sm-4.col-sm-offset-5(ng-show='profileForm.name.$error.maxlength && !profileForm.name.$pristine')
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.name.$error.maxlength && !profileForm.name.$pristine')
alert(type='danger')
span.ion-close-circled
| Your name must be fewer than 50 characters.
@ -31,43 +31,43 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='username') Username (path to public profile) *
.col-sm-4
input.form-control(type='text', placeholder='username' name='username', autocomplete="off", id='username', ng-model='user.profile.username', required='required', ng-minlength='5', ng-maxlength='20', ng-keypress='', unique-username='', ng-pattern="/^[A-z0-9_]+$/")
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.pattern")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.pattern")
alert(type='danger')
span.ion-close-circled
| Your username should only contain letters, numbers and underscores (az10_).
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.required")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.required")
alert(type='danger')
span.ion-close-circled
| Your username is required.
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.minlength && !profileForm.username.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.minlength && !profileForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| Your username must be at least 5 characters.
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.maxlength && !profileForm.username.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.maxlength && !profileForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| Your username must be fewer than 15 characters.
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.unique && !profileForm.username.$pristine && $scope.storedUsername !== user.profile.username")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.unique && !profileForm.username.$pristine && $scope.storedUsername !== user.profile.username")
alert(type='danger')
span.ion-close-circled
| That username is already taken.
| That username is already in use.
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='email') Email *
.col-sm-4
input.form-control(type='email', name='email', id='email', autocomplete="off", ng-model='user.email', required='required', ng-keypress='', unique-email='')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.email.$error.required")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.email.$error.required")
alert(type='danger')
span.ion-close-circled
| Your email address is required.
.col-sm-4.col-sm-offset-5(ng-show="profileForm.$error.email && !profileForm.email.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.$error.email && !profileForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid email format.
.col-sm-4.col-sm-offset-5(ng-show="profileForm.email.$error.unique && !profileForm.email.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.email.$error.unique && !profileForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| That email is already taken.
| That email is already in use.
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='location') Location
@ -77,17 +77,20 @@ block content
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='email') Link to Profile Photo (1:1 ratio)
.col-sm-4
input.form-control(type='url', name='picture', id='picture', ng-model='user.profile.picture', placeholder='http://')
input.form-control(type='url', name='picture', id='picture', ng-model='user.profile.picture', placeholder='http://www.example.com/image.jpg', ng-pattern="/[\.](jpg|png|jpeg|gif)\s?$/")
.col-sm-4.col-sm-offset-5(ng-show="profileForm.picture.$error.url && !profileForm.picture.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
| Please enter a valid URL format (http://www.example.com/image.jpg).
.col-sm-4.col-sm-offset-5(ng-show="profileForm.picture.$error.pattern")
alert(type='danger')
span.ion-close-circled
| The image URL must end in .jpg, .png, .jpeg or .gif.
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='bio') Bio (140 characters)
.col-sm-4
input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.profile.bio', ng-maxlength='140', id='bio')
.col-sm-4.col-sm-offset-5(ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
alert(type='danger')
span.ion-close-circled
| Your bio must be fewer than 140 characters.
@ -107,11 +110,11 @@ block content
.input-group.twitter-input
span.input-group-addon @
input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.profile.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/")
.col-sm-4.col-sm-offset-5(ng-show="profileForm.twitterHandle.$error.pattern")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.twitterHandle.$error.pattern")
alert(type='danger')
span.ion-close-circled
| Your Twitter handle should only contain letters, numbers and underscores (az10_).
.col-sm-4.col-sm-offset-5(ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
alert(type='danger')
span.ion-close-circled
| Your name must be fewer than 15 characters.
@ -119,7 +122,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='email') Github
.col-sm-4
input.form-control(type='url', name='githubProfile', id='githubProfile', autocomplete="off", ng-model='user.profile.githubProfile', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.githubProfile.$error.url && !profileForm.githubProfile.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.githubProfile.$error.url && !profileForm.githubProfile.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -128,7 +131,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='email') CodePen
.col-sm-4
input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.profile.codepenProfile', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -137,7 +140,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='email') CoderByte
.col-sm-4
input.form-control(type='url', name='coderbyteProfile', id='coderbyteProfile', autocomplete="off", ng-model='user.profile.coderbyteProfile', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.coderbyteProfile.$error.url && !profileForm.coderbyteProfile.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.coderbyteProfile.$error.url && !profileForm.coderbyteProfile.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -146,7 +149,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='email') LinkedIn
.col-sm-4
input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.profile.linkedinProfile', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -167,7 +170,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website1Title') Title
.col-sm-4
input.form-control(type='text', name='website1Title', id='website1Title', autocomplete="off", ng-model='user.portfolio.website1Title', ng-maxlength='140')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Title.$error.maxlength && !profileForm.website1Title.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website1Title.$error.maxlength && !profileForm.website1Title.$pristine")
alert(type='danger')
span.ion-close-circled
| Portfolio project title must be fewer than 140 characters.
@ -176,7 +179,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website1Link') Link
.col-sm-4
input.form-control(type='url', name='website1Link', id='website1Link', autocomplete="off", ng-model='user.portfolio.website1Link', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Link.$error.url && !profileForm.website1Link.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website1Link.$error.url && !profileForm.website1Link.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -184,11 +187,15 @@ 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://')
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?$/")
.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).
| 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
@ -197,7 +204,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website2Title') Title
.col-sm-4
input.form-control(type='text', name='website2Title', id='website2Title', autocomplete="off", ng-model='user.portfolio.website2Title', ng-maxlength='140')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Title.$error.maxlength && !profileForm.website2Title.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website2Title.$error.maxlength && !profileForm.website2Title.$pristine")
alert(type='danger')
span.ion-close-circled
| Portfolio project title must be fewer than 140 characters.
@ -206,18 +213,22 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website2Link') Link
.col-sm-4
input.form-control(type='url', name='website2Link', id='website2Link', autocomplete="off", ng-model='user.portfolio.website2Link', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Link.$error.url && !profileForm.website2Link.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website2Link.$error.url && !profileForm.website2Link.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
.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://')
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?$/")
.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).
| 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
@ -226,7 +237,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website3Title') Title
.col-sm-4
input.form-control(type='text', name='website3Title', id='website3Title', autocomplete="off", ng-model='user.portfolio.website3Title', ng-maxlength='140')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Title.$error.maxlength && !profileForm.website3Title.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website3Title.$error.maxlength && !profileForm.website3Title.$pristine")
alert(type='danger')
span.ion-close-circled
| Portfolio project title must be fewer than 140 characters.
@ -235,7 +246,7 @@ block content
label.col-sm-3.col-sm-offset-2.control-label(for='website3Link') Link
.col-sm-4
input.form-control(type='url', name='website3Link', id='website3Link', autocomplete="off", ng-model='user.portfolio.website3Link', placeholder='http://')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Link.$error.url && !profileForm.website3Link.$pristine")
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website3Link.$error.url && !profileForm.website3Link.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
@ -243,11 +254,15 @@ block content
.form-group
label.col-sm-3.col-sm-offset-2.control-label(for='website3Image') Image Link (4:3 ratio)
.col-sm-4
input.form-control(type='url', name='website3Image', id='website3Image', autocomplete="off", ng-model='user.portfolio.website3Image', placeholder='http://')
input.form-control(type='url', name='website3Image', id='website3Image', autocomplete="off", ng-model='user.portfolio.website3Image', placeholder='http://www.example.com/image.jpg', ng-pattern='/[\.](jpg|png|jpeg|gif)(\s+)?$/')
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Image.$error.url && !profileForm.website3Image.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid URL format (http://www.example.com).
| 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
@ -293,23 +308,7 @@ block content
a.btn.btn-lg.btn-block.btn-twitter.btn-link-social(href='/auth/twitter')
i.fa.fa-twitter
| Link Twitter with your account
br
- if (ch[0] > 0)
.panel.panel-primary
.panel-heading.text-center Completed Challenges:
.panel-body
.col-xs-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
.panel.panel-danger
.panel-heading.text-center Danger Zone:
.panel-body

View File

@ -7,46 +7,54 @@ block content
.form-group
.col-sm-6.col-sm-offset-3
input.form-control(type='email', ng-model='email', ng-keypress='', name='email', id='email', placeholder='email', autofocus, required, autocomplete="off", unique-email='')
.col-sm-6.col-sm-offset-3(ng-show="signupForm.email.$error.unique && !signupForm.email.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.email.$error.required && !signupForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| This email is taken.
| Your email address is required.
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.$error.email && !signupForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid email format.
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.email.$error.unique && !signupForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| That email address is already in use.
.form-group
.col-sm-6.col-sm-offset-3
input.form-control(type='text', name='username', ng-keypress='', autocomplete="off", id='username', placeholder='username', ng-model='username', unique-username='', required, ng-minlength=5, ng-maxlength=20, ng-pattern="/^[A-z0-9_]+$/")
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.pattern && !signupForm.username.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="profileForm.username.$error.pattern && !signupForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| Your username should only contain letters, numbers and underscores (az10_).
.col-sm-6.col-sm-offset-3(ng-show="signupForm.username.$error.unique && !signupForm.username.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.unique && !signupForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| This username is taken.
.col-sm-6.col-sm-offset-3(ng-show="signupForm.username.$error.minlength && !signupForm.username.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.minlength && !signupForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| Your username must be at least 5 characters long.
.col-sm-6.col-sm-offset-3(ng-show="signupForm.username.$error.maxlength && !signupForm.username.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.maxlength && !signupForm.username.$pristine")
alert(type='danger')
span.ion-close-circled
| Your usernames must be 20 characters or fewer.
.form-group
.col-sm-6.col-sm-offset-3
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=5)
.col-sm-6.col-sm-offset-3(ng-show="signupForm.password.$error.minlength && !signupForm.password.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.password.$error.minlength && !signupForm.password.$pristine")
alert(type='danger')
span.ion-close-circled
| Your password must be at least 8 characters long.
.form-group
.col-sm-6.col-sm-offset-3
input.form-control(type='password', ng-model='confirmPassword', name='confirmPassword', id='confirmPassword', placeholder='confirm password', required, ng-minlength=5)
.col-sm-6.col-sm-offset-3(ng-show="(confirmPassword !== password) && !signupForm.confirmPassword.$pristine")
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="(confirmPassword !== password) && !signupForm.confirmPassword.$pristine")
alert(type='danger')
span.ion-close-circled
| Passwords must match.
.form-group
.col-sm-offset-3.col-sm-6
button.btn.btn-success(type='submit')
.col-sm-6.col-sm-offset-3
button.btn.btn-success(type='submit', ng-disabled='signupForm.$invalid')
span.ion-person-add
| Signup
br

View File

@ -33,6 +33,9 @@ block content
h3.flat-top.bolded.wrappable= location
h4.flat-top.wrappable= bio
.col-xs-12.col-sm-12.col-md-3.text-center
if (user && user.profile.username === username)
a.btn.btn-warning(href="/account") Edit my Profile
.background-svg.img-center
.points-on-top
= "[ " + points + " ]"

View File

@ -0,0 +1,45 @@
extends ../layout-wide
block content
script(src='/js/lib/codemirror/lib/codemirror.js')
script(src='/js/lib/codemirror/addon/edit/closebrackets.js')
script(src='/js/lib/codemirror/addon/edit/matchbrackets.js')
script(src='/js/lib/codemirror/addon/lint/lint.js')
script(src='/js/lib/codemirror/addon/lint/javascript-lint.js')
script(src='//ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js')
script(src='/js/lib/chai/chai.js')
link(rel='stylesheet', href='/js/lib/codemirror/lib/codemirror.css')
link(rel='stylesheet', href='/js/lib/codemirror/addon/lint/lint.css')
link(rel='stylesheet', href='/js/lib/codemirror/theme/monokai.css')
link(rel="stylesheet", href="http://fonts.googleapis.com/css?family=Ubuntu+Mono")
script(src='/js/lib/codemirror/mode/javascript/javascript.js')
script(src='/js/lib/jailed/jailed.js')
script(src='/js/lib/bonfire/bonfireInit.js')
.row
script(type="text/javascript").
var tests = !{JSON.stringify(tests)};
var challengeSeed = !{JSON.stringify(challengeSeed)};
var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)};
var title = !{JSON.stringify(title)};
#mainEditorPanel.col-sm-12.col-md-7.col-xs-12
.panel.panel-primary.panel-bonfire
.panel-heading.text-center Playground
.panel.panel-body
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
#testCreatePanel.col-sm-12.col-md-5.col-xs-12
.panel.panel-primary.panel-bonfire
.panel-heading.text-center Output
.panel.panel-body
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
br
form.code
.form-group.codeMirrorView
textarea#codeOutput
br
ul#testSuite.list-group
br
script(src='/js/lib/bonfire/bonfireFramework.js')

View File

@ -90,6 +90,8 @@ block content
var challengeSeed = !{JSON.stringify(challengeSeed)};
var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)};
var passedBonfireHash = !{JSON.stringify(bonfireHash)};
var bonfireName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
.col-xs-12.col-sm-12.col-md-8
#mainEditorPanel
form.code
@ -108,7 +110,7 @@ block content
.text-center
.animated.zoomInDown.delay-half
span.landing-icon.ion-checkmark-circled.text-primary
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
form.form-horizontal(novalidate='novalidate', name='completedWithForm')
.form-group.text-center
@ -116,7 +118,7 @@ block content
// extra field to distract password tools like lastpass from injecting css into our username field
input.form-control(ng-show="false")
input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus)
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0")
.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

View File

@ -24,7 +24,7 @@ block content
.modal-body
.text-center
.animated.zoomInDown.delay-half
span.landing-icon.ion-checkmark-circled.text-primary
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-challenge-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge
- if (points && points > 2)

View File

@ -14,11 +14,11 @@ block content
label(class='col-sm-3 control-label', for='email') Your email *
.col-sm-8
input.form-control(type='text', name='email', id='email', autocomplete="off", ng-model='email', required='required', ng-keypress='')
.col-sm-8.col-sm-offset-3(ng-show="doneWithFirst100HoursForm.$error.email && !doneWithFirst100HoursForm.email.$pristine")
.col-sm-8.col-sm-offset-3(ng-cloak, ng-show="doneWithFirst100HoursForm.$error.email && !doneWithFirst100HoursForm.email.$pristine")
alert(type='danger')
span.ion-close-circled
| Please enter a valid email format.
.col-sm-8.col-sm-offset-3(ng-show="doneWithFirst100HoursForm.email.$invalid && doneWithFirst100HoursForm.email.$error.required && !doneWithFirst100HoursForm.email.$pristine")
.col-sm-8.col-sm-offset-3(ng-cloak, ng-show="doneWithFirst100HoursForm.email.$invalid && doneWithFirst100HoursForm.email.$error.required && !doneWithFirst100HoursForm.email.$pristine")
alert(type='danger')
span.ion-close-circled(id='#email-error').
Your email is required.

View File

@ -20,7 +20,7 @@ block content
label(class='col-sm-2 control-label', for='name') Your name *
.col-sm-8
input.form-control(type='text', name='name', id='name', autocomplete="off", ng-model='name', required='required')
.col-sm-8.col-sm-offset-2(ng-show="nonprofitForm.name.$invalid && nonprofitForm.name.$error.required && !nonprofitForm.name.$pristine")
.col-sm-8.col-sm-offset-2(ng-cloak, ng-show="nonprofitForm.name.$invalid && nonprofitForm.name.$error.required && !nonprofitForm.name.$pristine")
alert(type='danger')
span.ion-close-circled(id='#name-error')
| Your name is required.
@ -28,7 +28,7 @@ block content
label(class='col-sm-2 control-label', for='email') Your email *
.col-sm-8
input.form-control(type='text', name='email', id='email', autocomplete="off", ng-model='email', required='required')
.col-sm-8.col-sm-offset-2(ng-show="nonprofitForm.email.$invalid && nonprofitForm.email.$error.required && !nonprofitForm.email.$pristine")
.col-sm-8.col-sm-offset-2(ng-cloak, ng-show="nonprofitForm.email.$invalid && nonprofitForm.email.$error.required && !nonprofitForm.email.$pristine")
alert(type='danger')
span.ion-close-circled(id='#email-error').
Your email is required.
@ -36,7 +36,7 @@ block content
label(class='col-sm-2 control-label', for='message') Briefly describe what problem you need to solve, and for whom. *
.col-sm-8
textarea.form-control(type='text', name='message', id='message', rows='7', autocomplete="off", ng-model='message', required='required')
.col-sm-8.col-sm-offset-2(ng-show="nonprofitForm.message.$invalid && nonprofitForm.message.$error.required && !nonprofitForm.message.$pristine")
.col-sm-8.col-sm-offset-2(ng-cloak, ng-show="nonprofitForm.message.$invalid && nonprofitForm.message.$error.required && !nonprofitForm.message.$pristine")
alert(type='danger')
span.ion-close-circled(id='#message-error')
| Your message is required.

View File

@ -7,19 +7,19 @@ block content
.row
.col-xs-12.col-sm-12.col-md-3
h3.nowrap Get Connected
img.img-responsive(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_connect.svg', title='Get great references and connections to help you get a job')
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_connect.svg', title='Get great references and connections to help you get a job')
p.landing-p Join a community of busy, motivated professionals.
.col-xs-12.col-sm-12.col-md-3
h3.nowrap Learn JavaScript
img.img-responsive(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_learn.svg', title='Learn to code')
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_learn.svg', title='Learn to code')
p.landing-p Work together on Full Stack JavaScript coding challenges.
.col-xs-12.col-sm-12.col-md-3
h3.nowrap Build your Portfolio
img.img-responsive(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_portfolio.svg', title='Build a portfolio of apps for nonprofits')
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_portfolio.svg', title='Build a portfolio of apps for nonprofits')
p.landing-p Build apps that solve real problems for real people.
.col-xs-12.col-sm-12.col-md-3
h3.nowrap Help Nonprofits
img.img-responsive(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_nonprofits.svg', title='Help nonprofits')
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landingIcons_nonprofits.svg', title='Help nonprofits')
p.landing-p Give nonprofits a boost by empowering them with code.
.big-break
a.btn.btn-cta.signup-btn(href="/login") Start learning to code (it's free)

View File

@ -2,7 +2,7 @@ doctype html
html(ng-app='profileValidation', lang='en')
head
script(src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js")
script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js")
script(src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.11/angular.min.js")
script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js")
link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicon.ico')
link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css')
@ -30,4 +30,3 @@ script.
ga('create', 'UA-55446531-1', 'auto');
ga('require', 'displayfeatures');
ga('send', 'pageview');
script(src="//cdn.optimizely.com/js/999692993.js")

View File

@ -2,12 +2,10 @@ doctype html
html(ng-app='profileValidation', lang='en')
head
script(src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js")
script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js")
script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js")
script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js")
link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicon.ico')
link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css')
link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css')
link(rel='stylesheet', href='/css/lib/ionicons/ionicons.min.css')
include partials/meta
title #{title} | Free Code Camp
meta(charset='utf-8')
@ -31,4 +29,3 @@ script.
ga('create', 'UA-55446531-1', 'auto');
ga('require', 'displayfeatures');
ga('send', 'pageview');
script(src="//cdn.optimizely.com/js/999692993.js")

View File

@ -126,3 +126,9 @@
img.profile-image(src='https://s3.amazonaws.com/freecodecamp/kamal-sharif.jpg' alt="Kamal Sharif's picture")
h4.text-nowrap Dhaka, Bangladesh
p.negative-10 "I build applications that help other people improve their own lives."
.col-xs-12.col-sm-4.col-md-3.team-member
h3.negative-10.text-nowrap Patrick Ly
h4.negative-10.text-nowrap Community Builder
img.profile-image(src='https://s3.amazonaws.com/freecodecamp/patrick-ly.jpg' alt="Patrick Ly's picture")
h4.text-nowrap Los Angeles, California
p.negative-10 "I'm a student and self-taught Hip Hop dancer. Just using the web isn't enough for me. I want to create and maintain web apps, too"

View File

@ -4,24 +4,54 @@ meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1.0')
meta(name='csrf-token', content=_csrf)
meta(name='keywords', content='learn to code, learn how to code, code, coding, software engineer, software developer, mean stack, pair programming, node.js, angular.js, express.js, mongoDB, coding bootcamp')
meta(content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.", property="og:title")
meta(content="FreeCodeCamp", property="og:site_name")
meta(property="og:title", content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.")
meta(property="og:site_name", content="FreeCodeCamp")
meta(name='twitter:widgets:csp', content='on')
meta(name='p:domain_verify', content='d0bc047a482c03c24f1168004c2a216a')
meta(content="http://www.freecodecamp.com", property="og:url")
meta(content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.", property="og:description")
meta(content="https://pbs.twimg.com/profile_images/522961310212833280/XE6vGAaO.jpeg", property="og:image")
meta(content="article", property="og:type")
meta(content="https://www.facebook.com/freecodecamp", property="article:publisher")
meta(content="Responsive", property="article:section")
link(href="https://plus.google.com/+Freecodecamp", rel="publisher")
link(href="https://plus.google.com/+Freecodecamp", rel="author")
meta(content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.", name="description")
meta(content="@freecodecamp", name="twitter:creator")
meta(content="http://www.freecodecamp.com", name="twitter:url")
meta(content="@freecodecamp", name="twitter:site")
meta(content="summary_large_image", name="twitter:card")
meta(content="https://s3.amazonaws.com/freecodecamp/challenges.png", name="twitter:image:src")
meta(content="Free Code Camp: a community of busy people learning to code", name="twitter:title")
meta(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.", name="twitter:description")
meta(property="og:url", content="http://www.freecodecamp.com")
meta(property="og:description", content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.")
meta(property="og:image", content="https://pbs.twimg.com/profile_images/522961310212833280/XE6vGAaO.jpeg")
meta(property="og:type", content="article")
meta(property="article:publisher", content="https://www.facebook.com/freecodecamp")
meta(property="article:section", content="Responsive")
link(rel="publisher", href="https://plus.google.com/+Freecodecamp")
link(rel="author", href="https://plus.google.com/+Freecodecamp")
meta(name="description", content="Free Code Camp is a community of busy people who learn to code by collaborating on projects for nonprofits. Build your full stack JavaScript Portfolio today.")
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: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")
meta(name="msapplication-TileColor", content="#FFFFFF")
meta(name="msapplication-TileImage", content="/")
link(rel="android-chrome", sizes="144x144" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-144x144.png")
link(rel="android-chrome", sizes="192x192" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-192x192.png")
link(rel="android-chrome", sizes="36x36" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-36x36.png")
link(rel="android-chrome", sizes="48x48" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-48x48.png")
link(rel="android-chrome", sizes="72x72" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-72x72.png")
link(rel="android-chrome", sizes="96x96" href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-96x96.png")
link(rel="android-chrome-manifest", href="https://s3.amazonaws.com/freecodecamp/favicons/android-chrome-manifest.json")
link(rel="apple-touch-icon", sizes="114x114", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-114x114.png")
link(rel="apple-touch-icon", sizes="120x120", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-120x120.png")
link(rel="apple-touch-icon", sizes="144x144", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-144x144.png")
link(rel="apple-touch-icon", sizes="152x152", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-152x152.png")
link(rel="apple-touch-icon", sizes="180x180", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png")
link(rel="apple-touch-icon", sizes="57x57", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-57x57.png")
link(rel="apple-touch-icon", sizes="60x60", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-60x60.png")
link(rel="apple-touch-icon", sizes="72x72", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-72x72.png")
link(rel="apple-touch-icon", sizes="76x76", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-76x76.png")
link(rel="apple-touch-icon-precomposed", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-precomposed.png")
link(rel="apple-touch-icon", href="https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon.png")
link(rel="favicon", sizes="16x16", href="https://s3.amazonaws.com/freecodecamp/favicons/favicon-16x16.png")
link(rel="favicon", sizes="32x32", href="https://s3.amazonaws.com/freecodecamp/favicons/favicon-32x32.png")
link(rel="favicon", sizes="96x96", href="https://s3.amazonaws.com/freecodecamp/favicons/favicon-96x96.png")
link(rel="mstile", sizes="144x144", href="https://s3.amazonaws.com/freecodecamp/favicons/mstile-144x144.png")
link(rel="mstile", sizes="150x150", href="https://s3.amazonaws.com/freecodecamp/favicons/mstile-150x150.png")
link(rel="mstile", sizes="310x310", href="https://s3.amazonaws.com/freecodecamp/favicons/mstile-310x310.png")
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')

View File

@ -37,8 +37,16 @@
a(href='/account') [&nbsp;#{user.points}&nbsp;]
.hidden-xs
if user.profile.picture
a(href='/account')
if (user.profile.username)
a(href='/' + user.profile.username)
img.profile-picture.float-right(src='#{user.profile.picture}')
else
a(href='/account')
img.profile-picture.float-right(src='#{user.profile.picture}')
else
if (user.profile.username)
a(href='/' + user.profile.username)
img.profile-picture.float-right(src='#{user.gravatar(60)}')
else
a(href='/account')
img.profile-picture.float-right(src='#{user.gravatar(60)}')

View File

@ -10,6 +10,13 @@ block content
.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