diff --git a/README.md b/README.md index 9770de3ebc..0029a894e3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Our campers (students) start by working through our free, self-paced, browser-ba 80% of our campers are over 25, and nearly a fifth of our campers are women. -This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Slack](http://freecodecamp.slack.com), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). +This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). [Join our community](http://www.freecodecamp.com/signin)! @@ -27,7 +27,7 @@ Contributing 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 [Slack](http://freecodecamp.slack.com). +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. diff --git a/common/models/user.json b/common/models/user.json index a7caad6227..0254ff8407 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -134,7 +134,8 @@ "type": "string" }, "uncompletedBonfires": { - "type": "array" + "type": "array", + "default": [] }, "completedBonfires": { "type": [ diff --git a/config/secrets.js b/config/secrets.js index 2ea3cdef28..ed369d9766 100644 --- a/config/secrets.js +++ b/config/secrets.js @@ -13,10 +13,6 @@ module.exports = { key: process.env.BLOGGER_KEY }, - slack: { - key: process.env.SLACK_KEY - }, - mandrill: { user: process.env.MANDRILL_USER, password: process.env.MANDRILL_PASSWORD diff --git a/package.json b/package.json index 8213e9ac72..7fe7c38bd8 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "express-session": "~1.9.2", "express-validator": "~2.8.0", "font-awesome": "~4.3.0", - "forcedomain": "~0.4.0", "forever": "~0.14.1", "frameguard": "^0.2.2", "github-api": "~0.7.0", @@ -71,6 +70,7 @@ "node-slack": "0.0.7", "node-uuid": "^1.4.3", "nodemailer": "~1.3.0", + "object.assign": "^3.0.0", "passport-facebook": "^2.0.0", "passport-google-oauth2": "^0.1.6", "passport-linkedin-oauth2": "^1.2.1", diff --git a/pm2Start.js b/pm2Start.js new file mode 100644 index 0000000000..052f3bdbfa --- /dev/null +++ b/pm2Start.js @@ -0,0 +1,12 @@ +var pm2 = require('pm2'); +pm2.connect(function() { + pm2.start({ + name: 'server', + script: 'server/server.js', + exec_mode: 'cluster', + instances: '2', + max_memory_restart: '900M' + }, function(err, apps) { + pm2.disconnect(); + }); +}); diff --git a/public/js/calculator.js b/public/js/calculator.js index 8786b78bea..9cd991a9e7 100644 --- a/public/js/calculator.js +++ b/public/js/calculator.js @@ -154,6 +154,10 @@ $(document).ready(function () { }, 1000); }); + d3.selectAll("#chart").on("click", function () { + change(); + }); + function change() { if ($("body").data("state") === "stacked") { transitionGrouped(); diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.5.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js similarity index 57% rename from public/js/lib/coursewares/coursewaresJSFramework_0.0.5.js rename to public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index 2183c226d7..ae7237ef5d 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.5.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -1,3 +1,7 @@ +$(document).ready(function() { + $('#reset-button').on('click', resetEditor); +}); + var widgets = []; var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { lineNumbers: true, @@ -18,7 +22,7 @@ editor.setSize("100%", "auto"); // Hijack tab key to enter two spaces intead editor.setOption("extraKeys", { Tab: function(cm) { - if (cm.somethingSelected()){ + if (cm.somethingSelected()) { cm.indentSelection("add"); } else { var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); @@ -26,7 +30,7 @@ editor.setOption("extraKeys", { } }, "Shift-Tab": function(cm) { - if (cm.somethingSelected()){ + if (cm.somethingSelected()) { cm.indentSelection("subtract"); } else { var spaces = Array(cm.getOption("indentUnit") + 1).join(" "); @@ -40,12 +44,92 @@ editor.setOption("extraKeys", { }); +/* + Local Storage Update System By Andrew Cay(Resto) + codeStorage: singleton object that contains properties and methods related to + dealing with the localStorage system. + The keys work off of the variable challenge_name to make unique identifiers per bonfire + + Two extra functionalities: + Added anonymous version checking system incase of future updates to the system + Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage +*/ +var codeStorage = { + version: 0.01, + keyVersion:"saveVersion", + keyValue: null,//where the value of the editor is saved + updateWait: 2000,// 2 seconds + updateTimeoutId: null, + eventArray: []//for firing saves +}; +// Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") +codeStorage.hasSaved = function(){ + return ( updateTimeoutId === null ); +}; +codeStorage.onSave = function(func){ + codeStorage.eventArray.push(func); +}; +codeStorage.setSaveKey = function(key){ + codeStorage.keyValue = key + 'Val'; +}; +codeStorage.getEditorValue = function(){ + return ('' + localStorage.getItem(codeStorage.keyValue)); +}; + +codeStorage.isAlive = function() { + var val = this.getEditorValue() + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); +} +codeStorage.updateStorage = function(){ + if(typeof(Storage) !== undefined) { + var value = editor.getValue(); + localStorage.setItem(codeStorage.keyValue, value); + } else { + var debugging = false; + if( debugging ){ + console.log('no web storage'); + } + } + codeStorage.updateTimeoutId = null; + codeStorage.eventArray.forEach(function(func){ + func(); + }); +}; +//Update Version +(function(){ + var savedVersion = localStorage.getItem('saveVersion'); + if( savedVersion === null ){ + localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version + }else{ + if( savedVersion !== codeStorage.version ){ + //Update version + } + } +})(); + + + +///Set everything up one page +/// Update local save when editor has changed +codeStorage.setSaveKey(challenge_Name); +editor.on('keyup', function(){ + window.clearTimeout(codeStorage.updateTimeoutId); + codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); +}); + var attempts = 0; if (attempts) { attempts = 0; } +var resetEditor = function resetEditor() { + editor.setValue(allSeeds); + codeStorage.updateStorage(); + +}; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { lineNumbers: false, @@ -61,7 +145,10 @@ codeOutput.setValue('/**\n' + ' */'); codeOutput.setSize("100%", "100%"); var info = editor.getScrollInfo(); -var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top; +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); @@ -71,20 +158,20 @@ var editorValue; var challengeSeed = challengeSeed || null; var tests = tests || []; + var allSeeds = ''; (function() { challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; + allSeeds += elem + '\n'; }); })(); -editorValue = allSeeds; - +editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; myCodeMirror.setValue(editorValue); -function doLinting () { - editor.operation(function () { +function doLinting() { + editor.operation(function() { for (var i = 0; i < widgets.length; ++i) editor.removeLineWidget(widgets[i]); widgets.length = 0; @@ -106,14 +193,14 @@ function doLinting () { }); }; -$('#submitButton').on('click', function () { +$('#submitButton').on('click', function() { bonfireExecute(); }); function bonfireExecute() { attempts++; - ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); - userTests= null; + ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); + userTests = null; $('#codeOutput').empty(); var userJavaScript = myCodeMirror.getValue(); userJavaScript = removeComments(userJavaScript); @@ -145,16 +232,23 @@ var scrapeTests = function(userJavaScript) { } var counter = 0; - var regex = new RegExp(/(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/); + var regex = new RegExp( + /(expect(\s+)?\(.*\;)|(assert(\s+)?\(.*\;)|(assert\.\w.*\;)|(.*\.should\..*\;)/ + ); var match = regex.exec(userJavaScript); while (match != null) { var replacement = '//' + counter + testSalt; - userJavaScript = userJavaScript.substring(0, match.index) + replacement + userJavaScript.substring(match.index + match[0].length); + userJavaScript = userJavaScript.substring(0, match.index) + replacement + + userJavaScript.substring(match.index + match[0].length); if (!userTests) { - userTests= []; + userTests = []; } - userTests.push({"text": match[0], "line": counter, "err": null}); + userTests.push({ + "text": match[0], + "line": counter, + "err": null + }); counter++; match = regex.exec(userJavaScript); } @@ -176,17 +270,22 @@ var createTestDisplay = function() { if (pushed) { userTests.pop(); } - for (var i = 0; i < userTests.length;i++) { + for (var i = 0; i < userTests.length; i++) { var test = userTests[i]; var testDoc = document.createElement("div"); if (test.err != null) { console.log('Should be displaying bad tests'); $(testDoc) - .html("
" + test.text + "
" + test.err + "
") + .html( + "
" + + test.text + "
" + + test.err + "
") .appendTo($('#testSuite')); } else { $(testDoc) - .html("
" + test.text + "
") + .html( + "
" + + test.text + "
") .appendTo($('#testSuite')); } }; @@ -208,18 +307,21 @@ var runTests = function(err, data) { pushed = false; $('#testSuite').children().remove(); if (err && userTests.length > 0) { - userTests= [{text:"Program Execution Failure", err: "No user tests were run."}]; + userTests = [{ + text: "Program Execution Failure", + err: "No user tests were run." + }]; createTestDisplay(); } else if (userTests) { userTests.push(false); pushed = true; - userTests.forEach(function(chaiTestFromJSON, indexOfTestArray, __testArray){ + userTests.forEach(function(chaiTestFromJSON, indexOfTestArray, + __testArray) { try { if (chaiTestFromJSON) { var output = eval(reassembleTest(chaiTestFromJSON, data)); - debugger; } - } catch(error) { + } catch (error) { allTestsPassed = false; __testArray[indexOfTestArray].err = error.message; } finally { @@ -239,12 +341,12 @@ var runTests = function(err, data) { function showCompletion() { var time = Math.floor(Date.now()) - started; - ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time +', Attempts: ' + attempts); + ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time + + ', Attempts: ' + attempts); var bonfireSolution = myCodeMirror.getValue(); var didCompleteWith = $('#completed-with').val() || null; $.post( - '/completed-bonfire/', - { + '/completed-bonfire/', { challengeInfo: { challengeId: challenge_Id, challengeName: challenge_Name, @@ -252,10 +354,11 @@ function showCompletion() { challengeType: challengeType, solution: bonfireSolution } - }, function(res) { + }, + function(res) { if (res) { $('#complete-courseware-dialog').modal('show'); - $('#complete-courseware-dialog').keydown(function (e) { + $('#complete-courseware-dialog').keydown(function(e) { if (e.ctrlKey && e.keyCode == 13) { $('#next-courseware-button').click(); } diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index 65ec9f170c..472fe7351d 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -16,67 +16,11 @@ $(document).ready(function() { setCSRFToken($('meta[name="csrf-token"]').attr('content')); - $('#i-want-help').on('click', function() { - $('#help-modal').modal('hide'); - var editorValue = editor.getValue(); - var currentLocation = window.location.href; - $.post( - '/get-help', - { - payload: { - code: editorValue, - challenge: currentLocation - } - }, - function(res) { - if (res) { - window.open('https://freecodecamp.slack.com/messages/help/', '_blank') - } - } - ); - }); - - $('#i-want-help-editorless').on('click', function() { - $('#help-editorless-modal').modal('hide'); - var currentLocation = window.location.href; - $.post( - '/get-help', - { - payload: { - challenge: currentLocation - } - }, - function(res) { - if (res) { - window.open('https://freecodecamp.slack.com/messages/help/', '_blank') - } - } - ); - }); - $('#report-issue').on('click', function() { $('#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 screen shots if possible.', '_blank') }); - $('#i-want-to-pair').on('click', function() { - $('#pair-modal').modal('hide'); - var currentLocation = window.location.href; - $.post( - '/get-pair', - { - payload: { - challenge: currentLocation - } - }, - function(res) { - if (res) { - window.open('https://freecodecamp.slack.com/messages/letspair/', '_blank') - } - } - ); - }); - $('.checklist-element').each(function() { var checklistElementId = $(this).attr('id'); if(!!localStorage[checklistElementId]) { @@ -150,10 +94,6 @@ $(document).ready(function() { $('#help-modal').modal('show'); }); - $('#trigger-help-editorless-modal').on('click', function() { - $('#help-editorless-modal').modal('show'); - }); - $('#trigger-issue-modal').on('click', function() { $('#issue-modal').modal('show'); }); @@ -331,6 +271,7 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); + var commentSubmitButtonHandler = function commentSubmitButtonHandler() { $('#comment-button').unbind('click'); var data = $('#comment-box').val(); diff --git a/seed/bonfireMDNlinks.js b/seed/bonfireMDNlinks.js index 9a9ea86741..bedd51b2db 100644 --- a/seed/bonfireMDNlinks.js +++ b/seed/bonfireMDNlinks.js @@ -12,6 +12,7 @@ var links = "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", // ========= GLOBAL OBJECTS "Global Array Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", diff --git a/seed/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json index e14504dba4..58f6602791 100644 --- a/seed/challenges/advanced-bonfires.json +++ b/seed/challenges/advanced-bonfires.json @@ -81,7 +81,7 @@ "sym([1, 2, 3], [5, 2, 1, 4]);" ], "tests": [ - "expect(sym([1, 2, 3], [5, 2, 1, 4])).to.eqls([3, 5, 4])", + "expect(sym([1, 2, 3], [5, 2, 1, 4])).to.equal([3, 5, 4]);", "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');" @@ -230,7 +230,7 @@ "permAlone('aab');" ], "tests": [ - "expect(permAlone('aab')).to.be.a.number;", + "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);", diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index df4b6bc16c..bc75adb4ac 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -14,7 +14,7 @@ "Pair Programming is where two people code together on the same computer. It is an efficient way to collaborate, and widely practiced at software companies. Pair Programming is one of the core concepts of \"Agile\" Software Development, which you will hear more about later.", "Many people use Skype or Google Hangouts to pair program, but if you talk with professional software engineers, they will tell you that it's not really pair programming unless both people have the ability to use the keyboard and mouse.", "The most popular tool for pair programming is Screen Hero. You can download Screen Hero for Mac or Windows. Create your new user account from within the app.", - "We have a special chat room for people ready to pair program. Go to our http://freecodecamp.slack.com/messages/letspair and type \"Hello Pair Programmers!\"", + "We have a special chat room for people ready to pair program. Go to our LetsPair chatroom on gitter and type \"Hello Pair Programmers!\"", "If someone is available, they will be your \"pair\" - the person you pair programming with.", "If no one gets back to you in the first few minutes, don't worry. There will be lots of opportunities to pair program in the future.", "If someone does get back to you, private message them and ask for the email address they used to register Screen Hero.", @@ -180,7 +180,8 @@ "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(\"nope\"), false);", + "assert.deepEqual(palindrome(\"almostomla\"), false);" ], "challengeSeed": [ "function palindrome(str) {", @@ -422,6 +423,7 @@ ], "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');" ], @@ -656,7 +658,10 @@ ], "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([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');" ], "MDNlinks": [ "Arguments object", @@ -754,8 +759,8 @@ }, { "id": "a5de63ebea8dbee56860f4f2", - "name": "bonfire-diff-two-arrays", - "dashedName": "Bonfire: Diff Two Arrays", + "name": "Bonfire: Diff Two Arrays", + "dashedName": "bonfire-diff-two-arrays", "difficulty": "2.01", "description": [ "Compare two arrays and return a new array with any items not found in both of the original arrays.", @@ -853,7 +858,7 @@ "difficulty": "2.03", "description": [ "Perform a search and replace on the sentence using the arguments provided and return the new sentence.", - "First argument is the sentence the perform the search and replace on.", + "First argument is the sentence to perform the search and replace on.", "Second argument is the word that you will be replacing (before).", "Third argument is what you will be replacing the second argument with (after).", "NOTE: Preserve the case of the original word when you are replacing it. For example if you mean to replace the word 'Book' with the word 'dog', it should be replaced as 'Dog'", @@ -990,7 +995,8 @@ "expect(fearNotLetter('yz')).to.be.undefined;" ], "MDNlinks": [ - "String.charCodeAt()" + "String.charCodeAt()", + "String.fromCharCode()" ], "challengeType": 5, "nameCn": "", @@ -1091,7 +1097,7 @@ "dashedName": "bonfire-convert-html-entities", "difficulty": "2.07", "description": [ - "Convert the characters \"&\", \"<\", \">\", '\"', and \"'\", in a string to their corresponding HTML entities.", + "Convert the characters \"&\", \"<\", \">\", '\"' (double quote), and \"'\" (apostrophe), in a string to their corresponding HTML entities.", "Remember to use RSAP if you get stuck. Try to pair program. Write your own code." ], "challengeSeed": [ @@ -1104,10 +1110,16 @@ ], "tests": [ "assert.strictEqual(convert('Dolce & Gabbana'), 'Dolce & Gabbana', 'should escape characters');", + "assert.strictEqual(convert('Hamburgers < Pizza < Tacos'), 'Hamburgers < Pizza < Tacos', 'should escape characters');", + "assert.strictEqual(convert('Sixty > twelve'), 'Sixty > twelve', 'should escape characters');", + "assert.strictEqual(convert('Stuff in \"quotation marks\"'), 'Stuff in "quotation marks"', 'should escape characters');", + "assert.strictEqual(convert(\"Shindler's List\"), 'Shindler's List', 'should escape characters');", + "assert.strictEqual(convert('<>'), '<>', 'should escape characters');", "assert.strictEqual(convert('abc'), 'abc', 'should handle strings with nothing to escape');" ], "MDNlinks": [ - "RegExp" + "RegExp", + "HTML Entities" ], "challengeType": 5, "nameCn": "", diff --git a/seed/challenges/basic-html5-and-css.json b/seed/challenges/basic-html5-and-css.json index 4a8df44a71..7fdae6da09 100644 --- a/seed/challenges/basic-html5-and-css.json +++ b/seed/challenges/basic-html5-and-css.json @@ -943,7 +943,8 @@ "In addition to pixels, you can also specify a border-radius using a percentage." ], "tests": [ - "assert(parseInt($('img').css('border-top-left-radius')) > 48, 'Your image should have a border radius of 50 percent, making it perfectly circular.')" + "assert(parseInt($('img').css('border-top-left-radius')) > 48, 'Your image should have a border radius of 50 percent, making it perfectly circular.')", + "assert(editor.match(/50%/g), 'Be sure to use a percentage instead of a pixel value.')" ], "challengeSeed": [ "", @@ -1005,7 +1006,7 @@ ], "tests": [ "assert((/cat photos/gi).test($('a').text()), 'Your a element should have the anchor text of \"cat photos\"')", - "assert($('a').filter(function(index) { return /com/gi.test($('a').attr('href')); }).length > 0, 'You need an a element that links to http://catphotoapp.com.')", + "assert(/http:\\/\\/catphotoapp\\.com/gi.test($('a').attr('href')), 'You need an a element that links to http://catphotoapp.com.')", "assert(editor.match(/<\\/a>/g) && editor.match(/<\\/a>/g).length === editor.match(/a element has a closing tag.')" ], "challengeSeed": [ @@ -1540,7 +1541,7 @@ ], "tests": [ "assert($('input[placeholder]').length > 0, 'Add a placeholder attribute text input element.')", - "assert($('input').attr('placeholder').match(/cat\\s+photo\\s+URL/gi), 'Set the value of your placeholder attribute to \"cat photo URL\".')" + "assert($('input') && $('input').attr('placeholder') && $('input').attr('placeholder').match(/cat\\s+photo\\s+URL/gi), 'Set the value of your placeholder attribute to \"cat photo URL\".')" ], "challengeSeed": [ "", @@ -1613,7 +1614,7 @@ "For example: <form action=\"/url-where-you-want-to-submit-form-data\"></form>." ], "tests": [ - "assert($('form').length > 0, 'Wrap your text input element within a form element.')", + "assert($('form') && $('form').children('input') && $('form').children('input').length > 0, 'Wrap your text input element within a form element.')", "assert($('form').attr('action'), 'Your form element should have an action attribute.')", "assert(editor.match(/<\\/form>/g) && editor.match(/
/g).length === editor.match(/form element has a closing tag.')", "assert(editor.match(/\\/submit-cat-photo/ig), 'Make sure your form action is set to /submit-cat-photo.')" diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index eeac669f73..41a7229731 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -626,7 +626,7 @@ "The \"row\" class is applied to a div, and the buttons themselves can be wrapped within it." ], "tests": [ - "assert($('div.row:has(button)'), 'Your buttons should all be wrapped within the same div element with the class \"row\".')", + "assert($('div.row:has(button)').length > 0, 'Your buttons should all be wrapped within the same div element with the class \"row\".')", "assert($('div.col-xs-4:has(button)').length > 2, 'Each of your Bootstrap buttons should be wrapped within its own a div element with the class \"col-xs-4\".')", "assert(editor.match(/<\\/button>/g) && editor.match(/