fixed conflicts

This commit is contained in:
Aniruddh Agarwal
2015-08-25 22:21:33 +08:00
49 changed files with 913 additions and 892 deletions

2
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,2 @@
We're getting a lot of duplicate issues and bug reports that just aren't reporting actual bugs.
So, before you submit your issue, please read the [Help I've Found a Bug](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-I've-Found-a-Bug) wiki page.

View File

@ -66,6 +66,8 @@ bower install
# Create a .env file and populate it with the necessary API keys and secrets: # Create a .env file and populate it with the necessary API keys and secrets:
touch .env touch .env
# Install Gulp globally
npm install -g gulp
``` ```
Edit your .env file with the following API keys accordingly (if you only use email login, only the MONGOHQ_URL, SESSION_SECRET, MANDRILL_USER and MANDRILL_PASSWORD fields are necessary. Keep in mind if you want to use more services you'll have to get your own API keys for those services. Edit your .env file with the following API keys accordingly (if you only use email login, only the MONGOHQ_URL, SESSION_SECRET, MANDRILL_USER and MANDRILL_PASSWORD fields are necessary. Keep in mind if you want to use more services you'll have to get your own API keys for those services.
@ -112,6 +114,11 @@ DEBUG=true
# Start the mongo server # Start the mongo server
mongod mongod
# Create your mongo database.
# Type "mongo" in your terminal to access the mongo shell
use freecodecamp
# Exit the mongo shell with control + d
# Seed your database with the challenges # Seed your database with the challenges
node seed/ node seed/

View File

@ -4,12 +4,13 @@ import debugFactory from 'debug';
import { import {
setProfileFromGithub, setProfileFromGithub,
getFirstImageFromProfile, getFirstImageFromProfile,
getUsernameFromProvider,
getSocialProvider getSocialProvider
} from '../../server/utils/auth'; } from '../../server/utils/auth';
const debug = debugFactory('freecc:models:userIdent');
const { defaultProfileImage } = require('../utils/constantStrings.json'); const { defaultProfileImage } = require('../utils/constantStrings.json');
const githubRegex = (/github/i);
const debug = debugFactory('freecc:models:userIdent');
function createAccessToken(user, ttl, cb) { function createAccessToken(user, ttl, cb) {
if (arguments.length === 2 && typeof ttl === 'function') { if (arguments.length === 2 && typeof ttl === 'function') {
@ -158,14 +159,13 @@ export default function(UserIdent) {
userChanged = true; userChanged = true;
} }
if (!(/github/).test(provider) && profile) { if (!githubRegex.test(provider) && profile) {
debug('setting social', provider, (/github/g).test(provider)); user[provider] = getUsernameFromProvider(provider, profile);
debug('profile username', profile.username); userChanged = true;
user[provider] = profile.username;
} }
// if user signed in with github refresh their info // if user signed in with github refresh their info
if (/github/.test(provider) && profile && profile._json) { if (githubRegex.test(provider) && profile && profile._json) {
debug("user isn't github cool or username from github is different"); debug("user isn't github cool or username from github is different");
setProfileFromGithub(user, profile, profile._json); setProfileFromGithub(user, profile, profile._json);
userChanged = true; userChanged = true;

View File

@ -19,10 +19,12 @@ var Rx = require('rx'),
sync = require('browser-sync'), sync = require('browser-sync'),
inject = require('gulp-inject'), inject = require('gulp-inject'),
// css // css
less = require('gulp-less'), less = require('gulp-less'),
// lint // lint
jsonlint = require('gulp-jsonlint'),
eslint = require('gulp-eslint'); eslint = require('gulp-eslint');
@ -60,6 +62,11 @@ var paths = {
syncWatch: [ syncWatch: [
'public/**/*.*' 'public/**/*.*'
],
challenges: [
'seed/challenges/*.json',
'seed/under-construction/*.json'
] ]
}; };
@ -163,12 +170,20 @@ gulp.task('sync', ['serve'], function() {
}); });
}); });
gulp.task('lint', function() { gulp.task('lint-js', function() {
return gulp.src(['public/js/lib/**/*']) return gulp.src(['public/js/lib/**/*'])
.pipe(eslint()) .pipe(eslint())
.pipe(eslint.format()); .pipe(eslint.format());
}); });
gulp.task('lint-json', function() {
return gulp.src(paths.challenges)
.pipe(jsonlint())
.pipe(jsonlint.reporter());
});
gulp.task('test-challenges', ['lint-json']);
gulp.task('less', function() { gulp.task('less', function() {
return gulp.src('./public/css/*.less') return gulp.src('./public/css/*.less')
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
@ -182,6 +197,7 @@ gulp.task('build', ['less']);
gulp.task('watch', ['less', 'serve', 'sync'], function() { gulp.task('watch', ['less', 'serve', 'sync'], function() {
gulp.watch('./public/css/*.less', ['less']); gulp.watch('./public/css/*.less', ['less']);
gulp.watch(paths.challenges, ['test-challenges']);
}); });
gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']); gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']);

View File

@ -69,7 +69,7 @@
"lodash": "^3.9.3", "lodash": "^3.9.3",
"loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password", "loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password",
"loopback-boot": "^2.8.0", "loopback-boot": "^2.8.0",
"loopback-component-passport": "^1.5.0", "loopback-component-passport": "https://github.com/FreeCodeCamp/loopback-component-passport.git#feature/flashfailure",
"loopback-connector-mongodb": "^1.10.0", "loopback-connector-mongodb": "^1.10.0",
"lusca": "~1.0.2", "lusca": "~1.0.2",
"method-override": "~2.3.0", "method-override": "~2.3.0",
@ -120,6 +120,7 @@
"gulp": "~3.8.8", "gulp": "~3.8.8",
"gulp-eslint": "~0.9.0", "gulp-eslint": "~0.9.0",
"gulp-inject": "~1.0.2", "gulp-inject": "~1.0.2",
"gulp-jsonlint": "^1.1.0",
"gulp-nodemon": "^2.0.3", "gulp-nodemon": "^2.0.3",
"gulp-notify": "^2.2.0", "gulp-notify": "^2.2.0",
"gulp-plumber": "^1.0.1", "gulp-plumber": "^1.0.1",

View File

@ -262,6 +262,10 @@ ul {
font-size: 24px; font-size: 24px;
} }
.capitalize {
text-transform: capitalize;
}
.text-success { .text-success {
color: @brand-success; color: @brand-success;
} }
@ -679,9 +683,9 @@ thead {
margin-right: 0px; margin-right: 0px;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
&:hover {
padding-top: 14px; padding-top: 14px;
padding-bottom: 14px; padding-bottom: 12px;
&:hover {
color: #4a2b0f; color: #4a2b0f;
background-color: #eee; background-color: #eee;
text-decoration: none; text-decoration: none;

View File

@ -28,7 +28,7 @@ $(document).ready(function () {
$('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function (txt) { $('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
}) + ', and making $' + lastYearsIncome.toString().replace(/0000$/, '0,000') + ', your true costs will be:'); }) + ', and making $' + lastYearsIncome.toString().replace(/0000$/, '0,000') + ', your true costs will be:');
var categoryNames = ['Lost Wages', 'Financing Cost', 'Housing Cost', 'Tuition / Wage Garnishing']; var categoryNames = ['Lost Wages', 'Financing Cost', 'Housing Cost', 'Tuition / Est. Wage Garnishing'];
bootcamps.forEach(function (camp) { bootcamps.forEach(function (camp) {
var x0 = 0; var x0 = 0;
if (camp.cities.indexOf(city) > -1) { if (camp.cities.indexOf(city) > -1) {
@ -38,7 +38,7 @@ $(document).ready(function () {
} }
camp.mapping = [{ camp.mapping = [{
name: camp.name, name: camp.name,
label: 'Tuition / Wage Garnishing', label: 'Tuition / Est. Wage Garnishing',
value: +camp.cost, value: +camp.cost,
x0: x0, x0: x0,
x1: x0 += +camp.cost x1: x0 += +camp.cost

View File

@ -198,7 +198,7 @@ codeStorage.isAlive = function() {
return val !== 'null' && return val !== 'null' &&
val !== 'undefined' && val !== 'undefined' &&
(val && val.length > 0); (val && val.length > 0);
} };
codeStorage.updateStorage = function(){ codeStorage.updateStorage = function(){
if(typeof(Storage) !== undefined) { if(typeof(Storage) !== undefined) {
var value = editor.getValue(); var value = editor.getValue();
@ -252,14 +252,18 @@ var allSeeds = '';
editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds;
editor.setValue(editorValue); editor.setValue(editorValue.replace((/fccss/gi), '<script>').replace((/fcces/gi), '</script>'));
editor.refresh();
var resetEditor = function resetEditor() { var resetEditor = function resetEditor() {
editor.setValue(allSeeds); editor.setValue(allSeeds.replace((/fccss/gi), '<script>').replace((/fcces/gi), '</script>'));
updatePreview(); updatePreview();
codeStorage.updateStorage(); codeStorage.updateStorage();
}; };
/*
var challengeSeed = challengeSeed || null; var challengeSeed = challengeSeed || null;
var allSeeds = ''; var allSeeds = '';
(function() { (function() {
@ -273,3 +277,4 @@ var allSeeds = '';
}, 200); }, 200);
})(); })();
})(); })();
*/

View File

@ -388,3 +388,7 @@ var resetEditor = function resetEditor() {
editor.setValue(allSeeds); editor.setValue(allSeeds);
codeStorage.updateStorage(); codeStorage.updateStorage();
}; };
$(document).ready(function(){
bonfireExecute();
});

View File

@ -22,67 +22,6 @@ $(document).ready(function() {
setCSRFToken($('meta[name="csrf-token"]').attr('content')); setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('#i-want-help').on('click', function() {
$('#help-modal').modal('hide');
var editorValue = editor.getValue();
var currentLocation = window.location.href;
$.post(
'/get-help',
{
payload: {
code: editorValue,
challenge: currentLocation
}
},
function(res) {
if (res) {
window.open('https://gitter.im/FreeCodeCamp/Help', '_blank')
}
}
);
});
$('#i-want-help-editorless').on('click', function() {
$('#help-editorless-modal').modal('hide');
var currentLocation = window.location.href;
$.post(
'/get-help',
{
payload: {
challenge: currentLocation
}
},
function(res) {
if (res) {
window.open('https://gitter.im/FreeCodeCamp/Help', '_blank')
}
}
);
});
$('#report-issue').on('click', function() {
$('#issue-modal').modal('hide');
window.open('https://github.com/freecodecamp/freecodecamp/issues/new?&body=Challenge '+ window.location.href +' has an issue. Please describe how to reproduce it, and include links to screenshots if possible.', '_blank')
});
$('#i-want-to-pair').on('click', function() {
$('#pair-modal').modal('hide');
var currentLocation = window.location.href;
$.post(
'/get-pair',
{
payload: {
challenge: currentLocation
}
},
function(res) {
if (res) {
window.open('https://gitter.im/FreeCodeCamp/LetsPair', '_blank')
}
}
);
});
$('.checklist-element').each(function() { $('.checklist-element').each(function() {
var checklistElementId = $(this).attr('id'); var checklistElementId = $(this).attr('id');
if(!!localStorage[checklistElementId]) { if(!!localStorage[checklistElementId]) {
@ -118,50 +57,57 @@ $(document).ready(function() {
$(this).unbind("error").attr("src", "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png"); $(this).unbind("error").attr("src", "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png");
}); });
function reBindModals(){
$('.close-modal').unbind('click');
$('.close-modal').on('click', function(){
setTimeout(function() {
$('.close-modal').parent().parent().parent().parent().modal('hide');
}, 200);
});
$('#report-issue').unbind('click');
$('#report-issue').on('click', function() {
$('#issue-modal').modal('hide');
window.open('https://github.com/freecodecamp/freecodecamp/issues/new?&body=Challenge '+ window.location.href +' has an issue. Please describe how to reproduce it, and include links to screenshots if possible.', '_blank')
});
$('#completed-courseware').unbind('click');
$('#completed-courseware').on('click', function() { $('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show'); $('#complete-courseware-dialog').modal('show');
}); });
$('#completed-courseware-editorless').unbind('click');
$('#completed-courseware-editorless').on('click', function() { $('#completed-courseware-editorless').on('click', function() {
$('#complete-courseware-editorless-dialog').modal('show'); $('#complete-courseware-editorless-dialog').modal('show');
}); });
$('#trigger-pair-modal').unbind('click');
$('#trigger-pair-modal').on('click', function() { $('#trigger-pair-modal').on('click', function() {
$('#pair-modal').modal('show'); $('#pair-modal').modal('show');
}); });
$('#trigger-reset-modal').unbind('click');
$('#trigger-reset-modal').on('click', function() { $('#trigger-reset-modal').on('click', function() {
$('#reset-modal').modal('show'); $('#reset-modal').modal('show');
}); });
$('#trigger-help-modal').unbind('click');
$('#trigger-help-modal').on('click', function() { $('#trigger-help-modal').on('click', function() {
$('#help-modal').modal('show'); $('#help-modal').modal('show');
}); });
$('#trigger-issue-modal').unbind('click');
$('#trigger-issue-modal').on('click', function() { $('#trigger-issue-modal').on('click', function() {
$('#issue-modal').modal('show'); $('#issue-modal').modal('show');
}); });
$('#completed-zipline-or-basejump').unbind('click');
$('#completed-zipline-or-basejump').on('click', function() { $('#completed-zipline-or-basejump').on('click', function() {
$('#complete-zipline-or-basejump-dialog').modal('show'); $('#complete-zipline-or-basejump-dialog').modal('show');
}); });
$('#complete-courseware-dialog').on('hidden.bs.modal', function() { $('#next-courseware-button').unbind('click');
editor.focus();
});
$('#complete-challenge-dialog').on('hidden.bs.modal', function() {
editor.focus();
});
var challengeTypes = {
'HTML_CSS_JQ': '0',
'JAVASCRIPT': '1',
'VIDEO': '2',
'ZIPLINE': '3',
'BASEJUMP': '4',
'BONFIRE': '5'
};
$('#next-courseware-button').on('click', function() { $('#next-courseware-button').on('click', function() {
$('#next-courseware-button').unbind('click'); $('#next-courseware-button').unbind('click');
if ($('.signup-btn-nav').length < 1) { if ($('.signup-btn-nav').length < 1) {
@ -238,23 +184,38 @@ $(document).ready(function() {
} }
}); });
$('.next-challenge-button').unbind('click');
$('.next-challenge-button').on('click', function() { $('.next-challenge-button').on('click', function() {
l = location.pathname.split('/'); l = location.pathname.split('/');
window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
}); });
// Bonfire instructions functions $('#complete-courseware-dialog').on('hidden.bs.modal', function() {
$('#more-info').on('click', function() { editor.focus();
ga('send', 'event', 'Challenge', 'more-info', challengeName); });
$('#brief-instructions').hide();
$('#long-instructions').show().removeClass('hide');
$('#complete-zipline-or-basejump').on('hidden.bs.modal', function() {
editor.focus();
}); });
$('#less-info').on('click', function() { };
$('#brief-instructions').show();
$('#long-instructions').hide();
$(window).resize(function(){
reBindModals();
}); });
reBindModals();
var challengeTypes = {
'HTML_CSS_JQ': '0',
'JAVASCRIPT': '1',
'VIDEO': '2',
'ZIPLINE': '3',
'BASEJUMP': '4',
'BONFIRE': '5'
};
function upvoteHandler(e) { function upvoteHandler(e) {
e.preventDefault(); e.preventDefault();
var upvoteBtn = this; var upvoteBtn = this;
@ -356,6 +317,21 @@ $(document).ready(function() {
lockTop(); lockTop();
}); });
} }
var execInProgress = false;
document.getElementById('scroll-locker').addEventListener('previewUpdateSpy', function(e){
if (!execInProgress){
execInProgress = true;
setTimeout(function(){
if($($('.scroll-locker').children()[0]).height()-800 > e.detail){
$('.scroll-locker').scrollTop(e.detail);
}
else {
$('.scroll-locker').animate({"scrollTop":$($('.scroll-locker').children()[0]).height()}, 175);
}
execInProgress = false;
}, 750);
}
}, false);
} }
}); });

View File

@ -221,7 +221,28 @@
"cities": [ "cities": [
"portland" "portland"
] ]
}, { },
{
"name": "Viking Code School",
"cost": "18000",
"housing": "0",
"finance": false,
"weeks": "16",
"cities": [
"online"
]
},
{
"name": "App Academy",
"cost": "18000",
"housing": "500",
"finance": false,
"weeks": "12",
"cities": [
"san-francisco"
]
},
{
"name": "Turing School", "name": "Turing School",
"cost": "17500", "cost": "17500",
"housing": "400", "housing": "400",
@ -230,13 +251,4 @@
"cities": [ "cities": [
"denver" "denver"
] ]
}, {
"name": "Free Code Camp",
"cost": "0",
"housing": "0",
"finance": false,
"weeks": "0",
"cities": [
"online"
]
}] }]

View File

@ -20,7 +20,8 @@
"", "",
"" ""
], ],
"challengeType":1 "challengeType":1,
"type": "waypoint"
}, },
{ {
"id":"cf1111c1c16feddfaeb7bdef", "id":"cf1111c1c16feddfaeb7bdef",
@ -46,7 +47,8 @@
"", "",
"" ""
], ],
"challengeType":1 "challengeType":1,
"type": "waypoint"
} }
] ]
} }

View File

