chore: Remove unused/deprecated files
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
loopProtect,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
loopProtect.hit = function hit(line) {
|
||||
var err = 'Error: Exiting potential infinite loop at line ' +
|
||||
line +
|
||||
'. To disable loop protection, write: \n\\/\\/ noprotect\nas the first' +
|
||||
'line. Beware that if you do have an infinite loop in your code' +
|
||||
'this will crash your browser.';
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
common.addLoopProtect = function addLoopProtect(code = '') {
|
||||
return loopProtect(code);
|
||||
};
|
||||
|
||||
return common;
|
||||
})(window);
|
@@ -1,217 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
$,
|
||||
Rx: { Observable },
|
||||
common = { init: [] },
|
||||
Mousetrap
|
||||
} = global;
|
||||
|
||||
common.ctrlEnterClickHandler = function ctrlEnterClickHandler(e) {
|
||||
// ctrl + enter or cmd + enter
|
||||
if (
|
||||
e.keyCode === 13 &&
|
||||
(e.metaKey || e.ctrlKey)
|
||||
) {
|
||||
$('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler);
|
||||
if ($('#submit-challenge').length > 0) {
|
||||
$('#submit-challenge').click();
|
||||
} else {
|
||||
window.location = '/challenges/next-challenge?id=' + common.challengeId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
common.init.push(function($) {
|
||||
|
||||
var $marginFix = $('.innerMarginFix');
|
||||
$marginFix.css('min-height', $marginFix.height());
|
||||
|
||||
common.submitBtn$ = Observable.fromEvent($('#submitButton'), 'click');
|
||||
|
||||
common.resetBtn$ = Observable.fromEvent($('#reset-button'), 'click');
|
||||
|
||||
// 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
|
||||
);
|
||||
});
|
||||
|
||||
// set focus keybind
|
||||
Mousetrap.bind(['command+shift+e', 'ctrl+shift+e'], () => {
|
||||
common.editor.focus();
|
||||
});
|
||||
|
||||
// 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 solution = $('#public-url').val() || null;
|
||||
var githubLink = $('#github-url').val() || null;
|
||||
switch (common.challengeType) {
|
||||
case common.challengeTypes.VIDEO:
|
||||
data = {
|
||||
id: common.challengeId,
|
||||
name: common.challengeName,
|
||||
challengeType: +common.challengeType
|
||||
};
|
||||
$.ajax({
|
||||
url: '/completed-challenge/',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json'
|
||||
})
|
||||
.success(function(res) {
|
||||
if (!res) {
|
||||
return;
|
||||
}
|
||||
window.location.href = '/challenges/next-challenge?id=' +
|
||||
common.challengeId;
|
||||
})
|
||||
.fail(function() {
|
||||
window.location.replace(window.location.href);
|
||||
});
|
||||
|
||||
break;
|
||||
case common.challengeTypes.BASEJUMP:
|
||||
case common.challengeTypes.ZIPLINE:
|
||||
data = {
|
||||
id: common.challengeId,
|
||||
name: common.challengeName,
|
||||
challengeType: +common.challengeType,
|
||||
solution,
|
||||
githubLink
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/completed-zipline-or-basejump/',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json'
|
||||
})
|
||||
.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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (common.challengeName) {
|
||||
window.ga('send', 'event', 'Challenge', 'loaded', common.gaName);
|
||||
}
|
||||
|
||||
$('.modal').on('show.bs.modal', function() {
|
||||
$('.gitter-chat-embed, .map-aside')
|
||||
.addClass('is-collapsed');
|
||||
});
|
||||
|
||||
$('#complete-courseware-dialog').on('hidden.bs.modal', function() {
|
||||
if (common.editor.focus) {
|
||||
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');
|
||||
});
|
||||
|
||||
$('#show-solution').on('click', function() {
|
||||
$('#complete-courseware-dialog').modal('hide');
|
||||
});
|
||||
|
||||
$('#challenge-help-btn').on('click', function() {
|
||||
$('.map-aside, #chat-embed-main').addClass('is-collapsed');
|
||||
});
|
||||
|
||||
$('#help-ive-found-a-bug-wiki-article').on('click', function() {
|
||||
window.open(
|
||||
'http://forum.freecodecamp.com/t/how-to-report-a-bug/19543',
|
||||
'_blank'
|
||||
);
|
||||
});
|
||||
|
||||
$('#search-issue').on('click', function() {
|
||||
var queryIssue = window.location.href
|
||||
.toString()
|
||||
.split('?')[0]
|
||||
.replace(/(#*)$/, '');
|
||||
window.open(
|
||||
'https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=' +
|
||||
'is:issue is:all ' +
|
||||
(common.challengeName) +
|
||||
' OR ' +
|
||||
queryIssue
|
||||
.substr(queryIssue.lastIndexOf('challenges/') + 11)
|
||||
.replace('/', ''), '_blank');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,69 +0,0 @@
|
||||
// depends on: codeUri
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
localStorage,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const {
|
||||
replaceNoprotect
|
||||
} = common;
|
||||
|
||||
var challengePrefix = [
|
||||
'Bonfire: ',
|
||||
'Waypoint: ',
|
||||
'Zipline: ',
|
||||
'Basejump: ',
|
||||
'Checkpoint: '],
|
||||
item;
|
||||
|
||||
var codeStorage = {
|
||||
getStoredValue(key) {
|
||||
if (
|
||||
!localStorage ||
|
||||
typeof localStorage.getItem !== 'function' ||
|
||||
!key ||
|
||||
typeof key !== 'string'
|
||||
) {
|
||||
console.log('unable to read from storage');
|
||||
return '';
|
||||
}
|
||||
if (localStorage.getItem(key + 'Val')) {
|
||||
return '' + localStorage.getItem(key + 'Val');
|
||||
} else {
|
||||
for (var i = 0; i <= challengePrefix.length; i++) {
|
||||
item = localStorage.getItem(challengePrefix[i] + key + 'Val');
|
||||
if (item) {
|
||||
return '' + item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
isAlive: function(key) {
|
||||
var val = this.getStoredValue(key);
|
||||
return val !== 'null' &&
|
||||
val !== 'undefined' &&
|
||||
(val && val.length > 0);
|
||||
},
|
||||
|
||||
updateStorage(key, code) {
|
||||
if (
|
||||
!localStorage ||
|
||||
typeof localStorage.setItem !== 'function' ||
|
||||
!key ||
|
||||
typeof key !== 'string'
|
||||
) {
|
||||
console.log('unable to save to storage');
|
||||
return code;
|
||||
}
|
||||
localStorage.setItem(key + 'Val', replaceNoprotect(code));
|
||||
return code;
|
||||
}
|
||||
};
|
||||
|
||||
common.codeStorage = codeStorage;
|
||||
|
||||
return common;
|
||||
}(window, window.common));
|
@@ -1,148 +0,0 @@
|
||||
// store code in the URL
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
encodeURIComponent: encode,
|
||||
decodeURIComponent: decode,
|
||||
location,
|
||||
history,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const {
|
||||
replaceScriptTags,
|
||||
replaceSafeTags,
|
||||
replaceFormActionAttr,
|
||||
replaceFccfaaAttr,
|
||||
replaceNoprotect
|
||||
} = common;
|
||||
|
||||
const queryRegex = /^(\?|#\?)/;
|
||||
function encodeFcc(val) {
|
||||
return replaceScriptTags(replaceFormActionAttr(replaceNoprotect(val)));
|
||||
}
|
||||
|
||||
function decodeFcc(val) {
|
||||
return replaceSafeTags(replaceFccfaaAttr(val));
|
||||
}
|
||||
|
||||
var codeUri = {
|
||||
encode: function(code) {
|
||||
return encode(code);
|
||||
},
|
||||
decode: function(code) {
|
||||
try {
|
||||
return decode(code);
|
||||
} catch (ignore) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
isInQuery: function(query) {
|
||||
var decoded = codeUri.decode(query);
|
||||
if (!decoded || typeof decoded.split !== 'function') {
|
||||
return false;
|
||||
}
|
||||
return decoded
|
||||
.replace(queryRegex, '')
|
||||
.split('&')
|
||||
.reduce(function(found, param) {
|
||||
var key = param.split('=')[0];
|
||||
if (key === 'solution') {
|
||||
return true;
|
||||
}
|
||||
return found;
|
||||
}, false);
|
||||
},
|
||||
isAlive: function() {
|
||||
return codeUri.enabled &&
|
||||
codeUri.isInQuery(location.search) ||
|
||||
codeUri.isInQuery(location.hash);
|
||||
},
|
||||
getKeyInQuery(query, keyToFind = '') {
|
||||
return query
|
||||
.split('&')
|
||||
.reduce(function(oldValue, param) {
|
||||
var key = param.split('=')[0];
|
||||
var value = param
|
||||
.split('=')
|
||||
.slice(1)
|
||||
.join('=');
|
||||
|
||||
if (key === keyToFind) {
|
||||
return value;
|
||||
}
|
||||
return oldValue;
|
||||
}, null);
|
||||
},
|
||||
getSolutionFromQuery(query = '') {
|
||||
return decodeFcc(
|
||||
codeUri.decode(codeUri.getKeyInQuery(query, 'solution'))
|
||||
);
|
||||
},
|
||||
parse: function() {
|
||||
if (!codeUri.enabled) {
|
||||
return null;
|
||||
}
|
||||
var query;
|
||||
if (location.search && codeUri.isInQuery(location.search)) {
|
||||
query = location.search.replace(/^\?/, '');
|
||||
|
||||
if (history && typeof history.replaceState === 'function') {
|
||||
history.replaceState(
|
||||
history.state,
|
||||
null,
|
||||
location.href.split('?')[0]
|
||||
);
|
||||
location.hash = '#?' + encodeFcc(query);
|
||||
}
|
||||
} else {
|
||||
query = location.hash.replace(/^\#\?/, '');
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getSolutionFromQuery(query);
|
||||
},
|
||||
querify: function(solution) {
|
||||
if (!codeUri.enabled) {
|
||||
return null;
|
||||
}
|
||||
if (history && typeof history.replaceState === 'function') {
|
||||
// grab the url up to the query
|
||||
// destroy any hash symbols still clinging to life
|
||||
const url = (location.href.split('?')[0]).replace(/(#*)$/, '');
|
||||
history.replaceState(
|
||||
history.state,
|
||||
null,
|
||||
url +
|
||||
'#?' +
|
||||
(codeUri.shouldRun() ? '' : 'run=disabled&') +
|
||||
'solution=' +
|
||||
codeUri.encode(encodeFcc(solution))
|
||||
);
|
||||
} else {
|
||||
location.hash = '?solution=' +
|
||||
codeUri.encode(encodeFcc(solution));
|
||||
}
|
||||
|
||||
return solution;
|
||||
},
|
||||
enabled: true,
|
||||
shouldRun() {
|
||||
return !this.getKeyInQuery(
|
||||
(location.search || location.hash).replace(queryRegex, ''),
|
||||
'run'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
common.init.push(function() {
|
||||
codeUri.parse();
|
||||
});
|
||||
|
||||
common.codeUri = codeUri;
|
||||
common.shouldRun = () => codeUri.shouldRun();
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,119 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
Rx: { Subject, Observable },
|
||||
CodeMirror,
|
||||
emmetCodeMirror,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const { challengeType = '0', challengeTypes } = common;
|
||||
|
||||
if (
|
||||
!CodeMirror ||
|
||||
challengeType === challengeTypes.BASEJUMP ||
|
||||
challengeType === challengeTypes.ZIPLINE ||
|
||||
challengeType === challengeTypes.VIDEO ||
|
||||
challengeType === challengeTypes.STEP ||
|
||||
challengeType === challengeTypes.HIKES
|
||||
) {
|
||||
common.editor = {};
|
||||
return common;
|
||||
}
|
||||
|
||||
var editor = CodeMirror.fromTextArea(
|
||||
document.getElementById('codeEditor'),
|
||||
{
|
||||
lint: true,
|
||||
lineNumbers: true,
|
||||
mode: 'javascript',
|
||||
theme: 'monokai',
|
||||
runnable: true,
|
||||
matchBrackets: true,
|
||||
autoCloseBrackets: true,
|
||||
scrollbarStyle: 'null',
|
||||
lineWrapping: true,
|
||||
gutters: ['CodeMirror-lint-markers']
|
||||
}
|
||||
);
|
||||
|
||||
editor.setSize('100%', 'auto');
|
||||
|
||||
common.editorExecute$ = new Subject();
|
||||
common.editorKeyUp$ = Observable.fromEventPattern(
|
||||
(handler) => editor.on('keyup', handler),
|
||||
(handler) => editor.off('keyup', handler)
|
||||
);
|
||||
|
||||
editor.setOption('extraKeys', {
|
||||
Tab: function(cm) {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('add');
|
||||
} else {
|
||||
var spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
|
||||
cm.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
'Shift-Tab': function(cm) {
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('subtract');
|
||||
} else {
|
||||
var spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
|
||||
cm.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
'Ctrl-Enter': function() {
|
||||
common.editorExecute$.onNext();
|
||||
return false;
|
||||
},
|
||||
'Cmd-Enter': function() {
|
||||
common.editorExecute$.onNext();
|
||||
return false;
|
||||
},
|
||||
'Ctrl-/': function(cm) {
|
||||
cm.toggleComment();
|
||||
},
|
||||
'Cmd-/': function(cm) {
|
||||
cm.toggleComment();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var info = editor.getScrollInfo();
|
||||
|
||||
var after = editor.charCoords({
|
||||
line: editor.getCursor().line + 1,
|
||||
ch: 0
|
||||
}, 'local').top;
|
||||
|
||||
if (info.top + info.clientHeight < after) {
|
||||
editor.scrollTo(null, after - info.clientHeight + 3);
|
||||
}
|
||||
|
||||
if (emmetCodeMirror) {
|
||||
emmetCodeMirror(
|
||||
editor,
|
||||
{
|
||||
'Cmd-E': 'emmet.expand_abbreviation',
|
||||
Tab: 'emmet.expand_abbreviation_with_tab',
|
||||
Enter: 'emmet.insert_formatted_line_break_only'
|
||||
}
|
||||
);
|
||||
}
|
||||
common.init.push(function() {
|
||||
let editorValue;
|
||||
if (common.codeUri.isAlive()) {
|
||||
editorValue = common.codeUri.parse();
|
||||
} else {
|
||||
editorValue = common.codeStorage.isAlive(common.challengeName) ?
|
||||
common.codeStorage.getStoredValue(common.challengeName) :
|
||||
common.seed;
|
||||
}
|
||||
|
||||
editor.setValue(common.replaceSafeTags(editorValue));
|
||||
editor.refresh();
|
||||
});
|
||||
|
||||
common.editor = editor;
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,59 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
Rx: { Observable },
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const detectFunctionCall = /function\s*?\(|function\s+\w+\s*?\(/gi;
|
||||
const detectUnsafeJQ = /\$\s*?\(\s*?\$\s*?\)/gi;
|
||||
const detectUnsafeConsoleCall = /if\s\(null\)\sconsole\.log\(1\);/gi;
|
||||
const detectInComments = new RegExp(['\\/\\/.*?function.*?|',
|
||||
'\\/\\*[\\s\\w\\W]*?function',
|
||||
'[\\s\\w\\W]*?\\*\\/'].join(''), 'gi');
|
||||
|
||||
common.detectUnsafeCode$ = function detectUnsafeCode$(code) {
|
||||
const openingComments = code.match(/\/\*/gi);
|
||||
const closingComments = code.match(/\*\//gi);
|
||||
|
||||
// checks if the number of opening comments(/*) matches the number of
|
||||
// closing comments(*/)
|
||||
if (
|
||||
openingComments &&
|
||||
(
|
||||
!closingComments ||
|
||||
openingComments.length > closingComments.length
|
||||
)
|
||||
) {
|
||||
|
||||
return Observable.throw(
|
||||
new Error('SyntaxError: Unfinished multi-line comment')
|
||||
);
|
||||
}
|
||||
|
||||
if (code.match(detectUnsafeJQ)) {
|
||||
return Observable.throw(
|
||||
new Error('Unsafe $($)')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
code.match(/function/g) &&
|
||||
!code.match(detectFunctionCall) &&
|
||||
!code.match(detectInComments)
|
||||
) {
|
||||
return Observable.throw(
|
||||
new Error('SyntaxError: Unsafe or unfinished function declaration')
|
||||
);
|
||||
}
|
||||
|
||||
if (code.match(detectUnsafeConsoleCall)) {
|
||||
return Observable.throw(
|
||||
new Error('Invalid if (null) console.log(1); detected')
|
||||
);
|
||||
}
|
||||
|
||||
return Observable.just(code);
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,32 +0,0 @@
|
||||
window.common = (function({ $, common = { init: [] }}) {
|
||||
|
||||
common.displayTestResults = function displayTestResults(data = [], down) {
|
||||
$('#testSuite').children().remove();
|
||||
data.forEach(({ err = false, text = '' }) => {
|
||||
var iconClass = err ?
|
||||
'"ion-close-circled big-error-icon"' :
|
||||
'"ion-checkmark-circled big-success-icon"';
|
||||
|
||||
$('<div></div>').html(`
|
||||
<div class='row'>
|
||||
<div class='col-xs-2 text-center'>
|
||||
<i class=${iconClass}></i>
|
||||
</div>
|
||||
<div class='col-xs-10 test-output'>
|
||||
${text.split('message: ').pop().replace(/\'\);/g, '')}
|
||||
</div>
|
||||
<div class='ten-pixel-break'/>
|
||||
</div>
|
||||
`)
|
||||
.appendTo($('#testSuite'));
|
||||
});
|
||||
if (down) {
|
||||
$('#scroll-locker').animate(
|
||||
{ scrollTop: $(document).height() }, 'slow'
|
||||
);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,177 +0,0 @@
|
||||
$(document).ready(function() {
|
||||
const common = window.common;
|
||||
const { Observable } = window.Rx;
|
||||
const {
|
||||
addLoopProtect,
|
||||
challengeName,
|
||||
challengeType,
|
||||
challengeTypes
|
||||
} = common;
|
||||
|
||||
common.init.forEach(function(init) {
|
||||
init($);
|
||||
});
|
||||
|
||||
// only run if editor present
|
||||
if (common.editor.getValue) {
|
||||
const code$ = common.editorKeyUp$
|
||||
.debounce(750)
|
||||
.map(() => common.editor.getValue())
|
||||
.distinctUntilChanged()
|
||||
.shareReplay();
|
||||
|
||||
// update storage
|
||||
code$.subscribe(
|
||||
code => {
|
||||
common.codeStorage.updateStorage(common.challengeName, code);
|
||||
common.codeUri.querify(code);
|
||||
},
|
||||
err => console.error(err)
|
||||
);
|
||||
|
||||
code$
|
||||
// only run for HTML
|
||||
.filter(() => common.challengeType === challengeTypes.HTML)
|
||||
.flatMap(code => {
|
||||
return common.detectUnsafeCode$(code)
|
||||
.map(() => {
|
||||
const combinedCode = common.head + code + common.tail;
|
||||
|
||||
return addLoopProtect(combinedCode);
|
||||
})
|
||||
.flatMap(code => common.updatePreview$(code))
|
||||
.flatMap(() => common.checkPreview$({ code }))
|
||||
.catch(err => Observable.just({ err }));
|
||||
})
|
||||
.subscribe(
|
||||
({ err }) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return common.updatePreview$(`
|
||||
<h1>${err}</h1>
|
||||
`).subscribe(() => {});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
err => console.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
common.resetBtn$
|
||||
.doOnNext(() => {
|
||||
common.editor.setValue(common.replaceSafeTags(common.seed));
|
||||
})
|
||||
.flatMap(() => {
|
||||
return common.executeChallenge$()
|
||||
.catch(err => Observable.just({ err }));
|
||||
})
|
||||
.subscribe(
|
||||
({ err, output, originalCode, tests }) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return common.updateOutputDisplay('' + err);
|
||||
}
|
||||
common.codeStorage.updateStorage(challengeName, originalCode);
|
||||
common.codeUri.querify(originalCode);
|
||||
common.displayTestResults(tests, true);
|
||||
common.updateOutputDisplay(output);
|
||||
return null;
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
common.updateOutputDisplay('' + err);
|
||||
}
|
||||
);
|
||||
|
||||
Observable.merge(
|
||||
common.editorExecute$,
|
||||
common.submitBtn$
|
||||
)
|
||||
.flatMap(() => {
|
||||
common.appendToOutputDisplay('\n// testing challenge...');
|
||||
return common.executeChallenge$()
|
||||
.map(({ tests, ...rest }) => {
|
||||
const solved = tests.every(test => !test.err);
|
||||
return { ...rest, tests, solved };
|
||||
})
|
||||
.catch(err => Observable.just({ err }));
|
||||
})
|
||||
.subscribe(
|
||||
({ err, solved, output, tests }) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
if (common.challengeType === common.challengeTypes.HTML) {
|
||||
return common.updatePreview$(`
|
||||
<h1>${err}</h1>
|
||||
`).first().subscribe(() => {});
|
||||
}
|
||||
return common.updateOutputDisplay('' + err);
|
||||
}
|
||||
common.updateOutputDisplay(output);
|
||||
common.displayTestResults(tests, true);
|
||||
if (solved) {
|
||||
common.showCompletion();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
({ err }) => {
|
||||
console.error(err);
|
||||
common.updateOutputDisplay('' + err);
|
||||
}
|
||||
);
|
||||
|
||||
// initial challenge run to populate tests
|
||||
if (challengeType === challengeTypes.HTML) {
|
||||
var $preview = $('#preview');
|
||||
return Observable.fromCallback($preview.ready, $preview)()
|
||||
.delay(500)
|
||||
.flatMap(() => common.executeChallenge$())
|
||||
.catch(err => Observable.just({ err }))
|
||||
.subscribe(
|
||||
({ err, tests }) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
if (common.challengeType === common.challengeTypes.HTML) {
|
||||
return common.updatePreview$(`
|
||||
<h1>${err}</h1>
|
||||
`).subscribe(() => {});
|
||||
}
|
||||
return common.updateOutputDisplay('' + err);
|
||||
}
|
||||
common.displayTestResults(tests, false);
|
||||
return null;
|
||||
},
|
||||
({ err }) => {
|
||||
console.error(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
challengeType === challengeTypes.BONFIRE ||
|
||||
challengeType === challengeTypes.JS
|
||||
) {
|
||||
return Observable.just({})
|
||||
.delay(500)
|
||||
.flatMap(() => common.executeChallenge$())
|
||||
.catch(err => Observable.just({ err }))
|
||||
.subscribe(
|
||||
({ err, originalCode, tests }) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return common.updateOutputDisplay('' + err);
|
||||
}
|
||||
common.codeStorage.updateStorage(challengeName, originalCode);
|
||||
common.displayTestResults(tests, false);
|
||||
return null;
|
||||
},
|
||||
(err) => {
|
||||
console.error(err);
|
||||
common.updateOutputDisplay('' + err);
|
||||
}
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
@@ -1,57 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
ga,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const {
|
||||
addLoopProtect,
|
||||
getJsFromHtml,
|
||||
detectUnsafeCode$,
|
||||
updatePreview$,
|
||||
challengeType,
|
||||
challengeTypes
|
||||
} = common;
|
||||
|
||||
common.executeChallenge$ = function executeChallenge$() {
|
||||
const code = common.editor.getValue();
|
||||
const originalCode = code;
|
||||
const head = common.arrayToNewLineString(common.head);
|
||||
const tail = common.arrayToNewLineString(common.tail);
|
||||
const combinedCode = head + code + tail;
|
||||
|
||||
ga('send', 'event', 'Challenge', 'ran-code', common.gaName);
|
||||
|
||||
// run checks for unsafe code
|
||||
return detectUnsafeCode$(code)
|
||||
// add head and tail and detect loops
|
||||
.map(() => {
|
||||
if (challengeType !== challengeTypes.HTML) {
|
||||
return `<script>;${addLoopProtect(combinedCode)}/**/</script>`;
|
||||
}
|
||||
|
||||
return addLoopProtect(combinedCode);
|
||||
})
|
||||
.flatMap(code => updatePreview$(code))
|
||||
.flatMap(code => {
|
||||
let output;
|
||||
|
||||
if (
|
||||
challengeType === challengeTypes.HTML &&
|
||||
common.hasJs(code)
|
||||
) {
|
||||
output = common.getJsOutput(getJsFromHtml(code));
|
||||
} else if (challengeType !== challengeTypes.HTML) {
|
||||
output = common.getJsOutput(addLoopProtect(combinedCode));
|
||||
}
|
||||
|
||||
return common.runPreviewTests$({
|
||||
tests: common.tests.slice(),
|
||||
originalCode,
|
||||
output
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,23 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
common = { init: [] },
|
||||
document: doc
|
||||
} = global;
|
||||
|
||||
common.getIframe = function getIframe(id = 'preview') {
|
||||
let previewFrame = doc.getElementById(id);
|
||||
|
||||
// create and append a hidden preview frame
|
||||
if (!previewFrame) {
|
||||
previewFrame = doc.createElement('iframe');
|
||||
previewFrame.id = id;
|
||||
previewFrame.setAttribute('style', 'display: none');
|
||||
doc.body.appendChild(previewFrame);
|
||||
}
|
||||
|
||||
return previewFrame.contentDocument ||
|
||||
previewFrame.contentWindow.document;
|
||||
};
|
||||
|
||||
return common;
|
||||
})(window);
|
@@ -1,124 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
// common namespace
|
||||
// all classes should be stored here
|
||||
// called at the beginning of dom ready
|
||||
const {
|
||||
Rx: { Disposable, Observable, config },
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
config.longStackSupport = true;
|
||||
common.head = common.head || [];
|
||||
common.tail = common.tail || [];
|
||||
common.salt = Math.random();
|
||||
|
||||
common.challengeTypes = {
|
||||
HTML: '0',
|
||||
JS: '1',
|
||||
VIDEO: '2',
|
||||
ZIPLINE: '3',
|
||||
BASEJUMP: '4',
|
||||
BONFIRE: '5',
|
||||
HIKES: '6',
|
||||
STEP: '7'
|
||||
};
|
||||
|
||||
|
||||
common.arrayToNewLineString = function arrayToNewLineString(seedData) {
|
||||
seedData = Array.isArray(seedData) ? seedData : [seedData];
|
||||
return seedData.reduce(function(seed, line) {
|
||||
return '' + seed + line + '\n';
|
||||
}, '\n');
|
||||
};
|
||||
|
||||
common.seed = common.arrayToNewLineString(common.challengeSeed);
|
||||
|
||||
common.replaceNoprotect = function replaceNoprotect(value) {
|
||||
return value.replace(/noprotect/gi, '');
|
||||
};
|
||||
|
||||
common.replaceScriptTags = function replaceScriptTags(value) {
|
||||
return value
|
||||
.replace(/<script>/gi, 'fccss')
|
||||
.replace(/<\/script>/gi, 'fcces');
|
||||
};
|
||||
|
||||
common.replaceSafeTags = function replaceSafeTags(value) {
|
||||
return value
|
||||
.replace(/fccss/gi, '<script>')
|
||||
.replace(/fcces/gi, '</script>');
|
||||
};
|
||||
|
||||
common.replaceFormActionAttr = function replaceFormAction(value) {
|
||||
return value.replace(/<form[^>]*>/, function(val) {
|
||||
return val.replace(/action(\s*?)=/, 'fccfaa$1=');
|
||||
});
|
||||
};
|
||||
|
||||
common.replaceFccfaaAttr = function replaceFccfaaAttr(value) {
|
||||
return value.replace(/<form[^>]*>/, function(val) {
|
||||
return val.replace(/fccfaa(\s*?)=/, 'action$1=');
|
||||
});
|
||||
};
|
||||
|
||||
common.scopejQuery = function scopejQuery(str) {
|
||||
return str
|
||||
.replace(/\$/gi, 'j$')
|
||||
.replace(/document/gi, 'jdocument')
|
||||
.replace(/jQuery/gi, 'jjQuery');
|
||||
};
|
||||
|
||||
common.unScopeJQuery = function unScopeJQuery(str) {
|
||||
return str
|
||||
.replace(/j\$/gi, '$')
|
||||
.replace(/jdocument/gi, 'document')
|
||||
.replace(/jjQuery/gi, 'jQuery');
|
||||
};
|
||||
|
||||
const commentRegex = /(\/\*[^(\*\/)]*\*\/)|([ \n]\/\/[^\n]*)/g;
|
||||
common.removeComments = function removeComments(str) {
|
||||
return str.replace(commentRegex, '');
|
||||
};
|
||||
|
||||
const logRegex = /(console\.[\w]+\s*\(.*\;)/g;
|
||||
common.removeLogs = function removeLogs(str) {
|
||||
return str.replace(logRegex, '');
|
||||
};
|
||||
|
||||
common.reassembleTest = function reassembleTest(code = '', { line, text }) {
|
||||
var regexp = new RegExp('//' + line + common.salt);
|
||||
return code.replace(regexp, text);
|
||||
};
|
||||
|
||||
common.getScriptContent$ = function getScriptContent$(script) {
|
||||
return Observable.create(function(observer) {
|
||||
const jqXHR = $.get(script, null, null, 'text')
|
||||
.success(data => {
|
||||
observer.onNext(data);
|
||||
observer.onCompleted();
|
||||
})
|
||||
.fail(e => observer.onError(e))
|
||||
.always(() => observer.onCompleted());
|
||||
|
||||
return new Disposable(() => {
|
||||
jqXHR.abort();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const openScript = /\<\s?script\s?\>/gi;
|
||||
const closingScript = /\<\s?\/\s?script\s?\>/gi;
|
||||
|
||||
// detects if there is JavaScript in the first script tag
|
||||
common.hasJs = function hasJs(code) {
|
||||
return !!common.getJsFromHtml(code);
|
||||
};
|
||||
|
||||
// grabs the content from the first script tag in the code
|
||||
common.getJsFromHtml = function getJsFromHtml(code) {
|
||||
// grab user javaScript
|
||||
return (code.split(openScript)[1] || '').split(closingScript)[0] || '';
|
||||
};
|
||||
|
||||
return common;
|
||||
})(window);
|
@@ -1,58 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
CodeMirror,
|
||||
document: doc,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
const { challengeTypes, challengeType = '0' } = common;
|
||||
|
||||
if (
|
||||
!CodeMirror ||
|
||||
challengeType !== challengeTypes.JS &&
|
||||
challengeType !== challengeTypes.BONFIRE
|
||||
) {
|
||||
common.updateOutputDisplay = () => {};
|
||||
common.appendToOutputDisplay = () => {};
|
||||
return common;
|
||||
}
|
||||
|
||||
var codeOutput = CodeMirror.fromTextArea(
|
||||
doc.getElementById('codeOutput'),
|
||||
{
|
||||
lineNumbers: false,
|
||||
mode: 'text',
|
||||
theme: 'monokai',
|
||||
readOnly: 'nocursor',
|
||||
lineWrapping: true
|
||||
}
|
||||
);
|
||||
|
||||
codeOutput.setValue(`/**
|
||||
* Your output will go here.
|
||||
* Any console.log() - type
|
||||
* statements will appear in
|
||||
* your browser\'s DevTools
|
||||
* JavaScript console as well.
|
||||
*/`);
|
||||
|
||||
codeOutput.setSize('100%', '100%');
|
||||
|
||||
common.updateOutputDisplay = function updateOutputDisplay(str = '') {
|
||||
if (typeof str === 'function') {
|
||||
str = str.toString();
|
||||
}
|
||||
if (typeof str !== 'string') {
|
||||
str = JSON.stringify(str);
|
||||
}
|
||||
codeOutput.setValue(str);
|
||||
return str;
|
||||
};
|
||||
|
||||
common.appendToOutputDisplay = function appendToOutputDisplay(str = '') {
|
||||
codeOutput.setValue(codeOutput.getValue() + str);
|
||||
return str;
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,132 +0,0 @@
|
||||
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 - 50 + 'px');
|
||||
}
|
||||
|
||||
magiVal = $(window).height() - $('.navbar').height();
|
||||
|
||||
if (magiVal < 0) {
|
||||
magiVal = 0;
|
||||
}
|
||||
|
||||
$('.scroll-locker')
|
||||
.css('min-height', $('.editorScrollDiv').height())
|
||||
.css('height', magiVal - 50);
|
||||
} else {
|
||||
$('.editorScrollDiv').css('max-height', 500 + 'px');
|
||||
|
||||
$('.scroll-locker')
|
||||
.css('position', 'inherit')
|
||||
.css('top', 'inherit')
|
||||
.css('width', '100%')
|
||||
.css('max-height', '100%');
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
return 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;
|
||||
}(window));
|
@@ -1,56 +0,0 @@
|
||||
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));
|
@@ -1,40 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
Rx: { Observable },
|
||||
chai,
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
common.runTests$ = function runTests$({
|
||||
code,
|
||||
originalCode,
|
||||
userTests,
|
||||
...rest
|
||||
}) {
|
||||
|
||||
return Observable.from(userTests)
|
||||
.map(function(test) {
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
const assert = chai.assert;
|
||||
const editor = { getValue() { return originalCode; }};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
try {
|
||||
if (test) {
|
||||
/* eslint-disable no-eval */
|
||||
eval(common.reassembleTest(code, test));
|
||||
/* eslint-enable no-eval */
|
||||
}
|
||||
} catch (e) {
|
||||
test.err = e.message;
|
||||
}
|
||||
|
||||
return test;
|
||||
})
|
||||
.toArray()
|
||||
.map(tests => ({ ...rest, tests }));
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,90 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
$,
|
||||
moment,
|
||||
ga = (() => {}),
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
function submitChallengeHandler(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var solution = common.editor.getValue();
|
||||
|
||||
$('#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();
|
||||
});
|
||||
|
||||
let timezone = 'UTC';
|
||||
try {
|
||||
timezone = moment.tz.guess();
|
||||
} catch (err) {
|
||||
err.message = `
|
||||
known bug, see: https://github.com/moment/moment-timezone/issues/294:
|
||||
${err.message}
|
||||
`;
|
||||
console.error(err);
|
||||
}
|
||||
const data = JSON.stringify({
|
||||
id: common.challengeId,
|
||||
name: common.challengeName,
|
||||
challengeType: +common.challengeType,
|
||||
solution,
|
||||
timezone
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: '/completed-challenge/',
|
||||
type: 'POST',
|
||||
data,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json'
|
||||
})
|
||||
.success(function(res) {
|
||||
if (res) {
|
||||
window.location =
|
||||
'/challenges/next-challenge?id=' + common.challengeId;
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
window.location.replace(window.location.href);
|
||||
});
|
||||
}
|
||||
|
||||
common.showCompletion = function showCompletion() {
|
||||
|
||||
ga(
|
||||
'send',
|
||||
'event',
|
||||
'Challenge',
|
||||
'solved',
|
||||
common.gaName,
|
||||
true
|
||||
);
|
||||
|
||||
$('#complete-courseware-dialog').modal('show');
|
||||
$('#complete-courseware-dialog .modal-header').click();
|
||||
|
||||
$('#submit-challenge').off('click');
|
||||
$('#submit-challenge').on('click', submitChallengeHandler);
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,200 +0,0 @@
|
||||
window.common = (function({ $, common = { init: [] }}) {
|
||||
const stepClass = '.challenge-step';
|
||||
const prevBtnClass = '.challenge-step-btn-prev';
|
||||
const nextBtnClass = '.challenge-step-btn-next';
|
||||
const actionBtnClass = '.challenge-step-btn-action';
|
||||
const finishBtnClass = '.challenge-step-btn-finish';
|
||||
const submitBtnId = '#challenge-step-btn-submit';
|
||||
const 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');
|
||||
}
|
||||
return $.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
|
||||
return $el.parent()
|
||||
.find('.disabled')
|
||||
.replaceWith('<p class="col-sm-4 col-xs-12">' + 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();
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: '/completed-challenge/',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
id: common.challengeId,
|
||||
name: common.challengeName,
|
||||
challengeType: +common.challengeType
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json'
|
||||
})
|
||||
.success(function(res) {
|
||||
if (res) {
|
||||
window.location =
|
||||
'/challenges/next-challenge?id=' + common.challengeId;
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
window.location.replace(window.location.href);
|
||||
});
|
||||
}
|
||||
|
||||
common.init.push(function($) {
|
||||
if (common.challengeType !== '7') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$(prevBtnClass).click(handlePrevStepClick);
|
||||
$(nextBtnClass).click(handleNextStepClick);
|
||||
$(actionBtnClass).click(handleActionClick);
|
||||
$(finishBtnClass).click(handleFinishClick);
|
||||
return null;
|
||||
});
|
||||
|
||||
return common;
|
||||
}(window));
|
@@ -1,154 +0,0 @@
|
||||
window.common = (function(global) {
|
||||
const {
|
||||
Rx: { BehaviorSubject, Observable },
|
||||
common = { init: [] }
|
||||
} = global;
|
||||
|
||||
// the first script tag here is to proxy jQuery
|
||||
// We use the same jQuery on the main window but we change the
|
||||
// context to that of the iframe.
|
||||
var libraryIncludes = `
|
||||
<script>
|
||||
window.loopProtect = parent.loopProtect;
|
||||
window.__err = null;
|
||||
window.loopProtect.hit = function(line) {
|
||||
window.__err = new Error(
|
||||
'Potential infinite loop at line ' +
|
||||
line +
|
||||
'. To disable loop protection, write:' +
|
||||
' \\n\\/\\/ noprotect\\nas the first' +
|
||||
' line. Beware that if you do have an infinite loop in your code' +
|
||||
' this will crash your browser.'
|
||||
);
|
||||
};
|
||||
</script>
|
||||
<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; }
|
||||
/* FORM RESET: */
|
||||
textarea,
|
||||
select,
|
||||
input[type="date"],
|
||||
input[type="datetime"],
|
||||
input[type="datetime-local"],
|
||||
input[type="email"],
|
||||
input[type="month"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
input[type="search"],
|
||||
input[type="tel"],
|
||||
input[type="text"],
|
||||
input[type="time"],
|
||||
input[type="url"],
|
||||
input[type="week"] {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-background-clip: padding;
|
||||
-moz-background-clip: padding;
|
||||
background-clip:padding-box;
|
||||
-webkit-border-radius:0;
|
||||
-moz-border-radius:0;
|
||||
-ms-border-radius:0;
|
||||
-o-border-radius:0;
|
||||
border-radius:0;
|
||||
-webkit-appearance:none;
|
||||
background-color:#fff;
|
||||
color:#000;
|
||||
outline:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
text-align: left;
|
||||
font-size:1em;
|
||||
height: 1.8em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
select, select, select {
|
||||
background:#fff
|
||||
url('data:image/png;base64,\
|
||||
R0lGODlhDQAEAIAAAAAAAP8A/yH5BAEHAAEAL\
|
||||
AAAAAANAAQAAAILhA+hG5jMDpxvhgIAOw==');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 97% center;
|
||||
padding:0 25px 0 8px;
|
||||
font-size: .875em;
|
||||
}
|
||||
|
||||
// ! FORM RESET
|
||||
</style>
|
||||
`;
|
||||
const codeDisabledError = `
|
||||
<script>
|
||||
window.__err = new Error('code has been disabled');
|
||||
</script>
|
||||
`;
|
||||
|
||||
const iFrameScript$ =
|
||||
common.getScriptContent$('/js/iFrameScripts.js').shareReplay();
|
||||
const jQueryScript$ = common.getScriptContent$(
|
||||
'/bower_components/jquery/dist/jquery.js'
|
||||
).shareReplay();
|
||||
|
||||
// behavior subject allways remembers the last value
|
||||
// we use this to determine if runPreviewTest$ is defined
|
||||
// and prime it with false
|
||||
common.previewReady$ = new BehaviorSubject(false);
|
||||
|
||||
// These should be set up in the preview window
|
||||
// if this error is seen it is because the function tried to run
|
||||
// before the iframe has completely loaded
|
||||
common.runPreviewTests$ =
|
||||
common.checkPreview$ =
|
||||
() => Observable.throw(new Error('Preview not fully loaded'));
|
||||
|
||||
|
||||
common.updatePreview$ = function updatePreview$(code = '') {
|
||||
const preview = common.getIframe('preview');
|
||||
|
||||
return Observable.combineLatest(
|
||||
iFrameScript$,
|
||||
jQueryScript$,
|
||||
(iframe, jQuery) => ({
|
||||
iframeScript: `<script>${iframe}</script>`,
|
||||
jQuery: `<script>${jQuery}</script>`
|
||||
})
|
||||
)
|
||||
.first()
|
||||
.flatMap(({ iframeScript, jQuery }) => {
|
||||
// we make sure to override the last value in the
|
||||
// subject to false here.
|
||||
common.previewReady$.onNext(false);
|
||||
preview.open();
|
||||
preview.write(
|
||||
libraryIncludes +
|
||||
jQuery +
|
||||
(common.shouldRun() ? code : codeDisabledError) +
|
||||
'<!-- -->' +
|
||||
iframeScript
|
||||
);
|
||||
preview.close();
|
||||
// now we filter false values and wait for the first true
|
||||
return common.previewReady$
|
||||
.filter(ready => ready)
|
||||
.first()
|
||||
// the delay here is to give code within the iframe
|
||||
// control to run
|
||||
.delay(400);
|
||||
})
|
||||
.map(() => code);
|
||||
};
|
||||
|
||||
return common;
|
||||
}(window));
|
Reference in New Issue
Block a user