diff --git a/client/commonFramework.js b/client/commonFramework.js index 8abcb6a9dc..a28a1e743d 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -1,31 +1,72 @@ -// common namespace -// all classes should be stored here -var common = common || { - // init is an array of functions that are - // called at the beginning of dom ready - init: [] -}; +var common = (function() { + // common namespace + // all classes should be stored here + var common = window.common || { + // init is an array of functions that are + // called at the beginning of dom ready + init: [] + }; -common.challengeName = common.challengeName || window.challenge_Name ? - window.challenge_Name : - ''; + common.challengeName = common.challengeName || window.challenge_Name ? + window.challenge_Name : + ''; -common.challengeType = common.challengeType || window.challengeType ? - window.challengeType : - 0; + common.challengeType = common.challengeType || window.challengeType ? + window.challengeType : + 0; -common.challengeId = common.challengeId || window.challenge_Id; + common.challengeId = common.challengeId || window.challenge_Id; -common.challengeSeed = common.challengeSeed || window.challengeSeed ? - window.challengeSeed : - []; + common.challengeSeed = common.challengeSeed || window.challengeSeed ? + window.challengeSeed : + []; -common.seed = common.challengeSeed.reduce(function(seed, line) { - return seed + line + '\n'; -}, ''); + common.seed = common.challengeSeed.reduce(function(seed, line) { + return seed + line + '\n'; + }, ''); + + common.replaceScriptTags = function replaceScriptTags(value) { + return value + .replace(/'); + }; + + common.replaceFormActionAttr = function replaceFormAction(value) { + return value.replace(/]*>/, function(val) { + return val.replace(/action(\s*?)=/, 'fccfaa$1='); + }); + }; + + common.replaceFccfaaAttr = function replaceFccfaaAttr(value) { + return value.replace(/]*>/, function(val) { + return val.replace(/fccfaa(\s*?)=/, 'action$1='); + }); + }; + + return common; +})(); // store code in the URL common.codeUri = (function(common, encode, decode, location, history) { + var replaceScriptTags = common.replaceScriptTags; + var replaceSafeTags = common.replaceSafeTags; + var replaceFormActionAttr = common.replaceFormActionAttr; + var replaceFccfaaAttr = common.replaceFccfaaAttr; + + function encodeFcc(val) { + return replaceScriptTags(replaceFormActionAttr(val)); + } + + function decodeFcc(val) { + return replaceSafeTags(replaceFccfaaAttr(val)); + } + var codeUri = { encode: function(code) { return encode(code); @@ -54,10 +95,14 @@ common.codeUri = (function(common, encode, decode, location, history) { }, false); }, isAlive: function() { - return codeUri.isInQuery(location.search) || + return codeUri.enabled && + codeUri.isInQuery(location.search) || codeUri.isInQuery(location.hash); }, parse: function() { + if (!codeUri.enabled) { + return null; + } var query; if (location.search && codeUri.isInQuery(location.search)) { query = location.search.replace(/^\?/, ''); @@ -67,7 +112,7 @@ common.codeUri = (function(common, encode, decode, location, history) { null, location.href.split('?')[0] ); - location.hash = '#?' + query; + location.hash = '#?' + encodeFcc(query); } } else { query = location.hash.replace(/^\#\?/, ''); @@ -82,15 +127,21 @@ common.codeUri = (function(common, encode, decode, location, history) { var key = param.split('=')[0]; var value = param.split('=')[1]; if (key === 'solution') { - return codeUri.decode(value); + return decodeFcc(codeUri.decode(value || '')); } return solution; }, null); }, querify: function(solution) { - location.hash = '?solution=' + codeUri.encode(solution); + if (!codeUri.enabled) { + return null; + } + location.hash = '?solution=' + + codeUri.encode(encodeFcc(solution)); + return solution; - } + }, + enabled: true }; common.init.push(function() { @@ -306,12 +357,6 @@ var sandBox = (function(jailed, codeOutput) { return sandBox; }(window.jailed, common.codeOutput)); -function replaceSafeTags(value) { - return value - .replace(/fccss/gi, ''); -} - var BDDregex = new RegExp( '(expect(\\s+)?\\(.*\\;)|' + '(assert(\\s+)?\\(.*\\;)|' + @@ -408,7 +453,6 @@ var editor = (function(CodeMirror, emmetCodeMirror, common) { common.init.push(function() { var editorValue; if (common.codeUri.isAlive()) { - console.log('in query'); editorValue = common.codeUri.parse(); } else { editorValue = codeStorage.isAlive() ? @@ -416,7 +460,7 @@ var editor = (function(CodeMirror, emmetCodeMirror, common) { common.seed; } - editor.setValue(replaceSafeTags(editorValue)); + editor.setValue(common.replaceSafeTags(editorValue)); editor.refresh(); }); @@ -445,6 +489,7 @@ function workerError(error) { var housing = $('#testSuite'); if (display.html() !== error) { display.remove(); + housing.prepend( '
' + error.replace(/j\$/gi, '$').replace(/jdocument/gi, 'document').replace(/jjQuery/gi, 'jQuery') + @@ -471,7 +516,10 @@ function safeHTMLRun(test) { var codeStorage = common.codeStorage; if (common.challengeType === '0') { var previewFrame = document.getElementById('preview'); - var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; + + var preview = previewFrame.contentDocument || + previewFrame.contentWindow.document; + if (editor.getValue().match(/\/gi) !== null) { var s = editor .getValue() @@ -540,9 +588,11 @@ function updatePreview() { if (typeof prodOrDev !== 'undefined') { - var nodeEnv = prodOrDev === 'production' ? + /* eslint-disable no-unused-vars */ + var nodeEnv = window.prodOrDev === 'production' ? 'http://www.freecodecamp.com' : 'http://localhost:3001'; + /* eslint-enable no-unused-vars */ if (common.challengeType === '0') { setTimeout(updatePreview, 300); @@ -553,8 +603,11 @@ if (typeof prodOrDev !== 'undefined') { * "post" methods */ +/* eslint-disable no-unused-vars */ var testResults = []; var postSuccess = function(data) { +/* eslint-enable no-unused-vars */ + var testDoc = document.createElement('div'); $(testDoc).html( "
" + @@ -600,7 +653,9 @@ function showCompletion() { isInitRun = false; return; } - var time = Math.floor(Date.now()) - started; + + var time = Math.floor(Date.now()) - window.started; + ga( 'send', 'event', @@ -658,8 +713,11 @@ function showCompletion() { }); } +/* eslint-disable no-unused-vars */ var resetEditor = function resetEditor() { - editor.setValue(replaceSafeTags(common.seed)); +/* eslint-enable no-unused-vars */ + + editor.setValue(common.replaceSafeTags(common.seed)); $('#testSuite').empty(); bonfireExecute(true); common.codeStorage.updateStorage(); @@ -849,15 +907,15 @@ common.init.push((function() { var nextStep = getNextStep($(stepClass)); $(this) .parent() - .addClass('animated fadeOutLeft') - .delay(700) + .addClass('animated fadeOutLeft fast-animation') + .delay(250) .queue(function(next) { $(this).addClass('hidden'); if (nextStep) { $(nextStep) .removeClass('hidden') - .addClass('animated slideInRight') - .delay(1000) + .addClass('animated slideInRight fast-animation') + .delay(500) .queue(function(next) { $(this).removeClass('slideInRight'); next(); @@ -865,14 +923,53 @@ common.init.push((function() { } next(); }); - } - function handleActionClick() { - $(this) - .parent() - .find('.disabled') - .removeClass('disabled'); + function handleActionClick(e) { + var props = common.challengeSeed[0] || + { stepIndex: [] }; + + var $el = $(this); + var index = +$el.attr('id'); + var propIndex = props.stepIndex.indexOf(index); + + if (propIndex === -1) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + + // an API action + // prevent link from opening + e.preventDefault(); + var prop = props.properties[propIndex]; + var api = props.apis[propIndex]; + if (common[prop]) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + $ + .post(api) + .done(function(data) { + // assume a boolean indicates passing + if (typeof data === 'boolean') { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + // assume api returns string when fails + $el + .parent() + .find('.disabled') + .replaceWith('

