diff --git a/.travis.yml b/.travis.yml index 26804033f5..b6bd11e2c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js -services: - - mongodb - node_js: - - '0.10' \ No newline at end of file + - 'node' + - '1.6.4' + +sudo: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba4e245107..6830f00dc5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,2 +1,12 @@ We're getting a lot of duplicate issues and bug reports that just aren't reporting actual bugs. So, before you submit your issue, please read the [Help I've Found a Bug](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-I've-Found-a-Bug) wiki page. + +We welcome pull requests from Free Code Camp campers (our students) and seasoned JavaScript developers alike! Follow these steps to contribute: + +1. Check our [public Waffle Board](https://waffle.io/freecodecamp/freecodecamp). +2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp). +3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy. +4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)) +5. Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example `fix/email-login` would be a branch where I fix something specific to email login. +6. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [Free Code Camp's JavaScript Style Guide](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Free-Code-Camp-JavaScript-Style-Guide) (you can find a summary of those rules [here](https://github.com/FreeCodeCamp/FreeCodeCamp/blob/staging/.eslintrc). Please do not ignore any linting errors, as they are meant to **help** you and to ensure a clean and simple code base. Make sure none of your JavaScript is longer than 80 characters per line. +7. Once your code is ready, submit a [pull request](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Pull-Request-Contribute) from your branch to Free Code Camp's `staging` branch. We'll do a quick code review and give you feedback, then iterate from there. It may also be helpful to read about git [rebasing](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/git-rebase). diff --git a/README.md b/README.md index dba5013540..9b0c87d631 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - +![](https://s3.amazonaws.com/freecodecamp/wide-social-banner.png) [![Throughput Graph](https://graphs.waffle.io/freecodecamp/freecodecamp/throughput.svg)](https://waffle.io/freecodecamp/freecodecamp/metrics) @@ -7,7 +7,7 @@ Welcome to Free Code Camp's open source codebase! ======================= -Free Code Camp is an open-source community of busy people who learn to code, then build projects for nonprofits. +Free Code Camp is an open-source community of busy people who learn to code and build projects for nonprofits. Our campers (students) start by working through our free, self-paced, browser-based curriculum. Next, they build several practice projects. Finally, we pair two campers together with a stakeholder from a nonprofit organization, and help them build the solution the nonprofit has requested. @@ -24,7 +24,7 @@ This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We Wiki ------------ -We would love your help expanding our [wiki](https://github.com/freecodecamp/freecodecamp/wiki) with more information about learning to code and getting a coding job. +We would love your help expanding our [wiki](https://github.com/freecodecamp/freecodecamp/wiki). Our goal is to become a great resource for people learning to code, building local coding communities, and applying for coding jobs. Contributing ------------ @@ -35,10 +35,9 @@ We welcome pull requests from Free Code Camp campers (our students) and seasoned 2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp). 3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy. 4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)) -5. Name the branch something like `user-xxx` where user is your username and xxx is the issue number you are addressing. -6. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [AirBnB's JavaScript Style Guide](https://github.com/airbnb/javascript). Please do not ignore any linting errors, as they are meant to **help** you. Make sure none of your JavaScript is longer than 80 characters per line. -7. Once your code is ready, submit a pull request from your branch to Free Code Camp's `staging` branch. We'll do a quick code review and give you feedback, then iterate from there. -8. Once we accept one of your pull requests, one of the project owners (currently @quincylarson, @terakilobyte, and @berkeleytrue) will add you to our camper contributor group. +5. Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example `fix/email-login` would be a branch where I fix something specific to email login. +6. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [Free Code Camp's JavaScript Style Guide](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Free-Code-Camp-JavaScript-Style-Guide) (you can find a summary of those rules [here](https://github.com/FreeCodeCamp/FreeCodeCamp/blob/staging/.eslintrc). Please do not ignore any linting errors, as they are meant to **help** you and to ensure a clean and simple code base. Make sure none of your JavaScript is longer than 80 characters per line. +7. Once your code is ready, submit a [pull request](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Pull-Request-Contribute) from your branch to Free Code Camp's `staging` branch. We'll do a quick code review and give you feedback, then iterate from there. It may also be helpful to read about git [rebasing](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/git-rebase). Prerequisites ------------- @@ -70,10 +69,9 @@ touch .env 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. ``` - MONGOHQ_URL='mongodb://localhost:27017/freecodecamp' FACEBOOK_ID=stuff @@ -106,16 +104,14 @@ COOKIE_SECRET='this is a secret' PEER=stuff DEBUG=true - ``` ```bash - # Start the mongo server mongod -# Create your mongo database. -# Type "mongo" in your terminal to access the mongo shell +# Create your mongo database. +# Type "mongo" in your terminal to access the mongo shell use freecodecamp # Exit the mongo shell with control + d @@ -124,7 +120,6 @@ node seed/ # start the application gulp - ``` License diff --git a/bower.json b/bower.json index cd8cfd5cf3..1f833167fc 100644 --- a/bower.json +++ b/bower.json @@ -26,6 +26,7 @@ "moment": "~2.10.2", "angular-bootstrap": "~0.13.0", "ramda": "~0.13.0", - "jshint": "~2.7.0" + "jshint": "~2.7.0", + "lightbox2": "~2.8.1" } } diff --git a/client/commonFramework.js b/client/commonFramework.js index dd91756e06..8abcb6a9dc 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -1,6 +1,107 @@ -/* globals jailed, CodeMirror, challenge_Id, challenge_Name, challengeType */ +// 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: [] +}; + +common.challengeName = common.challengeName || window.challenge_Name ? + window.challenge_Name : + ''; + +common.challengeType = common.challengeType || window.challengeType ? + window.challengeType : + 0; + +common.challengeId = common.challengeId || window.challenge_Id; + +common.challengeSeed = common.challengeSeed || window.challengeSeed ? + window.challengeSeed : + []; + +common.seed = common.challengeSeed.reduce(function(seed, line) { + return seed + line + '\n'; +}, ''); + +// store code in the URL +common.codeUri = (function(common, encode, decode, location, history) { + var codeUri = { + encode: function(code) { + return encode(code); + }, + decode: function(code) { + try { + return decode(code); + } catch (ignore) { + return null; + } + }, + isInQuery: function(query) { + var decoded = codeUri.decode(query); + if (!decoded || typeof decoded.split !== 'function') { + return false; + } + return decoded + .split('?') + .splice(1) + .reduce(function(found, param) { + var key = param.split('=')[0]; + if (key === 'solution') { + return true; + } + return found; + }, false); + }, + isAlive: function() { + return codeUri.isInQuery(location.search) || + codeUri.isInQuery(location.hash); + }, + parse: function() { + var query; + if (location.search && codeUri.isInQuery(location.search)) { + query = location.search.replace(/^\?/, ''); + if (history && typeof history.replaceState === 'function') { + history.replaceState( + history.state, + null, + location.href.split('?')[0] + ); + location.hash = '#?' + query; + } + } else { + query = location.hash.replace(/^\#\?/, ''); + } + if (!query) { + return null; + } + + return query + .split('&') + .reduce(function(solution, param) { + var key = param.split('=')[0]; + var value = param.split('=')[1]; + if (key === 'solution') { + return codeUri.decode(value); + } + return solution; + }, null); + }, + querify: function(solution) { + location.hash = '?solution=' + codeUri.encode(solution); + return solution; + } + }; + + common.init.push(function() { + codeUri.parse(); + }); + + return codeUri; +}(common, encodeURIComponent, decodeURIComponent, location, history)); + // codeStorage -var codeStorageFactory = (function($, localStorage) { +common.codeStorageFactory = (function($, localStorage, codeUri) { var CodeStorageProps = { version: 0.01, @@ -41,7 +142,10 @@ var codeStorageFactory = (function($, localStorage) { updateStorage: function() { if (typeof localStorage !== 'undefined') { var value = this.editor.getValue(); + // store in localStorage localStorage.setItem(this.keyValue, value); + // also store code in URL + codeUri.querify(value); } else { console.log('no web storage'); } @@ -66,10 +170,47 @@ var codeStorageFactory = (function($, localStorage) { } return codeStorageFactory; -}($, localStorage)); +}($, localStorage, common.codeUri)); -var sandBox = (function() { +common.codeOutput = (function(CodeMirror, document, challengeType) { + if (!CodeMirror) { + return {}; + } + if ( + challengeType === '0' || + challengeType === '7' + ) { + return {}; + } + var codeOutput = CodeMirror.fromTextArea( + document.getElementById('codeOutput'), + { + lineNumbers: false, + mode: 'text', + theme: 'monokai', + readOnly: 'nocursor', + lineWrapping: true + } + ); + codeOutput.setValue( + '/**\n' + + ' * Your output will go here.\n' + + ' * Console.log() -type statements\n' + + ' * will appear in your browser\'s\n' + + ' * DevTools JavaScript console.\n' + + ' */' + ); + + codeOutput.setSize('100%', '100%'); + + return codeOutput; +}(window.CodeMirror, window.document, common.challengeType || 0)); + +var sandBox = (function(jailed, codeOutput) { + if (!jailed) { + return {}; + } var plugin = null; var sandBox = { @@ -150,7 +291,7 @@ var sandBox = (function() { endLoading(); console.log('resetting on fatal plugin error'); - if (challengeType === 0) { + if (common.challengeType === 0) { codeOutput.setValue( 'Sorry, your code is either too slow, has a fatal error, ' + 'or contains an infinite loop.' @@ -163,7 +304,7 @@ var sandBox = (function() { reset(); sandBox.submit = submit; return sandBox; -}()); +}(window.jailed, common.codeOutput)); function replaceSafeTags(value) { return value @@ -180,66 +321,110 @@ var BDDregex = new RegExp( var isInitRun = false; var initPreview = true; -var editor; -editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), { - lineNumbers: true, - mode: 'text', - theme: 'monokai', - runnable: true, - matchBrackets: true, - autoCloseBrackets: true, - scrollbarStyle: 'null', - lineWrapping: true, - gutters: ['CodeMirror-lint-markers'] -}); - -var codeStorage = codeStorageFactory(editor, challenge_Name); -var myCodeMirror = editor; - -editor.on('keyup', function() { - clearTimeout(codeStorage.updateTimeoutId); - codeStorage.updateTimeoutId = setTimeout( - codeStorage.updateStorage, - codeStorage.updateWait - ); -}); - -var editorValue; -var challengeSeed = challengeSeed || null; -var tests = tests || []; -var allSeeds = ''; - -(function() { - challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; - }); -})(); - -editor.setOption('extraKeys', { - Tab: function(cm) { - if (cm.somethingSelected()) { - cm.indentSelection('add'); - } else { - var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Shift-Tab': function(cm) { - if (cm.somethingSelected()) { - cm.indentSelection('subtract'); - } else { - var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Ctrl-Enter': function() { - bonfireExecute(true); - return false; +var editor = (function(CodeMirror, emmetCodeMirror, common) { + var codeStorageFactory = common.codeStorageFactory; + if (!CodeMirror) { + return {}; } -}); -editor.setSize('100%', 'auto'); + var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), { + lineNumbers: true, + mode: 'text', + theme: 'monokai', + runnable: true, + matchBrackets: true, + autoCloseBrackets: true, + scrollbarStyle: 'null', + lineWrapping: true, + gutters: ['CodeMirror-lint-markers'] + }); + + editor.setSize('100%', 'auto'); + + var codeStorage = common.codeStorage = + codeStorageFactory(editor, common.challengeName); + + editor.on('keyup', function() { + clearTimeout(codeStorage.updateTimeoutId); + codeStorage.updateTimeoutId = setTimeout( + codeStorage.updateStorage.bind(codeStorage), + codeStorage.updateWait + ); + }); + + // Initialize CodeMirror editor with a nice html5 canvas demo. + editor.on('keyup', function() { + clearTimeout(delay); + delay = setTimeout(updatePreview, 300); + }); + + editor.setOption('extraKeys', { + Tab: function(cm) { + if (cm.somethingSelected()) { + cm.indentSelection('add'); + } else { + var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); + cm.replaceSelection(spaces); + } + }, + 'Shift-Tab': function(cm) { + if (cm.somethingSelected()) { + cm.indentSelection('subtract'); + } else { + var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); + cm.replaceSelection(spaces); + } + }, + 'Ctrl-Enter': function() { + isInitRun = false; + bonfireExecute(true); + return false; + } + }); + + + var info = editor.getScrollInfo(); + + var after = editor.charCoords({ + line: editor.getCursor().line + 1, + ch: 0 + }, 'local').top; + + if (info.top + info.clientHeight < after) { + editor.scrollTo(null, after - info.clientHeight + 3); + } + + if (emmetCodeMirror) { + emmetCodeMirror( + editor, + { + 'Cmd-E': 'emmet.expand_abbreviation', + Tab: 'emmet.expand_abbreviation_with_tab', + Enter: 'emmet.insert_formatted_line_break_only' + } + ); + } + common.init.push(function() { + var editorValue; + if (common.codeUri.isAlive()) { + console.log('in query'); + editorValue = common.codeUri.parse(); + } else { + editorValue = codeStorage.isAlive() ? + codeStorage.getStoredValue() : + common.seed; + } + + editor.setValue(replaceSafeTags(editorValue)); + editor.refresh(); + }); + + return editor; +}(window.CodeMirror, window.emmetCodeMirror, common)); + + +var tests = tests || []; var libraryIncludes = "" + "" + @@ -283,7 +468,8 @@ function scopejQuery(str) { } function safeHTMLRun(test) { - if (challengeType === '0') { + var codeStorage = common.codeStorage; + if (common.challengeType === '0') { var previewFrame = document.getElementById('preview'); var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; if (editor.getValue().match(/\/gi) !== null) { @@ -293,7 +479,7 @@ function safeHTMLRun(test) { .split(/\<\s?\/\s?script\s?\>/gi)[0]; // 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; + 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; sandBox.submit(scopejQuery(s), function(cls, message) { if (cls) { @@ -358,17 +544,11 @@ if (typeof prodOrDev !== 'undefined') { 'http://www.freecodecamp.com' : 'http://localhost:3001'; - if (challengeType === '0') { + if (common.challengeType === '0') { setTimeout(updatePreview, 300); } } -// Initialize CodeMirror editor with a nice html5 canvas demo. -editor.on('keyup', function() { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); -}); - /** * "post" methods */ @@ -401,11 +581,20 @@ var postError = function(data) { var goodTests = 0; var testSuccess = function() { goodTests++; + // test successful run show completion if (goodTests === tests.length) { - showCompletion(); + return showCompletion(); } }; +function ctrlEnterClickHandler(e) { + // ctrl + enter + if (e.ctrlKey && e.keyCode === 13) { + $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); + $('#submit-challenge').click(); + } +} + function showCompletion() { if (isInitRun) { isInitRun = false; @@ -417,11 +606,14 @@ function showCompletion() { 'event', 'Challenge', 'solved', - challenge_Name + ', Time: ' + time + ', Attempts: ' + attempts + common.challengeName + ', Time: ' + time + ', Attempts: ' + attempts ); - var bonfireSolution = myCodeMirror.getValue(); + var bonfireSolution = editor.getValue(); var didCompleteWith = $('#completed-with').val() || null; + $('#complete-courseware-dialog').modal('show'); + $('#complete-courseware-dialog .modal-header').click(); + $('#submit-challenge').click(function(e) { e.preventDefault(); @@ -439,7 +631,9 @@ function showCompletion() { .delay(1000) .queue(function(next) { $(this).replaceWith( - '
submitting...
' + '
' + + 'submitting...
' ); next(); }); @@ -447,16 +641,17 @@ function showCompletion() { $.post( '/completed-bonfire/', { challengeInfo: { - challengeId: challenge_Id, - challengeName: challenge_Name, + challengeId: common.challengeId, + challengeName: common.challengeName, completedWith: didCompleteWith, - challengeType: challengeType, + challengeType: common.challengeType, solution: bonfireSolution } }, function(res) { if (res) { - window.location = '/challenges/next-challenge'; + window.location = + '/challenges/next-challenge?id=' + common.challengeId; } } ); @@ -464,10 +659,10 @@ function showCompletion() { } var resetEditor = function resetEditor() { - editor.setValue(replaceSafeTags(allSeeds)); + editor.setValue(replaceSafeTags(common.seed)); $('#testSuite').empty(); bonfireExecute(true); - codeStorage.updateStorage(); + common.codeStorage.updateStorage(); }; var attempts = 0; @@ -475,40 +670,10 @@ if (attempts) { attempts = 0; } -if (challengeType !== '0') { - var codeOutput = CodeMirror.fromTextArea( - document.getElementById('codeOutput'), - { - lineNumbers: false, - mode: 'text', - theme: 'monokai', - readOnly: 'nocursor', - lineWrapping: true - } - ); - - codeOutput.setValue('/**\n' + - ' * Your output will go here.\n' + ' * Console.log() -type statements\n' + - ' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' + - ' */'); - codeOutput.setSize('100%', '100%'); -} - -var info = editor.getScrollInfo(); - -var after = editor.charCoords({ - line: editor.getCursor().line + 1, - ch: 0 -}, 'local').top; - -if (info.top + info.clientHeight < after) { - editor.scrollTo(null, after - info.clientHeight + 3); -} var userTests; var testSalt = Math.random(); - var scrapeTests = function(userJavaScript) { // insert tests from mongo @@ -557,34 +722,38 @@ var createTestDisplay = function() { userTests.pop(); } for (var i = 0; i < userTests.length; i++) { - var test = userTests[i]; + var didTestPass = !userTests[i].err; + var testText = userTests[i].text + .split('message: ') + .pop() + .replace(/\'\);/g, ''); + var testDoc = document.createElement('div'); - if (test.err) { - console.log('Should be displaying bad tests'); + var iconClass = didTestPass ? + '"ion-checkmark-circled big-success-icon"' : + '"ion-close-circled big-error-icon"'; - $(testDoc).html( - "
" + - test.text + "
" + - test.err + "
" - ) - .appendTo($('#testSuite')); - - } else { - - $(testDoc).html( - "
" + - test.text + - "
" - ) - .appendTo($('#testSuite')); - } + $(testDoc).html( + "
" + + testText + + "
" + ) + .appendTo($('#testSuite')); } }; -var expect = chai.expect; -var assert = chai.assert; -var should = chai.should(); +(function(win, chai) { + if (!chai) { + return; + } + win.expect = chai.expect; + win.assert = chai.assert; + win.should = chai.should(); + +}(window, window.chai)); var reassembleTest = function(test, data) { @@ -594,6 +763,7 @@ var reassembleTest = function(test, data) { }; var runTests = function(err, data) { + var editorValue = editor.getValue(); // userTests = userTests ? null : []; var allTestsPassed = true; pushed = false; @@ -623,7 +793,9 @@ var runTests = function(err, data) { ) { try { if (chaiTestFromJSON) { + /* eslint-disable no-eval, no-unused-vars */ var output = eval(reassembleTest(chaiTestFromJSON, data)); + /* eslint-enable no-eval, no-unused-vars */ } } catch (error) { allTestsPassed = false; @@ -638,23 +810,139 @@ var runTests = function(err, data) { if (allTestsPassed) { allTestsPassed = false; showCompletion(); + } else { + isInitRun = false; } } }; +// step challenge +common.init.push((function() { + var stepClass = '.challenge-step'; + var nextBtnClass = '.challenge-step-btn-next'; + var actionBtnClass = '.challenge-step-btn-action'; + var finishBtnClass = '.challenge-step-btn-finish'; + var submitBtnId = '#challenge-step-btn-submit'; + var submitModalId = '#challenge-step-modal'; + + function getNextStep($challengeSteps) { + var length = $challengeSteps.length; + var $nextStep = false; + var nextStepIndex = 0; + $challengeSteps.each(function(index) { + var $step = $(this); + if ( + !$step.hasClass('hidden') && + index + 1 !== length + ) { + nextStepIndex = index + 1; + } + }); + + $nextStep = $challengeSteps[nextStepIndex]; + + return $nextStep; + } + + function handleNextStepClick(e) { + e.preventDefault(); + var nextStep = getNextStep($(stepClass)); + $(this) + .parent() + .addClass('animated fadeOutLeft') + .delay(700) + .queue(function(next) { + $(this).addClass('hidden'); + if (nextStep) { + $(nextStep) + .removeClass('hidden') + .addClass('animated slideInRight') + .delay(1000) + .queue(function(next) { + $(this).removeClass('slideInRight'); + next(); + }); + } + next(); + }); + + } + + function handleActionClick() { + $(this) + .parent() + .find('.disabled') + .removeClass('disabled'); + } + + function handleFinishClick(e) { + e.preventDefault(); + $(submitModalId).modal('show'); + $(submitModalId + '.modal-header').click(); + $(submitBtnId).click(handleSubmitClick); + } + + function handleSubmitClick(e) { + e.preventDefault(); + + $('#submit-challenge') + .attr('disabled', 'true') + .removeClass('btn-primary') + .addClass('btn-warning disabled'); + + var $checkmarkContainer = $('#checkmark-container'); + $checkmarkContainer.css({ height: $checkmarkContainer.innerHeight() }); + + $('#challenge-checkmark') + .addClass('zoomOutUp') + .delay(1000) + .queue(function(next) { + $(this).replaceWith( + '
' + + 'submitting...
' + ); + next(); + }); + + $.post( + '/completed-bonfire/', { + challengeInfo: { + challengeId: common.challengeId, + challengeName: common.challengeName, + challengeType: common.challengeType + } + }, + function(res) { + if (res) { + window.location = + '/challenges/next-challenge?id=' + common.challengeId; + } + } + ); + } + + return function($) { + $(nextBtnClass).click(handleNextStepClick); + $(actionBtnClass).click(handleActionClick); + $(finishBtnClass).click(handleFinishClick); + }; +}(window.$))); + function bonfireExecute(shouldTest) { + var codeOutput = common.codeOutput; initPreview = false; goodTests = 0; attempts++; - ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); + ga('send', 'event', 'Challenge', 'ran-code', common.challengeName); userTests = null; $('#testSuite').empty(); if ( - challengeType !== '0' && + common.challengeType !== '0' && !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) ) { - var userJavaScript = myCodeMirror.getValue(); + var userJavaScript = editor.getValue(); var failedCommentTest = false; // checks if the number of opening comments(/*) matches the number of @@ -674,7 +962,7 @@ function bonfireExecute(shouldTest) { if (userJavaScript.match(/function\s*?\(|function\s+\w+\s*?\(/gi)) { sandBox.submit(userJavaScript, function(cls, message) { if (failedCommentTest) { - myCodeMirror.setValue(myCodeMirror.getValue() + '*/'); + editor.setValue(editor.getValue() + '*/'); console.log('Caught Unfinished Comment'); codeOutput.setValue('Unfinished multi-line comment'); failedCommentTest = false; @@ -699,7 +987,7 @@ function bonfireExecute(shouldTest) { sandBox.submit(userJavaScript, function(cls, message) { if (failedCommentTest) { - myCodeMirror.setValue(myCodeMirror.getValue() + '*/'); + editor.setValue(editor.getValue() + '*/'); console.log('Caught Unfinished Comment'); codeOutput.setValue('Unfinished mulit-line comment'); failedCommentTest = false; @@ -729,7 +1017,7 @@ function bonfireExecute(shouldTest) { } if ( !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) && - challengeType === '0' + common.challengeType === '0' ) { safeHTMLRun(shouldTest); } else { @@ -743,26 +1031,37 @@ function bonfireExecute(shouldTest) { } $('#submitButton').on('click', function() { + isInitRun = false; bonfireExecute(true); }); $(document).ready(function() { + + common.init.forEach(function(init) { + init($); + }); + + // init modal keybindings on open + $('#complete-courseware-dialog').on('shown.bs.modal', function() { + $('#complete-courseware-dialog').keydown(ctrlEnterClickHandler); + }); + + // remove modal keybinds on close + $('#complete-courseware-dialog').on('hidden.bs.modal', function() { + $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); + }); + var $preview = $('#preview'); isInitRun = true; - editorValue = codeStorage.isAlive() ? - codeStorage.getStoredValue() : - allSeeds; - - myCodeMirror.setValue(replaceSafeTags(editorValue)); - if (typeof $preview.html() !== 'undefined') { $preview.load(function() { if (initPreview) { bonfireExecute(true); } }); - } else { + } else if (common.challengeType !== 7) { bonfireExecute(true); } + }); diff --git a/client/es6-shims.js b/client/es6-shims.js new file mode 100644 index 0000000000..43abd2ed1a --- /dev/null +++ b/client/es6-shims.js @@ -0,0 +1,2 @@ +require('object.assign').shim(); +require('es6-map/implement'); diff --git a/client/index.js b/client/index.js index 95be7ca9ae..44bdb0a82c 100644 --- a/client/index.js +++ b/client/index.js @@ -1,9 +1,10 @@ +import unused from './es6-shims'; // eslint-disable-line import Rx from 'rx'; import React from 'react'; import Fetchr from 'fetchr'; import debugFactory from 'debug'; import { Router } from 'react-router'; -import { history } from 'react-router/lib/BrowserHistory'; +import { createLocation, createHistory } from 'history'; import { hydrate } from 'thundercats'; import { Render } from 'thundercats-react'; @@ -17,21 +18,29 @@ const services = new Fetchr({ }); Rx.config.longStackSupport = !!debug.enabled; - +const history = createHistory(); +const appLocation = createLocation( + location.pathname + location.search +); // returns an observable -app$(history) +app$({ history, location: appLocation }) .flatMap( ({ AppCat }) => { + // instantiate the cat with service const appCat = AppCat(null, services); + // hydrate the stores return hydrate(appCat, catState) .map(() => appCat); }, - ({ initialState }, appCat) => ({ initialState, appCat }) + // not using nextLocation at the moment but will be used for + // redirects in the future + ({ nextLocation, props }, appCat) => ({ nextLocation, props, appCat }) ) - .flatMap(({ initialState, appCat }) => { + .flatMap(({ props, appCat }) => { + props.history = history; return Render( appCat, - React.createElement(Router, initialState), + React.createElement(Router, props), DOMContianer ); }) diff --git a/client/less/lib/bootstrap/variables.less b/client/less/lib/bootstrap/variables.less index 01e26f3433..8b8fefa81d 100755 --- a/client/less/lib/bootstrap/variables.less +++ b/client/less/lib/bootstrap/variables.less @@ -107,7 +107,7 @@ @border-radius-small: 3px; //** Global color for active items (e.g., navs or dropdowns). -@component-active-color: #eee; +@component-active-color: @gray-lighter; //** Global background color for active items (e.g., navs or dropdowns). @component-active-bg: @brand-primary; @@ -145,26 +145,26 @@ @btn-font-weight: normal; @btn-default-color: #333; -@btn-default-bg: #eee; +@btn-default-bg: @gray-lighter; @btn-default-border: #ccc; -@btn-primary-color: #eee; +@btn-primary-color: @gray-lighter; @btn-primary-bg: @brand-primary; @btn-primary-border: darken(@btn-primary-bg, 5%); -@btn-success-color: #eee; +@btn-success-color: @gray-lighter; @btn-success-bg: @brand-success; @btn-success-border: darken(@btn-success-bg, 5%); -@btn-info-color: #eee; +@btn-info-color: @gray-lighter; @btn-info-bg: @brand-info; @btn-info-border: darken(@btn-info-bg, 5%); -@btn-warning-color: #eee; +@btn-warning-color: @gray-lighter; @btn-warning-bg: @brand-warning; @btn-warning-border: darken(@btn-warning-bg, 5%); -@btn-danger-color: #eee; +@btn-danger-color: @gray-lighter; @btn-danger-bg: @brand-danger; @btn-danger-border: darken(@btn-danger-bg, 5%); @@ -176,7 +176,7 @@ //## //** `` background color -@input-bg: #eee; +@input-bg: @gray-lighter; //** `` background color @input-bg-disabled: @gray-lighter; @@ -223,7 +223,7 @@ //## Dropdown menu container and contents. //** Background for the dropdown menu. -@dropdown-bg: #eee; +@dropdown-bg: @gray-lighter; //** Dropdown menu `border-color`. @dropdown-border: rgba(0,0,0,.15); //** Dropdown menu `border-color` **for IE8**. @@ -359,10 +359,10 @@ @navbar-default-border: darken(@navbar-default-bg, 6.5%); // Navbar links -@navbar-default-link-color: #eee; +@navbar-default-link-color: @gray-lighter; @navbar-default-link-hover-color: #4a2b0f; -@navbar-default-link-hover-bg: #eee; -@navbar-default-link-active-color: #eee; +@navbar-default-link-hover-bg: @gray-lighter; +@navbar-default-link-active-color: @gray-lighter; @navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%); @navbar-default-link-disabled-color: #ccc; @navbar-default-link-disabled-bg: transparent; @@ -386,7 +386,7 @@ // Inverted navbar links @navbar-inverse-link-color: @gray-light; -@navbar-inverse-link-hover-color: #eee; +@navbar-inverse-link-hover-color: @gray-lighter; @navbar-inverse-link-hover-bg: transparent; @navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; @navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); @@ -395,12 +395,12 @@ // Inverted navbar brand label @navbar-inverse-brand-color: @navbar-inverse-link-color; -@navbar-inverse-brand-hover-color: #eee; +@navbar-inverse-brand-hover-color: @gray-lighter; @navbar-inverse-brand-hover-bg: transparent; // Inverted navbar toggle @navbar-inverse-toggle-hover-bg: #333; -@navbar-inverse-toggle-icon-bar-bg: #eee; +@navbar-inverse-toggle-icon-bar-bg: @gray-lighter; @navbar-inverse-toggle-border-color: #333; @@ -415,7 +415,7 @@ @nav-disabled-link-color: @gray-light; @nav-disabled-link-hover-color: @gray-light; -@nav-open-link-hover-color: #eee; +@nav-open-link-hover-color: @gray-lighter; //== Tabs @nav-tabs-border-color: #ddd; @@ -440,19 +440,19 @@ //## @pagination-color: @link-color; -@pagination-bg: #eee; +@pagination-bg: @gray-lighter; @pagination-border: #ddd; @pagination-hover-color: @link-hover-color; @pagination-hover-bg: @gray-lighter; @pagination-hover-border: #ddd; -@pagination-active-color: #eee; +@pagination-active-color: @gray-lighter; @pagination-active-bg: @brand-primary; @pagination-active-border: @brand-primary; @pagination-disabled-color: @gray-light; -@pagination-disabled-bg: #eee; +@pagination-disabled-bg: @gray-lighter; @pagination-disabled-border: #ddd; @@ -511,7 +511,7 @@ //** Tooltip max width @tooltip-max-width: 200px; //** Tooltip text color -@tooltip-color: #eee; +@tooltip-color: @gray-lighter; //** Tooltip background color @tooltip-bg: #000; @tooltip-opacity: .9; @@ -527,7 +527,7 @@ //## //** Popover body background color -@popover-bg: #eee; +@popover-bg: @gray-lighter; //** Popover maximum width @popover-max-width: 276px; //** Popover border color @@ -541,7 +541,7 @@ //** Popover arrow width @popover-arrow-width: 10px; //** Popover arrow color -@popover-arrow-color: #eee; +@popover-arrow-color: @gray-lighter; @popover-arrow-color: @popover-bg; //** Popover outer arrow width @@ -570,9 +570,9 @@ @label-danger-bg: @brand-danger; //** Default label text color -@label-color: #eee; +@label-color: @gray-lighter; //** Default text color of a linked label -@label-link-hover-color: #eee; +@label-link-hover-color: @gray-lighter; //== Modals @@ -588,7 +588,7 @@ @modal-title-line-height: @line-height-base; //** Background color of modal content area -@modal-content-bg: #eee; +@modal-content-bg: @gray-lighter; //** Modal content border color @modal-content-border-color: rgba(0,0,0,.2); //** Modal content border color **for IE8** @@ -640,7 +640,7 @@ //** Background color of the whole progress component @progress-bg: #f5f5f5; //** Progress bar text color -@progress-bar-color: #eee; +@progress-bar-color: @gray-lighter; //** Variable for setting rounded corners on progress bar. @progress-border-radius: @border-radius-base; @@ -662,7 +662,7 @@ //## //** Background color on `.list-group-item` -@list-group-bg: #eee; +@list-group-bg: @gray-lighter; //** `.list-group-item` border color @list-group-border: #ddd; //** List group border radius @@ -695,7 +695,7 @@ // //## -@panel-bg: #eee; +@panel-bg: @gray-lighter; @panel-body-padding: 15px; @panel-heading-padding: 10px 15px; @panel-footer-padding: @panel-heading-padding; @@ -709,7 +709,7 @@ @panel-default-border: #ddd; @panel-default-heading-bg: #f5f5f5; -@panel-primary-text: #eee; +@panel-primary-text: @gray-lighter; @panel-primary-border: @brand-primary; @panel-primary-heading-bg: @brand-primary; @@ -717,7 +717,7 @@ @panel-success-border: @state-success-border; @panel-success-heading-bg: @state-success-bg; -@panel-info-text: #eee; +@panel-info-text: @gray-lighter; @panel-info-border: darken(#4a2b0f, 5%); @panel-info-heading-bg: #4a2b0f; @@ -761,15 +761,15 @@ // //## -@badge-color: #eee; +@badge-color: @gray-lighter; //** Linked badge text color on hover -@badge-link-hover-color: #eee; +@badge-link-hover-color: @gray-lighter; @badge-bg: @gray-light; //** Badge text color in active nav link @badge-active-color: @link-color; //** Badge background color in active nav link -@badge-active-bg: #eee; +@badge-active-bg: @gray-lighter; @badge-font-weight: bold; @badge-line-height: 1; @@ -798,15 +798,15 @@ @carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); -@carousel-control-color: #eee; +@carousel-control-color: @gray-lighter; @carousel-control-width: 15%; @carousel-control-opacity: .5; @carousel-control-font-size: 20px; -@carousel-indicator-active-bg: #eee; -@carousel-indicator-border-color: #eee; +@carousel-indicator-active-bg: @gray-lighter; +@carousel-indicator-border-color: @gray-lighter; -@carousel-caption-color: #eee; +@carousel-caption-color: @gray-lighter; //== Close @@ -815,7 +815,7 @@ @close-font-weight: bold; @close-color: #000; -@close-text-shadow: 0 1px 0 #eee; +@close-text-shadow: 0 1px 0 @gray-lighter; //== Code @@ -825,7 +825,7 @@ @code-color: #c7254e; @code-bg: #f9f2f4; -@kbd-color: #eee; +@kbd-color: @gray-lighter; @kbd-bg: #333; @pre-bg: #f5f5f5; diff --git a/client/less/main.less b/client/less/main.less index 814e99cb54..067290e9be 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -37,8 +37,7 @@ html { overflow-x: hidden; } -input[type=checkbox] -{ +input[type=checkbox] { /* Double-sized Checkboxes */ -ms-transform: scale(2); /* IE */ -moz-transform: scale(2); /* FF */ @@ -48,7 +47,7 @@ input[type=checkbox] } body.full-screen-body-background { - background-color: #eeeeee; + background-color: @gray-lighter; } @@ -101,24 +100,10 @@ h1, h2, h3, h4, h5, h6, p, li { margin: 0 auto; } -.three-by-three { - height: 100px; -} - -.darker-background { - background-color: #dedede; -} - -/**/ - .btn-cta { font-size: 40px; } -.nonprofit-cta { - font-size: 28px; -} - .btn, .shadow { white-space: normal; -webkit-box-shadow: 2px 4px 1px rgba(0, 0, 0, 0.3); @@ -142,25 +127,6 @@ ul { word-wrap: break-word; } -.img-center { - margin:0 auto; -} - -.centered-iframe { - display:block; -} - -@media (min-width: 767px) { - .landing-panel-body { - padding-left: 40px; - padding-right: 40px; - } -} - -.landing-panel-heading { - font-size: 40px; -} - .panel-heading { font-size: 25px; } @@ -175,14 +141,6 @@ ul { font-size: 26px; } -.five-pixel-break { - height: 5px; -} - -.fifteen-pixel-break { - height: 15px; -} - .nav-height { height: 50px; border: none; @@ -193,55 +151,34 @@ ul { width: 200px; } -.completion-icon{ +.completion-icon { font-size: 150px; } -.responsive-container { position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden; } -.responsive-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } - -.positive-10 { - margin-top: 10px; -} - -.positive-15-bottom { - margin-bottom: 15px; -} - .positive-15 { margin-top: 15px; } -.negative-45 { - margin-top: -45px; - margin-bottom: -45px; +.positive-15-bottom { + margin-bottom: 15px; } -.negative-55 { - margin-top: -55px; - margin-bottom: -55px; +.positive-10 { + margin-top: 10px; } -.negative-10 { - margin-top: -10px; -} - -.negative-28 { - margin-top: -28px; -} - -.negative-35 { - margin-top: -35px; -} - -.negative-30 { - margin-top: -30px; +.positive-5 { + margin-top: 5px; } .negative-5 { margin-top: -5px; } +.negative-10 { + margin-top: -10px; +} + .negative-15 { margin-top: -15px; } @@ -250,14 +187,35 @@ ul { margin-top: -20px; } -.negative-bottom-margin-30 { +.negative-28 { + margin-top: -28px; +} + +.negative-30 { + margin-top: -30px; +} + +.negative-30-bottom { margin-bottom: -30px; } +.negative-35 { + margin-top: -35px; +} + +.negative-55 { + margin-top: -55px; + margin-bottom: -55px; +} + .large-p { font-size: 24px; } +.map-p { + font-size: 20px; +} + .large-li { font-size: 24px; } @@ -270,36 +228,11 @@ ul { color: @brand-success; } -.delay-1 { - -webkit-animation-delay: 1s; - animation-delay: 1s; -} - -.delay-2 { - -webkit-animation-delay: 2s; - animation-delay: 2s; -} - -.delay-4 { - -webkit-animation-delay: 4s; - animation-delay: 4s; -} - -.delay-10 { - -webkit-animation-delay: 10s; - animation-delay: 10s; -} - .fast-animation { -webkit-animation-duration: 0.5s; animation-duration: 0.5s; } -.slow-animation { - -webkit-animation-duration: 1.5s; - animation-duration: 1.5s; -} - .disabled { pointer-events: none; cursor: default; @@ -310,10 +243,6 @@ ul { display: none; } -.button-container { - height: 120px; -} - .nav-logo { height: 40px; margin-top: -10px; @@ -329,7 +258,7 @@ ul { } @media (max-width: 991px) and (min-width: 768px) { position: absolute; - right:0; + right: 0; margin-right: 10px; white-space: nowrap; } @@ -346,8 +275,8 @@ ul { .thin-progress-bar { height: 8px; - margin-top:3px; - margin-bottom:0px; + margin-top: 3px; + margin-bottom: 0px; width: 60%; margin-right: auto; margin-left: auto; @@ -357,8 +286,8 @@ ul { margin-bottom: -6px; } -.strikethrough { - text-decoration: line-through; +.lb-container { + padding: 0px; } .btn-social { @@ -377,7 +306,7 @@ ul { } .navbar-nav > li > a { - color: #eee; + color: @gray-lighter; &:hover { color: #4a2b0f; } @@ -396,11 +325,6 @@ ul { font-size: 63px; } -.scroll-lock { - overflow: hidden; - height: 100%; -} - .signup-btn.btn { background-color: #ffac33; background-image: linear-gradient(#ffcc4d, #ffac33); @@ -421,41 +345,9 @@ ul { background-image: none; box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); } -.profile-image { - border-radius: 5px; - width: 200px; - height: 200px; - padding-left: 5px; - padding-right: 5px; -} -.team-member { - height: 420px; -} - -*, *:before, *:after {box-sizing: border-box !important;} - -.masonry-row { - -moz-column-width: 18em; - -webkit-column-width: 18em; - -moz-column-gap: 1em; - -webkit-column-gap:1em; -} - -.masonry-block { - display: inline-block; - padding: .25rem; - width: 100%; -} - -.masonry-relative { - position:relative; - display: block; -} - -.next-challenge-button { - max-width: 1500px; - margin:0 auto; +*, *:before, *:after { + box-sizing: border-box !important; } .btn-big { @@ -505,13 +397,13 @@ thead { } .navbar-nav a { - color: #eee; + color: @gray-lighter; font-size: 20px; margin-top: -5px; margin-bottom: -5px; } .navbar-toggle { - color: #eee; + color: @gray-lighter; } .navbar-right { @@ -520,16 +412,11 @@ thead { } .signup-btn-nav { - margin-top:-2px !important; + margin-top: -2px !important; padding-top: 10px !important; padding-bottom: 10px !important; } -.nameline { - margin-top: -5px; - font-size: 40px; -} - .public-profile-img { height: 200px; width: 200px; @@ -543,23 +430,6 @@ thead { border-color: #78FA89; } -.desktop-narrow { - @media (min-width: 767px) { - marign: 0 auto; - width: 80%; - } -} - -.min650 { - min-height: 630px; -} - -.portfolio-image { - height: 225px; - width: 300px; - border-radius: 5px; -} - .flat-top { margin-top: -5px; } @@ -573,7 +443,7 @@ thead { } .points-on-top { - color: #eee; + color: @gray-lighter; font-size: 35px; z-index: 2; width: 60%; @@ -642,7 +512,7 @@ thead { .challenge-list-header { background-color: #215f1e; - color: #eee; + color: @gray-lighter; font-size: 36px; text-align: center; margin-bottom: -30px; @@ -650,19 +520,8 @@ thead { padding-left: 50px; } -.all-list-header { - background-color: #4A2B0F; - color: #eee; - font-size: 36px; - text-align: center; - margin-bottom: -30px; - border-radius: 5px 5px 0px 0px; - padding-left: 50px; - -} - .closing-x { - color: #eee; + color: @gray-lighter; font-size: 50px; text-align: right; } @@ -678,7 +537,7 @@ thead { position: absolute; a { font-size: 20px; - color: #eee; + color: @gray-lighter; margin-left: 0px; margin-right: 0px; padding-left: 10px; @@ -687,7 +546,7 @@ thead { padding-bottom: 12px; &:hover { color: #4a2b0f; - background-color: #eee; + background-color: @gray-lighter; text-decoration: none; } } @@ -702,19 +561,6 @@ thead { font-size: 15px; } -.jquery-exercises-well { - text-align: left; - height: 200px; -} - -#exercise-directory { - font-size: 20px; -} - -#current-exercise { - text-size: 250px; -} - .bonfire-instructions { margin-bottom: 5px; } @@ -764,16 +610,13 @@ form.code span { .test-output { font-size: 15px; font-family: "Ubuntu Mono"; + margin-top: 8px; } #mainEditorPanel .panel-body { padding-bottom: 0px; } -.panel-bonfire { - height: 100% -} - div.CodeMirror-scroll { padding-bottom: 30px; } @@ -812,11 +655,6 @@ iframe.iphone { } } -.nonprofit-help-select-text-height { - font-size: 40px; - padding-top: 20px; -} - // To adjust right margin, negative values bring the image closer to the edge of the screen .iphone-position { position: absolute; @@ -829,25 +667,6 @@ iframe.iphone { min-height: 650px; } -// This is used to give icons text for screen readers to read out, without needing the text to actually appear. -.icon-lock{ - font-size: 0px; -} - -.stats-text { - font-size: 26px; - line-height: 150%; -} - -.github-and-twitter-button-text { - padding-top: 10px; -} - -.gitter-imbed { - height: 100%; - margin-bottom: 50px; -} - .btn-primary-ghost { background: transparent; color: @brand-primary; @@ -908,7 +727,7 @@ iframe.iphone { padding-right: 8px; margin-left: 0px; margin-right: 2px; - text-align:left; + text-align: left; font-size: 10px; } @@ -918,38 +737,11 @@ iframe.iphone { font-size: 18px; } -.tight-h3 { - margin-top: -4px; - margin-bottom: -4px; -} - .story-list { padding-bottom: 30px; margin-bottom: 30px; } -.big-ion-up-arrow { - font-size: 45px; - margin-top: -10px; - margin-bottom: -15px; - text-align: center; -} - -.big-ion-up-arrow #upvote, #reply-to-main-post { - cursor: pointer; -} - -.story-up-votes { - padding-top: 0px; - margin-left: -5px; - text-align: center; -} - -.img-story-post { - max-width: 110px; - max-height: 110px; -} - .button-spacer { padding: 5px 0 2px 0; } @@ -1057,11 +849,7 @@ hr { } .cal-heatmap-container { - background-color: #EEEEEE; -} - -.checkbox-table label { - margin-left: 10px; + background-color: @gray-lighter; } .interested-camper-image { @@ -1070,16 +858,6 @@ hr { padding: 5px; } -.svg-challenge-map { - fill: #333; - height: 40px; -} - -.news-number { - font-size: 30px; - text-align: center; -} - .mobile-story-image { border-radius: 5px; width: 100%; @@ -1101,10 +879,6 @@ hr { opacity: 0.5; } -.same-line { - display: inline-block; -} - .padded-ionic-icon { padding-top: 5px; } @@ -1126,6 +900,10 @@ hr { color: @gray-light; } +code { + padding: 0; +} + @media only screen and (min-width: 993px) { .iframe-scroll { position: fixed !important; @@ -1142,10 +920,6 @@ hr { // Calculator styles -.initially-hidden { - display: none; -} - .chart rect { fill: steelblue; } diff --git a/client/main.js b/client/main.js index 8a72664bbf..c1578c1d6f 100644 --- a/client/main.js +++ b/client/main.js @@ -1,14 +1,45 @@ +var mapShareKey = 'map-shares'; +var lastCompleted = typeof lastCompleted !== 'undefined' ? + lastCompleted : + ''; + +function getMapShares() { + var alreadyShared = JSON.parse(localStorage.getItem(mapShareKey) || '[]'); + if (!alreadyShared || !Array.isArray(alreadyShared)) { + localStorage.setItem(mapShareKey, JSON.stringify([])); + alreadyShared = []; + } + return alreadyShared; +} + +function setMapShare(id) { + var alreadyShared = getMapShares(); + var found = false; + alreadyShared.forEach(function(_id) { + if (_id === id) { + found = true; + } + }); + if (!found) { + alreadyShared.push(id); + } + localStorage.setItem(mapShareKey, JSON.stringify(alreadyShared)); + return alreadyShared; +} + $(document).ready(function() { - var challengeName = typeof challengeName !== undefined ? challengeName : 'Untitled'; + + var challengeName = typeof challengeName !== 'undefined' ? + challengeName : + 'Untitled'; + if (challengeName) { ga('send', 'event', 'Challenge', 'load', challengeName); } - $(document).ready(function() { - if (typeof editor !== 'undefined') { - $('#reset-button').on('click', resetEditor); - } - }); + if (typeof editor !== 'undefined') { + $('#reset-button').on('click', resetEditor); + } var CSRF_HEADER = 'X-CSRF-Token'; @@ -66,10 +97,64 @@ $(document).ready(function() { }, 200); }); + $('#search-issue').unbind('click'); + $('#search-issue').on('click', function() { + var queryIssue = window.location.href.toString(); + window.open('https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=' + + 'is:issue is:all ' + (challenge_Name || challengeName) + ' OR ' + + queryIssue.substr(queryIssue.lastIndexOf('challenges/') + 11) + .replace('/', ''), '_blank'); + }); + + $('#help-ive-found-a-bug-wiki-article').unbind('click'); + $('#help-ive-found-a-bug-wiki-article').on('click', function() { + window.open("https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-I've-Found-a-Bug", '_blank'); + }); + $('#report-issue').unbind('click'); $('#report-issue').on('click', function() { + var textMessage = [ + 'Challenge [', + (challenge_Name || challengeName || window.location.href), + '](', + window.location.href, + ') has an issue.\n', + 'User Agent is: ', + navigator.userAgent, + '.\n', + 'Please describe how to reproduce this issue, and include ', + 'links to screenshots if possible.\n\n' + ].join(''); + + if (editor.getValue().trim()) { + var type; + switch (challengeType) { + case challengeTypes.HTML_CSS_JQ: + type = 'html'; + break; + case challengeTypes.JAVASCRIPT: + type = 'javascript'; + break; + default: + type = ''; + } + + textMessage += [ + 'My code:\n```', + type, + '\n', + editor.getValue(), + '\n```\n\n' + ].join(''); + } + + textMessage = encodeURIComponent(textMessage); + $('#issue-modal').modal('hide'); - window.open('https://github.com/freecodecamp/freecodecamp/issues/new?&body=Challenge '+ window.location.href +' has an issue. Please describe how to reproduce it, and include links to screenshots if possible.', '_blank') + window.open( + 'https://github.com/freecodecamp/freecodecamp/issues/new?&body=' + + textMessage, '_blank' + ); }); $('#completed-courseware').unbind('click'); @@ -125,7 +210,7 @@ $(document).ready(function() { }).success( function(res) { if (res) { - window.location.href = '/challenges/next-challenge'; + window.location.href = '/challenges/next-challenge?id=' + challenge_Id; } }).fail( function() { @@ -148,7 +233,7 @@ $(document).ready(function() { } }).success( function() { - window.location.href = '/challenges/next-challenge'; + window.location.href = '/challenges/next-challenge?id=' + challenge_Id; }).fail( function() { window.location.href = '/challenges'; @@ -171,25 +256,19 @@ $(document).ready(function() { verified: false } }).success(function() { - window.location.href = '/challenges/next-challenge'; + window.location.href = '/challenges/next-challenge?id=' + challenge_Id; }).fail(function() { window.location.replace(window.location.href); }); break; case challengeTypes.BONFIRE: - window.location.href = '/challenges/next-challenge'; + window.location.href = '/challenges/next-challenge?id=' + challenge_Id; default: break; } } }); - $('.next-challenge-button').unbind('click'); - $('.next-challenge-button').on('click', function() { - l = location.pathname.split('/'); - window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); - }); - $('#complete-courseware-dialog').on('hidden.bs.modal', function() { editor.focus(); }); @@ -333,6 +412,40 @@ $(document).ready(function() { } }, false); } + + + // map sharing + var alreadyShared = getMapShares(); + + if (lastCompleted && alreadyShared.indexOf(lastCompleted) === -1) { + $('div[id="' + lastCompleted + '"]') + .parent() + .parent() + .removeClass('hidden'); + } + + // on map view + $('.map-challenge-block-share').on('click', function(e) { + e.preventDefault(); + var challengeBlockName = $(this).children().attr('id'); + var challengeBlockEscapedName = challengeBlockName.replace(/\s/, '%20'); + var username = typeof window.username !== 'undefined' ? + window.username : + ''; + + var link = 'https://www.facebook.com/dialog/feed?' + + 'app_id=1644598365767721' + + '&display=page&' + + 'caption=I%20just%20completed%20the%20' + + challengeBlockEscapedName + + '%20section%20on%20Free%20Code%20Camp%2E' + + '&link=http%3A%2F%2Ffreecodecamp%2Ecom%2F' + + username + + '&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap'; + + setMapShare(challengeBlockName); + window.location.href = link; + }); }); function defCheck(a){ diff --git a/common/app/app-stream.jsx b/common/app/app-stream.jsx index dcba9b7ca0..82d5568c09 100644 --- a/common/app/app-stream.jsx +++ b/common/app/app-stream.jsx @@ -1,18 +1,17 @@ import Rx from 'rx'; -import assign from 'object.assign'; -import { Router } from 'react-router'; +import { match } from 'react-router'; import App from './App.jsx'; import AppCat from './Cat'; import childRoutes from './routes'; -const router$ = Rx.Observable.fromNodeCallback(Router.run, Router); +const route$ = Rx.Observable.fromNodeCallback(match); -const routes = assign({ components: App }, childRoutes); +const routes = Object.assign({ components: App }, childRoutes); -export default function app$(location) { - return router$(routes, location) - .map(([initialState, transistion]) => { - return { initialState, transistion, AppCat }; +export default function app$({ location, history }) { + return route$({ routes, location, history }) + .map(([nextLocation, props]) => { + return { nextLocation, props, AppCat }; }); } diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index 94f3403590..2d9d44a4fd 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -1,5 +1,4 @@ import { Actions } from 'thundercats'; -import assign from 'object.assign'; import debugFactory from 'debug'; const debug = debugFactory('freecc:hikes:actions'); @@ -45,7 +44,7 @@ export default Actions({ dashedName, oldState.currentHike ); - return assign({}, oldState, { currentHike }); + return Object.assign({}, oldState, { currentHike }); } }); } diff --git a/common/app/routes/Jobs/components/CreateJobModal.jsx b/common/app/routes/Jobs/components/CreateJobModal.jsx new file mode 100644 index 0000000000..446ed957d6 --- /dev/null +++ b/common/app/routes/Jobs/components/CreateJobModal.jsx @@ -0,0 +1,43 @@ +import React, { PropTypes } from 'react'; +import { History } from 'react-router'; +import { Button, Modal } from 'react-bootstrap'; + +export default React.createClass({ + displayName: 'CreateJobsModal', + + propTypes: { + onHide: PropTypes.func, + showModal: PropTypes.bool + }, + + mixins: [History], + + goToNewJob(onHide) { + onHide(); + this.history.pushState(null, '/jobs/new'); + }, + + render() { + const { + showModal, + onHide + } = this.props; + + return ( + + +

Welcome to Free Code Camp's board

+

We post jobs specifically target to our junior developers.

+ +
+
+ ); + } +}); diff --git a/common/app/routes/Jobs/components/Jobs.jsx b/common/app/routes/Jobs/components/Jobs.jsx index 4b37571eb2..a4d8354b3f 100644 --- a/common/app/routes/Jobs/components/Jobs.jsx +++ b/common/app/routes/Jobs/components/Jobs.jsx @@ -1,23 +1,43 @@ import React, { cloneElement, PropTypes } from 'react'; import { contain } from 'thundercats-react'; +import { History } from 'react-router'; import { Button, Jumbotron, Row } from 'react-bootstrap'; + +import CreateJobModal from './CreateJobModal.jsx'; import ListJobs from './List.jsx'; export default contain( { store: 'jobsStore', - fetchAction: 'jobActions.getJobs' + fetchAction: 'jobActions.getJobs', + actions: 'jobActions' }, React.createClass({ displayName: 'Jobs', + + mixins: [History], + propTypes: { children: PropTypes.element, - jobs: PropTypes.array + jobActions: PropTypes.object, + jobs: PropTypes.array, + showModal: PropTypes.bool }, - renderList(jobs) { + handleJobClick(id) { + const { jobActions } = this.props; + if (!id) { + return null; + } + jobActions.findJob(id); + this.history.pushState(null, `/jobs/${id}`); + }, + + renderList(handleJobClick, jobs) { return ( - + ); }, @@ -32,7 +52,12 @@ export default contain( }, render() { - const { children, jobs } = this.props; + const { + children, + jobs, + showModal, + jobActions + } = this.props; return (
@@ -46,15 +71,19 @@ export default contain(

{ this.renderChild(children, jobs) || - this.renderList(jobs) } - + this.renderList(this.handleJobClick, jobs) } + +
); } diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index b90050aa74..2457bcb7f0 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -1,81 +1,85 @@ import React, { PropTypes } from 'react'; -import { contain } from 'thundercats-react'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import moment from 'moment'; -export default contain( - { +export default React.createClass({ + displayName: 'ListJobs', + + propTypes: { + handleClick: PropTypes.func, + jobs: PropTypes.array }, - React.createClass({ - displayName: 'ListJobs', - propTypes: { - jobs: PropTypes.array - }, + renderJobs(handleClick, jobs =[]) { + const thumbnailStyle = { + backgroundColor: 'white', + maxHeight: '100px', + maxWidth: '100px' + }; - renderJobs(jobs =[]) { - const thumbnailStyle = { - backgroundColor: 'white', - maxHeight: '100px', - maxWidth: '100px' - }; - return jobs.map(( - { - id, - company, - position, - description, - logo, - city, - state, - email, - phone, - postedOn - }, - index - ) => { - const header = ( -
-

{ company }

-
- { position } -
-
- ); - return ( - - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
-
- ); - }); - }, - - render() { - const { jobs } = this.props; - return ( - - { this.renderJobs(jobs) } - + return jobs.map(( + { + id, + company, + position, + isHighlighted, + description, + logo, + city, + state, + email, + phone, + postedOn + }, + index + ) => { + const header = ( +
+

{ company }

+
+ { position } +
+
); - } - }) -); + return ( + + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

handleClick(id) }>{ description }

+
+
+ ); + }); + }, + + render() { + const { + handleClick, + jobs + } = this.props; + + return ( + + { this.renderJobs(handleClick, jobs) } + + ); + } +}); diff --git a/common/app/routes/Jobs/components/NewJob.jsx b/common/app/routes/Jobs/components/NewJob.jsx new file mode 100644 index 0000000000..4c369411f0 --- /dev/null +++ b/common/app/routes/Jobs/components/NewJob.jsx @@ -0,0 +1,319 @@ +import React, { PropTypes } from 'react'; +import { History } from 'react-router'; +import { contain } from 'thundercats-react'; +import debugFactory from 'debug'; +import { getDefaults } from '../utils'; + +import { + inHTMLData, + uriInSingleQuotedAttr +} from 'xss-filters'; + +import { + Button, + Col, + Input, + Row, + Well +} from 'react-bootstrap'; + +import { + isAscii, + isEmail, + isMobilePhone, + isURL +} from 'validator'; + +const debug = debugFactory('freecc:jobs:newForm'); + +const checkValidity = [ + 'position', + 'locale', + 'description', + 'email', + 'phone', + 'url', + 'logo', + 'name', + 'highlight' +]; + +function formatValue(value, validator, type = 'string') { + const formated = getDefaults(type); + if (validator && type === 'string') { + formated.valid = validator(value); + } + if (value) { + formated.value = value; + formated.bsStyle = formated.valid ? 'success' : 'error'; + } + return formated; +} + +function isValidURL(data) { + return isURL(data, { 'require_protocol': true }); +} + +function isValidPhone(data) { + return isMobilePhone(data, 'en-US'); +} + +export default contain({ + actions: 'jobActions', + store: 'jobsStore', + map({ form = {} }) { + const { + position, + locale, + description, + email, + phone, + url, + logo, + name, + highlight + } = form; + return { + position: formatValue(position, isAscii), + locale: formatValue(locale, isAscii), + description: formatValue(description, isAscii), + email: formatValue(email, isEmail), + phone: formatValue(phone, isValidPhone), + url: formatValue(url, isValidURL), + logo: formatValue(logo, isValidURL), + name: formatValue(name, isAscii), + highlight: formatValue(highlight, null, 'bool') + }; + }, + subscribeOnWillMount() { + return typeof window !== 'undefined'; + } + }, + React.createClass({ + displayName: 'NewJob', + + propTypes: { + jobActions: PropTypes.object, + position: PropTypes.object, + locale: PropTypes.object, + description: PropTypes.object, + email: PropTypes.object, + phone: PropTypes.object, + url: PropTypes.object, + logo: PropTypes.object, + name: PropTypes.object, + highlight: PropTypes.object + }, + + mixins: [History], + + handleSubmit(e) { + e.preventDefault(); + const props = this.props; + let valid = true; + checkValidity.forEach((prop) => { + // if value exist, check if it is valid + if (props[prop].value && props[prop].type !== 'boolean') { + valid = valid && !!props[prop].valid; + } + }); + + if (!valid) { + debug('form not valid'); + return; + } + + const { + position, + locale, + description, + email, + phone, + url, + logo, + name, + highlight, + jobActions + } = this.props; + + // sanitize user output + const jobValues = { + position: inHTMLData(position.value), + location: inHTMLData(locale.value), + description: inHTMLData(description.value), + email: inHTMLData(email.value), + phone: inHTMLData(phone.value), + url: uriInSingleQuotedAttr(url.value), + logo: uriInSingleQuotedAttr(logo.value), + name: inHTMLData(name.value), + highlight: !!highlight.value + }; + + const job = Object.keys(jobValues).reduce((accu, prop) => { + if (jobValues[prop]) { + accu[prop] = jobValues[prop]; + } + return accu; + }, {}); + + job.postedOn = new Date(); + debug('job sanitized', job); + jobActions.saveForm(job); + + this.history.pushState(null, '/jobs/new/preview'); + }, + + componentDidMount() { + const { jobActions } = this.props; + jobActions.getSavedForm(); + }, + + handleChange(name, { target: { value } }) { + const { jobActions: { handleForm } } = this.props; + handleForm({ [name]: value }); + }, + + render() { + const { + position, + locale, + description, + email, + phone, + url, + logo, + name, + highlight, + jobActions: { handleForm } + } = this.props; + const { handleChange } = this; + const labelClass = 'col-sm-offset-1 col-sm-2'; + const inputClass = 'col-sm-6'; + + return ( +
+ + + +

Create Your Job Post

+
+ +
+

Job Information

+
+ handleChange('position', e) } + placeholder='Position' + type='text' + value={ position.value } + wrapperClassName={ inputClass } /> + handleChange('locale', e) } + placeholder='Location' + type='text' + value={ locale.value } + wrapperClassName={ inputClass } /> + handleChange('description', e) } + placeholder='Description' + rows='10' + type='textarea' + value={ description.value } + wrapperClassName={ inputClass } /> + +
+

Company Information

+
+ handleChange('name', e) } + placeholder='Foo, INC' + type='text' + value={ name.value } + wrapperClassName={ inputClass } /> + handleChange('email', e) } + placeholder='Email' + type='email' + value={ email.value } + wrapperClassName={ inputClass } /> + handleChange('phone', e) } + placeholder='555-123-1234' + type='tel' + value={ phone.value } + wrapperClassName={ inputClass } /> + handleChange('url', e) } + placeholder='http://freecatphotoapp.com' + type='url' + value={ url.value } + wrapperClassName={ inputClass } /> + handleChange('logo', e) } + placeholder='http://freecatphotoapp.com/logo.png' + type='url' + value={ logo.value } + wrapperClassName={ inputClass } /> + +
+

Make it stand out

+
+ handleForm({ + highlight: !!checked + }) + } + type='checkbox' /> +
+ + + + + + + + + +
+ ); + } + }) +); diff --git a/common/app/routes/Jobs/components/Preview.jsx b/common/app/routes/Jobs/components/Preview.jsx new file mode 100644 index 0000000000..5b6081be5c --- /dev/null +++ b/common/app/routes/Jobs/components/Preview.jsx @@ -0,0 +1,14 @@ +// import React, { PropTypes } from 'react'; +import { contain } from 'thundercats-react'; +import ShowJob from './ShowJob.jsx'; + +export default contain( + { + store: 'JobsStore', + actions: 'JobActions', + map({ form: job = {} }) { + return { job }; + } + }, + ShowJob +); diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index 209917cff7..ce2512c27d 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -1,59 +1,24 @@ -import React, { PropTypes } from 'react'; -import { Thumbnail, Panel, Well } from 'react-bootstrap'; -import moment from 'moment'; +import { contain } from 'thundercats-react'; +import ShowJob from './ShowJob.jsx'; -const thumbnailStyle = { - backgroundColor: 'white', - maxHeight: '100px', - maxWidth: '100px' -}; -export default React.createClass({ - displayName: 'ShowJob', - propTypes: { - job: PropTypes.object +export default contain( + { + store: 'jobsStore', + fetchAction: 'jobActions.getJob', + map({ currentJob }) { + return { job: currentJob }; + }, + getPayload({ params: { id }, job = {} }) { + return { + id, + isPrimed: job.id === id + }; + }, + // using es6 destructuring + shouldContainerFetch({ job = {} }, { params: { id } } + ) { + return job.id !== id; + } }, - - renderHeader({ company, position }) { - return ( -
-

