var common = (function() {
// common namespace
// all classes should be stored here
var common = window.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';
}, '');
common.replaceScriptTags = function replaceScriptTags(value) {
return value
.replace(/');
};
common.replaceFormActionAttr = function replaceFormAction(value) {
return value.replace(/
/, function(val) {
return val.replace('action=', 'fccfaa=');
});
};
common.replaceFccfaaAttr = function replaceFccfaaAttr(value) {
return value.replace(//, function(val) {
return val.replace('fccfaa=', 'action=');
});
};
return common;
})();
// store code in the URL
common.codeUri = (function(common, encode, decode, location, history) {
var replaceScriptTags = common.replaceScriptTags;
var replaceSafeTags = common.replaceSafeTags;
var replaceFormActionAttr = common.replaceFormActionAttr;
var replaceFccfaaAttr = common.replaceFccfaaAttr;
function encodeFcc(val) {
return replaceScriptTags(replaceFormActionAttr(val));
}
function decodeFcc(val) {
return replaceSafeTags(replaceFccfaaAttr(val));
}
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.enabled &&
codeUri.isInQuery(location.search) ||
codeUri.isInQuery(location.hash);
},
parse: function() {
if (!codeUri.enabled) {
return null;
}
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 = '#?' + encodeFcc(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 decodeFcc(codeUri.decode(value || ''));
}
return solution;
}, null);
},
querify: function(solution) {
if (!codeUri.enabled) {
return null;
}
location.hash = '?solution=' +
codeUri.encode(encodeFcc(solution));
return solution;
},
enabled: true
};
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));
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(common.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(
'