Files
freeCodeCamp/client/main.js

642 lines
17 KiB
JavaScript
Raw Normal View History

2015-10-06 00:13:51 -07:00
var main = window.main || {};
main.mapShareKey = 'map-shares';
2015-10-14 21:26:22 -07:00
main.ga = window.ga || function() {};
2016-01-24 20:46:21 -08:00
main = (function(main, global) {
const { Mousetrap } = global;
2015-10-16 16:10:45 -07:00
// should be set before gitter script loads
((window.gitter = {}).chat = {}).options = {
disableDefaultChat: true
};
// wait for sidecar to load
main.chat = {};
main.chat.isOpen = false;
main.chat.createHelpChat = function createHelpChat() {
throw new Error('Sidecar chat has not initialized');
};
document.addEventListener('gitter-sidecar-ready', function(e) {
main.chat.GitterChat = e.detail.Chat;
2015-10-16 16:10:45 -07:00
main.chat.createHelpChat = function(room, helpChatBtnClass, roomTitle) {
2016-01-15 00:55:08 -08:00
// room is always in PascalCase
roomTitle = room
.replace(/([A-Z])/g, ' $1')
.replace('Java Script', 'JavaScript');
2015-10-16 13:28:53 -07:00
$('body').append(
'<aside id="chat-embed-help" class="gitter-chat-embed is-collapsed" />'
);
main.chat.helpChat = new main.chat.GitterChat({
2016-01-15 00:55:08 -08:00
room: `freecodecamp/${room}`,
2015-10-16 13:28:53 -07:00
activationElement: false,
targetElement: $('#chat-embed-help')
});
$(helpChatBtnClass).on('click', function() {
2015-10-16 15:00:26 -07:00
// is button already pressed?
// no? open chat
// yes? close chat
var shouldChatBeOpen = !$(this).hasClass('active');
main.chat.helpChat.toggleChat(shouldChatBeOpen);
if (shouldChatBeOpen) {
$(helpChatBtnClass).addClass('active');
}
});
2015-10-16 16:10:45 -07:00
var helpTitleAdd = false;
2015-10-16 15:00:26 -07:00
$('#chat-embed-help').on('gitter-chat-toggle', function(e) {
var shouldButtonBePressed = !!e.originalEvent.detail.state;
2015-10-16 16:10:45 -07:00
if (!helpTitleAdd) {
helpTitleAdd = true;
$('#chat-embed-help > .gitter-chat-embed-action-bar').prepend(
'<div class="chat-embed-main-title">' +
'<span>' +
roomTitle +
'</span>' +
'</div>'
);
}
2015-10-16 15:00:26 -07:00
if (shouldButtonBePressed) {
return $(helpChatBtnClass).addClass('active');
}
return $(helpChatBtnClass).removeClass('active');
});
};
2015-10-16 13:28:53 -07:00
$('body').append(
'<aside id="chat-embed-main" class="gitter-chat-embed is-collapsed" />'
);
main.chat.mainChat = new main.chat.GitterChat({
room: 'freecodecamp/freecodecamp',
2015-10-16 13:28:53 -07:00
activationElement: false,
targetElement: $('#chat-embed-main')
});
2015-10-16 16:10:45 -07:00
var mainChatTitleAdded = false;
$('#chat-embed-main').on('gitter-chat-toggle', function() {
if (mainChatTitleAdded) {
return null;
}
mainChatTitleAdded = true;
if ($('body').hasClass('night')) {
$('#chat-embed-main').addClass('night');
}
2015-10-16 16:10:45 -07:00
$('#chat-embed-main > .gitter-chat-embed-action-bar').prepend(
'<div class="chat-embed-main-title">' +
'<span>Free Code Camp\'s Main Chat</span>' +
'</div>'
);
2016-03-02 20:54:14 -08:00
return null;
2015-10-16 16:10:45 -07:00
});
$('#nav-chat-btn').on('click', function(event) {
if (!(event.ctrlKey || event.metaKey)) {
toggleMainChat();
}
window.ga('send', 'event', 'NAV', 'NAV-CHAT', 'Nav Chat Button Clicked');
});
function showMainChat() {
if (!main.chat.isOpen) {
2015-10-16 13:28:53 -07:00
main.chat.mainChat.toggleChat(true);
}
}
function collapseMainChat() {
$('#chat-embed-main').addClass('is-collapsed');
2016-02-07 16:51:57 +07:00
document.activeElement.blur();
}
2016-02-07 16:51:57 +07:00
function toggleMainChat() {
var isCollapsed = $('#chat-embed-main').hasClass('is-collapsed');
if (isCollapsed) {
showMainChat();
} else {
collapseMainChat();
}
2016-02-07 16:51:57 +07:00
}
// keyboard shortcuts: open main chat
Mousetrap.bind('g c', toggleMainChat);
});
return main;
2016-01-24 20:46:21 -08:00
}(main, window));
2015-09-28 19:47:45 -07:00
var lastCompleted = typeof lastCompleted !== 'undefined' ?
lastCompleted :
'';
2015-11-17 21:25:16 -08:00
main.getMapShares = function getMapShares() {
2015-10-06 00:13:51 -07:00
var alreadyShared = JSON.parse(
localStorage.getItem(main.mapShareKey) ||
'[]'
);
if (!alreadyShared || !Array.isArray(alreadyShared)) {
2015-10-06 00:13:51 -07:00
localStorage.setItem(main.mapShareKey, JSON.stringify([]));
alreadyShared = [];
}
return alreadyShared;
2015-11-17 21:25:16 -08:00
};
2015-11-17 21:25:16 -08:00
main.setMapShare = function setMapShare(id) {
var alreadyShared = main.getMapShares();
var found = false;
alreadyShared.forEach(function(_id) {
if (_id === id) {
found = true;
}
});
if (!found) {
alreadyShared.push(id);
}
2015-10-06 00:13:51 -07:00
localStorage.setItem(main.mapShareKey, JSON.stringify(alreadyShared));
return alreadyShared;
2015-11-17 21:25:16 -08:00
};
2014-10-17 19:23:53 -07:00
$(document).ready(function() {
const { Observable } = window.Rx;
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('img').error(function() {
$(this)
.unbind('error')
.attr(
'src',
'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'
);
});
// map sharing
2015-11-17 21:25:16 -08:00
var alreadyShared = main.getMapShares();
2015-09-28 19:47:45 -07:00
if (lastCompleted && alreadyShared.indexOf(lastCompleted) === -1) {
$('div[id="' + lastCompleted + '"]')
.parent()
.parent()
.removeClass('hidden');
}
// on map view
$('.map-challenge-block-share').on('click', function(e) {
e.preventDefault();
var challengeBlockName = $(this).children().attr('id');
var challengeBlockEscapedName = challengeBlockName.replace(/\s/, '%20');
2015-09-28 19:47:45 -07:00
var username = typeof window.username !== 'undefined' ?
window.username :
'';
var link = 'https://www.facebook.com/dialog/feed?' +
'app_id=1644598365767721' +
'&display=page&' +
2015-09-28 19:04:44 -07:00
'caption=I%20just%20completed%20the%20' +
challengeBlockEscapedName +
2015-09-28 20:05:22 -07:00
'%20section%20on%20Free%20Code%20Camp%2E' +
2015-09-28 19:04:44 -07:00
'&link=http%3A%2F%2Ffreecodecamp%2Ecom%2F' +
username +
'&redirect_uri=http%3A%2F%2Ffreecodecamp%2Ecom%2Fmap';
2015-09-28 19:47:45 -07:00
2015-11-17 21:25:16 -08:00
main.setMapShare(challengeBlockName);
window.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
window.location.href = link;
});
2016-01-12 22:26:19 -08:00
function expandCaret(item) {
$(item)
.prev().find('.fa-caret-right')
.removeClass('fa-caret-right').addClass('fa-caret-down');
}
function collapseCaret(item) {
$(item)
.prev().find('.fa-caret-down')
.removeClass('fa-caret-down').addClass('fa-caret-right');
}
function expandBlock(item) {
2016-03-16 23:13:08 -04:00
$(item).addClass('in').css('height', 'auto');
expandCaret(item);
}
function collapseBlock(item) {
2016-03-16 23:13:08 -04:00
$(item).removeClass('in').css('height', 'auto');
collapseCaret(item);
}
$.each($('.sr-only'), function(i, span) {
if ($(span).text() === ' Complete') {
$(span).parents('p').addClass('manip-hidden');
}
});
$.each($('.map-collapse'), function(i, div) {
if ($(div).find('.manip-hidden').length ===
$(div).find('p').length) {
collapseBlock(div);
$(div).prev('h3').addClass('faded');
$(div).prev('h2').addClass('faded');
}
});
var scrollTo, dashedName = localStorage.getItem('currentDashedName'),
elemsToSearch = $('p.padded-ionic-icon a'), currOrLastChallenge;
if (!dashedName && $('.sr-only').length) {
elemsToSearch = $('.sr-only');
}
currOrLastChallenge = elemsToSearch.filter(function() {
if (dashedName) {
return $(this).attr('href').match(dashedName);
2016-01-16 22:31:42 +04:00
}
return $(this).text() === ' Complete';
});
if (currOrLastChallenge.length) {
currOrLastChallenge = currOrLastChallenge[currOrLastChallenge.length - 1];
scrollTo = $(currOrLastChallenge).offset().top - 380;
$('html, body, .map-accordion').scrollTop(scrollTo);
}
2016-01-19 02:47:51 +04:00
if (String(window.location).match(/\/map$/ig)) {
$('body>.flashMessage').find('.alert').css('display', 'none');
$('.map-fixed-header').css('top', '50px');
2016-01-19 02:47:51 +04:00
}
2016-01-31 13:09:43 -08:00
// map global selectors
var mapFilter = $('#map-filter');
var mapShowAll = $('#showAll');
$('#nav-map-btn').on('click', function(event) {
if (!(event.ctrlKey || event.metaKey)) {
toggleMap();
}
});
$('.map-aside-action-collapse').on('click', collapseMap);
function showMap() {
if (!main.isMapAsideLoad) {
var mapAside = $('<iframe id = "map-aside-frame" >');
2016-02-12 21:11:01 -05:00
mapAside.attr({
src: '/map-aside',
2016-03-03 19:00:47 -05:00
frameBorder: '0'
2016-02-13 21:26:59 -05:00
});
2016-03-03 19:00:47 -05:00
$('.map-aside').append(mapAside);
if ($('body').hasClass('night')) {
mapAside.addClass('night');
}
main.isMapAsideLoad = true;
}
2016-01-12 22:26:19 -08:00
$('.map-aside').removeClass('is-collapsed');
}
2016-01-12 22:26:19 -08:00
function collapseMap() {
2016-01-12 22:26:19 -08:00
$('.map-aside').addClass('is-collapsed');
2016-02-07 16:51:57 +07:00
document.activeElement.blur();
}
function toggleMap() {
var isCollapsed = $('.map-aside').hasClass('is-collapsed');
if (isCollapsed) {
showMap();
} else {
collapseMap();
}
}
$('#nav-wiki-btn').on('click', function(event) {
if (!(event.ctrlKey || event.metaKey)) {
toggleWiki();
}
});
2016-02-09 12:44:08 -08:00
$('.wiki-aside-action-collapse').on('click', collapseWiki);
function getWikiPath() {
if (!window.common) {
return false;
}
var challengeType = window.common.challengeType;
var dashedName = window.common.dashedName;
var prefix = '';
// Get wiki prefix depending on type of challenge
// Type 0, 1 typically have solutions on the wiki prefixed with 'challenge-'
// Type 5 typically has a solution on the wiki prefixed with algorithm-'
if (challengeType === '0' || challengeType === '1') {
prefix = 'challenge-';
return prefix + dashedName;
}
if (challengeType === '5') {
prefix = 'algorithm-';
return prefix + dashedName;
}
return false;
}
2016-02-09 12:44:08 -08:00
function showWiki() {
if (!main.isWikiAsideLoad) {
var wikiURL = '//freecodecamp.github.io/wiki/en/';
2016-02-09 20:57:23 -08:00
var lang = window.location.toString().match(/\/\w{2}\//);
lang = (lang) ? lang[0] : '/en/';
var wikiPath = getWikiPath();
if (wikiPath) {
wikiURL = wikiURL + wikiPath + '/';
} else if (lang !== '/en/') {
// Strip default '/en/' language ending and add the user's language
wikiURL = wikiURL.substr(0, wikiURL.length - 4) + lang;
}
2016-02-09 12:44:08 -08:00
var wikiAside = $('<iframe>');
2016-02-12 21:11:01 -05:00
wikiAside.attr({
src: wikiURL,
2016-03-03 19:00:47 -05:00
frameBorder: '0'
2016-02-12 21:11:01 -05:00
});
2016-03-03 19:00:47 -05:00
$('.wiki-aside').append(wikiAside);
2016-02-09 12:44:08 -08:00
main.isWikiAsideLoad = true;
if ($('body').hasClass('night')) {
wikiAside.addClass('night');
}
2016-02-09 12:44:08 -08:00
}
$('.wiki-aside').removeClass('is-collapsed');
}
function collapseWiki() {
$('.wiki-aside').addClass('is-collapsed');
document.activeElement.blur();
}
function toggleWiki() {
var isCollapsed = $('.wiki-aside').hasClass('is-collapsed');
if (isCollapsed) {
showWiki();
} else {
collapseWiki();
}
}
$('#accordion').on('show.bs.collapse', function(e) {
2016-01-31 13:09:43 -08:00
expandCaret(e.target);
if ($('a[data-toggle=collapse]').length === $('.fa-caret-down').length) {
mapShowAll.text('Collapse all challenges');
mapShowAll.addClass('active');
}
}).on('hide.bs.collapse', function(e) {
2016-01-31 13:09:43 -08:00
collapseCaret(e.target);
if ($('a[data-toggle=collapse]').length === $('.fa-caret-right').length) {
mapShowAll.text('Expand all challenges');
mapShowAll.removeClass('active');
}
});
2016-01-31 13:09:43 -08:00
mapShowAll.on('click', () => {
var mapExpanded = mapShowAll.hasClass('active');
if (!mapExpanded) {
$.each($('.map-collapse:not(".in")'),
function(i, div) {
expandBlock(div);
});
2016-01-31 13:09:43 -08:00
mapShowAll.text('Collapse all challenges');
return mapShowAll.addClass('active');
} else {
$.each($('.map-collapse.in'), function(i, div) {
collapseBlock(div);
});
2016-01-31 13:09:43 -08:00
mapShowAll.text('Expand all challenges');
return mapShowAll.removeClass('active');
}
});
2016-01-31 13:09:43 -08:00
// Map live filter
mapFilter.on('keyup', () => {
if (mapFilter.val().length > 0) {
2016-04-18 22:06:21 +05:30
var regexString = mapFilter.val().replace(/ /g, '.');
var regex = new RegExp(regexString.split('').join('.*'), 'i');
2016-01-31 13:09:43 -08:00
// Hide/unhide challenges that match the regex
2016-01-30 11:01:37 -08:00
$('.challenge-title').each((index, title) => {
2016-01-31 13:09:43 -08:00
if (regex.test($(title).attr('name'))) {
expandBlock($(title).closest('.chapterBlock'));
expandBlock($(title).closest('.certBlock'));
2016-01-31 13:09:43 -08:00
$(title).removeClass('hidden');
2016-01-30 11:01:37 -08:00
} else {
$(title).addClass('hidden');
}
});
2016-01-31 13:09:43 -08:00
// Hide/unhide blocks with no matches
$.each($('.chapterBlock'), function(i, div) {
2016-01-30 11:01:37 -08:00
if ($(div).find('.hidden').length ===
2016-01-31 13:09:43 -08:00
$(div).find('p').length) {
$(div).addClass('hidden');
$(div).prev('h3').addClass('hidden');
} else {
2016-01-31 13:09:43 -08:00
$(div).removeClass('hidden');
$(div).prev('h3').removeClass('hidden');
2016-01-30 11:01:37 -08:00
}
});
2016-01-31 13:09:43 -08:00
// Hide/unhide superblocks with no matches
$.each($('.certBlock'), function(i, div) {
if ($(div).children('#nested').children('h3.hidden').length ===
2016-01-31 13:09:43 -08:00
$(div).children('#nested').children('h3').length) {
$(div).prev('h2').addClass('hidden');
} else {
2016-01-31 13:09:43 -08:00
$(div).prev('h2').removeClass('hidden');
}
2016-01-30 11:01:37 -08:00
});
2016-01-31 13:09:43 -08:00
// Display "Clear Filter" element
if (mapFilter.next().children().hasClass('fa-search')) {
mapFilter.next()
.children()
.removeClass('fa-search')
.addClass('fa-times');
mapFilter.next().addClass('filled');
// Scroll to the top of the page
$('html, body, .map-accordion').scrollTop(0);
}
} else {
2016-01-31 13:09:43 -08:00
clearMapFilter();
}
2016-01-31 13:09:43 -08:00
// Display not found if everything is hidden
if ($.find('.certBlock').length ===
$('.map-accordion').children('.hidden').length) {
$('#noneFound').show();
} else {
$('#noneFound').hide();
2016-01-30 11:01:37 -08:00
}
});
2016-01-31 13:09:43 -08:00
// Give focus to the search box by default
mapFilter.focus();
// Clicking the search button or x clears the map
$('.map-buttons .input-group-addon').on('click', clearMapFilter);
2016-01-30 11:01:37 -08:00
2016-01-31 13:09:43 -08:00
function clearMapFilter() {
mapFilter.val('');
mapFilter.next().children().removeClass('fa-times').addClass('fa-search');
mapFilter.next().removeClass('filled');
$('.map-accordion').find('.hidden').removeClass('hidden');
$('#noneFound').hide();
}
// Clear the search on escape key
mapFilter.on('keydown', (e) => {
if (e.keyCode === 27) {
e.preventDefault();
clearMapFilter();
}
});
window.Mousetrap.bind('esc', clearMapFilter);
// keyboard shortcuts: open map
2016-02-07 16:51:57 +07:00
window.Mousetrap.bind('g m', toggleMap);
function addAlert(message = '', type = 'alert-info') {
return $('.flashMessage').append($(`
<div class='alert ${type}'>
<button class='close' type='button', data-dismiss='alert'>
<span class='ion-close-circled' />
</Button>
<div>${message}</div>
</div>
`));
}
function toggleNightMode() {
if (!main.userId) {
return addAlert('You must be logged in to use our themes.');
}
const iframe$ = document.getElementById('map-aside-frame');
const body$ = $('body');
if (iframe$) {
iframe$.src = iframe$.src;
}
body$.hide();
let updateThemeTo;
if (body$.hasClass('night')) {
body$.removeClass('night');
updateThemeTo = 'default';
} else {
body$.addClass('night');
updateThemeTo = 'night';
}
body$.fadeIn('100');
const options = {
url: `/api/users/${main.userId}/update-theme`,
type: 'POST',
data: { theme: updateThemeTo },
dataType: 'json'
};
return $.ajax(options)
.success(() => console.log('theme updated successfully'))
.fail(err => {
let message;
try {
message = JSON.parse(err.responseText).error.message;
} catch (error) {
return null;
}
if (!message) {
return null;
}
return addAlert(message);
});
}
Observable.merge(
Observable.fromEvent($('#night-mode'), 'click'),
Observable.create(observer => {
window.Mousetrap.bind('g t n', () => observer.onNext());
})
)
.debounce(500)
.subscribe(toggleNightMode, err => console.error(err));
// Hot Keys
window.Mousetrap.bind('g n n', () => {
// Next Challenge
window.location = '/challenges/next-challenge';
});
window.Mousetrap.bind('g n a', () => {
// Account
window.location = '/account';
});
window.Mousetrap.bind('g n m', () => {
// Map
window.location = '/map';
});
window.Mousetrap.bind('g n w', () => {
// Wiki
window.location = '/wiki';
});
window.Mousetrap.bind('g n a', () => {
// About
window.location = '/about';
});
window.Mousetrap.bind('g n s', () => {
// Shop
window.location = '/shop';
});
window.Mousetrap.bind('g n o', () => {
// Settings
window.location = '/settings';
});
window.Mousetrap.bind('g n r', () => {
// Repo
window.location = 'https://github.com/freecodecamp/freecodecamp/';
});
2016-04-14 19:49:19 -07:00
(function getFlyer() {
const flyerKey = '__flyerId__';
$.ajax({
url: '/api/flyers/findOne',
method: 'GET',
dataType: 'JSON',
data: { filter: { order: 'id DESC' } }
})
// log error
.fail(err => console.error(err))
.done(flyer => {
const lastFlyerId = localStorage.getItem(flyerKey);
if (
!flyer ||
!flyer.isActive ||
lastFlyerId === flyer.id
) {
return;
}
$('#dismiss-bill').on('click', () => {
localStorage.setItem(flyerKey, flyer.id);
});
$('#bill-content').html(flyer.message);
$('#bill-board').fadeIn();
});
}());
2014-11-06 17:38:47 -08:00
});