Building out new challenge framework to allow controller to determine view type for challenge

This commit is contained in:
Nathan Leniz
2015-02-13 20:55:49 -05:00
parent 0eededde36
commit b5c0ad9942
14 changed files with 515 additions and 58 deletions

View File

@ -24,7 +24,6 @@ exports.index = function(req, res) {
details: '',
tests: [],
challengeSeed: '',
challengeEntryPoint: '',
cc: req.user ? req.user.bonfiresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
@ -105,7 +104,6 @@ exports.returnIndividualBonfire = function(req, res, next) {
details: bonfire.description.slice(1),
tests: bonfire.tests,
challengeSeed: bonfire.challengeSeed,
challengeEntryPoint: bonfire.challengeEntryPoint,
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
@ -131,7 +129,6 @@ exports.returnGenerator = function(req, res) {
details: null,
tests: null,
challengeSeed: null,
challengeEntryPoint: null,
bonfireHash: randomString()
});
};
@ -160,7 +157,6 @@ exports.testBonfire = function(req, res) {
bonfireTests = req.body.tests,
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireEntryPoint = req.body.challengeEntryPoint,
bonfireChallengeSeed = req.body.challengeSeed;
bonfireTests = bonfireTests.split('\r\n');
bonfireDescription = bonfireDescription.split('\r\n');
@ -176,7 +172,6 @@ exports.testBonfire = function(req, res) {
details: bonfireDescription.slice(1),
tests: bonfireTests,
challengeSeed: bonfireChallengeSeed,
challengeEntryPoint: bonfireEntryPoint,
cc: req.user ? req.user.bonfiresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
@ -202,7 +197,6 @@ exports.generateChallenge = function(req, res) {
bonfireTests = req.body.tests,
bonfireDifficulty = req.body.difficulty,
bonfireDescription = req.body.description,
bonfireEntryPoint = req.body.challengeEntryPoint,
bonfireChallengeSeed = req.body.challengeSeed;
bonfireTests = bonfireTests.split('\r\n');
bonfireDescription = bonfireDescription.split('\r\n');
@ -216,7 +210,6 @@ exports.generateChallenge = function(req, res) {
name: bonfireName,
difficulty: bonfireDifficulty,
description: bonfireDescription,
challengeEntryPoint: bonfireEntryPoint,
challengeSeed: bonfireChallengeSeed,
tests: bonfireTests
};

View File

@ -72,7 +72,8 @@ exports.returnIndividualCourseware = function(req, res, next) {
return res.redirect('../coursewares/' + dashedNameFull);
}
// Render the view for the user
var challengeType = {
0 : function() {
res.render('coursewares/show', {
title: courseware.name,
dashedName: dashedName,
@ -89,6 +90,66 @@ exports.returnIndividualCourseware = function(req, res, next) {
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
},
1 : function() {
res.render('coursewares/showJS', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment(),
});
},
2: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
cc: !!req.user,
points: req.user ? req.user.points : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
}
};
return challengeType[courseware.challengeType]();
//// Render the view for the user
//res.render('coursewares/show', {
// title: courseware.name,
// dashedName: dashedName,
// name: courseware.name,
// brief: courseware.description[0],
// details: courseware.description.slice(1),
// tests: courseware.tests,
// challengeSeed: courseware.challengeSeed,
// cc: !!req.user,
// points: req.user ? req.user.points : undefined,
// verb: resources.randomVerb(),
// phrase: resources.randomPhrase(),
// compliment: resources.randomCompliment(),
// coursewareHash: courseware._id,
// environment: resources.whichEnvironment()
//});
});
};

View File

@ -15,8 +15,7 @@ var bonfireSchema = new mongoose.Schema({
difficulty: String,
description: Array,
tests: Array,
challengeSeed: String,
challengeEntryPoint: String,
challengeSeed: String
});
module.exports = mongoose.model('Bonfire', bonfireSchema);

View File

@ -73,11 +73,10 @@ var editorValue;
var challengeSeed = challengeSeed || null;
var tests = tests || [];
var challengeEntryPoint = challengeEntryPoint || null;
if (challengeSeed !== null) {
editorValue = challengeSeed + '\n\n' + challengeEntryPoint;
editorValue = challengeSeed;
} else {
editorValue = nonChallengeValue;
}
@ -121,9 +120,7 @@ function bonfireExecute() {
userJavaScript = removeComments(userJavaScript);
userJavaScript = scrapeTests(userJavaScript);
// simple fix in case the user forgets to invoke their function
if (challengeEntryPoint && challengeSeed) {
//userJavaScript = challengeEntryPoint + ' ' + userJavaScript;
}
submit(userJavaScript, function(cls, message) {
if (cls) {
codeOutput.setValue(message.error);

View File

@ -0,0 +1,245 @@
var widgets = [];
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
lineNumbers: true,
mode: "javascript",
theme: 'monokai',
runnable: true,
lint: true,
matchBrackets: true,
autoCloseBrackets: true,
scrollbarStyle: 'null',
lineWrapping: true,
gutters: ["CodeMirror-lint-markers"],
onKeyEvent: doLinting
});
var editor = myCodeMirror;
editor.setSize("100%", "auto");
// Hijack tab key to enter two spaces intead
editor.setOption("extraKeys", {
Tab: function(cm) {
var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
cm.replaceSelection(spaces);
},
"Ctrl-Enter": function() {
bonfireExecute();
return false;
}
});
var attempts = 0;
if (attempts) {
attempts = 0;
}
// Default value for editor if one isn't provided in (i.e. a challenge)
var nonChallengeValue = '/*Welcome to Bonfire, Free Code Camp\'s future CoderByte replacement.\n' +
'Please feel free to use Bonfire as an in-browser playground and linting tool.\n' +
'Note that you can also write tests using Chai.js by using the keywords assert and expect */\n\n' +
'function test() {\n' +
' assert(2 !== 3, "2 is not equal to 3");\n' +
' return [1,2,3].map(function(elem) {\n' +
' return elem * elem;\n' +
' });\n' +
'}\n' +
'expect(test()).to.be.a("array");\n\n' +
'assert.deepEqual(test(), [1,4,9]);\n\n' +
'var foo = test();\n' +
'foo.should.be.a("array");\n\n' +
'test();\n';
var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), {
lineNumbers: false,
mode: "text",
theme: 'monokai',
readOnly: 'nocursor',
lineWrapping: true
});
codeOutput.setValue('/**\n' +
' * Your output will go here.\n' + ' * Console.log() -type statements\n' +
' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' +
' */');
codeOutput.setSize("100%", "100%");
var info = editor.getScrollInfo();
var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top;
if (info.top + info.clientHeight < after)
editor.scrollTo(null, after - info.clientHeight + 3);
var editorValue;
var challengeSeed = challengeSeed || null;
var tests = tests || [];
var allSeeds = '';
(function() {
challengeSeed.forEach(function(elem) {
allSeeds += elem + '\n';
});
})();
editorValue = allSeeds;
myCodeMirror.setValue(editorValue);
function doLinting () {
editor.operation(function () {
for (var i = 0; i < widgets.length; ++i)
editor.removeLineWidget(widgets[i]);
widgets.length = 0;
JSHINT(editor.getValue());
for (var i = 0; i < JSHINT.errors.length; ++i) {
var err = JSHINT.errors[i];
if (!err) continue;
var msg = document.createElement("div");
var icon = msg.appendChild(document.createElement("span"));
icon.innerHTML = "!!";
icon.className = "lint-error-icon";
msg.appendChild(document.createTextNode(err.reason));
msg.className = "lint-error";
widgets.push(editor.addLineWidget(err.line - 1, msg, {
coverGutter: false,
noHScroll: true
}));
}
});
};
$('#submitButton').on('click', function () {
bonfireExecute();
});
function bonfireExecute() {
attempts++;
ga('send', 'event', 'Challenge', 'ran-code', challengeName);
userTests= null;
$('#codeOutput').empty();
var userJavaScript = myCodeMirror.getValue();
userJavaScript = removeComments(userJavaScript);
userJavaScript = scrapeTests(userJavaScript);
// simple fix in case the user forgets to invoke their function
submit(userJavaScript, function(cls, message) {
if (cls) {
codeOutput.setValue(message.error);
runTests('Error', null);
} else {
codeOutput.setValue(message.output);
message.input = removeLogs(message.input);
runTests(null, message);
}
});
}
var userTests;
var testSalt = Math.random();
var scrapeTests = function(userJavaScript) {
// insert tests from mongo
for (var i = 0; i < tests.length; i++) {
userJavaScript += '\n' + tests[i];
}
var counter = 0;
var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/);
var match = regex.exec(userJavaScript);
while (match != null) {
var replacement = '//' + counter + testSalt;
userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length);
if (!userTests) {
userTests= [];
}
userTests.push({"text": match[0], "line": counter, "err": null});
counter++;
match = regex.exec(userJavaScript);
}
return userJavaScript;
};
function removeComments(userJavaScript) {
var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g);
return userJavaScript.replace(regex, '');
}
function removeLogs(userJavaScript) {
return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, '');
}
var pushed = false;
var createTestDisplay = function() {
if (pushed) {
userTests.pop();
}
for (var i = 0; i < userTests.length;i++) {
var test = userTests[i];
var testDoc = document.createElement("div");
if (test.err != null) {
$(testDoc)
.html("<div class='row'><div class='col-xs-1 text-center'><i class='ion-close-circled big-error-icon'></i></div><div class='col-xs-11 test-output wrappable'>" + test.text + "</div><div class='col-xs-11 test-output wrappable'>" + test.err + "</div></div><div class='ten-pixel-break'/>")
.prependTo($('#testSuite'))
} else {
$(testDoc)
.html("<div class='row'><div class='col-xs-1 text-center'><i class='ion-checkmark-circled big-success-icon'></i></div><div class='col-xs-11 test-output test-vertical-center wrappable'>" + test.text + "</div></div><div class='ten-pixel-break'/>")
.appendTo($('#testSuite'));
}
};
};
var expect = chai.expect;
var reassembleTest = function(test, data) {
var lineNum = test.line;
var regexp = new RegExp("\/\/" + lineNum + testSalt);
return data.input.replace(regexp, test.text);
};
var runTests = function(err, data) {
var allTestsPassed = true;
pushed = false;
$('#testSuite').children().remove();
if (err && userTests.length > 0) {
userTests= [{text:"Program Execution Failure", err: "No user tests were run."}];
createTestDisplay();
} else if (userTests) {
userTests.push(false);
pushed = true;
userTests.forEach(function(test, ix, arr){
try {
if (test) {
var output = eval(reassembleTest(test, data));
}
} catch(error) {
allTestsPassed = false;
console.log(error);
arr[ix].err = error.name + ":" + error.message;
}
});
if (allTestsPassed) {
allTestsPassed = false;
showCompletion();
}
}
};
function showCompletion() {
var time = Math.floor(Date.now() / 1000) - started;
ga('send', 'event', 'Challenge', 'solved', challengeName + ', Time: ' + time +', Attempts: ' + attempts);
$('#complete-courseware-dialog').modal('show');
$('#complete-courseware-dialog').keydown(function(e) {
if (e.ctrlKey && e.keyCode == 13) {
$('.next-bonfire-button').click();
}
});
}

