More refactoring

This commit is contained in:
Berkeley Martinez
2015-11-17 21:25:16 -08:00
parent 748f7ab93f
commit 29f90505b7
30 changed files with 1043 additions and 1569 deletions

View File

@ -1,733 +1,10 @@
var common = window.common || { init: [] };
var BDDregex = new RegExp(
'(expect(\\s+)?\\(.*\\;)|' +
'(assert(\\s+)?\\(.*\\;)|' +
'(assert\\.\\w.*\\;)|' +
'(.*\\.should\\..*\\;)/'
);
var libraryIncludes =
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/" +
"libs/animate.css/3.2.0/animate.min.css'/>" +
"<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/" +
"bootstrap/3.3.1/css/bootstrap.min.css'/>" +
"<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/" +
"font-awesome/4.2.0/css/font-awesome.min.css'/>" +
'<style>body { padding: 0px 3px 0px 3px; }</style>';
var iFrameScript = "<script src='/js/iFrameScripts.js'></script>";
function workerError(error) {
var display = $('.runTimeError');
var housing = $('#testSuite');
if (display.html() === error) {
return null;
}
display.remove();
housing.prepend(`
<div class="runTimeError" style="font-size: 18px;">
<code>
${common.unScopeJQuery(error)}
</code>
</div>
`);
display.hide().fadeIn(function() {
setTimeout(function() {
display.fadeOut(function() {
display.remove();
});
}, 1000);
});
}
common.safeHTMLRun = function safeHTMLRun(shouldTest) {
const codeStorage = common.codeStorage;
if (common.challengeType !== '0') {
return null;
}
const editorValue = common.editor.getValue();
const previewFrame = document.getElementById('preview');
const preview = previewFrame.contentDocument ||
previewFrame.contentWindow.document;
if (!editorValue.match(/\<script\>/gi)) {
preview.open();
preview.write(
libraryIncludes + editorValue + (shouldTest ? iFrameScript : '')
);
codeStorage.updateStorage();
preview.close();
return null;
}
// grab user javaScript
var s = editorValue
.split(/\<\s?script\s?\>/gi)[1]
.split(/\<\s?\/\s?script\s?\>/gi)[0];
// need to add jQuery here
s = `
document = {};
var navigator = function() {
this.geolocation = function() {
this.getCurrentPosition = function() {
this.coords = {latitude: "", longitude: ""};
return this;
};
return this;
};
return this;
};
${s}
`;
return common.detectLoop(s, function(cls, message) {
if (cls) {
console.log(message.error);
workerError(message.error);
}
preview.open();
preview.write(
libraryIncludes + editorValue + (shouldTest ? iFrameScript : '')
);
codeStorage.updateStorage();
preview.close();
});
};
common.updatePreview = function updatePreview() {
var editorValue = common.editor.getValue();
var openingComments = editorValue.match(/\<\!\-\-/gi);
var closingComments = editorValue.match(/\-\-\>/gi);
if (
openingComments &&
(
!closingComments ||
openingComments.length > closingComments.length
)
) {
common.editor.setValue(editorValue + '-->');
editorValue = editorValue + '-->';
}
if (!editorValue.match(/\$\s*?\(\s*?\$\s*?\)/gi)) {
common.safeHTMLRun(false);
} else {
workerError('Unsafe $($)');
}
};
common.init.push(() => {
if (common.challengeType === '0') {
common.updatePreview(false);
}
});
/* eslint-disable no-unused-vars */
var testResults = [];
var postSuccess = function(data) {
/* eslint-enable no-unused-vars */
var testDoc = document.createElement('div');
$(testDoc).html(`
<div class='row'>
<div class='col-xs-2 text-center'>
<i class='ion-checkmark-circled big-success-icon'></i>
</div>
<div class='col-xs-10 test-output test-vertical-center wrappable'>
${JSON.parse(data)}
</div>
`);
$('#testSuite').append(testDoc);
testSuccess();
};
/* eslint-disable no-unused-vars */
var postError = function(data) {
/* eslint-enable no-unused-vars */
var testDoc = document.createElement('div');
$(testDoc).html(`
<div class='row'>
<div class='col-xs-2 text-center'>
<i class='ion-close-circled big-error-icon'></i>
</div>
<div class='col-xs-10 test-vertical-center test-output wrappable'>
${JSON.parse(data)}
</div>
`);
$('#testSuite').append(testDoc);
};
var goodTests = 0;
var testSuccess = function() {
goodTests++;
// test successful run show completion
if (goodTests === common.tests.length) {
return showCompletion();
}
};
function ctrlEnterClickHandler(e) {
// ctrl + enter or cmd + enter
if (
e.metaKey && e.keyCode === 13 ||
e.ctrlKey && e.keyCode === 13
) {
$('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler);
if ($('#submit-challenge').length > 0) {
$('#submit-challenge').click();
} else {
window.location = '/challenges/next-challenge?id=' + common.challengeId;
}
}
}
function showCompletion() {
var time = Math.floor(Date.now()) - window.started;
window.ga(
'send',
'event',
'Challenge',
'solved',
common.challengeName + ', Time: ' + time + ', Attempts: ' + 0
);
var bonfireSolution = common.editor.getValue();
var didCompleteWith = $('#completed-with').val() || null;
$('#complete-courseware-dialog').modal('show');
$('#complete-courseware-dialog .modal-header').click();
$('#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(
'<div id="challenge-spinner" ' +
'class="animated zoomInUp inner-circles-loader">' +
'submitting...</div>'
);
next();
});
$.post(
'/completed-bonfire/', {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
completedWith: didCompleteWith,
challengeType: common.challengeType,
solution: bonfireSolution
}
},
function(res) {
if (res) {
window.location =
'/challenges/next-challenge?id=' + common.challengeId;
}
}
);
});
}
common.resetEditor = function resetEditor() {
common.editor.setValue(common.replaceSafeTags(common.seed));
$('#testSuite').empty();
common.executeChallenge(true);
common.codeStorage.updateStorage();
};
common.addTestsToString = function(userJavaScript, userTests = []) {
// insert tests from mongo
for (var i = 0; i < common.tests.length; i++) {
userJavaScript += '\n' + common.tests[i];
}
var counter = 0;
var match = BDDregex.exec(userJavaScript);
while (match) {
var replacement = '//' + counter + common.salt;
userJavaScript = userJavaScript.substring(0, match.index) +
replacement +
userJavaScript.substring(match.index + match[0].length);
userTests.push({
'text': match[0],
'line': counter,
'err': null
});
counter++;
match = BDDregex.exec(userJavaScript);
}
return userJavaScript;
};
function removeComments(userJavaScript) {
var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|([ \n]\/\/[^\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 didTestPass = !userTests[i].err;
var testText = userTests[i].text
.split('message: ')
.pop()
.replace(/\'\);/g, '');
var testDoc = document.createElement('div');
var iconClass = didTestPass ?
'"ion-checkmark-circled big-success-icon"' :
'"ion-close-circled big-error-icon"';
$(testDoc).html(
"<div class='row'><div class='col-xs-2 text-center'><i class=" +
iconClass +
"></i></div><div class='col-xs-10 test-output wrappable'>" +
testText +
"</div><div class='ten-pixel-break'/>"
)
.appendTo($('#testSuite'));
}
};
(function(win, chai) {
if (!chai) {
return;
}
win.expect = chai.expect;
win.assert = chai.assert;
win.should = chai.should();
}(window, window.chai));
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 head = common.arrayToNewLineString(common.head);
var tail = common.arrayToNewLineString(common.tail);
var editorValue = head + editor.getValue() + tail;
// 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) {
/* eslint-disable no-eval, no-unused-vars */
var output = eval(reassembleTest(chaiTestFromJSON, data));
/* eslint-enable no-eval, no-unused-vars */
}
} catch (error) {
allTestsPassed = false;
__testArray[indexOfTestArray].err = error.message;
} finally {
if (!chaiTestFromJSON) {
createTestDisplay();
}
}
});
if (allTestsPassed) {
allTestsPassed = false;
showCompletion();
} else {
isInitRun = false;
}
}
};
// step challenge
common.init.push((function() {
var stepClass = '.challenge-step';
var prevBtnClass = '.challenge-step-btn-prev';
var nextBtnClass = '.challenge-step-btn-next';
var actionBtnClass = '.challenge-step-btn-action';
var finishBtnClass = '.challenge-step-btn-finish';
var submitBtnId = '#challenge-step-btn-submit';
var submitModalId = '#challenge-step-modal';
function getPreviousStep($challengeSteps) {
var $prevStep = false;
var prevStepIndex = 0;
$challengeSteps.each(function(index) {
var $step = $(this);
if (
!$step.hasClass('hidden')
) {
prevStepIndex = index - 1;
}
});
$prevStep = $challengeSteps[prevStepIndex];
return $prevStep;
}
function getNextStep($challengeSteps) {
var length = $challengeSteps.length;
var $nextStep = false;
var nextStepIndex = 0;
$challengeSteps.each(function(index) {
var $step = $(this);
if (
!$step.hasClass('hidden') &&
index + 1 !== length
) {
nextStepIndex = index + 1;
}
});
$nextStep = $challengeSteps[nextStepIndex];
return $nextStep;
}
function handlePrevStepClick(e) {
e.preventDefault();
var prevStep = getPreviousStep($(stepClass));
$(this)
.parent().parent()
.removeClass('slideInLeft slideInRight')
.addClass('animated fadeOutRight fast-animation')
.delay(250)
.queue(function(prev) {
$(this).addClass('hidden');
if (prevStep) {
$(prevStep)
.removeClass('hidden')
.removeClass('fadeOutLeft fadeOutRight')
.addClass('animated slideInLeft fast-animation')
.delay(500)
.queue(function(prev) {
prev();
});
}
prev();
});
}
function handleNextStepClick(e) {
e.preventDefault();
var nextStep = getNextStep($(stepClass));
$(this)
.parent().parent()
.removeClass('slideInRight slideInLeft')
.addClass('animated fadeOutLeft fast-animation')
.delay(250)
.queue(function(next) {
$(this).addClass('hidden');
if (nextStep) {
$(nextStep)
.removeClass('hidden')
.removeClass('fadeOutRight fadeOutLeft')
.addClass('animated slideInRight fast-animation')
.delay(500)
.queue(function(next) {
next();
});
}
next();
});
}
function handleActionClick(e) {
var props = common.challengeSeed[0] ||
{ stepIndex: [] };
var $el = $(this);
var index = +$el.attr('id');
var propIndex = props.stepIndex.indexOf(index);
if (propIndex === -1) {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}
// an API action
// prevent link from opening
e.preventDefault();
var prop = props.properties[propIndex];
var api = props.apis[propIndex];
if (common[prop]) {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}
$
.post(api)
.done(function(data) {
// assume a boolean indicates passing
if (typeof data === 'boolean') {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}
// assume api returns string when fails
$el
.parent()
.find('.disabled')
.replaceWith('<p>' + data + '</p>');
})
.fail(function() {
console.log('failed');
});
}
function handleFinishClick(e) {
e.preventDefault();
$(submitModalId).modal('show');
$(submitModalId + '.modal-header').click();
$(submitBtnId).click(handleSubmitClick);
}
function handleSubmitClick(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')
.delay(1000)
.queue(function(next) {
$(this).replaceWith(
'<div id="challenge-spinner" ' +
'class="animated zoomInUp inner-circles-loader">' +
'submitting...</div>'
);
next();
});
$.post(
'/completed-bonfire/', {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
challengeType: common.challengeType
}
},
function(res) {
if (res) {
window.location =
'/challenges/next-challenge?id=' + common.challengeId;
}
}
);
}
return function($) {
$(prevBtnClass).click(handlePrevStepClick);
$(nextBtnClass).click(handleNextStepClick);
$(actionBtnClass).click(handleActionClick);
$(finishBtnClass).click(handleFinishClick);
};
}(window.$)));
function bonfireExecute(shouldTest) {
var head = common.arrayToNewLineString(common.head);
var tail = common.arrayToNewLineString(common.tail);
var codeOutput = common.codeOutput;
initPreview = false;
goodTests = 0;
attempts++;
ga('send', 'event', 'Challenge', 'ran-code', common.challengeName);
userTests = null;
$('#testSuite').empty();
if (
common.challengeType !== '0' &&
!editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi)
) {
var userJavaScript = head + editor.getValue() + tail;
var failedCommentTest = false;
var openingComments = userJavaScript.match(/\/\*/gi);
// checks if the number of opening comments(/*) matches the number of
// closing comments(*/)
if (
openingComments &&
openingComments.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)) {
common.sandBox.submit(userJavaScript, function(cls, message) {
if (failedCommentTest) {
editor.setValue(editor.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 {
common.sandBox.submit(userJavaScript, function(cls, message) {
if (failedCommentTest) {
editor.setValue(editor.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) &&
common.challengeType === '0'
) {
safeHTMLRun(shouldTest);
} else {
workerError('Unsafe $($)');
}
}
setTimeout(function() {
var $marginFix = $('.innerMarginFix');
$marginFix.css('min-height', $marginFix.height());
}, 1000);
}
common.init($ => {
$('#submitButton').on('click', function() {
common.executeChallenge(true);
});
});
$(document).ready(function() {
var common = window.common;
common.init.forEach(function(init) {
init($);
});
// init modal keybindings on open
$('#complete-courseware-dialog').on('shown.bs.modal', function() {
$('#complete-courseware-dialog').keydown(ctrlEnterClickHandler);
});
// remove modal keybinds on close
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
$('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler);
});
var $preview = $('#preview');
if (typeof $preview.html() !== 'undefined') {
$preview.load(function() {
@ -741,5 +18,4 @@ $(document).ready(function() {
) {
common.executeChallenge(true);
}
});

View File

@ -0,0 +1,37 @@
window.common = (function(global) {
const {
$,
Rx: { Observable, Disposable },
common = { init: [] }
} = global;
function getFaux() {
return new Observable(function(observer) {
const jqXHR = $.get('/js/faux.js')
.success(data => observer.onNext(data))
.fail(e => observer.onError(e))
.always(() => observer.onCompleted());
return new Disposable(() => {
jqXHR.abort();
});
});
}
const faux$ = getFaux().shareReplay();
common.safeHTMLRun = function safeHTMLRun(code) {
if (!code.match(/\<script\>/gi)) {
return Observable.just(code);
}
// grab user javaScript
var scriptCode = code
.split(/\<\s?script\s?\>/gi)[1]
.split(/\<\s?\/\s?script\s?\>/gi)[0];
return faux$.map(faux => faux + scriptCode);
};
return common;
}(window));

View File

@ -0,0 +1,41 @@
window.common = (function({ common = { init: [] }}) {
var BDDregex = new RegExp(
'(expect(\\s+)?\\(.*\\;)|' +
'(assert(\\s+)?\\(.*\\;)|' +
'(assert\\.\\w.*\\;)|' +
'(.*\\.should\\..*\\;)/'
);
common.addTestsToString = function(code) {
const userTests = [];
// insert tests from mongo
for (var i = 0; i < common.tests.length; i++) {
code += '\n' + common.tests[i];
}
var counter = 0;
var match = BDDregex.exec(code);
while (match) {
var replacement = '//' + counter + common.salt;
code = code.substring(0, match.index) +
replacement +
code.substring(match.index + match[0].length);
userTests.push({
err: null,
text: match[0],
line: counter
});
counter++;
match = BDDregex.exec(code);
}
return { code, userTests };
};
return common;
}(window));

View File

@ -0,0 +1,262 @@
window.common = (function({ $, common = { init: [] }}) {
common.ctrlEnterClickHandler = function ctrlEnterClickHandler(e) {
// ctrl + enter or cmd + enter
if (
e.metaKey && e.keyCode === 13 ||
e.ctrlKey && e.keyCode === 13
) {
$('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler);
if ($('#submit-challenge').length > 0) {
$('#submit-challenge').click();
} else {
window.location = '/challenges/next-challenge?id=' + common.challengeId;
}
}
};
common.resetEditor = function resetEditor() {
common.editor.setValue(common.replaceSafeTags(common.seed));
common.executeChallenge(true);
common.codeStorage.updateStorage();
};
common.init.push(function($) {
var $marginFix = $('.innerMarginFix');
$marginFix.css('min-height', $marginFix.height());
// init modal keybindings on open
$('#complete-courseware-dialog').on('shown.bs.modal', function() {
$('#complete-courseware-dialog').keydown(common.ctrlEnterClickHandler);
});
// remove modal keybinds on close
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
$('#complete-courseware-dialog').off(
'keydown',
common.ctrlEnterClickHandler
);
});
// video checklist binding
$('.challenge-list-checkbox').on('change', function() {
var checkboxId = $(this).parent().parent().attr('id');
if ($(this).is(':checked')) {
$(this).parent().siblings().children().addClass('faded');
if (!localStorage || !localStorage[checkboxId]) {
localStorage[checkboxId] = true;
}
}
if (!$(this).is(':checked')) {
$(this).parent().siblings().children().removeClass('faded');
if (localStorage[checkboxId]) {
localStorage.removeItem(checkboxId);
}
}
});
$('.checklist-element').each(function() {
var checklistElementId = $(this).attr('id');
if (localStorage[checklistElementId]) {
$(this).children().children('li').addClass('faded');
$(this).children().children('input').trigger('click');
}
});
// video challenge submit
$('#next-courseware-button').on('click', function() {
$('#next-courseware-button').unbind('click');
if ($('.signup-btn-nav').length < 1) {
var data;
var completedWith = $('#completed-with').val() || null;
var publicURL = $('#public-url').val() || null;
var githubURL = $('#github-url').val() || null;
switch (common.challengeType) {
case common.challengeTypes.HTML:
case common.challengeTypes.JS:
case common.challengeTypes.VIDEO:
data = {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName
}
};
$.post('/completed-challenge/', data)
.success(function(res) {
if (!res) {
return;
}
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
})
.fail(function() {
window.location.href = '/challenges';
});
break;
case common.challengeTypes.BASEJUMP:
case common.challengeTypes.ZIPLINE:
data = {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
completedWith: completedWith,
publicURL: publicURL,
githubURL: githubURL,
challengeType: common.challengeType,
verified: false
}
};
$.post('/completed-zipline-or-basejump/', data)
.success(function() {
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
})
.fail(function() {
window.location.replace(window.location.href);
});
break;
case common.challengeTypes.BONFIRE:
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
break;
default:
console.log('Happy Coding!');
break;
}
}
});
$('#submitButton').on('click', function() {
common.executeChallenge(true);
});
if (common.editor) {
$('#reset-button').on('click', common.resetEditor);
}
if (common.challengeName) {
window.ga('send', 'event', 'Challenge', 'load', common.challengeName);
}
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
common.editor.focus();
});
$('#trigger-issue-modal').on('click', function() {
$('#issue-modal').modal('show');
});
$('#trigger-help-modal').on('click', function() {
$('#help-modal').modal('show');
});
$('#trigger-reset-modal').on('click', function() {
$('#reset-modal').modal('show');
});
$('#trigger-pair-modal').on('click', function() {
$('#pair-modal').modal('show');
});
$('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show');
});
$('#help-ive-found-a-bug-wiki-article').on('click', function() {
window.open(
'https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/' +
"Help-I've-Found-a-Bug",
'_blank'
);
});
$('#search-issue').on('click', function() {
var queryIssue = window.location.href.toString();
window.open(
'https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=' +
'is:issue is:all ' +
(common.challengeName) +
' OR ' +
queryIssue
.substr(queryIssue.lastIndexOf('challenges/') + 11)
.replace('/', ''), '_blank');
});
$('#gist-share').on('click', function() {
var gistWindow = window.open('', '_blank');
$('#gist-share')
.attr('disabled', 'true')
.removeClass('btn-danger')
.addClass('btn-warning disabled');
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ('withCredentials' in xhr) {
xhr.open(method, url, true);
} else if (typeof XDomainRequest !== 'undefined') {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
return xhr;
}
var request = createCORSRequest('post', 'https://api.github.com/gists');
if (!request) {
return null;
}
request.onload = function() {
if (
request.readyState === 4 &&
request.status === 201 &&
request.statusText === 'Created'
) {
gistWindow.location.href =
JSON.parse(request.responseText)['html_url'];
}
};
var description = common.username ?
'http://www.freecodecamp.com/' + common.username + ' \'s s' :
'S';
var data = {
description: description + 'olution for ' + common.challengeName,
public: true,
files: {}
};
var queryIssue = window.location.href.toString().split('#?')[0];
var filename = queryIssue
.substr(queryIssue.lastIndexOf('challenges/') + 11)
.replace('/', '') + '.js';
data.files[filename] = {
content: '// ' +
common.challengeName +
'\n' +
(common.username ? '// Author: @' + common.username + '\n' : '') +
'// Challenge: ' +
queryIssue +
'\n' +
'// Learn to Code at Free Code Camp (www.freecodecamp.com)' +
'\n\n' +
window.editor.getValue().trim()
};
request.send(JSON.stringify(data));
});
});
return common;
}(window));

View File

@ -0,0 +1,37 @@
// depends on: codeUri
window.common = (function(global) {
const {
localStorage,
common = { init: [] }
} = global;
var codeStorage = {
getStoredValue(key) {
return '' + localStorage.getItem(key + 'Val');
},
isAlive: function() {
var val = this.getStoredValue();
return val !== 'null' &&
val !== 'undefined' &&
(val && val.length > 0);
},
updateStorage(key, code) {
if (
!localStorage ||
typeof localStorage !== 'function' ||
!key ||
typeof key !== 'string'
) {
console.log('unable to save to storage');
return code;
}
localStorage.setItem(key + 'Val', code);
}
};
common.codeStorage = codeStorage;
return common;
}(window, window.common));

View File

@ -1,82 +0,0 @@
// depends on: codeUri
window.common = (function(global ) {
const {
$,
localStorage,
common = { init: [] }
} = global;
const {
codeUri
} = common;
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
);
}
common.codeStorageFactory = codeStorageFactory;
return common;
}(window, window.common));

