diff --git a/package.json b/package.json index 4863f4d47a..7fe7c38bd8 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "node-slack": "0.0.7", "node-uuid": "^1.4.3", "nodemailer": "~1.3.0", + "object.assign": "^3.0.0", "passport-facebook": "^2.0.0", "passport-google-oauth2": "^0.1.6", "passport-linkedin-oauth2": "^1.2.1", diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index 16073e2554..ae7237ef5d 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -1,3 +1,7 @@ +$(document).ready(function() { + $('#reset-button').on('click', resetEditor); +}); + var widgets = []; var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { lineNumbers: true, @@ -41,73 +45,90 @@ editor.setOption("extraKeys", { /* - Local Storage Update System By Andrew Cay(Resto) - localBonfire: singleton object that contains properties and methods related to - dealing with the localStorage system. - The keys work off of the variable challenge_name to make unique identifiers per bonfire + Local Storage Update System By Andrew Cay(Resto) + codeStorage: singleton object that contains properties and methods related to + dealing with the localStorage system. + The keys work off of the variable challenge_name to make unique identifiers per bonfire - Two extra functionalities: - Added anonymous version checking system incase of future updates to the system - Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage + Two extra functionalities: + Added anonymous version checking system incase of future updates to the system + Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage */ -var localBonfire = { - version: 0.01, - keyVersion:"saveVersion", - keyStamp: challenge_Name + 'Stamp', - keyValue: challenge_Name + 'Val', - stampExpireTime: (1000 *60) *60 *24, - updateWait: 1500,// 1.5 seconds - updateTimeoutId: null +var codeStorage = { + version: 0.01, + keyVersion:"saveVersion", + keyValue: null,//where the value of the editor is saved + updateWait: 2000,// 2 seconds + updateTimeoutId: null, + eventArray: []//for firing saves }; -localBonfire.getEditorValue = function(){ - return localStorage.getItem(localBonfire.keyValue); +// Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") +codeStorage.hasSaved = function(){ + return ( updateTimeoutId === null ); }; -localBonfire.getStampTime = function(){ - //localstorage always saves as strings. - return Number.parseInt( localStorage.getItem(localBonfire.keyStamp) ); +codeStorage.onSave = function(func){ + codeStorage.eventArray.push(func); }; -localBonfire.isAlive = function(){// returns true if IDE was edited within expire time - return ( Date.now() - localBonfire.getStampTime() < localBonfire.stampExpireTime ); +codeStorage.setSaveKey = function(key){ + codeStorage.keyValue = key + 'Val'; }; -localBonfire.updateStorage = function(){ - if(typeof(Storage) !== undefined) { - var stamp = Date.now(), - value = editor.getValue(); - localStorage.setItem(localBonfire.keyValue, value); - localStorage.setItem(localBonfire.keyStamp, stamp); - } else { - if( debugging ){ - console.log('no web storage'); - } - } - localBonfire.updateTimeoutId = null; +codeStorage.getEditorValue = function(){ + return ('' + localStorage.getItem(codeStorage.keyValue)); }; -// ANONYMOUS 1 TIME UPDATE VERSION + +codeStorage.isAlive = function() { + var val = this.getEditorValue() + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); +} +codeStorage.updateStorage = function(){ + if(typeof(Storage) !== undefined) { + var value = editor.getValue(); + localStorage.setItem(codeStorage.keyValue, value); + } else { + var debugging = false; + if( debugging ){ + console.log('no web storage'); + } + } + codeStorage.updateTimeoutId = null; + codeStorage.eventArray.forEach(function(func){ + func(); + }); +}; +//Update Version (function(){ - var savedVersion = localStorage.getItem('saveVersion'); - if( savedVersion === null ){ - localStorage.setItem(localBonfire.keyVersion, localBonfire.version);//just write current version - }else{ - //do checking if not current version - if( savedVersion !== localBonfire.version ){ - //update version - } - } + var savedVersion = localStorage.getItem('saveVersion'); + if( savedVersion === null ){ + localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version + }else{ + if( savedVersion !== codeStorage.version ){ + //Update version + } + } })(); -editor.on('keyup', function(codMir, event){ - window.clearTimeout(localBonfire.updateTimeoutId); - localBonfire.updateTimeoutId = window.setTimeout(localBonfire.updateStorage, localBonfire.updateWait); + + +///Set everything up one page +/// Update local save when editor has changed +codeStorage.setSaveKey(challenge_Name); +editor.on('keyup', function(){ + window.clearTimeout(codeStorage.updateTimeoutId); + codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); }); + var attempts = 0; if (attempts) { attempts = 0; } -var resetEditor = function() { +var resetEditor = function resetEditor() { editor.setValue(allSeeds); - localBonfire.updateStorage(); + codeStorage.updateStorage(); + }; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { @@ -141,11 +162,11 @@ var tests = tests || []; var allSeeds = ''; (function() { challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; + allSeeds += elem + '\n'; }); })(); -editorValue = (localBonfire.isAlive())? localBonfire.getEditorValue() : allSeeds; +editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; myCodeMirror.setValue(editorValue); diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index 98ea7c23ed..472fe7351d 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -271,6 +271,7 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); + var commentSubmitButtonHandler = function commentSubmitButtonHandler() { $('#comment-button').unbind('click'); var data = $('#comment-box').val(); diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 4b3d23e0e0..02cecc639e 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -31,8 +31,15 @@ */ var R = require('ramda'), + Rx = require('rx'), + assign = require('object.assign'), + debug = require('debug')('freecc:challenges'), utils = require('../utils'), + + // this would be so much cleaner with destructering... saveUser = require('../utils/rx').saveUser, + observableQueryFromModel = require('../utils/rx').observableQueryFromModel, + userMigration = require('../utils/middleware').userMigration, ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo; @@ -42,6 +49,28 @@ var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames(); var getMDNLinks = utils.getMDNLinks; +var challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i; +function dasherize(name) { + return ('' + name) + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^a-z0-9\-\.]/gi, ''); +} + +function unDasherize(name) { + return ('' + name).replace(/\-/g, ' '); +} + +function updateUserProgress(user, challengeId, completedChallenge) { + var index = user.uncompletedChallenges.indexOf(challengeId); + if (index > -1) { + user.progressTimestamps.push(Date.now()); + user.uncompletedChallenges.splice(index, 1); + } + user.completedChallenges.push(completedChallenge); + return user; +} + module.exports = function(app) { var router = app.loopback.Router(); var Challenge = app.models.Challenge; @@ -56,7 +85,7 @@ module.exports = function(app) { router.get('/map', challengeMap); router.get( '/challenges/next-challenge', - ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), + ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'), returnNextChallenge ); @@ -64,7 +93,7 @@ module.exports = function(app) { router.get( '/challenges/', - ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), + ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'), returnCurrentChallenge ); @@ -114,7 +143,7 @@ module.exports = function(app) { function() {}, next, function() { - res.redirect('../challenges/' + nextChallengeName); + res.redirect('/challenges/' + nextChallengeName); } ); } @@ -130,6 +159,7 @@ module.exports = function(app) { return elem; } }); + if (!req.user.currentChallenge) { req.user.currentChallenge = {}; req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0]; @@ -146,40 +176,53 @@ module.exports = function(app) { function() {}, next, function() { - res.redirect('../challenges/' + nameString); + res.redirect('/challenges/' + nameString); } ); } function returnIndividualChallenge(req, res, next) { - var dashedName = req.params.challengeName; + var origChallengeName = req.params.challengeName; + var unDashedName = unDasherize(origChallengeName); + var challengeName = challangesRegex.test(unDashedName) ? + // remove first word if matches + unDashedName.split(' ').slice(1).join(' ') : + unDashedName; + + debug('looking for ', challengeName); Challenge.findOne( - { where: { dashedName: dashedName }}, + { where: { name: { like: challengeName, options: 'i' } } }, function(err, challenge) { if (err) { return next(err); } // Handle not found if (!challenge) { + debug('did not find challenge for ' + origChallengeName); req.flash('errors', { msg: '404: We couldn\'t find a challenge with the name `' + - dashedName + + origChallengeName + '` Please double check the name.' }); return res.redirect('/challenges'); } // Redirect to full name if the user only entered a partial + if (dasherize(challenge.name) !== origChallengeName) { + debug('redirecting to fullname'); + return res.redirect('/challenges/' + dasherize(challenge.name)); + } + if (req.user) { req.user.currentChallenge = { challengeId: challenge.id, challengeName: challenge.name, dashedName: challenge.dashedName, - challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds). - map(function (key) { + challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds) + .map(function (key) { return challengeMapWithIds[key] .filter(function (elem) { - return String(elem) === challenge.id; + return elem === ('' + challenge.id); }) .map(function () { return key; @@ -191,7 +234,7 @@ module.exports = function(app) { var commonLocals = { title: challenge.name, - dashedName: dashedName, + dashedName: origChallengeName, name: challenge.name, details: challenge.description.slice(1), tests: challenge.tests, @@ -235,143 +278,123 @@ module.exports = function(app) { } function completedBonfire(req, res, next) { - var isCompletedWith = req.body.challengeInfo.completedWith || ''; - var isCompletedDate = Math.round(+new Date()); + debug('compltedBonfire'); + var completedWith = req.body.challengeInfo.completedWith || false; var challengeId = req.body.challengeInfo.challengeId; - var isSolution = req.body.challengeInfo.solution; - var challengeName = req.body.challengeInfo.challengeName; - if (isCompletedWith) { - User.find({ - where: { 'profile.username': isCompletedWith.toLowerCase() }, - limit: 1 - }, function (err, pairedWith) { - if (err) { return next(err); } + var challengeData = { + id: challengeId, + name: req.body.challengeInfo.challengeName, + completedDate: Math.round(+new Date()), + solution: req.body.challengeInfo.solution, + challengeType: 5 + }; - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - pairedWith = pairedWith.pop(); + observableQueryFromModel( + User, + 'findOne', + { where: { username: ('' + completedWith).toLowerCase() } } + ) + .doOnNext(function(pairedWith) { + debug('paired with ', pairedWith); if (pairedWith) { - - index = pairedWith.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - pairedWith.progressTimestamps.push(Date.now() || 0); - pairedWith.uncompletedChallenges.splice(index, 1); - - } - - pairedWith.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: req.user.id, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: pairedWith.id, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); + updateUserProgress( + pairedWith, + challengeId, + assign({ completedWith: req.user.id }, challengeData) + ); } - // User said they paired, but pair wasn't found - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - req.user.save(function (err, user) { - if (err) { return next(err); } - - if (pairedWith) { - pairedWith.save(function (err, paired) { - if (err) { - return next(err); - } - if (user && paired) { - return res.send(true); - } - }); - } else if (user) { - res.send(true); + }) + .withLatestFrom( + Rx.Observable.just(req.user), + function(pairedWith, user) { + debug('yo'); + return { + user: user, + pairedWith: pairedWith + }; + } + ) + // side effects should always be done in do's and taps + .doOnNext(function(dats) { + updateUserProgress( + dats.user, + challengeId, + dats.pairedWith ? + // paired programmer found and adding to data + assign({ completedWith: dats.pairedWith.id }, challengeData) : + // user said they paired, but pair wasn't found + challengeData + ); + }) + // not iterate users + .flatMap(function(dats) { + debug('flatmap'); + return Rx.Observable.from([dats.user, dats.pairedWith]); + }) + // save user + .flatMap(function(user) { + // save user will do nothing if user is falsey + return saveUser(user); + }) + .subscribe( + function(user) { + debug('onNext'); + if (user) { + debug('user %s saved', user.username); } - }); - }); - } else { - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err) { - if (err) { return next(err); } - res.send(true); - }); - } + }, + next, + function() { + debug('completed'); + return res.status(200).send(true); + } + ); } function completedChallenge(req, res, next) { - var isCompletedDate = Math.round(+new Date()); + var completedDate = Math.round(+new Date()); var challengeId = req.body.challengeInfo.challengeId; - req.user.completedChallenges.push({ - id: challengeId, - completedDate: isCompletedDate, - name: req.body.challengeInfo.challengeName, - solution: null, - githubLink: null, - verified: true - }); - var index = req.user.uncompletedChallenges.indexOf(challengeId); - - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err, user) { - if (err) { - return next(err); + updateUserProgress( + req.user, + challengeId, + { + id: challengeId, + completedDate: completedDate, + name: req.body.challengeInfo.challengeName, + solution: null, + githubLink: null, + verified: true } - if (user) { - res.sendStatus(200); - } - }); + ); + + saveUser(req.user) + .subscribe( + function() { }, + next, + function() { + res.sendStatus(200); + } + ); } function completedZiplineOrBasejump(req, res, next) { - var isCompletedWith = req.body.challengeInfo.completedWith || false; - var isCompletedDate = Math.round(+new Date()); + var completedWith = req.body.challengeInfo.completedWith || false; + var completedDate = Math.round(+new Date()); var challengeId = req.body.challengeInfo.challengeId; var solutionLink = req.body.challengeInfo.publicURL; - var githubLink = req.body.challengeInfo.challengeType === '4' - ? req.body.challengeInfo.githubURL : true; + + var githubLink = req.body.challengeInfo.challengeType === '4' ? + req.body.challengeInfo.githubURL : + true; + var challengeType = req.body.challengeInfo.challengeType === '4' ? - 4 : 3; + 4 : + 3; + if (!solutionLink || !githubLink) { req.flash('errors', { msg: 'You haven\'t supplied the necessary URLs for us to inspect ' + @@ -380,93 +403,64 @@ module.exports = function(app) { return res.sendStatus(403); } - if (isCompletedWith) { - User.find({ - where: { 'profile.username': isCompletedWith.toLowerCase() }, - limit: 1 - }, function (err, pairedWithFromMongo) { - if (err) { return next(err); } - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); + var challengeData = { + id: challengeId, + name: req.body.challengeInfo.challengeName, + completedDate: completedDate, + solution: solutionLink, + githubLink: githubLink, + challengeType: challengeType, + verified: false + }; + + observableQueryFromModel( + User, + 'findOne', + { where: { username: completedWith.toLowerCase() } } + ) + .doOnNext(function(pairedWith) { + if (pairedWith) { + updateUserProgress( + pairedWith, + challengeId, + assign({ completedWith: req.user.id }, challengeData) + ); } - var pairedWith = pairedWithFromMongo.pop(); - - req.user.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.challengeName, - completedWith: pairedWith.id, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - - req.user.save(function (err, user) { - if (err) { return next(err); } - - if (req.user.id.toString() === pairedWith.id.toString()) { - return res.sendStatus(200); + }) + .withLatestFrom(Rx.Observable.just(req.user), function(user, pairedWith) { + return { + user: user, + pairedWith: pairedWith + }; + }) + .doOnNext(function(dats) { + updateUserProgress( + dats.user, + challengeId, + dats.pairedWith ? + assign({ completedWith: dats.pairedWith.id }, challengeData) : + challengeData + ); + }) + .flatMap(function(dats) { + return Rx.Observable.from([dats.user, dats.pairedWith]); + }) + // save users + .flatMap(function(user) { + // save user will do nothing if user is falsey + return saveUser(user); + }) + .subscribe( + function(user) { + if (user) { + debug('user %s saved', user.username); } - index = pairedWith.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - pairedWith.progressTimestamps.push(Date.now() || 0); - pairedWith.uncompletedChallenges.splice(index, 1); - - } - - pairedWith.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.coursewareName, - completedWith: req.user.id, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - pairedWith.save(function (err, paired) { - if (err) { - return next(err); - } - if (user && paired) { - return res.sendStatus(200); - } - }); - }); - }); - } else { - - req.user.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err, user) { - if (err) { - return next(err); + }, + next, + function() { + return res.status(200).send(true); } - // NOTE(berks): under certain conditions this will not close - // the response. - if (user) { - return res.sendStatus(200); - } - }); - } + ); } function challengeMap(req, res, next) { diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index c0092b558d..84059c564e 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -13,7 +13,7 @@ module.exports = function(app) { var router = app.loopback.Router(); var User = app.models.User; var Challenge = app.models.Challenge; - var Story = app.models.Store; + var Story = app.models.Story; var FieldGuide = app.models.FieldGuide; var Nonprofit = app.models.Nonprofit; @@ -76,15 +76,15 @@ module.exports = function(app) { users: function(callback) { User.find( { - where: { 'profile.username': { nlike: '' } }, - fields: { 'profile.username': true } + where: { username: { nlike: '' } }, + fields: { username: true } }, function(err, users) { if (err) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(users) + Rx.Observable.from(users, null, null, Rx.Scheduler.default) .map(function(user) { return user.username; }) @@ -107,7 +107,7 @@ module.exports = function(app) { debug('Challenge err: ', err); callback(err); } else { - Rx.Observable.from(challenges) + Rx.Observable.from(challenges, null, null, Rx.Scheduler.default) .map(function(challenge) { return challenge.name; }) @@ -127,7 +127,7 @@ module.exports = function(app) { debug('Story err: ', err); callback(err); } else { - Rx.Observable.from(stories) + Rx.Observable.from(stories, null, null, Rx.Scheduler.default) .map(function(story) { return story.link; }) @@ -148,7 +148,7 @@ module.exports = function(app) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(nonprofits) + Rx.Observable.from(nonprofits, null, null, Rx.Scheduler.default) .map(function(nonprofit) { return nonprofit.name; }) @@ -168,7 +168,12 @@ module.exports = function(app) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(fieldGuides) + Rx.Observable.from( + fieldGuides, + null, + null, + Rx.Scheduler.default + ) .map(function(fieldGuide) { return fieldGuide.name; }) @@ -184,7 +189,7 @@ module.exports = function(app) { if (err) { return next(err); } - setTimeout(function() { + process.nextTick(function() { res.header('Content-Type', 'application/xml'); res.render('resources/sitemap', { appUrl: appUrl, @@ -195,11 +200,15 @@ module.exports = function(app) { nonprofits: results.nonprofits, fieldGuides: results.fieldGuides }); - }, 0); + }); } ); } + function chat(req, res) { + res.redirect('//gitter.im/FreeCodeCamp/FreeCodeCamp'); + } + function bootcampCalculator(req, res) { res.render('resources/calculator', { title: 'Coding Bootcamp Cost Calculator', @@ -260,7 +269,7 @@ module.exports = function(app) { } function unsubscribe(req, res, next) { - User.findOne({ email: req.params.email }, function(err, user) { + User.findOne({ where: { email: req.params.email } }, function(err, user) { if (user) { if (err) { return next(err); diff --git a/server/boot/user.js b/server/boot/user.js index f36fbe56a2..4575a6decf 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -4,7 +4,7 @@ var _ = require('lodash'), crypto = require('crypto'), nodemailer = require('nodemailer'), moment = require('moment'), - //debug = require('debug')('freecc:cntr:userController'), + // debug = require('debug')('freecc:cntr:userController'), secrets = require('../../config/secrets'); diff --git a/server/config.local.js b/server/config.local.js index c781159696..9b9fc132d6 100644 --- a/server/config.local.js +++ b/server/config.local.js @@ -17,4 +17,4 @@ module.exports = { clientID: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET } -}; \ No newline at end of file +}; diff --git a/server/datasources.local.js b/server/datasources.local.js index 276a4e8dee..8ad9d08abc 100644 --- a/server/datasources.local.js +++ b/server/datasources.local.js @@ -3,8 +3,8 @@ var secrets = require('../config/secrets'); module.exports = { db: { connector: 'mongodb', - connectionTimeout: 15000, - url: process.env.MONGOHQ_URL + connectionTimeout: 5000, + url: secrets.db }, mail: { connector: 'mail', diff --git a/server/utils/rx.js b/server/utils/rx.js new file mode 100644 index 0000000000..8a4003c00c --- /dev/null +++ b/server/utils/rx.js @@ -0,0 +1,25 @@ +var Rx = require('rx'); +var debug = require('debug')('freecc:rxUtils'); + +exports.saveUser = function saveUser(user) { + return new Rx.Observable.create(function(observer) { + if (!user || typeof user.save !== 'function') { + debug('no user or save method'); + observer.onNext(); + return observer.onCompleted(); + } + user.save(function(err, savedUser) { + if (err) { + return observer.onError(err); + } + debug('user saved'); + observer.onNext(savedUser); + observer.onCompleted(); + }); + }); +}; + +exports.observableQueryFromModel = + function observableQueryFromModel(Model, method, query) { + return Rx.Observable.fromNodeCallback(Model[method], Model)(query); + }; diff --git a/server/views/account/show.jade b/server/views/account/show.jade index 0f7add7096..f1edc4d3c7 100644 --- a/server/views/account/show.jade +++ b/server/views/account/show.jade @@ -47,88 +47,32 @@ block content .background-svg.img-center .points-on-top = "[ " + (progressTimestamps.length) + " ]" - - - .row - .col-xs-12 - if (website1Title && website1Link && website1Image) - .row - .col-xs-12.col-md-5 - img.img-center.img-responsive.portfolio-image(src=website1Image, alt="@#{username}'s #{website1Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website1Title - a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website1Title && website1Link && !website1Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website1Title - a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website2Title && website2Link && website2Image) - .row - .col-xs-12.col-md-5 - img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website2Title - a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website2Title && website2Link && !website2Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website2Title - a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website3Title && website3Link && website3Image) - .row - .col-xs-12.col-md-5 - img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website3Title - a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') - i.fa.icon-beaker - | Try it out - if (website3Title && website3Link && !website3Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website3Title - a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') - i.fa.icon-beaker - | Try it out - .spacer .hidden-xs.hidden-sm.col-md-12 #cal-heatmap.d3-centered script. $(document).ready(function () { - setTimeout(function () { - var cal = new CalHeatMap(); - var calendar = !{JSON.stringify(calender)}; - cal.init({ - itemSelector: "#cal-heatmap", - domain: "month", - subDomain: "x_day", - domainGutter: 10, - data: calendar, - cellSize: 15, - align: 'center', - cellRadius: 3, - cellPadding: 2, - tooltip: true, - range: 6, - start: new Date().setDate(new Date().getDate() - 150), - legendColors: ["#cccccc", "#215f1e"], - legend: [1, 2, 3], - label: { - position: "top" - } - }); - }, 300); + var cal = new CalHeatMap(); + var calendar = !{JSON.stringify(calender)}; + cal.init({ + itemSelector: "#cal-heatmap", + domain: "month", + subDomain: "x_day", + domainGutter: 10, + data: calendar, + cellSize: 15, + align: 'center', + cellRadius: 3, + cellPadding: 2, + tooltip: true, + range: 6, + start: new Date().setDate(new Date().getDate() - 150), + legendColors: ["#cccccc", "#215f1e"], + legend: [1, 2, 3], + label: { + position: "top" + } + }); }); .row .hidden-xs.col-sm-12.text-center diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade index 1eae48b09e..cec1c955e7 100644 --- a/server/views/coursewares/showBonfire.jade +++ b/server/views/coursewares/showBonfire.jade @@ -84,20 +84,20 @@ block content label.negative-10.btn.btn-primary.btn-block#submitButton i.fa.fa-play |   Run code (ctrl + enter) - .button-spacer - .btn-group.input-group.btn-group-justified - label.btn.btn-success#resetButton - i.fa.fa-refresh - |   Reset - label.btn.btn-success#trigger-help-modal - i.fa.fa-medkit - |   Help - label.btn.btn-success#trigger-pair-modal - i.fa.fa-user-plus - |   Pair - label.btn.btn-success#trigger-issue-modal - i.fa.fa-bug - |   Bug + .button-spacer + .btn-group.input-group.btn-group-justified + label.btn.btn-success#trigger-reset-modal + i.fa.fa-refresh + |   Reset + label.btn.btn-success#trigger-help-modal + i.fa.fa-medkit + |   Help + label.btn.btn-success#trigger-pair-modal + i.fa.fa-user-plus + |   Pair + label.btn.btn-success#trigger-issue-modal + i.fa.fa-bug + |   Bug .button-spacer form.code .form-group.codeMirrorView diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade index 0316cbe202..e35b9c5e8e 100644 --- a/server/views/partials/navbar.jade +++ b/server/views/partials/navbar.jade @@ -14,7 +14,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height li a(href='/map') Map li - a(href='gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat + a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat li a(href='/stories') News li