| @@ -0,0 +1,5 @@ | ||
| { | ||
| "preset": "airbnb", | ||
| "excludeFiles": ["build/**", "node_modules/**"], | ||
| "validateQuoteMarks": null | ||
| } |
| @@ -0,0 +1,201 @@ | ||
| Apache License | ||
| Version 2.0, January 2004 | ||
| http://www.apache.org/licenses/ | ||
|
|
||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
|
|
||
| 1. Definitions. | ||
|
|
||
| "License" shall mean the terms and conditions for use, reproduction, | ||
| and distribution as defined by Sections 1 through 9 of this document. | ||
|
|
||
| "Licensor" shall mean the copyright owner or entity authorized by | ||
| the copyright owner that is granting the License. | ||
|
|
||
| "Legal Entity" shall mean the union of the acting entity and all | ||
| other entities that control, are controlled by, or are under common | ||
| control with that entity. For the purposes of this definition, | ||
| "control" means (i) the power, direct or indirect, to cause the | ||
| direction or management of such entity, whether by contract or | ||
| otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
| outstanding shares, or (iii) beneficial ownership of such entity. | ||
|
|
||
| "You" (or "Your") shall mean an individual or Legal Entity | ||
| exercising permissions granted by this License. | ||
|
|
||
| "Source" form shall mean the preferred form for making modifications, | ||
| including but not limited to software source code, documentation | ||
| source, and configuration files. | ||
|
|
||
| "Object" form shall mean any form resulting from mechanical | ||
| transformation or translation of a Source form, including but | ||
| not limited to compiled object code, generated documentation, | ||
| and conversions to other media types. | ||
|
|
||
| "Work" shall mean the work of authorship, whether in Source or | ||
| Object form, made available under the License, as indicated by a | ||
| copyright notice that is included in or attached to the work | ||
| (an example is provided in the Appendix below). | ||
|
|
||
| "Derivative Works" shall mean any work, whether in Source or Object | ||
| form, that is based on (or derived from) the Work and for which the | ||
| editorial revisions, annotations, elaborations, or other modifications | ||
| represent, as a whole, an original work of authorship. For the purposes | ||
| of this License, Derivative Works shall not include works that remain | ||
| separable from, or merely link (or bind by name) to the interfaces of, | ||
| the Work and Derivative Works thereof. | ||
|
|
||
| "Contribution" shall mean any work of authorship, including | ||
| the original version of the Work and any modifications or additions | ||
| to that Work or Derivative Works thereof, that is intentionally | ||
| submitted to Licensor for inclusion in the Work by the copyright owner | ||
| or by an individual or Legal Entity authorized to submit on behalf of | ||
| the copyright owner. For the purposes of this definition, "submitted" | ||
| means any form of electronic, verbal, or written communication sent | ||
| to the Licensor or its representatives, including but not limited to | ||
| communication on electronic mailing lists, source code control systems, | ||
| and issue tracking systems that are managed by, or on behalf of, the | ||
| Licensor for the purpose of discussing and improving the Work, but | ||
| excluding communication that is conspicuously marked or otherwise | ||
| designated in writing by the copyright owner as "Not a Contribution." | ||
|
|
||
| "Contributor" shall mean Licensor and any individual or Legal Entity | ||
| on behalf of whom a Contribution has been received by Licensor and | ||
| subsequently incorporated within the Work. | ||
|
|
||
| 2. Grant of Copyright License. Subject to the terms and conditions of | ||
| this License, each Contributor hereby grants to You a perpetual, | ||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| copyright license to reproduce, prepare Derivative Works of, | ||
| publicly display, publicly perform, sublicense, and distribute the | ||
| Work and such Derivative Works in Source or Object form. | ||
|
|
||
| 3. Grant of Patent License. Subject to the terms and conditions of | ||
| this License, each Contributor hereby grants to You a perpetual, | ||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| (except as stated in this section) patent license to make, have made, | ||
| use, offer to sell, sell, import, and otherwise transfer the Work, | ||
| where such license applies only to those patent claims licensable | ||
| by such Contributor that are necessarily infringed by their | ||
| Contribution(s) alone or by combination of their Contribution(s) | ||
| with the Work to which such Contribution(s) was submitted. If You | ||
| institute patent litigation against any entity (including a | ||
| cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
| or a Contribution incorporated within the Work constitutes direct | ||
| or contributory patent infringement, then any patent licenses | ||
| granted to You under this License for that Work shall terminate | ||
| as of the date such litigation is filed. | ||
|
|
||
| 4. Redistribution. You may reproduce and distribute copies of the | ||
| Work or Derivative Works thereof in any medium, with or without | ||
| modifications, and in Source or Object form, provided that You | ||
| meet the following conditions: | ||
|
|
||
| (a) You must give any other recipients of the Work or | ||
| Derivative Works a copy of this License; and | ||
|
|
||
| (b) You must cause any modified files to carry prominent notices | ||
| stating that You changed the files; and | ||
|
|
||
| (c) You must retain, in the Source form of any Derivative Works | ||
| that You distribute, all copyright, patent, trademark, and | ||
| attribution notices from the Source form of the Work, | ||
| excluding those notices that do not pertain to any part of | ||
| the Derivative Works; and | ||
|
|
||
| (d) If the Work includes a "NOTICE" text file as part of its | ||
| distribution, then any Derivative Works that You distribute must | ||
| include a readable copy of the attribution notices contained | ||
| within such NOTICE file, excluding those notices that do not | ||
| pertain to any part of the Derivative Works, in at least one | ||
| of the following places: within a NOTICE text file distributed | ||
| as part of the Derivative Works; within the Source form or | ||
| documentation, if provided along with the Derivative Works; or, | ||
| within a display generated by the Derivative Works, if and | ||
| wherever such third-party notices normally appear. The contents | ||
| of the NOTICE file are for informational purposes only and | ||
| do not modify the License. You may add Your own attribution | ||
| notices within Derivative Works that You distribute, alongside | ||
| or as an addendum to the NOTICE text from the Work, provided | ||
| that such additional attribution notices cannot be construed | ||
| as modifying the License. | ||
|
|
||
| You may add Your own copyright statement to Your modifications and | ||
| may provide additional or different license terms and conditions | ||
| for use, reproduction, or distribution of Your modifications, or | ||
| for any such Derivative Works as a whole, provided Your use, | ||
| reproduction, and distribution of the Work otherwise complies with | ||
| the conditions stated in this License. | ||
|
|
||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
| any Contribution intentionally submitted for inclusion in the Work | ||
| by You to the Licensor shall be under the terms and conditions of | ||
| this License, without any additional terms or conditions. | ||
| Notwithstanding the above, nothing herein shall supersede or modify | ||
| the terms of any separate license agreement you may have executed | ||
| with Licensor regarding such Contributions. | ||
|
|
||
| 6. Trademarks. This License does not grant permission to use the trade | ||
| names, trademarks, service marks, or product names of the Licensor, | ||
| except as required for reasonable and customary use in describing the | ||
| origin of the Work and reproducing the content of the NOTICE file. | ||
|
|
||
| 7. Disclaimer of Warranty. Unless required by applicable law or | ||
| agreed to in writing, Licensor provides the Work (and each | ||
| Contributor provides its Contributions) on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
| implied, including, without limitation, any warranties or conditions | ||
| of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
| PARTICULAR PURPOSE. You are solely responsible for determining the | ||
| appropriateness of using or redistributing the Work and assume any | ||
| risks associated with Your exercise of permissions under this License. | ||
|
|
||
| 8. Limitation of Liability. In no event and under no legal theory, | ||
| whether in tort (including negligence), contract, or otherwise, | ||
| unless required by applicable law (such as deliberate and grossly | ||
| negligent acts) or agreed to in writing, shall any Contributor be | ||
| liable to You for damages, including any direct, indirect, special, | ||
| incidental, or consequential damages of any character arising as a | ||
| result of this License or out of the use or inability to use the | ||
| Work (including but not limited to damages for loss of goodwill, | ||
| work stoppage, computer failure or malfunction, or any and all | ||
| other commercial damages or losses), even if such Contributor | ||
| has been advised of the possibility of such damages. | ||
|
|
||
| 9. Accepting Warranty or Additional Liability. While redistributing | ||
| the Work or Derivative Works thereof, You may choose to offer, | ||
| and charge a fee for, acceptance of support, warranty, indemnity, | ||
| or other liability obligations and/or rights consistent with this | ||
| License. However, in accepting such obligations, You may act only | ||
| on Your own behalf and on Your sole responsibility, not on behalf | ||
| of any other Contributor, and only if You agree to indemnify, | ||
| defend, and hold each Contributor harmless for any liability | ||
| incurred by, or claims asserted against, such Contributor by reason | ||
| of your accepting any such warranty or additional liability. | ||
|
|
||
| END OF TERMS AND CONDITIONS | ||
|
|
||
| APPENDIX: How to apply the Apache License to your work. | ||
|
|
||
| To apply the Apache License to your work, attach the following | ||
| boilerplate notice, with the fields enclosed by brackets "{}" | ||
| replaced with your own identifying information. (Don't include | ||
| the brackets!) The text should be enclosed in the appropriate | ||
| comment syntax for the file format. We also recommend that a | ||
| file or class name and description of purpose be included on the | ||
| same "printed page" as the copyright notice for easier | ||
| identification within third-party archives. | ||
|
|
||
| Copyright {yyyy} {name of copyright owner} | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. |
| @@ -0,0 +1,83 @@ | ||
| ## Udorimio | ||
|
|
||
|
|
||
| ### Directory Layout | ||
|
|
||
| ``` | ||
| . | ||
| ├── /build/ # The folder for compiled output | ||
| ├── /docs/ # Documentation files for the project | ||
| ├── /node_modules/ # 3rd-party libraries and utilities | ||
| ├── /src/ # The source code of the application | ||
| │ ├── /actions/ # Action creators that allow to trigger a dispatch to stores | ||
| │ ├── /api/ # REST API / Relay endpoints | ||
| │ ├── /components/ # React components | ||
| │ ├── /constants/ # Constants (action types etc.) | ||
| │ ├── /content/ # Static content (plain HTML or Markdown, Jade, you name it) | ||
| │ ├── /core/ # Core framework and utility functions | ||
| │ ├── /decorators/ # Higher-order React components | ||
| │ ├── /public/ # Static files which are copied into the /build/public folder | ||
| │ ├── /stores/ # Stores contain the application state and logic | ||
| │ ├── /app.js # Client-side startup script | ||
| │ ├── /config.js # Global application settings | ||
| │ ├── /routes.js # Universal (isomorphic) application routes | ||
| │ └── /server.js # Server-side startup script | ||
| ├── /tools/ # Build automation scripts and utilities | ||
| │ ├── /lib/ # Library for utility snippets | ||
| │ ├── /build.js # Builds the project from source to output (build) folder | ||
| │ ├── /bundle.js # Bundles the web resources into package(s) through Webpack | ||
| │ ├── /clean.js # Cleans up the output (build) folder | ||
| │ ├── /webpack.config.js # Configurations for client-side and server-side bundles | ||
| │ ├── /copy.js # Copies static files to output (build) folder | ||
| │ ├── /run.js # Helper function for running build automation tasks | ||
| │ ├── /serve.js # Launches the Node.js/Express web server | ||
| │ └── /start.js # Launches the development web server with "live reload" | ||
| │── package.json # The list of 3rd party libraries and utilities | ||
| └── preprocessor.js # ES6 transpiler settings for Jest | ||
| ``` | ||
|
|
||
| ### Getting Started | ||
|
|
||
| Just clone the repo and start hacking: | ||
|
|
||
| ```shell | ||
| $ npm install # Install Node.js components listed in ./package.json | ||
| $ npm start # Compile and launch | ||
| ``` | ||
|
|
||
| ### How to Build | ||
|
|
||
| ```shell | ||
| $ npm run build # or, `npm run build -- --release` | ||
| ``` | ||
|
|
||
| By default, it builds in *debug* mode. If you need to build in release | ||
| mode, just add a `-- --release` flag. This will optimize the output bundle for | ||
| production. | ||
|
|
||
| ### How to Run | ||
|
|
||
| ```shell | ||
| $ npm start # or, `npm start -- --release` | ||
| ``` | ||
|
|
||
| This will start a light-weight development server with "live reload" and | ||
| synchronized browsing across multiple devices and browsers. | ||
|
|
||
| ### How to Test | ||
|
|
||
| Run unit tests powered by [Jest](https://facebook.github.io/jest/) with the following | ||
| [npm](https://www.npmjs.org/doc/misc/npm-scripts.html) command: | ||
|
|
||
| ```shell | ||
| $ npm test | ||
| ``` | ||
|
|
||
| Test any javascript module by creating a `__tests__/` directory where | ||
| the file is. Append `-test.js` to the filename and [Jest](https://facebook.github.io/jest/) will do the rest. | ||
|
|
||
|
|
||
| ### License | ||
|
|
||
| Code is licensed under Apache License 2.0 | ||
|
|
| @@ -0,0 +1,85 @@ | ||
| { | ||
| "private": true, | ||
| "engines": { | ||
| "node": ">=4.1 <5", | ||
| "npm": ">=3.1 <4" | ||
| }, | ||
| "dependencies": { | ||
| "babel-core": "5.8.34", | ||
| "bluebird": "3.0.6", | ||
| "classnames": "2.2.1", | ||
| "eventemitter3": "1.1.1", | ||
| "express": "4.13.3", | ||
| "fastclick": "1.0.6", | ||
| "fbjs": "0.5.0", | ||
| "flux": "2.1.1", | ||
| "front-matter": "2.0.1", | ||
| "history": "1.14.0", | ||
| "jade": "1.11.0", | ||
| "materialize-css": "^0.97.3", | ||
| "normalize.css": "3.0.3", | ||
| "react": "0.14.3", | ||
| "react-dom": "0.14.3", | ||
| "react-routing": "0.0.5", | ||
| "source-map-support": "0.4.0", | ||
| "superagent": "1.5.0" | ||
| }, | ||
| "devDependencies": { | ||
| "assets-webpack-plugin": "^3.2.0", | ||
| "autoprefixer": "^6.1.2", | ||
| "babel": "^5.8.34", | ||
| "babel-eslint": "^4.1.6", | ||
| "babel-loader": "^5.4.0", | ||
| "babel-plugin-react-transform": "^1.1.1", | ||
| "browser-sync": "^2.10.0", | ||
| "css-loader": "^0.23.0", | ||
| "csscomb": "^3.1.8", | ||
| "del": "^2.2.0", | ||
| "eslint": "^1.10.3", | ||
| "eslint-config-airbnb": "^2.0.0", | ||
| "eslint-loader": "^1.1.1", | ||
| "eslint-plugin-react": "^3.11.3", | ||
| "file-loader": "^0.8.5", | ||
| "gaze": "^0.5.2", | ||
| "git-repository": "^0.1.1", | ||
| "glob": "^6.0.1", | ||
| "isomorphic-style-loader": "0.0.5", | ||
| "jest-cli": "^0.8.0", | ||
| "jscs": "^2.6.0", | ||
| "lodash.merge": "^3.3.2", | ||
| "mkdirp": "^0.5.1", | ||
| "ncp": "^2.0.0", | ||
| "postcss": "^5.0.12", | ||
| "postcss-import": "^7.1.3", | ||
| "postcss-loader": "^0.8.0", | ||
| "precss": "^1.3.0", | ||
| "react-transform-catch-errors": "^1.0.0", | ||
| "react-transform-hmr": "^1.0.1", | ||
| "redbox-react": "^1.2.0", | ||
| "replace": "^0.3.0", | ||
| "url-loader": "^0.5.7", | ||
| "webpack": "^1.12.9", | ||
| "webpack-dev-middleware": "^1.4.0", | ||
| "webpack-hot-middleware": "^2.6.0" | ||
| }, | ||
| "jest": { | ||
| "rootDir": "./src", | ||
| "scriptPreprocessor": "../preprocessor.js", | ||
| "unmockedModulePathPatterns": [ | ||
| "fbjs", | ||
| "react" | ||
| ] | ||
| }, | ||
| "scripts": { | ||
| "lint": "eslint src tools && jscs src tools", | ||
| "csslint": "csscomb src/components --lint --verbose", | ||
| "csscomb": "csscomb src/components --verbose", | ||
| "test": "eslint src && jest", | ||
| "clean": "babel-node tools/run clean", | ||
| "copy": "babel-node tools/run copy", | ||
| "bundle": "babel-node tools/run bundle", | ||
| "build": "babel-node tools/run build", | ||
| "serve": "babel-node tools/run serve", | ||
| "start": "babel-node tools/run start" | ||
| } | ||
| } |
| @@ -0,0 +1,19 @@ | ||
| 'use strict'; | ||
|
|
||
| var babel = require('babel-core'); | ||
|
|
||
| module.exports = { | ||
| process: function(src, filename) { | ||
| // Ignore files other than .js, .es, .jsx or .es6 | ||
| if (!babel.canCompile(filename)) { | ||
| return ''; | ||
| } | ||
| // Ignore all files within node_modules | ||
| if (filename.indexOf('node_modules') === -1) { | ||
| return babel.transform(src, { | ||
| filename: filename | ||
| }).code; | ||
| } | ||
| return src; | ||
| } | ||
| }; |
| @@ -0,0 +1,66 @@ | ||
| import fs from 'fs'; | ||
| import { | ||
| join | ||
| } | ||
| from 'path'; | ||
| import { | ||
| Router | ||
| } | ||
| from 'express'; | ||
| import Promise from 'bluebird'; | ||
| import jade from 'jade'; | ||
| import fm from 'front-matter'; | ||
|
|
||
| // A folder with Jade/Markdown/HTML content pages | ||
| const CONTENT_DIR = join(__dirname, './content'); | ||
|
|
||
| // Extract 'front matter' metadata and generate HTML | ||
| const parseJade = (path, jadeContent) => { | ||
| const fmContent = fm(jadeContent); | ||
| const htmlContent = jade.render(fmContent.body); | ||
| return Object.assign({ | ||
| path, content: htmlContent | ||
| }, fmContent.attributes); | ||
| }; | ||
|
|
||
| const readFile = Promise.promisify(fs.readFile); | ||
| const fileExists = filename => new Promise(resolve => { | ||
| fs.exists(filename, resolve); | ||
| }); | ||
|
|
||
| const router = new Router(); | ||
|
|
||
| router.get('/', async(req, res, next) => { | ||
| try { | ||
| const path = req.query.path; | ||
|
|
||
| if (!path || path === 'undefined') { | ||
| res.status(400).send({ | ||
| error: `The 'path' query parameter cannot be empty.` | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| let fileName = join(CONTENT_DIR, (path === '/' ? '/index' : path) + '.jade'); | ||
| if (!(await fileExists(fileName))) { | ||
| fileName = join(CONTENT_DIR, path + '/index.jade'); | ||
| } | ||
|
|
||
| if (!(await fileExists(fileName))) { | ||
| res.status(404).send({ | ||
| error: `The page '${path}' is not found.` | ||
| }); | ||
| } else { | ||
| const source = await readFile(fileName, { | ||
| encoding: 'utf8' | ||
| }); | ||
| const content = parseJade(path, source); | ||
| res.status(200).send(content); | ||
| } | ||
| } catch (err) { | ||
| next(err); | ||
| } | ||
| }); | ||
|
|
||
| export | ||
| default router; |
| @@ -0,0 +1,99 @@ | ||
| import 'babel-core/polyfill'; | ||
| import ReactDOM from 'react-dom'; | ||
| import FastClick from 'fastclick'; | ||
| import Router from './routes'; | ||
| import Location from './core/Location'; | ||
| import { | ||
| addEventListener, removeEventListener | ||
| } | ||
| from './core/DOMUtils'; | ||
|
|
||
| let cssContainer = document.getElementById('css'); | ||
| const appContainer = document.getElementById('app'); | ||
| const context = { | ||
| insertCss: styles => styles._insertCss(), | ||
| onSetTitle: value => document.title = value, | ||
| onSetMeta: (name, content) => { | ||
| // Remove and create a new <meta /> tag in order to make it work | ||
| // with bookmarks in Safari | ||
| const elements = document.getElementsByTagName('meta'); | ||
| [].slice.call(elements).forEach((element) => { | ||
| if (element.getAttribute('name') === name) { | ||
| element.parentNode.removeChild(element); | ||
| } | ||
| }); | ||
| const meta = document.createElement('meta'); | ||
| meta.setAttribute('name', name); | ||
| meta.setAttribute('content', content); | ||
| document.getElementsByTagName('head')[0].appendChild(meta); | ||
| }, | ||
| }; | ||
|
|
||
| function render(state) { | ||
| Router.dispatch(state, (newState, component) => { | ||
| ReactDOM.render(component, appContainer, () => { | ||
| // Restore the scroll position if it was saved into the state | ||
| if (state.scrollY !== undefined) { | ||
| window.scrollTo(state.scrollX, state.scrollY); | ||
| } else { | ||
| window.scrollTo(0, 0); | ||
| } | ||
|
|
||
| // Remove the pre-rendered CSS because it's no longer used | ||
| // after the React app is launched | ||
| if (cssContainer) { | ||
| cssContainer.parentNode.removeChild(cssContainer); | ||
| cssContainer = null; | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| function run() { | ||
| let currentLocation = null; | ||
| let currentState = null; | ||
|
|
||
| // Make taps on links and buttons work fast on mobiles | ||
| FastClick.attach(document.body); | ||
|
|
||
| // Re-render the app when window.location changes | ||
| const unlisten = Location.listen(location => { | ||
| currentLocation = location; | ||
| currentState = Object.assign({}, location.state, { | ||
| path: location.pathname, | ||
| query: location.query, | ||
| state: location.state, | ||
| context, | ||
| }); | ||
| render(currentState); | ||
| }); | ||
|
|
||
| // Save the page scroll position into the current location's state | ||
| const supportPageOffset = window.pageXOffset !== undefined; | ||
| const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat'); | ||
| const setPageOffset = () => { | ||
| currentLocation.state = currentLocation.state || Object.create(null); | ||
| if (supportPageOffset) { | ||
| currentLocation.state.scrollX = window.pageXOffset; | ||
| currentLocation.state.scrollY = window.pageYOffset; | ||
| } else { | ||
| currentLocation.state.scrollX = isCSS1Compat ? | ||
| document.documentElement.scrollLeft : document.body.scrollLeft; | ||
| currentLocation.state.scrollY = isCSS1Compat ? | ||
| document.documentElement.scrollTop : document.body.scrollTop; | ||
| } | ||
| }; | ||
|
|
||
| addEventListener(window, 'scroll', setPageOffset); | ||
| addEventListener(window, 'pagehide', () => { | ||
| removeEventListener(window, 'scroll', setPageOffset); | ||
| unlisten(); | ||
| }); | ||
| } | ||
|
|
||
| // Run the application when both DOM is ready and page content is loaded | ||
| if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) { | ||
| run(); | ||
| } else { | ||
| document.addEventListener('DOMContentLoaded', run, false); | ||
| } |
| @@ -0,0 +1,59 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import emptyFunction from 'fbjs/lib/emptyFunction'; | ||
| import s from './App.scss'; | ||
| import Header from '../Header'; | ||
| import Feedback from '../Feedback'; | ||
| import Footer from '../Footer'; | ||
|
|
||
| class App extends Component { | ||
|
|
||
| static propTypes = { | ||
| context: PropTypes.shape({ | ||
| insertCss: PropTypes.func, | ||
| onSetTitle: PropTypes.func, | ||
| onSetMeta: PropTypes.func, | ||
| onPageNotFound: PropTypes.func, | ||
| }), | ||
| children: PropTypes.element.isRequired, | ||
| error: PropTypes.object, | ||
| }; | ||
|
|
||
| static childContextTypes = { | ||
| insertCss: PropTypes.func.isRequired, | ||
| onSetTitle: PropTypes.func.isRequired, | ||
| onSetMeta: PropTypes.func.isRequired, | ||
| onPageNotFound: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| getChildContext() { | ||
| const context = this.props.context; | ||
| return { | ||
| insertCss: context.insertCss || emptyFunction, | ||
| onSetTitle: context.onSetTitle || emptyFunction, | ||
| onSetMeta: context.onSetMeta || emptyFunction, | ||
| onPageNotFound: context.onPageNotFound || emptyFunction, | ||
| }; | ||
| } | ||
|
|
||
| componentWillMount() { | ||
| this.removeCss = this.props.context.insertCss(s); | ||
| } | ||
|
|
||
| componentWillUnmount() { | ||
| this.removeCss(); | ||
| } | ||
|
|
||
| render() { | ||
| return !this.props.error ? ( | ||
| <div> | ||
| <Header /> | ||
| {this.props.children} | ||
| <Feedback /> | ||
| <Footer /> | ||
| </div> | ||
| ) : this.props.children; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default App; |
| @@ -0,0 +1 @@ | ||
| @import '../variables.scss'; |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "App", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./App.js" | ||
| } |
| @@ -0,0 +1,31 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import s from './ContentPage.scss'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
|
|
||
| @withStyles(s) | ||
| class ContentPage extends Component { | ||
|
|
||
| static propTypes = { | ||
| path: PropTypes.string.isRequired, | ||
| content: PropTypes.string.isRequired, | ||
| title: PropTypes.string, | ||
| }; | ||
|
|
||
| static contextTypes = { | ||
| onSetTitle: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| render() { | ||
| this.context.onSetTitle(this.props.title); | ||
| return ( | ||
| <div className={s.root}> | ||
| <div className={s.container}> | ||
| <div dangerouslySetInnerHTML={{ __html: this.props.content || '' }} /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default ContentPage; |
| @@ -0,0 +1,11 @@ | ||
| @import '../variables.scss'; | ||
|
|
||
| .root { | ||
|
|
||
| } | ||
|
|
||
| .container { | ||
| margin: 0 auto; | ||
| padding: 0 0 40px; | ||
| max-width: $max-content-width; | ||
| } |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "ContentPage", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./ContentPage.js" | ||
| } |
| @@ -0,0 +1,30 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import s from './ErrorPage.scss'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
|
|
||
| const title = 'Error'; | ||
|
|
||
| @withStyles(s) | ||
| class ErrorPage extends Component { | ||
|
|
||
| static contextTypes = { | ||
| onSetTitle: PropTypes.func.isRequired, | ||
| onPageNotFound: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| componentWillMount() { | ||
| this.context.onSetTitle(title); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <div> | ||
| <h1>{title}</h1> | ||
| <p>Sorry, an critical error occurred on this page.</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default ErrorPage; |
| @@ -0,0 +1,44 @@ | ||
| * { | ||
| margin: 0; | ||
| line-height: 1.2; | ||
| } | ||
|
|
||
| html { | ||
| display: table; | ||
| width: 100%; | ||
| height: 100%; | ||
| color: #888; | ||
| text-align: center; | ||
| font-family: sans-serif; | ||
| } | ||
|
|
||
| body { | ||
| display: table-cell; | ||
| margin: 2em auto; | ||
| vertical-align: middle; | ||
| } | ||
|
|
||
| h1 { | ||
| color: #555; | ||
| font-weight: 400; | ||
| font-size: 2em; | ||
| } | ||
|
|
||
| p { | ||
| margin: 0 auto; | ||
| width: 280px; | ||
| } | ||
|
|
||
| @media only screen and (max-width: 280px) { | ||
|
|
||
| body, p { | ||
| width: 95%; | ||
| } | ||
|
|
||
| h1 { | ||
| font-size: 1.5em; | ||
| margin: 0 0 0.3em; | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "ErrorPage", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./ErrorPage.js" | ||
| } |
| @@ -0,0 +1,27 @@ | ||
| import React, { Component } from 'react'; | ||
| import s from './Footer.scss'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
| import Link from '../Link'; | ||
|
|
||
| @withStyles(s) | ||
| class Footer extends Component { | ||
|
|
||
| render() { | ||
| return ( | ||
| <div className={s.root}> | ||
| <div className={s.container}> | ||
| <span className={s.text}>© Your Company</span> | ||
| <span className={s.spacer}>·</span> | ||
| <a className={s.link} href="/" onClick={Link.handleClick}>Home</a> | ||
| <span className={s.spacer}>·</span> | ||
| <a className={s.link} href="/privacy" onClick={Link.handleClick}>Privacy</a> | ||
| <span className={s.spacer}>·</span> | ||
| <a className={s.link} href="/not-found" onClick={Link.handleClick}>Not Found</a> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default Footer; |
| @@ -0,0 +1,43 @@ | ||
| @import '../variables.scss'; | ||
|
|
||
| .root { | ||
| background: #333; | ||
| color: #fff; | ||
| } | ||
|
|
||
| .container { | ||
| margin: 0 auto; | ||
| padding: 20px 15px; | ||
| max-width: $max-content-width; | ||
| text-align: center; | ||
| } | ||
|
|
||
| .text { | ||
| color: rgba(255, 255, 255, .5); | ||
| } | ||
|
|
||
| .textMuted { | ||
| composes: text; | ||
| color: rgba(255, 255, 255, .3); | ||
| } | ||
|
|
||
| .spacer { | ||
| color: rgba(255, 255, 255, .3); | ||
| } | ||
|
|
||
| .text, | ||
| .link { | ||
| padding: 2px 5px; | ||
| font-size: 1em; | ||
| } | ||
|
|
||
| .link, | ||
| .link:active, | ||
| .link:visited { | ||
| color: rgba(255, 255, 255, .6); | ||
| text-decoration: none; | ||
| } | ||
|
|
||
| .link:hover { | ||
| color: rgba(255, 255, 255, 1); | ||
| } |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "Footer", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./Footer.js" | ||
| } |
| @@ -0,0 +1,28 @@ | ||
| import React, { | ||
| Component | ||
| } | ||
| from 'react'; | ||
| import s from './Header.scss'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
| import Link from '../Link'; | ||
|
|
||
| @withStyles(s) | ||
| class Header extends Component { | ||
|
|
||
| render() { | ||
| return ( | ||
| <nav className="nav-wrapper primary-bg dark"> | ||
| <a href="/" onClick={Link.handleClick} className="brand-logo"> | ||
| <img src={require('./udormio_logo.png')} alt="Udormio" height="40px" style={{margin:'10px'}}/> | ||
| </a> | ||
| <ul id="nav-mobile" className="right hide-on-med-and-down"> | ||
| <input type="button" id="logout" className="btn-large primary-bg dark" value="logout" /> | ||
| </ul> | ||
| </nav> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export | ||
| default Header; |
| @@ -0,0 +1 @@ | ||
| @import '../variables.scss'; |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "Header", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./Header.js" | ||
| } |
| @@ -0,0 +1,54 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import config from '../../config'; | ||
|
|
||
| class Html extends Component { | ||
|
|
||
| static propTypes = { | ||
| title: PropTypes.string, | ||
| description: PropTypes.string, | ||
| css: PropTypes.string, | ||
| body: PropTypes.string.isRequired, | ||
| entry: PropTypes.string.isRequired, | ||
| }; | ||
|
|
||
| static defaultProps = { | ||
| title: '', | ||
| description: '', | ||
| }; | ||
|
|
||
| trackingCode() { | ||
| return ({ __html: | ||
| `(function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=` + | ||
| `function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;` + | ||
| `e=o.createElement(i);r=o.getElementsByTagName(i)[0];` + | ||
| `e.src='https://www.google-analytics.com/analytics.js';` + | ||
| `r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));` + | ||
| `ga('create','${config.googleAnalyticsId}','auto');ga('send','pageview');`, | ||
| }); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <html className="no-js" lang=""> | ||
| <head> | ||
| <meta charSet="utf-8" /> | ||
| <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> | ||
| <title>{this.props.title}</title> | ||
| <meta name="description" content={this.props.description} /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <link rel="apple-touch-icon" href="apple-touch-icon.png" /> | ||
| <link rel="stylesheet" href="vender/materialize/css/materialize.min.css"/> | ||
| <style id="css" dangerouslySetInnerHTML={{ __html: this.props.css }} /> | ||
| </head> | ||
| <body> | ||
| <div id="app" dangerouslySetInnerHTML={{ __html: this.props.body }} /> | ||
| <script src={this.props.entry}></script> | ||
| <script dangerouslySetInnerHTML={this.trackingCode()} /> | ||
| </body> | ||
| </html> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default Html; |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "Html", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./Html.js" | ||
| } |
| @@ -0,0 +1,64 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import parsePath from 'history/lib/parsePath'; | ||
| import Location from '../../core/Location'; | ||
|
|
||
| function isLeftClickEvent(event) { | ||
| return event.button === 0; | ||
| } | ||
|
|
||
| function isModifiedEvent(event) { | ||
| return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); | ||
| } | ||
|
|
||
| class Link extends Component { | ||
|
|
||
| static propTypes = { | ||
| to: PropTypes.string.isRequired, | ||
| query: PropTypes.object, | ||
| state: PropTypes.object, | ||
| onClick: PropTypes.func, | ||
| }; | ||
|
|
||
| static handleClick = (event) => { | ||
| let allowTransition = true; | ||
| let clickResult; | ||
|
|
||
| if (this.props && this.props.onClick) { | ||
| clickResult = this.props.onClick(event); | ||
| } | ||
|
|
||
| if (isModifiedEvent(event) || !isLeftClickEvent(event)) { | ||
| return; | ||
| } | ||
|
|
||
| if (clickResult === false || event.defaultPrevented === true) { | ||
| allowTransition = false; | ||
| } | ||
|
|
||
| event.preventDefault(); | ||
|
|
||
| if (allowTransition) { | ||
| const link = event.currentTarget; | ||
| if (this.props && this.props.to) { | ||
| Location.push({ | ||
| ...(parsePath(this.props.to)), | ||
| state: this.props && this.props.state || null, | ||
| }); | ||
| } else { | ||
| Location.push({ | ||
| pathname: link.pathname, | ||
| search: link.search, | ||
| state: this.props && this.props.state || null, | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| render() { | ||
| const { to, query, ...props } = this.props; | ||
| return <a href={Location.createHref(to, query)} onClick={Link.handleClick.bind(this)} {...props} />; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default Link; |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "Link", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./Link.js" | ||
| } |
| @@ -0,0 +1,25 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
|
|
||
| @withStyles(s) | ||
| class LoginPage extends Component { | ||
|
|
||
| static contextTypes = { | ||
| onSetTitle: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| componentWillMount() { | ||
| this.context.onSetTitle('Log In'); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <div> | ||
|
|
||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default LoginPage; |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "LoginPage", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./LoginPage.js" | ||
| } |
| @@ -0,0 +1,31 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
| import s from './NotFoundPage.scss'; | ||
| import withStyles from '../../decorators/withStyles'; | ||
|
|
||
| const title = 'Page Not Found'; | ||
|
|
||
| @withStyles(s) | ||
| class NotFoundPage extends Component { | ||
|
|
||
| static contextTypes = { | ||
| onSetTitle: PropTypes.func.isRequired, | ||
| onPageNotFound: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| componentWillMount() { | ||
| this.context.onSetTitle(title); | ||
| this.context.onPageNotFound(); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <div> | ||
| <h1>{title}</h1> | ||
| <p>Sorry, but the page you were trying to view does not exist.</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default NotFoundPage; |
| @@ -0,0 +1,43 @@ | ||
| * { | ||
| margin: 0; | ||
| line-height: 1.2; | ||
| } | ||
|
|
||
| html { | ||
| display: table; | ||
| width: 100%; | ||
| height: 100%; | ||
| color: #888; | ||
| text-align: center; | ||
| font-family: sans-serif; | ||
| } | ||
|
|
||
| body { | ||
| display: table-cell; | ||
| margin: 2em auto; | ||
| vertical-align: middle; | ||
| } | ||
|
|
||
| h1 { | ||
| color: #555; | ||
| font-weight: 400; | ||
| font-size: 2em; | ||
| } | ||
|
|
||
| p { | ||
| margin: 0 auto; | ||
| width: 280px; | ||
| } | ||
|
|
||
| @media only screen and (max-width: 280px) { | ||
|
|
||
| body, p { | ||
| width: 95%; | ||
| } | ||
|
|
||
| h1 { | ||
| font-size: 1.5em; | ||
| margin: 0 0 0.3em; | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "NotFoundPage", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "main": "./NotFoundPage.js" | ||
| } |
| @@ -0,0 +1,5 @@ | ||
| /* | ||
| * Animations | ||
| * ========================================================================== */ | ||
|
|
||
| $animation-swift-out: .45s cubic-bezier(0.3, 1, 0.4, 1) 0s; |
| @@ -0,0 +1,4 @@ | ||
| export | ||
| default { | ||
| googleAnalyticsId: 'UA-XXXXX-X', | ||
| }; |
| @@ -0,0 +1,6 @@ | ||
| import keyMirror from 'fbjs/lib/keyMirror'; | ||
|
|
||
| export | ||
| default keyMirror({ | ||
|
|
||
| }); |
| @@ -0,0 +1,47 @@ | ||
| --- | ||
| title: About Us | ||
| component: ContentPage | ||
| --- | ||
| p. | ||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat | ||
| tortor fermentum mi fermentum dignissim. Nullam vel ipsum ut ligula elementum | ||
| lobortis. Maecenas aliquam, massa laoreet lacinia pretium, nisi urna venenatis | ||
| tortor, nec imperdiet tellus libero efficitur metus. Fusce semper posuere | ||
| ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem | ||
| pharetra commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat | ||
| augue libero, id consectetur tortor bibendum non. Quisque nec fringilla lorem. | ||
| Nullam efficitur vulputate mauris, nec maximus leo dignissim id. | ||
| p. | ||
| In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. | ||
| Morbi pellentesque venenatis felis sed convallis. Nulla varius, nibh vitae | ||
| placerat tempus, mauris sem elementum ipsum, eget sollicitudin nisl est vel | ||
| purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras pharetra | ||
| sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non | ||
| scelerisque purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus | ||
| posuere placerat. Sed et libero eu nibh tristique mollis a eget lectus. Donec | ||
| interdum augue sollicitudin vehicula hendrerit. Vivamus justo orci, molestie | ||
| ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu | ||
| tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in | ||
| consequat elit tellus auctor nulla. Donec placerat elementum diam, vitae | ||
| imperdiet lectus luctus at. | ||
| p. | ||
| Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam | ||
| non quam nisi. Donec rutrum turpis ac diam blandit, id pulvinar mauris | ||
| suscipit. Pellentesque tincidunt libero ultricies risus iaculis, sit amet | ||
| consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit | ||
| ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum | ||
| justo sapien, sit amet consequat orci accumsan et. Aliquam ornare ullamcorper | ||
| sem sed finibus. Nullam ac lacus pulvinar, egestas felis ut, accumsan est. | ||
| p. | ||
| Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem | ||
| hendrerit aliquam. Integer eu varius orci. Vestibulum ante ipsum primis in | ||
| faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum ante ipsum | ||
| primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut at mauris | ||
| nibh. Suspendisse maximus ac eros at vestibulum. | ||
| p. | ||
| Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas | ||
| tortor et dui consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, | ||
| vulputate nisl. Aenean congue varius ex, sit amet bibendum odio posuere at. | ||
| Nulla facilisi. In finibus, nulla vitae tincidunt ornare, sapien nulla | ||
| fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam | ||
| enim. |
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| title: Main | ||
| component: ContentPage | ||
| --- |
| @@ -0,0 +1,52 @@ | ||
| --- | ||
| title: Privacy Policy | ||
| component: ContentPage | ||
| --- | ||
| mixin breadcrumb | ||
| ol | ||
| li <a href="/">Home</a> | ||
| li <a href="/privacy">Privacy</a> | ||
|
|
||
| p. | ||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat | ||
| tortor fermentum mi fermentum dignissim. Nullam vel ipsum ut ligula elementum | ||
| lobortis. Maecenas aliquam, massa laoreet lacinia pretium, nisi urna venenatis | ||
| tortor, nec imperdiet tellus libero efficitur metus. Fusce semper posuere | ||
| ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem | ||
| pharetra commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat | ||
| augue libero, id consectetur tortor bibendum non. Quisque nec fringilla lorem. | ||
| Nullam efficitur vulputate mauris, nec maximus leo dignissim id. | ||
| p. | ||
| In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. | ||
| Morbi pellentesque venenatis felis sed convallis. Nulla varius, nibh vitae | ||
| placerat tempus, mauris sem elementum ipsum, eget sollicitudin nisl est vel | ||
| purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras pharetra | ||
| sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non | ||
| scelerisque purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus | ||
| posuere placerat. Sed et libero eu nibh tristique mollis a eget lectus. Donec | ||
| interdum augue sollicitudin vehicula hendrerit. Vivamus justo orci, molestie | ||
| ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu | ||
| tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in | ||
| consequat elit tellus auctor nulla. Donec placerat elementum diam, vitae | ||
| imperdiet lectus luctus at. | ||
| p. | ||
| Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam | ||
| non quam nisi. Donec rutrum turpis ac diam blandit, id pulvinar mauris | ||
| suscipit. Pellentesque tincidunt libero ultricies risus iaculis, sit amet | ||
| consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit | ||
| ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum | ||
| justo sapien, sit amet consequat orci accumsan et. Aliquam ornare ullamcorper | ||
| sem sed finibus. Nullam ac lacus pulvinar, egestas felis ut, accumsan est. | ||
| p. | ||
| Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem | ||
| hendrerit aliquam. Integer eu varius orci. Vestibulum ante ipsum primis in | ||
| faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum ante ipsum | ||
| primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut at mauris | ||
| nibh. Suspendisse maximus ac eros at vestibulum. | ||
| p. | ||
| Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas | ||
| tortor et dui consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, | ||
| vulputate nisl. Aenean congue varius ex, sit amet bibendum odio posuere at. | ||
| Nulla facilisi. In finibus, nulla vitae tincidunt ornare, sapien nulla | ||
| fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam | ||
| enim. |
| @@ -0,0 +1,17 @@ | ||
| export | ||
| function addEventListener(node, event, listener) { | ||
| if (node.addEventListener) { | ||
| node.addEventListener(event, listener, false); | ||
| } else { | ||
| node.attachEvent('on' + event, listener); | ||
| } | ||
| } | ||
|
|
||
| export | ||
| function removeEventListener(node, event, listener) { | ||
| if (node.removeEventListener) { | ||
| node.removeEventListener(event, listener, false); | ||
| } else { | ||
| node.detachEvent('on' + event, listener); | ||
| } | ||
| } |
| @@ -0,0 +1,9 @@ | ||
| import { | ||
| Dispatcher | ||
| } | ||
| from 'flux'; | ||
|
|
||
| const dispatcher = new Dispatcher(); | ||
|
|
||
| export | ||
| default dispatcher; |
| @@ -0,0 +1,39 @@ | ||
| import request from 'superagent'; | ||
| import { | ||
| canUseDOM | ||
| } | ||
| from 'fbjs/lib/ExecutionEnvironment'; | ||
|
|
||
| function getUrl(path) { | ||
| if (path.startsWith('http') || canUseDOM) { | ||
| return path; | ||
| } | ||
|
|
||
| return process.env.WEBSITE_HOSTNAME ? | ||
| `http://${process.env.WEBSITE_HOSTNAME}${path}` : | ||
| `http://127.0.0.1:${global.server.get('port')}${path}`; | ||
| } | ||
|
|
||
| const HttpClient = { | ||
|
|
||
| get: path => new Promise((resolve, reject) => { | ||
| request | ||
| .get(getUrl(path)) | ||
| .accept('application/json') | ||
| .end((err, res) => { | ||
| if (err) { | ||
| if (err.status === 404) { | ||
| resolve(null); | ||
| } else { | ||
| reject(err); | ||
| } | ||
| } else { | ||
| resolve(res.body); | ||
| } | ||
| }); | ||
| }), | ||
|
|
||
| }; | ||
|
|
||
| export | ||
| default HttpClient; |
| @@ -0,0 +1,12 @@ | ||
| import { | ||
| canUseDOM | ||
| } | ||
| from 'fbjs/lib/ExecutionEnvironment'; | ||
| import createHistory from 'history/lib/createBrowserHistory'; | ||
| import createMemoryHistory from 'history/lib/createMemoryHistory'; | ||
| import useQueries from 'history/lib/useQueries'; | ||
|
|
||
| const location = useQueries(canUseDOM ? createHistory : createMemoryHistory)(); | ||
|
|
||
| export | ||
| default location; |
| @@ -0,0 +1,21 @@ | ||
| import React, { Component, PropTypes } from 'react'; | ||
|
|
||
| export default (...styles) =>{ | ||
| return (BaseComponent) => class StyledComponent extends Component { | ||
| static contextTypes = { | ||
| insertCss: PropTypes.func.isRequired, | ||
| }; | ||
|
|
||
| componentWillMount() { | ||
| this.removeCss = this.context.insertCss.apply(undefined, styles); | ||
| } | ||
|
|
||
| componentWillUnmount() { | ||
| this.removeCss(); | ||
| } | ||
|
|
||
| render() { | ||
| return (<BaseComponent {...this.props} />); | ||
| } | ||
| }; | ||
| }; |
| @@ -0,0 +1,57 @@ | ||
| import React, { Component } from 'react'; // eslint-disable-line no-unused-vars | ||
| import EventEmitter from 'eventemitter3'; | ||
| import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; | ||
|
|
||
| let EE; | ||
| let viewport = { width: 1366, height: 768 }; // Default size for server-side rendering | ||
| const RESIZE_EVENT = 'resize'; | ||
|
|
||
| function handleWindowResize() { | ||
| if (viewport.width !== window.innerWidth || viewport.height !== window.innerHeight) { | ||
| viewport = { width: window.innerWidth, height: window.innerHeight }; | ||
| EE.emit(RESIZE_EVENT, viewport); | ||
| } | ||
| } | ||
|
|
||
| function withViewport(ComposedComponent) { | ||
| return class WithViewport extends Component { | ||
|
|
||
| constructor() { | ||
| super(); | ||
|
|
||
| this.state = { | ||
| viewport: canUseDOM ? { width: window.innerWidth, height: window.innerHeight } : viewport, | ||
| }; | ||
| } | ||
|
|
||
| componentDidMount() { | ||
| if (!EE) { | ||
| EE = new EventEmitter(); | ||
| window.addEventListener('resize', handleWindowResize); | ||
| window.addEventListener('orientationchange', handleWindowResize); | ||
| } | ||
|
|
||
| EE.on(RESIZE_EVENT, this.handleResize, this); | ||
| } | ||
|
|
||
| componentWillUnmount() { | ||
| EE.removeListener(RESIZE_EVENT, this.handleResize, this); | ||
| if (!EE.listeners(RESIZE_EVENT, true)) { | ||
| window.removeEventListener('resize', handleWindowResize); | ||
| window.removeEventListener('orientationchange', handleWindowResize); | ||
| EE = null; | ||
| } | ||
| } | ||
|
|
||
| render() { | ||
| return <ComposedComponent {...this.props} viewport={this.state.viewport}/>; | ||
| } | ||
|
|
||
| handleResize(value) { | ||
| this.setState({ viewport: value }); // eslint-disable-line react/no-set-state | ||
| } | ||
|
|
||
| }; | ||
| } | ||
|
|
||
| export default withViewport; |
| @@ -0,0 +1,21 @@ | ||
| The MIT License (MIT) | ||
|
|
||
| Copyright (c) 2014-2015 Materialize | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| @@ -0,0 +1,70 @@ | ||
|  | ||
| =========== | ||
|
|
||
| [](https://gitter.im/Dogfalo/materialize?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
| Materialize, a CSS Framework based on material design | ||
|
|
||
| ### Current Version : v0.97.2 | ||
|
|
||
| ## Sass Requirements: | ||
| - Ruby Sass 3.3+, LibSass 0.6+ | ||
|
|
||
| ## Supported Browsers: | ||
| Chrome 35+, Firefox 31+, Safari 7+, IE 10+ | ||
|
|
||
| ## Changelog | ||
| - v0.97.2 (Nov 8, 2015) | ||
| - Image support inside select | ||
| - Optgroup supported in select | ||
| - Multiple select added | ||
| - Card styling fixes | ||
| - Breadcrumbs added | ||
| - Scrollable tabs | ||
| - Tooltips and dropdowns position themselves more intelligently inside the window | ||
| - FAB menu is click-toggleable | ||
| - Horizontal FAB support added | ||
| - v0.97.1 (Sep 13, 2015) | ||
| - Added new range slider with uses noUiSlider to provide powerful options | ||
| - Added CSS for Chips | ||
| - Toasts support adding of html elements | ||
| - Fixed select destroy/creation bug | ||
| - Bugfixes for dropdown, badges, collections, scrollfire | ||
| - Added default preloader color variable | ||
| - File input now supports multiple files and dynamically loaded elements | ||
| - v0.97.0 (June 21, 2015) | ||
| - **Documentation changed to use Official Google Icon web font** | ||
| - **Input errors added** | ||
| - Flicker on Firefox on dropdowns fixed | ||
| - Pagination made more responsive | ||
| - Modal now prevents scrolling | ||
| - Modal animation added | ||
| - Support for multiple modals added | ||
| - Programmatic control of FAB to open/close added | ||
| - Programmatic control of slider to play/pause added | ||
| - Plus many more bug fixes | ||
| - v0.96.1 (April 6, 2015) | ||
| - Dropdown Fixes | ||
| - Materialize functions fixed for Meteor | ||
| - v0.96.0 (April 1, 2015) | ||
| - **Toasts, transitions, scrollfire added under Materialize namespace** | ||
| - **Dropdown is now created as a child of its parent** | ||
| - Collapsibles supports nesting | ||
| - Modal Bottom Sheet added | ||
| - Indeterminate Checkboxes added | ||
| - New Checkbox Style Added | ||
| - Text Inputs supports placeholder/readonly | ||
| - Google Inbox-like Collapsible added | ||
| - Text Character Counter added | ||
| - Waves no longer breaks on SVG's | ||
|
|
||
| - v0.95.3 (Feb 25, 2015) | ||
| - Parallax image loading / responsiveness fixes | ||
| - Date picker supports month/year as dropdown | ||
| - Dismissable collection items | ||
| - Avatar collection items | ||
| - Pagination Added | ||
| - ScrollFire fixes | ||
|
|
||
|
|
||
| ## Contributing | ||
| [Please read CONTRIBUTING.md for more information](CONTRIBUTING.md) |
| @@ -0,0 +1,35 @@ | ||
| import React from 'react'; | ||
| import Router from 'react-routing/src/Router'; | ||
| import http from './core/HttpClient'; | ||
| import App from './components/App'; | ||
| import ContentPage from './components/ContentPage'; | ||
| import ContactPage from './components/ContactPage'; | ||
| import LoginPage from './components/LoginPage'; | ||
| import RegisterPage from './components/RegisterPage'; | ||
| import NotFoundPage from './components/NotFoundPage'; | ||
| import ErrorPage from './components/ErrorPage'; | ||
|
|
||
| const router = new Router(on => { | ||
| on('*', async (state, next) => { | ||
| const component = await next(); | ||
| return component && <App context={state.context}>{component}</App>; | ||
| }); | ||
|
|
||
| on('/contact', async () => <ContactPage />); | ||
|
|
||
| on('/login', async () => <LoginPage />); | ||
|
|
||
| on('/register', async () => <RegisterPage />); | ||
|
|
||
| on('*', async (state) => { | ||
| const content = await http.get(`/api/content?path=${state.path}`); | ||
| return content && <ContentPage {...content} />; | ||
| }); | ||
|
|
||
| on('error', (state, error) => state.statusCode === 404 ? | ||
| <App context={state.context} error={error}><NotFoundPage /></App> : | ||
| <App context={state.context} error={error}><ErrorPage /></App> | ||
| ); | ||
| }); | ||
|
|
||
| export default router; |
| @@ -0,0 +1,57 @@ | ||
| import 'babel-core/polyfill'; | ||
| import path from 'path'; | ||
| import express from 'express'; | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom/server'; | ||
| import Router from './routes'; | ||
| import Html from './components/Html'; | ||
| import assets from './assets.json'; | ||
|
|
||
| const server = global.server = express(); | ||
| const port = process.env.PORT || 5000; | ||
| server.set('port', port); | ||
|
|
||
| // | ||
| // Register Node.js middleware | ||
| // ----------------------------------------------------------------------------- | ||
| server.use(express.static(path.join(__dirname, 'public'))); | ||
|
|
||
| // | ||
| // Register API middleware | ||
| // ----------------------------------------------------------------------------- | ||
| server.use('/api/content', require('./api/content')); | ||
|
|
||
| // | ||
| // Register server-side rendering middleware | ||
| // ----------------------------------------------------------------------------- | ||
| server.get('*', async (req, res, next) => { | ||
| try { | ||
| let statusCode = 200; | ||
| const data = { title: '', description: '', css: '', body: '', entry: assets.app.js }; | ||
| const css = []; | ||
| const context = { | ||
| insertCss: styles => css.push(styles._getCss()), | ||
| onSetTitle: value => data.title = value, | ||
| onSetMeta: (key, value) => data[key] = value, | ||
| onPageNotFound: () => statusCode = 404, | ||
| }; | ||
|
|
||
| await Router.dispatch({ path: req.path, context }, (state, component) => { | ||
| data.body = ReactDOM.renderToString(component); | ||
| data.css = css.join(''); | ||
| }); | ||
|
|
||
| const html = ReactDOM.renderToStaticMarkup(<Html {...data} />); | ||
| res.status(statusCode).send('<!doctype html>\n' + html); | ||
| } catch (err) { | ||
| next(err); | ||
| } | ||
| }); | ||
|
|
||
| // | ||
| // Launch the server | ||
| // ----------------------------------------------------------------------------- | ||
| server.listen(port, () => { | ||
| /* eslint-disable no-console */ | ||
| console.log(`The server is running at http://localhost:${port}/`); | ||
| }); |
| @@ -12,7 +12,6 @@ async function copy() { | ||
|
|
||
| await Promise.all([ | ||
| ncp('src/public', 'build/public'), | ||
| ncp('src/content', 'build/content'), | ||
| ncp('package.json', 'build/package.json'), | ||
| ]); | ||