' + data + '

'); + }) + .fail(function() { + console.log('failed'); + }); } function handleFinishClick(e) { diff --git a/client/less/main.less b/client/less/main.less index 067290e9be..4340350da7 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -490,11 +490,17 @@ thead { border-radius: 5px; } +.story-section { + height: 500px; +} + .testimonial-copy { - font-size: 20px; - text-align: center; + text-align: justify; + font-size: 18px !important; + margin-left: 20px; + margin-right: 20px; @media (min-width: 991px) and (max-width: 1199px) { - height: 120px; + height: 140px; } @media (min-width: 1200px) { height: 90px; diff --git a/client/main.js b/client/main.js index c1578c1d6f..892610e1eb 100644 --- a/client/main.js +++ b/client/main.js @@ -1,12 +1,21 @@ -var mapShareKey = 'map-shares'; +var main = window.main || {}; + +main.mapShareKey = 'map-shares'; + +main.ga = window.ga || function() {}; + var lastCompleted = typeof lastCompleted !== 'undefined' ? lastCompleted : ''; function getMapShares() { - var alreadyShared = JSON.parse(localStorage.getItem(mapShareKey) || '[]'); + var alreadyShared = JSON.parse( + localStorage.getItem(main.mapShareKey) || + '[]' + ); + if (!alreadyShared || !Array.isArray(alreadyShared)) { - localStorage.setItem(mapShareKey, JSON.stringify([])); + localStorage.setItem(main.mapShareKey, JSON.stringify([])); alreadyShared = []; } return alreadyShared; @@ -23,7 +32,7 @@ function setMapShare(id) { if (!found) { alreadyShared.push(id); } - localStorage.setItem(mapShareKey, JSON.stringify(alreadyShared)); + localStorage.setItem(main.mapShareKey, JSON.stringify(alreadyShared)); return alreadyShared; } @@ -133,6 +142,7 @@ $(document).ready(function() { type = 'html'; break; case challengeTypes.JAVASCRIPT: + case challengeTypes.BONFIRE: type = 'javascript'; break; default: @@ -444,6 +454,7 @@ $(document).ready(function() { '&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap'; setMapShare(challengeBlockName); + main.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share'); window.location.href = link; }); }); diff --git a/common/app/flux/Store.js b/common/app/flux/Store.js index c745e16ac0..30ee4e7ff3 100644 --- a/common/app/flux/Store.js +++ b/common/app/flux/Store.js @@ -8,13 +8,17 @@ const initValue = { points: 0 }; -export default Store(initValue) - .refs({ displayName: 'AppStore' }) - .init(({ instance: appStore, args: [cat] }) => { +export default Store({ + refs: { + displayName: 'AppStore', + value: initValue + }, + init({ instance: appStore, args: [cat] }) { const { setUser, setTitle } = cat.getActions('appActions'); const register = createRegistrar(appStore); register(setter(fromMany(setUser, setTitle))); return appStore; - }); + } +}); diff --git a/common/app/routes/Hikes/components/Lecture.jsx b/common/app/routes/Hikes/components/Lecture.jsx index ea3c85191d..e47dc0fc98 100644 --- a/common/app/routes/Hikes/components/Lecture.jsx +++ b/common/app/routes/Hikes/components/Lecture.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Button, Col, Row, Panel } from 'react-bootstrap'; -import { Navigation } from 'react-router'; +import { History } from 'react-router'; import Vimeo from 'react-vimeo'; import debugFactory from 'debug'; @@ -8,7 +8,7 @@ const debug = debugFactory('freecc:hikes'); export default React.createClass({ displayName: 'Lecture', - mixins: [Navigation], + mixins: [History], propTypes: { currentHike: PropTypes.object, @@ -20,7 +20,7 @@ export default React.createClass({ handleFinish() { debug('loading questions'); const { dashedName } = this.props.params; - this.transitionTo(`/hikes/${dashedName}/questions/1`); + this.history.pushState(null, `/hikes/${dashedName}/questions/1`); }, renderTranscript(transcript, dashedName) { diff --git a/common/app/routes/Hikes/components/Question.jsx b/common/app/routes/Hikes/components/Question.jsx index e7fe7662d9..4f7e97f149 100644 --- a/common/app/routes/Hikes/components/Question.jsx +++ b/common/app/routes/Hikes/components/Question.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Spring } from 'react-motion'; -import { Navigation, TransitionHook } from 'react-router'; +import { History, Lifecycle } from 'react-router'; import debugFactory from 'debug'; import { Button, @@ -19,8 +19,8 @@ export default React.createClass({ displayName: 'Question', mixins: [ - Navigation, - TransitionHook + History, + Lifecycle ], propTypes: { @@ -150,7 +150,8 @@ export default React.createClass({ postJSON$('/completed-challenge', { id, name }).subscribeOnCompleted(() => { if (tests[nextQuestionIndex]) { - return this.transitionTo( + return this.history.pushState( + null, `/hikes/${ dashedName }/questions/${ nextQuestionIndex + 1 }` ); } @@ -168,13 +169,13 @@ export default React.createClass({ }, null); if (nextHike) { - return this.transitionTo(`/hikes/${ nextHike.dashedName }`); + return this.history.pushState(null, `/hikes/${ nextHike.dashedName }`); } debug( 'next Hike was not found, currentHike %s', currentHike.dashedName ); - this.transitionTo('/hikes'); + this.history.pushState(null, '/hikes'); }); }, diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index 2d9d44a4fd..f6ed407fae 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -3,7 +3,7 @@ import debugFactory from 'debug'; const debug = debugFactory('freecc:hikes:actions'); -function getCurrentHike(hikes =[{}], dashedName, currentHike) { +function getCurrentHike(hikes = [{}], dashedName, currentHike) { if (!dashedName) { debug('no dashedName'); return hikes[0]; diff --git a/common/app/routes/Hikes/flux/Store.js b/common/app/routes/Hikes/flux/Store.js index 4c7080b1c2..9755ed679a 100644 --- a/common/app/routes/Hikes/flux/Store.js +++ b/common/app/routes/Hikes/flux/Store.js @@ -5,12 +5,16 @@ const initialValue = { currentHike: {} }; -export default Store(initialValue) - .refs({ displayName: 'HikesStore'}) - .init(({ instance: hikeStore, args: [cat] }) => { +export default Store({ + refs: { + displayName: 'HikesStore', + value: initialValue + }, + init({ instance: hikeStore, args: [cat] }) { let { setHikes } = cat.getActions('hikesActions'); hikeStore.register(setHikes); return hikeStore; - }); + } +}); diff --git a/common/app/routes/Jobs/flux/Store.js b/common/app/routes/Jobs/flux/Store.js index b2f5132013..8f3acd2949 100644 --- a/common/app/routes/Jobs/flux/Store.js +++ b/common/app/routes/Jobs/flux/Store.js @@ -6,9 +6,12 @@ const { transformer } = Store; -export default Store({ showModal: false }) - .refs({ displayName: 'JobsStore' }) - .init(({ instance: jobsStore, args: [cat] }) => { +export default Store({ + refs: { + displayName: 'JobsStore', + value: { showModal: false } + }, + init({ instance: jobsStore, args: [cat] }) { const { setJobs, findJob, @@ -27,4 +30,5 @@ export default Store({ showModal: false }) register(transformer(findJob)); register(handleForm); - }); + } +}); diff --git a/common/models/pledge.json b/common/models/pledge.json new file mode 100644 index 0000000000..08e46df414 --- /dev/null +++ b/common/models/pledge.json @@ -0,0 +1,55 @@ +{ + "name": "pledge", + "base": "PersistedModel", + "idInjection": true, + "trackChanges": false, + "properties": { + "nonprofit": { + "type": "string", + "index": true + }, + "amount": { + "type": "number" + }, + "dateStarted": { + "type": "date", + "defaultFn": "now" + }, + "dateEnded": { + "type": "date" + }, + "formerUserId": { + "type": "string" + }, + "isOrphaned": { + "type": "boolean" + }, + "isCompleted": { + "type": "boolean", + "default": "false" + } + }, + "validations": [], + "relations": { + "user": { + "type": "hasMany", + "model": "user", + "foreignKey": "userId" + } + }, + "acls": [ + { + "accessType": "*", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "DENY" + }, + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ], + "methods": [] +} diff --git a/common/models/user.js b/common/models/user.js index 06761e0209..bb746ed8d8 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -322,7 +322,7 @@ module.exports = function(User) { }) // no results means this is the first brownie point given by giver // so return -1 to indicate receiver should receive point - .firstOrDefault(null, -1) + .first({ defaultValue: -1 }) .flatMap((browniePointsFromGiver) => { if (browniePointsFromGiver === -1) { diff --git a/common/models/user.json b/common/models/user.json index c46d1c2f13..f85cc1023c 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -102,14 +102,31 @@ }, "isLocked": { "type": "boolean", - "default": false + "default": false, + "description": "Campers profile does not show challenges/certificates to the public" }, "currentChallenge": { "type": {} }, "isUniqMigrated": { "type": "boolean", - "default": false + "default": false, + "description": "Campers completedChallenges array is free of duplicates" + }, + "isHonest": { + "type": "boolean", + "default": false, + "description": "Camper has signed academic honesty policy" + }, + "isFrontEndCert": { + "type": "boolean", + "defaut": false, + "description": "Camper is front end certified" + }, + "isFullStackCert": { + "type": "boolean", + "default": false, + "description": "Campers is full stack certified" }, "completedChallenges": { "type": [ @@ -149,6 +166,11 @@ "type": "hasMany", "model": "userIdentity", "foreignKey": "" + }, + "pledge": { + "type": "hasOne", + "model": "pledge", + "foreignKey": "" } }, "acls": [ diff --git a/package.json b/package.json index c3f7c0bebb..06d6a933ce 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "json-loader": "^0.5.2", "less": "~2.5.1", "lodash": "^3.9.3", - "loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password", - "loopback-boot": "2.8.2", + "loopback": "^2.22.0", + "loopback-boot": "^2.13.0", "loopback-component-passport": "https://github.com/FreeCodeCamp/loopback-component-passport.git#feature/flashfailure", "loopback-connector-mongodb": "^1.10.0", "lusca": "~1.0.2", @@ -89,17 +89,17 @@ "react": "^0.13.3", "react-bootstrap": "~0.23.7", "react-motion": "~0.1.0", - "react-router": "^1.0.0-rc1", + "react-router": "https://github.com/BerkeleyTrue/react-router.git#freecodecamp", "react-vimeo": "^0.0.3", "request": "~2.53.0", "rev-del": "^1.0.5", - "rx": "^2.5.3", + "rx": "^4.0.0", "sanitize-html": "~1.6.1", "sort-keys": "^1.1.1", "source-map-support": "^0.3.2", "store": "https://github.com/berkeleytrue/store.js.git#feature/noop-server", - "thundercats": "^2.1.0", - "thundercats-react": "^0.1.0", + "thundercats": "^3.0.0", + "thundercats-react": "^0.3.0", "twit": "~1.1.20", "uglify-js": "~2.4.15", "validator": "^3.22.1", diff --git a/public/fonts/saxmono.ttf b/public/fonts/saxmono.ttf new file mode 100755 index 0000000000..76c77d6411 Binary files /dev/null and b/public/fonts/saxmono.ttf differ diff --git a/seed/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json index db81a04400..8ff025e2fe 100644 --- a/seed/challenges/advanced-bonfires.json +++ b/seed/challenges/advanced-bonfires.json @@ -1,6 +1,7 @@ { "name": "Advanced Algorithm Scripting", "order": 15, + "time": "50h", "challenges": [ { "id": "aff0395860f5d3034dc0bfc9", @@ -39,7 +40,9 @@ "assert(telephoneCheck(\"27576227382\") === false, 'message: telephoneCheck(\"27576227382\") should return false.');", "assert(telephoneCheck(\"(275)76227382\") === false, 'message: telephoneCheck(\"(275)76227382\") should return false.');", "assert(telephoneCheck(\"2(757)6227382\") === false, 'message: telephoneCheck(\"2(757)6227382\") should return false.');", - "assert(telephoneCheck(\"2(757)622-7382\") === false, 'message: telephoneCheck(\"2(757)622-7382\") should return false.');" + "assert(telephoneCheck(\"2(757)622-7382\") === false, 'message: telephoneCheck(\"2(757)622-7382\") should return false.');", + "assert(telephoneCheck(\"555)-555-5555\") === false, 'message: telephoneCheck(\"555)555-5555\") should return false.');", + "assert(telephoneCheck(\"(555-555-5555\") === false, 'message: telephoneCheck(\"(555-555-5555\") should return false.');" ], "challengeSeed": [ "function telephoneCheck(str) {", @@ -142,6 +145,7 @@ "assert.deepEqual(drawer(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]]), [[\"QUARTER\", 0.50]], 'message: drawer(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]]) should return [[\"QUARTER\", 0.50]].');", "assert.deepEqual(drawer(3.26, 100.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]]), [[\"TWENTY\", 60.00], [\"TEN\", 20.00], [\"FIVE\", 15], [\"ONE\", 1], [\"QUARTER\", 0.50], [\"DIME\", 0.20], [\"PENNY\", 0.04]], 'message: drawer(3.26, 100.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]]) should return [[\"TWENTY\", 60.00], [\"TEN\", 20.00], [\"FIVE\", 15], [\"ONE\", 1], [\"QUARTER\", 0.50], [\"DIME\", 0.20], [\"PENNY\", 0.04]].');", "assert.deepEqual(drawer(19.50, 20.00, [[\"PENNY\", 0.01], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 0], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]), \"Insufficient Funds\", 'message: drawer(19.50, 20.00, [[\"PENNY\", 0.01], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 0], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]) should return \"Insufficient Funds\".');", + "assert.deepEqual(drawer(19.50, 20.00, [[\"PENNY\", 0.01], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 1.00], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]), \"Insufficient Funds\", 'message: drawer(19.50, 20.00, [[\"PENNY\", 0.01], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 1.00], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]) should return \"Insufficient Funds\".');", "assert.deepEqual(drawer(19.50, 20.00, [[\"PENNY\", 0.50], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 0], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]), \"Closed\", 'message: drawer(19.50, 20.00, [[\"PENNY\", 0.50], [\"NICKEL\", 0], [\"DIME\", 0], [\"QUARTER\", 0], [\"ONE\", 0], [\"FIVE\", 0], [\"TEN\", 0], [\"TWENTY\", 0], [\"ONE HUNDRED\", 0]]) should return \"Closed\".');" ], "MDNlinks": [ diff --git a/seed/challenges/angularjs.json b/seed/challenges/angularjs.json index 92c85e751b..905c120f33 100644 --- a/seed/challenges/angularjs.json +++ b/seed/challenges/angularjs.json @@ -1,6 +1,7 @@ { "name": "AngularJS", "order": 16, + "time": "5h", "challenges": [ { "id": "bd7154d8c441eddfaeb5bdef", diff --git a/seed/challenges/automated-testing-and-debugging.json b/seed/challenges/automated-testing-and-debugging.json index b5ab25c2f9..0c7304045b 100644 --- a/seed/challenges/automated-testing-and-debugging.json +++ b/seed/challenges/automated-testing-and-debugging.json @@ -1,6 +1,7 @@ { "name": "Automated Testing and Debugging", "order": 14, + "time": "15m", "challenges": [ { "id":"cf1111c1c16feddfaeb6bdef", diff --git a/seed/challenges/basejumps.json b/seed/challenges/basejumps.json index c0dc0a0ba3..627e95482f 100644 --- a/seed/challenges/basejumps.json +++ b/seed/challenges/basejumps.json @@ -1,6 +1,7 @@ { "name": "Full Stack JavaScript Projects", "order": 20, + "time": "300h", "challenges": [ { "id": "bd7158d8c443eddfaeb5bcef", @@ -52,7 +53,7 @@ "Before you publish to Heroku, you should free up as much memory as possible on Cloud9. In each of the Cloud9 terminal prompt tabs where MongoDB and Grunt are running, press the control + c hotkey to shut down these processes.", "Run the following command in a Cloud9 terminal prompt tab: npm install grunt-contrib-imagemin --save-dev && npm install --save-dev && heroku login. At this point, the terminal will prompt you to log in to Heroku from the command line.", "Now run yo angular-fullstack:heroku. You can choose a name for your Heroku project, or Heroku will create a random one for you. You can choose whether you want to deploy to servers the US or the EU.", - "Set the config flag for your Heroku environment and add MongoLab for your MongoDB instance by running the following command: cd ~/workspace/dist && heroku config:set NODE_ENV=production && heroku addons:create mongolab.", + "Set the config flag for your Heroku environment and add MongoLab for your MongoDB instance by running the following command: cd ~/workspace/dist && heroku config:set NODE_ENV=production && heroku addons:add mongolab.", "As you build your app, you should frequently commit changes to your codebase. Make sure you're in the ~/workspace directory by running cd ~/workspace. Then you can use this code to stage the changes to your changes and commit them: git commit -am \"your commit message\". Note that you should replace \"your commit message\" with a short summary of the changes you made to your code, such as \"added a records controller and corresponding routes\".", "You can push these new commits to GitHub by running git push origin master, and to Heroku by running grunt --force && grunt buildcontrol:heroku.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 931c157ce1..d3a0f22893 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -1,6 +1,7 @@ { "name": "Basic Algorithm Scripting", - "order": 7, + "order": 8, + "time": "50h", "challenges": [ { "id": "ad7123c8c441eddfaeb5bdef", @@ -60,7 +61,7 @@ " return str;", "}", "", - "reverseString(\"hello\", \"\");" + "reverseString(\"hello\");" ], "MDNlinks": [ "Global String Object", @@ -103,7 +104,7 @@ " return num;", "}", "", - "factorialize(5, '');" + "factorialize(5);" ], "MDNlinks": [ "Arithmetic Operators" @@ -224,7 +225,7 @@ " return str;", "}", "", - "titleCase(\"I'm a little tea pot\", \"\");" + "titleCase(\"I'm a little tea pot\");" ], "tests": [ "assert(typeof(titleCase(\"I'm a little tea pot\")) === \"string\", 'message: titleCase() should return a string.');", @@ -300,13 +301,14 @@ " return str;", "}", "", - "end(\"Bastian\", \"n\", \"\");" + "end(\"Bastian\", \"n\");" ], "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(\"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.');" ], "MDNlinks": [ @@ -338,7 +340,7 @@ " return str;", "}", "", - "repeat(\"abc\", 3, \"\");" + "repeat(\"abc\", 3);" ], "tests": [ "assert(repeat(\"*\", 3) === \"***\", 'message: repeat(\"*\", 3) should return \"***\".');", @@ -367,6 +369,7 @@ "description": [ "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.", + "If the length of the string is less than or equal to 3 characters, then the length of the three dots is not added to the string length.", "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ @@ -375,13 +378,14 @@ " return str;", "}", "", - "truncate(\"A-tisket a-tasket A green and yellow basket\", 11, \"\");" + "truncate(\"A-tisket a-tasket A green and yellow basket\", 11);" ], "tests": [ - "assert(truncate(\"A-tisket a-tasket A green and yellow basket\", 11) === \"A-tisket...\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", 1) should return \"A-tisket...\".');", + "assert(truncate(\"A-tisket a-tasket A green and yellow basket\", 11) === \"A-tisket...\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", 11) should return \"A-tisket...\".');", "assert(truncate(\"Peter Piper picked a peck of pickled peppers\", 14) === \"Peter Piper...\", 'message: truncate(\"Peter Piper picked a peck of pickled peppers\", 14) should return \"Peter Piper...\".');", "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\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length) should return \"A-tisket a-tasket A green and yellow basket\".');", - "assert(truncate('A-tisket a-tasket A green and yellow basket', 'A-tisket a-tasket A green and yellow basket'.length + 2) === 'A-tisket a-tasket A green and yellow basket', 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length + 2) should return \"A-tisket a-tasket A green and yellow basket\".');" + "assert(truncate('A-tisket a-tasket A green and yellow basket', 'A-tisket a-tasket A green and yellow basket'.length + 2) === 'A-tisket a-tasket A green and yellow basket', 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length + 2) should return \"A-tisket a-tasket A green and yellow basket\".');", + "assert(truncate(\"A-\", 1) === \"A...\", 'message: truncate(\"A-\", 1) should return \"A...\".');" ], "MDNlinks": [ "String.slice()" @@ -412,7 +416,7 @@ " return arr;", "}", "", - "chunk([\"a\", \"b\", \"c\", \"d\"], 2, \"\");" + "chunk([\"a\", \"b\", \"c\", \"d\"], 2);" ], "tests": [ "assert.deepEqual(chunk([\"a\", \"b\", \"c\", \"d\"], 2), [[\"a\", \"b\"], [\"c\", \"d\"]], 'message: chunk([\"a\", \"b\", \"c\", \"d\"], 2) should return [[\"a\", \"b\"], [\"c\", \"d\"]].');", @@ -450,12 +454,13 @@ " return arr;", "}", "", - "slasher([1, 2, 3], 2, \"\");" + "slasher([1, 2, 3], 2);" ], "tests": [ - "assert.deepEqual(slasher([1, 2, 3], 2), [3], 'message: slasher([1, 2, 3], 2, [3]) should return [3].');", + "assert.deepEqual(slasher([1, 2, 3], 2), [3], 'message: slasher([1, 2, 3], 2) should return [3].');", "assert.deepEqual(slasher([1, 2, 3], 0), [1, 2, 3], 'message: slasher([1, 2, 3], 0) should return [1, 2, 3].');", - "assert.deepEqual(slasher([1, 2, 3], 9), [], 'message: slasher([1, 2, 3], 9) should return [].');" + "assert.deepEqual(slasher([1, 2, 3], 9), [], 'message: slasher([1, 2, 3], 9) should return [].');", + "assert.deepEqual(slasher([1, 2, 3], 4), [], 'message: slasher([1, 2, 3], 4) should return [].');" ], "MDNlinks": [ "Array.slice()", @@ -489,7 +494,7 @@ " return arr;", "}", "", - "mutation([\"hello\", \"hey\"], \"\");" + "mutation([\"hello\", \"hey\"]);" ], "tests": [ "assert(mutation([\"hello\", \"hey\"]) === false, 'message: mutation([\"hello\", \"hey\"]) should return false.');", @@ -531,7 +536,7 @@ " return arr;", "}", "", - "bouncer([7, \"ate\", \"\", false, 9], \"\");" + "bouncer([7, \"ate\", \"\", false, 9]);" ], "tests": [ "assert.deepEqual(bouncer([7, \"ate\", \"\", false, 9]), [7, \"ate\", 9], 'message: bouncer([7, \"ate\", \"\", false, 9]) should return [7, \"ate\", 9].');", @@ -568,7 +573,7 @@ " return arr;", "}", "", - "destroyer([1, 2, 3, 1, 2, 3], 2, 3, \"\");" + "destroyer([1, 2, 3, 1, 2, 3], 2, 3);" ], "tests": [ "assert.deepEqual(destroyer([1, 2, 3, 1, 2, 3], 2, 3), [1, 1], 'message: destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1].');", @@ -608,7 +613,7 @@ " return num;", "}", "", - "where([40, 60], 50, \"\");" + "where([40, 60], 50);" ], "MDNlinks": [ "Array.sort()" diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index c10af0def1..e215cddda0 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -1,6 +1,7 @@ { "name": "Basic JavaScript", - "order": 5, + "order": 6, + "time": "3h", "challenges": [ { "id":"bd7123c9c441eddfaeb4bdef", @@ -529,7 +530,7 @@ ], "tests": [ "assert((function(d){if(d[0] == 'John' && d[1] == 23 && d[2] == undefined){return true;}else{return false;}})(myArray), 'message: myArray should only contain [\"John\", 23].');", - "assert((function(d){if(d[0] == 'cat' && d[1] == 2 && d[2] == undefined){return true;}else{return false;}})(removed), 'message: removed should only contain [\"cat\"], 2.');" + "assert((function(d){if(d[0] == 'cat' && d[1] == 2 && d[2] == undefined){return true;}else{return false;}})(removed), 'message: removed should only contain [\"cat\", 2].');" ], "challengeSeed": [ "// var numbers = [1,2,3];", @@ -802,7 +803,7 @@ "assert.deepEqual(myArray, [0,1,2,3,4], 'message: myArray should equal [0,1,2,3,4].');" ], "challengeSeed":[ - "ourArray = [];", + "var ourArray = [];", "for(var i = 0; i < 5; i++){", " ourArray.push(i);", "}", @@ -939,7 +940,7 @@ "var min = 0;", "var max = 9;", "function myFunction() {", - " // Make myFunction return a random number between zero and nine instead of a decimal", + " // Make myFunction return a random number between min and max values instead of a decimal", " // Only change code below this line.", "", " return Math.random();", @@ -1039,7 +1040,7 @@ ], "tests":[ "assert(test === 2, 'message: Your RegEx should have found two numbers in the testString.');", - "assert(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'message: You should be using the following expression /\\\\d+/gi to find the numbers in the testString.');" + "assert(editor.getValue().match(/\\/\\\\d\\+\\//g), 'message: You should be using the following expression /\\d+/g to find the numbers in the testString.');" ], "challengeSeed":[ "var test = (function() {", @@ -1047,7 +1048,7 @@ "", " // Only change code below this line.", "", - " var expression = /.+/gi;", + " var expression = /.+/g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", @@ -1059,17 +1060,18 @@ }, { "id":"cf1111c1c12feddfaeb8bdef", - "title": "Find White Space with Regular Expressions", + "title": "Find Whitespace with Regular Expressions", "difficulty":"9.986", "description":[ - "We can also use selectors like \\s to find spaces in a string.", + "We can also use selectors like \\s to find whitespace in a string.", + "The whitespace characters are \" \" (space), \\r (carriage return), \\n (newline), \\t (tab), and \\f (form feed).", "It is used like this:", "/\\s+/g", - "Select all the spaces in the sentence string." + "Select all the whitespace characters in the sentence string." ], "tests":[ "assert(test === 7, 'message: Your RegEx should have found seven spaces in the testString.');", - "assert(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'message: You should be using the following expression /\\\\s+/gi to find the spaces in the testString.');" + "assert(editor.getValue().match(/\\/\\\\s\\+\\//g), 'message: You should be using the following expression /\\s+/g to find the spaces in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1077,7 +1079,7 @@ "", " // Only change code below this line.", "", - " var expression = /.+/gi;", + " var expression = /.+/g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", @@ -1092,12 +1094,12 @@ "title": "Invert Regular Expression Matches with JavaScript", "difficulty":"9.987", "description":[ - "Use /\\S/gi to match everything that isn't a space in the string.", + "Use /\\S/g to match everything that isn't a space in the string.", "You can invert any match by using the uppercase version of the selector \\s versus \\S for example." ], "tests":[ "assert(test === 49, 'message: Your RegEx should have found forty nine non-space characters in the testString.');", - "assert(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'message: You should be using the following expression /\\\\S/gi to find non-space characters in the testString.');" + "assert(editor.getValue().match(/\\/\\\\S\\/g/g), 'message: You should be using the following expression /\\S/g to find non-space characters in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1105,7 +1107,7 @@ "", " // Only change code below this line.", "", - " var expression = /./gi;", + " var expression = /./g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", @@ -1127,10 +1129,10 @@ "Math.floor(Math.random() * (3 - 1 + 1)) + 1;" ], "tests":[ - "assert(typeof(runSlots($(\".slot\"))[0]) === \"number\", 'message: slotOne should be a random number.')", - "assert(typeof(runSlots($(\".slot\"))[1]) === \"number\", 'message: slotTwo should be a random number.')", - "assert(typeof(runSlots($(\".slot\"))[2]) === \"number\", 'message: slotThree should be a random number.')", - "assert((function(){if(editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi) !== null){return editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi).length >= 3;}else{return false;}})(), 'message: You should have used Math.floor(Math.random() * (3 - 1 + 1)) + 1; three times to generate your random numbers.')" + "assert(typeof(runSlots($(\".slot\"))[0]) === \"number\", 'slotOne should be a random number.')", + "assert(typeof(runSlots($(\".slot\"))[1]) === \"number\", 'slotTwo should be a random number.')", + "assert(typeof(runSlots($(\".slot\"))[2]) === \"number\", 'slotThree should be a random number.')", + "assert((function(){if(editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi) !== null){return editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi).length >= 3;}else{return false;}})(), 'You should have used Math.floor(Math.random() * (3 - 1 + 1)) + 1; three times to generate your random numbers.')" ], "challengeSeed":[ "fccss", diff --git a/seed/challenges/basic-ziplines.json b/seed/challenges/basic-ziplines.json index cf2ac272f6..4565f364a8 100644 --- a/seed/challenges/basic-ziplines.json +++ b/seed/challenges/basic-ziplines.json @@ -1,50 +1,53 @@ { "name": "Basic Front End Development Projects", - "order": 8, + "order": 9, + "time": "100h", "challenges": [ { "id": "bd7158d8c442eddfbeb5bd1f", "title": "Get Set for Ziplines", - "difficulty": 1.00, - "challengeSeed": ["125658022"], + "challengeSeed": [], "description": [ - "Now you're ready to start our Zipline challenges. These front-end development challenges will give you many opportunities to apply the HTML, CSS, jQuery and JavaScript you've learned to build static (database-less) applications.", - "Whatever you do, don't get discouraged! Remember to use Read-Search-Ask if you get stuck.", - "We'll build these challenges using CodePen, a popular tool for creating, sharing, and discovering static web applications.", - "Go to http://codepen.io and create an account.", - "Click your user image in the top right corner, then click the \"New pen\" button that drops down.", - "Drag the windows around and press the buttons in the lower-right hand corner to change the orientation to suit your preference.", - "Click the gear next to CSS. Then under the \"Add External CSS\" section, use the \"Quick-add\" select box to select Bootstrap. Then click \"Save & Close\".", - "Verify that bootstrap is active by adding the following code to your HTML: <h1 class='text-primary'>Hello CodePen!</h1>. The text's color should be Bootstrap blue.", - "Click the gear next to JavaScript. Click the \"Quick-add\" select box and choose jQuery (not jQuery UI).", - "Click the \"Quick-add\" select box again and choose Bootstrap. Then click \"Save & Close\".", - "Now add the following code to your JavaScript: $(document).ready(function() { $('.text-primary').text('Hi CodePen!') });. Click the \"Save\" button at the top. Your \"Hello CodePen!\" should change to \"Hi CodePen!\". This means that jQuery is working.", - "You can use this CodePen that you've just created as a starting point for your Ziplines. Just click the \"fork\" button at the top of your CodePen and it will create a duplicate CodePen.", - "Now you're ready for your first Zipline. Click the \"I've completed this challenge\" button." + [ + "http://i.imgur.com/6WLULsC.gif", + "A gif showing how to create a Codepen account.", + "For our front end Zipline challenges, we'll use a popular browser-based code editor called CodePen. Open CodePen and click \"Sign up\" in the upper right hand corner, then scroll down to the free plan and click \"Sign up\" again. Click the \"Use info from GitHub button\", then add your email address and create a password. Click the \"Sign up\" button. Then in the upper right hand corner, click \"New pen\".", + "http://codepen.io" + ], + [ + "http://i.imgur.com/U4y9RJ1.gif", + "A gif showing that you can type \"hello world\" will output \"hello world\" in the preview window. You can also drag windows to resize them, and change their orientation.", + "In the HTML box, create an h1 element with the text \"Hello World\". You can drag the frames around to resize them. You can also click the \"Change View\" button and change the orientation of the frames.", + "" + ], + [ + "http://i.imgur.com/G9KFQDL.gif", + "A gif showing the process of adding Bootstrap to your pen.", + "Click the gear in the upper left hand corner of the CSS box, then scroll down to \"Quick add\" and choose Bootstrap. Now give your h1 element the class of \"text-primary\" to change its color and prove that Bootstrap is now available.", + "" + ], + [ + "http://i.imgur.com/Gi3aig0.gif", + "A gif showing the process of adding Animate.css and jQuery to pen.", + "Click the gear in the upper left hand corner of the CSS box, then scroll down to \"Quick add\" and choose Animate.css. Click the gear in the upper left hand corner of the JS box, then scroll down to \"Quick add\" and choose jQuery. Let's prove that Animate.css and jQuery are loaded properly. In the JS box, add the following code to make your h1 element bounce: $(document).ready(function(){ $(\"h1\").addClass(\"animated bounce\"); });.", + "" + ], + [ + "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.", + "" + ] ], - "type": "waypoint", - "challengeType": 2, + "type": "Waypoint", + "challengeType": 7, "tests": [], "nameCn": "", "descriptionCn": [], "nameFr": "", "descriptionFr": [], - "nameRu": "Приготовьтесь к Zipline'ам", - "descriptionRu": [ - "Теперь вы готовы приступить к Zipline'ам. Это задания по фронт-энд разработке, в них вы примените ранее изученные HTML, CSS, jQuery и JavaScript и создадите статические (не использующие базу данных) приложения.", - "Ни в коем случае не унывайте! Воспользуйтесь Read-Search-Ask, если что-то не получается.", - "Задания мы будем выполнять используя CodePen - популярный инструмент для создания и обмена статическими веб приложениями.", - "Перейдите по ссылке http://codepen.io и создайте аккаунт.", - "Нажмите на ваш аватар в правом верхнем углу, а затем в открывшемся меню на кнопку \"New pen\".", - "Выберите удобное расположение окон с помощью кнопок в правом нижнем углу, отрегулируйте их ширину.", - "Нажмите на звездочку рядом с CSS. Затем в секции \"Add External CSS\" выберите \"Quick-add\" и добавьте Bootstrap. Нажмите \"Save & Close\".", - "Проверьте, что Bootstrap подключен добавив следующий HTML код: <h1 class='text-primary'>Hello CodePen!</h1>. Цвет текста должен быть синим.", - "Нажмите на звездочку рядом с JavaScript. Нажмите \"Quick-add\" и выберите jQuery (не jQuery UI). Нажмите \"Save & Close\".", - "Снова нажмите на поле \"Quick-add\", выберите Bootstrap и затем нажмите \"Save & Close\".", - "Теперь добавьте следующий код в окошко JavaScript: $(document).ready(function() { $('.text-primary').text('Hi CodePen!') });. Нажмите на кнопку \"Save\" расположенную наверху. Текст \"Hello CodePen!\" должен измениться на \"Hi CodePen!\". Это значит что jQuery работает.", - "CodePen, который мы создали, можно использовать в качестве отправной точки для ваших Zipline'ов. Кликните кнопку \"fork\", чтобы создать копию текущего CodePen'a.", - "Все готово для первого Zipline'а. Жмите кнопку \"I've completed this challenge\"." - ], + "nameRu": "", + "descriptionRu": [], "nameEs": "", "descriptionEs": [], "namePt": "", @@ -53,7 +56,6 @@ { "id": "bd7158d8c242eddfaeb5bd13", "title": "Build a Personal Portfolio Webpage", - "difficulty": 1.01, "challengeSeed": ["133315782"], "description": [ "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/ThiagoFerreir4/full/eNMxEp.", @@ -107,7 +109,6 @@ { "id": "bd7158d8c442eddfaeb5bd13", "title": "Build a Random Quote Machine", - "difficulty": 1.02, "challengeSeed": ["126415122"], "description": [ "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/AdventureBear/full/vEoVMw.", @@ -117,7 +118,6 @@ "Here are the user stories you must enable, and optional bonus user stories:", "User Story: As a user, I can click a button to show me a new random quote.", "Bonus User Story: As a user, I can press a button to tweet out a quote.", - "Note that you can either put your quotes into an array and show them at random, or use an API to get quotes, such as http://forismatic.com/en/api/.", "Remember to use Read-Search-Ask if you get stuck.", "When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.", "If you'd like immediate feedback on your project from fellow campers, click this button and paste in a link to your CodePen project.

Click here then add your link to your tweet's text" @@ -203,7 +203,7 @@ "Here are the user stories you must enable, and optional bonus user stories:", "User Story: As a user, I can add, subtract, multiply and divide two numbers.", "Bonus User Story: I can clear the input field with a clear button.", - "Bonus User Story: I can keep chaining mathematical operations together until I hit the clear button, and the calculator will tell me the correct output.", + "Bonus User Story: I can keep chaining mathematical operations together until I hit the equal button, and the calculator will tell me the correct output.", "Remember to use Read-Search-Ask if you get stuck.", "When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.", "If you'd like immediate feedback on your project from fellow campers, click this button and paste in a link to your CodePen project.

Click here then add your link to your tweet's text" diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index d405871390..b97d45b04c 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -1,6 +1,7 @@ { "name": "Responsive Design with Bootstrap", "order": 3, + "time": "2h", "challenges": [ { "id": "bad87fee1348bd9acde08712", @@ -9,7 +10,7 @@ "Now let's go back to our Cat Photo App. This time, we'll style it using the popular Bootstrap responsive CSS framework.", "Bootstrap will figure out how wide your screen is and respond by resizing your HTML elements - hence the name Responsive Design.", "With responsive design, there is no need to design a mobile version of your website. It will look good on devices with screens of any width.", - "You can add Bootstrap to any app just by including it with <link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css\"/> at the top of your HTML. But we've gone ahead and automatically added it to your Cat Photo App for you.", + "You can add Bootstrap to any app just by including it with <link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css\"/> at the top of your HTML. But we've added it for you to this page behind the scenes.", "To get started, we should nest all of our HTML in a div element with the class container-fluid." ], "tests": [ @@ -2110,7 +2111,7 @@ "tests": [ "assert(editor.match(//g) && editor.match(/-->/g).length > 0, 'Be sure to close your comment with -->.')" + "assert(editor.match(/-->.*\\n+.+/g), 'Be sure to close your comment with -->.')" ], "challengeSeed": [ "
", diff --git a/seed/challenges/front-end-development-certificate.json b/seed/challenges/front-end-development-certificate.json index fa0bb1ce40..4d6a83c406 100644 --- a/seed/challenges/front-end-development-certificate.json +++ b/seed/challenges/front-end-development-certificate.json @@ -1,17 +1,41 @@ { "name": "Claim Your Front End Development Certificate", "order": 12, + "time": "5m", "challenges": [ { "id": "561add10cb82ac38a17513be", "title": "Claim Your Front End Development Certificate", - "difficulty": 0.00, - "challengeSeed": [], + "challengeSeed": [ + { + "properties": ["isHonest", "isFrontEndCert"], + "apis": ["/certificate/honest", "/certificate/verify/front-end"], + "stepIndex": [1, 2] + } + ], "description": [ [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/syJxavV.jpg", + "An image of our Front End Development Certificate", + "This challenge will give you your verified Front End Development Certificate. Before we issue your certificate, we must verify that you have completed all of our basic and intermediate Bonfires, and all our basic and intermediate Ziplines. You must also accept our Academic Honesty Pledge. Click the button below to start this process.", + "" + ], + [ + "http://i.imgur.com/HArFfMN.jpg", + "The definition of plagiarism: Plagiarism (noun) - copying someone else’s work and presenting it as your own without crediting them", + "By clicking below, you pledge that all of your submitted code A) is code you or your pair personally wrote, or B) comes from open source libraries like jQuery, or C) has been clearly attributed to its original authors. You also give us permission to audit your challenge solutions and revoke your certificate if we discover evidence of plagiarism.", + "#" + ], + [ + "http://i.imgur.com/14F2Van.jpg", + "An image of the text \"Front End Development Certificate requirements\"", + "Let's confirm that you have completed all of our basic and intermediate Bonfires, and all our basic and intermediate Ziplines. Click the button below to verify this.", + "#" + ], + [ + "http://i.imgur.com/16SIhHO.jpg", + "An image of the word \"Congratulations\"", + "Congratulations! We've added your Front End Development Certificate to your certificate to your portfolio page. Unless you choose to hide your solutions, this certificate will remain publicly visible and verifiable.", "" ] ], diff --git a/seed/challenges/full-stack-development-certificate.json b/seed/challenges/full-stack-development-certificate.json index 901883aebc..1d0cd92963 100644 --- a/seed/challenges/full-stack-development-certificate.json +++ b/seed/challenges/full-stack-development-certificate.json @@ -1,17 +1,42 @@ { "name": "Claim Your Full Stack Development Certificate", "order": 21, + "time": "5m", "challenges": [ { "id": "660add10cb82ac38a17513be", "title": "Claim Your Full Stack Development Certificate", "difficulty": 0.00, - "challengeSeed": [], + "challengeSeed": [ + { + "properties": ["isHonest", "isFullStackCert"], + "apis": ["/certificate/honest", "/certificate/verify/full-stack"], + "stepIndex": [1, 2] + } + ], "description": [ [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/sKYQhdG.jpg", + "An image of our Full Stack Development Certificate", + "This challenge will give you your verified Full Stack Development Certificate. Before we issue your certificate, we must verify that you have completed all of Bonfires, Ziplines and Basejumps. You must also accept our Academic Honesty Pledge. Click the button below to start this process.", + "" + ], + [ + "http://i.imgur.com/HArFfMN.jpg", + "The definition of plagiarism: Plagiarism (noun) - copying someone else’s work and presenting it as your own without crediting them", + "By clicking below, you pledge that all of your submitted code A) is code you or your pair personally wrote, or B) comes from open source libraries like jQuery, or C) has been clearly attributed to its original authors. You also give us permission to audit your challenge solutions and revoke your certificate if we discover evidence of plagiarism.", + "#" + ], + [ + "http://i.imgur.com/2qn7tHp.jpg", + "An image of the text \"Full Stack Development Certificate requirements\"", + "Let's confirm that you have completed all of our Bonfires, Ziplines and Basejumps. Click the button below to verify this.", + "#" + ], + [ + "http://i.imgur.com/16SIhHO.jpg", + "An image of the word \"Congratulations\"", + "Congratulations! We've added your Full Stack Development Certificate to your certificate to your portfolio page. Unless you choose to hide your solutions, this certificate will remain publicly visible and verifiable.", "" ] ], diff --git a/seed/challenges/gear-up-for-success.json b/seed/challenges/gear-up-for-success.json new file mode 100644 index 0000000000..7a6fd0a670 --- /dev/null +++ b/seed/challenges/gear-up-for-success.json @@ -0,0 +1,111 @@ +{ + "name": "Gear up for Success", + "order": 4, + "time": "10m", + "challenges": [ + { + "id": "560add65cb82ac38a17513c2", + "title": "Browse Camper News", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/YNwsMXM.gif", + "A gif showing how you can access our Camper News page and click the \"upvote\" button to upvote a story.", + "Click the \"News\" button in your upper right hand corner. You can browse links on Camper News and upvote ones that you enjoy.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "560add65cb82ac38a17513c1", + "title": "Reference our Wiki", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/4GO4zcI.gif", + "A gif showing how you can click the \"Wiki\" button in your upper-right corner to access the wiki.", + "Click the \"Wiki\" button in your upper right hand corner. Our community has contributed lots of useful information to this searchable wiki.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "570add8ccb82ac38a17513c3", + "title": "Join our LinkedIn Alumni Network", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/vJyiXzU.gif", + "A gif showing how you can click the link below and fill in the necessary fields to add your Free Code Camp studies to your LinkedIn profile.", + "You can add Free Code Camp to your LinkedIn education background. Set your graduation date as next year. For \"Degree\", type \"Full Stack Web Development Certification\". For \"Field of study\", type \"Computer Software Engineering\". Then click \"Save Changes\".", + "https://www.linkedin.com/profile/edit-education?school=Free+Code+Camp" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "560add8ccb81ac38a17513c4", + "title": "Commit to a Goal and a Nonprofit", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/Og1ifsn.gif", + "A gif showing how you can commit to a goal for your Free Code Camp studies and pledge a monthly donation to a nonprofit to give you external motivation to reach that goal.", + "You can set a goal and pledge to donate to a nonprofit each month until you achieve that goal. give you external motivation in your quest to learn to code, as well as the opportunity to help nonprofits right away. Choose your goal, choose a monthly donation. When you click \"commit\", the nonprofit's donate page will open in a new tab. You can change your commitment or stop it at any time.", + "/commit" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + } + ] +} diff --git a/seed/challenges/getting-started.json b/seed/challenges/getting-started.json index c35c3541f8..3513468561 100644 --- a/seed/challenges/getting-started.json +++ b/seed/challenges/getting-started.json @@ -1,6 +1,7 @@ { "name": "Get Started with Free Code Camp", "order": 1, + "time": "10m", "challenges": [ { "id": "560add10cb82ac38a17513be", @@ -9,13 +10,13 @@ "description": [ [ "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", + "A picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", "" ], [ "http://i.imgur.com/pYsTbjI.jpg", - "a screenshot of our curriculum alongside a screenshot of our chat room.", + "A screenshot of our curriculum alongside a screenshot of our chat room.", "Learning to code is hard. To succeed, you'll need lots of practice and support. That's why we've created a rigorous curriculum and supportive community.", "" ], @@ -32,14 +33,14 @@ "" ], [ - "http://i.imgur.com/q4IjuCL.jpg", - "a screenshot of our Front End Development Certificate", + "http://i.imgur.com/sKYQhdG.jpg", + "A screenshot of our Front End Development Certificate", "About half way through our curriculum, you'll earn a verified Front End Development Certificate. If you can finish our entire curriculum, you'll earn a verified Full Stack Development Certificate.", "" ], [ "http://i.imgur.com/yXyxbDd.jpg", - "a screen shot of our nonprofit project directory.", + "A screen shot of our nonprofit project directory.", "Then you'll build several real-life projects for nonprofits. By the time you finish, you'll have a portfolio of real apps that people use every day.", "" ] @@ -65,7 +66,7 @@ "description": [ [ "http://i.imgur.com/EAR7Lvh.jpg", - "a screenshot of our one of our Gitter chat rooms.", + "A screenshot of our one of our Gitter chat rooms.", "Now let's join Free Code Camp's chat rooms. You can come here any time of day to hang out, ask questions, or find another camper to pair program with. First you'll need a GitHub account.", "" ], @@ -82,15 +83,15 @@ "https://github.com/settings/profile" ], [ - "http://i.imgur.com/OXL3G3n.gif", - "Click the link below to navigate to Free Code Camp's open-source repository. In the upper right hand corner, you can click the \"star\" button to star this repository.", + "http://i.imgur.com/pYk0wOk.gif", + "A gif showing how you can star a GitHub repo.", "Go to Free Code Camp's open-source repository and \"star\" it. \"Starring\" is the GitHub equivalent of \"liking\" something.", "https://github.com/freecodecamp/freecodecamp" ], [ - "http://i.imgur.com/EZHzKCV.gif", + "http://i.imgur.com/zwYPeQT.gif", "A gif showing you how to click the link below to go to our chat room and click the \"sign in with GitHub\" button. Then you can click into the text input field and type a message to your fellow campers.", - " Now that you have a GitHub account, you can join our main chat room by logging in with GitHub. Introduce yourself by saying \"Hello world!\". Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.", + "Now that you have a GitHub account, you can join our main chat room by logging in with GitHub. Introduce yourself by saying \"Hello world!\". Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.", "https://gitter.im/FreeCodeCamp/FreeCodeCamp" ], [ @@ -112,7 +113,7 @@ "" ], [ - "http://i.imgur.com/SLQ27Gr.gif", + "http://i.imgur.com/WvQvNGN.gif", "A gif showing how you can click the link below to download a native chat room app for your computer.", "You can also download the chat room app to your computer or phone.", "https://gitter.im/apps" @@ -134,51 +135,13 @@ }, { "id": "560add56cb82ac38a17513c0", - "title": "Configure your Public Profile", + "title": "Configure your Code Portfolio", "challengeSeed": [], "description": [ [ - "http://i.imgur.com/FkEzbto.gif", - "A gif showing how you can click your profile image in your upper right hand corner to access the account page and connect GitHub.", - "Check out your portfolio page. Click your picture your upper right hand corner. To activate your portfolio page, you'll need to link your GitHub account with Free Code Camp.", - "" - ], - [ - "http://i.imgur.com/WKzEr1q.gif", - "A gif showing how you can access your profile page and hover over different days to see how many brownie points you got on those days.", - "Your portfolio page shows your progress and how many Brownie Points you have. You can get Brownie Points by completing challenges and by helping other campers in our chat rooms. If you get Brownie Points on several days in a row, you'll get a streak.", - "" - ] - ], - "type": "Waypoint", - "challengeType": 7, - "tests": [], - "nameCn": "", - "descriptionCn": [], - "nameFr": "", - "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], - "nameEs": "", - "descriptionEs": [], - "namePt": "", - "descriptionPt": [] - }, - { - "id": "560add65cb82ac38a17513c1", - "title": "Try our Wiki and Camper News", - "challengeSeed": [], - "description": [ - [ - "http://i.imgur.com/DoOqkNW.gif", - "A gif showing how you can click the \"Wiki\" button in your upper-right corner to access the wiki.", - "Try this: Click the \"Wiki\" button in your upper right hand corner. Our community has contributed lots of useful information to this searchable wiki.", - "" - ], - [ - "http://i.imgur.com/nmSiMy1.gif", - "A gif showing how you can access our Camper News page and click the \"upvote\" button to upvote a story.", - "Click the \"News\" button in your upper right hand corner. You can browse links on Camper News and upvote ones that you enjoy.", + "http://i.imgur.com/tP2ccTE.gif", + "A gif showing how you can click your profile image in your upper right hand corner to your code portfolio and connect GitHub.", + "Check out your code portfolio. Click your picture in your upper right hand corner. To activate your code portfolio, you'll need to link your GitHub account with Free Code Camp. Your code portfolio shows your progress and how many Brownie Points you have. You can get Brownie Points by completing challenges and by helping other campers in our chat rooms. If you get Brownie Points on several days in a row, you'll get a streak.", "" ] ], @@ -208,9 +171,9 @@ "" ], [ - "http://i.imgur.com/EZHzKCV.gif", + "http://i.imgur.com/fTFMjwf.gif", "A gif showing how you can click the link below, find your city on the list of Campsites, then click on the Facebook link for your city and join your city's Facebook group.", - "Find your city on this list, click the \"Facebook\" link, then click the \"Join group\" button to apply to join your city's Facebook group (someone from the campsite should approve you shortly). If your city isn't on this list, scroll to the bottom of the wiki article for instructions for how you can create your city's Campsite.", + "Find your city on this list and click it. This will take you to your city's Campsite's Facebook group. Click the \"Join group\" button to apply to join your city's Facebook group. Someone from the campsite should approve you shortly. If your city isn't on this list, scroll to the bottom of the wiki article for instructions for how you can create your city's Campsite.", "https://github.com/FreeCodeCamp/freecodecamp/wiki/List-of-Free-Code-Camp-city-based-Campsites" ] ], @@ -228,38 +191,6 @@ "namePt": "", "descriptionPt": [] }, - { - "id": "560add8ccb82ac38a17513c3", - "title": "Join our Alumni Network and Commit to Your Goal", - "challengeSeed": [], - "description": [ - [ - "http://i.imgur.com/P7qfJXt.gif", - "A gif showing how you can click the link below and fill in the necessary fields to add your Free Code Camp studies to your LinkedIn profile.", - "You can add Free Code Camp to your LinkedIn education background. Set your graduation date as next year. For \"Degree\", type \"Full Stack Web Development\". For \"Field of study\", type \"Computer Software Engineering\". Then click \"Save Changes\".", - "https://www.linkedin.com/profile/edit-education?school=Free+Code+Camp" - ], - [ - "", - "", - "Free Code Camp will always be free. If you want to feel more motivated to earn our certificates faster, we encourage you to instead donate each month to a nonprofit.", - "" - ] - ], - "type": "Waypoint", - "challengeType": 7, - "tests": [], - "nameCn": "", - "descriptionCn": [], - "nameFr": "", - "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], - "nameEs": "", - "descriptionEs": [], - "namePt": "", - "descriptionPt": [] - }, { "id": "560add8ccb82ac38a17513c4", "title": "Learn What to Do If You Get Stuck", @@ -290,8 +221,8 @@ "https://gitter.im/FreeCodeCamp/Help" ], [ - "http://i.imgur.com/WsfzvVo.gif", - "A gif showing us clicking the \"map\" button in our upper right hand corner and browsing our challenge map.", + "http://i.imgur.com/ZRgXraT.gif", + "A gif showing us scrolling through our challenge map.", "Now you're ready to start coding! The \"Map\" button in your upper right hand corner will show you our challenge map. This map shows all our coding challenges. We recommend that you complete these from top to bottom, at a sustainable pace. You can also return to your next challenge by clicking the \"Learn\" button.", "" ] diff --git a/seed/challenges/git.json b/seed/challenges/git.json index d3abf068ba..8a642ab615 100644 --- a/seed/challenges/git.json +++ b/seed/challenges/git.json @@ -1,6 +1,7 @@ { "name": "Git", "order" : 17, + "time": "3h", "challenges": [ { "id": "bd7353d8c341eddeaeb5bd0f", diff --git a/seed/challenges/hikes.json b/seed/challenges/hikes.json index 1f5d7f3113..0e9dbb56a4 100644 --- a/seed/challenges/hikes.json +++ b/seed/challenges/hikes.json @@ -1,6 +1,7 @@ { "name": "Hikes", "order": 0.050, + "time": "3h", "challenges": [ { "id": "bd7128d8c441eddfbeb5bddf", diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 3b2f72853e..c54d12541a 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -1,6 +1,7 @@ { "name": "HTML5 and CSS", "order": 2, + "time": "3h", "challenges": [ { "id": "bd7123c8c441eddfaeb5bdef", @@ -541,7 +542,6 @@ "assert($(\"p\").length > 1, 'You need 2 p elements with Kitty Ipsum text.')", "assert(editor.match(/<\\/p>/g) && editor.match(/<\\/p>/g).length === editor.match(/

p elements has a closing tag.')", "assert.isTrue((/Purr\\s+jump\\s+eat/gi).test($(\"p\").text()), 'Your p element should contain the first few words of the provided additional kitty ipsum text.')", - "assert($(\"p.red-text\").length === 2, 'Give each of your p elements the red-text class.')", "assert($(\"p\").css(\"font-size\") === \"16px\", 'Give elements with the red-text class a font-size of 16px.')" ], "challengeSeed": [ @@ -1122,7 +1122,7 @@ "assert($(\"a\").text().match(/cat\\sphotos/gi), 'Your a element should have the anchor text of \"cat photos\"')", "assert($(\"p\") && $(\"p\").length > 2, 'Create a new p element around your a element.')", "assert($(\"a[href=\\\"http://www.freecatphotoapp.com\\\"]\").parent().is(\"p\"), 'Your a element should be nested within your new p element.')", - "assert($(\"p\").text().match(/^View\\smore\\s/gi), 'Your p element should have the text \"View more \" (with a space after it).')", + "assert($(\"p\").text().match(/^\\s*View\\smore\\s/gi), 'Your p element should have the text \"View more \" (with a space after it).')", "assert(!$(\"a\").text().match(/View\\smore/gi), 'Your a element should not have the text \"View more\".')", "assert(editor.match(/<\\/p>/g) && editor.match(/

/g).length === editor.match(/

p elements has a closing tag.')", "assert(editor.match(/<\\/a>/g) && editor.match(//g).length === editor.match(/a elements has a closing tag.')" @@ -1411,7 +1411,7 @@ "assert($(\"ul\").length > 0, 'Create a ul element.')", "assert($(\"ul li\").length > 2, 'You should have three li elements within your ul element.')", "assert(editor.match(/<\\/ul>/g) && editor.match(/