Merge branch 'staging' of github.com:FreeCodeCamp/freecodecamp into staging
This commit is contained in:
@ -135,7 +135,8 @@ exports.returnCurrentChallenge = function(req, res, next) {
|
|||||||
}
|
}
|
||||||
var nameString = req.user.currentChallenge.challengeName.trim()
|
var nameString = req.user.currentChallenge.challengeName.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s/g, '-');
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/[^a-z0-9\-]/gi, '');
|
||||||
req.user.save(function(err) {
|
req.user.save(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
@ -147,7 +148,10 @@ exports.returnCurrentChallenge = function(req, res, next) {
|
|||||||
exports.returnIndividualChallenge = function(req, res, next) {
|
exports.returnIndividualChallenge = function(req, res, next) {
|
||||||
var dashedName = req.params.challengeName;
|
var dashedName = req.params.challengeName;
|
||||||
|
|
||||||
var challengeName = dashedName.replace(/\-/g, ' ');
|
var challengeName = dashedName.replace(/\-/g, ' ')
|
||||||
|
.split(' ')
|
||||||
|
.slice(1)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
Challenge.find({'name': new RegExp(challengeName, 'i')},
|
Challenge.find({'name': new RegExp(challengeName, 'i')},
|
||||||
function(err, challengeFromMongo) {
|
function(err, challengeFromMongo) {
|
||||||
@ -164,7 +168,10 @@ exports.returnIndividualChallenge = function(req, res, next) {
|
|||||||
}
|
}
|
||||||
var challenge = challengeFromMongo.pop();
|
var challenge = challengeFromMongo.pop();
|
||||||
// Redirect to full name if the user only entered a partial
|
// Redirect to full name if the user only entered a partial
|
||||||
var dashedNameFull = challenge.name.toLowerCase().replace(/\s/g, '-');
|
var dashedNameFull = challenge.name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/[^a-z0-9\-]/gi, '');
|
||||||
if (dashedNameFull !== dashedName) {
|
if (dashedNameFull !== dashedName) {
|
||||||
return res.redirect('../challenges/' + dashedNameFull);
|
return res.redirect('../challenges/' + dashedNameFull);
|
||||||
} else {
|
} else {
|
||||||
@ -317,44 +324,62 @@ exports.completedBonfire = function (req, res, next) {
|
|||||||
req.user.uncompletedChallenges.splice(index, 1);
|
req.user.uncompletedChallenges.splice(index, 1);
|
||||||
}
|
}
|
||||||
pairedWith = pairedWith.pop();
|
pairedWith = pairedWith.pop();
|
||||||
|
if (pairedWith) {
|
||||||
|
|
||||||
index = pairedWith.uncompletedChallenges.indexOf(challengeId);
|
index = pairedWith.uncompletedChallenges.indexOf(challengeId);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
pairedWith.progressTimestamps.push(Date.now() || 0);
|
pairedWith.progressTimestamps.push(Date.now() || 0);
|
||||||
pairedWith.uncompletedChallenges.splice(index, 1);
|
pairedWith.uncompletedChallenges.splice(index, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pairedWith.completedChallenges.push({
|
||||||
|
_id: challengeId,
|
||||||
|
name: challengeName,
|
||||||
|
completedWith: req.user._id,
|
||||||
|
completedDate: isCompletedDate,
|
||||||
|
solution: isSolution,
|
||||||
|
challengeType: 5
|
||||||
|
});
|
||||||
|
|
||||||
|
req.user.completedChallenges.push({
|
||||||
|
_id: challengeId,
|
||||||
|
name: challengeName,
|
||||||
|
completedWith: pairedWith._id,
|
||||||
|
completedDate: isCompletedDate,
|
||||||
|
solution: isSolution,
|
||||||
|
challengeType: 5
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// User said they paired, but pair wasn't found
|
||||||
|
req.user.completedChallenges.push({
|
||||||
|
_id: challengeId,
|
||||||
|
name: challengeName,
|
||||||
|
completedWith: null,
|
||||||
|
completedDate: isCompletedDate,
|
||||||
|
solution: isSolution,
|
||||||
|
challengeType: 5
|
||||||
|
});
|
||||||
|
|
||||||
pairedWith.completedChallenges.push({
|
|
||||||
_id: challengeId,
|
|
||||||
name: challengeName,
|
|
||||||
completedWith: req.user._id,
|
|
||||||
completedDate: isCompletedDate,
|
|
||||||
solution: isSolution,
|
|
||||||
challengeType: 5
|
|
||||||
});
|
|
||||||
|
|
||||||
req.user.completedChallenges.push({
|
|
||||||
_id: challengeId,
|
|
||||||
name: challengeName,
|
|
||||||
completedWith: pairedWith._id,
|
|
||||||
completedDate: isCompletedDate,
|
|
||||||
solution: isSolution,
|
|
||||||
challengeType: 5
|
|
||||||
});
|
|
||||||
|
|
||||||
req.user.save(function (err, user) {
|
req.user.save(function (err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
pairedWith.save(function (err, paired) {
|
if (pairedWith) {
|
||||||
if (err) {
|
pairedWith.save(function (err, paired) {
|
||||||
return next(err);
|
if (err) {
|
||||||
}
|
return next(err);
|
||||||
if (user && paired) {
|
}
|
||||||
|
if (user && paired) {
|
||||||
|
res.send(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (user) {
|
||||||
res.send(true);
|
res.send(true);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@ module.exports = {
|
|||||||
var noDuplicatedChallenges = R.uniq(completedList);
|
var noDuplicatedChallenges = R.uniq(completedList);
|
||||||
|
|
||||||
|
|
||||||
var challengeList = resources.allChallenges();
|
var challengeList = resources.getChallengeMapForDisplay();
|
||||||
var completedChallengeList = noDuplicatedChallenges
|
var completedChallengeList = noDuplicatedChallenges
|
||||||
.map(function(challenge) {
|
.map(function(challenge) {
|
||||||
return challenge._id;
|
return challenge._id;
|
||||||
|
@ -5,7 +5,8 @@ var R = require('ramda'),
|
|||||||
exports.returnIndividualFieldGuide = function(req, res, next) {
|
exports.returnIndividualFieldGuide = function(req, res, next) {
|
||||||
var dashedName = req.params.fieldGuideName;
|
var dashedName = req.params.fieldGuideName;
|
||||||
|
|
||||||
var fieldGuideName = dashedName.replace(/\-/g, ' ');
|
var fieldGuideName = dashedName.replace(/\-/g, ' ')
|
||||||
|
.replace(/[^a-z0-9\s]/gi, '');
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
var completed = req.user.completedFieldGuides;
|
var completed = req.user.completedFieldGuides;
|
||||||
@ -39,7 +40,9 @@ exports.returnIndividualFieldGuide = function(req, res, next) {
|
|||||||
|
|
||||||
var fieldGuide = R.head(fieldGuideFromMongo);
|
var fieldGuide = R.head(fieldGuideFromMongo);
|
||||||
var dashedNameFull =
|
var dashedNameFull =
|
||||||
fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
|
fieldGuide.name.toLowerCase()
|
||||||
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/[^a-z0-9\-]/gi, '');
|
||||||
|
|
||||||
if (dashedNameFull !== dashedName) {
|
if (dashedNameFull !== dashedName) {
|
||||||
return res.redirect('../field-guide/' + dashedNameFull);
|
return res.redirect('../field-guide/' + dashedNameFull);
|
||||||
@ -68,7 +71,7 @@ exports.showAllFieldGuides = function(req, res) {
|
|||||||
|
|
||||||
exports.returnNextFieldGuide = function(req, res, next) {
|
exports.returnNextFieldGuide = function(req, res, next) {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
return res.redirect('/field-guide/how-do-i-use-this-guide?');
|
return res.redirect('/field-guide/how-do-i-use-this-guide');
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayedFieldGuides =
|
var displayedFieldGuides =
|
||||||
@ -89,9 +92,11 @@ exports.returnNextFieldGuide = function(req, res, next) {
|
|||||||
].join('')
|
].join('')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return res.redirect('../field-guide/how-do-i-use-this-guide?');
|
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
||||||
}
|
}
|
||||||
var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
|
var nameString = fieldGuide.name.toLowerCase()
|
||||||
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/[^a-z0-9\-]/gi, '');
|
||||||
return res.redirect('../field-guide/' + nameString);
|
return res.redirect('../field-guide/' + nameString);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,7 @@ var async = require('async'),
|
|||||||
* Cached values
|
* Cached values
|
||||||
*/
|
*/
|
||||||
var allFieldGuideIds, allFieldGuideNames, allNonprofitNames,
|
var allFieldGuideIds, allFieldGuideNames, allNonprofitNames,
|
||||||
challengeMap, challengeMapWithIds,
|
challengeMap, challengeMapForDisplay, challengeMapWithIds,
|
||||||
challengeMapWithNames, allChallengeIds, allChallenges;
|
challengeMapWithNames, allChallengeIds, allChallenges;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,13 +67,24 @@ Array.zip = function(left, right, combinerFunction) {
|
|||||||
});
|
});
|
||||||
challengeMap = _.cloneDeep(localChallengeMap);
|
challengeMap = _.cloneDeep(localChallengeMap);
|
||||||
}
|
}
|
||||||
//todo remove this debug
|
|
||||||
debug(challengeMap);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
|
getChallengeMapForDisplay: function() {
|
||||||
|
if (!challengeMapForDisplay) {
|
||||||
|
challengeMapForDisplay = {};
|
||||||
|
Object.keys(challengeMap).forEach(function(key) {
|
||||||
|
challengeMapForDisplay[key] = {
|
||||||
|
name: challengeMap[key].name,
|
||||||
|
challenges: challengeMap[key].challenges
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return challengeMapForDisplay;
|
||||||
|
},
|
||||||
|
|
||||||
getChallengeMapWithIds: function() {
|
getChallengeMapWithIds: function() {
|
||||||
if (!challengeMapWithIds) {
|
if (!challengeMapWithIds) {
|
||||||
challengeMapWithIds = {};
|
challengeMapWithIds = {};
|
||||||
|
@ -239,10 +239,28 @@ var runTests = function(err, data) {
|
|||||||
function showCompletion() {
|
function showCompletion() {
|
||||||
var time = Math.floor(Date.now()) - started;
|
var time = Math.floor(Date.now()) - started;
|
||||||
ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time +', Attempts: ' + attempts);
|
ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time +', Attempts: ' + attempts);
|
||||||
$('#complete-courseware-dialog').modal('show');
|
var bonfireSolution = myCodeMirror.getValue();
|
||||||
$('#complete-courseware-dialog').keydown(function(e) {
|
var didCompleteWith = $('#completed-with').val() || null;
|
||||||
if (e.ctrlKey && e.keyCode == 13) {
|
$.post(
|
||||||
$('#next-courseware-button').click();
|
'/completed-bonfire/',
|
||||||
|
{
|
||||||
|
challengeInfo: {
|
||||||
|
challengeId: challenge_Id,
|
||||||
|
challengeName: challenge_Name,
|
||||||
|
completedWith: didCompleteWith,
|
||||||
|
challengeType: challengeType,
|
||||||
|
solution: bonfireSolution
|
||||||
|
}
|
||||||
|
}, function(res) {
|
||||||
|
if (res) {
|
||||||
|
$('#complete-courseware-dialog').modal('show');
|
||||||
|
$('#complete-courseware-dialog').keydown(function (e) {
|
||||||
|
if (e.ctrlKey && e.keyCode == 13) {
|
||||||
|
$('#next-courseware-button').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -215,29 +215,10 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case challengeTypes.BONFIRE:
|
case challengeTypes.BONFIRE:
|
||||||
var bonfireSolution = myCodeMirror.getValue();
|
window.location.href = '/challenges/next-challenge';
|
||||||
var didCompleteWith = $('#completed-with').val() || null;
|
|
||||||
$.post(
|
|
||||||
'/completed-bonfire/',
|
|
||||||
{
|
|
||||||
challengeInfo: {
|
|
||||||
challengeId: challenge_Id,
|
|
||||||
challengeName: challenge_Name,
|
|
||||||
completedWith: didCompleteWith,
|
|
||||||
challengeType: challengeType,
|
|
||||||
solution: bonfireSolution
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(res) {
|
|
||||||
if (res) {
|
|
||||||
window.location.href = '/challenges/next-challenge';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "Waypoint: Basic HTML5 and CSS",
|
"name": "Basic HTML5 and CSS",
|
||||||
"order" : 0.002,
|
"order" : 0.002,
|
||||||
"challenges": [
|
"challenges": [
|
||||||
{
|
{
|
||||||
|
@ -23,23 +23,25 @@ block content
|
|||||||
span.text-primary #{daysRunning}  
|
span.text-primary #{daysRunning}  
|
||||||
| days ago.
|
| days ago.
|
||||||
.spacer
|
.spacer
|
||||||
h3.negative-15
|
.negative-15
|
||||||
ol
|
for challengeBlock in challengeList
|
||||||
for challenge in challengeList
|
h3 #{challengeBlock.name}
|
||||||
if completedChallengeList.indexOf(challenge._id) > -1
|
ol
|
||||||
.row
|
for challenge in challengeBlock.challenges
|
||||||
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center
|
if completedChallengeList.indexOf(challenge._id) > -1
|
||||||
.col-xs-12.col-sm-9.col-md-10
|
.row
|
||||||
li.faded
|
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p
|
||||||
a(href="/challenges/#{challenge.name}")= challenge.name
|
.col-xs-12.col-sm-9.col-md-10
|
||||||
|
li.faded.large-p
|
||||||
|
a(href="/challenges/#{challenge.name}")= challenge.name
|
||||||
|
|
||||||
else
|
else
|
||||||
.row
|
.row
|
||||||
.hidden-xs.col-sm-3.col-md-2
|
.hidden-xs.col-sm-3.col-md-2
|
||||||
span
|
span
|
||||||
.col-xs-12.col-sm-9.col-md-10
|
.col-xs-12.col-sm-9.col-md-10
|
||||||
li
|
li.large-p
|
||||||
a(href="/challenges/#{challenge.name}")= challenge.name
|
a(href="/challenges/#{challenge.name}")= challenge.name
|
||||||
|
|
||||||
h2
|
h2
|
||||||
span.ion-ios-heart Nonprofit Projects (800 hours of real-world experience)*
|
span.ion-ios-heart Nonprofit Projects (800 hours of real-world experience)*
|
||||||
|
@ -16,7 +16,7 @@ block content
|
|||||||
script(type='text/javascript', src='/js/lib/jailed/jailed.js')
|
script(type='text/javascript', src='/js/lib/jailed/jailed.js')
|
||||||
script(type='text/javascript', src='/js/lib/coursewares/sandbox.js')
|
script(type='text/javascript', src='/js/lib/coursewares/sandbox.js')
|
||||||
|
|
||||||
.row
|
.row(ng-controller="pairedWithController")
|
||||||
.col-xs-12.col-sm-12.col-md-4.bonfire-top
|
.col-xs-12.col-sm-12.col-md-4.bonfire-top
|
||||||
#testCreatePanel
|
#testCreatePanel
|
||||||
h1#bonfire-name.text-center= name
|
h1#bonfire-name.text-center= name
|
||||||
@ -79,6 +79,18 @@ block content
|
|||||||
span.ion-arrow-up-b
|
span.ion-arrow-up-b
|
||||||
| Less information
|
| Less information
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
form.form-horizontal(novalidate='novalidate', name='completedWithForm')
|
||||||
|
.form-group.text-center
|
||||||
|
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2.animated.fadeIn
|
||||||
|
// extra field to distract password tools like lastpass from injecting css into our username field
|
||||||
|
input.form-control(ng-show="false")
|
||||||
|
label(for="existingUser") If you're pairing with someone, enter their FreeCodeCamp username here
|
||||||
|
input.form-control#completed-with(name="existingUser", placeholder="Pair Username", existing-username='', ng-model="existingUser", autofocus)
|
||||||
|
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-cloak, ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0")
|
||||||
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| Username not found
|
||||||
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
|
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
|
||||||
.button-spacer
|
.button-spacer
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
@ -115,30 +127,18 @@ block content
|
|||||||
textarea#codeEditor(autofocus=true, style='display: none;')
|
textarea#codeEditor(autofocus=true, style='display: none;')
|
||||||
script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.2.js')
|
script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.2.js')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#complete-courseware-dialog.modal(tabindex='-1')
|
#complete-courseware-dialog.modal(tabindex='-1')
|
||||||
.modal-dialog.animated.zoomIn.fast-animation
|
.modal-dialog.animated.zoomIn.fast-animation
|
||||||
.modal-content
|
.modal-content
|
||||||
.modal-header.challenge-list-header= compliment
|
.modal-header.challenge-list-header= compliment
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
||||||
.modal-body(ng-controller="pairedWithController")
|
.modal-body
|
||||||
.text-center
|
.text-center
|
||||||
.animated.zoomInDown.delay-half
|
.animated.zoomInDown.delay-half
|
||||||
span.completion-icon.ion-checkmark-circled.text-primary
|
span.completion-icon.ion-checkmark-circled.text-primary
|
||||||
- if (user)
|
- if (user)
|
||||||
form.form-horizontal(novalidate='novalidate', name='completedWithForm')
|
|
||||||
.form-group.text-center
|
|
||||||
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2.animated.fadeIn
|
|
||||||
// extra field to distract password tools like lastpass from injecting css into our username field
|
|
||||||
input.form-control(ng-show="false")
|
|
||||||
input.form-control#completed-with(name="existingUser", placeholder="If you paired, enter your pair's username here", existing-username='', ng-model="existingUser", autofocus)
|
|
||||||
.col-xs-10.col-xs-offset-1.col-sm-8.col-sm-offset-2.col-md-8.col-md-offset-2(ng-cloak, ng-show="completedWithForm.$error.exists && !completedWithForm.existingUser.$pristine && existingUser.length > 0")
|
|
||||||
alert(type='danger')
|
|
||||||
span.ion-close-circled
|
|
||||||
| Username not found
|
|
||||||
|
|
||||||
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next bonfire (ctrl + enter)
|
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf) Go to my next challenge (ctrl + enter)
|
||||||
|
|
||||||
|
|
||||||
- if (user.progressTimestamps.length > 2)
|
- if (user.progressTimestamps.length > 2)
|
||||||
|
Reference in New Issue
Block a user