Merge pull request #12636 from BerkeleyTrue/chore/remove-deprecated-files
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));
|
|
@ -1,13 +0,0 @@
|
|||||||
script(src=rev('/js', 'vendor-challenges.js'))
|
|
||||||
script(src=rev('/js', 'commonFramework.js'))
|
|
||||||
script.
|
|
||||||
if (typeof localStorage !== 'undefined') {
|
|
||||||
localStorage.setItem('currentDashedName', typeof common !== 'undefined' && common.dashedName || '');
|
|
||||||
}
|
|
||||||
var common = window.common || { init: [] };
|
|
||||||
common.helpRoom = !{JSON.stringify(helpRoom)};
|
|
||||||
document.addEventListener('gitter-sidecar-ready', function(e) {
|
|
||||||
if (window.main) {
|
|
||||||
window.main.chat.createHelpChat(common.helpRoom, '#challenge-help-btn');
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,21 +0,0 @@
|
|||||||
#issue-modal.modal(tabindex='-1')
|
|
||||||
.modal-dialog.animated.fadeIn.fast-animation
|
|
||||||
.modal-content
|
|
||||||
.modal-header.challenge-list-header Did you find a bug?
|
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
|
||||||
.modal-body.text-center
|
|
||||||
h3 Before you submit a new issue, read "Help I've Found a Bug" and browse other issues with this challenge.
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block#help-ive-found-a-bug-wiki-article(name='_csrf', value=_csrf) Read "Help I've Found a Bug"
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block#search-issue(name='_csrf', value=_csrf) Browse other issues with this challenge
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block#report-issue(name='_csrf', value=_csrf) Create my GitHub issue
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
|
||||||
|
|
||||||
#reset-modal.modal(tabindex='-1')
|
|
||||||
.modal-dialog.animated.fadeInUp.fast-animation
|
|
||||||
.modal-content
|
|
||||||
.modal-header.challenge-list-header Clear your code?
|
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
|
||||||
.modal-body
|
|
||||||
h3 This will restore your code editor to its original state.
|
|
||||||
a.btn.btn-lg.btn-warning.btn-block#reset-button(href='#', data-dismiss='modal', aria-hidden='true') Clear my code
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
|
File diff suppressed because one or more lines are too long
@ -1,31 +0,0 @@
|
|||||||
doctype html
|
|
||||||
html(lang='en')
|
|
||||||
script(src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js")
|
|
||||||
script(src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js")
|
|
||||||
link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css')
|
|
||||||
link(rel='stylesheet', href='/css/main.css')
|
|
||||||
link(rel='stylesheet', href='/css/Vimeo.css')
|
|
||||||
|
|
||||||
include partials/meta
|
|
||||||
meta(charset='utf-8')
|
|
||||||
meta(http-equiv='X-UA-Compatible', content='IE=edge')
|
|
||||||
meta(name='viewport', content='width=device-width, initial-scale=1.0')
|
|
||||||
meta(name='csrf-token', content=_csrf)
|
|
||||||
script.
|
|
||||||
(function(i,s,o,g,r,a,m){ i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
ga('create', 'UA-55446531-1', 'auto');
|
|
||||||
ga('require', 'displayfeatures');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
body.top-and-bottom-margins
|
|
||||||
include partials/navbar
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.panel.panel-info
|
|
||||||
p redirecting you... please wait...
|
|
||||||
script.
|
|
||||||
setTimeout(function() {
|
|
||||||
window.location = 'http://freecodecamp.com'
|
|
||||||
}, 500);
|
|
@ -1,256 +0,0 @@
|
|||||||
extends ../layout
|
|
||||||
block content
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-6.col-md-offset-3
|
|
||||||
h2.text-center Statistics
|
|
||||||
hr
|
|
||||||
ul.population-table
|
|
||||||
li
|
|
||||||
span.tag Established:	
|
|
||||||
span.text-primary #{daysRunning}
|
|
||||||
| days ago
|
|
||||||
li.nowrap
|
|
||||||
span.tag Completed:	
|
|
||||||
span.text-primary #{globalCompletedCount}
|
|
||||||
| challenges
|
|
||||||
li.nowrap
|
|
||||||
span.tag Donated:	
|
|
||||||
span.text-primary $1,050,000
|
|
||||||
| in pro-bono code
|
|
||||||
li.nowrap
|
|
||||||
span.tag Pledged:	
|
|
||||||
span.text-primary $#{globalPledgedAmount}
|
|
||||||
| to nonprofits
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-6.col-md-offset-3
|
|
||||||
h2.text-center Certificates
|
|
||||||
hr
|
|
||||||
ul.population-table
|
|
||||||
li.nowrap
|
|
||||||
span.tag Front End:	
|
|
||||||
span.text-primary #{frontEndCount}
|
|
||||||
| earned
|
|
||||||
li.nowrap
|
|
||||||
span.tag Data Viz:	
|
|
||||||
span.text-primary #{dataVisCount}
|
|
||||||
| earned
|
|
||||||
li.nowrap
|
|
||||||
span.tag Back End:	
|
|
||||||
span.text-primary #{backEndCount}
|
|
||||||
| earned
|
|
||||||
li.nowrap
|
|
||||||
span.tag All three:	
|
|
||||||
span.text-primary #{allThreeCount}
|
|
||||||
| earned
|
|
||||||
.spacer
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-6.col-md-offset-3
|
|
||||||
h2.text-center Links
|
|
||||||
hr
|
|
||||||
table.table.link-table
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-medium
|
|
||||||
td
|
|
||||||
a(href='//medium.freecodecamp.com', target='_blank') Our Medium publication
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-youtube
|
|
||||||
td
|
|
||||||
a(href="//youtube.com/freecodecamp", target='_blank') Our YouTube channel
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-github
|
|
||||||
td
|
|
||||||
a(href="//github.com/freecodecamp", target='_blank') Our GitHub repository
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-linkedin
|
|
||||||
td
|
|
||||||
a(href="//www.linkedin.com/edu/school?id=166029", target='_blank') Our LinkedIn university page
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-twitter
|
|
||||||
td
|
|
||||||
a(href="//twitter.com/freecodecamp", target='_blank') Our Twitter feed
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-facebook
|
|
||||||
td
|
|
||||||
a(href="//facebook.com/freecodecamp", target='_blank') Our Facebook page
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-reddit
|
|
||||||
td
|
|
||||||
a(href="//www.reddit.com/r/freecodecamp", target='_blank') Our Subreddit
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-lock
|
|
||||||
td
|
|
||||||
a(href="/privacy") Our privacy policy
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-balance-scale
|
|
||||||
td
|
|
||||||
a(href="/code-of-conduct") Our code of conduct
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-university
|
|
||||||
td
|
|
||||||
a(href="/academic-honesty") Our academic honesty policy
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-book
|
|
||||||
td
|
|
||||||
a(href="/terms") Our terms of service
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-heart
|
|
||||||
td
|
|
||||||
a(href="/how-nonprofit-projects-work") How our nonprofit projects work
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-video-camera
|
|
||||||
td
|
|
||||||
a(href="//www.youtube.com/watch?v=O993J5KCR_w&list=PLWKjhJtqVAbnQ048Pa8sAqJoVRhx8TJtM" target='_blank') Nonprofit project demo videos
|
|
||||||
tr
|
|
||||||
td.text-center
|
|
||||||
i.fa.fa-comment
|
|
||||||
td
|
|
||||||
a(href="/stories") Stories from campers who've become professional software developers
|
|
||||||
.spacer
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-6.col-md-offset-3
|
|
||||||
h2.text-center Frequently Asked Questions
|
|
||||||
hr
|
|
||||||
html
|
|
||||||
<h4>What is freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">We’re an open source community that helps you learn to code.</p>
|
|
||||||
<h4>How do you help me learn to code?</h4>
|
|
||||||
<p class="negative-15">You can work through our self-paced coding challenges, build projects, and earn certificates. We also connect you with people in your city so you can code together.</p>
|
|
||||||
<h4>Can freeCodeCamp help me get a job as a software developer?</h4>
|
|
||||||
<p class="negative-15">Yes. Thousands of people have gotten software developer jobs after joining our open source community.</p>
|
|
||||||
<h4>Is freeCodeCamp a coding bootcamp?</h4>
|
|
||||||
<p class="negative-15">No. A lot of coding bootcamps use freeCodeCamp as part of their curriculum, though.</p>
|
|
||||||
<h4>I work for a school/bootcamp/MOOC. Can we fast track our certified students to work on your nonprofit projects?</h4>
|
|
||||||
<p class="negative-15">Our pro bono Code Grant program is designed for developers to gain real world experience by building projects for nonprofit organizations. We recognize the value of this opportunity for your students. However, at this time, only those who have completed the Front End, Back End, and Data Visualization certifications at freeCodeCamp are eligible to participate. <br><br>We would encourage you to consider creating a skills-based volunteer initiative at your school/bootcamp/MOOC. For help getting started, contact <a href=mailto:Michael@freecodecamp.com>Michael@freecodecamp.com.</a></p>
|
|
||||||
<h4>Is freeCodeCamp really free?</h4>
|
|
||||||
<p class="negative-15">Yes. Our curriculum, nonprofit projects, and verified certificates are all free.</p>
|
|
||||||
<h4>How can I help nonprofits through freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">Once you finish the first 1,200 hours of challenges, you’ll get to build a series of solutions for nonprofits. You’ll work in pairs, under the supervision of a volunteer project manager and a stakeholder from the nonprofit.</p>
|
|
||||||
<h4>How much money has freeCodeCamp saved nonprofits?</h4>
|
|
||||||
<p class="negative-15">Our community has donated more than a million dollars worth of pro-bono coding for nonprofits so far.</p>
|
|
||||||
<h4>Is there a way I can help nonprofits before I earn my certificates?</h4>
|
|
||||||
<p class="negative-15">Yes. You can <a href="/commit">pledge a monthly donation</a> to a nonprofit of your choosing while you learn.</p>
|
|
||||||
<h4>How long does freeCodeCamp take?</h4>
|
|
||||||
<p class="negative-15">It takes about 2,080 hours to complete our Full Stack Developer certificate. This translates into one year of full-time coding. We’re completely self-paced though, so take as long as you need.</p>
|
|
||||||
<h4>Is freeCodeCamp a replacement for a 4-year degree?</h4>
|
|
||||||
<p class="negative-15">No. Please don’t drop out of college just to pursue freeCodeCamp. You can pursue both concurrently. Even though you don’t need a 4-year degree to work as a software developer, it still helps a lot.</p>
|
|
||||||
<h4>I'm a teacher. Is freeCodeCamp an appropriate resource for my class?</h4>
|
|
||||||
<p class="negative-15">Yes. Many high school, college, and adult ed programs incorporate freeCodeCamp into their coursework. We're open source, so no license or special permission from us is necessary. We're even building special tools for teachers.</p>
|
|
||||||
<h4>Should I complete all of these challenges in order?</h4>
|
|
||||||
<p class="negative-15">We’ve put a lot of thought into how we introduce concepts. But you’re free to jump around.</p>
|
|
||||||
<h4>Do I have to use CodePen for the front end projects?</h4>
|
|
||||||
<p class="negative-15">As long as your code is publicly viewable, and you have a live demo, you can use whatever you want.</p>
|
|
||||||
<h4>When will you release the additional React, Sass, and D3 challenges?</h4>
|
|
||||||
<p class="negative-15">We hope to release these in September 2016. In the meantime, other people have completed our React/Sass and D3 projects by referencing their official documentation.</p>
|
|
||||||
<h4>How did freeCodeCamp get started?</h4>
|
|
||||||
<p class="negative-15"><a href="https://www.quora.com/profile/Quincy-Larson" target="_blank">Quincy</a> started our open source community in 2014. He is now just one of many active contributors. You can read more in this <a href="https://medium.freecodecamp.com/how-i-made-my-first-million-dollars-in-pro-bono-code-86f911040484#.pgftldy1b" target="_blank">Medium story</a>.</p>
|
|
||||||
<h4>How is freeCodeCamp incorporated?</h4>
|
|
||||||
<p class="negative-15">Quincy set up a company in California called freeCodeCamp, Inc. to support the freeCodeCamp community. It pays for servers and other expenses. It also sponsors Quincy, <a href="https://gitter.im/berkeleytrue" target="_blank">Berkeley</a>, and <a href="https://gitter.im/codenonprofit" target="_blank">Michael</a>, who oversee infrastructure and nonprofit projects.</p>
|
|
||||||
<h4>Can I get a job at freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">We can't afford to hire anyone, but we can serve as a springboard for your next job. Our contributors have landed jobs at companies like GitHub and Spotify.</p>
|
|
||||||
<h4>Can my company advertise on freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">We don’t show ads.</p>
|
|
||||||
<h4>Can I invest in freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">We're self-funded and don't want outside investment.</p>
|
|
||||||
<h4>Can I donate to freeCodeCamp?</h4>
|
|
||||||
<p class="negative-15">We don’t accept donations. Instead, you should <a href="/shop" target="_blank">support us through our shop</a>.</p>
|
|
||||||
|
|
||||||
.spacer
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-6.col-md-offset-3
|
|
||||||
h2.text-center Whom to contact for what
|
|
||||||
table.table.table-stripe.response-table
|
|
||||||
tr
|
|
||||||
td Support (
|
|
||||||
a(href='mailto:team@freecodecamp.com') team@freecodecamp.com
|
|
||||||
| )
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/QuincyLarson' target='_blank') @QuincyLarson
|
|
||||||
tr
|
|
||||||
td Facebook page
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/QuincyLarson' target='_blank') @QuincyLarson
|
|
||||||
tr
|
|
||||||
td Twitter feed
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/QuincyLarson' target='_blank') @QuincyLarson
|
|
||||||
tr
|
|
||||||
td Medium publication
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/QuincyLarson' target='_blank') @QuincyLarson
|
|
||||||
tr
|
|
||||||
td Media inquiries
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/QuincyLarson' target='_blank') @QuincyLarson
|
|
||||||
tr
|
|
||||||
td Open source codebase contributions
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/BerkeleyTrue' target='_blank') @BerkeleyTrue
|
|
||||||
tr
|
|
||||||
td Server problems
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/BerkeleyTrue' target='_blank') @BerkeleyTrue
|
|
||||||
tr
|
|
||||||
td Nonprofit projects
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/CodeNonprofit' target='_blank') @CodeNonprofit
|
|
||||||
tr
|
|
||||||
td Volunteer agile project managers
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/CodeNonprofit' target='_blank') @CodeNonprofit
|
|
||||||
tr
|
|
||||||
td Commit program
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/CodeNonprofit' target='_blank') @CodeNonprofit
|
|
||||||
tr
|
|
||||||
td Video challenge curriculum
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/BrianaMarie' target='_blank') @BrianaMarie
|
|
||||||
|  / 
|
|
||||||
a(href='https://gitter.im/atjonathan' target='_blank') @atjonathan
|
|
||||||
tr
|
|
||||||
td YouTube channel
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/Septimus' target='_blank') @Septimus
|
|
||||||
|  / 
|
|
||||||
a(href='https://gitter.im/atjonathan' target='_blank') @atjonathan
|
|
||||||
tr
|
|
||||||
td Core curriculum
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/HKuz' target='_blank') @HKuz
|
|
||||||
tr
|
|
||||||
td Data Science and Open Data
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/Evaristoc' target='_blank') @Evaristoc
|
|
||||||
tr
|
|
||||||
td CamperBot
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/LTegman' target='_blank') @LTegman
|
|
||||||
tr
|
|
||||||
td Translation and Internationalization
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/Vtamara' target='_blank') @Vtamara
|
|
||||||
tr
|
|
||||||
td Wiki
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/Rafase282' target='_blank') @Rafase282
|
|
||||||
tr
|
|
||||||
td Local groups
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/Hallaathrad' target='_blank') @Hallaathrad
|
|
||||||
tr
|
|
||||||
td Forum
|
|
||||||
td
|
|
||||||
a(href='https://gitter.im/m-henderson' target='_blank') @m-henderson
|
|
@ -1,87 +0,0 @@
|
|||||||
extends ../layout
|
|
||||||
block content
|
|
||||||
h2.text-center Buy cool stuff and support our open source community:
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/0JSMQlO.jpg')
|
|
||||||
h3 Bonfire Function Call T-shirt - $24.99
|
|
||||||
p This comfortable jet black T-shirt features our community's iconic "bonfire function call" logo.
|
|
||||||
p Also available in high quality American Apparel women's fitted tee sizes.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href='http://bit.ly/2cIteT1' target='_blank' onClick="ga('send', 'event', 'Shop', 'clicked', 'bonfire function call tshirt');") Get this classic shirt
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/Ev3dGx2.jpg')
|
|
||||||
h3 Bonfire Function Call Hoodie - $49.99
|
|
||||||
p Stay warm and dress like a developer. This freeCodeCamp hoodie comes with pockets and a zipper for maximum convenience.
|
|
||||||
p Fits great on men, women, and kids.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href='http://bit.ly/2cHlCx6' target='_blank' onClick="ga('send', 'event', 'Shop', 'clicked', 'bonfire function call hoodie');") Get this toasty hoodie
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/nlupFKM.jpg')
|
|
||||||
h3 It's all talk until the code runs T-shirt - $24.99
|
|
||||||
p Ward Cunningham, inventor of the wiki, famously said, "It's all talk until the code runs." Spread his wisdom with this jet black t-shirt.
|
|
||||||
p Also available in high quality American Apparel women's fitted tee sizes.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href='http://bit.ly/2bQ97AU' target='_blank' onClick="ga('send', 'event', 'Shop', 'clicked', 'all talk tshirt');") Get this sweet shirt
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/XcruzJf.jpg')
|
|
||||||
h3 Celebrate the Open Data Movement T-shirt - $24.99
|
|
||||||
p All around the world, scientists and governments are releasing open data. freeCodeCamp's datasets are among the most popular with Kaggle's community of 600,000+ data scientists.
|
|
||||||
p Also available in high quality American Apparel women's fitted tee sizes.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href='http://bit.ly/2celkPa' target='_blank' onClick="ga('send', 'event', 'Shop', 'clicked', 'open data tshirt');") Get this awesome shirt
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/H4q9EAo.jpg')
|
|
||||||
h3 Rabbit Skins Baby Onesie - $19.99
|
|
||||||
p Inspire your baby to become a coder!
|
|
||||||
p These onesies come in pink and green, and range in size from 6 months to 2 years old.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href='http://bit.ly/2b4Gq12' target='_blank' onClick="ga('send', 'event', 'Shop', 'clicked', 'open data onesie');") Get this adorable onesie
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/FNFqn9Tg.jpg')
|
|
||||||
h3 freeCodeCamp laptop stickers
|
|
||||||
p Get 2 for only $5, with free shipping anywhere!
|
|
||||||
p These durable 2" (5 cm) stickers sport a matte finish, and look great anywhere - especially on your laptop.
|
|
||||||
html.
|
|
||||||
<form action="//www.paypal.com/cgi-bin/webscr" onSubmit="ga('send', 'event', 'Shop', 'clicked', 'Freecodecamp sticker');" method="post" target="_blank">
|
|
||||||
<input type="hidden" name="cmd" value="_s-xclick">
|
|
||||||
<input type="hidden" name="hosted_button_id" value="R9AGYKJUCU37N">
|
|
||||||
<input type="submit" class="btn btn-cta signup-btn btn-block" name="submit" value='Get these with PayPal or a Credit Card'>
|
|
||||||
<img alt="" border="0" src="//www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
|
||||||
</form>
|
|
||||||
hr
|
|
||||||
|
|
||||||
.big-break
|
|
||||||
h2.text-center These audiobooks are extremely popular with developers:
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/0aHcAtS.jpg')
|
|
||||||
h3 The Innovators: How a Group of Hackers, Geniuses, and Geeks Created the Digital Revolution
|
|
||||||
p Get this audiobook and another of your choosing for free with an Audible free trial.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(target='_blank' href="http://amzn.to/2aZVCR6" onClick="ga('send', 'event', 'Shop', 'clicked', 'book-the-innovators');") Get this audiobook for free
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/1088eKE.jpg')
|
|
||||||
h3 The Master Switch: The Rise and Fall of Information Empires
|
|
||||||
p Get this audiobook and another of your choosing for free with an Audible free trial.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(target='_blank' href="http://amzn.to/2cjtFDH" onClick="ga('send', 'event', 'Shop', 'clicked', 'book-master-switch');") Get this audiobook for free
|
|
||||||
|
|
||||||
hr
|
|
||||||
.row
|
|
||||||
.col-xs-12.col-sm-10.col-sm-offset-1.col-md-8.col-md-offset-2.text-center
|
|
||||||
img.img-responsive.img-center(src='//i.imgur.com/kAzbbvk.jpg')
|
|
||||||
h3 Hackers: Heroes of the Computer Revolution
|
|
||||||
p Get this audiobook and another of your choosing for free with an Audible free trial.
|
|
||||||
a.btn.btn-cta.signup-btn.btn-block(target='_blank' href="http://amzn.to/2bIHlYP" onClick="ga('send', 'event', 'Shop', 'clicked', 'book-hackers');") Get this audiobook for free
|
|
@ -1,61 +0,0 @@
|
|||||||
.themeCustom a.link:link, .themeCustom a.link:visited, .themeCustom a:link, .themeCustom a:visited {
|
|
||||||
color: #0066cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .sh_header_search .well {
|
|
||||||
background: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .sh_header_search .header_wrapper {
|
|
||||||
border-bottom-color: #cccccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .column-left {
|
|
||||||
background-color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .column-left .filters .handle {
|
|
||||||
background-color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .column-left .filters .handle:hover {
|
|
||||||
background-color: #cccccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .column-left .recent_searches, .themeCustom .column-left .search_tools, .themeCustom .column-left .search_links, .themeCustom .column-left .sort_jobs {
|
|
||||||
border-top-color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom #search_title .text-highlight {
|
|
||||||
color: #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .inline-filters {
|
|
||||||
background: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom input[type=radio]:checked + label > span, .themeCustom .sort_jobs input[type=radio]:checked + label > span, .themeCustom .social-network-logins input[type=radio]:checked + label > span {
|
|
||||||
background-color: #444444;
|
|
||||||
border-color: #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.themeCustom .filters .filter ul li a, .themeCustom #c_expired a, .themeCustom .result a, .themeCustom .expand_search a, .themeCustom .simplyhired-intl a, .themeCustom .filters .filter ul li a:visited, .themeCustom #c_expired a:visited, .themeCustom .result a:visited, .themeCustom .expand_search a:visited, .themeCustom .simplyhired-intl a:visited, .themeCustom .job h2 {
|
|
||||||
color: #0066cc;}
|
|
||||||
|
|
||||||
.sh.theme-base .btn-sh2 {
|
|
||||||
background-color: #999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh.theme-base .sh_header_search .skin-search-promo .btn-large{
|
|
||||||
background-color: #999999;
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh .job .new {
|
|
||||||
background-color: #999999;
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh .job .partner-exclusive {
|
|
||||||
background-color: #F15A22;
|
|
||||||
}
|
|
Reference in New Issue
Block a user