// common namespace // all classes should be stored here var common = common || { // init is an array of functions that are // called at the beginning of dom ready init: [] }; common.challengeName = common.challengeName || window.challenge_Name ? window.challenge_Name : ''; common.challengeType = common.challengeType || window.challengeType ? window.challengeType : 0; common.challengeId = common.challengeId || window.challenge_Id; common.challengeSeed = common.challengeSeed || window.challengeSeed ? window.challengeSeed : []; common.seed = common.challengeSeed.reduce(function(seed, line) { return seed + line + '\n'; }, ''); // store code in the URL common.codeUri = (function(common, encode, decode, location, history) { var codeUri = { encode: function(code) { return encode(code); }, decode: function(code) { try { return decode(code); } catch (ignore) { return null; } }, isInQuery: function(query) { var decoded = codeUri.decode(query); if (!decoded || typeof decoded.split !== 'function') { return false; } return decoded .split('?') .splice(1) .reduce(function(found, param) { var key = param.split('=')[0]; if (key === 'solution') { return true; } return found; }, false); }, isAlive: function() { return codeUri.isInQuery(location.search) || codeUri.isInQuery(location.hash); }, parse: function() { var query; if (location.search && codeUri.isInQuery(location.search)) { query = location.search.replace(/^\?/, ''); if (history && typeof history.replaceState === 'function') { history.replaceState( history.state, null, location.href.split('?')[0] ); location.hash = '#?' + query; } } else { query = location.hash.replace(/^\#\?/, ''); } if (!query) { return null; } return query .split('&') .reduce(function(solution, param) { var key = param.split('=')[0]; var value = param.split('=')[1]; if (key === 'solution') { return codeUri.decode(value); } return solution; }, null); }, querify: function(solution) { location.hash = '?solution=' + codeUri.encode(solution); return solution; } }; common.init.push(function() { codeUri.parse(); }); return codeUri; }(common, encodeURIComponent, decodeURIComponent, location, history)); // codeStorage common.codeStorageFactory = (function($, localStorage, codeUri) { 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(); // store in localStorage localStorage.setItem(this.keyValue, value); // also store code in URL codeUri.querify(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, common.codeUri)); common.codeOutput = (function(CodeMirror, document, challengeType) { if (!CodeMirror) { return {}; } if ( challengeType === '0' || challengeType === '7' ) { return {}; } var codeOutput = CodeMirror.fromTextArea( document.getElementById('codeOutput'), { lineNumbers: false, mode: 'text', theme: 'monokai', readOnly: 'nocursor', lineWrapping: true } ); codeOutput.setValue( '/**\n' + ' * Your output will go here.\n' + ' * Console.log() -type statements\n' + ' * will appear in your browser\'s\n' + ' * DevTools JavaScript console.\n' + ' */' ); codeOutput.setSize('100%', '100%'); return codeOutput; }(window.CodeMirror, window.document, common.challengeType || 0)); var sandBox = (function(jailed, codeOutput) { if (!jailed) { return {}; } var plugin = null; var sandBox = { }; 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 (common.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; }(window.jailed, common.codeOutput)); 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 = (function(CodeMirror, emmetCodeMirror, common) { var codeStorageFactory = common.codeStorageFactory; if (!CodeMirror) { return {}; } var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), { lineNumbers: true, mode: 'text', theme: 'monokai', runnable: true, matchBrackets: true, autoCloseBrackets: true, scrollbarStyle: 'null', lineWrapping: true, gutters: ['CodeMirror-lint-markers'] }); editor.setSize('100%', 'auto'); var codeStorage = common.codeStorage = codeStorageFactory(editor, common.challengeName); editor.on('keyup', function() { clearTimeout(codeStorage.updateTimeoutId); codeStorage.updateTimeoutId = setTimeout( codeStorage.updateStorage.bind(codeStorage), codeStorage.updateWait ); }); // Initialize CodeMirror editor with a nice html5 canvas demo. editor.on('keyup', function() { clearTimeout(delay); delay = setTimeout(updatePreview, 300); }); editor.setOption('extraKeys', { Tab: function(cm) { if (cm.somethingSelected()) { cm.indentSelection('add'); } else { var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); cm.replaceSelection(spaces); } }, 'Shift-Tab': function(cm) { if (cm.somethingSelected()) { cm.indentSelection('subtract'); } else { var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); cm.replaceSelection(spaces); } }, 'Ctrl-Enter': function() { isInitRun = false; bonfireExecute(true); return false; } }); var info = editor.getScrollInfo(); var after = editor.charCoords({ line: editor.getCursor().line + 1, ch: 0 }, 'local').top; if (info.top + info.clientHeight < after) { editor.scrollTo(null, after - info.clientHeight + 3); } if (emmetCodeMirror) { emmetCodeMirror( editor, { 'Cmd-E': 'emmet.expand_abbreviation', Tab: 'emmet.expand_abbreviation_with_tab', Enter: 'emmet.insert_formatted_line_break_only' } ); } common.init.push(function() { var editorValue; if (common.codeUri.isAlive()) { console.log('in query'); editorValue = common.codeUri.parse(); } else { editorValue = codeStorage.isAlive() ? codeStorage.getStoredValue() : common.seed; } editor.setValue(replaceSafeTags(editorValue)); editor.refresh(); }); return editor; }(window.CodeMirror, window.emmetCodeMirror, common)); var tests = tests || []; var libraryIncludes = "" + "" + "" + "" + "" + "" + "" + '' + ''; 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') +
'