diff --git a/README.md b/README.md
index 9b0c87d631..0611cd1816 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Our campers (students) start by working through our free, self-paced, browser-ba
80% of our campers are over 25, and nearly a fifth of our campers are women.
-This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp).
+This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [blog](http://medium.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp).
[Join our community here](http://www.freecodecamp.com/signin).
@@ -54,22 +54,26 @@ The easiest way to get started is to clone the repository:
# Get the latest snapshot
git clone --depth=1 https://github.com/freecodecamp/freecodecamp.git freecodecamp
+# Change directory
cd freecodecamp
# Install NPM dependencies
npm install
+# Install Gulp globally
+npm install -g gulp
+
+# Install Bower globally
+npm install -g bower
+
# Install Bower dependencies
bower install
# Create a .env file and populate it with the necessary API keys and secrets:
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. If you only use a subset or no OAuth2 authentication methods, you may want to remove them from ```server/passport-providers.js``` - otherwise the server will complain about missing clientIDs at launch.
```
MONGOHQ_URL='mongodb://localhost:27017/freecodecamp'
@@ -107,20 +111,20 @@ DEBUG=true
```
```bash
-# Start the mongo server
+# Start the mongo server in a seperate terminal
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
-node seed/
+# Initialize Free Code Camp
+# This will seed the database for the first time.
+# This command should only be run once.
+npm run first-time
# start the application
gulp
```
+Now navigate to your browser and open http://localhost:3001
+If the app loads, congratulations - you're all set. Otherwise, let us know by opening a GitHub issue and with your error.
+
License
-------
diff --git a/client/commonFramework.js b/client/commonFramework.js
index a28a1e743d..2505fe08f4 100644
--- a/client/commonFramework.js
+++ b/client/commonFramework.js
@@ -374,8 +374,9 @@ var editor = (function(CodeMirror, emmetCodeMirror, common) {
}
var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), {
+ lint: true,
lineNumbers: true,
- mode: 'text',
+ mode: 'javascript',
theme: 'monokai',
runnable: true,
matchBrackets: true,
@@ -529,6 +530,20 @@ function safeHTMLRun(test) {
// add feuxQuery
s = 'var document = \"\"; var $ = function() {return(new function() {this.add=function() {return(this);};this.addBack=function() {return(this);};this.addClass=function() {return(this);};this.after=function() {return(this);};this.ajaxComplete=function() {return(this);};this.ajaxError=function() {return(this);};this.ajaxSend=function() {return(this);};this.ajaxStart=function() {return(this);};this.ajaxStop=function() {return(this);};this.ajaxSuccess=function() {return(this);};this.andSelf=function() {return(this);};this.animate=function() {return(this);};this.append=function() {return(this);};this.appendTo=function() {return(this);};this.attr=function() {return(this);};this.before=function() {return(this);};this.bind=function() {return(this);};this.blur=function() {return(this);};this.callbacksadd=function() {return(this);};this.callbacksdisable=function() {return(this);};this.callbacksdisabled=function() {return(this);};this.callbacksempty=function() {return(this);};this.callbacksfire=function() {return(this);};this.callbacksfired=function() {return(this);};this.callbacksfireWith=function() {return(this);};this.callbackshas=function() {return(this);};this.callbackslock=function() {return(this);};this.callbackslocked=function() {return(this);};this.callbacksremove=function() {return(this);};this.change=function() {return(this);};this.children=function() {return(this);};this.clearQueue=function() {return(this);};this.click=function() {return(this);};this.clone=function() {return(this);};this.closest=function() {return(this);};this.contents=function() {return(this);};this.context=function() {return(this);};this.css=function() {return(this);};this.data=function() {return(this);};this.dblclick=function() {return(this);};this.delay=function() {return(this);};this.delegate=function() {return(this);};this.dequeue=function() {return(this);};this.detach=function() {return(this);};this.die=function() {return(this);};this.each=function() {return(this);};this.empty=function() {return(this);};this.end=function() {return(this);};this.eq=function() {return(this);};this.error=function() {return(this);};this.fadeIn=function() {return(this);};this.fadeOut=function() {return(this);};this.fadeTo=function() {return(this);};this.fadeToggle=function() {return(this);};this.filter=function() {return(this);};this.find=function() {return(this);};this.finish=function() {return(this);};this.first=function() {return(this);};this.focus=function() {return(this);};this.focusin=function() {return(this);};this.focusout=function() {return(this);};this.get=function() {return(this);};this.has=function() {return(this);};this.hasClass=function() {return(this);};this.height=function() {return(this);};this.hide=function() {return(this);};this.hover=function() {return(this);};this.html=function() {return(this);};this.index=function() {return(this);};this.innerHeight=function() {return(this);};this.innerWidth=function() {return(this);};this.insertAfter=function() {return(this);};this.insertBefore=function() {return(this);};this.is=function() {return(this);};this.jQuery=function() {return(this);};this.jquery=function() {return(this);};this.keydown=function() {return(this);};this.keypress=function() {return(this);};this.keyup=function() {return(this);};this.last=function() {return(this);};this.length=function() {return(this);};this.live=function() {return(this);};this.load=function() {return(this);};this.load=function() {return(this);};this.map=function() {return(this);};this.mousedown=function() {return(this);};this.mouseenter=function() {return(this);};this.mouseleave=function() {return(this);};this.mousemove=function() {return(this);};this.mouseout=function() {return(this);};this.mouseover=function() {return(this);};this.mouseup=function() {return(this);};this.next=function() {return(this);};this.nextAll=function() {return(this);};this.nextUntil=function() {return(this);};this.not=function() {return(this);};this.off=function() {return(this);};this.offset=function() {return(this);};this.offsetParent=function() {return(this);};this.on=function() {return(this);};this.one=function() {return(this);};this.outerHeight=function() {return(this);};this.outerWidth=function() {return(this);};this.parent=function() {return(this);};this.parents=function() {return(this);};this.parentsUntil=function() {return(this);};this.position=function() {return(this);};this.prepend=function() {return(this);};this.prependTo=function() {return(this);};this.prev=function() {return(this);};this.prevAll=function() {return(this);};this.prevUntil=function() {return(this);};this.promise=function() {return(this);};this.prop=function() {return(this);};this.pushStack=function() {return(this);};this.queue=function() {return(this);};this.ready=function() {return(this);};this.remove=function() {return(this);};this.removeAttr=function() {return(this);};this.removeClass=function() {return(this);};this.removeData=function() {return(this);};this.removeProp=function() {return(this);};this.replaceAll=function() {return(this);};this.replaceWith=function() {return(this);};this.resize=function() {return(this);};this.scroll=function() {return(this);};this.scrollLeft=function() {return(this);};this.scrollTop=function() {return(this);};this.select=function() {return(this);};this.selector=function() {return(this);};this.serialize=function() {return(this);};this.serializeArray=function() {return(this);};this.show=function() {return(this);};this.siblings=function() {return(this);};this.size=function() {return(this);};this.slice=function() {return(this);};this.slideDown=function() {return(this);};this.slideToggle=function() {return(this);};this.slideUp=function() {return(this);};this.stop=function() {return(this);};this.submit=function() {return(this);};this.text=function() {return(this);};this.toArray=function() {return(this);};this.toggle=function() {return(this);};this.toggle=function() {return(this);};this.toggleClass=function() {return(this);};this.trigger=function() {return(this);};this.triggerHandler=function() {return(this);};this.unbind=function() {return(this);};this.undelegate=function() {return(this);};this.unload=function() {return(this);};this.unwrap=function() {return(this);};this.val=function() {return(this);};this.width=function() {return(this);};this.wrap=function() {return(this);};this.wrapAll=function() {return(this);};this.wrapInner=function() {return(this);}});};$.ajax=function() {return($);};$.ajaxPrefilter=function() {return($);};$.ajaxSetup=function() {return($);};$.ajaxTransport=function() {return($);};$.boxModel=function() {return($);};$.browser=function() {return($);};$.Callbacks=function() {return($);};$.contains=function() {return($);};$.cssHooks=function() {return($);};$.cssNumber=function() {return($);};$.data=function() {return($);};$.Deferred=function() {return($);};$.dequeue=function() {return($);};$.each=function() {return($);};$.error=function() {return($);};$.extend=function() {return($);};$.fnextend=function() {return($);};$.fxinterval=function() {return($);};$.fxoff=function() {return($);};$.get=function() {return($);};$.getJSON=function() {return($);};$.getScript=function() {return($);};$.globalEval=function() {return($);};$.grep=function() {return($);};$.hasData=function() {return($);};$.holdReady=function() {return($);};$.inArray=function() {return($);};$.isArray=function() {return($);};$.isEmptyObject=function() {return($);};$.isFunction=function() {return($);};$.isNumeric=function() {return($);};$.isPlainObject=function() {return($);};$.isWindow=function() {return($);};$.isXMLDoc=function() {return($);};$.makeArray=function() {return($);};$.map=function() {return($);};$.merge=function() {return($);};$.noConflict=function() {return($);};$.noop=function() {return($);};$.now=function() {return($);};$.param=function() {return($);};$.parseHTML=function() {return($);};$.parseJSON=function() {return($);};$.parseXML=function() {return($);};$.post=function() {return($);};$.proxy=function() {return($);};$.queue=function() {return($);};$.removeData=function() {return($);};$.sub=function() {return($);};$.support=function() {return($);};$.trim=function() {return($);};$.type=function() {return($);};$.unique=function() {return($);};$.when=function() {return($);};$.always=function() {return($);};$.done=function() {return($);};$.fail=function() {return($);};$.isRejected=function() {return($);};$.isResolved=function() {return($);};$.notify=function() {return($);};$.notifyWith=function() {return($);};$.pipe=function() {return($);};$.progress=function() {return($);};$.promise=function() {return($);};$.reject=function() {return($);};$.rejectWith=function() {return($);};$.resolve=function() {return($);};$.resolveWith=function() {return($);};$.state=function() {return($);};$.then=function() {return($);};$.currentTarget=function() {return($);};$.data=function() {return($);};$.delegateTarget=function() {return($);};$.isDefaultPrevented=function() {return($);};$.isImmediatePropagationStopped=function() {return($);};$.isPropagationStopped=function() {return($);};$.metaKey=function() {return($);};$.namespace=function() {return($);};$.pageX=function() {return($);};$.pageY=function() {return($);};$.preventDefault=function() {return($);};$.relatedTarget=function() {return($);};$.result=function() {return($);};$.stopImmediatePropagation=function() {return($);};$.stopPropagation=function() {return($);};$.target=function() {return($);};$.timeStamp=function() {return($);};$.type=function() {return($);};$.which=function() {return($);};' + s;
+ // add spoofigator
+
+ s = " var navigator = " +
+ "function(){" +
+ " this.geolocation=function(){" +
+ " this.getCurrentPosition=function(){" +
+ " this.coords = {latitude: \"\", longitude: \"\"};" +
+ " return(this);" +
+ " };" +
+ " return(this);" +
+ " };" +
+ " return(this);" +
+ "};" + s;
+
sandBox.submit(scopejQuery(s), function(cls, message) {
if (cls) {
console.log(message.error);
diff --git a/common/app/components/Footer/links.json b/common/app/components/Footer/links.json
index 92199b127f..8089a868e4 100644
--- a/common/app/components/Footer/links.json
+++ b/common/app/components/Footer/links.json
@@ -2,7 +2,7 @@
{
"className": "ion-speakerphone",
"content": " Blog ",
- "href": "http://blog.freecodecamp.com",
+ "href": "http://medium.freecodecamp.com",
"target": "_blank"
},
{
diff --git a/common/models/challenge.json b/common/models/challenge.json
index 84cf3a4c6b..81d6af6eef 100644
--- a/common/models/challenge.json
+++ b/common/models/challenge.json
@@ -82,6 +82,10 @@
},
"descriptionPt": {
"type": "array"
+ },
+ "solutions": {
+ "type": "array",
+ "default": []
}
},
"validations": [],
diff --git a/package.json b/package.json
index 06d6a933ce..1c6f77559a 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,8 @@
"url": "https://github.com/freecodecamp/freecodecamp.git"
},
"scripts": {
+ "first-time": "npm run create-rev && echo '\n\nseeding database\n\n' && node seed && node seed/nonprofits",
+ "create-rev": "test ! -e server/rev-manifest.json && echo '\n\ncreating manifest\n\n' && touch server/rev-manifest.json && echo '{}' >> server/rev-manifest.json",
"build": "gulp build",
"start": "babel-node server/server.js",
"prestart-production": "bower cache clean && bower install && gulp build",
@@ -49,6 +51,13 @@
"forever": "~0.14.1",
"frameguard": "^0.2.2",
"github-api": "~0.7.0",
+ "gulp": "~3.8.8",
+ "gulp-eslint": "~0.9.0",
+ "gulp-inject": "~1.0.2",
+ "gulp-jsonlint": "^1.1.0",
+ "gulp-nodemon": "^2.0.3",
+ "gulp-notify": "^2.2.0",
+ "gulp-plumber": "^1.0.1",
"gulp-less": "^3.0.3",
"gulp-minify-css": "~0.5.1",
"gulp-reduce-file": "0.0.1",
@@ -114,13 +123,6 @@
"browserify": "^10.2.4",
"chai": "~1.10.0",
"envify": "^3.4.0",
- "gulp": "~3.8.8",
- "gulp-eslint": "~0.9.0",
- "gulp-inject": "~1.0.2",
- "gulp-jsonlint": "^1.1.0",
- "gulp-nodemon": "^2.0.3",
- "gulp-notify": "^2.2.0",
- "gulp-plumber": "^1.0.1",
"istanbul": "^0.3.15",
"loopback-explorer": "^1.7.2",
"loopback-testing": "^1.1.0",
diff --git a/seed/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json
index 8ff025e2fe..04ab7c18b2 100644
--- a/seed/challenges/advanced-bonfires.json
+++ b/seed/challenges/advanced-bonfires.json
@@ -57,6 +57,9 @@
"MDNlinks": [
"RegExp"
],
+ "solutions": [
+ "var re = /^(?:(?:\\+?1\\s*(?:[.-]\\s*)?)?(?:\\(\\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\\s*\\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\\s*(?:[.-]\\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\\s*(?:[.-]\\s*)?([0-9]{4})$/;\n\nfunction telephoneCheck(str) {\n return !!str.match(re);\n}\n\ntelephoneCheck(\"555-555-5555\");"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -88,13 +91,15 @@
"tests": [
"assert.sameMembers(sym([1, 2, 3], [5, 2, 1, 4]), [3, 5, 4], 'message: sym([1, 2, 3], [5, 2, 1, 4]) should return [3, 5, 4].');",
"assert.sameMembers(sym([1, 2, 5], [2, 3, 5], [3, 4, 5]), [1, 4, 5], 'message: sym([1, 2, 5], [2, 3, 5], [3, 4, 5]) should return [1, 4, 5]');",
- "assert.sameMembers(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]), [1, 4, 5], 'message: sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]) should return [1, 4, 5].');",
- "assert.sameMembers(sym([1, 1]), [1], 'message: sym([1, 1]) should return [1].');"
- ],
+ "assert.sameMembers(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]), [1, 4, 5], 'message: sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]) should return [1, 4, 5].');"
+ ],
"MDNlinks": [
"Array.reduce()",
"Symmetric Difference"
],
+ "solutions": [
+ "function sym(args) {\n var index = -1;\n var length = arguments.length;\n var result;\n while (++index < length) {\n var array = arguments[index];\n result = result ? diff(result, array).concat(diff(array, result)) : array;\n }\n return result ? uniq(result) : [];\n}\n\nfunction uniq(arr) {\n var h = Object.create(null);\n var u = [];\n arr.forEach(function(v) {\n if (v in h) return;\n h[v] = true;\n u.push(v);\n });\n return u;\n}\n\nfunction diff(a, b) {\n var h = Object.create(null);\n b.forEach(function(v) {\n h[v] = true; \n });\n return a.filter(function(v) { return !(v in h);});\n}\nsym([1, 2, 3], [5, 2, 1, 4]);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -151,6 +156,9 @@
"MDNlinks": [
"Global Object"
],
+ "solutions": [
+ "var VALUES = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];\n\nfunction drawer(price, cash, cid) {\n cash = ~~(cash * 100);\n price = ~~(price * 100);\n var diff = cash-price;\n cid.forEach(function(c) {\n c[1] = ~~(c[1] * 100);\n });\n var totalCid = cid.reduce(function(a, c) {\n return a + c[1];\n }, 0);\n if (diff > totalCid) {\n return \"Insufficient Funds\";\n }\n if (diff === totalCid) {\n return \"Closed\";\n }\n \n var change = []; \n var index = cid.length;\n while (diff > 0 && --index > -1) {\n var t = 0;\n var value = VALUES[index];\n while (diff >= value && cid[index][1] > 0) {\n t += value;\n cid[index][1] -= value;\n diff -= value;\n }\n if (t) {\n change.push([cid[index][0], t/100]);\n }\n console.log(JSON.stringify(change));\n }\n // Here is your change, ma'am.\n return change;\n}\n\n// Example cash-in-drawer array:\n// [['PENNY', 1.01],\n// ['NICKEL', 2.05],\n// ['DIME', 3.10],\n// ['QUARTER', 4.25],\n// ['ONE', 90.00],\n// ['FIVE', 55.00],\n// ['TEN', 20.00],\n// ['TWENTY', 60.00],\n// ['ONE HUNDRED', 100.00]]\n\ndrawer(19.50, 20.00, [['PENNY', 1.01], ['NICKEL', 2.05], ['DIME', 3.10], ['QUARTER', 4.25], ['ONE', 90.00], ['FIVE', 55.00], ['TEN', 20.00], ['TWENTY', 60.00], ['ONE HUNDRED', 100.00]]);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -205,6 +213,9 @@
"MDNlinks": [
"Global Array Object"
],
+ "solutions": [
+ "function inventory(arr1, arr2) {\n arr2.forEach(function(item) {\n createOrUpdate(arr1, item);\n });\n // All inventory must be accounted for or you're fired!\n return arr1;\n}\n\nfunction createOrUpdate(arr1, item) {\n var index = -1;\n while (++index < arr1.length) {\n if (arr1[index][1] === item[1]) {\n arr1[index][0] += item[0];\n return;\n }\n if (arr1[index][1] > item[1]) {\n break;\n }\n }\n arr1.splice(index, 0, item);\n}\n\n// Example inventory lists\nvar curInv = [\n [21, 'Bowling Ball'],\n [2, 'Dirty Sock'],\n [1, 'Hair Pin'],\n [5, 'Microphone']\n];\n\nvar newInv = [\n [2, 'Hair Pin'],\n [3, 'Half-Eaten Apple'],\n [67, 'Bowling Ball'],\n [7, 'Toothpaste']\n];\n\ninventory(curInv, newInv);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -246,6 +257,9 @@
"Permutations",
"RegExp"
],
+ "solutions": [
+ "function permAlone(str) {\n return permutor(str).filter(function(perm) {\n return !perm.match(/(.)\\1/g);\n }).length;\n}\n\nfunction permutor(str) {\n // http://staff.roguecc.edu/JMiller/JavaScript/permute.html\n //permArr: Global array which holds the list of permutations\n //usedChars: Global utility array which holds a list of \"currently-in-use\" characters\n var permArr = [], usedChars = [];\n function permute(input) {\n //convert input into a char array (one element for each character)\n var i, ch, chars = input.split(\"\");\n for (i = 0; i < chars.length; i++) {\n //get and remove character at index \"i\" from char array\n ch = chars.splice(i, 1);\n //add removed character to the end of used characters\n usedChars.push(ch);\n //when there are no more characters left in char array to add, add used chars to list of permutations\n if (chars.length === 0) permArr[permArr.length] = usedChars.join(\"\");\n //send characters (minus the removed one from above) from char array to be permuted\n permute(chars.join(\"\"));\n //add removed character back into char array in original position\n chars.splice(i, 0, ch);\n //remove the last character used off the end of used characters array\n usedChars.pop();\n }\n }\n permute(str);\n return permArr;\n}\n\npermAlone('aab');\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -291,6 +305,9 @@
"String.substr()",
"parseInt()"
],
+ "solutions": [
+ "function friendly(str) {\n var dates = str.map(function(s) {return s.split('-').map(Number);});\n var start = dates[0];\n var end = dates[1];\n if (str[0] === str[1]) {\n return [readable(start)];\n }\n if (start[0] !== end[0]) {\n if (start[0] + 1 === end[0] && start[1] > end[1]) {\n start[0] = undefined;\n end[0] = undefined;\n }\n return dates.map(readable);\n }\n start[0] = undefined;\n end[0] = undefined;\n if (start[1] !== end[1]) {\n return dates.map(readable);\n }\n end[1] = undefined;\n return dates.map(readable);\n}\n\nfunction readable(arr) {\n var ordD = arr[2] + nth(arr[2]);\n if (!arr[1]) {\n return ordD;\n }\n return MONTH[arr[1]] + \" \" + ordD + (!arr[0] ? \"\" : \", \" + arr[0]);\n}\n\nvar MONTH = {1: \"January\",\n 2: \"February\",\n 3: \"March\",\n 4: \"April\",\n 5: \"May\",\n 6: \"June\",\n 7: \"July\",\n 8: \"August\",\n 9: \"September\",\n 10: \"October\",\n 11: \"November\",\n 12: \"December\"};\n\nfunction nth(d) {\n if(d>3 && d<21) return 'th';\n switch (d % 10) {\n case 1: return \"st\";\n case 2: return \"nd\";\n case 3: return \"rd\";\n default: return \"th\";\n }\n} \n\nfriendly(['2015-07-01', '2015-07-04']);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json
index e2e2fa0e3f..8ac6cbcae7 100644
--- a/seed/challenges/basic-bonfires.json
+++ b/seed/challenges/basic-bonfires.json
@@ -28,6 +28,9 @@
"",
"meetBonfire(\"You can do this!\");"
],
+ "solutions": [
+ "function meetBonfire(argument) {\n // Good luck!\n console.log(\"you can read this function's argument in the developer tools\", argument);\n\n return true;\n}\n\n\n\nmeetBonfire(\"You can do this!\");\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -69,6 +72,9 @@
"Array.reverse()",
"Array.join()"
],
+ "solutions": [
+ "function reverseString(str) {\n return str.split('').reverse().join(\"\");\n}\n\nreverseString('hello');\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -109,6 +115,9 @@
"MDNlinks": [
"Arithmetic Operators"
],
+ "solutions": [
+ "function factorialize(num) {\n return num === 1 ? 1 : num * factorialize(num-1);\n}\n\nfactorialize(5);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -159,6 +168,9 @@
"String.replace()",
"String.toLowerCase()"
],
+ "solutions": [
+ "function palindrome(str) {\n var a = str.toLowerCase().replace(/[^a-z]/g, '');\n console.log(a.split('').reverse().join(''));\n return a == a.split('').reverse().join('');\n}\n\n\n\npalindrome(\"eye\");\npalindrome(\"A man, a plan, a canal. Panama\");\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -199,6 +211,9 @@
"String.split()",
"String.length"
],
+ "solutions": [
+ "function findLongestWord(str) {\n return str.split(' ').sort(function(a, b) { return b.length - a.length;})[0].length;\n}\n\nfindLongestWord('The quick brown fox jumped over the lazy dog');\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -236,6 +251,9 @@
"MDNlinks": [
"String.charAt()"
],
+ "solutions": [
+ "function titleCase(str) {\n return str.split(' ').map(function(word) {\n return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();\n }).join(' ');\n}\n\ntitleCase(\"I'm a little tea pot\");\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -274,6 +292,9 @@
"MDNlinks": [
"Comparison Operators"
],
+ "solutions": [
+ "function largestOfFour(arr) {\n return arr.map(function(subArr) {\n return Math.max.apply(null, subArr);\n });\n}\n\nlargestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -306,7 +327,7 @@
"tests": [
"assert(end(\"Bastian\", \"n\") === true, 'message: end(\"Bastian\", \"n\") should return true.');",
"assert(end(\"Connor\", \"n\") === false, 'message: end(\"Connor\", \"n\") should return false.');",
- "assert(end(\"Walking on water and developing software from a specification are easy if both are frozen.\", \"specification\") === false, 'message: end(\"Walking on water and developing software from a specification are easy if both are frozen.\", \"specification\") should return false.');",
+ "assert(end(\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\") === false, '\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\") should return false.');",
"assert(end(\"He has to give me a new name\", \"name\") === true, 'message: end(\"He has to give me a new name\", \"name\") should return true.');",
"assert(end(\"He has to give me a new name\", \"me\") === true, 'message: end(\"He has to give me a new name\", \"me\") should return true.');",
"assert(end(\"If you want to save our world, you must hurry. We dont know how much longer we can withstand the nothing\", \"mountain\") === false, 'message: end(\"If you want to save our world, you must hurry. We dont know how much longer we can withstand the nothing\", \"mountain\") should return false.');"
@@ -314,6 +335,9 @@
"MDNlinks": [
"String.substr()"
],
+ "solutions": [
+ "function end(str, target) {\n // \"Never give up and good luck will find you.\"\n // -- Falcor\n return str.substring(str.length-target.length) === target;\n}\n\nend('Bastian', 'n');\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -350,6 +374,9 @@
"MDNlinks": [
"Global String Object"
],
+ "solutions": [
+ "function repeat(str, num) {\n if (num < 0) return '';\n return num === 1 ? str : str + repeat(str, num-1);\n}\n\nrepeat('abc', 3);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -390,6 +417,9 @@
"MDNlinks": [
"String.slice()"
],
+ "solutions": [
+ "function truncate(str, num) {\n if (str.length > num) {\n return str.substring(0, num-3) + '...';\n }\n return str;\n}\n\ntruncate('A-tisket a-tasket A green and yellow basket', 11);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -427,6 +457,9 @@
"MDNlinks": [
"Array.push()"
],
+ "solutions": [
+ "function chunk(arr, size) {\n var out = [];\n for (var i = 0; i < arr.length; i+=size) {\n out.push(arr.slice(i,i+size));\n }\n return out;\n}\n\nchunk(['a', 'b', 'c', 'd'], 2);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -466,6 +499,9 @@
"Array.slice()",
"Array.splice()"
],
+ "solutions": [
+ "function slasher(arr, howMany) {\n // it doesn't always pay to be first\n return arr.slice(howMany);\n}\n\nslasher([1, 2, 3], 2);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -509,6 +545,9 @@
"MDNlinks": [
"Array.indexOf()"
],
+ "solutions": [
+ "function mutation(arr) {\n var hash = Object.create(null);\n arr[0].toLowerCase().split('').forEach(function(c) {\n hash[c] = true;\n });\n return !arr[1].toLowerCase().split('').filter(function(c) {\n return !hash[c];\n }).length;\n}\n\nmutation(['hello', 'hey']);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -547,6 +586,9 @@
"Boolean Objects",
"Array.filter()"
],
+ "solutions": [
+ "function bouncer(arr) {\n // Don't show a false ID to this bouncer.\n return arr.filter(function(e) {return e;});\n}\n\nbouncer([7, 'ate', '', false, 9]);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -586,6 +628,9 @@
"Arguments object",
"Array.filter()"
],
+ "solutions": [
+ "function destroyer(arr) {\n var hash = Object.create(null);\n [].slice.call(arguments, 1).forEach(function(e) {\n hash[e] = true;\n });\n // Remove all the values\n return arr.filter(function(e) { return !(e in hash);});\n}\n\ndestroyer([1, 2, 3, 1, 2, 3], 2, 3);\n"
+ ],
"type": "bonfire",
"challengeType": 5,
"nameCn": "",
@@ -603,8 +648,9 @@
"id": "a24c1a4622e3c05097f71d67",
"title": "Where do I belong",
"description": [
- "Return the lowest index at which a value (second argument) should be inserted into a sorted array (first argument).",
- "For example, where([1,2,3,4], 1.5) should return 1 because it is greater than 1 (0th index), but less than 2 (1st index).",
+ "Return the lowest index at which a value (second argument) should be inserted into an array (first argument) once it has been sorted.",
+ "For example, where([1,2,3,4], 1.5) should return 1 because it is greater than 1 (index 0), but less than 2 (index 1).",
+ "Likewise, where([20,3,5], 19) should return 2 because it is less than 20 (index 2) and greater than 5 (index 1).",
"Remember to use Read-Search-Ask if you get stuck. Write your own code."
],
"challengeSeed": [
@@ -618,12 +664,15 @@
"MDNlinks": [
"Array.sort()"
],
+ "solutions": [
+ "function where(arr, num) {\n // Find my place in this sorted array.\n return num;\n}\n\nwhere([40, 60], 50);\n"
+ ],
"tests": [
"assert(where([10, 20, 30, 40, 50], 35) === 3, 'message: where([10, 20, 30, 40, 50], 35) should return 3.');",
"assert(where([10, 20, 30, 40, 50], 30) === 2, 'message: where([10, 20, 30, 40, 50], 30) should return 2.');",
"assert(where([40, 60], 50) === 1, 'message: where([40, 60,], 50) should return 1.');",
- "assert(where([5, 3, 20, 3], 3) === 0, 'message: where([5, 3, 20, 3], 3) should return 0.');",
- "assert(where([2, 20, 10], 1) === 0, 'message: where([2, 20, 10], 1) should return 0.');",
+ "assert(where([5, 3, 20, 3], 5) === 2, 'message: where([5, 3, 20, 3], 5) should return 2.');",
+ "assert(where([2, 20, 10], 19) === 2, 'message: where([2, 20, 10], 19) should return 2.');",
"assert(where([2, 5, 10], 15) === 3, 'message: where([2, 5, 10], 15) should return 3.');"
],
"type": "bonfire",
diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json
index 9ff39ae512..5c9da5407a 100644
--- a/seed/challenges/basic-javascript.json
+++ b/seed/challenges/basic-javascript.json
@@ -938,7 +938,7 @@
"description": [
"Random numbers are useful for creating random behavior.",
"JavaScript has a Math.random() function that generates a random decimal number.",
- "Change myFunction to return a random number instead of returning 0."
+ "Change myFunction to return a random number instead of returning 0.",
"Note that you can return a function, just like you would return a variable or value."
],
"tests": [
@@ -972,7 +972,7 @@
"Note that because we're rounding down, it's impossible to actually get 20.",
"Putting everything together, this is what our code looks like:",
"Math.floor(Math.random() * 20);",
- "See how Math.floor takes (Math.random() * 20) as its argument? That's right - you can pass a function to another function as an argument."
+ "See how Math.floor takes (Math.random() * 20) as its argument? That's right - you can pass a function to another function as an argument.",
"Let's use this technique to generate and return a random whole number between 0 and 9."
],
"tests": [
@@ -1003,7 +1003,7 @@
"title": "Generate Random Whole Numbers within a Range",
"description": [
"Instead of generating a random number between zero and a given number like we did before, we can generate a random number that falls within a range of two specific numbers.",
- "To do this, we'll define a minimum number min and a maximum number max."
+ "To do this, we'll define a minimum number min and a maximum number max.",
"Here's the formula we'll use. Take a moment to read and try to understand what this code is doing.",
"Math.floor(Math.random() * (max - min + 1)) + min",
"Define two variables: myMin and myMax, and set them both equal to numbers.",
@@ -1035,7 +1035,7 @@
"",
"",
"",
- "",
+ "",
"// Only change code above this line.",
"",
"",
@@ -1186,7 +1186,7 @@
"description": [
"You can invert any match by using the uppercase version of the regular expression selector.",
"For example, \\s will match any whitespace, and \\S will match anything that isn't whitespace.",
- "Use /\\S/g to count the number of non-whitespace characters in testString.",
+ "Use /\\S/g to count the number of non-whitespace characters in testString."
],
"tests": [
"assert(editor.getValue().match(/\\/\\\\S\\/g/g), 'message: Use the /\\S/g regular expression to find non-space characters in testString.');",
@@ -1860,4 +1860,4 @@
"challengeType": 0
}
]
-}
\ No newline at end of file
+}
diff --git a/seed/challenges/basic-ziplines.json b/seed/challenges/basic-ziplines.json
index 4565f364a8..0cd948d614 100644
--- a/seed/challenges/basic-ziplines.json
+++ b/seed/challenges/basic-ziplines.json
@@ -35,7 +35,7 @@
[
"http://i.imgur.com/Wzt6Y9Y.gif",
"A gif showing the process of saving and forking a pen.",
- "Save your pen with the \"Save\" button. Then click the \"Fork\" button. This will create a fork (copy) of your pen that you can experimient with.",
+ "Save your pen with the \"Save\" button. Then click the \"Fork\" button. This will create a fork (copy) of your pen that you can experiment with.",
""
]
],
@@ -68,7 +68,7 @@
"User Story: As a user, I can click different buttons that will take me to the portfolio creator's different social media pages.",
"User Story: 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.)",
"Bonus User Story: 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 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.",
"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: <a target='_blank'>.",
"Remember to use Read-Search-Ask if you get stuck.",
diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json
index e921750ae7..239ecf1903 100644
--- a/seed/challenges/bootstrap.json
+++ b/seed/challenges/bootstrap.json
@@ -93,14 +93,15 @@
"id": "bad87fee1348bd9acde08812",
"title": "Make Images Mobile Responsive",
"description": [
- "First, add a new image below the existing one. Set it's src attribute to http://bit.ly/fcc-running-cats.",
+ "First, add a new image below the existing one. Set its src attribute to http://bit.ly/fcc-running-cats.",
"It would be great if this image could be exactly the width of our phone's screen.",
"Fortunately, with Bootstrap, all we need to do is add the img-responsive class to your image. Do this, and the image should perfectly fit the width of your page."
],
"tests": [
- "assert($(\"img\").length > 1, 'You should have a total of two images.')",
- "assert($(\"img\").hasClass(\"img-responsive\"), 'Your new image should have the class img-responsive')",
- "assert(new RegExp(\"http://bit.ly/fcc-running-cats\", \"gi\").test($(\"img.img-responsive\").attr(\"src\")), 'Add a second image with the src of http://bit.ly/fcc-running-cats')"
+ "assert($(\"img\").length === 2, 'You should have a total of two images.')",
+ "assert($(\"img:eq(1)\").hasClass(\"img-responsive\"), 'Your new image should be below your old one and have the class img-responsive.')",
+ "assert($(\"img:eq(1)\").attr(\"src\") === \"http://bit.ly/fcc-running-cats\", 'Your new image should have a src of http://bit.ly/fcc-running-cats.')",
+ "assert(editor.match(//g).length === 2 && editor.match(/img element has a closing angle bracket.')"
],
"challengeSeed": [
"",
@@ -178,8 +179,7 @@
"title": "Center Text with Bootstrap",
"description": [
"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 text-center to our h2 element.",
- "Remember that you can add several classes to the same element by separating each of them with a space, like this:",
- "<h2 class=\"red-text text-center\">your text</h2>"
+ "Remember that you can add several classes to the same element by separating each of them with a space, like this: <h2 class=\"red-text text-center\">your text</h2>."
],
"tests": [
"assert($(\"h2\").hasClass(\"text-center\"), 'Your h2 element should be centered by applying the class text-center')"
@@ -345,7 +345,7 @@
"id": "bad87fee1348cd8acef08812",
"title": "Create a Block Element Bootstrap Button",
"description": [
- "Normally, your button 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 button 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 and any elements following it will flow onto a \"new line\" below the block.",
"This image illustrates the difference between inline elements and block-level elements:",
"",
"Note that these buttons still need the btn class.",
@@ -892,8 +892,7 @@
"",
"By using the span 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 span element. Then give that span the class text-danger to make the text red.",
- "Here's how you would do this with the \"Top 3 things cats hate\" element:",
- "<p>Top 3 things cats <span class = \"text-danger\">hate</span></p>"
+ "Here's how you would do this with the \"Top 3 things cats hate\" element: <p>Top 3 things cats <span class = \"text-danger\">hate</span></p>"
],
"tests": [
"assert($(\"p span\") && $(\"p span\").length > 0, 'Your span element should be inside your p element.')",
@@ -1909,7 +1908,7 @@
"title": "Add ID Attributes to Bootstrap Elements",
"description": [
"Recall that in addition to class attributes, you can give each of your elements an id attribute.",
- "Each id should be unique to a specific element.",
+ "Each id must be unique to a specific element and used only once per page.",
"Let's give a unique id to each of our div elements of class well.",
"Remember that you can give an element an id like this:",
"<div class=\"well\" id=\"center-well\">",
@@ -2008,7 +2007,7 @@
"title": "Give Each Element a Unique ID",
"description": [
"We will also want to be able to use jQuery to target each button by its unique id.",
- "Give each of your buttons a unique id, starting with target1 and ending with target6."
+ "Give each of your buttons a unique id like, starting with target1 and ending with target6."
],
"tests": [
"assert($(\"#left-well\").children(\"#target1\") && $(\"#left-well\").children(\"#target1\").length > 0, 'One button element should have the id target1.')",
@@ -2059,7 +2058,7 @@
"title": "Label Bootstrap Buttons",
"description": [
"Just like we labeled our wells, we want to label our buttons.",
- "Give each of your button elements text that corresponds to its id."
+ "Give each of your button elements text that corresponds to their id."
],
"tests": [
"assert(new RegExp(\"#target1\",\"gi\").test($(\"#target1\").text()), 'Give your button element with the id target1 the text #target1.')",
@@ -2116,7 +2115,7 @@
],
"tests": [
"assert(editor.match(/.*\\n+.+/g), 'Be sure to close your comment with -->.')"
],
"challengeSeed": [
diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json
index 1ebf6e3858..58adc46b54 100644
--- a/seed/challenges/html5-and-css.json
+++ b/seed/challenges/html5-and-css.json
@@ -390,6 +390,7 @@
"assert(!$(\"h2\").attr(\"style\"), 'Remove the style attribute from your h2 element.')",
"assert($(\"style\") && $(\"style\").length > 1, 'Create a style element.')",
"assert($(\"h2\").css(\"color\") === \"rgb(0, 0, 255)\", 'Your h2 element should be blue.')",
+ "assert(editor.match(/h2\\s*\\{\\s*color:\\s*blue;\\s*\\}/g), 'Ensure that your stylesheet h2 declaration is valid with a semicolon and closing brace')",
"assert(editor.match(/<\\/style>/g) && editor.match(/<\\/style>/g).length === (editor.match(/