├── sass ├── dependencies │ └── _theme.scss ├── _vendor.scss ├── vendor │ ├── _tomorrow.scss │ └── _normalize.scss └── app.scss ├── slides ├── 28.md ├── 11.md ├── 5.md ├── 3.md ├── 6.md ├── 7.md ├── 17.md ├── 29.md ├── 1.md ├── 10.md ├── 25.md ├── 30.md ├── 21.md ├── 15.md ├── 31.md ├── 18.md ├── 8.md ├── 12.md ├── 22.md ├── 16.md ├── 2.md ├── 33.md ├── 35.md ├── 32.md ├── 19.md ├── 13.md ├── 20.md ├── 24.md ├── 34.md ├── 26.md ├── 27.md ├── 9.md ├── 23.md ├── 4.md └── 14.md ├── preview.png ├── src ├── shared │ ├── theme.js │ ├── init.js │ ├── actions │ │ └── SlideActions.js │ ├── components │ │ ├── View.js │ │ ├── Button.js │ │ ├── PLLogo.js │ │ ├── Markdown.js │ │ ├── AppHandler.js │ │ ├── SlideHandler.js │ │ ├── Slide.js │ │ └── SlideProgressBar.js │ ├── routes.js │ ├── Flux.js │ └── stores │ │ └── SlideStore.js ├── server │ ├── init.js │ ├── app.js │ ├── routes │ │ ├── public │ │ │ └── index.js │ │ ├── index.js │ │ ├── middleware.js │ │ └── utils │ │ │ └── renderAppToString.js │ ├── index.js │ └── webpack.js ├── client │ └── app.js └── scripts │ └── buildSlides.js ├── nodemon.json ├── views ├── base.jade └── app.jade ├── .gitignore ├── webpack.config.js ├── .eslintrc ├── webpack.config.dev.js ├── package.json ├── README.md ├── Makefile └── npm-shrinkwrap.json /sass/dependencies/_theme.scss: -------------------------------------------------------------------------------- 1 | $theme: ( 2 | 3 | ); 4 | -------------------------------------------------------------------------------- /slides/28.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | # State is evil 6 | -------------------------------------------------------------------------------- /slides/11.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | # Before React... 6 | -------------------------------------------------------------------------------- /slides/5.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | ## React is *weird* 6 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdlite/the-react-way/HEAD/preview.png -------------------------------------------------------------------------------- /sass/_vendor.scss: -------------------------------------------------------------------------------- 1 | @import 'vendor/normalize'; 2 | @import 'vendor/tomorrow'; 3 | -------------------------------------------------------------------------------- /slides/3.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | ## React is *different* 6 | -------------------------------------------------------------------------------- /slides/6.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | ## `different !== better` 6 | -------------------------------------------------------------------------------- /slides/7.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | ## `conventionalWisdom !== good` 6 | -------------------------------------------------------------------------------- /slides/17.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | ## It doesn't have to be this way 6 | -------------------------------------------------------------------------------- /slides/29.md: -------------------------------------------------------------------------------- 1 | ## "Shared mutable state is the root of all evil" 2 | 3 | Sound familiar? 4 | -------------------------------------------------------------------------------- /src/shared/theme.js: -------------------------------------------------------------------------------- 1 | const theme = { 2 | colors: { 3 | red: '#f00' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /slides/1.md: -------------------------------------------------------------------------------- 1 | The React Way 2 | ============= 3 | 4 | Frontend Awesome Meetup 5 | March 19, 2015 6 | -------------------------------------------------------------------------------- /src/server/init.js: -------------------------------------------------------------------------------- 1 | import sourceMapSupport from 'source-map-support'; 2 | sourceMapSupport.install(); 3 | -------------------------------------------------------------------------------- /slides/10.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | # React: a new and better way to create web applications 6 | -------------------------------------------------------------------------------- /slides/25.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | # Separation of concerns 6 | 7 | *Not* technologies 8 | -------------------------------------------------------------------------------- /slides/30.md: -------------------------------------------------------------------------------- 1 | ## The DOM is a giant pile of global state 2 | 3 | Don't tie your application logic to the DOM 4 | -------------------------------------------------------------------------------- /src/shared/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Polyfills 3 | */ 4 | require('babel/external-helpers'); 5 | import 'babel/polyfill'; 6 | -------------------------------------------------------------------------------- /slides/21.md: -------------------------------------------------------------------------------- 1 | ## The power of `render()` 2 | - No more update logic! 3 | - Views are functions of their props and state 4 | -------------------------------------------------------------------------------- /slides/15.md: -------------------------------------------------------------------------------- 1 | ## At the very least, you're wrangling three separate technologies 2 | 3 | Only point of communication is the DOM 4 | -------------------------------------------------------------------------------- /slides/31.md: -------------------------------------------------------------------------------- 1 | ## The problem with two-way data binding 2 | 3 | - Hard to tell where state "lives" 4 | - Implicit dependencies 5 | -------------------------------------------------------------------------------- /slides/18.md: -------------------------------------------------------------------------------- 1 | --- 2 | centered: true 3 | --- 4 | 5 | # Declarative user interfaces 6 | 7 | Tell the computer what to do, not how to do it 8 | -------------------------------------------------------------------------------- /slides/8.md: -------------------------------------------------------------------------------- 1 | ## Conventions are useful... 2 | 3 | - Benefit from the experience of others 4 | - Learn from their mistakes 5 | - Don't reinvent the wheel 6 | -------------------------------------------------------------------------------- /slides/12.md: -------------------------------------------------------------------------------- 1 | ## Web development is really complicated 2 | 3 | - Many different technologies at different layers 4 | - Even "simple" tasks require lots of coordination 5 | -------------------------------------------------------------------------------- /slides/22.md: -------------------------------------------------------------------------------- 1 | ## Props vs. state 2 | 3 | - props: passed down by the owner 4 | - "immutable" 5 | - unidirectional data flow 6 | - state: local state 7 | - state changes trigger re-renders 8 | -------------------------------------------------------------------------------- /src/shared/actions/SlideActions.js: -------------------------------------------------------------------------------- 1 | import { Actions } from 'flummox'; 2 | 3 | export default class SlideActions extends Actions { 4 | loadSlides(slides) { 5 | return slides; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /slides/16.md: -------------------------------------------------------------------------------- 1 | ## Current "best practices" 2 | 3 | - Object-oriented patterns 4 | - MVC 5 | - Events 6 | - Keep style, logic, and markup isolated 7 | - Use two-way data binding 8 | - Fat models 9 | -------------------------------------------------------------------------------- /slides/2.md: -------------------------------------------------------------------------------- 1 | Andrew Clark 2 | ------------ 3 | 4 | Web Developer at [Parisleaf](http://parisleaf.com) 5 | 6 | [Twitter](https://twitter.com/acdlite)/[GitHub](https://github.com/acdlite): @acdlite 7 | -------------------------------------------------------------------------------- /slides/33.md: -------------------------------------------------------------------------------- 1 | ## Functional programming concepts in React 2 | - Immutable state 3 | - No defensive copying 4 | - More efficient renders 5 | - Components are functions 6 | - Manage side-effects 7 | -------------------------------------------------------------------------------- /src/server/app.js: -------------------------------------------------------------------------------- 1 | // Create koa app 2 | import koa from 'koa'; 3 | const app = koa(); 4 | 5 | // Add routes 6 | import routes from './routes'; 7 | routes(app); 8 | 9 | export default app; 10 | -------------------------------------------------------------------------------- /slides/35.md: -------------------------------------------------------------------------------- 1 | ## Give it a try 2 | 3 | - React is really easy to get started with 4 | - Very simple API 5 | - Everything is a component 6 | - props and state 7 | - Use it in your existing apps today 8 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "lib/", 4 | "views/", 5 | "public/slides.json" 6 | ], 7 | "ext": "js json jade yaml", 8 | "env": { 9 | "NODE_ENV": "development" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /slides/32.md: -------------------------------------------------------------------------------- 1 | ## Most of the time, you don't need two-way data binding 2 | 3 | - The exception, not the rule 4 | - Make it explicit 5 | - Some things are harder, like forms 6 | - But the trade-off is worth it 7 | -------------------------------------------------------------------------------- /src/server/routes/public/index.js: -------------------------------------------------------------------------------- 1 | import Router from 'koa-router'; 2 | const router = new Router(); 3 | 4 | import login from './login'; 5 | import appView from './appView'; 6 | 7 | login(router); 8 | appView(router); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /slides/19.md: -------------------------------------------------------------------------------- 1 | ### A simple React component 2 | 3 | ```js 4 | class Hello extends React.Component { 5 | render() { 6 | return ( 7 | Hello, {this.props.name} 8 | ); 9 | } 10 | } 11 | 12 | React.render(, el); 13 | ``` 14 | -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | // Initialization 2 | require('../shared/init'); 3 | import './init'; 4 | 5 | import app from './app'; 6 | 7 | // Start listening 8 | const port = process.env.PORT || 3000; 9 | app.listen(port); 10 | console.log(`App started listening on port ${port}`); 11 | -------------------------------------------------------------------------------- /slides/13.md: -------------------------------------------------------------------------------- 1 | ## Example: image carousel 2 | 3 | - Data loaded from a database 4 | - Server-side language renders to an HTML string 5 | - HTML is loaded into DOM 6 | - JavaScript reads DOM and recognizes markup 7 | - JavaScript manipulates DOM to animate slider 8 | - Assisted by external CSS 9 | -------------------------------------------------------------------------------- /views/base.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(class='no-js') 3 | head 4 | meta(charset='utf-8') 5 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 6 | 7 | meta(name='viewport', content='width=device-width, initial-scale=1') 8 | 9 | block css 10 | 11 | body 12 | block body 13 | -------------------------------------------------------------------------------- /slides/20.md: -------------------------------------------------------------------------------- 1 | ### A simple React component 2 | 3 | ```js 4 | class Hello extends React.Component { 5 | render() { 6 | return ( 7 | React.createElement("span", null, "Hello, ", this.props.name) 8 | ); 9 | } 10 | } 11 | 12 | React.render(React.createElement(Hello, {name: "world"}), el); 13 | ``` 14 | -------------------------------------------------------------------------------- /slides/24.md: -------------------------------------------------------------------------------- 1 | ## Everything is a component 2 | 3 | - No distinction between views and controllers 4 | - No models necessary: just use plain objects and arrays 5 | - Components are *composable* 6 | - A React app is one big component: 7 | 8 | ```js 9 | React.render(, document.getElementById('#app')); 10 | ``` 11 | -------------------------------------------------------------------------------- /slides/34.md: -------------------------------------------------------------------------------- 1 | ## Flux 2 | - An alternative to MVC 3 | - Flux stores act as centralized global state 4 | - No cascading events 5 | - Shameless plug: [Flummox](https://github.com/acdlite/flummox), a minimal isomorphic Flux library 6 | - No singletons = server-side rendering for free 7 | - Just like vanilla Flux, only better :) 8 | -------------------------------------------------------------------------------- /slides/26.md: -------------------------------------------------------------------------------- 1 | ## The proper separation of concerns is between components, not technologies 2 | 3 | - Everything you need to know about a component is in one module 4 | - Unidirectional data flow is predictable, easy to follow 5 | - Data comes from the owner 6 | - No cascading events, no querying the DOM 7 | - But you can if you want to using lifecycle methods 8 | -------------------------------------------------------------------------------- /src/shared/components/View.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const View = React.createClass({ 4 | render() { 5 | const { className, ...props } = this.props; 6 | 7 | return ( 8 |
12 | ); 13 | } 14 | }); 15 | 16 | export default View; 17 | -------------------------------------------------------------------------------- /slides/27.md: -------------------------------------------------------------------------------- 1 | ### DOM is abstracted away completely 2 | 3 | - Synthetic event system 4 | - `components !== domElements` 5 | - Use components for [routing](https://github.com/rackt/react-router), or for [side-loading data](https://github.com/acdlite/flummox/blob/master/docs/api/FluxComponent.md). 6 | - Use React in non-browser environments 7 | - Server-side rendering 8 | - React Native (!!!) 9 | -------------------------------------------------------------------------------- /src/shared/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router'; 3 | import AppHandler from './components/AppHandler'; 4 | import SlideHandler from './components/SlideHandler'; 5 | 6 | export default ( 7 | 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /slides/9.md: -------------------------------------------------------------------------------- 1 | ## ...but sometimes they get in the way 2 | 3 | - Avoid filter bubbles 4 | - Keep an open mind 5 | - Don't let "best practices" become dogma 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/shared/Flux.js: -------------------------------------------------------------------------------- 1 | import { Flummox } from 'flummox'; 2 | import SlideActions from './actions/SlideActions'; 3 | import SlideStore from './stores/SlideStore'; 4 | 5 | export default class Flux extends Flummox { 6 | constructor({ slides }) { 7 | super(); 8 | 9 | const slideActions = this.createActions('slides', SlideActions); 10 | this.createStore('slides', SlideStore, this); 11 | 12 | slideActions.loadSlides(slides); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/stores/SlideStore.js: -------------------------------------------------------------------------------- 1 | import { Store } from 'flummox'; 2 | 3 | export default class SlideStore extends Store { 4 | constructor(flux) { 5 | super(); 6 | 7 | const actions = flux.getActions('slides'); 8 | this.register(actions.loadSlides, this.handleLoadSlides); 9 | 10 | this.state = { 11 | slides: [], 12 | } 13 | } 14 | 15 | handleLoadSlides(newSlides) { 16 | this.setState({ 17 | slides: newSlides, 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /views/app.jade: -------------------------------------------------------------------------------- 1 | extends ./base.jade 2 | 3 | block css 4 | if (env.NODE_ENV !== 'development') 5 | style 6 | include ../public/css/app.min.css 7 | 8 | block body 9 | #app!=appString 10 | script#slides-data(type='application/json')!=slidesString 11 | 12 | if (env.NODE_ENV == 'development') 13 | script(src='http://0.0.0.0:8081/js/app.js', defer) 14 | else 15 | script(src='/js/app.min.js', defer) 16 | 17 | script(src='//platform.twitter.com/widgets.js', async) 18 | -------------------------------------------------------------------------------- /src/shared/components/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Button = React.createClass({ 4 | getDefaultProps() { 5 | return { 6 | component: 'button', 7 | }; 8 | }, 9 | 10 | render() { 11 | const { className, component: Component, ...props } = this.props; 12 | 13 | return ( 14 | 19 | ); 20 | } 21 | }); 22 | 23 | export default Button; 24 | -------------------------------------------------------------------------------- /src/server/webpack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('../shared/init'); 4 | 5 | // Start webpack server 6 | import webpack from 'webpack'; 7 | import WebpackDevServer from 'webpack-dev-server'; 8 | import config from '../../webpack.config.dev'; 9 | 10 | new WebpackDevServer(webpack(config), { 11 | publicPath: config.output.publicPath, 12 | hot: true, 13 | }) 14 | .listen(8081, 'localhost', function (err, result) { 15 | if (err) console.log(err); 16 | 17 | console.log('Dev server listening at localhost:8081'); 18 | }); 19 | -------------------------------------------------------------------------------- /src/shared/components/PLLogo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import View from './View'; 3 | import Button from './Button'; 4 | 5 | const PLLogo = React.createClass({ 6 | render() { 7 | return ( 8 | 12 | 18 | 19 | ); 20 | } 21 | }); 22 | 23 | export default PLLogo; 24 | -------------------------------------------------------------------------------- /slides/23.md: -------------------------------------------------------------------------------- 1 | ### A stateful component 2 | 3 | ```js 4 | class Timer extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { secondsElapsed: 0 }; 8 | } 9 | 10 | tick() { 11 | this.setState({secondsElapsed: this.state.secondsElapsed + 1}); 12 | } 13 | 14 | componentDidMount() { 15 | this.interval = setInterval(this.tick.bind(this), 1000); 16 | } 17 | 18 | componentWillUnmount() { 19 | clearInterval(this.interval); 20 | } 21 | 22 | render() { 23 | return ( 24 |
Seconds Elapsed: {this.state.secondsElapsed}
25 | ); 26 | } 27 | }); 28 | 29 | React.render(, el); 30 | ``` 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | /lib 30 | /public 31 | -------------------------------------------------------------------------------- /slides/4.md: -------------------------------------------------------------------------------- 1 | ## Whaaa? 2 | 3 | ```jsx 4 | render() { 5 | const { currentSlide, totalSlides } = this.getSlidePositions(); 6 | 7 | return ( 8 | 9 | 10 | 11 | 12 | {currentSlide} / {totalSlides} 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /src/client/app.js: -------------------------------------------------------------------------------- 1 | require('../shared/init'); 2 | 3 | import React from 'react'; 4 | import Router from 'react-router'; 5 | import FluxComponent from 'flummox/component'; 6 | import Flux from '../shared/Flux'; 7 | import routes from '../shared/routes'; 8 | 9 | const slides = JSON.parse( 10 | document.getElementById('slides-data').innerText 11 | ); 12 | 13 | // Initialize flux 14 | const flux = new Flux({ slides }); 15 | 16 | // Render app 17 | Router.run(routes, Router.HistoryLocation, async (Handler, state) => { 18 | React.render( 19 | 20 | 21 | , 22 | document.getElementById('app') 23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | module.exports = { 7 | entry: './src/client/app', 8 | output: { 9 | path: path.join(__dirname, '/public/js/'), 10 | filename: 'app.min.js', 11 | publicPath: '/js/' 12 | }, 13 | plugins: [ 14 | new webpack.DefinePlugin({ 15 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 16 | }), 17 | new webpack.optimize.UglifyJsPlugin({ 18 | compress: { 19 | warnings: false 20 | } 21 | }), 22 | ], 23 | resolve: { 24 | extensions: ['', '.js'] 25 | }, 26 | module: { 27 | loaders: [ 28 | { test: /\.jsx?$/, loaders: ['babel-loader?experimental&externalHelpers'], exclude: /node_modules/ } 29 | ] 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/server/routes/index.js: -------------------------------------------------------------------------------- 1 | import middleware from './middleware'; 2 | import router from 'koa-router'; 3 | import renderAppToString from './utils/renderAppToString'; 4 | 5 | export default function(app) { 6 | middleware(app); 7 | 8 | app.use(router(app)); 9 | 10 | app.get(/.*/, function *() { 11 | let appString, slidesString; 12 | 13 | try { 14 | const result = yield renderAppToString({ 15 | initialPath: this.url, 16 | }); 17 | 18 | appString = result.appString; 19 | slidesString = result.slidesString; 20 | } catch (error) { 21 | if (error.redirect) { 22 | return this.redirect(error.redirect); 23 | } 24 | 25 | throw error; 26 | } 27 | 28 | yield this.render('app', { 29 | appString, 30 | slidesString, 31 | env: process.env, 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "rules": { 9 | "strict": 0, 10 | "quotes": [2, "single"], 11 | "curly": [2, "multi-line"], 12 | "no-underscore-dangle": 0, 13 | "comma-dangle": 0, 14 | "consistent-return": 0, 15 | 16 | // Buggy with JSX 17 | // https://github.com/babel/babel-eslint/issues/29 18 | "no-undef": 1, 19 | 20 | // Doesn't work with chai expect assertions 21 | "no-unused-expressions": 0, 22 | 23 | // Doesn't work inside ES6 template strings 24 | "comma-spacing": 0, 25 | 26 | // Doesn't work with ES6 classes 27 | // https://github.com/babel/babel-eslint/issues/8 28 | "no-unused-vars": 0, 29 | 30 | "no-empty": 0, 31 | "no-use-before-define": 0 32 | }, 33 | "globals": { 34 | "expect": true, 35 | "fetch": true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | module.exports = { 7 | devtool: 'inline-source-map', 8 | entry: [ 9 | 'webpack-dev-server/client?http://localhost:8081', 10 | 'webpack/hot/only-dev-server', 11 | './public/css/app.css', 12 | './src/client/app', 13 | ], 14 | output: { 15 | path: path.join(__dirname, '/public/js/'), 16 | filename: 'app.js', 17 | publicPath: 'http://localhost:8081/js/', 18 | }, 19 | plugins: [ 20 | new webpack.HotModuleReplacementPlugin(), 21 | new webpack.NoErrorsPlugin(), 22 | new webpack.DefinePlugin({ 23 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 24 | 'process.env.BASE_URL': JSON.stringify(process.env.BASE_URL), 25 | }), 26 | ], 27 | resolve: { 28 | extensions: ['', '.js'] 29 | }, 30 | module: { 31 | loaders: [ 32 | { test: /\.jsx?$/, loaders: ['react-hot', 'babel-loader?experimental&externalHelpers'], exclude: /node_modules/ }, 33 | { test: /\.css/, loader: "style-loader!css-loader" }, 34 | ] 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/shared/components/Markdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Remarkable from 'remarkable'; 3 | import hljs from 'highlight.js'; 4 | 5 | const md = new Remarkable({ 6 | html: true, 7 | linkify: true, 8 | typographer: true, 9 | highlight: (str, lang) => { 10 | if (lang && hljs.getLanguage(lang)) { 11 | try { 12 | return hljs.highlight(lang, str).value; 13 | } catch (err) {} 14 | } 15 | 16 | try { 17 | return hljs.highlightAuto(str).value; 18 | } catch (err) {} 19 | 20 | return ''; // use external default escaping 21 | } 22 | }); 23 | 24 | const Markdown = React.createClass({ 25 | getDefaultProps() { 26 | return { 27 | src: '', 28 | }; 29 | }, 30 | 31 | componentDidMount() { 32 | if (typeof twttr !== 'undefined') twttr.widgets.load(); 33 | }, 34 | 35 | render() { 36 | const { src, ...props } = this.props; 37 | const html = md.render(src); 38 | 39 | return ( 40 |
44 | ); 45 | } 46 | }); 47 | 48 | export default Markdown; 49 | -------------------------------------------------------------------------------- /src/shared/components/AppHandler.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Flux from 'flummox/component'; 3 | import { RouteHandler } from 'react-router'; 4 | import SlideProgressBar from './SlideProgressBar'; 5 | import PLLogo from './PLLogo'; 6 | import View from './View'; 7 | 8 | const AppHandler = React.createClass({ 9 | render() { 10 | return ( 11 | 17 | 18 | ({ 20 | slide: store.state.slides[this.props.params.currentSlide - 1], 21 | }) 22 | }}> 23 | 24 | 25 | 26 | ({ 28 | totalSlides: store.state.slides.length, 29 | }) 30 | }}> 31 | 32 | 33 | 34 | ); 35 | } 36 | }); 37 | 38 | export default AppHandler; 39 | -------------------------------------------------------------------------------- /src/shared/components/SlideHandler.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import Flux from 'flummox/component'; 3 | import Slide from './Slide'; 4 | import View from './View'; 5 | 6 | const { CSSTransitionGroup } = React.addons; 7 | 8 | const SlideHandler = React.createClass({ 9 | 10 | statics: { 11 | willTransitionTo(transition, params, query) { 12 | const { currentSlide } = params; 13 | const integerCurrentSlide = parseInt(currentSlide); 14 | 15 | if (`${integerCurrentSlide}` !== currentSlide) { 16 | return transition.redirect(Number.isNaN(integerCurrentSlide) 17 | ? '/1' 18 | : `/${integerCurrentSlide}` 19 | ); 20 | } 21 | } 22 | }, 23 | 24 | render() { 25 | const { slide } = this.props; 26 | 27 | return ( 28 | 36 | 37 | 38 | ); 39 | } 40 | }); 41 | 42 | export default SlideHandler; 43 | -------------------------------------------------------------------------------- /src/scripts/buildSlides.js: -------------------------------------------------------------------------------- 1 | require('../shared/init'); 2 | 3 | import frontmatter from 'front-matter'; 4 | import fs from 'fs'; 5 | import path from 'path'; 6 | 7 | const slideDir = 'slides'; 8 | 9 | const outline = fs.createWriteStream('public/outline.txt'); 10 | 11 | const slides = fs.readdirSync('slides') 12 | .filter(filename => filename.endsWith('.md')) 13 | .sort(sortAlphabetically) 14 | .map((filename, i, filenames) => { 15 | const slideNumber = i + 1; 16 | const fullFilename = path.join(slideDir, filename); 17 | 18 | const contents = fs.readFileSync(fullFilename, 'utf8'); 19 | 20 | const { attributes, body } = frontmatter(contents); 21 | 22 | outline.write(contents); 23 | if (slideNumber !== filenames.length) { 24 | outline.write(`\n\n*** ${slideNumber} ***\n\n`); 25 | } 26 | 27 | return { 28 | body, 29 | slideNumber, 30 | ...attributes, 31 | } 32 | }); 33 | 34 | const output = fs.writeFileSync('public/slides.json', JSON.stringify(slides)); 35 | 36 | function sortAlphabetically(a, b) { 37 | a = parseInt(a); 38 | b = parseInt(b); 39 | if (a < b) return -1; 40 | if (a > b) return 1; 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/server/routes/middleware.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import bodyParser from 'koa-bodyparser'; 4 | import gzip from 'koa-gzip'; 5 | import fresh from 'koa-fresh'; 6 | import conditional from 'koa-conditional-get'; 7 | import etag from 'koa-etag'; 8 | import serve from 'koa-static'; 9 | import json from 'koa-json'; 10 | import qs from 'koa-qs'; 11 | import views from 'koa-views'; 12 | 13 | export default function(app) { 14 | // Error handling 15 | app.use(function *(next) { 16 | try { 17 | yield next; 18 | } catch (error) { 19 | this.status = error.status || 500; 20 | this.body = error.message; 21 | this.app.emit('error', error, this); 22 | } 23 | }); 24 | 25 | // Body parsing 26 | app.use(bodyParser()); 27 | 28 | // gzip compression 29 | app.use(gzip()); 30 | 31 | // Conditional GET 32 | app.use(conditional()); 33 | 34 | // Freshness testing 35 | app.use(fresh()); 36 | 37 | // etags 38 | app.use(etag()); 39 | 40 | // Serve static assets from `public` directory 41 | app.use(serve('public')); 42 | 43 | // Pretty-print JSON responses 44 | if (process.env.NODE_ENV === 'development') app.use(json()); 45 | 46 | // Add nesting support to query strings 47 | qs(app); 48 | 49 | // View rendering 50 | app.use(views(path.join(process.cwd(), 'views'), { 51 | cache: true, 52 | default: 'jade', 53 | })); 54 | } 55 | -------------------------------------------------------------------------------- /src/shared/components/Slide.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import View from './View'; 3 | import Markdown from './Markdown'; 4 | 5 | const style = { 6 | slide: { 7 | flexGrow: 1, 8 | justifyContent: 'center', 9 | padding: '3em', 10 | backgroundSize: 'cover', 11 | backgroundPosition: 'center', 12 | } 13 | }; 14 | 15 | const Slide = React.createClass({ 16 | render() { 17 | const { slide } = this.props; 18 | 19 | if (!slide) return null; 20 | 21 | const { 22 | slideNumber, 23 | body, 24 | verticalAlign, 25 | centered, 26 | image, 27 | ...props } = slide; 28 | 29 | let alignItems; 30 | switch (verticalAlign) { 31 | case 'bottom': 32 | alignItems = 'flex-end'; 33 | break; 34 | case 'top': 35 | alignItems = 'flex-start'; 36 | break; 37 | default: 38 | alignItems = 'center'; 39 | } 40 | 41 | const slideStyle = { 42 | textAlign: centered ? 'center' : 'left', 43 | alignItems 44 | }; 45 | 46 | if (image) slideStyle.backgroundImage = `url(${image})`; 47 | 48 | return ( 49 | 53 | 56 | 57 | ); 58 | } 59 | }); 60 | 61 | export default Slide; 62 | -------------------------------------------------------------------------------- /slides/14.md: -------------------------------------------------------------------------------- 1 | Even for this "simple" task, update logic is complicated: 2 | 3 | ```js 4 | Slick.prototype.addSlide = Slick.prototype.slickAdd = function(markup, index, addBefore) { 5 | 6 | var _ = this; 7 | 8 | if (typeof(index) === 'boolean') { 9 | addBefore = index; 10 | index = null; 11 | } else if (index < 0 || (index >= _.slideCount)) { 12 | return false; 13 | } 14 | 15 | _.unload(); 16 | 17 | if (typeof(index) === 'number') { 18 | if (index === 0 && _.$slides.length === 0) { 19 | $(markup).appendTo(_.$slideTrack); 20 | } else if (addBefore) { 21 | $(markup).insertBefore(_.$slides.eq(index)); 22 | } else { 23 | $(markup).insertAfter(_.$slides.eq(index)); 24 | } 25 | } else { 26 | if (addBefore === true) { 27 | $(markup).prependTo(_.$slideTrack); 28 | } else { 29 | $(markup).appendTo(_.$slideTrack); 30 | } 31 | } 32 | 33 | _.$slides = _.$slideTrack.children(this.options.slide); 34 | 35 | _.$slideTrack.children(this.options.slide).detach(); 36 | 37 | _.$slideTrack.append(_.$slides); 38 | 39 | _.$slides.each(function(index, element) { 40 | $(element).attr('data-slick-index', index); 41 | }); 42 | 43 | _.$slidesCache = _.$slides; 44 | 45 | _.reinit(); 46 | 47 | }; 48 | ``` 49 | 50 | (I'm not picking on the Slick library — it's actually really good.) 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-react-way", 3 | "version": "0.0.0", 4 | "description": "Presentation for Frontend Awesome Meetup, March 2015", 5 | "main": "lib/server", 6 | "scripts": { 7 | "prestart": "make fast-build", 8 | "start": "node lib/server" 9 | }, 10 | "author": "Andrew Clark ", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "autoprefixer": "~5.1.0", 14 | "babel": "~4.7.16", 15 | "babel-core": "~4.7.16", 16 | "babel-eslint": "~2.0.2", 17 | "babel-loader": "~4.2.0", 18 | "clean-css": "~3.1.8", 19 | "css-loader": "~0.9.1", 20 | "eslint": "~0.17.1", 21 | "front-matter": "~1.0.0", 22 | "json-sass": "~1.3.3", 23 | "react-hot-loader": "~1.2.3", 24 | "source-map-support": "~0.2.10", 25 | "style-loader": "~0.8.3", 26 | "watch": "~0.14.0", 27 | "webpack": "~1.7.3", 28 | "webpack-dev-server": "~1.7.0" 29 | }, 30 | "dependencies": { 31 | "flummox": "~2.13.2", 32 | "highlight.js": "~8.4.0", 33 | "htmlescape": "~1.0.0", 34 | "keymaster": "~1.6.2", 35 | "koa": "~0.18.1", 36 | "koa-bodyparser": "~1.4.1", 37 | "koa-conditional-get": "~1.0.2", 38 | "koa-etag": "~2.0.0", 39 | "koa-fresh": "0.0.3", 40 | "koa-gzip": "~0.1.0", 41 | "koa-json": "~1.1.1", 42 | "koa-qs": "~2.0.0", 43 | "koa-router": "~4.2.0", 44 | "koa-static": "~1.4.9", 45 | "koa-views": "~2.1.2", 46 | "react": "~0.12.2", 47 | "react-router": "~0.12.4", 48 | "remarkable": "~1.6.0", 49 | "then-jade": "~2.2.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/server/routes/utils/renderAppToString.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import React from 'react'; 3 | import Router from 'react-router'; 4 | import FluxComponent from 'flummox/component'; 5 | import routes from '../../../shared/routes'; 6 | import Flux from '../../../shared/Flux'; 7 | import htmlescape from 'htmlescape'; 8 | 9 | const slidesString = fs.readFileSync('public/slides.json', 'utf8'); 10 | const slides = JSON.parse(slidesString); 11 | 12 | const escapedSlidesString = htmlescape(slides); 13 | 14 | export default async ({ initialPath }) => { 15 | 16 | const router = Router.create({ 17 | routes: routes, 18 | location: initialPath, 19 | onError: error => { 20 | throw error; 21 | }, 22 | onAbort: abortReason => { 23 | const error = new Error(); 24 | 25 | if (abortReason.constructor.name === 'Redirect') { 26 | const { to, params, query } = abortReason; 27 | const url = router.makePath(to, params, query); 28 | error.redirect = url; 29 | } 30 | 31 | throw error; 32 | } 33 | }); 34 | 35 | const flux = new Flux({ slides }); 36 | 37 | const { Handler, state } = await new Promise((resolve, reject) => { 38 | router.run((_Handler, _state) => 39 | resolve({ Handler: _Handler, state: _state }) 40 | ); 41 | }); 42 | 43 | const appString = React.renderToString( 44 | 45 | 46 | 47 | ); 48 | 49 | return { 50 | appString, 51 | slidesString: escapedSlidesString, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The React Way 2 | ============= 3 | 4 | An isomorphic React slide deck, about React. 5 | 6 | [Watch the video.](https://www.youtube.com/watch?v=HOpyRiXB5WQ) 7 | 8 | Presented at [Frontend Awesome Meetup](http://www.meetup.com/Gainesville-Front-End-Dev-Meetup/) 9 | March 19, 2015 10 | 11 | ![preview](preview.png) 12 | 13 | Running the slide deck 14 | ---------------------- 15 | 16 | Only tested in Chrome. Probably won't work in Safari because I didn't care about adding prefixes to flexbox properties. 17 | 18 | Run `npm start` to start the server. Then navigate to http://localhost:3000. (You could use wget to convert this to a static site.) 19 | 20 | Run `make watch` instead to start the server in development mode, with hot reloading. 21 | 22 | How it's made 23 | ------------- 24 | 25 | The slides are generated from Markdown documents located in `slides`. A build task combines the slides into a JSON document, which is embedded into the page by the server to be loaded by the React app. 26 | 27 | - [React](http://facebook.github.io/react/) 28 | - [Flummox](https://github.com/acdlite/flummox) 29 | - Overkill in this case, but used for illustration purposes. 30 | - [React Router](https://github.com/rackt/react-router) 31 | - [React Hot Loader](http://gaearon.github.io/react-hot-loader/) 32 | - [Remarkable](https://github.com/jonschlinkert/remarkable) 33 | - [front-matter](https://github.com/jxson/front-matter) 34 | - [Sass/Libsass](http://sass-lang.com/) 35 | - [Make](http://www.gnu.org/software/make/manual/make.html) 36 | - [Babel](https://babeljs.io/) 37 | - [iojs](https://iojs.org) 38 | - [koa](http://koajs.com/) 39 | 40 | ...and more. 41 | -------------------------------------------------------------------------------- /sass/vendor/_tomorrow.scss: -------------------------------------------------------------------------------- 1 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 2 | 3 | /* Tomorrow Comment */ 4 | .hljs-comment { 5 | color: #8e908c; 6 | } 7 | 8 | /* Tomorrow Red */ 9 | .hljs-variable, 10 | .hljs-attribute, 11 | .hljs-tag, 12 | .hljs-regexp, 13 | .ruby .hljs-constant, 14 | .xml .hljs-tag .hljs-title, 15 | .xml .hljs-pi, 16 | .xml .hljs-doctype, 17 | .html .hljs-doctype, 18 | .css .hljs-id, 19 | .css .hljs-class, 20 | .css .hljs-pseudo { 21 | color: #c82829; 22 | } 23 | 24 | /* Tomorrow Orange */ 25 | .hljs-number, 26 | .hljs-preprocessor, 27 | .hljs-pragma, 28 | .hljs-built_in, 29 | .hljs-literal, 30 | .hljs-params, 31 | .hljs-constant { 32 | color: #f5871f; 33 | } 34 | 35 | /* Tomorrow Yellow */ 36 | .ruby .hljs-class .hljs-title, 37 | .css .hljs-rules .hljs-attribute { 38 | color: #eab700; 39 | } 40 | 41 | /* Tomorrow Green */ 42 | .hljs-string, 43 | .hljs-value, 44 | .hljs-inheritance, 45 | .hljs-header, 46 | .ruby .hljs-symbol, 47 | .xml .hljs-cdata { 48 | color: #718c00; 49 | } 50 | 51 | /* Tomorrow Aqua */ 52 | .hljs-title, 53 | .css .hljs-hexcolor { 54 | color: #3e999f; 55 | } 56 | 57 | /* Tomorrow Blue */ 58 | .hljs-function, 59 | .python .hljs-decorator, 60 | .python .hljs-title, 61 | .ruby .hljs-function .hljs-title, 62 | .ruby .hljs-title .hljs-keyword, 63 | .perl .hljs-sub, 64 | .javascript .hljs-title, 65 | .coffeescript .hljs-title { 66 | color: #4271ae; 67 | } 68 | 69 | /* Tomorrow Purple */ 70 | .hljs-keyword, 71 | .javascript .hljs-function { 72 | color: #8959a8; 73 | } 74 | 75 | .hljs { 76 | display: block; 77 | overflow-x: auto; 78 | background: white; 79 | color: #4d4d4c; 80 | padding: 0.5em; 81 | -webkit-text-size-adjust: none; 82 | } 83 | 84 | .coffeescript .javascript, 85 | .javascript .xml, 86 | .tex .hljs-formula, 87 | .xml .javascript, 88 | .xml .vbscript, 89 | .xml .css, 90 | .xml .hljs-cdata { 91 | opacity: 0.5; 92 | } 93 | -------------------------------------------------------------------------------- /sass/app.scss: -------------------------------------------------------------------------------- 1 | @import "vendor"; 2 | @import url(//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro); 3 | 4 | html { 5 | font-family: 'Source Sans Pro', sans-serif; 6 | background-color: #146cce; 7 | color: #fff; 8 | } 9 | 10 | a { 11 | color: #fff; 12 | } 13 | 14 | pre, code { 15 | font-family: 'Source Code Pro'; 16 | } 17 | 18 | .Button { 19 | display: inline; 20 | background: transparent; 21 | -webkit-appearance: none; 22 | -moz-appearance: none; 23 | -webkit-tap-highlight-color: transparent; 24 | outline: none; 25 | border: none; 26 | padding: 0; 27 | margin: 0; 28 | overflow: visible; 29 | text-decoration: none; 30 | color: inherit; 31 | text-align: start; 32 | white-space: normal; 33 | 34 | &:hover { 35 | cursor: pointer; 36 | } 37 | } 38 | 39 | // Muahahahahaha 40 | .View { 41 | box-sizing: border-box; 42 | display: flex; 43 | position: relative; 44 | } 45 | 46 | .Slide { 47 | position: absolute; 48 | height: 100%; 49 | width: 100%; 50 | top: 0; 51 | left: 0; 52 | 53 | @media (min-width: 40em) { 54 | font-size: 2.5vw; 55 | } 56 | 57 | pre code { 58 | @extend .hljs; 59 | max-height: 16em; 60 | font-size: 80%; 61 | } 62 | } 63 | 64 | .SlideProgressBar { 65 | &-wrapper { 66 | transform: translateY(100%); 67 | transition: transform 150ms; 68 | } 69 | 70 | &:hover &-wrapper { 71 | transform: none; 72 | } 73 | } 74 | 75 | .SlideTransition--fade{ 76 | $transition-duration: 150ms; 77 | 78 | &-enter { 79 | opacity: 0.01; 80 | transition: opacity $transition-duration ease-in; 81 | } 82 | 83 | &-enter.SlideTransition--fade-enter-active { 84 | opacity: 1; 85 | } 86 | 87 | &-leave { 88 | opacity: 1; 89 | transition: opacity $transition-duration ease-in; 90 | } 91 | 92 | &-leave.SlideTransition--fade-leave-active { 93 | opacity: 0.01; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BABEL_CMD = node_modules/.bin/babel 2 | ESLINT_CMD = node_modules/.bin/eslint 3 | WEBPACK_CMD = node_modules/.bin/webpack 4 | SASS_CMD = sassc 5 | WATCH_CMD = node_modules/.bin/watch 6 | AUTOPREFIXER_CMD = node_modules/.bin/autoprefixer 7 | CLEANCSS_CMD = node_modules/.bin/cleancss 8 | JSON_SASS_CMD = node_modules/.bin/json-sass 9 | 10 | BABEL_ARGS = --experimental --external-helpers --source-maps-inline --blacklist regenerator,es6.blockScoping --optional asyncToGenerator 11 | 12 | SRC_JS = $(shell find src -name "*.js") 13 | LIB_JS = $(patsubst src/%.js,lib/%.js,$(SRC_JS)) 14 | 15 | # Build application 16 | build: build-dev minify-css 17 | 18 | build-dev: js webpack css slides 19 | 20 | # Build application quickly 21 | # Faster on first build, but not after that 22 | fast-build: fast-js build 23 | 24 | # Watch for changes 25 | watch: minify-css 26 | @NODE_ENV=development $(MAKE) -j5 dev-server webpack-server watch-css watch-js watch-slides 27 | 28 | dev-server: $(LIB_JS) 29 | nodemon ./lib/server | bunyan 30 | 31 | webpack-server: $(LIB_JS) 32 | node ./lib/server/webpack 33 | 34 | slides: public/slides.json 35 | 36 | public/slides.json: js 37 | node ./lib/scripts/buildSlides.js 38 | 39 | watch-slides: 40 | $(WATCH_CMD) "node ./lib/scripts/buildSlides.js" slides 41 | 42 | # Clean up 43 | clean: 44 | rm -rf lib 45 | rm -rf public/js/ 46 | rm -rf public/css/ 47 | rm -f sass/_theme.scss 48 | rm -f public/slides.json 49 | 50 | webpack: public/js/app.js 51 | 52 | public/js/app.js: $(SRC_JS) 53 | $(WEBPACK_CMD) 54 | 55 | # Transpile JavaScript using Babel 56 | js: $(LIB_JS) 57 | 58 | $(LIB_JS): lib/%.js: src/%.js 59 | mkdir -p $(dir $@) && $(BABEL_CMD) $< -o $@ $(BABEL_ARGS) 60 | 61 | fast-js: 62 | $(BABEL_CMD) src -d lib $(BABEL_ARGS) 63 | 64 | watch-js: 65 | $(BABEL_CMD) src -d lib $(BABEL_ARGS) -w 66 | 67 | # Compile Sass 68 | css: public/css/app.css 69 | 70 | minify-css: css public/css/app.min.css 71 | 72 | public/css/app.css: sass/app.scss theme 73 | mkdir -p $(dir $@) && $(SASS_CMD) -m $< | $(AUTOPREFIXER_CMD) > $@ 74 | 75 | public/css/app.min.css: public/css/app.css 76 | $(CLEANCSS_CMD) $< > $@ 77 | 78 | watch-css: 79 | $(WATCH_CMD) "mkdir -p public/css && $(SASS_CMD) -m sass/app.scss | $(AUTOPREFIXER_CMD) > public/css/app.css" sass 80 | 81 | theme: sass/dependencies/_theme.scss 82 | 83 | sass/dependencies/_theme.scss: lib/shared/theme.js 84 | mkdir -p $(dir $@) && $(JSON_SASS_CMD) -i $< \ 85 | | sed '1s/^/$$theme: /' \ 86 | > $@ 87 | 88 | .PHONY: build build-dev test fast-build watch dev-server webpack-server clean 89 | .PHONY: webpack js fast-js weatch-js css minify-css watch-css theme 90 | -------------------------------------------------------------------------------- /src/shared/components/SlideProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigation, Link } from 'react-router'; 3 | import View from './View'; 4 | import Button from './Button'; 5 | 6 | const style = { 7 | bar: { 8 | position: 'absolute', 9 | bottom: 0, 10 | width: '100%', 11 | }, 12 | 13 | button: { 14 | padding: '0.75em', 15 | fontSize: '1.5em', 16 | position: 'relative', 17 | top: '0.125em', 18 | }, 19 | 20 | wrapper: { 21 | justifyContent: 'space-between', 22 | flexGrow: 1, 23 | }, 24 | }; 25 | 26 | const SlideProgressBar = React.createClass({ 27 | mixins: [Navigation], 28 | 29 | componentDidMount() { 30 | const key = require('keymaster'); 31 | key('space', this.nextSlide); 32 | key('right', this.nextSlide); 33 | key('left', this.prevSlide); 34 | }, 35 | 36 | componentWillUnmount() { 37 | const key = require('keymaster'); 38 | key.unbind('space'); 39 | key.unbind('right'); 40 | key.unbind('left'); 41 | }, 42 | 43 | getNextSlide() { 44 | const { currentSlide, totalSlides } = this.getSlidePositions(); 45 | 46 | return (currentSlide >= totalSlides) ? 1 : currentSlide + 1; 47 | }, 48 | 49 | getPrevSlide() { 50 | const { currentSlide, totalSlides } = this.getSlidePositions(); 51 | 52 | return (currentSlide <= 1) ? totalSlides : currentSlide - 1; 53 | }, 54 | 55 | nextSlide(event) { 56 | event.preventDefault(); 57 | 58 | this.goToSlide(this.getNextSlide()); 59 | }, 60 | 61 | prevSlide(event) { 62 | event.preventDefault(); 63 | 64 | this.goToSlide(this.getPrevSlide()); 65 | }, 66 | 67 | goToSlide(slideNumber) { 68 | this.transitionTo(`/${slideNumber}`); 69 | }, 70 | 71 | getSlidePositions() { 72 | const currentSlide = parseInt(this.props.currentSlide); 73 | const totalSlides = parseInt(this.props.totalSlides); 74 | 75 | return { currentSlide, totalSlides }; 76 | }, 77 | 78 | progress(currentSlide, totalSlides) { 79 | // let bullets = []; 80 | // 81 | // for (let i = 0; i < totalSlides; i++) { 82 | // const n = i + 1; 83 | // 84 | // bullets.push( 85 | // 86 | // ); 87 | // } 88 | // 89 | // return bullets; 90 | 91 | return ( 92 | 93 | {currentSlide} / {totalSlides} 94 | 95 | ); 96 | }, 97 | 98 | render() { 99 | const { currentSlide, totalSlides } = this.getSlidePositions(); 100 | 101 | return ( 102 | 103 | 104 | 105 | {this.progress(currentSlide, totalSlides)} 106 | 107 | 108 | 109 | ); 110 | } 111 | 112 | }); 113 | 114 | const Bullet = React.createClass({ 115 | render() { 116 | const { currentSlide, slideNumber } = this.props; 117 | 118 | return ( 119 | 128 | ) 129 | } 130 | }) 131 | 132 | export default SlideProgressBar; 133 | -------------------------------------------------------------------------------- /sass/vendor/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-react-way", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "flummox": { 6 | "version": "2.13.2", 7 | "from": "flummox@>=2.0.0 <3.0.0", 8 | "dependencies": { 9 | "eventemitter3": { 10 | "version": "0.1.6", 11 | "from": "eventemitter3@>=0.1.6 <0.2.0", 12 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-0.1.6.tgz" 13 | }, 14 | "flux": { 15 | "version": "2.0.1", 16 | "from": "flux@>=2.0.1 <2.1.0", 17 | "resolved": "https://registry.npmjs.org/flux/-/flux-2.0.1.tgz" 18 | }, 19 | "object-assign": { 20 | "version": "2.0.0", 21 | "from": "object-assign@>=2.0.0 <2.1.0", 22 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.0.0.tgz" 23 | }, 24 | "uniqueid": { 25 | "version": "0.1.0", 26 | "from": "uniqueid@>=0.1.0 <0.2.0", 27 | "resolved": "https://registry.npmjs.org/uniqueid/-/uniqueid-0.1.0.tgz" 28 | } 29 | } 30 | }, 31 | "highlight.js": { 32 | "version": "8.4.0", 33 | "from": "highlight.js@*", 34 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-8.4.0.tgz" 35 | }, 36 | "htmlescape": { 37 | "version": "1.0.0", 38 | "from": "htmlescape@*", 39 | "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.0.0.tgz" 40 | }, 41 | "keymaster": { 42 | "version": "1.6.2", 43 | "from": "keymaster@*", 44 | "resolved": "https://registry.npmjs.org/keymaster/-/keymaster-1.6.2.tgz" 45 | }, 46 | "koa": { 47 | "version": "0.18.1", 48 | "from": "koa@*", 49 | "resolved": "https://registry.npmjs.org/koa/-/koa-0.18.1.tgz", 50 | "dependencies": { 51 | "accepts": { 52 | "version": "1.2.5", 53 | "from": "accepts@>=1.2.2 <2.0.0", 54 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.5.tgz", 55 | "dependencies": { 56 | "negotiator": { 57 | "version": "0.5.1", 58 | "from": "negotiator@0.5.1", 59 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.1.tgz" 60 | } 61 | } 62 | }, 63 | "co": { 64 | "version": "4.5.1", 65 | "from": "co@>=4.4.0 <5.0.0", 66 | "resolved": "https://registry.npmjs.org/co/-/co-4.5.1.tgz" 67 | }, 68 | "composition": { 69 | "version": "2.1.1", 70 | "from": "composition@>=2.1.1 <3.0.0", 71 | "resolved": "https://registry.npmjs.org/composition/-/composition-2.1.1.tgz", 72 | "dependencies": { 73 | "memorizer": { 74 | "version": "1.0.0", 75 | "from": "memorizer@>=1.0.0 <2.0.0", 76 | "resolved": "https://registry.npmjs.org/memorizer/-/memorizer-1.0.0.tgz" 77 | }, 78 | "native-or-bluebird": { 79 | "version": "1.2.0", 80 | "from": "native-or-bluebird@>=1.1.2 <2.0.0", 81 | "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz" 82 | } 83 | } 84 | }, 85 | "content-disposition": { 86 | "version": "0.5.0", 87 | "from": "content-disposition@>=0.5.0 <0.6.0", 88 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" 89 | }, 90 | "content-type": { 91 | "version": "1.0.1", 92 | "from": "content-type@>=1.0.0 <2.0.0", 93 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" 94 | }, 95 | "cookies": { 96 | "version": "0.5.0", 97 | "from": "cookies@>=0.5.0 <0.6.0", 98 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.5.0.tgz", 99 | "dependencies": { 100 | "keygrip": { 101 | "version": "1.0.1", 102 | "from": "keygrip@>=1.0.0 <1.1.0", 103 | "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.1.tgz" 104 | } 105 | } 106 | }, 107 | "debug": { 108 | "version": "2.1.3", 109 | "from": "debug@*", 110 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.3.tgz", 111 | "dependencies": { 112 | "ms": { 113 | "version": "0.7.0", 114 | "from": "ms@0.7.0", 115 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.0.tgz" 116 | } 117 | } 118 | }, 119 | "delegates": { 120 | "version": "0.1.0", 121 | "from": "delegates@0.1.0", 122 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-0.1.0.tgz" 123 | }, 124 | "destroy": { 125 | "version": "1.0.3", 126 | "from": "destroy@>=1.0.3 <2.0.0", 127 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" 128 | }, 129 | "error-inject": { 130 | "version": "1.0.0", 131 | "from": "error-inject@>=1.0.0 <1.1.0", 132 | "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz" 133 | }, 134 | "escape-html": { 135 | "version": "1.0.1", 136 | "from": "escape-html@>=1.0.1 <1.1.0", 137 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" 138 | }, 139 | "fresh": { 140 | "version": "0.2.4", 141 | "from": "fresh@>=0.2.4 <0.3.0", 142 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz" 143 | }, 144 | "http-assert": { 145 | "version": "1.1.1", 146 | "from": "http-assert@>=1.1.0 <2.0.0", 147 | "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.1.1.tgz", 148 | "dependencies": { 149 | "deep-equal": { 150 | "version": "1.0.0", 151 | "from": "deep-equal@>=1.0.0 <1.1.0", 152 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.0.tgz" 153 | } 154 | } 155 | }, 156 | "http-errors": { 157 | "version": "1.3.1", 158 | "from": "http-errors@>=1.2.8 <2.0.0", 159 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", 160 | "dependencies": { 161 | "inherits": { 162 | "version": "2.0.1", 163 | "from": "inherits@>=2.0.1 <2.1.0", 164 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" 165 | } 166 | } 167 | }, 168 | "koa-compose": { 169 | "version": "2.3.0", 170 | "from": "koa-compose@>=2.3.0 <3.0.0", 171 | "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-2.3.0.tgz" 172 | }, 173 | "koa-is-json": { 174 | "version": "1.0.0", 175 | "from": "koa-is-json@>=1.0.0 <2.0.0", 176 | "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz" 177 | }, 178 | "mime-types": { 179 | "version": "2.0.10", 180 | "from": "mime-types@>=2.0.7 <3.0.0", 181 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.10.tgz", 182 | "dependencies": { 183 | "mime-db": { 184 | "version": "1.8.0", 185 | "from": "mime-db@>=1.8.0 <1.9.0", 186 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.8.0.tgz" 187 | } 188 | } 189 | }, 190 | "on-finished": { 191 | "version": "2.2.0", 192 | "from": "on-finished@>=2.1.0 <3.0.0", 193 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.0.tgz", 194 | "dependencies": { 195 | "ee-first": { 196 | "version": "1.1.0", 197 | "from": "ee-first@1.1.0", 198 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" 199 | } 200 | } 201 | }, 202 | "only": { 203 | "version": "0.0.2", 204 | "from": "only@0.0.2", 205 | "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz" 206 | }, 207 | "parseurl": { 208 | "version": "1.3.0", 209 | "from": "parseurl@>=1.3.0 <2.0.0", 210 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" 211 | }, 212 | "statuses": { 213 | "version": "1.2.1", 214 | "from": "statuses@>=1.2.0 <2.0.0", 215 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" 216 | }, 217 | "type-is": { 218 | "version": "1.6.1", 219 | "from": "type-is@>=1.5.5 <2.0.0", 220 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.1.tgz", 221 | "dependencies": { 222 | "media-typer": { 223 | "version": "0.3.0", 224 | "from": "media-typer@0.3.0", 225 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" 226 | } 227 | } 228 | }, 229 | "vary": { 230 | "version": "1.0.0", 231 | "from": "vary@>=1.0.0 <2.0.0", 232 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.0.tgz" 233 | } 234 | } 235 | }, 236 | "koa-bodyparser": { 237 | "version": "1.4.1", 238 | "from": "koa-bodyparser@*", 239 | "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-1.4.1.tgz", 240 | "dependencies": { 241 | "co-body": { 242 | "version": "1.1.0", 243 | "from": "co-body@>=1.1.0 <1.2.0", 244 | "resolved": "https://registry.npmjs.org/co-body/-/co-body-1.1.0.tgz", 245 | "dependencies": { 246 | "qs": { 247 | "version": "2.3.3", 248 | "from": "qs@>=2.3.3 <2.4.0", 249 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" 250 | }, 251 | "raw-body": { 252 | "version": "1.3.3", 253 | "from": "raw-body@>=1.3.3 <1.4.0", 254 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.3.tgz", 255 | "dependencies": { 256 | "bytes": { 257 | "version": "1.0.0", 258 | "from": "bytes@1.0.0", 259 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" 260 | }, 261 | "iconv-lite": { 262 | "version": "0.4.7", 263 | "from": "iconv-lite@0.4.7", 264 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.7.tgz" 265 | } 266 | } 267 | } 268 | } 269 | }, 270 | "copy-to": { 271 | "version": "2.0.1", 272 | "from": "copy-to@>=2.0.1 <2.1.0", 273 | "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz" 274 | } 275 | } 276 | }, 277 | "koa-conditional-get": { 278 | "version": "1.0.2", 279 | "from": "koa-conditional-get@*", 280 | "resolved": "https://registry.npmjs.org/koa-conditional-get/-/koa-conditional-get-1.0.2.tgz" 281 | }, 282 | "koa-etag": { 283 | "version": "2.0.0", 284 | "from": "koa-etag@*", 285 | "resolved": "https://registry.npmjs.org/koa-etag/-/koa-etag-2.0.0.tgz", 286 | "dependencies": { 287 | "etag": { 288 | "version": "1.5.1", 289 | "from": "etag@>=1.3.0 <2.0.0", 290 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz", 291 | "dependencies": { 292 | "crc": { 293 | "version": "3.2.1", 294 | "from": "crc@3.2.1", 295 | "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz" 296 | } 297 | } 298 | }, 299 | "mz": { 300 | "version": "1.3.0", 301 | "from": "mz@>=1.0.1 <2.0.0", 302 | "resolved": "https://registry.npmjs.org/mz/-/mz-1.3.0.tgz", 303 | "dependencies": { 304 | "native-or-bluebird": { 305 | "version": "1.2.0", 306 | "from": "native-or-bluebird@>=1.0.0 <2.0.0", 307 | "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz" 308 | }, 309 | "thenify": { 310 | "version": "3.1.0", 311 | "from": "thenify@>=3.0.0 <4.0.0", 312 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.1.0.tgz" 313 | }, 314 | "thenify-all": { 315 | "version": "1.6.0", 316 | "from": "thenify-all@>=1.0.0 <2.0.0", 317 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" 318 | } 319 | } 320 | } 321 | } 322 | }, 323 | "koa-fresh": { 324 | "version": "0.0.3", 325 | "from": "koa-fresh@*", 326 | "resolved": "https://registry.npmjs.org/koa-fresh/-/koa-fresh-0.0.3.tgz" 327 | }, 328 | "koa-gzip": { 329 | "version": "0.1.0", 330 | "from": "koa-gzip@*", 331 | "resolved": "https://registry.npmjs.org/koa-gzip/-/koa-gzip-0.1.0.tgz", 332 | "dependencies": { 333 | "thunkify-wrap": { 334 | "version": "0.1.1", 335 | "from": "thunkify-wrap@0.1.1", 336 | "resolved": "https://registry.npmjs.org/thunkify-wrap/-/thunkify-wrap-0.1.1.tgz" 337 | } 338 | } 339 | }, 340 | "koa-json": { 341 | "version": "1.1.1", 342 | "from": "koa-json@*", 343 | "resolved": "https://registry.npmjs.org/koa-json/-/koa-json-1.1.1.tgz", 344 | "dependencies": { 345 | "koa-is-json": { 346 | "version": "1.0.0", 347 | "from": "koa-is-json@>=1.0.0 <2.0.0", 348 | "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz" 349 | }, 350 | "streaming-json-stringify": { 351 | "version": "1.0.1", 352 | "from": "streaming-json-stringify@>=1.0.0 <2.0.0", 353 | "resolved": "https://registry.npmjs.org/streaming-json-stringify/-/streaming-json-stringify-1.0.1.tgz" 354 | } 355 | } 356 | }, 357 | "koa-qs": { 358 | "version": "2.0.0", 359 | "from": "koa-qs@*", 360 | "resolved": "https://registry.npmjs.org/koa-qs/-/koa-qs-2.0.0.tgz", 361 | "dependencies": { 362 | "merge-descriptors": { 363 | "version": "0.0.2", 364 | "from": "merge-descriptors@>=0.0.2 <0.1.0", 365 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz" 366 | }, 367 | "qs": { 368 | "version": "2.3.3", 369 | "from": "qs@>=2.3.3 <2.4.0", 370 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" 371 | } 372 | } 373 | }, 374 | "koa-router": { 375 | "version": "4.2.0", 376 | "from": "koa-router@*", 377 | "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-4.2.0.tgz", 378 | "dependencies": { 379 | "debug": { 380 | "version": "2.1.3", 381 | "from": "debug@>=2.1.0 <3.0.0", 382 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.3.tgz", 383 | "dependencies": { 384 | "ms": { 385 | "version": "0.7.0", 386 | "from": "ms@0.7.0", 387 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.0.tgz" 388 | } 389 | } 390 | }, 391 | "http-errors": { 392 | "version": "1.3.1", 393 | "from": "http-errors@>=1.3.1 <2.0.0", 394 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", 395 | "dependencies": { 396 | "inherits": { 397 | "version": "2.0.1", 398 | "from": "inherits@>=2.0.1 <2.1.0", 399 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" 400 | }, 401 | "statuses": { 402 | "version": "1.2.1", 403 | "from": "statuses@>=1.0.0 <2.0.0", 404 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" 405 | } 406 | } 407 | }, 408 | "koa-compose": { 409 | "version": "2.3.0", 410 | "from": "koa-compose@>=2.3.0 <3.0.0", 411 | "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-2.3.0.tgz" 412 | }, 413 | "methods": { 414 | "version": "1.1.1", 415 | "from": "methods@>=1.0.1 <2.0.0", 416 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.1.tgz" 417 | }, 418 | "path-to-regexp": { 419 | "version": "1.0.3", 420 | "from": "path-to-regexp@>=1.0.0 <2.0.0", 421 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.0.3.tgz", 422 | "dependencies": { 423 | "isarray": { 424 | "version": "0.0.1", 425 | "from": "isarray@0.0.1", 426 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" 427 | } 428 | } 429 | } 430 | } 431 | }, 432 | "koa-static": { 433 | "version": "1.4.9", 434 | "from": "koa-static@*", 435 | "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-1.4.9.tgz", 436 | "dependencies": { 437 | "debug": { 438 | "version": "2.1.3", 439 | "from": "debug@*", 440 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.3.tgz", 441 | "dependencies": { 442 | "ms": { 443 | "version": "0.7.0", 444 | "from": "ms@0.7.0", 445 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.0.tgz" 446 | } 447 | } 448 | }, 449 | "koa-send": { 450 | "version": "1.3.1", 451 | "from": "koa-send@>=1.3.1 <2.0.0", 452 | "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-1.3.1.tgz", 453 | "dependencies": { 454 | "mz": { 455 | "version": "1.3.0", 456 | "from": "mz@>=1.0.1 <2.0.0", 457 | "resolved": "https://registry.npmjs.org/mz/-/mz-1.3.0.tgz", 458 | "dependencies": { 459 | "native-or-bluebird": { 460 | "version": "1.2.0", 461 | "from": "native-or-bluebird@>=1.0.0 <2.0.0", 462 | "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz" 463 | }, 464 | "thenify": { 465 | "version": "3.1.0", 466 | "from": "thenify@>=3.0.0 <4.0.0", 467 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.1.0.tgz" 468 | }, 469 | "thenify-all": { 470 | "version": "1.6.0", 471 | "from": "thenify-all@>=1.0.0 <2.0.0", 472 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" 473 | } 474 | } 475 | } 476 | } 477 | } 478 | } 479 | }, 480 | "koa-views": { 481 | "version": "2.1.2", 482 | "from": "koa-views@>=2.1.2 <2.2.0", 483 | "resolved": "https://registry.npmjs.org/koa-views/-/koa-views-2.1.2.tgz", 484 | "dependencies": { 485 | "co-views": { 486 | "version": "0.2.0", 487 | "from": "co-views@>=0.2.0 <0.3.0", 488 | "resolved": "https://registry.npmjs.org/co-views/-/co-views-0.2.0.tgz", 489 | "dependencies": { 490 | "co-render": { 491 | "version": "0.0.1", 492 | "from": "co-render@0.0.1", 493 | "resolved": "https://registry.npmjs.org/co-render/-/co-render-0.0.1.tgz", 494 | "dependencies": { 495 | "consolidate": { 496 | "version": "0.9.1", 497 | "from": "consolidate@>=0.9.1 <0.10.0", 498 | "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.9.1.tgz" 499 | } 500 | } 501 | } 502 | } 503 | }, 504 | "debug": { 505 | "version": "0.7.4", 506 | "from": "debug@>=0.7.4 <0.8.0", 507 | "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" 508 | }, 509 | "koa-send": { 510 | "version": "1.3.1", 511 | "from": "koa-send@>=1.2.4 <2.0.0", 512 | "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-1.3.1.tgz", 513 | "dependencies": { 514 | "mz": { 515 | "version": "1.3.0", 516 | "from": "mz@>=1.0.1 <2.0.0", 517 | "resolved": "https://registry.npmjs.org/mz/-/mz-1.3.0.tgz", 518 | "dependencies": { 519 | "native-or-bluebird": { 520 | "version": "1.2.0", 521 | "from": "native-or-bluebird@>=1.0.0 <2.0.0", 522 | "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz" 523 | }, 524 | "thenify": { 525 | "version": "3.1.0", 526 | "from": "thenify@>=3.0.0 <4.0.0", 527 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.1.0.tgz" 528 | }, 529 | "thenify-all": { 530 | "version": "1.6.0", 531 | "from": "thenify-all@>=1.0.0 <2.0.0", 532 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" 533 | } 534 | } 535 | } 536 | } 537 | }, 538 | "object-assign": { 539 | "version": "2.0.0", 540 | "from": "object-assign@>=2.0.0 <3.0.0", 541 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.0.0.tgz" 542 | } 543 | } 544 | }, 545 | "react": { 546 | "version": "0.12.2", 547 | "from": "react@>=0.12.0 <0.13.0", 548 | "resolved": "https://registry.npmjs.org/react/-/react-0.12.2.tgz", 549 | "dependencies": { 550 | "envify": { 551 | "version": "3.4.0", 552 | "from": "envify@>=3.0.0 <4.0.0", 553 | "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.0.tgz", 554 | "dependencies": { 555 | "through": { 556 | "version": "2.3.6", 557 | "from": "through@>=2.3.4 <2.4.0", 558 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz" 559 | }, 560 | "jstransform": { 561 | "version": "10.1.0", 562 | "from": "jstransform@>=10.0.1 <11.0.0", 563 | "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-10.1.0.tgz", 564 | "dependencies": { 565 | "base62": { 566 | "version": "0.1.1", 567 | "from": "base62@0.1.1", 568 | "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz" 569 | }, 570 | "esprima-fb": { 571 | "version": "13001.1001.0-dev-harmony-fb", 572 | "from": "esprima-fb@13001.1001.0-dev-harmony-fb", 573 | "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-13001.1001.0-dev-harmony-fb.tgz" 574 | }, 575 | "source-map": { 576 | "version": "0.1.31", 577 | "from": "source-map@0.1.31", 578 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", 579 | "dependencies": { 580 | "amdefine": { 581 | "version": "0.1.0", 582 | "from": "amdefine@>=0.0.4", 583 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" 584 | } 585 | } 586 | } 587 | } 588 | } 589 | } 590 | } 591 | } 592 | }, 593 | "react-router": { 594 | "version": "0.12.4", 595 | "from": "react-router@*", 596 | "resolved": "https://registry.npmjs.org/react-router/-/react-router-0.12.4.tgz", 597 | "dependencies": { 598 | "qs": { 599 | "version": "2.3.3", 600 | "from": "qs@2.3.3", 601 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" 602 | } 603 | } 604 | }, 605 | "remarkable": { 606 | "version": "1.6.0", 607 | "from": "remarkable@*", 608 | "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.6.0.tgz", 609 | "dependencies": { 610 | "argparse": { 611 | "version": "0.1.16", 612 | "from": "argparse@>=0.1.15 <0.2.0", 613 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", 614 | "dependencies": { 615 | "underscore": { 616 | "version": "1.7.0", 617 | "from": "underscore@>=1.7.0 <1.8.0", 618 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" 619 | }, 620 | "underscore.string": { 621 | "version": "2.4.0", 622 | "from": "underscore.string@>=2.4.0 <2.5.0", 623 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" 624 | } 625 | } 626 | }, 627 | "autolinker": { 628 | "version": "0.15.2", 629 | "from": "autolinker@>=0.15.0 <0.16.0", 630 | "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.2.tgz" 631 | } 632 | } 633 | }, 634 | "then-jade": { 635 | "version": "2.2.1", 636 | "from": "then-jade@*", 637 | "resolved": "https://registry.npmjs.org/then-jade/-/then-jade-2.2.1.tgz", 638 | "dependencies": { 639 | "regenerator": { 640 | "version": "0.4.12", 641 | "from": "regenerator@>=0.4.0 <0.5.0", 642 | "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.4.12.tgz", 643 | "dependencies": { 644 | "commander": { 645 | "version": "2.1.0", 646 | "from": "commander@2.1.0", 647 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz" 648 | }, 649 | "esprima": { 650 | "version": "1.1.0-dev-harmony", 651 | "from": "../../../../var/folders/sc/wwsv685s0zv1xnrkcnk05qv00000gn/T/npm-6989-afbd13f7/git-cache-e6749c1efa13/a41a40b49046747b3af57341cda048bbd3d9df79", 652 | "resolved": "git://github.com/ariya/esprima.git#a41a40b49046747b3af57341cda048bbd3d9df79" 653 | }, 654 | "recast": { 655 | "version": "0.6.10", 656 | "from": "recast@>=0.6.5 <0.7.0", 657 | "resolved": "https://registry.npmjs.org/recast/-/recast-0.6.10.tgz", 658 | "dependencies": { 659 | "esprima": { 660 | "version": "1.1.0-dev-harmony", 661 | "from": "../../../../var/folders/sc/wwsv685s0zv1xnrkcnk05qv00000gn/T/npm-6989-afbd13f7/git-cache-20f8a2bf2fea/a41a40b49046747b3af57341cda048bbd3d9df79", 662 | "resolved": "git+https://github.com/ariya/esprima.git#a41a40b49046747b3af57341cda048bbd3d9df79" 663 | }, 664 | "source-map": { 665 | "version": "0.1.32", 666 | "from": "source-map@0.1.32", 667 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", 668 | "dependencies": { 669 | "amdefine": { 670 | "version": "0.1.0", 671 | "from": "amdefine@>=0.0.4", 672 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" 673 | } 674 | } 675 | }, 676 | "cls": { 677 | "version": "0.1.5", 678 | "from": "cls@>=0.1.3 <0.2.0", 679 | "resolved": "https://registry.npmjs.org/cls/-/cls-0.1.5.tgz" 680 | }, 681 | "ast-types": { 682 | "version": "0.4.13", 683 | "from": "ast-types@>=0.4.7 <0.5.0", 684 | "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.4.13.tgz", 685 | "dependencies": { 686 | "depd": { 687 | "version": "1.0.0", 688 | "from": "depd@>=1.0.0 <1.1.0", 689 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" 690 | } 691 | } 692 | } 693 | } 694 | }, 695 | "private": { 696 | "version": "0.1.6", 697 | "from": "private@>=0.1.5 <0.2.0", 698 | "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" 699 | }, 700 | "defs": { 701 | "version": "0.6.2", 702 | "from": "defs@>=0.6.2 <0.7.0", 703 | "resolved": "https://registry.npmjs.org/defs/-/defs-0.6.2.tgz", 704 | "dependencies": { 705 | "alter": { 706 | "version": "0.2.0", 707 | "from": "alter@>=0.2.0 <0.3.0", 708 | "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", 709 | "dependencies": { 710 | "stable": { 711 | "version": "0.1.5", 712 | "from": "stable@>=0.1.3 <0.2.0", 713 | "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" 714 | } 715 | } 716 | }, 717 | "breakable": { 718 | "version": "0.1.0", 719 | "from": "breakable@>=0.1.0 <0.2.0", 720 | "resolved": "https://registry.npmjs.org/breakable/-/breakable-0.1.0.tgz" 721 | }, 722 | "ast-traverse": { 723 | "version": "0.1.1", 724 | "from": "ast-traverse@>=0.1.1 <0.2.0", 725 | "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz" 726 | }, 727 | "simple-fmt": { 728 | "version": "0.1.0", 729 | "from": "simple-fmt@>=0.1.0 <0.2.0", 730 | "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" 731 | }, 732 | "simple-is": { 733 | "version": "0.2.0", 734 | "from": "simple-is@>=0.2.0 <0.3.0", 735 | "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" 736 | }, 737 | "stringmap": { 738 | "version": "0.2.2", 739 | "from": "stringmap@>=0.2.2 <0.3.0", 740 | "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" 741 | }, 742 | "stringset": { 743 | "version": "0.2.1", 744 | "from": "stringset@>=0.2.1 <0.3.0", 745 | "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" 746 | }, 747 | "tryor": { 748 | "version": "0.1.2", 749 | "from": "tryor@>=0.1.2 <0.2.0", 750 | "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" 751 | }, 752 | "esprima": { 753 | "version": "1.0.4", 754 | "from": "esprima@>=1.0.0 <1.1.0", 755 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" 756 | } 757 | } 758 | } 759 | } 760 | }, 761 | "then-yield": { 762 | "version": "0.0.1", 763 | "from": "then-yield@0.0.1", 764 | "resolved": "https://registry.npmjs.org/then-yield/-/then-yield-0.0.1.tgz" 765 | }, 766 | "barrage": { 767 | "version": "1.1.0", 768 | "from": "barrage@>=1.1.0 <1.2.0", 769 | "resolved": "https://registry.npmjs.org/barrage/-/barrage-1.1.0.tgz" 770 | }, 771 | "promise": { 772 | "version": "6.0.1", 773 | "from": "promise@>=6.0.0 <6.1.0", 774 | "resolved": "https://registry.npmjs.org/promise/-/promise-6.0.1.tgz", 775 | "dependencies": { 776 | "asap": { 777 | "version": "1.0.0", 778 | "from": "asap@>=1.0.0 <1.1.0", 779 | "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz" 780 | } 781 | } 782 | }, 783 | "with": { 784 | "version": "4.0.1", 785 | "from": "with@>=4.0.0 <4.1.0", 786 | "resolved": "https://registry.npmjs.org/with/-/with-4.0.1.tgz", 787 | "dependencies": { 788 | "acorn": { 789 | "version": "0.11.0", 790 | "from": "acorn@>=0.11.0 <0.12.0", 791 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-0.11.0.tgz" 792 | }, 793 | "acorn-globals": { 794 | "version": "1.0.2", 795 | "from": "acorn-globals@>=1.0.1 <2.0.0", 796 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.2.tgz" 797 | } 798 | } 799 | }, 800 | "jade": { 801 | "version": "1.3.0", 802 | "from": "jade@1.3.0", 803 | "resolved": "https://registry.npmjs.org/jade/-/jade-1.3.0.tgz", 804 | "dependencies": { 805 | "commander": { 806 | "version": "2.1.0", 807 | "from": "commander@2.1.0", 808 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz" 809 | }, 810 | "mkdirp": { 811 | "version": "0.3.5", 812 | "from": "mkdirp@>=0.3.5 <0.4.0", 813 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" 814 | }, 815 | "transformers": { 816 | "version": "2.1.0", 817 | "from": "transformers@2.1.0", 818 | "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", 819 | "dependencies": { 820 | "promise": { 821 | "version": "2.0.0", 822 | "from": "promise@>=2.0.0 <2.1.0", 823 | "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", 824 | "dependencies": { 825 | "is-promise": { 826 | "version": "1.0.1", 827 | "from": "is-promise@>=1.0.0 <2.0.0", 828 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz" 829 | } 830 | } 831 | }, 832 | "css": { 833 | "version": "1.0.8", 834 | "from": "css@>=1.0.8 <1.1.0", 835 | "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", 836 | "dependencies": { 837 | "css-parse": { 838 | "version": "1.0.4", 839 | "from": "css-parse@1.0.4", 840 | "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz" 841 | }, 842 | "css-stringify": { 843 | "version": "1.0.5", 844 | "from": "css-stringify@1.0.5", 845 | "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz" 846 | } 847 | } 848 | }, 849 | "uglify-js": { 850 | "version": "2.2.5", 851 | "from": "uglify-js@>=2.2.5 <2.3.0", 852 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", 853 | "dependencies": { 854 | "source-map": { 855 | "version": "0.1.43", 856 | "from": "source-map@>=0.1.7 <0.2.0", 857 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", 858 | "dependencies": { 859 | "amdefine": { 860 | "version": "0.1.0", 861 | "from": "amdefine@>=0.0.4", 862 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" 863 | } 864 | } 865 | }, 866 | "optimist": { 867 | "version": "0.3.7", 868 | "from": "optimist@>=0.3.5 <0.4.0", 869 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", 870 | "dependencies": { 871 | "wordwrap": { 872 | "version": "0.0.2", 873 | "from": "wordwrap@>=0.0.2 <0.1.0", 874 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" 875 | } 876 | } 877 | } 878 | } 879 | } 880 | } 881 | }, 882 | "character-parser": { 883 | "version": "1.2.0", 884 | "from": "character-parser@1.2.0", 885 | "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz" 886 | }, 887 | "monocle": { 888 | "version": "1.1.51", 889 | "from": "monocle@1.1.51", 890 | "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz", 891 | "dependencies": { 892 | "readdirp": { 893 | "version": "0.2.5", 894 | "from": "readdirp@>=0.2.3 <0.3.0", 895 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz", 896 | "dependencies": { 897 | "minimatch": { 898 | "version": "2.0.4", 899 | "from": "minimatch@>=0.2.4", 900 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.4.tgz", 901 | "dependencies": { 902 | "brace-expansion": { 903 | "version": "1.1.0", 904 | "from": "brace-expansion@>=1.0.0 <2.0.0", 905 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", 906 | "dependencies": { 907 | "balanced-match": { 908 | "version": "0.2.0", 909 | "from": "balanced-match@>=0.2.0 <0.3.0", 910 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" 911 | }, 912 | "concat-map": { 913 | "version": "0.0.1", 914 | "from": "concat-map@0.0.1", 915 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 916 | } 917 | } 918 | } 919 | } 920 | } 921 | } 922 | } 923 | } 924 | }, 925 | "with": { 926 | "version": "3.0.1", 927 | "from": "with@>=3.0.0 <3.1.0", 928 | "resolved": "https://registry.npmjs.org/with/-/with-3.0.1.tgz", 929 | "dependencies": { 930 | "uglify-js": { 931 | "version": "2.4.17", 932 | "from": "uglify-js@>=2.4.12 <2.5.0", 933 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.17.tgz", 934 | "dependencies": { 935 | "async": { 936 | "version": "0.2.10", 937 | "from": "async@>=0.2.6 <0.3.0", 938 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" 939 | }, 940 | "source-map": { 941 | "version": "0.1.34", 942 | "from": "source-map@0.1.34", 943 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", 944 | "dependencies": { 945 | "amdefine": { 946 | "version": "0.1.0", 947 | "from": "amdefine@>=0.0.4", 948 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" 949 | } 950 | } 951 | }, 952 | "yargs": { 953 | "version": "1.3.3", 954 | "from": "yargs@>=1.3.3 <1.4.0", 955 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz" 956 | }, 957 | "uglify-to-browserify": { 958 | "version": "1.0.2", 959 | "from": "uglify-to-browserify@>=1.0.0 <1.1.0", 960 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" 961 | } 962 | } 963 | } 964 | } 965 | }, 966 | "constantinople": { 967 | "version": "2.0.1", 968 | "from": "constantinople@>=2.0.0 <2.1.0", 969 | "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-2.0.1.tgz", 970 | "dependencies": { 971 | "uglify-js": { 972 | "version": "2.4.17", 973 | "from": "uglify-js@>=2.4.12 <2.5.0", 974 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.17.tgz", 975 | "dependencies": { 976 | "async": { 977 | "version": "0.2.10", 978 | "from": "async@>=0.2.6 <0.3.0", 979 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" 980 | }, 981 | "source-map": { 982 | "version": "0.1.34", 983 | "from": "source-map@0.1.34", 984 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", 985 | "dependencies": { 986 | "amdefine": { 987 | "version": "0.1.0", 988 | "from": "amdefine@>=0.0.4", 989 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" 990 | } 991 | } 992 | }, 993 | "yargs": { 994 | "version": "1.3.3", 995 | "from": "yargs@>=1.3.3 <1.4.0", 996 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz" 997 | }, 998 | "uglify-to-browserify": { 999 | "version": "1.0.2", 1000 | "from": "uglify-to-browserify@>=1.0.0 <1.1.0", 1001 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" 1002 | } 1003 | } 1004 | } 1005 | } 1006 | } 1007 | } 1008 | } 1009 | } 1010 | } 1011 | } 1012 | } 1013 | --------------------------------------------------------------------------------