View File

@ -66,6 +66,9 @@ $(document).ready(function() {
completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash);
});
$('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show');
});
$('#complete-bonfire-dialog').on('hidden.bs.modal', function() {
editor.focus();

View File

@ -157,9 +157,7 @@
"Truncate a string (first argument) if it is longer than the given maximum string length (second argument). Return the truncated string with a '...' ending.",
"Note that the three dots at the end add to the string length."
],
"challengeEntryPoint":"truncate('A-tisket a-tasket A green and yellow basket', 11);",
"challengeSeed":"function truncate(str, num) {\n // Clear out that junk in your trunk\r\n return str;\r\n}",
"challengeSeed":"function truncate(str, num) {\n // Clear out that junk in your trunk\r\n return str;\r\n}\n\ntruncate('A-tisket a-tasket A green and yellow basket', 11);",
"tests": [
"expect(truncate('A-tisket a-tasket A green and yellow basket', 11)).to.eqls('A-tisket...');",
"assert(truncate('A-tisket a-tasket A green and yellow basket', 'A-tisket a-tasket A green and yellow basket'.length) === 'A-tisket a-tasket A green and yellow basket', 'should not truncate if string is = length');",
@ -209,19 +207,6 @@
"assert.deepEqual(bouncer([false, null, 0]), [], 'should return empty array if all elements are falsey');"
]
},
{
"_id": "a8e512fbe388ac2f9198f0fa",
"name": "Where art thou",
"difficulty": "1.55",
"description": [
"Maketh a function that looks through a list (first argument) and returns an array of all objects that have equivalent property values (second argument)."
],
"challengeSeed": "function where(collection, source) {\n var arr = [];\r\n // What's in a name?\r\n return arr;\r\n}\n\nwhere([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' });",
"tests": [
"assert.deepEqual(where([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' }), [{ first: 'Tybalt', last: 'Capulet' }], 'should return an array of objects');",
"assert.deepEqual(where([{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }], { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1 }], 'should return with multiples');"
]
},
{
"_id": "a39963a4c10bc8b4d4f06d7e",
"name": "Seek and Destroy",
@ -231,13 +216,13 @@
],
"challengeSeed": "function destroyer(arr) {\n // Remove all the values\r\n return arr;\r\n}\n\ndestroyer([1, 2, 3, 1, 2, 3], 2, 3);",
"tests": [
"assert.strictEqual(destroyer([1, 2, 3, 1, 2, 3], 2, 3), [1, 1], 'should remove correct values from an array');",
"assert.strictEqual(destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3), [1, 5, 1], 'should remove correct values from an array');"
"assert.deepEqual(destroyer([1, 2, 3, 1, 2, 3], 2, 3), [1, 1], 'should remove correct values from an array');",
"assert.deepEqual(destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3), [1, 5, 1], 'should remove correct values from an array');"
]
},
{
"_id": "a24c1a4622e3c05097f71d67",
"name": "Where do I belong?",
"name": "Where do I belong",
"difficulty": "1.61",
"description": [
"Return the lowest index at which a value (second argument) should be inserted into a sorted array (first argument)."
@ -479,8 +464,7 @@
"Those methods are getFirstName(), getLastName(), getFullName(), setFirstName(), setLastName(), and setFullName().",
"These methods must be the only available means for interacting with the object."
],
"challengeSeed": "var Person = function(firstAndLast) {\n return firstAndLast;\r\n};\n\nvar bob = new Person('Bob Ross');",
"challengeEntryPoint": "var bob = new Person('Bob Ross');\nbob.getFullName();",
"challengeSeed": "var Person = function(firstAndLast) {\n return firstAndLast;\r\n};\n\nvar bob = new Person('Bob Ross');\n\nvar bob = new Person('Bob Ross');\nbob.getFullName();",
"tests": [
"expect(Object.keys(bob).length).to.eql(6);",
"expect(bob instanceof Person).to.be.true;",

View File

@ -1,8 +1,51 @@
[
{
"_id" : "bbbbbbbbbbbbbbbbbbbbbbbb",
"name": "Learn how Free Code Camp Works",
"difficulty": 0.00,
"description": [
"Watch this 1-minute video, or simply read this summary:",
"Welcome to Free Code Camp. We're a community of busy people learning to code.",
"We built this community because learning to code is hard. But anyone who can stay motivated can learn to code. And the best way to stay motivated is to code with friends.",
"To maximize accessibility, all our challenges are self-paced, browser-based, and free.",
"All of us start with the same 100 hours of interactive coding challenges. These cover Computer Science and databases. They also cover in-demand JavaScript tools like jQuery, Node.js and MongoDB.",
"Once we have a basic understanding of web development, we'll spend another 900 hours putting that theory into practice. We'll build full stack solutions for nonprofits.",
"By the end of this process, we'll be good at coding. We'll have a portfolio of apps with happy users to prove it. We'll also have an alumni network of fellow coders and nonprofits ready to serve as references.",
"If you make it through Free Code Camp, you will be able to get a coding job. There are far more job openings out there than there are qualified coders to fill them.",
"Also, for every pure coding job, there are at least 5 additional jobs that require some coding skills. So even if you decide not to pursue coding as a career, you'll still walk away with a valuable job skill.",
"There are 3 keys to succeeding in our community: do the challenges, make friends, and find a routine.",
"Now it's time to join our chatroom. Click the \"I've completed this challenge\" button to move on to your next challenge."
],
"tests": [
""
],
"challengeSeed": [
"114486344"
],
"challengeType" : 2,
"completionMessage": ""
},
{
"_id": "bd7123c8c441eddfaeb5bdef",
"name": "Meet Booleans",
"difficulty": "0.001",
"description": [
"Return true"
],
"tests": [
"expect(welcomeToBooleans()).to.be.a(\"boolean\");",
"expect(welcomeToBooleans()).to.be.true;"
],
"challengeSeed": [
"function welcomeToBooleans() {\n // Good luck!\n return false;\n}\n\nwelcomeToBooleans();"
],
"challengeType": 1,
"completionMessage": ""
},
{
"_id" : "bd7123c8c441eddfaeb5bdef",
"name": "Start our Challenges",
"difficulty": "0.00",
"difficulty": "0.002",
"description": [
"Welcome to Free Code Camp's first challenge! Click on the button below for further instructions.",
"Awesome. Now you can read the rest of this challenge's instructions.",

View File

@ -42,4 +42,4 @@ block content
br
ul#testSuite.list-group
br
script(src='/js/lib/bonfire/bonfireFramework_v0.1.1.js')
script(src='/js/lib/bonfire/bonfireFramework_v0.1.2.js')

View File

@ -87,7 +87,6 @@ block content
script(type="text/javascript").
var tests = !{JSON.stringify(tests)};
var challengeSeed = !{JSON.stringify(challengeSeed)};
var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)};
var passedBonfireHash = !{JSON.stringify(bonfireHash)};
var challengeName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
@ -96,7 +95,7 @@ block content
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
script(src='/js/lib/bonfire/bonfireFramework_v0.1.1.js')
script(src='/js/lib/bonfire/bonfireFramework_v0.1.2.js')

View File

@ -40,7 +40,10 @@ block content
span.ion-help-circled
| Less information
- if (cc)
a.btn.btn-primary.btn-lg.btn-block#next-courseware-button Go to my next challenge (ctrl + enter)
a.btn.btn-primary.btn-lg.btn-block#next-courseware-button
| Go to my next challenge
br
| (ctrl + enter)
script.
var userLoggedIn = true;
@ -70,7 +73,7 @@ block content
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
script(src='/js/lib/coursewares/coursewaresFramework_v0.1.1.js')
script(src='/js/lib/coursewares/coursewaresHCJQFramework_v0.1.1.js')
.col-md-4.col-lg-3
.hidden-xs.hidden-sm
img.iphone-position(src="https://s3.amazonaws.com/freecodecamp/iphone6-frame.png")

View File

@ -0,0 +1,83 @@
extends ../layout-wide
block content
script(src='/js/lib/codemirror/lib/codemirror.js')
script(src='/js/lib/codemirror/addon/edit/closebrackets.js')
script(src='/js/lib/codemirror/addon/edit/matchbrackets.js')
script(src='/js/lib/codemirror/addon/lint/lint.js')
script(src='/js/lib/codemirror/addon/lint/javascript-lint.js')
script(src='//ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js')
script(src='/js/lib/chai/chai.js')
link(rel='stylesheet', href='/js/lib/codemirror/lib/codemirror.css')
link(rel='stylesheet', href='/js/lib/codemirror/addon/lint/lint.css')
link(rel='stylesheet', href='/js/lib/codemirror/theme/monokai.css')
link(rel="stylesheet", href="http://fonts.googleapis.com/css?family=Ubuntu+Mono")
script(src='/js/lib/codemirror/mode/javascript/javascript.js')
script(src='/js/lib/jailed/jailed.js')
script(src='/js/lib/bonfire/bonfireInit.js')
.row
.col-xs-12.col-sm-12.col-md-4.bonfire-top
#testCreatePanel
h1.text-center= name
.well
.row
.col-xs-12
.bonfire-instructions
p= brief
#brief-instructions
.text-center
button#more-info.btn.btn-info
span.ion-help-circled
| More information
#long-instructions.row.hide
.col-xs-12
for sentence in details
p!= sentence
.text-center
button#less-info.btn.btn-info
span.ion-help-circled
| Less information
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
br
form.code
.form-group.codeMirrorView
textarea#codeOutput
br
#testSuite.hidden
br
script(type="text/javascript").
var tests = !{JSON.stringify(tests)};
var challengeSeed = !{JSON.stringify(challengeSeed)};
var passedBonfireHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var started = Math.floor(Date.now() / 1000);
.col-xs-12.col-sm-12.col-md-8
#mainEditorPanel
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
script(src='/js/lib/coursewares/coursewaresJSFramework.js')
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header= compliment
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body(ng-controller="pairedWithController")
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &nbsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
//#all-bonfires-dialog.modal(tabindex='-1')
// .modal-dialog.animated.fadeInUp.fast-animation
// .modal-content
// .modal-header.challenge-list-header Bonfires
// a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
// .modal-body
// include ../partials/bonfires

View File

@ -0,0 +1,47 @@
extends ../layout
block content
.row
.col-sm-12.col-md-12.col-xs-12
.panel.panel-primary
.panel-heading.text-center
h1 #{name}
script.
var challengeName = !{JSON.stringify(name)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
.panel.panel-body
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item(src='//player.vimeo.com/video/#{video}')
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2
h3 Steps:
h4
ol
for step in details
li!= step
.btn.btn-primary.btn-big.btn-block#completed-courseware I've completed this challenge
.ten-pixel-break
.btn.btn-success.btn-big.btn-block.all-challenges Show me all the challenges
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header= compliment
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &nbsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
#all-challenges-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.challenge-list-header Challenges
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body