More refactoring
This commit is contained in:
@ -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() {
|
$(document).ready(function() {
|
||||||
|
var common = window.common;
|
||||||
|
|
||||||
common.init.forEach(function(init) {
|
common.init.forEach(function(init) {
|
||||||
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');
|
var $preview = $('#preview');
|
||||||
if (typeof $preview.html() !== 'undefined') {
|
if (typeof $preview.html() !== 'undefined') {
|
||||||
$preview.load(function() {
|
$preview.load(function() {
|
||||||
@ -741,5 +18,4 @@ $(document).ready(function() {
|
|||||||
) {
|
) {
|
||||||
common.executeChallenge(true);
|
common.executeChallenge(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
37
client/commonFramework/add-faux-stream.js
Normal file
37
client/commonFramework/add-faux-stream.js
Normal 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));
|
41
client/commonFramework/add-test-to-string.js
Normal file
41
client/commonFramework/add-test-to-string.js
Normal 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));
|
262
client/commonFramework/bindings.js
Normal file
262
client/commonFramework/bindings.js
Normal 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));
|
37
client/commonFramework/code-storage.js
Normal file
37
client/commonFramework/code-storage.js
Normal 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));
|
@ -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));
|
|
@ -5,25 +5,35 @@ window.common = (function(global) {
|
|||||||
common = { init: [] }
|
common = { init: [] }
|
||||||
} = global;
|
} = global;
|
||||||
|
|
||||||
if (!CodeMirror) {
|
const { challengeType = '0' } = common;
|
||||||
return {};
|
|
||||||
|
if (
|
||||||
|
!CodeMirror ||
|
||||||
|
challengeType === '0' ||
|
||||||
|
challengeType === '7'
|
||||||
|
) {
|
||||||
|
common.editor = {};
|
||||||
|
return common;
|
||||||
}
|
}
|
||||||
|
|
||||||
var delay;
|
var delay;
|
||||||
var codeStorageFactory = common.codeStorageFactory;
|
var codeStorageFactory = common.codeStorageFactory;
|
||||||
|
|
||||||
var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), {
|
var editor = CodeMirror.fromTextArea(
|
||||||
lint: true,
|
document.getElementById('codeEditor'),
|
||||||
lineNumbers: true,
|
{
|
||||||
mode: 'javascript',
|
lint: true,
|
||||||
theme: 'monokai',
|
lineNumbers: true,
|
||||||
runnable: true,
|
mode: 'javascript',
|
||||||
matchBrackets: true,
|
theme: 'monokai',
|
||||||
autoCloseBrackets: true,
|
runnable: true,
|
||||||
scrollbarStyle: 'null',
|
matchBrackets: true,
|
||||||
lineWrapping: true,
|
autoCloseBrackets: true,
|
||||||
gutters: ['CodeMirror-lint-markers']
|
scrollbarStyle: 'null',
|
||||||
});
|
lineWrapping: true,
|
||||||
|
gutters: ['CodeMirror-lint-markers']
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
editor.setSize('100%', 'auto');
|
editor.setSize('100%', 'auto');
|
||||||
|
|
79
client/commonFramework/detect-loops-stream.js
Normal file
79
client/commonFramework/detect-loops-stream.js
Normal 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));
|
@ -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));
|
|
119
client/commonFramework/execute-challenge-stream.js
Normal file
119
client/commonFramework/execute-challenge-stream.js
Normal 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));
|
@ -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));
|
|
@ -10,6 +10,18 @@ window.common = (function(global) {
|
|||||||
common.tail = common.tail || [];
|
common.tail = common.tail || [];
|
||||||
common.salt = Math.random();
|
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) {
|
common.arrayToNewLineString = function arrayToNewLineString(seedData) {
|
||||||
seedData = Array.isArray(seedData) ? seedData : [seedData];
|
seedData = Array.isArray(seedData) ? seedData : [seedData];
|
||||||
return seedData.reduce(function(seed, line) {
|
return seedData.reduce(function(seed, line) {
|
||||||
@ -67,13 +79,11 @@ window.common = (function(global) {
|
|||||||
return str.replace(logRegex, '');
|
return str.replace(logRegex, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
common.reassembleTest = function reassembleTest(test, data) {
|
common.reassembleTest = function reassembleTest(code = '', { line, text }) {
|
||||||
var lineNum = test.line;
|
var regexp = new RegExp('\/\/' + line + common.salt);
|
||||||
var regexp = new RegExp('\/\/' + lineNum + common.salt);
|
return code.replace(regexp, text);
|
||||||
return data.input.replace(regexp, test.text);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return common;
|
return common;
|
||||||
})();
|
})();
|
||||||
|
|
@ -7,18 +7,17 @@ window.common = (function(global) {
|
|||||||
|
|
||||||
const { challengeType = '0' } = common;
|
const { challengeType = '0' } = common;
|
||||||
|
|
||||||
if (!CodeMirror) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
!CodeMirror ||
|
||||||
challengeType === '0' ||
|
challengeType === '0' ||
|
||||||
challengeType === '7'
|
challengeType === '7'
|
||||||
) {
|
) {
|
||||||
return {};
|
common.updateOutputDisplay = () => {};
|
||||||
|
common.appendToOutputDisplay = () => {};
|
||||||
|
return common;
|
||||||
}
|
}
|
||||||
|
|
||||||
common.codeOutput = CodeMirror.fromTextArea(
|
var codeOutput = CodeMirror.fromTextArea(
|
||||||
doc.getElementById('codeOutput'),
|
doc.getElementById('codeOutput'),
|
||||||
{
|
{
|
||||||
lineNumbers: false,
|
lineNumbers: false,
|
||||||
@ -29,7 +28,7 @@ window.common = (function(global) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
common.codeOutput.setValue(`
|
codeOutput.setValue(`
|
||||||
/**
|
/**
|
||||||
* Your output will go here.
|
* Your output will go here.
|
||||||
* Console.log() -type statements
|
* 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;
|
return common;
|
||||||
}(window));
|
}(window));
|
132
client/commonFramework/phone-scroll-lock.js
Normal file
132
client/commonFramework/phone-scroll-lock.js
Normal 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;
|
||||||
|
}());
|
56
client/commonFramework/report-issue.js
Normal file
56
client/commonFramework/report-issue.js
Normal 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));
|
35
client/commonFramework/run-tests-stream.js
Normal file
35
client/commonFramework/run-tests-stream.js
Normal 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));
|
@ -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));
|
|
70
client/commonFramework/show-completion.js
Normal file
70
client/commonFramework/show-completion.js
Normal 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));
|
@ -8,15 +8,11 @@ window.common = (function({ $, common = { init: [] }}) {
|
|||||||
const submitModalId = '#challenge-step-modal';
|
const submitModalId = '#challenge-step-modal';
|
||||||
|
|
||||||
function getPreviousStep($challengeSteps) {
|
function getPreviousStep($challengeSteps) {
|
||||||
var length = $challengeSteps.length;
|
|
||||||
var $prevStep = false;
|
var $prevStep = false;
|
||||||
var prevStepIndex = 0;
|
var prevStepIndex = 0;
|
||||||
$challengeSteps.each(function(index) {
|
$challengeSteps.each(function(index) {
|
||||||
var $step = $(this);
|
var $step = $(this);
|
||||||
if (
|
if (!$step.hasClass('hidden')) {
|
||||||
!$step.hasClass('hidden') &&
|
|
||||||
index + 1 !== length
|
|
||||||
) {
|
|
||||||
prevStepIndex = index - 1;
|
prevStepIndex = index - 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -50,7 +46,8 @@ window.common = (function({ $, common = { init: [] }}) {
|
|||||||
var prevStep = getPreviousStep($(stepClass));
|
var prevStep = getPreviousStep($(stepClass));
|
||||||
$(this)
|
$(this)
|
||||||
.parent()
|
.parent()
|
||||||
.removeClass('fadeOutLeft')
|
.parent()
|
||||||
|
.removeClass('slideInLeft slideInRight')
|
||||||
.addClass('animated fadeOutRight fast-animation')
|
.addClass('animated fadeOutRight fast-animation')
|
||||||
.delay(250)
|
.delay(250)
|
||||||
.queue(function(prev) {
|
.queue(function(prev) {
|
||||||
@ -58,7 +55,7 @@ window.common = (function({ $, common = { init: [] }}) {
|
|||||||
if (prevStep) {
|
if (prevStep) {
|
||||||
$(prevStep)
|
$(prevStep)
|
||||||
.removeClass('hidden')
|
.removeClass('hidden')
|
||||||
.removeClass('slideInRight')
|
.removeClass('fadeOutLeft fadeOutRight')
|
||||||
.addClass('animated slideInLeft fast-animation')
|
.addClass('animated slideInLeft fast-animation')
|
||||||
.delay(500)
|
.delay(500)
|
||||||
.queue(function(prev) {
|
.queue(function(prev) {
|
||||||
@ -74,7 +71,8 @@ window.common = (function({ $, common = { init: [] }}) {
|
|||||||
var nextStep = getNextStep($(stepClass));
|
var nextStep = getNextStep($(stepClass));
|
||||||
$(this)
|
$(this)
|
||||||
.parent()
|
.parent()
|
||||||
.removeClass('fadeOutRight')
|
.parent()
|
||||||
|
.removeClass('slideInRight slideInLeft')
|
||||||
.addClass('animated fadeOutLeft fast-animation')
|
.addClass('animated fadeOutLeft fast-animation')
|
||||||
.delay(250)
|
.delay(250)
|
||||||
.queue(function(next) {
|
.queue(function(next) {
|
||||||
@ -82,7 +80,7 @@ window.common = (function({ $, common = { init: [] }}) {
|
|||||||
if (nextStep) {
|
if (nextStep) {
|
||||||
$(nextStep)
|
$(nextStep)
|
||||||
.removeClass('hidden')
|
.removeClass('hidden')
|
||||||
.removeClass('slideInLeft')
|
.removeClass('fadeOutRight fadeOutLeft')
|
||||||
.addClass('animated slideInRight fast-animation')
|
.addClass('animated slideInRight fast-animation')
|
||||||
.delay(500)
|
.delay(500)
|
||||||
.queue(function(next) {
|
.queue(function(next) {
|
0
client/commonFramework/test-script-stream.js
Normal file
0
client/commonFramework/test-script-stream.js
Normal file
40
client/commonFramework/update-preview.js
Normal file
40
client/commonFramework/update-preview.js
Normal 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));
|
@ -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 $() {
|
function $() {
|
||||||
if (!(this instanceof $)) {
|
if (!(this instanceof $)) {
|
||||||
return new $();
|
return new $();
|
@ -7,20 +7,22 @@ window.$(function() {
|
|||||||
var tests = parent.tests;
|
var tests = parent.tests;
|
||||||
var common = parent.common;
|
var common = parent.common;
|
||||||
var editorValue = common.editor.getValue();
|
var editorValue = common.editor.getValue();
|
||||||
|
var editor = common.editor;
|
||||||
|
|
||||||
common.tests.forEach(test => {
|
var userTests = common.tests.map(test => {
|
||||||
|
var userTest = {};
|
||||||
try {
|
try {
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
eval(test);
|
eval(test);
|
||||||
/* eslint-enable no-eval */
|
/* eslint-enable no-eval */
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parent.postError(JSON.stringify(e.message.split(':').shift()));
|
userTest.err = e.message.split(':').shift();
|
||||||
} finally {
|
} finally {
|
||||||
parent.postSuccess(
|
userTest.text = test
|
||||||
JSON.stringify(
|
.split(',')
|
||||||
test.split(',').pop().replace(/\'/g, '').replace(/\)/, '')
|
.pop()
|
||||||
)
|
.replace(/\'/g, '')
|
||||||
);
|
.replace(/\)/, '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
493
client/main.js
493
client/main.js
@ -4,15 +4,6 @@ main.mapShareKey = 'map-shares';
|
|||||||
|
|
||||||
main.ga = window.ga || function() {};
|
main.ga = window.ga || function() {};
|
||||||
|
|
||||||
main.challengeTypes = {
|
|
||||||
'HTML_CSS_JQ': '0',
|
|
||||||
'JAVASCRIPT': '1',
|
|
||||||
'VIDEO': '2',
|
|
||||||
'ZIPLINE': '3',
|
|
||||||
'BASEJUMP': '4',
|
|
||||||
'BONFIRE': '5'
|
|
||||||
};
|
|
||||||
|
|
||||||
main = (function(main) {
|
main = (function(main) {
|
||||||
|
|
||||||
// should be set before gitter script loads
|
// should be set before gitter script loads
|
||||||
@ -112,47 +103,11 @@ main = (function(main) {
|
|||||||
return main;
|
return main;
|
||||||
}(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' ?
|
var lastCompleted = typeof lastCompleted !== 'undefined' ?
|
||||||
lastCompleted :
|
lastCompleted :
|
||||||
'';
|
'';
|
||||||
|
|
||||||
function getMapShares() {
|
main.getMapShares = function getMapShares() {
|
||||||
var alreadyShared = JSON.parse(
|
var alreadyShared = JSON.parse(
|
||||||
localStorage.getItem(main.mapShareKey) ||
|
localStorage.getItem(main.mapShareKey) ||
|
||||||
'[]'
|
'[]'
|
||||||
@ -163,10 +118,10 @@ function getMapShares() {
|
|||||||
alreadyShared = [];
|
alreadyShared = [];
|
||||||
}
|
}
|
||||||
return alreadyShared;
|
return alreadyShared;
|
||||||
}
|
};
|
||||||
|
|
||||||
function setMapShare(id) {
|
main.setMapShare = function setMapShare(id) {
|
||||||
var alreadyShared = getMapShares();
|
var alreadyShared = main.getMapShares();
|
||||||
var found = false;
|
var found = false;
|
||||||
alreadyShared.forEach(function(_id) {
|
alreadyShared.forEach(function(_id) {
|
||||||
if (_id === id) {
|
if (_id === id) {
|
||||||
@ -178,23 +133,10 @@ function setMapShare(id) {
|
|||||||
}
|
}
|
||||||
localStorage.setItem(main.mapShareKey, JSON.stringify(alreadyShared));
|
localStorage.setItem(main.mapShareKey, JSON.stringify(alreadyShared));
|
||||||
return alreadyShared;
|
return alreadyShared;
|
||||||
}
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(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 CSRF_HEADER = 'X-CSRF-Token';
|
||||||
|
|
||||||
var setCSRFToken = function(securityToken) {
|
var setCSRFToken = function(securityToken) {
|
||||||
@ -207,38 +149,6 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
|
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() {
|
$('img').error(function() {
|
||||||
$(this)
|
$(this)
|
||||||
.unbind('error')
|
.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) {
|
function upvoteHandler(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var upvoteBtn = this;
|
var upvoteBtn = this;
|
||||||
@ -602,101 +217,9 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
$('#story-submit').on('click', storySubmitButtonHandler);
|
$('#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
|
// map sharing
|
||||||
var alreadyShared = getMapShares();
|
var alreadyShared = main.getMapShares();
|
||||||
|
|
||||||
if (lastCompleted && alreadyShared.indexOf(lastCompleted) === -1) {
|
if (lastCompleted && alreadyShared.indexOf(lastCompleted) === -1) {
|
||||||
$('div[id="' + lastCompleted + '"]')
|
$('div[id="' + lastCompleted + '"]')
|
||||||
@ -724,8 +247,8 @@ $(document).ready(function() {
|
|||||||
username +
|
username +
|
||||||
'&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap';
|
'&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap';
|
||||||
|
|
||||||
setMapShare(challengeBlockName);
|
main.setMapShare(challengeBlockName);
|
||||||
main.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
|
window.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
|
||||||
window.location.href = link;
|
window.location.href = link;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,22 +13,18 @@ function importScript(url, error) {
|
|||||||
|
|
||||||
function run(code, cb) {
|
function run(code, cb) {
|
||||||
var err = null;
|
var err = null;
|
||||||
var result = {
|
var result = {};
|
||||||
input: code,
|
|
||||||
output: null,
|
|
||||||
type: null
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var codeExec = runHidden(code);
|
var codeExec = runHidden(code);
|
||||||
result.type = typeof codeExec;
|
result.type = typeof codeExec;
|
||||||
result.output = stringify(codeExec);
|
result.output = stringify(codeExec);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e.mesage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err.message, null);
|
cb(err, null);
|
||||||
} else {
|
} else {
|
||||||
cb(null, result);
|
cb(null, result);
|
||||||
}
|
}
|
||||||
@ -40,7 +36,7 @@ function run(code, cb) {
|
|||||||
// protects even the worker scope from being accessed
|
// protects even the worker scope from being accessed
|
||||||
function runHidden(code) {
|
function runHidden(code) {
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable no-unused-vars */
|
||||||
var indexedDB = null;
|
var indexedDB = null;
|
||||||
var location = null;
|
var location = null;
|
||||||
var navigator = null;
|
var navigator = null;
|
||||||
@ -63,21 +59,15 @@ function runHidden(code) {
|
|||||||
var dump = null;
|
var dump = null;
|
||||||
var onoffline = null;
|
var onoffline = null;
|
||||||
var ononline = null;
|
var ononline = null;
|
||||||
/* eslint-enable */
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
var error = null;
|
var error = null;
|
||||||
error = importScript(
|
|
||||||
error,
|
|
||||||
'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
error = importScript(
|
error = importScript(
|
||||||
'https://cdnjs.cloudflare.com/ajax/libs/chai/2.2.0/chai.min.js'
|
'https://cdnjs.cloudflare.com/ajax/libs/chai/2.2.0/chai.min.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/* eslint-disable*/
|
/* eslint-disable*/
|
||||||
var expect = chai.expect;
|
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ block content
|
|||||||
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
||||||
include ../partials/challenge-modals
|
include ../partials/challenge-modals
|
||||||
script(type="text/javascript").
|
script(type="text/javascript").
|
||||||
var common = window.common = { init: [] };
|
var common = window.common = window.common || { init: [] };
|
||||||
|
|
||||||
common.tests = !{JSON.stringify(tests)};
|
common.tests = !{JSON.stringify(tests)};
|
||||||
common.head = !{JSON.stringify(head)};
|
common.head = !{JSON.stringify(head)};
|
||||||
|
@ -39,25 +39,6 @@ block content
|
|||||||
|
|
||||||
script(type="text/javascript").
|
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')
|
#complete-courseware-editorless-dialog.modal(tabindex='-1')
|
||||||
.modal-dialog.animated.fadeIn.fast-animation
|
.modal-dialog.animated.fadeIn.fast-animation
|
||||||
.modal-content
|
.modal-content
|
||||||
@ -69,21 +50,48 @@ block content
|
|||||||
span.completion-icon.ion-checkmark-circled.text-primary
|
span.completion-icon.ion-checkmark-circled.text-primary
|
||||||
if (user)
|
if (user)
|
||||||
a.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf) I've completed this challenge (ctrl + enter)
|
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
|
else
|
||||||
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) I've completed this challenge (ctrl + enter)
|
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
|
include ../partials/challenge-modals
|
||||||
script.
|
script.
|
||||||
var common = window.common = { init: [] };
|
var common = window.common || { init: [] };
|
||||||
|
|
||||||
common.challengeId = !{JSON.stringify(challengeId)};
|
common.challengeId = !{JSON.stringify(challengeId)};
|
||||||
common.challengeName = !{JSON.stringify(name)};
|
common.challengeName = !{JSON.stringify(name)};
|
||||||
common.challengeType = !{JSON.stringify(challengeType)};
|
common.challengeType = !{JSON.stringify(challengeType)};
|
||||||
common.dashedName = !{JSON.stringify(dashedName)};
|
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) {
|
document.addEventListener('gitter-sidecar-ready', function(e) {
|
||||||
if (window.main) {
|
if (window.main) {
|
||||||
|
@ -105,9 +105,14 @@ block content
|
|||||||
if (!!{ JSON.stringify(user ? true : false)}) {
|
if (!!{ JSON.stringify(user ? true : false)}) {
|
||||||
$('#complete-zipline-or-basejump-dialog').on('keydown', common.modalControlEnterHandler);
|
$('#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) {
|
document.addEventListener('gitter-sidecar-ready', function(e) {
|
||||||
var room = 'freecodecamp/help';
|
var room = 'freecodecamp/help';
|
||||||
var title;
|
var title;
|
||||||
|
Reference in New Issue
Block a user