├── .gitattributes ├── src ├── pages │ ├── AboutPage │ │ ├── AboutPage.scss │ │ ├── AboutPage.spec.js │ │ ├── AboutPage.i18n.js │ │ └── AboutPage.js │ ├── AssetsPage │ │ ├── AssetsPage.scss │ │ ├── AssetsPage.spec.js │ │ ├── AssetsPage.i18n.js │ │ └── AssetsPage.js │ ├── ChunkPage │ │ ├── ChunkPage.scss │ │ ├── ChunkPage.spec.js │ │ ├── ChunkPage.i18n.js │ │ └── ChunkPage.js │ ├── ChunksPage │ │ ├── ChunksPage.scss │ │ ├── ChunksPage.spec.js │ │ ├── ChunksPage.i18n.js │ │ └── ChunksPage.js │ ├── ErrorPage │ │ ├── ErrorPage.scss │ │ ├── ErrorPage.spec.js │ │ ├── ErrorPage.i18n.js │ │ └── ErrorPage.js │ ├── ErrorsPage │ │ ├── ErrorsPage.scss │ │ ├── ErrorsPage.spec.js │ │ ├── ErrorsPage.i18n.js │ │ └── ErrorsPage.js │ ├── HintsPage │ │ ├── HintsPage.scss │ │ ├── HintsPage.spec.js │ │ ├── HintsPage.i18n.js │ │ └── HintsPage.js │ ├── ModulePage │ │ ├── ModulePage.scss │ │ ├── ModulePage.spec.js │ │ ├── ModulePage.i18n.js │ │ └── ModulePage.js │ ├── ModulesPage │ │ ├── ModulesPage.scss │ │ ├── ModulesPage.spec.js │ │ ├── ModulesPage.i18n.js │ │ └── ModulesPage.js │ ├── SelectPage │ │ ├── SelectPage.scss │ │ ├── SelectPage.spec.js │ │ ├── SelectPage.i18n.js │ │ └── SelectPage.js │ ├── UploadPage │ │ ├── UploadPage.scss │ │ ├── UploadPage.spec.js │ │ ├── UploadPage.i18n.js │ │ └── UploadPage.js │ ├── WarningsPage │ │ ├── WarningsPage.scss │ │ ├── WarningsPage.spec.js │ │ ├── WarningsPage.i18n.js │ │ └── WarningsPage.js │ ├── ProfileEditPage │ │ ├── ProfileEditPage.i18n.js │ │ └── ProfileEditPage.js │ └── HomePage │ │ ├── HomePage.scss │ │ ├── HomePage.i18n.js │ │ ├── HomePageHero.js │ │ └── HomePage.js ├── containers │ ├── MainHeader │ │ ├── MainHeader.scss │ │ └── MainHeader.js │ ├── DevTools.js │ ├── ProfileEditForm │ │ ├── ProfileEditForm.scss │ │ ├── ProfileEditForm.validations.js │ │ ├── ProfileEditForm.i18n.js │ │ └── ProfileEditForm.js │ ├── HeroPageLayout.js │ ├── AdminPageLayout.js │ ├── Root.js │ ├── AppContainer.js │ ├── Sidebar │ │ └── Sidebar.js │ ├── AuthenticatedComponent.js │ └── LanguageSelectionDropdown │ │ └── LanguageSelectionDropdown.js ├── static │ ├── robots.txt │ ├── humans.txt │ ├── favicon.ico │ ├── favicon.png │ ├── images │ │ ├── network-ai.jpg │ │ ├── network-bg.png │ │ ├── facebook_logo.png │ │ ├── twitter_logo.png │ │ ├── default-profile.png │ │ ├── twitter_logo_2x.png │ │ ├── facebook_logo_2x.png │ │ └── webpack-r3analyse.png │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── api │ │ └── profile.json ├── settings.js ├── _BIN │ ├── pages │ │ ├── error │ │ │ ├── error.jade │ │ │ └── page.js │ │ ├── errors │ │ │ ├── errors.jade │ │ │ └── page.js │ │ ├── home │ │ │ ├── page.js │ │ │ └── home.jade │ │ ├── warnings │ │ │ ├── warnings.jade │ │ │ └── page.js │ │ ├── assets │ │ │ ├── page.js │ │ │ └── assets.jade │ │ ├── chunks │ │ │ ├── page.js │ │ │ └── chunks.jade │ │ ├── select │ │ │ ├── page.js │ │ │ └── application.jade │ │ ├── chunk │ │ │ ├── page.js │ │ │ └── chunk.jade │ │ ├── module │ │ │ ├── page.js │ │ │ └── module.jade │ │ ├── modules │ │ │ ├── page.js │ │ │ └── modules.jade │ │ ├── upload │ │ │ ├── application.jade │ │ │ └── page.js │ │ └── hints │ │ │ ├── page.js │ │ │ └── hints.jade │ ├── googleAnalytics.js │ ├── entry.js │ ├── Graphs │ │ ├── chunks.js │ │ └── modules.js │ └── app.js ├── declarations │ ├── i18n-types.js │ └── app.js ├── utils │ ├── findById.js │ ├── formatSize.js │ └── percentageToColor.js ├── translations │ ├── index.js │ ├── es.js │ ├── en.js │ └── pl.js ├── components │ ├── Hero │ │ ├── index.js │ │ ├── HeroBackground.js │ │ ├── HeroContent.js │ │ ├── Hero.scss │ │ └── Hero.js │ ├── Login │ │ └── Login.js │ ├── VAlign │ │ ├── VAlign.scss │ │ └── VAlign.js │ ├── DropdownProfileCard │ │ ├── DropdownProfileCard.scss │ │ ├── DropdownProfileCard.js │ │ └── DropdownProfileCard.spec.js │ ├── Spinner │ │ ├── Spinner.scss │ │ └── Spinner.js │ ├── FormFields │ │ ├── DropDown.js │ │ ├── TextInput.js │ │ ├── HorizontalRadioGroup.js │ │ ├── DropDown.spec.js │ │ ├── FormErrorMessages.js │ │ ├── TextInput.spec.js │ │ └── HorizontalRadioGroup.spec.js │ ├── MainFooter │ │ ├── MainFooter.scss │ │ └── MainFooter.js │ ├── UserDropdownMenu │ │ └── UserDropdownMenu.js │ └── DocumentTitle.js ├── styles │ ├── base.scss │ ├── admin.scss │ ├── app.scss │ └── vendor │ │ ├── normalize.css │ │ └── skeleton.css ├── redux │ ├── root-reducer.js │ ├── modules │ │ ├── language │ │ │ └── language.js │ │ ├── auth │ │ │ ├── auth-reducer.js │ │ │ └── auth-actions.js │ │ ├── user │ │ │ ├── user-reducer.js │ │ │ ├── user-actions.spec.js │ │ │ └── user-actions.js │ │ ├── document-title │ │ │ └── document-title.js │ │ └── spinner │ │ │ └── spinner.js │ └── configure-store.js ├── shared │ ├── forms.js │ └── links.js ├── api │ └── user.js ├── index.html ├── app.js └── routes.js ├── interfaces └── debug.js ├── .gitignore ├── .eslintignore ├── .editorconfig ├── .travis.yml ├── config ├── environments │ ├── development.js │ └── production.js └── index.js ├── README.md ├── .babelrc ├── .flowconfig ├── LICENSE ├── server └── index.js ├── package.json └── webpack.config.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * eol=lf -------------------------------------------------------------------------------- /src/pages/AboutPage/AboutPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/AboutPage/AboutPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/AssetsPage/AssetsPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ChunkPage/ChunkPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ChunkPage/ChunkPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ChunksPage/ChunksPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ErrorPage/ErrorPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ErrorPage/ErrorPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ErrorsPage/ErrorsPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/HintsPage/HintsPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/HintsPage/HintsPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ModulePage/ModulePage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ModulesPage/ModulesPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/SelectPage/SelectPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/UploadPage/UploadPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/containers/MainHeader/MainHeader.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/AssetsPage/AssetsPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ChunksPage/ChunksPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ErrorsPage/ErrorsPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ModulePage/ModulePage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ModulesPage/ModulesPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/SelectPage/SelectPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/UploadPage/UploadPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/WarningsPage/WarningsPage.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/WarningsPage/WarningsPage.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /interfaces/debug.js: -------------------------------------------------------------------------------- 1 | declare var __DEBUG__: boolean; 2 | -------------------------------------------------------------------------------- /src/static/humans.txt: -------------------------------------------------------------------------------- 1 | # Check it out: http://humanstxt.org/ 2 | -------------------------------------------------------------------------------- /src/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/favicon.ico -------------------------------------------------------------------------------- /src/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/favicon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | dist/ 3 | node_modules/ 4 | lib-cov/ 5 | coverage/ 6 | .DS_Store 7 | *.log 8 | *.map 9 | -------------------------------------------------------------------------------- /src/static/images/network-ai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/network-ai.jpg -------------------------------------------------------------------------------- /src/static/images/network-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/network-bg.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/** 2 | node_modules/** 3 | dist/** 4 | *.spec.js 5 | src/index.html 6 | src/_BIN 7 | interfaces/** 8 | -------------------------------------------------------------------------------- /src/static/images/facebook_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/facebook_logo.png -------------------------------------------------------------------------------- /src/static/images/twitter_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/twitter_logo.png -------------------------------------------------------------------------------- /src/static/images/default-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/default-profile.png -------------------------------------------------------------------------------- /src/static/images/twitter_logo_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/twitter_logo_2x.png -------------------------------------------------------------------------------- /src/settings.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'r3analyse', 3 | description: 'Rethink, React, Redux - Webpack Analyse Tool', 4 | } 5 | -------------------------------------------------------------------------------- /src/static/images/facebook_logo_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/facebook_logo_2x.png -------------------------------------------------------------------------------- /src/static/images/webpack-r3analyse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/images/webpack-r3analyse.png -------------------------------------------------------------------------------- /src/_BIN/pages/error/error.jade: -------------------------------------------------------------------------------- 1 | .jumbotron 2 | h1 Page #{page} not found 3 | p: a.btn.btn-primary.btn-lg(href="#home") Back home -------------------------------------------------------------------------------- /src/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/analyse-tool/master/src/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/declarations/i18n-types.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export type FormattedMessageType = { 4 | id: string; 5 | defaultMessage: string; 6 | description?: string; 7 | }; 8 | -------------------------------------------------------------------------------- /src/pages/ProfileEditPage/ProfileEditPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'profile.edit.title', 4 | defaultMessage: 'Edit Profile', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/_BIN/pages/error/page.js: -------------------------------------------------------------------------------- 1 | module.exports = function(err, page) { 2 | document.title = "error"; 3 | $(".page").html(require("./error.jade")({ 4 | err : err, 5 | page: page 6 | })); 7 | }; -------------------------------------------------------------------------------- /src/_BIN/pages/errors/errors.jade: -------------------------------------------------------------------------------- 1 | if errors.length === 0 2 | h2 No errors. 3 | each error in errors 4 | .alert.alert-danger 5 | h4= error.header 6 | pre= error.text 7 | h5= error.footer -------------------------------------------------------------------------------- /src/utils/findById.js: -------------------------------------------------------------------------------- 1 | module.exports = function findById(array, id) { 2 | for (let i = 0; i < array.length; i++) { 3 | if (array[i].id === id) { return array[i]; } 4 | } 5 | return null; 6 | }; 7 | -------------------------------------------------------------------------------- /src/_BIN/pages/home/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function() { 4 | document.title = "home"; 5 | $(".page").html(require("./home.jade")({ 6 | stats: app.stats 7 | })); 8 | }; -------------------------------------------------------------------------------- /src/translations/index.js: -------------------------------------------------------------------------------- 1 | import en from './en'; 2 | import es from './es'; 3 | import pl from './pl'; 4 | 5 | const messages = { 6 | en, 7 | es, 8 | pl, 9 | }; 10 | 11 | export default messages; 12 | -------------------------------------------------------------------------------- /src/_BIN/pages/warnings/warnings.jade: -------------------------------------------------------------------------------- 1 | if warnings.length === 0 2 | h2 No warnings. 3 | each warning in warnings 4 | .alert.alert-warning 5 | h4= warning.header 6 | pre= warning.text 7 | h5= warning.footer -------------------------------------------------------------------------------- /src/_BIN/pages/assets/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function() { 4 | document.title = "assets"; 5 | $(".page").html(require("./assets.jade")({ 6 | stats: app.stats 7 | })); 8 | }; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | end_of_line = lf 4 | indent_size = 2 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | 8 | [*.md] 9 | max_line_length = 0 10 | trim_trailing_whitespace = false 11 | -------------------------------------------------------------------------------- /src/components/Hero/index.js: -------------------------------------------------------------------------------- 1 | import Hero from './Hero'; 2 | import HeroBackground from './HeroBackground'; 3 | import HeroContent from './HeroContent'; 4 | 5 | export { 6 | Hero, 7 | HeroContent, 8 | HeroBackground, 9 | }; 10 | -------------------------------------------------------------------------------- /src/pages/HomePage/HomePage.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/base.scss"; 2 | 3 | .title { 4 | font-weight: bold; 5 | } 6 | 7 | 8 | .hero-title { 9 | font-weight: bold; 10 | color: $white; 11 | text-transform: uppercase; 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/formatSize.js: -------------------------------------------------------------------------------- 1 | module.exports = function formatSize(s) { 2 | if (s < 2048) { return `${ s } bytes`; } 3 | s /= 1024; 4 | if (s < 2048) { return `${ Math.round(s) } KiB`; } 5 | s /= 1024; 6 | return `${ Math.round(s) } MiB`; 7 | }; 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | env: 6 | - NODE_ENV=development NPM_CONFIG_PROGRESS=false 7 | 8 | node_js: 9 | - '6.8' 10 | 11 | script: 12 | - npm run travis 13 | 14 | notifications: 15 | slack: 16 | secure: "" 17 | -------------------------------------------------------------------------------- /src/pages/AboutPage/AboutPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'aboutPage.title', 4 | defaultMessage: 'About Us', 5 | }, 6 | overview: { 7 | id: 'aboutPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ChunkPage/ChunkPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'chunkPage.title', 4 | defaultMessage: 'Chunk', 5 | }, 6 | overview: { 7 | id: 'chunkPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ErrorPage/ErrorPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'errorPage.title', 4 | defaultMessage: 'Error', 5 | }, 6 | overview: { 7 | id: 'errorPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/HintsPage/HintsPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'hintsPage.title', 4 | defaultMessage: 'Hints', 5 | }, 6 | overview: { 7 | id: 'hintsPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/AssetsPage/AssetsPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'assetsPage.title', 4 | defaultMessage: 'Assets', 5 | }, 6 | overview: { 7 | id: 'assetsPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ChunksPage/ChunksPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'chunksPage.title', 4 | defaultMessage: 'Chunks', 5 | }, 6 | overview: { 7 | id: 'chunksPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ErrorsPage/ErrorsPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'errorsPage.title', 4 | defaultMessage: 'Errors', 5 | }, 6 | overview: { 7 | id: 'errorsPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ModulePage/ModulePage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'modulePage.title', 4 | defaultMessage: 'Module', 5 | }, 6 | overview: { 7 | id: 'modulePage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/SelectPage/SelectPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'selectPage.title', 4 | defaultMessage: 'Select', 5 | }, 6 | overview: { 7 | id: 'selectPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/UploadPage/UploadPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'uploadPage.title', 4 | defaultMessage: 'Upload', 5 | }, 6 | overview: { 7 | id: 'uploadPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/ModulesPage/ModulesPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'modulesPage.title', 4 | defaultMessage: 'Modules', 5 | }, 6 | overview: { 7 | id: 'modulesPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/WarningsPage/WarningsPage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'warningsPage.title', 4 | defaultMessage: 'Warnings', 5 | }, 6 | overview: { 7 | id: 'warningsPage.overview', 8 | defaultMessage: 'overview', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/Login/Login.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const Login = (props) => 4 | Login; 5 | 6 | Login.displayName = 'Login'; 7 | Login.propTypes = { 8 | onClick: PropTypes.func.isRequired, 9 | }; 10 | 11 | export default Login; 12 | -------------------------------------------------------------------------------- /config/environments/development.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: { 3 | devtool: 'cheap-module-eval-source-map', 4 | }, 5 | 6 | compiler: { 7 | hash_type: 'hash', 8 | stats: { 9 | chunks: false, 10 | chunkModules: false, 11 | colors: true, 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/_BIN/pages/chunks/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | var chunksGraph = require("../../graphs/chunks"); 3 | 4 | module.exports = function() { 5 | document.title = "chunks"; 6 | $(".page").html(require("./chunks.jade")({ 7 | stats: app.stats 8 | })); 9 | chunksGraph.show(); 10 | return function() { 11 | chunksGraph.hide(); 12 | } 13 | }; -------------------------------------------------------------------------------- /src/containers/DevTools.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createDevTools } from 'redux-devtools'; 3 | import LogMonitor from 'redux-devtools-log-monitor'; 4 | import DockMonitor from 'redux-devtools-dock-monitor'; 5 | 6 | export default createDevTools( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/declarations/app.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // store custom type declarations here 4 | 5 | export type User = { 6 | userId: string; 7 | name: string; 8 | givenName: string; 9 | familyName: string; 10 | nickname: string; 11 | picture: string; 12 | email: string; 13 | emailVerified: boolean; 14 | roles: string[]; 15 | createdAt: string; 16 | updatedAt: string; 17 | locale: string; 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/VAlign/VAlign.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: table; 3 | position: relative; 4 | } 5 | 6 | .horizontal { 7 | width: 100%; 8 | } 9 | 10 | .vertical { 11 | height: 100%; 12 | } 13 | .top, .middle, .bottom { 14 | display: table-cell; 15 | } 16 | 17 | .top { 18 | vertical-align: top; 19 | } 20 | 21 | .middle { 22 | vertical-align: middle; 23 | } 24 | 25 | .bottom { 26 | vertical-align: bottom; 27 | } 28 | -------------------------------------------------------------------------------- /config/environments/production.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | server: { 3 | hostname: 'bartekus.github.io/r3', 4 | port: 80, 5 | }, 6 | 7 | webpack: { 8 | devtool: 'source-map', 9 | output: { 10 | publicPath: '/r3', 11 | }, 12 | }, 13 | 14 | compiler: { 15 | hash_type: 'chunkhash', 16 | stats: { 17 | chunks: true, 18 | chunkModules: true, 19 | colors: true, 20 | }, 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/_BIN/pages/select/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function(stats) { 4 | document.title = "select"; 5 | $("body").html(require("./application.jade")(stats)); 6 | $(".modal").modal({ show: true }); 7 | $(".js-select").click(function() { 8 | var index = $(this).data("index"); 9 | app.load(stats.children[index]); 10 | $(".modal").modal("hide"); 11 | app.loadPage("home"); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/styles/base.scss: -------------------------------------------------------------------------------- 1 | $padding-size: 25px; 2 | 3 | $color-footer: #E9E9E9; 4 | 5 | $fonts: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 6 | 7 | 8 | // colors 9 | $white: #f5f5f5; 10 | $light-grey: #f5f5f5; 11 | $medium-grey: #82888a; 12 | $dark-grey: #565a5c; 13 | 14 | 15 | // admin 16 | $admin-nav-background-color: #363c4a; 17 | $admin-nav-text-color: #bbb; 18 | $admin-nav-dropdown-menu-bg: #ffffff; 19 | $admin-nav-dropdown-menu-color: #777888; 20 | -------------------------------------------------------------------------------- /src/containers/ProfileEditForm/ProfileEditForm.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 13px 0; 3 | } 4 | 5 | .picture { 6 | display: inline-block; 7 | max-width: 100%; 8 | max-height: 100%; 9 | border-radius: 10px; 10 | } 11 | 12 | .details { 13 | display: inline-block; 14 | padding: 0 0 0 14px; 15 | vertical-align: top; 16 | } 17 | 18 | .fullName { 19 | font-size: 24px; 20 | font-weight: 400; 21 | } 22 | 23 | .updateButton { 24 | margin-top: 20px; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Hero/HeroBackground.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Hero.scss'; 3 | 4 | export default class HeroBackground extends React.Component { 5 | static displayName = 'HeroBackground'; 6 | static propTypes = { 7 | image: React.PropTypes.string.isRequired, 8 | }; 9 | 10 | render() { 11 | return ( 12 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # analyse-tool 2 | 3 | [![Join the chat at https://gitter.im/webpack/analyse-tool](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/webpack/analyse-tool?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/webpack/analyse-tool.svg)](https://travis-ci.org/webpack/analyse-tool) 4 | 5 | A tool to analyse your webpack build result. It allows to browse the compilation and points out options to optimize the build. 6 | -------------------------------------------------------------------------------- /src/components/Hero/HeroContent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Hero.scss'; 3 | 4 | export default class HeroContent extends React.Component { 5 | static displayName = 'HeroContent'; 6 | static propTypes = { 7 | children: React.PropTypes.node, 8 | }; 9 | 10 | render() { 11 | const children = this.props.children; 12 | return ( 13 |
14 | {children || ''} 15 |
16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/_BIN/pages/chunk/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | var modulesGraph = require("../../graphs/modules"); 3 | 4 | module.exports = function(id) { 5 | id = parseInt(id, 10); 6 | document.title = "chunk " + id; 7 | $(".page").html(require("./chunk.jade")({ 8 | stats: app.stats, 9 | id : id, 10 | chunk: app.mapChunks[id] 11 | })); 12 | modulesGraph.show(); 13 | modulesGraph.setActiveChunk(id); 14 | return function() { 15 | modulesGraph.hide(); 16 | } 17 | }; -------------------------------------------------------------------------------- /src/styles/admin.scss: -------------------------------------------------------------------------------- 1 | @import 'base'; 2 | 3 | #content { 4 | margin-top: 1rem; 5 | } 6 | 7 | .box { 8 | background-color: white; 9 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.02); 10 | border: 1px solid #DBDBDC; 11 | border-radius: 3px; 12 | min-height: 20px; 13 | } 14 | 15 | .box-title { 16 | margin: 0; 17 | border-bottom: 1px solid black; 18 | font-size: 15px; 19 | padding: 0 15px; 20 | color: #4b4f5d; 21 | border-bottom-color: #f2f2f2; 22 | font-size: 15px; 23 | line-height: 48px; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/DropdownProfileCard/DropdownProfileCard.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | min-height: 40px; 4 | padding: 13px 13px 13px 56px !important; 5 | margin: 0 !important; 6 | } 7 | 8 | .picture { 9 | display: block; 10 | position: absolute !important; 11 | left: 14px; 12 | top: 14px; 13 | width: 31px; 14 | height: 31px; 15 | border-radius: 20px; 16 | } 17 | 18 | .nickname { 19 | display: block; 20 | } 21 | 22 | .username { 23 | display: block; 24 | font-weight: 800; 25 | color: #454545; 26 | } 27 | -------------------------------------------------------------------------------- /src/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import 'base'; 2 | 3 | // Some best-practice CSS that's useful for most apps 4 | // Just remove them if they're not what you want 5 | html { 6 | box-sizing: border-box; 7 | } 8 | 9 | html, body { 10 | margin: 0; 11 | padding: 0; 12 | height: 100%; 13 | } 14 | 15 | *, 16 | *:before, 17 | *:after { 18 | box-sizing: inherit; 19 | } 20 | body { 21 | font-family: $fonts; 22 | } 23 | 24 | a { 25 | cursor: pointer; 26 | } 27 | 28 | table pre { 29 | margin-bottom: 0; 30 | } 31 | 32 | .sortable-th { 33 | cursor: pointer; 34 | } 35 | -------------------------------------------------------------------------------- /src/_BIN/pages/errors/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function() { 4 | document.title = "errors"; 5 | $(".page").html(require("./errors.jade")({ 6 | stats : app.stats, 7 | errors: app.stats.errors.map(function(str) { 8 | str = str.split("\n"); 9 | var header = str.shift(); 10 | var footer = str.pop(); 11 | if (!/^ @/.test(footer)) str.push(footer), footer = ""; 12 | return { 13 | header: header, 14 | text : str.join("\n"), 15 | footer: footer 16 | } 17 | }) 18 | })); 19 | }; -------------------------------------------------------------------------------- /src/_BIN/pages/module/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | var modulesGraph = require("../../graphs/modules"); 3 | 4 | module.exports = function(id) { 5 | id = parseInt(id, 10); 6 | var m = app.mapModulesUid[id]; 7 | document.title = "module " + m.id; 8 | $(".page").html(require("./module.jade")({ 9 | stats : app.stats, 10 | id : id, 11 | module: m, 12 | issuer: app.mapModulesUid[m.issuerUid] 13 | })); 14 | modulesGraph.show(); 15 | modulesGraph.setActiveModule(id); 16 | return function() { 17 | modulesGraph.hide(); 18 | } 19 | }; -------------------------------------------------------------------------------- /src/_BIN/pages/warnings/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function() { 4 | document.title = "warnings"; 5 | $(".page").html(require("./warnings.jade")({ 6 | stats : app.stats, 7 | warnings: app.stats.warnings.map(function(str) { 8 | str = str.split("\n"); 9 | var header = str.shift(); 10 | var footer = str.pop(); 11 | if (!/^ @/.test(footer)) str.push(footer), footer = ""; 12 | return { 13 | header: header, 14 | text : str.join("\n"), 15 | footer: footer 16 | } 17 | }) 18 | })); 19 | }; -------------------------------------------------------------------------------- /src/static/api/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "userId": "1", 3 | "name": "Bart", 4 | "givenName": "Bartek", 5 | "familyName": "Kus", 6 | "nickname": "Bartekus", 7 | "picture": "/images/default-profile.png", 8 | "email": "bartekus@gmail.com", 9 | "emailVerified": true, 10 | "roles": [ 11 | "admin" 12 | ], 13 | "gender": "male", 14 | "age": 33, 15 | "notes": "The secret of joy in work is contained in one word\n\t\t\t– excellence.\nTo know how to do something well is to enjoy it.", 16 | "createdAt": "2016-01-01T00:00:00.000Z", 17 | "updatedAt": "2016-01-01T00:00:00.000Z", 18 | "locale": "en-CA" 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Spinner/Spinner.scss: -------------------------------------------------------------------------------- 1 | .spinner { 2 | -webkit-animation: spin 1000ms infinite linear; 3 | animation: spin 1000ms infinite linear; 4 | margin-right: 10px; 5 | } 6 | 7 | @-webkit-keyframes spin { 8 | 0% { 9 | -webkit-transform: rotate(0deg); 10 | transform: rotate(0deg); 11 | } 12 | 100% { 13 | -webkit-transform: rotate(359deg); 14 | transform: rotate(359deg); 15 | } 16 | } 17 | 18 | @keyframes spin { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(359deg); 25 | transform: rotate(359deg); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/_BIN/pages/assets/assets.jade: -------------------------------------------------------------------------------- 1 | table.table.table-condensed 2 | thead 3 | tr 4 | th assets 5 | th size 6 | th chunks 7 | th names 8 | th flags 9 | tbody 10 | each asset in stats.assets 11 | tr 12 | td: pre: code= asset.name 13 | td= require("../../formatSize")(asset.size) 14 | td 15 | each chunk in asset.chunks 16 | a.btn.btn-info(href="#chunk/#{chunk}")= chunk 17 | = " " 18 | td 19 | each name in asset.chunkNames 20 | code= name 21 | = " " 22 | td 23 | if module.emitted 24 | span.label.label-success emitted 25 | = " " -------------------------------------------------------------------------------- /src/components/Hero/Hero.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | height: 600px; 3 | position: relative; 4 | background: black; 5 | color: white; 6 | transition: height .3s; 7 | } 8 | 9 | .move-up { 10 | margin-top: -50px; 11 | } 12 | 13 | .content { 14 | height: 550px; 15 | top: 50px; 16 | position: relative; 17 | padding-bottom: 100px; 18 | padding-left: 25px; 19 | padding-right: 25px; 20 | } 21 | 22 | .background { 23 | position: absolute; 24 | width: 100%; 25 | height: 100%; 26 | background-size: cover; 27 | overflow: hidden; 28 | img { 29 | width: 100%; 30 | height: auto; 31 | } 32 | } 33 | 34 | .hero-small { 35 | height: 400px; 36 | 37 | .content { 38 | height: 350px; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/redux/root-reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { routerReducer as routing } from 'react-router-redux'; 3 | import { documentTitleReducer as documentTitle } from './modules/document-title/document-title'; 4 | import { spinnerReducer as spinner } from './modules/spinner/spinner'; 5 | import { languageReducer as language } from './modules/language/language'; 6 | import { reducer as form } from 'redux-form'; 7 | import user from './modules/user/user-reducer'; 8 | import auth from './modules/auth/auth-reducer'; 9 | 10 | const rootReducer = combineReducers({ 11 | auth, 12 | spinner, 13 | user, 14 | language, 15 | routing, 16 | documentTitle, 17 | form, 18 | }); 19 | 20 | export default rootReducer; 21 | -------------------------------------------------------------------------------- /src/components/Hero/Hero.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Hero.scss'; 3 | import classNames from 'classnames'; 4 | 5 | export default class Hero extends React.Component { 6 | static displayName = 'Hero'; 7 | static propTypes = { 8 | children: React.PropTypes.node, 9 | displayUnderNavbar: React.PropTypes.bool, 10 | small: React.PropTypes.bool, 11 | }; 12 | 13 | render() { 14 | const classes = classNames({ 15 | [styles.hero]: true, 16 | [styles['move-up']]: this.props.displayUnderNavbar, 17 | [styles['hero-small']]: this.props.small, 18 | }); 19 | 20 | return ( 21 |
22 | {this.props.children} 23 |
24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/HomePage/HomePage.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | title: { 3 | id: 'homePage.title', 4 | defaultMessage: 'Home Page', 5 | }, 6 | button: { 7 | clickMe: { 8 | id: 'homePage.button.clickMe', 9 | defaultMessage: 'Home Page', 10 | }, 11 | }, 12 | para: { 13 | pressCtrlH: { 14 | id: 'homePage.para.pressCtrlH', 15 | defaultMessage: 'Press Ctrl-H for Redux Dev Tools.', 16 | }, 17 | autoUpdate: { 18 | id: 'homePage.para.autoUpdate', 19 | defaultMessage: 'This page auto-updates.', 20 | }, 21 | es7Decorator: { 22 | id: 'homePage.para.es7Decorator', 23 | defaultMessage: 'It also demonstrates use of the @autobind ES7 decorator.', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "env": { 4 | "test": { 5 | "presets": ["es2015", "stage-0", "react"], 6 | "plugins": [ 7 | "transform-decorators-legacy", 8 | "transform-runtime", 9 | "add-module-exports" 10 | ] 11 | }, 12 | "development": { 13 | "presets": ["es2015", "stage-0", "react"], 14 | "plugins": [ 15 | "react-hot-loader/babel", 16 | "transform-decorators-legacy", 17 | "transform-runtime", 18 | "add-module-exports" 19 | ] 20 | }, 21 | "production": { 22 | "presets": ["es2015", "stage-0", "react"], 23 | "plugins": [ 24 | "transform-decorators-legacy", 25 | "transform-runtime", 26 | "add-module-exports" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/shared/forms.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | error: { 3 | email: { 4 | id: 'common.form.error.email', 5 | defaultMessage: 'email address invalid', 6 | }, 7 | max: { 8 | id: 'common.form.error.max', 9 | defaultMessage: 'maximum value: {max}', 10 | }, 11 | maxLength: { 12 | id: 'common.form.error.maxLength', 13 | defaultMessage: 'maximum length: {maxLength}', 14 | }, 15 | min: { 16 | id: 'common.form.error.min', 17 | defaultMessage: 'minimum value: {min}', 18 | }, 19 | minLength: { 20 | id: 'common.form.error.minLength', 21 | defaultMessage: 'minimum length: {minLength}', 22 | }, 23 | required: { 24 | id: 'common.form.error.required', 25 | defaultMessage: 'this field is required', 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/Spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Modal } from 'react-bootstrap'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import styles from './Spinner.scss'; 5 | 6 | const Spinner = ({ canShow, messageId }) => { 7 | const className = `glyphicon glyphicon-refresh ${ styles.spinner }`; 8 | 9 | return ( 10 | 11 | 12 | 13 | {canShow && 14 | 15 | } 16 | 17 | 18 | ); 19 | }; 20 | 21 | Spinner.displayName = 'Spinner'; 22 | Spinner.propTypes = { 23 | canShow: PropTypes.bool.isRequired, 24 | messageId: PropTypes.string, 25 | }; 26 | 27 | export default Spinner; 28 | -------------------------------------------------------------------------------- /src/_BIN/pages/modules/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | var modulesGraph = require("../../graphs/modules"); 3 | 4 | function renderTable() { 5 | $(".page").html(require("./modules.jade")({ 6 | stats: app.stats 7 | })); 8 | } 9 | 10 | module.exports = function() { 11 | document.title = "modules"; 12 | renderTable(); 13 | 14 | var sortDir; 15 | $(document).on('click', '.size-th', function() { 16 | sortDir = sortDir === 'desc' ? 'asc' : 'desc'; 17 | app.stats.modules.sort(function(a, b) { 18 | return sortDir === 'asc' 19 | ? b.size - a.size 20 | : a.size - b.size; 21 | }); 22 | renderTable(); 23 | }); 24 | 25 | modulesGraph.show(); 26 | modulesGraph.setNormal(); 27 | return function() { 28 | $(document).off('click', '.size-th'); 29 | modulesGraph.hide(); 30 | } 31 | }; -------------------------------------------------------------------------------- /src/containers/ProfileEditForm/ProfileEditForm.validations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | age: { 3 | required: true, 4 | min: 18, 5 | max: 99, 6 | }, 7 | email: { 8 | required: true, 9 | email: true, 10 | validateOnBlur: true, 11 | }, 12 | emailVerified: true, 13 | familyName: { 14 | required: true, 15 | validateOnBlur: true, 16 | minLength: 2, 17 | }, 18 | gender: { 19 | required: true, 20 | validateOnBlur: true, 21 | }, 22 | givenName: { 23 | required: true, 24 | validateOnBlur: true, 25 | minLength: 1, 26 | }, 27 | locale: { 28 | required: true, 29 | validateOnBlur: true, 30 | }, 31 | nickname: { 32 | required: false, 33 | validateOnBlur: true, 34 | maxLength: 10, 35 | }, 36 | notes: { 37 | required: false, 38 | validateOnBlur: true, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/redux/modules/language/language.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | export const CHANGE_LANGUAGE = '@@language/CHANGE_LANGUAGE'; 3 | export const LOCAL_STORAGE_KEY = 'redux:language'; 4 | export const DEFAULT_LANGUAGE = 'en'; 5 | 6 | type LanguageAction = { 7 | type: string; 8 | language: string; 9 | }; 10 | 11 | function getLanguage() { 12 | const language = localStorage.getItem(LOCAL_STORAGE_KEY); 13 | 14 | return language || DEFAULT_LANGUAGE; 15 | } 16 | 17 | export function changeLanguage(language: string): LanguageAction { 18 | localStorage.setItem(LOCAL_STORAGE_KEY, language); 19 | 20 | return { 21 | type: CHANGE_LANGUAGE, 22 | language, 23 | }; 24 | } 25 | 26 | export function languageReducer(state: string = getLanguage(), action: LanguageAction): string { 27 | return (action.type === CHANGE_LANGUAGE) ? action.language : state; 28 | } 29 | -------------------------------------------------------------------------------- /src/_BIN/pages/select/application.jade: -------------------------------------------------------------------------------- 1 | nav.navbar.navbar-default 2 | .container-fluid 3 | ul.nav.navbar-nav 4 | li: a(href="") Open 5 | li: a(href="#home") Home 6 | li: a(href="#modules") Modules 7 | li: a(href="#chunks") Chunks 8 | li: a(href="#assets") Assets 9 | li: a(href="#warnings") Warnings 10 | li: a(href="#errors") Errors 11 | li: a(href="#hints") Hints 12 | #sigma-modules(style="width: 99%; height: 500px; display:none;") 13 | #sigma-chunks(style="width: 99%; height: 500px; display:none;") 14 | .page 15 | 16 | .modal.fade: .modal-dialog: .modal-content 17 | .modal-header 18 | h4.modal-title Select webpack compile 19 | .modal-body 20 | each val, index in children 21 | button.js-select.btn.btn-primary.btn-block(data-index=index) #{val.name || ("Compile #" + (index+1))} 22 | .modal-footer 23 | -------------------------------------------------------------------------------- /src/components/FormFields/DropDown.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Element } from 'react'; 3 | import { FormControl, ControlLabel, FormGroup } from 'react-bootstrap'; 4 | 5 | class DropDown extends React.Component { 6 | static displayName = 'DropDown'; 7 | static propTypes = { 8 | label: PropTypes.string.isRequired, 9 | field: PropTypes.object.isRequired, 10 | values: PropTypes.array.isRequired, 11 | }; 12 | 13 | render(): Element { 14 | const { field, label, values } = this.props; 15 | 16 | return ( 17 | 18 | {label} 19 | 20 | {values.map(value => )} 21 | 22 | 23 | ); 24 | } 25 | } 26 | 27 | export default DropDown; 28 | -------------------------------------------------------------------------------- /src/_BIN/pages/upload/application.jade: -------------------------------------------------------------------------------- 1 | nav.navbar.navbar-default 2 | .container-fluid 3 | ul.nav.navbar-nav 4 | li: a(href="") Open 5 | li: a(href="#home") Home 6 | li: a(href="#modules") Modules 7 | li: a(href="#chunks") Chunks 8 | li: a(href="#assets") Assets 9 | li: a(href="#warnings") Warnings 10 | li: a(href="#errors") Errors 11 | li: a(href="#hints") Hints 12 | #sigma-modules(style="width: 99%; height: 500px; display:none;") 13 | #sigma-chunks(style="width: 99%; height: 500px; display:none;") 14 | .page 15 | 16 | .modal.fade: .modal-dialog: .modal-content 17 | .modal-header 18 | h4.modal-title Upload webpack stats 19 | .modal-body 20 | .form-group 21 | label(for="file") JSON file 22 | input.form-control(type="file", id="file") 23 | .form-group 24 | label(for="example") Example 25 | div: button(type="btn btn-default", id="example") Use example 26 | .modal-footer -------------------------------------------------------------------------------- /src/_BIN/googleAnalytics.js: -------------------------------------------------------------------------------- 1 | if(typeof GA_TRACKING_CODE !== "undefined") { 2 | (function(window, document, script, url, r, tag, firstScriptTag) { 3 | window['GoogleAnalyticsObject']=r; 4 | window[r] = window[r] || function() { 5 | (window[r].q = window[r].q || []).push(arguments) 6 | }; 7 | window[r].l = 1*new Date(); 8 | tag = document.createElement(script), 9 | firstScriptTag = document.getElementsByTagName(script)[0]; 10 | tag.async = 1; 11 | tag.src = url; 12 | firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 13 | })( 14 | window, 15 | document, 16 | 'script', 17 | '//www.google-analytics.com/analytics.js', 18 | 'ga' 19 | ); 20 | 21 | var ga = window.ga; 22 | 23 | ga('create', GA_TRACKING_CODE, GA_TRACKING_CONFIG); 24 | // ga('send', 'pageview'); 25 | 26 | module.exports = function() { 27 | return window.ga.apply(window.ga, arguments); 28 | }; 29 | } else { 30 | module.exports = function() {console.log(arguments)} 31 | } 32 | -------------------------------------------------------------------------------- /src/components/DropdownProfileCard/DropdownProfileCard.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { LinkContainer } from 'react-router-bootstrap'; 3 | import { NavItem } from 'react-bootstrap'; 4 | import styles from './DropdownProfileCard.scss'; 5 | 6 | const DropdownProfileCard = ({ picture, name, nickname }) => 7 | ( 8 | 9 | 10 | 11 | {name} 12 | {nickname} 13 | 14 | 15 | ); 16 | 17 | DropdownProfileCard.displayName = 'DropdownProfileCard'; 18 | DropdownProfileCard.propTypes = { 19 | picture: PropTypes.string.isRequired, 20 | name: PropTypes.string.isRequired, 21 | nickname: PropTypes.string, 22 | }; 23 | 24 | export default DropdownProfileCard; 25 | -------------------------------------------------------------------------------- /src/containers/HeroPageLayout.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import MainHeader from '../containers/MainHeader/MainHeader'; 4 | import MainFooter from '../components/MainFooter/MainFooter'; 5 | import Spinner from '../components/Spinner/Spinner'; 6 | 7 | const mapStateToProps = ({ spinner }) => ({ spinner }); 8 | const HeroPageLayout = (props) => 9 | ( 10 |
11 | 12 |
13 | {props.children} 14 |
15 | 16 | 20 |
21 | ); 22 | 23 | HeroPageLayout.displayName = 'HeroPageLayout'; 24 | HeroPageLayout.propTypes = { 25 | spinner : PropTypes.object, 26 | children: PropTypes.element, 27 | }; 28 | 29 | export default connect(mapStateToProps)(HeroPageLayout); 30 | -------------------------------------------------------------------------------- /src/_BIN/pages/chunks/chunks.jade: -------------------------------------------------------------------------------- 1 | table.table.table-condensed 2 | thead 3 | tr 4 | th id 5 | th names 6 | th modules 7 | th size 8 | th parents 9 | th flags 10 | tbody 11 | each chunk in stats.chunks 12 | tr 13 | td: a.btn.btn-info(href="#chunk/#{chunk.id}")= chunk.id 14 | td 15 | each name in chunk.names 16 | code= name 17 | = " " 18 | td= chunk.modules ? chunk.modules.length : "N/A" 19 | td= require("../../formatSize")(chunk.size) 20 | td 21 | each parent in chunk.parents 22 | a.btn.btn-info(href="#chunk/#{parent}")= parent 23 | = " " 24 | td 25 | if chunk.rendered 26 | span.label.label-success rendered 27 | = " " 28 | if chunk.initial 29 | span.label.label-info initial 30 | = " " 31 | if chunk.entry 32 | span.label.label-danger entry 33 | -------------------------------------------------------------------------------- /src/redux/modules/auth/auth-reducer.js: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { 3 | getState, 4 | LOGIN_SUCCESS, 5 | LOGIN_FAILURE, 6 | LOGOUT_REQUEST, 7 | } from './auth-actions'; 8 | 9 | if (__DEBUG__) { 10 | debug.enable('auth-reducer:*'); 11 | } 12 | 13 | const log = debug('auth-reducer:debug'); 14 | 15 | const authReducer = (state = getState(), action) => { 16 | let newState; 17 | 18 | switch (action.type) { 19 | case LOGIN_SUCCESS: 20 | newState = Object.assign({}, action.state); 21 | break; 22 | case LOGIN_FAILURE: 23 | newState = Object.assign({}, action.state); 24 | break; 25 | case LOGOUT_REQUEST: 26 | newState = Object.assign({}, action.state); 27 | break; 28 | default: 29 | newState = state; 30 | } 31 | 32 | if (newState !== state) { 33 | // only log if state has changed 34 | log('action:', action, 'state:', state, 'newState:', newState); 35 | } 36 | 37 | return newState; 38 | }; 39 | 40 | export default authReducer; 41 | -------------------------------------------------------------------------------- /src/components/MainFooter/MainFooter.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/base.scss"; 2 | 3 | .footer { 4 | background-color: $color-footer; 5 | width: 100%; 6 | 7 | ul { 8 | padding: 0; 9 | margin: 0; 10 | text-align: center; 11 | } 12 | } 13 | 14 | .item { 15 | display: inline-block; 16 | margin: 1rem; 17 | 18 | a { 19 | font-size: 1.2rem; 20 | font-weight: bold; 21 | color: #BCBCBC; 22 | text-transform: uppercase; 23 | } 24 | } 25 | .social { 26 | padding: 25px; 27 | a { 28 | text-transform: uppercase; 29 | font-weight: bold; 30 | padding: 10px 30px 10px 55px; 31 | opacity: .3; 32 | text-decoration: none; 33 | color: #373737; 34 | transition: opacity .3s; 35 | 36 | &:hover { 37 | opacity: 1; 38 | } 39 | } 40 | } 41 | 42 | .twitter { 43 | background-image: url('/images/twitter_logo.png'); 44 | text-align: right; 45 | } 46 | 47 | .facebook { 48 | background-image: url('/images/facebook_logo.png'); 49 | text-align: left; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/UserDropdownMenu/UserDropdownMenu.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { FormattedMessage } from 'react-intl'; 3 | import { NavDropdown, MenuItem, NavItem } from 'react-bootstrap'; 4 | import DropdownProfileCard from '../../components/DropdownProfileCard/DropdownProfileCard'; 5 | import { links } from '../../shared/links'; 6 | 7 | const UserDropdownMenu = (props) => { 8 | const { name, picture, nickname } = props.user; 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | UserDropdownMenu.displayName = 'UserDropdownMenu'; 22 | UserDropdownMenu.propTypes = { 23 | user: PropTypes.object.isRequired, 24 | logout: PropTypes.func.isRequired, 25 | }; 26 | 27 | export default UserDropdownMenu; 28 | -------------------------------------------------------------------------------- /src/containers/AdminPageLayout.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import MainHeader from '../containers/MainHeader/MainHeader'; 4 | import MainFooter from '../components/MainFooter/MainFooter'; 5 | import Spinner from '../components/Spinner/Spinner'; 6 | import '../styles/admin.scss'; 7 | 8 | const mapStateToProps = ({ spinner }) => ({ spinner }); 9 | const AdminPageLayout = (props) => 10 | ( 11 |
12 | 13 |
14 | {props.children} 15 |
16 | 17 | 21 |
22 | ); 23 | 24 | AdminPageLayout.displayName = 'AdminPageLayout'; 25 | AdminPageLayout.propTypes = { 26 | spinner: PropTypes.object, 27 | children: PropTypes.element, 28 | }; 29 | 30 | export default connect(mapStateToProps)(AdminPageLayout); 31 | -------------------------------------------------------------------------------- /src/_BIN/pages/upload/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | 3 | module.exports = function() { 4 | var nextPage = Array.prototype.slice.call(arguments); 5 | document.title = "upload"; 6 | $("body").html(require("./application.jade")()); 7 | $(".modal").modal({ show: true }); 8 | $("#file").change(loadFromFile); 9 | $("#example").click(loadFromExample); 10 | 11 | function loadFromExample() { 12 | require(["./example.json"], function(example) { 13 | app.load(example); 14 | $(".modal").modal("hide"); 15 | app.loadPage.apply(app, nextPage); 16 | }); 17 | } 18 | 19 | function loadFromFile() { 20 | var files = $("#file")[0].files; 21 | var fileReader = new FileReader(); 22 | fileReader.readAsText(files[0]); 23 | fileReader.onload = function() { 24 | var data = fileReader.result; 25 | app.load(JSON.parse(data)); 26 | $(".modal").modal("hide"); 27 | app.loadPage.apply(app, nextPage); 28 | }; 29 | fileReader.onerror = function(err) { 30 | alert(err); 31 | }; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/FormFields/TextInput.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | /* @flow */ 3 | import React, { PropTypes, Element } from 'react'; 4 | import classNames from 'classnames'; 5 | 6 | class TextInput extends React.Component { 7 | static displayName = 'TextInput'; 8 | static propTypes = { 9 | field: PropTypes.object.isRequired, 10 | children: PropTypes.object, 11 | placeholder: PropTypes.string, 12 | type: PropTypes.string, 13 | }; 14 | 15 | render(): Element { 16 | const inputClasses = classNames({ 17 | 'form-group': true, 18 | 'has-error': this.props.field.invalid, 19 | }); 20 | const type = this.props.type || 'text'; 21 | const { field, placeholder, children } = this.props; 22 | 23 | return ( 24 |
25 | 31 | { children } 32 |
33 | ); 34 | } 35 | } 36 | 37 | export default TextInput; 38 | -------------------------------------------------------------------------------- /src/containers/Root.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0 */ 2 | import React, { PropTypes } from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { Router } from 'react-router'; 5 | 6 | export default class Root extends React.Component { 7 | static displayName = 'Root'; 8 | static propTypes = { 9 | history: PropTypes.object.isRequired, 10 | store: PropTypes.object.isRequired, 11 | routes: PropTypes.element.isRequired, 12 | }; 13 | 14 | // redux devtools pane 15 | get devTools() { 16 | let returnValue = ; 17 | 18 | if (DEVELOPMENT) { 19 | const DevTools = require('./DevTools'); 20 | 21 | returnValue = ; 22 | } 23 | 24 | return returnValue; 25 | } 26 | 27 | render() { 28 | const { history, store, routes } = this.props; 29 | 30 | return ( 31 | 32 |
33 | 34 | {this.devTools} 35 |
36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/containers/AppContainer.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { IntlProvider } from 'react-intl'; 4 | import DocumentTitle from '../components/DocumentTitle'; 5 | import messages from '../translations'; 6 | import '../styles/app.scss'; 7 | 8 | const mapStateToProps = ({ language, documentTitle }) => ({ language, documentTitle }); 9 | class AppContainer extends Component { 10 | static displayName = 'AppContainer'; 11 | static propTypes = { 12 | language: PropTypes.string.isRequired, 13 | documentTitle: PropTypes.object.isRequired, 14 | children: PropTypes.element.isRequired, 15 | }; 16 | 17 | render() { 18 | const { language, children } = this.props; 19 | 20 | return ( 21 | 22 | 23 | {children} 24 | 25 | 26 | ); 27 | } 28 | } 29 | 30 | export default connect(mapStateToProps)(AppContainer); 31 | -------------------------------------------------------------------------------- /src/utils/percentageToColor.js: -------------------------------------------------------------------------------- 1 | function hsv2rgb(h, s, v) { 2 | h = (h % 1 + 1) % 1; // wrap hue 3 | 4 | const i = Math.floor(h * 6); 5 | const f = h * 6 - i; 6 | const p = v * (1 - s); 7 | const q = v * (1 - s * f); 8 | const t = v * (1 - s * (1 - f)); 9 | 10 | switch (i) { 11 | case 0: 12 | return [v, t, p]; 13 | case 1: 14 | return [q, v, p]; 15 | case 2: 16 | return [p, v, t]; 17 | case 3: 18 | return [p, q, v]; 19 | case 4: 20 | return [t, p, v]; 21 | case 5: 22 | return [v, p, q]; 23 | } 24 | return [0, 0, 0]; 25 | } 26 | 27 | function toString(rgb) { 28 | return `rgb(${ rgb.map(x => Math.floor(256 * x)).join(',') })`; 29 | } 30 | 31 | exports.colorSpace = function colorSpace(p) { 32 | const rgb = hsv2rgb(p, 1, 0.7); 33 | return toString(rgb); 34 | }; 35 | 36 | exports.greenRed = function greenRed(p) { 37 | const rgb = hsv2rgb((1 - p) / 3, 1, 0.7); 38 | return toString(rgb); 39 | }; 40 | 41 | exports.blue = function blue(p) { 42 | const rgb = hsv2rgb(p / 3 + 0.5, 1, 0.7); 43 | return toString(rgb); 44 | }; 45 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import axios from 'axios'; 4 | import type { User } from 'declarations/app'; 5 | 6 | export const getProfile = (): Promise => 7 | axios.get('/api/profile.json').then(response => response.data); 8 | 9 | 10 | export const updateProfile = (user: User): Promise => 11 | // NOTE: this is where the actual `PUT` http request would be called: 12 | // it is mocked here in order to keep things simple (ie, no actual service needs to be built) 13 | // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise 14 | new Promise((resolve) => { 15 | // updated user needs to have given and family names concatenated to produce the `name` property 16 | const name = `${ user.givenName } ${ user.familyName }`; 17 | const updatedUser = Object.assign({}, user, { name }); 18 | 19 | // insert a short delay to simulate service call delay 20 | setTimeout(() => resolve(updatedUser), 700); 21 | }); 22 | // So to simulate an http request that fails: 23 | // return new Promise((resolve, reject) => reject()); 24 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | r3analyse 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/components/DocumentTitle.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { injectIntl } from 'react-intl'; 4 | 5 | type DocumentTitleProps = { 6 | title: Object; 7 | children: any; 8 | intl: { 9 | formatMessage: Function; 10 | }; 11 | }; 12 | 13 | class DocumentTitle extends Component { 14 | static displayName = 'DocumentTitle'; 15 | static propTypes = { 16 | title: PropTypes.object.isRequired, 17 | children: PropTypes.element.isRequired, 18 | }; 19 | 20 | componentWillMount(): void { 21 | this.updateDocumentTitle(this.props); 22 | } 23 | 24 | componentWillUpdate(newProps: DocumentTitleProps): void { 25 | this.updateDocumentTitle(newProps); 26 | } 27 | 28 | props: DocumentTitleProps; 29 | 30 | updateDocumentTitle(props: DocumentTitleProps): void { 31 | document.title = props.intl.formatMessage(props.title); 32 | } 33 | 34 | render(): React.Element { 35 | return ( 36 |
37 | {this.props.children} 38 |
39 | ); 40 | } 41 | } 42 | 43 | export default injectIntl(DocumentTitle); 44 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*babylon 3 | .*babel 4 | .*npmconf 5 | .*fbjs 6 | .*/node_modules/.*/test/ 7 | 8 | [include] 9 | 10 | [libs] 11 | interfaces/ 12 | 13 | [options] 14 | module.name_mapper='^api/\(.*\)' -> '/src/api/\1' 15 | module.name_mapper='^components/\(.*\)' -> '/src/components/\1' 16 | module.name_mapper='^containers/\(.*\)' -> '/src/containers/\1' 17 | module.name_mapper='^declarations/\(.*\)' -> '/src/declarations/\1' 18 | module.name_mapper='^layouts/\(.*\)' -> '/src/layouts/\1' 19 | module.name_mapper='^pages/\(.*\)' -> '/src/pages/\1' 20 | module.name_mapper='^redux/\(.*\)' -> '/src/redux/\1' 21 | module.name_mapper='^routes/\(.*\)' -> '/src/routes/\1' 22 | module.name_mapper='^shared/\(.*\)' -> '/src/shared/\1' 23 | module.name_mapper='^styles/\(.*\)' -> '/src/styles/\1' 24 | module.name_mapper='^app-config$' -> '/src/app-config' 25 | 26 | module.name_mapper='.*\(.scss\)' -> 'empty/object' 27 | 28 | esproposal.class_static_fields=enable 29 | esproposal.decorators=ignore 30 | -------------------------------------------------------------------------------- /src/redux/modules/user/user-reducer.js: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { 3 | getUser, 4 | SET_USER, 5 | UPDATE_USER_SUCCESS, 6 | UPDATE_USER_FAILURE, 7 | CLEAR_USER, 8 | } from './user-actions'; 9 | 10 | if (__DEBUG__) { 11 | debug.enable('user-reducer:*'); 12 | } 13 | 14 | const log = debug('user-reducer:debug'); 15 | 16 | const userReducer = (state = getUser(), action) => { 17 | let newState; 18 | 19 | switch (action.type) { 20 | case SET_USER: 21 | newState = Object.assign({}, action.user); 22 | break; 23 | case CLEAR_USER: 24 | newState = null; 25 | break; 26 | case UPDATE_USER_SUCCESS: 27 | newState = Object.assign({}, action.user); 28 | break; 29 | case UPDATE_USER_FAILURE: 30 | // NOTE: this is not essential, but it's useful to explicitly define 31 | newState = state; 32 | break; 33 | default: 34 | newState = state; 35 | } 36 | 37 | if (newState !== state) { 38 | // only log if state has changed 39 | log('action:', action, 'state:', state, 'newState:', newState); 40 | } 41 | 42 | return newState; 43 | }; 44 | 45 | export default userReducer; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/components/DropdownProfileCard/DropdownProfileCard.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestUtils from 'react-addons-test-utils'; 3 | import DropdownProfileCard from './DropdownProfileCard'; 4 | import { LinkContainer } from 'react-router-bootstrap'; 5 | 6 | function shallowRender(component) { 7 | const renderer = TestUtils.createRenderer(); 8 | 9 | renderer.render(component); 10 | return renderer.getRenderOutput(); 11 | } 12 | 13 | function shallowRenderWithProps(props = {}) { 14 | return shallowRender(); 15 | } 16 | 17 | describe('[Page] Landing Page', function landingPageSpec() { 18 | beforeEach(() => { 19 | this.props = { 20 | picture: 'picture', 21 | name: 'A User', 22 | nickname: 'a.user', 23 | }; 24 | this.component = shallowRenderWithProps(this.props); 25 | }); 26 | 27 | it('Should render as a ', () => { 28 | expect(this.component.type).to.equal(LinkContainer); 29 | }); 30 | 31 | // TODO: Update this! 32 | // it('Should render 3 children', () => { 33 | // expect(this.component.props.children.length).to.equal(3); 34 | // }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/pages/HomePage/HomePageHero.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import site from './../../settings'; 3 | import { Hero, HeroContent, HeroBackground } from '../../components/Hero/index'; 4 | import { VAContainer, VAMiddle } from '../../components/VAlign/VAlign'; 5 | import styles from './HomePage.scss'; 6 | 7 | const HomePageHero = (props) => 8 | ( 9 | 10 | 11 | 12 | 13 | 14 |
15 |

16 | {site.name} 17 |

18 | 19 |

20 | {site.description} 21 |

22 |
23 |
24 |
25 |
26 |
27 | ); 28 | 29 | HomePageHero.displayName = 'HomePageHero'; 30 | 31 | HomePageHero.propTypes = { 32 | backgroundImage: PropTypes.string.isRequired, 33 | }; 34 | 35 | export default HomePageHero; 36 | -------------------------------------------------------------------------------- /src/components/FormFields/HorizontalRadioGroup.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Element } from 'react'; 3 | 4 | // Support for radio buttons in react-bootstrap is currently lame. 5 | // It's worth watching this though: 6 | // https://github.com/react-bootstrap/react-bootstrap/pull/962 7 | class HorizontalRadioGroup extends React.Component { 8 | static displayName = 'HorizontalRadioGroup'; 9 | static propTypes = { 10 | field: PropTypes.object.isRequired, 11 | values: PropTypes.array.isRequired, 12 | }; 13 | 14 | render(): Element { 15 | const { field, values } = this.props; 16 | 17 | return ( 18 |
19 | { 20 | values.map(value => 21 | 31 | ) 32 | } 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default HorizontalRadioGroup; 39 | -------------------------------------------------------------------------------- /src/components/MainFooter/MainFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { links } from '../../shared/links'; 5 | import styles from './MainFooter.scss'; 6 | 7 | const footerLinks = [ 8 | links.aboutUs, 9 | links.assets, 10 | links.chunk, 11 | links.chunks, 12 | links.error, 13 | links.errors, 14 | links.hints, 15 | links.module, 16 | links.modules, 17 | links.select, 18 | links.upload, 19 | links.warnings, 20 | ]; 21 | export default class MainFooter extends React.Component { 22 | static displayName = 'MainFooter'; 23 | static propTypes = { 24 | children: React.PropTypes.node, 25 | }; 26 | 27 | render() { 28 | return ( 29 |
30 | 41 |
42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/redux/modules/document-title/document-title.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import type { FormattedMessageType } from '../../../declarations/i18n-types'; 3 | import site from '../../../settings'; 4 | 5 | const UPDATE_DOCUMENT_TITLE = '@@document-title/UPDATE'; 6 | const RESET_DOCUMENT_TITLE = '@@document-title/RESET'; 7 | 8 | export type DocumentTitleAction = { 9 | type: string; 10 | documentTitle: FormattedMessageType; 11 | }; 12 | 13 | const initialState: FormattedMessageType = { 14 | id: 'site.name', 15 | defaultMessage: site.name, 16 | }; 17 | 18 | // Action Creators 19 | 20 | export function updateDocumentTitle(documentTitle: FormattedMessageType): DocumentTitleAction { 21 | return { 22 | type: UPDATE_DOCUMENT_TITLE, 23 | documentTitle, 24 | }; 25 | } 26 | 27 | export function resetDocumentTitle(): DocumentTitleAction { 28 | return { 29 | type: RESET_DOCUMENT_TITLE, 30 | documentTitle: initialState, 31 | }; 32 | } 33 | 34 | // Reducer 35 | export function documentTitleReducer( 36 | state: FormattedMessageType = initialState, 37 | action: DocumentTitleAction): FormattedMessageType { 38 | switch (action.type) { 39 | case RESET_DOCUMENT_TITLE: 40 | case UPDATE_DOCUMENT_TITLE: 41 | return action.documentTitle; 42 | default: 43 | return state; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/containers/Sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import { Link } from 'react-router'; 3 | import { connect } from 'react-redux'; 4 | 5 | class Sidebar extends Component { 6 | static displayName = 'Sidebar'; 7 | static propTypes = { 8 | dispatch: PropTypes.func, 9 | user: PropTypes.object, 10 | isAdmin: PropTypes.bool, 11 | }; 12 | 13 | render() { 14 | return ( 15 |
    16 |
  • 17 | 18 | Profile 19 | 20 |
  • 21 |
  • 22 | 23 | Profile 24 | 25 |
  • 26 |
  • 27 | 28 | Profile 29 | 30 |
  • 31 |
  • 32 | 33 | Favourites 34 | 35 |
  • 36 |
37 | ); 38 | } 39 | } 40 | 41 | const mapStateToProps = (state) => ({ 42 | isAuthenticated: state.auth.isAuthenticated, 43 | isAdmin: state.auth.isAdmin, 44 | user: state.user, 45 | }); 46 | 47 | export default connect(mapStateToProps)(Sidebar); 48 | -------------------------------------------------------------------------------- /src/components/VAlign/VAlign.js: -------------------------------------------------------------------------------- 1 | /* eslint react/no-multi-comp: 0 */ 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | import styles from './VAlign.scss'; 5 | 6 | export const VAMiddle = ({ children = '' }) =>
{children}
; 7 | export const VATop = ({ children = '' }) =>
{children}
; 8 | export const VABottom = ({ children = '' }) =>
{children}
; 9 | 10 | export const VAContainer = ({ horizontal, vertical, children = '' }) => { 11 | const classes = classNames({ 12 | [styles.container]: true, 13 | [styles.vertical]: vertical, 14 | [styles.horizontal]: horizontal, 15 | }); 16 | 17 | return
{children}
; 18 | }; 19 | 20 | VAContainer.displayName = 'VAContainer'; 21 | VAContainer.propTypes = { 22 | children: React.PropTypes.node, 23 | horizontal: React.PropTypes.bool, 24 | vertical: React.PropTypes.bool, 25 | }; 26 | 27 | VAMiddle.displayName = 'VAMiddle'; 28 | VAMiddle.propTypes = { 29 | children: React.PropTypes.node, 30 | }; 31 | 32 | VATop.displayName = 'VATop'; 33 | VATop.propTypes = { 34 | children: React.PropTypes.node, 35 | }; 36 | 37 | VABottom.displayName = 'VABottom'; 38 | VABottom.propTypes = { 39 | children: React.PropTypes.node, 40 | }; 41 | -------------------------------------------------------------------------------- /src/_BIN/pages/modules/modules.jade: -------------------------------------------------------------------------------- 1 | table.table.table-condensed 2 | thead 3 | tr 4 | th id 5 | th name 6 | th.sortable-th.size-th size 7 | th chunks 8 | th flags 9 | tbody 10 | each module in stats.modules 11 | tr 12 | td 13 | if typeof module.uid === "number" 14 | a.btn.btn-success(href="#module/#{module.uid}")= module.id 15 | else 16 | span.btn.btn-success= module.id 17 | td: pre: code= module.name.split("!").join("\n") 18 | td= require("../../formatSize")(module.size) 19 | td 20 | each chunk in module.chunks 21 | a.btn.btn-info(href="#chunk/#{chunk}")= chunk 22 | = " " 23 | td 24 | if module.built 25 | span.label.label-success built 26 | = " " 27 | if !module.cacheable 28 | span.label.label-warning not cacheable 29 | = " " 30 | if module.prefetched 31 | span.label.label-success prefetched 32 | = " " 33 | if module.failed 34 | span.label.label-danger failed 35 | = " " 36 | if module.warnings 37 | span.label.label-warning= module.warnings + " warnings" 38 | = " " 39 | if module.errors 40 | span.label.label-danger= module.errors + " errors" 41 | -------------------------------------------------------------------------------- /src/redux/configure-store.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0 */ 2 | import { createStore, applyMiddleware, compose } from 'redux'; 3 | import { persistState } from 'redux-devtools'; 4 | import { routerMiddleware } from 'react-router-redux'; 5 | import thunk from 'redux-thunk'; 6 | import rootReducer from './root-reducer'; 7 | import DevTools from '../containers/DevTools'; 8 | // import createLogger from 'redux-logger'; 9 | 10 | export default function configureStore(initialState, history) { 11 | // Installs hooks that always keep react-router and redux store in sync 12 | const middleware = [thunk, routerMiddleware(history)]; 13 | let store; 14 | 15 | if (__DEBUG__) { 16 | // middleware.push(createLogger()); 17 | store = createStore(rootReducer, initialState, compose( 18 | applyMiddleware(...middleware), 19 | DevTools.instrument(), 20 | persistState( 21 | window.location.href.match( 22 | /[?&]debug_session=([^&#]+)\b/ 23 | ) 24 | ) 25 | )); 26 | } else { 27 | store = createStore(rootReducer, initialState, compose(applyMiddleware(...middleware), f => f)); 28 | } 29 | 30 | if (module.hot) { 31 | // Enable Webpack hot module replacement for reducers 32 | module.hot.accept('./root-reducer', () => { 33 | const nextRootReducer = require('./root-reducer').default; 34 | store.replaceReducer(nextRootReducer); 35 | }); 36 | } 37 | 38 | return store; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/FormFields/DropDown.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestUtils from 'react-addons-test-utils'; 3 | import DropDown from './DropDown'; 4 | import { FormControl, ControlLabel, FormGroup } from 'react-bootstrap'; 5 | 6 | function shallowRender(component) { 7 | const renderer = TestUtils.createRenderer(); 8 | 9 | renderer.render(component); 10 | 11 | return renderer.getRenderOutput(); 12 | } 13 | 14 | function shallowRenderWithProps(props = {}) { 15 | return shallowRender(); 16 | } 17 | 18 | describe('[FormField] DropDown', function dropDownSpec() { 19 | beforeEach(() => { 20 | this.props = { 21 | label: 'a label', 22 | field: {}, 23 | values: ['one', 'two', 'three', 'four'], 24 | }; 25 | this.component = shallowRenderWithProps(this.props); 26 | }); 27 | 28 | it('Should render as ', () => { 29 | expect(this.component.type).to.equal(FormGroup); 30 | }); 31 | 32 | // TODO: Update this! 33 | // it('Should render correct number of children', () => { 34 | // expect(this.component.props.children.length).to.equal(this.props.values.length); 35 | // }); 36 | // 37 | // it('Should render correct `value` for 2nd child', () => { 38 | // expect(this.component.props.children[1].props.value).to.equal(this.props.values[1]); 39 | // }); 40 | // 41 | // it('Should render correct `children` for 3rd child', () => { 42 | // expect(this.component.props.children[2].props.children).to.equal(this.props.values[2]); 43 | // }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/pages/AboutPage/AboutPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './AboutPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class AboutPage extends Component { 14 | static displayName = 'AboutPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(AboutPage); 51 | -------------------------------------------------------------------------------- /src/pages/ChunkPage/ChunkPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ChunkPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ChunkPage extends Component { 14 | static displayName = 'ChunkPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ChunkPage); 51 | -------------------------------------------------------------------------------- /src/pages/ErrorPage/ErrorPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ErrorPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ErrorPage extends Component { 14 | static displayName = 'ErrorPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ErrorPage); 51 | -------------------------------------------------------------------------------- /src/pages/HintsPage/HintsPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './HintsPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class HintsPage extends Component { 14 | static displayName = 'HintsPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(HintsPage); 51 | -------------------------------------------------------------------------------- /src/pages/AssetsPage/AssetsPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './AssetsPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class AssetsPage extends Component { 14 | static displayName = 'AssetsPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(AssetsPage); 51 | -------------------------------------------------------------------------------- /src/pages/ChunksPage/ChunksPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ChunksPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ChunksPage extends Component { 14 | static displayName = 'ChunksPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ChunksPage); 51 | -------------------------------------------------------------------------------- /src/pages/ErrorsPage/ErrorsPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ErrorsPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ErrorsPage extends Component { 14 | static displayName = 'ErrorsPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ErrorsPage); 51 | -------------------------------------------------------------------------------- /src/pages/ModulePage/ModulePage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ModulePage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ModulePage extends Component { 14 | static displayName = 'ModulePage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ModulePage); 51 | -------------------------------------------------------------------------------- /src/pages/SelectPage/SelectPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './SelectPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class SelectPage extends Component { 14 | static displayName = 'SelectPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(SelectPage); 51 | -------------------------------------------------------------------------------- /src/pages/UploadPage/UploadPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './UploadPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class UploadPage extends Component { 14 | static displayName = 'UploadPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(UploadPage); 51 | -------------------------------------------------------------------------------- /src/pages/ModulesPage/ModulesPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './ModulesPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class ModulesPage extends Component { 14 | static displayName = 'ModulesPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(ModulesPage); 51 | -------------------------------------------------------------------------------- /src/pages/WarningsPage/WarningsPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { connect } from 'react-redux'; 5 | import { Grid, Row, Col } from 'react-bootstrap'; 6 | import { messages } from './WarningsPage.i18n'; 7 | import { HeroBackground, Hero } from '../../components/Hero'; 8 | import { 9 | updateDocumentTitle, 10 | resetDocumentTitle, 11 | } from '../../redux/modules/document-title/document-title'; 12 | 13 | class WarningsPage extends Component { 14 | static displayName = 'WarningsPage'; 15 | static propTypes = { 16 | dispatch: PropTypes.func.isRequired, 17 | }; 18 | 19 | componentDidMount() { 20 | this.props.dispatch(updateDocumentTitle(messages.title)); 21 | } 22 | 23 | componentWillUnmount() { 24 | this.props.dispatch(resetDocumentTitle()); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 |

40 | 41 |

42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default connect(() => ({}))(WarningsPage); 51 | -------------------------------------------------------------------------------- /src/redux/modules/spinner/spinner.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import debug from 'debug'; 3 | 4 | type SpinnerState = { 5 | canShow: boolean; 6 | messageId: ?string; 7 | }; 8 | 9 | type SpinnerAction = { 10 | type: string; 11 | state: SpinnerState; 12 | }; 13 | 14 | export const SHOW_SPINNER: string = '@@spinner/SHOW_SPINNER'; 15 | export const HIDE_SPINNER: string = '@@spinner/HIDE_SPINNER'; 16 | 17 | export const DEFAULT_SPINNER_STATE: SpinnerState = { 18 | canShow: false, 19 | messageId: null, 20 | }; 21 | 22 | if (__DEBUG__) { 23 | debug.enable('spinner-reducer:*'); 24 | } 25 | 26 | const log = debug('spinner-reducer:debug'); 27 | 28 | export function showSpinner(messageId: string): SpinnerAction { 29 | return { 30 | type: SHOW_SPINNER, 31 | state: { 32 | canShow: true, 33 | messageId, 34 | }, 35 | }; 36 | } 37 | 38 | export function hideSpinner(): SpinnerAction { 39 | return { 40 | type: HIDE_SPINNER, 41 | state: DEFAULT_SPINNER_STATE, 42 | }; 43 | } 44 | 45 | export function spinnerReducer( 46 | state: SpinnerState = DEFAULT_SPINNER_STATE, 47 | action: any): SpinnerState { 48 | let newState: SpinnerState = state; 49 | 50 | switch (action.type) { 51 | case SHOW_SPINNER: 52 | case HIDE_SPINNER: 53 | newState = Object.assign({}, state, action.state); 54 | break; 55 | default: 56 | newState = state; 57 | } 58 | 59 | if (newState !== state) { 60 | // only log if state has changed 61 | log('action:', action, 'state:', state, 'newState:', newState); 62 | } 63 | 64 | return newState; 65 | } 66 | -------------------------------------------------------------------------------- /src/_BIN/entry.js: -------------------------------------------------------------------------------- 1 | var ga = require("./googleAnalytics"); 2 | require("./style.css"); 3 | 4 | var app = require("./app"); 5 | 6 | var lastHash = ""; 7 | window.addEventListener("hashchange", function() { 8 | if (location.hash !== lastHash) { 9 | lastHash = location.hash; 10 | loadPageFromHash(); 11 | } 12 | }, false); 13 | loadPageFromHash(); 14 | 15 | function loadPageFromHash() { 16 | loadPage.apply(null, location.hash.replace(/^#/, "").split("/")); 17 | } 18 | 19 | var lastPage; 20 | 21 | function loadPage(name) { 22 | if (!name) name = "home"; 23 | var pageBundle; 24 | var args = Array.prototype.slice.call(arguments, 1); 25 | if (!app.stats && name != "select") { 26 | args.unshift(name); 27 | name = "upload"; 28 | } 29 | try { 30 | pageBundle = require("bundle!./pages/" + name + "/page.js"); 31 | } catch (err) { 32 | pageBundle = function(cb) { 33 | cb(require("./pages/error/page.js")); 34 | }; 35 | args.unshift(err, name); 36 | } 37 | pageBundle(function(page) { 38 | $(function() { 39 | if (lastPage) lastPage(); 40 | lastPage = page.apply(null, args); 41 | window.scrollTo(0, 0); 42 | if (name !== "upload") { 43 | ga('send', 'pageview', { 44 | page : window.location.pathname.replace(/\/$/, "") + "/" + [name].concat(args).join("/"), 45 | title: document.title 46 | }); 47 | } 48 | }); 49 | }); 50 | } 51 | app.loadPage = loadPage; 52 | 53 | require.include("sigma.js"); 54 | require.include("./percentageToColor"); 55 | require.include("./findById"); 56 | require.include("./graphs/modules"); 57 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0 */ 2 | const path = require('path'); 3 | const merge = require('lodash.merge'); 4 | 5 | const NODE_ENV = process.env.NODE_ENV || 'development'; 6 | const APP_DIR = 'src'; 7 | const DIST_DIR = 'dist'; 8 | const SERVER_DIR = 'server'; 9 | const STATIC_DIR = 'static'; 10 | const STYLES_DIR = 'styles'; 11 | const PROJECT_ROOT = path.resolve(__dirname, '..'); 12 | 13 | const config = { 14 | env: NODE_ENV, 15 | 16 | paths: { 17 | root: PROJECT_ROOT, 18 | app: path.resolve(PROJECT_ROOT, APP_DIR), 19 | dist: path.resolve(PROJECT_ROOT, DIST_DIR), 20 | server: path.resolve(PROJECT_ROOT, SERVER_DIR), 21 | static: path.resolve(PROJECT_ROOT, APP_DIR, STATIC_DIR), 22 | styles: path.resolve(PROJECT_ROOT, APP_DIR, STYLES_DIR), 23 | entryFile: path.resolve(PROJECT_ROOT, APP_DIR, 'app.js'), 24 | }, 25 | 26 | server: { 27 | hostname: 'localhost', 28 | port: process.env.PORT || 3000, 29 | }, 30 | 31 | webpack: { 32 | vendor: [ 33 | 'react', 34 | 'react-redux', 35 | 'react-router', 36 | 'react-intl', 37 | 'redux', 38 | 'redux-form', 39 | 'react-router-redux', 40 | 'react-bootstrap', 41 | 'react-router-bootstrap', 42 | ], 43 | output: { 44 | publicPath: '/', 45 | }, 46 | }, 47 | 48 | compiler: { 49 | hash_type: 'hash', 50 | stats: { 51 | chunks: false, 52 | chunkModules: false, 53 | colors: true, 54 | }, 55 | }, 56 | }; 57 | 58 | 59 | if (NODE_ENV === 'test') { 60 | module.exports = config; 61 | } else { 62 | module.exports = merge({}, config, require(`./environments/${ NODE_ENV }`)); 63 | } 64 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const yargs = require('yargs'); 2 | const express = require('express'); 3 | const webpack = require('webpack'); 4 | const debug = require('debug'); 5 | const config = require('../config'); 6 | const history = require('connect-history-api-fallback'); 7 | const webpackconfig = require('../webpack.config'); 8 | const webpackDevMiddleware = require('webpack-dev-middleware'); 9 | const webpackHotMiddleware = require('webpack-hot-middleware'); 10 | const browserSync = require('browser-sync'); 11 | 12 | debug.enable('app:*'); 13 | 14 | const app = express(); 15 | const compiler = webpack(webpackconfig); 16 | 17 | const log = debug('app:server'); 18 | 19 | log('Redirecting all other requests to index.html'); 20 | app.use(history({ verbose: false })); 21 | 22 | log('Enabling webpack dev middleware.'); 23 | app.use(webpackDevMiddleware(compiler, { 24 | lazy: false, 25 | noInfo: false, 26 | quiet: false, 27 | stats: config.compiler.stats, 28 | })); 29 | 30 | log('Enabling Webpack Hot Module Replacement (HMR).'); 31 | app.use(webpackHotMiddleware(compiler)); 32 | 33 | log(`Serving static content from ${ config.paths.static }`); 34 | app.use(express.static(config.paths.static)); 35 | 36 | const port = yargs.argv.port || config.server.port; 37 | app.listen(port, config.server.hostname, () => { 38 | log(`Server is now running at http://${ config.server.hostname }:${ port }.`); 39 | }); 40 | 41 | if (yargs.argv.withBrowsersync) { 42 | browserSync.init({ 43 | proxy: `${ config.server.hostname }:${ port }`, 44 | port: 4000, 45 | ui: { 46 | port: 4040, 47 | weinre: { port: 4444 }, 48 | }, 49 | }); 50 | } 51 | 52 | module.exports = app; 53 | -------------------------------------------------------------------------------- /src/redux/modules/user/user-actions.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | UPDATE_USER_REQUEST, 3 | UPDATE_USER_SUCCESS, 4 | UPDATE_USER_FAILURE, 5 | updateUserRequest, 6 | updateUserSuccess, 7 | updateUserFailure, 8 | updateUser, 9 | } from './user-actions'; 10 | 11 | describe('updateUserRequest', () => { 12 | it(`returns an action with type ${UPDATE_USER_REQUEST}`, () => { 13 | const action = updateUserRequest(); 14 | expect(action.type).to.be.equal(UPDATE_USER_REQUEST); 15 | }); 16 | 17 | it('does not contain user state', () => { 18 | const action = updateUserRequest(); 19 | expect(action.user).to.be.eq(undefined); 20 | }); 21 | }); 22 | 23 | describe('updateUserSuccess', () => { 24 | it(`returns an action with type ${UPDATE_USER_SUCCESS}`, () => { 25 | const action = updateUserSuccess(); 26 | expect(action.type).to.be.equal(UPDATE_USER_SUCCESS); 27 | }); 28 | 29 | it('contains the updated user', () => { 30 | const user = { name: 'Test' }; 31 | const action = updateUserSuccess(user); 32 | expect(action.user).to.be.equal(user); 33 | }); 34 | }); 35 | 36 | describe('updateUserFailure', () => { 37 | it(`returns an action with type ${UPDATE_USER_FAILURE}`, () => { 38 | const action = updateUserFailure(); 39 | expect(action.type).to.be.equal(UPDATE_USER_FAILURE); 40 | }); 41 | 42 | it('does not contain user state', () => { 43 | const action = updateUserFailure(); 44 | expect(action.user).to.be.eq(undefined); 45 | }); 46 | }); 47 | 48 | describe('updateUser', () => { 49 | it(`is an asynchronous action`, () => { 50 | const action = updateUser(); 51 | expect(action).to.be.a('function'); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/components/FormFields/FormErrorMessages.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import React, { PropTypes, Element } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import FormMessages from 'redux-form-validation'; 5 | import { messages } from '../../shared/forms'; 6 | 7 | const FormErrorMessages = (props: Object): Element => ( 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | 34 | FormErrorMessages.displayName = 'FormErrorMessages'; 35 | 36 | FormErrorMessages.propTypes = { 37 | field: PropTypes.object.isRequired, 38 | min: PropTypes.number, 39 | max: PropTypes.number, 40 | minLength: PropTypes.number, 41 | maxLength: PropTypes.number, 42 | }; 43 | 44 | export default FormErrorMessages; 45 | -------------------------------------------------------------------------------- /src/containers/AuthenticatedComponent.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { routeActions } from 'react-router-redux'; 4 | import classNames from 'classnames'; 5 | 6 | 7 | const requireAuthentication = (ComposedComponent) => { 8 | const mapStateToProps = (state) => ({ 9 | isAuthenticated: state.auth.isAuthenticated, 10 | }); 11 | 12 | class AuthenticatedComponent extends Component { 13 | static displayName = 'AuthenticatedComponent'; 14 | static propTypes = { 15 | dispatch: PropTypes.func.isRequired, 16 | isAuthenticated: PropTypes.bool.isRequired, 17 | }; 18 | 19 | componentWillMount() { 20 | this.checkAuth(this.props); 21 | } 22 | 23 | componentWillReceiveProps(props) { 24 | this.checkAuth(props); 25 | } 26 | 27 | checkAuth(props) { 28 | if (!props.isAuthenticated) { 29 | this.redirectToLogin(); 30 | } 31 | } 32 | 33 | redirectToLogin() { 34 | // this would dispatch a notification then a route change. 35 | this.props.dispatch(routeActions.replace({ pathname: '/' })); 36 | } 37 | 38 | render() { 39 | const { isAuthenticated } = this.props; 40 | const wrapperClass = classNames({ 41 | 'is-authenticated': isAuthenticated, 42 | 'requires-authentication': !isAuthenticated, 43 | }); 44 | 45 | return ( 46 |
47 | { isAuthenticated ? : null } 48 |
49 | ); 50 | } 51 | } 52 | 53 | return connect(mapStateToProps)(AuthenticatedComponent); 54 | }; 55 | 56 | export { 57 | requireAuthentication, 58 | }; 59 | -------------------------------------------------------------------------------- /src/_BIN/pages/home/home.jade: -------------------------------------------------------------------------------- 1 | .container-fluid 2 | .row 3 | .col-md-12 4 | .page-header 5 | h1 webpack analyse 6 | .row 7 | .col-md-3 8 | .well: center 9 | h1 10 | a(href="http://webpack.github.io/") webpack 11 | br 12 | a(href="http://webpack.github.io/docs/changelog.html")= stats.version 13 | .col-md-3 14 | .well: center 15 | h1 16 | = stats.time 17 | br 18 | | ms 19 | .col-md-6 20 | .well: center 21 | h1 22 | | hash 23 | br 24 | = stats.hash 25 | .row 26 | .col-md-3 27 | .well: center 28 | h1: a(href="#modules") 29 | = stats.modules.length 30 | br 31 | | modules 32 | .col-md-3 33 | .well: center 34 | h1: a(href="#chunks") 35 | = stats.chunks.length 36 | br 37 | | chunks 38 | .col-md-3 39 | .well: center 40 | h1: a(href="#assets") 41 | = stats.assets.length 42 | br 43 | | assets 44 | .col-md-3 45 | .well: center 46 | h1 47 | if stats.errors.length && stats.warnings.length 48 | a(href="#warnings") 49 | = stats.warnings.length 50 | | warnings 51 | br 52 | a(href="#errors") 53 | = stats.errors.length 54 | | errors 55 | else if stats.errors.length 56 | a(href="#errors") 57 | = stats.errors.length 58 | br 59 | | errors 60 | else if stats.warnings.length 61 | a(href="#warnings") 62 | = stats.warnings.length 63 | br 64 | | warnings 65 | else 66 | | no 67 | br 68 | | warnings/errors 69 | -------------------------------------------------------------------------------- /src/containers/LanguageSelectionDropdown/LanguageSelectionDropdown.js: -------------------------------------------------------------------------------- 1 | /* eslint no-invalid-this: 0 */ 2 | import React, { PropTypes, Component } from 'react'; 3 | import { NavDropdown, MenuItem } from 'react-bootstrap'; 4 | import { FormattedMessage } from 'react-intl'; 5 | import { connect } from 'react-redux'; 6 | import find from 'lodash/find'; 7 | 8 | import { changeLanguage } from '../../redux/modules/language/language'; 9 | 10 | const supportedLanguages = [ 11 | { 12 | id: 'lang.en', 13 | key: 'en', 14 | description: 'English', 15 | defaultMessage: 'English', 16 | }, 17 | { 18 | id: 'lang.es', 19 | key: 'es', 20 | description: 'Español', 21 | defaultMessage: 'Español', 22 | }, 23 | { 24 | id: 'lang.pl', 25 | key: 'pl', 26 | description: 'Polski', 27 | defaultMessage: 'Polski', 28 | }, 29 | ]; 30 | 31 | class LanguageSelectionDropdown extends Component { 32 | static displayName = 'LanguageSelectionDropdown'; 33 | static propTypes = { 34 | dispatch: PropTypes.func.isRequired, 35 | language: PropTypes.string.isRequired, 36 | }; 37 | 38 | languageText(lang) { 39 | return ; 40 | } 41 | 42 | handleLanguageChange = (lang: string) => () => 43 | this.props.dispatch(changeLanguage(lang)); 44 | 45 | render() { 46 | const currentLanguage = find(supportedLanguages, { key: this.props.language }); 47 | 48 | return ( 49 | 50 | {supportedLanguages.map(lang => 51 | 52 | {this.languageText(lang)} 53 | 54 | )} 55 | 56 | ); 57 | } 58 | } 59 | 60 | const mapStateToProps = ({ language }) => ({ language }); 61 | 62 | export default connect(mapStateToProps)(LanguageSelectionDropdown); 63 | -------------------------------------------------------------------------------- /src/pages/ProfileEditPage/ProfileEditPage.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import debug from 'debug'; 4 | import React, { PropTypes, Component, Element } from 'react'; 5 | import { FormattedMessage } from 'react-intl'; 6 | import { autobind } from 'core-decorators'; 7 | import { connect } from 'react-redux'; 8 | import type { User } from '../../declarations/app'; 9 | import ProfileEditForm from '../../containers/ProfileEditForm/ProfileEditForm'; 10 | import { updateUser } from '../../redux/modules/user/user-actions'; 11 | import { messages } from './ProfileEditPage.i18n'; 12 | import { 13 | updateDocumentTitle, 14 | resetDocumentTitle, 15 | } from '../../redux/modules/document-title/document-title'; 16 | 17 | if (__DEBUG__) { 18 | debug.enable('profile-edit-page:*'); 19 | } 20 | 21 | const log = debug('profile-edit-page:debug'); 22 | 23 | export class ProfileEditPage extends Component { 24 | static displayName = 'ProfileEditPage'; 25 | static propTypes = { 26 | dispatch: PropTypes.func.isRequired, 27 | user: PropTypes.object, 28 | }; 29 | 30 | componentDidMount() { 31 | this.props.dispatch(updateDocumentTitle(messages.title)); 32 | } 33 | 34 | componentWillUnmount() { 35 | this.props.dispatch(resetDocumentTitle()); 36 | } 37 | 38 | @autobind 39 | handleUpdate(user: User) { 40 | log('handleUpdate(): user:', user); 41 | this.props.dispatch(updateUser(user)); 42 | } 43 | 44 | render(): Element { 45 | return ( 46 |
47 |

48 | 49 |

50 | 51 |
52 | 53 |
54 | 55 |
56 | ); 57 | } 58 | } 59 | 60 | const mapStateToProps = (state) => ({ 61 | user: state.user, 62 | }); 63 | 64 | export default connect(mapStateToProps)(ProfileEditPage); 65 | -------------------------------------------------------------------------------- /src/containers/ProfileEditForm/ProfileEditForm.i18n.js: -------------------------------------------------------------------------------- 1 | export const messages = { 2 | age: { 3 | placeholder: { 4 | id: 'profile.form.age.placeholder', 5 | defaultMessage: 'age', 6 | }, 7 | }, 8 | email: { 9 | label: { 10 | id: 'profile.form.email.label', 11 | defaultMessage: 'Email', 12 | }, 13 | placeholder: { 14 | id: 'profile.form.email.placeholder', 15 | defaultMessage: 'email', 16 | }, 17 | }, 18 | emailVerified: { 19 | label: { 20 | id: 'profile.form.emailVerified.label', 21 | defaultMessage: 'Status', 22 | }, 23 | placeholder: { 24 | id: 'profile.form.emailVerified.placeholder', 25 | defaultMessage: 'verified', 26 | }, 27 | }, 28 | familyName: { 29 | placeholder: { 30 | id: 'profile.form.familyName.placeholder', 31 | defaultMessage: 'family name', 32 | }, 33 | }, 34 | gender: { 35 | label: { 36 | id: 'profile.form.gender.label', 37 | defaultMessage: 'Gender', 38 | }, 39 | male: { 40 | label: { 41 | id: 'profile.form.gender.male.label', 42 | defaultMessage: 'male', 43 | }, 44 | }, 45 | female: { 46 | label: { 47 | id: 'profile.form.gender.female.label', 48 | defaultMessage: 'female', 49 | }, 50 | }, 51 | }, 52 | givenName: { 53 | placeholder: { 54 | id: 'profile.form.givenName.placeholder', 55 | defaultMessage: 'given name', 56 | }, 57 | }, 58 | locale: { 59 | label: { 60 | id: 'profile.form.locale.label', 61 | defaultMessage: 'locale', 62 | }, 63 | }, 64 | nickname: { 65 | placeholder: { 66 | id: 'profile.form.nickname.placeholder', 67 | defaultMessage: 'nickname', 68 | }, 69 | }, 70 | notes: { 71 | label: { 72 | id: 'profile.form.notes.label', 73 | defaultMessage: 'notes', 74 | }, 75 | }, 76 | reset: { 77 | label: { 78 | id: 'profile.form.reset.label', 79 | defaultMessage: 'reset', 80 | }, 81 | }, 82 | save: { 83 | label: { 84 | id: 'profile.form.save.label', 85 | defaultMessage: 'save', 86 | }, 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /* eslint global-require: 0 */ 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { Router, browserHistory } from 'react-router'; 5 | import { Provider } from 'react-redux'; 6 | import { syncHistoryWithStore } from 'react-router-redux'; 7 | import debug from 'debug'; 8 | import { addLocaleData } from 'react-intl'; 9 | import en from 'react-intl/locale-data/en'; 10 | import es from 'react-intl/locale-data/es'; 11 | import pl from 'react-intl/locale-data/pl'; 12 | import configureStore from './redux/configure-store'; 13 | import routes from './routes'; 14 | import Root from './containers/Root'; 15 | import Redbox from 'redbox-react'; 16 | 17 | addLocaleData(en); 18 | addLocaleData(es); 19 | addLocaleData(pl); 20 | 21 | if (__DEBUG__) { 22 | debug.enable('app:*'); 23 | } 24 | 25 | const initialState = window.__INITIAL_STATE__; 26 | const store = configureStore(initialState, browserHistory); 27 | const history = syncHistoryWithStore(browserHistory, store); 28 | const rootElement = document.getElementById('root'); 29 | 30 | if (__DEBUG__) { 31 | const { AppContainer } = require('react-hot-loader'); 32 | 33 | const render = () => { 34 | ReactDOM.render( 35 | 36 | 41 | , 42 | rootElement 43 | ); 44 | }; 45 | render(); 46 | 47 | if (module.hot) { 48 | module.hot.accept('./containers/Root', () => { 49 | const NextApp = require('./containers/Root').default; 50 | render( 51 | 52 | 53 | 58 | 59 | , 60 | rootElement 61 | ); 62 | }); 63 | } 64 | 65 | } else { 66 | ReactDOM.render( 67 | 68 | 73 | , 74 | rootElement 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /src/_BIN/Graphs/chunks.js: -------------------------------------------------------------------------------- 1 | const app = require('../app'); 2 | const Sigma = require('sigma'); 3 | const findById = require('../../utils/findById'); 4 | const percentageToColor = require('../../utils/percentageToColor').greenRed; 5 | 6 | const element = document.getElementById('sigma-chunks'); 7 | 8 | const nodes = []; 9 | const edges = []; 10 | const chunkCount = app.stats.chunks.length; 11 | let maxSize = 0; 12 | app.stats.chunks.forEach((chunk, idx) => { 13 | if (chunk.size > maxSize) { maxSize = chunk.size; } 14 | }); 15 | app.stats.chunks.forEach((chunk, idx) => { 16 | const color = percentageToColor(Math.pow((chunk.size + 1) / (maxSize + 1), 1 / 4)); 17 | nodes.push({ 18 | id : `chunk${chunk.id}`, 19 | chunkId : chunk.id, 20 | size : Math.ceil(Math.sqrt(chunk.size + 1)), 21 | type : 'webpack', 22 | shortLabel: `${ chunk.id }`, 23 | label : `[${ chunk.id }] ${ chunk.origins.map(o => o.reasons.concat(o.name).concat(o.moduleName).join(' ')).join(', ') }`, 24 | x : (Math.cos(idx / chunkCount * Math.PI * 2) * chunkCount), 25 | y : (Math.sin(idx / chunkCount * Math.PI * 2) * chunkCount), 26 | color : color, 27 | }); 28 | }); 29 | app.stats.chunks.forEach(function (chunk) { 30 | chunk.parents.forEach(function (parent) { 31 | edges.push({ 32 | id : `edge${ chunk.id }-${ parent }`, 33 | source: `chunk${ parent }`, 34 | target: `chunk${ chunk.id }`, 35 | arrow : 'target', 36 | type : 'arrow', 37 | size : chunk.parents.length, 38 | }); 39 | }); 40 | }); 41 | const s = new Sigma({ 42 | graph : { 43 | nodes: nodes, 44 | edges: edges, 45 | }, 46 | renderer: { 47 | type : 'canvas', 48 | container: element, 49 | }, 50 | settings: { 51 | edgeColor : 'target', 52 | maxNodeSize: 20, 53 | minNodeSize: 4, 54 | maxEdgeSize: 3, 55 | minEdgeSize: 1, 56 | }, 57 | }); 58 | s.bind('clickNode', function (e) { 59 | window.location.hash = `#chunk/${ e.data.node.chunkId }`; 60 | }); 61 | 62 | s.refresh(); 63 | 64 | exports.show = function () { 65 | element.style.display = 'block'; 66 | s.refresh(); 67 | s.startForceAtlas2(); 68 | s.renderers[0].resize(); 69 | }; 70 | 71 | exports.hide = function () { 72 | element.style.display = 'none'; 73 | s.stopForceAtlas2(); 74 | }; 75 | -------------------------------------------------------------------------------- /src/_BIN/pages/hints/page.js: -------------------------------------------------------------------------------- 1 | var app = require("../../app"); 2 | var findById = require("../../findById"); 3 | 4 | module.exports = function() { 5 | document.title = "hints"; 6 | var multiRefs = []; 7 | app.stats.modules.forEach(function(module) { 8 | var requiresSum = {}; 9 | module.dependencies.forEach(function(d) { 10 | if (!requiresSum[d.moduleId]) 11 | requiresSum[d.moduleId] = { 12 | module : module, 13 | count : 1, 14 | otherModule: findById(app.stats.modules, d.moduleId) 15 | }; 16 | else 17 | requiresSum[d.moduleId].count++; 18 | }); 19 | Object.keys(requiresSum).forEach(function(id) { 20 | var item = requiresSum[id]; 21 | if (item.count >= 2) 22 | multiRefs.push(item); 23 | }); 24 | }); 25 | multiRefs.forEach(function(item) { 26 | var refModLength = (item.otherModule.id + "").length; 27 | item.saving = item.count * (2 + refModLength) - 6 - refModLength; 28 | }); 29 | multiRefs = multiRefs.filter(function(item) { 30 | return item.saving > 10; 31 | }); 32 | multiRefs.sort(function(a, b) { 33 | return b.saving - a.saving; 34 | }); 35 | 36 | var multiChunks = []; 37 | app.stats.modules.forEach(function(module) { 38 | if (module.chunks.length >= 2) { 39 | multiChunks.push({ 40 | module: module, 41 | count : module.chunks.length, 42 | saving: ((module.chunks.length - 1) * module.size) 43 | }); 44 | } 45 | }); 46 | multiChunks = multiChunks.filter(function(item) { 47 | return item.saving > 100; 48 | }); 49 | multiChunks.sort(function(a, b) { 50 | return b.saving - a.saving; 51 | }); 52 | 53 | var modulesByTimestamp = app.stats.modules.filter(function(m) { 54 | return typeof m.timestamp === "number"; 55 | }).sort(function(a, b) { 56 | return b.timestamp - a.timestamp; 57 | }).slice(0, 10); 58 | 59 | var longChains = modulesByTimestamp.map(function(m) { 60 | var chain = [m]; 61 | while (typeof m.issuerUid === "number") { 62 | m = app.mapModulesUid[m.issuerUid]; 63 | if (!m) break; 64 | chain.unshift(m); 65 | } 66 | return chain; 67 | }); 68 | 69 | $(".page").html(require("./hints.jade")({ 70 | stats : app.stats, 71 | multiRefs : multiRefs, 72 | multiChunks: multiChunks, 73 | longChains : longChains 74 | })); 75 | }; -------------------------------------------------------------------------------- /src/translations/es.js: -------------------------------------------------------------------------------- 1 | // Spanish translations 2 | export default { 3 | 'aboutPage.title': 'Sobre Nosotros', 4 | 'aboutPage.overview': 'para traducir...', 5 | 'common.form.error.required': 'Este campo es obligatorio', 6 | 'common.form.error.email': 'Correo no válido', 7 | 'common.form.error.max': 'No puede ser más que {max}', 8 | 'common.form.error.maxLength': `No puede tener más de 9 | {maxLength, plural, one {1 carácter} other {{maxLength} caracteres}}`, 10 | 'common.form.error.min': 'Debe ser al menos {min}', 11 | 'common.form.error.minLength': `Debe tener al menos 12 | {minLength, plural, one {1 carácter} other {{minLength} caracteres}}`, 13 | 'homePage.title': 'Inicio', 14 | 'homePage.button.clickMe': 'Cliquéame (log)', 15 | 'homePage.para.pressCtrlH': 'Presione ctrl-h para alternar el panel de Redux Dev Tools.', 16 | 'homePage.para.autoUpdate': 'Esta página se actualiza automáticamente cuando haces cambios y guardas.', 17 | 'homePage.para.es7Decorator': `Esta página también demuestra el uso del decorador 18 | @autobind.`, 19 | 'lang.en': 'English', 20 | 'lang.es': 'Español', 21 | 'lang.pl': 'Polski', 22 | 'navigation.aboutUs': 'Sobre Nosotros', 23 | 'navigation.account': 'Cuenta', 24 | 'navigation.faq': 'FAQ', 25 | 'navigation.help': 'Ayuda', 26 | 'navigation.home': 'r3analyse', 27 | 'navigation.logIn': 'Iniciar Sesión', 28 | 'navigation.logOut': 'Terminar Sesión', 29 | 'navigation.policies': 'Normas', 30 | 'navigation.privacy': 'Privacidad', 31 | 'navigation.terms': 'Términos & Condiciones', 32 | 'profile.edit.title': 'Editar Perfil', 33 | 'profile.form.age.placeholder': 'Edad', 34 | 'profile.form.emailVerified.label': 'Correo verificado', 35 | 'profile.form.email.placeholder': 'correo', 36 | 'profile.form.familyName.placeholder': 'appellido', 37 | 'profile.form.gender.male.label': 'Masculino', 38 | 'profile.form.gender.female.label': 'Femenino', 39 | 'profile.form.givenName.placeholder': 'nombre', 40 | 'profile.form.locale.label': 'Idioma', 41 | 'profile.form.nickname.placeholder': 'nickname', 42 | 'profile.form.notes.label': 'Apuntes', 43 | 'profile.form.reset.label': 'Resetear', 44 | 'profile.form.save.label': 'Guardar', 45 | 'profile.message.updatingUserDetails': 'Actualizando detalles del usuario...', 46 | 'site.message.loggingIn': 'Iniciando sesión...', 47 | 'site.message.loggingOut': 'Terminando sesion...', 48 | 'site.name': 'r3analyse', 49 | }; 50 | -------------------------------------------------------------------------------- /src/translations/en.js: -------------------------------------------------------------------------------- 1 | // English translations 2 | export default { 3 | 'aboutPage.title': 'About Us', 4 | 'aboutPage.overview': `Currently implemented as a stateless component, 5 | so will not auto-update when changes are made.`, 6 | 'common.form.error.required': 'This field is required', 7 | 'common.form.error.email': 'Email address is invalid', 8 | 'common.form.error.max': 'May not be greater than {max}', 9 | 'common.form.error.maxLength': `May not be more than 10 | {maxLength, plural, one {1 character} other {{maxLength} characters}} long`, 11 | 'common.form.error.min': 'Must be at least {min}', 12 | 'common.form.error.minLength': `Must be at least 13 | {minLength, plural, one {1 character} other {{minLength} characters}} long`, 14 | 'homePage.title': 'Home Page', 15 | 'homePage.button.clickMe': 'Click me (log)', 16 | 'homePage.para.pressCtrlH': 'Press ctrl-h to toggle Redux Dev Tools (development build only).', 17 | 'homePage.para.autoUpdate': 'This pages auto-updates when you make changes and save.', 18 | 'homePage.para.es7Decorator': `This page also demonstrates the use of the 19 | @autobind ES7 decorator.`, 20 | 'lang.en': 'English', 21 | 'lang.es': 'Español', 22 | 'lang.pl': 'Polski', 23 | 'navigation.aboutUs': 'About Us', 24 | 'navigation.account': 'Account', 25 | 'navigation.faq': 'FAQ', 26 | 'navigation.help': 'Help', 27 | 'navigation.home': 'r3analyse', 28 | 'navigation.logIn': 'Log in', 29 | 'navigation.logOut': 'Log out', 30 | 'navigation.policies': 'Policies', 31 | 'navigation.privacy': 'Privacy', 32 | 'navigation.terms': 'Terms & Conditions', 33 | 'profile.edit.title': 'Edit Profile', 34 | 'profile.form.age.placeholder': 'Age', 35 | 'profile.form.emailVerified.label': 'Email verified', 36 | 'profile.form.email.placeholder': 'email', 37 | 'profile.form.familyName.placeholder': 'family name', 38 | 'profile.form.gender.male.label': 'Male', 39 | 'profile.form.gender.female.label': 'Female', 40 | 'profile.form.givenName.placeholder': 'given name', 41 | 'profile.form.locale.label': 'Locale', 42 | 'profile.form.nickname.placeholder': 'nickname', 43 | 'profile.form.notes.label': 'Notes', 44 | 'profile.form.reset.label': 'Reset', 45 | 'profile.form.save.label': 'Save', 46 | 'profile.message.updatingUserDetails': 'Updating user details...', 47 | 'site.message.loggingIn': 'Logging in...', 48 | 'site.message.loggingOut': 'Logging out...', 49 | 'site.name': 'r3analyse', 50 | }; 51 | -------------------------------------------------------------------------------- /src/shared/links.js: -------------------------------------------------------------------------------- 1 | import site from '../settings'; 2 | 3 | export const links = { 4 | home: { 5 | to: '/', 6 | id: 'navigation.home', 7 | description: 'Go to Home Page', 8 | defaultMessage: site.name, 9 | }, 10 | 11 | aboutUs: { 12 | to: '/pages/about-us', 13 | id: 'navigation.aboutUs', 14 | description: 'About Us page link', 15 | defaultMessage: 'About Us', 16 | }, 17 | 18 | assets: { 19 | to: '/pages/assets', 20 | id: 'navigation.assets', 21 | description: 'Assets', 22 | defaultMessage: 'Assets', 23 | }, 24 | 25 | chunk: { 26 | to: '/pages/chunk', 27 | id: 'navigation.chunk', 28 | description: 'Chunk', 29 | defaultMessage: 'Chunk', 30 | }, 31 | 32 | chunks: { 33 | to: '/pages/chunks', 34 | id: 'navigation.chunks', 35 | description: 'Chunks', 36 | defaultMessage: 'Chunks', 37 | }, 38 | 39 | error: { 40 | to: '/pages/error', 41 | id: 'navigation.error', 42 | description: 'Error', 43 | defaultMessage: 'Error', 44 | }, 45 | 46 | errors: { 47 | to: '/pages/errors', 48 | id: 'navigation.errors', 49 | description: 'Errors', 50 | defaultMessage: 'Errors', 51 | }, 52 | 53 | hints: { 54 | to: '/pages/hints', 55 | id: 'navigation.hints', 56 | description: 'Hints', 57 | defaultMessage: 'Hints', 58 | }, 59 | 60 | module: { 61 | to: '/pages/module', 62 | id: 'navigation.module', 63 | description: 'Module', 64 | defaultMessage: 'Module', 65 | }, 66 | 67 | modules: { 68 | to: '/pages/modules', 69 | id: 'navigation.modules', 70 | description: 'Modules', 71 | defaultMessage: 'Modules', 72 | }, 73 | 74 | select: { 75 | to: '/pages/select', 76 | id: 'navigation.select', 77 | description: 'Select', 78 | defaultMessage: 'Select', 79 | }, 80 | 81 | upload: { 82 | to: '/pages/upload', 83 | id: 'navigation.upload', 84 | description: 'Upload', 85 | defaultMessage: 'Upload', 86 | }, 87 | 88 | warnings: { 89 | to: '/pages/warnings', 90 | id: 'navigation.warnings', 91 | description: 'Warnings', 92 | defaultMessage: 'Warnings', 93 | }, 94 | 95 | logIn: { 96 | id: 'navigation.logIn', 97 | description: 'Log in', 98 | defaultMessage: 'Log in', 99 | }, 100 | 101 | logOut: { 102 | id: 'navigation.logOut', 103 | description: 'Log out', 104 | defaultMessage: 'Log out', 105 | }, 106 | 107 | }; 108 | -------------------------------------------------------------------------------- /src/pages/HomePage/HomePage.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { FormattedMessage, FormattedHTMLMessage } from 'react-intl'; 3 | import { connect } from 'react-redux'; 4 | import { Grid, Row, Col, Button } from 'react-bootstrap'; 5 | import debug from 'debug'; 6 | import { autobind } from 'core-decorators'; 7 | import styles from './HomePage.scss'; 8 | import { messages } from './HomePage.i18n'; 9 | import HomePageHero from './HomePageHero'; 10 | import { 11 | updateDocumentTitle, 12 | resetDocumentTitle, 13 | } from '../../redux/modules/document-title/document-title'; 14 | 15 | if (__DEBUG__) { 16 | debug.enable('home-page:*'); 17 | } 18 | 19 | const log = debug('home-page:info'); 20 | 21 | export class HomePage extends React.Component { 22 | static displayName = 'HomePage'; 23 | static propTypes = { 24 | isAuthenticated: PropTypes.bool, 25 | dispatch: PropTypes.func, 26 | }; 27 | 28 | // executes only on the client 29 | componentDidMount() { 30 | this.props.dispatch(updateDocumentTitle(messages.title)); 31 | } 32 | 33 | componentWillUnmount() { 34 | log('remove custom document title'); 35 | this.props.dispatch(resetDocumentTitle()); 36 | } 37 | 38 | @autobind 39 | handleButtonClick() { 40 | log('button click handler context:', this); 41 | } 42 | 43 | render() { 44 | return ( 45 |
46 | 47 | 48 | 49 | 50 |

51 | 52 |

53 |

54 | 55 |

56 |

57 | 58 |

59 |

60 | 61 |

62 | 63 |
64 | 65 | 66 | 69 | 70 | 71 |
72 |
73 | ); 74 | } 75 | } 76 | 77 | const mapStateToProps = (state) => 78 | ({ isAuthenticated: state.isAuthenticated }); 79 | 80 | export default connect(mapStateToProps)(HomePage); 81 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, IndexRedirect } from 'react-router'; 3 | import { requireAuthentication as restrict } from './containers/AuthenticatedComponent'; 4 | 5 | import AppContainer from './containers/AppContainer'; 6 | import HeroPageLayout from './containers/HeroPageLayout'; 7 | import AdminPageLayout from './containers/AdminPageLayout'; 8 | import HomePage from './pages/HomePage/HomePage'; 9 | import AboutPage from './pages/AboutPage/AboutPage'; 10 | import AssetsPage from './pages/AssetsPage/AssetsPage'; 11 | import ChunkPage from './pages/ChunkPage/ChunkPage'; 12 | import ChunksPage from './pages/ChunksPage/ChunksPage'; 13 | import ErrorPage from './pages/ErrorPage/ErrorPage'; 14 | import ErrorsPage from './pages/ErrorsPage/ErrorsPage'; 15 | import HintsPage from './pages/HintsPage/HintsPage'; 16 | import ModulePage from './pages/ModulePage/ModulePage'; 17 | import ModulesPage from './pages/ModulesPage/ModulesPage'; 18 | import SelectPage from './pages/SelectPage/SelectPage'; 19 | import UploadPage from './pages/UploadPage/UploadPage'; 20 | import WarningsPage from './pages/WarningsPage/WarningsPage'; 21 | import ProfileEditPage from './pages/ProfileEditPage/ProfileEditPage'; 22 | 23 | export default( 24 | // Route components without path will render their children... 25 | 26 | // until a match is found... 27 | 28 | // here 29 | 30 | // Routes without a component will render their children: 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | -------------------------------------------------------------------------------- /src/translations/pl.js: -------------------------------------------------------------------------------- 1 | // Polish translations 2 | export default { 3 | 'aboutPage.title': 'O Nas', 4 | 'aboutPage.overview': `Obecnie zaimplementowany jako bezstanowy komponentu, 5 | więc nie będzie się odświeżać po wykonaniu zmian.`, 6 | 'common.form.error.required': 'To pole jest wymagane.', 7 | 'common.form.error.email': 'Adres email jest nieprawidłowy', 8 | 'common.form.error.max': 'Nie może być większy niż {max}', 9 | 'common.form.error.maxLength': `Nie może być wyższy niż 10 | {maxLength, plural, one {1 character} czy {{maxLength} characters}} w długości`, 11 | 'common.form.error.min': 'Musi być przynajmniej {min}', 12 | 'common.form.error.minLength': `Musi być przynajmniej 13 | {minLength, plural, one {1 character} innej {{minLength} characters}} w długości`, 14 | 'homePage.title': 'Wstępna Strona', 15 | 'homePage.button.clickMe': 'Kliknij (log)', 16 | 'homePage.para.pressCtrlH': 'Naciśnij ctrl-h ' + 17 | 'aby włączyć Redux Narzędzia Konstruktorskie (w proceśie budowniczym tylko).', 18 | 'homePage.para.autoUpdate': 'Ta strona automatycznie sie odświeża po wprowadzeniu i zachowaniu zmian.', 19 | 'homePage.para.es7Decorator': `Ta strona pokazuje również używanie 20 | @autobind ES7 dekoratorów.`, 21 | 'lang.en': 'Angielski', 22 | 'lang.es': 'Hiszpański', 23 | 'lang.pl': 'Polski', 24 | 'navigation.aboutUs': 'O Nas', 25 | 'navigation.account': 'Konto', 26 | 'navigation.faq': 'FAQ', 27 | 'navigation.help': 'Pomoc', 28 | 'navigation.home': 'r3analyse', 29 | 'navigation.logIn': 'Zaloguj się', 30 | 'navigation.logOut': 'Wyloguj się', 31 | 'navigation.policies': 'Ustawy', 32 | 'navigation.privacy': 'Prywatność', 33 | 'navigation.terms': 'Zasady i Warunki', 34 | 'profile.edit.title': 'Edytuj profil', 35 | 'profile.form.age.placeholder': 'Wiek', 36 | 'profile.form.emailVerified.label': 'Email zweryfikowany', 37 | 'profile.form.email.placeholder': 'email', 38 | 'profile.form.familyName.placeholder': 'nazwisko rodowe', 39 | 'profile.form.gender.male.label': 'Mężczyzna', 40 | 'profile.form.gender.female.label': 'Kobieta', 41 | 'profile.form.givenName.placeholder': 'nadane imię', 42 | 'profile.form.locale.label': 'Region', 43 | 'profile.form.nickname.placeholder': 'ksywa', 44 | 'profile.form.notes.label': 'Notatki', 45 | 'profile.form.reset.label': 'Zresetować', 46 | 'profile.form.save.label': 'Zapisać', 47 | 'profile.message.updatingUserDetails': 'Aktualizowanie danych użytkownika...', 48 | 'site.message.loggingIn': 'Podczas logowania...', 49 | 'site.message.loggingOut': 'Podczas wylogowania...', 50 | 'site.name': 'r3analyse', 51 | }; 52 | -------------------------------------------------------------------------------- /src/components/FormFields/TextInput.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestUtils from 'react-addons-test-utils'; 3 | import TextInput from './TextInput'; 4 | 5 | const shallowRender = (component) => { 6 | const renderer = TestUtils.createRenderer(); 7 | 8 | renderer.render(component); 9 | 10 | return renderer.getRenderOutput(); 11 | }; 12 | 13 | const shallowRenderWithProps = (props = {}) => 14 | shallowRender(); 15 | 16 | describe('[FormField] TextInput', function textInputSpec() { 17 | beforeEach(() => { 18 | this.props = { 19 | field: {}, 20 | }; 21 | this.component = shallowRenderWithProps(this.props); 22 | }); 23 | 24 | it('Should render as
', () => { 25 | expect(this.component.type).to.equal('div'); 26 | }); 27 | it('Should have no placeholder', () => { 28 | const input = this.component.props.children[0]; 29 | 30 | expect(input.props.placeholder).to.equal(undefined); 31 | }); 32 | it('Should have text type', () => { 33 | const input = this.component.props.children[0]; 34 | 35 | expect(input.props.type).to.equal('text'); 36 | }); 37 | it('Should set class names correctly', () => { 38 | expect(this.component.props.className).to.equal('form-group'); 39 | }); 40 | it('Should have no children', () => { 41 | const secondChild = this.component.props.children[1]; 42 | 43 | expect(secondChild).to.equal(undefined); 44 | }); 45 | 46 | describe('With type', () => { 47 | const type = 'uluru'; 48 | 49 | beforeEach(() => { 50 | this.props.type = type; 51 | this.component = shallowRenderWithProps(this.props); 52 | }); 53 | 54 | it('Should set type', () => { 55 | const input = this.component.props.children[0]; 56 | 57 | expect(input.props.type).to.equal(type); 58 | }); 59 | }); 60 | 61 | describe('With placeholder', () => { 62 | const placeholder = 'holding place'; 63 | 64 | beforeEach(() => { 65 | this.props.placeholder = placeholder; 66 | this.component = shallowRenderWithProps(this.props); 67 | }); 68 | 69 | it('Should set placeholder', () => { 70 | const input = this.component.props.children[0]; 71 | 72 | expect(input.props.placeholder).to.equal(placeholder); 73 | }); 74 | }); 75 | 76 | describe('When field is invalid', () => { 77 | beforeEach(() => { 78 | this.props.field.invalid = true; 79 | this.component = shallowRenderWithProps(this.props); 80 | }); 81 | 82 | it('Should set class names correctly', () => { 83 | expect(this.component.props.className).to.equal('form-group has-error'); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /src/redux/modules/user/user-actions.js: -------------------------------------------------------------------------------- 1 | /* eslint no-undefined: 0 */ 2 | /* @flow */ 3 | import type { User } from '../../../declarations/app'; 4 | 5 | import { updateProfile } from '../../../api/user'; 6 | import { showSpinner, hideSpinner } from '../spinner/spinner'; 7 | 8 | export const SET_USER = '@@user/SET_USER'; 9 | export const CLEAR_USER = '@@user/CLEAR_USER'; 10 | export const UPDATE_USER_REQUEST = '@@user/UPDATE_USER_REQUEST'; 11 | export const UPDATE_USER_SUCCESS = '@@user/UPDATE_USER_SUCCESS'; 12 | export const UPDATE_USER_FAILURE = '@@user/UPDATE_USER_FAILURE'; 13 | export const LOCAL_STORAGE_KEY:string = 'redux:user'; 14 | 15 | type UserAction = { 16 | type: string; 17 | user?: ?User; 18 | }; 19 | 20 | const initialUser: User = { 21 | userId: '', 22 | name: '', 23 | givenName: '', 24 | familyName: '', 25 | nickname: '', 26 | picture: '', 27 | email: '', 28 | emailVerified: false, 29 | roles: [], 30 | createdAt: '', 31 | updatedAt: '', 32 | locale: '', 33 | }; 34 | 35 | const persistUser = (user: ?User) => { 36 | localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(user)); 37 | }; 38 | 39 | export const getUser = (): ?User => { 40 | const storedUser = localStorage.getItem(LOCAL_STORAGE_KEY); 41 | let user: ?User; 42 | 43 | if (storedUser) { 44 | user = JSON.parse(storedUser); 45 | } else { 46 | user = initialUser; 47 | } 48 | 49 | return user; 50 | }; 51 | 52 | export const setUser = (user: ?User): UserAction => { 53 | persistUser(user); 54 | 55 | return { 56 | type: SET_USER, 57 | user, 58 | }; 59 | }; 60 | 61 | export const clearUser = (): UserAction => { 62 | persistUser(null); 63 | 64 | return { 65 | type: CLEAR_USER, 66 | user: undefined, 67 | }; 68 | }; 69 | 70 | export const updateUserRequest = (): UserAction => ({ 71 | type: UPDATE_USER_REQUEST, 72 | }); 73 | 74 | export const updateUserSuccess = (user: User): UserAction => ({ 75 | type: UPDATE_USER_SUCCESS, 76 | user, 77 | }); 78 | 79 | export const updateUserFailure = (): UserAction => ({ 80 | type: UPDATE_USER_FAILURE, 81 | }); 82 | 83 | export const updateUser = (user: User): Function => dispatch => { 84 | dispatch({ 85 | type: UPDATE_USER_REQUEST, 86 | user, 87 | }); 88 | 89 | dispatch(showSpinner('profile.message.updatingUserDetails')); 90 | 91 | updateProfile(user).then( 92 | response => { 93 | dispatch(updateUserSuccess(response)); 94 | dispatch(hideSpinner()); 95 | dispatch(setUser(response)); 96 | }, 97 | () => { 98 | dispatch(updateUserFailure()); 99 | dispatch(hideSpinner()); 100 | } 101 | ); 102 | }; 103 | -------------------------------------------------------------------------------- /src/components/FormFields/HorizontalRadioGroup.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestUtils from 'react-addons-test-utils'; 3 | import HorizontalRadioGroup from './HorizontalRadioGroup'; 4 | 5 | const shallowRender = (component) => { 6 | const renderer = TestUtils.createRenderer(); 7 | 8 | renderer.render(component); 9 | 10 | return renderer.getRenderOutput(); 11 | }; 12 | 13 | const shallowRenderWithProps = (props = {}) => 14 | shallowRender(); 15 | 16 | describe('[FormField] HorizontalRadioGroup', function horizontalRadioGroupSpec() { 17 | beforeEach(() => { 18 | this.props = { 19 | field: { 20 | name: 'Home town', 21 | value: 'perth', 22 | }, 23 | values: [ 24 | { 25 | label: 'Perth', 26 | value: 'perth', 27 | }, 28 | { 29 | label: 'Melbourne', 30 | value: 'melbourne', 31 | }, 32 | { 33 | label: 'Sydney', 34 | value: 'sydney', 35 | }, 36 | ], 37 | }; 38 | this.component = shallowRenderWithProps(this.props); 39 | }); 40 | 41 | it('Should render as
', () => { 42 | expect(this.component.type).to.equal('div'); 43 | }); 44 | 45 | it('Should render correct number of children', () => { 46 | expect(this.component.props.children.length).to.equal(this.props.values.length); 47 | }); 48 | 49 | it('Should render label correctly for 2nd item', () => { 50 | const index = 1; 51 | const child = this.component.props.children[index]; 52 | const label = child.props.children[1]; 53 | 54 | expect(label).to.equal(this.props.values[index].label); 55 | }); 56 | 57 | it('Should set value correctly for 3rd item', () => { 58 | const index = 2; 59 | const child = this.component.props.children[index]; 60 | const input = child.props.children[0]; 61 | const value = input.props.value; 62 | 63 | expect(value).to.equal(this.props.values[index].value); 64 | }); 65 | 66 | it('Should set checked correctly for 1st item', () => { 67 | const index = 0; 68 | const child = this.component.props.children[index]; 69 | const input = child.props.children[0]; 70 | const checked = input.props.checked; 71 | const value = input.props.value; 72 | const expectedChecked = (value === this.props.field.value); 73 | 74 | expect(checked).to.equal(expectedChecked); 75 | }); 76 | 77 | it('Should set checked correctly for 2nd item', () => { 78 | const index = 1; 79 | const child = this.component.props.children[index]; 80 | const input = child.props.children[0]; 81 | const checked = input.props.checked; 82 | const value = input.props.value; 83 | const expectedChecked = (value === this.props.field.value); 84 | 85 | expect(checked).to.equal(expectedChecked); 86 | }); 87 | 88 | it('Should set checked correctly for 3rd item', () => { 89 | const index = 2; 90 | const child = this.component.props.children[index]; 91 | const input = child.props.children[0]; 92 | const checked = input.props.checked; 93 | const value = input.props.value; 94 | const expectedChecked = (value === this.props.field.value); 95 | 96 | expect(checked).to.equal(expectedChecked); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /src/redux/modules/auth/auth-actions.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { getProfile } from '../../../api/user'; 4 | import { setUser, clearUser } from '../user/user-actions'; 5 | import { showSpinner, hideSpinner } from '../spinner/spinner'; 6 | 7 | export const LOGIN_REQUEST = '@@auth/LOGIN_REQUEST'; 8 | export const LOGIN_SUCCESS = '@@auth/LOGIN_SUCCESS'; 9 | export const LOGIN_FAILURE = '@@auth/LOGIN_FAILURE'; 10 | export const LOGOUT_REQUEST = '@@auth/LOGOUT_REQUEST'; 11 | export const LOGOUT_SUCCESS = '@@auth/LOGOUT_SUCCESS'; 12 | export const LOCAL_STORAGE_KEY = 'redux:auth'; 13 | 14 | type AuthState = { 15 | isLoading: boolean; 16 | isAuthenticated: boolean; 17 | isAdmin: boolean; 18 | token: ?string; 19 | }; 20 | 21 | type AuthAction = { 22 | type: string; 23 | state: ?AuthState; 24 | }; 25 | 26 | const initialState = { 27 | isLoading: true, 28 | isAuthenticated: false, 29 | isAdmin: false, 30 | token: null, 31 | }; 32 | 33 | const loginRequestAction: AuthAction = { 34 | type: LOGIN_REQUEST, 35 | state: initialState, 36 | }; 37 | 38 | const persistState = (state: ?AuthState) => { 39 | localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(state)); 40 | }; 41 | 42 | export const getState = (): AuthState => { 43 | const storedState = localStorage.getItem(LOCAL_STORAGE_KEY); 44 | let state: ?AuthState; 45 | 46 | if (storedState) { 47 | state = JSON.parse(storedState); 48 | } else { 49 | state = initialState; 50 | } 51 | 52 | return state; 53 | }; 54 | 55 | export const loginSuccess = (): AuthAction => { 56 | const state = { 57 | isLoading: false, 58 | isAuthenticated: true, 59 | isAdmin: true, 60 | token: 'eyJ0eXAasdfiOi', 61 | }; 62 | 63 | persistState(state); 64 | 65 | return { 66 | type: LOGIN_SUCCESS, 67 | state, 68 | }; 69 | }; 70 | 71 | export const loginFailure = (): AuthAction => { 72 | persistState(initialState); 73 | 74 | return { 75 | type: LOGIN_FAILURE, 76 | state: initialState, 77 | }; 78 | }; 79 | 80 | export const loginRequest = (): Function => 81 | // Returning a function works because `redux-thunk` middleware is installed: 82 | // https://github.com/gaearon/redux-thunk 83 | // See `configure-store.js`. 84 | dispatch => { 85 | dispatch(loginRequestAction); 86 | dispatch(showSpinner('site.message.loggingIn')); 87 | 88 | getProfile().then( 89 | response => { 90 | // insert a short delay to simulate service call delay - remove in real application 91 | setTimeout(() => { 92 | dispatch(loginSuccess(response)); 93 | dispatch(hideSpinner()); 94 | dispatch(setUser(response)); 95 | }, 700); 96 | }, 97 | () => { 98 | dispatch(loginFailure()); 99 | dispatch(hideSpinner()); 100 | dispatch(clearUser()); 101 | } 102 | ); 103 | }; 104 | 105 | export const logoutRequest = (): Function => dispatch => { 106 | dispatch({ 107 | type: LOGOUT_REQUEST, 108 | }); 109 | dispatch(showSpinner('site.message.loggingOut')); 110 | 111 | // insert a short delay to simulate service call delay - remove in real application 112 | setTimeout(() => { 113 | persistState(initialState); 114 | dispatch(clearUser()); 115 | dispatch(hideSpinner()); 116 | dispatch({ 117 | type: LOGOUT_SUCCESS, 118 | state: initialState, 119 | }); 120 | }, 700); 121 | }; 122 | -------------------------------------------------------------------------------- /src/styles/vendor/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | html { 3 | font-family: sans-serif; 4 | -webkit-text-size-adjust: 100%; 5 | -ms-text-size-adjust: 100%; 6 | } 7 | body { 8 | margin: 0; 9 | } 10 | article, 11 | aside, 12 | details, 13 | figcaption, 14 | figure, 15 | footer, 16 | header, 17 | hgroup, 18 | main, 19 | menu, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | audio, 26 | canvas, 27 | progress, 28 | video { 29 | display: inline-block; 30 | vertical-align: baseline; 31 | } 32 | audio:not([controls]) { 33 | display: none; 34 | height: 0; 35 | } 36 | [hidden], 37 | template { 38 | display: none; 39 | } 40 | a { 41 | background-color: transparent; 42 | } 43 | a:active, 44 | a:hover { 45 | outline: 0; 46 | } 47 | abbr[title] { 48 | border-bottom: 1px dotted; 49 | } 50 | b, 51 | strong { 52 | font-weight: bold; 53 | } 54 | dfn { 55 | font-style: italic; 56 | } 57 | h1 { 58 | margin: .67em 0; 59 | font-size: 2em; 60 | } 61 | mark { 62 | color: #000; 63 | background: #ff0; 64 | } 65 | small { 66 | font-size: 80%; 67 | } 68 | sub, 69 | sup { 70 | position: relative; 71 | font-size: 75%; 72 | line-height: 0; 73 | vertical-align: baseline; 74 | } 75 | sup { 76 | top: -.5em; 77 | } 78 | sub { 79 | bottom: -.25em; 80 | } 81 | img { 82 | border: 0; 83 | } 84 | svg:not(:root) { 85 | overflow: hidden; 86 | } 87 | figure { 88 | margin: 1em 40px; 89 | } 90 | hr { 91 | height: 0; 92 | -webkit-box-sizing: content-box; 93 | -moz-box-sizing: content-box; 94 | box-sizing: content-box; 95 | } 96 | pre { 97 | overflow: auto; 98 | } 99 | code, 100 | kbd, 101 | pre, 102 | samp { 103 | font-family: monospace, monospace; 104 | font-size: 1em; 105 | } 106 | button, 107 | input, 108 | optgroup, 109 | select, 110 | textarea { 111 | margin: 0; 112 | font: inherit; 113 | color: inherit; 114 | } 115 | button { 116 | overflow: visible; 117 | } 118 | button, 119 | select { 120 | text-transform: none; 121 | } 122 | button, 123 | html input[type="button"], 124 | input[type="reset"], 125 | input[type="submit"] { 126 | -webkit-appearance: button; 127 | cursor: pointer; 128 | } 129 | button[disabled], 130 | html input[disabled] { 131 | cursor: default; 132 | } 133 | button::-moz-focus-inner, 134 | input::-moz-focus-inner { 135 | padding: 0; 136 | border: 0; 137 | } 138 | input { 139 | line-height: normal; 140 | } 141 | input[type="checkbox"], 142 | input[type="radio"] { 143 | -webkit-box-sizing: border-box; 144 | -moz-box-sizing: border-box; 145 | box-sizing: border-box; 146 | padding: 0; 147 | } 148 | input[type="number"]::-webkit-inner-spin-button, 149 | input[type="number"]::-webkit-outer-spin-button { 150 | height: auto; 151 | } 152 | input[type="search"] { 153 | -webkit-box-sizing: content-box; 154 | -moz-box-sizing: content-box; 155 | box-sizing: content-box; 156 | -webkit-appearance: textfield; 157 | } 158 | input[type="search"]::-webkit-search-cancel-button, 159 | input[type="search"]::-webkit-search-decoration { 160 | -webkit-appearance: none; 161 | } 162 | fieldset { 163 | padding: .35em .625em .75em; 164 | margin: 0 2px; 165 | border: 1px solid #c0c0c0; 166 | } 167 | legend { 168 | padding: 0; 169 | border: 0; 170 | } 171 | textarea { 172 | overflow: auto; 173 | } 174 | optgroup { 175 | font-weight: bold; 176 | } 177 | table { 178 | border-spacing: 0; 179 | border-collapse: collapse; 180 | } 181 | td, 182 | th { 183 | padding: 0; 184 | } 185 | -------------------------------------------------------------------------------- /src/_BIN/pages/module/module.jade: -------------------------------------------------------------------------------- 1 | .container-fluid 2 | .row 3 | .col-md-6: .well 4 | table(width="100%"): tbody: tr 5 | td: a.btn.btn-success.disabled(href="#module/#{module.uid}")= module.id 6 | td: pre: code= module.name.split("!").join("\n") 7 | .col-md-3: .well 8 | h4 time 9 | if module.time 10 | code= module.time + "ms" 11 | if module.timestamp 12 | = " finished @ " 13 | code= module.timestamp + "ms" 14 | else 15 | | Compile with --profile. 16 | .col-md-3: .well 17 | h4 size 18 | = require("../../formatSize")(module.size) 19 | .row 20 | .col-md-3: .well 21 | h4 flags 22 | if module.built 23 | span.label.label-success built 24 | = " " 25 | if !module.cacheable 26 | span.label.label-warning not cacheable 27 | = " " 28 | if module.prefetched 29 | span.label.label-success prefetched 30 | = " " 31 | if module.failed 32 | span.label.label-danger failed 33 | = " " 34 | if module.warnings 35 | span.label.label-warning= module.warnings + " warnings" 36 | = " " 37 | if module.errors 38 | span.label.label-danger= module.errors + " errors" 39 | .col-md-3: .well 40 | h4 chunks 41 | each chunk in module.chunks 42 | a.btn.btn-info(href="#chunk/#{chunk}")= chunk 43 | .col-md-6: .well 44 | if issuer 45 | h4 issuer 46 | table(width="100%"): tbody: tr 47 | td 48 | if typeof issuer.uid === "number" 49 | a.btn.btn-success(href="#module/#{issuer.uid}")= issuer.id 50 | else 51 | span.btn.btn-success= issuer.id 52 | td: pre: code= issuer.name.split("!").join("\n") 53 | .row 54 | .col-md-12: .well 55 | h4 reasons 56 | table.table.table-condensed 57 | thead 58 | tr 59 | th type 60 | th(colspan=2) module 61 | th user request 62 | th location 63 | tbody 64 | each reason in module.reasons 65 | tr 66 | td= reason.type 67 | td(style="width: 1px") 68 | if typeof reason.moduleUid === "number" 69 | a.btn.btn-success(href="#module/#{reason.moduleUid}")= reason.moduleId 70 | else 71 | span.btn.btn-success= reason.moduleId 72 | td: if reason.module 73 | pre: code= reason.module.split("!").join("\n") 74 | td: if reason.userRequest 75 | pre: code= reason.userRequest.split("!").join("\n") 76 | td 77 | if reason.loc 78 | code= reason.loc 79 | .row 80 | .col-md-12: .well 81 | h4 dependencies 82 | table.table 83 | thead 84 | tr 85 | th type 86 | th(colspan=2) module 87 | th user request 88 | th location 89 | tbody 90 | each dependency in module.dependencies 91 | tr 92 | td= dependency.type 93 | td(style="width: 1px") 94 | if typeof dependency.moduleUid === "number" 95 | a.btn.btn-success(href="#module/#{dependency.moduleUid}")= dependency.moduleId 96 | else 97 | span.btn.btn-success= dependency.moduleId 98 | td: if dependency.module 99 | pre: code= dependency.module.split("!").join("\n") 100 | td: if dependency.userRequest 101 | pre: code= dependency.userRequest.split("!").join("\n") 102 | td 103 | if dependency.loc 104 | code= dependency.loc 105 | .row 106 | .col-md-12 107 | pre: code= module.source -------------------------------------------------------------------------------- /src/_BIN/pages/hints/hints.jade: -------------------------------------------------------------------------------- 1 | .container-fluid 2 | .row 3 | .col-md-12 4 | if multiChunks.length === 0 && multiRefs.length === 0 && longChains.length === 0 5 | h2 Everything seem to be fine. 6 | if multiChunks.length > 0 7 | h2 Module in multiple chunks 8 | p Check if it is a good idea to move modules into a common parent. You may want to use require.include or insert them into the parents require.ensure array. 9 | table.table.table-bordered 10 | thead 11 | tr 12 | th module 13 | th name 14 | th count 15 | th chunks 16 | th saving 17 | tbody 18 | for item in multiChunks 19 | tr 20 | td 21 | if typeof item.module.uid === "number" 22 | a.btn.btn-success(href="#module/#{item.module.uid}")= item.module.id 23 | else 24 | span.btn.btn-success= item.module.id 25 | splittedModule = item.module.name.split("!") 26 | td: pre: code= splittedModule.join("\n") 27 | td= item.count 28 | td 29 | for chunk, idx in item.module.chunks 30 | if idx > 0 31 | = " " 32 | a.btn.btn-info(href="#chunk/#{chunk}")= chunk 33 | td= require("../../formatSize")(item.saving); 34 | if multiRefs.length > 0 35 | h2 Multiple references to the same module 36 | p Refactor this: 37 | pre: code. 38 | var a = require("xyz").a; 39 | var b = require("xyz").b; 40 | var c = require("xyz").c; 41 | p To this: 42 | pre: code. 43 | var xyz = require("xyz"); 44 | var a = xyz.a; 45 | var a = xyz.b; 46 | var c = xyz.c; 47 | table.table.table-bordered 48 | thead 49 | tr 50 | th module 51 | th name 52 | th count 53 | th referenced module 54 | th referenced name 55 | th saving 56 | tbody 57 | for item in multiRefs 58 | tr 59 | td 60 | if typeof item.module.uid === "number" 61 | a.btn.btn-success(href="#module/#{item.module.uid}")= item.module.id 62 | else 63 | span.btn.btn-success= item.module.id 64 | td: pre: code= item.module.name.split("!").join("\n") 65 | td= item.count 66 | td 67 | if typeof item.otherModule.uid === "number" 68 | a.btn.btn-success(href="#module/#{item.otherModule.uid}")= item.otherModule.id 69 | else 70 | span.btn.btn-success= item.otherModule.id 71 | td: pre: code= item.otherModule.name.split("!").join("\n") 72 | td= require("../../formatSize")(item.saving) + "+" 73 | if longChains.length > 0 74 | h2 Long module build chains 75 | p Use prefetching to increase build performance. Prefetch a module from the middle of the chain. 76 | for chain in longChains 77 | table.table.table-bordered 78 | thead 79 | tr 80 | th module 81 | th name 82 | th time 83 | th finished @ 84 | tbody 85 | for module in chain 86 | tr 87 | td 88 | if typeof module.uid === "number" 89 | a.btn.btn-success(href="#module/#{module.uid}")= module.id 90 | else 91 | span.btn.btn-success= module.id 92 | td: pre: code= module.name.split("!").join("\n") 93 | td= module.time + " ms" 94 | td= module.timestamp + " ms" 95 | -------------------------------------------------------------------------------- /src/_BIN/pages/chunk/chunk.jade: -------------------------------------------------------------------------------- 1 | .container-fluid 2 | .row 3 | .col-md-4: .well 4 | h4 chunk id 5 | = id 6 | .col-md-4: .well 7 | h4 size 8 | = require("../../formatSize")(chunk.size) 9 | .col-md-4: .well 10 | h4 names 11 | each name in chunk.names 12 | code= name 13 | .row 14 | if chunk.parents.length && chunk.children.length 15 | .col-md-3: .well 16 | h4 parents 17 | each parent in chunk.parents 18 | a.btn.btn-info(href="#chunk/#{parent}")= parent 19 | = " " 20 | .col-md-3: .well 21 | h4 children 22 | each child in chunk.children 23 | a.btn.btn-info(href="#chunk/#{child}")= child 24 | = " " 25 | else if chunk.parents.length 26 | .col-md-6: .well 27 | h4 parents 28 | each parent in chunk.parents 29 | a.btn.btn-info(href="#chunk/#{parent}")= parent 30 | = " " 31 | else if chunk.children.length 32 | .col-md-6: .well 33 | h4 children 34 | each child in chunk.children 35 | a.btn.btn-info(href="#chunk/#{child}")= child 36 | = " " 37 | .col-md-6: .well 38 | h4 files 39 | each file in chunk.files 40 | a.btn.btn-default(href="#assets")= file 41 | = " " 42 | .row 43 | .col-md-12: .well 44 | h4 origins 45 | table.table.table-condensed 46 | thead 47 | tr 48 | th reasons 49 | th name 50 | th(colspan="2") module 51 | th location 52 | tbody 53 | each origin in chunk.origins 54 | tr 55 | td 56 | each reason in origin.reasons 57 | code= reason 58 | = " " 59 | td: code= origin.name 60 | td 61 | if typeof origin.moduleUid === "number" 62 | a.btn.btn-success(href="#module/#{origin.moduleUid}")= origin.moduleId 63 | else 64 | span.btn.btn-success= origin.moduleId 65 | td: pre: code= origin.moduleName.split("!").join("\n"); 66 | td 67 | if origin.loc 68 | code= origin.loc 69 | if chunk.modules && chunk.modules.length > 0 70 | .row: .col-md-12: .well 71 | h4 modules 72 | table.table.table-condensed 73 | thead 74 | tr 75 | th id 76 | th name 77 | th size 78 | th chunks 79 | th flags 80 | tbody 81 | each module in chunk.modules 82 | tr 83 | td 84 | if typeof module.uid === "number" 85 | a.btn.btn-success(href="#module/#{module.uid}")= module.id 86 | else 87 | span.btn.btn-success= module.id 88 | td: pre: code= module.name.split("!").join("\n") 89 | td= require("../../formatSize")(module.size) 90 | td 91 | each chunk in module.chunks 92 | a.btn.btn-info(href="#chunk/#{chunk}")= chunk 93 | = " " 94 | td 95 | if module.built 96 | span.label.label-success built 97 | = " " 98 | if !module.cacheable 99 | span.label.label-warning not cacheable 100 | = " " 101 | if module.prefetched 102 | span.label.label-success prefetched 103 | = " " 104 | if module.failed 105 | span.label.label-danger failed 106 | = " " 107 | if module.warnings 108 | span.label.label-warning= module.warnings + " warnings" 109 | = " " 110 | if module.errors 111 | span.label.label-danger= module.errors + " errors" 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "analyse-tool", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A tool to analyse your webpack build result. It allows to browse the compilation and points out options to optimize the build.", 6 | "main": "index.js", 7 | "engines": { 8 | "node": ">=6.8.0", 9 | "npm": "^3.0.0" 10 | }, 11 | "scripts": { 12 | "build": "npm run dist:clean && npm run copy:static && npm run compile", 13 | "dist:clean": "rm -rf dist && mkdir dist", 14 | "copy:static": "cp -r src/static/* dist", 15 | "compile": "NODE_ENV=production webpack -p --bail", 16 | "dev": "node server", 17 | "dev:bs": "node server --with-browsersync", 18 | "flow": "flow check", 19 | "lint": "eslint . ./", 20 | "start": "npm run dev", 21 | "start:bs": "npm run dev:bs", 22 | "test": "npm run lint && npm run flow", 23 | "travis": "npm test" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/webpack/analyse-tool.git" 28 | }, 29 | "keywords": [ 30 | "webpack", 31 | "analyse" 32 | ], 33 | "author": "", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/webpack/analyse-tool/issues" 37 | }, 38 | "homepage": "https://github.com/webpack/analyse-tool#readme", 39 | "dependencies": { 40 | "axios": "^0.15.1", 41 | "classnames": "^2.2.5", 42 | "empty": "^0.10.1", 43 | "lodash.merge": "^4.6.0", 44 | "react": "^15.4.0-rc.4", 45 | "react-bootstrap": "^0.30.5", 46 | "react-dom": "^15.4.0-rc.4", 47 | "react-intl": "2.1.5", 48 | "react-redux": "^5.0.0-beta.3", 49 | "react-router": "^3.0.0-beta.1", 50 | "react-router-bootstrap": "^0.23.1", 51 | "react-router-redux": "^4.0.6", 52 | "redux": "^3.6.0", 53 | "redux-form": "^5.3.3", 54 | "redux-form-validation": "^0.0.8", 55 | "redux-logger": "^2.7.0", 56 | "redux-thunk": "^2.1.0", 57 | "sigma": "^1.1.0", 58 | "url": "^0.11.0" 59 | }, 60 | "devDependencies": { 61 | "babel-core": "^6.17.0", 62 | "babel-eslint": "^7.0.0", 63 | "babel-loader": "^6.2.5", 64 | "babel-plugin-add-module-exports": "^0.2.1", 65 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 66 | "babel-plugin-transform-runtime": "^6.15.0", 67 | "babel-preset-es2015": "^6.16.0", 68 | "babel-preset-react": "^6.16.0", 69 | "babel-preset-stage-0": "^6.16.0", 70 | "babel-register": "^6.16.3", 71 | "babel-runtime": "^6.11.6", 72 | "browser-sync": "^2.17.3", 73 | "chai": "^3.5.0", 74 | "connect-history-api-fallback": "^1.3.0", 75 | "core-decorators": "^0.13.0", 76 | "css-loader": "^0.25.0", 77 | "cssnano": "^3.7.7", 78 | "eslint": "3.8.0", 79 | "eslint-config-airbnb": "12.0.0", 80 | "eslint-loader": "^1.6.0", 81 | "eslint-plugin-babel": "^3.3.0", 82 | "eslint-plugin-flowtype": "^2.20.0", 83 | "eslint-plugin-import": "^2.0.1", 84 | "eslint-plugin-jsx-a11y": "^2.2.3", 85 | "eslint-plugin-react": "^6.4.1", 86 | "eventsource-polyfill": "^0.9.6", 87 | "express": "^5.0.0-alpha.2", 88 | "extract-text-webpack-plugin": "^2.0.0-beta.4", 89 | "file-loader": "^0.9.0", 90 | "flow-bin": "^0.33.0", 91 | "fs-extra": "^0.30.0", 92 | "html-webpack-plugin": "^2.22.0", 93 | "isparta-loader": "^2.0.0", 94 | "json-loader": "^0.5.4", 95 | "karma": "^1.3.0", 96 | "karma-chai": "^0.1.0", 97 | "karma-mocha": "^1.2.0", 98 | "karma-mocha-reporter": "^2.2.0", 99 | "karma-phantomjs-launcher": "^1.0.2", 100 | "karma-sinon": "^1.0.5", 101 | "karma-sourcemap-loader": "^0.3.7", 102 | "karma-webpack": "^1.8.0", 103 | "lodash.head": "^4.0.1", 104 | "lodash.tail": "^4.1.1", 105 | "mocha": "^3.1.2", 106 | "node-sass": "^3.10.1", 107 | "phantomjs-polyfill": "0.0.2", 108 | "phantomjs-prebuilt": "^2.1.13", 109 | "postcss-loader": "^1.0.0", 110 | "react-addons-test-utils": "^15.4.0-rc.4", 111 | "react-hot-loader": "^3.0.0-beta.6", 112 | "redbox-react": "^1.3.2", 113 | "redux-devtools": "^3.3.1", 114 | "redux-devtools-dock-monitor": "^1.1.1", 115 | "redux-devtools-log-monitor": "^1.0.12-alpha", 116 | "sass-loader": "^4.0.2", 117 | "sinon": "^2.0.0-pre.3", 118 | "style-loader": "^0.13.1", 119 | "url-loader": "^0.5.7", 120 | "webpack": "^2.1.0-beta.25", 121 | "webpack-dev-middleware": "^1.8.4", 122 | "webpack-hot-middleware": "^2.13.0" 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/_BIN/app.js: -------------------------------------------------------------------------------- 1 | exports.stats = null; 2 | exports.mapModules = null; 3 | exports.mapChunks = null; 4 | 5 | function load(stats) { 6 | stats.assets.sort(function (a, b) { 7 | return b.size - a.size; 8 | }); 9 | stats.modules.sort(function (a, b) { 10 | return a.id - b.id; 11 | }); 12 | const mapModules = {}; 13 | const mapModulesIdent = {}; 14 | const mapModulesUid = {}; 15 | stats.modules.forEach(function (module, idx) { 16 | mapModules[module.id] = module; 17 | mapModulesIdent[`$${ module.identifier }`] = module; 18 | mapModulesUid[module.uid = idx] = module; 19 | module.dependencies = []; 20 | }); 21 | const mapChunks = {}; 22 | stats.chunks.forEach(function (chunk) { 23 | mapChunks[chunk.id] = chunk; 24 | chunk.children = []; 25 | }); 26 | stats.modules.forEach(function (module) { 27 | module.reasons.forEach(function (reason) { 28 | const m = mapModulesIdent[`$${ reason.moduleIdentifier }`]; 29 | if (!m) { return; } 30 | reason.moduleUid = m.uid; 31 | m.dependencies.push({ 32 | type : reason.type, 33 | moduleId : module.id, 34 | moduleUid : module.uid, 35 | module : module.name, 36 | userRequest: reason.userRequest, 37 | loc : reason.loc, 38 | }); 39 | }); 40 | module.issuerUid = mapModulesIdent[`$${ module.issuer }`] && mapModulesIdent[`$${ module.issuer }`].uid; 41 | (function setTimestamp(module) { 42 | if (typeof module.timestamp === 'number') { return module.timestamp; } 43 | if (!module.profile) { return; } 44 | const factory = module.profile.factory || 0; 45 | const building = module.profile.building || 0; 46 | module.time = factory + building; 47 | if (!module.issuer) { return module.timestamp = module.time; } 48 | const issuer = mapModulesIdent[`$${ module.issuer }`]; 49 | if (!issuer) { return module.timestamp = NaN; } 50 | setTimestamp(issuer); 51 | module.timestamp = issuer.timestamp + module.time; 52 | }(module)); 53 | }); 54 | stats.chunks.forEach(function (chunk) { 55 | chunk.parents.forEach(function (parent) { 56 | const c = mapChunks[parent]; 57 | c.children.push(chunk.id); 58 | }); 59 | chunk.origins.forEach(function (origin) { 60 | const m = mapModulesIdent[`$${ origin.moduleIdentifier }`]; 61 | if (!m) { return; } 62 | origin.moduleUid = m.uid; 63 | }); 64 | }); 65 | stats.modules.forEach(function (module) { 66 | module.dependencies.sort(function (a, b) { 67 | if (!a.loc && !b.loc) { return 0; } 68 | if (!a.loc) { return 1; } 69 | if (!b.loc) { return -1; } 70 | a = a.loc.split(/[:-]/); 71 | b = b.loc.split(/[:-]/); 72 | if (+a[0] < +b[0]) { return -1; } 73 | if (+a[0] > +b[0]) { return 1; } 74 | if (+a[1] < +b[1]) { return -1; } 75 | if (+a[1] > +b[1]) { return 1; } 76 | return 0; 77 | }); 78 | }); 79 | let maxLength = 0; 80 | stats.assets.forEach(function (a) { 81 | if (a.name.length > maxLength) { maxLength = a.name.length; } 82 | }); 83 | stats.assets.forEach(function (a) { 84 | a.normalizedName = a.name; 85 | while (a.normalizedName.length < maxLength) { 86 | a.normalizedName = ` ${ a.normalizedName }`; 87 | } 88 | }); 89 | stats.assets.sort(function (a, b) { 90 | a = a.normalizedName; 91 | b = b.normalizedName; 92 | return a < b ? -1 : 1; 93 | }); 94 | exports.stats = stats; 95 | exports.mapChunks = mapChunks; 96 | exports.mapModules = mapModules; 97 | exports.mapModulesUid = mapModulesUid; 98 | exports.mapModulesIdent = mapModulesIdent; 99 | 100 | const ga = require('./googleAnalytics'); 101 | ga('set', 'dimension1', `${ categorize(stats.modules.length) }`); 102 | ga('set', 'dimension2', `${ categorize(stats.chunks.length) }`); 103 | ga('set', 'dimension3', `${ categorize(stats.assets.length) }`); 104 | ga('set', 'dimension4', `${ categorize(stats.time) }`); 105 | } 106 | 107 | exports.load = function (stats) { 108 | const isMultiCompile = !stats.assets && !stats.modules && stats.children && stats.children.length > 1; 109 | if (isMultiCompile) { 110 | exports.loadPage('select', stats); 111 | } else { 112 | load(stats); 113 | } 114 | }; 115 | 116 | function categorize(number) { 117 | if (number <= 0) { return 0; } 118 | let factor = 1; 119 | do { 120 | if (number <= 10) { return number * factor; } 121 | factor *= 10; 122 | number = Math.floor(number / 10); 123 | } while (number > 0); 124 | return ''; 125 | } 126 | -------------------------------------------------------------------------------- /src/containers/MainHeader/MainHeader.js: -------------------------------------------------------------------------------- 1 | /* eslint operator-linebreak: 0 */ 2 | import React, { PropTypes } from 'react'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { Link } from 'react-router'; 5 | import { LinkContainer } from 'react-router-bootstrap'; 6 | import { Navbar, Nav, NavItem } from 'react-bootstrap'; 7 | import { connect } from 'react-redux'; 8 | import { autobind } from 'core-decorators'; 9 | import debug from 'debug'; 10 | import UserDropdownMenu from '../../components/UserDropdownMenu/UserDropdownMenu'; 11 | import { loginRequest, logoutRequest } from '../../redux/modules/auth/auth-actions'; 12 | import LanguageSelectionDropdown from '../LanguageSelectionDropdown/LanguageSelectionDropdown'; 13 | import { links } from '../../shared/links'; 14 | 15 | if (__DEBUG__) { 16 | debug.enable('app:*'); 17 | } 18 | 19 | const log = debug('app:main-header'); 20 | 21 | class MainHeader extends React.Component { 22 | static displayName = 'MainHeader'; 23 | static propTypes = { 24 | dispatch: PropTypes.func, 25 | isAuthenticated: PropTypes.bool, 26 | user: PropTypes.object, 27 | }; 28 | 29 | @autobind 30 | onLogin() { 31 | this.props.dispatch(loginRequest()); 32 | } 33 | 34 | @autobind 35 | onLogout() { 36 | this.props.dispatch(logoutRequest()); 37 | } 38 | 39 | willReceiveProps(props) { 40 | log('main-header will receive props', props); 41 | } 42 | 43 | render() { 44 | const { user, isAuthenticated } = this.props; 45 | 46 | return ( 47 | 48 | 49 | 50 | 51 | 52 | { /* The above is equivalent to 53 | */ } 56 | 57 | 58 | 59 | 60 | 61 | 131 | 132 | 133 | ); 134 | } 135 | } 136 | 137 | const mapStateToProps = (state) => ({ 138 | isAuthenticated: state.auth.isAuthenticated, 139 | user: state.user, 140 | language: state.language, 141 | }); 142 | 143 | export default connect(mapStateToProps)(MainHeader); 144 | -------------------------------------------------------------------------------- /src/_BIN/Graphs/modules.js: -------------------------------------------------------------------------------- 1 | const app = require('../app'); 2 | const Sigma = require('sigma.js'); 3 | const findById = require('../../utils/findById'); 4 | const percentageToColor = require('../../utils/percentageToColor').greenRed; 5 | const percentageToColor2 = require('../../utils/percentageToColor').blue; 6 | 7 | const element = document.getElementById('sigma-modules'); 8 | 9 | const nodes = []; 10 | const edges = []; 11 | const moduleCount = app.stats.modules.length; 12 | const chunkCount = app.stats.chunks.length; 13 | let maxTimestamp = 0; 14 | let maxSize = 0; 15 | 16 | app.stats.modules.forEach(function(module, idx) { 17 | if (module.size > maxSize) { maxSize = module.size; } 18 | if (module.timestamp > maxTimestamp) { maxTimestamp = module.timestamp; } 19 | }); 20 | 21 | app.stats.modules.forEach(function(module, idx) { 22 | const color = percentageToColor(Math.pow((module.size + 1) / (maxSize + 1), 1 / 4)); 23 | const done = {}; 24 | const uniqueReasons = module.reasons.filter(function(reason) { 25 | const parent = reason.module; 26 | if (done[`$${ parent }`]) { return false; } 27 | done[`$${ parent }`] = true; 28 | return true; 29 | }); 30 | const uid = module.uid; 31 | nodes.push({ 32 | id : `module${ uid }`, 33 | uid : uid, 34 | moduleUid : uid, 35 | moduleId : module.id, 36 | module : module, 37 | type : 'webpack', 38 | size : module.size + 1, 39 | label : `[${ module.id }] ${ module.name }`, 40 | shortLabel : `${ module.id }`, 41 | x : Math.cos(idx / moduleCount * Math.PI * 2) * Math.sqrt(uniqueReasons.length + 1) * Math.sqrt(moduleCount), 42 | y : Math.sin(idx / moduleCount * Math.PI * 2) * Math.sqrt(uniqueReasons.length + 1) * Math.sqrt(moduleCount), 43 | originalColor: color, 44 | color : color, 45 | }); 46 | const edgeColor = typeof module.timestamp === 'number' ? percentageToColor2(module.timestamp / maxTimestamp) : undefined; 47 | uniqueReasons.forEach(function(reason) { 48 | const parentIdent = reason.moduleIdentifier; 49 | const parentModule = app.mapModulesIdent[`$${ parentIdent }`]; 50 | if (!parentModule) { return; } 51 | const weight = 1 / uniqueReasons.length / uniqueReasons.length; 52 | const async = !module.chunks.some(function(chunk) { 53 | return (function isInChunks(chunks, checked) { 54 | if (chunks.length === 0) { return false; } 55 | if (chunks.indexOf(chunk) >= 0) { return true; } 56 | chunks = chunks.filter(function(c) { 57 | return checked.indexOf(c) < 0; 58 | }); 59 | if (chunks.length === 0) { return false; } 60 | return chunks.some(function(c) { 61 | return isInChunks(app.mapChunks[c].parents, checked.concat(c)); 62 | }); 63 | }(parentModule.chunks, [])); 64 | }); 65 | edges.push({ 66 | id : `edge${ module.uid }-${ +parentModule.uid }`, 67 | sourceModuleUid: parentModule.uid, 68 | sourceModule : parentModule, 69 | source : `module${ parentModule.uid }`, 70 | targetModule : module, 71 | targetModuleUid: module.uid, 72 | target : `module${ module.uid }`, 73 | arrow : 'target', 74 | type : async ? 'dashedArrow' : 'arrow', 75 | lineDash : module.chunks.length === 0 ? [2] : [5], 76 | originalColor : edgeColor, 77 | color : edgeColor, 78 | size : weight, 79 | weight : async ? weight / 4 : weight, 80 | }); 81 | }); 82 | }); 83 | const s = new Sigma({ 84 | graph : { 85 | nodes: nodes, 86 | edges: edges, 87 | }, 88 | renderer: { 89 | type : 'canvas', 90 | container: element, 91 | }, 92 | settings: { 93 | edgeColor : 'target', 94 | maxNodeSize: 4, 95 | minNodeSize: 4, 96 | maxEdgeSize: 2, 97 | minEdgeSize: 0.05, 98 | }, 99 | }); 100 | 101 | let activeModuleUid = null; 102 | 103 | s.bind('clickNode', function(e) { 104 | if (e.data.node.moduleUid === activeModuleUid) { 105 | window.location.hash = '#modules'; 106 | } else { 107 | window.location.hash = `#module/${ e.data.node.moduleUid }`; 108 | } 109 | }); 110 | 111 | s.refresh(); 112 | 113 | exports.show = function () { 114 | element.style.display = 'block'; 115 | s.refresh(); 116 | s.startForceAtlas2(); 117 | s.renderers[0].resize(); 118 | }; 119 | 120 | exports.hide = function () { 121 | element.style.display = 'none'; 122 | s.stopForceAtlas2(); 123 | }; 124 | 125 | exports.setNormal = function () { 126 | activeModuleUid = null; 127 | s.graph.nodes().forEach(function (n) { 128 | n.color = n.originalColor; 129 | n.active = false; 130 | }); 131 | s.graph.edges().forEach(function (e) { 132 | e.color = e.originalColor; 133 | }); 134 | s.refresh(); 135 | }; 136 | 137 | exports.setActiveModule = function(activeModule) { 138 | activeModuleUid = activeModule; 139 | const colors = {}; 140 | const m = app.mapModulesUid[activeModule]; 141 | m.reasons.forEach(function (r) { 142 | colors[r.moduleUid] = '#ff0000'; 143 | }); 144 | m.dependencies.forEach(function (d) { 145 | colors[d.moduleUid] = '#00aa00'; 146 | }); 147 | colors[activeModule] = '#000000'; 148 | s.graph.nodes().forEach(function (n) { 149 | n.color = colors[n.moduleUid] || '#aaaaaa'; 150 | }); 151 | s.graph.edges().forEach(function (e) { 152 | if (e.targetModuleUid === activeModule) { 153 | e.color = '#ff0000'; 154 | } else if (e.sourceModuleUid === activeModule) { 155 | e.color = '#00aa00'; 156 | } else { 157 | e.color = '#aaaaaa'; 158 | } 159 | }); 160 | s.refresh(); 161 | }; 162 | 163 | exports.setActiveChunk = function (activeChunk) { 164 | activeModuleUid = null; 165 | s.graph.nodes().forEach(function (n) { 166 | const m = n.module; 167 | if (m.chunks.indexOf(activeChunk) >= 0) { 168 | n.color = '#000000'; 169 | } else { 170 | n.color = '#aaaaaa'; 171 | n.active = false; 172 | } 173 | }); 174 | s.graph.edges().forEach(function (e) { 175 | const sm = e.sourceModule; 176 | const tm = e.targetModule; 177 | const sc = sm.chunks.indexOf(activeChunk) >= 0; 178 | const tc = tm.chunks.indexOf(activeChunk) >= 0; 179 | 180 | if (sc && tc) { 181 | e.color = '#000000'; 182 | } else if (sc) { 183 | e.color = '#00aa00'; 184 | } else if (tc) { 185 | e.color = '#ff0000'; 186 | } else { 187 | e.color = '#aaaaaa'; 188 | } 189 | }); 190 | s.refresh(); 191 | }; 192 | -------------------------------------------------------------------------------- /src/containers/ProfileEditForm/ProfileEditForm.js: -------------------------------------------------------------------------------- 1 | /* eslint no-restricted-syntax: 0 */ 2 | /* @flow */ 3 | import React, { PropTypes, Element } from 'react'; 4 | import { Button, Checkbox, FormGroup, ControlLabel, FormControl, Row, Col } from 'react-bootstrap'; 5 | import { FormattedMessage, intlShape, injectIntl } from 'react-intl'; 6 | import { reduxForm } from 'redux-form'; 7 | import { autobind } from 'core-decorators'; 8 | import { generateValidation } from 'redux-form-validation'; 9 | 10 | import TextInput from '../../components/FormFields/TextInput'; 11 | import HorizontalRadioGroup from '../../components/FormFields/HorizontalRadioGroup'; 12 | import DropDown from '../../components/FormFields/DropDown'; 13 | import FormErrorMessages from '../../components/FormFields/FormErrorMessages'; 14 | import validations from './ProfileEditForm.validations'; 15 | 16 | import { messages } from './ProfileEditForm.i18n'; 17 | import styles from './ProfileEditForm.scss'; 18 | 19 | const MALE: string = 'male'; 20 | const FEMALE: string = 'female'; 21 | 22 | class ProfileEditForm extends React.Component { 23 | static displayName = 'ProfileEditForm'; 24 | static propTypes = { 25 | fields: PropTypes.object.isRequired, 26 | intl: intlShape.isRequired, 27 | values: PropTypes.object.isRequired, 28 | pristine: PropTypes.bool.isRequired, 29 | invalid: PropTypes.bool.isRequired, 30 | resetForm: PropTypes.func.isRequired, 31 | user: PropTypes.object.isRequired, 32 | handleUpdate: PropTypes.func.isRequired, 33 | }; 34 | 35 | @autobind 36 | onUpdateClick() { 37 | this.props.handleUpdate(Object.assign({}, this.props.user, this.props.values)); 38 | } 39 | 40 | @autobind 41 | onResetClick() { 42 | this.props.resetForm(); 43 | } 44 | 45 | isUpdateButtonDisabled(): boolean { 46 | return this.props.pristine || this.props.invalid; 47 | } 48 | 49 | isResetButtonDisabled(): boolean { 50 | return this.props.pristine; 51 | } 52 | 53 | render(): Element { 54 | const fields = Object.assign({}, this.props.fields); 55 | for (const prop in fields) { 56 | if ({}.hasOwnProperty.call(fields, prop)) { 57 | delete fields[prop].initialValue; 58 | delete fields[prop].autofill; 59 | delete fields[prop].onUpdate; 60 | delete fields[prop].valid; 61 | delete fields[prop].invalid; 62 | delete fields[prop].dirty; 63 | delete fields[prop].pristine; 64 | delete fields[prop].active; 65 | delete fields[prop].touched; 66 | delete fields[prop].visited; 67 | delete fields[prop].autofilled; 68 | } 69 | } 70 | 71 | const { givenName, familyName, nickname, email, emailVerified, age, gender, locale, notes } = fields; 72 | const { formatMessage } = this.props.intl; 73 | const genderValues = [ 74 | { 75 | label: formatMessage(messages.gender.male.label), 76 | value: MALE, 77 | }, 78 | { 79 | label: formatMessage(messages.gender.female.label), 80 | value: FEMALE, 81 | }, 82 | ]; 83 | 84 | // in a real app, the locales would be populated via service call: 85 | const locales: string[] = ['en-CA', 'en-US', 'en-GB', 'en-AU', 'es-ES', 'es-CR', 'es-NI']; 86 | 87 | return ( 88 |
89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | {formatMessage(messages.email.label)} 108 | 109 | 110 | 111 | 112 | 113 | {formatMessage(messages.emailVerified.label)} 114 | 115 | {formatMessage(messages.emailVerified.placeholder)} 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | {formatMessage(messages.age.placeholder)} 126 | 127 | 128 | 129 | 130 | 131 | {formatMessage(messages.gender.label)} 132 | 133 | 134 | 135 | 136 | 137 | 138 | {formatMessage(messages.notes.label)} 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |   153 | { 154 | // Need to include the preceding non-breaking space, because when React renders the HTML, 155 | // there is no gap in the markup between the buttons - which results in the buttons being 156 | // flush up against each other. 157 | } 158 | 165 | 166 | 167 |
168 |
169 | ); 170 | } 171 | } 172 | 173 | const reduxFormConfig: Object = { 174 | form: 'editProfile', 175 | ...generateValidation(validations), 176 | }; 177 | 178 | const mapStateToProps = (state, props) => ({ initialValues: props.user }); 179 | 180 | export default reduxForm(reduxFormConfig, mapStateToProps)(injectIntl(ProfileEditForm)); 181 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint operator-linebreak: 0 */ 2 | /* eslint comma-dangle: 0 */ 3 | const head = require('lodash.head'); 4 | const tail = require('lodash.tail'); 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 8 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 9 | const cssnano = require('cssnano'); 10 | const debug = require('debug'); 11 | // const config = require('./config'); 12 | 13 | debug.enable('app:*'); 14 | 15 | const log = debug('app:webpack'); 16 | 17 | // Environment 18 | const NODE_ENV = process.env.NODE_ENV || 'development'; 19 | const DEVELOPMENT = NODE_ENV === 'development'; 20 | const TESTING = NODE_ENV === 'test'; 21 | const PRODUCTION = NODE_ENV === 'production'; 22 | const __DEBUG__ = DEVELOPMENT; 23 | log(`Starting in ${ NODE_ENV } mode.`); 24 | 25 | // Webpack scss handling setup 26 | const loaderOrLoaders = DEVELOPMENT ? 'loader' : 'use'; 27 | log(`SCSS webpack ${ loaderOrLoaders } enabled.`); 28 | /* Any .scss file in ./src/... *except* those in ./src/styles/ 29 | * are local css modules. the class names and ids will be changed to: 30 | * [name]-[local]-[hash:base64:5] */ 31 | const devLoaders = { 32 | test: /\.scss$/, 33 | exclude: path.resolve(__dirname, 'src', 'styles'), 34 | include: [ 35 | path.resolve(__dirname, 'src', 'components'), 36 | path.resolve(__dirname, 'src', 'containers'), 37 | path.resolve(__dirname, 'src', 'pages') 38 | ], 39 | // include: /src\/(?!styles).+/, 40 | use: [ 41 | 'style', 42 | 'css?modules&sourceMap&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]', 43 | 'postcss', 44 | 'sass', 45 | ] 46 | }; 47 | const prodLoader = { 48 | test: /\.scss$/, 49 | exclude: path.resolve(__dirname, 'src', 'styles'), 50 | include: [ 51 | path.resolve(__dirname, 'src', 'components'), 52 | path.resolve(__dirname, 'src', 'containers'), 53 | path.resolve(__dirname, 'src', 'pages') 54 | ], 55 | // include: /src\/(?!styles).+/, 56 | loader: ExtractTextPlugin.extract( 57 | { fallbackLoader: 'style-loader', 58 | loader: 'css-loader?' + 59 | 'modules&sourceMap&importLoaders=1&localIdentName=[name]-[local]-[hash:base64:5]&sourceMap?' + 60 | 'postcss?' + 61 | 'sass?' + 62 | 'sourceMap?', 63 | } 64 | ) 65 | }; 66 | const scssLoader = DEVELOPMENT ? devLoaders : prodLoader; 67 | 68 | const devCompiler = { 69 | hash_type: 'hash', 70 | stats: { 71 | chunks: false, 72 | chunkModules: false, 73 | colors: true, 74 | }, 75 | }; 76 | const prodCompiler = { 77 | hash_type: 'chunkhash', 78 | stats: { 79 | chunks: true, 80 | chunkModules: true, 81 | colors: true, 82 | }, 83 | }; 84 | const webpackCompiler = DEVELOPMENT ? devCompiler : prodCompiler; 85 | 86 | const devTool = 'cheap-module-eval-source-map'; 87 | const prodTool = 'source-map'; 88 | const webpackDevTools = DEVELOPMENT ? devTool : prodTool; 89 | 90 | const devPublicPath = '/'; 91 | const prodPublicPath = '/'; 92 | const webpackPublicPath = DEVELOPMENT ? devPublicPath : prodPublicPath; 93 | 94 | // Webpack configuration 95 | log('Creating webpack configuration...'); 96 | const webpackconfig = { 97 | entry: { 98 | app: ['./src/app'], 99 | vendor: [ 100 | 'react', 101 | 'react-redux', 102 | 'react-router', 103 | 'react-intl', 104 | 'redux', 105 | 'redux-form', 106 | 'react-router-redux', 107 | 'react-bootstrap', 108 | 'react-router-bootstrap', 109 | ], 110 | }, 111 | 112 | output: { 113 | path: path.resolve(__dirname, 'dist'), 114 | filename: `[name].[${ webpackCompiler.hash_type }].js`, 115 | publicPath: webpackPublicPath, 116 | }, 117 | 118 | module: { 119 | rules: [ 120 | { 121 | test: /\.jsx?$/, 122 | loader: 'eslint', 123 | enforce: 'pre', 124 | exclude: path.resolve(__dirname, 'node_modules'), 125 | }, 126 | { 127 | test: /\.jsx?$/, 128 | loader: 'babel-loader', 129 | include: path.resolve(__dirname, 'src'), 130 | }, 131 | { 132 | test: /\.json$/, 133 | loader: 'json', 134 | }, 135 | scssLoader, 136 | /* Any .scss files in ./src/styles are treated as normal (not local) 137 | * sass files, and so class names and ids will remain as specified */ 138 | { 139 | test: /\.scss$/, 140 | include: path.resolve(__dirname, 'src', 'styles'), 141 | loader: 'style!css!postcss!sass', 142 | }, 143 | // File loaders 144 | /* eslint-disable */ 145 | { 146 | test: /\.woff(\?.*)?$/, 147 | loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff' 148 | }, 149 | { 150 | test: /\.woff2(\?.*)?$/, 151 | loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2' 152 | }, 153 | { 154 | test: /\.ttf(\?.*)?$/, 155 | loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream' 156 | }, 157 | { 158 | test: /\.eot(\?.*)?$/, 159 | loader: 'file?prefix=fonts/&name=[path][name].[ext]' 160 | }, 161 | { 162 | test: /\.svg(\?.*)?$/, 163 | loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' 164 | }, 165 | { 166 | test: /\.(png|jpg)$/, 167 | loader: 'url?limit=8192' 168 | }, 169 | /* eslint-enable */ 170 | ], 171 | }, 172 | 173 | devtool: webpackDevTools, 174 | 175 | resolve: { 176 | modules: [ 177 | 'node_modules', 178 | path.join(__dirname, 'src') 179 | ], 180 | 181 | extensions: ['.js', '.json', '.jsx', '.css', '.scss'], 182 | 183 | alias: { 184 | react: path.join(__dirname, 'node_modules', 'react'), 185 | }, 186 | }, 187 | 188 | plugins: [ 189 | new webpack.DefinePlugin({ DEVELOPMENT, PRODUCTION, __DEBUG__ }), 190 | new HtmlWebpackPlugin({ 191 | template: path.join(__dirname, 'src', 'index.html'), 192 | hash: true, 193 | favicon: path.join(__dirname, 'src', 'static', 'favicon.png'), 194 | filename: 'index.html', 195 | inject: 'body', 196 | minify: { 197 | collapseWhitespace: true, 198 | }, 199 | }), 200 | new webpack.LoaderOptionsPlugin({ 201 | test: /\.xxx$/, // may apply this only for some modules 202 | options: { 203 | postcss: [ 204 | cssnano({ 205 | sourcemap: true, 206 | autoprefixer: { 207 | add: true, 208 | remove: true, 209 | browsers: ['last 2 versions'], 210 | }, 211 | safe: true, 212 | discardComments: { 213 | removeAll: true, 214 | }, 215 | }), 216 | ], 217 | sassLoader: { 218 | resolveLoader: path.join(__dirname, 'src', 'styles'), 219 | }, 220 | eslint: { 221 | configFile: path.join(__dirname, '.eslintrc'), 222 | }, 223 | } 224 | }) 225 | ], 226 | }; 227 | 228 | if (!TESTING) { 229 | webpackconfig.plugins.push(new webpack.optimize.CommonsChunkPlugin({ 230 | names: ['vendor'], 231 | })); 232 | } 233 | 234 | if (DEVELOPMENT) { 235 | log('Extending webpack configuration with development settings.'); 236 | 237 | log('Adding HMR entry points.'); 238 | webpackconfig.entry.app.unshift( 239 | 'webpack-hot-middleware/client', 240 | 'react-hot-loader/patch' 241 | ); 242 | 243 | log('Enable development plugins (HMR, NoErrors).'); 244 | webpackconfig.plugins.push( 245 | new webpack.HotModuleReplacementPlugin(), 246 | new webpack.NoErrorsPlugin() 247 | ); 248 | } 249 | 250 | if (PRODUCTION) { 251 | log('Extending webpack configuration with production settings.'); 252 | 253 | log('Add uglify and dedupe plugins.'); 254 | webpackconfig.plugins.push( 255 | new webpack.optimize.UglifyJsPlugin({ 256 | sourceMap: true, 257 | compress: { 258 | unused: true, 259 | dead_code: true, 260 | warnings: false, 261 | }, 262 | }), 263 | new webpack.optimize.DedupePlugin() 264 | ); 265 | 266 | log('Apply ExtractTextPlugin to CSS loaders.'); 267 | webpackconfig.module.rules.filter(loader => 268 | loader.use && loader.use.find(name => /css/.test(name.split('?')[0])) 269 | ).forEach(loader => { 270 | /* eslint-disable */ 271 | const first = head(loader.use); 272 | const rest = tail(loader.use); 273 | loader.loader = ExtractTextPlugin.extract(first, rest.join('!')); 274 | delete loader.use; 275 | /* eslint-enable */ 276 | }); 277 | webpackconfig.plugins.push( 278 | new ExtractTextPlugin('[name].[contenthash].css') 279 | ); 280 | } 281 | 282 | module.exports = webpackconfig; 283 | -------------------------------------------------------------------------------- /src/styles/vendor/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | --------------------------------------------------------------------------------