@ -461,8 +461,8 @@
], ],
"tests": [ "tests": [
"assert.deepEqual(slasher([1, 2, 3], 2), [3], 'should drop the first two elements');", "assert.deepEqual(slasher([1, 2, 3], 2), [3], 'should drop the first two elements');",
"assert.deepEqual(slasher([1, 2, 3], 0), [1, 2, 3], 'should return all elements when n < 1');", "assert.deepEqual(slasher([1, 2, 3], 0), [1, 2, 3], 'should return all elements');",
"assert.deepEqual(slasher([1, 2, 3], 9), [], 'should return an empty array when n >= array.length');" "assert.deepEqual(slasher([1, 2, 3], 9), [], 'should return an empty array');"
], ],
"MDNlinks": [ "MDNlinks": [
"Array.slice()", "Array.slice()",
@ -506,7 +506,8 @@
"expect(mutation(['Mary', 'Army'])).to.be.true;", "expect(mutation(['Mary', 'Army'])).to.be.true;",
"expect(mutation(['Mary', 'Aarmy'])).to.be.true;", "expect(mutation(['Mary', 'Aarmy'])).to.be.true;",
"expect(mutation(['Alien', 'line'])).to.be.true;", "expect(mutation(['Alien', 'line'])).to.be.true;",
"expect(mutation(['floor', 'for'])).to.be.true;" "expect(mutation(['floor', 'for'])).to.be.true;",
"expect(mutation(['hello', 'neo'])).to.be.false;"
], ],
"MDNlinks": [ "MDNlinks": [
"Array.indexOf()" "Array.indexOf()"

View File

@ -13,7 +13,8 @@
"<code>// This is a comment.</code>", "<code>// This is a comment.</code>",
"The slash-star-star-slash comment will comment out everything between the <code>/*</code> and the <code>*/</code> characters:", "The slash-star-star-slash comment will comment out everything between the <code>/*</code> and the <code>*/</code> characters:",
"<code>/* This is also a comment */</code>", "<code>/* This is also a comment */</code>",
"Try creating one of each." "Try creating one of each.",
"And one more thing you need to notice. Starting at this waypoint in JavaScript related challenges (except AngularJS, all Ziplines, Git, Node.js and Express.js, MongoDB and Full Stack JavaScript Projects) you can see contents of <code>assert()</code> functions (in some challenges <code>except()</code>, <code>assert.equal()</code> and so on) which are used to test your code. It's part of these challenges that you are able to see the tests that are running against your code."
], ],
"tests":[ "tests":[
"assert(editor.getValue().match(/(\\/\\/)...../g), 'Create a <code>\\/\\/</code> style comment that contains at least five letters');", "assert(editor.getValue().match(/(\\/\\/)...../g), 'Create a <code>\\/\\/</code> style comment that contains at least five letters');",
@ -113,7 +114,7 @@
"description": [ "description": [
"<code>data structures</code> have <code>properties</code>. For example, <code>strings</code> have a property called <code>.length</code> that will tell you how many characters are in the string.", "<code>data structures</code> have <code>properties</code>. For example, <code>strings</code> have a property called <code>.length</code> that will tell you how many characters are in the string.",
"For example, if we created a variable <code>var firstName = \"Charles\"</code>, we could find out how long the string \"Charles\" is by using the <code>firstName.length</code> property.", "For example, if we created a variable <code>var firstName = \"Charles\"</code>, we could find out how long the string \"Charles\" is by using the <code>firstName.length</code> property.",
"Use the <code>.length</code> property to count the number of characters in the <code>lastNameLength</code> variable." "Use the <code>.length</code> property to count the number of characters in the <code>lastName</code> variable."
], ],
"tests": [ "tests": [
"assert((function(){if(typeof(lastNameLength) !== \"undefined\" && typeof(lastNameLength) === \"number\" && lastNameLength === 8){return(true);}else{return(false);}})(), 'lastNameLength should be equal to eight.');", "assert((function(){if(typeof(lastNameLength) !== \"undefined\" && typeof(lastNameLength) === \"number\" && lastNameLength === 8){return(true);}else{return(false);}})(), 'lastNameLength should be equal to eight.');",
@ -150,7 +151,7 @@
"<code>Bracket notation</code> is a way to get a character at a specific <code>index</code> within a string.", "<code>Bracket notation</code> is a way to get a character at a specific <code>index</code> within a string.",
"Computers don't start counting at 1 like humans do. They start at 0.", "Computers don't start counting at 1 like humans do. They start at 0.",
"For example, the character at index 0 in the word \"Charles\" is \"C\". So if <code>var firstName = \"Charles\"</code>, you can get the value of the first letter of the string by using <code>firstName[0]</code>.", "For example, the character at index 0 in the word \"Charles\" is \"C\". So if <code>var firstName = \"Charles\"</code>, you can get the value of the first letter of the string by using <code>firstName[0]</code>.",
"Use <code>bracket notation</code> to find the first character in a the <code>firstLetterOfLastName</code> variable.", "Use <code>bracket notation</code> to find the first character in the <code>firstLetterOfLastName</code> variable.",
"Try looking at the <code>firstLetterOfFirstName</code> variable declaration if you get stuck." "Try looking at the <code>firstLetterOfFirstName</code> variable declaration if you get stuck."
], ],
"tests": [ "tests": [
@ -216,7 +217,7 @@
"In order to get the last letter of a string, you can subtract one from the string's length.", "In order to get the last letter of a string, you can subtract one from the string's length.",
"For example, if <code>var firstName = \"Charles\"</code>, you can get the value of the last letter of the string by using <code>firstName[firstName.length - 1]</code>.", "For example, if <code>var firstName = \"Charles\"</code>, you can get the value of the last letter of the string by using <code>firstName[firstName.length - 1]</code>.",
"Use <code>bracket notation</code> to find the last character in the <code>lastName</code> variable.", "Use <code>bracket notation</code> to find the last character in the <code>lastName</code> variable.",
"Try looking at the <code>lastLetterOfLastName</code> variable declaration if you get stuck." "Try looking at the <code>lastLetterOfFirstName</code> variable declaration if you get stuck."
], ],
"tests": [ "tests": [
"assert(lastLetterOfLastName === \"e\", 'lastLetterOfLastName should be \"e\"');", "assert(lastLetterOfLastName === \"e\", 'lastLetterOfLastName should be \"e\"');",
@ -432,9 +433,9 @@
"challengeSeed": [ "challengeSeed": [
"//var array = [\"John\", 23];", "//var array = [\"John\", 23];",
"", "",
"var myArray = [];",
"// Only change code below this line.", "// Only change code below this line.",
"", "",
"var myArray = [];",
"", "",
"// Only change code above this line.", "// Only change code above this line.",
"// We use this function to show you the value of your variable in your output box.", "// We use this function to show you the value of your variable in your output box.",
@ -457,9 +458,9 @@
], ],
"challengeSeed":[ "challengeSeed":[
"var ourArray = [[\"the universe\", \"everything\", 42]];", "var ourArray = [[\"the universe\", \"everything\", 42]];",
"var myArray = [];",
"// Only change code below this line.", "// Only change code below this line.",
"", "",
"var myArray = [];",
"", "",
"// Only change code above this line.", "// Only change code above this line.",
"// We use this function to show you the value of your variable in your output box.", "// We use this function to show you the value of your variable in your output box.",
@ -496,7 +497,7 @@
"// Only change code above this line.", "// Only change code above this line.",
"// We use this function to show you the value of your variable in your output box.", "// We use this function to show you the value of your variable in your output box.",
"// You'll learn about functions soon.", "// You'll learn about functions soon.",
"if(typeof(myArray) !== \"undefined\" && typeof(data) !== \"undefined\"){(function(y,z){return('myArray = ' + JSON.stringify(y) + ', data = ' + JSON.stringify(z));})(myArray, data);}" "if(typeof(myArray) !== \"undefined\" && typeof(myData) !== \"undefined\"){(function(y,z){return('myArray = ' + JSON.stringify(y) + ', myData = ' + JSON.stringify(z));})(myArray, myData);}"
], ],
"type": "waypoint", "type": "waypoint",
"challengeType": 1 "challengeType": 1
@ -606,7 +607,7 @@
"Take the myArray array and <code>shift()</code> the first value off of it." "Take the myArray array and <code>shift()</code> the first value off of it."
], ],
"tests": [ "tests": [
"assert((function(d){if(d[0] == 23 && d[1][0] == 'dog' && d[1][1] == 3 && d[2] == undefined){return(true);}else{return(false);}})(myArray), 'myArray should only have the first two values left([\"John\", 23])');", "assert((function(d){if(d[0] == 23 && d[1][0] == 'dog' && d[1][1] == 3 && d[2] == undefined){return(true);}else{return(false);}})(myArray), 'myArray should only have the last two values left([23, [\"dog\", 3]])');",
"assert((function(d){if(d === 'John' && typeof(myRemoved) === 'string'){return(true);}else{return(false);}})(myRemoved), 'myRemoved should contain \"John\"');" "assert((function(d){if(d === 'John' && typeof(myRemoved) === 'string'){return(true);}else{return(false);}})(myRemoved), 'myRemoved should contain \"John\"');"
], ],
"challengeSeed": [ "challengeSeed": [
@ -634,7 +635,7 @@
"difficulty": "9.9818", "difficulty": "9.9818",
"description": [ "description": [
"Now that we've learned how to <code>shift</code>things from the start of the array, we need to learn how to <code>unshift</code>stuff back to the start", "Now that we've learned how to <code>shift</code>things from the start of the array, we need to learn how to <code>unshift</code>stuff back to the start",
"Let's take the code we had last time and <code>unshift</code>this value to the end: <code>\"Paul\" </code>" "Let's take the code we had last time and <code>unshift</code>this value to the start: <code>\"Paul\" </code>"
], ],
"tests": [ "tests": [
"assert((function(d){if(d[0].toLowerCase() == 'paul' && d[1] == 23 && d[2][0] != undefined && d[2][0] == 'dog' && d[2][1] != undefined && d[2][1] == 3){return(true);}else{return(false);}})(myArray), 'myArray should now have [\"Paul\", 23, [\"dog\", 3]])');" "assert((function(d){if(d[0].toLowerCase() == 'paul' && d[1] == 23 && d[2][0] != undefined && d[2][0] == 'dog' && d[2][1] != undefined && d[2][1] == 3){return(true);}else{return(false);}})(myArray), 'myArray should now have [\"Paul\", 23, [\"dog\", 3]])');"
@ -643,7 +644,7 @@
"var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];",
"ourArray.shift();", "ourArray.shift();",
"ourArray.unshift([\"happy\", \"joy\"]);", "ourArray.unshift([\"happy\", \"joy\"]);",
"// ourArray now equals [[\"happy\", \"joy\"], \"Stimpson\", \"J\"]", "// ourArray now equals [[\"happy\", \"joy\"], \"J\", [\"cat\"]]",
"", "",
"var myArray = [\"John\", 23, [\"dog\", 3]];", "var myArray = [\"John\", 23, [\"dog\", 3]];",
"myArray.shift();", "myArray.shift();",
@ -670,8 +671,8 @@
"<code>function functionName (a, b) {</code>", "<code>function functionName (a, b) {</code>",
"<code>&thinsp;&thinsp;return(a + b);</code>", "<code>&thinsp;&thinsp;return(a + b);</code>",
"<code>}</code>", "<code>}</code>",
"We can \"call\" our function like this: <code>functionName();</code>, and it will run and return it's <code>return</code> value to us.", "We can \"call\" our function like this: <code>functionName();</code>, and it will run and return its <code>return</code> value to us.",
"Create and call a function called <code>myFunction</code>." "Create and call a function called <code>myFunction</code> that returns the sum of a and b."
], ],
"tests":[ "tests":[
"assert((function(){if(typeof(f) !== \"undefined\" && typeof(f) === \"number\" && f === a + b && editor.getValue().match(/return/gi).length >= 1 && editor.getValue().match(/a/gi).length >= 1 && editor.getValue().match(/b/gi).length >= 1 && editor.getValue().match(/\\+/gi).length >= 1){return(true);}else{return(false);}})(), 'Your function should return the value of a + b');" "assert((function(){if(typeof(f) !== \"undefined\" && typeof(f) === \"number\" && f === a + b && editor.getValue().match(/return/gi).length >= 1 && editor.getValue().match(/a/gi).length >= 1 && editor.getValue().match(/b/gi).length >= 1 && editor.getValue().match(/\\+/gi).length >= 1){return(true);}else{return(false);}})(), 'Your function should return the value of a + b');"
@ -755,11 +756,11 @@
"title": "Manipulate JavaScript Objects", "title": "Manipulate JavaScript Objects",
"difficulty":"9.9823", "difficulty":"9.9823",
"description":[ "description":[
"There are many ways to add and add and remove properties from objects.", "There are many ways to add and remove properties from objects.",
"For example, we can add properties to objects like this:", "For example, we can add properties to objects like this:",
"<code>myObject.myProperty = \"myValue\";</code>", "<code>myObject.myProperty = \"myValue\";</code>",
"We can also delete them like this:", "We can also delete them like this:",
"<code>delete(myObject.myProperty);</code>", "<code>delete myObject.myProperty;</code>",
"Let's add the property \"bark\", and delete the property \"tails\"." "Let's add the property \"bark\", and delete the property \"tails\"."
], ],
"tests":[ "tests":[
@ -774,8 +775,8 @@
"// \"friends\": [\"everything!\"]", "// \"friends\": [\"everything!\"]",
"// };", "// };",
"", "",
"// ourDog.bark(\"arf!\");", "// ourDog.bark = \"arf!\";",
"// delete(ourDog.tails);", "// delete ourDog.tails;",
"", "",
"var myDog = {", "var myDog = {",
" \"name\": \"Camper\",", " \"name\": \"Camper\",",
@ -818,6 +819,10 @@
"assert.deepEqual(myArray, [0,1,2,3,4], 'myArray should equal [0,1,2,3,4]');" "assert.deepEqual(myArray, [0,1,2,3,4], 'myArray should equal [0,1,2,3,4]');"
], ],
"challengeSeed":[ "challengeSeed":[
"ourArray = [];",
"for(var i = 0; i < 5; i++){",
" ourArray.push(i);",
"}",
"var myArray = [];", "var myArray = [];",
"//Push the numbers zero through four to myArray using a \"for loop\" like above.", "//Push the numbers zero through four to myArray using a \"for loop\" like above.",
"", "",
@ -832,14 +837,14 @@
"difficulty":"9.9825", "difficulty":"9.9825",
"description":[ "description":[
"You can run the same code multiple times by using a loop.", "You can run the same code multiple times by using a loop.",
"Another type of JavaScript loop is called a \"while loop\" because it runs \"while\" something is true, and stops once that something is no longer true.", "Another type of JavaScript loop is called a \"while loop\", because it runs \"while\" something is true and stops once that something is no longer true.",
"<code>var ourArray = [];</code>", "<code>var ourArray = [];</code>",
"<code>var i = 0;</code>", "<code>var i = 0;</code>",
"<code>while(i < 5) {</code>", "<code>while(i < 5) {</code>",
"<code>&thinsp;&thinsp;ourArray.push(i);</code>", "<code>&thinsp;&thinsp;ourArray.push(i);</code>",
"<code>&thinsp;&thinsp;i++;</code>", "<code>&thinsp;&thinsp;i++;</code>",
"<code>}</code>", "<code>}</code>",
"Let's try getting a for loop to work by pushing values to an array." "Let's try getting a while loop to work by pushing values to an array."
], ],
"tests":[ "tests":[
"assert(editor.getValue().match(/while/g), 'You should be using a while loop for this.');", "assert(editor.getValue().match(/while/g), 'You should be using a while loop for this.');",
@ -859,7 +864,7 @@
"title": "Generate Random Fractions with JavaScript", "title": "Generate Random Fractions with JavaScript",
"difficulty":"9.9827", "difficulty":"9.9827",
"description":[ "description":[
"Random numbers are useful for creating random behaviours and games.", "Random numbers are useful for creating random behavior.",
"JavaScript has a <code>Math.random()</code> function that generates a random decimal number.", "JavaScript has a <code>Math.random()</code> function that generates a random decimal number.",
"Use <code>Math.random()</code> to get <code>myFunction</code> to return a random number." "Use <code>Math.random()</code> to get <code>myFunction</code> to return a random number."
], ],
@ -889,17 +894,17 @@
"title": "Generate Random Whole Numbers with JavaScript", "title": "Generate Random Whole Numbers with JavaScript",
"difficulty":"9.9828", "difficulty":"9.9828",
"description":[ "description":[
"It's great that we can create random decimal numbers, but it's even more useful if we lot more useful to generate a random whole number.", "It's great that we can create random decimal numbers, but it's even more useful if we use it to generate a random whole number.",
"To achieve this we can multiply the random number by ten and use the <code>Math.floor()</code> to convert the decimal number to a whole number", "To achieve this we can multiply the random number by ten and use the <code>Math.floor()</code> to convert the decimal number to a whole number.",
"This technique gives us a whole number between zero and nine", "This technique gives us a whole number between zero and nine.",
"Example:", "Example:",
"<code>Math.floor(Math.random()*10);</code>", "<code>Math.floor(Math.random()*10);</code>",
"Let's give this technique a go now" "Let's give this technique a go now."
], ],
"tests":[ "tests":[
"assert(typeof(myFunction()) === \"number\", 'The result of myFunction should be a number');", "assert(typeof(myFunction()) === \"number\", 'The result of myFunction should be a number');",
"assert(editor.getValue().match(/Math.random/g), 'You should be using Math.random to create a random number');", "assert(editor.getValue().match(/Math.random/g), 'You should be using Math.random to create a random number');",
"assert(!(''+myFunction()).match(/\\./g), 'You should have multiplied the result of Math.random but 10 to make it a number that\\'s greater then zero');", "assert(!(''+myFunction()).match(/\\./g), 'You should have multiplied the result of Math.random by 10 to make it a number that\\'s greater than zero');",
"assert(editor.getValue().match(/Math.floor/g), 'You should use Math.floor to remove the decimal part of the number');" "assert(editor.getValue().match(/Math.floor/g), 'You should use Math.floor to remove the decimal part of the number');"
], ],
"challengeSeed":[ "challengeSeed":[
@ -935,7 +940,7 @@
], ],
"challengeSeed":[ "challengeSeed":[
"var min = 0;", "var min = 0;",
"var max = 12;", "var max = 9;",
"function myFunction() {", "function myFunction() {",
" // Make myFunction return a random number between zero and nine instead of a decimal", " // Make myFunction return a random number between zero and nine instead of a decimal",
" // Only change code below this line.", " // Only change code below this line.",
@ -956,16 +961,16 @@
"difficulty":"9.983", "difficulty":"9.983",
"description":[ "description":[
"We can use if statements in JavaScript to only execute code if a certain condition is met.", "We can use if statements in JavaScript to only execute code if a certain condition is met.",
"if statements require some sort of boolean condition evaluate.", "if statements require some sort of boolean condition to evaluate.",
"Example:", "Example:",
"<code> if (1 == 2) {</code>", "<code> if (1 === 2) {</code>",
"<code>&thinsp;&thinsp;return(true);</code>", "<code>&thinsp;&thinsp;return(true);</code>",
"<code>}</code>", "<code>}</code>",
"<code>else {</code>", "<code>else {</code>",
"<code>&thinsp;&thinsp;return(false);</code>", "<code>&thinsp;&thinsp;return(false);</code>",
"<code>}</code>", "<code>}</code>",
"Let's use <code>if</code> and <code>else</code> statements to make a coin-flip game.", "Let's use <code>if</code> and <code>else</code> statements to make a coin-flip game.",
"Create an <code>if-else statement</code> to return <code>heads</code> if the flip var is zero and to return <code>tails</code> if it's not." "Create an <code>if-else statement</code> to return <code>heads</code> if the flip var is zero, or else return <code>tails</code> if it's not."
], ],
"tests":[ "tests":[
"assert((function(){if(myFunction() === \"heads\" || myFunction() === \"tails\"){return(true);}else{return(false);}})(), 'myFunction should either return heads or tails');", "assert((function(){if(myFunction() === \"heads\" || myFunction() === \"tails\"){return(true);}else{return(false);}})(), 'myFunction should either return heads or tails');",
@ -975,7 +980,7 @@
"challengeSeed":[ "challengeSeed":[
"function myFunction(){", "function myFunction(){",
" var flip = Math.floor(Math.random() * (1 - 0 + 1)) + 0;", " var flip = Math.floor(Math.random() * (1 - 0 + 1)) + 0;",
" // Create and if else statement here to return \"heads\" if flip is 0. Otherwise return \"tails\".", " // Create an if-else statement here to return \"heads\" if flip is 0. Otherwise return \"tails\".",
"", "",
" // Only change code below this line.", " // Only change code below this line.",
"", "",
@ -1002,7 +1007,7 @@
"<code>g</code> means that we want to search the entire string for this pattern.", "<code>g</code> means that we want to search the entire string for this pattern.",
"<code>i</code> means that we want to ignore the case (uppercase or lowercase) when searching for the pattern.", "<code>i</code> means that we want to ignore the case (uppercase or lowercase) when searching for the pattern.",
"<code>Regular expressions</code> are usually surrounded by <code>/</code> symbols.", "<code>Regular expressions</code> are usually surrounded by <code>/</code> symbols.",
"Let's try selecting all the occurances of the word <code>and</code> in the string <code>George Boole and Alan Turing went to the shop and got some milk</code>. We can do this by replacing the <code>+</code> part of our regular expression with the word <code>and</code>." "Let's try selecting all the occurrences of the word <code>and</code> in the string <code>George Boole and Alan Turing went to the shop and got some milk</code>. We can do this by replacing the <code>+</code> part of our regular expression with the word <code>and</code>."
], ],
"tests":[ "tests":[
"assert(test==2, 'Your <code>regular expression</code> should find two occurrences of the word <code>and</code>');", "assert(test==2, 'Your <code>regular expression</code> should find two occurrences of the word <code>and</code>');",
@ -1036,11 +1041,11 @@
], ],
"tests":[ "tests":[
"assert(test === 2, 'Your RegEx should have found two numbers in the testString');", "assert(test === 2, 'Your RegEx should have found two numbers in the testString');",
"assert(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'You should be using the following expression /\\d+/gi to find the numbers in the testString');" "assert(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'You should be using the following expression /\\\\d+/gi to find the numbers in the testString');"
], ],
"challengeSeed":[ "challengeSeed":[
"var test = (function() {", "var test = (function() {",
" var testString = \"There's 3 cats but 4 dogs.\";", " var testString = \"There are 3 cats but 4 dogs.\";",
"", "",
" // Only change code below this line.", " // Only change code below this line.",
"", "",
@ -1066,7 +1071,7 @@
], ],
"tests":[ "tests":[
"assert(test === 7, 'Your RegEx should have found seven spaces in the <code>testString</code>.');", "assert(test === 7, 'Your RegEx should have found seven spaces in the <code>testString</code>.');",
"assert(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'You should be using the following expression /\\s+/gi to find the spaces in the <code>testString</code>.');" "assert(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'You should be using the following expression /\\\\s+/gi to find the spaces in the <code>testString</code>.');"
], ],
"challengeSeed":[ "challengeSeed":[
"var test = (function(){", "var test = (function(){",
@ -1093,12 +1098,12 @@
"You can invert any match by using the uppercase version of the selector <code>\\s</code> versus <code>\\S</code> for example." "You can invert any match by using the uppercase version of the selector <code>\\s</code> versus <code>\\S</code> for example."
], ],
"tests":[ "tests":[
"assert(test === 36, 'Your RegEx should have found seven spaces in the <code>testString</code>.');", "assert(test === 49, 'Your RegEx should have found forty nine non-space characters in the <code>testString</code>.');",
"assert(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'You should be using the following expression <code>/\\+S/gi</code> to find the spaces in the <code>testString</code>.');" "assert(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'You should be using the following expression <code>/\\\\S/gi</code> to find non-space characters in the <code>testString</code>.');"
], ],
"challengeSeed":[ "challengeSeed":[
"var test = (function(){", "var test = (function(){",
" var testString = \"How many spaces are there in this sentence?\";", " var testString = \"How many non-space characters are there in this sentence?\";",
"", "",
" // Only change code below this line.", " // Only change code below this line.",
"", "",
@ -1274,7 +1279,7 @@
"Now that our slots will each generate random numbers, we need to check whether they've all returned the same number.", "Now that our slots will each generate random numbers, we need to check whether they've all returned the same number.",
"If they have, we should notify our user that they've won.", "If they have, we should notify our user that they've won.",
"Otherwise, we should return <code>null</code>, which is a JavaScript data structure that means nothing.", "Otherwise, we should return <code>null</code>, which is a JavaScript data structure that means nothing.",
"If all three numbers match, we should change the value of win to the number that we have three of or leave it as null.", "If all three numbers match, we should return the number that we have in three of slots or leave it as <code>null</code>.",
"Let's create an <code>if statement</code> with multiple conditions in order to check whether all numbers are equal.", "Let's create an <code>if statement</code> with multiple conditions in order to check whether all numbers are equal.",
"<code>if(slotOne !== slotTwo || slotTwo !== slotThree){</code>", "<code>if(slotOne !== slotTwo || slotTwo !== slotThree){</code>",
"<code>&thinsp;&thinsp;return(null);</code>", "<code>&thinsp;&thinsp;return(null);</code>",
@ -1604,7 +1609,7 @@
"Set up all three slots like this, then click the \"Go\" button to play the slot machine." "Set up all three slots like this, then click the \"Go\" button to play the slot machine."
], ],
"tests":[ "tests":[
"assert(editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi) && editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi).length >= 3, 'Use the provided code three times. One for each slot')", "assert((editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi) && editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi).length >= 3) || (editor.match(/\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi) && editor.match(/\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)\\.html\\(\\'\\<img\\s?src\\s?=\\s?\"\\'\\s?\\+\\s?images\\[\\w+\\-1\\]\\s?\\+\\s?\\'\"\\>\\'\\);/gi).length >= 3), 'Use the provided code three times. One for each slot')",
"assert(editor.match(/slotOne/gi) && editor.match(/slotOne/gi).length >= 7, 'You should have used the slotOne value at least once')", "assert(editor.match(/slotOne/gi) && editor.match(/slotOne/gi).length >= 7, 'You should have used the slotOne value at least once')",
"assert(editor.match(/slotTwo/gi) && editor.match(/slotTwo/gi).length >= 8, 'You should have used the slotTwo value at least once')", "assert(editor.match(/slotTwo/gi) && editor.match(/slotTwo/gi).length >= 8, 'You should have used the slotTwo value at least once')",
"assert(editor.match(/slotThree/gi) && editor.match(/slotThree/gi).length >= 7, 'You should have used the slotThree value at least once')" "assert(editor.match(/slotThree/gi) && editor.match(/slotThree/gi).length >= 7, 'You should have used the slotThree value at least once')"

View File

@ -45,14 +45,15 @@
"<span class='text-info'>Rule #1:</span> Don't look at the example project's code on CodePen. Figure it out for yourself.", "<span class='text-info'>Rule #1:</span> Don't look at the example project's code on CodePen. Figure it out for yourself.",
"<span class='text-info'>Rule #2:</span> You may use whichever libraries or APIs you need.", "<span class='text-info'>Rule #2:</span> You may use whichever libraries or APIs you need.",
"<span class='text-info'>Rule #3:</span> Reverse engineer the example project's functionality, and also feel free to personalize it.", "<span class='text-info'>Rule #3:</span> Reverse engineer the example project's functionality, and also feel free to personalize it.",
"<span class='text-info'>Hint:</span> If you don't want to start from scratch, you can fork this simple Bootstrap portfolio template on CodePen: <a href='http://codepen.io/FreeCodeCamp/pen/mJNqQj/' target='_blank'>http://codepen.io/FreeCodeCamp/pen/mJNqQj</a>.",
"Here are the <a href='http://en.wikipedia.org/wiki/User_story' target='_blank'>user stories</a> you must enable, and optional bonus user stories:", "Here are the <a href='http://en.wikipedia.org/wiki/User_story' target='_blank'>user stories</a> you must enable, and optional bonus user stories:",
"<span class='text-info'>User Story:</span> As a user, I can access all of the portfolio webpage's content just by scrolling.", "<span class='text-info'>User Story:</span> As a user, I can access all of the portfolio webpage's content just by scrolling.",
"<span class='text-info'>User Story:</span> As a user, I can click different buttons that will take me to the portfolio creator's different social media pages.", "<span class='text-info'>User Story:</span> As a user, I can click different buttons that will take me to the portfolio creator's different social media pages.",
"<span class='text-info'>User Story:</span> As a user, I can see thumbnail images of different projects the portfolio creator has built (if you don't haven't built any websites before, use placeholders.)", "<span class='text-info'>User Story:</span> As a user, I can see thumbnail images of different projects the portfolio creator has built (if you haven't built any websites before, use placeholders.)",
"<span class='text-info'>Bonus User Story:</span> As a user, I navigate to different sections of the webpage by clicking buttons in the navigation.", "<span class='text-info'>Bonus User Story:</span> As a user, I navigate to different sections of the webpage by clicking buttons in the navigation.",
"Don't worry if you don't have anything to showcase on your portfolio yet - you will build several several apps on the next few CodePen challenges, and can come back and update your portfolio later.", "Don't worry if you don't have anything to showcase on your portfolio yet - you will build several several apps on the next few CodePen challenges, and can come back and update your portfolio later.",
"There are many great portfolio templates out there, but for this challenge, you'll need to build a portfolio page yourself. Using Bootstrap will make this much easier for you.", "There are many great portfolio templates out there, but for this challenge, you'll need to build a portfolio page yourself. Using Bootstrap will make this much easier for you.",
"Note that CodePen.io overrides the Window.open() function, so if you want to open windows using jquery, you will need to target invisible anchor elements like this one: <code>&lt;a target='_blank'&rt;</a>.", "Note that CodePen.io overrides the Window.open() function, so if you want to open windows using jquery, you will need to target invisible anchor elements like this one: <code>&lt;a target='_blank'&gt;</a></code>.",
"Remember to use <a href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank'>Read-Search-Ask</a> if you get stuck.", "Remember to use <a href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank'>Read-Search-Ask</a> if you get stuck.",
"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.", "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.<br><br><a class='btn btn-primary btn-block' href='https://twitter.com/intent/tweet?text=Check%20out%20the%20project%20I%20just%20built%20with%20%40FreeCodeCamp:%20%0A%20%23LearnToCode%20%23JavaScript' target='_blank'>Click here then add your link to your tweet's text</a>" "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.<br><br><a class='btn btn-primary btn-block' href='https://twitter.com/intent/tweet?text=Check%20out%20the%20project%20I%20just%20built%20with%20%40FreeCodeCamp:%20%0A%20%23LearnToCode%20%23JavaScript' target='_blank'>Click here then add your link to your tweet's text</a>"

View File

@ -133,7 +133,7 @@
"", "",
" <p>Click here for <a href=\"#\">cat photos</a>.</p>", " <p>Click here for <a href=\"#\">cat photos</a>.</p>",
"", "",
" <a href=\"#\"><img class=\"smaller-image thick-green-border\" src=\"https://bit.ly/fcc-relaxing-cat\"></a>", " <a href=\"#\"><img class=\"smaller-image thick-green-border\" src=\"http://bit.ly/fcc-relaxing-cat\"></a>",
"", "",
" <p>Things cats love:</p>", " <p>Things cats love:</p>",
" <ul>", " <ul>",
@ -176,8 +176,8 @@
"title": "Center Text with Bootstrap", "title": "Center Text with Bootstrap",
"difficulty": 2.03, "difficulty": 2.03,
"description": [ "description": [
"Now that we're using Bootstrap, we can center our heading elements to make them look better. All we need to do is add the class <code>text-center</code> to our <code>h1</code> and <code>h2</code> elements.", "Now that we're using Bootstrap, we can center our heading element to make it look better. All we need to do is add the class <code>text-center</code> to our <code>h2</code> element.",
"Remember that you can add several classes to the same element by separating each of them with a space, like this: <code>&#60h2 class=\"text-red text-center\"&#62your text&#60/h2&#62</code>." "Remember that you can add several classes to the same element by separating each of them with a space, like this: <code>&#60h2 class=\"red-text text-center\"&#62your text&#60/h2&#62</code>."
], ],
"tests": [ "tests": [
"assert($(\"h2\").hasClass(\"text-center\"), 'Your <code>h2</code> element should be centered by applying the class <code>text-center</code>')" "assert($(\"h2\").hasClass(\"text-center\"), 'Your <code>h2</code> element should be centered by applying the class <code>text-center</code>')"
@ -347,7 +347,7 @@
"description": [ "description": [
"Normally, your <code>button</code> elements are only as wide as the text that they contain. By making them block elements, your button will stretch to fill your page's entire horizontal space.", "Normally, your <code>button</code> elements are only as wide as the text that they contain. By making them block elements, your button will stretch to fill your page's entire horizontal space.",
"This image illustrates the difference between <code>inline</code> elements and <code>block-level</code> elements:", "This image illustrates the difference between <code>inline</code> elements and <code>block-level</code> elements:",
"<img class=\"img-responsive\" src=\"https://www.evernote.com/l/AHTFU358y71AV6mokPeuTEgrZVdUJ4A8v3AB/image.png\" alt=\"An \"inline\" button is as small as the text it contains. In this image, it's centered. Below it is a \"block-level\" button, which stretches to fill the entire horizontal space.'>", "<img class=\"img-responsive\" src=\"http://i.imgur.com/O32cDWE.png\" alt=\"An \"inline\" button is as small as the text it contains. In this image, it's centered. Below it is a \"block-level\" button, which stretches to fill the entire horizontal space.'>",
"Note that these buttons still need the <code>btn</code> class.", "Note that these buttons still need the <code>btn</code> class.",
"Add Bootstrap's <code>btn-block</code> class to your Bootstrap button." "Add Bootstrap's <code>btn-block</code> class to your Bootstrap button."
], ],
@ -614,7 +614,7 @@
"Note that these buttons still need the <code>btn</code> and <code>btn-block</code> classes." "Note that these buttons still need the <code>btn</code> and <code>btn-block</code> classes."
], ],
"tests": [ "tests": [
"assert(new RegExp(\"delete\",\"gi\").test($(\"button\").text()), 'Create a new <code>button</code> element with the text \"delete\".')", "assert(new RegExp(\"Delete\",\"gi\").test($(\"button\").text()), 'Create a new <code>button</code> element with the text \"Delete\".')",
"assert($(\"button.btn-block.btn\").length > 2, 'All of your Bootstrap buttons should have the <code>btn</code> and <code>btn-block</code> classes.')", "assert($(\"button.btn-block.btn\").length > 2, 'All of your Bootstrap buttons should have the <code>btn</code> and <code>btn-block</code> classes.')",
"assert($(\"button\").hasClass(\"btn-danger\"), 'Your new button should have the class <code>btn-danger</code>.')", "assert($(\"button\").hasClass(\"btn-danger\"), 'Your new button should have the class <code>btn-danger</code>.')",
"assert(editor.match(/<\\/button>/g) && editor.match(/<button/g) && editor.match(/<\\/button>/g).length === editor.match(/<button/g).length, 'Make sure all your <code>button</code> elements have a closing tag.')" "assert(editor.match(/<\\/button>/g) && editor.match(/<button/g) && editor.match(/<\\/button>/g).length === editor.match(/<button/g).length, 'Make sure all your <code>button</code> elements have a closing tag.')"
@ -700,7 +700,7 @@
"description": [ "description": [
"Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a <code>div</code> element.", "Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a <code>div</code> element.",
"Here's a diagram of how Bootstrap's 12-column grid layout works:", "Here's a diagram of how Bootstrap's 12-column grid layout works:",
"<a href=\"https://www.evernote.com/l/AHTwlE2XCLhGFYJzoye_QfsF3ho6y87via4B/image.png\" target=\"_blank\"><img class=\"img-responsive\" src=\"https://www.evernote.com/l/AHTwlE2XCLhGFYJzoye_QfsF3ho6y87via4B/image.png\"></a>", "<a href=\"http://i.imgur.com/FaYuui8.png\" target=\"_blank\"><img class=\"img-responsive\" src=\"http://i.imgur.com/FaYuui8.png\" alt=\"an image illustrating Bootstrap's grid system\"></a>",
"Note that in this illustration, the <code>col-md-*</code> class is being used. Here, <code>md</code> means medium, and <code>*</code> is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.", "Note that in this illustration, the <code>col-md-*</code> class is being used. Here, <code>md</code> means medium, and <code>*</code> is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.",
"In the Cat Photo App that we're building, we'll use <code>col-xs-*</code>, where <code>xs</code> means extra small (like an extra-small mobile phone screen), and <code>*</code> is the number of columns specifying how many columns wide the element should be.", "In the Cat Photo App that we're building, we'll use <code>col-xs-*</code>, where <code>xs</code> means extra small (like an extra-small mobile phone screen), and <code>*</code> is the number of columns specifying how many columns wide the element should be.",
"Put the <code>Like</code>, <code>Info</code> and <code>Delete</code> buttons side-by-side by nesting all three of them within one <code>&#60;div class=\"row\"&#62;</code> element, then each of them within a <code>&#60;div class=\"col-xs-4\"&#62;</code> element.", "Put the <code>Like</code>, <code>Info</code> and <code>Delete</code> buttons side-by-side by nesting all three of them within one <code>&#60;div class=\"row\"&#62;</code> element, then each of them within a <code>&#60;div class=\"col-xs-4\"&#62;</code> element.",
@ -795,7 +795,7 @@
"We can clean up our code and make our Cat Photo App look more conventional by using Bootstrap's built-in styles instead of the custom styles we created earlier.", "We can clean up our code and make our Cat Photo App look more conventional by using Bootstrap's built-in styles instead of the custom styles we created earlier.",
"Don't worry - there will be plenty of time to customize our CSS later.", "Don't worry - there will be plenty of time to customize our CSS later.",
"Delete the <code>.red-text</code>, <code>p</code>, and <code>.smaller-image</code> CSS declarations from your <code>style</code> element so that the only declarations left in your <code>style</code> element are <code>h2</code> and <code>thick-green-border</code>.", "Delete the <code>.red-text</code>, <code>p</code>, and <code>.smaller-image</code> CSS declarations from your <code>style</code> element so that the only declarations left in your <code>style</code> element are <code>h2</code> and <code>thick-green-border</code>.",
"Then Delete the <code>p</code> element that contains a dead link. Then remove the <code>red-text</code> class from your <code>h2</code> element and replace it with the <code>text-primary</code> Bootstrap class.", "Then delete the <code>p</code> element that contains a dead link. Then remove the <code>red-text</code> class from your <code>h2</code> element and replace it with the <code>text-primary</code> Bootstrap class.",
"Finally, remove the \"smaller-image\" class from your first <code>img</code> element and replace it with the <code>img-responsive</code> class." "Finally, remove the \"smaller-image\" class from your first <code>img</code> element and replace it with the <code>img-responsive</code> class."
], ],
"tests": [ "tests": [
@ -893,9 +893,9 @@
"title": "Use Spans for Inline Elements", "title": "Use Spans for Inline Elements",
"difficulty": 2.105, "difficulty": 2.105,
"description": [ "description": [
"You can use use spans to create inline elements. Remember when we used the <code>btn-block</code> class to make the button grow fill the entire row?", "You can use spans to create inline elements. Remember when we used the <code>btn-block</code> class to make the button fill the entire row?",
"This image illustrates the difference between <code>inline</code> elements and <code>block-level</code> elements:", "This image illustrates the difference between <code>inline</code> elements and <code>block-level</code> elements:",
"<img class=\"img-responsive\" src=\"https://www.evernote.com/l/AHTFU358y71AV6mokPeuTEgrZVdUJ4A8v3AB/image.png\" alt=\"An \"inline\" button is as small as the text it contains. In this image, it's centered. Below it is a \"block-level\" button, which stretches to fill the entire horizontal space.'>", "<img class=\"img-responsive\" src=\"http://i.imgur.com/O32cDWE.png\" alt=\"An \"inline\" button is as small as the text it contains. In this image, it's centered. Below it is a \"block-level\" button, which stretches to fill the entire horizontal space.'>",
"By using the <code>span</code> element, you can put several elements together, and even style different parts of the same element differently.", "By using the <code>span</code> element, you can put several elements together, and even style different parts of the same element differently.",
"Nest the word \"love\" in your \"Things cats love\" element below within a <code>span</code> element. Then give that <code>span</code> the class <code>text-danger</code> to make the text red.", "Nest the word \"love\" in your \"Things cats love\" element below within a <code>span</code> element. Then give that <code>span</code> the class <code>text-danger</code> to make the text red.",
"Here's how you would do this with the \"Top 3 things cats hate\" element: <code>&#60;p&#62;Top 3 things cats &#60;span class = \"text-danger\"&#62;hate&#60;/span&#62;&#60;/p&#62;</code>" "Here's how you would do this with the \"Top 3 things cats hate\" element: <code>&#60;p&#62;Top 3 things cats &#60;span class = \"text-danger\"&#62;hate&#60;/span&#62;&#60;/p&#62;</code>"
@ -997,7 +997,7 @@
"We will make a simple heading for our Cat Photo App by putting them in the same row.", "We will make a simple heading for our Cat Photo App by putting them in the same row.",
"Remember, Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a <code>div</code> element.", "Remember, Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a <code>div</code> element.",
"Here's a diagram of how Bootstrap's 12-column grid layout works:", "Here's a diagram of how Bootstrap's 12-column grid layout works:",
"<a href=\"https://www.evernote.com/l/AHTwlE2XCLhGFYJzoye_QfsF3ho6y87via4B/image.png\" target=\"_blank\"><img class=\"img-responsive\" src=\"https://www.evernote.com/l/AHTwlE2XCLhGFYJzoye_QfsF3ho6y87via4B/image.png\"></a>", "<a href=\"http://i.imgur.com/FaYuui8.png\" target=\"_blank\"><img class=\"img-responsive\" src=\"http://i.imgur.com/FaYuui8.png\" alt=\"an image illustrating Bootstrap's grid system\"></a>",
"Note that in this illustration, the <code>col-md-*</code> class is being used. Here, <code>md</code> means medium, and <code>*</code> is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.", "Note that in this illustration, the <code>col-md-*</code> class is being used. Here, <code>md</code> means medium, and <code>*</code> is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.",
"In the Cat Photo App that we're building, we'll use <code>col-xs-*</code>, where <code>xs</code> means extra small (like an extra-small mobile phone screen), and <code>*</code> is the number of columns specifying how many columns wide the element should be.", "In the Cat Photo App that we're building, we'll use <code>col-xs-*</code>, where <code>xs</code> means extra small (like an extra-small mobile phone screen), and <code>*</code> is the number of columns specifying how many columns wide the element should be.",
"Nest your first image and your <code>h2</code> element within a single <code>&#60;div class=\"row\"&#62;</code> element. Nest your <code>h2</code> text within a <code>&#60;div class=\"col-xs-8\"&#62;</code> and your image in a <code>&#60;div class=\"col-xs-4\"&#62;</code> so that they are on the same line.", "Nest your first image and your <code>h2</code> element within a single <code>&#60;div class=\"row\"&#62;</code> element. Nest your <code>h2</code> text within a <code>&#60;div class=\"col-xs-8\"&#62;</code> and your image in a <code>&#60;div class=\"col-xs-4\"&#62;</code> so that they are on the same line.",
@ -1532,7 +1532,7 @@
"difficulty": 2.17, "difficulty": 2.17,
"description": [ "description": [
"Now let's get your form <code>input</code> and your submission <code>button</code> on the same line. We'll do this the same way we have previously: by using a <code>div</code> element with the class <code>row</code>, and other <code>div</code> elements within it using the <code>col-xs-*</code> class.", "Now let's get your form <code>input</code> and your submission <code>button</code> on the same line. We'll do this the same way we have previously: by using a <code>div</code> element with the class <code>row</code>, and other <code>div</code> elements within it using the <code>col-xs-*</code> class.",
"Nest both your form's text <code>input</code> and submit <code>button</code> within a <code>div</code> with the class <code>row</code>. Nest your form's text <code>input</code> within a div with the class of \"col-xs-7\". Nest your form's submit <code>button</code> in a <code>div</code> with the class <code>col-xs-5</code>.", "Nest both your form's text <code>input</code> and submit <code>button</code> within a <code>div</code> with the class <code>row</code>. Nest your form's text <code>input</code> within a div with the class of <code>col-xs-7</code>. Nest your form's submit <code>button</code> in a <code>div</code> with the class <code>col-xs-5</code>.",
"This is the last challenge we'll do for our Cat Photo App for now. We hope you've enjoyed learning Font Awesome, Bootstrap, and responsive design!" "This is the last challenge we'll do for our Cat Photo App for now. We hope you've enjoyed learning Font Awesome, Bootstrap, and responsive design!"
], ],
"tests": [ "tests": [
@ -1769,8 +1769,8 @@
"Nest one <code>div</code> element with the class <code>well</code> within each of your <code>col-xs-6</code> <code>div</code> elements." "Nest one <code>div</code> element with the class <code>well</code> within each of your <code>col-xs-6</code> <code>div</code> elements."
], ],
"tests": [ "tests": [
"assert($(\"div\").length > 4, 'Add two <code>div</code> elements inside your <code>div class=\"well\"></code> element both with the class <code>col-xs-6</code>')", "assert($(\"div\").length > 4, 'Add a <code>div</code> element with the class <code>well</code> inside each of your <code>div class=\"col-xs-6\"> elements</code>')",
"assert($(\"div.col-xs-6\").children(\"div.well\").length > 1, 'Nest both of your <code>div class=\"col-xs-6\"</code> elements within your <code>div class=\"row\"</code> element.')", "assert($(\"div.col-xs-6 div.well\").length > 1, 'Nest both of your <code>div class=\"col-xs-6\"</code> elements within your <code>div class=\"row\"</code> element.')",
"assert(editor.match(/<\\/div>/g) && editor.match(/<div/g) && editor.match(/<\\/div>/g).length === editor.match(/<div/g).length, 'Make sure all your <code>div</code> elements have closing tags.')" "assert(editor.match(/<\\/div>/g) && editor.match(/<div/g) && editor.match(/<\\/div>/g).length === editor.match(/<div/g).length, 'Make sure all your <code>div</code> elements have closing tags.')"
], ],
"challengeSeed": [ "challengeSeed": [

View File

@ -16,7 +16,7 @@
"Choose Node.js in the selection area below the name field.", "Choose Node.js in the selection area below the name field.",
"Click the Create button. Then click into your new workspace.", "Click the Create button. Then click into your new workspace.",
"In the lower right hand corner you should see a terminal window. In this window use the following commands. You don't need to know what these mean at this point.", "In the lower right hand corner you should see a terminal window. In this window use the following commands. You don't need to know what these mean at this point.",
"Install <code>how-to-npm</code> with this command: <code>npm install -g git-it</code>", "Install <code>git-it</code> with this command: <code>npm install -g git-it</code>",
"Now start the tutorial by running <code>git-it</code>.", "Now start the tutorial by running <code>git-it</code>.",
"Note that you can resize the c9.io's windows by dragging their borders.", "Note that you can resize the c9.io's windows by dragging their borders.",
"Make sure that you are always in your project's \"workspace\" directory. You can always navigate back to this directory by running this command: <code>cd ~/workspace</code>.", "Make sure that you are always in your project's \"workspace\" directory. You can always navigate back to this directory by running this command: <code>cd ~/workspace</code>.",

View File

@ -428,8 +428,12 @@
"description": [ "description": [
"Create a CSS class called <code>red-text</code> and apply it to your <code>h2</code> element.", "Create a CSS class called <code>red-text</code> and apply it to your <code>h2</code> element.",
"Classes are reusable styles that can be added to HTML elements.", "Classes are reusable styles that can be added to HTML elements.",
"Here's the anatomy of a CSS class:", "Here's an example CSS class declaration:",
"<img class=\"img-responsive\" alt=\"a diagram of how style tags are composed, which is also described in detail on the following lines.\" src=\"https://www.evernote.com/l/AHSCzZV0l_dDLrqD8r9JsHaBWfEzdN0OpRwB/image.png\">", "<code>&#60;style&#62;</code>",
"<code>&thinsp;&thinsp;.blue-text {</code>",
"<code>&thinsp;&thinsp;&thinsp;&thinsp;color: blue;</code>",
"<code>&thinsp;&thinsp;}</code>",
"<code>&#60;/style&#62;</code>",
"You can see that we've created a CSS class called <code>blue-text</code> within the <code>&#60;style&#62;</code> tag.", "You can see that we've created a CSS class called <code>blue-text</code> within the <code>&#60;style&#62;</code> tag.",
"You can apply a class to an HTML element like this: <code>&#60;h2 class=\"blue-text\"&#62;CatPhotoApp&#60;/h2&#62;</code>.", "You can apply a class to an HTML element like this: <code>&#60;h2 class=\"blue-text\"&#62;CatPhotoApp&#60;/h2&#62;</code>.",
"Note that in your CSS <code>style</code> element, classes should start with a period. In your HTML elements' class declarations, classes shouldn't start with a period.", "Note that in your CSS <code>style</code> element, classes should start with a period. In your HTML elements' class declarations, classes shouldn't start with a period.",
@ -749,13 +753,13 @@
"title": "Add Images to your Website", "title": "Add Images to your Website",
"difficulty": 1.17, "difficulty": 1.17,
"description": [ "description": [
"You can add images to your website by using the <code>img</code> element, and point to an specific image's URL using the <code>src</code> attribute.", "You can add images to your website by using the <code>img</code> element, and point to a specific image's URL using the <code>src</code> attribute.",
"An example of this would be <code>&#60img src=\"www.your-image-source.com/your-image.jpg\"&#62</code>. Note that in most cases, <code>img</code> elements are self-closing.", "An example of this would be <code>&#60img src=\"http://www.your-image-source.com/your-image.jpg\"&#62</code>. Note that in most cases, <code>img</code> elements are self-closing.",
"Try it with this image: <code>https://bit.ly/fcc-relaxing-cat</code>." "Try it with this image: <code>https://bit.ly/fcc-relaxing-cat</code>."
], ],
"tests": [ "tests": [
"assert($(\"img\").length > 0, 'Your page should have an image element.')", "assert($(\"img\").length > 0, 'Your page should have an image element.')",
"assert(new RegExp(\"\\/\\/bit.ly\\/fcc-relaxing-cat\", \"gi\").test($(\"img\").attr(\"src\")), 'Your image should have have a <code>src</code> attribute that points to the kitten image.')" "assert(new RegExp(\"\\/\\/bit.ly\\/fcc-relaxing-cat\", \"gi\").test($(\"img\").attr(\"src\")), 'Your image should have a <code>src</code> attribute that points to the kitten image.')"
], ],
"challengeSeed": [ "challengeSeed": [
"<link href=\"http://fonts.googleapis.com/css?family=Lobster\" rel=\"stylesheet\" type=\"text/css\">", "<link href=\"http://fonts.googleapis.com/css?family=Lobster\" rel=\"stylesheet\" type=\"text/css\">",
@ -1056,13 +1060,13 @@
"description": [ "description": [
"<code>a</code> elements, also known as <code>anchor</code> elements, are used to link to content outside of the current page.", "<code>a</code> elements, also known as <code>anchor</code> elements, are used to link to content outside of the current page.",
"Here's a diagram of an <code>a</code> element. In this case, the <code>a</code> element is used in the middle of a paragraph element, which means the link will appear in the middle of a sentence.", "Here's a diagram of an <code>a</code> element. In this case, the <code>a</code> element is used in the middle of a paragraph element, which means the link will appear in the middle of a sentence.",
"<img class=\"img-responsive\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"https://www.evernote.com/l/AHSaNaepx_lG9LhhPkVYmagcedpmAeITDsQB/image.png\">", "<img class=\"img-responsive\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"http://i.imgur.com/hviuZwe.png\">",
"Here's an example: <code>&#60;p&#62;Here's a &#60;a href=\"http://freecodecamp.com\"&#62; link to Free Code Camp&#60;/a&#62; for you to follow.&#60;/p&#62;</code>.", "Here's an example: <code>&#60;p&#62;Here's a &#60;a href=\"http://freecodecamp.com\"&#62; link to Free Code Camp&#60;/a&#62; for you to follow.&#60;/p&#62;</code>.",
"Create an <code>a</code> element that links to <code>http://catphotoapp.com</code> and has \"cat photos\" as its <code>anchor text</code>." "Create an <code>a</code> element that links to <code>http://catphotoapp.com</code> and has \"cat photos\" as its <code>anchor text</code>."
], ],
"tests": [ "tests": [
"assert((/cat photos/gi).test($(\"a\").text()), 'Your <code>a</code> element should have the <code>anchor text</code> of \"cat photos\"')", "assert((/cat photos/gi).test($(\"a\").text()), 'Your <code>a</code> element should have the <code>anchor text</code> of \"cat photos\"')",
"assert(/http:\\/\\/catphotoapp\\.com/gi.test($(\"a\").attr(\"href\")), 'You need an <code>a</code> element that links to <code>http&#58;//catphotoapp.com<code>.')", "assert(/http:\\/\\/catphotoapp\\.com/gi.test($(\"a\").attr(\"href\")), 'You need an <code>a</code> element that links to <code>http&#58;//catphotoapp.com</code>.')",
"assert(editor.match(/<\\/a>/g) && editor.match(/<\\/a>/g).length === editor.match(/<a/g).length, 'Make sure your <code>a</code> element has a closing tag.')" "assert(editor.match(/<\\/a>/g) && editor.match(/<\\/a>/g).length === editor.match(/<a/g).length, 'Make sure your <code>a</code> element has a closing tag.')"
], ],
"challengeSeed": [ "challengeSeed": [
@ -1126,7 +1130,7 @@
"difficulty": 1.23, "difficulty": 1.23,
"description": [ "description": [
"Again, here's a diagram of an <code>a</code> element for your reference:", "Again, here's a diagram of an <code>a</code> element for your reference:",
"<img class=\"img-responsive\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"https://www.evernote.com/l/AHSaNaepx_lG9LhhPkVYmagcedpmAeITDsQB/image.png\">", "<img class=\"img-responsive\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"http://i.imgur.com/hviuZwe.png\">",
"Here's an example: <code>&#60;p&#62;Here's a &#60;a href=\"http://freecodecamp.com\"&#62; link to Free Code Camp&#60;/a&#62; for you to follow.&#60;/p&#62;</code>.", "Here's an example: <code>&#60;p&#62;Here's a &#60;a href=\"http://freecodecamp.com\"&#62; link to Free Code Camp&#60;/a&#62; for you to follow.&#60;/p&#62;</code>.",
"<code>Nesting</code> just means putting one element inside of another element.", "<code>Nesting</code> just means putting one element inside of another element.",
"Now nest your <code>a</code> element within a new <code>p</code> element so that the surrounding paragraph says \"click here for cat photos\", but where only \"cat photos\" is a link, and the rest of the text is rest is plain text." "Now nest your <code>a</code> element within a new <code>p</code> element so that the surrounding paragraph says \"click here for cat photos\", but where only \"cat photos\" is a link, and the rest of the text is rest is plain text."
@ -3117,7 +3121,7 @@
"difficulty": 1.46, "difficulty": 1.46,
"description": [ "description": [
"Now let's start fresh and talk about CSS inheritance.", "Now let's start fresh and talk about CSS inheritance.",
"Every HTML page has <code>body</code> element.", "Every HTML page has a <code>body</code> element.",
"We can prove that the <code>body</code> element exists here by giving it a <code>background-color</code> of black.", "We can prove that the <code>body</code> element exists here by giving it a <code>background-color</code> of black.",
"We can do this by adding <code>body { background-color: black; }</code> to our <code>style</code> element." "We can do this by adding <code>body { background-color: black; }</code> to our <code>style</code> element."
], ],
@ -3235,7 +3239,8 @@
"Our \"pink-text\" class overrode our <code>body</code> element's CSS declaration!", "Our \"pink-text\" class overrode our <code>body</code> element's CSS declaration!",
"We just proved that our classes will override the <code>body</code> element's CSS. So the next logical question is, what can we do to override our <code>pink-text</code> class?", "We just proved that our classes will override the <code>body</code> element's CSS. So the next logical question is, what can we do to override our <code>pink-text</code> class?",
"Create an additional CSS class called <code>blue-text</code> that gives an element the color blue. Make sure it's below your <code>pink-text</code> class declaration.", "Create an additional CSS class called <code>blue-text</code> that gives an element the color blue. Make sure it's below your <code>pink-text</code> class declaration.",
"Apply the <code>blue-text</code> class to your <code>h1</code> element in addition to your <code>pink-text</code> class, and let's see which one wins." "Apply the <code>blue-text</code> class to your <code>h1</code> element in addition to your <code>pink-text</code> class, and let's see which one wins.",
"Applying multiple class attributes to a HTML element is done with a space between them like this: <code>class=\"class1 class2\"</code>"
], ],
"tests": [ "tests": [
"assert($(\"h1\").hasClass(\"pink-text\"), 'Your <code>h1</code> element should have the class <code>pink-text</code>.')", "assert($(\"h1\").hasClass(\"pink-text\"), 'Your <code>h1</code> element should have the class <code>pink-text</code>.')",
@ -3280,12 +3285,14 @@
"Let's override your <code>pink-text</code> and <code>blue-text</code> classes, and make your <code>h1</code> element orange, by giving the <code>h1</code> element an id and then styling that id.", "Let's override your <code>pink-text</code> and <code>blue-text</code> classes, and make your <code>h1</code> element orange, by giving the <code>h1</code> element an id and then styling that id.",
"Give your <code>h1</code> element the <code>id</code> attribute of <code>orange-text</code>. Remember, id styles look like this: <code>&#60;h1 id=\"orange-text\"&#62;</code>", "Give your <code>h1</code> element the <code>id</code> attribute of <code>orange-text</code>. Remember, id styles look like this: <code>&#60;h1 id=\"orange-text\"&#62;</code>",
"Leave the <code>blue-text</code> and <code>pink-text</code> classes on your <code>h1</code> element.", "Leave the <code>blue-text</code> and <code>pink-text</code> classes on your <code>h1</code> element.",
"Create a CSS declaration for your <code>orange-text</code> id in your <code>style</code> element. Here's and example of what this looks like: <code>#brown-text { color: brown; }</code>" "Create a CSS declaration for your <code>orange-text</code> id in your <code>style</code> element. Here's an example of what this looks like: <code>#brown-text { color: brown; }</code>"
], ],
"tests": [ "tests": [
"assert($(\"h1\").hasClass(\"pink-text\"), 'Your <code>h1</code> element should have the class <code>pink-text</code>.')", "assert($(\"h1\").hasClass(\"pink-text\"), 'Your <code>h1</code> element should have the class <code>pink-text</code>.')",
"assert($(\"h1\").hasClass(\"blue-text\"), 'Your <code>h1</code> element should have the class <code>blue-text</code>.')", "assert($(\"h1\").hasClass(\"blue-text\"), 'Your <code>h1</code> element should have the class <code>blue-text</code>.')",
"assert($(\"h1\").attr(\"id\") === \"orange-text\", 'Give your <code>h1</code> element the id of <code>orange-text</code>.')", "assert($(\"h1\").attr(\"id\") === \"orange-text\", 'Give your <code>h1</code> element the id of <code>orange-text</code>.')",
"assert(editor.match(/#orange-text\\s*{/gi), 'Create a CSS declaration for your <code>orange-text</code> id')",
"assert(!editor.match(/<h1.*style.*>/gi), 'Do not give your <code>h1</code> any <code>style</code> attributes.')",
"assert($(\"h1\").css(\"color\") === \"rgb(255, 165, 0)\", 'Your <code>h1</code> element should be orange.')" "assert($(\"h1\").css(\"color\") === \"rgb(255, 165, 0)\", 'Your <code>h1</code> element should be orange.')"
], ],
"challengeSeed": [ "challengeSeed": [
@ -3387,7 +3394,7 @@
"assert($(\"h1\").hasClass(\"blue-text\"), 'Your <code>h1</code> element should have the class <code>blue-text</code>.')", "assert($(\"h1\").hasClass(\"blue-text\"), 'Your <code>h1</code> element should have the class <code>blue-text</code>.')",
"assert($(\"h1\").attr(\"id\") === \"orange-text\", 'Your <code>h1</code> element should have the id of <code>orange-text</code>.')", "assert($(\"h1\").attr(\"id\") === \"orange-text\", 'Your <code>h1</code> element should have the id of <code>orange-text</code>.')",
"assert(editor.match(/<h1.*style/gi) && editor.match(/<h1.*style.*color:/gi).length > 0, 'Your <code>h1</code> element should have the inline style of <code>color&#58; white</code>.')", "assert(editor.match(/<h1.*style/gi) && editor.match(/<h1.*style.*color:/gi).length > 0, 'Your <code>h1</code> element should have the inline style of <code>color&#58; white</code>.')",
"assert(editor.match(/pink.*\\!important/gi) && editor.match(/pink.*\\!important;/gi).length > 0, 'Your <code>pink-text</code> class should have the <code>!important</code> keyword to override all other declarations.')", "assert(editor.match(/pink.*\\!important;/gi), 'Your <code>pink-text</code> class should have the <code>!important</code> keyword to override all other declarations.')",
"assert($(\"h1\").css(\"color\") === \"rgb(255, 192, 203)\", 'Your <code>h1</code> element should be pink.')" "assert($(\"h1\").css(\"color\") === \"rgb(255, 192, 203)\", 'Your <code>h1</code> element should be pink.')"
], ],
"challengeSeed": [ "challengeSeed": [
@ -3431,7 +3438,7 @@
"description": [ "description": [
"Did you know there are other ways to represent colors in CSS? One of these ways is called hexadecimal code, or <code>hex code</code> for short.", "Did you know there are other ways to represent colors in CSS? One of these ways is called hexadecimal code, or <code>hex code</code> for short.",
"<code>Decimal</code> means the numbers zero through nine - the numbers that people use in everyday life. <code>Hexadecimal</code> includes these 10 numbers, plus the letters A, B, C, D, E and F. This means that Hexidecimal has a total of 16 possible values, instead of the 10 possible values that our normal base-10 number system gives us.", "<code>Decimal</code> means the numbers zero through nine - the numbers that people use in everyday life. <code>Hexadecimal</code> includes these 10 numbers, plus the letters A, B, C, D, E and F. This means that Hexidecimal has a total of 16 possible values, instead of the 10 possible values that our normal base-10 number system gives us.",
"With CSS, we use 6 hexidecimal number to represent colors. For example, <code>#000000</code> is the lowest possible value, and it represents the color black.", "With CSS, we use 6 hexadecimal numbers to represent colors. For example, <code>#000000</code> is the lowest possible value, and it represents the color black.",
"Replace the word <code>black</code> in our <code>body</code> element's background-color with its <code>hex code</code> representation, <code>#000000</code>. " "Replace the word <code>black</code> in our <code>body</code> element's background-color with its <code>hex code</code> representation, <code>#000000</code>. "
], ],
"tests": [ "tests": [
@ -3749,7 +3756,7 @@
"description": [ "description": [
"Another way you can represent colors in CSS is by using <code>rgb</code> values.", "Another way you can represent colors in CSS is by using <code>rgb</code> values.",
"RGB values look like this: <code>rgb(0, 0, 0)</code> for black and <code>rgb(255, 255, 255)</code> for white.", "RGB values look like this: <code>rgb(0, 0, 0)</code> for black and <code>rgb(255, 255, 255)</code> for white.",
"Instead of using six hexadecimal digits like you do with hex code, with <code>rbg</code> you specify the brightness of each color with a number between 0 and 255.", "Instead of using six hexadecimal digits like you do with hex code, with <code>rgb</code> you specify the brightness of each color with a number between 0 and 255.",
"If you do the math, 16 times 16 is 256 total values. So <code>rgb</code>, which starts counting from zero, has the exact same number of possible values as hex code.", "If you do the math, 16 times 16 is 256 total values. So <code>rgb</code>, which starts counting from zero, has the exact same number of possible values as hex code.",
"Let's replace the hex code in our <code>body</code> element's background color with the RGB value for black: <code>rgb(0, 0, 0)</code>" "Let's replace the hex code in our <code>body</code> element's background color with the RGB value for black: <code>rgb(0, 0, 0)</code>"
], ],

View File

@ -138,9 +138,9 @@
"id": "bd7158d8c442eddfaeb5bd1c", "id": "bd7158d8c442eddfaeb5bd1c",
"title": "Build a Simon Game", "title": "Build a Simon Game",
"difficulty": 1.07, "difficulty": 1.07,
"challengeSeed": ["126415123"], "challengeSeed": ["137213633"],
"description": [ "description": [
"<span class='text-info'>Objective:</span> Build a <a href='http://codepen.io' target='_blank'>CodePen.io</a> app that successfully reverse-engineers this: <a href='http://codepen.io/dting/full/KpJXZV/' target='_blank'>http://codepen.io/dting/full/KpJXZV/</a>.", "<span class='text-info'>Objective:</span> Build a <a href='http://codepen.io' target='_blank'>CodePen.io</a> app that successfully reverse-engineers this: <a href='http://codepen.io/Em-Ant/full/QbRyqq/' target='_blank'>http://codepen.io/dting/full/QbRyqq/</a>.",
"<span class='text-info'>Rule #1:</span> Don't look at the example project's code on CodePen. Figure it out for yourself.", "<span class='text-info'>Rule #1:</span> Don't look at the example project's code on CodePen. Figure it out for yourself.",
"<span class='text-info'>Rule #2:</span> You may use whichever libraries or APIs you need.", "<span class='text-info'>Rule #2:</span> You may use whichever libraries or APIs you need.",
"<span class='text-info'>Rule #3:</span> Reverse engineer the example project's functionality, and also feel free to personalize it.", "<span class='text-info'>Rule #3:</span> Reverse engineer the example project's functionality, and also feel free to personalize it.",

View File

@ -17,7 +17,7 @@
"assert(editor.match(/<script>/g), 'Create a <code>script</code> element.')", "assert(editor.match(/<script>/g), 'Create a <code>script</code> element.')",
"assert(editor.match(/<\\/script>/g) && editor.match(/<script/g) && editor.match(/<\\/script>/g).length === editor.match(/<script/g).length, 'Make sure your <code>script</code> element has a closing tag.')", "assert(editor.match(/<\\/script>/g) && editor.match(/<script/g) && editor.match(/<\\/script>/g).length === editor.match(/<script/g).length, 'Make sure your <code>script</code> element has a closing tag.')",
"assert(editor.match(/\\$\\(document\\)\\.ready\\(function\\(\\)\\s?\\{/g), 'Add <code>$(document).ready(function() {</code> to the beginning of your <code>script</code> element.')", "assert(editor.match(/\\$\\(document\\)\\.ready\\(function\\(\\)\\s?\\{/g), 'Add <code>$(document).ready(function() {</code> to the beginning of your <code>script</code> element.')",
"assert(editor.match(/\\n\\s+?\\}\\);/g), 'Close your <code>$(document).ready(function() {</code> function with <code>});</code>.')" "assert(editor.match(/\\n?\\s+?\\}\\);/g), 'Close your <code>$(document).ready(function() {</code> function with <code>});</code>.')"
], ],
"challengeSeed": [ "challengeSeed": [
"", "",
@ -154,7 +154,7 @@
"difficulty": 3.04, "difficulty": 3.04,
"description": [ "description": [
"You can also target elements by their id attributes.", "You can also target elements by their id attributes.",
"First target your <code>div</code> element with the id <code>target3</code> by using the <code>$(\"#target3\")</code> selector.", "First target your <code>button</code> element with the id <code>target3</code> by using the <code>$(\"#target3\")</code> selector.",
"Note that, just like with CSS declarations, you type a <code>#</code> before the id's name.", "Note that, just like with CSS declarations, you type a <code>#</code> before the id's name.",
"Then use jQuery's <code>.addClass()</code> function to add the classes <code>animated</code> and <code>fadeOut</code>.", "Then use jQuery's <code>.addClass()</code> function to add the classes <code>animated</code> and <code>fadeOut</code>.",
"Here's how you'd make the <code>button</code> element with the id <code>target6</code> fade out: <code>$(\"#target6\").addClass(\"animated fadeOut\")</code>." "Here's how you'd make the <code>button</code> element with the id <code>target6</code> fade out: <code>$(\"#target6\").addClass(\"animated fadeOut\")</code>."
@ -263,7 +263,7 @@
], ],
"tests": [ "tests": [
"assert(editor.match(/\\$\\(.*button/g), 'Use the <code>$&#40\"button\"&#41</code> selector.')", "assert(editor.match(/\\$\\(.*button/g), 'Use the <code>$&#40\"button\"&#41</code> selector.')",
"assert(editor.match(/\\$\\(.*.btn/g), 'Use the <code>$&#40\".btn\"&#41</code> selector.')", "assert(editor.match(/\\$\\(.*\\.btn/g), 'Use the <code>$&#40\".btn\"&#41</code> selector.')",
"assert(editor.match(/\\$\\(.*#target1/g), 'Use the <code>$&#40\"#target1\"&#41</code> selector.')", "assert(editor.match(/\\$\\(.*#target1/g), 'Use the <code>$&#40\"#target1\"&#41</code> selector.')",
"assert(editor.match(/addClass/g) && editor.match(/addClass/g).length > 2, 'Only add one class with each of your three selectors.')", "assert(editor.match(/addClass/g) && editor.match(/addClass/g).length > 2, 'Only add one class with each of your three selectors.')",
"assert($(\"#target1\").hasClass(\"animated\") && $(\"#target1\").hasClass(\"shake\") && $(\"#target1\").hasClass(\"btn-primary\"), 'Your <code>#target1</code> element should have the classes <code>animated</code>&#130; <code>shake</code> and <code>btn-primary</code>.')", "assert($(\"#target1\").hasClass(\"animated\") && $(\"#target1\").hasClass(\"shake\") && $(\"#target1\").hasClass(\"btn-primary\"), 'Your <code>#target1</code> element should have the classes <code>animated</code>&#130; <code>shake</code> and <code>btn-primary</code>.')",
@ -314,7 +314,7 @@
], ],
"tests": [ "tests": [
"assert($(\".btn-default\").length === 0, 'Remove the <code>btn-default</code> class from all of your <code>button</code> elements.')", "assert($(\".btn-default\").length === 0, 'Remove the <code>btn-default</code> class from all of your <code>button</code> elements.')",
"assert(editor.match(/btn btn-default/g), 'Only use jQuery to add these classes to the element.')" "assert(editor.match(/btn btn-default/g), 'Only use jQuery to remove this class from the element.')"
], ],
"challengeSeed": [ "challengeSeed": [
"fccss", "fccss",
@ -415,7 +415,7 @@
"You can also change the non-CSS properties of HTML elements with jQuery. For example, you can disable buttons.", "You can also change the non-CSS properties of HTML elements with jQuery. For example, you can disable buttons.",
"When you disable a button, it will become grayed-out and can no longer be clicked.", "When you disable a button, it will become grayed-out and can no longer be clicked.",
"jQuery has a function called <code>.prop()</code> that allows you to adjust the properties of elements.", "jQuery has a function called <code>.prop()</code> that allows you to adjust the properties of elements.",
"Here's how you would disable all buttons: <code>$('#button').prop('disabled', true);</code>", "Here's how you would disable all buttons: <code>$(\"button\").prop(\"disabled\", true);</code>",
"Disable only the <code>target1</code> button." "Disable only the <code>target1</code> button."
], ],
"tests": [ "tests": [
@ -617,7 +617,7 @@
"title": "Target the Parent of an Element Using jQuery", "title": "Target the Parent of an Element Using jQuery",
"difficulty": 3.13, "difficulty": 3.13,
"description": [ "description": [
"Every HTML elements has a <code>parent</code> element from which it <code>inherits</code> properties.", "Every HTML element has a <code>parent</code> element from which it <code>inherits</code> properties.",
"For example, your <code>jQuery Playground</code> <code>h3</code> element has the parent element of <code>&#60;div class=\"container-fluid\"&#62</code>, which itself has the parent <code>body</code>.", "For example, your <code>jQuery Playground</code> <code>h3</code> element has the parent element of <code>&#60;div class=\"container-fluid\"&#62</code>, which itself has the parent <code>body</code>.",
"jQuery has a function called <code>parent()</code> that allows you to access the parent of whichever element you've selected.", "jQuery has a function called <code>parent()</code> that allows you to access the parent of whichever element you've selected.",
"Give the parent of the <code>#target1</code> element background-color of red.", "Give the parent of the <code>#target1</code> element background-color of red.",
@ -672,7 +672,7 @@
"title": "Target the Children of an Element Using jQuery", "title": "Target the Children of an Element Using jQuery",
"difficulty": 3.14, "difficulty": 3.14,
"description": [ "description": [
"Many HTML elements have <code>children</code> elements from which they <code>inherit</code> properties.", "Many HTML elements have <code>children</code> elements from which <code>inherit</code> their properties.",
"For example, every HTML element is a child of your <code>body</code> element, and your \"jQuery Playground\" <code>h3</code> element is a child of your <code>&#60;div class=\"container-fluid\"&#62</code> element.", "For example, every HTML element is a child of your <code>body</code> element, and your \"jQuery Playground\" <code>h3</code> element is a child of your <code>&#60;div class=\"container-fluid\"&#62</code> element.",
"jQuery has a function called <code>children()</code> that allows you to access the children of whichever element you've selected.", "jQuery has a function called <code>children()</code> that allows you to access the children of whichever element you've selected.",
"Give all the children of your <code>#right-well</code> element a color of green.", "Give all the children of your <code>#right-well</code> element a color of green.",
@ -786,7 +786,7 @@
"difficulty": 3.16, "difficulty": 3.16,
"description": [ "description": [
"You can also target all the even-numbered elements.", "You can also target all the even-numbered elements.",
"Here's how you would target all the odd-numbered elements with class <code>target</code> and give them classes: <code>$('.target:odd').addClass('animated shake');</code>", "Here's how you would target all the odd-numbered elements with class <code>target</code> and give them classes: <code>$(\".target:odd\").addClass(\"animated shake\");</code>",
"Try selecting all the even-numbered elements - that is, what your browser will consider even-numbered elements - and giving them the classes of <code>animated</code> and <code>shake</code>." "Try selecting all the even-numbered elements - that is, what your browser will consider even-numbered elements - and giving them the classes of <code>animated</code> and <code>shake</code>."
], ],
"tests": [ "tests": [

View File

@ -17,7 +17,7 @@
"difficulty":0, "difficulty":0,
"description":[ "description":[
"Before we dive into Object Oriented Programming, let's revisit JavaScript objects.", "Before we dive into Object Oriented Programming, let's revisit JavaScript objects.",
"Give your <code>motorBike</code> object a <code>wheels</code>, <code>engine</code> and <code>seats</code> attribute and set them to numbers." "Give your <code>motorBike</code> object a <code>wheels</code>, <code>engines</code> and <code>seats</code> attribute and set them to numbers."
], ],
"tests":[ "tests":[
"assert(typeof(motorBike.engines) === 'number', '<code>engines</code> should be have a <code>engines</code> attribute set to a number.');", "assert(typeof(motorBike.engines) === 'number', '<code>engines</code> should be have a <code>engines</code> attribute set to a number.');",
@ -54,7 +54,7 @@
"difficulty":0, "difficulty":0,
"description":[ "description":[
"We are also able to create objects using <code>constructor</code> functions.", "We are also able to create objects using <code>constructor</code> functions.",
"Give your <code>motorBike</code> object a <code>wheels</code>, <code>engine</code> and <code>seats</code> attribute and set them to numbers." "Give your <code>motorBike</code> object a <code>wheels</code>, <code>engines</code> and <code>seats</code> attribute and set them to numbers."
], ],
"tests":[ "tests":[
"assert(typeof((new MotorBike()).engines) === 'number', '<code>engines</code> should be have a <code>engines</code> attribute set to a number.');", "assert(typeof((new MotorBike()).engines) === 'number', '<code>engines</code> should be have a <code>engines</code> attribute set to a number.');",
@ -62,7 +62,7 @@
"assert(typeof((new MotorBike()).seats) === 'number', '<code>seats</code> should be have a <code>engines</code> attribute set to a number.');" "assert(typeof((new MotorBike()).seats) === 'number', '<code>seats</code> should be have a <code>engines</code> attribute set to a number.');"
], ],
"challengeSeed":[ "challengeSeed":[
"// Let's add the properties engine and seats to the car in the same way that the property wheels has been added below. They should both be numbers.", "// Let's add the properties engines and seats to the car in the same way that the property wheels has been added below. They should both be numbers.",
"var Car = function() {", "var Car = function() {",
" this.wheels = 4;", " this.wheels = 4;",
" this.engines = 1;", " this.engines = 1;",

View File

@ -0,0 +1,5 @@
{
"name": "D3",
"order": 0.099,
"challenges": []
}

View File

@ -1,35 +1,48 @@
{ {
"name": "JSON APIs and Ajax", "name": "JSON APIs and Ajax",
"order": 0.007, "order": 0.0065,
"challenges": [ "challenges": [
{ {
"id": "bad87fed1348bd9aeca08826", "id": "bad87fed1348bd9aeca08826",
"title": "Trigger on click Events with jQuery", "title": "Trigger on click Events with jQuery",
"difficulty": 3.19, "difficulty": 3.19,
"description": [ "description": [
"<code>.on('click', function() {</code>", "<code>$(\"#getMessage\").on(\"click\", function(){</code>",
"", "<code>&thinsp;&thinsp;$(\".message\").html(\"Here is the message\");</code>",
"});</code>" "<code>});</code>"
], ],
"tests": [ "tests": [
"assert(editor.match(/\\$\\(\\s?\\\"\\#getMessage\\\"\\s?\\)\\.on\\s?\\(\\s?\\\"click\\\"\\,\\s?function\\s?\\(\\)\\s?\\{/gi), 'You should have bound the click event to the getMessage button')",
"assert(editor.match(/\\$\\(\\s?\\\"\\.message\\\"\\s?\\)\\.html\\(\\s?\\\"Here\\sis\\sthe\\smessage\\\"\\s?\\);/gi), 'You should set te value of the #message box to be the message given in the description')",
"assert(editor.match(/\\}\\);/gi) && editor.match(/\\}\\);/gi).length >= 2, 'Make sure that you close off all of your functions')"
], ],
"challengeSeed": [ "challengeSeed": [
"fccss", "fccss",
" $(document).ready(function() {</code>", " $(document).ready(function() {",
" ", " ",
"<code> });</code>", " });",
"fcces", "fcces",
"", "",
"<!-- You shouldn't need to modify code below this line -->", "<!-- You shouldn't need to modify code below this line -->",
"", "",
"<div class=\"container-fluid\">", "<div class=\"container-fluid\">",
" <form action=\"/submit-cat-photo\">", " <div class = \"row text-center\">",
" <label><input type=\"radio\" name=\"indoor-outdoor\"> Indoor</label>", " <h2>Cat Photo Finder</h2>",
" <label><input type=\"radio\" name=\"indoor-outdoor\"> Outdoor</label>", " </div>",
" <input type=\"text\" placeholder=\"cat photo URL\" required>", " <br/>",
" <button type=\"submit\">Submit</button>", " <div class = \"row text-center\">",
" </form>", " <div class = \"col-xs-12 well Message\">",
" The message will go here",
" </div>",
" </div>",
" <br/>",
" <div class = \"row text-center\">",
" <div class = \"col-xs-12\">",
" <button id = \"getMessage\" class = \"btn btn-primary\">",
" Get Message",
" </button>",
" </div>",
" </div>",
"</div>" "</div>"
], ],
"challengeType": 0, "challengeType": 0,
@ -46,69 +59,12 @@
], ],
"tests": [ "tests": [
],
"challengeSeed": [
"fccss",
"catPhotoJSON = [",
" {",
" \"id\": 0,",
" \"imageLink\": \"https://s3.amazonaws.com/freecodecamp/funny-cat.jpg\",",
" \"codeNames\": [",
" \"Juggernaut\",",
" \"Mrs. Wallace\",",
" \"Buttercup\"",
" ]",
" },",
" {",
" \"id\": 1,",
" \"imageLink\": \"https://s3.amazonaws.com/freecodecamp/grumpy-cat.jpg\",",
" \"codeNames\": [",
" \"Oscar\",",
" \"Scrooge\",",
" \"Tyrion\"",
" ]",
" },",
" {",
" \"id\": 2,",
" \"imageLink\": \"https://s3.amazonaws.com/freecodecamp/mischievous-cat.jpg\",",
" \"codeNames\": [",
" \"The Doctor\",",
" \"Loki\",",
" \"Joker\"",
" ]",
" }",
" ]",
"",
"<code> });</code>",
"fcces",
"",
"<!-- You shouldn't need to modify code below this line -->",
"",
"<div class=\"container-fluid\">",
" <div class=\"jumbotron\" id=\"output\">",
" </div>",
"</div>"
],
"challengeType": 0,
"type": "waypoint"
},
{
"id": "bad84fee1348bd9aecc58826",
"title": "Read Data from an Element Using jQuery",
"dashedName": "waypoint-read-data-from-an-element-using-jquery",
"difficulty": 3.17,
"description": [
"Let's make everything roll with <code>rollOut</code>."
],
"tests": [
], ],
"challengeSeed": [ "challengeSeed": [
"fccss", "fccss",
" $(document).ready(function() {", " $(document).ready(function() {",
" $(\"button\").on(\"click\", function() {", " $(\"#getMessage\").on(\"click\", function(){",
"", " $(\".message\").html(\"Here is the message\");",
" });", " });",
" });", " });",
"fcces", "fcces",
@ -116,16 +72,23 @@
"<!-- You shouldn't need to modify code below this line -->", "<!-- You shouldn't need to modify code below this line -->",
"", "",
"<div class=\"container-fluid\">", "<div class=\"container-fluid\">",
" <div class=\"row\">", " <div class = \"row text-center\">",
" <div class=\"col-xs-2\">", " <h2>Cat Photo Finder</h2>",
" <input type=\"checkbox\" id=\"check-me\">", " </div>",
" <br/>",
" <div class = \"row text-center\">",
" <div class = \"col-xs-12 well Message\">",
" The message will go here",
" </div>",
" </div>",
" <br/>",
" <div class = \"row text-center\">",
" <div class = \"col-xs-12\">",
" <button id = \"getMessage\" class = \"btn btn-primary\">",
" Get Message",
" </button>",
" </div>", " </div>",
" <div class=\"col-xs-10\">",
" <p>#check-me</p>",
" </div>", " </div>",
" <button class=\"btn btn-block btn-primary\">#click-me</button>",
" <span>Is the checkbox checked?</span>",
" <span id=\"checked-state\"></span>",
"</div>" "</div>"
], ],
"challengeType": 0, "challengeType": 0,

View File

@ -0,0 +1,5 @@
{
"name": "ReactJS",
"order": 0.099,
"challenges": []
}

View File

@ -1,5 +1,9 @@
import{ Observable } from 'rx';
import { observeMethod, observeQuery } from '../utils/rx'; import { observeMethod, observeQuery } from '../utils/rx';
import { getSocialProvider } from '../utils/auth'; import { getSocialProvider } from '../utils/auth';
import debugFactory from 'debug';
const debug = debugFactory('fcc:userIdent');
export default function({ models }) { export default function({ models }) {
const { User, UserIdentity, UserCredential } = models; const { User, UserIdentity, UserCredential } = models;
@ -20,15 +24,18 @@ export default function({ models }) {
options = {}; options = {};
} }
const user$ = findUserById(userId); const user$ = findUserById(userId);
console.log('provider', provider); const query = {
console.log('id', profile.id); where: {
findIdent({
provider: getSocialProvider(provider), provider: getSocialProvider(provider),
externalId: profile.id externalId: profile.id
}) }
};
debug('link identity query', query);
findIdent(query)
.flatMap(identity => { .flatMap(identity => {
const modified = new Date(); const modified = new Date();
if (!identity || identity.externalId !== profile.id) { if (!identity) {
return observeQuery(UserIdentity, 'create', { return observeQuery(UserIdentity, 'create', {
provider: getSocialProvider(provider), provider: getSocialProvider(provider),
externalId: profile.id, externalId: profile.id,
@ -40,9 +47,14 @@ export default function({ models }) {
modified modified
}); });
} }
if (identity.userId.toString() !== userId.toString()) {
return Observable.throw(
new Error("It looks like you already have an account associated with that sign in method. Here's what you can do: 1) Sign out of this account. 2) Use that sign in method to sign into your other account. 3) Delete that account. 4) Then sign back into this account and you'll be able to link it here. If you need help, send us an email at team@freecodecamp.com.")
);
}
identity.credentials = credentials; identity.credentials = credentials;
return observeQuery(identity, 'updateAttributes', { return observeQuery(identity, 'updateAttributes', {
profile: getSocialProvider(provider), profile,
credentials, credentials,
modified modified
}); });

View File

@ -1,6 +1,6 @@
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import { Observable } from 'rx'; import { Observable, Scheduler } from 'rx';
import assign from 'object.assign'; import assign from 'object.assign';
import debugFactory from 'debug'; import debugFactory from 'debug';
import utils from '../utils'; import utils from '../utils';
@ -18,11 +18,16 @@ import {
} from '../utils/middleware'; } from '../utils/middleware';
const debug = debugFactory('freecc:challenges'); const debug = debugFactory('freecc:challenges');
const challengeMapWithNames = utils.getChallengeMapWithNames(); const challengesRegex = /^(bonfire|waypoint|zipline|basejump)/i;
const challengeMapWithIds = utils.getChallengeMapWithIds();
const challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
const challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i;
const firstChallenge = 'waypoint-say-hello-to-html-elements'; const firstChallenge = 'waypoint-say-hello-to-html-elements';
const challengeView = {
0: 'coursewares/showHTML',
1: 'coursewares/showJS',
2: 'coursewares/showVideo',
3: 'coursewares/showZiplineOrBasejump',
4: 'coursewares/showZiplineOrBasejump',
5: 'coursewares/showBonfire'
};
const dasherize = utils.dasherize; const dasherize = utils.dasherize;
const unDasherize = utils.unDasherize; const unDasherize = utils.unDasherize;
@ -63,7 +68,13 @@ module.exports = function(app) {
const findChallenge$ = observeMethod(Challenge, 'find'); const findChallenge$ = observeMethod(Challenge, 'find');
// create a stream of all the challenges // create a stream of all the challenges
const challenge$ = findChallenge$(challengesQuery) const challenge$ = findChallenge$(challengesQuery)
.flatMap(challenges => Observable.from(challenges)) .doOnNext(() => debug('query challenges'))
.flatMap(challenges => Observable.from(
challenges,
null,
null,
Scheduler.default
))
.shareReplay(); .shareReplay();
// create a stream of challenge blocks // create a stream of challenge blocks
@ -128,16 +139,12 @@ module.exports = function(app) {
app.use(router); app.use(router);
function returnNextChallenge(req, res, next) { function returnNextChallenge(req, res, next) {
// find the user's current challenge and block
// look in that block and find the index of their current challenge
// if index + 1 < block.challenges.length
// serve index + 1 challenge
// otherwise increment block key and serve the first challenge in that block
// unless the next block is undefined, which means no next block
let nextChallengeName = firstChallenge; let nextChallengeName = firstChallenge;
const challengeId = req.user.currentChallenge.challengeId; const challengeId = req.user.currentChallenge ?
req.user.currentChallenge.challengeId :
'bd7123c8c441eddfaeb5bdef';
// find challenge // find challenge
return challenge$ return challenge$
.map(challenge => challenge.toJSON()) .map(challenge => challenge.toJSON())
@ -165,7 +172,12 @@ module.exports = function(app) {
.elementAt(blockIndex) .elementAt(blockIndex)
.flatMap(block => { .flatMap(block => {
// find where our challenge lies in the block // find where our challenge lies in the block
const challengeIndex$ = Observable.from(block.challenges) const challengeIndex$ = Observable.from(
block.challenges,
null,
null,
Scheduler.default
)
.findIndex(({ id }) => id === challengeId); .findIndex(({ id }) => id === challengeId);
// grab next challenge in this block // grab next challenge in this block
@ -204,44 +216,53 @@ module.exports = function(app) {
res.redirect('/challenges/' + nextChallengeName); res.redirect('/challenges/' + nextChallengeName);
} }
); );
} }
function returnCurrentChallenge(req, res, next) { function returnCurrentChallenge(req, res, next) {
Observable.just(req.user)
.flatMap(user => {
if (!req.user.currentChallenge) { if (!req.user.currentChallenge) {
req.user.currentChallenge = {}; return challenge$
req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0]; .first()
req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0]; .flatMap(challenge => {
req.user.currentChallenge.dashedName = user.currentChallenge = {
challengeMapWithDashedNames['0'][0]; challengeId: challenge.id,
challengeName: challenge.name,
dashedName: challenge.dashedName
};
return saveUser(user);
});
} }
return Observable.just(user);
var nameString = req.user.currentChallenge.dashedName; })
.map(user => user.currentChallenge.dashedName)
saveUser(req.user)
.subscribe( .subscribe(
function() {}, function(challengeName) {
res.redirect('/challenges/' + challengeName);
},
next, next,
function() { function() {
res.redirect('/challenges/' + nameString);
} }
); );
} }
function returnIndividualChallenge(req, res, next) { function returnIndividualChallenge(req, res, next) {
var origChallengeName = req.params.challengeName; const origChallengeName = req.params.challengeName;
var unDashedName = unDasherize(origChallengeName); const unDashedName = unDasherize(origChallengeName);
var challengeName = challangesRegex.test(unDashedName) ? const challengeName = challengesRegex.test(unDashedName) ?
// remove first word if matches // remove first word if matches
unDashedName.split(' ').slice(1).join(' ') : unDashedName.split(' ').slice(1).join(' ') :
unDashedName; unDashedName;
debug('looking for ', challengeName); const testChallengeName = new RegExp(challengeName, 'i');
Challenge.findOne( debug('looking for %s', testChallengeName);
{ where: { name: { like: challengeName, options: 'i' } } }, challenge$
function(err, challenge) { .filter((challenge) => {
if (err) { return next(err); } return testChallengeName.test(challenge.name);
})
.lastOrDefault(null)
.flatMap(challenge => {
// Handle not found // Handle not found
if (!challenge) { if (!challenge) {
@ -252,14 +273,14 @@ module.exports = function(app) {
origChallengeName + origChallengeName +
'` Please double check the name.' '` Please double check the name.'
}); });
return res.redirect('/challenges'); return Observable.just('/challenges');
}
// Redirect to full name if the user only entered a partial
if (dasherize(challenge.name) !== origChallengeName) {
debug('redirecting to fullname');
return res.redirect('/challenges/' + dasherize(challenge.name));
} }
if (dasherize(challenge.name) !== origChallengeName) {
return Observable.just('/challenges/' + dasherize(challenge.name));
}
if (challenge) {
if (req.user) { if (req.user) {
req.user.currentChallenge = { req.user.currentChallenge = {
challengeId: challenge.id, challengeId: challenge.id,
@ -268,7 +289,9 @@ module.exports = function(app) {
}; };
} }
var commonLocals = { // save user does nothing if user does not exist
return saveUser(req.user)
.map(() => ({
title: challenge.name, title: challenge.name,
dashedName: origChallengeName, dashedName: origChallengeName,
name: challenge.name, name: challenge.name,
@ -289,28 +312,21 @@ module.exports = function(app) {
MDNlinks: getMDNLinks(challenge.MDNlinks), MDNlinks: getMDNLinks(challenge.MDNlinks),
// htmls specific // htmls specific
environment: utils.whichEnvironment() environment: utils.whichEnvironment()
}; }));
// TODO Berkeley
var challengeView = {
0: 'coursewares/showHTML',
1: 'coursewares/showJS',
2: 'coursewares/showVideo',
3: 'coursewares/showZiplineOrBasejump',
4: 'coursewares/showZiplineOrBasejump',
5: 'coursewares/showBonfire'
};
saveUser(req.user)
.subscribe(
function() {},
next,
function() {
var view = challengeView[challenge.challengeType];
res.render(view, commonLocals);
} }
})
.subscribe(
function(data) {
if (typeof data === 'string') {
debug('redirecting to %s', data);
return res.redirect(data);
}
var view = challengeView[data.challengeType];
res.render(view, data);
},
next,
function() {}
); );
});
} }
function completedBonfire(req, res, next) { function completedBonfire(req, res, next) {
@ -320,7 +336,7 @@ module.exports = function(app) {
var challengeData = { var challengeData = {
id: challengeId, id: challengeId,
name: req.body.challengeInfo.challengeName, name: req.body.challengeInfo.challengeName || '',
completedDate: Math.round(+new Date()), completedDate: Math.round(+new Date()),
solution: req.body.challengeInfo.solution, solution: req.body.challengeInfo.solution,
challengeType: 5 challengeType: 5
@ -362,7 +378,7 @@ module.exports = function(app) {
challengeData challengeData
); );
}) })
// not iterate users // iterate users
.flatMap(function(dats) { .flatMap(function(dats) {
debug('flatmap'); debug('flatmap');
return Observable.from([dats.user, dats.pairedWith]); return Observable.from([dats.user, dats.pairedWith]);
@ -399,7 +415,7 @@ module.exports = function(app) {
{ {
id: id || challengeId, id: id || challengeId,
completedDate: completedDate, completedDate: completedDate,
name: name || challengeName, name: name || challengeName || '',
solution: null, solution: null,
githubLink: null, githubLink: null,
verified: true verified: true
@ -446,7 +462,7 @@ module.exports = function(app) {
var challengeData = { var challengeData = {
id: challengeId, id: challengeId,
name: req.body.challengeInfo.challengeName, name: req.body.challengeInfo.challengeName || '',
completedDate: completedDate, completedDate: completedDate,
solution: solutionLink, solution: solutionLink,
githubLink: githubLink, githubLink: githubLink,

View File

@ -29,7 +29,7 @@ module.exports = function(loopbackApp) {
loopbackApp.once('started', function() { loopbackApp.once('started', function() {
app.listen(port, function() { app.listen(port, function() {
console.log('https redirect lisenting on port %s', port); console.log('https redirect listening on port %s', port);
}); });
}); });
}; };

View File

@ -18,6 +18,7 @@ var time48Hours = 172800000;
var unDasherize = utils.unDasherize; var unDasherize = utils.unDasherize;
var dasherize = utils.dasherize; var dasherize = utils.dasherize;
var getURLTitle = utils.getURLTitle; var getURLTitle = utils.getURLTitle;
var ifNoUser401 = require('../utils/middleware').ifNoUser401;
function hotRank(timeValue, rank) { function hotRank(timeValue, rank) {
/* /*
@ -63,12 +64,12 @@ module.exports = function(app) {
router.get('/stories/hotStories', hotJSON); router.get('/stories/hotStories', hotJSON);
router.get('/stories/submit', submitNew); router.get('/stories/submit', submitNew);
router.get('/stories/submit/new-story', preSubmit); router.get('/stories/submit/new-story', preSubmit);
router.post('/stories/preliminary', newStory); router.post('/stories/preliminary', ifNoUser401, newStory);
router.post('/stories/', storySubmission); router.post('/stories/', ifNoUser401, storySubmission);
router.get('/news/', hot); router.get('/news/', hot);
router.post('/stories/search', getStories); router.post('/stories/search', getStories);
router.get('/news/:storyName', returnIndividualStory); router.get('/news/:storyName', returnIndividualStory);
router.post('/stories/upvote/', upvote); router.post('/stories/upvote/', ifNoUser401, upvote);
router.get('/stories/:storyName', redirectToNews); router.get('/stories/:storyName', redirectToNews);
app.use(router); app.use(router);
@ -107,8 +108,12 @@ module.exports = function(app) {
}); });
} }
function preSubmit(req, res) { function preSubmit(req, res, next) {
var data = req.query; var data = req.query;
if (typeof data.url !== 'string') {
req.flash('errors', { msg: 'No URL supplied with story' });
return next(new TypeError('No URL supplied with story'));
}
var cleanedData = cleanData(data.url); var cleanedData = cleanData(data.url);
if (data.url.replace(/&/g, '&amp;') !== cleanedData) { if (data.url.replace(/&/g, '&amp;') !== cleanedData) {

View File

@ -1,25 +1,52 @@
var _ = require('lodash'), import _ from 'lodash';
async = require('async'), import async from 'async';
moment = require('moment'), import moment from 'moment';
debug = require('debug')('freecc:cntr:userController'); import debugFactory from 'debug';
import { ifNoUser401 } from '../utils/middleware';
const debug = debugFactory('freecc:boot:user');
const daysBetween = 1.5;
function calcCurrentStreak(cals) { function calcCurrentStreak(cals) {
const revCals = cals.slice().reverse(); const revCals = cals.concat([Date.now()]).slice().reverse();
let streakBroken = false; let streakBroken = false;
return revCals const lastDayInStreak = revCals
.reduce((current, cal, index) => { .reduce((current, cal, index) => {
// if streak not borken and diff between this cal and the call after it const before = revCals[index === 0 ? 0 : index - 1];
// is equal to zero
// moment.diff will return the days between rounded down
if ( if (
!streakBroken && !streakBroken &&
moment(revCals[index === 0 ? 0 : index - 1]).diff(cal, 'days') === 0 moment(before).diff(cal, 'days', true) < daysBetween
) { ) {
return current + 1; return index;
} }
return 1; streakBroken = true;
}, 1); return current;
}, 0);
const lastTimestamp = revCals[lastDayInStreak];
return Math.ceil(moment().diff(lastTimestamp, 'days', true));
}
function calcLongestStreak(cals) {
let tail = cals[0];
const longest = cals.reduce((longest, head, index) => {
const last = cals[index === 0 ? 0 : index - 1];
// is streak broken
if (moment(head).diff(last, 'days', true) > daysBetween) {
tail = head;
}
if (dayDiff(longest) < dayDiff([head, tail])) {
return [head, tail];
}
return longest;
}, [cals[0], cals[0]]);
return Math.ceil(dayDiff(longest));
}
function dayDiff([head, tail]) {
return moment(head).diff(tail, 'days', true);
} }
module.exports = function(app) { module.exports = function(app) {
@ -42,8 +69,11 @@ module.exports = function(app) {
router.get('/email-signup', getEmailSignup); router.get('/email-signup', getEmailSignup);
router.get('/email-signin', getEmailSignin); router.get('/email-signin', getEmailSignin);
router.get('/account/api', getAccountAngular); router.get('/account/api', getAccountAngular);
router.post('/account/password', postUpdatePassword); router.post(
router.post('/account/delete', postDeleteAccount); '/account/delete',
ifNoUser401,
postDeleteAccount
);
router.get('/account/unlink/:provider', getOauthUnlink); router.get('/account/unlink/:provider', getOauthUnlink);
router.get('/account', getAccount); router.get('/account', getAccount);
// Ensure this is the last route! // Ensure this is the last route!
@ -129,15 +159,10 @@ module.exports = function(app) {
objOrNum : objOrNum :
objOrNum.timestamp; objOrNum.timestamp;
}) })
.map(time => { .sort();
return moment(time).format('YYYY-MM-DD');
});
user.currentStreak = calcCurrentStreak(cals); user.currentStreak = calcCurrentStreak(cals);
user.longestStreak = calcLongestStreak(cals);
if (user.currentStreak > user.longestStreak) {
user.longestStreak = user.currentStreak;
}
const data = user const data = user
.progressTimestamps .progressTimestamps
@ -146,6 +171,9 @@ module.exports = function(app) {
objOrNum : objOrNum :
objOrNum.timestamp; objOrNum.timestamp;
}) })
.filter((timestamp) => {
return !!timestamp;
})
.reduce((data, timeStamp) => { .reduce((data, timeStamp) => {
data[(timeStamp / 1000)] = 1; data[(timeStamp / 1000)] = 1;
return data; return data;
@ -155,6 +183,10 @@ module.exports = function(app) {
return obj.challengeType === 3 || obj.challengeType === 4; return obj.challengeType === 3 || obj.challengeType === 4;
}); });
const bonfires = user.completedChallenges.filter(function(obj) {
return obj.challengeType === 5 && (obj.name || '').match(/Bonfire/g);
});
res.render('account/show', { res.render('account/show', {
title: 'Camper ' + user.username + '\'s portfolio', title: 'Camper ' + user.username + '\'s portfolio',
username: user.username, username: user.username,
@ -162,15 +194,16 @@ module.exports = function(app) {
isMigrationGrandfathered: user.isMigrationGrandfathered, isMigrationGrandfathered: user.isMigrationGrandfathered,
isGithubCool: user.isGithubCool, isGithubCool: user.isGithubCool,
location: user.location, location: user.location,
githubProfile: user.github, github: user.githubURL,
linkedinProfile: user.linkedin, linkedin: user.linkedin,
googleProfile: user.google, google: user.google,
facebookProfile: user.facebook, facebook: user.facebook,
twitterHandle: user.twitter, twitter: user.twitter,
picture: user.picture, picture: user.picture,
progressTimestamps: user.progressTimestamps, progressTimestamps: user.progressTimestamps,
calender: data, calender: data,
challenges: challenges, challenges: challenges,
bonfires: bonfires,
moment: moment, moment: moment,
longestStreak: user.longestStreak, longestStreak: user.longestStreak,
currentStreak: user.currentStreak currentStreak: user.currentStreak
@ -179,34 +212,6 @@ module.exports = function(app) {
); );
} }
function postUpdatePassword(req, res, next) {
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('/account');
}
User.findById(req.user.id, function(err, user) {
if (err) { return next(err); }
user.password = req.body.password;
user.save(function(err) {
if (err) { return next(err); }
req.flash('success', { msg: 'Password has been changed.' });
res.redirect('/account');
});
});
}
function postDeleteAccount(req, res, next) { function postDeleteAccount(req, res, next) {
User.destroyById(req.user.id, function(err) { User.destroyById(req.user.id, function(err) {
if (err) { return next(err); } if (err) { return next(err); }

View File

@ -3,7 +3,7 @@ var secrets = require('../config/secrets');
module.exports = { module.exports = {
db: { db: {
connector: 'mongodb', connector: 'mongodb',
connectionTimeout: 5000, connectionTimeout: 10000,
url: secrets.db url: secrets.db
}, },
mail: { mail: {

View File

@ -2,6 +2,14 @@
"initial:before": { "initial:before": {
"loopback#favicon": { "loopback#favicon": {
"params": "$!../public/favicon.ico" "params": "$!../public/favicon.ico"
},
"loopback#static": {
"params": [
"$!../public",
{
"maxAge": "86400000"
}
]
} }
}, },
"initial": { "initial": {
@ -42,14 +50,6 @@
"routes": { "routes": {
}, },
"files": { "files": {
"loopback#static": {
"params": [
"$!../public",
{
"maxAge": "86400000"
}
]
}
}, },
"final": { "final": {
}, },

View File

@ -1,10 +1,31 @@
const pathsOfNoReturn = [
'link',
'bower_components',
'auth',
'login',
'logout',
'signin',
'signup',
'fonts',
'favicon',
'js',
'css'
];
const pathsOfNoReturnRegex = new RegExp(pathsOfNoReturn.join('|'), 'i');
export default function addReturnToUrl() { export default function addReturnToUrl() {
return function(req, res, next) { return function(req, res, next) {
// Remember original destination before login. // Remember original destination before login.
var path = req.path.split('/')[1]; var path = req.path.split('/')[1];
if (/auth|login|logout|signin|signup|fonts|favicon/i.test(path)) {
if (req.method !== 'GET') {
return next(); return next();
} else if (/\/stories\/\w+/i.test(req.path)) { }
if (pathsOfNoReturnRegex.test(path)) {
return next();
}
if (/\/stories\/\w+/i.test(req.path)) {
return next(); return next();
} }
req.session.returnTo = req.path; req.session.returnTo = req.path;

View File

@ -1,5 +1,6 @@
var successRedirect = '/'; var successRedirect = '/';
var failureRedirect = '/login'; var failureRedirect = '/login';
var linkFailureRedirect = '/account';
module.exports = { module.exports = {
local: { local: {
provider: 'local', provider: 'local',
@ -34,7 +35,7 @@ module.exports = {
callbackURL: '/link/facebook/callback', callbackURL: '/link/facebook/callback',
callbackPath: '/link/facebook/callback', callbackPath: '/link/facebook/callback',
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: linkFailureRedirect,
scope: ['email', 'user_likes'], scope: ['email', 'user_likes'],
link: true, link: true,
failureFlash: true failureFlash: true
@ -63,7 +64,7 @@ module.exports = {
callbackURL: '/link/google/callback', callbackURL: '/link/google/callback',
callbackPath: '/link/google/callback', callbackPath: '/link/google/callback',
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: linkFailureRedirect,
scope: ['email', 'profile'], scope: ['email', 'profile'],
link: true, link: true,
failureFlash: true failureFlash: true
@ -89,7 +90,7 @@ module.exports = {
callbackURL: '/link/twitter/callback', callbackURL: '/link/twitter/callback',
callbackPath: '/link/twitter/callback', callbackPath: '/link/twitter/callback',
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: linkFailureRedirect,
consumerKey: process.env.TWITTER_KEY, consumerKey: process.env.TWITTER_KEY,
consumerSecret: process.env.TWITTER_SECRET, consumerSecret: process.env.TWITTER_SECRET,
link: true, link: true,
@ -120,7 +121,7 @@ module.exports = {
callbackURL: '/link/linkedin/callback', callbackURL: '/link/linkedin/callback',
callbackPath: '/link/linkedin/callback', callbackPath: '/link/linkedin/callback',
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: linkFailureRedirect,
clientID: process.env.LINKEDIN_ID, clientID: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET, clientSecret: process.env.LINKEDIN_SECRET,
scope: ['r_basicprofile', 'r_emailaddress'], scope: ['r_basicprofile', 'r_emailaddress'],
@ -152,7 +153,7 @@ module.exports = {
callbackURL: '/auth/github/callback/link', callbackURL: '/auth/github/callback/link',
callbackPath: '/auth/github/callback/link', callbackPath: '/auth/github/callback/link',
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: linkFailureRedirect,
clientID: process.env.GITHUB_ID, clientID: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET, clientSecret: process.env.GITHUB_SECRET,
scope: ['email'], scope: ['email'],

View File

@ -12,6 +12,7 @@ var uuid = require('node-uuid'),
var setProfileFromGithub = require('./utils/auth').setProfileFromGithub; var setProfileFromGithub = require('./utils/auth').setProfileFromGithub;
var getSocialProvider = require('./utils/auth').getSocialProvider; var getSocialProvider = require('./utils/auth').getSocialProvider;
var getUsernameFromProvider = require('./utils/auth').getUsernameFromProvider;
var generateKey = var generateKey =
require('loopback-component-passport/lib/models/utils').generateKey; require('loopback-component-passport/lib/models/utils').generateKey;
@ -69,7 +70,10 @@ var passportOptions = {
} }
if (!(/github/).test(provider)) { if (!(/github/).test(provider)) {
userObj[getSocialProvider(provider)] = profile.username; userObj[getSocialProvider(provider)] = getUsernameFromProvider(
getSocialProvider(provider),
profile
);
} }
if (/github/.test(provider)) { if (/github/.test(provider)) {

View File

@ -1,5 +1,20 @@
import assign from 'object.assign'; import assign from 'object.assign';
const providerHash = {
facebook: ({ id }) => id,
twitter: ({ username }) => username,
linkedin({ _json }) {
return _json && _json.publicProfileUrl || null;
},
google: ({ id }) => id
};
export function getUsernameFromProvider(provider, profile) {
return typeof providerHash[provider] === 'function' ?
providerHash[provider](profile) :
null;
}
// using es6 argument destructing // using es6 argument destructing
export function setProfileFromGithub( export function setProfileFromGithub(
user, user,

View File

@ -1,12 +1,5 @@
var path = require('path'), var cheerio = require('cheerio'),
// debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'), request = require('request'),
R = require('ramda'),
_ = require('lodash'),
fs = require('fs'),
MDNlinks = require('../../seed/bonfireMDNlinks'), MDNlinks = require('../../seed/bonfireMDNlinks'),
resources = require('./resources.json'), resources = require('./resources.json'),
nonprofits = require('../../seed/nonprofits.json'); nonprofits = require('../../seed/nonprofits.json');
@ -14,33 +7,7 @@ var path = require('path'),
/** /**
* Cached values * Cached values
*/ */
var allNonprofitNames, var allNonprofitNames;
challengeMap, challengeMapForDisplay, challengeMapWithIds,
challengeMapWithNames, allChallengeIds,
challengeMapWithDashedNames;
(function() {
if (!challengeMap) {
var localChallengeMap = {};
var files = fs.readdirSync(
path.join(__dirname, '../../seed/challenges')
);
var keyCounter = 0;
files = files.map(function(file) {
return require(
path.join(__dirname, '../../seed/challenges/' + file)
);
});
files = files.sort(function(a, b) {
return a.order - b.order;
});
files.forEach(function(file) {
localChallengeMap[keyCounter++] = file;
});
challengeMap = _.cloneDeep(localChallengeMap);
}
})();
module.exports = { module.exports = {
dasherize: function dasherize(name) { dasherize: function dasherize(name) {
@ -59,75 +26,6 @@ module.exports = {
.trim(); .trim();
}, },
getChallengeMapForDisplay: function() {
if (!challengeMapForDisplay) {
challengeMapForDisplay = {};
Object.keys(challengeMap).forEach(function(key) {
challengeMapForDisplay[key] = {
name: challengeMap[key].title,
dashedName: challengeMap[key].title.replace(/\s/g, '-'),
challenges: challengeMap[key].challenges,
completedCount: challengeMap[key].challenges
};
});
}
return challengeMapForDisplay;
},
getChallengeMapWithIds: function() {
if (!challengeMapWithIds) {
challengeMapWithIds = {};
Object.keys(challengeMap).forEach(function(key) {
var onlyIds = challengeMap[key].challenges.map(function(elem) {
return elem.id;
});
challengeMapWithIds[key] = onlyIds;
});
}
return challengeMapWithIds;
},
allChallengeIds: function() {
if (!allChallengeIds) {
allChallengeIds = [];
Object.keys(this.getChallengeMapWithIds()).forEach(function(key) {
allChallengeIds.push(challengeMapWithIds[key]);
});
allChallengeIds = R.flatten(allChallengeIds);
}
return allChallengeIds;
},
getChallengeMapWithNames: function() {
if (!challengeMapWithNames) {
challengeMapWithNames = {};
Object.keys(challengeMap).
forEach(function(key) {
var onlyNames = challengeMap[key].challenges.map(function(elem) {
return elem.name;
});
challengeMapWithNames[key] = onlyNames;
});
}
return challengeMapWithNames;
},
getChallengeMapWithDashedNames: function() {
if (!challengeMapWithDashedNames) {
challengeMapWithDashedNames = {};
Object.keys(challengeMap).
forEach(function(key) {
var onlyNames = challengeMap[key].challenges.map(function(elem) {
return elem.dashedName;
});
challengeMapWithDashedNames[key] = onlyNames;
});
}
return challengeMapWithDashedNames;
},
randomPhrase: function() { randomPhrase: function() {
return resources.phrases[ return resources.phrases[
Math.floor(Math.random() * resources.phrases.length) Math.floor(Math.random() * resources.phrases.length)

View File

@ -51,3 +51,10 @@ exports.ifNoUserSend = function ifNoUserSend(sendThis) {
return res.status(200).send(sendThis); return res.status(200).send(sendThis);
}; };
}; };
exports.ifNoUser401 = function ifNoUser401(req, res, next) {
if (req.user) {
return next();
}
return res.status(401).end();
};

View File

@ -2,7 +2,7 @@ extends ../layout
block content block content
script. script.
var challengeName = 'Account View' var challengeName = 'Account View'
.panel.panel-info(ng-controller="profileValidationController") .panel.panel-info
.panel-heading.text-center Manage your account here .panel-heading.text-center Manage your account here
.panel-body .panel-body
.row .row
@ -14,28 +14,28 @@ block content
else else
a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/link/github') a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/link/github')
i.fa.fa-github i.fa.fa-github
| Update my profile from Github | Update my profile from GitHub
//if (!user.twitter) if (!user.twitter)
// .col-xs-12 .col-xs-12
// a.btn.btn-lg.btn-block.btn-twitter.btn-link-social(href='/link/twitter') a.btn.btn-lg.btn-block.btn-twitter.btn-link-social(href='/link/twitter')
// i.fa.fa-twitter i.fa.fa-twitter
// | Add my Twitter to my profile | Add my Twitter to my profile
//if (!user.facebook) if (!user.facebook)
// .col-xs-12 .col-xs-12
// a.btn.btn-lg.btn-block.btn-facebook.btn-link-social(href='/link/facebook') a.btn.btn-lg.btn-block.btn-facebook.btn-link-social(href='/link/facebook')
// i.fa.fa-facebook i.fa.fa-facebook
// | Add my Facebook to my profile | Add my Facebook to my profile
//if (!user.linkedin) if (!user.linkedin)
// .col-xs-12 .col-xs-12
// a.btn.btn-lg.btn-block.btn-linkedin.btn-link-social(href='/link/linkedin') a.btn.btn-lg.btn-block.btn-linkedin.btn-link-social(href='/link/linkedin')
// i.fa.fa-linkedin i.fa.fa-linkedin
// | Add my LinkedIn to my profile | Add my LinkedIn to my profile
//if (!user.google) if (!user.google)
// .col-xs-12 .col-xs-12
// a.btn.btn-lg.btn-block.btn-google-plus.btn-link-social(href='/link/google') a.btn.btn-lg.btn-block.btn-google-plus.btn-link-social(href='/link/google')
// i.fa.fa-google-plus i.fa.fa-google-plus
// | Add my Google+ to my profile | Add my Google+ to my profile
.big-spacer .big-spacer
.col-xs-12 .col-xs-12
a.btn.btn-lg.btn-block.btn-warning.btn-link-social(href='/logout') a.btn.btn-lg.btn-block.btn-warning.btn-link-social(href='/logout')
@ -72,4 +72,4 @@ block content
input(type='hidden', name='_csrf', value=_csrf) input(type='hidden', name='_csrf', value=_csrf)
button.btn.btn-danger.btn-block(type='submit') button.btn.btn-danger.btn-block(type='submit')
span.ion-trash-b span.ion-trash-b
| Yes, Delete my account | Yes, delete my account

View File

@ -24,15 +24,15 @@ block content
img.img-center.img-responsive.public-profile-img(src='https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png') img.img-center.img-responsive.public-profile-img(src='https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png')
h1.text-center.negative-5.profile-social-icons h1.text-center.negative-5.profile-social-icons
if (twitter) if (twitter)
a.fa.fa-twitter-square.text-primary(title="@#{username}'s Twitter Profile", href=twitter, target='_blank') a.fa.fa-twitter-square.text-primary(title="@#{username}'s Twitter Profile", href='https://twitter.com/' + twitter, target='_blank')
if (github) if (github)
a.fa.fa-github-square.text-primary(title="@#{username}'s GitHub Profile", href=github, target='_blank') a.fa.fa-github-square.text-primary(title="@#{username}'s GitHub Profile", href=github, target='_blank')
if (linkedin) if (linkedin)
a.fa.fa-linkedin-square.text-primary(title="@#{username}'s LinkedIn Profile", href=linkedin, target='_blank') a.fa.fa-linkedin-square.text-primary(title="@#{username}'s LinkedIn Profile", href=linkedin, target='_blank')
if (facebook) if (facebook)
a.fa.fa-facebook-square.text-primary(title="@#{username}'s Facebook Profile", href=facebook, target='_blank') a.fa.fa-facebook-square.text-primary(title="@#{username}'s Facebook Profile", href='https://facebook.com/' + facebook, target='_blank')
if (google) if (google)
a.fa.fa-google-square.text-primary(title="@#{username}'s Google Profile", href=google, target='_blank') a.fa.fa-google-plus-square.text-primary(title="@#{username}'s Google Profile", href='https://plus.google.com/' + google, target='_blank')
.visible-md.visible-lg .visible-md.visible-lg
.col-xs-12.col-sm-12.col-md-4.text-justify .col-xs-12.col-sm-12.col-md-4.text-justify
h1.flat-top.wrappable= name h1.flat-top.wrappable= name
@ -94,3 +94,21 @@ block content
td.col-xs-2= moment(challenge.completedDate, 'x').format("MMM DD, YYYY") td.col-xs-2= moment(challenge.completedDate, 'x').format("MMM DD, YYYY")
td.col-xs-6 td.col-xs-6
a(href=challenge.solution, target='_blank') View my solution a(href=challenge.solution, target='_blank') View my solution
if (user && user.username === username)
br
if (bonfires.length > 0)
.col-sm-12
table.table.table-striped
thead
tr
th.col-xs-4 Bonfire
th.col-xs-2 Completed
th.col-xs-6 Solution
for bonfire in bonfires
tr
td.col-xs-4
a(href='/challenges/' + bonfire.name, target='_blank')= bonfire.name
td.col-xs-2= moment(bonfire.completedDate, 'x').format("MMM DD, YYYY")
td.col-xs-6
pre.wrappable= bonfire.solution
br

View File

@ -85,7 +85,9 @@ block content
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10 .hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.faded.large-p.negative-10 li.faded.large-p.negative-10
a(href="/challenges/#{challenge.dashedName}")= challenge.title a(href="/challenges/#{challenge.dashedName}")
span.capitalize= challenge.type + ': '
span= challenge.title
else else
.row .row
@ -93,7 +95,9 @@ block content
span.negative-10 span.negative-10
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.large-p.negative-10 li.large-p.negative-10
a(href="/challenges/#{challenge.dashedName}")= challenge.title a(href="/challenges/#{challenge.dashedName}")
span.capitalize= challenge.type + ': '
span= challenge.title
//#announcementModal.modal(tabindex='-1') //#announcementModal.modal(tabindex='-1')
// .modal-dialog.animated.fadeInUp.fast-animation // .modal-dialog.animated.fadeInUp.fast-animation

View File

@ -18,7 +18,7 @@ block content
.row(ng-controller="pairedWithController") .row(ng-controller="pairedWithController")
.col-xs-12.col-sm-12.col-md-4.col-lg-3 .col-xs-12.col-sm-12.col-md-4.col-lg-3
.scroll-locker //.scroll-locker(id = "scroll-locker")
#testCreatePanel.well #testCreatePanel.well
h3.text-center.negative-10= name h3.text-center.negative-10= name
.positive-15.positive-15-bottom .positive-15.positive-15-bottom

View File

@ -21,7 +21,7 @@ block content
script(src='/js/lib/codemirror/addon/emmet/emmet.js') script(src='/js/lib/codemirror/addon/emmet/emmet.js')
.row.courseware-height .row.courseware-height
.col-xs-12.col-sm-12.col-md-3.col-lg-3 .col-xs-12.col-sm-12.col-md-3.col-lg-3
.scroll-locker //.scroll-locker(id = "scroll-locker")
.innerMarginFix(style = "width: 99%;") .innerMarginFix(style = "width: 99%;")
.well .well
.row .row

View File

@ -16,7 +16,7 @@ block content
script(type='text/javascript', src='/js/lib/coursewares/sandbox.js') script(type='text/javascript', src='/js/lib/coursewares/sandbox.js')
.row(ng-controller="pairedWithController") .row(ng-controller="pairedWithController")
.col-xs-12.col-sm-12.col-md-4.col-lg-3 .col-xs-12.col-sm-12.col-md-4.col-lg-3
.scroll-locker //.scroll-locker(id = "scroll-locker")
.innerMarginFix(style = "width: 99%;") .innerMarginFix(style = "width: 99%;")
#testCreatePanel.well #testCreatePanel.well
h3.text-center.negative-10= name h3.text-center.negative-10= name

View File

@ -9,7 +9,7 @@
a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-install-Screenhero' target='_blank') Screenhero a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-install-Screenhero' target='_blank') Screenhero
| . | .
h3 Other campers may then message you about pair programming. h3 Other campers may then message you about pair programming.
a.btn.btn-lg.btn-primary.btn-block(href='https://gitter.im/FreeCodeCamp/LetsPair', data-dismiss='modal', aria-hidden='true' target='_blank') Take me to the pair programming room a.btn.btn-lg.btn-primary.btn-block.close-modal(href='https://gitter.im/FreeCodeCamp/LetsPair', target='_blank') Take me to the pair programming room
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
#issue-modal.modal(tabindex='-1') #issue-modal.modal(tabindex='-1')
@ -30,11 +30,11 @@
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body.text-center .modal-body.text-center
h3 Remember to use &thinsp; h3 Remember to use &thinsp;
a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank') Read-Search-Ask a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck', target='_blank') Read-Search-Ask
| . | .
h3 If you've already read the errors and searched Google, you should ask for help. h3 If you've already read the errors and searched Google, you should ask for help.
h3 This will take you to our help room. h3 This will take you to our help room.
a.btn.btn-lg.btn-primary.btn-block(href='https://gitter.im/FreeCodeCamp/LetsPair', data-dismiss='modal', aria-hidden='true' target='_blank') Take me to the help room a.btn.btn-lg.btn-primary.btn-block.close-modal(href='https://gitter.im/FreeCodeCamp/help', target='_blank') Take me to the help room
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
#reset-modal.modal(tabindex='-1') #reset-modal.modal(tabindex='-1')

View File

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

View File

@ -3,7 +3,7 @@
a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') &nbsp;Blog&nbsp;&nbsp; a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') &nbsp;Blog&nbsp;&nbsp;
a.ion-social-github(href="http://github.com/freecodecamp", target='_blank') &nbsp;GitHub&nbsp;&nbsp; a.ion-social-github(href="http://github.com/freecodecamp", target='_blank') &nbsp;GitHub&nbsp;&nbsp;
a.ion-social-twitch-outline(href="/twitch") &thinsp;Twitch&nbsp; a.ion-social-twitch-outline(href="/twitch") &thinsp;Twitch&nbsp;
a.ion-social-linkedin(href="/linkedin") &thinsp;LinkedIn&nbsp; a.ion-social-linkedin(href="https://www.linkedin.com/edu/school?id=166029", target='_blank') &thinsp;LinkedIn&nbsp;
a.ion-social-facebook(href="//facebook.com/freecodecamp") &nbsp;Facebook&nbsp;&nbsp; a.ion-social-facebook(href="//facebook.com/freecodecamp") &nbsp;Facebook&nbsp;&nbsp;
a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') &nbsp;Twitter&nbsp;&nbsp; a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank') &nbsp;Twitter&nbsp;&nbsp;
a.ion-locked(href="//github.com/FreeCodeCamp/freecodecamp/wiki/Free-Code-Camp's-Privacy-Policy") &nbsp;Privacy&nbsp;&nbsp; a.ion-locked(href="//github.com/FreeCodeCamp/freecodecamp/wiki/Free-Code-Camp's-Privacy-Policy") &nbsp;Privacy&nbsp;&nbsp;
@ -14,7 +14,7 @@
span.sr-only Free Code Camp on GitHub span.sr-only Free Code Camp on GitHub
a.ion-social-twitch-outline(href="/twitch") a.ion-social-twitch-outline(href="/twitch")
span.sr-only Free Code Camp Live Pair Programming on Twitch.tv span.sr-only Free Code Camp Live Pair Programming on Twitch.tv
a.ion-social-linkedin(href="/linkedin") a.ion-social-linkedin(href="https://www.linkedin.com/edu/school?id=166029", target='_blank')
span.sr-only Free Code Camp LinkedIn Alumni Network span.sr-only Free Code Camp LinkedIn Alumni Network
a.ion-social-facebook(href="//facebook.com/freecodecamp") a.ion-social-facebook(href="//facebook.com/freecodecamp")
span.sr-only Free Code Camp local groups on Facebook span.sr-only Free Code Camp local groups on Facebook

View File

@ -19,7 +19,7 @@ script.
script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.js") script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.js")
script. script.
if (typeof window.angular === 'undefined') { if (typeof window.angular === 'undefined') {
document.write('<script src="/bower_components/angular/angular.min.js"><\/script>') document.write('<script src="/bower_components/angular/angular.min.js"><\/script>');
} }
script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap.min.js") script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap.min.js")
script. script.

View File

@ -101,7 +101,6 @@ block content
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 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 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.
.spacer .spacer
.row .row
.col-xs-12.col-sm-6 .col-xs-12.col-sm-6

View File

@ -103,7 +103,7 @@ script.
"</a>" + "</a>" +
"</div>" + "</div>" +
"<div class='story-byline col-xs-12 wrappable'>" + "<div class='story-byline col-xs-12 wrappable'>" +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/news/" + linkedName + "'>more info.more info...</a> · " + "<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/news/" + linkedName + "'>more info...</a> · " +
rank + (rank > 1 ? " points" : " point") + " · posted " + rank + (rank > 1 ? " points" : " point") + " · posted " +
moment(data[i].timePosted).fromNow() + moment(data[i].timePosted).fromNow() +
" by <a href='/" + data[i].author.username + "'>@" + data[i].author.username + " by <a href='/" + data[i].author.username + "'>@" + data[i].author.username +