diff --git a/client/commonFramework/add-test-to-string.js b/client/commonFramework/add-test-to-string.js index b649e14678..3a0f47b1f6 100644 --- a/client/commonFramework/add-test-to-string.js +++ b/client/commonFramework/add-test-to-string.js @@ -7,13 +7,10 @@ window.common = (function({ common = { init: [] }}) { '(.*\\.should\\..*\\;)/' ); - common.addTestsToString = function(code) { + common.addTestsToString = function({ code, tests = [], ...rest }) { const userTests = []; - // insert tests from mongo - for (var i = 0; i < common.tests.length; i++) { - code += '\n' + common.tests[i]; - } + code = tests.reduce((code, test) => '\n' + code + test, code); var counter = 0; var match = BDDregex.exec(code); @@ -34,7 +31,7 @@ window.common = (function({ common = { init: [] }}) { match = BDDregex.exec(code); } - return { code, userTests }; + return { ...rest, code, userTests }; }; return common; diff --git a/client/commonFramework/bindings.js b/client/commonFramework/bindings.js index 805913b6a1..b994eca55e 100644 --- a/client/commonFramework/bindings.js +++ b/client/commonFramework/bindings.js @@ -1,4 +1,4 @@ -window.common = (function({ $, common = { init: [] }}) { +window.common = (function({ $, Rx, common = { init: [] }}) { common.ctrlEnterClickHandler = function ctrlEnterClickHandler(e) { // ctrl + enter or cmd + enter @@ -15,12 +15,6 @@ window.common = (function({ $, common = { init: [] }}) { } }; - common.resetEditor = function resetEditor() { - common.editor.setValue(common.replaceSafeTags(common.seed)); - common.executeChallenge(true); - common.codeStorage.updateStorage(); - }; - common.init.push(function($) { var $marginFix = $('.innerMarginFix'); @@ -133,20 +127,18 @@ window.common = (function({ $, common = { init: [] }}) { } }); - $('#submitButton').on('click', function() { - common.executeChallenge(true); - }); + common.submitBtn$ = Rx.Observable.fromEvent($('#submitButton'), 'click'); - if (common.editor) { - $('#reset-button').on('click', common.resetEditor); - } + common.resetBtn$ = Rx.Observable.fromEvent($('#reset-button'), 'click'); if (common.challengeName) { window.ga('send', 'event', 'Challenge', 'load', common.challengeName); } $('#complete-courseware-dialog').on('hidden.bs.modal', function() { - common.editor.focus(); + if (common.editor.focus) { + common.editor.focus(); + } }); $('#trigger-issue-modal').on('click', function() { diff --git a/client/commonFramework/code-storage.js b/client/commonFramework/code-storage.js index df3de11308..d2999b2e2f 100644 --- a/client/commonFramework/code-storage.js +++ b/client/commonFramework/code-storage.js @@ -7,6 +7,15 @@ window.common = (function(global) { var codeStorage = { getStoredValue(key) { + if ( + !localStorage || + typeof localStorage.getItem !== 'function' || + !key || + typeof key !== 'string' + ) { + console.log('unable to save to storage'); + return ''; + } return '' + localStorage.getItem(key + 'Val'); }, @@ -20,7 +29,7 @@ window.common = (function(global) { updateStorage(key, code) { if ( !localStorage || - typeof localStorage !== 'function' || + typeof localStorage.setItem !== 'function' || !key || typeof key !== 'string' ) { @@ -28,6 +37,7 @@ window.common = (function(global) { return code; } localStorage.setItem(key + 'Val', code); + return code; } }; diff --git a/client/commonFramework/create-editor.js b/client/commonFramework/create-editor.js index 90283fc4d1..809b71db33 100644 --- a/client/commonFramework/create-editor.js +++ b/client/commonFramework/create-editor.js @@ -10,7 +10,7 @@ window.common = (function(global) { if ( !CodeMirror || - challengeType === challengeTypes.BONFIRE || + challengeType === challengeTypes.BASEJUMP || challengeType === challengeTypes.ZIPLINE || challengeType === challengeTypes.VIDEO || challengeType === challengeTypes.STEP || diff --git a/client/commonFramework/detect-loops-stream.js b/client/commonFramework/detect-loops-stream.js index 193b28165d..e60c49eea9 100644 --- a/client/commonFramework/detect-loops-stream.js +++ b/client/commonFramework/detect-loops-stream.js @@ -35,7 +35,7 @@ window.common = (function(global) { this.plugin = new jailed.Plugin(path + 'plugin.js'); this.startTimeout(); this.plugin.whenConnected(() => { - this.endTimeout(); + this.cancelTimout(); }); }, destroyPlugin() { @@ -45,8 +45,8 @@ window.common = (function(global) { // sends the input to the plugin for evaluation - common.detectLoops = function detectLoops({ code = '', ...rest }) { - return new Observable(function(observer) { + common.detectLoops$ = function detectLoops$({ code = '', ...rest }) { + return Observable.create(function(observer) { const sandbox = Object.create(Sandbox); sandbox.createPlugin(); diff --git a/client/commonFramework/end.js b/client/commonFramework/end.js index 42e2b2832c..0ae2026670 100644 --- a/client/commonFramework/end.js +++ b/client/commonFramework/end.js @@ -1,21 +1,72 @@ $(document).ready(function() { - var common = window.common; + const common = window.common; + const { Observable } = window.Rx; + const { challengeName, challengeType, challengeTypes } = common; common.init.forEach(function(init) { init($); }); + + common.resetBtn$ + .doOnNext(() => { + common.editor.setValue(common.replaceSafeTags(common.seed)); + }) + .flatMap(() => { + return common.executeChallenge$(); + }) + .subscribe( + ({ output, original }) => { + common.codeStorage.updateStorage(challengeName, original); + common.updateOutputDisplay('' + output); + }, + ({ err }) => { + common.updateOutputDisplay('' + err); + } + ); + + common.submitBtn$ + .flatMap(() => { + return common.executeChallenge$(); + }) + .subscribe( + ({ output, original, userTests }) => { + common.updateOutputDisplay(output); + common.codeStorage.updateStorage(challengeName, original); + } + ); + var $preview = $('#preview'); - if (typeof $preview.html() !== 'undefined') { + if ($preview.html()) { $preview.load(function() { - common.executeChallenge(true); + common.executeChallenge() + .subscribe( + ({ output = '' }) => { + common.updateOutputDisplay(output); + }, + ({ err }) => { + common.updateOutputDisplay('' + err); + } + ); }); } else if ( - common.challengeType !== '2' && - common.challengeType !== '3' && - common.challengeType !== '4' && - common.challengeType !== '7' + challengeType !== '2' && + challengeType !== '3' && + challengeType !== '4' && + challengeType !== '7' ) { - common.executeChallenge(true); + common.executeChallenge$() + .subscribe( + ({ original }) => { + // common.updateOutputDisplay('' + output); + common.codeStorage.updateStorage(challengeName, original); + }, + ({ err }) => { + if (err.stack) { + console.error(err); + } + common.updateOutputDisplay('' + err); + } + ); } }); diff --git a/client/commonFramework/execute-challenge-stream.js b/client/commonFramework/execute-challenge-stream.js index cfc2b48b28..dfcfdf0d9e 100644 --- a/client/commonFramework/execute-challenge-stream.js +++ b/client/commonFramework/execute-challenge-stream.js @@ -29,25 +29,24 @@ window.common = (function(global) { // checks if the number of opening comments(/*) matches the number of // closing comments(*/) - Observable.just({ code }) - .flatMap(code => { + return Observable.just({ code }) + .flatMap(({ code }) => { if ( code.match(/\$\s*?\(\s*?\$\s*?\)/gi) && openingComments && openingComments.length > code.match(/\*\//gi).length ) { - return Observable.just({ + return Observable.throw({ err: 'SyntaxError: Unfinished multi-line comment', - code: code + code }); } if (code.match(detectUnsafeJQ)) { - return Observable.just({ + return Observable.throw({ err: 'Unsafe $($)', - output: 'Unsafe $($)', - code: code + code }); } @@ -55,9 +54,9 @@ window.common = (function(global) { code.match(/function/g) && !code.match(detectFunctionCall) ) { - return Observable.just({ + return Observable.throw({ err: 'SyntaxError: Unsafe or unfinished function declaration', - code: code + code }); } @@ -68,50 +67,54 @@ window.common = (function(global) { openingComments && openingComments.length > closingComments.length ) { - return Observable.just({ + return Observable.throw({ err: 'SyntaxError: Unfinished HTML comment', - code: code + code }); } } if (code.match(detectUnsafeConsoleCall)) { - return Observable.just({ + return Observable.throw({ err: 'Invalid if (null) console.log(1); detected', - code: code + code }); } // add head and tail and detect loops - return Observable.just({ code: head + code + tail }) - .map(code => { - if (common.challengeType === common.challengeTypes.HTML) { - return common.getScriptCode(code); - } + return Observable.just({ code: head + code + tail, original: code }); + }) + .map(data => { + if (common.challengeType === common.challengeTypes.HTML) { + return common.getScriptCode(data); + } - return common.addTestsToString( - common.removeComments(code), - common.tests.slice() - ); - }) - .flatMap(common.detectLoops) - .flatMap(({ err, code, data, userTests }) => { - if (err) { - return Observable.just({ - err, - code, - data - }); - } + return common.addTestsToString(Object.assign( + data, + { + code: common.removeComments(code), + tests: common.tests.slice() + } + )); + }) + .flatMap(common.detectLoops$) + .flatMap(({ err, code, data, userTests, original }) => { + if (err) { + return Observable.throw({ err }); + } - return common.runTests$({ - output: data.output.replace(/\\\"/gi, ''), - data, - code, - userTests - }); + return common.runTests$({ + data, + code, + userTests, + original, + output: data.output.replace(/\\\"/gi, '') }); - + }) + .catch(e => { + return e && e.err ? + Observable.throw(e) : + Observable.throw({ err: e }); }); }; diff --git a/client/commonFramework/init.js b/client/commonFramework/init.js index 1b57cc9230..b508a421ca 100644 --- a/client/commonFramework/init.js +++ b/client/commonFramework/init.js @@ -3,9 +3,11 @@ window.common = (function(global) { // all classes should be stored here // called at the beginning of dom ready const { + Rx: { config }, common = { init: [] } } = global; + config.longStackSupport = true; common.head = common.head || []; common.tail = common.tail || []; common.salt = Math.random(); @@ -70,7 +72,7 @@ window.common = (function(global) { }; const commentRegex = /(\/\*[^(\*\/)]*\*\/)|([ \n]\/\/[^\n]*)/g; - common.removeLogs = function removeComments(str) { + common.removeComments = function removeComments(str) { return str.replace(commentRegex, ''); }; diff --git a/client/commonFramework/output-display.js b/client/commonFramework/output-display.js index 1fea84fee9..7842e16da8 100644 --- a/client/commonFramework/output-display.js +++ b/client/commonFramework/output-display.js @@ -28,23 +28,21 @@ window.common = (function(global) { } ); - codeOutput.setValue(` - /** - * Your output will go here. - * Console.log() -type statements - * will appear in your browser\'s - * DevTools JavaScript console. - */' - `); + codeOutput.setValue(`/** + * Your output will go here. + * Console.log() -type statements + * will appear in your browser\'s + * DevTools JavaScript console. + */'`); codeOutput.setSize('100%', '100%'); - common.updateOutputDisplay = function updateOutputDisplay(str) { + common.updateOutputDisplay = function updateOutputDisplay(str = '') { codeOutput.setValue(str); return str; }; - common.appendToOutputDisplay = function appendToOutputDisplay(str) { + common.appendToOutputDisplay = function appendToOutputDisplay(str = '') { codeOutput.setValue(codeOutput.getValue() + str); return str; }; diff --git a/gulpfile.js b/gulpfile.js index 693599c721..034491cc5b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -108,20 +108,24 @@ var paths = { commonFramework: [ 'init', + 'bindings', + 'add-test-to-string', + 'add-faux-stream', 'code-storage', 'code-uri', 'create-editor', 'detect-loops-stream', 'display-test-results', 'execute-challenge-stream', - 'out-display', + 'output-display', 'phone-scroll-lock', 'report-issue', 'run-tests-stream', 'show-completion', 'step-challenge', 'test-script-stream', - 'update-preview' + 'update-preview', + 'end' ], less: './client/less/main.less',