├── client ├── sagas │ ├── README.md │ ├── index.js │ ├── title-saga.js │ ├── err-saga.js │ └── hard-go-to-saga.js ├── es6-shims.js ├── less │ ├── lib │ │ ├── ionicons │ │ │ ├── ionicons.less │ │ │ └── _ionicons-font.less │ │ ├── font-awesome-4.3.0 │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── core.less │ │ │ │ ├── stacked.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── path.less │ │ │ │ ├── animated.less │ │ │ │ └── mixins.less │ │ │ └── scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── font-awesome.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _animated.scss │ │ │ │ └── _mixins.scss │ │ └── bootstrap │ │ │ ├── mixins │ │ │ ├── center-block.less │ │ │ ├── text-emphasis.less │ │ │ ├── size.less │ │ │ ├── background-variant.less │ │ │ ├── opacity.less │ │ │ ├── text-overflow.less │ │ │ ├── tab-focus.less │ │ │ ├── labels.less │ │ │ ├── resize.less │ │ │ ├── progress-bar.less │ │ │ ├── nav-divider.less │ │ │ ├── reset-filter.less │ │ │ ├── alerts.less │ │ │ ├── nav-vertical-align.less │ │ │ ├── responsive-visibility.less │ │ │ ├── pagination.less │ │ │ ├── border-radius.less │ │ │ ├── panels.less │ │ │ ├── list-group.less │ │ │ ├── hide-text.less │ │ │ ├── clearfix.less │ │ │ ├── table-row.less │ │ │ ├── image.less │ │ │ └── buttons.less │ │ │ ├── wells.less │ │ │ ├── breadcrumbs.less │ │ │ ├── responsive-embed.less │ │ │ ├── close.less │ │ │ ├── media.less │ │ │ ├── component-animations.less │ │ │ ├── thumbnails.less │ │ │ ├── utilities.less │ │ │ ├── pager.less │ │ │ ├── jumbotron.less │ │ │ ├── mixins.less │ │ │ ├── badges.less │ │ │ ├── bootstrap.less │ │ │ └── labels.less │ ├── signin.less │ ├── jobs.less │ └── chat.less ├── README.md └── commonFramework │ ├── add-loop-protect.js │ ├── get-iframe.js │ ├── display-test-results.js │ ├── run-tests-stream.js │ └── output-display.js ├── common ├── app │ ├── middlewares.js │ ├── components │ │ ├── Flash │ │ │ ├── index.jsx │ │ │ └── Queue.jsx │ │ ├── Footer │ │ │ ├── README.md │ │ │ ├── index.js │ │ │ ├── links.json │ │ │ └── Footer.jsx │ │ ├── Nav │ │ │ ├── index.js │ │ │ └── links.json │ │ ├── README.md │ │ └── NotFound │ │ │ └── index.jsx │ ├── index.js │ ├── routes │ │ ├── Admin │ │ │ └── README.md │ │ ├── Jobs │ │ │ ├── README.md │ │ │ ├── redux │ │ │ │ ├── index.js │ │ │ │ ├── types.js │ │ │ │ ├── save-job-saga.js │ │ │ │ ├── apply-promo-saga.js │ │ │ │ ├── actions.js │ │ │ │ ├── jobs-form-normalizer.js │ │ │ │ └── fetch-jobs-saga.js │ │ │ ├── utils.js │ │ │ ├── index.js │ │ │ └── components │ │ │ │ ├── JobNotFound.jsx │ │ │ │ └── NewJobCompleted.jsx │ │ ├── Bonfires │ │ │ └── README.md │ │ ├── Hikes │ │ │ ├── index.js │ │ │ ├── redux │ │ │ │ ├── index.js │ │ │ │ ├── selectors.js │ │ │ │ ├── types.js │ │ │ │ └── fetch-hikes-saga.js │ │ │ └── components │ │ │ │ └── Map.jsx │ │ └── index.js │ ├── shared │ │ └── README.md │ ├── redux │ │ ├── index.js │ │ ├── types.js │ │ ├── reducer.js │ │ ├── actions.js │ │ └── fetch-user-saga.js │ ├── sagas.js │ ├── provide-store.js │ ├── utils │ │ ├── create-types.js │ │ ├── render.js │ │ ├── shallow-equals.js │ │ └── Professor-Context.js │ └── create-reducer.js ├── utils │ ├── constantStrings.json │ ├── index.js │ └── services-creator.js ├── config.global.js └── models │ ├── User-Credential.json │ ├── User-Identity.json │ └── pledge.json ├── .eslintignore ├── server ├── views │ ├── sponsors │ │ └── sponsors.html │ ├── certificate │ │ ├── index.jade │ │ ├── back-end.jade │ │ ├── front-end.jade │ │ ├── full-stack.jade │ │ ├── data-vis.jade │ │ ├── script.jade │ │ └── font.jade │ ├── resources │ │ ├── unsubscribed.jade │ │ ├── nonprofits-form.jade │ │ ├── pmi-acp-agile-project-managers-form.jade │ │ ├── the-fastest-web-page-on-the-internet.jade │ │ ├── labs.jade │ │ ├── shop.jade │ │ ├── dashboard.jade │ │ └── stories.jade │ ├── wiki │ │ └── show.jade │ ├── layout.jade │ ├── layout-react.jade │ ├── partials │ │ ├── stylesheets.jade │ │ ├── challenge-footer.jade │ │ ├── flash.jade │ │ ├── react-stylesheets.jade │ │ ├── scripts.jade │ │ └── challenge-modals.jade │ ├── layout-wide.jade │ ├── account │ │ ├── forgot.jade │ │ ├── reset.jade │ │ ├── email-signup.jade │ │ └── email-signin.jade │ ├── feed.jade │ ├── commit │ │ ├── directory.jade │ │ └── pledge.jade │ ├── stories │ │ └── index.jade │ └── redirect-https.jade ├── config.production.js ├── boot │ ├── authentication.js │ ├── restApi.js │ ├── t-wiki.js │ ├── a-services.js │ ├── home.js │ ├── explorer.js │ ├── redirects.js │ ├── redirectHttps.js │ └── about.js ├── debug-entry.js ├── utils │ ├── certTypes.json │ ├── commit-goals.json │ ├── constantStrings.json │ ├── date-utils.js │ ├── middleware.js │ ├── constants.js │ ├── bad-id-map.js │ ├── rx.js │ ├── commit.js │ └── auth.js ├── datasources.json ├── middlewares │ ├── cookie-parser.js │ ├── global-locals.js │ ├── constant-headers.js │ ├── express-rx.js │ ├── jade-helpers.js │ ├── validator.js │ ├── sessions.js │ ├── keymetrics.js │ ├── add-return-to.js │ ├── revision-helpers.js │ └── error-handlers.js ├── README.md ├── config.development.js ├── manifests │ └── README.md ├── ssl-config.js ├── datasources.local.js ├── config.local.js ├── config.json ├── services │ ├── user.js │ ├── hikes.js │ └── job.js ├── production-start.js └── model-config.json ├── .node-inspectorrc ├── .bowerrc ├── .travis.yml ├── public ├── favicon.ico ├── images │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── Wendy.jpg │ ├── bill.jpg │ ├── date.png │ ├── fcc.jpg │ ├── fcc.png │ ├── intro.png │ ├── juhe.png │ ├── media.png │ ├── weibo.png │ ├── 8v3t84p.jpg │ ├── D7Y5luw.jpg │ ├── Elb3dfj.jpg │ ├── Et3iD74.jpg │ ├── PDGQ9ZM.jpg │ ├── QPpjPac.jpg │ ├── Shirly.jpg │ ├── Taylor.jpg │ ├── UVB9hxp.jpg │ ├── WvQvNGN.gif │ ├── caonana.jpg │ ├── commit.png │ ├── enroll.png │ ├── favicon.ico │ ├── github.jpg │ ├── github.png │ ├── gitter.png │ ├── jianshu.png │ ├── k8btNUB.jpg │ ├── n6GeSEm.gif │ ├── nsvNixW.jpg │ ├── pbW7K5S.jpg │ ├── qianjie.jpg │ ├── startup.jpg │ ├── startup.png │ ├── vDTMJSh.gif │ ├── weixin.png │ ├── wilddog.png │ ├── wjlDigg.jpg │ ├── wujing.jpg │ ├── yXyxbDd.jpg │ ├── zuanshi.png │ ├── as-seen-on.png │ ├── chenghui.jpg │ ├── community.png │ ├── fcc-code.png │ ├── fcc-weibo.png │ ├── fourcircle.png │ ├── funny-cat.jpg │ ├── grumpy-cat.jpg │ ├── myCatPhoto.png │ ├── eightskills.png │ ├── fcczhongguo.jpg │ ├── freecodecamp.png │ ├── logo-navbar.png │ ├── relaxing-cat.jpg │ ├── running-cat.jpg │ ├── simonSound1.mp3 │ ├── simonSound2.mp3 │ ├── simonSound3.mp3 │ ├── simonSound4.mp3 │ ├── threepeople.png │ ├── iphone6-frame.png │ ├── mischievous-cat.jpg │ ├── zhangjianqiang.jpg │ ├── cover-green-brown.png │ ├── fcc-performance-1.png │ ├── fcc-performance-2.png │ ├── qrcode_for_nearest.jpg │ ├── wide-social-banner.png │ └── camper-image-placeholder.png ├── robots.txt ├── favicons │ ├── green.ico │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── ms-icon-70x70.png │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ ├── apple-touch-icon.png │ ├── android-chrome-36x36.png │ ├── android-chrome-48x48.png │ ├── android-chrome-72x72.png │ ├── android-chrome-96x96.png │ ├── android-chrome-144x144.png │ ├── android-chrome-192x192.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-precomposed.png │ ├── browserconfig.xml │ └── android-chrome-manifest.json ├── fonts │ ├── ionicons.eot │ ├── ionicons.ttf │ ├── saxmono.ttf │ ├── FontAwesome.otf │ ├── Lato-Bold.ttf │ ├── Lato-Light.ttf │ ├── ionicons.woff │ ├── Lato-Regular.ttf │ ├── Ubuntu-Regular.ttf │ ├── UbuntuMono-Regular.ttf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ └── glyphicons-halflings-regular.eot ├── js │ ├── lib │ │ ├── jailed │ │ │ └── _frame.html │ │ └── loop-protect │ │ │ └── LICENSE.md │ └── dashboard.js ├── css │ ├── ubuntu.css │ └── lato.css ├── browserconfig.xml ├── json │ └── cats.json └── favicons.html ├── .gitattributes ├── .babelrc ├── seed ├── create-challenge-bundle.js ├── challenges │ ├── 04-video-challenges │ │ ├── agile.json │ │ ├── tools.json │ │ ├── devops.json │ │ ├── statistics.json │ │ ├── gamification.json │ │ ├── visual-design.json │ │ ├── accessibility.json │ │ ├── computer-science.json │ │ ├── game-development.json │ │ ├── machine-learning.json │ │ ├── data-visualization.json │ │ ├── math-for-programmers.json │ │ ├── user-experience-design.json │ │ ├── mobile-javascript-development.json │ │ ├── embedded-and-internet-of-things.json │ │ └── software-engineering-principles.json │ └── 02-data-visualization-certification │ │ ├── d3.json │ │ ├── react.json │ │ └── sass.json └── get-emails.js ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── sample.env ├── bower.json ├── .gitignore ├── pm2Start.js ├── webpack.config.js ├── leanTest.js └── webpack.config.node.js /client/sagas/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/app/middlewares.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | public/**/*.js 2 | -------------------------------------------------------------------------------- /common/app/components/Flash/index.jsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/views/sponsors/sponsors.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.node-inspectorrc: -------------------------------------------------------------------------------- 1 | { 2 | "save-live-edit": true 3 | } -------------------------------------------------------------------------------- /common/app/components/Footer/README.md: -------------------------------------------------------------------------------- 1 | Currently not used 2 | -------------------------------------------------------------------------------- /common/app/index.js: -------------------------------------------------------------------------------- 1 | export default from './create-app.jsx'; 2 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "public/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /common/app/components/Nav/index.js: -------------------------------------------------------------------------------- 1 | export default from './Nav.jsx'; 2 | -------------------------------------------------------------------------------- /common/app/components/README.md: -------------------------------------------------------------------------------- 1 | things like NavBar and Footer go here 2 | -------------------------------------------------------------------------------- /common/app/routes/Admin/README.md: -------------------------------------------------------------------------------- 1 | in case we ever want an admin panel 2 | -------------------------------------------------------------------------------- /common/app/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export default from './Footer.jsx'; 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '4.2.1' 5 | 6 | sudo: false 7 | -------------------------------------------------------------------------------- /client/es6-shims.js: -------------------------------------------------------------------------------- 1 | require('object.assign').shim(); 2 | require('es6-map/implement'); 3 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/README.md: -------------------------------------------------------------------------------- 1 | This folder contains everything relative to Jobs board 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicon.ico -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | public/js/lib/codemirror/* linguist-vendored 2 | *.jsx linguist-language=JavaScript 3 | -------------------------------------------------------------------------------- /common/app/routes/Bonfires/README.md: -------------------------------------------------------------------------------- 1 | This folder contains things relative to the bonfires' screens 2 | -------------------------------------------------------------------------------- /common/app/shared/README.md: -------------------------------------------------------------------------------- 1 | Here is where all components that are shared between multiple views 2 | -------------------------------------------------------------------------------- /public/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/1.png -------------------------------------------------------------------------------- /public/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/2.png -------------------------------------------------------------------------------- /public/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/3.png -------------------------------------------------------------------------------- /public/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/4.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | Sitemap: http://www.freecodecamp.cn/sitemap.xml 4 | -------------------------------------------------------------------------------- /server/config.production.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | host: process.env.HOST || 'localhost' 3 | }; 4 | -------------------------------------------------------------------------------- /public/images/Wendy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/Wendy.jpg -------------------------------------------------------------------------------- /public/images/bill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/bill.jpg -------------------------------------------------------------------------------- /public/images/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/date.png -------------------------------------------------------------------------------- /public/images/fcc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc.jpg -------------------------------------------------------------------------------- /public/images/fcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc.png -------------------------------------------------------------------------------- /public/images/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/intro.png -------------------------------------------------------------------------------- /public/images/juhe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/juhe.png -------------------------------------------------------------------------------- /public/images/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/media.png -------------------------------------------------------------------------------- /public/images/weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/weibo.png -------------------------------------------------------------------------------- /public/favicons/green.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/green.ico -------------------------------------------------------------------------------- /public/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/ionicons.eot -------------------------------------------------------------------------------- /public/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/ionicons.ttf -------------------------------------------------------------------------------- /public/fonts/saxmono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/saxmono.ttf -------------------------------------------------------------------------------- /public/images/8v3t84p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/8v3t84p.jpg -------------------------------------------------------------------------------- /public/images/D7Y5luw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/D7Y5luw.jpg -------------------------------------------------------------------------------- /public/images/Elb3dfj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/Elb3dfj.jpg -------------------------------------------------------------------------------- /public/images/Et3iD74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/Et3iD74.jpg -------------------------------------------------------------------------------- /public/images/PDGQ9ZM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/PDGQ9ZM.jpg -------------------------------------------------------------------------------- /public/images/QPpjPac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/QPpjPac.jpg -------------------------------------------------------------------------------- /public/images/Shirly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/Shirly.jpg -------------------------------------------------------------------------------- /public/images/Taylor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/Taylor.jpg -------------------------------------------------------------------------------- /public/images/UVB9hxp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/UVB9hxp.jpg -------------------------------------------------------------------------------- /public/images/WvQvNGN.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/WvQvNGN.gif -------------------------------------------------------------------------------- /public/images/caonana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/caonana.jpg -------------------------------------------------------------------------------- /public/images/commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/commit.png -------------------------------------------------------------------------------- /public/images/enroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/enroll.png -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/favicon.ico -------------------------------------------------------------------------------- /public/images/github.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/github.jpg -------------------------------------------------------------------------------- /public/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/github.png -------------------------------------------------------------------------------- /public/images/gitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/gitter.png -------------------------------------------------------------------------------- /public/images/jianshu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/jianshu.png -------------------------------------------------------------------------------- /public/images/k8btNUB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/k8btNUB.jpg -------------------------------------------------------------------------------- /public/images/n6GeSEm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/n6GeSEm.gif -------------------------------------------------------------------------------- /public/images/nsvNixW.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/nsvNixW.jpg -------------------------------------------------------------------------------- /public/images/pbW7K5S.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/pbW7K5S.jpg -------------------------------------------------------------------------------- /public/images/qianjie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/qianjie.jpg -------------------------------------------------------------------------------- /public/images/startup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/startup.jpg -------------------------------------------------------------------------------- /public/images/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/startup.png -------------------------------------------------------------------------------- /public/images/vDTMJSh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/vDTMJSh.gif -------------------------------------------------------------------------------- /public/images/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/weixin.png -------------------------------------------------------------------------------- /public/images/wilddog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/wilddog.png -------------------------------------------------------------------------------- /public/images/wjlDigg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/wjlDigg.jpg -------------------------------------------------------------------------------- /public/images/wujing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/wujing.jpg -------------------------------------------------------------------------------- /public/images/yXyxbDd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/yXyxbDd.jpg -------------------------------------------------------------------------------- /public/images/zuanshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/zuanshi.png -------------------------------------------------------------------------------- /public/js/lib/jailed/_frame.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/favicon.ico -------------------------------------------------------------------------------- /public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/Lato-Light.ttf -------------------------------------------------------------------------------- /public/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/ionicons.woff -------------------------------------------------------------------------------- /public/images/as-seen-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/as-seen-on.png -------------------------------------------------------------------------------- /public/images/chenghui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/chenghui.jpg -------------------------------------------------------------------------------- /public/images/community.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/community.png -------------------------------------------------------------------------------- /public/images/fcc-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc-code.png -------------------------------------------------------------------------------- /public/images/fcc-weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc-weibo.png -------------------------------------------------------------------------------- /public/images/fourcircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fourcircle.png -------------------------------------------------------------------------------- /public/images/funny-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/funny-cat.jpg -------------------------------------------------------------------------------- /public/images/grumpy-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/grumpy-cat.jpg -------------------------------------------------------------------------------- /public/images/myCatPhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/myCatPhoto.png -------------------------------------------------------------------------------- /public/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /public/images/eightskills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/eightskills.png -------------------------------------------------------------------------------- /public/images/fcczhongguo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcczhongguo.jpg -------------------------------------------------------------------------------- /public/images/freecodecamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/freecodecamp.png -------------------------------------------------------------------------------- /public/images/logo-navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/logo-navbar.png -------------------------------------------------------------------------------- /public/images/relaxing-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/relaxing-cat.jpg -------------------------------------------------------------------------------- /public/images/running-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/running-cat.jpg -------------------------------------------------------------------------------- /public/images/simonSound1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/simonSound1.mp3 -------------------------------------------------------------------------------- /public/images/simonSound2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/simonSound2.mp3 -------------------------------------------------------------------------------- /public/images/simonSound3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/simonSound3.mp3 -------------------------------------------------------------------------------- /public/images/simonSound4.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/simonSound4.mp3 -------------------------------------------------------------------------------- /public/images/threepeople.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/threepeople.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-0"], 3 | "plugins": ["babel-plugin-add-module-exports"] 4 | } 5 | -------------------------------------------------------------------------------- /public/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/ms-icon-70x70.png -------------------------------------------------------------------------------- /public/fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /public/images/iphone6-frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/iphone6-frame.png -------------------------------------------------------------------------------- /public/images/mischievous-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/mischievous-cat.jpg -------------------------------------------------------------------------------- /public/images/zhangjianqiang.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/zhangjianqiang.jpg -------------------------------------------------------------------------------- /public/favicons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/favicons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/favicons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/fonts/UbuntuMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/UbuntuMono-Regular.ttf -------------------------------------------------------------------------------- /public/images/cover-green-brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/cover-green-brown.png -------------------------------------------------------------------------------- /public/images/fcc-performance-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc-performance-1.png -------------------------------------------------------------------------------- /public/images/fcc-performance-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/fcc-performance-2.png -------------------------------------------------------------------------------- /common/utils/constantStrings.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultProfileImage": "https://freecodecamp.cn/images/camper-image-placeholder.png" 3 | } 4 | -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /public/images/qrcode_for_nearest.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/qrcode_for_nearest.jpg -------------------------------------------------------------------------------- /public/images/wide-social-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/wide-social-banner.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-36x36.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-48x48.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-72x72.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-96x96.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-144x144.png -------------------------------------------------------------------------------- /public/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/images/camper-image-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/images/camper-image-placeholder.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /server/boot/authentication.js: -------------------------------------------------------------------------------- 1 | module.exports = function enableAuthentication(app) { 2 | // enable authentication 3 | app.enableAuth(); 4 | }; 5 | -------------------------------------------------------------------------------- /public/favicons/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/public/favicons/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/less/lib/ionicons/ionicons.less: -------------------------------------------------------------------------------- 1 | @import "_ionicons-variables"; 2 | @import "_ionicons-font"; 3 | @import "_ionicons-animation"; 4 | @import "_ionicons-icons"; -------------------------------------------------------------------------------- /server/debug-entry.js: -------------------------------------------------------------------------------- 1 | // use this file with runners like node-debug 2 | // or mocha. 3 | require('babel-register'); 4 | var app = require('./server'); 5 | app.start(); 6 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/client/less/lib/font-awesome-4.3.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /server/boot/restApi.js: -------------------------------------------------------------------------------- 1 | module.exports = function mountRestApi(app) { 2 | var restApiRoot = app.get('restApiRoot'); 3 | app.use(restApiRoot, app.loopback.rest()); 4 | }; 5 | -------------------------------------------------------------------------------- /server/utils/certTypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "frontEnd": "isFrontEndCert", 3 | "backEnd": "isBackEndCert", 4 | "dataVis": "isDataVisCert", 5 | "fullStack": "isFullStackCert" 6 | } 7 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/center-block.less: -------------------------------------------------------------------------------- 1 | // Center-align a block level element 2 | 3 | .center-block() { 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calamus0427/freecodecamp.cn/dev/client/less/lib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /server/datasources.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": { 3 | "name": "db", 4 | "connector": "mongodb" 5 | }, 6 | "mail": { 7 | "name": "mail", 8 | "connector": "mail" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/text-emphasis.less: -------------------------------------------------------------------------------- 1 | // Typography 2 | 3 | .text-emphasis-variant(@color) { 4 | color: @color; 5 | a&:hover { 6 | color: darken(@color, 10%); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This is the entry point for the client code. 2 | Code that should run only on the client should be put here. 3 | 4 | NOTE(berks): For react specific stuff this should be the entry point 5 | -------------------------------------------------------------------------------- /server/middlewares/cookie-parser.js: -------------------------------------------------------------------------------- 1 | import cookieParser from 'cookie-parser'; 2 | import secrets from '../../config/secrets'; 3 | 4 | export default cookieParser.bind(cookieParser, secrets.cookieSecret); 5 | -------------------------------------------------------------------------------- /server/views/certificate/index.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .panel.panel-info 4 | .panel-heading.text-center 5 | h1 Certificate 6 | .panel-body 7 | p foo 8 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/size.less: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | .size(@width; @height) { 4 | width: @width; 5 | height: @height; 6 | } 7 | 8 | .square(@size) { 9 | .size(@size; @size); 10 | } 11 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/background-variant.less: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | 3 | .bg-variant(@color) { 4 | background-color: @color; 5 | a&:hover { 6 | background-color: darken(@color, 10%); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/opacity.less: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | .opacity(@opacity) { 4 | opacity: @opacity; 5 | // IE8 filter 6 | @opacity-ie: (@opacity * 100); 7 | filter: ~"alpha(opacity=@{opacity-ie})"; 8 | } 9 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | Everything to do with the server. 2 | 3 | One file that is not tracked here is `rev-manifest.json`. 4 | It is generated at runtime and its contents changes as the contents 5 | of client side files change. 6 | -------------------------------------------------------------------------------- /server/middlewares/global-locals.js: -------------------------------------------------------------------------------- 1 | export default function globalLocals() { 2 | return function(req, res, next) { 3 | // Make user object available in templates. 4 | res.locals.user = req.user; 5 | next(); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/text-overflow.less: -------------------------------------------------------------------------------- 1 | // Text overflow 2 | // Requires inline-block or block for proper styling 3 | 4 | .text-overflow() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /seed/create-challenge-bundle.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var getChallenges = require('./getChallenges'); 3 | 4 | var challengeSpecs = getChallenges(); 5 | 6 | fs.writeFileSync('seed/challenge-bundle.json', JSON.stringify(challengeSpecs)); 7 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/tab-focus.less: -------------------------------------------------------------------------------- 1 | // WebKit-style focus 2 | 3 | .tab-focus() { 4 | // Default 5 | outline: thin dotted; 6 | // WebKit 7 | outline: 5px auto -webkit-focus-ring-color; 8 | outline-offset: -2px; 9 | } 10 | -------------------------------------------------------------------------------- /server/views/resources/unsubscribed.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .panel.panel-info 4 | .panel-body.text-center 5 | h1 You have successfully been unsubscribed. 6 | h2 Whatever you do, keep coding! :) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /client/less/signin.less: -------------------------------------------------------------------------------- 1 | .github-login { 2 | position: relative; 3 | width: 300px; 4 | margin-left: auto; 5 | margin-right: auto; 6 | .first { 7 | position: relative; 8 | height:0; 9 | top:-30px; 10 | right:-180px; 11 | color:red 12 | } 13 | } -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/labels.less: -------------------------------------------------------------------------------- 1 | // Labels 2 | 3 | .label-variant(@color) { 4 | background-color: @color; 5 | 6 | &[href] { 7 | &:hover, 8 | &:focus { 9 | background-color: darken(@color, 10%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/resize.less: -------------------------------------------------------------------------------- 1 | // Resize anything 2 | 3 | .resizable(@direction) { 4 | resize: @direction; // Options: horizontal, vertical, both 5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` 6 | } 7 | -------------------------------------------------------------------------------- /public/css/ubuntu.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Ubuntu"; 3 | src: url(/fonts/Ubuntu-Regular.ttf) format("truetype"); 4 | } 5 | 6 | @font-face { 7 | font-family: "Ubuntu Mono"; 8 | src: url(/fonts/UbuntuMono-Regular.ttf) format("truetype"); 9 | } 10 | -------------------------------------------------------------------------------- /common/app/redux/index.js: -------------------------------------------------------------------------------- 1 | export { default as reducer } from './reducer'; 2 | export { default as actions } from './actions'; 3 | export { default as types } from './types'; 4 | 5 | import fetchUserSaga from './fetch-user-saga'; 6 | export const sagas = [ fetchUserSaga ]; 7 | -------------------------------------------------------------------------------- /server/views/certificate/back-end.jade: -------------------------------------------------------------------------------- 1 | include font 2 | #name.cert-name= name 3 | img#cert.img-abs(src='//i.imgur.com/yBKoMVP.jpg') 4 | .cert-date= date 5 | .cert-link verify this certification at: http://freecodecamp.cn/#{username}/back-end-certification 6 | include script 7 | -------------------------------------------------------------------------------- /server/utils/commit-goals.json: -------------------------------------------------------------------------------- 1 | { 2 | "frontEndCert": "Front End Development Certification", 3 | "backEndCert": "Back End Development Certification", 4 | "dataVisCert": "Data Visualisation Certification", 5 | "fullStackCert": "Full Stack Development Certification" 6 | } 7 | -------------------------------------------------------------------------------- /server/views/certificate/front-end.jade: -------------------------------------------------------------------------------- 1 | include font 2 | #name.cert-name= name 3 | img#cert.img-abs(src='//i.imgur.com/ToFZKBd.jpg') 4 | .cert-date= date 5 | .cert-link verify this certification at: http://freecodecamp.cn/#{username}/front-end-certification 6 | include script 7 | -------------------------------------------------------------------------------- /server/views/certificate/full-stack.jade: -------------------------------------------------------------------------------- 1 | include font 2 | #name.cert-name= name 3 | img#cert.img-abs(src='//i.imgur.com/Z4PgjBQ.jpg') 4 | .cert-date= date 5 | .cert-link verify this certification at: http://freecodecamp.cn/#{username}/full-stack-certification 6 | include script 7 | -------------------------------------------------------------------------------- /client/sagas/index.js: -------------------------------------------------------------------------------- 1 | import errSaga from './err-saga'; 2 | import titleSaga from './title-saga'; 3 | import localStorageSaga from './local-storage-saga'; 4 | import hardGoToSaga from './hard-go-to-saga'; 5 | 6 | export default [ errSaga, titleSaga, localStorageSaga, hardGoToSaga ]; 7 | -------------------------------------------------------------------------------- /server/views/certificate/data-vis.jade: -------------------------------------------------------------------------------- 1 | include font 2 | #name.cert-name= name 3 | img#cert.img-abs(src='//i.imgur.com/l7tIptn.jpg') 4 | .cert-date= date 5 | .cert-link verify this certification at: http://freecodecamp.cn/#{username}/data-visualization-certification 6 | include script 7 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/progress-bar.less: -------------------------------------------------------------------------------- 1 | // Progress bars 2 | 3 | .progress-bar-variant(@color) { 4 | background-color: @color; 5 | 6 | // Deprecated parent class requirement as of v3.2.0 7 | .progress-striped & { 8 | #gradient > .striped(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /common/app/sagas.js: -------------------------------------------------------------------------------- 1 | import { sagas as appSagas } from './redux'; 2 | import { sagas as hikesSagas} from './routes/Hikes/redux'; 3 | import { sagas as jobsSagas } from './routes/Jobs/redux'; 4 | 5 | export default [ 6 | ...appSagas, 7 | ...hikesSagas, 8 | ...jobsSagas 9 | ]; 10 | -------------------------------------------------------------------------------- /common/app/routes/Hikes/index.js: -------------------------------------------------------------------------------- 1 | import Hikes from './components/Hikes.jsx'; 2 | import Hike from './components/Hike.jsx'; 3 | 4 | export default { 5 | path: 'videos', 6 | component: Hikes, 7 | childRoutes: [{ 8 | path: ':dashedName', 9 | component: Hike 10 | }] 11 | }; 12 | -------------------------------------------------------------------------------- /common/app/routes/Hikes/redux/index.js: -------------------------------------------------------------------------------- 1 | export actions from './actions'; 2 | export reducer from './reducer'; 3 | export types from './types'; 4 | 5 | import answerSaga from './answer-saga'; 6 | import fetchHikesSaga from './fetch-hikes-saga'; 7 | 8 | export const sagas = [ answerSaga, fetchHikesSaga ]; 9 | -------------------------------------------------------------------------------- /server/views/resources/nonprofits-form.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | iframe(src="https://docs.google.com/forms/d/1MtQDh8E1Nvkg5jdOucS4LSyk6oMQrN8XTwmQGtgJPAQ/viewform?embedded=true", frameborder="0", marginheight="0", marginwidth="0", width='102%', height=2550, scrolling="no") Loading... 4 | -------------------------------------------------------------------------------- /public/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /server/views/wiki/show.jade: -------------------------------------------------------------------------------- 1 | extends ../layout-wide 2 | block content 3 | iframe#wikiFrame(frameborder='no') 4 | script. 5 | var lang = window.location.toString().match(/\/\w{2}\//); 6 | lang = (lang) ? lang[0] : '/en/'; 7 | $('#wikiFrame').attr('src','//freecodecamp.github.io/wiki' + lang); 8 | -------------------------------------------------------------------------------- /public/js/dashboard.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('app',[]); 2 | app.controller('dashboard',['$scope','$http', function($scope,$http){ 3 | $http.post('/dashboard').then(function(res){ 4 | var out = res.data; 5 | $scope.items = out; 6 | },function(err){ 7 | console.log(err); 8 | }) 9 | }]) 10 | -------------------------------------------------------------------------------- /common/app/provide-store.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/display-name */ 2 | import React from 'react'; 3 | import { Provider } from 'react-redux'; 4 | 5 | export default function provideStore(element, store) { 6 | return React.createElement( 7 | Provider, 8 | { store }, 9 | element 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /common/app/redux/types.js: -------------------------------------------------------------------------------- 1 | import createTypes from '../utils/create-types'; 2 | 3 | export default createTypes([ 4 | 'updateTitle', 5 | 6 | 'fetchUser', 7 | 'setUser', 8 | 9 | 'makeToast', 10 | 'updatePoints', 11 | 'handleError', 12 | // used to hit the server 13 | 'hardGoTo' 14 | ], 'app'); 15 | -------------------------------------------------------------------------------- /client/less/jobs.less: -------------------------------------------------------------------------------- 1 | .jobs-list-highlight { 2 | background-color: #ffc 3 | } 4 | 5 | a.jobs-list-highlight:hover { 6 | background-color: #ffc 7 | } 8 | 9 | .jobs-list { 10 | cursor: pointer; 11 | cursor: hand; 12 | } 13 | 14 | .jobs-checkbox-spacer input[type="checkbox"] { 15 | margin-left: -23px 16 | } 17 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/nav-divider.less: -------------------------------------------------------------------------------- 1 | // Horizontal dividers 2 | // 3 | // Dividers (basically an hr) within dropdowns and nav lists 4 | 5 | .nav-divider(@color: #e5e5e5) { 6 | height: 1px; 7 | margin: ((@line-height-computed / 2) - 1) 0; 8 | overflow: hidden; 9 | background-color: @color; 10 | } 11 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/reset-filter.less: -------------------------------------------------------------------------------- 1 | // Reset filters for IE 2 | // 3 | // When you need to remove a gradient background, do not forget to use this to reset 4 | // the IE filter for IE9 and below. 5 | 6 | .reset-filter() { 7 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); 8 | } 9 | -------------------------------------------------------------------------------- /server/views/resources/pmi-acp-agile-project-managers-form.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | iframe(src="https://docs.google.com/forms/d/1Y9wvQAx5s_puGcC-NRZpKLMgT0iZwajGMbkKlh-piGg/viewform?embedded=true", frameborder="0", marginheight="0", marginwidth="0", width='102%', height=2000, scrolling="no") Loading... 4 | -------------------------------------------------------------------------------- /server/boot/t-wiki.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | var router = app.loopback.Router(); 3 | router.get('/wiki/*', showWiki); 4 | router.get('/wiki', showWiki); 5 | 6 | app.use(router); 7 | 8 | function showWiki(req, res) { 9 | res.render('wiki/show', { title: 'Wiki | Free Code Camp' }); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /common/app/routes/index.js: -------------------------------------------------------------------------------- 1 | import Jobs from './Jobs'; 2 | import Hikes from './Hikes'; 3 | import NotFound from '../components/NotFound/index.jsx'; 4 | 5 | export default { 6 | path: '/', 7 | childRoutes: [ 8 | Jobs, 9 | Hikes, 10 | { 11 | path: '*', 12 | component: NotFound 13 | } 14 | ] 15 | }; 16 | -------------------------------------------------------------------------------- /server/middlewares/constant-headers.js: -------------------------------------------------------------------------------- 1 | export default function constantHeaders() { 2 | return function(req, res, next) { 3 | res.header('Access-Control-Allow-Origin', '*'); 4 | res.header('Access-Control-Allow-Headers', 5 | 'Origin, X-Requested-With, Content-Type, Accept' 6 | ); 7 | next(); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /server/middlewares/express-rx.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx'; 2 | 3 | // add rx methods to express 4 | export default function expressRx() { 5 | return function expressRx(req, res, next) { 6 | // render to observable stream 7 | res.render$ = Observable.fromNodeCallback(res.render, res); 8 | next(); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /server/utils/constantStrings.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitHubUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36", 3 | "frontEndChallengeId": "561add10cb82ac38a17513be", 4 | "dataVisChallengeId": "561add10cb82ac38a17513b3", 5 | "backEndChallengeId": "660add10cb82ac38a17513be" 6 | } 7 | -------------------------------------------------------------------------------- /common/app/routes/Hikes/redux/selectors.js: -------------------------------------------------------------------------------- 1 | // use this file for common selectors 2 | import { createSelector } from 'reselect'; 3 | 4 | export const getCurrentHike = createSelector( 5 | state => state.hikesApp.hikes.entities, 6 | state => state.hikesApp.currentHike, 7 | (hikesMap, currentHikeDashedName) => (hikesMap[currentHikeDashedName] || {}) 8 | ); 9 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | include partials/meta 5 | include partials/stylesheets 6 | body.top-and-bottom-margins 7 | include partials/scripts 8 | include partials/navbar 9 | .container-fluid 10 | include partials/flash 11 | block content 12 | include partials/footer 13 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/alerts.less: -------------------------------------------------------------------------------- 1 | // Alerts 2 | 3 | .alert-variant(@background; @border; @text-color) { 4 | background-color: @background; 5 | border-color: @border; 6 | color: @text-color; 7 | 8 | hr { 9 | border-top-color: darken(@border, 5%); 10 | } 11 | .alert-link { 12 | color: darken(@text-color, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/config.global.js: -------------------------------------------------------------------------------- 1 | // The path where to mount the REST API app 2 | exports.restApiRoot = '/api'; 3 | // 4 | // The URL where the browser client can access the REST API is available 5 | // Replace with a full url (including hostname) if your client is being 6 | // served from a different server than your REST API. 7 | exports.restApiUrl = exports.restApiRoot; 8 | -------------------------------------------------------------------------------- /server/views/certificate/script.jade: -------------------------------------------------------------------------------- 1 | script. 2 | (function() { 3 | var containerWidth = document.getElementById('cert').offsetWidth; 4 | var nameDiv = document.getElementById('name'); 5 | var nameWidth = nameDiv.offsetWidth; 6 | console.log(containerWidth, nameWidth); 7 | nameDiv.style.left = ((containerWidth - nameWidth) / 2) + 15; 8 | })(); 9 | -------------------------------------------------------------------------------- /server/utils/date-utils.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment-timezone'; 2 | 3 | // day count between two epochs (inclusive) 4 | export function dayCount([head, tail], timezone = 'UTC') { 5 | return Math.ceil( 6 | moment(moment(head).tz(timezone).endOf('day')).diff( 7 | moment(tail).tz(timezone).startOf('day'), 8 | 'days', 9 | true) 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /server/views/layout-react.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | if title 5 | title= title 6 | else 7 | title Free Code Camp 8 | include partials/react-stylesheets 9 | body.top-and-bottom-margins(style='overflow: hidden') 10 | .container 11 | #fcc!= markup 12 | script!= state 13 | script(src=rev('/js', 'bundle.js')) 14 | -------------------------------------------------------------------------------- /common/app/utils/create-types.js: -------------------------------------------------------------------------------- 1 | // createTypes(types: String[], prefix: String) => Object 2 | export default function createTypes(types = [], prefix = '') { 3 | if (!Array.isArray(types) || typeof prefix !== 'string') { 4 | return {}; 5 | } 6 | return types.reduce((types, type) => { 7 | types[type] = prefix + '.' + type; 8 | return types; 9 | }, {}); 10 | } 11 | -------------------------------------------------------------------------------- /common/models/User-Credential.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "userCredential", 3 | "plural": "userCredentials", 4 | "base": "UserCredential", 5 | "properties": {}, 6 | "validations": [], 7 | "relations": { 8 | "user": { 9 | "type": "belongsTo", 10 | "model": "user", 11 | "foreignKey": "userId" 12 | } 13 | }, 14 | "acls": [], 15 | "methods": [] 16 | } 17 | -------------------------------------------------------------------------------- /server/middlewares/jade-helpers.js: -------------------------------------------------------------------------------- 1 | const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint):\s/i; 2 | 3 | export default function jadeHelpers() { 4 | return function jadeHelpersMiddleware(req, res, next) { 5 | res.locals.removeOldTerms = function removeOldTerms(str = '') { 6 | return str.replace(challengesRegex, ''); 7 | }; 8 | 9 | next(); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /server/views/resources/the-fastest-web-page-on-the-internet.jade: -------------------------------------------------------------------------------- 1 | h1 This is the fastest web page on the internet. 2 | h2 This is raw HTML with no CSS and no JavaScript. 3 | h2 This is served to you lightning fast from the cloud using Node.js and NGINX. 4 | h2 Unfortunately, this doesn't do anything. 5 | h2 I guess speed isn't everything, after all. 6 | h2 7 | a(href='/') Learn to code more useful websites than this one 8 | -------------------------------------------------------------------------------- /common/app/components/Nav/links.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "content": "Map", 3 | "link": "/map" 4 | }, { 5 | "content": "Chat", 6 | "link": "//gitter.im/FreeCodeCamp/chinese", 7 | "target": "_blank" 8 | },{ 9 | "content": "Wiki", 10 | "link": "/wiki", 11 | "target": "_blank" 12 | },{ 13 | "content": "About", 14 | "link": "/about" 15 | },{ 16 | "content": "Shop", 17 | "link": "/shop", 18 | "target": "_blank" 19 | }] 20 | -------------------------------------------------------------------------------- /server/views/partials/stylesheets.jade: -------------------------------------------------------------------------------- 1 | link(rel='stylesheet', type='text/css' href='/css/lato.css') 2 | link(rel="stylesheet" type="text/css" href="/bower_components/cal-heatmap/cal-heatmap.css") 3 | link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css') 4 | link(rel='stylesheet', href='/bower_components/lightbox2/dist/css/lightbox.css') 5 | link(rel='stylesheet', href=rev('/css', 'main-f928abf444.css')) 6 | -------------------------------------------------------------------------------- /server/config.development.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | host: '127.0.0.1', 3 | sessionSecret: process.env.SESSION_SECRET, 4 | 5 | trello: { 6 | key: process.env.TRELLO_KEY, 7 | secret: process.env.TRELLO_SECRET 8 | }, 9 | 10 | blogger: { 11 | key: process.env.BLOGGER_KEY 12 | }, 13 | 14 | github: { 15 | clientID: process.env.GITHUB_ID, 16 | clientSecret: process.env.GITHUB_SECRET 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/index.js: -------------------------------------------------------------------------------- 1 | export actions from './actions'; 2 | export reducer from './reducer'; 3 | export types from './types'; 4 | 5 | import fetchJobsSaga from './fetch-jobs-saga'; 6 | import saveJobSaga from './save-job-saga'; 7 | import applyPromoSaga from './apply-promo-saga'; 8 | 9 | export formNormalizer from './jobs-form-normalizer'; 10 | 11 | export const sagas = [ fetchJobsSaga, saveJobSaga, applyPromoSaga ]; 12 | -------------------------------------------------------------------------------- /common/utils/index.js: -------------------------------------------------------------------------------- 1 | export function nameSpacedTransformer(ns, transformer) { 2 | if (!transformer) { 3 | return nameSpacedTransformer.bind(null, ns); 4 | } 5 | return (state) => { 6 | const newState = transformer(state[ns]); 7 | 8 | // nothing has changed 9 | // noop 10 | if (!newState || newState === state[ns]) { 11 | return null; 12 | } 13 | 14 | return { ...state, [ns]: newState }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /server/manifests/README.md: -------------------------------------------------------------------------------- 1 | This folder contains a list of json files representing the name 2 | of revisioned client files. It is empty due to the fact that the 3 | files are generated at runtime and their content is determined by 4 | the content of the files they are derived from. 5 | 6 | Since the build process is not exactly the same on every machine, 7 | these files are not tracked in github otherwise conflicts arise when 8 | building on our servers. 9 | -------------------------------------------------------------------------------- /server/ssl-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by nathanleniz on 6/16/15. 3 | */ 4 | 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | 8 | if (process.env.NODE_ENV === 'production') { 9 | exports.privateKey = 10 | fs.readFileSync(path.join(__dirname, 11 | '../../private/privatekey.pem')); 12 | exports.certificate = 13 | fs.readFileSync(path.join(__dirname, 14 | '../../private/certificate.pem')); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/nav-vertical-align.less: -------------------------------------------------------------------------------- 1 | // Navbar vertical align 2 | // 3 | // Vertically center elements in the navbar. 4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. 5 | 6 | .navbar-vertical-align(@element-height) { 7 | margin-top: ((@navbar-height - @element-height) / 2); 8 | margin-bottom: ((@navbar-height - @element-height) / 2); 9 | } 10 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #492c14 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/css/lato.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Lato"; 3 | src: url(/fonts/Lato-Regular.ttf) format("truetype"); 4 | } 5 | 6 | @font-face { 7 | font-family: "Lato Light"; 8 | src: url(/fonts/Lato-Light.ttf) format("truetype"); 9 | } 10 | 11 | @font-face { 12 | font-family: "Lato Bold"; 13 | src: url(/fonts/Lato-Bold.ttf) format("truetype"); 14 | } 15 | .qr_code_pc { 16 | position: fixed; 17 | top: 100px; 18 | right: 100px; 19 | } 20 | -------------------------------------------------------------------------------- /server/middlewares/validator.js: -------------------------------------------------------------------------------- 1 | import validator from 'express-validator'; 2 | 3 | export default function() { 4 | return validator({ 5 | customValidators: { 6 | matchRegex(param, regex) { 7 | return regex.test(param); 8 | }, 9 | isString(value) { 10 | return typeof value === 'string'; 11 | }, 12 | isNumber(value) { 13 | return typeof value === 'number'; 14 | } 15 | } 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/responsive-visibility.less: -------------------------------------------------------------------------------- 1 | // Responsive utilities 2 | 3 | // 4 | // More easily include all the states for responsive-utilities.less. 5 | .responsive-visibility() { 6 | display: block !important; 7 | table& { display: table; } 8 | tr& { display: table-row !important; } 9 | th&, 10 | td& { display: table-cell !important; } 11 | } 12 | 13 | .responsive-invisibility() { 14 | display: none !important; 15 | } 16 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/agile.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agile", 3 | "order": 6, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbec5bdde", 9 | "title": "Learn Agile Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Agile" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tools", 3 | "order": 19, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd3", 9 | "title": "Learn Tools Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Tools" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/types.js: -------------------------------------------------------------------------------- 1 | import createTypes from '../../../utils/create-types'; 2 | 3 | export default createTypes([ 4 | 'fetchJobs', 5 | 'fetchJobsCompleted', 6 | 7 | 'findJob', 8 | 'saveJob', 9 | 'saveForm', 10 | 11 | 'saveCompleted', 12 | 13 | 'clearForm', 14 | 15 | 'loadSavedForm', 16 | 'loadSavedFormCompleted', 17 | 18 | 'clearPromo', 19 | 'updatePromo', 20 | 'applyPromo', 21 | 'applyPromoCompleted' 22 | ], 'jobs'); 23 | -------------------------------------------------------------------------------- /seed/challenges/02-data-visualization-certification/d3.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "D3", 3 | "order": 16, 4 | "isComingSoon": true, 5 | "time": "5 hours", 6 | "helpRoom": "chinese", 7 | "challenges": [ 8 | { 9 | "id": "bd7158d8c423ede2aab5bdee", 10 | "title": "Learn D3 Challenges", 11 | "description": [], 12 | "challengeSeed": [], 13 | "tests": [], 14 | "type": "waypoint", 15 | "challengeType": 3 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/devops.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DevOps", 3 | "order": 16, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd5", 9 | "title": "Learn DevOps Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "DevOps" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /server/middlewares/sessions.js: -------------------------------------------------------------------------------- 1 | import session from 'express-session'; 2 | import MongoStoreFactory from 'connect-mongo'; 3 | import secrets from '../../config/secrets'; 4 | 5 | const MongoStore = MongoStoreFactory(session); 6 | 7 | export default function sessionsMiddleware() { 8 | return session({ 9 | resave: true, 10 | saveUninitialized: true, 11 | secret: secrets.sessionSecret, 12 | store: new MongoStore({ url: secrets.db }) 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /seed/challenges/02-data-visualization-certification/react.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "React", 3 | "order": 14, 4 | "isComingSoon": true, 5 | "time": "5 hours", 6 | "helpRoom": "chinese", 7 | "challenges": [ 8 | { 9 | "id": "bd7158d8c423ede3aeb5bdee", 10 | "title": "Learn React Challenges", 11 | "description": [], 12 | "challengeSeed": [], 13 | "tests": [], 14 | "type": "waypoint", 15 | "challengeType": 3 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/02-data-visualization-certification/sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sass", 3 | "order": 13, 4 | "isComingSoon": true, 5 | "time": "5 hours", 6 | "helpRoom": "chinese", 7 | "challenges": [ 8 | { 9 | "id": "bd7158d8c423ede2aeb5bdee", 10 | "title": "Learn Sass Challenges", 11 | "description": [], 12 | "challengeSeed": [], 13 | "tests": [], 14 | "type": "waypoint", 15 | "challengeType": 3 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /server/datasources.local.js: -------------------------------------------------------------------------------- 1 | var secrets = require('../config/secrets'); 2 | 3 | module.exports = { 4 | db: { 5 | connector: 'mongodb', 6 | connectionTimeout: 10000, 7 | url: secrets.db 8 | }, 9 | mail: { 10 | connector: 'mail', 11 | transports: [{ 12 | type: 'smtp', 13 | service: 'Mandrill', 14 | auth: { 15 | user: secrets.mandrill.user, 16 | pass: secrets.mandrill.password 17 | } 18 | }] 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/statistics.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Statistics", 3 | "order": 18, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd4", 9 | "title": "Learn Statistics Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Statistics" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/gamification.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gamification", 3 | "order": 12, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd9", 9 | "title": "Learn Gamification Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Gamification" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/visual-design.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Visual Design", 3 | "order": 21, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd1", 9 | "title": "Learn Visual Design Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Visual Design" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/accessibility.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Accessibility", 3 | "order": 5.5, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bddd", 9 | "title": "Learn Accessibility Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Accessibility" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/computer-science.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Computer Science", 3 | "order": 7, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bddc", 9 | "title": "Learn Computer Science Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Computer Science" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/game-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Game Development", 3 | "order": 10, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd0", 9 | "title": "Learn Game Development Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Game Development" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/machine-learning.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Machine Learning", 3 | "order": 13, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd8", 9 | "title": "Learn Machine Learning Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Machine Learning" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /server/config.local.js: -------------------------------------------------------------------------------- 1 | var globalConfig = require('../common/config.global'); 2 | 3 | module.exports = { 4 | restApiRoot: globalConfig.restApi, 5 | sessionSecret: process.env.SESSION_SECRET, 6 | 7 | trello: { 8 | key: process.env.TRELLO_KEY, 9 | secret: process.env.TRELLO_SECRET 10 | }, 11 | 12 | blogger: { 13 | key: process.env.BLOGGER_KEY 14 | }, 15 | 16 | github: { 17 | clientID: process.env.GITHUB_ID, 18 | clientSecret: process.env.GITHUB_SECRET 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/data-visualization.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Data Visualization", 3 | "order": 8, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdde", 9 | "title": "Learn Data Visualization Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Data Visualization" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /server/views/layout-wide.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | include partials/meta 5 | include partials/stylesheets 6 | if showAside 7 | body.map-aside-body 8 | include partials/scripts 9 | block content 10 | else 11 | body.no-top-and-bottom-margins.full-screen-body-background 12 | include partials/scripts 13 | include partials/navbar 14 | include partials/flash 15 | block content 16 | include partials/footer 17 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/math-for-programmers.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Math for Programmers", 3 | "order": 14, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd7", 9 | "title": "Learn Math for Programmers Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Math for Programmers" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /client/less/chat.less: -------------------------------------------------------------------------------- 1 | .chat-embed-main-title { 2 | display: flex; 3 | flex-grow: 1; 4 | padding-left: 31px; 5 | padding-top: 7px; 6 | } 7 | 8 | .gitter-chat-embed { 9 | z-index: 100; 10 | position: fixed; 11 | 12 | top: 0; 13 | left: 60%; 14 | bottom: 0; 15 | right: 0; 16 | 17 | display: flex; 18 | flex-direction: row; 19 | transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7); 20 | } 21 | 22 | .gitter-chat-embed.is-collapsed:not(.is-loading) { 23 | transform: translateX(110%); 24 | } 25 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/user-experience-design.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "User Experience Design", 3 | "order": 20, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd2", 9 | "title": "Learn User Experience Design Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "User Experience Design" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /common/app/routes/Hikes/redux/types.js: -------------------------------------------------------------------------------- 1 | import createTypes from '../../../utils/create-types'; 2 | 3 | export default createTypes([ 4 | 'fetchHikes', 5 | 'fetchHikesCompleted', 6 | 'resetHike', 7 | 8 | 'toggleQuestionView', 9 | 'grabQuestion', 10 | 'releaseQuestion', 11 | 'moveQuestion', 12 | 13 | 'answerQuestion', 14 | 15 | 'startShake', 16 | 'endShake', 17 | 18 | 'primeNextQuestion', 19 | 'goToNextQuestion', 20 | 'transitionHike', 21 | 22 | 'hikeCompleted', 23 | 'goToNextHike' 24 | ], 'videos'); 25 | -------------------------------------------------------------------------------- /common/models/User-Identity.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "userIdentity", 3 | "plural": "userIdentities", 4 | "base": "UserIdentity", 5 | "properties": {}, 6 | "validations": [], 7 | "relations": { 8 | "user": { 9 | "type": "belongsTo", 10 | "model": "user", 11 | "foreignKey": "userId" 12 | } 13 | }, 14 | "acls": [ 15 | { 16 | "accessType": "*", 17 | "principalType": "ROLE", 18 | "principalId": "$everyone", 19 | "permission": "DENY" 20 | } 21 | ], 22 | "methods": [] 23 | } 24 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/mobile-javascript-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mobile JavaScript Development", 3 | "order": 15, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdd6", 9 | "title": "Learn Mobile JavaScript Development Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Mobile JavaScript Development" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /client/sagas/title-saga.js: -------------------------------------------------------------------------------- 1 | // (doc: Object) => 2 | // () => 3 | // (next: (action: Action) => Object) => 4 | // titleSage(action: Action) => Object|Void 5 | export default ({ doc }) => ({ getState }) => next => { 6 | return function titleSage(action) { 7 | // get next state 8 | const result = next(action); 9 | if (action.type !== 'app.updateTitle') { 10 | return result; 11 | } 12 | const state = getState(); 13 | const newTitle = state.app.title; 14 | doc.title = newTitle; 15 | return result; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/embedded-and-internet-of-things.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Embedded and Internet of Things", 3 | "order": 9, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441eddfbeb5bdda", 9 | "title": "Learn Embedded and Internet of Things Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Embedded and Internet of Things" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /seed/challenges/04-video-challenges/software-engineering-principles.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Software Engineering Principles", 3 | "order": 17, 4 | "time": "0 hours", 5 | "isComingSoon": true, 6 | "challenges": [ 7 | { 8 | "id": "bd7128d8c441ecdfbeb5bdd5", 9 | "title": "Learn Software Engineering Principles Challenges", 10 | "description": [], 11 | "challengeSeed": [], 12 | "tests": [], 13 | "type": "hike", 14 | "challengeType": 6, 15 | "nameEs": "Software Engineering Principles" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/pagination.less: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | .pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) { 4 | > li { 5 | > a, 6 | > span { 7 | padding: @padding-vertical @padding-horizontal; 8 | font-size: @font-size; 9 | } 10 | &:first-child { 11 | > a, 12 | > span { 13 | .border-left-radius(@border-radius); 14 | } 15 | } 16 | &:last-child { 17 | > a, 18 | > span { 19 | .border-right-radius(@border-radius); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/json/cats.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "imageLink": "/images/funny-cat.jpg", 5 | "codeNames": [ 6 | "Juggernaut", 7 | "Mrs. Wallace", 8 | "Buttercup" 9 | ] 10 | }, 11 | { 12 | "id": 1, 13 | "imageLink": "/images/grumpy-cat.jpg", 14 | "codeNames": [ 15 | "Oscar", 16 | "Scrooge", 17 | "Tyrion" 18 | ] 19 | }, 20 | { 21 | "id": 2, 22 | "imageLink": "/images/mischievous-cat.jpg", 23 | "codeNames": [ 24 | "The Doctor", 25 | "Loki", 26 | "Joker" 27 | ] 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/border-radius.less: -------------------------------------------------------------------------------- 1 | // Single side border-radius 2 | 3 | .border-top-radius(@radius) { 4 | border-top-right-radius: @radius; 5 | border-top-left-radius: @radius; 6 | } 7 | .border-right-radius(@radius) { 8 | border-bottom-right-radius: @radius; 9 | border-top-right-radius: @radius; 10 | } 11 | .border-bottom-radius(@radius) { 12 | border-bottom-right-radius: @radius; 13 | border-bottom-left-radius: @radius; 14 | } 15 | .border-left-radius(@radius) { 16 | border-bottom-left-radius: @radius; 17 | border-top-left-radius: @radius; 18 | } 19 | -------------------------------------------------------------------------------- /common/app/components/NotFound/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const win = typeof window !== 'undefined' ? window : {}; 4 | 5 | function goToServer(path) { 6 | win.location = '/' + path; 7 | } 8 | 9 | export default class extends React.Component { 10 | static displayName = 'NotFound'; 11 | 12 | static propTypes = { 13 | params: PropTypes.object 14 | }; 15 | 16 | componentWillMount() { 17 | goToServer(this.props.params.splat); 18 | } 19 | 20 | componentDidMount() { 21 | } 22 | 23 | render() { 24 | return ; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/app/create-reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { reducer as formReducer } from 'redux-form'; 3 | 4 | import { reducer as app } from './redux'; 5 | import { reducer as hikesApp } from './routes/Hikes/redux'; 6 | import { 7 | reducer as jobsApp, 8 | formNormalizer as jobsNormalizer 9 | } from './routes/Jobs/redux'; 10 | 11 | export default function createReducer(sideReducers = {}) { 12 | return combineReducers({ 13 | ...sideReducers, 14 | app, 15 | hikesApp, 16 | jobsApp, 17 | form: formReducer.normalize(jobsNormalizer) 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /server/boot/a-services.js: -------------------------------------------------------------------------------- 1 | import Fetchr from 'fetchr'; 2 | import getHikesService from '../services/hikes'; 3 | import getJobServices from '../services/job'; 4 | import getUserServices from '../services/user'; 5 | 6 | export default function bootServices(app) { 7 | const hikesService = getHikesService(app); 8 | const jobServices = getJobServices(app); 9 | const userServices = getUserServices(app); 10 | 11 | Fetchr.registerFetcher(hikesService); 12 | Fetchr.registerFetcher(jobServices); 13 | Fetchr.registerFetcher(userServices); 14 | app.use('/services', Fetchr.middleware()); 15 | } 16 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /server/views/partials/challenge-footer.jade: -------------------------------------------------------------------------------- 1 | script(src=rev('/js', 'vendor-challenges.js')) 2 | script(src=rev('/js', 'commonFramework.js')) 3 | script. 4 | if (typeof localStorage !== 'undefined') { 5 | localStorage.setItem('currentDashedName', typeof common !== 'undefined' && common.dashedName || ''); 6 | } 7 | var common = window.common || { init: [] }; 8 | common.helpRoom = !{JSON.stringify(helpRoom)}; 9 | document.addEventListener('gitter-sidecar-ready', function(e) { 10 | if (window.main) { 11 | window.main.chat.createHelpChat(common.helpRoom, '#challenge-help-btn'); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 注意:这里只处理 FreeCodeCamp.cn 的相关问题,不处理 w3cschool 的任何问题 2 | ## 注意:请不要乱开无意义的 Issue,违者直接加黑名单 3 | ## 注意:Issue 是用来处理代码库中的 bug,不是用来提问题的。完成挑战过程中遇到的代码问题请在[gitter 讨论组](https://gitter.im/FreeCodeCamp/chinese) 或者 QQ 群 526289580 中讨论 (QQ 群验证答案: ___**freeCodeCamp**___ 注意大小写) 4 | 5 | #### FreeCodeCamp.cn Issue 模板 6 | 如何使用本模板: 7 | * 尽可能多填些相关信息,方便管理员参考 8 | * 对于无法给出的内容,请删除掉那一行 9 | 10 | # 以上内容请在提交 Issue 之前删除 11 | 12 | #### 浏览器信息 13 | * 浏览器名称,版本号 14 | * 操作系统 15 | 16 | #### 问题描述 17 | * 问题是在什么时候,什么情况下遇到的 18 | * 简要说明如何重现 19 | 20 | #### 你的代码 21 | 22 | ``` 23 | 如果和代码相关,请在这里粘贴你的全部代码 24 | ``` 25 | 26 | #### 截图(如果有) 27 | -------------------------------------------------------------------------------- /common/app/components/Flash/Queue.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Alert } from 'react-bootstrap'; 3 | 4 | export default React.createClass({ 5 | displayName: 'FlashQueue', 6 | 7 | propTypes: { 8 | messages: PropTypes.array 9 | }, 10 | 11 | renderMessages(messages) { 12 | return messages.map(() => { 13 | return ( 14 | 15 | ); 16 | }); 17 | }, 18 | 19 | render() { 20 | const { messages = [] } = this.props; 21 | return ( 22 |
23 | { this.renderMessages(messages) } 24 |
25 | ); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | MONGOHQ_URL='mongodb://localhost:27017/freecodecamp' 2 | 3 | FACEBOOK_ID=stuff 4 | FACEBOOK_SECRET=stuff 5 | 6 | GITHUB_ID=stuff 7 | GITHUB_SECRET=stuff 8 | 9 | GOOGLE_ID=stuff 10 | GOOGLE_SECRET=stuff 11 | 12 | LINKEDIN_ID=stuff 13 | LINKEDIN_SECRET=stuff 14 | 15 | MANDRILL_PASSWORD=stuff 16 | MANDRILL_USER=stuff 17 | 18 | TRELLO_KEY=stuff 19 | TRELLO_SECRET=stuff 20 | 21 | TWITTER_KEY=stuff 22 | TWITTER_SECRET=stuff 23 | TWITTER_TOKEN=stuff 24 | TWITTER_TOKEN_SECRET=stuff 25 | 26 | BLOGGER_KEY=stuff 27 | 28 | SESSION_SECRET=secretstuff 29 | COOKIE_SECRET='this is a secret' 30 | 31 | PEER=stuff 32 | DEBUG=true 33 | -------------------------------------------------------------------------------- /client/sagas/err-saga.js: -------------------------------------------------------------------------------- 1 | // () => 2 | // (store: Store) => 3 | // (next: (action: Action) => Object) => 4 | // errSaga(action: Action) => Object|Void 5 | export default () => ({ dispatch }) => next => { 6 | return function errorSaga(action) { 7 | const result = next(action); 8 | if (!action.error) { return result; } 9 | 10 | console.error(action.error); 11 | return dispatch({ 12 | type: 'app.makeToast', 13 | payload: { 14 | type: 'error', 15 | title: 'Oops, something went wrong', 16 | message: 'Something went wrong, please try again later' 17 | } 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/utils.js: -------------------------------------------------------------------------------- 1 | const defaults = { 2 | 'string': { 3 | value: '', 4 | valid: false, 5 | pristine: true, 6 | type: 'string' 7 | }, 8 | bool: { 9 | value: false, 10 | type: 'boolean' 11 | } 12 | }; 13 | 14 | export function getDefaults(type, value) { 15 | if (!type) { 16 | return defaults['string']; 17 | } 18 | if (value) { 19 | return Object.assign({}, defaults[type], { value }); 20 | } 21 | return Object.assign({}, defaults[type]); 22 | } 23 | 24 | export function isJobValid(job) { 25 | return job && 26 | !job.isFilled && 27 | job.isApproved && 28 | job.isPaid; 29 | } 30 | -------------------------------------------------------------------------------- /client/sagas/hard-go-to-saga.js: -------------------------------------------------------------------------------- 1 | import { hardGoTo } from '../../common/app/redux/types'; 2 | 3 | const loc = typeof window !== 'undefined' ? 4 | window.location : 5 | {}; 6 | 7 | export default () => ({ dispatch }) => next => { 8 | return function hardGoToSaga(action) { 9 | const result = next(action); 10 | if (action.type !== hardGoTo) { 11 | return result; 12 | } 13 | 14 | if (!loc.pathname) { 15 | dispatch({ 16 | type: 'app.error', 17 | error: new Error('no location object found') 18 | }); 19 | } 20 | 21 | loc.pathname = action.payload || '/map'; 22 | return null; 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "restApiRoot": "/api", 3 | "host": "127.0.0.1", 4 | "port": 3000, 5 | "legacyExplorer": false, 6 | "remoting": { 7 | "context": { 8 | "enableHttpContext": false 9 | }, 10 | "rest": { 11 | "normalizeHttpPath": false, 12 | "xml": false 13 | }, 14 | "json": { 15 | "strict": false, 16 | "limit": "100kb" 17 | }, 18 | "urlencoded": { 19 | "extended": true, 20 | "limit": "100kb" 21 | }, 22 | "cors": { 23 | "origin": true, 24 | "credentials": true 25 | }, 26 | "errorHandler": { 27 | "disableStackTrace": false 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/middlewares/keymetrics.js: -------------------------------------------------------------------------------- 1 | import pmx from 'pmx'; 2 | 3 | export default function keymetrics() { 4 | if (process.env.NODE_ENV !== 'production') { 5 | return (err, req, res, next) => next(err); 6 | } 7 | return (err, req, res, next) => { 8 | if (res.statusCode < 400) { res.statusCode = 500; } 9 | 10 | err.url = req.url; 11 | err.component = req.url; 12 | err.action = req.method; 13 | err.params = req.body; 14 | err.session = req.session; 15 | err.username = req.user ? req.user.username : 'anonymous'; 16 | err.userId = req.user ? req.user.id : 'anonymous'; 17 | 18 | return next(pmx.notify(err)); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/panels.less: -------------------------------------------------------------------------------- 1 | // Panels 2 | 3 | .panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) { 4 | border-color: @border; 5 | 6 | & > .panel-heading { 7 | color: @heading-text-color; 8 | background-color: @heading-bg-color; 9 | border-color: @heading-border; 10 | 11 | + .panel-collapse > .panel-body { 12 | border-top-color: @border; 13 | } 14 | .badge { 15 | color: @heading-bg-color; 16 | background-color: @heading-text-color; 17 | } 18 | } 19 | & > .panel-footer { 20 | + .panel-collapse > .panel-body { 21 | border-bottom-color: @border; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @well-bg; 12 | border: 1px solid @well-border; 13 | border-radius: @border-radius-base; 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: @border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: @border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /client/commonFramework/add-loop-protect.js: -------------------------------------------------------------------------------- 1 | window.common = (function(global) { 2 | const { 3 | loopProtect, 4 | common = { init: [] } 5 | } = global; 6 | 7 | loopProtect.hit = function hit(line) { 8 | var err = 'Error: Exiting potential infinite loop at line ' + 9 | line + 10 | '. To disable loop protection, write: \n\\/\\/ noprotect\nas the first' + 11 | 'line. Beware that if you do have an infinite loop in your code' + 12 | 'this will crash your browser.'; 13 | console.error(err); 14 | }; 15 | 16 | common.addLoopProtect = function addLoopProtect(code = '') { 17 | return loopProtect(code); 18 | }; 19 | 20 | return common; 21 | })(window); 22 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/list-group.less: -------------------------------------------------------------------------------- 1 | // List Groups 2 | 3 | .list-group-item-variant(@state; @background; @color) { 4 | .list-group-item-@{state} { 5 | color: @color; 6 | background-color: @background; 7 | 8 | a& { 9 | color: @color; 10 | 11 | .list-group-item-heading { 12 | color: inherit; 13 | } 14 | 15 | &:hover, 16 | &:focus { 17 | color: @color; 18 | background-color: darken(@background, 5%); 19 | } 20 | &.active, 21 | &.active:hover, 22 | &.active:focus { 23 | color: #fff; 24 | background-color: @color; 25 | border-color: @color; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/views/account/forgot.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .col-sm-6.col-sm-offset-3 4 | form(method='POST', action="/forgot") 5 | h2.text-center 密码重置 6 | input(type='hidden', name='_csrf', value=_csrf) 7 | .form-group 8 | p.large-p 我们会把密码重置链接发送到你的邮箱里 9 | input.form-control.input-lg(type='email', name='email', id='email', placeholder='邮箱', autofocus=true required) 10 | .form-group 11 | button.btn.btn-primary.btn-lg.btn-block(type='submit') 12 | i.fa.fa-key 13 | | 重置密码 14 | br 15 | br 16 | br 17 | br 18 | br 19 | br 20 | br 21 | br 22 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/hide-text.less: -------------------------------------------------------------------------------- 1 | // CSS image replacement 2 | // 3 | // Heads up! v3 launched with with only `.hide-text()`, but per our pattern for 4 | // mixins being reused as classes with the same name, this doesn't hold up. As 5 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. 6 | // 7 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 8 | 9 | // Deprecated as of v3.0.1 (will be removed in v4) 10 | .hide-text() { 11 | font: ~"0/0" a; 12 | color: transparent; 13 | text-shadow: none; 14 | background-color: transparent; 15 | border: 0; 16 | } 17 | 18 | // New mixin to use as of v3.0.1 19 | .text-hide() { 20 | .hide-text(); 21 | } 22 | -------------------------------------------------------------------------------- /client/commonFramework/get-iframe.js: -------------------------------------------------------------------------------- 1 | window.common = (function(global) { 2 | const { 3 | common = { init: [] }, 4 | document: doc 5 | } = global; 6 | 7 | common.getIframe = function getIframe(id = 'preview') { 8 | let previewFrame = doc.getElementById(id); 9 | 10 | // create and append a hidden preview frame 11 | if (!previewFrame) { 12 | previewFrame = doc.createElement('iframe'); 13 | previewFrame.id = id; 14 | previewFrame.setAttribute('style', 'display: none'); 15 | doc.body.appendChild(previewFrame); 16 | } 17 | 18 | return previewFrame.contentDocument || 19 | previewFrame.contentWindow.document; 20 | }; 21 | 22 | return common; 23 | })(window); 24 | -------------------------------------------------------------------------------- /server/views/feed.jade: -------------------------------------------------------------------------------- 1 | doctype xml 2 | rss(version="2.0", xmlns:atom="http://www.w3.org/2005/Atom") 3 | channel 4 | title= title 5 | link= url 6 | description= description 7 | atom:link(href="http://www.freecodecamp.com/news/feed", rel="self", type="application/rss+xml") 8 | for post in FeedPosts 9 | if (post.link).match(/https*:\/\/\w+(\.\w+)*/) 10 | item 11 | title #{ post.headline.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') } 12 | description 13 | pubDate= (new Date(post.timePosted)).toUTCString() 14 | link= post.link 15 | guid(isPermaLink="false")= post.link 16 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | // Clearfix 2 | // 3 | // For modern browsers 4 | // 1. The space content is one way to avoid an Opera bug when the 5 | // contenteditable attribute is included anywhere else in the document. 6 | // Otherwise it causes space to appear at the top and bottom of elements 7 | // that are clearfixed. 8 | // 2. The use of `table` rather than `block` is only necessary if using 9 | // `:before` to contain the top-margins of child elements. 10 | // 11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/ 12 | 13 | .clearfix() { 14 | &:before, 15 | &:after { 16 | content: " "; // 1 17 | display: table; // 2 18 | } 19 | &:after { 20 | clear: both; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: @breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: @breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/views/commit/directory.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | h1.text-center Commit to one of these nonprofits 4 | hr 5 | .row 6 | .col-xs-12.col-sm-10.col-sm-offset-1 7 | for nonprofit in nonprofits 8 | .col-xs-12.col-sm-6.col-md-4.height-400 9 | .text-center 10 | h2= nonprofit.displayName 11 | img.testimonial-image.img-responsive.img-center(src=nonprofit.imgUrl) 12 | .button-spacer 13 | a.text-center(href='/commit?nonprofit=#{nonprofit.name}') Commit to #{nonprofit.displayName} 14 | p= nonprofit.description 15 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /server/utils/middleware.js: -------------------------------------------------------------------------------- 1 | export function ifNoUserRedirectTo(url, message, type = 'errors') { 2 | return function(req, res, next) { 3 | const { path } = req; 4 | if (req.user) { 5 | return next(); 6 | } 7 | 8 | req.flash(type, { 9 | msg: message || `You must be signed to go to ${path}` 10 | }); 11 | 12 | return res.redirect(url); 13 | }; 14 | } 15 | 16 | export function ifNoUserSend(sendThis) { 17 | return function(req, res, next) { 18 | if (req.user) { 19 | return next(); 20 | } 21 | return res.status(200).send(sendThis); 22 | }; 23 | } 24 | 25 | export function ifNoUser401(req, res, next) { 26 | if (req.user) { 27 | return next(); 28 | } 29 | return res.status(401).end(); 30 | } 31 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/responsive-embed.less: -------------------------------------------------------------------------------- 1 | // Embeds responsive 2 | // 3 | // Credit: Nicolas Gallagher and SUIT CSS. 4 | 5 | .embed-responsive { 6 | position: relative; 7 | display: block; 8 | height: 0; 9 | padding: 0; 10 | overflow: hidden; 11 | 12 | .embed-responsive-item, 13 | iframe, 14 | embed, 15 | object, 16 | video { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | bottom: 0; 21 | height: 100%; 22 | width: 100%; 23 | border: 0; 24 | } 25 | 26 | // Modifier class for 16:9 aspect ratio 27 | &.embed-responsive-16by9 { 28 | padding-bottom: 56.25%; 29 | } 30 | 31 | // Modifier class for 4:3 aspect ratio 32 | &.embed-responsive-4by3 { 33 | padding-bottom: 75%; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freecodecamp", 3 | "version": "0.0.0", 4 | "homepage": "http://freecodecamp.cn", 5 | "private": true, 6 | "ignore": [ 7 | "**/.*", 8 | "node_modules", 9 | "bower_components", 10 | "public/bower_components", 11 | "test", 12 | "tests" 13 | ], 14 | "dependencies": { 15 | "d3": "~3.5.5", 16 | "jquery": "~2.1.4", 17 | "cal-heatmap": "~3.5.2", 18 | "bootstrap": "~3.3.4", 19 | "font-awesome": "~4.5.0", 20 | "moment": "~2.10.2", 21 | "moment-timezone": "~0.5.0", 22 | "jshint": "~2.9.0", 23 | "lightbox2": "~2.8.1", 24 | "rxjs": "~4.0.6", 25 | "CodeMirror": "~5.8.0", 26 | "chai": "~3.4.1", 27 | "clipboard": "~1.5.5", 28 | "mousetrap": "~1.5.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | .floo 11 | .flooignore 12 | builtAssets/ 13 | pm2.js 14 | *.env 15 | pids 16 | logs 17 | results 18 | tmp 19 | 20 | npm-debug.log 21 | node_modules 22 | .idea 23 | *.iml 24 | .git 25 | .DS_Store 26 | Thumbs.db 27 | bower_components 28 | bundle.js 29 | coverage 30 | .remote-sync.json 31 | 32 | server/*.bundle.js 33 | public/js/bundle* 34 | 35 | *.map 36 | 37 | // revision manifest 38 | server/rev-manifest.json 39 | server/manifests/* 40 | !server/manifests/README.md 41 | 42 | public/js/main* 43 | // public/js/commonFramework* 44 | public/js/sandbox* 45 | public/js/iFrameScripts* 46 | public/js/plugin* 47 | public/js/vendor* 48 | public/js/faux* 49 | 50 | 51 | server/rev-manifest.json 52 | -------------------------------------------------------------------------------- /server/utils/constants.js: -------------------------------------------------------------------------------- 1 | exports.blacklistedUsernames = [ 2 | 'bonfire', 3 | 'account', 4 | 'user', 5 | 'challenge', 6 | 'challenges', 7 | 'completed-challenge', 8 | 'completed-zipline-or-basejump', 9 | 'completed-bonfire', 10 | 'map', 11 | 'learn-to-code', 12 | 'about', 13 | 'api', 14 | 'explorer', 15 | 'field-guide', 16 | 'completed-field-guide', 17 | 'jobs', 18 | 'nonprofits', 19 | 'api', 20 | 'sitemap.xml', 21 | 'get-help', 22 | 'chat', 23 | 'twitch', 24 | 'get-pai', 25 | 'get-help', 26 | 'nonprofits', 27 | 'nonproifts-form', 28 | 'jobs-form', 29 | 'unsubscribe', 30 | 'unsubscribed', 31 | 'cats.json', 32 | 'agile', 33 | 'privacy', 34 | 'stories', 35 | 'signin', 36 | 'signout', 37 | 'forgot', 38 | 'reset' 39 | ]; 40 | -------------------------------------------------------------------------------- /common/app/utils/render.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import { Disposable, Observable } from 'rx'; 3 | import ProfessorContext from './Professor-Context'; 4 | 5 | export default function render(Component, DOMContainer) { 6 | let ContextedComponent; 7 | try { 8 | ContextedComponent = ProfessorContext.wrap(Component); 9 | } catch (e) { 10 | return Observable.throw(e); 11 | } 12 | 13 | return Observable.create(observer => { 14 | try { 15 | ReactDOM.render(ContextedComponent, DOMContainer, function() { 16 | observer.onNext(this); 17 | }); 18 | } catch (e) { 19 | return observer.onError(e); 20 | } 21 | 22 | return Disposable.create(() => { 23 | return ReactDOM.unmountComponentAtNode(DOMContainer); 24 | }); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /server/views/resources/labs.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | h2.text-center Projects Created by Free Code Camp Campers 4 | hr 5 | .row 6 | .col-xs-12.col-sm-10.col-sm-offset-1 7 | for project in projects 8 | .spacer 9 | .row 10 | .col-xs-12.col-sm-3 11 | img.img-responsive(src=project.image) 12 | .col-xs-12.col-sm-9.negative-15 13 | h3 14 | a(href=project.url) #{project.name} 15 | h4 by  16 | a(href='https://freecodecamp.com/' + project.camper) #{project.camper} 17 | p= project.description 18 | -------------------------------------------------------------------------------- /server/views/commit/pledge.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .panel.panel-info 4 | .panel-body 5 | h3.text-center You've committed! 6 | .row 7 | .col-xs-12.col-sm-6.col-sm-offset-3 8 | p Congratulations, you have committed to giving 9 | span(style='text-transform: capitalize') #{nonprofit} 10 | | #{amount} dollars a month until you have reached your goal 11 | | of completing your #{goal} 12 | .row 13 | .col-xs-12.col-sm-6.col-sm-offset-3 14 | img.img-responsive(src='//i.imgur.com/U1CyEuA.jpg' alt="Girl Develop It participants coding at tables.") 15 | p Girl Develop It is a nonprofit that provides in-person classes for women to learn to code. 16 | -------------------------------------------------------------------------------- /server/boot/home.js: -------------------------------------------------------------------------------- 1 | import { defaultProfileImage } from '../../common/utils/constantStrings.json'; 2 | 3 | const message = 4 | 'Learn to Code and Help Nonprofits'; 5 | 6 | module.exports = function(app) { 7 | var router = app.loopback.Router(); 8 | router.get('/', addDefaultImage, index); 9 | 10 | app.use(router); 11 | 12 | function addDefaultImage(req, res, next) { 13 | if (!req.user || req.user.picture) { 14 | return next(); 15 | } 16 | req.user.picture = defaultProfileImage; 17 | return req.user.save(function(err) { 18 | if (err) { return next(err); } 19 | return next(); 20 | }); 21 | } 22 | 23 | function index(req, res) { 24 | if (req.user) { 25 | return res.redirect('/challenges/current-challenge'); 26 | } 27 | return res.render('home', { title: message }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /server/views/account/reset.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | .col-sm-8.col-sm-offset-2.jumbotron 5 | form(action='/reset-password?access_token=#{accessToken}', method='POST') 6 | h1 Reset Password 7 | input(type='hidden', name='_csrf', value=_csrf) 8 | .form-group 9 | label(for='password') 新密码 10 | input.form-control(type='password', name='password', value='', placeholder='新密码', autofocus=true) 11 | .form-group 12 | label(for='confirm') 确认密码 13 | input.form-control(type='password', name='confirm', value='', placeholder='确认密码') 14 | .form-group 15 | button.btn.btn-primary.btn-reset(type='submit') 16 | i.fa.fa-keyboard-o 17 | | 更改密码 18 | br 19 | br 20 | br 21 | br 22 | br 23 | br 24 | br 25 | br 26 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button& { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/table-row.less: -------------------------------------------------------------------------------- 1 | // Tables 2 | 3 | .table-row-variant(@state; @background) { 4 | // Exact selectors below required to override `.table-striped` and prevent 5 | // inheritance to nested tables. 6 | .table > thead > tr, 7 | .table > tbody > tr, 8 | .table > tfoot > tr { 9 | > td.@{state}, 10 | > th.@{state}, 11 | &.@{state} > td, 12 | &.@{state} > th { 13 | background-color: @background; 14 | } 15 | } 16 | 17 | // Hover states for `.table-hover` 18 | // Note: this is not available for cells or rows within `thead` or `tfoot`. 19 | .table-hover > tbody > tr { 20 | > td.@{state}:hover, 21 | > th.@{state}:hover, 22 | &.@{state}:hover > td, 23 | &:hover > .@{state}, 24 | &.@{state}:hover > th { 25 | background-color: darken(@background, 5%); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /server/services/user.js: -------------------------------------------------------------------------------- 1 | import debugFactory from 'debug'; 2 | import assign from 'object.assign'; 3 | 4 | const censor = '**********************:P********'; 5 | const debug = debugFactory('fcc:services:user'); 6 | const protectedUserFields = { 7 | id: censor, 8 | password: censor, 9 | profiles: censor 10 | }; 11 | 12 | export default function userServices() { 13 | return { 14 | name: 'user', 15 | read: (req, resource, params, config, cb) => { 16 | let { user } = req; 17 | if (user) { 18 | debug('user is signed in'); 19 | // Zalgo!!! 20 | return process.nextTick(() => { 21 | cb(null, assign({}, user.toJSON(), protectedUserFields)); 22 | }); 23 | } 24 | debug('user is not signed in'); 25 | return process.nextTick(() => { 26 | cb(null, {}); 27 | }); 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /seed/get-emails.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-process-exit */ 2 | require('dotenv').load(); 3 | var secrets = require('../config/secrets'), 4 | mongodb = require('mongodb'), 5 | MongoClient = mongodb.MongoClient; 6 | 7 | MongoClient.connect(secrets.db, function(err, database) { 8 | if (err) { 9 | throw err; 10 | } 11 | 12 | database.collection('user').aggregate([ 13 | {$match: { 'email': { $exists: true } } }, 14 | {$match: { 'email': { $ne: '' } } }, 15 | {$match: { 'email': { $ne: null } } }, 16 | {$match: { 'sendMonthlyEmail': true } }, 17 | {$match: { 'email': { $not: /(test|fake)/i } } }, 18 | {$group: { '_id': 1, 'emails': {$addToSet: '$email' } } } 19 | ], function(err, results) { 20 | if (err) { throw err; } 21 | 22 | console.log('\"email\"\n\"' + results[0].emails.join('\"\n\"') + '\"'); 23 | process.exit(0); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /server/views/partials/flash.jade: -------------------------------------------------------------------------------- 1 | .row.flashMessage 2 | .col-xs-12 3 | if (messages.errors || messages.error) 4 | .alert.alert-danger.fade.in 5 | button.close(type='button', data-dismiss='alert') 6 | span.ion-close-circled 7 | for error in (messages.errors || messages.error) 8 | div!= error.msg || error 9 | if messages.info 10 | .alert.alert-info.fade.in 11 | button.close(type='button', data-dismiss='alert') 12 | span.ion-close-circled 13 | for info in messages.info 14 | div!= info.msg 15 | if messages.success 16 | .alert.alert-success.fade.in 17 | button.close(type='button', data-dismiss='alert') 18 | span.ion-close-circled 19 | for success in messages.success 20 | div!= success.msg 21 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/media.less: -------------------------------------------------------------------------------- 1 | .media { 2 | // Proper spacing between instances of .media 3 | margin-top: 15px; 4 | 5 | &:first-child { 6 | margin-top: 0; 7 | } 8 | } 9 | 10 | .media-right, 11 | .media > .pull-right { 12 | padding-left: 10px; 13 | } 14 | 15 | .media-left, 16 | .media > .pull-left { 17 | padding-right: 10px; 18 | } 19 | 20 | .media-left, 21 | .media-right, 22 | .media-body { 23 | display: table-cell; 24 | vertical-align: top; 25 | } 26 | 27 | .media-middle { 28 | vertical-align: middle; 29 | } 30 | 31 | .media-bottom { 32 | vertical-align: bottom; 33 | } 34 | 35 | // Reset margins on headings for tighter default spacing 36 | .media-heading { 37 | margin-top: 0; 38 | margin-bottom: 5px; 39 | } 40 | 41 | // Media list variation 42 | // 43 | // Undo default ul/ol styles 44 | .media-list { 45 | padding-left: 0; 46 | list-style: none; 47 | } 48 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | visibility: hidden; 21 | 22 | &.in { display: block; visibility: visible; } 23 | tr&.in { display: table-row; } 24 | tbody&.in { display: table-row-group; } 25 | } 26 | 27 | .collapsing { 28 | position: relative; 29 | height: 0; 30 | overflow: hidden; 31 | .transition-property(~"height, visibility"); 32 | .transition-duration(.35s); 33 | .transition-timing-function(ease); 34 | } 35 | -------------------------------------------------------------------------------- /server/production-start.js: -------------------------------------------------------------------------------- 1 | // this ensures node understands the future 2 | require('babel-register'); 3 | 4 | var startTime = Date.now(); 5 | var timeoutHandler; 6 | // this is where server starts booting up 7 | var app = require('./server'); 8 | console.log('waiting for db to connect'); 9 | 10 | 11 | var onConnect = function() { 12 | console.log('db connected in %s ms', Date.now() - startTime); 13 | if (timeoutHandler) { 14 | clearTimeout(timeoutHandler); 15 | } 16 | app.start(); 17 | }; 18 | 19 | var timeoutHandler = setTimeout(function() { 20 | var message = 21 | 'db did not after ' + 22 | (Date.now() - startTime) + 23 | ' ms connect crashing hard'; 24 | 25 | console.log(message); 26 | // purposely shutdown server 27 | // pm2 should restart this in production 28 | throw new Error(message); 29 | }, 15000); 30 | 31 | app.dataSources.db.on('connected', onConnect); 32 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pm2Start.js: -------------------------------------------------------------------------------- 1 | require('dotenv').load(); 2 | var pm2 = require('pm2'); 3 | var nodemailer = require('nodemailer'); 4 | var moment = require('moment-timezone'); 5 | var _ = require('lodash'); 6 | 7 | var instances = process.env.INSTANCES || 1; 8 | var serverName = process.env.SERVER_NAME || 'server'; 9 | var maxMemory = process.env.MAX_MEMORY || '590M'; 10 | 11 | 12 | var mailReceiver = process.env.MAIL_RECEIVER || false; 13 | 14 | pm2.connect(function() { 15 | pm2.start({ 16 | name: serverName, 17 | script: 'server/production-start.js', 18 | 'exec_mode': 'cluster', 19 | instances: instances, 20 | 'max_memory_restart': maxMemory, 21 | 'NODE_ENV': 'production' 22 | }, function() { 23 | console.log( 24 | 'pm2 started %s with %s instances at %s max memory', 25 | serverName, 26 | instances, 27 | maxMemory 28 | ); 29 | pm2.disconnect(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /server/views/certificate/font.jade: -------------------------------------------------------------------------------- 1 | style. 2 | @font-face { 3 | font-family: "Sax Mono"; 4 | src: url("/fonts/saxmono.ttf") format("truetype"); 5 | } 6 | 7 | body { 8 | display: inline-block; 9 | font-family: "Sax Mono", monospace; 10 | margin: 0; 11 | position: absolute; 12 | text-align: center; 13 | } 14 | 15 | .img-abs { 16 | left 0; 17 | position: relative; 18 | top: 0; 19 | width: 2000px 20 | } 21 | 22 | .cert-name { 23 | font-size: 64px; 24 | left: 1000px; 25 | position: absolute; 26 | top: 704px; 27 | z-index: 1000; 28 | } 29 | 30 | .cert-date { 31 | font-size: 60px; 32 | left: 760px; 33 | position: absolute; 34 | top: 1004.8px; 35 | z-index: 1000; 36 | } 37 | 38 | .cert-link { 39 | font-size: 22px; 40 | left: 120px; 41 | position: absolute; 42 | top: 1488px; 43 | z-index: 1000; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /common/app/redux/reducer.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from 'redux-actions'; 2 | import types from './types'; 3 | 4 | export default handleActions( 5 | { 6 | [types.updateTitle]: (state, { payload = 'Learn To Code' }) => ({ 7 | ...state, 8 | title: payload + ' | Free Code Camp' 9 | }), 10 | 11 | [types.makeToast]: (state, { payload: toast }) => ({ 12 | ...state, 13 | toast 14 | }), 15 | 16 | [types.setUser]: (state, { payload: user }) => ({ ...state, ...user }), 17 | 18 | [types.challengeSaved]: (state, { payload: { points = 0 } }) => ({ 19 | ...state, 20 | points 21 | }), 22 | 23 | [types.updatePoints]: (state, { payload: points }) => ({ 24 | ...state, 25 | points 26 | }) 27 | }, 28 | { 29 | title: 'Learn To Code | Free Code Camp', 30 | username: null, 31 | picture: null, 32 | points: 0, 33 | isSignedIn: false 34 | } 35 | ); 36 | -------------------------------------------------------------------------------- /public/favicons/android-chrome-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/save-job-saga.js: -------------------------------------------------------------------------------- 1 | import { push } from 'react-router-redux'; 2 | import { Observable } from 'rx'; 3 | 4 | import { saveCompleted } from './actions'; 5 | import { saveJob } from './types'; 6 | 7 | import { handleError } from '../../../redux/types'; 8 | 9 | export default ({ services }) => ({ dispatch }) => next => { 10 | return function saveJobSaga(action) { 11 | const result = next(action); 12 | if (action.type !== saveJob) { 13 | return result; 14 | } 15 | const { payload: job } = action; 16 | 17 | return services.createService$({ 18 | service: 'jobs', 19 | params: { job } 20 | }) 21 | .retry(3) 22 | .flatMap(job => Observable.of( 23 | saveCompleted(job), 24 | push('/jobs/new/check-out') 25 | )) 26 | .catch(error => Observable.just({ 27 | type: handleError, 28 | error 29 | })) 30 | .doOnNext(dispatch); 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | display: block; 9 | padding: @thumbnail-padding; 10 | margin-bottom: @line-height-computed; 11 | line-height: @line-height-base; 12 | background-color: @thumbnail-bg; 13 | border: 1px solid @thumbnail-border; 14 | border-radius: @thumbnail-border-radius; 15 | .transition(border .2s ease-in-out); 16 | 17 | > img, 18 | a > img { 19 | &:extend(.img-responsive); 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | // Add a hover state for linked versions only 25 | a&:hover, 26 | a&:focus, 27 | a&.active { 28 | border-color: @link-color; 29 | } 30 | 31 | // Image captions 32 | .caption { 33 | padding: @thumbnail-caption-padding; 34 | color: @thumbnail-caption-color; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/index.js: -------------------------------------------------------------------------------- 1 | import Jobs from './components/Jobs.jsx'; 2 | import NewJob from './components/NewJob.jsx'; 3 | import Show from './components/Show.jsx'; 4 | import Preview from './components/Preview.jsx'; 5 | import JobTotal from './components/JobTotal.jsx'; 6 | import NewJobCompleted from './components/NewJobCompleted.jsx'; 7 | 8 | /* 9 | * index: /jobs list jobs 10 | * show: /jobs/:id show one job 11 | * create /jobs/new create a new job 12 | */ 13 | 14 | export default { 15 | childRoutes: [{ 16 | path: '/jobs', 17 | component: Jobs 18 | }, { 19 | path: 'jobs/new', 20 | component: NewJob 21 | }, { 22 | path: 'jobs/new/preview', 23 | component: Preview 24 | }, { 25 | path: 'jobs/new/check-out', 26 | component: JobTotal 27 | }, { 28 | path: 'jobs/new/completed', 29 | component: NewJobCompleted 30 | }, { 31 | path: 'jobs/:id', 32 | component: Show 33 | }] 34 | }; 35 | -------------------------------------------------------------------------------- /client/commonFramework/display-test-results.js: -------------------------------------------------------------------------------- 1 | window.common = (function({ $, common = { init: [] }}) { 2 | 3 | common.displayTestResults = function displayTestResults(data = []) { 4 | $('#testSuite').children().remove(); 5 | data.forEach(({ err = false, text = '' }) => { 6 | var iconClass = err ? 7 | '"ion-close-circled big-error-icon"' : 8 | '"ion-checkmark-circled big-success-icon"'; 9 | 10 | $('
').html(` 11 |
12 |
13 | 14 |
15 |
16 | ${text.split('message: ').pop().replace(/\'\);/g, '')} 17 |
18 |
19 |
20 | `) 21 | .appendTo($('#testSuite')); 22 | }); 23 | 24 | return data; 25 | }; 26 | 27 | return common; 28 | }(window)); 29 | -------------------------------------------------------------------------------- /common/app/utils/shallow-equals.js: -------------------------------------------------------------------------------- 1 | // original sourc 2 | // https://github.com/rackt/react-redux/blob/master/src/utils/shallowEqual.js 3 | // MIT license 4 | export default function shallowEqual(objA, objB) { 5 | if (objA === objB) { 6 | return true; 7 | } 8 | 9 | if ( 10 | typeof objA !== 'object' || 11 | objA === null || 12 | typeof objB !== 'object' || 13 | objB === null 14 | ) { 15 | return false; 16 | } 17 | 18 | var keysA = Object.keys(objA); 19 | var keysB = Object.keys(objB); 20 | 21 | if (keysA.length !== keysB.length) { 22 | return false; 23 | } 24 | 25 | // Test for A's keys different from B. 26 | var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB); 27 | for (var i = 0; i < keysA.length; i++) { 28 | if ( 29 | !bHasOwnProperty(keysA[i]) || 30 | objA[keysA[i]] !== objB[keysA[i]] 31 | ) { 32 | return false; 33 | } 34 | } 35 | 36 | return true; 37 | } 38 | -------------------------------------------------------------------------------- /server/services/hikes.js: -------------------------------------------------------------------------------- 1 | import debugFactory from 'debug'; 2 | import assign from 'object.assign'; 3 | 4 | const debug = debugFactory('fcc:services:hikes'); 5 | 6 | export default function hikesService(app) { 7 | const Challenge = app.models.Challenge; 8 | 9 | return { 10 | name: 'hikes', 11 | read: (req, resource, { dashedName } = {}, config, cb) => { 12 | const query = { 13 | where: { challengeType: '6' }, 14 | order: ['order ASC', 'suborder ASC' ] 15 | }; 16 | 17 | debug('dashedName', dashedName); 18 | if (dashedName) { 19 | assign(query.where, { 20 | dashedName: { like: dashedName, options: 'i' } 21 | }); 22 | } 23 | debug('query', query); 24 | Challenge.find(query, (err, hikes) => { 25 | if (err) { 26 | return cb(err); 27 | } 28 | return cb(null, hikes.map(hike => hike.toJSON())); 29 | }); 30 | } 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/components/JobNotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { LinkContainer } from 'react-router-bootstrap'; 3 | import { Button, Row, Col } from 'react-bootstrap'; 4 | 5 | export default class extends React.Component { 6 | static displayName = 'NoJobFound'; 7 | 8 | shouldComponentUpdate() { 9 | return false; 10 | } 11 | 12 | render() { 13 | return ( 14 |
15 | 16 | 19 |
20 | No job found... 21 | 22 | 28 | 29 |
30 | 31 |
32 |
33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/boot/explorer.js: -------------------------------------------------------------------------------- 1 | module.exports = function mountLoopBackExplorer(app) { 2 | if (process.env.NODE_ENV === 'production') { 3 | return; 4 | } 5 | var explorer; 6 | try { 7 | explorer = require('loopback-component-explorer'); 8 | } catch (err) { 9 | // Print the message only when the app was started via `app.listen()`. 10 | // Do not print any message when the project is used as a component. 11 | app.once('started', function() { 12 | console.log( 13 | 'Run `npm install loopback-component-explorer` to enable ' + 14 | 'the LoopBack explorer' 15 | ); 16 | }); 17 | return; 18 | } 19 | 20 | var restApiRoot = app.get('restApiRoot'); 21 | var mountPath = '/explorer'; 22 | 23 | explorer(app, { basePath: restApiRoot, mountPath }); 24 | app.once('started', function() { 25 | var baseUrl = app.get('url').replace(/\/$/, ''); 26 | 27 | console.log('Browse your REST API at %s%s', baseUrl, mountPath); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /common/app/redux/actions.js: -------------------------------------------------------------------------------- 1 | import { createAction } from 'redux-actions'; 2 | import types from './types'; 3 | 4 | // updateTitle(title: String) => Action 5 | export const updateTitle = createAction(types.updateTitle); 6 | 7 | let id = 0; 8 | // makeToast({ type?: String, message: String, title: String }) => Action 9 | export const makeToast = createAction( 10 | types.makeToast, 11 | toast => { 12 | id += 1; 13 | return { 14 | ...toast, 15 | id, 16 | type: toast.type || 'info' 17 | }; 18 | } 19 | ); 20 | 21 | // fetchUser() => Action 22 | // used in combination with fetch-user-saga 23 | export const fetchUser = createAction(types.fetchUser); 24 | 25 | // setUser(userInfo: Object) => Action 26 | export const setUser = createAction(types.setUser); 27 | 28 | // updatePoints(points: Number) => Action 29 | export const updatePoints = createAction(types.updatePoints); 30 | 31 | // hardGoTo(path: String) => Action 32 | export const hardGoTo = createAction(types.hardGoTo); 33 | -------------------------------------------------------------------------------- /common/app/routes/Hikes/components/Map.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Link } from 'react-router'; 3 | import { ListGroup, ListGroupItem } from 'react-bootstrap'; 4 | 5 | export default React.createClass({ 6 | displayName: 'HikesMap', 7 | 8 | propTypes: { 9 | hikes: PropTypes.array 10 | }, 11 | 12 | render() { 13 | const { 14 | hikes = [{}] 15 | } = this.props; 16 | 17 | const vidElements = hikes.map(({ title, dashedName }) => { 18 | return ( 19 | 20 | 21 |

{ title }

22 | 23 |
24 | ); 25 | }); 26 | 27 | return ( 28 |
29 |
30 |

Welcome To Hikes!

31 |
32 |
33 | 34 | { vidElements } 35 | 36 |
37 | ); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /server/views/stories/index.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | if (user) 4 | script. 5 | var isLoggedIn = true; 6 | var userId = !{JSON.stringify(user.id)}; 7 | var username = !{JSON.stringify(user.username)}; 8 | else 9 | script. 10 | var isLoggedIn = false; 11 | script. 12 | var challengeName = 'Camper News'; 13 | var page = !{JSON.stringify(page)}; 14 | h1.text-center Camper News 15 | hr 16 | .spacer 17 | include news-nav 18 | .spacer 19 | if (page === 'hot') 20 | include hot-stories 21 | if (page === 'submit') 22 | if (user) 23 | include preliminary-submit 24 | else 25 | .spacer 26 | .text-center 27 | a.btn.btn-cta.signup-btn.btn-primary(href="/login") Sign in to post your story (it's free) 28 | .spacer 29 | if (page === 'storySubmission') 30 | include submit-story 31 | if (page === 'show') 32 | include show 33 | -------------------------------------------------------------------------------- /client/less/lib/ionicons/_ionicons-font.less: -------------------------------------------------------------------------------- 1 | // Ionicons Font Path 2 | // -------------------------- 3 | 4 | @font-face { 5 | font-family: @ionicons-font-family; 6 | src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}"); 7 | src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}#iefix") format("embedded-opentype"), 8 | url("@{ionicons-font-path}/ionicons.ttf?v=@{ionicons-version}") format("truetype"), 9 | url("@{ionicons-font-path}/ionicons.woff?v=@{ionicons-version}") format("woff"), 10 | url("@{ionicons-font-path}/ionicons.svg?v=@{ionicons-version}#Ionicons") format("svg"); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | 15 | .ion { 16 | display: inline-block; 17 | font-family: @ionicons-font-family; 18 | speak: none; 19 | font-style: normal; 20 | font-weight: normal; 21 | font-variant: normal; 22 | text-transform: none; 23 | text-rendering: auto; 24 | line-height: 1; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | } -------------------------------------------------------------------------------- /server/boot/redirects.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | var router = app.loopback.Router(); 3 | 4 | router.get('/nonprofit-project-instructions', function(req, res) { 5 | res.redirect( 6 | 301, 7 | '//github.com/FreeCodeCamp/freecodecamp/wiki/' + 8 | "How-Free-Code-Camp's-Nonprofit-Projects-work" 9 | ); 10 | }); 11 | 12 | router.get('/agile', function(req, res) { 13 | res.redirect(301, '/pmi-acp-agile-project-managers'); 14 | }); 15 | 16 | router.get('/privacy', function(req, res) { 17 | res.redirect( 18 | 301, 19 | '//github.com/FreeCodeCamp/freecodecamp/wiki/' + 20 | "Free-Code-Camp's-Privacy-Policy" 21 | ); 22 | }); 23 | 24 | router.get('/learn-to-code', function(req, res) { 25 | res.redirect(301, '/map'); 26 | }); 27 | 28 | router.get('/field-guide/*', function(req, res) { 29 | res.redirect(302, '/wiki'); 30 | }); 31 | 32 | router.get('/about', function(req, res) { 33 | res.redirect(301, '/map'); 34 | }); 35 | 36 | app.use(router); 37 | }; 38 | -------------------------------------------------------------------------------- /server/utils/bad-id-map.js: -------------------------------------------------------------------------------- 1 | export default { 2 | bg9997c9c79feddfaeb9bdef: '56bbb991ad1ed5201cd392ca', 3 | bg9995c9c69feddfaeb9bdef: '56bbb991ad1ed5201cd392cb', 4 | bg9994c9c69feddfaeb9bdef: '56bbb991ad1ed5201cd392cc', 5 | bg9996c9c69feddfaeb9bdef: '56bbb991ad1ed5201cd392cd', 6 | bg9997c9c69feddfaeb9bdef: '56bbb991ad1ed5201cd392ce', 7 | bg9997c9c89feddfaeb9bdef: '56bbb991ad1ed5201cd392cf', 8 | bg9998c9c99feddfaeb9bdef: '56bbb991ad1ed5201cd392d0', 9 | bg9999c9c99feddfaeb9bdef: '56bbb991ad1ed5201cd392d1', 10 | bg9999c9c99feedfaeb9bdef: '56bbb991ad1ed5201cd392d2', 11 | bg9999c9c99fdddfaeb9bdef: '56bbb991ad1ed5201cd392d3', 12 | bb000000000000000000001: '56bbb991ad1ed5201cd392d4', 13 | bc000000000000000000001: '56bbb991ad1ed5201cd392d5', 14 | bb000000000000000000002: '56bbb991ad1ed5201cd392d6', 15 | bb000000000000000000003: '56bbb991ad1ed5201cd392d7', 16 | bb000000000000000000004: '56bbb991ad1ed5201cd392d8', 17 | bb000000000000000000005: '56bbb991ad1ed5201cd392d9', 18 | bb000000000000000000006: '56bbb991ad1ed5201cd392da' 19 | }; 20 | -------------------------------------------------------------------------------- /server/views/partials/react-stylesheets.jade: -------------------------------------------------------------------------------- 1 | link(rel='stylesheet', type='text/css' href='/css/lato.css') 2 | link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css') 3 | link(rel='stylesheet', href=rev('/css', 'main.css')) 4 | link(rel='stylesheet', href='/css/Vimeo.css') 5 | 6 | include meta 7 | meta(charset='utf-8') 8 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 9 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 10 | meta(name='csrf-token', content=_csrf) 11 | script. 12 | var _vds = _vds || []; 13 | window._vds = _vds; 14 | (function(){ 15 | _vds.push(['setAccountId', 'b350d488f8e3e5f1']); 16 | (function() { 17 | var vds = document.createElement('script'); 18 | vds.type='text/javascript'; 19 | vds.async = true; 20 | vds.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'dn-growing.qbox.me/vds.js'; 21 | var s = document.getElementsByTagName('script')[0]; 22 | s.parentNode.insertBefore(vds, s); 23 | })(); 24 | })(); 25 | -------------------------------------------------------------------------------- /client/commonFramework/run-tests-stream.js: -------------------------------------------------------------------------------- 1 | window.common = (function(global) { 2 | const { 3 | Rx: { Observable }, 4 | chai, 5 | common = { init: [] } 6 | } = global; 7 | 8 | common.runTests$ = function runTests$({ 9 | code, 10 | originalCode, 11 | userTests, 12 | ...rest 13 | }) { 14 | 15 | return Observable.from(userTests) 16 | .map(function(test) { 17 | 18 | /* eslint-disable no-unused-vars */ 19 | const assert = chai.assert; 20 | const editor = { getValue() { return originalCode; }}; 21 | /* eslint-enable no-unused-vars */ 22 | 23 | try { 24 | if (test) { 25 | /* eslint-disable no-eval */ 26 | eval(common.reassembleTest(code, test)); 27 | /* eslint-enable no-eval */ 28 | } 29 | } catch (e) { 30 | test.err = e.message; 31 | } 32 | 33 | return test; 34 | }) 35 | .toArray() 36 | .map(tests => ({ ...rest, tests })); 37 | }; 38 | 39 | return common; 40 | }(window)); 41 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | .clearfix(); 11 | } 12 | .center-block { 13 | .center-block(); 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | .text-hide(); 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | visibility: hidden !important; 48 | } 49 | 50 | 51 | // For Affix plugin 52 | // ------------------------- 53 | 54 | .affix { 55 | position: fixed; 56 | } 57 | -------------------------------------------------------------------------------- /common/app/utils/Professor-Context.js: -------------------------------------------------------------------------------- 1 | import React, { Children, PropTypes } from 'react'; 2 | 3 | class ProfessorContext extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.professor = props.professor; 7 | } 8 | static displayName = 'ProfessorContext'; 9 | 10 | static propTypes = { 11 | professor: PropTypes.object, 12 | children: PropTypes.element.isRequired 13 | }; 14 | 15 | static childContextTypes = { 16 | professor: PropTypes.object 17 | }; 18 | 19 | getChildContext() { 20 | return { professor: this.professor }; 21 | } 22 | 23 | render() { 24 | return Children.only(this.props.children); 25 | } 26 | } 27 | 28 | /* eslint-disable react/display-name, react/prop-types */ 29 | ProfessorContext.wrap = function wrap(Component, professor) { 30 | const props = {}; 31 | if (professor) { 32 | props.professor = professor; 33 | } 34 | 35 | return React.createElement( 36 | ProfessorContext, 37 | props, 38 | Component 39 | ); 40 | }; 41 | 42 | export default ProfessorContext; 43 | -------------------------------------------------------------------------------- /server/utils/rx.js: -------------------------------------------------------------------------------- 1 | import Rx from 'rx'; 2 | import debugFactory from 'debug'; 3 | 4 | const debug = debugFactory('fcc:rxUtils'); 5 | 6 | export function saveInstance(instance) { 7 | return new Rx.Observable.create(function(observer) { 8 | if (!instance || typeof instance.save !== 'function') { 9 | debug('no instance or save method'); 10 | observer.onNext(); 11 | return observer.onCompleted(); 12 | } 13 | return instance.save(function(err, savedInstance) { 14 | if (err) { 15 | return observer.onError(err); 16 | } 17 | debug('instance saved'); 18 | observer.onNext(savedInstance); 19 | return observer.onCompleted(); 20 | }); 21 | }); 22 | } 23 | 24 | // alias saveInstance 25 | export const saveUser = saveInstance; 26 | 27 | export function observeQuery(Model, method, query) { 28 | return Rx.Observable.fromNodeCallback(Model[method], Model)(query); 29 | } 30 | 31 | export function observeMethod(context, methodName) { 32 | return Rx.Observable.fromNodeCallback(context[methodName], context); 33 | } 34 | -------------------------------------------------------------------------------- /server/views/account/email-signup.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | script. 4 | var challengeName = 'Email Signup' 5 | h2.text-center 账户注册: 6 | form.form-horizontal(method='POST', action='/api/users', name="signupForm") 7 | .row 8 | .col-sm-6.col-sm-offset-3 9 | input(type='hidden', name='_csrf', value=_csrf) 10 | .form-group 11 | input.input-lg.form-control(type='email', name='email', id='email', placeholder='邮箱', autofocus, required, autocomplete="off") 12 | .form-group 13 | input.input-lg.form-control(type='password', name='password', id='password', placeholder='密码', required, pattern=".{8,50}", title="密码长度为8-50之间") 14 | .form-group 15 | button.btn.btn-lg.btn-primary.btn-block(type='submit') 16 | span.ion-person-add 17 | | 注册 18 | br 19 | br 20 | br 21 | br 22 | br 23 | br 24 | br 25 | br 26 | -------------------------------------------------------------------------------- /common/app/redux/fetch-user-saga.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx'; 2 | import { handleError, setUser, fetchUser } from './types'; 3 | 4 | export default ({ services }) => ({ dispatch }) => next => { 5 | return function getUserSaga(action) { 6 | if (action.type !== fetchUser) { 7 | return next(action); 8 | } 9 | 10 | return services.readService$({ service: 'user' }) 11 | .map(({ 12 | username, 13 | picture, 14 | progressTimestamps = [], 15 | isFrontEndCert, 16 | isBackEndCert, 17 | isFullStackCert 18 | }) => { 19 | return { 20 | type: setUser, 21 | payload: { 22 | username, 23 | picture, 24 | points: progressTimestamps.length, 25 | isFrontEndCert, 26 | isBackEndCert, 27 | isFullStackCert, 28 | isSignedIn: true 29 | } 30 | }; 31 | }) 32 | .catch(error => Observable.just({ 33 | type: handleError, 34 | error 35 | })) 36 | .doOnNext(dispatch); 37 | }; 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /server/middlewares/add-return-to.js: -------------------------------------------------------------------------------- 1 | const pathsOfNoReturn = [ 2 | 'link', 3 | 'bower_components', 4 | 'auth', 5 | 'login', 6 | 'logout', 7 | 'signin', 8 | 'signup', 9 | 'fonts', 10 | 'favicon', 11 | 'js', 12 | 'css' 13 | ]; 14 | 15 | const pathsWhiteList = [ 16 | 'news', 17 | 'challenges', 18 | 'map', 19 | 'news', 20 | 'commit' 21 | ]; 22 | 23 | const pathsOfNoReturnRegex = new RegExp(pathsOfNoReturn.join('|'), 'i'); 24 | const whiteListRegex = new RegExp(pathsWhiteList.join('|'), 'i'); 25 | 26 | export default function addReturnToUrl() { 27 | return function(req, res, next) { 28 | // Remember original destination before login. 29 | var path = req.path.split('/')[1]; 30 | 31 | if ( 32 | req.method !== 'GET' || 33 | pathsOfNoReturnRegex.test(path) || 34 | !whiteListRegex.test(path) || 35 | (/news/i).test(path) && (/hot/i).test(req.path) 36 | ) { 37 | return next(); 38 | } 39 | req.session.returnTo = req.originalUrl === '/map-aside' ? 40 | '/map' : 41 | req.originalUrl; 42 | return next(); 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /server/services/job.js: -------------------------------------------------------------------------------- 1 | const whereFilt = { 2 | where: { 3 | isFilled: false, 4 | isPaid: true, 5 | isApproved: true 6 | }, 7 | order: 'postedOn DESC' 8 | }; 9 | 10 | export default function getJobServices(app) { 11 | const { Job } = app.models; 12 | 13 | return { 14 | name: 'jobs', 15 | create(req, resource, { job } = {}, body, config, cb) { 16 | if (!job) { 17 | return cb(new Error('job creation should get a job object')); 18 | } 19 | 20 | Object.assign(job, { 21 | isPaid: false, 22 | isApproved: false 23 | }); 24 | 25 | return Job.create(job, (err, savedJob) => { 26 | cb(err, savedJob.toJSON()); 27 | }); 28 | }, 29 | read(req, resource, params, config, cb) { 30 | const id = params ? params.id : null; 31 | if (id) { 32 | return Job.findById(id) 33 | .then(job => cb(null, job.toJSON())) 34 | .catch(cb); 35 | } 36 | return Job.find(whereFilt) 37 | .then(jobs => cb(null, jobs.map(job => job.toJSON()))) 38 | .catch(cb); 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: @line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | &:extend(.clearfix all); 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: @pager-bg; 19 | border: 1px solid @pager-border; 20 | border-radius: @pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: @pager-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: @pager-disabled-color; 50 | background-color: @pager-bg; 51 | cursor: @cursor-disabled; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/apply-promo-saga.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx'; 2 | 3 | import { applyPromo } from './types'; 4 | import { applyPromoCompleted } from './actions'; 5 | import { postJSON$ } from '../../../../utils/ajax-stream'; 6 | 7 | export default () => ({ dispatch }) => next => { 8 | return function applyPromoSaga(action) { 9 | if (action.type !== applyPromo) { 10 | return next(action); 11 | } 12 | 13 | const { id, code = '', type = null } = action.payload; 14 | 15 | const body = { 16 | id, 17 | code: code.replace(/[^\d\w\s]/, '') 18 | }; 19 | 20 | if (type) { 21 | body.type = type; 22 | } 23 | 24 | return postJSON$('/api/promos/getButton', body) 25 | .retry(3) 26 | .map(({ promo }) => { 27 | if (!promo || !promo.buttonId) { 28 | throw new Error('No promo returned by server'); 29 | } 30 | 31 | return applyPromoCompleted(promo); 32 | }) 33 | .catch(error => Observable.just({ 34 | type: 'app.handleError', 35 | error 36 | })) 37 | .doOnNext(dispatch); 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | .fa-icon-rotate(@degrees, @rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 17 | -webkit-transform: rotate(@degrees); 18 | -ms-transform: rotate(@degrees); 19 | transform: rotate(@degrees); 20 | } 21 | 22 | .fa-icon-flip(@horiz, @vert, @rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 24 | -webkit-transform: scale(@horiz, @vert); 25 | -ms-transform: scale(@horiz, @vert); 26 | transform: scale(@horiz, @vert); 27 | } 28 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | 5 | var __DEV__ = process.env.NODE_ENV !== 'production'; 6 | 7 | module.exports = { 8 | entry: './client', 9 | output: { 10 | filename: 'bundle.js', 11 | path: path.join(__dirname, '/public/js'), 12 | publicPath: 'public/' 13 | }, 14 | module: { 15 | loaders: [ 16 | { 17 | test: /\.jsx?$/, 18 | include: [ 19 | path.join(__dirname, 'client/'), 20 | path.join(__dirname, 'common/') 21 | ], 22 | loaders: [ 23 | 'babel-loader' 24 | ] 25 | }, 26 | { 27 | test: /\.json$/, 28 | loaders: [ 29 | 'json-loader' 30 | ] 31 | } 32 | ] 33 | }, 34 | plugins: [ 35 | new webpack.optimize.DedupePlugin(), 36 | new webpack.optimize.OccurenceOrderPlugin(true), 37 | new webpack.DefinePlugin({ 38 | 'process.env': { 39 | 'NODE_ENV': JSON.stringify(__DEV__ ? 'development' : 'production') 40 | }, 41 | '__DEVTOOLS__': !__DEV__ 42 | }) 43 | ] 44 | }; 45 | -------------------------------------------------------------------------------- /server/views/resources/shop.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | h1.text-center Shop 4 | hr 5 | .row 6 | .col-xs-12.col-sm-8.col-sm-offset-2.col-md-6.col-md-offset-3.text-center 7 | img.img-responsive.img-center(src='//i.imgur.com/MH1CvwY.jpg') 8 | h3 Free Code Camp laptop stickers are here! 9 | h4 Get two for only $5, with free shipping anywhere! 10 | p These durable 2" (5 cm) stickers sport a matte finish, and look great anywhere - especially on your laptop. 11 | html. 12 |
13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /client/less/lib/font-awesome-4.3.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | @mixin fa-icon-rotate($degrees, $rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 17 | -webkit-transform: rotate($degrees); 18 | -ms-transform: rotate($degrees); 19 | transform: rotate($degrees); 20 | } 21 | 22 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 24 | -webkit-transform: scale($horiz, $vert); 25 | -ms-transform: scale($horiz, $vert); 26 | transform: scale($horiz, $vert); 27 | } 28 | -------------------------------------------------------------------------------- /server/boot/redirectHttps.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var loopback = require('loopback'); 3 | var express = require('express'); 4 | 5 | var port = 1337; 6 | 7 | // this will listen to traffic on port 1337 8 | // The purpose is to redirect any user who is direct to https 9 | // instead of http by mistake. Our nginx proxy server will listen 10 | // for https traffic and serve from this port on this server. 11 | // the view being send will have a short timeout and a redirect 12 | module.exports = function(loopbackApp) { 13 | var app = express(); 14 | app.set('view engine', 'jade'); 15 | // views in ../views' 16 | app.set('views', path.join(__dirname, '..')); 17 | 18 | // server static files 19 | app.use(loopback.static(path.join( 20 | __dirname, 21 | '../', 22 | '../public' 23 | ))); 24 | 25 | // all traffic will be redirected on page load; 26 | app.use(function(req, res) { 27 | return res.render('views/redirect-https'); 28 | }); 29 | 30 | loopbackApp.once('started', function() { 31 | app.listen(port, function() { 32 | console.log('https redirect listening on port %s', port); 33 | }); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /server/views/resources/dashboard.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | style. 4 | table,th,td{ 5 | padding-left: 5px; 6 | text-align:center; 7 | } 8 | tr:nth-child(even){ 9 | background-color: #FAEBD7; 10 | } 11 | tr:nth-child(odd){ 12 | background-color: white; 13 | } 14 | th, tr{ 15 | padding:10px; 16 | } 17 | table{margin: 0 auto;} 18 | .row(ng-app="app") 19 | .col-md-12 20 | h2.text-center FCC同城学习小组 21 | if user 22 | table(ng-controller="dashboard") 23 | thead 24 | tr 25 | th id 26 | th 姓名 27 | th 邮箱 28 | th 手机号 29 | th 微信号 30 | th 地点 31 | th 背景 32 | tbody 33 | tr(ng-repeat="item in items") 34 | th {{item.username}} 35 | th {{item.fullname}} 36 | th {{item.email}} 37 | th {{item.telphone}} 38 | th {{item.wechat}} 39 | th {{item.location}} 40 | th {{item.background}} 41 | script(src="/js/angular.min.js") 42 | script(src="/js/dashboard.js") 43 | -------------------------------------------------------------------------------- /public/js/lib/loop-protect/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 JS Bin Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/middlewares/revision-helpers.js: -------------------------------------------------------------------------------- 1 | import manifest from '../rev-manifest.json'; 2 | 3 | const __DEV__ = process.env.NODE_ENV === 'development'; 4 | const manifestPath = '../rev-manifest.json'; 5 | 6 | export default function({ globalPrepend = '' } = {}) { 7 | 8 | function rev(manifest, scopedPrepend, asset) { 9 | return `${globalPrepend}${scopedPrepend}/${ manifest[asset] || asset }`; 10 | } 11 | const boundRev = rev.bind(null, manifest); 12 | 13 | return function(req, res, next) { 14 | // in dev environment, we reread the manifest on every call 15 | // this means we do not need to restart server on every change to 16 | // client code 17 | if (__DEV__) { 18 | // we first need to remove the manifest from require cache 19 | delete require.cache[require.resolve(manifestPath)]; 20 | // and re-require 21 | const manifest = require(manifestPath); 22 | res.locals.rev = rev.bind(null, manifest); 23 | return next(); 24 | } 25 | 26 | // in production we take use the initially loaded manifest 27 | // since this should not change in production 28 | res.locals.rev = boundRev; 29 | return next(); 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /server/views/account/email-signin.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .row 4 | .col-xs-12 5 | h2.text-center 账户登录: 6 | .col-sm-6.col-sm-offset-3 7 | form(method='POST', action='/api/users/login') 8 | input(type='hidden', name='_csrf', value=_csrf) 9 | .form-group 10 | input.input-lg.form-control(type='email', name='email', id='email', placeholder='邮箱', autofocus=true, autocomplete=false) 11 | .form-group 12 | input.input-lg.form-control(type='password', name='password', id='password', placeholder='密码', autocomplete=false) 13 | button.btn.btn-primary.btn-lg.btn-block(type='submit') 14 | span.ion-android-hand 15 | | 登录 16 | .button-spacer 17 | .button-spacer 18 | a.btn.btn-info.btn-lg.btn-block(href='/forgot') 忘记密码? 19 | br 20 | br 21 | br 22 | br 23 | br 24 | br 25 | br 26 | br 27 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: @jumbotron-padding (@jumbotron-padding / 2); 8 | margin-bottom: @jumbotron-padding; 9 | color: @jumbotron-color; 10 | background-color: @jumbotron-bg; 11 | 12 | h1, 13 | .h1 { 14 | color: @jumbotron-heading-color; 15 | } 16 | p { 17 | margin-bottom: (@jumbotron-padding / 2); 18 | font-size: @jumbotron-font-size; 19 | font-weight: 200; 20 | } 21 | 22 | > hr { 23 | border-top-color: darken(@jumbotron-bg, 10%); 24 | } 25 | 26 | .container &, 27 | .container-fluid & { 28 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 29 | } 30 | 31 | .container { 32 | max-width: 100%; 33 | } 34 | 35 | @media screen and (min-width: @screen-sm-min) { 36 | padding: (@jumbotron-padding * 1.6) 0; 37 | 38 | .container & { 39 | padding-left: (@jumbotron-padding * 2); 40 | padding-right: (@jumbotron-padding * 2); 41 | } 42 | 43 | h1, 44 | .h1 { 45 | font-size: (@font-size-base * 4.5); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/app/components/Footer/links.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "className": "ion-speakerphone", 4 | "content": " Blog ", 5 | "href": "http://medium.freecodecamp.com", 6 | "target": "_blank" 7 | }, 8 | { 9 | "className": "ion-social-twitch-outline", 10 | "content": " Twitch ", 11 | "href": "http://www.twitch.tv/freecodecamp", 12 | "target": "_blank" 13 | }, 14 | { 15 | "className": "ion-social-github", 16 | "content": " Github ", 17 | "href": "http://github.com/freecodecamp", 18 | "target": "_blank" 19 | }, 20 | { 21 | "className": "ion-social-twitter", 22 | "content": " Twitter ", 23 | "href": "http://twitter.com/freecodecamp", 24 | "target": "_blank" 25 | }, 26 | { 27 | "className": "ion-social-facebook", 28 | "content": " Facebook ", 29 | "href": "http://facebook.com/freecodecamp", 30 | "target": "_blank" 31 | }, 32 | { 33 | "className": "ion-information-circled", 34 | "content": " About ", 35 | "href": "/learn-to-code", 36 | "target": "_self" 37 | }, 38 | { 39 | "className": "ion-locked", 40 | "content": " Privacy ", 41 | "href": "/privacy'", 42 | "target": "_self" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/actions.js: -------------------------------------------------------------------------------- 1 | import { createAction } from 'redux-actions'; 2 | 3 | import types from './types'; 4 | 5 | export const fetchJobs = createAction(types.fetchJobs); 6 | export const fetchJobsCompleted = createAction( 7 | types.fetchJobsCompleted, 8 | (currentJob, jobs) => ({ currentJob, jobs }) 9 | ); 10 | 11 | export const findJob = createAction(types.findJob); 12 | 13 | // saves to database 14 | export const saveJob = createAction(types.saveJob); 15 | // saves to localStorage 16 | export const saveForm = createAction(types.saveForm); 17 | 18 | export const saveCompleted = createAction(types.saveCompleted); 19 | 20 | export const clearForm = createAction(types.clearForm); 21 | 22 | export const loadSavedForm = createAction(types.loadSavedForm); 23 | export const loadSavedFormCompleted = createAction( 24 | types.loadSavedFormCompleted 25 | ); 26 | 27 | export const clearPromo = createAction(types.clearPromo); 28 | export const updatePromo = createAction( 29 | types.updatePromo, 30 | ({ target: { value = '' } = {} } = {}) => value 31 | ); 32 | 33 | export const applyPromo = createAction(types.applyPromo); 34 | export const applyPromoCompleted = createAction(types.applyPromoCompleted); 35 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/components/NewJobCompleted.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { LinkContainer } from 'react-router-bootstrap'; 3 | import { Button, Col, Row } from 'react-bootstrap'; 4 | 5 | export default class extends React.createClass { 6 | static displayName = 'NewJobCompleted'; 7 | 8 | render() { 9 | return ( 10 |
11 |
12 | 13 |

14 | Your Position has Been Submitted 15 |

16 |
17 | 18 | 21 | We’ll review your listing and email you when it’s live. 22 |
23 | Thank you for listing this job with Free Code Camp. 24 | 25 |
26 |
27 | 28 | 34 | 35 |
36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/image.less: -------------------------------------------------------------------------------- 1 | // Image Mixins 2 | // - Responsive image 3 | // - Retina image 4 | 5 | 6 | // Responsive image 7 | // 8 | // Keep images from scaling beyond the width of their parents. 9 | .img-responsive(@display: block) { 10 | display: @display; 11 | max-width: 100%; // Part 1: Set a maximum relative to the parent 12 | height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching 13 | } 14 | 15 | 16 | // Retina image 17 | // 18 | // Short retina mixin for setting background-image and -size. Note that the 19 | // spelling of `min--moz-device-pixel-ratio` is intentional. 20 | .img-retina(@file-1x; @file-2x; @width-1x; @height-1x) { 21 | background-image: url("@{file-1x}"); 22 | 23 | @media 24 | only screen and (-webkit-min-device-pixel-ratio: 2), 25 | only screen and ( min--moz-device-pixel-ratio: 2), 26 | only screen and ( -o-min-device-pixel-ratio: 2/1), 27 | only screen and ( min-device-pixel-ratio: 2), 28 | only screen and ( min-resolution: 192dpi), 29 | only screen and ( min-resolution: 2dppx) { 30 | background-image: url("@{file-2x}"); 31 | background-size: @width-1x @height-1x; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/boot/about.js: -------------------------------------------------------------------------------- 1 | import dedent from 'dedent'; 2 | import moment from 'moment'; 3 | 4 | import { observeMethod } from '../utils/rx'; 5 | 6 | function numberWithCommas(x) { 7 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); 8 | } 9 | 10 | export default function about(app) { 11 | const router = app.loopback.Router(); 12 | const User = app.models.User; 13 | const userCount$ = observeMethod(User, 'count'); 14 | 15 | function showAbout(req, res, next) { 16 | const daysRunning = moment().diff(new Date('5/5/2016'), 'days'); 17 | 18 | userCount$() 19 | .map(camperCount => numberWithCommas(camperCount)) 20 | .doOnNext(camperCount => { 21 | res.render('resources/about', { 22 | camperCount, 23 | daysRunning, 24 | title: dedent` 25 | About our Open Source Community, our social media presence, 26 | and how to contact us`.split('\n').join(' '), 27 | globalCompletedCount: numberWithCommas( 28 | 12952 + (Math.floor((Date.now() - 1463557575258) / 18000)) 29 | ) 30 | }); 31 | }) 32 | .subscribe(() => {}, next); 33 | } 34 | 35 | router.get('/about', showAbout); 36 | app.use(router); 37 | } 38 | -------------------------------------------------------------------------------- /common/models/pledge.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pledge", 3 | "base": "PersistedModel", 4 | "idInjection": true, 5 | "trackChanges": false, 6 | "properties": { 7 | "nonprofit": { 8 | "type": "string", 9 | "index": true 10 | }, 11 | "amount": { 12 | "type": "number" 13 | }, 14 | "dateStarted": { 15 | "type": "date", 16 | "defaultFn": "now" 17 | }, 18 | "dateEnded": { 19 | "type": "date" 20 | }, 21 | "formerUserId": { 22 | "type": "string" 23 | }, 24 | "isOrphaned": { 25 | "type": "boolean" 26 | }, 27 | "isCompleted": { 28 | "type": "boolean", 29 | "default": "false" 30 | } 31 | }, 32 | "validations": [], 33 | "relations": { 34 | "user": { 35 | "type": "hasMany", 36 | "model": "user", 37 | "foreignKey": "userId" 38 | } 39 | }, 40 | "acls": [ 41 | { 42 | "accessType": "*", 43 | "principalType": "ROLE", 44 | "principalId": "$everyone", 45 | "permission": "DENY" 46 | }, 47 | { 48 | "accessType": "READ", 49 | "principalType": "ROLE", 50 | "principalId": "$everyone", 51 | "permission": "ALLOW" 52 | } 53 | ], 54 | "methods": [] 55 | } 56 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | 4 | // Utilities 5 | @import "mixins/hide-text.less"; 6 | @import "mixins/opacity.less"; 7 | @import "mixins/image.less"; 8 | @import "mixins/labels.less"; 9 | @import "mixins/reset-filter.less"; 10 | @import "mixins/resize.less"; 11 | @import "mixins/responsive-visibility.less"; 12 | @import "mixins/size.less"; 13 | @import "mixins/tab-focus.less"; 14 | @import "mixins/text-emphasis.less"; 15 | @import "mixins/text-overflow.less"; 16 | @import "mixins/vendor-prefixes.less"; 17 | 18 | // Components 19 | @import "mixins/alerts.less"; 20 | @import "mixins/buttons.less"; 21 | @import "mixins/panels.less"; 22 | @import "mixins/pagination.less"; 23 | @import "mixins/list-group.less"; 24 | @import "mixins/nav-divider.less"; 25 | @import "mixins/forms.less"; 26 | @import "mixins/progress-bar.less"; 27 | @import "mixins/table-row.less"; 28 | 29 | // Skins 30 | @import "mixins/background-variant.less"; 31 | @import "mixins/border-radius.less"; 32 | @import "mixins/gradients.less"; 33 | 34 | // Layout 35 | @import "mixins/clearfix.less"; 36 | @import "mixins/center-block.less"; 37 | @import "mixins/nav-vertical-align.less"; 38 | @import "mixins/grid-framework.less"; 39 | @import "mixins/grid.less"; 40 | -------------------------------------------------------------------------------- /leanTest.js: -------------------------------------------------------------------------------- 1 | //Setup 2 | var contacts = [ 3 | { 4 | "firstName": "Akira", 5 | "lastName": "Laine", 6 | "number": "0543236543", 7 | "likes": ["Pizza", "Coding", "Brownie Points"] 8 | }, 9 | { 10 | "firstName": "Harry", 11 | "lastName": "Potter", 12 | "number": "0994372684", 13 | "likes": ["Hogwarts", "Magic", "Hagrid"] 14 | }, 15 | { 16 | "firstName": "Sherlock", 17 | "lastName": "Holmes", 18 | "number": "0487345643", 19 | "likes": ["Intriguing Cases", "Violin"] 20 | }, 21 | { 22 | "firstName": "Kristian", 23 | "lastName": "Vos", 24 | "number": "unknown", 25 | "likes": ["Javascript", "Gaming", "Foxes"] 26 | } 27 | ]; 28 | 29 | 30 | function lookUp(firstName, prop){ 31 | // Only change code below this line 32 | for(var i in contacts){ 33 | if(contacts[i].firstName == firstName){ 34 | if(contacts[i].hasOwnProperty(prop)){ 35 | return contacts[i][prop]; 36 | }else{ 37 | return "No such property"; 38 | } 39 | }else{ 40 | return "No such contact"; 41 | } 42 | } 43 | // Only change code above this line 44 | } 45 | 46 | // Change these values to test your function 47 | lookUp("Akira", "likes"); 48 | -------------------------------------------------------------------------------- /common/utils/services-creator.js: -------------------------------------------------------------------------------- 1 | import { Observable, Disposable } from 'rx'; 2 | import Fetchr from 'fetchr'; 3 | import stampit from 'stampit'; 4 | 5 | function callbackObserver(observer) { 6 | return (err, res) => { 7 | if (err) { 8 | return observer.onError(err); 9 | } 10 | 11 | observer.onNext(res); 12 | return observer.onCompleted(); 13 | }; 14 | } 15 | 16 | 17 | export default stampit({ 18 | init({ args: [ options ] }) { 19 | this.services = new Fetchr(options); 20 | }, 21 | methods: { 22 | readService$({ service: resource, params, config }) { 23 | return Observable.create(observer => { 24 | this.services.read( 25 | resource, 26 | params, 27 | config, 28 | callbackObserver(observer) 29 | ); 30 | 31 | return Disposable.create(() => observer.dispose()); 32 | }); 33 | }, 34 | createService$({ service: resource, params, body, config }) { 35 | return Observable.create(observer => { 36 | this.services.create( 37 | resource, 38 | params, 39 | body, 40 | config, 41 | callbackObserver(observer) 42 | ); 43 | 44 | return Disposable.create(() => observer.dispose()); 45 | }); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/jobs-form-normalizer.js: -------------------------------------------------------------------------------- 1 | import normalizeUrl from 'normalize-url'; 2 | import { 3 | inHTMLData, 4 | uriInSingleQuotedAttr 5 | } from 'xss-filters'; 6 | 7 | const normalizeOptions = { 8 | stripWWW: false 9 | }; 10 | 11 | function ifDefinedNormalize(normalizer) { 12 | return value => value ? normalizer(value) : value; 13 | } 14 | 15 | function formatUrl(url) { 16 | if ( 17 | typeof url === 'string' && 18 | url.length > 4 && 19 | url.indexOf('.') !== -1 20 | ) { 21 | // prevent trailing / from being stripped during typing 22 | let lastChar = ''; 23 | if (url.substring(url.length - 1) === '/') { 24 | lastChar = '/'; 25 | } 26 | return normalizeUrl(url, normalizeOptions) + lastChar; 27 | } 28 | return url; 29 | } 30 | 31 | export default { 32 | NewJob: { 33 | position: ifDefinedNormalize(inHTMLData), 34 | locale: ifDefinedNormalize(inHTMLData), 35 | description: ifDefinedNormalize(inHTMLData), 36 | email: ifDefinedNormalize(inHTMLData), 37 | url: ifDefinedNormalize(value => formatUrl(uriInSingleQuotedAttr(value))), 38 | logo: ifDefinedNormalize(value => formatUrl(uriInSingleQuotedAttr(value))), 39 | company: ifDefinedNormalize(inHTMLData), 40 | howToApply: ifDefinedNormalize(inHTMLData) 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /common/app/components/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Col, Row, Grid } from 'react-bootstrap'; 3 | 4 | import links from './links.json'; 5 | 6 | export default class extends React.Component { 7 | static displayName = 'Footer'; 8 | renderLinks(mobile) { 9 | return links.map(link => { 10 | return ( 11 | 16 | { this.renderContent(mobile, link.content) } 17 | 18 | ); 19 | }); 20 | } 21 | 22 | renderContent(mobile, content) { 23 | if (mobile) { 24 | return ( 25 | 26 | content; 27 | 28 | ); 29 | } 30 | return content; 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | 37 | 40 | { this.renderLinks() } 41 | 42 | 45 | { this.renderLinks(true) } 46 | 47 | 48 | 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/favicons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | .btn-xs & { 32 | top: 0; 33 | padding: 1px 5px; 34 | } 35 | 36 | // Hover state, but only for links 37 | a& { 38 | &:hover, 39 | &:focus { 40 | color: @badge-link-hover-color; 41 | text-decoration: none; 42 | cursor: pointer; 43 | } 44 | } 45 | 46 | // Account for badges in navs 47 | a.list-group-item.active > &, 48 | .nav-pills > .active > a > & { 49 | color: @badge-active-color; 50 | background-color: @badge-active-bg; 51 | } 52 | .nav-pills > li > a > & { 53 | margin-left: 3px; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/mixins/buttons.less: -------------------------------------------------------------------------------- 1 | // Button variants 2 | // 3 | // Easily pump out default styles, as well as :hover, :focus, :active, 4 | // and disabled options for all buttons 5 | 6 | .button-variant(@color; @background; @border) { 7 | color: @color; 8 | background-color: @background; 9 | border-color: @border; 10 | 11 | &:hover, 12 | &:focus, 13 | &.focus, 14 | &:active, 15 | &.active, 16 | .open > .dropdown-toggle& { 17 | color: @gray-lighter; 18 | background-color: @background; 19 | border-color: darken(@border, 12%); 20 | } 21 | &:active, 22 | &.active, 23 | .open > .dropdown-toggle& { 24 | background-image: none; 25 | } 26 | &.disabled, 27 | &[disabled], 28 | fieldset[disabled] & { 29 | &, 30 | &:hover, 31 | &:focus, 32 | &.focus, 33 | &:active, 34 | &.active { 35 | background-color: @background; 36 | border-color: @border; 37 | } 38 | } 39 | 40 | .badge { 41 | color: @background; 42 | background-color: @color; 43 | } 44 | } 45 | 46 | // Button sizes 47 | .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { 48 | padding: @padding-vertical @padding-horizontal; 49 | font-size: @font-size; 50 | line-height: @line-height; 51 | border-radius: @border-radius; 52 | } 53 | -------------------------------------------------------------------------------- /webpack.config.node.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | 5 | var nodeModules = fs.readdirSync('node_modules') 6 | .filter(function(x) { 7 | return ['.bin'].indexOf(x) === -1; 8 | }) 9 | .reduce(function(nodeModules, module) { 10 | nodeModules[module] = 'commonjs ' + module; 11 | return nodeModules; 12 | }, {}); 13 | 14 | module.exports = { 15 | devtool: 'sourcemap', 16 | target: 'node', 17 | entry: './common/app', 18 | // keeps webpack from bundling modules 19 | externals: nodeModules, 20 | output: { 21 | filename: 'app-stream.bundle.js', 22 | path: path.join(__dirname, '/server'), 23 | publicPath: 'public/' 24 | }, 25 | module: { 26 | loaders: [ 27 | { 28 | test: /\.jsx?$/, 29 | include: [ 30 | path.join(__dirname, 'client/'), 31 | path.join(__dirname, 'common/') 32 | ], 33 | loaders: [ 34 | 'babel-loader' 35 | ] 36 | }, 37 | { 38 | test: /\.json$/, 39 | loaders: [ 40 | 'json-loader' 41 | ] 42 | } 43 | ] 44 | }, 45 | plugins: [ 46 | new webpack.BannerPlugin( 47 | 'require("source-map-support").install();', 48 | { 49 | raw: true, 50 | entryOnly: false 51 | } 52 | ) 53 | ] 54 | }; 55 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/bootstrap.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables.less"; 3 | @import "mixins.less"; 4 | 5 | // Reset and dependencies 6 | @import "normalize.less"; 7 | @import "print.less"; 8 | @import "glyphicons.less"; 9 | 10 | // Core CSS 11 | @import "scaffolding.less"; 12 | @import "type.less"; 13 | @import "code.less"; 14 | @import "grid.less"; 15 | @import "tables.less"; 16 | @import "forms.less"; 17 | @import "buttons.less"; 18 | 19 | // Components 20 | @import "component-animations.less"; 21 | @import "dropdowns.less"; 22 | @import "button-groups.less"; 23 | @import "input-groups.less"; 24 | @import "navs.less"; 25 | @import "navbar.less"; 26 | @import "breadcrumbs.less"; 27 | @import "pagination.less"; 28 | @import "pager.less"; 29 | @import "labels.less"; 30 | @import "badges.less"; 31 | @import "jumbotron.less"; 32 | @import "thumbnails.less"; 33 | @import "alerts.less"; 34 | @import "progress-bars.less"; 35 | @import "media.less"; 36 | @import "list-group.less"; 37 | @import "panels.less"; 38 | @import "responsive-embed.less"; 39 | @import "wells.less"; 40 | @import "close.less"; 41 | 42 | // Components w/ JavaScript 43 | @import "modals.less"; 44 | @import "tooltip.less"; 45 | @import "popovers.less"; 46 | @import "carousel.less"; 47 | 48 | // Utility classes 49 | @import "utilities.less"; 50 | @import "responsive-utilities.less"; 51 | -------------------------------------------------------------------------------- /server/views/partials/scripts.jade: -------------------------------------------------------------------------------- 1 | 2 | script. 3 | var _vds = _vds || []; 4 | window._vds = _vds; 5 | (function(){ 6 | _vds.push(['setAccountId', 'b350d488f8e3e5f1']); 7 | (function() { 8 | var vds = document.createElement('script'); 9 | vds.type='text/javascript'; 10 | vds.async = true; 11 | vds.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'dn-growing.qbox.me/vds.js'; 12 | var s = document.getElementsByTagName('script')[0]; 13 | s.parentNode.insertBefore(vds, s); 14 | })(); 15 | })(); 16 | (function(i,s,o,g,r,a,m){ i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 17 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 18 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 19 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 20 | ga('create', 'UA-76000773-1', 'auto'); 21 | ga('require', 'displayfeatures'); 22 | ga('send', 'pageview'); 23 | // Leave the below lines alone! 24 | script. 25 | (function(global) { 26 | global.main = global.main || {}; 27 | global.main.isLoggedIn = !{JSON.stringify(!!user)}; 28 | global.main.userId = !{JSON.stringify(user && user.id || false)}; 29 | }(window)) 30 | script(src=rev('/js', 'vendor-main.js')) 31 | script(src=rev('/js', 'main.js')) 32 | -------------------------------------------------------------------------------- /client/less/lib/bootstrap/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | a& { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | 32 | // Quick fix for labels in buttons 33 | .btn & { 34 | position: relative; 35 | top: -1px; 36 | } 37 | } 38 | 39 | // Colors 40 | // Contextual variations (linked labels get darker on :hover) 41 | 42 | .label-default { 43 | .label-variant(@label-default-bg); 44 | } 45 | 46 | .label-primary { 47 | .label-variant(@label-primary-bg); 48 | } 49 | 50 | .label-success { 51 | .label-variant(@label-success-bg); 52 | } 53 | 54 | .label-info { 55 | .label-variant(@label-info-bg); 56 | } 57 | 58 | .label-warning { 59 | .label-variant(@label-warning-bg); 60 | } 61 | 62 | .label-danger { 63 | .label-variant(@label-danger-bg); 64 | } 65 | -------------------------------------------------------------------------------- /common/app/routes/Jobs/redux/fetch-jobs-saga.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx'; 2 | import { normalize, Schema, arrayOf } from 'normalizr'; 3 | 4 | import { fetchJobsCompleted } from './actions'; 5 | import { fetchJobs } from './types'; 6 | import { handleError } from '../../../redux/types'; 7 | 8 | const job = new Schema('job', { idAttribute: 'id' }); 9 | 10 | export default ({ services }) => ({ dispatch }) => next => { 11 | return function fetchJobsSaga(action) { 12 | if (action.type !== fetchJobs) { 13 | return next(action); 14 | } 15 | 16 | const { payload: id } = action; 17 | const data = { service: 'jobs' }; 18 | if (id) { 19 | data.params = { id }; 20 | } 21 | 22 | return services.readService$(data) 23 | .map(jobs => { 24 | if (!Array.isArray(jobs)) { 25 | jobs = [jobs]; 26 | } 27 | 28 | const { entities, result } = normalize( 29 | { jobs }, 30 | { jobs: arrayOf(job) } 31 | ); 32 | 33 | 34 | return fetchJobsCompleted( 35 | result.jobs[0], 36 | { 37 | entities: entities.job, 38 | results: result.jobs 39 | } 40 | ); 41 | }) 42 | .catch(error => { 43 | return Observable.just({ 44 | type: handleError, 45 | error 46 | }); 47 | }) 48 | .doOnNext(dispatch); 49 | }; 50 | }; 51 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## FreeCodeCamp Pull Request template 2 | Please, go through these steps before you submit a PR. 3 | 4 | 1. Make sure that your PR is not a duplicate. 5 | 2. If not, then make sure that: 6 | 7 | 2.1. You have done your changes in a separate branch. Branches MUST have descriptive names that start with either the `fix/` or `feature/` prefixes. Good examples are: `fix/signin-issue` or `feature/issue-templates`. 8 | 9 | 2.2. You have a descriptive commit message with a short title (first line). 10 | 11 | 2.3. You have only one commit (if not, [squash](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/git-rebase#squashing-multiple-commits-into-one) them into one commit). 12 | 13 | 2.4. `npm test` doesn't throw any error. If it does, fix them first and amend your commit (`git commit --amend`). 14 | 15 | 3. **After** these steps, you're ready to open a pull request. 16 | 17 | 3.1. Your pull request MUST NOT target the `master` branch on FreeCodeCamp's repository. You probably want to target `staging` instead. 18 | 19 | 3.2. Give a descriptive title to your PR. 20 | 21 | 3.3. Provide a description of your changes. 22 | 23 | 3.4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). 24 | 25 | IMPORTANT: Please review the [CONTRIBUTING.md](../CONTRIBUTING.md) file for detailed contributing guidelines. 26 | 27 | **PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING** 28 | -------------------------------------------------------------------------------- /common/app/routes/Hikes/redux/fetch-hikes-saga.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx'; 2 | import { normalize, Schema, arrayOf } from 'normalizr'; 3 | // import debug from 'debug'; 4 | 5 | import types from './types'; 6 | import { fetchHikesCompleted } from './actions'; 7 | import { handleError } from '../../../redux/types'; 8 | 9 | import { findCurrentHike } from './utils'; 10 | 11 | // const log = debug('fcc:fetch-hikes-saga'); 12 | const hike = new Schema('hike', { idAttribute: 'dashedName' }); 13 | 14 | export default ({ services }) => ({ dispatch }) => next => { 15 | return function fetchHikesSaga(action) { 16 | if (action.type !== types.fetchHikes) { 17 | return next(action); 18 | } 19 | 20 | const dashedName = action.payload; 21 | return services.readService$({ service: 'hikes' }) 22 | .map(hikes => { 23 | const { entities, result } = normalize( 24 | { hikes }, 25 | { hikes: arrayOf(hike) } 26 | ); 27 | 28 | hikes = { 29 | entities: entities.hike, 30 | results: result.hikes 31 | }; 32 | 33 | const currentHike = findCurrentHike(hikes, dashedName); 34 | 35 | return fetchHikesCompleted(hikes, currentHike); 36 | }) 37 | .catch(error => { 38 | return Observable.just({ 39 | type: handleError, 40 | error 41 | }); 42 | }) 43 | .doOnNext(dispatch); 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /server/middlewares/error-handlers.js: -------------------------------------------------------------------------------- 1 | import errorHanlder from 'errorhandler'; 2 | import accepts from 'accepts'; 3 | 4 | export default function prodErrorHandler() { 5 | if (process.env.NODE_ENV === 'development') { 6 | return errorHanlder({ log: true }); 7 | } 8 | // error handling in production. 9 | // disabling eslint due to express parity rules for error handlers 10 | return function(err, req, res, next) { // eslint-disable-line 11 | // respect err.status 12 | if (err.status) { 13 | res.statusCode = err.status; 14 | } 15 | 16 | // default status code to 500 17 | if (res.statusCode < 400) { 18 | res.statusCode = 500; 19 | } 20 | 21 | // parse res type 22 | var accept = accepts(req); 23 | var type = accept.type('html', 'json', 'text'); 24 | 25 | var message = 'Oops! Something went wrong. Please try again later'; 26 | if (type === 'html') { 27 | if (typeof req.flash === 'function') { 28 | req.flash('errors', { 29 | msg: message 30 | }); 31 | } 32 | return res.redirect('/map'); 33 | // json 34 | } else if (type === 'json') { 35 | res.setHeader('Content-Type', 'application/json'); 36 | return res.send({ 37 | message: message 38 | }); 39 | // plain text 40 | } else { 41 | res.setHeader('Content-Type', 'text/plain'); 42 | return res.send(message); 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /server/model-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "sources": [ 4 | "loopback/common/models", 5 | "loopback/server/models", 6 | "../common/models", 7 | "./models" 8 | ] 9 | }, 10 | "User": { 11 | "dataSource": "db", 12 | "public": false 13 | }, 14 | "AccessToken": { 15 | "dataSource": "db", 16 | "public": false 17 | }, 18 | "ACL": { 19 | "dataSource": "db", 20 | "public": false 21 | }, 22 | "RoleMapping": { 23 | "dataSource": "db", 24 | "public": false 25 | }, 26 | "Role": { 27 | "dataSource": "db", 28 | "public": false 29 | }, 30 | "Email": { 31 | "dataSource": "mail", 32 | "public": false 33 | }, 34 | "challenge": { 35 | "dataSource": "db", 36 | "public": true 37 | }, 38 | "job": { 39 | "dataSource": "db", 40 | "public": true 41 | }, 42 | "nonprofit": { 43 | "dataSource": "db", 44 | "public": true 45 | }, 46 | "story": { 47 | "dataSource": "db", 48 | "public": true 49 | }, 50 | "pledge": { 51 | "dataSource": "db", 52 | "public": true 53 | }, 54 | "promo": { 55 | "dataSource": "db", 56 | "public": true 57 | }, 58 | "user": { 59 | "dataSource": "db", 60 | "public": true 61 | }, 62 | "userCredential": { 63 | "dataSource": "db", 64 | "public": true 65 | }, 66 | "userIdentity": { 67 | "dataSource": "db", 68 | "public": true 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /server/views/redirect-https.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | script(src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js") 4 | script(src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js") 5 | link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css') 6 | link(rel='stylesheet', href='/css/main.css') 7 | link(rel='stylesheet', href='/css/Vimeo.css') 8 | 9 | include partials/meta 10 | meta(charset='utf-8') 11 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 12 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 13 | meta(name='csrf-token', content=_csrf) 14 | script. 15 | var _vds = _vds || []; 16 | window._vds = _vds; 17 | (function(){ 18 | _vds.push(['setAccountId', 'b350d488f8e3e5f1']); 19 | (function() { 20 | var vds = document.createElement('script'); 21 | vds.type='text/javascript'; 22 | vds.async = true; 23 | vds.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'dn-growing.qbox.me/vds.js'; 24 | var s = document.getElementsByTagName('script')[0]; 25 | s.parentNode.insertBefore(vds, s); 26 | })(); 27 | })(); 28 | body.top-and-bottom-margins 29 | include partials/navbar 30 | .container 31 | .row 32 | .panel.panel-info 33 | p redirecting you... please wait... 34 | script. 35 | setTimeout(function() { 36 | window.location = 'http://freecodecamp.cn' 37 | }, 500); 38 | -------------------------------------------------------------------------------- /server/utils/commit.js: -------------------------------------------------------------------------------- 1 | import dedent from 'dedent'; 2 | import debugFactory from 'debug'; 3 | import { Observable } from 'rx'; 4 | 5 | import commitGoals from './commit-goals.json'; 6 | const debug = debugFactory('fcc:utils/commit'); 7 | 8 | export { commitGoals }; 9 | 10 | export function completeCommitment$(user) { 11 | const { 12 | isFrontEndCert, 13 | isDataVisCert, 14 | isBackEndCert, 15 | isFullStackCert 16 | } = user; 17 | 18 | return Observable.fromNodeCallback(user.pledge, user)() 19 | .flatMap(pledge => { 20 | if (!pledge) { 21 | return Observable.just('No pledge found'); 22 | } 23 | 24 | const { goal } = pledge; 25 | 26 | if ( 27 | (isFrontEndCert && goal === commitGoals.frontEndCert) || 28 | (isDataVisCert && goal === commitGoals.dataVisCert) || 29 | (isBackEndCert && goal === commitGoals.backEndCert) || 30 | (isFullStackCert && goal === commitGoals.fullStackCert) 31 | ) { 32 | debug('marking goal complete'); 33 | pledge.isCompleted = true; 34 | pledge.dateEnded = new Date(); 35 | pledge.formerUserId = pledge.userId; 36 | pledge.userId = null; 37 | return Observable.fromNodeCallback(pledge.save, pledge)(); 38 | } 39 | return Observable.just(dedent` 40 | You have not yet reached your goal of completing the ${goal} 41 | Please retry when you have met the requirements. 42 | `); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /client/commonFramework/output-display.js: -------------------------------------------------------------------------------- 1 | window.common = (function(global) { 2 | const { 3 | CodeMirror, 4 | document: doc, 5 | common = { init: [] } 6 | } = global; 7 | 8 | const { challengeTypes, challengeType = '0' } = common; 9 | 10 | if ( 11 | !CodeMirror || 12 | challengeType !== challengeTypes.JS && 13 | challengeType !== challengeTypes.BONFIRE 14 | ) { 15 | common.updateOutputDisplay = () => {}; 16 | common.appendToOutputDisplay = () => {}; 17 | return common; 18 | } 19 | 20 | var codeOutput = CodeMirror.fromTextArea( 21 | doc.getElementById('codeOutput'), 22 | { 23 | lineNumbers: false, 24 | mode: 'text', 25 | theme: 'monokai', 26 | readOnly: 'nocursor', 27 | lineWrapping: true 28 | } 29 | ); 30 | 31 | codeOutput.setValue(`/** 32 | * Your output will go here. 33 | * Any console.log() -type 34 | * statements will appear in 35 | * your browser\'s DevTools 36 | * JavaScript console. 37 | */`); 38 | 39 | codeOutput.setSize('100%', '100%'); 40 | 41 | common.updateOutputDisplay = function updateOutputDisplay(str = '') { 42 | if (typeof str !== 'string') { 43 | str = JSON.stringify(str); 44 | } 45 | codeOutput.setValue(str); 46 | return str; 47 | }; 48 | 49 | common.appendToOutputDisplay = function appendToOutputDisplay(str = '') { 50 | codeOutput.setValue(codeOutput.getValue() + str); 51 | return str; 52 | }; 53 | 54 | return common; 55 | }(window)); 56 | -------------------------------------------------------------------------------- /server/views/resources/stories.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | h1.text-center 学员感受 4 | hr 5 | .row 6 | .col-xs-12.col-sm-10.col-sm-offset-1 7 | .row 8 | for story in stories 9 | .col-xs-12.col-sm-6.col-md-4 10 | .height-500 11 | a(href=story.linkedin target='_blank') 12 | img.testimonial-image.img-responsive.img-center(src=story.image) 13 | h3.text-center= story.camper 14 | |   15 | if story.linkedin 16 | a.fa.fa-linkedin-square.text-primary(alt="#{story.camper}'s LinkedIn Profile", href=story.linkedin, target='_blank') 17 | else 18 | a.fa.fa-github-square.text-primary(alt="#{story.camper}'s GitHub Profile", href=story.github, target='_blank') 19 | p.small-p.text-justify= story.quote 20 | .col-xs-12.col-sm-10.col-sm-offset-1 21 | if moreStories 22 | .text-center 23 | a.btn.btn-lg.btn-primary.btn-primary-ghost.btn-block(href="/all-stories") 查看更多 24 | .spacer 25 | 26 | if !user 27 | .text-center 28 | a.btn.btn-cta.signup-btn.btn-block(href="/login") 准备好了,Go! 29 | -------------------------------------------------------------------------------- /server/utils/auth.js: -------------------------------------------------------------------------------- 1 | import assign from 'object.assign'; 2 | 3 | const providerHash = { 4 | facebook: ({ id }) => id, 5 | twitter: ({ username }) => username, 6 | linkedin({ _json }) { 7 | return _json && _json.publicProfileUrl || null; 8 | }, 9 | google: ({ id }) => id 10 | }; 11 | 12 | export function getUsernameFromProvider(provider, profile) { 13 | return typeof providerHash[provider] === 'function' ? 14 | providerHash[provider](profile) : 15 | null; 16 | } 17 | 18 | // using es6 argument destructing 19 | export function setProfileFromGithub( 20 | user, 21 | { 22 | profileUrl: githubURL, 23 | username 24 | }, 25 | { 26 | id: githubId, 27 | 'avatar_url': picture, 28 | email: githubEmail, 29 | 'created_at': joinedGithubOn, 30 | blog: website, 31 | location, 32 | name 33 | } 34 | ) { 35 | return assign( 36 | user, 37 | { isGithubCool: true, isMigrationGrandfathered: false }, 38 | { 39 | name, 40 | username: username.toLowerCase(), 41 | location, 42 | joinedGithubOn, 43 | website, 44 | picture, 45 | githubId, 46 | githubURL, 47 | githubEmail, 48 | githubProfile: githubURL 49 | } 50 | ); 51 | } 52 | 53 | export function getFirstImageFromProfile(profile) { 54 | return profile && profile.photos && profile.photos[0] ? 55 | profile.photos[0].value : 56 | null; 57 | } 58 | 59 | export function getSocialProvider(provider) { 60 | return provider.split('-')[0]; 61 | } 62 | -------------------------------------------------------------------------------- /server/views/partials/challenge-modals.jade: -------------------------------------------------------------------------------- 1 | #issue-modal.modal(tabindex='-1') 2 | .modal-dialog.animated.fadeIn.fast-animation 3 | .modal-content 4 | .modal-header.challenge-list-header 你发现了一个bug吗? 5 | a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × 6 | .modal-body.text-center 7 | h3 在你提交一个新的问题之前,请先阅读“求助,我发现了一个bug”,然后浏览这个挑战的其他问题。 8 | a.btn.btn-lg.btn-primary.btn-block#help-ive-found-a-bug-wiki-article(name='_csrf', value=_csrf) 求助,我发现了一个bug 9 | a.btn.btn-lg.btn-primary.btn-block#search-issue(name='_csrf', value=_csrf) 浏览这个挑战的其他问题 10 | a.btn.btn-lg.btn-primary.btn-block#report-issue(name='_csrf', value=_csrf) 创建一个新问题 11 | a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') 取消 12 | 13 | #reset-modal.modal(tabindex='-1') 14 | .modal-dialog.animated.fadeInUp.fast-animation 15 | .modal-content 16 | .modal-header.challenge-list-header 重置你的代码吗? 17 | a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × 18 | .modal-body 19 | h4.text-center 重置将会让你的代码编辑器回到初始状态 20 | h4.text-center 当代码一直无法通过测试时,可以尝试重置,然后重写一遍,这招非常有效。 21 | a.btn.btn-lg.btn-warning.btn-block#reset-button(href='#', data-dismiss='modal', aria-hidden='true') 重置 22 | a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') 取消 23 | --------------------------------------------------------------------------------