View File

@ -5,14 +5,23 @@ window.common = (function(global) {
common = { init: [] }
} = global;
if (!CodeMirror) {
return {};
const { challengeType = '0' } = common;
if (
!CodeMirror ||
challengeType === '0' ||
challengeType === '7'
) {
common.editor = {};
return common;
}
var delay;
var codeStorageFactory = common.codeStorageFactory;
var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), {
var editor = CodeMirror.fromTextArea(
document.getElementById('codeEditor'),
{
lint: true,
lineNumbers: true,
mode: 'javascript',
@ -23,7 +32,8 @@ window.common = (function(global) {
scrollbarStyle: 'null',
lineWrapping: true,
gutters: ['CodeMirror-lint-markers']
});
}
);
editor.setSize('100%', 'auto');

View File

@ -0,0 +1,79 @@
window.common = (function(global) {
const {
jailed,
document: doc,
Rx: { Observable, Disposable },
common = { init: [] }
} = global;
if (!jailed) {
return (code, cb) => cb(new Error('Could not load jailed plugin'));
}
// obtaining absolute path of this script
var scripts = doc.getElementsByTagName('script');
var path = scripts[scripts.length - 1].src
.split('?')[0]
.split('/')
.slice(0, -1)
.join('/') + '/';
var Sandbox = {
startTimeout() {
this.timeoutId = setTimeout(() => {
this.error = new Error('Plugin failed to initialize');
this.destroyPlugin();
}, 3000);
},
cancelTimout() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
},
createPlugin() {
this.plugin = new jailed.Plugin(path + 'plugin.js');
this.startTimeout();
this.plugin.whenConnected(() => {
this.endTimeout();
});
},
destroyPlugin() {
this.plugin.disconnect();
}
};
// sends the input to the plugin for evaluation
common.detectLoops = function detectLoops({ code = '', ...rest }) {
return new Observable(function(observer) {
const sandbox = Object.create(Sandbox);
sandbox.createPlugin();
sandbox.plugin.whenConnected(() => {
sandbox.plugin.remote.run(code, (err, data) => {
observer.onNext({ ...rest, err, code, data });
observer.onCompleted();
});
});
sandbox.plugin.whenDisconnected(() => {
if (sandbox.disposed) {
return null;
}
if (sandbox.error) {
observer.onNext({ ...rest, err: sandbox.error, code, data: {} });
}
observer.onCompleted();
});
return new Disposable(() => {
sandbox.disposed = true;
sandbox.destroyPlugin();
});
});
};
return common;
}(window));

View File

@ -1,57 +0,0 @@
window.common = (function(global) {
const {
jailed,
document: doc,
common = { init: [] }
} = global;
if (!jailed) {
return (code, cb) => cb(new Error('Could not load jailed plugin'));
}
// obtaining absolute path of this script
var scripts = doc.getElementsByTagName('script');
var path = scripts[scripts.length - 1].src
.split('?')[0]
.split('/')
.slice(0, -1)
.join('/') + '/';
var sandbox = {
timeoutId: null,
startTimeout() {
this.timeoutId = setTimeout(() => {
this.disconnect();
}, 3000);
},
endTimeout() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
},
createPlugin() {
this.plugin = new jailed.Plugin(path + 'plugin.js');
this.plugin.whenDisconnected(() => {
this.endTimeout();
});
},
destroyPlugin() {
this.plugin.disconnect();
}
};
// sends the input to the plugin for evaluation
common.detectLoops = function detectLoops(code, callback) {
sandbox.createPlugin();
sandbox.plugin.whenConnected(() => {
this.endTimeout();
sandbox.plugin.remote.run(code, callback);
});
};
return common;
}(window));