{ company }

-
- { position } -
-
- ); - }, - - render() { - const { job } = this.props; - const { - logo, - position, - city, - state, - email, - phone, - postedOn, - description - } = job; - - return ( - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
- ); - } -}); + ShowJob +); diff --git a/common/app/routes/Jobs/components/ShowJob.jsx b/common/app/routes/Jobs/components/ShowJob.jsx new file mode 100644 index 0000000000..1a048a3fff --- /dev/null +++ b/common/app/routes/Jobs/components/ShowJob.jsx @@ -0,0 +1,67 @@ +import React, { PropTypes } from 'react'; +import { Row, Thumbnail, Panel, Well } from 'react-bootstrap'; +import moment from 'moment'; + +const thumbnailStyle = { + backgroundColor: 'white', + maxHeight: '100px', + maxWidth: '100px' +}; + +export default React.createClass({ + displayName: 'ShowJob', + propTypes: { + job: PropTypes.object, + params: PropTypes.object + }, + + renderHeader({ company, position }) { + return ( +
+

{ company }

+
+ { position } +
+
+ ); + }, + + render() { + const { job = {} } = this.props; + const { + logo, + position, + city, + company, + state, + email, + phone, + postedOn, + description + } = job; + + return ( +
+ + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

{ description }

+
+
+
+ ); + } +}); diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/flux/Actions.js index 68d5eeae57..5900e6dda1 100644 --- a/common/app/routes/Jobs/flux/Actions.js +++ b/common/app/routes/Jobs/flux/Actions.js @@ -1,27 +1,104 @@ import { Actions } from 'thundercats'; +import store from 'store'; import debugFactory from 'debug'; const debug = debugFactory('freecc:jobs:actions'); +const assign = Object.assign; export default Actions({ setJobs: null, - getJob(id) { - return { id }; + // findJob assumes that the job is already in the list of jobs + findJob(id) { + return oldState => { + const { currentJob = {}, jobs = [] } = oldState; + // currentJob already set + // do nothing + if (currentJob.id === id) { + return null; + } + const foundJob = jobs.reduce((newJob, job) => { + if (job.id === id) { + return job; + } + return newJob; + }, null); + + // if no job found this will be null which is a op noop + return foundJob ? + assign({}, oldState, { currentJob: foundJob }) : + null; + }; }, + setError: null, + getJob: null, getJobs(params) { return { params }; + }, + openModal() { + return { showModal: true }; + }, + closeModal() { + return { showModal: false }; + }, + handleForm(value) { + return { + transform(oldState) { + const { form } = oldState; + const newState = assign({}, oldState); + newState.form = assign( + {}, + form, + value + ); + return newState; + } + }; + }, + saveForm: null, + getSavedForm: null, + setForm(form) { + return { form }; } }) .refs({ displayName: 'JobActions' }) .init(({ instance: jobActions, args: [services] }) => { jobActions.getJobs.subscribe(() => { - services.read('job', null, null, (err, jobs) => { + services.read('jobs', null, null, (err, jobs) => { if (err) { debug('job services experienced an issue', err); - jobActions.setJobs({ jobs: [] }); + return jobActions.setError({ err }); } jobActions.setJobs({ jobs }); }); }); + + jobActions.getJob.subscribe(({ id, isPrimed }) => { + // job is already set, do nothing. + if (isPrimed) { + debug('job is primed'); + return; + } + services.read('jobs', { id }, null, (err, job) => { + if (err) { + debug('job services experienced an issue', err); + return jobActions.setError({ err }); + } + if (job) { + jobActions.setJobs({ currentJob: job }); + } + jobActions.setJobs({}); + }); + }); + + jobActions.saveForm.subscribe((form) => { + store.set('newJob', form); + }); + + jobActions.getSavedForm.subscribe(() => { + const job = store.get('newJob'); + if (job && !Array.isArray(job) && typeof job === 'object') { + jobActions.setForm(job); + } + }); return jobActions; }); diff --git a/common/app/routes/Jobs/flux/Store.js b/common/app/routes/Jobs/flux/Store.js index 803bc7b0bd..b2f5132013 100644 --- a/common/app/routes/Jobs/flux/Store.js +++ b/common/app/routes/Jobs/flux/Store.js @@ -1,10 +1,30 @@ import { Store } from 'thundercats'; -const { setter } = Store; +const { + createRegistrar, + setter, + transformer +} = Store; -export default Store() +export default Store({ showModal: false }) .refs({ displayName: 'JobsStore' }) .init(({ instance: jobsStore, args: [cat] }) => { - let jobActions = cat.getActions('JobActions'); - jobsStore.register(setter(jobActions.setJobs)); + const { + setJobs, + findJob, + setError, + openModal, + closeModal, + handleForm, + setForm + } = cat.getActions('JobActions'); + const register = createRegistrar(jobsStore); + register(setter(setJobs)); + register(setter(setError)); + register(setter(openModal)); + register(setter(closeModal)); + register(setter(setForm)); + + register(transformer(findJob)); + register(handleForm); }); diff --git a/common/app/routes/Jobs/index.js b/common/app/routes/Jobs/index.js index 9d52bf2b53..6c556c994e 100644 --- a/common/app/routes/Jobs/index.js +++ b/common/app/routes/Jobs/index.js @@ -1,14 +1,26 @@ import Jobs from './components/Jobs.jsx'; +import NewJob from './components/NewJob.jsx'; +import Show from './components/Show.jsx'; +import Preview from './components/Preview.jsx'; /* - * show: /jobs - * showOne: /jobs/:id - * edit /jobs/:id - * delete /jobs/:id - * createOne /jobs/new + * index: /jobs list jobs + * show: /jobs/:id show one job + * create /jobs/new create a new job */ export default { - path: 'jobs', - component: Jobs + childRoutes: [{ + path: '/jobs', + component: Jobs + }, { + path: 'jobs/new', + component: NewJob + }, { + path: 'jobs/new/preview', + component: Preview + }, { + path: 'jobs/:id', + component: Show + }] }; diff --git a/common/app/routes/Jobs/utils.js b/common/app/routes/Jobs/utils.js new file mode 100644 index 0000000000..aeb0396c12 --- /dev/null +++ b/common/app/routes/Jobs/utils.js @@ -0,0 +1,22 @@ +const defaults = { + 'string': { + value: '', + valid: false, + pristine: true, + type: 'string' + }, + bool: { + value: false, + type: 'boolean' + } +}; + +export function getDefaults(type, value) { + if (!type) { + return defaults['string']; + } + if (value) { + return Object.assign({}, defaults[type], { value }); + } + return Object.assign({}, defaults[type]); +} diff --git a/common/app/routes/index.js b/common/app/routes/index.js index 973e9f2bf0..1f14733f82 100644 --- a/common/app/routes/index.js +++ b/common/app/routes/index.js @@ -3,12 +3,8 @@ import Hikes from './Hikes'; export default { path: '/', - getChildRoutes(locationState, cb) { - setTimeout(() => { - cb(null, [ - Jobs, - Hikes - ]); - }, 0); - } + childRoutes: [ + Jobs, + Hikes + ] }; diff --git a/common/models/job.json b/common/models/job.json index a3392fee82..f77fc6defa 100644 --- a/common/models/job.json +++ b/common/models/job.json @@ -1,6 +1,7 @@ { "name": "job", "base": "PersistedModel", + "strict": true, "idInjection": true, "trackChanges": false, "properties": { @@ -29,6 +30,9 @@ "state": { "type": "string" }, + "url": { + "type": "string" + }, "country": { "type": "string" }, @@ -38,7 +42,7 @@ "description": { "type": "string" }, - "isApproverd": { + "isApproved": { "type": "boolean" }, "isHighlighted": { diff --git a/common/models/story.json b/common/models/story.json index 5d8e56dfb2..d338199a48 100644 --- a/common/models/story.json +++ b/common/models/story.json @@ -34,10 +34,6 @@ "description": { "type": "string" }, - "originalStoryAuthorEmail": { - "type": "string", - "default": "" - }, "rank": { "type": "number", "default": 0 diff --git a/common/models/user.json b/common/models/user.json index d0d8bed6e8..c46d1c2f13 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -88,39 +88,6 @@ "google": { "type": "string" }, - "completedBonfires": { - "type": [ - { - "id": "string", - "name": "string", - "completedWith": "string", - "completedDate": "string", - "solution": "string" - } - ], - "default": [] - }, - "uncompletedCoursewares": { - "type": "array", - "default": [] - }, - "completedCoursewares": { - "type": [ - { - "completedDate": { - "type": "string", - "defaultFn": "now" - }, - "id": "string", - "name": "string", - "completedWith": "string", - "solution": "string", - "githubLink": "string", - "verified": "boolean" - } - ], - "default": [] - }, "currentStreak": { "type": "number", "default": 0 @@ -133,13 +100,22 @@ "type": "boolean", "default": true }, + "isLocked": { + "type": "boolean", + "default": false + }, "currentChallenge": { "type": {} }, + "isUniqMigrated": { + "type": "boolean", + "default": false + }, "completedChallenges": { "type": [ { "completedDate": "number", + "lastUpdated": "number", "id": "string", "name": "string", "completedWith": "string", @@ -157,6 +133,9 @@ "rand": { "type": "number", "index": true + }, + "tshirtVote": { + "type": "number" } }, "validations": [], diff --git a/gulpfile.js b/gulpfile.js index b5eee5417a..fd5fdda38d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,6 @@ +// enable debug for gulp +process.env.DEBUG = process.env.DEBUG || 'freecc:*'; + require('babel-core/register'); var Rx = require('rx'), gulp = require('gulp'), @@ -6,6 +9,7 @@ var Rx = require('rx'), // utils plumber = require('gulp-plumber'), notify = require('gulp-notify'), + gutil = require('gulp-util'), reduce = require('gulp-reduce-file'), sortKeys = require('sort-keys'), debug = require('debug')('freecc:gulp'), @@ -25,6 +29,7 @@ var Rx = require('rx'), // rev rev = require('gulp-rev'), revReplace = require('gulp-rev-replace'), + revDel = require('rev-del'), // lint jsonlint = require('gulp-jsonlint'), @@ -33,6 +38,7 @@ var Rx = require('rx'), Rx.config.longStackSupport = true; +var __DEV__ = process.env.NODE_ENV !== 'production'; var reloadDelay = 1000; var reload = sync.reload; var paths = { @@ -43,6 +49,8 @@ var paths = { '!public/js/bundle*', 'node_modules/', 'client/', + 'seed', + 'server/manifests/*.json', 'server/rev-manifest.json' ], @@ -67,8 +75,7 @@ var paths = { ], dependents: [ - 'client/commonFramework.js', - 'client/sandbox.js' + 'client/commonFramework.js' ], less: './client/less/main.less', @@ -107,11 +114,22 @@ function errorHandler() { this.emit('end'); } +function delRev(dest, manifestName) { + // in production do not delete old revisions + if (!__DEV__) { + return gutil.noop(); + } + return revDel({ + oldManifest: path.join(paths.manifest, manifestName), + dest: dest + }); +} + gulp.task('serve', function(cb) { var called = false; nodemon({ script: paths.server, - ext: '.js .json', + ext: '.jsx .js .json', ignore: paths.serverIgnore, exec: path.join(__dirname, 'node_modules/.bin/babel-node'), env: { @@ -143,7 +161,7 @@ var syncDepenedents = [ 'js', 'less', 'dependents', - 'pack-client', + 'pack-watch', 'build-manifest' ]; @@ -173,6 +191,9 @@ gulp.task('lint-json', function() { gulp.task('test-challenges', ['lint-json']); gulp.task('pack-client', function() { + var manifestName = 'react-manifest.json'; + var dest = webpackConfig.output.path; + return gulp.src(webpackConfig.entry) .pipe(plumber({ errorHandler: errorHandler })) .pipe(webpack(Object.assign( @@ -180,30 +201,73 @@ gulp.task('pack-client', function() { webpackConfig, webpackOptions ))) - .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(gulp.dest(dest)) .pipe(rev()) // copy files to public - .pipe(gulp.dest(webpackConfig.output.path)) - // create and merge manifest - .pipe(rev.manifest('react-manifest.json')) + .pipe(gulp.dest(dest)) + // create manifest + .pipe(rev.manifest(manifestName)) + // delete old rev + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); -gulp.task('pack-watch', function() { - return gulp.src(webpackConfig.entry) +var defaultStatsOptions = { + colors: gutil.colors.supportsColor, + hash: false, + timings: false, + chunks: false, + chunkModules: false, + modules: false, + children: true, + version: true, + cached: false, + cachedAssets: false, + reasons: false, + source: false, + errorDetails: false +}; + +gulp.task('pack-watch', function(cb) { + var called = false; + gulp.src(webpackConfig.entry) .pipe(plumber({ errorHandler: errorHandler })) .pipe(webpack(Object.assign( {}, webpackConfig, webpackOptions, { watch: true } - ))) - .pipe(gulp.dest(webpackConfig.output.path)) + ), null, function(notUsed, stats) { + if (stats) { + gutil.log(stats.toString(defaultStatsOptions)); + } + + if (!called) { + debug('webpack watch completed'); + called = true; + cb(); + } + + })) + .pipe(gulp.dest(webpackConfig.output.path)); +}); + +gulp.task('pack-watch-manifest', function() { + var manifestName = 'react-manifest.json'; + var dest = webpackConfig.output.path; + return gulp.src(dest + '/bundle.js') .pipe(rev()) // copy files to public - .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(gulp.dest(dest)) // create manifest - .pipe(rev.manifest('react-manifest.json')) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); @@ -217,32 +281,45 @@ gulp.task('pack-node', function() { gulp.task('pack', ['pack-client', 'pack-node']); gulp.task('less', function() { + var manifestName = 'css-manifest.json'; + var dest = paths.css; return gulp.src(paths.less) .pipe(plumber({ errorHandler: errorHandler })) // copile .pipe(less({ paths: [ path.join(__dirname, 'less', 'includes') ] })) - .pipe(gulp.dest(paths.css)) + .pipe(gulp.dest(dest)) // add revision .pipe(rev()) // copy files to public - .pipe(gulp.dest(paths.css)) + .pipe(gulp.dest(dest)) // create and merge manifest - .pipe(rev.manifest('css-manifest.json')) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); gulp.task('js', function() { + var manifestName = 'js-manifest.json'; + var dest = paths.publicJs; + return gulp.src(paths.js) .pipe(plumber({ errorHandler: errorHandler })) - .pipe(gulp.dest(paths.publicJs)) + .pipe(gulp.dest(dest)) // create registry file .pipe(rev()) // copy revisioned assets to dest - .pipe(gulp.dest(paths.publicJs)) + .pipe(gulp.dest(dest)) // create manifest file - .pipe(rev.manifest('js-manifest.json')) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) // copy manifest file to dest .pipe(gulp.dest(paths.manifest)); }); @@ -250,6 +327,9 @@ gulp.task('js', function() { // commonFramework depend on iFrameScripts // sandbox depends on plugin gulp.task('dependents', ['js'], function() { + var manifestName = 'dependents-manifest.json'; + var dest = paths.publicJs; + var manifest = gulp.src( path.join(__dirname, paths.manifest, 'js-manifest.json') ); @@ -257,9 +337,14 @@ gulp.task('dependents', ['js'], function() { return gulp.src(paths.dependents) .pipe(plumber({ errorHandler: errorHandler })) .pipe(revReplace({ manifest: manifest })) + .pipe(gulp.dest(dest)) .pipe(rev()) - .pipe(gulp.dest(paths.publicJs)) - .pipe(rev.manifest('dependents-manifest.json')) + .pipe(gulp.dest(dest)) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); @@ -301,7 +386,9 @@ var watchDependents = [ 'dependents', 'serve', 'sync', - 'build-manifest' + 'build-manifest', + 'pack-watch', + 'pack-watch-manifest' ]; gulp.task('watch', watchDependents, function() { @@ -311,7 +398,15 @@ gulp.task('watch', watchDependents, function() { gulp.watch(paths.js, ['js', 'dependents']); gulp.watch(paths.dependents, ['dependents']); gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']); + gulp.watch(webpackConfig.output.path + '/bundle.js', ['pack-watch-manifest']); }); -gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']); +gulp.task('default', [ + 'less', + 'serve', + 'pack-watch', + 'pack-watch-manifest', + 'watch', + 'sync' +]); diff --git a/package.json b/package.json index d35a1c4cf9..c3f7c0bebb 100644 --- a/package.json +++ b/package.json @@ -11,25 +11,15 @@ "prestart-production": "bower cache clean && bower install && gulp build", "start-production": "node pm2Start", "lint": "eslint --ext=.js,.jsx .", - "test": "mocha --compilers js:babel/register" + "test": "gulp test-challenges" }, "license": "(BSD-3-Clause AND CC-BY-SA-4.0)", - "contributors": [ - { - "name": "Quincy Larson", - "url": "https://github.com/QuincyLarson" - }, - { - "name": "Nathan Leniz", - "url": "https://github.com/terakilobyte" - } - ], "dependencies": { "accepts": "~1.2.5", "async": "~0.9.0", - "babel": "5.6.14", - "babel-core": "5.6.15", - "babel-eslint": "^4.0.5", + "babel": "5.8.23", + "babel-core": "5.8.23", + "babel-eslint": "4.1.1", "babel-loader": "5.2.2", "bcrypt-nodejs": "~0.0.3", "body-parser": "^1.13.2", @@ -46,6 +36,7 @@ "dedent": "^0.4.0", "dotenv": "~0.4.0", "errorhandler": "~1.3.0", + "es6-map": "^0.1.1", "eslint": "^1.1.0", "eslint-plugin-react": "^3.2.1", "express": "~4.10.4", @@ -63,13 +54,14 @@ "gulp-reduce-file": "0.0.1", "gulp-rev": "^6.0.1", "gulp-rev-replace": "^0.4.2", + "gulp-util": "^3.0.6", "gulp-webpack": "^1.5.0", "helmet": "~0.9.0", "helmet-csp": "^0.2.3", + "history": "^1.9.0", "jade": "~1.8.0", "json-loader": "^0.5.2", - "less": "~1.7.5", - "less-middleware": "~2.0.1", + "less": "~2.5.1", "lodash": "^3.9.3", "loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password", "loopback-boot": "2.8.2", @@ -95,33 +87,33 @@ "pmx": "^0.3.16", "ramda": "~0.10.0", "react": "^0.13.3", - "react-bootstrap": "^0.23.7", + "react-bootstrap": "~0.23.7", "react-motion": "~0.1.0", - "react-router": "https://github.com/BerkeleyTrue/react-router#freecodecamp", + "react-router": "^1.0.0-rc1", "react-vimeo": "^0.0.3", "request": "~2.53.0", + "rev-del": "^1.0.5", "rx": "^2.5.3", "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", "twit": "~1.1.20", "uglify-js": "~2.4.15", - "validator": "~3.22.1", + "validator": "^3.22.1", "webpack": "^1.9.12", + "xss-filters": "^1.2.6", "yui": "~3.18.1" }, "devDependencies": { - "babel-eslint": "^3.1.7", "blessed": "~0.0.37", "bower-main-files": "~0.0.4", "browser-sync": "~1.8.1", "browserify": "^10.2.4", "chai": "~1.10.0", "envify": "^3.4.0", - "eslint": "^0.21.2", - "eslint-plugin-react": "^2.3.0", "gulp": "~3.8.8", "gulp-eslint": "~0.9.0", "gulp-inject": "~1.0.2", diff --git a/public/css/lato.css b/public/css/lato.css new file mode 100644 index 0000000000..57941353cc --- /dev/null +++ b/public/css/lato.css @@ -0,0 +1,9 @@ +@font-face { + font-family: "Lato"; + src: url(/fonts/Lato-Regular.ttf) format("truetype"); +} + +@font-face { + font-family: "Lato Light"; + src: url(/fonts/Lato-Light.ttf) format("truetype"); +} diff --git a/public/css/ubuntu.css b/public/css/ubuntu.css new file mode 100644 index 0000000000..7036f2e4f4 --- /dev/null +++ b/public/css/ubuntu.css @@ -0,0 +1,9 @@ +@font-face { + font-family: "Ubuntu"; + src: url(/fonts/Ubuntu-Regular.ttf) format("truetype"); +} + +@font-face { + font-family: "Ubuntu Mono"; + src: url(/fonts/UbuntuMono-Regular.ttf) format("truetype"); +} diff --git a/public/fonts/Lato-Light.ttf b/public/fonts/Lato-Light.ttf old mode 100755 new mode 100644 diff --git a/public/fonts/Lato-Regular.ttf b/public/fonts/Lato-Regular.ttf new file mode 100644 index 0000000000..04ea8efb13 Binary files /dev/null and b/public/fonts/Lato-Regular.ttf differ diff --git a/public/fonts/Ubuntu-Regular.ttf b/public/fonts/Ubuntu-Regular.ttf new file mode 100644 index 0000000000..d748728a20 Binary files /dev/null and b/public/fonts/Ubuntu-Regular.ttf differ diff --git a/public/fonts/UbuntuMono-Regular.ttf b/public/fonts/UbuntuMono-Regular.ttf new file mode 100644 index 0000000000..fdd309d716 Binary files /dev/null and b/public/fonts/UbuntuMono-Regular.ttf differ diff --git a/public/fonts/ionicons.eot b/public/fonts/ionicons.eot old mode 100755 new mode 100644 diff --git a/public/fonts/ionicons.svg b/public/fonts/ionicons.svg old mode 100755 new mode 100644 diff --git a/public/fonts/ionicons.ttf b/public/fonts/ionicons.ttf old mode 100755 new mode 100644 diff --git a/public/fonts/ionicons.woff b/public/fonts/ionicons.woff old mode 100755 new mode 100644 diff --git a/seed/bonfireMDNlinks.js b/seed/bonfireMDNlinks.js index 0e0b0f89b1..d3beb5908d 100644 --- a/seed/bonfireMDNlinks.js +++ b/seed/bonfireMDNlinks.js @@ -1,93 +1,95 @@ -// MDN Links +/** + * MDN Links + * + * These links are for Bonfires. Each key/value pair is used to render a Bonfire with appropriate links. + * The text of the key is what the link text will be, e.g. Global Array Object + * General convention is to use the page title of the MDN reference page. + * + **/ -/* These links are for Bonfires. Each key/value pair is used to render a Bonfire with appropriate links. - - - The text of the key is what the link text will be, e.g. Global Array Object - General convention is to use the page title of the MDN reference page. -*/ -var links = - { +var links = { // ========= NON MDN REFS "Currying": "https://leanpub.com/javascript-allonge/read#pabc", "Smallest Common Multiple": "https://www.mathsisfun.com/least-common-multiple.html", "Permutations": "https://www.mathsisfun.com/combinatorics/combinations-permutations.html", "HTML Entities": "http://dev.w3.org/html5/html-author/charref", "Symmetric Difference": "https://www.youtube.com/watch?v=PxffSUQRkG4", + "Roman Numerals": "http://www.mathsisfun.com/roman-numerals.html", - // ========= GLOBAL OBJECTS - "Global Array Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", - "Global Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", - "Global String Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String", - "Boolean Objects" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean", - "RegExp" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp", - "Global Function Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function", - "Arguments object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments", + // ========= GLOBAL OBJECTS + "Global Array Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", + "Global Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "Global String Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String", + "Boolean Objects": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean", + "RegExp": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp", + "Global Function Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function", + "Arguments object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments", "Closures": "https://developer.mozilla.org/en-US/docs/" + "Web/JavaScript/Closures", - // ========= GLOBAL OBJECT METHODS - "parseInt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt", + // ========= GLOBAL OBJECT METHODS + "parseInt()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt", - // ========= PROPERTIES/MISC - "String.length" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length", + // ========= PROPERTIES/MISC + "String.length": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length", - // ========== OBJECT METHODS - "Object.getOwnPropertyNames()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames", - "Object.keys()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys", - "Object.hasOwnProperty()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty", + // ========== OBJECT METHODS + "Object.getOwnPropertyNames()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames", + "Object.keys()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys", + "Object.hasOwnProperty()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty", - // ======== STRING METHODS - "String.charAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt", - "String.charCodeAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt", - "String.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat", - "String.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf", - "String.fromCharCode()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode", - "String.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf", - "String.match()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match", - "String.replace()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace", - "String.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice", - "String.split()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split", - "String.substring()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring", - "String.substr()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr", - "String.toLowerCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase", - "String.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toString", - "String.toUpperCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase", - // ======== ARRAY METHODS - "Array.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat", - "Array.every()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every", - "Array.filter()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter", - "Array.forEach()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach", - "Array.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf", - "Array.isArray()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray", - "Array.join()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join", - "Array.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf", - "Array.map()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map", - "Array.pop()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop", - "Array.push()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push", - "Array.reduce()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce", - "Array.reverse()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse", - "Array.shift()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift", - "Array.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice", - "Array.some()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some", - "Array.sort()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort", - "Array.splice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice", - "Array.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString", + // ======== STRING METHODS + "String.charAt()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt", + "String.charCodeAt()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt", + "String.concat()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat", + "String.indexOf()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf", + "String.fromCharCode()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode", + "String.lastIndexOf()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf", + "String.match()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match", + "String.replace()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace", + "String.slice()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice", + "String.split()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split", + "String.substring()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring", + "String.substr()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr", + "String.toLowerCase()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase", + "String.toString()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toString", + "String.toUpperCase()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase", - // ======== MATH METHODS - "Math.max()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max", - "Math.min()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min", - "Math.pow()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow", - "Remainder" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_(.25)", + // ======== ARRAY METHODS + "Array.concat()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat", + "Array.every()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every", + "Array.filter()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter", + "Array.forEach()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach", + "Array.indexOf()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf", + "Array.isArray()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray", + "Array.join()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join", + "Array.lastIndexOf()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf", + "Array.map()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map", + "Array.pop()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop", + "Array.push()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push", + "Array.reduce()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce", + "Array.reverse()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse", + "Array.shift()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift", + "Array.slice()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice", + "Array.some()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some", + "Array.sort()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort", + "Array.splice()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice", + "Array.toString()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString", - // ======== GENERAL JAVASCRIPT REFERENCES - "Arithmetic Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators", - "Comparison Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators", - "Details of the Object Model" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model", + // ======== MATH METHODS + "Math.max()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max", + "Math.min()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min", + "Math.pow()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow", + "Remainder": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_(.25)", + + // ======== GENERAL JAVASCRIPT REFERENCES + "Arithmetic Operators": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators", + "Comparison Operators": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators", + "Details of the Object Model": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model", "For Loops": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for" - }; +}; module.exports = links; diff --git a/seed/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json index 62a5f2cd3c..db81a04400 100644 --- a/seed/challenges/advanced-bonfires.json +++ b/seed/challenges/advanced-bonfires.json @@ -1,41 +1,45 @@ { "name": "Advanced Algorithm Scripting", - "order": 0.013, + "order": 15, "challenges": [ { "id": "aff0395860f5d3034dc0bfc9", "title": "Validate US Telephone Numbers", - "difficulty": "4.01", "description": [ "Return true if the passed string is a valid US phone number", "The user may fill out the form field any way they choose as long as it is a valid US number. The following are all valid formats for US numbers:", - "555-555-5555, (555)555-5555, (555) 555-5555, 555 555 5555, 5555555555, 1 555 555 5555", - "For this challenge you will be presented with a string such as \"800-692-7753\" or \"8oo-six427676;laskdjf\". Your job is to validate or reject the US phone number based on any combination of the formats provided above. The area code is required. If the country code is provided, you must confirm that the country code is \"1\". Return true if the string is a valid US phone number; otherwise false.", + "555-555-5555", + "(555)555-5555", + "(555) 555-5555", + "555 555 5555", + "5555555555", + "1 555 555 5555", + "For this challenge you will be presented with a string such as 800-692-7753 or 8oo-six427676;laskdjf. Your job is to validate or reject the US phone number based on any combination of the formats provided above. The area code is required. If the country code is provided, you must confirm that the country code is 1. Return true if the string is a valid US phone number; otherwise false.", "Remember to use Read-Search-Ask if you get stuck. Try to pair program. Write your own code." ], "tests": [ - "expect(telephoneCheck(\"555-555-5555\")).to.be.a(\"boolean\");", - "assert.deepEqual(telephoneCheck(\"1 555-555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"1 (555) 555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"5555555555\"), true);", - "assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"(555)555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"1(555)555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"1 555 555 5555\"), true);", - "assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);", - "assert.deepEqual(telephoneCheck(\"1 456 789 4444\"), true);", - "assert.deepEqual(telephoneCheck(\"123**&!!asdf#\"), false);", - "assert.deepEqual(telephoneCheck(\"55555555\"), false);", - "assert.deepEqual(telephoneCheck(\"(6505552368)\"), false);", - "assert.deepEqual(telephoneCheck(\"2 (757) 622-7382\"), false);", - "assert.deepEqual(telephoneCheck(\"0 (757) 622-7382\"), false);", - "assert.deepEqual(telephoneCheck(\"-1 (757) 622-7382\"), false);", - "assert.deepEqual(telephoneCheck(\"2 757 622-7382\"), false);", - "assert.deepEqual(telephoneCheck(\"10 (757) 622-7382\"), false);", - "assert.deepEqual(telephoneCheck(\"27576227382\"), false);", - "assert.deepEqual(telephoneCheck(\"(275)76227382\"), false);", - "assert.deepEqual(telephoneCheck(\"2(757)6227382\"), false);", - "assert.deepEqual(telephoneCheck(\"2(757)622-7382\"), false);" + "assert.isBoolean(telephoneCheck(\"555-555-5555\"), 'message: should return a boolean.');", + "assert(telephoneCheck(\"1 555-555-5555\") === true, 'message: telephoneCheck(\"1 555-555-5555\") should return true.');", + "assert(telephoneCheck(\"1 (555) 555-5555\") === true, 'message: telephoneCheck(\"1 (555) 555-5555\") should return true.');", + "assert(telephoneCheck(\"5555555555\") === true, 'message: telephoneCheck(\"5555555555\") should return true.');", + "assert(telephoneCheck(\"555-555-5555\") === true, 'message: telephoneCheck(\"555-555-5555\") should return true.');", + "assert(telephoneCheck(\"(555)555-5555\") === true, 'message: telephoneCheck(\"(555)555-5555\") should return true.');", + "assert(telephoneCheck(\"1(555)555-5555\") === true, 'message: telephoneCheck(\"1(555)555-5555\") should return true.');", + "assert(telephoneCheck(\"1 555 555 5555\") === true, 'message: telephoneCheck(\"1 555 555 5555\") should return true.');", + "assert(telephoneCheck(\"555-555-5555\") === true, 'message: telephoneCheck(\"555-555-5555\") should return true.');", + "assert(telephoneCheck(\"1 456 789 4444\") === true, 'message: telephoneCheck(\"1 456 789 4444\") should return true.');", + "assert(telephoneCheck(\"123**&!!asdf#\") === false, 'message: telephoneCheck(\"123**&!!asdf#\") should return false.');", + "assert(telephoneCheck(\"55555555\") === false, 'message: telephoneCheck(\"55555555\") should return false.');", + "assert(telephoneCheck(\"(6505552368)\") === false, 'message: telephoneCheck(\"(6505552368)\") should return false');", + "assert(telephoneCheck(\"2 (757) 622-7382\") === false, 'message: telephoneCheck(\"2 (757) 622-7382\") should return false.');", + "assert(telephoneCheck(\"0 (757) 622-7382\") === false, 'message: telephoneCheck(\"0 (757) 622-7382\") should return false.');", + "assert(telephoneCheck(\"-1 (757) 622-7382\") === false, 'message: telephoneCheck(\"-1 (757) 622-7382\") should return false');", + "assert(telephoneCheck(\"2 757 622-7382\") === false, 'message: telephoneCheck(\"2 757 622-7382\") should return false.');", + "assert(telephoneCheck(\"10 (757) 622-7382\") === false, 'message: telephoneCheck(\"10 (757) 622-7382\") should return false.');", + "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.');" ], "challengeSeed": [ "function telephoneCheck(str) {", @@ -66,7 +70,6 @@ { "id": "a3f503de51cf954ede28891d", "title": "Symmetric Difference", - "difficulty": "4.02", "description": [ "Create a function that takes two or more arrays and returns an array of the symmetric difference of the provided arrays.", "The mathematical term symmetric difference refers to the elements in two sets that are in either the first or second set, but not in both.", @@ -74,16 +77,16 @@ ], "challengeSeed": [ "function sym(args) {", - " return arguments;", + " return args;", "}", "", "sym([1, 2, 3], [5, 2, 1, 4]);" ], "tests": [ - "assert.deepEqual(sym([1, 2, 3], [5, 2, 1, 4]), [3, 5, 4], 'should return an array of unique values');", - "assert.deepEqual(sym([1, 2, 5], [2, 3, 5], [3, 4, 5]), [1, 4, 5], 'should return the symmetric difference of the given arrays');", - "assert.deepEqual(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]), [1, 4, 5], 'should return an array of unique values');", - "assert.deepEqual(sym([1, 1]), [1], 'should return an array of unique values');" + "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].');" ], "MDNlinks": [ "Array.reduce()", @@ -105,7 +108,6 @@ { "id": "aa2e6f85cab2ab736c9a9b24", "title": "Exact Change", - "difficulty": "4.03", "description": [ "Design a cash register drawer function that accepts purchase price as the first argument, payment as the second argument, and cash-in-drawer (cid) as the third argument.", "cid is a 2d array listing available currency.", @@ -121,26 +123,26 @@ "}", "", "// Example cash-in-drawer array:", - "// [['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]]", + "// [[\"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]]", "", - "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]]);" + "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]]);" ], "tests": [ - "expect(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]])).to.be.a('array');", - "expect(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]])).to.be.a('string');", - "expect(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]])).to.be.a('string');", - "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]], 'return correct change');", - "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] ], 'return correct change with multiple coins and bills');", - "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', '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\", 'cash-in-drawer equals change');" + "assert.isArray(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]]), '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 an array.');", + "assert.isString(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]]), '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 a string.');", + "assert.isString(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]]), '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 a string.');", + "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.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": [ "Global Object" @@ -161,7 +163,6 @@ { "id": "a56138aff60341a09ed6c480", "title": "Inventory Update", - "difficulty": "4.04", "description": [ "Compare and update inventory stored in a 2d array against a second 2d array of a fresh delivery. Update current inventory item quantity, and if an item cannot be found, add the new item and quantity into the inventory array in alphabetical order.", "Remember to use Read-Search-Ask if you get stuck. Try to pair program. Write your own code." @@ -174,28 +175,28 @@ "", "// Example inventory lists", "var curInv = [", - " [21, 'Bowling Ball'],", - " [2, 'Dirty Sock'],", - " [1, 'Hair Pin'],", - " [5, 'Microphone']", + " [21, \"Bowling Ball\"],", + " [2, \"Dirty Sock\"],", + " [1, \"Hair Pin\"],", + " [5, \"Microphone\"]", "];", "", "var newInv = [", - " [2, 'Hair Pin'],", - " [3, 'Half-Eaten Apple'],", - " [67, 'Bowling Ball'],", - " [7, 'Toothpaste']", + " [2, \"Hair Pin\"],", + " [3, \"Half-Eaten Apple\"],", + " [67, \"Bowling Ball\"],", + " [7, \"Toothpaste\"]", "];", "", "inventory(curInv, newInv);" ], "tests": [ - "expect(inventory([[21, 'Bowling Ball'], [2, 'Dirty Sock'], [1, 'Hair Pin'], [5, 'Microphone']], [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']])).to.be.a('array');", - "assert.equal(inventory([[21, 'Bowling Ball'], [2, 'Dirty Sock'], [1, 'Hair Pin'], [5, 'Microphone']], [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']]).length, 6);", - "assert.deepEqual(inventory([[21, 'Bowling Ball'], [2, 'Dirty Sock'], [1, 'Hair Pin'], [5, 'Microphone']], [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']]), [[88, 'Bowling Ball'], [2, 'Dirty Sock'], [3, 'Hair Pin'], [3, 'Half-Eaten Apple'], [5, 'Microphone'], [7, 'Toothpaste']]);", - "assert.deepEqual(inventory([[21, 'Bowling Ball'], [2, 'Dirty Sock'], [1, 'Hair Pin'], [5, 'Microphone']], []), [[21, 'Bowling Ball'], [2, 'Dirty Sock'], [1, 'Hair Pin'], [5, 'Microphone']]);", - "assert.deepEqual(inventory([], [[2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [67, 'Bowling Ball'], [7, 'Toothpaste']]), [[67, 'Bowling Ball'], [2, 'Hair Pin'], [3, 'Half-Eaten Apple'], [7, 'Toothpaste']]);", - "assert.deepEqual(inventory([[0, 'Bowling Ball'], [0, 'Dirty Sock'], [0, 'Hair Pin'], [0, 'Microphone']], [[1, 'Hair Pin'], [1, 'Half-Eaten Apple'], [1, 'Bowling Ball'], [1, 'Toothpaste']]), [[1, 'Bowling Ball'], [0, 'Dirty Sock'], [1, 'Hair Pin'], [1, 'Half-Eaten Apple'], [0, 'Microphone'], [1, 'Toothpaste']]);" + "assert.isArray(inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]), 'message: inventory() should return an array.');", + "assert.equal(inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]).length, 6, 'message: inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]).length should return an array with a length of 6.');", + "assert.deepEqual(inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]), [[88, \"Bowling Ball\"], [2, \"Dirty Sock\"], [3, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [5, \"Microphone\"], [7, \"Toothpaste\"]], 'message: inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]) should return [[88, \"Bowling Ball\"], [2, \"Dirty Sock\"], [3, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [5, \"Microphone\"], [7, \"Toothpaste\"]].');", + "assert.deepEqual(inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], []), [[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], 'message: inventory([[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]], []) should return [[21, \"Bowling Ball\"], [2, \"Dirty Sock\"], [1, \"Hair Pin\"], [5, \"Microphone\"]].');", + "assert.deepEqual(inventory([], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]), [[67, \"Bowling Ball\"], [2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [7, \"Toothpaste\"]], 'message: inventory([], [[2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [67, \"Bowling Ball\"], [7, \"Toothpaste\"]]) should return [[67, \"Bowling Ball\"], [2, \"Hair Pin\"], [3, \"Half-Eaten Apple\"], [7, \"Toothpaste\"]].');", + "assert.deepEqual(inventory([[0, \"Bowling Ball\"], [0, \"Dirty Sock\"], [0, \"Hair Pin\"], [0, \"Microphone\"]], [[1, \"Hair Pin\"], [1, \"Half-Eaten Apple\"], [1, \"Bowling Ball\"], [1, \"Toothpaste\"]]), [[1, \"Bowling Ball\"], [0, \"Dirty Sock\"], [1, \"Hair Pin\"], [1, \"Half-Eaten Apple\"], [0, \"Microphone\"], [1, \"Toothpaste\"]], 'message: inventory([[0, \"Bowling Ball\"], [0, \"Dirty Sock\"], [0, \"Hair Pin\"], [0, \"Microphone\"]], [[1, \"Hair Pin\"], [1, \"Half-Eaten Apple\"], [1, \"Bowling Ball\"], [1, \"Toothpaste\"]]) should return [[1, \"Bowling Ball\"], [0, \"Dirty Sock\"], [1, \"Hair Pin\"], [1, \"Half-Eaten Apple\"], [0, \"Microphone\"], [1, \"Toothpaste\"]].');" ], "MDNlinks": [ "Global Array Object" @@ -216,7 +217,6 @@ { "id": "a7bf700cd123b9a54eef01d5", "title": "No repeats please", - "difficulty": "4.05", "description": [ "Return the number of total permutations of the provided string that don't have repeated consecutive letters.", "For example, 'aab' should return 2 because it has 6 total permutations, but only 2 of them don't have the same letter (in this case 'a') repeating.", @@ -230,13 +230,13 @@ "permAlone('aab');" ], "tests": [ - "expect(permAlone('aab')).to.be.a('number');", - "expect(permAlone('aab')).to.equal(2);", - "expect(permAlone('aaa')).to.equal(0);", - "expect(permAlone('aabb')).to.equal(8);", - "expect(permAlone('abcdefa')).to.equal(3600);", - "expect(permAlone('abfdefa')).to.equal(2640);", - "expect(permAlone('zzzzzzzz')).to.equal(0);" + "assert.isNumber(permAlone('aab'), 'message: permAlone() should return a number.');", + "assert.strictEqual(permAlone('aab'), 2, 'message: permAlone(aab) should return 2.');", + "assert.strictEqual(permAlone('aaa'), 0, 'message: permAlone(aaa) should return 0.');", + "assert.strictEqual(permAlone('aabb'), 8, 'message: permAlone(aabb) should return 8.');", + "assert.strictEqual(permAlone('abcdefa'), 3600, 'message: permAlone(abcdefa) should return 3600.');", + "assert.strictEqual(permAlone('abfdefa'), 2640, 'message: permAlone(abfdefa) should return 2640.');", + "assert.strictEqual(permAlone('zzzzzzzz'), 0, 'message: permAlone(zzzzzzzz) should return 0.');" ], "MDNlinks": [ "Permutations", @@ -259,7 +259,6 @@ "id": "a19f0fbe1872186acd434d5a", "title": "Friendly Date Ranges", "dashedName": "bonfire-friendly-date-ranges", - "difficulty": "4.06", "description": [ "Implement a way of converting two dates into a more friendly date range that could be presented to a user.", "It must not show any redundant information in the date range.", @@ -276,12 +275,12 @@ "friendly(['2015-07-01', '2015-07-04']);" ], "tests": [ - "assert.deepEqual(friendly(['2015-07-01', '2015-07-04']), ['July 1st','4th'], 'ending month should be omitted since it is already mentioned');", - "assert.deepEqual(friendly(['2015-12-01', '2016-02-03']), ['December 1st','February 3rd'], 'two months apart can be inferred if it is the next year');", - "assert.deepEqual(friendly(['2015-12-01', '2017-02-03']), ['December 1st, 2015','February 3rd, 2017']);", - "assert.deepEqual(friendly(['2016-03-01', '2016-05-05']), ['March 1st','May 5th'], 'one month apart can be inferred it is the same year');", - "assert.deepEqual(friendly(['2017-01-01', '2017-01-01']), ['January 1st, 2017'], 'since we do not duplicate only return once');", - "assert.deepEqual(friendly(['2022-09-05', '2023-09-04']), ['September 5th, 2022','September 4th, 2023']);" + "assert.deepEqual(friendly(['2015-07-01', '2015-07-04']), ['July 1st','4th'], 'message: friendly([\"2015-07-01\", \"2015-07-04\"]) should return [\"July 1st\",\"4th\"].');", + "assert.deepEqual(friendly(['2015-12-01', '2016-02-03']), ['December 1st','February 3rd'], 'message: friendly([\"2015-12-01\", \"2016-02-03\"]) should return [\"December 1st\",\"February 3rd\"].');", + "assert.deepEqual(friendly(['2015-12-01', '2017-02-03']), ['December 1st, 2015','February 3rd, 2017'], 'message: friendly([\"2015-12-01\", \"2017-02-03\"]) should return [\"December 1st, 2015\",\"February 3rd, 2017\"].');", + "assert.deepEqual(friendly(['2016-03-01', '2016-05-05']), ['March 1st','May 5th'], 'message: friendly([\"2016-03-01\", \"2016-05-05\"]) should return [\"March 1st\",\"May 5th\"]');", + "assert.deepEqual(friendly(['2017-01-01', '2017-01-01']), ['January 1st, 2017'], 'message: friendly([\"2017-01-01\", \"2017-01-01\"]) should return [\"January 1st, 2017\"].');", + "assert.deepEqual(friendly(['2022-09-05', '2023-09-04']), ['September 5th, 2022','September 4th, 2023'], 'message: friendly([\"2022-09-05\", \"2023-09-04\"]) should return [\"September 5th, 2022\",\"September 4th, 2023\"].');" ], "MDNlinks": [ "String.split()", diff --git a/seed/challenges/angularjs.json b/seed/challenges/angularjs.json index 7a3155ed4f..92c85e751b 100644 --- a/seed/challenges/angularjs.json +++ b/seed/challenges/angularjs.json @@ -1,11 +1,10 @@ { "name": "AngularJS", - "order": 0.014, + "order": 16, "challenges": [ { "id": "bd7154d8c441eddfaeb5bdef", "title": "Get Started with Angular.js", - "difficulty": 0.34, "challengeSeed": ["114684726"], "description": [ "Code School has a short, free Angular.js course. This will give us a quick tour of Angular.js's mechanics and features.", @@ -29,7 +28,6 @@ { "id": "bd7155d8c441eddfaeb5bdef", "title": "Apply Angular.js Directives", - "difficulty": 0.35, "challengeSeed": ["114684727"], "description": [ "Directives serve as markers in your HTML. When Angular.js compiles your HTML, it will alter the behavior of DOM elements based on the directives you've used.", @@ -53,7 +51,6 @@ { "id": "bd7156d8c441eddfaeb5bdef", "title": "Power Forms with Angular.js", - "difficulty": 0.36, "challengeSeed": ["114684729"], "description": [ "One area where Angular.js really shines is its powerful web forms.", @@ -77,7 +74,6 @@ { "id": "bd7157d8c441eddfaeb5bdef", "title": "Customize Angular.js Directives", - "difficulty": 0.37, "challengeSeed": ["114685062"], "description": [ "Now we'll learn how to modify existing Angular.js directives, and even build directives of your own.", @@ -100,7 +96,6 @@ { "id": "bd7158d8c441eddfaeb5bdef", "title": "Create Angular.js Services", - "difficulty": 0.38, "challengeSeed": ["114685060"], "description": [ "Services are functions that you can use and reuse throughout your Angular.js app to get things done.", diff --git a/seed/challenges/automated-testing-and-debugging.json b/seed/challenges/automated-testing-and-debugging.json index 9cf677a024..b5ab25c2f9 100644 --- a/seed/challenges/automated-testing-and-debugging.json +++ b/seed/challenges/automated-testing-and-debugging.json @@ -1,6 +1,6 @@ { "name": "Automated Testing and Debugging", - "order": 0.012, + "order": 14, "challenges": [ { "id":"cf1111c1c16feddfaeb6bdef", @@ -13,7 +13,7 @@ "console.log('Hello world!')" ], "tests":[ - "assert(editor.getValue().match(/console\\.log\\(/gi), 'You should use the console.log method to log \"Hello world!\" to your JavaScript console.');" + "assert(editor.getValue().match(/console\\.log\\(/gi), 'message: You should use the console.log method to log \"Hello world!\" to your JavaScript console.');" ], "challengeSeed":[ "", @@ -28,19 +28,19 @@ "title":"Using typeof", "difficulty":0, "description":[ - "typeof is a useful method that we can use to check the type of a variable.", - "One thing to be careful of is that an array has the type objects.", - "Try using each of these to see the types they have.", + "You can use typeof to check the data structure, or type, of a variable.", + "Note that in JavaScript, arrays are technically a type of object.", + "Try using typeof on each of the following to see which types they have.", "console.log(typeof(\"\"));", "console.log(typeof(0));", "console.log(typeof([]));", "console.log(typeof({}));" ], "tests":[ - "assert(editor.getValue().match(/console\\.log\\(typeof\\(\"\"\\)\\);/gi), 'You should console.log the typeof a string.');", - "assert(editor.getValue().match(/console\\.log\\(typeof\\(0\\)\\);/gi), 'You should console.log the typeof a number.');", - "assert(editor.getValue().match(/console\\.log\\(typeof\\(\\[\\]\\)\\);/gi), 'You should console.log the typeof an array.');", - "assert(editor.getValue().match(/console\\.log\\(typeof\\(\\{\\}\\)\\);/gi), 'You should console.log the typeof a object.');" + "assert(editor.getValue().match(/console\\.log\\(typeof\\(\"\"\\)\\);/gi), 'message: You should console.log the typeof a string.');", + "assert(editor.getValue().match(/console\\.log\\(typeof\\(0\\)\\);/gi), 'message: You should console.log the typeof a number.');", + "assert(editor.getValue().match(/console\\.log\\(typeof\\(\\[\\]\\)\\);/gi), 'message: You should console.log the typeof an array.');", + "assert(editor.getValue().match(/console\\.log\\(typeof\\(\\{\\}\\)\\);/gi), 'message: You should console.log the typeof a object.');" ], "challengeSeed":[ "", diff --git a/seed/challenges/basejumps.json b/seed/challenges/basejumps.json index fcad55c0d9..c0dc0a0ba3 100644 --- a/seed/challenges/basejumps.json +++ b/seed/challenges/basejumps.json @@ -1,11 +1,10 @@ { "name": "Full Stack JavaScript Projects", - "order": 0.019, + "order": 20, "challenges": [ { "id": "bd7158d8c443eddfaeb5bcef", "title": "Get Set for Basejumps", - "difficulty": 2.00, "challengeSeed": ["128451852"], "description": [ "Objective: Get the MEAN stack running on Cloud 9, push your code to GitHub, and deploy it to Heroku.", @@ -13,11 +12,11 @@ "If you don't already have Cloud 9 account, create one now at http://c9.io.", "Now let's get your development environment ready for a new Angular-Fullstack application provided by Yeoman.", "Open up http://c9.io and sign in to your account.", - "Click on Create New Workspace at the top right of the c9.io page, then click on the \"Create a new workspace\" popup that appears below it the button after you click on it.", - "Give your workspace a name.", + "Click on the \"+\" icon at the top right of the c9.io page to create a new workspace.", + "Give your workspace a name and an optional description.", "Choose Node.js in the selection area below the name field.", - "Click the Create button. Then click into your new workspace.", - "In the lower right hand corner you should see a terminal window. In this window use the following commands. You don't need to know what these mean at this point.", + "Click the \"Create workspace\" button.", + "Once C9 builds and loads your workspace, you should see a terminal window in the lower right hand corner. In this window use the following commands. You don't need to know what these mean at this point.", "Never run this command on your local machine. But in your Cloud 9 terminal window, run: rm -rf * && echo \"export NODE_PATH=$NODE_PATH:/home/ubuntu/.nvm/v0.10.35/lib/node_modules\" >> ~/.bashrc && source ~/.bashrc && npm install -g yo grunt grunt-cli generator-angular-fullstack && yo angular-fullstack", "Yeoman will prompt you to answer some questions. Answer them like this:", "What would you like to write scripts with? JavaScript", @@ -54,7 +53,7 @@ "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.", - "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 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\".", + "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.", "Now you're ready to move on to your first Basejump. Click the \"I've completed this challenge\" and move on." @@ -66,7 +65,6 @@ { "id": "bd7158d8c443eddfaeb5bdef", "title": "Build a Voting App", - "difficulty": 2.01, "challengeSeed": ["133315786"], "description": [ "Objective: Build a full stack JavaScript app that successfully reverse-engineers this: http://votingapp.herokuapp.com/ and deploy it to Heroku.", @@ -79,14 +77,14 @@ "User Story: As an authenticated user, I can see the aggregate results of my polls.", "User Story: As an authenticated user, I can delete polls that I decide I don't want anymore.", "User Story: As an authenticated user, I can create a poll with any number of possible items.", - "Bonus User Story: As an unauthenticated user, I can see everyone's polls, but I can't vote on anything.", + "Bonus User Story: As an unauthenticated or authenticated user, I can see and vote on everyone's polls.", "Bonus User Story: As an unauthenticated or authenticated user, I can see the results of polls in chart form. (This could be implemented using Chart.js or Google Charts.)", "Bonus User Story: As an authenticated user, if I don't like the options on a poll, I can create a new option.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku. If you pair programmed with a friend, enter his or her Free Code Camp username as well so that you both get credit for completing it.", - "If you'd like immediate feedback on your project, click this button and paste in a link to your Heroku project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], - "type": "basejumps", + "type": "basejump", "challengeType": 4, "tests": [], "nameCn": "", @@ -103,7 +101,6 @@ { "id": "bd7158d8c443eddfaeb5bdff", "title": "Build a Nightlife Coordination App", - "difficulty": 2.02, "challengeSeed": ["133315781"], "description": [ "Objective: Build a full stack JavaScript app that successfully reverse-engineers this: http://whatsgoinontonight.herokuapp.com/ and deploy it to Heroku.", @@ -118,9 +115,9 @@ "Hint: Try using the Yelp API to find venues in the cities your users search for.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku. If you pair programmed with a friend, enter his or her Free Code Camp username as well so that you both get credit for completing it.", - "If you'd like immediate feedback on your project, click this button and paste in a link to your Heroku project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], - "type": "basejumps", + "type": "basejump", "challengeType": 4, "tests": [], "nameCn": "", @@ -137,7 +134,6 @@ { "id": "bd7158d8c443eddfaeb5bd0e", "title": "Chart the Stock Market", - "difficulty": 2.03, "challengeSeed": ["133315787"], "description": [ "Objective: Build a full stack JavaScript app that successfully reverse-engineers this: http://stockstream.herokuapp.com/ and deploy it to Heroku.", @@ -151,9 +147,9 @@ "Bonus User Story: As a user, I can see changes in real-time when any other user adds or removes a stock.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku. If you pair programmed with a friend, enter his or her Free Code Camp username as well so that you both get credit for completing it.", - "If you'd like immediate feedback on your project, click this button and paste in a link to your Heroku project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], - "type": "basejumps", + "type": "basejump", "challengeType": 4, "tests": [], "nameCn": "", @@ -170,7 +166,6 @@ { "id": "bd7158d8c443eddfaeb5bd0f", "title": "Manage a Book Trading Club", - "difficulty": 2.04, "challengeSeed": ["133316032"], "description": [ "Objective: Build a full stack JavaScript app that successfully reverse-engineers this: http://bookjump.herokuapp.com/ and deploy it to Heroku.", @@ -184,9 +179,9 @@ "Bonus User Story: As an authenticated user, I can propose a trade and wait for the other user to accept the trade.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku. If you pair programmed with a friend, enter his or her Free Code Camp username as well so that you both get credit for completing it.", - "If you'd like immediate feedback on your project, click this button and paste in a link to your Heroku project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], - "type": "basejumps", + "type": "basejump", "challengeType": 4, "tests": [], "nameCn": "", @@ -203,7 +198,6 @@ { "id": "bd7158d8c443eddfaeb5bdee", "title": "Build a Pinterest Clone", - "difficulty": 2.05, "challengeSeed": ["133315784"], "description": [ "Objective: Build a full stack JavaScript app that successfully reverse-engineers this: http://stark-lowlands-3680.herokuapp.com/ and deploy it to Heroku.", @@ -220,9 +214,9 @@ "Hint: Masonry.js is a library that allows for Pinterest-style image grids.", "If you need further guidance on using Yeoman Angular-Fullstack Generator, check out: https://github.com/clnhll/guidetobasejumps.", "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku. If you pair programmed with a friend, enter his or her Free Code Camp username as well so that you both get credit for completing it.", - "If you'd like immediate feedback on your project, click this button and paste in a link to your Heroku project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], - "type": "basejumps", + "type": "basejump", "challengeType": 4, "tests": [], "nameCn": "", diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 32964b0378..931c157ce1 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -1,20 +1,19 @@ { "name": "Basic Algorithm Scripting", - "order": 0.006, + "order": 7, "challenges": [ { "id": "ad7123c8c441eddfaeb5bdef", "title": "Meet Bonfire", - "difficulty": "0", "description": [ "Your goal is to fix the failing test.", - "First, run all the tests by clicking \"Run code\" or by pressing Control + Enter.", + "First, run all the tests by clicking \"Run tests\" or by pressing Control + Enter.", "The failing test is in red. Fix the code so that all tests pass. Then you can move on to the next Bonfire.", "Make this function return true no matter what." ], "tests": [ - "expect(meetBonfire()).to.be.a(\"boolean\");", - "expect(meetBonfire()).to.be.true;" + "assert(typeof(meetBonfire()) === \"boolean\", 'message: meetBonfire() should return a boolean value.');", + "assert(meetBonfire() === true, 'message: meetBonfire() should return true.');" ], "challengeSeed": [ "function meetBonfire(argument) {", @@ -44,25 +43,24 @@ { "id": "a202eed8fc186c8434cb6d61", "title": "Reverse a String", - "difficulty": "1.01", "tests": [ - "expect(reverseString('hello')).to.be.a('String');", - "expect(reverseString('hello')).to.equal('olleh');", - "expect(reverseString('Howdy')).to.equal('ydwoH');", - "expect(reverseString('Greetings from Earth')).to.equal('htraE morf sgniteerG');" + "assert(typeof(reverseString(\"hello\")) === \"string\", 'message: reverseString() should return a string.');", + "assert(reverseString(\"hello\") === \"olleh\", 'message: reverseString(\"hello\") should become \"olleh\".');", + "assert(reverseString(\"Howdy\") === \"ydwoH\", 'message: reverseString(\"Howdy\") should become \"ydwoH\".');", + "assert(reverseString(\"Greetings from Earth\") === \"htraE morf sgniteerG\", 'message: reverseString(\"Greetings from Earth\") should return \"htraE morf sgniteerG\".');" ], "description": [ "Reverse the provided string.", "You may need to turn the string into an array before you can reverse it.", "Your result must be a string.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function reverseString(str) {", " return str;", "}", "", - "reverseString('hello');" + "reverseString(\"hello\", \"\");" ], "MDNlinks": [ "Global String Object", @@ -87,25 +85,25 @@ "id": "a302f7aae1aa3152a5b413bc", "title": "Factorialize a Number", "tests": [ - "expect(factorialize(5)).to.be.a(\"Number\");", - "expect(factorialize(5)).to.equal(120);", - "expect(factorialize(10)).to.equal(3628800);", - "expect(factorialize(20)).to.equal(2432902008176640000);" + "assert(typeof(factorialize(5)) === \"number\", 'message: factorialize() should return a number.');", + "assert(factorialize(5) === 120, 'message: factorialize(5) should return 120.');", + "assert(factorialize(10) === 3628800, 'message: factorialize(10) should return 3628800.');", + "assert(factorialize(20) === 2432902008176640000, 'message: factorialize(20) should return 2432902008176640000.');", + "assert(factorialize(0) === 1, 'message: factorialize(0) should return 1.');" ], - "difficulty": "1.02", "description": [ "Return the factorial of the provided integer.", "If the integer is represented with the letter n, a factorial is the product of all positive integers less than or equal to n.", - "Factorials are often represented with the shorthand notation n!", - "For example: 5! = 1 * 2 * 3 * 4 * 5 = 120f", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Factorials are often represented with the shorthand notation n!", + "For example: 5! = 1 * 2 * 3 * 4 * 5 = 120", + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function factorialize(num) {", " return num;", "}", "", - "factorialize(5);" + "factorialize(5, '');" ], "MDNlinks": [ "Arithmetic Operators" @@ -126,23 +124,25 @@ { "id": "aaa48de84e1ecc7c742e1124", "title": "Check for Palindromes", - "difficulty": "1.03", "description": [ "Return true if the given string is a palindrome. Otherwise, return false.", "A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation, case, and spacing.", "You'll need to remove punctuation and turn everything lower case in order to check for palindromes.", "We'll pass strings with varying formats, such as \"racecar\", \"RaceCar\", and \"race CAR\" among others.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "tests": [ - "expect(palindrome(\"eye\")).to.be.a(\"boolean\");", - "assert.deepEqual(palindrome(\"eye\"), true);", - "assert.deepEqual(palindrome(\"race car\"), true);", - "assert.deepEqual(palindrome(\"not a palindrome\"), false);", - "assert.deepEqual(palindrome(\"A man, a plan, a canal. Panama\"), true);", - "assert.deepEqual(palindrome(\"never odd or even\"), true);", - "assert.deepEqual(palindrome(\"nope\"), false);", - "assert.deepEqual(palindrome(\"almostomla\"), false);" + "assert(typeof(palindrome(\"eye\")) === \"boolean\", 'message: palindrome() should return a boolean.');", + "assert(palindrome(\"eye\") === true, 'message: palindrome(\"eye\") should return true.');", + "assert(palindrome(\"race car\") === true, 'message: palindrome(\"race car\") should return true.');", + "assert(palindrome(\"not a palindrome\") === false, 'message: palindrome(\"not a palindrome\") should return false.');", + "assert(palindrome(\"A man, a plan, a canal. Panama\") === true, 'message: palindrome(\"A man, a plan, a canal. Panama\") should return true.');", + "assert(palindrome(\"never odd or even\") === true, 'message: palindrome(\"never odd or even\") should return true.');", + "assert(palindrome(\"nope\") === false, 'message: palindrome(\"nope\") should return false.');", + "assert(palindrome(\"almostomla\") === false, 'message: palindrome(\"almostomla\") should return false.');", + "assert(palindrome(\"My age is 0, 0 si ega ym.\") === true, 'message: palindrome(\"My age is 0, 0 si ega ym.\") should return true.');", + "assert(palindrome(\"1 eye for of 1 eye.\") === false, 'message: palindrome(\"1 eye for of 1 eye.\") should return false.');", + "assert(palindrome(\"0_0 (: /-\\ :) 0-0\") === true, 'message: palindrome(\"0_0 (: /-\\\\ :) 0-0\") should return true.');" ], "challengeSeed": [ "function palindrome(str) {", @@ -174,26 +174,25 @@ { "id": "a26cbbe9ad8655a977e1ceb5", "title": "Find the Longest Word in a String", - "difficulty": "1.04", "description": [ "Return the length of the longest word in the provided sentence.", "Your response should be a number.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function findLongestWord(str) {", " return str.length;", "}", "", - "findLongestWord('The quick brown fox jumped over the lazy dog');" + "findLongestWord(\"The quick brown fox jumped over the lazy dog\");" ], "tests": [ - "expect(findLongestWord('The quick brown fox jumped over the lazy dog')).to.be.a('Number');", - "expect(findLongestWord('The quick brown fox jumped over the lazy dog')).to.equal(6);", - "expect(findLongestWord('May the force be with you')).to.equal(5);", - "expect(findLongestWord('Google do a barrel roll')).to.equal(6);", - "expect(findLongestWord('What is the average airspeed velocity of an unladen swallow')).to.equal(8);", - "expect(findLongestWord('What if we try a super-long word such as otorhinolaryngology')).to.equal(19);" + "assert(typeof(findLongestWord(\"The quick brown fox jumped over the lazy dog\")) === \"number\", 'message: findLongestWord() should return a number.');", + "assert(findLongestWord(\"The quick brown fox jumped over the lazy dog\") === 6, 'message: findLongestWord(\"The quick brown fox jumped over the lazy dog\") should return 6.');", + "assert(findLongestWord(\"May the force be with you\") === 5, 'message: findLongestWord(\"May the force be with you\") should return 5.');", + "assert(findLongestWord(\"Google do a barrel roll\") === 6, 'message: findLongestWord(\"Google do a barrel roll\") should return 6.');", + "assert(findLongestWord(\"What is the average airspeed velocity of an unladen swallow\") === 8, 'message: findLongestWord(\"What is the average airspeed velocity of an unladen swallow\") should return 8.');", + "assert(findLongestWord(\"What if we try a super-long word such as otorhinolaryngology\") === 19, 'message: findLongestWord(\"What if we try a super-long word such as otorhinolaryngology\") should return 19.');" ], "MDNlinks": [ "String.split()", @@ -215,24 +214,23 @@ { "id": "ab6137d4e35944e21037b769", "title": "Title Case a Sentence", - "difficulty": "1.05", "description": [ - "Return the provided string with the first letter of each word capitalized.", - "For the purpose of this exercise, you should also capitalize connecting words like 'the' and 'of'.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Return the provided string with the first letter of each word capitalized. Make sure the rest of the word is in lower case.", + "For the purpose of this exercise, you should also capitalize connecting words like \"the\" and \"of\".", + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function titleCase(str) {", " return str;", "}", "", - "titleCase(\"I'm a little tea pot\");" + "titleCase(\"I'm a little tea pot\", \"\");" ], "tests": [ - "expect(titleCase(\"I'm a little tea pot\")).to.be.a('String');", - "expect(titleCase(\"I'm a little tea pot\")).to.equal(\"I'm A Little Tea Pot\");", - "expect(titleCase(\"sHoRt AnD sToUt\")).to.equal(\"Short And Stout\");", - "expect(titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\")).to.equal(\"Here Is My Handle Here Is My Spout\");" + "assert(typeof(titleCase(\"I'm a little tea pot\")) === \"string\", 'message: titleCase() should return a string.');", + "assert(titleCase(\"I'm a little tea pot\") === \"I'm A Little Tea Pot\", 'message: titleCase(\"I'm a little tea pot\") should return \"I'm A Little Tea Pot\".');", + "assert(titleCase(\"sHoRt AnD sToUt\") === \"Short And Stout\", 'message: titleCase(\"sHoRt AnD sToUt\") should return \"Short And Stout\".');", + "assert(titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\") === \"Here Is My Handle Here Is My Spout\", 'message: titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\") should return \"Here Is My Handle Here Is My Spout\".');" ], "MDNlinks": [ "String.charAt()" @@ -253,12 +251,11 @@ { "id": "a789b3483989747d63b0e427", "title": "Return Largest Numbers in Arrays", - "difficulty": "1.06", "description": [ "Return an array consisting of the largest number from each provided sub-array. For simplicity, the provided array will contain exactly 4 sub-arrays.", "Remember, you can iterate through an array with a simple for loop, and access each member with array syntax arr[i] .", "If you are writing your own Chai.js tests, be sure to use a deep equal statement instead of an equal statement when comparing arrays.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function largestOfFour(arr) {", @@ -269,9 +266,9 @@ "largestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]);" ], "tests": [ - "expect( largestOfFour( [[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]) ).to.be.a('array');", - "assert.deepEqual( largestOfFour( [[13, 27, 18, 26], [4, 5, 1, 3], [32, 35, 37, 39], [1000, 1001, 857, 1]]), [27,5,39,1001], 'arrays should match.');", - "assert.deepEqual( largestOfFour( [[4, 9, 1, 3], [13, 35, 18, 26], [32, 35, 97, 39], [1000000, 1001, 857, 1]]), [9,35,97,1000000], 'arrays should match.');" + "assert(largestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]).constructor === Array, 'message: largestOfFour() should return an array.');", + "assert.deepEqual(largestOfFour([[13, 27, 18, 26], [4, 5, 1, 3], [32, 35, 37, 39], [1000, 1001, 857, 1]]), [27,5,39,1001], 'message: largestOfFour([[13, 27, 18, 26], [4, 5, 1, 3], [32, 35, 37, 39], [1000, 1001, 857, 1]]) should return [27,5,39,1001].');", + "assert.deepEqual(largestOfFour([[4, 9, 1, 3], [13, 35, 18, 26], [32, 35, 97, 39], [1000000, 1001, 857, 1]]), [9,35,97,1000000], 'message: largestOfFour([[4, 9, 1, 3], [13, 35, 18, 26], [32, 35, 97, 39], [1000000, 1001, 857, 1]]) should return [9, 35, 97, 1000000].');" ], "MDNlinks": [ "Comparison Operators" @@ -292,10 +289,9 @@ { "id": "acda2fb1324d9b0fa741e6b5", "title": "Confirm the Ending", - "difficulty": "1.07", "description": [ "Check if a string (first argument) ends with the given target string (second argument).", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function end(str, target) {", @@ -304,14 +300,14 @@ " return str;", "}", "", - "end('Bastian', 'n');" + "end(\"Bastian\", \"n\", \"\");" ], "tests": [ - "assert.strictEqual(end('Bastian', 'n'), true, 'should equal true if target equals end of string');", - "assert.strictEqual(end('Connor', 'n'), false, 'should equal false if target does not equal end of string');", - "assert.strictEqual(end('Walking on water and developing software from a specification are easy if both are frozen.', 'specification'), false, 'should equal false if target does not equal end of string');", - "assert.strictEqual(end('He has to give me a new name', 'name'), true, 'should equal true if target equals end of string');", - "assert.strictEqual(end('If you want to save our world, you must hurry. We dont know how much longer we can withstand the nothing', 'mountain'), false, 'should equal false if target does not equal end of string');" + "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(\"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": [ "String.substr()" @@ -332,10 +328,9 @@ { "id": "afcc8d540bea9ea2669306b6", "title": "Repeat a string repeat a string", - "difficulty": "1.08", "description": [ "Repeat a given string (first argument) n times (second argument). Return an empty string if n is a negative number.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function repeat(str, num) {", @@ -343,12 +338,12 @@ " return str;", "}", "", - "repeat('abc', 3);" + "repeat(\"abc\", 3, \"\");" ], "tests": [ - "assert.strictEqual(repeat('*', 3), '***', 'should repeat a string n times');", - "assert.strictEqual(repeat('abc', 3), 'abcabcabc', 'should repeat a string n times');", - "assert.strictEqual(repeat('abc', -2), '', 'should return an empty string for negative numbers');" + "assert(repeat(\"*\", 3) === \"***\", 'message: repeat(\"*\", 3) should return \"***\".');", + "assert(repeat(\"abc\", 3) === \"abcabcabc\", 'message: repeat(\"abc\", 3) should return \"abcabcabc\".');", + "assert(repeat(\"abc\", -2) === \"\", 'message: repeat(\"abc\", -2) should return \"\".');" ], "MDNlinks": [ "Global String Object" @@ -369,11 +364,10 @@ { "id": "ac6993d51946422351508a41", "title": "Truncate a string", - "difficulty": "1.09", "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.", + "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.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function truncate(str, num) {", @@ -381,13 +375,13 @@ " 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": [ - "expect(truncate('A-tisket a-tasket A green and yellow basket', 11)).to.eqls('A-tisket...');", - "expect(truncate('Peter Piper picked a peck of pickled peppers', 14)).to.eqls('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', 'should not truncate if string is = length');", - "assert.strictEqual(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', 'should not truncate if string is < length');" + "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(\"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\".');" ], "MDNlinks": [ "String.slice()" @@ -408,10 +402,9 @@ { "id": "a9bd25c716030ec90084d8a1", "title": "Chunky Monkey", - "difficulty": "1.10", "description": [ "Write a function that splits an array (first argument) into groups the length of size (second argument) and returns them as a multidimensional array.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function chunk(arr, size) {", @@ -419,13 +412,13 @@ " 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']], 'should return chunked arrays');", - "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]], 'should return chunked arrays');", - "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 2), [[0, 1], [2, 3], [4, 5]], 'should return chunked arrays');", - "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 4), [[0, 1, 2, 3], [4, 5]], 'should return the last chunk as remaining elements');" + "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\"]].');", + "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]], 'message: chunk([0, 1, 2, 3, 4, 5], 3) should return [[0, 1, 2], [3, 4, 5]].');", + "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 2), [[0, 1], [2, 3], [4, 5]], 'message: chunk([0, 1, 2, 3, 4, 5], 2) should return [[0, 1], [2, 3], [4, 5]].');", + "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 4), [[0, 1, 2, 3], [4, 5]], 'message: chunk([0, 1, 2, 3, 4, 5], 4) should return [[0, 1, 2, 3], [4, 5]].');" ], "MDNlinks": [ "Array.push()" @@ -446,10 +439,10 @@ { "id": "ab31c21b530c0dafa9e241ee", "title": "Slasher Flick", - "difficulty": "1.11", "description": [ "Return the remaining elements of an array after chopping off n elements from the head.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "The head meaning the beginning of the array, or the zeroth index", + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function slasher(arr, howMany) {", @@ -457,12 +450,12 @@ " return arr;", "}", "", - "slasher([1, 2, 3], 2);" + "slasher([1, 2, 3], 2, \"\");" ], "tests": [ - "assert.deepEqual(slasher([1, 2, 3], 2), [3], 'should drop the first two elements');", - "assert.deepEqual(slasher([1, 2, 3], 0), [1, 2, 3], 'should return all elements');", - "assert.deepEqual(slasher([1, 2, 3], 9), [], 'should return an empty array');" + "assert.deepEqual(slasher([1, 2, 3], 2), [3], 'message: slasher([1, 2, 3], 2, [3]) 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 [].');" ], "MDNlinks": [ "Array.slice()", @@ -484,30 +477,29 @@ { "id": "af2170cad53daa0770fabdea", "title": "Mutations", - "difficulty": "1.12", "description": [ "Return true if the string in the first element of the array contains all of the letters of the string in the second element of the array.", - "For example, ['hello', 'Hello'], should return true because all of the letters in the second string are present in the first, ignoring case.", - "The arguments ['hello', 'hey'] should return false because the string 'hello' does not contain a 'y'.", - "Lastly, ['Alien', 'line'], should return true because all of the letters in 'line' are present in 'Alien'.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "For example, [\"hello\", \"Hello\"], should return true because all of the letters in the second string are present in the first, ignoring case.", + "The arguments [\"hello\", \"hey\"] should return false because the string \"hello\" does not contain a \"y\".", + "Lastly, [\"Alien\", \"line\"], should return true because all of the letters in \"line\" are present in \"Alien\".", + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function mutation(arr) {", " return arr;", "}", "", - "mutation(['hello', 'hey']);" + "mutation([\"hello\", \"hey\"], \"\");" ], "tests": [ - "expect(mutation(['hello', 'hey'])).to.be.false;", - "expect(mutation(['hello', 'Hello'])).to.be.true;", - "expect(mutation(['zyxwvutsrqponmlkjihgfedcba', 'qrstu'])).to.be.true;", - "expect(mutation(['Mary', 'Army'])).to.be.true;", - "expect(mutation(['Mary', 'Aarmy'])).to.be.true;", - "expect(mutation(['Alien', 'line'])).to.be.true;", - "expect(mutation(['floor', 'for'])).to.be.true;", - "expect(mutation(['hello', 'neo'])).to.be.false;" + "assert(mutation([\"hello\", \"hey\"]) === false, 'message: mutation([\"hello\", \"hey\"]) should return false.');", + "assert(mutation([\"hello\", \"Hello\"]) === true, 'message: mutation([\"hello\", \"Hello\"]) should return true.');", + "assert(mutation([\"zyxwvutsrqponmlkjihgfedcba\", \"qrstu\"]) === true, 'message: mutation([\"zyxwvutsrqponmlkjihgfedcba\", \"qrstu\"]) should return true.');", + "assert(mutation([\"Mary\", \"Army\"]) === true, 'message: mutation([\"Mary\", \"Army\"]) should return true.');", + "assert(mutation([\"Mary\", \"Aarmy\"]) === true, 'message: mutation([\"Mary\", \"Aarmy\"]) should return true.');", + "assert(mutation([\"Alien\", \"line\"]) === true, 'message: mutation([\"Alien\", \"line\"]) should return true.');", + "assert(mutation([\"floor\", \"for\"]) === true, 'message: mutation([\"floor\", \"for\"]) should return true.');", + "assert(mutation([\"hello\", \"neo\"]) === false, 'message: mutation([\"hello\", \"neo\"]) should return false.');" ], "MDNlinks": [ "Array.indexOf()" @@ -527,12 +519,11 @@ }, { "id": "adf08ec01beb4f99fc7a68f2", - "title": "Falsey Bouncer", - "difficulty": "1.50", + "title": "Falsy Bouncer", "description": [ - "Remove all falsey values from an array.", - "Falsey values in javascript are false, null, 0, \"\", undefined, and NaN.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remove all falsy values from an array.", + "Falsy values in javascript are false, null, 0, \"\", undefined, and NaN.", + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function bouncer(arr) {", @@ -540,12 +531,12 @@ " return arr;", "}", "", - "bouncer([7, 'ate', '', false, 9]);" + "bouncer([7, \"ate\", \"\", false, 9], \"\");" ], "tests": [ - "assert.deepEqual(bouncer([7, 'ate', '', false, 9]), [7, 'ate', 9], 'should remove falsey values');", - "assert.deepEqual(bouncer(['a', 'b', 'c']), ['a', 'b', 'c'], 'should return full array if no falsey elements');", - "assert.deepEqual(bouncer([false, null, 0]), [], 'should return empty array if all elements are falsey');" + "assert.deepEqual(bouncer([7, \"ate\", \"\", false, 9]), [7, \"ate\", 9], 'message: bouncer([7, \"ate\", \"\", false, 9]) should return [7, \"ate\", 9].');", + "assert.deepEqual(bouncer([\"a\", \"b\", \"c\"]), [\"a\", \"b\", \"c\"], 'message: bouncer([\"a\", \"b\", \"c\"]) should return [\"a\", \"b\", \"c\"].');", + "assert.deepEqual(bouncer([false, null, 0, NaN, undefined, \"\"]), [], 'message: bouncer([false, null, 0, NaN, undefined, \"\"]) should return [].');" ], "MDNlinks": [ "Boolean Objects", @@ -564,55 +555,12 @@ "namePt": "", "descriptionPt": [] }, - { - "id": "a8e512fbe388ac2f9198f0fa", - "title": "Where art thou", - "difficulty": "1.55", - "description": [ - "Make a function that looks through an array (first argument) and returns an array of all objects that have equivalent property and value pair (second argument).", - "For example, if the first argument is [{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], and the second argument is { last: 'Capulet' }, then you must return the the third object from the array (the first argument), because it contains the property and it's value, that was passed on as the second argument.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." - ], - "challengeSeed": [ - "function where(collection, source) {", - " var arr = [];", - " // What's in a name?", - " return arr;", - "}", - "", - "where([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' });" - ], - "tests": [ - "assert.deepEqual(where([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' }), [{ first: 'Tybalt', last: 'Capulet' }], 'should return an array of objects');", - "assert.deepEqual(where([{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }], { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }], 'should return with multiples');", - "assert.deepEqual(where([{ 'a': 1, 'b': 2 }, { 'a': 1 }, { 'a': 1, 'b': 2, 'c': 2 }], { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 1, 'b': 2, 'c': 2 }], 'should return two objects in array');", - "assert.deepEqual(where([{ 'a': 5 }, { 'a': 5 }, { 'a': 5, 'b': 10 }], { 'a': 5, 'b': 10 }), [{ 'a': 5, 'b': 10 }], 'should return a single object in array');" - ], - "MDNlinks": [ - "Global Object", - "Object.hasOwnProperty()", - "Object.keys()" - ], - "type": "bonfire", - "challengeType": 5, - "nameCn": "", - "descriptionCn": [], - "nameFr": "", - "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], - "nameEs": "", - "descriptionEs": [], - "namePt": "", - "descriptionPt": [] - }, { "id": "a39963a4c10bc8b4d4f06d7e", "title": "Seek and Destroy", - "difficulty": "1.60", "description": [ "You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function destroyer(arr) {", @@ -620,14 +568,14 @@ " 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], 'should remove correct values from an array');", - "assert.deepEqual(destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3), [1, 5, 1], 'should remove correct values from an array');", - "assert.deepEqual(destroyer([3, 5, 1, 2, 2], 2, 3, 5), [1], 'should accept more than two additional arguments');", - "assert.deepEqual(destroyer([2, 3, 2, 3], 2, 3), [], 'should remove correct values from an array');", - "assert.deepEqual(destroyer(['tree', 'hamburger', 53], 'tree', 53), ['hamburger'], 'should handle NaN-elements');" + "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].');", + "assert.deepEqual(destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3), [1, 5, 1], 'message: destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3) should return [1, 5, 1].');", + "assert.deepEqual(destroyer([3, 5, 1, 2, 2], 2, 3, 5), [1], 'message: destroyer([3, 5, 1, 2, 2], 2, 3, 5) should return [1].');", + "assert.deepEqual(destroyer([2, 3, 2, 3], 2, 3), [], 'message: destroyer([2, 3, 2, 3], 2, 3) should return [].');", + "assert.deepEqual(destroyer([\"tree\", \"hamburger\", 53], \"tree\", 53), [\"hamburger\"], 'message: destroyer([\"tree\", \"hamburger\", 53], \"tree\", 53) should return [\"hamburger\"].');" ], "MDNlinks": [ "Arguments object", @@ -649,11 +597,10 @@ { "id": "a24c1a4622e3c05097f71d67", "title": "Where do I belong", - "difficulty": "1.61", "description": [ "Return the lowest index at which a value (second argument) should be inserted into a sorted array (first argument).", "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).", - "Remember to use Read-Search-Ask if you get stuck. Write your own code." + "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ "function where(arr, num) {", @@ -661,18 +608,18 @@ " return num;", "}", "", - "where([40, 60], 50);" + "where([40, 60], 50, \"\");" ], "MDNlinks": [ "Array.sort()" ], "tests": [ - "assert.strictEqual(where([10, 20, 30, 40, 50], 35), 3, '35 should be placed at index 3');", - "assert.strictEqual(where([10, 20, 30, 40, 50], 30), 2, '30 should be placed at index 2');", - "assert.strictEqual(where([40, 60], 50), 1, '50 should be placed at index 1');", - "assert.strictEqual(where([5, 3, 20, 3], 3), 0, '3 should be placed at index 0');", - "assert.strictEqual(where([2, 20, 10], 1), 0, '1 should be placed at index 0');", - "assert.strictEqual(where([2, 5, 10], 15), 3, '15 should be placed at index 3');" + "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([2, 5, 10], 15) === 3, 'message: where([2, 5, 10], 15) should return 3.');" ], "type": "bonfire", "challengeType": 5, diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index a4c3cabdb9..c10af0def1 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -1,6 +1,6 @@ { "name": "Basic JavaScript", - "order": 0.005, + "order": 5, "challenges": [ { "id":"bd7123c9c441eddfaeb4bdef", @@ -17,9 +17,9 @@ "And one more thing you need to notice. Starting at this waypoint in JavaScript related challenges (except AngularJS, all Ziplines, Git, Node.js and Express.js, MongoDB and Full Stack JavaScript Projects) you can see contents of assert() functions (in some challenges except(), assert.equal() and so on) which are used to test your code. It's part of these challenges that you are able to see the tests that are running against your code." ], "tests":[ - "assert(editor.getValue().match(/(\\/\\/)...../g), 'Create a // style comment that contains at least five letters');", - "assert(editor.getValue().match(/(\\/\\*)...../g), 'Create a /* */ style comment that contains at least five letters.');", - "assert(editor.getValue().match(/(\\*\\/)/g), 'Make sure that you close the comment with a */');" + "assert(editor.getValue().match(/(\\/\\/)...../g), 'message: Create a // style comment that contains at least five letters');", + "assert(editor.getValue().match(/(\\/\\*)[\\w\\W]{5,}(?=\\*\\/)/gm), 'message: Create a /* */ style comment that contains at least five letters.');", + "assert(editor.getValue().match(/(\\*\\/)/g), 'message: Make sure that you close the comment with a */');" ], "challengeSeed":[ ], @@ -29,20 +29,19 @@ { "id": "bd7123c9c441eddfaeb5bdef", "title": "Understand Boolean Values", - "difficulty": "9.98001", "description": [ "In computer science, data structures are things that hold data. JavaScript has seven of these. For example, the Number data structure holds numbers.", "Let's learn about the most basic data structure of all: the Boolean. Booleans can only hold the value of either true or false. They are basically little on-off switches.", "Let's modify our welcomeToBooleansfunction so that it will return trueinstead of falsewhen the run button is clicked." ], "tests": [ - "assert(typeof(welcomeToBooleans()) === 'boolean', 'The welcomeToBooleans() function should return a boolean (true/false) value.');", - "assert(welcomeToBooleans() === true, 'welcomeToBooleans() should return true.');" + "assert(typeof(welcomeToBooleans()) === 'boolean', 'message: The welcomeToBooleans() function should return a boolean (true/false) value.');", + "assert(welcomeToBooleans() === true, 'message: welcomeToBooleans() should return true.');" ], "challengeSeed": [ "function welcomeToBooleans() {", "", - "// don't change code above here", + "// Only change code below this line.", "", " return false;", "", @@ -57,7 +56,6 @@ { "id": "bd7123c9c443eddfaeb5bdef", "title": "Declare JavaScript Variables", - "difficulty": "9.9801", "description": [ "When we store data in a data structure, we call it a variable. These variables are no different from the x and y variables you use in math.", "Let's create our first variable and call it \"myName\".", @@ -66,7 +64,7 @@ "Look at the ourName example if you get stuck." ], "tests": [ - "assert((function(){/**/if(typeof(myName) !== \"undefined\" && typeof(myName) === \"string\" && myName.length > 0){return true;}else{return false;}/**/})(), 'myName should be a string that contains at least one character in it');" + "assert((function(){if(typeof(myName) !== \"undefined\" && typeof(myName) === \"string\" && myName.length > 0){return true;}else{return false;}})(), 'message: myName should be a string that contains at least one character in it.');" ], "challengeSeed": [ "// var ourName = \"Free Code Camp\";", @@ -84,17 +82,16 @@ { "id": "bd7123c9c444eddfaeb5bdef", "title": "Declare String Variables", - "difficulty": "9.9802", "description": [ "In the previous challenge, we used the code var myName = \"your name\". This is what we call a String variable. It is nothing more than a \"string\" of characters. JavaScript strings are always wrapped in quotes.", "Now let's create two new string variables: myFirstNameand myLastName and assign them the values of your first and last name, respectively." ], "tests": [ - "assert((function(){if(typeof(myFirstName) !== \"undefined\" && typeof(myFirstName) === \"string\" && myFirstName.length > 0){return true;}else{return false;}})(), 'myFirstName should be a string with a least one character in it');", - "assert((function(){if(typeof(myLastName) !== \"undefined\" && typeof(myLastName) === \"string\" && myLastName.length > 0){return true;}else{return false;}})(), 'myLastName should be a string with a least one character in it');" + "assert((function(){if(typeof(myFirstName) !== \"undefined\" && typeof(myFirstName) === \"string\" && myFirstName.length > 0){return true;}else{return false;}})(), 'message: myFirstName should be a string with at least one character in it.');", + "assert((function(){if(typeof(myLastName) !== \"undefined\" && typeof(myLastName) === \"string\" && myLastName.length > 0){return true;}else{return false;}})(), 'message: myLastName should be a string with at least one character in it.');" ], "challengeSeed": [ - "// name = \"Alan Turing\";", + "// var name = \"Alan Turing\";", "// var firstName = \"Alan\";", "// var lastName = \"Turing\";", "", @@ -110,15 +107,14 @@ { "id": "bd7123c9c448eddfaeb5bdef", "title": "Check the Length Property of a String Variable", - "difficulty": "9.9809", "description": [ - "data structures have properties. For example, strings have a property called .length that will tell you how many characters are in the string.", + "Data structures have properties. For example, strings have a property called .length that will tell you how many characters are in the string.", "For example, if we created a variable var firstName = \"Charles\", we could find out how long the string \"Charles\" is by using the firstName.length property.", "Use the .length property to count the number of characters in the lastName variable." ], "tests": [ - "assert((function(){if(typeof(lastNameLength) !== \"undefined\" && typeof(lastNameLength) === \"number\" && lastNameLength === 8){return true;}else{return false;}})(), 'lastNameLength should be equal to eight.');", - "assert((function(){if(editor.getValue().match(/\\.length/gi) && editor.getValue().match(/\\.length/gi).length >= 2 && editor.getValue().match(/var lastNameLength \\= 0;/gi) && editor.getValue().match(/var lastNameLength \\= 0;/gi).length >= 1){return true;}else{return false;}})(), 'You should be getting the length of lastName by using .length like this: lastName.length');" + "assert((function(){if(typeof(lastNameLength) !== \"undefined\" && typeof(lastNameLength) === \"number\" && lastNameLength === 8){return true;}else{return false;}})(), 'message: lastNameLength should be equal to eight.');", + "assert((function(){if(editor.getValue().match(/\\.length/gi) && editor.getValue().match(/\\.length/gi).length >= 2 && editor.getValue().match(/var lastNameLength \\= 0;/gi) && editor.getValue().match(/var lastNameLength \\= 0;/gi).length >= 1){return true;}else{return false;}})(), 'message: You should be getting the length of lastName by using .length like this: lastName.length.');" ], "challengeSeed": [ "var firstNameLength = 0;", @@ -129,7 +125,7 @@ "", "var lastName = \"Lovelace\";", "", - "// don't change code above here", + "// Only change code below this line.", "", "lastNameLength = lastName;", "", @@ -146,7 +142,6 @@ { "id": "bd7123c9c549eddfaeb5bdef", "title": "Use Bracket Notation to Find the First Character in a String", - "difficulty": "9.9810", "description": [ "Bracket notation is a way to get a character at a specific index within a string.", "Computers don't start counting at 1 like humans do. They start at 0.", @@ -155,7 +150,7 @@ "Try looking at the firstLetterOfFirstName variable declaration if you get stuck." ], "tests": [ - "assert((function(){if(typeof(firstLetterOfLastName) !== \"undefined\" && editor.getValue().match(/\\[0\\]/gi) && typeof(firstLetterOfLastName) === \"string\" && firstLetterOfLastName === \"L\"){return true;}else{return false;}})(), 'The first letter of firstLetterOfLastName should be a L');" + "assert((function(){if(typeof(firstLetterOfLastName) !== \"undefined\" && editor.getValue().match(/\\[0\\]/gi) && typeof(firstLetterOfLastName) === \"string\" && firstLetterOfLastName === \"L\"){return true;}else{return false;}})(), 'message: The first letter of firstLetterOfLastName should be a \"L\".');" ], "challengeSeed": [ "var firstLetterOfFirstName = \"\";", @@ -181,7 +176,6 @@ { "id": "bd7123c9c450eddfaeb5bdef", "title": "Use Bracket Notation to Find the Nth Character in a String", - "difficulty": "9.9811", "description": [ "You can also use bracket Notationto get the character at other positions within a string.", "Remember that computers start counting at 0, so the first character is actually the zeroth character.", @@ -189,7 +183,7 @@ "Try looking at the secondLetterOfFirstName variable declaration if you get stuck." ], "tests": [ - "assert(thirdLetterOfLastName === 'v', 'The third letter of lastName should be a \"v\"');" + "assert(thirdLetterOfLastName === 'v', 'message: The third letter of lastName should be a \"v\".');" ], "challengeSeed": [ "var firstName = \"Ada\";", @@ -212,7 +206,6 @@ { "id": "bd7123c9c451eddfaeb5bdef", "title": "Use Bracket Notation to Find the Last Character in a String", - "difficulty": "9.9812", "description": [ "In order to get the last letter of a string, you can subtract one from the string's length.", "For example, if var firstName = \"Charles\", you can get the value of the last letter of the string by using firstName[firstName.length - 1].", @@ -220,8 +213,8 @@ "Try looking at the lastLetterOfFirstName variable declaration if you get stuck." ], "tests": [ - "assert(lastLetterOfLastName === \"e\", 'lastLetterOfLastName should be \"e\"');", - "assert(editor.getValue().match(/\\.length/g), 'You have to use .length to get the last letter');" + "assert(lastLetterOfLastName === \"e\", 'message: lastLetterOfLastName should be \"e\".');", + "assert(editor.getValue().match(/\\.length/g).length === 2, 'message: You have to use .length to get the last letter.');" ], "challengeSeed": [ "var firstName = \"Ada\";", @@ -244,16 +237,15 @@ { "id": "bd7123c9c452eddfaeb5bdef", "title": "Use Bracket Notation to Find the Nth-to-Last Character in a String", - "difficulty": "9.9813", "description": [ "In order to get the last letter of a string, you can subtract one from the string's length.", "For example, you can get the value of the third-to-last letter of the var firstName = \"Charles\" string by using firstName[firstName.length - 3].", "Use bracket notation to find the second-to-last character in the lastName string.", - "Try looking at the lastLetterOfLastName variable declaration if you get stuck." + "Try looking at the thirdToLastLetterOfFirstName variable declaration if you get stuck." ], "tests": [ - "assert(secondToLastLetterOfLastName === 'c', 'secondToLastLetterOfLastName should be \"c\".');", - "assert(editor.getValue().match(/\\.length/g), 'You have to use .length to get the third last letter.');" + "assert(secondToLastLetterOfLastName === 'c', 'message: secondToLastLetterOfLastName should be \"c\".');", + "assert(editor.getValue().match(/\\.length/g).length === 2, 'message: You have to use .length to get the second last letter.');" ], "challengeSeed": [ "var firstName = \"Ada\";", @@ -276,14 +268,13 @@ { "id": "cf1111c1c11feddfaeb3bdef", "title": "Add Two Numbers with JavaScript", - "difficulty": "9.98141", "description": [ "Let's try to add two numbers using JavaScript.", "JavaScript uses the + symbol for addition.", "Replace the 0 with the correct number so you can get the result mentioned in the comment." ], "tests": [ - "assert((function(){if(sum === 20 && editor.getValue().match(/\\+/g)){return true;}else{return false;}})(), 'Make the variable sum equal 20');" + "assert((function(){if(sum === 20 && editor.getValue().match(/\\+/g).length >= 2){return true;}else{return false;}})(), 'message: Make the variable sum equal 20.');" ], "challengeSeed": [ "var sum = 10 + 0; //make this equal to 20 by changing the 0 into the appropriate number.", @@ -299,14 +290,13 @@ { "id": "cf1111c1c11feddfaeb4bdef", "title": "Subtract One Number from Another with JavaScript", - "difficulty": "9.98142", "description": [ "We can also subtract one number from another.", - "JavaScript uses use the - symbol for subtraction.", + "JavaScript uses the - symbol for subtraction.", "Replace the 0 with the correct number so you can get the result mentioned in the comment." ], "tests": [ - "assert((function(){if(difference === 12 && editor.getValue().match(/\\-/g)){return true;}else{return false;}})(), 'Make the variable difference equal 12');" + "assert((function(){if(difference === 12 && editor.getValue().match(/\\-/g)){return true;}else{return false;}})(), 'message: Make the variable difference equal 12.');" ], "challengeSeed": [ "var difference = 45 - 0; //make this equal to 12 by changing the 0 into the appropriate number.", @@ -322,17 +312,16 @@ { "id": "cf1231c1c11feddfaeb5bdef", "title": "Multiply Two Numbers with JavaScript", - "difficulty": "9.98143", "description": [ "We can also multiply one number by another.", - "JavaScript uses use the * symbol for multiplication.", + "JavaScript uses the * symbol for multiplication.", "Replace the 0 with the correct number so you can get the result mentioned in the comment." ], "tests": [ - "assert((function(){if(product === 80 && editor.getValue().match(/\\*/g)){return true;}else{return false;}})(), 'Make the variable product equal 80.');" + "assert((function(){if(product === 80 && editor.getValue().match(/\\*/g)){return true;}else{return false;}})(), 'message: Make the variable product equal 80.');" ], "challengeSeed": [ - "var product = 8 * 0; //make this equal to 80 by changing the 0 into the appropriate number.", + "var product = 8 * 0; // Make this equal to 80 by changing the 0 into the appropriate number.", "", "// Only change code above this line.", "// We use this function to show you the value of your variable in your output box.", @@ -345,14 +334,13 @@ { "id": "cf1111c1c11feddfaeb6bdef", "title": "Divide One Number by Another with JavaScript", - "difficulty": "9.9814", "description": [ "We can also divide one number by another.", - "JavaScript uses use the / symbol for division.", + "JavaScript uses the / symbol for division.", "Replace the 0 with the correct number so you can get the result mentioned in the comment." ], "tests": [ - "assert((function(){if(quotient === 2 && editor.getValue().match(/\\//g)){return true;}else{return false;}})(), 'Make the variable quotient equal 2.');" + "assert((function(){if(quotient === 2 && editor.getValue().match(/var\\s*?quotient\\s*?\\=\\s*?\\d+\\s*?\\/\\s*?\\d+\\s*?;/g)){return true;}else{return false;}})(), 'message: Make the variable quotient equal 2.');" ], "challengeSeed": [ "var quotient = 66 / 0; //make this equal to 2 by changing the 0 into the appropriate number.", @@ -368,13 +356,12 @@ { "id": "cf1391c1c11feddfaeb4bdef", "title": "Create Decimal Numbers with JavaScript", - "difficulty": "9.9815", "description": [ "JavaScript number variables can also have decimals.", "Let's create a variable myDecimal and give it a decimal value." ], "tests": [ - "assert((function(){if(typeof(myDecimal) !== \"undefined\" && typeof(myDecimal) === \"number\" && editor.getValue().match(/\\./g).length >=2){return true;}else{return false;}})(), 'myDecimal should be a decimal point number.');" + "assert((function(){if(typeof(myDecimal) !== \"undefined\" && typeof(myDecimal) === \"number\" && editor.getValue().match(/\\./g).length >=2){return true;}else{return false;}})(), 'message: myDecimal should be a decimal point number.');" ], "challengeSeed": [ "// var ourDecimal = 5.7;", @@ -393,14 +380,13 @@ { "id": "bd7993c9c69feddfaeb7bdef", "title": "Perform Arithmetic Operations on Decimals with JavaScript", - "difficulty": "9.98151", "description": [ "In JavaScript, you can also perform calculations with decimal numbers, just like whole numbers.", "Replace the 0.0 with the correct number so that you get the result mentioned in the comments." ], "tests": [ - "assert((function(){if(product === 5.0 && editor.getValue().match(/\\*/g)){return true;}else{return false;}})(), 'Make the variable product equal 5.0.');", - "assert((function(){if(quotient === 2.2 && editor.getValue().match(/\\//g)){return true;}else{return false;}})(), 'Make the variable quotient equal 2.2.');" + "assert((function(){if(product === 5.0 && editor.getValue().match(/\\*/g)){return true;}else{return false;}})(), 'message: Make the variable product equal 5.0.');", + "assert((function(){if(quotient === 2.2 && editor.getValue().match(/\\//g)){return true;}else{return false;}})(), 'message: Make the variable quotient equal 2.2.');" ], "challengeSeed": [ "var quotient = 4.4 / 2.0; // equals 2.2", @@ -418,20 +404,19 @@ { "id": "bd7993c9c69feddfaeb8bdef", "title": "Store Multiple Values in one Variable using JavaScript Arrays", - "difficulty": "9.9816", "description": [ "With JavaScript array variables, we can store several pieces of data in one place.", - "You start an array declaration with an opening bracket, end it with a closing bracket, and put a comma between each entry, like this: var sandwich = [\"peanut butter\", \"jelly\", \"bread\"].", - "Now let's create a new array called myArray that contains both a string and a number.", - "Refer to the comments if you get stuck." + "You start an array declaration with an opening square bracket, end it with a closing square bracket, and put a comma between each entry, like this: var sandwich = [\"peanut butter\", \"jelly\", \"bread\"].", + "Now let's create a new array called myArray that contains both a string and a number (in that order).", + "Refer to the commented code in the text editor if you get stuck." ], "tests": [ - "assert(typeof(myArray) == 'object', 'myArray should be an array');", - "assert(typeof(myArray[0]) !== 'undefined' && typeof(myArray[0]) == 'string', 'The first item in myArray should be a string');", - "assert(typeof(myArray[1]) !== 'undefined' && typeof(myArray[1]) == 'number', 'The second item in myArray should be a number');" + "assert(typeof(myArray) == 'object', 'message: myArray should be an array.');", + "assert(typeof(myArray[0]) !== 'undefined' && typeof(myArray[0]) == 'string', 'message: The first item in myArray should be a string.');", + "assert(typeof(myArray[1]) !== 'undefined' && typeof(myArray[1]) == 'number', 'message: The second item in myArray should be a number.');" ], "challengeSeed": [ - "//var array = [\"John\", 23];", + "// var array = [\"John\", 23];", "", "// Only change code below this line.", "", @@ -450,11 +435,11 @@ "title": "Nest one Array within Another Array", "difficulty":"9.98161", "description":[ - "You can also nest arrays within other arrays, like this: [[\"Bulls\", 43]].", + "You can also nest arrays within other arrays, like this: [[\"Bulls\", 23]].", "Let's now go create a nested array called myArray." ], "tests":[ - "assert(Array.isArray(myArray) && myArray.some(Array.isArray), 'myArray should have at least one array nested within another array.');" + "assert(Array.isArray(myArray) && myArray.some(Array.isArray), 'message: myArray should have at least one array nested within another array.');" ], "challengeSeed":[ "var ourArray = [[\"the universe\", \"everything\", 42]];", @@ -484,11 +469,11 @@ "Create a variable called myData and set it to equal the first value of myArray." ], "tests":[ - "assert((function(){if(typeof(myArray) != 'undefined' && typeof(myData) != 'undefined' && myArray[0] == myData){return true;}else{return false;}})(), 'The variable myData should equal the first value of myArray');" + "assert((function(){if(typeof(myArray) != 'undefined' && typeof(myData) != 'undefined' && myArray[0] == myData){return true;}else{return false;}})(), 'message: The variable myData should equal the first value of myArray.');" ], "challengeSeed":[ - "//var ourArray = [1,2,3];", - "//var ourData = ourArray[0]; // equals 1", + "// var ourArray = [1,2,3];", + "// var ourData = ourArray[0]; // equals 1", "", "var myArray = [1,2,3];", "// Only change code below this line.", @@ -514,13 +499,13 @@ "Now modify the data stored at index 0 of myArray to the value of 3." ], "tests":[ - "assert((function(){if(typeof(myArray) != 'undefined' && myArray[0] == 3 && myArray[1] == 2 && myArray[2] == 3){return true;}else{return false;}})(), 'myArray should now be [3,2,3]');", - "assert((function(){if(editor.getValue().match(/myArray\\[0\\]\\s?=\\s?/g)){return true;}else{return false;}})(), 'You should be using correct index to modify the value in myArray');" + "assert((function(){if(typeof(myArray) != 'undefined' && myArray[0] == 3 && myArray[1] == 2 && myArray[2] == 3){return true;}else{return false;}})(), 'message: myArray should now be [3,2,3].');", + "assert((function(){if(editor.getValue().match(/myArray\\[0\\]\\s?=\\s?/g)){return true;}else{return false;}})(), 'message: You should be using correct index to modify the value in myArray.');" ], "challengeSeed":[ "var ourArray = [1,2,3];", "ourArray[1] = 3;", - "// ourArray[1] now equals [1,3,3].", + "// ourArray now equals [1,3,3].", "var myArray = [1,2,3];", "// Only change code below this line.", "", @@ -536,23 +521,22 @@ { "id": "bg9994c9c69feddfaeb9bdef", "title": "Manipulate Arrays With pop()", - "difficulty": "9.9818", "description": [ "Another way to change the data in an array is with the .pop() function.", ".pop()is used to \"pop\" a value off of the end of an array. We can retrieve this value by performing pop() in a variable declaration.", "Any type of variable can be \"popped\" off of an array.", - "Use the .pop() function to remove the last item from myArray." + "Use the .pop() function to remove the last item from myArray." ], "tests": [ - "assert((function(d){if(d[0] == 'John' && d[1] == 23 && d[2] == undefined){return true;}else{return false;}})(myArray), 'myArray should only have the first two values left([\"John\", 23])');", - "assert((function(d){if(d[0] == 'cat' && d[1] == 2 && d[2] == undefined){return true;}else{return false;}})(removed), 'myArray should only have the first two values left([\"cat\"], 2)');" + "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.');" ], "challengeSeed": [ - "//var numbers = [1,2,3];", - "//console.log(numbers); // logs [1,2,3]", - "//var removed = numbers.pop();", - "//console.log(numbers); // logs [1,2]", - "//console.log(removed); // logs 3", + "// var numbers = [1,2,3];", + "// console.log(numbers); // logs [1,2,3]", + "// var removed = numbers.pop();", + "// console.log(numbers); // logs [1,2]", + "// console.log(removed); // logs 3", "", "var myArray = [\"John\", 23, [\"cat\", 2]];", "// Only change code below this line.", @@ -570,23 +554,22 @@ { "id": "bg9995c9c69feddfaeb9bdef", "title": "Manipulate Arrays With push()", - "difficulty": "9.9818", "description": [ "Not only can you pop() data off of the end of an array, you can also push() data onto the end of an array.", - "Take the myArray array and push() this value to the end of it: [\"dog\", 3]." + "Take the myArray array and push() this value to the end of it: [\"dog\", 3]." ], "tests": [ - "assert((function(d){if(d[2] != undefined && d[0] == 'John' && d[1] == 23 && d[2][0] == 'dog' && d[2][1] == 3 && d[2].length == 2){return true;}else{return false;}})(myArray), 'myArray should only have the first two values left([\"John\", 23, [\"dog\", 3]])');" + "assert((function(d){if(d[2] != undefined && d[0] == 'John' && d[1] == 23 && d[2][0] == 'dog' && d[2][1] == 3 && d[2].length == 2){return true;}else{return false;}})(myArray), 'message: myArray should now equal [\"John\", 23, [\"dog\", 3]].');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", "ourArray.pop();", "ourArray.push([\"happy\", \"joy\"]);", - "// ourArray now equals [\"Stimpson\", \"J\", [\"happy\", \"joy\"]]", + "// ourArray now equals [\"Stimpson\", \"J\", [\"happy\", \"joy\"]].", "", "var myArray = [\"John\", 23, [\"cat\", 2]];", "myArray.pop();", - "//Add a [\"dog\", 3] to the end of myArray using push()", + "// Add a [\"dog\", 3] to the end of myArray using push().", "// Only change code below this line.", "", "", @@ -601,24 +584,23 @@ { "id": "bg9996c9c69feddfaeb9bdef", "title": "Manipulate Arrays With shift()", - "difficulty": "9.9817", "description": [ "pop() always removes the last element of an array. What if you want to remove the first? That's where .shift() comes in.", - "Take the myArray array and shift() the first value off of it." + "Take the myArray array and shift() the first value off of it. Set myRemoved to the first value of myArray using shift()." ], "tests": [ - "assert((function(d){if(d[0] == 23 && d[1][0] == 'dog' && d[1][1] == 3 && d[2] == undefined){return true;}else{return false;}})(myArray), 'myArray should only have the last two values left([23, [\"dog\", 3]])');", - "assert((function(d){if(d === 'John' && typeof(myRemoved) === 'string'){return true;}else{return false;}})(myRemoved), 'myRemoved should contain \"John\"');" + "assert((function(d){if(d[0] == 23 && d[1][0] == 'dog' && d[1][1] == 3 && d[2] == undefined){return true;}else{return false;}})(myArray), 'message: myArray should now equal [23, [\"dog\", 3]].');", + "assert((function(d){if(d === 'John' && typeof(myRemoved) === 'string'){return true;}else{return false;}})(myRemoved), 'message: myRemoved should contain \"John\".');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", "ourRemoved = ourArray.shift();", - "// ourArray now equals [\"J\", [\"cat\"]]", + "// ourArray now equals [\"J\", [\"cat\"]].", "", "var myArray = [\"John\", 23, [\"dog\", 3]];", "// Only change code below this line.", "", - "var myRemoved = myArray; // This should be [\"John\"] and myArray should now be [23, [\"dog\", 3]]", + "var myRemoved = myArray; // This should be [\"John\"] and myArray should now be [23, [\"dog\", 3]].", "", "// Only change code above this line.", "", @@ -632,24 +614,24 @@ { "id": "bg9997c9c69feddfaeb9bdef", "title": "Manipulate Arrays With unshift()", - "difficulty": "9.9818", "description": [ - "Now that we've learned how to shiftthings from the start of the array, we need to learn how to unshiftstuff back to the start", - "Let's take the code we had last time and unshiftthis value to the start: \"Paul\" " + "Now that we've learned how to shiftthings from the start of the array, we need to learn how to unshiftstuff back to the start.", + "Let's take the code we had last time and unshiftthis value to the start: \"Paul\"." ], "tests": [ - "assert((function(d){if(d[0].toLowerCase() == 'paul' && d[1] == 23 && d[2][0] != undefined && d[2][0] == 'dog' && d[2][1] != undefined && d[2][1] == 3){return true;}else{return false;}})(myArray), 'myArray should now have [\"Paul\", 23, [\"dog\", 3]])');" + "assert((function(d){if(typeof(d[0]) === \"string\" && d[0].toLowerCase() == 'paul' && d[1] == 23 && d[2][0] != undefined && d[2][0] == 'dog' && d[2][1] != undefined && d[2][1] == 3){return true;}else{return false;}})(myArray), 'message: myArray should now have [\"Paul\", 23, [\"dog\", 3]]).');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", "ourArray.shift();", - "ourArray.unshift([\"happy\", \"joy\"]);", - "// ourArray now equals [[\"happy\", \"joy\"], \"J\", [\"cat\"]]", + "// ourArray now equals [\"J\", [\"cat\"]]", + "ourArray.unshift(\"happy\");", + "// ourArray now equals [\"happy\", \"J\", [\"cat\"]]", "", "var myArray = [\"John\", 23, [\"dog\", 3]];", "myArray.shift();", "", - "// Add \"Paul\" to the start of myArray", + "// Add \"Paul\" to the start of myArray.", "// Only change code below this line.", "", "", @@ -666,26 +648,27 @@ "title": "Write Reusable JavaScript with Functions", "difficulty":"9.9819", "description":[ - "In JavaScript, we can divide up our code into reusable parts called functions.", + "In JavaScript, we can divide up our code into reusable parts called functions.", "Here's an example of a function:", - "function functionName (a, b) {", + "function functionName(a, b) {", "  return a + b;", "}", - "We can \"call\" our function like this: functionName();, and it will run and return its return value to us.", - "Create and call a function called myFunction that returns the sum of a and b." + "After writing the above lines in our code, we can then pass values to our function and the result following the return statement will be returned.", + "For example, we can pass numbers 4 and 2 by “calling” the function later in our code like this: functionName(4, 2).", + "In this example, the function will return the number 6 as this is the result of 4 + 2.", + "Create and call a function called myFunction that returns the sum of a and b." ], "tests":[ - "assert((function(){if(typeof(f) !== \"undefined\" && typeof(f) === \"number\" && f === a + b && editor.getValue().match(/return/gi).length >= 1 && editor.getValue().match(/a/gi).length >= 1 && editor.getValue().match(/b/gi).length >= 1 && editor.getValue().match(/\\+/gi).length >= 1){return true;}else{return false;}})(), 'Your function should return the value of a + b');" + "assert((function(){if(typeof(f) !== \"undefined\" && f === a + b){return true;}else{return false;}})(), 'message: Your function should return the value of a + b');" ], "challengeSeed":[ "var a = 4;", "var b = 5;", "", - "ourFunction = function() {", + "function ourFunction(a, b) {", " return a - b;", - "};", + "}", "", - "// Don't modify above this line", "// Create a function called myFunction that returns the value of a plus b.", "// Only change code below this line.", "", @@ -719,23 +702,23 @@ "};", "", "Objects are useful for storing data in a structured way, and can represent real world objects, like a cat.", - "Let's try to make an Object that represents a dog called myDog which contains the properties 'name' (String), 'legs' (Number), 'tails' (Number) and 'friends' (Array)!" + "Let's try to make an object that represents a dog called myDog which contains the properties 'name' (String), 'legs' (Number), 'tails' (Number) and 'friends' (Array)!" ], "tests":[ - "assert((function(z){if(z.hasOwnProperty(\"name\") && z.name !== undefined && typeof(z.name) === \"string\"){return true;}else{return false;}})(myDog), 'myDog should contain the property name and it should be a string');", - "assert((function(z){if(z.hasOwnProperty(\"legs\") && z.legs !== undefined && typeof(z.legs) === \"number\"){return true;}else{return false;}})(myDog), 'myDog should contain the property legs and it should be a number');", - "assert((function(z){if(z.hasOwnProperty(\"tails\") && z.tails !== undefined && typeof(z.tails) === \"number\"){return true;}else{return false;}})(myDog), 'myDog should contain the property tails and it should be a number');", - "assert((function(z){if(z.hasOwnProperty(\"friends\") && z.friends !== undefined && Array.isArray(z.friends)){return true;}else{return false;}})(myDog), 'myDog should contain the property friends and it should be an array');" + "assert((function(z){if(z.hasOwnProperty(\"name\") && z.name !== undefined && typeof(z.name) === \"string\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property name and it should be a string.');", + "assert((function(z){if(z.hasOwnProperty(\"legs\") && z.legs !== undefined && typeof(z.legs) === \"number\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property legs and it should be a number.');", + "assert((function(z){if(z.hasOwnProperty(\"tails\") && z.tails !== undefined && typeof(z.tails) === \"number\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property tails and it should be a number.');", + "assert((function(z){if(z.hasOwnProperty(\"friends\") && z.friends !== undefined && Array.isArray(z.friends)){return true;}else{return false;}})(myDog), 'message: myDog should contain the property friends and it should be an array.');" ], "challengeSeed":[ - "//var ourDog = {", + "// var ourDog = {", "// \"name\": \"Camper\",", "// \"legs\": 4,", "// \"tails\": 1,", "// \"friends\": [\"everything!\"]", - "//};", + "// };", "", - "// add the name(string), legs(number), tails(number) and friends(array) properties to myDog.", + "// Add the name (string), legs (number), tails (number) and friends (array) properties to myDog.", "// You can set them to whatever you want.", "", "// Only change code below this line.", @@ -761,11 +744,11 @@ "myObject.myProperty = \"myValue\";", "We can also delete them like this:", "delete myObject.myProperty;", - "Let's add the property \"bark\", and delete the property \"tails\"." + "Let's add the property \"bark\", and delete the property \"tails\"." ], "tests":[ - "assert(myDog.bark !== undefined, 'Add the property \"bark\" to myDog.');", - "assert(myDog.tails === undefined, 'Delete the property \"tails\" from myDog.');" + "assert(myDog.bark !== undefined, 'message: Add the property \"bark\" to myDog.');", + "assert(myDog.tails === undefined, 'message: Delete the property \"tails\" from myDog.');" ], "challengeSeed":[ "// var ourDog = {", @@ -815,8 +798,8 @@ "Let's try getting a for loop to work by pushing values to an array." ], "tests":[ - "assert(editor.getValue().match(/for/g), 'You should be using a for loop for this.');", - "assert.deepEqual(myArray, [0,1,2,3,4], 'myArray should equal [0,1,2,3,4]');" + "assert(editor.getValue().match(/for/g), 'message: You should be using a for loop for this.');", + "assert.deepEqual(myArray, [0,1,2,3,4], 'message: myArray should equal [0,1,2,3,4].');" ], "challengeSeed":[ "ourArray = [];", @@ -824,8 +807,15 @@ " ourArray.push(i);", "}", "var myArray = [];", - "//Push the numbers zero through four to myArray using a \"for loop\" like above.", "", + "// Only change code below this line.", + "", + "// Push the numbers zero through four to myArray using a \"for loop\" like above.", + "", + "// Only change code above this line.", + "// We use this function to show you the value of your variable in your output box.", + "// You'll learn about functions soon.", + "if(typeof(myArray) !== \"undefined\"){(function(){return myArray;})();}", "" ], "type": "waypoint", @@ -847,13 +837,19 @@ "Let's try getting a while loop to work by pushing values to an array." ], "tests":[ - "assert(editor.getValue().match(/while/g), 'You should be using a while loop for this.');", - "assert.deepEqual(myArray, [0,1,2,3,4], 'myArray should equal [0,1,2,3,4]');" + "assert(editor.getValue().match(/while/g), 'message: You should be using a while loop for this.');", + "assert.deepEqual(myArray, [0,1,2,3,4], 'message: myArray should equal [0,1,2,3,4].');" ], "challengeSeed":[ "var myArray = [];", - "//Push the numbers zero through four to myArray", + "// Only change code below this line.", "", + "// Push the numbers zero through four to myArray using a \"while loop\".", + "", + "// Only change code above this line.", + "// We use this function to show you the value of your variable in your output box.", + "// You'll learn about functions soon.", + "if(typeof(myArray) !== \"undefined\"){(function(){return myArray;})();}", "" ], "type": "waypoint", @@ -869,18 +865,18 @@ "Use Math.random() to get myFunction to return a random number." ], "tests":[ - "assert(typeof(myFunction()) === \"number\", 'myFunction should return a random number');", - "assert((myFunction()+''). match(/\\./g), 'The number returned by myFunction should be a decimal');", - "assert(editor.getValue().match(/Math\\.random/g).length >= 2, 'You should be using Math.random to generate the random decimal number');" + "assert(typeof(myFunction()) === \"number\", 'message: myFunction should return a random number.');", + "assert((myFunction()+''). match(/\\./g), 'message: The number returned by myFunction should be a decimal.');", + "assert(editor.getValue().match(/Math\\.random/g).length >= 2, 'message: You should be using Math.random to generate the random decimal number.');" ], "challengeSeed":[ "function myFunction() {", - " //Change the 0 to Math.random()", + " // Change the 0 to Math.random().", " // Only change code below this line.", "", " return 0;", "", - "// Only change code above this line.", + " // Only change code above this line.", "}", "", "// We use this function to show you the value of your variable in your output box.", @@ -895,21 +891,21 @@ "difficulty":"9.9828", "description":[ "It's great that we can create random decimal numbers, but it's even more useful if we use it to generate a random whole number.", - "To achieve this we can multiply the random number by ten and use the Math.floor() to convert the decimal number to a whole number.", + "To achieve this we can multiply the random number by ten and use the Math.floor() to convert the decimal number to the nearest less than or equal whole number.", "This technique gives us a whole number between zero and nine.", "Example:", "Math.floor(Math.random()*10);", "Let's give this technique a go now." ], "tests":[ - "assert(typeof(myFunction()) === \"number\", 'The result of myFunction should be a number');", - "assert(editor.getValue().match(/Math.random/g), 'You should be using Math.random to create a random number');", - "assert(!(''+myFunction()).match(/\\./g), 'You should have multiplied the result of Math.random by 10 to make it a number that\\'s greater than zero');", - "assert(editor.getValue().match(/Math.floor/g), 'You should use Math.floor to remove the decimal part of the number');" + "assert(typeof(myFunction()) === \"number\", 'message: The result of myFunction should be a number.');", + "assert(editor.getValue().match(/Math.random/g), 'message: You should be using Math.random to create a random number.');", + "assert(editor.getValue().match(/\\(\\s*?Math.random\\s*?\\(\\s*?\\)\\s*?\\*\\s*?10\\s*?\\)/g) || editor.getValue().match(/\\(\\s*?10\\s*?\\*\\s*?Math.random\\s*?\\(\\s*?\\)\\s*?\\)/g), 'message: You should have multiplied the result of Math.random by 10 to make it a number that is between zero and nine.');", + "assert(editor.getValue().match(/Math.floor/g), 'message: You should use Math.floor to remove the decimal part of the number.');" ], "challengeSeed":[ "function myFunction(){", - " // Make myFunction return a random number betweenzero and nine> instead of a decimal", + " // Make myFunction return a random number between zero and nine instead of a decimal.", "", " // Only change code below this line.", "", @@ -931,12 +927,13 @@ "description":[ "We can use a certain mathematical expression to get a random number between two numbers.", "Math.floor(Math.random() * (max - min + 1)) + min", - "By using this we can control the output of a random number." + "By using this, we can control the output of a random number." ], "tests":[ - "assert(myFunction() >= min, 'The random number that\\'s generated by myFunction should be greater than or equal to the minimum number');", - "assert(myFunction() <= max, 'The random number that\\'s generated by myFunction should be less than or equal to the maximum number');", - "assert((function(){if(editor.getValue().match(/max/g).length >= 2 && editor.getValue().match(/min/g).length >= 2 && editor.getValue().match(/Math.floor/g) && editor.getValue().match(/Math.random/g)){return true;}else{return false;}})(), 'You should be using the function given in the description to calculate the random in number in a range');" + "assert(myFunction() >= min, 'message: The random number generated by myFunction should be greater than or equal to the minimum number.');", + "assert(myFunction() <= max, 'message: The random number generated by myFunction should be less than or equal to the maximum number.');", + "assert(myFunction() % 1 === 0 , 'message: The random number generated by myFunction should be an integer, not a decimal.');", + "assert((function(){if(editor.getValue().match(/max/g).length >= 2 && editor.getValue().match(/min/g).length >= 2 && editor.getValue().match(/Math.floor/g) && editor.getValue().match(/Math.random/g)){return true;}else{return false;}})(), 'message: You should be using the function given in the description to calculate the random in number in a range.');" ], "challengeSeed":[ "var min = 0;", @@ -957,39 +954,40 @@ }, { "id":"cf1111c1c12feddfaeb3bdef", - "title": "Use Conditional Logic with If-Else Statements", + "title": "Use Conditional Logic with If and Else Statements", "difficulty":"9.983", "description":[ - "We can use if statements in JavaScript to only execute code if a certain condition is met.", - "if statements require some sort of boolean condition to evaluate.", - "Example:", + "We can use if statements in JavaScript to only execute code if a certain condition is met.", + "if statements require some sort of boolean condition to evaluate.", + "For example:", " if (1 === 2) {", "  return true;", - "}", - "else {", + "} else {", "  return false;", "}", "Let's use if and else statements to make a coin-flip game.", - "Create an if-else statement to return heads if the flip var is zero, or else return tails if it's not." + "Create if and else statements to return the string \"heads\" if the flip variable is zero, or else return the string \"tails\" if the flip variable is not zero." ], "tests":[ - "assert((function(){if(myFunction() === \"heads\" || myFunction() === \"tails\"){return true;}else{return false;}})(), 'myFunction should either return heads or tails');", - "assert(editor.getValue().match(/if/g).length >= 3, 'You should have created a new if statement');", - "assert(editor.getValue().match(/else/g).length >= 2, 'You should have created a new else statement');" + "assert((function(){var result = myFunction();if(result === 'heads' || result === 'tails'){return true;} else {return false;}})(), 'message: myFunction should either return heads or tails.');", + "assert((function(){var result = myFunction();if(result === 'heads' && flip === 0 || result === 'tails' && flip !== 0){return true;} else {return false;}})(), 'message: myFunction should return heads when flip equals 0 and tails when flip equals 1.');", + "assert(editor.getValue().match(/if/g).length >= 4, 'message: You should have created a new if statement.');", + "assert(editor.getValue().match(/else/g).length >= 2, 'message: You should have created a new else statement.');" ], "challengeSeed":[ + "var flip = Math.floor(Math.random() * (1 - 0 + 1)) + 0;", "function myFunction(){", - " var flip = Math.floor(Math.random() * (1 - 0 + 1)) + 0;", " // Create an if-else statement here to return \"heads\" if flip is 0. Otherwise return \"tails\".", "", " // Only change code below this line.", "", "", + "", + " // Only change code above this line.", "}", "", - "// Only change code above this line.", "// We use this function to show you the value of your variable in your output box.", - "(function(){return myFunction();})();" + "var result = myFunction();if(typeof(flip) !== \"undefined\" && typeof(flip) === \"number\" && typeof(result) !== \"undefined\" && typeof(result) === \"string\"){(function(y,z){return 'flip = ' + y.toString() + ', text = ' + z;})(flip, result);}" ], "type": "waypoint", "challengeType": 1 @@ -1005,17 +1003,17 @@ "the is the pattern we want to match.", "g means that we want to search the entire string for this pattern instead of just the first match.", "i means that we want to ignore the case (uppercase or lowercase) when searching for the pattern.", - "Regular expressions are written by surrounding the pattern with a / symbol.", - "Let's try selecting all the occurrences of the word and in the string George Boole and Alan Turing went to the shop and got some milk. We can do this by replacing the ... part of our regular expression with the current regular expression with the word and." + "Regular expressions are written by surrounding the pattern with / symbols.", + "Let's try selecting all the occurrences of the word and in the string Ada Lovelace and Charles Babbage designed the first computer and the software that would have run on it. We can do this by replacing the . part of our regular expression with the current regular expression with the word and." ], "tests":[ - "assert(test==2, 'Your regular expression should find two occurrences of the word and');", - "assert(editor.getValue().match(/\\/and\\/gi/), 'You should have used regular expressions to find the word and');" + "assert(test==2, 'message: Your regular expression should find two occurrences of the word and.');", + "assert(editor.getValue().match(/\\/and\\/gi/), 'message: You should have used regular expressions to find the word and.');" ], "challengeSeed":[ "var test = (function() {", - " var testString = \"George Boole and Alan Turing went to the shop and got some milk\";", - " var expressionToGetMilk = /milk/gi;", + " var testString = \"Ada Lovelace and Charles Babbage designed the first computer and the software that would have run on it.\";", + " var expressionToGetSoftware = /software/gi;", " // Only change code below this line.", "", " var expression = /./gi;", @@ -1035,12 +1033,13 @@ "description":[ "We can use special selectors in Regular Expressions to select a particular type of value.", "One such selector is the digit selector \\d which is used to grab the numbers in a string.", - "It is used like this: /\\d+/g.", - "Use the \\d selector to select the number of numbers in the string." + "It is used like this: /\\d/g.", + "For numbers this is often written as /\\d+/g, where the + following the digit selector allows this regular expression to match multi-digit numbers.", + "Use the \\d selector to select the number of numbers in the string, allowing for the possibility of multi-digit numbers." ], "tests":[ - "assert(test === 2, 'Your RegEx should have found two numbers in the testString');", - "assert(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'You should be using the following expression /\\\\d+/gi to find the numbers in the testString');" + "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.');" ], "challengeSeed":[ "var test = (function() {", @@ -1069,8 +1068,8 @@ "Select all the spaces in the sentence string." ], "tests":[ - "assert(test === 7, 'Your RegEx should have found seven spaces in the testString.');", - "assert(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'You should be using the following expression /\\\\s+/gi to find the spaces in the testString.');" + "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.');" ], "challengeSeed":[ "var test = (function(){", @@ -1093,12 +1092,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/gi 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, 'Your RegEx should have found forty nine non-space characters in the testString.');", - "assert(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'You should be using the following expression /\\\\S/gi to find non-space characters in the testString.');" + "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.');" ], "challengeSeed":[ "var test = (function(){", @@ -1124,14 +1123,14 @@ "We are now going to try and combine some of the stuff we've just learned and create the logic for a slot machine game.", "For this we will need to generate three random numbers between 1 and 3 to represent the possible values of each individual slot.", "Store the three random numbers in slotOne, slotTwo and slotThree.", - "Generate the random numbers by using the system we used earlier:", + "Generate the random numbers by using the system we used earlier (an explanation of the formula can be found here):", "Math.floor(Math.random() * (3 - 1 + 1)) + 1;" ], "tests":[ - "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.')" + "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.')" ], "challengeSeed":[ "fccss", @@ -1285,7 +1284,7 @@ "}" ], "tests":[ - "assert((function(){var data = runSlots();if(data === null){return true}else{if(data[0] === data[1] && data[1] === data[2]){return true;}else{return false;}}})(), 'If all three of our random numbers are the same we should return that number. Otherwise we should return null.')" + "assert((function(){var data = runSlots();if(data === null){return true}else{if(data[0] === data[1] && data[1] === data[2]){return true;}else{return false;}}})(), 'If all three of our random numbers are the same we should return that number. Otherwise we should return null.')" ], "challengeSeed":[ "fccss", @@ -1443,8 +1442,8 @@ "Use the above selector to display each number in its corresponding slot." ], "tests":[ - "assert((function(){runSlots();if($($(\".slot\")[0]).html().replace(/\\s/gi, \"\") !== \"\" && $($(\".slot\")[1]).html().replace(/\\s/gi, \"\") !== \"\" && $($(\".slot\")[2]).html().replace(/\\s/gi, \"\") !== \"\"){return true;}else{return false;}})(), 'You should be displaying the result of the slot numbers in the corresponding slots')", - "assert((function(){if(editor.match( /\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)/gi )){if(editor.match( /\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)/gi ).length >= 3 && editor.match( /\\.html\\(slotOne\\)/gi ) && editor.match( /\\.html\\(slotTwo\\)/gi ) && editor.match( /\\.html\\(slotThree\\)/gi )){return true;}else{return false;}}else{return false;}})(), 'You should have used the the selector given in the description to select each slot and assign it the value of slotOne, slotTwo and slotThree respectively')" + "assert((function(){runSlots();if($($(\".slot\")[0]).html().replace(/\\s/gi, \"\") !== \"\" && $($(\".slot\")[1]).html().replace(/\\s/gi, \"\") !== \"\" && $($(\".slot\")[2]).html().replace(/\\s/gi, \"\") !== \"\"){return true;}else{return false;}})(), 'You should be displaying the result of the slot numbers in the corresponding slots.')", + "assert((editor.match( /\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[\\d\\]\\s*?\\)/gi) && editor.match( /\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[\\d\\]\\s*?\\)/gi ).length >= 3 && editor.match( /\\.html\\(slotOne\\)/gi ) && editor.match( /\\.html\\(slotTwo\\)/gi ) && editor.match( /\\.html\\(slotThree\\)/gi )), 'You should have used the the selector given in the description to select each slot and assign it the value of slotOne, slotTwo and slotThree respectively.')" ], "challengeSeed":[ "fccss", @@ -1598,7 +1597,7 @@ }, { "id":"cf1111c1c11feddfaeb1bdff", - "title": "Give your JavaScript Slot Machine some stylish images", + "title": "Give your JavaScript Slot Machine some Stylish Images", "difficulty":"9.9901", "description":[ "Now let's add some images to our slots.", @@ -1608,10 +1607,13 @@ "Set up all three slots like this, then click the \"Go\" button to play the slot machine." ], "tests":[ - "assert((editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\\\'\\);/gi) && editor.match(/\\$\\(\\$\\(\\'\\.slot\\'\\)\\[\\d\\]\\)\\.html\\(\\'\\\\'\\);/gi).length >= 3) || (editor.match(/\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)\\.html\\(\\'\\\\'\\);/gi) && editor.match(/\\$\\(\\$\\(\\\"\\.slot\\\"\\)\\[\\d\\]\\)\\.html\\(\\'\\\\'\\);/gi).length >= 3), 'Use the provided code three times. One for each slot')", - "assert(editor.match(/slotOne/gi) && editor.match(/slotOne/gi).length >= 7, 'You should have used the slotOne value at least once')", - "assert(editor.match(/slotTwo/gi) && editor.match(/slotTwo/gi).length >= 8, 'You should have used the slotTwo value at least once')", - "assert(editor.match(/slotThree/gi) && editor.match(/slotThree/gi).length >= 7, 'You should have used the slotThree value at least once')" + "assert((editor.match(/\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[\\d\\]\\s*?\\)\\.html\\(\\s*?\\'\\\\'\\s*?\\);/gi) && editor.match(/\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[\\d\\]\\s*?\\)\\.html\\(\\s*?\\'\\\\'\\s*?\\);/gi).length >= 3), 'Use the provided code three times. One for each slot.')", + "assert(editor.match(/\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[0\\]\\s*?\\)/gi), 'You should have used $('.slot')[0] at least once.')", + "assert(editor.match(/\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[1\\]\\s*?\\)/gi), 'You should have used $('.slot')[1] at least once.')", + "assert(editor.match(/\\$\\s*?\\(\\s*?\\$\\s*?\\(\\s*?(?:'|\")\\s*?\\.slot\\s*?(?:'|\")\\s*?\\)\\[2\\]\\s*?\\)/gi), 'You should have used $('.slot')[2] at least once.')", + "assert(editor.match(/slotOne/gi) && editor.match(/slotOne/gi).length >= 7, 'You should have used the slotOne value at least once.')", + "assert(editor.match(/slotTwo/gi) && editor.match(/slotTwo/gi).length >= 8, 'You should have used the slotTwo value at least once.')", + "assert(editor.match(/slotThree/gi) && editor.match(/slotThree/gi).length >= 7, 'You should have used the slotThree value at least once.')" ], "challengeSeed":[ "fccss", diff --git a/seed/challenges/basic-ziplines.json b/seed/challenges/basic-ziplines.json index d97d049b80..cf2ac272f6 100644 --- a/seed/challenges/basic-ziplines.json +++ b/seed/challenges/basic-ziplines.json @@ -1,6 +1,6 @@ { "name": "Basic Front End Development Projects", - "order": 0.008, + "order": 8, "challenges": [ { "id": "bd7158d8c442eddfbeb5bd1f", @@ -16,7 +16,8 @@ "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). Then click \"Save & Close\".", + "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." @@ -28,8 +29,22 @@ "descriptionCn": [], "nameFr": "", "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], + "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\"." + ], "nameEs": "", "descriptionEs": [], "namePt": "", @@ -56,7 +71,7 @@ "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.", "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, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], "type": "zipline", "challengeType": 3, @@ -65,8 +80,25 @@ "descriptionCn": [], "nameFr": "", "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], + "nameRu": "Создайте сайт-портфолио", + "descriptionRu": [ + "Задание: Создайте CodePen.io который успешно копирует вот этот: http://codepen.io/ThiagoFerreir4/full/eNMxEp.", + "Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.", + "Правило #2: Можете использовать любые библиотеки или API, которые потребуются.", + "Правило #3: Воссоздайте функционал приведенного примера и не стесняйтесь добавить что-нибудь от себя.", + "Подсказка: Если вы не хотите создавать портфолио с нуля, можете взять за основу этот простой Bootstrap шаблон: http://codepen.io/FreeCodeCamp/pen/mJNqQj.", + "Реализуйте следующие пользовательские истории, сделайте также бонусные по желанию:", + "Пользовательская история: В качестве пользователя, я могу получить доступ ко всей информации на странице просто прокрутив ее сверху вниз.", + "Пользовательская история: В качестве пользователя, я могу нажать на различные кнопки и перейти к социальным страницам владельца портфолио.", + "Пользовательская история: В качестве пользователя, я могу увидеть эскизы проектов, которые были созданы владельцем портфолио (используйте временную картинку если у вас пока нету собственных веб-страниц).", + "Бонусная пользовательская история: В качестве пользователя, я могу перемещаться к различным частям страницы нажимая на соответствующие навигационные кнопки.", + "Не переживайте если вам пока нечего показать в портфолио - вы создадите несколько веб приложений в следующих заданиях, а затем вернетесь и обновите портфолио.", + "В сети существует много шаблонов для портфолио, но в этом задании вам необходимо создать собственную уникальную страницу. Использование Bootstrap сделает этот процесс намного проще.", + "Обратите внимание, что CodePen.io переопределяет функцию Window.open(), поэтому, если вы хотите открывать окна используя jQuery, необходимо будет адресовать невидимые якорные элементы, такие как этот: <a target='_blank'&rt;.", + "Если что-то не получается, воспользуйтесь Read-Search-Ask.", + "Когда выполните задание кликните кнопку \"I've completed this challenge\" и добавьте ссылку на ваш CodePen. Если вы программировали с кем-то в паре, также добавьте имя вашего напарника.", + "Если вы хотите получить немедленную оценку вашего проекта, нажмите эту кнопку и добавьте ссылку на ваш CodePen. В противном случае мы проверим его перед тем как вы приступите к проектам для некоммерческих организаций.

Click here then add your link to your tweet's text" + ], "nameEs": "", "descriptionEs": [], "namePt": "", @@ -88,7 +120,7 @@ "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, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], "type": "zipline", "challengeType": 3, @@ -97,41 +129,20 @@ "descriptionCn": [], "nameFr": "", "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], - "nameEs": "", - "descriptionEs": [], - "namePt": "", - "descriptionPt": [] - }, - { - "id": "bd7158d8c442eddfaeb5bd10", - "title": "Show the Local Weather", - "difficulty": 1.03, - "challengeSeed": ["126415127"], - "description": [ - "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/AdventureBear/full/yNBJRj.", - "Rule #1: Don't look at the example project's code on CodePen. Figure it out for yourself.", - "Rule #2: You may use whichever libraries or APIs you need.", - "Rule #3: Reverse engineer the example project's functionality, and also feel free to personalize it.", - "Here are the user stories you must enable, and optional bonus user stories:", - "User Story: As a user, I can see the weather in my current location.", - "Bonus User Story: As a user, I can see an icon depending on the temperature..", - "Bonus User Story: As a user, I see a different background image depending on the temperature (e.g. snowy mountain, hot desert).", - "Bonus User Story: As a user, I can push a button to toggle between Fahrenheit and Celsius.", - "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, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "nameRu": "Создайте генератор случайных цитат", + "descriptionRu": [ + "Задание: Создайте CodePen.io который успешно копирует вот этот: http://codepen.io/AdventureBear/full/vEoVMw.", + "Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.", + "Правило #2: Можете использовать любые библиотеки или API, которые потребуются.", + "Правило #3: Воссоздайте функционал приведенного примера и не стесняйтесь добавить что-нибудь от себя.", + "Реализуйте следующие пользовательские истории, сделайте также бонусные по желанию:", + "Пользовательская история: В качестве пользователя, я могу нажать на кнопку и получить случайную цитату.", + "Бонусная пользовательская история: В качестве пользователя, я могу нажать на кнопку и опубликовать цитату в Twitter'e.", + "Цитаты можно добавить в массив и случайным образом выводить одну из них, либо можно воспользоваться соответствующим API, например http://forismatic.com/en/api/.", + "Если что-то не получается, воспользуйтесь Read-Search-Ask.", + "Когда выполните задание кликните кнопку \"I've completed this challenge\" и добавьте ссылку на ваш CodePen. Если вы программировали с кем-то в паре, также добавьте имя вашего напарника.", + "Если вы хотите получить немедленную оценку вашего проекта, нажмите эту кнопку и добавьте ссылку на ваш CodePen. В противном случае мы проверим его перед тем как вы приступите к проектам для некоммерческих организаций.

Click here then add your link to your tweet's text" ], - "type": "zipline", - "challengeType": 3, - "tests": [], - "nameCn": "", - "descriptionCn": [], - "nameFr": "", - "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], "nameEs": "", "descriptionEs": [], "namePt": "", @@ -140,7 +151,6 @@ { "id": "bd7158d8c442eddfaeb5bd0f", "title": "Build a Pomodoro Clock", - "difficulty": 1.04, "challengeSeed": ["126411567"], "description": [ "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/GeoffStorbeck/full/RPbGxZ/.", @@ -153,7 +163,7 @@ "Bonus User Story: As a user, I can customize the length of each pomodoro.", "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, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], "type": "zipline", "challengeType": 3, @@ -162,35 +172,41 @@ "descriptionCn": [], "nameFr": "", "descriptionFr": [], - "nameRu": "", - "descriptionRu": [], + "nameRu": "Создайте таймер Pomodoro", + "descriptionRu": [ + "Задание: Создайте CodePen.io который успешно копирует вот этот: http://codepen.io/GeoffStorbeck/full/RPbGxZ/.", + "Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.", + "Правило #2: Можете использовать любые библиотеки или API, которые потребуются.", + "Правило #3: Воссоздайте функционал приведенного примера и не стесняйтесь добавить что-нибудь от себя.", + "Реализуйте следующие пользовательские истории, сделайте также бонусные по желанию:", + "Пользовательская история: В качестве пользователя, я могу запустить 25 минутную 'помидорку', по истечении которой таймер выключится.", + "Бонусная пользовательская история: В качестве пользователя, я могу сбросить таймер для установки следующей 'помидорки'.", + "Бонусная пользовательская история: В качестве пользователя, я могу выбирать длительность 'помидорки'.", + "Если что-то не получается, воспользуйтесь Read-Search-Ask.", + "Когда выполните задание кликните кнопку \"I've completed this challenge\" и добавьте ссылку на ваш CodePen. Если вы программировали с кем-то в паре, также добавьте имя вашего напарника.", + "Если вы хотите получить немедленную оценку вашего проекта, нажмите эту кнопку и добавьте ссылку на ваш CodePen. В противном случае мы проверим его перед тем как вы приступите к проектам для некоммерческих организаций.

Click here then add your link to your tweet's text" + ], "nameEs": "", "descriptionEs": [], "namePt": "", "descriptionPt": [] }, { - "id": "bd7158d8c442eddfaeb5bd1f", - "title": "Use the Twitch.tv JSON API", - "difficulty": 1.05, - "challengeSeed": ["126411564"], + "id": "bd7158d8c442eddfaeb5bd17", + "title": "Build a JavaScript Calculator", + "challengeSeed": ["126411565"], "description": [ - "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/GeoffStorbeck/full/GJKRxZ.", + "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/GeoffStorbeck/full/zxgaqw.", "Rule #1: Don't look at the example project's code on CodePen. Figure it out for yourself.", "Rule #2: You may use whichever libraries or APIs you need.", "Rule #3: Reverse engineer the example project's functionality, and also feel free to personalize it.", "Here are the user stories you must enable, and optional bonus user stories:", - "User Story: As a user, I can see whether Free Code Camp is currently streaming on Twitch.tv.", - "User Story: As a user, I can click the status output and be sent directly to the Free Code Camp's Twitch.tv channel.", - "User Story: As a user, if Free Code Camp is streaming, I can see additional details about what they are streaming.", - "Bonus User Story: As a user, I can search through the streams listed.", - "Bonus User Story: As a user, I will see a placeholder notification if a streamer has closed their Twitch account. You can verify this works by adding brunofin and comster404 to your array of Twitch streamers.", - "Hint: Here's an example call to Twitch.tv's JSON API: https://api.twitch.tv/kraken/streams/freecodecamp.", - "Hint: The relevant documentation about this API call is here: https://github.com/justintv/Twitch-API/blob/master/v3_resources/streams.md#get-streamschannel.", - "Hint: Here's an array of the Twitch.tv usernames of people who regularly stream coding: [\"freecodecamp\", \"storbeck\", \"terakilobyte\", \"habathcx\",\"RobotCaleb\",\"thomasballinger\",\"noobs2ninjas\",\"beohoff\"]", + "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.", "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, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.

Click here then add your link to your tweet's text" + "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" ], "type": "zipline", "challengeType": 3, diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 58fc65c721..d405871390 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -1,11 +1,10 @@ { "name": "Responsive Design with Bootstrap", - "order": 0.003, + "order": 3, "challenges": [ { "id": "bad87fee1348bd9acde08712", "title": "Use Responsive Design with Bootstrap Fluid Containers", - "difficulty": 2.01, "description": [ "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.", @@ -89,7 +88,6 @@ { "id": "bad87fee1348bd9acde08812", "title": "Make Images Mobile Responsive", - "difficulty": 2.02, "description": [ "First, add a new image below the existing one. Set it's 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.", @@ -174,7 +172,6 @@ { "id": "bad87fee1348bd8acde08812", "title": "Center Text with Bootstrap", - "difficulty": 2.03, "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>." @@ -257,7 +254,6 @@ { "id": "bad87fee1348cd8acdf08812", "title": "Create a Bootstrap Button", - "difficulty": 2.04, "description": [ "Bootstrap has its own styles for button elements, which look much better than the plain HTML ones.", "Create a new button element below your large kitten photo. Give it the class btn and the text of \"Like\"." @@ -343,11 +339,10 @@ { "id": "bad87fee1348cd8acef08812", "title": "Create a Block Element Bootstrap Button", - "difficulty": 2.05, "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.", "This image illustrates the difference between inline elements and block-level elements:", - "\"An", + "\"An", "Note that these buttons still need the btn class.", "Add Bootstrap's btn-block class to your Bootstrap button." ], @@ -432,7 +427,6 @@ { "id": "bad87fee1348cd8acef08811", "title": "Taste the Bootstrap Button Color Rainbow", - "difficulty": 2.06, "description": [ "The btn-primary class is the main color you'll use in your app. It is useful for highlighting actions you want your user to take.", "Add Bootstrap's btn-primary class to your button.", @@ -519,7 +513,6 @@ { "id": "bad87fee1348cd8acef08813", "title": "Call out Optional Actions with Button Info", - "difficulty": 2.07, "description": [ "Bootstrap comes with several pre-defined colors for buttons. The btn-info class is used to call attention to optional actions that the user can take.", "Create a new block-level Bootstrap button below your \"Like\" button with the text \"Info\", and add Bootstrap's btn-info and btn-block classes to it.", @@ -607,7 +600,6 @@ { "id": "bad87fee1348ce8acef08814", "title": "Warn your Users of a Dangerous Action", - "difficulty": 2.08, "description": [ "Bootstrap comes with several pre-defined colors for buttons. The btn-danger class is the button color you'll use to notify users that the button performs a destructive action, such as deleting a cat photo.", "Create a button with the text \"Delete\" and give it the class btn-danger.", @@ -696,11 +688,10 @@ { "id": "bad88fee1348ce8acef08815", "title": "Use the Bootstrap Grid to Put Elements Side By Side", - "difficulty": 2.09, "description": [ "Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a div element.", "Here's a diagram of how Bootstrap's 12-column grid layout works:", - "\"an", + "\"an", "Note that in this illustration, the col-md-* class is being used. Here, md means medium, and * is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.", "In the Cat Photo App that we're building, we'll use col-xs-*, where xs means extra small (like an extra-small mobile phone screen), and * is the number of columns specifying how many columns wide the element should be.", "Put the Like, Info and Delete buttons side-by-side by nesting all three of them within one <div class=\"row\"> element, then each of them within a <div class=\"col-xs-4\"> element.", @@ -790,7 +781,6 @@ { "id": "bad87fee1347bd9aedf08845", "title": "Ditch Custom CSS for Bootstrap", - "difficulty": 2.10, "description": [ "We can clean up our code and make our Cat Photo App look more conventional by using Bootstrap's built-in styles instead of the custom styles we created earlier.", "Don't worry - there will be plenty of time to customize our CSS later.", @@ -891,11 +881,10 @@ { "id": "bad87fee1348bd9aedf08845", "title": "Use Spans for Inline Elements", - "difficulty": 2.105, "description": [ "You can use spans to create inline elements. Remember when we used the btn-block class to make the button fill the entire row?", "This image illustrates the difference between inline elements and block-level elements:", - "\"An", + "\"An", "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>" @@ -909,19 +898,11 @@ "challengeSeed": [ "", "", "", "
", - "

CatPhotoApp

", + "

CatPhotoApp

", "", - "

Click here for cat photos.

", - "", - " ", + " ", "", " ", "
", @@ -992,12 +968,11 @@ { "id": "bad87fee1348bd9aede08845", "title": "Create a Custom Heading", - "difficulty": 2.11, "description": [ "We will make a simple heading for our Cat Photo App by putting them in the same row.", "Remember, Bootstrap uses a responsive grid system, which makes it easy to put elements into rows and specify each element's relative width. Most of Bootstrap's classes can be applied to a div element.", "Here's a diagram of how Bootstrap's 12-column grid layout works:", - "\"an", + "\"an", "Note that in this illustration, the col-md-* class is being used. Here, md means medium, and * is a number specifying how many columns wide the element should be. In this case, the column width of an element on a medium-sized screen, such as a laptop, is being specified.", "In the Cat Photo App that we're building, we'll use col-xs-*, where xs means extra small (like an extra-small mobile phone screen), and * is the number of columns specifying how many columns wide the element should be.", "Nest your first image and your h2 element within a single <div class=\"row\"> element. Nest your h2 text within a <div class=\"col-xs-8\"> and your image in a <div class=\"col-xs-4\"> so that they are on the same line.", @@ -1081,7 +1056,6 @@ { "id": "bad87fee1348bd9aedd08845", "title": "Add Font Awesome Icons to our Buttons", - "difficulty": 2.12, "description": [ "Font Awesome is a convenient library of icons. These icons are vector graphics, stored in the .svg file format. These icons are treated just like fonts. You can specify their size using pixels, and they will assume the font size of their parent HTML elements.", "Use Font Awesome to add a thumbs-up icon to your like button by giving it a i element with the classes fa and fa-thumbs-up." @@ -1166,15 +1140,14 @@ { "id": "bad87fee1348bd9aedc08845", "title": "Add Font Awesome Icons to all of our Buttons", - "difficulty": 2.13, "description": [ "Font Awesome is a convenient library of icons. These icons are vector graphics, stored in the .svg file format. These icons are treated just like fonts. You can specify their size using pixels, and they will assume the font size of their parent HTML elements.", "Use Font Awesome to add a info-circle icon to your info button and a trash icon to your delete button." ], "tests": [ - "assert($(\"i\").hasClass(\"fa fa-trash\"), 'You should add a <i class=\"fa fa-trash\"></i> within your delete button element.')", - "assert($(\"i\").hasClass(\"fa fa-info-circle\"), 'You should add a <i class=\"fa fa-info-circle\"></i> within your info button element.')", - "assert(editor.match(/<\\/i>/g) && editor.match(/<\\/i/g).length > 2, 'Make sure each of your i elements has a closing tag.')" + "assert($(\".btn-danger > i\").hasClass(\"fa fa-trash\"), 'You should add a <i class=\"fa fa-trash\"></i> within your delete button element.')", + "assert($(\".btn-info > i\").hasClass(\"fa fa-info-circle\"), 'You should add a <i class=\"fa fa-info-circle\"></i> within your info button element.')", + "assert(editor.match(/<\\/i>/g) && editor.match(/<\\/i>/g).length > 2 && $(\".btn-primary > i\").hasClass(\"fa fa-thumbs-up\"), 'Make sure each of your i elements has a closing tag and <i class=\"fa fa-thumbs-up\"></i> is in your like button element.')" ], "challengeSeed": [ "", @@ -1251,7 +1224,6 @@ { "id": "bad87fee1348bd9aedb08845", "title": "Responsively Style Radio Buttons", - "difficulty": 2.14, "description": [ "You can use Bootstrap's col-xs-* classes on form elements, too! This way, our radio buttons will be evenly spread out across the page, regardless of how wide the screen resolution is.", "Nest all of your radio buttons within a <div class=\"row\"> element. Then nest each of them within a <div class=\"col-xs-6\"> element." @@ -1336,7 +1308,6 @@ { "id": "bad87fee1348bd9aeda08845", "title": "Responsively Style Checkboxes", - "difficulty": 2.15, "description": [ "You can use Bootstrap's col-xs-* classes on form elements, too! This way, our checkboxes will be evenly spread out across the page, regardless of how wide the screen resolution is.", "Nest all your checkboxes in a <div class=\"row\"> element. Then nest each of them in a <div class=\"col-xs-4\"> element." @@ -1428,7 +1399,6 @@ { "id": "bad87fee1348bd9aed908845", "title": "Style Text Inputs as Form Controls", - "difficulty": 2.16, "description": [ "You can add the fa-paper-plane Font Awesome icon by adding <i class=\"fa fa-paper-plane\"></i> within your submit button element.", "Give your form's text input field a class of form-control. Give your form's submit button the classes btn btn-primary. Also give this button the Font Awesome icon of fa-paper-plane." @@ -1529,7 +1499,6 @@ { "id": "bad87fee1348bd9aec908845", "title": "Line up Form Elements Responsively with Bootstrap", - "difficulty": 2.17, "description": [ "Now let's get your form input and your submission button on the same line. We'll do this the same way we have previously: by using a div element with the class row, and other div elements within it using the col-xs-* class.", "Nest both your form's text input and submit button within a div with the class row. Nest your form's text input within a div with the class of col-xs-7. Nest your form's submit button in a div with the class col-xs-5.", @@ -1631,7 +1600,6 @@ { "id": "bad87fee1348bd9aec908846", "title": "Create a Bootstrap Headline", - "difficulty": 2.18, "description": [ "Now let's build something from scratch to practice our HTML, CSS and Bootstrap skills.", "We'll build a jQuery playground, which we'll soon put to use in our jQuery challenges.", @@ -1666,7 +1634,6 @@ { "id": "bad87fee1348bd9aec908746", "title": "House our page within a Bootstrap Container Fluid Div", - "difficulty": 2.18, "description": [ "Now let's make sure all the content on your page is mobile-responsive.", "Let's nest your h3 element within a div element with the class container-fluid." @@ -1696,7 +1663,6 @@ { "id": "bad87fee1348bd9bec908846", "title": "Create a Bootstrap Row", - "difficulty": 2.19, "description": [ "Now we'll create a Bootstrap row for our inline elements.", "Create a div element with the class row." @@ -1729,13 +1695,12 @@ { "id": "bad87fee1348bd9aec908847", "title": "Split your Bootstrap Row", - "difficulty": 2.20, "description": [ "Now that we have a Bootstrap Row, let's split it into two columns to house our elements.", "Create two div elements within your row, both with the class col-xs-6." ], "tests": [ - "assert($(\"div.row\").children(\"div.col-xs-6\").length > 1, 'Nest two div class=\"col-xs-6\" elements within your div class=\"row\" element.')", + "assert($(\"div.row > div.col-xs-6\").length > 1, 'Nest two div class=\"col-xs-6\" elements within your div class=\"row\" element.')", "assert(editor.match(/<\\/div>/g) && editor.match(/
/g).length === editor.match(/
div elements have closing tags.')" ], "challengeSeed": [ @@ -1763,14 +1728,13 @@ { "id": "bad87fee1348bd9aec908848", "title": "Create Bootstrap Wells", - "difficulty": 2.21, "description": [ "Bootstrap has a class called well that can create a visual sense of depth for your columns.", "Nest one div element with the class well within each of your col-xs-6 div elements." ], "tests": [ - "assert($(\"div\").length > 4, 'Add a div element with the class well inside each of your div class=\"col-xs-6\" elements')", - "assert($(\"div.col-xs-6 div.well\").length > 1, 'Nest both of your div class=\"col-xs-6\" elements within your div class=\"row\" element.')", + "assert($(\"div.col-xs-6\").not(\":has(>div.well)\").length < 1, 'Add a div element with the class well inside each of your div elements with the class \"col-xs-6\"')", + "assert($(\"div.row > div.col-xs-6\").length > 1, 'Nest both of your div elements with the class \"col-xs-6\" within your div element with the class \"row\".')", "assert(editor.match(/<\\/div>/g) && editor.match(/
/g).length === editor.match(/
div elements have closing tags.')" ], "challengeSeed": [ @@ -1802,7 +1766,6 @@ { "id": "bad87fee1348bd9aec908849", "title": "Add Elements within your Bootstrap Wells", - "difficulty": 2.22, "description": [ "Now we're several div elements deep on each column of our row. This is as deep as we'll need to go. Now we can add our button elements.", "Nest three button elements within each of your well div elements." @@ -1849,7 +1812,6 @@ { "id": "bad87fee1348bd9aec908850", "title": "Apply the Default Bootstrap Button Style", - "difficulty": 2.23, "description": [ "Bootstrap has another button class called btn-default.", "Apply both the btn and btn-default classes to each of your button elements." @@ -1895,7 +1857,6 @@ { "id": "bad87fee1348bd9aec908852", "title": "Create a Class to Target with jQuery Selectors", - "difficulty": 2.24, "description": [ "Not every class needs to have corresponding CSS. Sometimes we create classes just for the purpose of selecting these elements more easily using jQuery.", "Give each of your button elements the class target." @@ -1940,7 +1901,6 @@ { "id": "bad87fee1348bd9aec908853", "title": "Add ID Attributes to Bootstrap Elements", - "difficulty": 2.25, "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.", @@ -1949,8 +1909,8 @@ "Give the well on the left the id of left-well. Give the well on the right the id of right-well." ], "tests": [ - "assert($(\"#left-well\") && $(\"#left-well\").length > 0, 'Give your left well the id of left-well.')", - "assert($(\"#right-well\") && $(\"#right-well\").length > 0, 'Give your right well the id of right-well.')" + "assert($(\".col-xs-6\").children(\"#left-well\") && $(\".col-xs-6\").children(\"#left-well\").length > 0, 'Give your left well the id of left-well.')", + "assert($(\".col-xs-6\").children(\"#right-well\") && $(\".col-xs-6\").children(\"#right-well\").length > 0, 'Give your right well the id of right-well.')" ], "challengeSeed": [ "
", @@ -1989,14 +1949,13 @@ { "id": "bad87fee1348bd9aec908854", "title": "Label Bootstrap Wells", - "difficulty": 2.26, "description": [ "For the sake of clarity, let's label both of our wells with their ids.", "Above your left-well, inside its col-xs-6 div element, add a h4 element with the text #left-well.", "Above your right-well, inside its col-xs-6 div element, add a h4 element with the text #right-well." ], "tests": [ - "assert($(\".col-xs-6\").children(\"h4\") && $(\".col-xs-6\").children(\"h4\").length > 1, 'Add an h4 element to each of your <div class=\\\"col-xs-6\\\"> elements.');", + "assert($(\".col-xs-6\").children(\"h4\") && $(\".col-xs-6\").children(\"h4\").length > 1, 'Add an h4 element to each of your <div class=\"col-xs-6\"> elements.');", "assert(new RegExp(\"#left-well\",\"gi\").test($(\"h4\").text()), 'One h4 element should have the text #left-well.');", "assert(new RegExp(\"#right-well\",\"gi\").test($(\"h4\").text()), 'One h4 element should have the text #right-well.');", "assert(editor.match(/<\\/h4>/g) && editor.match(/

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

h4 elements have closing tags.')" @@ -2040,18 +1999,17 @@ { "id": "bad87fee1348bd9aec908855", "title": "Give Each Element a Unique ID", - "difficulty": 2.27, "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 like, starting with target1 and ending with target6." ], "tests": [ - "assert($(\"#target1\") && $(\"#target1\").length > 0, 'One button element should have the id target1.')", - "assert($(\"#target2\") && $(\"#target2\").length > 0, 'One button element should have the id target2.')", - "assert($(\"#target3\") && $(\"#target3\").length > 0, 'One button element should have the id target3.')", - "assert($(\"#target4\") && $(\"#target4\").length > 0, 'One button element should have the id target4.')", - "assert($(\"#target5\") && $(\"#target5\").length > 0, 'One button element should have the id target5.')", - "assert($(\"#target6\") && $(\"#target6\").length > 0, 'One button element should have the id target6.')" + "assert($(\"#left-well\").children(\"#target1\") && $(\"#left-well\").children(\"#target1\").length > 0, 'One button element should have the id target1.')", + "assert($(\"#left-well\").children(\"#target2\") && $(\"#left-well\").children(\"#target2\").length > 0, 'One button element should have the id target2.')", + "assert($(\"#left-well\").children(\"#target3\") && $(\"#left-well\").children(\"#target3\").length > 0, 'One button element should have the id target3.')", + "assert($(\"#right-well\").children(\"#target4\") && $(\"#right-well\").children(\"#target4\").length > 0, 'One button element should have the id target4.')", + "assert($(\"#right-well\").children(\"#target5\") && $(\"#right-well\").children(\"#target5\").length > 0, 'One button element should have the id target5.')", + "assert($(\"#right-well\").children(\"#target6\") && $(\"#right-well\").children(\"#target6\").length > 0, 'One button element should have the id target6.')" ], "challengeSeed": [ "
", @@ -2092,7 +2050,6 @@ { "id": "bad87fee1348bd9aec908856", "title": "Label Bootstrap Buttons", - "difficulty": 2.28, "description": [ "Just like we labeled our wells, we want to label our buttons.", "Give each of your button elements text that corresponds to their id." @@ -2144,7 +2101,6 @@ { "id": "bad87fee1348bd9aec908857", "title": "Use Comments to Clarify Code", - "difficulty": 2.29, "description": [ "When we start using jQuery, we will modify HTML elements without needing to actually change them in HTML.", "Let's make sure that everyone knows they shouldn't actually modify any of this code directly.", diff --git a/seed/challenges/front-end-development-certificate.json b/seed/challenges/front-end-development-certificate.json new file mode 100644 index 0000000000..fa0bb1ce40 --- /dev/null +++ b/seed/challenges/front-end-development-certificate.json @@ -0,0 +1,226 @@ +{ + "name": "Claim Your Front End Development Certificate", + "order": 12, + "challenges": [ + { + "id": "561add10cb82ac38a17513be", + "title": "Claim Your Front End Development Certificate", + "difficulty": 0.00, + "challengeSeed": [], + "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.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [ + { + "id": "ad7123c8c441eddfaeb5bdef", + "title": "Meet Bonfire" + }, + { + "id": "a202eed8fc186c8434cb6d61", + "title": "Reverse a String" + }, + { + "id": "a302f7aae1aa3152a5b413bc", + "title": "Factorialize a Number" + }, + { + "id": "aaa48de84e1ecc7c742e1124", + "title": "Check for Palindromes" + }, + { + "id": "a26cbbe9ad8655a977e1ceb5", + "title": "Find the Longest Word in a String" + }, + { + "id": "ab6137d4e35944e21037b769", + "title": "Title Case a Sentence" + }, + { + "id": "a789b3483989747d63b0e427", + "title": "Return Largest Numbers in Arrays" + }, + { + "id": "acda2fb1324d9b0fa741e6b5", + "title": "Confirm the Ending" + }, + { + "id": "afcc8d540bea9ea2669306b6", + "title": "Repeat a string repeat a string" + }, + { + "id": "ac6993d51946422351508a41", + "title": "Truncate a string" + }, + { + "id": "a9bd25c716030ec90084d8a1", + "title": "Chunky Monkey" + }, + { + "id": "ab31c21b530c0dafa9e241ee", + "title": "Slasher Flick" + }, + { + "id": "af2170cad53daa0770fabdea", + "title": "Mutations" + }, + { + "id": "adf08ec01beb4f99fc7a68f2", + "title": "Falsy Bouncer" + }, + { + "id": "a39963a4c10bc8b4d4f06d7e", + "title": "Seek and Destroy" + }, + { + "id": "a24c1a4622e3c05097f71d67", + "title": "Where do I belong" + }, + { + "id": "a3566b1109230028080c9345", + "title": "Sum All Numbers in a Range" + }, + { + "id": "a5de63ebea8dbee56860f4f2", + "title": "Diff Two Arrays" + }, + { + "id": "a7f4d8f2483413a6ce226cac", + "title": "Roman Numeral Converter" + }, + { + "id": "a8e512fbe388ac2f9198f0fa", + "title": "Where art thou" + }, + { + "id": "a0b5010f579e69b815e7c5d6", + "title": "Search and Replace" + }, + { + "id": "aa7697ea2477d1316795783b", + "title": "Pig Latin" + }, + { + "id": "afd15382cdfb22c9efe8b7de", + "title": "DNA Pairing" + }, + { + "id": "af7588ade1100bde429baf20", + "title": "Missing letters" + }, + { + "id": "a77dbc43c33f39daa4429b4f", + "title": "Boo who" + }, + { + "id": "a105e963526e7de52b219be9", + "title": "Sorted Union" + }, + { + "id": "a6b0bb188d873cb2c8729495", + "title": "Convert HTML Entities" + }, + { + "id": "a103376db3ba46b2d50db289", + "title": "Spinal Tap Case" + }, + { + "id": "a5229172f011153519423690", + "title": "Sum All Odd Fibonacci Numbers" + }, + { + "id": "a3bfc1673c0526e06d3ac698", + "title": "Sum All Primes" + }, + { + "id": "ae9defd7acaf69703ab432ea", + "title": "Smallest Common Multiple" + }, + { + "id": "a6e40f1041b06c996f7b2406", + "title": "Finders Keepers" + }, + { + "id": "a5deed1811a43193f9f1c841", + "title": "Drop it" + }, + { + "id": "ab306dbdcc907c7ddfc30830", + "title": "Steamroller" + }, + { + "id": "a8d97bd4c764e91f9d2bda01", + "title": "Binary Agents" + }, + { + "id": "a10d2431ad0c6a099a4b8b52", + "title": "Everything Be True" + }, + { + "id": "a97fd23d9b809dac9921074f", + "title": "Arguments Optional" + }, + { + "id": "bd7158d8c442eddfbeb5bd1f", + "title": "Get Set for Ziplines" + }, + { + "id": "bd7158d8c242eddfaeb5bd13", + "title": "Build a Personal Portfolio Webpage" + }, + { + "id": "bd7158d8c442eddfaeb5bd13", + "title": "Build a Random Quote Machine" + }, + { + "id": "bd7158d8c442eddfaeb5bd0f", + "title": "Build a Pomodoro Clock" + }, + { + "id": "bd7158d8c442eddfaeb5bd17", + "title": "Build a JavaScript Calculator" + }, + { + "id": "bd7158d8c442eddfaeb5bd10", + "title": "Show the Local Weather" + }, + { + "id": "bd7158d8c442eddfaeb5bd1f", + "title": "Use the Twitch.tv JSON API" + }, + { + "id": "bd7158d8c442eddfaeb5bd18", + "title": "Stylize Stories on Camper News" + }, + { + "id": "bd7158d8c442eddfaeb5bd19", + "title": "Build a Wikipedia Viewer" + }, + { + "id": "bd7158d8c442eedfaeb5bd1c", + "title": "Build a Tic Tac Toe Game" + }, + { + "id": "bd7158d8c442eddfaeb5bd1c", + "title": "Build a Simon Game" + } + ], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + } + ] +} diff --git a/seed/challenges/full-stack-development-certificate.json b/seed/challenges/full-stack-development-certificate.json new file mode 100644 index 0000000000..901883aebc --- /dev/null +++ b/seed/challenges/full-stack-development-certificate.json @@ -0,0 +1,286 @@ +{ + "name": "Claim Your Full Stack Development Certificate", + "order": 21, + "challenges": [ + { + "id": "660add10cb82ac38a17513be", + "title": "Claim Your Full Stack Development Certificate", + "difficulty": 0.00, + "challengeSeed": [], + "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.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [ + { + "id": "ad7123c8c441eddfaeb5bdef", + "title": "Meet Bonfire" + }, + { + "id": "a202eed8fc186c8434cb6d61", + "title": "Reverse a String" + }, + { + "id": "a302f7aae1aa3152a5b413bc", + "title": "Factorialize a Number" + }, + { + "id": "aaa48de84e1ecc7c742e1124", + "title": "Check for Palindromes" + }, + { + "id": "a26cbbe9ad8655a977e1ceb5", + "title": "Find the Longest Word in a String" + }, + { + "id": "ab6137d4e35944e21037b769", + "title": "Title Case a Sentence" + }, + { + "id": "a789b3483989747d63b0e427", + "title": "Return Largest Numbers in Arrays" + }, + { + "id": "acda2fb1324d9b0fa741e6b5", + "title": "Confirm the Ending" + }, + { + "id": "afcc8d540bea9ea2669306b6", + "title": "Repeat a string repeat a string" + }, + { + "id": "ac6993d51946422351508a41", + "title": "Truncate a string" + }, + { + "id": "a9bd25c716030ec90084d8a1", + "title": "Chunky Monkey" + }, + { + "id": "ab31c21b530c0dafa9e241ee", + "title": "Slasher Flick" + }, + { + "id": "af2170cad53daa0770fabdea", + "title": "Mutations" + }, + { + "id": "adf08ec01beb4f99fc7a68f2", + "title": "Falsy Bouncer" + }, + { + "id": "a39963a4c10bc8b4d4f06d7e", + "title": "Seek and Destroy" + }, + { + "id": "a24c1a4622e3c05097f71d67", + "title": "Where do I belong" + }, + { + "id": "a3566b1109230028080c9345", + "title": "Sum All Numbers in a Range" + }, + { + "id": "a5de63ebea8dbee56860f4f2", + "title": "Diff Two Arrays" + }, + { + "id": "a7f4d8f2483413a6ce226cac", + "title": "Roman Numeral Converter" + }, + { + "id": "a8e512fbe388ac2f9198f0fa", + "title": "Where art thou" + }, + { + "id": "a0b5010f579e69b815e7c5d6", + "title": "Search and Replace" + }, + { + "id": "aa7697ea2477d1316795783b", + "title": "Pig Latin" + }, + { + "id": "afd15382cdfb22c9efe8b7de", + "title": "DNA Pairing" + }, + { + "id": "af7588ade1100bde429baf20", + "title": "Missing letters" + }, + { + "id": "a77dbc43c33f39daa4429b4f", + "title": "Boo who" + }, + { + "id": "a105e963526e7de52b219be9", + "title": "Sorted Union" + }, + { + "id": "a6b0bb188d873cb2c8729495", + "title": "Convert HTML Entities" + }, + { + "id": "a103376db3ba46b2d50db289", + "title": "Spinal Tap Case" + }, + { + "id": "a5229172f011153519423690", + "title": "Sum All Odd Fibonacci Numbers" + }, + { + "id": "a3bfc1673c0526e06d3ac698", + "title": "Sum All Primes" + }, + { + "id": "ae9defd7acaf69703ab432ea", + "title": "Smallest Common Multiple" + }, + { + "id": "a6e40f1041b06c996f7b2406", + "title": "Finders Keepers" + }, + { + "id": "a5deed1811a43193f9f1c841", + "title": "Drop it" + }, + { + "id": "ab306dbdcc907c7ddfc30830", + "title": "Steamroller" + }, + { + "id": "a8d97bd4c764e91f9d2bda01", + "title": "Binary Agents" + }, + { + "id": "a10d2431ad0c6a099a4b8b52", + "title": "Everything Be True" + }, + { + "id": "a97fd23d9b809dac9921074f", + "title": "Arguments Optional" + }, + { + "id": "bd7158d8c442eddfbeb5bd1f", + "title": "Get Set for Ziplines" + }, + { + "id": "bd7158d8c242eddfaeb5bd13", + "title": "Build a Personal Portfolio Webpage" + }, + { + "id": "bd7158d8c442eddfaeb5bd13", + "title": "Build a Random Quote Machine" + }, + { + "id": "bd7158d8c442eddfaeb5bd0f", + "title": "Build a Pomodoro Clock" + }, + { + "id": "bd7158d8c442eddfaeb5bd17", + "title": "Build a JavaScript Calculator" + }, + { + "id": "bd7158d8c442eddfaeb5bd10", + "title": "Show the Local Weather" + }, + { + "id": "bd7158d8c442eddfaeb5bd1f", + "title": "Use the Twitch.tv JSON API" + }, + { + "id": "bd7158d8c442eddfaeb5bd18", + "title": "Stylize Stories on Camper News" + }, + { + "id": "bd7158d8c442eddfaeb5bd19", + "title": "Build a Wikipedia Viewer" + }, + { + "id": "bd7158d8c442eedfaeb5bd1c", + "title": "Build a Tic Tac Toe Game" + }, + { + "id": "bd7158d8c442eddfaeb5bd1c", + "title": "Build a Simon Game" + }, + { + "id": "a2f1d72d9b908d0bd72bb9f6", + "title": "Make a Person" + }, + { + "id": "af4afb223120f7348cdfc9fd", + "title": "Map the Debris" + }, + { + "id": "a3f503de51cfab748ff001aa", + "title": "Pairwise" + }, + { + "id": "aff0395860f5d3034dc0bfc9", + "title": "Validate US Telephone Numbers" + }, + { + "id": "a3f503de51cf954ede28891d", + "title": "Symmetric Difference" + }, + { + "id": "aa2e6f85cab2ab736c9a9b24", + "title": "Exact Change" + }, + { + "id": "a56138aff60341a09ed6c480", + "title": "Inventory Update" + }, + { + "id": "a7bf700cd123b9a54eef01d5", + "title": "No repeats please" + }, + { + "id": "a19f0fbe1872186acd434d5a", + "title": "Friendly Date Ranges" + }, + { + "id": "bd7158d8c443eddfaeb5bcef", + "title": "Get Set for Basejumps" + }, + { + "id": "bd7158d8c443eddfaeb5bdef", + "title": "Build a Voting App" + }, + { + "id": "bd7158d8c443eddfaeb5bdff", + "title": "Build a Nightlife Coordination App" + }, + { + "id": "bd7158d8c443eddfaeb5bd0e", + "title": "Chart the Stock Market" + }, + { + "id": "bd7158d8c443eddfaeb5bd0f", + "title": "Manage a Book Trading Club" + }, + { + "id": "bd7158d8c443eddfaeb5bdee", + "title": "Build a Pinterest Clone" + } + ], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + } + ] +} diff --git a/seed/challenges/getting-started.json b/seed/challenges/getting-started.json new file mode 100644 index 0000000000..c35c3541f8 --- /dev/null +++ b/seed/challenges/getting-started.json @@ -0,0 +1,314 @@ +{ + "name": "Get Started with Free Code Camp", + "order": 1, + "challenges": [ + { + "id": "560add10cb82ac38a17513be", + "title": "Learn how Free Code Camp Works", + "challengeSeed": [], + "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/pYsTbjI.jpg", + "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.", + "" + ], + [ + "http://i.imgur.com/D7Y5luw.jpg", + "A graph of the rate of job growth against growth in computer science degree graduates. There are 1.4 million jobs and only 400 million people to fill them.", + "There are thousands of coding jobs currently going unfilled, and the demand for coders grows every year. If you want a coding job, we can help prepare you to get one.", + "" + ], + [ + "http://i.imgur.com/dLx8nrg.jpg", + "An illustration showing that you will learn HTML5, CSS3, JavaScript, Databases, Git, Node.js, Angular.js and Agile.", + "First you'll work through our rigorous 800-hour curriculum learn technologies like HTML5, Node.js and databases. It's self-paced and 100% free.", + "" + ], + [ + "http://i.imgur.com/q4IjuCL.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.", + "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.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "560add37cb82ac38a17513bf", + "title": "Create a GitHub Account and Join our Chat Rooms", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/EAR7Lvh.jpg", + "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.", + "" + ], + [ + "http://i.imgur.com/FEImaEN.gif", + "A gif showing you to click the link below to go to GitHub. Fill in the necessary fields and click submit.", + "Create an account with GitHub. Be sure to use your real email address - GitHub will keep this private.", + "https://github.com/join" + ], + [ + "http://i.imgur.com/ALN6zPK.gif", + "A gif showing you how to click the profile image in the upper right hand corner of GitHub. Upload a photo of yourself or you will continue to use the automatically generated pixel art. Then fill in the remaining form fields and click submit.", + "Click the pixel art in the upper right hand corner of GitHub, then choose settings. Upload a picture of yourself. A picture of your face works best. This is how your fellow campers will see you in our chat rooms, so put your best foot forward. You can add your city and your name if you want.", + "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.", + "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", + "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.", + "https://gitter.im/FreeCodeCamp/FreeCodeCamp" + ], + [ + "http://i.imgur.com/Ecs5XAd.gif", + "A gif showing you how you can click the settings button in the upper right hand corner and modify your notification settings.", + "Our chat rooms are extremely active. You should change your settings so you're only notified if someone mentions you.", + "" + ], + [ + "http://i.imgur.com/T0bGJPe.gif", + "A gif showing how you can click on a user profile image to initiate a private message with that user.", + "Please note that all of our chat rooms are visible to the public. If you need to share sensitive information, such as an email address or phone number, do it in a private message.", + "" + ], + [ + "http://i.imgur.com/vDTMJSh.gif", + "A gif showing that you can tab back and forth between challenges and our chat rooms.", + "Keep our chat room open while you work through our challenges. That way, you can ask for help if you get stuck. You can also socialize with other campers when you feel like taking a break.", + "" + ], + [ + "http://i.imgur.com/SLQ27Gr.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" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "560add56cb82ac38a17513c0", + "title": "Configure your Public Profile", + "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.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + }, + { + "id": "560add71cb82ac38a17513c2", + "title": "Join a Campsite in Your City", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/Elb3dfj.jpg", + "A picture of some of our campers meeting in a local cafe. 3 men and 3 women are sitting around a table with laptops out, and are smiling and coding.", + "Our Campsites help you code with campers in your city. You can coordinate study groups or attend local coding events together.", + "" + ], + [ + "http://i.imgur.com/EZHzKCV.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.", + "https://github.com/FreeCodeCamp/freecodecamp/wiki/List-of-Free-Code-Camp-city-based-Campsites" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "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", + "challengeSeed": [], + "description": [ + [ + "http://i.imgur.com/EWWZBag.jpg", + "An image with the text \"1. Read the error 2. Search Google 3. Ask for help.", + "Let's cover one last thing before you start working through our challenges: how to get help. Any time you get stuck or don't know what to do next: Read-Search-Ask.", + "" + ], + [ + "http://i.imgur.com/99BfAcK.jpg", + "An image showing jQuery documentation", + "First, read the documentation or error message. A key skill that good coders have is the ability to interpret and then follow instructions.", + "" + ], + [ + "http://i.imgur.com/GxvrsFb.gif", + "A gif showing you how to do an advanced Google search. First, we enter the query \"jquery doesn't run when my page loads\". Then we click search tools button and change the \"Any time\" select box to \"within the last year\". Then we click on a result and read through the article and find our answer.", + "If that didn't help, search Google. Good Google queries take a lot of practice. When you search Google, you usually want to include the language or framework you're using. You also want to limit the results to a recent period.", + "" + ], + [ + "http://i.imgur.com/LZYU7p2.gif", + "A gif showing us following the link below to go to the help chat room and ask \"jquery doesn't run when my page loads\".", + "If that didn't help, ask your friends. If you have trouble, you can ask your fellow campers in our help chat room.", + "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.", + "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.", + "" + ] + ], + "type": "Waypoint", + "challengeType": 7, + "tests": [], + "nameCn": "", + "descriptionCn": [], + "nameFr": "", + "descriptionFr": [], + "nameRu": "", + "descriptionRu": [], + "nameEs": "", + "descriptionEs": [], + "namePt": "", + "descriptionPt": [] + } + ] +} diff --git a/seed/challenges/git.json b/seed/challenges/git.json index aa03fb0a18..d3abf068ba 100644 --- a/seed/challenges/git.json +++ b/seed/challenges/git.json @@ -1,21 +1,20 @@ { "name": "Git", - "order" : 0.016, + "order" : 17, "challenges": [ { "id": "bd7353d8c341eddeaeb5bd0f", "title": "Save your Code Revisions Forever with Git", - "difficulty": 0.01, "challengeSeed": ["133316034"], "description": [ "We'll build this Waypoint on Cloud 9, a powerful online code editor with a full Ubuntu Linux workspace, all running in the cloud.", "If you don't already have Cloud 9 account, create one now at http://c9.io.", "Open up http://c9.io and sign in to your account.", - "Click on Create New Workspace at the top right of the c9.io page, then click on the \"Create a new workspace\" popup that appears below it the button after you click on it.", - "Give your workspace a name.", + "Click on the \"+\" icon at the top right of the c9.io page to create a new workspace.", + "Give your workspace a name and an optional description.", "Choose Node.js in the selection area below the name field.", - "Click the Create button. Then click into your new workspace.", - "In the lower right hand corner you should see a terminal window. In this window use the following commands. You don't need to know what these mean at this point.", + "Click the \"Create workspace\" button.", + "Once C9 builds and loads your workspace, you should see a terminal window in the lower right hand corner. In this window use the following commands. You don't need to know what these mean at this point.", "Install git-it with this command: npm install -g git-it", "Now start the tutorial by running git-it.", "Note that you can resize the c9.io's windows by dragging their borders.", diff --git a/seed/challenges/hikes.json b/seed/challenges/hikes.json index e24da79d85..1f5d7f3113 100644 --- a/seed/challenges/hikes.json +++ b/seed/challenges/hikes.json @@ -5,7 +5,6 @@ { "id": "bd7128d8c441eddfbeb5bddf", "title": "Computer Basics 1: The 4 Basic Parts of a Computer", - "difficulty": 9.01, "challengeSeed": [ "132542064" ], @@ -45,7 +44,6 @@ { "id": "bd7127d8c441eddfbeb5bddf", "title": "Computer Basics 2: More Computer Hardware", - "difficulty": 9.02, "challengeSeed": [ "132542458" ], @@ -84,7 +82,6 @@ { "id": "bd7126d8c441eddfbeb5bddf", "title": "Computer Basics 3: Intro to Binary Code", - "difficulty": 9.03, "challengeSeed": [ "132542757" ], @@ -118,7 +115,6 @@ { "id": "bd7125d8c441eddfbeb5bddf", "title": "Computer Basics 4: Decoding a Binary Number", - "difficulty": 9.04, "challengeSeed": [ "132543332" ], @@ -154,7 +150,6 @@ { "id": "bd7124d8c441eddfbeb5bddf", "title": "Computer Basics 5: How To Measure Data Size", - "difficulty": 9.05, "challengeSeed": [ "132543959" ], @@ -193,7 +188,6 @@ { "id": "bd7123d8c441eddfbeb5bddf", "title": "Computer Basics 6: Measuring Data Speed", - "difficulty": 9.06, "challengeSeed": [ "132545171" ], @@ -230,7 +224,6 @@ { "id": "bd7122d8c441eddfbeb5bddf", "title": "Computer Basics 7: Binary Bytes", - "difficulty": 9.07, "challengeSeed": [ "132545417" ], @@ -264,7 +257,6 @@ { "id": "bd7121d8c441eddfbeb5bddf", "title": "Computer Basics 8: Types of Computers", - "difficulty": 9.08, "challengeSeed": [ "132546182" ], @@ -303,7 +295,6 @@ { "id": "bd7120d8c441eddfbeb5bddf", "title": "Computer Basics 9: More on the Motherboard", - "difficulty": 9.09, "challengeSeed": [ "132547285" ], @@ -344,7 +335,6 @@ { "id": "bd712fd8c441eddfbeb5bddf", "title": "Computer Basics 10: Data Networks", - "difficulty": 9.10, "challengeSeed": [ "132547590" ], @@ -384,7 +374,6 @@ { "id": "bd712ed8c441eddfbeb5bddf", "title": "Computer Basics 11: IP Addresses", - "difficulty": 9.11, "challengeSeed": [ "132548071" ], @@ -423,7 +412,6 @@ { "id": "bd712dd8c441eddfbeb5bddf", "title": "Computer Basics 12: How the Internet Works", - "difficulty": 9.12, "challengeSeed": [ "132548579" ], @@ -462,7 +450,6 @@ { "id": "bd712cd8c441eddfbeb5bddf", "title": "Computer Basics 13: Software", - "difficulty": 9.13, "challengeSeed": [ "132548908" ], @@ -495,7 +482,6 @@ { "id": "bd712bd8c441eddfbeb5bddf", "title": "What Do Programmers Do?", - "difficulty": 9.14, "challengeSeed": [ "133166912" ], @@ -532,7 +518,6 @@ { "id": "bd712ad8c441eddfbeb5bddf", "title": "Console and Logging", - "difficulty": 9.15, "challengeSeed": [ "133170880" ], @@ -569,7 +554,6 @@ { "id": "bd7119d8c441eddfbeb5bddf", "title": "Variables In Code", - "difficulty": 9.16, "challengeSeed": [ "133172920" ], @@ -602,7 +586,6 @@ { "id": "bd7029d8c441eddfbeb5bddf", "title": "Source Code", - "difficulty": 9.17, "challengeSeed": [ "133177129" ], @@ -647,7 +630,6 @@ { "id": "bd7129d8b441eddfbeb5bddf", "title": "Routers and Packets", - "difficulty": 9.18, "challengeSeed": [ "133181251" ], @@ -698,7 +680,6 @@ { "id": "bd7129d8a441eddfbeb5bddf", "title": "Hardware: Chips and Moore's Law", - "difficulty": 9.19, "challengeSeed": [ "133182057" ], @@ -735,7 +716,6 @@ { "id": "bd7129d80441eddfbeb5bddf", "title": "Analog vs Digital and File Compression", - "difficulty": 9.20, "challengeSeed": [ "133182587" ], @@ -774,7 +754,6 @@ { "id": "bd7129d89441eddfbeb5bddf", "title": "Computer Security", - "difficulty": 9.21, "challengeSeed": [ "133186284" ], diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 95c185354e..3b2f72853e 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -1,11 +1,10 @@ { "name": "HTML5 and CSS", - "order": 0.002, + "order": 2, "challenges": [ { "id": "bd7123c8c441eddfaeb5bdef", "title": "Say Hello to HTML Elements", - "difficulty": 1.01, "description": [ "Welcome to Free Code Camp's first coding challenge!", "You can edit code in your text editor, which we've embedded into this web page.", @@ -54,8 +53,9 @@ { "id": "bad87fee1348bd9aedf0887a", "title": "Headline with the h2 Element", - "difficulty": 1.02, "description": [ + "Over the next few challenges, we'll build an HTML5 app that will look something like this:", + "\"A", "Add an h2 tag that says \"CatPhotoApp\" to create a second HTML element below your \"Hello World\" h1 element.", "The h2 element you enter will create an h2 element on the website.", "This element tells the browser how to render the text that it contains.", @@ -98,7 +98,6 @@ { "id": "bad87fee1348bd9aedf08801", "title": "Inform with the Paragraph Element", - "difficulty": 1.03, "description": [ "Create a p element below your h2 element, and give it the text \"Hello Paragraph\".", "p elements are the preferred element for normal-sized paragraph text on websites. P is short for \"paragraph\".", @@ -139,7 +138,6 @@ { "id": "bad87fee1348bd9aedf08802", "title": "Uncomment HTML", - "difficulty": 1.04, "description": [ "Uncomment your h1, h2 and p elements.", "Commenting is a way that you can leave comments within your code without affecting the code itself.", @@ -189,7 +187,6 @@ { "id": "bad87fee1348bd9aedf08804", "title": "Comment out HTML", - "difficulty": 1.05, "description": [ "Comment out your h1 element and your p element, but leave your h2 element uncommented.", "Remember that in order to start a comment, you need to use <!-- and to end a comment, you need to use -->.", @@ -236,7 +233,6 @@ { "id": "bad87fee1348bd9aedf08833", "title": "Fill in the Blank with Placeholder Text", - "difficulty": 1.06, "description": [ "Web developers traditionally use lorem ipsum text as placeholder text. It's called lorem ipsum text because those are the first two words of a famous passage by Cicero of Ancient Rome.", "lorem ipsum text has been used as placeholder text by typesetters since the 16th century, and this tradition continues on the web.", @@ -283,7 +279,6 @@ { "id": "bad87fed1348bd9aedf08833", "title": "Delete HTML Elements", - "difficulty": 1.07, "description": [ "Delete your h1 element so we can simplify our view.", "Our phone doesn't have much vertical space.", @@ -327,7 +322,6 @@ { "id": "bad87fee1348bd9aedf08803", "title": "Change the Color of Text", - "difficulty": 1.08, "description": [ "Change your h2 element's style so that its text color is red.", "We can do this by changing the \"style\" of your h2 element.", @@ -369,7 +363,6 @@ { "id": "bad87fee1348bd9aedf08805", "title": "Use CSS Selectors to Style Elements", - "difficulty": 1.09, "description": [ "Delete your h2 element's style attribute and instead create a CSS style element. Add the necessary CSS to turn all h2 elements blue.", "With CSS, there are hundreds of CSS properties that you can use to change the way an element looks on your page.", @@ -383,7 +376,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(/<\\/style>/g) && editor.match(/<\\/style>/g).length === editor.match(/