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/commonFrameWork.js b/public/js/lib/coursewares/commonFrameWork.js new file mode 100644 index 0000000000..0cdac5519f --- /dev/null +++ b/public/js/lib/coursewares/commonFrameWork.js @@ -0,0 +1,537 @@ +var editor; +var widgets = []; +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"], + onKeyEvent: doLinting +}); + +var myCodeMirror = editor; + +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 +}; +codeStorage.hasSaved = function(){ + return ( updateTimeoutId === null ); +}; +codeStorage.onSave = function(func){ + codeStorage.eventArray.push(func); +}; +codeStorage.setSaveKey = function(key){ + codeStorage.keyValue = key + 'Val'; +}; +codeStorage.getEditorValue = function(){ + return ('' + localStorage.getItem(codeStorage.keyValue)); +}; + +codeStorage.isAlive = function() { + var val = this.getEditorValue(); + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); +}; +codeStorage.updateStorage = function(){ + if(typeof(Storage) !== undefined) { + var value = editor.getValue(); + localStorage.setItem(codeStorage.keyValue, value); + } else { + var debugging = false; + if( debugging ){ + console.log('no web storage'); + } + } + codeStorage.updateTimeoutId = null; + codeStorage.eventArray.forEach(function(func){ + func(); + }); +}; +(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 + } + } +})(); +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'; + }); +})(); + +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 + })); + } + }); +} + +/*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); + } + }, + "Ctrl-Enter": function() { + bonfireExecute(); + return false; + } +}); + +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); +}); + +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(s){ + return(s.replace(/\$/gi, "j$").replace(/document/gi, "jdocument").replace(/jQuery/gi, "jjQuery")); +} + +function safeHTMLRun(test){ + 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]; + 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; + 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(); + } + } +} + +if(typeof prodOrDev !== 'undefined') { + var nodeEnv = prodOrDev === 'production' ? 'http://www.freecodecamp.com' : 'http://localhost:3001'; + if(challengeType === "0") + { + 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(/\$\(\$\)/gi) ) { + safeHTMLRun(false); + } + } + + 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() { + 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(); + } + }); + } + } + ); +} + +var resetEditor = function resetEditor() { + editor.setValue(allSeeds.replace((/fccss/gi), '')); + updatePreview(); + 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 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) { + //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() { + goodTests = 0; + attempts++; + ga('send', 'event', 'Challenge', 'ran-code', challenge_Name); + userTests = null; + $('#testSuite').empty(); + if(challengeType !== "0"){ + 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 + + if(userJavaScript.match(/function/gi)){ + if(userJavaScript.match(/function\s+?\(|function\s+\w+\s*?\(/gi)){ + 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); + } + }); + } + else{ + codeOutput.setValue("Unsafe or Unfinished function declaration"); + } + } + } + else { + editorValueForIFrame = editor.getValue(); + if (failedCommentTest) { + editor.setValue(editor.getValue() + "-->"); + editorValueForIFrame = editorValueForIFrame + "-->"; + } + if(!editor.getValue().match(/\$\(\$\)/gi) && challengeType === "0") { + safeHTMLRun(true); + } + } +} + +$('#submitButton').on('click', function() { + bonfireExecute(); +}); + +$(document).ready(function(){ + editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; + myCodeMirror.setValue(editorValue); + bonfireExecute(); +}); 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 89dc0d9dd6..0000000000 --- a/public/js/lib/coursewares/coursewaresHCJQFramework_0.1.9.js +++ /dev/null @@ -1,280 +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(){ - 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() { - 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 ddeb9519f4..0000000000 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ /dev/null @@ -1,394 +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(){ - 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 index 55c80ea99d..75452114eb 100644 --- a/public/js/lib/coursewares/sandbox.js +++ b/public/js/lib/coursewares/sandbox.js @@ -73,7 +73,9 @@ var reset = function() { 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."); + if(challengeType === 0){ + codeOutput.setValue("Sorry, your code is either too slow, has a fatal error, or contains an infinite loop."); + } reset(); }, 10); }); diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade index 769311c29c..58a00b5f54 100644 --- a/server/views/coursewares/showBonfire.jade +++ b/server/views/coursewares/showBonfire.jade @@ -123,7 +123,9 @@ block content form.code .form-group.codeMirrorView textarea#codeEditor(autofocus=true, style='display: none;') - script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.6.js') + script(src='/js/lib/coursewares/commonFrameWork.js') + script. + editor.setOption("mode", "javascript"); #complete-courseware-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation diff --git a/server/views/coursewares/showHTML.jade b/server/views/coursewares/showHTML.jade index 85970895f9..65659f2f9c 100644 --- a/server/views/coursewares/showHTML.jade +++ b/server/views/coursewares/showHTML.jade @@ -31,10 +31,11 @@ block content for sentence in details p.wrappable.negative-10!= sentence .negative-bottom-margin-30 + label.negative-10.btn.btn-primary.btn-block#submitButton + i.fa.fa-play + |   Run code (ctrl + enter) + br if (user) - label.btn.btn-primary.btn-block.negative-10#next-courseware-button - .ion-checkmark-circled - |   Go to my next challenge (ctrl + enter) .button-spacer .btn-group.input-group.btn-group-justified label.btn.btn-success#trigger-reset-modal @@ -71,6 +72,9 @@ block content form.code .codeMirrorView textarea#codeEditor(autofocus=true, style='display: none;') + script(src = '/js/lib/coursewares/commonFrameWork.js') + script. + editor.setOption("mode", "text/html"); .col-md-4.col-lg-3 .hidden-xs.hidden-sm img.iphone-position.iframe-scroll(src="https://s3.amazonaws.com/freecodecamp/iphone6-frame.png", style = "z-index: -2;") @@ -86,6 +90,8 @@ block content .text-center .animated.zoomInDown.delay-half span.completion-icon.ion-checkmark-circled.text-primary - a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress + if(user) + a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge') Go to the next challenge + else + a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress include ../partials/challenge-modals - script(src="/js/lib/coursewares/coursewaresHCJQFramework_0.1.9.js") diff --git a/server/views/coursewares/showJS.jade b/server/views/coursewares/showJS.jade index 85ab33adaa..2d8acf85a3 100644 --- a/server/views/coursewares/showJS.jade +++ b/server/views/coursewares/showJS.jade @@ -72,7 +72,9 @@ block content form.code .codeMirrorView textarea#codeEditor(autofocus=true, style='display: none;') - script(src='/js/lib/coursewares/coursewaresJSFramework_0.0.6.js') + script(src = '/js/lib/coursewares/commonFrameWork.js') + script. + editor.setOption("mode", "javascript"); #complete-courseware-dialog.modal(tabindex='-1') .modal-dialog.animated.zoomIn.fast-animation .modal-content