View File

@ -0,0 +1,119 @@
// what are the executeChallenge functions?
// Should be responsible for starting after a submit action
// Should not be responsible for displaying results
// Should return results
// should grab editor value
// depends on main editor
window.common = (function(global) {
const {
ga,
Rx: { Observable },
common = { init: [] }
} = global;
let attempts = 0;
const detectFunctionCall = /function\s*?\(|function\s+\w+\s*?\(/gi;
const detectUnsafeJQ = /\$\s*?\(\s*?\$\s*?\)/gi;
const detectUnsafeConsoleCall = /if\s\(null\)\sconsole\.log\(1\);/gi;
common.executeChallenge$ = function executeChallenge$() {
const code = common.editor.getValue();
const head = common.arrayToNewLineString(common.head);
const tail = common.arrayToNewLineString(common.tail);
attempts++;
ga('send', 'event', 'Challenge', 'ran-code', common.challengeName);
let openingComments = code.match(/\/\*/gi);
// checks if the number of opening comments(/*) matches the number of
// closing comments(*/)
Observable.just({ code })
.flatMap(code => {
if (
code.match(/\$\s*?\(\s*?\$\s*?\)/gi) &&
openingComments &&
openingComments.length > code.match(/\*\//gi).length
) {
return Observable.just({
err: 'SyntaxError: Unfinished multi-line comment',
code: code
});
}
if (code.match(detectUnsafeJQ)) {
return Observable.just({
err: 'Unsafe $($)',
output: 'Unsafe $($)',
code: code
});
}
if (
code.match(/function/g) &&
!code.match(detectFunctionCall)
) {
return Observable.just({
err: 'SyntaxError: Unsafe or unfinished function declaration',
code: code
});
}
if (common.challengeType === '0') {
let openingComments = code.match(/\<\!\-\-/gi);
let closingComments = code.match(/\-\-\>/gi) || [];
if (
openingComments &&
openingComments.length > closingComments.length
) {
return Observable.just({
err: 'SyntaxError: Unfinished HTML comment',
code: code
});
}
}
if (code.match(detectUnsafeConsoleCall)) {
return Observable.just({
err: 'Invalid if (null) console.log(1); detected',
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 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.runTests$({
output: data.output.replace(/\\\"/gi, ''),
data,
code,
userTests
});
});
});
};
return common;
}(window));

View File

@ -1,94 +0,0 @@
window.common = (function(global) {
const {
ga,
common = { init: [] }
} = global;
let attempts = 0;
common.executeChallenge = function executeChallenge(shouldTest) {
const editorValue = common.editor.getValue();
const head = common.arrayToNewLineString(common.head);
const tail = common.arrayToNewLineString(common.tail);
const codeOutput = common.codeOutput;
attempts++;
ga('send', 'event', 'Challenge', 'ran-code', common.challengeName);
$('#testSuite').empty();
const openingComments = editorValue.match(/\/\*/gi);
// checks if the number of opening comments(/*) matches the number of
// closing comments(*/)
if (
editorValue.match(/\$\s*?\(\s*?\$\s*?\)/gi) &&
openingComments &&
openingComments.length > editorValue.match(/\*\//gi).length
) {
common.editor.setValue(common.editor.getValue() + '*/');
codeOutput.setValue('Unfinished multi-line comment');
return null;
}
if (common.challengeType !== '0') {
let userJavaScript = head + common.editor.getValue() + tail;
userJavaScript = common.removeComments(userJavaScript);
userJavaScript = common.addTests(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)) {
common.runInSand(userJavaScript, function(err, message) {
if (err) {
codeOutput.setValue(err);
if (shouldTest) {
common.runTests('Error', null);
}
} else {
codeOutput.setValue(message.output);
codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, ''));
message.input = common.removeLogs(message.input);
if (shouldTest) {
common.runTests(null, message);
}
}
});
} else {
codeOutput.setValue('Unsafe or unfinished function declaration');
}
} else {
common.runInSand(userJavaScript, function(cls, message) {
if (cls) {
codeOutput.setValue(message.error);
if (shouldTest) {
common.runTests('Error', null);
}
} else {
codeOutput.setValue(message.output);
codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, ''));
message.input = common.removeLogs(message.input);
if (shouldTest) {
common.runTests(null, message);
}
}
});
}
} else if (
!editorValue.match(/\$\s*?\(\s*?\$\s*?\)/gi) &&
common.challengeType === '0'
) {
common.safeHTMLRun(shouldTest);
} else {
common.workerError('Unsafe $($)');
}
setTimeout(function() {
var $marginFix = $('.innerMarginFix');
$marginFix.css('min-height', $marginFix.height());
}, 1000);
};
return common;
}(window));

