diff --git a/.gitignore b/.gitignore index 86cbd7b0c8..0f96fb3ee1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,20 @@ coverage .remote-sync.json server/*.bundle.js -public/js/*.bundle.js +public/js/bundle* + *.map + +// revision manifest +server/rev-manifest.json +server/manifests/* +!server/manifests/README.md + +public/js/main* +public/js/commonFramework* +public/js/sandbox* +public/js/iFrameScripts* +public/js/plugin* +public/css/main* + +server/rev-manifest.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..ba4e245107 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,2 @@ +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. diff --git a/README.md b/README.md index d62477d7bd..dba5013540 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ bower install # Create a .env file and populate it with the necessary API keys and secrets: touch .env +# Install Gulp globally +npm install -g gulp ``` Edit your .env file with the following API keys accordingly (if you only use email login, only the MONGOHQ_URL, SESSION_SECRET, MANDRILL_USER and MANDRILL_PASSWORD fields are necessary. Keep in mind if you want to use more services you'll have to get your own API keys for those services. @@ -112,6 +114,11 @@ DEBUG=true # Start the mongo server mongod +# Create your mongo database. +# Type "mongo" in your terminal to access the mongo shell +use freecodecamp +# Exit the mongo shell with control + d + # Seed your database with the challenges node seed/ diff --git a/client/README.md b/client/README.md index 5ba2586944..b722fceb9c 100644 --- a/client/README.md +++ b/client/README.md @@ -1,4 +1,4 @@ -This is the entry point for the client +This is the entry point for the client code Code that should only run on the client should be put here. NOTE(berks): For react specific stuff this should be the entry point diff --git a/client/commonFramework.js b/client/commonFramework.js new file mode 100644 index 0000000000..91af03d409 --- /dev/null +++ b/client/commonFramework.js @@ -0,0 +1,769 @@ +/* globals jailed, CodeMirror, challenge_Id, challenge_Name, challengeType */ +// codeStorage +var codeStorageFactory = (function($, localStorage) { + + var CodeStorageProps = { + version: 0.01, + keyVersion: 'saveVersion', + keyValue: null, + updateWait: 2000, + updateTimeoutId: null + }; + + var CodeStorage = { + hasSaved: function() { + return this.updateTimeoutId === null; + }, + + onSave: function(func) { + this.eventArray.push(func); + }, + + setSaveKey: function(key) { + this.keyValue = key + 'Val'; + }, + + getStoredValue: function() { + return '' + localStorage.getItem(this.keyValue); + }, + + setEditor: function(editor) { + this.editor = editor; + }, + + isAlive: function() { + var val = this.getStoredValue(); + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); + }, + + updateStorage: function() { + if (typeof localStorage !== 'undefined') { + var value = this.editor.getValue(); + localStorage.setItem(this.keyValue, value); + } else { + console.log('no web storage'); + } + this.updateTimeoutId = null; + } + }; + + function codeStorageFactory(editor, challengeName) { + var codeStorage = Object.create(CodeStorage); + $.extend(codeStorage, CodeStorageProps); + codeStorage.setEditor(editor); + codeStorage.setSaveKey(challengeName); + return codeStorage; + } + + var savedVersion = localStorage.getItem(CodeStorageProps.keyVersion); + if (savedVersion === null) { + localStorage.setItem( + CodeStorageProps.keyVersion, + CodeStorageProps.version + ); + } + + return codeStorageFactory; +}($, localStorage)); + +var sandBox = (function() { + + var plugin = null; + + var sandBox = { + }; + + var printCallback; + + // sends the input to the plugin for evaluation + function submit(code, callback) { + printCallback = callback; + + // postpone the evaluation until the plugin is initialized + plugin.whenConnected(function() { + if (requests === 0) { + startLoading(); + } + + requests++; + plugin.remote.run(code); + }); + } + + // puts the message on the terminal + var print = function(cls, msg) { + printCallback(cls, msg); + }; + + + // will restart the plugin if it does not respond + var disconnectTimeout = null; + var startLoading = function() { + disconnectTimeout = setTimeout(disconnect, 3000); + }; + + var endLoading = function() { + clearTimeout(disconnectTimeout); + }; + + var disconnect = function() { + plugin.disconnect(); + }; + + + // interface provided to the plugin + var api = { + output: function(data) { + endLoading(); + // print('input', data.input); + + if (data.error) { + print('Error', data); + reset(); + } else { + print(null, data); + reset(); + } + } + }; + + + // obtaining absolute path of this script + var scripts = document.getElementsByTagName('script'); + var path = scripts[scripts.length - 1].src + .split('?')[0] + .split('/') + .slice(0, -1) + .join('/') + '/'; + + var requests; + + // (re)initializes the plugin + var reset = function() { + requests = 0; + plugin = new jailed.Plugin(path + 'plugin.js', api); + plugin.whenDisconnected( function() { + // give some time to handle the last responce + setTimeout( function() { + endLoading(); + console.log('resetting on fatal plugin error'); + + if (challengeType === 0) { + codeOutput.setValue( + 'Sorry, your code is either too slow, has a fatal error, ' + + 'or contains an infinite loop.' + ); + } + reset(); + }, 10); + }); + }; + reset(); + sandBox.submit = submit; + return sandBox; +}()); + +function replaceSafeTags(value) { + return value + .replace(/fccss/gi, ''); +} + +var BDDregex = new RegExp( + '(expect(\\s+)?\\(.*\\;)|' + + '(assert(\\s+)?\\(.*\\;)|' + + '(assert\\.\\w.*\\;)|' + + '(.*\\.should\\..*\\;)/' +); + +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; + } +}); + +editor.setSize('100%', 'auto'); + +var libraryIncludes = "" + + "" + + "" + + "" + + "" + + "" + + "" + + '' + + ''; + +var editorValueForIFrame; +var iFrameScript = ""; +var delay; + +function workerError(error) { + var display = $('.runTimeError'); + var housing = $('#testSuite'); + if (display.html() !== error) { + display.remove(); + housing.prepend( + '
' + + error.replace(/j\$/gi, '$').replace(/jdocument/gi, 'document').replace(/jjQuery/gi, 'jQuery') + + '
' + ); + display.hide().fadeIn(function() { + setTimeout(function() { + display.fadeOut(function() { + display.remove(); + }); + }, 1000); + }); + } +} + +function scopejQuery(str) { + return str + .replace(/\$/gi, 'j$') + .replace(/document/gi, 'jdocument') + .replace(/jQuery/gi, 'jjQuery'); +} + +function safeHTMLRun(test) { + if (challengeType === '0') { + var previewFrame = document.getElementById('preview'); + var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; + if (editor.getValue().match(/\/gi) !== null) { + var s = editor + .getValue() + .split(/\<\s?script\s?\>/gi)[1] + .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; + + sandBox.submit(scopejQuery(s), function(cls, message) { + if (cls) { + console.log(message.error); + workerError(message.error); + } else if (test) { + preview.open(); + preview.write(libraryIncludes + editor.getValue() + iFrameScript); + codeStorage.updateStorage(); + preview.close(); + } else { + preview.open(); + preview.write(libraryIncludes + editor.getValue()); + codeStorage.updateStorage(); + preview.close(); + } + }); + } else if (test) { + preview.open(); + preview.write(libraryIncludes + editor.getValue() + iFrameScript); + codeStorage.updateStorage(); + preview.close(); + } else { + preview.open(); + preview.write(libraryIncludes + editor.getValue()); + codeStorage.updateStorage(); + preview.close(); + } + } +} + +function updatePreview() { + editorValueForIFrame = editor.getValue(); + var failedCommentTest = false; + if ( + editorValueForIFrame.match(/\<\!\-\-/gi) && + editorValueForIFrame.match(/\-\-\>/gi) == null + ) { + failedCommentTest = true; + } else if ( + editorValueForIFrame.match(/\<\!\-\-/gi) && + editorValueForIFrame.match(/\<\!\-\-/gi).length > editorValueForIFrame.match(/\-\-\>/gi).length + ) { + failedCommentTest = true; + } + + if (failedCommentTest) { + editor.setValue(editor.getValue() + '-->'); + editorValueForIFrame = editorValueForIFrame + '-->'; + } + + if (!editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi)) { + safeHTMLRun(false); + } else { + workerError('Unsafe $($)'); + } +} + +if (typeof prodOrDev !== 'undefined') { + + var nodeEnv = prodOrDev === 'production' ? + 'http://www.freecodecamp.com' : + 'http://localhost:3001'; + + if (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 + */ + +var testResults = []; +var postSuccess = function(data) { + var testDoc = document.createElement('div'); + $(testDoc).html( + "
" + + JSON.parse(data) + + '
' + ); + + $('#testSuite').append(testDoc); + testSuccess(); +}; + +var postError = function(data) { + var testDoc = document.createElement('div'); + + $(testDoc).html( + "
" + + JSON.parse(data) + + '
' + ); + + $('#testSuite').append(testDoc); +}; + +var goodTests = 0; +var testSuccess = function() { + goodTests++; + if (goodTests === tests.length) { + showCompletion(); + } +}; + +function showCompletion() { + if (isInitRun) { + isInitRun = false; + return; + } + var time = Math.floor(Date.now()) - started; + ga( + 'send', + 'event', + 'Challenge', + 'solved', + challenge_Name + ', Time: ' + time + ', Attempts: ' + attempts + ); + var bonfireSolution = myCodeMirror.getValue(); + var didCompleteWith = $('#completed-with').val() || null; + $('#complete-courseware-dialog').modal('show'); + $('#submit-challenge').click(function(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') + // .removeClass('zoomInDown') + .delay(1000) + .queue(function(next) { + $(this).replaceWith( + '
submitting...
' + ); + next(); + }); + + $.post( + '/completed-bonfire/', { + challengeInfo: { + challengeId: challenge_Id, + challengeName: challenge_Name, + completedWith: didCompleteWith, + challengeType: challengeType, + solution: bonfireSolution + } + }, + function(res) { + if (res) { + window.location = '/challenges/next-challenge'; + } + } + ); + }); +} + +var resetEditor = function resetEditor() { + editor.setValue(replaceSafeTags(allSeeds)); + $('#testSuite').empty(); + bonfireExecute(true); + codeStorage.updateStorage(); +}; + +var attempts = 0; +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 + for (var i = 0; i < tests.length; i++) { + userJavaScript += '\n' + tests[i]; + } + + var counter = 0; + var match = BDDregex.exec(userJavaScript); + + while (match) { + var replacement = '//' + counter + testSalt; + userJavaScript = userJavaScript.substring(0, match.index) + + replacement + + userJavaScript.substring(match.index + match[0].length); + + if (!userTests) { + userTests = []; + } + + userTests.push({ + 'text': match[0], + 'line': counter, + 'err': null + }); + + counter++; + match = BDDregex.exec(userJavaScript); + } + + return userJavaScript; +}; + +function removeComments(userJavaScript) { + var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); + return userJavaScript.replace(regex, ''); +} + +function removeLogs(userJavaScript) { + return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); +} + +var pushed = false; +var createTestDisplay = function() { + if (pushed) { + userTests.pop(); + } + for (var i = 0; i < userTests.length; i++) { + var test = userTests[i]; + var testDoc = document.createElement('div'); + + if (test.err) { + console.log('Should be displaying bad tests'); + + $(testDoc).html( + "
" + + test.text + "
" + + test.err + "
" + ) + .appendTo($('#testSuite')); + + } else { + + $(testDoc).html( + "
" + + test.text + + "
" + ) + .appendTo($('#testSuite')); + } + } +}; + +var expect = chai.expect; +var assert = chai.assert; +var should = chai.should(); + + +var reassembleTest = function(test, data) { + var lineNum = test.line; + var regexp = new RegExp('\/\/' + lineNum + testSalt); + return data.input.replace(regexp, test.text); +}; + +var runTests = function(err, data) { + // userTests = userTests ? null : []; + var allTestsPassed = true; + pushed = false; + $('#testSuite').children().remove(); + if (err && userTests.length > 0) { + userTests = [{ + text: 'Program Execution Failure', + err: 'No user tests were run.' + }]; + createTestDisplay(); + + // Add blocks to test exploits here! + } else if (editorValue.match(/if\s\(null\)\sconsole\.log\(1\);/gi)) { + allTestsPassed = false; + userTests = [{ + text: 'Program Execution Failure', + err: 'Invalid if (null) console.log(1); detected' + }]; + createTestDisplay(); + } else if (userTests) { + userTests.push(false); + pushed = true; + userTests.forEach(function( + chaiTestFromJSON, + indexOfTestArray, + __testArray + ) { + try { + if (chaiTestFromJSON) { + var output = eval(reassembleTest(chaiTestFromJSON, data)); + } + } catch (error) { + allTestsPassed = false; + __testArray[indexOfTestArray].err = error.message; + } finally { + if (!chaiTestFromJSON) { + createTestDisplay(); + } + } + }); + + if (allTestsPassed) { + allTestsPassed = false; + showCompletion(); + } + } +}; + +function bonfireExecute(shouldTest) { + initPreview = false; + goodTests = 0; + attempts++; + ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); + userTests = null; + $('#testSuite').empty(); + + if ( + challengeType !== '0' && + !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) + ) { + var userJavaScript = myCodeMirror.getValue(); + var failedCommentTest = false; + + // checks if the number of opening comments(/*) matches the number of + // closing comments(*/) + if ( + userJavaScript.match(/\/\*/gi) && + userJavaScript.match(/\/\*/gi).length > userJavaScript.match(/\*\//gi).length + ) { + failedCommentTest = true; + } + + userJavaScript = removeComments(userJavaScript); + userJavaScript = scrapeTests(userJavaScript); + // simple fix in case the user forgets to invoke their function + + if (userJavaScript.match(/function/gi)) { + if (userJavaScript.match(/function\s*?\(|function\s+\w+\s*?\(/gi)) { + sandBox.submit(userJavaScript, function(cls, message) { + if (failedCommentTest) { + myCodeMirror.setValue(myCodeMirror.getValue() + '*/'); + console.log('Caught Unfinished Comment'); + codeOutput.setValue('Unfinished multi-line comment'); + failedCommentTest = false; + } else if (cls) { + codeOutput.setValue(message.error); + if (shouldTest) { + runTests('Error', null); + } + } else { + codeOutput.setValue(message.output); + codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, '')); + message.input = removeLogs(message.input); + if (shouldTest) { + runTests(null, message); + } + } + }); + } else { + codeOutput.setValue('Unsafe or unfinished function declaration'); + } + } else { + sandBox.submit(userJavaScript, function(cls, message) { + + if (failedCommentTest) { + myCodeMirror.setValue(myCodeMirror.getValue() + '*/'); + console.log('Caught Unfinished Comment'); + codeOutput.setValue('Unfinished mulit-line comment'); + failedCommentTest = false; + } else if (cls) { + codeOutput.setValue(message.error); + if (shouldTest) { + runTests('Error', null); + } + } else { + codeOutput.setValue(message.output); + codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, '')); + message.input = removeLogs(message.input); + + if (shouldTest) { + runTests(null, message); + } + } + }); + } + } else { + + editorValueForIFrame = editor.getValue(); + + if (failedCommentTest) { + editor.setValue(editor.getValue() + '-->'); + editorValueForIFrame = editorValueForIFrame + '-->'; + } + if ( + !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) && + challengeType === '0' + ) { + safeHTMLRun(shouldTest); + } else { + workerError('Unsafe $($)'); + } + } + setTimeout(function() { + var $marginFix = $('.innerMarginFix'); + $marginFix.css('min-height', $marginFix.height()); + $('#submitButton').click(); + }, 1000); +} + +$('#submitButton').on('click', function() { + bonfireExecute(true); +}); + +$(document).ready(function() { + 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 { + bonfireExecute(true); + } +}); diff --git a/public/js/lib/coursewares/iFrameScripts_0.0.4.js b/client/iFrameScripts.js similarity index 100% rename from public/js/lib/coursewares/iFrameScripts_0.0.4.js rename to client/iFrameScripts.js diff --git a/public/css/lib/animate.min.less b/client/less/lib/animate.min.less similarity index 100% rename from public/css/lib/animate.min.less rename to client/less/lib/animate.min.less diff --git a/public/css/lib/bootstrap-social/bootstrap-social.less b/client/less/lib/bootstrap-social/bootstrap-social.less similarity index 100% rename from public/css/lib/bootstrap-social/bootstrap-social.less rename to client/less/lib/bootstrap-social/bootstrap-social.less diff --git a/public/css/lib/bootstrap/alerts.less b/client/less/lib/bootstrap/alerts.less similarity index 100% rename from public/css/lib/bootstrap/alerts.less rename to client/less/lib/bootstrap/alerts.less diff --git a/public/css/lib/bootstrap/badges.less b/client/less/lib/bootstrap/badges.less similarity index 100% rename from public/css/lib/bootstrap/badges.less rename to client/less/lib/bootstrap/badges.less diff --git a/public/css/lib/bootstrap/bootstrap.less b/client/less/lib/bootstrap/bootstrap.less similarity index 100% rename from public/css/lib/bootstrap/bootstrap.less rename to client/less/lib/bootstrap/bootstrap.less diff --git a/public/css/lib/bootstrap/breadcrumbs.less b/client/less/lib/bootstrap/breadcrumbs.less similarity index 100% rename from public/css/lib/bootstrap/breadcrumbs.less rename to client/less/lib/bootstrap/breadcrumbs.less diff --git a/public/css/lib/bootstrap/button-groups.less b/client/less/lib/bootstrap/button-groups.less similarity index 100% rename from public/css/lib/bootstrap/button-groups.less rename to client/less/lib/bootstrap/button-groups.less diff --git a/public/css/lib/bootstrap/buttons.less b/client/less/lib/bootstrap/buttons.less similarity index 100% rename from public/css/lib/bootstrap/buttons.less rename to client/less/lib/bootstrap/buttons.less diff --git a/public/css/lib/bootstrap/carousel.less b/client/less/lib/bootstrap/carousel.less similarity index 100% rename from public/css/lib/bootstrap/carousel.less rename to client/less/lib/bootstrap/carousel.less diff --git a/public/css/lib/bootstrap/close.less b/client/less/lib/bootstrap/close.less similarity index 100% rename from public/css/lib/bootstrap/close.less rename to client/less/lib/bootstrap/close.less diff --git a/public/css/lib/bootstrap/code.less b/client/less/lib/bootstrap/code.less similarity index 100% rename from public/css/lib/bootstrap/code.less rename to client/less/lib/bootstrap/code.less diff --git a/public/css/lib/bootstrap/component-animations.less b/client/less/lib/bootstrap/component-animations.less similarity index 100% rename from public/css/lib/bootstrap/component-animations.less rename to client/less/lib/bootstrap/component-animations.less diff --git a/public/css/lib/bootstrap/dropdowns.less b/client/less/lib/bootstrap/dropdowns.less similarity index 100% rename from public/css/lib/bootstrap/dropdowns.less rename to client/less/lib/bootstrap/dropdowns.less diff --git a/public/css/lib/bootstrap/forms.less b/client/less/lib/bootstrap/forms.less similarity index 100% rename from public/css/lib/bootstrap/forms.less rename to client/less/lib/bootstrap/forms.less diff --git a/public/css/lib/bootstrap/glyphicons.less b/client/less/lib/bootstrap/glyphicons.less similarity index 100% rename from public/css/lib/bootstrap/glyphicons.less rename to client/less/lib/bootstrap/glyphicons.less diff --git a/public/css/lib/bootstrap/grid.less b/client/less/lib/bootstrap/grid.less similarity index 100% rename from public/css/lib/bootstrap/grid.less rename to client/less/lib/bootstrap/grid.less diff --git a/public/css/lib/bootstrap/input-groups.less b/client/less/lib/bootstrap/input-groups.less similarity index 100% rename from public/css/lib/bootstrap/input-groups.less rename to client/less/lib/bootstrap/input-groups.less diff --git a/public/css/lib/bootstrap/jumbotron.less b/client/less/lib/bootstrap/jumbotron.less similarity index 100% rename from public/css/lib/bootstrap/jumbotron.less rename to client/less/lib/bootstrap/jumbotron.less diff --git a/public/css/lib/bootstrap/labels.less b/client/less/lib/bootstrap/labels.less similarity index 100% rename from public/css/lib/bootstrap/labels.less rename to client/less/lib/bootstrap/labels.less diff --git a/public/css/lib/bootstrap/list-group.less b/client/less/lib/bootstrap/list-group.less similarity index 100% rename from public/css/lib/bootstrap/list-group.less rename to client/less/lib/bootstrap/list-group.less diff --git a/public/css/lib/bootstrap/media.less b/client/less/lib/bootstrap/media.less similarity index 100% rename from public/css/lib/bootstrap/media.less rename to client/less/lib/bootstrap/media.less diff --git a/public/css/lib/bootstrap/mixins.less b/client/less/lib/bootstrap/mixins.less similarity index 100% rename from public/css/lib/bootstrap/mixins.less rename to client/less/lib/bootstrap/mixins.less diff --git a/public/css/lib/bootstrap/mixins/alerts.less b/client/less/lib/bootstrap/mixins/alerts.less similarity index 100% rename from public/css/lib/bootstrap/mixins/alerts.less rename to client/less/lib/bootstrap/mixins/alerts.less diff --git a/public/css/lib/bootstrap/mixins/background-variant.less b/client/less/lib/bootstrap/mixins/background-variant.less similarity index 100% rename from public/css/lib/bootstrap/mixins/background-variant.less rename to client/less/lib/bootstrap/mixins/background-variant.less diff --git a/public/css/lib/bootstrap/mixins/border-radius.less b/client/less/lib/bootstrap/mixins/border-radius.less similarity index 100% rename from public/css/lib/bootstrap/mixins/border-radius.less rename to client/less/lib/bootstrap/mixins/border-radius.less diff --git a/public/css/lib/bootstrap/mixins/buttons.less b/client/less/lib/bootstrap/mixins/buttons.less similarity index 100% rename from public/css/lib/bootstrap/mixins/buttons.less rename to client/less/lib/bootstrap/mixins/buttons.less diff --git a/public/css/lib/bootstrap/mixins/center-block.less b/client/less/lib/bootstrap/mixins/center-block.less similarity index 100% rename from public/css/lib/bootstrap/mixins/center-block.less rename to client/less/lib/bootstrap/mixins/center-block.less diff --git a/public/css/lib/bootstrap/mixins/clearfix.less b/client/less/lib/bootstrap/mixins/clearfix.less similarity index 100% rename from public/css/lib/bootstrap/mixins/clearfix.less rename to client/less/lib/bootstrap/mixins/clearfix.less diff --git a/public/css/lib/bootstrap/mixins/forms.less b/client/less/lib/bootstrap/mixins/forms.less similarity index 100% rename from public/css/lib/bootstrap/mixins/forms.less rename to client/less/lib/bootstrap/mixins/forms.less diff --git a/public/css/lib/bootstrap/mixins/gradients.less b/client/less/lib/bootstrap/mixins/gradients.less similarity index 100% rename from public/css/lib/bootstrap/mixins/gradients.less rename to client/less/lib/bootstrap/mixins/gradients.less diff --git a/public/css/lib/bootstrap/mixins/grid-framework.less b/client/less/lib/bootstrap/mixins/grid-framework.less similarity index 100% rename from public/css/lib/bootstrap/mixins/grid-framework.less rename to client/less/lib/bootstrap/mixins/grid-framework.less diff --git a/public/css/lib/bootstrap/mixins/grid.less b/client/less/lib/bootstrap/mixins/grid.less similarity index 100% rename from public/css/lib/bootstrap/mixins/grid.less rename to client/less/lib/bootstrap/mixins/grid.less diff --git a/public/css/lib/bootstrap/mixins/hide-text.less b/client/less/lib/bootstrap/mixins/hide-text.less similarity index 100% rename from public/css/lib/bootstrap/mixins/hide-text.less rename to client/less/lib/bootstrap/mixins/hide-text.less diff --git a/public/css/lib/bootstrap/mixins/image.less b/client/less/lib/bootstrap/mixins/image.less similarity index 100% rename from public/css/lib/bootstrap/mixins/image.less rename to client/less/lib/bootstrap/mixins/image.less diff --git a/public/css/lib/bootstrap/mixins/labels.less b/client/less/lib/bootstrap/mixins/labels.less similarity index 100% rename from public/css/lib/bootstrap/mixins/labels.less rename to client/less/lib/bootstrap/mixins/labels.less diff --git a/public/css/lib/bootstrap/mixins/list-group.less b/client/less/lib/bootstrap/mixins/list-group.less similarity index 100% rename from public/css/lib/bootstrap/mixins/list-group.less rename to client/less/lib/bootstrap/mixins/list-group.less diff --git a/public/css/lib/bootstrap/mixins/nav-divider.less b/client/less/lib/bootstrap/mixins/nav-divider.less similarity index 100% rename from public/css/lib/bootstrap/mixins/nav-divider.less rename to client/less/lib/bootstrap/mixins/nav-divider.less diff --git a/public/css/lib/bootstrap/mixins/nav-vertical-align.less b/client/less/lib/bootstrap/mixins/nav-vertical-align.less similarity index 100% rename from public/css/lib/bootstrap/mixins/nav-vertical-align.less rename to client/less/lib/bootstrap/mixins/nav-vertical-align.less diff --git a/public/css/lib/bootstrap/mixins/opacity.less b/client/less/lib/bootstrap/mixins/opacity.less similarity index 100% rename from public/css/lib/bootstrap/mixins/opacity.less rename to client/less/lib/bootstrap/mixins/opacity.less diff --git a/public/css/lib/bootstrap/mixins/pagination.less b/client/less/lib/bootstrap/mixins/pagination.less similarity index 100% rename from public/css/lib/bootstrap/mixins/pagination.less rename to client/less/lib/bootstrap/mixins/pagination.less diff --git a/public/css/lib/bootstrap/mixins/panels.less b/client/less/lib/bootstrap/mixins/panels.less similarity index 100% rename from public/css/lib/bootstrap/mixins/panels.less rename to client/less/lib/bootstrap/mixins/panels.less diff --git a/public/css/lib/bootstrap/mixins/progress-bar.less b/client/less/lib/bootstrap/mixins/progress-bar.less similarity index 100% rename from public/css/lib/bootstrap/mixins/progress-bar.less rename to client/less/lib/bootstrap/mixins/progress-bar.less diff --git a/public/css/lib/bootstrap/mixins/reset-filter.less b/client/less/lib/bootstrap/mixins/reset-filter.less similarity index 100% rename from public/css/lib/bootstrap/mixins/reset-filter.less rename to client/less/lib/bootstrap/mixins/reset-filter.less diff --git a/public/css/lib/bootstrap/mixins/resize.less b/client/less/lib/bootstrap/mixins/resize.less similarity index 100% rename from public/css/lib/bootstrap/mixins/resize.less rename to client/less/lib/bootstrap/mixins/resize.less diff --git a/public/css/lib/bootstrap/mixins/responsive-visibility.less b/client/less/lib/bootstrap/mixins/responsive-visibility.less similarity index 100% rename from public/css/lib/bootstrap/mixins/responsive-visibility.less rename to client/less/lib/bootstrap/mixins/responsive-visibility.less diff --git a/public/css/lib/bootstrap/mixins/size.less b/client/less/lib/bootstrap/mixins/size.less similarity index 100% rename from public/css/lib/bootstrap/mixins/size.less rename to client/less/lib/bootstrap/mixins/size.less diff --git a/public/css/lib/bootstrap/mixins/tab-focus.less b/client/less/lib/bootstrap/mixins/tab-focus.less similarity index 100% rename from public/css/lib/bootstrap/mixins/tab-focus.less rename to client/less/lib/bootstrap/mixins/tab-focus.less diff --git a/public/css/lib/bootstrap/mixins/table-row.less b/client/less/lib/bootstrap/mixins/table-row.less similarity index 100% rename from public/css/lib/bootstrap/mixins/table-row.less rename to client/less/lib/bootstrap/mixins/table-row.less diff --git a/public/css/lib/bootstrap/mixins/text-emphasis.less b/client/less/lib/bootstrap/mixins/text-emphasis.less similarity index 100% rename from public/css/lib/bootstrap/mixins/text-emphasis.less rename to client/less/lib/bootstrap/mixins/text-emphasis.less diff --git a/public/css/lib/bootstrap/mixins/text-overflow.less b/client/less/lib/bootstrap/mixins/text-overflow.less similarity index 100% rename from public/css/lib/bootstrap/mixins/text-overflow.less rename to client/less/lib/bootstrap/mixins/text-overflow.less diff --git a/public/css/lib/bootstrap/mixins/vendor-prefixes.less b/client/less/lib/bootstrap/mixins/vendor-prefixes.less similarity index 100% rename from public/css/lib/bootstrap/mixins/vendor-prefixes.less rename to client/less/lib/bootstrap/mixins/vendor-prefixes.less diff --git a/public/css/lib/bootstrap/modals.less b/client/less/lib/bootstrap/modals.less similarity index 100% rename from public/css/lib/bootstrap/modals.less rename to client/less/lib/bootstrap/modals.less diff --git a/public/css/lib/bootstrap/navbar.less b/client/less/lib/bootstrap/navbar.less similarity index 100% rename from public/css/lib/bootstrap/navbar.less rename to client/less/lib/bootstrap/navbar.less diff --git a/public/css/lib/bootstrap/navs.less b/client/less/lib/bootstrap/navs.less similarity index 100% rename from public/css/lib/bootstrap/navs.less rename to client/less/lib/bootstrap/navs.less diff --git a/public/css/lib/bootstrap/normalize.less b/client/less/lib/bootstrap/normalize.less similarity index 100% rename from public/css/lib/bootstrap/normalize.less rename to client/less/lib/bootstrap/normalize.less diff --git a/public/css/lib/bootstrap/pager.less b/client/less/lib/bootstrap/pager.less similarity index 100% rename from public/css/lib/bootstrap/pager.less rename to client/less/lib/bootstrap/pager.less diff --git a/public/css/lib/bootstrap/pagination.less b/client/less/lib/bootstrap/pagination.less similarity index 100% rename from public/css/lib/bootstrap/pagination.less rename to client/less/lib/bootstrap/pagination.less diff --git a/public/css/lib/bootstrap/panels.less b/client/less/lib/bootstrap/panels.less similarity index 100% rename from public/css/lib/bootstrap/panels.less rename to client/less/lib/bootstrap/panels.less diff --git a/public/css/lib/bootstrap/popovers.less b/client/less/lib/bootstrap/popovers.less similarity index 100% rename from public/css/lib/bootstrap/popovers.less rename to client/less/lib/bootstrap/popovers.less diff --git a/public/css/lib/bootstrap/print.less b/client/less/lib/bootstrap/print.less similarity index 100% rename from public/css/lib/bootstrap/print.less rename to client/less/lib/bootstrap/print.less diff --git a/public/css/lib/bootstrap/progress-bars.less b/client/less/lib/bootstrap/progress-bars.less similarity index 100% rename from public/css/lib/bootstrap/progress-bars.less rename to client/less/lib/bootstrap/progress-bars.less diff --git a/public/css/lib/bootstrap/responsive-embed.less b/client/less/lib/bootstrap/responsive-embed.less similarity index 100% rename from public/css/lib/bootstrap/responsive-embed.less rename to client/less/lib/bootstrap/responsive-embed.less diff --git a/public/css/lib/bootstrap/responsive-utilities.less b/client/less/lib/bootstrap/responsive-utilities.less similarity index 100% rename from public/css/lib/bootstrap/responsive-utilities.less rename to client/less/lib/bootstrap/responsive-utilities.less diff --git a/public/css/lib/bootstrap/scaffolding.less b/client/less/lib/bootstrap/scaffolding.less similarity index 100% rename from public/css/lib/bootstrap/scaffolding.less rename to client/less/lib/bootstrap/scaffolding.less diff --git a/public/css/lib/bootstrap/tables.less b/client/less/lib/bootstrap/tables.less similarity index 100% rename from public/css/lib/bootstrap/tables.less rename to client/less/lib/bootstrap/tables.less diff --git a/public/css/lib/bootstrap/theme.less b/client/less/lib/bootstrap/theme.less similarity index 100% rename from public/css/lib/bootstrap/theme.less rename to client/less/lib/bootstrap/theme.less diff --git a/public/css/lib/bootstrap/thumbnails.less b/client/less/lib/bootstrap/thumbnails.less similarity index 100% rename from public/css/lib/bootstrap/thumbnails.less rename to client/less/lib/bootstrap/thumbnails.less diff --git a/public/css/lib/bootstrap/tooltip.less b/client/less/lib/bootstrap/tooltip.less similarity index 100% rename from public/css/lib/bootstrap/tooltip.less rename to client/less/lib/bootstrap/tooltip.less diff --git a/public/css/lib/bootstrap/type.less b/client/less/lib/bootstrap/type.less similarity index 100% rename from public/css/lib/bootstrap/type.less rename to client/less/lib/bootstrap/type.less diff --git a/public/css/lib/bootstrap/utilities.less b/client/less/lib/bootstrap/utilities.less similarity index 100% rename from public/css/lib/bootstrap/utilities.less rename to client/less/lib/bootstrap/utilities.less diff --git a/public/css/lib/bootstrap/variables.less b/client/less/lib/bootstrap/variables.less similarity index 100% rename from public/css/lib/bootstrap/variables.less rename to client/less/lib/bootstrap/variables.less diff --git a/public/css/lib/bootstrap/wells.less b/client/less/lib/bootstrap/wells.less similarity index 100% rename from public/css/lib/bootstrap/wells.less rename to client/less/lib/bootstrap/wells.less diff --git a/public/css/lib/font-awesome-4.3.0/css/font-awesome.css b/client/less/lib/font-awesome-4.3.0/css/font-awesome.css similarity index 100% rename from public/css/lib/font-awesome-4.3.0/css/font-awesome.css rename to client/less/lib/font-awesome-4.3.0/css/font-awesome.css diff --git a/public/css/lib/font-awesome-4.3.0/css/font-awesome.min.css b/client/less/lib/font-awesome-4.3.0/css/font-awesome.min.css similarity index 100% rename from public/css/lib/font-awesome-4.3.0/css/font-awesome.min.css rename to client/less/lib/font-awesome-4.3.0/css/font-awesome.min.css diff --git a/public/css/lib/font-awesome-4.3.0/fonts/FontAwesome.otf b/client/less/lib/font-awesome-4.3.0/fonts/FontAwesome.otf similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/FontAwesome.otf rename to client/less/lib/font-awesome-4.3.0/fonts/FontAwesome.otf diff --git a/public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot b/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot rename to client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot diff --git a/public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg b/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg rename to client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg diff --git a/public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf b/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf rename to client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf diff --git a/public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff b/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff rename to client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff diff --git a/public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 b/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 similarity index 100% rename from public/css/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 rename to client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 diff --git a/public/css/lib/font-awesome-4.3.0/less/animated.less b/client/less/lib/font-awesome-4.3.0/less/animated.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/animated.less rename to client/less/lib/font-awesome-4.3.0/less/animated.less diff --git a/public/css/lib/font-awesome-4.3.0/less/bordered-pulled.less b/client/less/lib/font-awesome-4.3.0/less/bordered-pulled.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/bordered-pulled.less rename to client/less/lib/font-awesome-4.3.0/less/bordered-pulled.less diff --git a/public/css/lib/font-awesome-4.3.0/less/core.less b/client/less/lib/font-awesome-4.3.0/less/core.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/core.less rename to client/less/lib/font-awesome-4.3.0/less/core.less diff --git a/public/css/lib/font-awesome-4.3.0/less/fixed-width.less b/client/less/lib/font-awesome-4.3.0/less/fixed-width.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/fixed-width.less rename to client/less/lib/font-awesome-4.3.0/less/fixed-width.less diff --git a/public/css/lib/font-awesome-4.3.0/less/font-awesome.less b/client/less/lib/font-awesome-4.3.0/less/font-awesome.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/font-awesome.less rename to client/less/lib/font-awesome-4.3.0/less/font-awesome.less diff --git a/public/css/lib/font-awesome-4.3.0/less/icons.less b/client/less/lib/font-awesome-4.3.0/less/icons.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/icons.less rename to client/less/lib/font-awesome-4.3.0/less/icons.less diff --git a/public/css/lib/font-awesome-4.3.0/less/larger.less b/client/less/lib/font-awesome-4.3.0/less/larger.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/larger.less rename to client/less/lib/font-awesome-4.3.0/less/larger.less diff --git a/public/css/lib/font-awesome-4.3.0/less/list.less b/client/less/lib/font-awesome-4.3.0/less/list.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/list.less rename to client/less/lib/font-awesome-4.3.0/less/list.less diff --git a/public/css/lib/font-awesome-4.3.0/less/mixins.less b/client/less/lib/font-awesome-4.3.0/less/mixins.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/mixins.less rename to client/less/lib/font-awesome-4.3.0/less/mixins.less diff --git a/public/css/lib/font-awesome-4.3.0/less/path.less b/client/less/lib/font-awesome-4.3.0/less/path.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/path.less rename to client/less/lib/font-awesome-4.3.0/less/path.less diff --git a/public/css/lib/font-awesome-4.3.0/less/rotated-flipped.less b/client/less/lib/font-awesome-4.3.0/less/rotated-flipped.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/rotated-flipped.less rename to client/less/lib/font-awesome-4.3.0/less/rotated-flipped.less diff --git a/public/css/lib/font-awesome-4.3.0/less/stacked.less b/client/less/lib/font-awesome-4.3.0/less/stacked.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/stacked.less rename to client/less/lib/font-awesome-4.3.0/less/stacked.less diff --git a/public/css/lib/font-awesome-4.3.0/less/variables.less b/client/less/lib/font-awesome-4.3.0/less/variables.less similarity index 100% rename from public/css/lib/font-awesome-4.3.0/less/variables.less rename to client/less/lib/font-awesome-4.3.0/less/variables.less diff --git a/public/css/lib/font-awesome-4.3.0/scss/_animated.scss b/client/less/lib/font-awesome-4.3.0/scss/_animated.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_animated.scss rename to client/less/lib/font-awesome-4.3.0/scss/_animated.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_bordered-pulled.scss b/client/less/lib/font-awesome-4.3.0/scss/_bordered-pulled.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_bordered-pulled.scss rename to client/less/lib/font-awesome-4.3.0/scss/_bordered-pulled.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_core.scss b/client/less/lib/font-awesome-4.3.0/scss/_core.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_core.scss rename to client/less/lib/font-awesome-4.3.0/scss/_core.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_fixed-width.scss b/client/less/lib/font-awesome-4.3.0/scss/_fixed-width.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_fixed-width.scss rename to client/less/lib/font-awesome-4.3.0/scss/_fixed-width.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_icons.scss b/client/less/lib/font-awesome-4.3.0/scss/_icons.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_icons.scss rename to client/less/lib/font-awesome-4.3.0/scss/_icons.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_larger.scss b/client/less/lib/font-awesome-4.3.0/scss/_larger.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_larger.scss rename to client/less/lib/font-awesome-4.3.0/scss/_larger.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_list.scss b/client/less/lib/font-awesome-4.3.0/scss/_list.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_list.scss rename to client/less/lib/font-awesome-4.3.0/scss/_list.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_mixins.scss b/client/less/lib/font-awesome-4.3.0/scss/_mixins.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_mixins.scss rename to client/less/lib/font-awesome-4.3.0/scss/_mixins.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_path.scss b/client/less/lib/font-awesome-4.3.0/scss/_path.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_path.scss rename to client/less/lib/font-awesome-4.3.0/scss/_path.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_rotated-flipped.scss b/client/less/lib/font-awesome-4.3.0/scss/_rotated-flipped.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_rotated-flipped.scss rename to client/less/lib/font-awesome-4.3.0/scss/_rotated-flipped.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_stacked.scss b/client/less/lib/font-awesome-4.3.0/scss/_stacked.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_stacked.scss rename to client/less/lib/font-awesome-4.3.0/scss/_stacked.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/_variables.scss b/client/less/lib/font-awesome-4.3.0/scss/_variables.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/_variables.scss rename to client/less/lib/font-awesome-4.3.0/scss/_variables.scss diff --git a/public/css/lib/font-awesome-4.3.0/scss/font-awesome.scss b/client/less/lib/font-awesome-4.3.0/scss/font-awesome.scss similarity index 100% rename from public/css/lib/font-awesome-4.3.0/scss/font-awesome.scss rename to client/less/lib/font-awesome-4.3.0/scss/font-awesome.scss diff --git a/public/css/lib/ionicons/_ionicons-animation.less b/client/less/lib/ionicons/_ionicons-animation.less similarity index 100% rename from public/css/lib/ionicons/_ionicons-animation.less rename to client/less/lib/ionicons/_ionicons-animation.less diff --git a/public/css/lib/ionicons/_ionicons-font.less b/client/less/lib/ionicons/_ionicons-font.less similarity index 100% rename from public/css/lib/ionicons/_ionicons-font.less rename to client/less/lib/ionicons/_ionicons-font.less diff --git a/public/css/lib/ionicons/_ionicons-icons.less b/client/less/lib/ionicons/_ionicons-icons.less similarity index 100% rename from public/css/lib/ionicons/_ionicons-icons.less rename to client/less/lib/ionicons/_ionicons-icons.less diff --git a/public/css/lib/ionicons/_ionicons-variables.less b/client/less/lib/ionicons/_ionicons-variables.less similarity index 100% rename from public/css/lib/ionicons/_ionicons-variables.less rename to client/less/lib/ionicons/_ionicons-variables.less diff --git a/public/css/lib/ionicons/ionicons.less b/client/less/lib/ionicons/ionicons.less similarity index 100% rename from public/css/lib/ionicons/ionicons.less rename to client/less/lib/ionicons/ionicons.less diff --git a/public/css/lib/ionicons/ionicons.min.css b/client/less/lib/ionicons/ionicons.min.css similarity index 100% rename from public/css/lib/ionicons/ionicons.min.css rename to client/less/lib/ionicons/ionicons.min.css diff --git a/public/css/main.less b/client/less/main.less similarity index 86% rename from public/css/main.less rename to client/less/main.less index 2deec50cca..814e99cb54 100644 --- a/public/css/main.less +++ b/client/less/main.less @@ -683,9 +683,9 @@ thead { margin-right: 0px; padding-left: 10px; padding-right: 10px; + padding-top: 14px; + padding-bottom: 12px; &:hover { - padding-top: 14px; - padding-bottom: 12px; color: #4a2b0f; background-color: #eee; text-decoration: none; @@ -1177,3 +1177,104 @@ hr { // border-width: 1px; // border-style: solid; //} + +// source: https://github.com/jlong/css-spinners/blob/master/css/spinner/inner-circles.css +// license MIT +/* :not(:required) hides this rule from IE9 and below */ +.inner-circles-loader:not(:required) { + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + position: relative; + display: inline-block; + width: 125px; + height: 125px; + background: rgba(25, 165, 152, 0.5); + border-radius: 50%; + overflow: hidden; + text-indent: -9999px; + margin-top: 10%; + /* Hides inner circles outside base circle at safari */ + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC); +} +.inner-circles-loader:not(:required):before, .inner-circles-loader:not(:required):after { + content: ''; + position: absolute; + top: 0; + display: inline-block; + width: 125px; + height: 125px; + border-radius: 50%; +} +.inner-circles-loader:not(:required):before { + -moz-animation: inner-circles-loader 3s infinite; + -webkit-animation: inner-circles-loader 3s infinite; + animation: inner-circles-loader 3s infinite; + -moz-transform-origin: 0 50%; + -ms-transform-origin: 0 50%; + -webkit-transform-origin: 0 50%; + transform-origin: 0 50%; + left: 0; + background: #c7efcf; +} +.inner-circles-loader:not(:required):after { + -moz-animation: inner-circles-loader 3s 0.2s reverse infinite; + -webkit-animation: inner-circles-loader 3s 0.2s reverse infinite; + animation: inner-circles-loader 3s 0.2s reverse infinite; + -moz-transform-origin: 100% 50%; + -ms-transform-origin: 100% 50%; + -webkit-transform-origin: 100% 50%; + transform-origin: 100% 50%; + right: 0; + background: #eef5db; +} + +@-moz-keyframes inner-circles-loader { + 0% { + -moz-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + -moz-transform: rotate(360deg); + transform: rotate(360deg); + } + 100% { + -moz-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@-webkit-keyframes inner-circles-loader { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@keyframes inner-circles-loader { + 0% { + -moz-transform: rotate(0deg); + -ms-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + -moz-transform: rotate(360deg); + -ms-transform: rotate(360deg); + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + 100% { + -moz-transform: rotate(0deg); + -ms-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} diff --git a/public/js/main_0.0.3.js b/client/main.js similarity index 87% rename from public/js/main_0.0.3.js rename to client/main.js index 35f34fbde3..8a72664bbf 100644 --- a/public/js/main_0.0.3.js +++ b/client/main.js @@ -59,44 +59,11 @@ $(document).ready(function() { function reBindModals(){ - $('#i-want-help').unbind('click'); - $('#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://gitter.im/FreeCodeCamp/Help', '_blank') - } - } - ); - }); - - $('#i-want-help-editorless').unbind('click'); - $('#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://gitter.im/FreeCodeCamp/Help', '_blank') - } - } - ); + $('.close-modal').unbind('click'); + $('.close-modal').on('click', function(){ + setTimeout(function() { + $('.close-modal').parent().parent().parent().parent().modal('hide'); + }, 200); }); $('#report-issue').unbind('click'); @@ -105,25 +72,6 @@ $(document).ready(function() { 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') }); - $('#i-want-help').unbind('click'); - $('#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://gitter.im/FreeCodeCamp/LetsPair', '_blank') - } - } - ); - }); - $('#completed-courseware').unbind('click'); $('#completed-courseware').on('click', function() { $('#complete-courseware-dialog').modal('show'); @@ -242,20 +190,6 @@ $(document).ready(function() { window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); }); - // Bonfire instructions functions - $('#more-info').unbind('click'); - $('#more-info').on('click', function() { - ga('send', 'event', 'Challenge', 'more-info', challengeName); - $('#brief-instructions').hide(); - $('#long-instructions').show().removeClass('hide'); - - }); - $('#less-info').unbind('click'); - $('#less-info').on('click', function() { - $('#brief-instructions').show(); - $('#long-instructions').hide(); - }); - $('#complete-courseware-dialog').on('hidden.bs.modal', function() { editor.focus(); }); diff --git a/public/js/lib/coursewares/plugin_v0.1.5.js b/client/plugin.js similarity index 65% rename from public/js/lib/coursewares/plugin_v0.1.5.js rename to client/plugin.js index 9e63a18b88..36f869db1a 100644 --- a/public/js/lib/coursewares/plugin_v0.1.5.js +++ b/client/plugin.js @@ -1,6 +1,17 @@ - +/* eslint-disable no-eval */ +/* global importScripts, application */ // executes the given code and handles the result -var run = function(code) { + +function importScript(url, error) { + try { + importScripts(url); + } catch (e) { + error = e; + } + return error; +} + +function run(code) { var result = { input: code, output: null, @@ -18,21 +29,13 @@ var run = function(code) { application.remote.output(result); self.close(); -}; +} // protects even the worker scope from being accessed -var runHidden = function(code) { +function runHidden(code) { - var importScript = function(url) { - var error = null; - try { - importScripts(url); - } catch (e) { - error = e; - console.log('Unable to load %s!', url); - } - }; + /* eslint-disable */ var indexedDB = null; var location = null; var navigator = null; @@ -55,25 +58,37 @@ var runHidden = function(code) { var dump = null; var onoffline = null; var ononline = null; - importScripts( - "https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js"); - importScripts( - "https://cdnjs.cloudflare.com/ajax/libs/chai/2.2.0/chai.min.js" - ) + /* eslint-enable */ + var error = null; + error = importScript( + error, + 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js' + ); + + error = importScript( + 'https://cdnjs.cloudflare.com/ajax/libs/chai/2.2.0/chai.min.js' + ); + + + /* eslint-disable*/ var expect = chai.expect; var assert = chai.assert; + /* eslint-enable */ + if (error) { + return error; + } return eval(code); } // converts the output into a string -var stringify = function(output) { +function stringify(output) { var result; - if (typeof output == 'undefined') { + if (typeof output === 'undefined') { result = 'undefined'; } else if (output === null) { result = 'null'; @@ -84,5 +99,4 @@ var stringify = function(output) { return result; } - -application.setInterface({run:run}); +application.setInterface({ run: run }); diff --git a/common/app/routes/Jobs/components/Jobs.jsx b/common/app/routes/Jobs/components/Jobs.jsx index 7f31a329dd..4b37571eb2 100644 --- a/common/app/routes/Jobs/components/Jobs.jsx +++ b/common/app/routes/Jobs/components/Jobs.jsx @@ -1,7 +1,7 @@ import React, { cloneElement, PropTypes } from 'react'; import { contain } from 'thundercats-react'; import { Button, Jumbotron, Row } from 'react-bootstrap'; -import ShowJobs from './Show.jsx'; +import ListJobs from './List.jsx'; export default contain( { @@ -15,9 +15,9 @@ export default contain( jobs: PropTypes.array }, - renderShow(jobs) { + renderList(jobs) { return ( - + ); }, @@ -53,7 +53,7 @@ export default contain( { this.renderChild(children, jobs) || - this.renderShow(jobs) } + this.renderList(jobs) }
); diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx new file mode 100644 index 0000000000..b90050aa74 --- /dev/null +++ b/common/app/routes/Jobs/components/List.jsx @@ -0,0 +1,81 @@ +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( + { + }, + React.createClass({ + displayName: 'ListJobs', + + propTypes: { + jobs: PropTypes.array + }, + + 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) } + + ); + } + }) +); diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index 321e00cd02..209917cff7 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -1,81 +1,59 @@ import React, { PropTypes } from 'react'; -import { contain } from 'thundercats-react'; -import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; +import { Thumbnail, Panel, Well } from 'react-bootstrap'; import moment from 'moment'; -export default contain( - { +const thumbnailStyle = { + backgroundColor: 'white', + maxHeight: '100px', + maxWidth: '100px' +}; +export default React.createClass({ + displayName: 'ShowJob', + propTypes: { + job: PropTypes.object }, - React.createClass({ - displayName: 'ShowJobs', - propTypes: { - jobs: PropTypes.array - }, + renderHeader({ company, position }) { + return ( +
+

{ company }

+
+ { position } +
+
+ ); + }, - renderJobs(jobs =[]) { - const thumbnailStyle = { - backgroundColor: 'white', - maxHeight: '200px', - maxWidth: '200px' - }; - 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 { job } = this.props; + const { + logo, + position, + city, + state, + email, + phone, + postedOn, + description + } = job; - render() { - const { jobs } = this.props; - return ( - - { this.renderJobs(jobs) } - - ); - } - }) -); + return ( + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

{ description }

+
+ ); + } +}); diff --git a/common/models/user.js b/common/models/user.js index c318a88b94..06761e0209 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -123,6 +123,14 @@ module.exports = function(User) { }); }); + User.beforeRemote('login', function(ctx, notUsed, next) { + const { body } = ctx.req; + if (body && typeof body.email === 'string') { + body.email = body.email.toLowerCase(); + } + next(); + }); + User.afterRemote('login', function(ctx, accessToken, next) { var res = ctx.res; var req = ctx.req; diff --git a/gulpfile.js b/gulpfile.js index 8e1e345716..b5eee5417a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -6,8 +6,9 @@ var Rx = require('rx'), // utils plumber = require('gulp-plumber'), notify = require('gulp-notify'), + reduce = require('gulp-reduce-file'), + sortKeys = require('sort-keys'), debug = require('debug')('freecc:gulp'), - bower = require('bower-main-files'), // react app webpack = require('gulp-webpack'), @@ -18,11 +19,15 @@ var Rx = require('rx'), nodemon = require('gulp-nodemon'), sync = require('browser-sync'), - inject = require('gulp-inject'), // css less = require('gulp-less'), + // rev + rev = require('gulp-rev'), + revReplace = require('gulp-rev-replace'), + // lint + jsonlint = require('gulp-jsonlint'), eslint = require('gulp-eslint'); @@ -35,12 +40,14 @@ var paths = { serverIgnore: [ 'gulpfile.js', 'public/', - '!public/js/bundle.js', + '!public/js/bundle*', 'node_modules/', - 'client/' + 'client/', + 'server/rev-manifest.json' ], publicJs: './public/js', + css: 'public/css', loopback: { client: './client/loopbackClient', @@ -53,6 +60,21 @@ var paths = { dest: 'public/js' }, + js: [ + 'client/main.js', + 'client/iFrameScripts.js', + 'client/plugin.js' + ], + + dependents: [ + 'client/commonFramework.js', + 'client/sandbox.js' + ], + + less: './client/less/main.less', + + manifest: 'server/manifests/', + node: { src: './client', dest: 'common/app' @@ -60,6 +82,11 @@ var paths = { syncWatch: [ 'public/**/*.*' + ], + + challenges: [ + 'seed/challenges/*.json', + 'seed/under-construction/*.json' ] }; @@ -80,47 +107,6 @@ function errorHandler() { this.emit('end'); } -gulp.task('inject', function() { - gulp.src('views/home.jade') - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(inject(gulp.src(bower()), { - // ignorePath: '/public' - })) - .pipe(gulp.dest('views')); -}); - -gulp.task('pack-client', function() { - return gulp.src(webpackConfig.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(Object.assign( - {}, - webpackConfig, - webpackOptions - ))) - .pipe(gulp.dest(webpackConfig.output.path)); -}); - -gulp.task('pack-watch', function() { - return gulp.src(webpackConfig.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(Object.assign( - {}, - webpackConfig, - webpackOptions, - { watch: true } - ))) - .pipe(gulp.dest(webpackConfig.output.path)); -}); - -gulp.task('pack-node', function() { - return gulp.src(webpackConfigNode.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(webpackConfigNode)) - .pipe(gulp.dest(webpackConfigNode.output.path)); -}); - -gulp.task('pack', ['pack-client', 'pack-node']); - gulp.task('serve', function(cb) { var called = false; nodemon({ @@ -152,7 +138,16 @@ gulp.task('serve', function(cb) { }); }); -gulp.task('sync', ['serve'], function() { +var syncDepenedents = [ + 'serve', + 'js', + 'less', + 'dependents', + 'pack-client', + 'build-manifest' +]; + +gulp.task('sync', syncDepenedents, function() { sync.init(null, { proxy: 'http://localhost:3000', logLeval: 'debug', @@ -163,25 +158,159 @@ gulp.task('sync', ['serve'], function() { }); }); -gulp.task('lint', function() { +gulp.task('lint-js', function() { return gulp.src(['public/js/lib/**/*']) .pipe(eslint()) .pipe(eslint.format()); }); -gulp.task('less', function() { - return gulp.src('./public/css/*.less') +gulp.task('lint-json', function() { + return gulp.src(paths.challenges) + .pipe(jsonlint()) + .pipe(jsonlint.reporter()); +}); + +gulp.task('test-challenges', ['lint-json']); + +gulp.task('pack-client', function() { + return gulp.src(webpackConfig.entry) .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(Object.assign( + {}, + webpackConfig, + webpackOptions + ))) + .pipe(gulp.dest(webpackConfig.output.path)) + .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(paths.manifest)); +}); + +gulp.task('pack-watch', function() { + return gulp.src(webpackConfig.entry) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(Object.assign( + {}, + webpackConfig, + webpackOptions, + { watch: true } + ))) + .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(rev()) + // copy files to public + .pipe(gulp.dest(webpackConfig.output.path)) + // create manifest + .pipe(rev.manifest('react-manifest.json')) + .pipe(gulp.dest(paths.manifest)); +}); + +gulp.task('pack-node', function() { + return gulp.src(webpackConfigNode.entry) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(webpackConfigNode)) + .pipe(gulp.dest(webpackConfigNode.output.path)); +}); + +gulp.task('pack', ['pack-client', 'pack-node']); + +gulp.task('less', function() { + return gulp.src(paths.less) + .pipe(plumber({ errorHandler: errorHandler })) + // copile .pipe(less({ paths: [ path.join(__dirname, 'less', 'includes') ] })) - .pipe(gulp.dest('./public/css/')); + .pipe(gulp.dest(paths.css)) + // add revision + .pipe(rev()) + // copy files to public + .pipe(gulp.dest(paths.css)) + // create and merge manifest + .pipe(rev.manifest('css-manifest.json')) + .pipe(gulp.dest(paths.manifest)); }); -gulp.task('build', ['less']); +gulp.task('js', function() { + return gulp.src(paths.js) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(gulp.dest(paths.publicJs)) + // create registry file + .pipe(rev()) + // copy revisioned assets to dest + .pipe(gulp.dest(paths.publicJs)) + // create manifest file + .pipe(rev.manifest('js-manifest.json')) + // copy manifest file to dest + .pipe(gulp.dest(paths.manifest)); +}); -gulp.task('watch', ['less', 'serve', 'sync'], function() { - gulp.watch('./public/css/*.less', ['less']); +// commonFramework depend on iFrameScripts +// sandbox depends on plugin +gulp.task('dependents', ['js'], function() { + var manifest = gulp.src( + path.join(__dirname, paths.manifest, 'js-manifest.json') + ); + + return gulp.src(paths.dependents) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(revReplace({ manifest: manifest })) + .pipe(rev()) + .pipe(gulp.dest(paths.publicJs)) + .pipe(rev.manifest('dependents-manifest.json')) + .pipe(gulp.dest(paths.manifest)); +}); + +function collector(file, memo) { + return Object.assign({}, JSON.parse(file.contents), memo); +} + +function done(manifest) { + return sortKeys(manifest); +} + +function buildManifest() { + return gulp.src(paths.manifest + '*.json') + .pipe(reduce('rev-manifest.json', collector, done, {})) + .pipe(gulp.dest('server/')); +} + +var buildDependents = ['less', 'js', 'dependents']; + +gulp.task('build-manifest', buildDependents, function() { + return buildManifest(); +}); + +gulp.task('build-manifest-watch', function() { + return buildManifest(); +}); + +gulp.task('build', [ + 'less', + 'js', + 'dependents', + 'pack-client', + 'build-manifest' +]); + +var watchDependents = [ + 'less', + 'js', + 'dependents', + 'serve', + 'sync', + 'build-manifest' +]; + +gulp.task('watch', watchDependents, function() { + gulp.watch(paths.less, ['less']); + gulp.watch(paths.js, ['js']); + gulp.watch(paths.challenges, ['test-challenges']); + gulp.watch(paths.js, ['js', 'dependents']); + gulp.watch(paths.dependents, ['dependents']); + gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']); }); gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']); diff --git a/package.json b/package.json index b14ee1e15f..d35a1c4cf9 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "url": "https://github.com/freecodecamp/freecodecamp.git" }, "scripts": { - "build-production": "webpack", + "build": "gulp build", "start": "babel-node server/server.js", - "prestart-production": "bower cache clean && bower install && gulp build && npm run build-production", + "prestart-production": "bower cache clean && bower install && gulp build", "start-production": "node pm2Start", "lint": "eslint --ext=.js,.jsx .", "test": "mocha --compilers js:babel/register" @@ -43,6 +43,7 @@ "csso": "~1.3.11", "dateformat": "~1.0.11", "debug": "~2.1.0", + "dedent": "^0.4.0", "dotenv": "~0.4.0", "errorhandler": "~1.3.0", "eslint": "^1.1.0", @@ -59,6 +60,9 @@ "github-api": "~0.7.0", "gulp-less": "^3.0.3", "gulp-minify-css": "~0.5.1", + "gulp-reduce-file": "0.0.1", + "gulp-rev": "^6.0.1", + "gulp-rev-replace": "^0.4.2", "gulp-webpack": "^1.5.0", "helmet": "~0.9.0", "helmet-csp": "^0.2.3", @@ -68,7 +72,7 @@ "less-middleware": "~2.0.1", "lodash": "^3.9.3", "loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password", - "loopback-boot": "^2.8.0", + "loopback-boot": "2.8.2", "loopback-component-passport": "https://github.com/FreeCodeCamp/loopback-component-passport.git#feature/flashfailure", "loopback-connector-mongodb": "^1.10.0", "lusca": "~1.0.2", @@ -98,6 +102,7 @@ "request": "~2.53.0", "rx": "^2.5.3", "sanitize-html": "~1.6.1", + "sort-keys": "^1.1.1", "source-map-support": "^0.3.2", "thundercats": "^2.1.0", "thundercats-react": "^0.1.0", @@ -120,6 +125,7 @@ "gulp": "~3.8.8", "gulp-eslint": "~0.9.0", "gulp-inject": "~1.0.2", + "gulp-jsonlint": "^1.1.0", "gulp-nodemon": "^2.0.3", "gulp-notify": "^2.2.0", "gulp-plumber": "^1.0.1", diff --git a/public/css/lib/Vimeo.css b/public/css/Vimeo.css similarity index 100% rename from public/css/lib/Vimeo.css rename to public/css/Vimeo.css diff --git a/public/js/calculator.js b/public/js/calculator.js index 207611c9e6..9ae06e890a 100644 --- a/public/js/calculator.js +++ b/public/js/calculator.js @@ -28,7 +28,7 @@ $(document).ready(function () { $('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }) + ', and making $' + lastYearsIncome.toString().replace(/0000$/, '0,000') + ', your true costs will be:'); - var categoryNames = ['Lost Wages', 'Financing Cost', 'Housing Cost', 'Tuition / Wage Garnishing']; + var categoryNames = ['Lost Wages', 'Financing Cost', 'Housing Cost', 'Tuition / Est. Wage Garnishing']; bootcamps.forEach(function (camp) { var x0 = 0; if (camp.cities.indexOf(city) > -1) { @@ -38,7 +38,7 @@ $(document).ready(function () { } camp.mapping = [{ name: camp.name, - label: 'Tuition / Wage Garnishing', + label: 'Tuition / Est. Wage Garnishing', value: +camp.cost, x0: x0, x1: x0 += +camp.cost diff --git a/public/js/lib/chai/chai.js b/public/js/lib/chai/chai.js index 77451223e0..4191939dfa 100644 --- a/public/js/lib/chai/chai.js +++ b/public/js/lib/chai/chai.js @@ -1,5 +1,5 @@ -;(function(){ +(function(){ /** * Require the module at `name`. @@ -4797,4 +4797,4 @@ } else { (this || window)["chai"] = require("chai"); } -})() \ No newline at end of file +})(); diff --git a/public/js/lib/coursewares/coursewaresHCJQFramework_0.1.9.js b/public/js/lib/coursewares/coursewaresHCJQFramework_0.1.9.js deleted file mode 100644 index 658fbee226..0000000000 --- a/public/js/lib/coursewares/coursewaresHCJQFramework_0.1.9.js +++ /dev/null @@ -1,282 +0,0 @@ -/** - * Created by nathanleniz on 2/2/15. - */ - -var widgets = []; -var editor = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { - lineNumbers: true, - mode: "text/html", - theme: 'monokai', - runnable: true, - matchBrackets: true, - autoCloseBrackets: true, - scrollbarStyle: 'null', - lineWrapping: true, - gutters: ["CodeMirror-lint-markers"], - onKeyEvent: doLinting -}); - -var defaultKeymap = { - 'Cmd-E': 'emmet.expand_abbreviation', - 'Tab': 'emmet.expand_abbreviation_with_tab', - 'Enter': 'emmet.insert_formatted_line_break_only' -}; - -emmetCodeMirror(editor, defaultKeymap); - - -// Hijack tab key to insert two spaces instead -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); - } - } -}); - -editor.setSize("100%", "auto"); - -var libraryIncludes = "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""; - - -var editorValueForIFrame; -var iFrameScript = ""; - -var delay; -// Initialize CodeMirror editor with a nice html5 canvas demo. -editor.on("keyup", function () { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); -}); - -var nodeEnv = prodOrDev === 'production' ? 'http://www.freecodecamp.com' : 'http://localhost:3001'; -function updatePreview() { - editorValueForIFrame = editor.getValue(); - var failedCommentTest = false; - if(editorValueForIFrame.match(/\<\!\-\-/gi) && editorValueForIFrame.match(/\-\-\>/gi) == null){ - failedCommentTest = true; - } - else if(editorValueForIFrame.match(/\<\!\-\-/gi) && editorValueForIFrame.match(/\<\!\-\-/gi).length > editorValueForIFrame.match(/\-\-\>/gi).length){ - failedCommentTest = true; - } - if(failedCommentTest){ - editor.setValue(editor.getValue()+ "-->"); - editorValueForIFrame = editorValueForIFrame + "-->"; - } - goodTests = 0; - var previewFrame = document.getElementById('preview'); - var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - preview.open(); - $('#testSuite').empty(); - preview.write(libraryIncludes + editor.getValue() + iFrameScript); - codeStorage.updateStorage(); - preview.close(); - -} -setTimeout(updatePreview, 300); - -/** - * "post" methods - */ - -var testResults = []; -var postSuccess = function(data) { - var testDoc = document.createElement("div"); - $(testDoc) - .html("
" + JSON.parse(data) + "
"); - $('#testSuite').append(testDoc); - testSuccess(); -}; - -var postError = function(data) { - var testDoc = document.createElement("div"); - $(testDoc) - .html("
" + JSON.parse(data) + "
"); - $('#testSuite').append(testDoc); -}; -var goodTests = 0; -var testSuccess = function() { - goodTests++; - if (goodTests === tests.length) { - showCompletion(); - } -}; - -function doLinting () { - editor.operation(function () { - for (var i = 0; i < widgets.length; ++i) - editor.removeLineWidget(widgets[i]); - widgets.length = 0; - JSHINT(editor.getValue()); - for (var i = 0; i < JSHINT.errors.length; ++i) { - var err = JSHINT.errors[i]; - if (!err) continue; - var msg = document.createElement("div"); - var icon = msg.appendChild(document.createElement("span")); - icon.innerHTML = "!!"; - icon.className = "lint-error-icon"; - msg.appendChild(document.createTextNode(err.reason)); - msg.className = "lint-error"; - widgets.push(editor.addLineWidget(err.line - 1, msg, { - coverGutter: false, - noHScroll: true - })); - } - }); -}; - -//$('#testSuite').empty(); -function showCompletion() { - var time = Math.floor(Date.now()) - started; - ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time); - $('#next-courseware-button').removeAttr('disabled'); - $('#next-courseware-button').addClass('animated tada'); - if (!userLoggedIn) { - $('#complete-courseware-dialog').modal('show'); - } - $('body').keydown(function(e) { - if (e.ctrlKey && e.keyCode == 13) { - $('#next-courseware-button').click(); - $('#next-courseware-button').unbind('click'); - } - }); -} - -/* - 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(){ - document.getElementById('scroll-locker').dispatchEvent(new CustomEvent('previewUpdateSpy', {'detail': $('.scroll-locker').scrollTop()})); - 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 editorValue; - - -var challengeSeed = challengeSeed || null; -var tests = tests || []; - - -var allSeeds = ''; -(function() { - challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; - }); -})(); - -editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; - -editor.setValue(editorValue.replace((/fccss/gi), '')); -editor.refresh(); - -var resetEditor = function resetEditor() { - document.getElementById('scroll-locker').dispatchEvent(new CustomEvent('previewUpdateSpy', {'detail': $('.scroll-locker').scrollTop()})); - editor.setValue(allSeeds.replace((/fccss/gi), '')); - updatePreview(); - codeStorage.updateStorage(); -}; - - - -/* -var challengeSeed = challengeSeed || null; -var allSeeds = ''; -(function() { - challengeSeed.forEach(function(elem) { - allSeeds += elem.replace(/fccss/g, '') + '\n'; - }); - editor.setValue(allSeeds); - (function() { - setTimeout(function() { - editor.refresh(); - }, 200); - })(); -})(); -*/ diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js deleted file mode 100644 index ce34d777be..0000000000 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ /dev/null @@ -1,395 +0,0 @@ -var widgets = []; -var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { - lineNumbers: true, - mode: "javascript", - theme: 'monokai', - runnable: true, - lint: true, - matchBrackets: true, - autoCloseBrackets: true, - scrollbarStyle: 'null', - lineWrapping: true, - gutters: ["CodeMirror-lint-markers"], - onKeyEvent: doLinting -}); -var editor = myCodeMirror; -editor.setSize("100%", "auto"); - -// Hijack tab key to enter two spaces intead -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(); - return false; - } -}); - - -var attempts = 0; -if (attempts) { - attempts = 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); - -function doLinting() { - editor.operation(function() { - for (var i = 0; i < widgets.length; ++i) - editor.removeLineWidget(widgets[i]); - widgets.length = 0; - JSHINT(editor.getValue()); - for (var i = 0; i < JSHINT.errors.length; ++i) { - var err = JSHINT.errors[i]; - if (!err) continue; - var msg = document.createElement("div"); - var icon = msg.appendChild(document.createElement("span")); - icon.innerHTML = "!!"; - icon.className = "lint-error-icon"; - msg.appendChild(document.createTextNode(err.reason)); - msg.className = "lint-error"; - widgets.push(editor.addLineWidget(err.line - 1, msg, { - coverGutter: false, - noHScroll: true - })); - } - }); -} - -$('#submitButton').on('click', function() { - bonfireExecute(); -}); - -function bonfireExecute() { - attempts++; - ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); - userTests = null; - $('#codeOutput').empty(); - var userJavaScript = myCodeMirror.getValue(); - var failedCommentTest = false; - if(userJavaScript.match(/\/\*/gi) && userJavaScript.match(/\*\//gi) == null){ - failedCommentTest = true; - } - else if(!failedCommentTest && userJavaScript.match(/\/\*/gi) && userJavaScript.match(/\/\*/gi).length > userJavaScript.match(/\*\//gi).length){ - failedCommentTest = true; - } - userJavaScript = removeComments(userJavaScript); - userJavaScript = scrapeTests(userJavaScript); - // simple fix in case the user forgets to invoke their function - - submit(userJavaScript, function(cls, message) { - if(failedCommentTest){ - myCodeMirror.setValue(myCodeMirror.getValue() + "*/"); - console.log('Caught Unfinished Comment'); - codeOutput.setValue("Unfinished mulit-line comment"); - failedCommentTest = false; - } - else if (cls) { - codeOutput.setValue(message.error); - runTests('Error', null); - } else { - codeOutput.setValue(message.output); - codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi,'')); - message.input = removeLogs(message.input); - runTests(null, message); - } - }); -} - - -var userTests; -var testSalt = Math.random(); - - -var scrapeTests = function(userJavaScript) { - - // insert tests from mongo - for (var i = 0; i < tests.length; i++) { - userJavaScript += '\n' + tests[i]; - } - - var counter = 0; - 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); - - if (!userTests) { - userTests = []; - } - userTests.push({ - "text": match[0], - "line": counter, - "err": null - }); - counter++; - match = regex.exec(userJavaScript); - } - - return userJavaScript; -}; - -function removeComments(userJavaScript) { - var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); - return userJavaScript.replace(regex, ''); -} - -function removeLogs(userJavaScript) { - return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); -} - -var pushed = false; -var createTestDisplay = function() { - if (pushed) { - userTests.pop(); - } - 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 + "
") - .appendTo($('#testSuite')); - } else { - $(testDoc) - .html( - "
" + - test.text + "
") - .appendTo($('#testSuite')); - } - }; -}; - -var expect = chai.expect; -var assert = chai.assert; -var should = chai.should(); - - -var reassembleTest = function(test, data) { - var lineNum = test.line; - var regexp = new RegExp("\/\/" + lineNum + testSalt); - return data.input.replace(regexp, test.text); -}; - -var runTests = function(err, data) { - var allTestsPassed = true; - pushed = false; - $('#testSuite').children().remove(); - if (err && userTests.length > 0) { - userTests = [{ - text: "Program Execution Failure", - err: "No user tests were run." - }]; - createTestDisplay(); - } - //Add blocks to test exploits here! - else if(editorValue.match(/if\s\(null\)\sconsole\.log\(1\);/gi)){ - allTestsPassed = false; - userTests = [{ - text: "Program Execution Failure", - err: "Invalid if (null) console.log(1); detected" - }]; - createTestDisplay(); - } - else if (userTests) { - userTests.push(false); - pushed = true; - userTests.forEach(function(chaiTestFromJSON, indexOfTestArray, - __testArray) { - try { - if (chaiTestFromJSON) { - var output = eval(reassembleTest(chaiTestFromJSON, data)); - } - } catch (error) { - allTestsPassed = false; - __testArray[indexOfTestArray].err = error.message; - } finally { - if (!chaiTestFromJSON) { - createTestDisplay(); - } - } - }); - - if (allTestsPassed) { - allTestsPassed = false; - showCompletion(); - } - - } -}; - -function showCompletion() { - var time = Math.floor(Date.now()) - started; - ga('send', 'event', 'Challenge', 'solved', challenge_Name + ', Time: ' + time + - ', Attempts: ' + attempts); - var bonfireSolution = myCodeMirror.getValue(); - var didCompleteWith = $('#completed-with').val() || null; - $.post( - '/completed-bonfire/', { - challengeInfo: { - challengeId: challenge_Id, - challengeName: challenge_Name, - completedWith: didCompleteWith, - challengeType: challengeType, - solution: bonfireSolution - } - }, - function(res) { - if (res) { - $('#complete-courseware-dialog').modal('show'); - $('#complete-courseware-dialog').keydown(function(e) { - if (e.ctrlKey && e.keyCode == 13) { - $('#next-courseware-button').click(); - } - }); - } - } - ); - -} - - -/* - 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(){ - document.getElementById('scroll-locker').dispatchEvent(new CustomEvent('previewUpdateSpy', {'detail': $('.scroll-locker').scrollTop()})); - 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 editorValue; - - -var challengeSeed = challengeSeed || null; -var tests = tests || []; - - -var allSeeds = ''; -(function() { - challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; - }); -})(); - -editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; - -myCodeMirror.setValue(editorValue); - -var resetEditor = function resetEditor() { - editor.setValue(allSeeds); - codeStorage.updateStorage(); -}; - -$(document).ready(function(){ - bonfireExecute(); -}); diff --git a/public/js/lib/coursewares/sandbox.js b/public/js/lib/coursewares/sandbox.js deleted file mode 100644 index 55c80ea99d..0000000000 --- a/public/js/lib/coursewares/sandbox.js +++ /dev/null @@ -1,87 +0,0 @@ -var printCallback; -// sends the input to the plugin for evaluation -var submit = function(code,callback) { - printCallback = callback; - // postpone the evaluation until the plugin is initialized - plugin.whenConnected( - function() { - if (requests == 0) { - startLoading(); - } - - requests++; - plugin.remote.run(code); - } - ); -}; - -// puts the message on the terminal -var print = function(cls, msg) { - printCallback(cls,msg); -}; - - -// will restart the plugin if it does not respond -var disconnectTimeout = null; -var startLoading = function() { - disconnectTimeout = setTimeout(disconnect, 3000); -}; - -var endLoading = function() { - clearTimeout(disconnectTimeout); -}; - -var disconnect = function() { - plugin.disconnect(); -}; - - -// interface provided to the plugin -var api = { - output: function(data) { - endLoading(); - //print('input', data.input); - if (data.error) { - print('Error', data); - reset(); - } else { - print(null, data); - reset(); - } - } -}; - - -// obtaining absolute path of this script -var scripts = document.getElementsByTagName('script'); -var path = scripts[scripts.length-1].src - .split('?')[0] - .split('/') - .slice(0, -1) - .join('/')+'/'; - - - -var requests; - -// (re)initializes the plugin -var reset = function() { - requests = 0; - plugin = new jailed.Plugin(path+'plugin_v0.1.5.js', api); - plugin.whenDisconnected( function() { - // give some time to handle the last responce - setTimeout( function() { - endLoading(); - console.log("resetting on fatal plugin error"); - codeOutput.setValue("Sorry, your code is either too slow, has a fatal error, or contains an infinite loop."); - reset(); - }, 10); - }); -}; - - - -// initialize everything -var plugin = null; - -reset(); diff --git a/public/json/bootcamps.json b/public/json/bootcamps.json index 41b906c20b..19b016607c 100644 --- a/public/json/bootcamps.json +++ b/public/json/bootcamps.json @@ -221,7 +221,28 @@ "cities": [ "portland" ] -}, { +}, + { + "name": "Viking Code School", + "cost": "18000", + "housing": "0", + "finance": false, + "weeks": "16", + "cities": [ + "online" + ] + }, + { + "name": "App Academy", + "cost": "18000", + "housing": "500", + "finance": false, + "weeks": "12", + "cities": [ + "san-francisco" + ] + }, + { "name": "Turing School", "cost": "17500", "housing": "400", @@ -230,13 +251,4 @@ "cities": [ "denver" ] -}, { - "name": "Free Code Camp", - "cost": "0", - "housing": "0", - "finance": false, - "weeks": "0", - "cities": [ - "online" - ] }] diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 539ce1d349..cbde7cb108 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -569,7 +569,8 @@ "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 values (second argument).", + "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": [ @@ -583,7 +584,9 @@ ], "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 }, { '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", diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index 82e1dfb639..e0b290e32b 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -13,7 +13,8 @@ "// This is a comment.", "The slash-star-star-slash comment will comment out everything between the /* and the */ characters:", "/* This is also a comment */", - "Try creating one of each." + "Try creating one of each.", + "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');", @@ -65,7 +66,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;}/**/})(), 'myName should be a string that contains at least one character in it');" ], "challengeSeed": [ "// var ourName = \"Free Code Camp\";", @@ -75,7 +76,7 @@ "// We use this function to show you the value of your variable in your output box.", "// You'll learn about functions soon.", "", - "if(typeof(myName) !== \"undefined\"){(function(v){return(v);})(myName);}" + "if(typeof(myName) !== \"undefined\"){(function(v){return v;})(myName);}" ], "type": "waypoint", "challengeType": 1 @@ -89,8 +90,8 @@ "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;}})(), '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');" ], "challengeSeed": [ "// name = \"Alan Turing\";", @@ -101,7 +102,7 @@ "// 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(myFirstName) !== \"undefined\" && typeof(myLastName) !== \"undefined\"){(function(){return(myFirstName + ', ' + myLastName);})();}" + "if(typeof(myFirstName) !== \"undefined\" && typeof(myLastName) !== \"undefined\"){(function(){return myFirstName + ', ' + myLastName;})();}" ], "type": "waypoint", "challengeType": 1 @@ -116,8 +117,8 @@ "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;}})(), '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');" ], "challengeSeed": [ "var firstNameLength = 0;", @@ -137,7 +138,7 @@ "// 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(lastNameLength) !== \"undefined\"){(function(){return(lastNameLength);})();}" + "if(typeof(lastNameLength) !== \"undefined\"){(function(){return lastNameLength;})();}" ], "type": "waypoint", "challengeType": 1 @@ -154,7 +155,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;}})(), 'The first letter of firstLetterOfLastName should be a L');" ], "challengeSeed": [ "var firstLetterOfFirstName = \"\";", @@ -172,7 +173,7 @@ "// 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.", - "(function(v){return(v);})(firstLetterOfLastName);" + "(function(v){return v;})(firstLetterOfLastName);" ], "type": "waypoint", "challengeType": 1 @@ -188,7 +189,7 @@ "Try looking at the secondLetterOfFirstName variable declaration if you get stuck." ], "tests": [ - "assert(thirdLetterOfLastName === 'v', 'The third last letter of lastName should be a \"v\"');" + "assert(thirdLetterOfLastName === 'v', 'The third letter of lastName should be a \"v\"');" ], "challengeSeed": [ "var firstName = \"Ada\";", @@ -203,7 +204,7 @@ "// 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.", - "(function(v){return(v);})(thirdLetterOfLastName);" + "(function(v){return v;})(thirdLetterOfLastName);" ], "type": "waypoint", "challengeType": 1 @@ -235,7 +236,7 @@ "// 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.", - "(function(v){return(v);})(lastLetterOfLastName);" + "(function(v){return v;})(lastLetterOfLastName);" ], "type": "waypoint", "challengeType": 1 @@ -267,7 +268,7 @@ "// 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.", - "(function(v){return(v);})(secondToLastLetterOfLastName);" + "(function(v){return v;})(secondToLastLetterOfLastName);" ], "type": "waypoint", "challengeType": 1 @@ -282,7 +283,7 @@ "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)){return true;}else{return false;}})(), 'Make the variable sum equal 20');" ], "challengeSeed": [ "var sum = 10 + 0; //make this equal to 20 by changing the 0 into the appropriate number.", @@ -290,7 +291,7 @@ "// 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.", - "(function(z){return('sum='+z);})(sum);" + "(function(z){return 'sum='+z;})(sum);" ], "type": "waypoint", "challengeType": 1 @@ -305,7 +306,7 @@ "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;}})(), 'Make the variable difference equal 12');" ], "challengeSeed": [ "var difference = 45 - 0; //make this equal to 12 by changing the 0 into the appropriate number.", @@ -313,7 +314,7 @@ "// 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.", - "(function(z){return('difference='+z);})(difference);" + "(function(z){return 'difference='+z;})(difference);" ], "type": "waypoint", "challengeType": 1 @@ -328,7 +329,7 @@ "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;}})(), 'Make the variable product equal 80.');" ], "challengeSeed": [ "var product = 8 * 0; //make this equal to 80 by changing the 0 into the appropriate number.", @@ -336,7 +337,7 @@ "// 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.", - "(function(z){return('product='+z);})(product);" + "(function(z){return 'product='+z;})(product);" ], "type": "waypoint", "challengeType": 1 @@ -351,7 +352,7 @@ "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(/\\//g)){return true;}else{return false;}})(), 'Make the variable quotient equal 2.');" ], "challengeSeed": [ "var quotient = 66 / 0; //make this equal to 2 by changing the 0 into the appropriate number.", @@ -359,7 +360,7 @@ "// 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.", - "(function(z){return('quotient='+z);})(quotient);" + "(function(z){return 'quotient='+z;})(quotient);" ], "type": "waypoint", "challengeType": 1 @@ -373,7 +374,7 @@ "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;}})(), 'myDecimal should be a decimal point number.');" ], "challengeSeed": [ "// var ourDecimal = 5.7;", @@ -384,7 +385,7 @@ "// 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.", - "(function(){if(typeof(myDecimal) !== \"undefined\"){return(myDecimal);}})();" + "(function(){if(typeof(myDecimal) !== \"undefined\"){return myDecimal;}})();" ], "type": "waypoint", "challengeType": 1 @@ -398,8 +399,8 @@ "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;}})(), '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.');" ], "challengeSeed": [ "var quotient = 4.4 / 2.0; // equals 2.2", @@ -409,7 +410,7 @@ "// 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.", - "(function(y){return('product='+y);})(product);" + "(function(y){return 'product='+y;})(product);" ], "type": "waypoint", "challengeType": 1 @@ -439,7 +440,7 @@ "// 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.", - "(function(z){return(z);})(myArray);" + "(function(z){return z;})(myArray);" ], "type": "waypoint", "challengeType": 1 @@ -464,7 +465,7 @@ "// 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);})();}" + "if(typeof(myArray) !== \"undefined\"){(function(){return myArray;})();}" ], "type": "waypoint", "challengeType": 1 @@ -483,7 +484,7 @@ "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;}})(), 'The variable myData should equal the first value of myArray');" ], "challengeSeed":[ "//var ourArray = [1,2,3];", @@ -496,7 +497,7 @@ "// 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\" && typeof(myData) !== \"undefined\"){(function(y,z){return('myArray = ' + JSON.stringify(y) + ', myData = ' + JSON.stringify(z));})(myArray, myData);}" + "if(typeof(myArray) !== \"undefined\" && typeof(myData) !== \"undefined\"){(function(y,z){return 'myArray = ' + JSON.stringify(y) + ', myData = ' + JSON.stringify(z);})(myArray, myData);}" ], "type": "waypoint", "challengeType": 1 @@ -513,8 +514,8 @@ "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(/[0]/g).length >= 1 && editor.getValue().match(/=/g).length >= 2){return(true);}else{return(false);}})(), 'You should be using indexes to modify the values in myArray');" + "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');" ], "challengeSeed":[ "var ourArray = [1,2,3];", @@ -527,7 +528,7 @@ "// 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);})();}" + "if(typeof(myArray) !== \"undefined\"){(function(){return myArray;})();}" ], "type": "waypoint", "challengeType": 1 @@ -543,8 +544,8 @@ "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), '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)');" ], "challengeSeed": [ "//var numbers = [1,2,3];", @@ -561,7 +562,7 @@ "// 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.", - "(function(y, z){return('myArray = ' + JSON.stringify(y) + ' & removed = ' + JSON.stringify(z));})(myArray, removed);" + "(function(y, z){return 'myArray = ' + JSON.stringify(y) + ' & removed = ' + JSON.stringify(z);})(myArray, removed);" ], "type": "waypoint", "challengeType": 1 @@ -575,7 +576,7 @@ "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), 'myArray should only have the first two values left([\"John\", 23, [\"dog\", 3]])');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", @@ -592,7 +593,7 @@ "// 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.", - "(function(z){return('myArray = ' + JSON.stringify(z));})(myArray);" + "(function(z){return 'myArray = ' + JSON.stringify(z);})(myArray);" ], "type": "waypoint", "challengeType": 1 @@ -606,8 +607,8 @@ "Take the myArray array and shift() the first value off of it." ], "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 first two values left([\"John\", 23])');", - "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), '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\"');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", @@ -623,7 +624,7 @@ "", "// We use this function to show you the value of your variable in your output box.", "// You'll learn about functions soon.", - "(function(y, z){return('myArray = ' + JSON.stringify(y) + ' & myRemoved = ' + JSON.stringify(z));})(myArray, myRemoved);" + "(function(y, z){return 'myArray = ' + JSON.stringify(y) + ' & myRemoved = ' + JSON.stringify(z);})(myArray, myRemoved);" ], "type": "waypoint", "challengeType": 1 @@ -637,7 +638,7 @@ "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(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]])');" ], "challengeSeed": [ "var ourArray = [\"Stimpson\", \"J\", [\"cat\"]];", @@ -655,7 +656,7 @@ "// 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.", - "(function(y, z){return('myArray = ' + JSON.stringify(y));})(myArray);" + "(function(y, z){return 'myArray = ' + JSON.stringify(y);})(myArray);" ], "type": "waypoint", "challengeType": 1 @@ -668,13 +669,13 @@ "In JavaScript, we can divide up our code into reusable parts called functions.", "Here's an example of a function:", "function functionName (a, b) {", - "  return(a + b);", + "  return a + b;", "}", - "We can \"call\" our function like this: functionName();, and it will run and return it's return value to us.", - "Create and call a function called myFunction." + "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." ], "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\" && 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');" ], "challengeSeed":[ "var a = 4;", @@ -684,9 +685,9 @@ " 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.", + "// 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.", "", "", "", @@ -696,7 +697,7 @@ "// You'll learn about functions soon.", "if(typeof(myFunction) !== \"undefined\"){", "var f=myFunction(a,b);", - "(function(){return(f);})();", + "(function(){return f;})();", "}" ], "type": "waypoint", @@ -717,14 +718,14 @@ " \"enemies\": [\"Water\", \"Dogs\"]", "};", "", - "Objects are useful for storing data in a structured way, and can represents real world objects, like a cats.", - "Let's try to make an Object that represents a dog called myDog!" + "Objects are useful for storing data in a structured way, and can represents 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)!" ], "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), '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');" ], "challengeSeed":[ "//var ourDog = {", @@ -745,7 +746,7 @@ "", "// Only change code above this line.", "// We use this function to show you the value of your variable in your output box.", - "(function(z){return(z);})(myDog);" + "(function(z){return z;})(myDog);" ], "type": "waypoint", "challengeType": 1 @@ -794,7 +795,7 @@ "", "// Only change code above this line.", "// We use this function to show you the value of your variable in your output box.", - "(function(z){return(z);})(myDog);" + "(function(z){return z;})(myDog);" ], "type": "waypoint", "challengeType": 1 @@ -843,7 +844,7 @@ "  ourArray.push(i);", "  i++;", "}", - "Let's try getting a for loop to work by pushing values to an array." + "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.');", @@ -877,13 +878,13 @@ " //Change the 0 to Math.random()", " // Only change code below this line.", "", - " return(0);", + " return 0;", "", "// 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());})();" + "(function(){return myFunction();})();" ], "type": "waypoint", "challengeType": 1 @@ -894,16 +895,16 @@ "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", - "This technique gives us a whole number between zero and nine", + "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.", + "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" + "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 but 10 to make it a number that\\'s greater then zero');", + "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');" ], "challengeSeed":[ @@ -912,13 +913,13 @@ "", " // Only change code below this line.", "", - " return(Math.random());", + " return Math.random();", "", " // 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());})();" + "(function(){return myFunction();})();" ], "type": "waypoint", "challengeType": 1 @@ -935,7 +936,7 @@ "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((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');" ], "challengeSeed":[ "var min = 0;", @@ -944,12 +945,12 @@ " // Make myFunction return a random number between zero and nine instead of a decimal", " // Only change code below this line.", "", - " return(Math.random());", + " return Math.random();", "}", "", "// 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());})();" + "(function(){return myFunction();})();" ], "type": "waypoint", "challengeType": 1 @@ -963,16 +964,16 @@ "if statements require some sort of boolean condition to evaluate.", "Example:", " if (1 === 2) {", - "  return(true);", + "  return true;", "}", "else {", - "  return(false);", + "  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." ], "tests":[ - "assert((function(){if(myFunction() === \"heads\" || myFunction() === \"tails\"){return(true);}else{return(false);}})(), 'myFunction should either return heads or tails');", + "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');" ], @@ -988,7 +989,7 @@ "", "// 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());})();" + "(function(){return myFunction();})();" ], "type": "waypoint", "challengeType": 1 @@ -1021,8 +1022,8 @@ "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", - " return(testString.match(expression).length);", - "})();(function(){return(test);})();" + " return testString.match(expression).length;", + "})();(function(){return test;})();" ], "type": "waypoint", "challengeType": 1 @@ -1039,7 +1040,7 @@ ], "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(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'You should be using the following expression /\\\\d+/gi to find the numbers in the testString');" ], "challengeSeed":[ "var test = (function() {", @@ -1051,8 +1052,8 @@ "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", - " return(testString.match(expression).length);", - "})();(function(){return(test);})();" + " return testString.match(expression).length;", + "})();(function(){return test;})();" ], "type": "waypoint", "challengeType": 1 @@ -1069,7 +1070,7 @@ ], "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(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'You should be using the following expression /\\\\s+/gi to find the spaces in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1081,8 +1082,8 @@ "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", - " return(testString.match(expression).length);", - "})();(function(){return(test);})();" + " return testString.match(expression).length;", + "})();(function(){return test;})();" ], "type": "waypoint", "challengeType": 1 @@ -1097,7 +1098,7 @@ ], "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(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'You should be using the following expression /\\\\S/gi to find non-space characters in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1109,8 +1110,8 @@ "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", - " return(testString.match(expression).length);", - "})();(function(){return(test);})();" + " return testString.match(expression).length;", + "})();(function(){return test;})();" ], "type": "waypoint", "challengeType":1 @@ -1130,7 +1131,7 @@ "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((function(){if(editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi) !== null){return editor.match(/Math\\.floor\\(\\s?Math\\.random\\(\\)\\s?\\*\\s?\\(\\s?3\\s?\\-\\s?1\\s?\\+\\s?1\\s?\\)\\s?\\)\\s?\\+\\s?1;/gi).length >= 3;}else{return false;}})(), 'You should have used Math.floor(Math.random() * (3 - 1 + 1)) + 1; three times to generate your random numbers.')" ], "challengeSeed":[ "fccss", @@ -1153,7 +1154,7 @@ " if(slotOne !== undefined && slotTwo !== undefined && slotThree !== undefined){", " $(\".logger\").html(slotOne + \" \" + slotTwo + \" \" + slotThree);", " }", - " return([slotOne, slotTwo, slotThree]);", + " return [slotOne, slotTwo, slotThree];", " }", "", " $(document).ready(function(){", @@ -1277,14 +1278,14 @@ "Now that our slots will each generate random numbers, we need to check whether they've all returned the same number.", "If they have, we should notify our user that they've won.", "Otherwise, we should return null, which is a JavaScript data structure that means nothing.", - "If all three numbers match, we should change the value of win to the number that we have three of or leave it as null.", + "If all three numbers match, we should return the number that we have in three of slots or leave it as null.", "Let's create an if statement with multiple conditions in order to check whether all numbers are equal.", "if(slotOne !== slotTwo || slotTwo !== slotThree){", - "  return(null);", + "  return null;", "}" ], "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", @@ -1313,7 +1314,7 @@ " $(\".logger\").append(\" \" + slotTwo);", " $(\".logger\").append(\" \" + slotThree);", " }", - " return([slotOne, slotTwo, slotThree]);", + " return [slotOne, slotTwo, slotThree];", " }", "", " $(document).ready(function(){", @@ -1442,8 +1443,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((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')" ], "challengeSeed":[ "fccss", @@ -1468,7 +1469,7 @@ " // Only change code above this line.", " ", " if(slotOne !== slotTwo || slotTwo !== slotThree){", - " return(null);", + " return null;", " }", " ", " if(slotOne !== undefined && slotTwo !== undefined && slotThree !== undefined){", @@ -1477,7 +1478,7 @@ " $(\".logger\").append(\" \" + slotThree);", " }", " ", - " return([slotOne, slotTwo, slotThree]);", + " return [slotOne, slotTwo, slotThree];", " }", "", " $(document).ready(function(){", @@ -1635,7 +1636,7 @@ " // Only change code above this line.", " ", " if(slotOne !== slotTwo || slotTwo !== slotThree){", - " return(null);", + " return null;", " }", " ", " if(slotOne !== undefined && slotTwo !== undefined && slotThree !== undefined){", @@ -1644,7 +1645,7 @@ " $('.logger').append(' ' + slotThree);", " }", " ", - " return([slotOne, slotTwo, slotThree]);", + " return [slotOne, slotTwo, slotThree];", " }", "", " $(document).ready(function(){", diff --git a/seed/challenges/basic-ziplines.json b/seed/challenges/basic-ziplines.json index 37e547c4e3..d97d049b80 100644 --- a/seed/challenges/basic-ziplines.json +++ b/seed/challenges/basic-ziplines.json @@ -45,6 +45,7 @@ "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.", + "Hint: If you don't want to start from scratch, you can fork this simple Bootstrap portfolio template on CodePen: http://codepen.io/FreeCodeCamp/pen/mJNqQj.", "Here are the user stories you must enable, and optional bonus user stories:", "User Story: As a user, I can access all of the portfolio webpage's content just by scrolling.", "User Story: As a user, I can click different buttons that will take me to the portfolio creator's different social media pages.", diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 34b5451572..5f5d0ff63e 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -176,8 +176,8 @@ "title": "Center Text with Bootstrap", "difficulty": 2.03, "description": [ - "Now that we're using Bootstrap, we can center our heading elements to make them look better. All we need to do is add the class text-center to our h1 and h2 elements.", - "Remember that you can add several classes to the same element by separating each of them with a space, like this: <h2 class=\"text-red text-center\">your text</h2>." + "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>." ], "tests": [ "assert($(\"h2\").hasClass(\"text-center\"), 'Your h2 element should be centered by applying the class text-center')" @@ -347,7 +347,7 @@ "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." ], @@ -614,7 +614,7 @@ "Note that these buttons still need the btn and btn-block classes." ], "tests": [ - "assert(new RegExp(\"delete\",\"gi\").test($(\"button\").text()), 'Create a new button element with the text \"delete\".')", + "assert(new RegExp(\"Delete\",\"gi\").test($(\"button\").text()), 'Create a new button element with the text \"Delete\".')", "assert($(\"button.btn-block.btn\").length > 2, 'All of your Bootstrap buttons should have the btn and btn-block classes.')", "assert($(\"button\").hasClass(\"btn-danger\"), 'Your new button should have the class btn-danger.')", "assert(editor.match(/<\\/button>/g) && editor.match(/