diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 07289986d1..92d494046b 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -8,13 +8,14 @@ var _ = require('lodash'), * Bonfire controller */ -exports.courseware = function(req, res) { - res.render('courseware/show.jade', { +var highestBonfireNumber = resources.numberOfBonfires(); + +exports.bonfireNames = function(req, res) { + res.render('bonfires/showList', { + bonfireList: resources.allBonfireNames() }); }; -var highestBonfireNumber = resources.numberOfBonfires(); - exports.index = function(req, res) { res.render('bonfire/show.jade', { completedWith: null, diff --git a/controllers/courseware.js b/controllers/courseware.js new file mode 100644 index 0000000000..b966dba1bb --- /dev/null +++ b/controllers/courseware.js @@ -0,0 +1,207 @@ +var _ = require('lodash'), + debug = require('debug')('freecc:cntr:coursewares'), + Courseware = require('./../models/Courseware'), + User = require('./../models/User'), + resources = require('./resources'); + +/** + * Courseware controller + */ + +exports.courseware = function(req, res) { + res.render('courseware/show.jade', { + }); +}; + +var highestCourswareNumber = resources.numberOfCoursewares(); + +exports.returnNextCourseware = function(req, res, next) { + if (!req.user) { + return res.redirect('coursewares/welcome-to-courseware'); + } + var completed = req.user.completedBonfires.map(function (elem) { + return elem._id; + }); + + req.user.uncompletedBonfires = resources.allBonfireIds().filter(function (elem) { + if (completed.indexOf(elem) === -1) { + return elem; + } + }); + req.user.save(); + + var uncompletedCoursewares = req.user.uncompletedCoursewares; + + var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares[0]}); + displayedCoursewares.exec(function(err, bonfire) { + if (err) { + next(err); + } + + courseware = courseware.pop(); + nameString = courseware[0].name.toLowerCase().replace(/\s/g, '-'); + return res.redirect('/bonfires/' + nameString); + //res.render('bonfire/show', { + // completedWith: null, + // title: bonfire[bonfireNumber].name, + // name: bonfire[bonfireNumber].name, + // difficulty: +bonfire[bonfireNumber].difficulty, + // brief: bonfire[bonfireNumber].description[0], + // details: bonfire[bonfireNumber].description.slice(1), + // tests: bonfire[bonfireNumber].tests, + // challengeSeed: bonfire[bonfireNumber].challengeSeed, + // challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + // cc: req.user ? req.user.bonfiresHash : undefined, + // points: req.user ? req.user.points : undefined, + // verb: resources.randomVerb(), + // phrase: resources.randomPhrase(), + // compliments: resources.randomCompliment(), + // bonfires: bonfire, + // bonfireHash: bonfire[bonfireNumber]._id + //}); + }); +}; + +exports.returnIndividualBonfire = function(req, res, next) { + var dashedName = req.params.bonfireName; + + bonfireName = dashedName.replace(/\-/g, ' '); + var bonfireNumber = 0; + + Bonfire.find({"name" : new RegExp(bonfireName, 'i')}, function(err, bonfire) { + if (err) { + next(err); + } + if (bonfire.length < 1) { + req.flash('errors', { + msg: "404: We couldn't find a bonfire with that name. Please double check the name." + }); + return res.redirect('/bonfires/meet-bonfire') + } else { + res.render('bonfire/show', { + completedWith: null, + title: bonfire[bonfireNumber].name, + dashedName: dashedName, + name: bonfire[bonfireNumber].name, + difficulty: Math.floor(+bonfire[bonfireNumber].difficulty), + brief: bonfire[bonfireNumber].description[0], + details: bonfire[bonfireNumber].description.slice(1), + tests: bonfire[bonfireNumber].tests, + challengeSeed: bonfire[bonfireNumber].challengeSeed, + challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint, + cc: !!req.user, + points: req.user ? req.user.points : undefined, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + bonfires: bonfire, + bonfireHash: bonfire[bonfireNumber]._id + + }); + } + }); +}; + +/** + * Bonfire generator + */ +exports.returnGenerator = function(req, res) { + res.render('bonfire/generator', { + title: null, + name: null, + difficulty: null, + brief: null, + details: null, + tests: null, + challengeSeed: null, + challengeEntryPoint: null, + bonfireHash: randomString() + }); +}; + +/** + * Post for bonfire generation + */ + +function randomString() { + var chars = "0123456789abcdef"; + var string_length = 23; + var randomstring = 'a'; + for (var i=0; i 0) { + return elem; + } +} + +exports.publicGenerator = function(req, res) { + res.render('bonfire/public-generator'); +} + +exports.generateChallenge = function(req, res) { + var bonfireName = req.body.name, + 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'); + bonfireTests.filter(getRidOfEmpties); + bonfireDescription.filter(getRidOfEmpties); + bonfireChallengeSeed = bonfireChallengeSeed.replace('\r', ''); + + + var response = { + _id: randomString(), + name: bonfireName, + difficulty: bonfireDifficulty, + description: bonfireDescription, + challengeEntryPoint: bonfireEntryPoint, + challengeSeed: bonfireChallengeSeed, + tests: bonfireTests + }; + res.send(response); +} \ No newline at end of file diff --git a/controllers/resources.js b/controllers/resources.js index aefafe02c0..92fa9e9bcf 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -202,7 +202,21 @@ module.exports = { }) .map(function(elem) { return elem._id; + }); + }, + allBonfireNames: function() { + return bonfires.map(function(elem) { + return { + name: elem.name, + difficulty: elem.difficulty + } }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map(function(elem) { + return elem.name; + }); } }; diff --git a/models/Bonfire.js b/models/Bonfire.js index 5cb531547e..d8785f7196 100644 --- a/models/Bonfire.js +++ b/models/Bonfire.js @@ -17,7 +17,6 @@ var bonfireSchema = new mongoose.Schema({ tests: Array, challengeSeed: String, challengeEntryPoint: String, - challengeEntryPointNegate: String }); module.exports = mongoose.model('Bonfire', bonfireSchema); diff --git a/models/Courseware.js b/models/Courseware.js new file mode 100644 index 0000000000..d2055aab26 --- /dev/null +++ b/models/Courseware.js @@ -0,0 +1,21 @@ +var mongoose = require('mongoose'); +var secrets = require('../config/secrets'); + +/** + * + * @type {exports.Schema} + */ + +var coursewareSchema = new mongoose.Schema({ + name: { + type: String, + unique: true + }, + difficulty: String, + description: Array, + tests: Array, + challengeSeed: String, + challengeEntryPoint: String +}); + +module.exports = mongoose.model('Courseware', coursewareSchema); \ No newline at end of file diff --git a/public/css/main.less b/public/css/main.less index b140ab74f2..911d10dfa8 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -643,6 +643,10 @@ div.CodeMirror-scroll { margin-top: -30px; } +iframe.iphone { + width: 401px; + position: absolute; +} //uncomment this to see the dimensions of all elements outlined in red diff --git a/seed_data/coursewares.json b/seed_data/coursewares.json new file mode 100644 index 0000000000..a773aa40fd --- /dev/null +++ b/seed_data/coursewares.json @@ -0,0 +1,19 @@ +[ + { + "_id" : "bd7123c8c441eddfaeb5bdef", + "name": "Meet Bonfire", + "difficulty": "0", + "description": [ + "Click the button below for further instructions.", + "Your goal is to fix the failing test.", + "First, run all the tests by clickin \"Run code\" or by pressing Control + Enter", + "The failing test is in red. Fix the code so that all tests pass. Then you can move on to the next Bonfire." + ], + "tests": [ + "expect(meetBonfire(\"test\")).to.be.a(\"boolean\");", + "expect(meetBonfire(\"test\")).to.be.true;" + ], + "challengeSeed": "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\nreturn false;\n}\n\n", + "challengeEntryPoint": "meetBonfire(\"You can do this!\");" + } +] \ No newline at end of file diff --git a/seed_data/seed.js b/seed_data/seed.js index 797f704d5e..6549c3cc71 100644 --- a/seed_data/seed.js +++ b/seed_data/seed.js @@ -1,20 +1,23 @@ require('dotenv').load(); var Challenge = require('../models/Challenge.js'), Bonfire = require('../models/Bonfire.js'), + Courseware = require('../models/Courseware.js'), mongoose = require('mongoose'), secrets = require('../config/secrets'), challenges = require('./challenges.json'), + coursewares = require('./coursewares.json'), bonfires = require('./bonfires.json'); mongoose.connect(secrets.db); var counter = 0; +var offerings = 3; var CompletionMonitor = function() { counter++; console.log('call ' + counter); - if (counter < 2) { + if (counter < offerings) { return; } else { process.exit(0); @@ -53,7 +56,21 @@ Bonfire.remove({}, function(err, data) { CompletionMonitor(); }); console.log('bonfires'); - }); - +Courseware.remove({}, function(err, data) { + if (err) { + console.error(err); + } else { + console.log('Deleted ', data); + } + Courseware.create(coursewares, function(err, data) { + if (err) { + console.log(err); + } else { + console.log('Saved ', data); + } + CompletionMonitor(); + }); + console.log('coursewares'); +}); \ No newline at end of file diff --git a/views/courseware/show.jade b/views/courseware/show.jade deleted file mode 100644 index bf1d6212d6..0000000000 --- a/views/courseware/show.jade +++ /dev/null @@ -1,60 +0,0 @@ -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') - script(src='/js/lib/codemirror/mode/xml/xml.js') - script(src='/js/lib/codemirror/mode/css/css.js') - script(src='/js/lib/codemirror/mode/htmlmixed/htmlmixed.js') - style(type='text/css'). - style. - .CodeMirror { - float: left; - width: 50%; - border: 1px solid black; - } - iframe { - width: 49%; - float: left; - height: 300px; - border: 1px solid black; - border-left: 0px; - } - - textarea#code(name='code') - - - iframe#preview - script(src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js") - script(src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js") - style(src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css") - script. - var delay; - // Initialize CodeMirror editor with a nice html5 canvas demo. - var editor = CodeMirror.fromTextArea(document.getElementById('code'), { - mode: 'text/html' - }); - editor.on("change", function () { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); - }); - function updatePreview() { - var previewFrame = document.getElementById('preview'); - var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - preview.open(); - preview.write(editor.getValue()); - preview.close(); - } - setTimeout(updatePreview, 300); - \ No newline at end of file diff --git a/views/coursewares/show.jade b/views/coursewares/show.jade new file mode 100644 index 0000000000..4a6ba082a7 --- /dev/null +++ b/views/coursewares/show.jade @@ -0,0 +1,121 @@ +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') + script(src='/js/lib/codemirror/mode/xml/xml.js') + script(src='/js/lib/codemirror/mode/css/css.js') + script(src='/js/lib/codemirror/mode/htmlmixed/htmlmixed.js') + .row + .col-xs-12.col-sm-12.col-md-3.bonfire-top + h2.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 + br + script(type="text/javascript"). + var tests = !{JSON.stringify(tests)}; + var challengeSeed = !{JSON.stringify(challengeSeed)}; + var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)}; + var passedBonfireHash = !{JSON.stringify(bonfireHash)}; + .col-xs-12.col-sm-12.col-md-5 + #mainEditorPanel + form.code + .form-group.codeMirrorView + textarea#codeEditor(autofocus=true) + script(src='/js/lib/bonfire/bonfireFramework.js') + #complete-bonfire-dialog.modal(tabindex='-1') + .col-xs-12.col-sm-12.col-md-4 + iframe.iphone#preview + + + //.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.landing-icon.ion-checkmark-circled.text-primary + // - if (cc) + // 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-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-bonfire-button(name='_csrf', value=_csrf, aria-hidden='true', ng-disabled='completedWithForm.$invalid && existingUser.length > 0') 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%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank") + // i.fa.fa-twitter   + // = 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 + + //img(src="https://s3.amazonaws.com/freecodecamp/iphone6-frame.png") + //script(src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js") + //script(src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js") + //style(src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css") + script. + var delay; + // Initialize CodeMirror editor with a nice html5 canvas demo. + var editor = CodeMirror.fromTextArea(document.getElementById('code'), { + mode: 'text/html' + }); + editor.on("change", function () { + clearTimeout(delay); + delay = setTimeout(updatePreview, 300); + }); + function updatePreview() { + var previewFrame = document.getElementById('preview'); + var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; + preview.open(); + preview.write(editor.getValue()); + preview.close(); + } + setTimeout(updatePreview, 300); + \ No newline at end of file