View File

@ -10,6 +10,18 @@ window.common = (function(global) {
common.tail = common.tail || [];
common.salt = Math.random();
common.challengeTypes = {
HTML: '0',
JS: '1',
VIDEO: '2',
ZIPLINE: '3',
BASEJUMP: '4',
BONFIRE: '5',
HIKES: '6',
STEP: '7'
};
common.arrayToNewLineString = function arrayToNewLineString(seedData) {
seedData = Array.isArray(seedData) ? seedData : [seedData];
return seedData.reduce(function(seed, line) {
@ -67,13 +79,11 @@ window.common = (function(global) {
return str.replace(logRegex, '');
};
common.reassembleTest = function reassembleTest(test, data) {
var lineNum = test.line;
var regexp = new RegExp('\/\/' + lineNum + common.salt);
return data.input.replace(regexp, test.text);
common.reassembleTest = function reassembleTest(code = '', { line, text }) {
var regexp = new RegExp('\/\/' + line + common.salt);
return code.replace(regexp, text);
};
return common;
})();

View File

@ -7,18 +7,17 @@ window.common = (function(global) {
const { challengeType = '0' } = common;
if (!CodeMirror) {
return {};
}
if (
!CodeMirror ||
challengeType === '0' ||
challengeType === '7'
) {
return {};
common.updateOutputDisplay = () => {};
common.appendToOutputDisplay = () => {};
return common;
}
common.codeOutput = CodeMirror.fromTextArea(
var codeOutput = CodeMirror.fromTextArea(
doc.getElementById('codeOutput'),
{
lineNumbers: false,
@ -29,7 +28,7 @@ window.common = (function(global) {
}
);
common.codeOutput.setValue(`
codeOutput.setValue(`
/**
* Your output will go here.
* Console.log() -type statements
@ -38,7 +37,17 @@ window.common = (function(global) {
*/'
`);
common.codeOutput.setSize('100%', '100%');
codeOutput.setSize('100%', '100%');
common.updateOutputDisplay = function updateOutputDisplay(str) {
codeOutput.setValue(str);
return str;
};
common.appendToOutputDisplay = function appendToOutputDisplay(str) {
codeOutput.setValue(codeOutput.getValue() + str);
return str;
};
return common;
}(window));

View File

@ -0,0 +1,132 @@
window.common = (function({ common = { init: [] }}) {
common.lockTop = function lockTop() {
var magiVal;
if ($(window).width() >= 990) {
if ($('.editorScrollDiv').html()) {
magiVal = $(window).height() - $('.navbar').height();
if (magiVal < 0) {
magiVal = 0;
}
$('.editorScrollDiv').css('height', magiVal - 85 + 'px');
}
magiVal = $(window).height() - $('.navbar').height();
if (magiVal < 0) {
magiVal = 0;
}
$('.scroll-locker')
.css('min-height', $('.editorScrollDiv').height())
.css('height', magiVal - 185);
} else {
$('.editorScrollDiv').css('max-height', 500 + 'px');
$('.scroll-locker')
.css('position', 'inherit')
.css('top', 'inherit')
.css('width', '100%')
.css('max-height', '85%');
}
};
common.init.push(function($) {
// fakeiphone positioning hotfix
if (
$('.iphone-position').html() ||
$('.iphone').html()
) {
var startIphonePosition = parseInt(
$('.iphone-position')
.css('top')
.replace('px', ''),
10
);
var startIphone = parseInt(
$('.iphone')
.css('top')
.replace('px', ''),
10
);
$(window).on('scroll', function() {
var courseHeight = $('.courseware-height').height();
var courseTop = $('.courseware-height').offset().top;
var windowScrollTop = $(window).scrollTop();
var phoneHeight = $('.iphone-position').height();
if (courseHeight + courseTop - windowScrollTop - phoneHeight <= 0) {
$('.iphone-position').css(
'top',
startIphonePosition +
courseHeight +
courseTop -
windowScrollTop -
phoneHeight
);
$('.iphone').css(
'top',
startIphonePosition +
courseHeight +
courseTop -
windowScrollTop -
phoneHeight +
120
);
} else {
$('.iphone-position').css('top', startIphonePosition);
$('.iphone').css('top', startIphone);
}
});
}
if ($('.scroll-locker').html()) {
if ($('.scroll-locker').html()) {
common.lockTop();
$(window).on('resize', function() {
common.lockTop();
});
$(window).on('scroll', function() {
common.lockTop();
});
}
var execInProgress = false;
// why is this not $???
document
.getElementById('scroll-locker')
.addEventListener(
'previewUpdateSpy',
function(e) {
if (execInProgress) {
return null;
}
execInProgress = true;
setTimeout(function() {
if (
$($('.scroll-locker').children()[0]).height() - 800 > e.detail
) {
$('.scroll-locker').scrollTop(e.detail);
} else {
var scrollTop = $($('.scroll-locker').children()[0]).height();
$('.scroll-locker').animate({ scrollTop: scrollTop }, 175);
}
execInProgress = false;
}, 750);
},
false
);
}
});
return common;
}());

View File

@ -0,0 +1,56 @@
window.common = (function({ common = { init: [] } }) {
common.init.push(function($) {
$('#report-issue').on('click', function() {
var textMessage = [
'Challenge [',
(common.challengeName || window.location.pathname),
'](',
window.location.href,
') has an issue.\n',
'User Agent is: <code>',
navigator.userAgent,
'</code>.\n',
'Please describe how to reproduce this issue, and include ',
'links to screenshots if possible.\n\n'
].join('');
if (
common.editor &&
typeof common.editor.getValue === 'function' &&
common.editor.getValue().trim()
) {
var type;
switch (common.challengeType) {
case common.challengeTypes.html:
type = 'html';
break;
case common.challengeTypes.js:
case common.challengeTypes.bonfire:
type = 'javascript';
break;
default:
type = '';
}
textMessage += [
'My code:\n```',
type,
'\n',
common.editor.getValue(),
'\n```\n\n'
].join('');
}
textMessage = encodeURIComponent(textMessage);
$('#issue-modal').modal('hide');
window.open(
'https://github.com/freecodecamp/freecodecamp/issues/new?&body=' +
textMessage,
'_blank'
);
});
});
return common;
}(window));

View File

@ -0,0 +1,35 @@
window.common = (function(global) {
const {
Rx: { Observable },
chai,
common = { init: [] }
} = global;
common.runTests$ = function runTests$({ code, userTests, ...rest }) {
return Observable.from(userTests)
.map(function(test) {
/* eslint-disable no-unused-vars */
const assert = chai.assert;
const editor = { getValue() { return code; }};
/* eslint-enable no-unused-vars */
try {
if (test) {
/* eslint-disable no-eval */
eval(common.reassembleTest(test, code));
/* eslint-enable no-eval */
}
} catch (e) {
test.err = e.message;
}
return test;
})
.toArray()
.map(tests => ({ ...rest, tests }));
};
return common;
}(window));

View File

@ -1,43 +0,0 @@
window.common = (function({ common = { init: [] }}) {
common.runTests = function runTests(err, data) {
var head = common.arrayToNewLineString(common.head);
var tail = common.arrayToNewLineString(common.tail);
var userTests = Array.isArray(userTests) ? userTests.slice() : [];
var editorValue = head + common.editor.getValue() + tail;
if (err) {
userTests = [{
text: 'Program Execution Failure',
err
}];
return userTests;
}
// Add blocks to test exploits here!
if (editorValue.match(/if\s\(null\)\sconsole\.log\(1\);/gi)) {
userTests = [{
text: 'Program Execution Failure',
err: 'Invalid if (null) console.log(1); detected'
}];
return userTests;
}
return userTests.map(function(test) {
try {
if (test) {
/* eslint-disable no-eval, no-unused-vars */
var output = eval(common.reassembleTest(test, data));
/* eslint-enable no-eval, no-unused-vars */
}
} catch (e) {
test.err = e.message;
}
return test;
});
};
return common;
}(window));

View File

@ -0,0 +1,70 @@
window.common = (function(global) {
const {
$,
ga = (() => {}),
common = { init: [] }
} = global;
common.showCompletion = function showCompletion() {
var time = Math.floor(Date.now()) - window.started;
ga(
'send',
'event',
'Challenge',
'solved',
common.challengeName + ', Time: ' + time + ', Attempts: ' + 0
);
var bonfireSolution = common.editor.getValue();
var didCompleteWith = $('#completed-with').val() || null;
$('#complete-courseware-dialog').modal('show');
$('#complete-courseware-dialog .modal-header').click();
$('#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(
'<div id="challenge-spinner" ' +
'class="animated zoomInUp inner-circles-loader">' +
'submitting...</div>'
);
next();
});
$.post(
'/completed-bonfire/', {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
completedWith: didCompleteWith,
challengeType: common.challengeType,
solution: bonfireSolution
}
},
function(res) {
if (res) {
window.location =
'/challenges/next-challenge?id=' + common.challengeId;
}
}
);
});
};
return common;
}(window));

View File

@ -8,15 +8,11 @@ window.common = (function({ $, common = { init: [] }}) {
const submitModalId = '#challenge-step-modal';
function getPreviousStep($challengeSteps) {
var length = $challengeSteps.length;
var $prevStep = false;
var prevStepIndex = 0;
$challengeSteps.each(function(index) {
var $step = $(this);
if (
!$step.hasClass('hidden') &&
index + 1 !== length
) {
if (!$step.hasClass('hidden')) {
prevStepIndex = index - 1;
}
});
@ -50,7 +46,8 @@ window.common = (function({ $, common = { init: [] }}) {
var prevStep = getPreviousStep($(stepClass));
$(this)
.parent()
.removeClass('fadeOutLeft')
.parent()
.removeClass('slideInLeft slideInRight')
.addClass('animated fadeOutRight fast-animation')
.delay(250)
.queue(function(prev) {
@ -58,7 +55,7 @@ window.common = (function({ $, common = { init: [] }}) {
if (prevStep) {
$(prevStep)
.removeClass('hidden')
.removeClass('slideInRight')
.removeClass('fadeOutLeft fadeOutRight')
.addClass('animated slideInLeft fast-animation')
.delay(500)
.queue(function(prev) {
@ -74,7 +71,8 @@ window.common = (function({ $, common = { init: [] }}) {
var nextStep = getNextStep($(stepClass));
$(this)
.parent()
.removeClass('fadeOutRight')
.parent()
.removeClass('slideInRight slideInLeft')
.addClass('animated fadeOutLeft fast-animation')
.delay(250)
.queue(function(next) {
@ -82,7 +80,7 @@ window.common = (function({ $, common = { init: [] }}) {
if (nextStep) {
$(nextStep)
.removeClass('hidden')
.removeClass('slideInLeft')
.removeClass('fadeOutRight fadeOutLeft')
.addClass('animated slideInRight fast-animation')
.delay(500)
.queue(function(next) {

View File

@ -0,0 +1,40 @@
window.common = (function({ common = { init: [] } }) {
var libraryIncludes = `
<link
rel='stylesheet'
href='//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.min.css'
/>
<link
rel='stylesheet'
href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'
/>
<link
rel='stylesheet'
href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css'
/>
<style>
body { padding: 0px 3px 0px 3px; }
</style>
`;
var iFrameScript = "<script src='/js/iFrameScripts.js'></script>";
common.updatePreview = function updatePreview(code = '', shouldTest = false) {
const previewFrame = document.getElementById('preview');
const preview = previewFrame.contentDocument ||
previewFrame.contentWindow.document;
if (!preview) {
return code;
}
preview.open();
preview.write(libraryIncludes + code + (shouldTest ? iFrameScript : ''));
preview.close();
return code;
};
return common;
}(window));

View File

@ -1,3 +1,14 @@
document = {};
var navigator = function() {
this.geolocation = function() {
this.getCurrentPosition = function() {
this.coords = {latitude: "", longitude: ""};
return this;
};
return this;
};
return this;
};
function $() {
if (!(this instanceof $)) {
return new $();

View File

@ -7,20 +7,22 @@ window.$(function() {
var tests = parent.tests;
var common = parent.common;
var editorValue = common.editor.getValue();
var editor = common.editor;
common.tests.forEach(test => {
var userTests = common.tests.map(test => {
var userTest = {};
try {
/* eslint-disable no-eval */
eval(test);
/* eslint-enable no-eval */
} catch (e) {
parent.postError(JSON.stringify(e.message.split(':').shift()));
userTest.err = e.message.split(':').shift();
} finally {
parent.postSuccess(
JSON.stringify(
test.split(',').pop().replace(/\'/g, '').replace(/\)/, '')
)
);
userTest.text = test
.split(',')
.pop()
.replace(/\'/g, '')
.replace(/\)/, '');
}
});
});

View File

@ -4,15 +4,6 @@ main.mapShareKey = 'map-shares';
main.ga = window.ga || function() {};
main.challengeTypes = {
'HTML_CSS_JQ': '0',
'JAVASCRIPT': '1',
'VIDEO': '2',
'ZIPLINE': '3',
'BASEJUMP': '4',
'BONFIRE': '5'
};
main = (function(main) {
// should be set before gitter script loads
@ -112,47 +103,11 @@ main = (function(main) {
return main;
}(main));
main.lockTop = function lockTop() {
var magiVal;
if ($(window).width() >= 990) {
if ($('.editorScrollDiv').html()) {
magiVal = $(window).height() -
$('.navbar').height();
if (magiVal < 0) {
magiVal = 0;
}
$('.editorScrollDiv').css('height', magiVal - 35 + 'px');
}
magiVal = $(window).height() -
$('.navbar').height();
if (magiVal < 0) {
magiVal = 0;
}
$('.scroll-locker')
.css('min-height', $('.editorScrollDiv').height())
.css('height', magiVal - 185);
} else {
$('.editorScrollDiv').css('max-height', 500 + 'px');
$('.scroll-locker')
.css('position', 'inherit')
.css('top', 'inherit')
.css('width', '100%')
.css('max-height', '85%');
}
};
var lastCompleted = typeof lastCompleted !== 'undefined' ?
lastCompleted :
'';
function getMapShares() {
main.getMapShares = function getMapShares() {
var alreadyShared = JSON.parse(
localStorage.getItem(main.mapShareKey) ||
'[]'
@ -163,10 +118,10 @@ function getMapShares() {
alreadyShared = [];
}
return alreadyShared;
}
};
function setMapShare(id) {
var alreadyShared = getMapShares();
main.setMapShare = function setMapShare(id) {
var alreadyShared = main.getMapShares();
var found = false;
alreadyShared.forEach(function(_id) {
if (_id === id) {
@ -178,23 +133,10 @@ function setMapShare(id) {
}
localStorage.setItem(main.mapShareKey, JSON.stringify(alreadyShared));
return alreadyShared;
}
};
$(document).ready(function() {
var challengeName = typeof challengeName !== 'undefined' ?
challengeName :
'';
if (challengeName) {
ga('send', 'event', 'Challenge', 'load', challengeName);
}
if (typeof editor !== 'undefined') {
$('#reset-button').on('click', window.resetEditor);
}
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function(securityToken) {
@ -207,38 +149,6 @@ $(document).ready(function() {
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('.checklist-element').each(function() {
var checklistElementId = $(this).attr('id');
if (localStorage[checklistElementId]) {
$(this).children().children('li').addClass('faded');
$(this).children().children('input').trigger('click');
}
});
$('.start-challenge').on('click', function() {
$(this).parent().remove();
$('.challenge-content')
.removeClass('hidden-element')
.addClass('animated fadeInDown');
});
$('.challenge-list-checkbox').on('change', function() {
var checkboxId = $(this).parent().parent().attr('id');
if ($(this).is(':checked')) {
$(this).parent().siblings().children().addClass('faded');
if (!localStorage || !localStorage[checkboxId]) {
localStorage[checkboxId] = true;
}
}
if (!$(this).is(':checked')) {
$(this).parent().siblings().children().removeClass('faded');
if (localStorage[checkboxId]) {
localStorage.removeItem(checkboxId);
}
}
});
$('img').error(function() {
$(this)
.unbind('error')
@ -248,301 +158,6 @@ $(document).ready(function() {
);
});
function reBindModals() {
if (!window.common) {
return;
}
var common = window.common;
$('.close-modal').unbind('click');
$('.close-modal').on('click', function() {
setTimeout(function() {
$('.close-modal').parent().parent().parent().parent().modal('hide');
}, 200);
});
$('#search-issue').unbind('click');
$('#search-issue').on('click', function() {
var queryIssue = window.location.href.toString();
window.open(
'https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=' +
'is:issue is:all ' +
(common.challengeName) +
' OR ' +
queryIssue
.substr(queryIssue.lastIndexOf('challenges/') + 11)
.replace('/', ''), '_blank');
});
$('#gist-share').unbind('click');
$('#gist-share').on('click', function() {
var gistWindow = window.open('', '_blank');
$('#gist-share')
.attr('disabled', 'true')
.removeClass('btn-danger')
.addClass('btn-warning disabled');
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ('withCredentials' in xhr) {
xhr.open(method, url, true);
} else if (typeof XDomainRequest !== 'undefined') {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
return xhr;
}
var request = createCORSRequest('post', 'https://api.github.com/gists');
if (!request) {
return null;
}
request.onload = function() {
if (
request.readyState === 4 &&
request.status === 201 &&
request.statusText === 'Created'
) {
gistWindow.location.href =
JSON.parse(request.responseText)['html_url'];
}
};
var description = common.username ?
'http://www.freecodecamp.com/' + common.username + ' \'s s' :
'S';
var data = {
description: description + 'olution for ' + common.challengeName,
public: true,
files: {}
};
var queryIssue = window.location.href.toString().split('#?')[0];
var filename = queryIssue
.substr(queryIssue.lastIndexOf('challenges/') + 11)
.replace('/', '') + '.js';
data.files[filename] = {
content: '// ' +
common.challengeName +
'\n' +
(common.username ? '// Author: @' + common.username + '\n' : '') +
'// Challenge: ' +
queryIssue +
'\n' +
'// Learn to Code at Free Code Camp (www.freecodecamp.com)' +
'\n\n' +
window.editor.getValue().trim()
};
request.send(JSON.stringify(data));
});
$('#help-ive-found-a-bug-wiki-article').unbind('click');
$('#help-ive-found-a-bug-wiki-article').on('click', function() {
window.open(
'https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/' +
"Help-I've-Found-a-Bug",
'_blank'
);
});
$('#report-issue').unbind('click');
$('#report-issue').on('click', function() {
var textMessage = [
'Challenge [',
(common.challengeName || window.location.href),
'](',
window.location.href,
') has an issue.\n',
'User Agent is: <code>',
navigator.userAgent,
'</code>.\n',
'Please describe how to reproduce this issue, and include ',
'links to screenshots if possible.\n\n'
].join('');
if (
window.editor &&
typeof window.editor.getValue === 'function' &&
window.editor.getValue().trim()
) {
var type;
switch (common.challengeType) {
case main.challengeTypes.HTML_CSS_JQ:
type = 'html';
break;
case main.challengeTypes.JAVASCRIPT:
case main.challengeTypes.BONFIRE:
type = 'javascript';
break;
default:
type = '';
}
textMessage += [
'My code:\n```',
type,
'\n',
window.editor.getValue(),
'\n```\n\n'
].join('');
}
textMessage = encodeURIComponent(textMessage);
$('#issue-modal').modal('hide');
window.open(
'https://github.com/freecodecamp/freecodecamp/issues/new?&body=' +
textMessage, '_blank'
);
});
$('#completed-courseware').unbind('click');
$('#completed-courseware').on('click', function() {
$('#complete-courseware-dialog').modal('show');
});
$('#completed-courseware-editorless').unbind('click');
$('#completed-courseware-editorless').on('click', function() {
$('#complete-courseware-editorless-dialog').modal('show');
});
$('#trigger-pair-modal').unbind('click');
$('#trigger-pair-modal').on('click', function() {
$('#pair-modal').modal('show');
});
$('#trigger-reset-modal').unbind('click');
$('#trigger-reset-modal').on('click', function() {
$('#reset-modal').modal('show');
});
$('#trigger-help-modal').unbind('click');
$('#trigger-help-modal').on('click', function() {
$('#help-modal').modal('show');
});
$('#trigger-issue-modal').unbind('click');
$('#trigger-issue-modal').on('click', function() {
$('#issue-modal').modal('show');
});
$('#completed-zipline-or-basejump').unbind('click');
$('#completed-zipline-or-basejump').on('click', function() {
$('#complete-zipline-or-basejump-dialog').modal('show');
});
$('#next-courseware-button').unbind('click');
$('#next-courseware-button').on('click', function() {
$('#next-courseware-button').unbind('click');
if ($('.signup-btn-nav').length < 1) {
var data;
var completedWith;
var publicURL;
switch (common.challengeType) {
case main.challengeTypes.HTML_CSS_JQ:
case main.challengeTypes.JAVASCRIPT:
case main.challengeTypes.VIDEO:
data = {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName
}
};
$.post('/completed-challenge/', data)
.success(function(res) {
if (!res) {
return;
}
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
})
.fail(function() {
window.location.href = '/challenges';
});
break;
case main.challengeTypes.ZIPLINE:
completedWith = $('#completed-with').val() || null;
publicURL = $('#public-url').val() || null;
data = {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
completedWith: completedWith,
publicURL: publicURL,
challengeType: common.challengeType
}
};
$.post('/completed-zipline-or-basejump/', data)
.success(function() {
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
})
.fail(function() {
window.location.href = '/challenges';
});
break;
case main.challengeTypes.BASEJUMP:
completedWith = $('#completed-with').val() || null;
publicURL = $('#public-url').val() || null;
var githubURL = $('#github-url').val() || null;
data = {
challengeInfo: {
challengeId: common.challengeId,
challengeName: common.challengeName,
completedWith: completedWith,
publicURL: publicURL,
githubURL: githubURL,
challengeType: common.challengeType,
verified: false
}
};
$.post('/completed-zipline-or-basejump/', data)
.success(function() {
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
})
.fail(function() {
window.location.replace(window.location.href);
});
break;
case main.challengeTypes.BONFIRE:
window.location.href = '/challenges/next-challenge?id=' +
common.challengeId;
break;
default:
console.log('Happy Coding!');
break;
}
}
});
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
window.editor.focus();
});
$('#complete-zipline-or-basejump').on('hidden.bs.modal', function() {
window.editor.focus();
});
}
$(window).resize(function() {
reBindModals();
});
reBindModals();
function upvoteHandler(e) {
e.preventDefault();
var upvoteBtn = this;
@ -602,101 +217,9 @@ $(document).ready(function() {
$('#story-submit').on('click', storySubmitButtonHandler);
// fakeiphone positioning hotfix
if (
$('.iphone-position').html() ||
$('.iphone').html()
) {
var startIphonePosition = parseInt(
$('.iphone-position')
.css('top')
.replace('px', ''),
10
);
var startIphone = parseInt(
$('.iphone')
.css('top')
.replace('px', ''),
10
);
$(window).on('scroll', function() {
var courseHeight = $('.courseware-height').height();
var courseTop = $('.courseware-height').offset().top;
var windowScrollTop = $(window).scrollTop();
var phoneHeight = $('.iphone-position').height();
if (courseHeight + courseTop - windowScrollTop - phoneHeight <= 0) {
$('.iphone-position').css(
'top',
startIphonePosition +
courseHeight +
courseTop -
windowScrollTop -
phoneHeight
);
$('.iphone').css(
'top',
startIphonePosition +
courseHeight +
courseTop -
windowScrollTop -
phoneHeight +
120
);
} else {
$('.iphone-position').css('top', startIphonePosition);
$('.iphone').css('top', startIphone);
}
});
}
if ($('.scroll-locker').html()) {
if ($('.scroll-locker').html()) {
main.lockTop();
$(window).on('resize', function() {
main.lockTop();
});
$(window).on('scroll', function() {
main.lockTop();
});
}
var execInProgress = false;
// why is this not $???
document
.getElementById('scroll-locker')
.addEventListener(
'previewUpdateSpy',
function(e) {
if (execInProgress) {
return null;
}
execInProgress = true;
setTimeout(function() {
if (
$($('.scroll-locker').children()[0]).height() - 800 > e.detail
) {
$('.scroll-locker').scrollTop(e.detail);
} else {
var scrollTop = $($('.scroll-locker').children()[0]).height();
$('.scroll-locker').animate({ scrollTop: scrollTop }, 175);
}
execInProgress = false;
}, 750);
},
false
);
}
// map sharing
var alreadyShared = getMapShares();
var alreadyShared = main.getMapShares();
if (lastCompleted && alreadyShared.indexOf(lastCompleted) === -1) {
$('div[id="' + lastCompleted + '"]')
@ -724,8 +247,8 @@ $(document).ready(function() {
username +
'&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap';
setMapShare(challengeBlockName);
main.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
main.setMapShare(challengeBlockName);
window.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
window.location.href = link;
});
});

View File

@ -13,22 +13,18 @@ function importScript(url, error) {
function run(code, cb) {
var err = null;
var result = {
input: code,
output: null,
type: null
};
var result = {};
try {
var codeExec = runHidden(code);
result.type = typeof codeExec;
result.output = stringify(codeExec);
} catch (e) {
err = e;
err = e.mesage;
}
if (err) {
cb(err.message, null);
cb(err, null);
} else {
cb(null, result);
}
@ -40,7 +36,7 @@ function run(code, cb) {
// protects even the worker scope from being accessed
function runHidden(code) {
/* eslint-disable */
/* eslint-disable no-unused-vars */
var indexedDB = null;
var location = null;
var navigator = null;
@ -63,21 +59,15 @@ function runHidden(code) {
var dump = null;
var onoffline = null;
var ononline = null;
/* eslint-enable */
/* eslint-enable no-unused-vars */
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 */

View File

@ -102,7 +102,7 @@ block content
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
include ../partials/challenge-modals
script(type="text/javascript").
var common = window.common = { init: [] };
var common = window.common = window.common || { init: [] };
common.tests = !{JSON.stringify(tests)};
common.head = !{JSON.stringify(head)};

View File

@ -39,25 +39,6 @@ block content
script(type="text/javascript").
var controlEnterHandler = function(e) {
$('body').unbind('keydown');
if (e.metaKey && e.keyCode === 13 ||
e.ctrlKey && e.keyCode === 13) {
$('#complete-courseware-editorless-dialog').modal('show');
} else {
$('body').bind('keydown', controlEnterHandler);
}
};
var modalControlEnterHandler = function(e) {
$('#complete-courseware-editorless-dialog').unbind('keydown');
if (e.metaKey && e.keyCode === 13 ||
e.ctrlKey && e.keyCode === 13) {
$('#next-courseware-button').click();
} else {
$('#complete-courseware-editorless-dialog').bind('keydown', modalControlEnterHandler);
}
};
#complete-courseware-editorless-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeIn.fast-animation
.modal-content
@ -69,21 +50,48 @@ block content
span.completion-icon.ion-checkmark-circled.text-primary
if (user)
a.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf) I've completed this challenge (ctrl + enter)
script.
$('#complete-courseware-editorless-dialog').bind('keydown', modalControlEnterHandler);
else
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) I've completed this challenge (ctrl + enter)
script.
$('body').bind('keydown', controlEnterHandler);
include ../partials/challenge-modals
script.
var common = window.common = { init: [] };
var common = window.common || { init: [] };
common.challengeId = !{JSON.stringify(challengeId)};
common.challengeName = !{JSON.stringify(name)};
common.challengeType = !{JSON.stringify(challengeType)};
common.dashedName = !{JSON.stringify(dashedName)};
common.init.push(function($) {
function controlEnterHandler(e) {
if (
e.keyCode === 13 &&
e.ctrlKey ||
e.metaKey
) {
$('body').unbind('keydown');
$('#complete-courseware-editorless-dialog').modal('show');
}
};
function modalControlEnterHandler(e) {
if (
e.keyCode === 13 &&
e.ctrlKey ||
e.metaKey
) {
$('#complete-courseware-editorless-dialog').unbind('keydown');
$('#next-courseware-button').click();
}
};
if (!{JSON.stringify(user && user.username ? true : false)}) {
$('#complete-courseware-editorless-dialog').bind('keydown', modalControlEnterHandler);
}
$('body').bind('keydown', controlEnterHandler);
$('#completed-courseware-editorless').on('click', function() {
$('#complete-courseware-editorless-dialog').modal('show');
});
});
document.addEventListener('gitter-sidecar-ready', function(e) {
if (window.main) {

View File

@ -105,7 +105,12 @@ block content
if (!!{ JSON.stringify(user ? true : false)}) {
$('#complete-zipline-or-basejump-dialog').on('keydown', common.modalControlEnterHandler);
}
$('#completed-zipline-or-basejump').on('click', function() {
$('#complete-zipline-or-basejump-dialog').modal('show');
});
});
document.addEventListener('gitter-sidecar-ready', function(e) {