├── .babelrc ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── config ├── default.json ├── development.json ├── index.js └── production.json ├── index.js ├── package.json ├── server ├── index.js └── render.js ├── src ├── actions │ ├── data.js │ ├── files.jsx │ └── users.js ├── apiClient.jsx ├── assets │ ├── css │ │ ├── animation.css │ │ ├── fontello-codes.css │ │ ├── fontello-embedded.css │ │ ├── fontello-ie7-codes.css │ │ ├── fontello-ie7.css │ │ ├── fontello.css │ │ ├── tomorrow-night.css │ │ └── video.css │ ├── font │ │ ├── fontello.eot │ │ ├── fontello.svg │ │ ├── fontello.ttf │ │ ├── fontello.woff │ │ └── fontello.woff2 │ ├── img │ │ ├── Video.jpg │ │ ├── dropicon.png │ │ ├── facebook.svg │ │ ├── github.svg │ │ ├── home-cloud.svg │ │ ├── home-code.svg │ │ ├── home-laptop.svg │ │ ├── home-web.svg │ │ ├── menulogo.png │ │ └── twitter.svg │ ├── js │ │ ├── dropzone.js │ │ └── homejs │ ├── st.less │ ├── sty.css │ └── style.css ├── client.js ├── components │ ├── DevTools.jsx │ ├── HtmlDocument.jsx │ ├── Root.dev.jsx │ ├── Root.jsx │ ├── Root.prod.jsx │ ├── components │ │ ├── Header.jsx │ │ ├── XScript.jsx │ │ ├── file │ │ │ └── CommentsBar.jsx │ │ ├── home │ │ │ ├── HomeAbout.jsx │ │ │ ├── HomeCode.jsx │ │ │ ├── HomeDownload.jsx │ │ │ ├── HomeUpload.jsx │ │ │ ├── HomeWeb.jsx │ │ │ └── loader.jsx │ │ ├── icons │ │ │ ├── IconDownloadMac.jsx │ │ │ ├── IconDownloadWin.jsx │ │ │ ├── IconHomeCloud.jsx │ │ │ ├── IconHomeCode.jsx │ │ │ ├── IconHomeLaptop.jsx │ │ │ ├── IconHomeWeb.jsx │ │ │ └── IconLogo.jsx │ │ ├── list │ │ │ └── file.jsx │ │ ├── login │ │ │ ├── login.jsx │ │ │ └── signup.jsx │ │ └── viewers │ │ │ ├── Code.jsx │ │ │ ├── Default.jsx │ │ │ ├── Image.jsx │ │ │ ├── Pdf.jsx │ │ │ ├── Text.jsx │ │ │ ├── Url.jsx │ │ │ └── Video.jsx │ └── pages │ │ ├── App.jsx │ │ ├── Downloads.jsx │ │ ├── File.jsx │ │ ├── Home.jsx │ │ ├── List.jsx │ │ ├── Login.jsx │ │ └── NotFound.jsx ├── constants.js ├── reducers │ ├── app.js │ ├── data.js │ ├── files.js │ ├── index.js │ └── users.js ├── routes.js ├── server.js └── store │ ├── composeStore.js │ ├── configureStore.dev.js │ ├── configureStore.js │ ├── configureStore.prod.js │ └── middlewares.js ├── webpack.config.js └── webpack ├── config.js ├── dev.config.js ├── prod.config.js ├── server.js └── utils ├── mkdirs.js ├── notify-stats.js └── write-stats.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | *.log 4 | .idea 5 | !config/default.json 6 | !config/travis.json 7 | coverage/ 8 | public/build/ 9 | server/build/ 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6 2 | 3 | RUN mkdir -p /usr/src/app 4 | WORKDIR /usr/src/app 5 | 6 | # Install app dependencies 7 | COPY ./package.json /usr/src/app/ 8 | RUN npm install 9 | 10 | # Bundle app source 11 | COPY . /usr/src/app 12 | 13 | RUN npm run build 14 | 15 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Tommy Chen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dripr UI 2 | https://dripr.io 3 | Dripr is a screenshot/file sharing platform, similar to cloudapp. 4 | This is a universal (isomorphic) web application usign some cool features. 5 | 6 | ## What's under the hood? 7 | Basically a React app which connects to some APIs (node.js). When a file is uploaded it gets stored in Amazon AWS s3, then there's some Amazon AWS Lambda running to do some tasks, like resizing an image for example. In front of s3 there is cloudfront (cdn). 8 | 9 | There's also a desktop app where you can see your uploaded files and take screenshots which will be directly uploaded. 10 | 11 | ## Features 12 | - Upload Images 13 | - Upload Videos 14 | - Upload Code (Paste) 15 | - Upload a Link, a redirect will be made, so you can keep track of the visits 16 | - Upload text, text will be displayed in a nice way 17 | - Login (via facebook) to store everything you upload 18 | 19 | ## We used 20 | 21 | - [React] 22 | - [Redux] 23 | - [React Router] 24 | - [Babel] 25 | - [Webpack] 26 | 27 | ## Usage 28 | 29 | ** Copy configuration files ** 30 | 31 | Copy /config/default.json to /config/development.json. Change it with the desired values 32 | 33 | **Start the development server** 34 | 35 | The server will run on [localhost:4000](http://localhost:4000). 36 | 37 | ``` bash 38 | $ npm run dev 39 | ``` 40 | 41 | or 42 | 43 | ``` bash 44 | $ npm run devw 45 | ``` 46 | 47 | on Windows 48 | 49 | 50 | **Build source files** 51 | 52 | Compiles JavaScript files with JavaScript and extracts CSS files from JavaScript. 53 | 54 | ``` bash 55 | $ npm run build 56 | ``` 57 | 58 | **Start the production server** 59 | 60 | ``` bash 61 | $ npm start 62 | ``` 63 | 64 | **Run ESLint** 65 | 66 | ``` bash 67 | $ npm run eslint 68 | ``` 69 | 70 | **Run JSCS** 71 | 72 | ``` bash 73 | $ npm run jscs 74 | ``` 75 | 76 | ## License 77 | 78 | MIT 79 | 80 | [React]: http://facebook.github.io/react/ 81 | [Redux]: https://github.com/gaearon/redux 82 | [React Router]: http://rackt.github.io/react-router 83 | [Babel]: https://babeljs.io/ 84 | [Webpack]: http://webpack.github.io/ 85 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 4000, 3 | "host": "0.0.0.0", 4 | "apiUrl": "http://localhost:8101", 5 | "facebookToken": "103491386705646", 6 | "config": {} 7 | } 8 | -------------------------------------------------------------------------------- /config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8100, 3 | "host": "0.0.0.0", 4 | "apiUrl": "http://localhost:3099", 5 | "facebookToken": "103491386705646", 6 | "config": { 7 | "googleAnalytics": "UA-63854870-1", 8 | "adsOptimal": "25341" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/object/merge'; 2 | 3 | const env = process.env.NODE_ENV || 'development'; 4 | 5 | export default merge({ 6 | port: 4000, 7 | host: '0.0.0.0' 8 | }, require('./' + env)); 9 | -------------------------------------------------------------------------------- /config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8100, 3 | "host": "0.0.0.0", 4 | "apiUrl": "https://api.dripr.io", 5 | "facebookToken": "103491386705646", 6 | "config": { 7 | "googleAnalytics": "UA-63854870-1", 8 | "adsOptimal": "25341" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | require('./server'); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "driprui", 3 | "version": "0.0.0", 4 | "description": "Dripr UI", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "cross-env NODE_ENV=production webpack --stats --progress", 8 | "stats": "cross-env NODE_ENV=production webpack --json > stats.json", 9 | "dev": "NODE_ENV=development webpack --watch --config webpack/server.js & NODE_ENV=development node index", 10 | "devw": "start cross-env NODE_ENV=development webpack --watch --config webpack/server.js & start cross-env NODE_ENV=development node index", 11 | "eslint": "eslint ./src --ext .js,.jsx", 12 | "jscs": "jscs .", 13 | "start": "cross-env NODE_ENV=production node index" 14 | }, 15 | "author": "Goncalo Margalho (http://dripr.io)", 16 | "repository": "DevAlien/dripr-ui", 17 | "dependencies": { 18 | "bluebird": "^3.0.5", 19 | "body-parser": "^1.14.1", 20 | "classnames": "^2.1.5", 21 | "cookie": "*", 22 | "cookie-session": "^1.2.0", 23 | "cross-env": "^1.0.8", 24 | "csurf": "^1.8.3", 25 | "elemental": "^0.5.13", 26 | "express": "^4.13.3", 27 | "filesize": "^3.1.4", 28 | "ga-react-router": "^2.0.0", 29 | "graceful-fs": "^4.1.2", 30 | "highlight.js": "^8.9.1", 31 | "history": "^1.12.6", 32 | "immutable": "^3.7.5", 33 | "isomorphic-fetch": "^2.1.1", 34 | "keymirror": "^0.1.1", 35 | "lodash": "^3.10.1", 36 | "moment": "^2.11.2", 37 | "nodeify": "^1.0.0", 38 | "normalize.css": "^3.0.3", 39 | "react": "^0.14.0", 40 | "react-addons-css-transition-group": "^15.1.0", 41 | "react-dom": "^0.14.0", 42 | "react-dropzone": "^3.1.0", 43 | "react-facebook-login": "^2.0.5", 44 | "react-facebook-login2": "^2.0.6", 45 | "react-h5-video": "^1.0.0", 46 | "react-helmet": "^2.3.1", 47 | "react-highlight": "^0.6.1", 48 | "react-player": "^0.5.4", 49 | "react-redirect": "^1.0.0", 50 | "react-redux": "^4.0.0", 51 | "react-router": "^1.0.0", 52 | "redux": "^3.0.2", 53 | "redux-actions": "^0.8.0", 54 | "redux-promise": "^0.5.0", 55 | "redux-simple-router": "0.0.8", 56 | "redux-thunk": "^1.0.0", 57 | "serialize-javascript": "^1.1.2", 58 | "serve-static": "^1.10.0" 59 | }, 60 | "devDependencies": { 61 | "babel-core": "^5.8.33", 62 | "babel-loader": "^5.3.2", 63 | "babel-plugin-react-transform": "^1.1.1", 64 | "css-loader": "^0.22.0", 65 | "extract-text-webpack-plugin": "^0.9.1", 66 | "file-loader": "^0.8.4", 67 | "json-loader": "^0.5.3", 68 | "less": "^2.6.0", 69 | "less-loader": "^2.2.2", 70 | "null-loader": "^0.1.1", 71 | "purify-css": "^1.1.9", 72 | "react-transform-catch-errors": "^1.0.0", 73 | "react-transform-hmr": "^1.0.1", 74 | "redbox-react": "^1.1.1", 75 | "redux-devtools": "^3.0.0-beta-3", 76 | "redux-devtools-dock-monitor": "^1.0.0-beta-3", 77 | "redux-devtools-log-monitor": "^1.0.0-beta-3", 78 | "require-uncached": "^1.0.2", 79 | "style-loader": "^0.13.0", 80 | "url-loader": "^0.5.6", 81 | "webpack": "^1.12.2", 82 | "webpack-dev-middleware": "^1.2.0", 83 | "webpack-hot-middleware": "^2.4.1" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import path from 'path'; 3 | import serveStatic from 'serve-static'; 4 | import webpack from 'webpack'; 5 | import * as config from '../config'; 6 | 7 | const server = express(); 8 | const PUBLIC_PATH = path.join(__dirname, '../public'); 9 | 10 | if (process.env.NODE_ENV === 'development') { 11 | const webpackConfig = require('../webpack/dev.config').client; 12 | const compiler = webpack(webpackConfig); 13 | console.log(webpackConfig.output.publicPath) 14 | server.use(require('webpack-dev-middleware')(compiler, { 15 | noInfo: true, 16 | publicPath: webpackConfig.output.publicPath 17 | })); 18 | 19 | server.use(require('webpack-hot-middleware')(compiler)); 20 | } else { 21 | server.use(serveStatic(PUBLIC_PATH, { 22 | maxAge: 1000 * 60 * 60 * 24 * 30, // 1 month 23 | index: false 24 | })); 25 | } 26 | 27 | server.get('/*', require('./render')); 28 | 29 | server.use((err, req, res, next) => { 30 | console.error(err.stack || err); 31 | res.status(500).send('Server error'); 32 | }); 33 | 34 | server.listen(config.port, config.host, () => { 35 | console.log('Server listening on %s:%d', config.host, config.port); 36 | }); 37 | -------------------------------------------------------------------------------- /server/render.js: -------------------------------------------------------------------------------- 1 | import requireUncached from 'require-uncached'; 2 | import * as config from '../config'; 3 | 4 | function getWebpackStats() { 5 | if (process.env.NODE_ENV === 'production') { 6 | return require('../public/build/webpack-stats.json'); 7 | } 8 | 9 | return requireUncached('../public/build/webpack-stats.json'); 10 | } 11 | 12 | function getServerModule() { 13 | if (process.env.NODE_ENV === 'production') { 14 | return require('./build/main'); 15 | } 16 | 17 | return requireUncached('./build/main'); 18 | } 19 | 20 | export default function(req, res, next) { 21 | const webpackStats = getWebpackStats(); 22 | const createHtmlResponse = getServerModule(); 23 | 24 | createHtmlResponse({ 25 | webpackStats, 26 | request: req, 27 | config 28 | }, (err, {status, url, body}) => { 29 | if (err) return next(err); 30 | 31 | if (status === 302) { 32 | return res.redirect(url); 33 | } 34 | 35 | res.status(status).send(body); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/actions/data.js: -------------------------------------------------------------------------------- 1 | import {ActionTypes} from '../constants'; 2 | 3 | export const getData = () => { 4 | return (dispatch, getState) => { 5 | return dispatch({ 6 | types: [ActionTypes.DATA, ActionTypes.DATA_SUCCESS, ActionTypes.DATA_FAILED], 7 | promise: (client) => client.fetch('/files') 8 | }); 9 | }; 10 | }; 11 | 12 | export const loadUser = () => { 13 | return (dispatch, getState) => { 14 | return dispatch({ 15 | types: [ActionTypes.GETUSER, ActionTypes.GETUSER_SUCCESS, ActionTypes.GETUSER_FAILED], 16 | promise: (client) => client.fetch('/users/own') 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/actions/files.jsx: -------------------------------------------------------------------------------- 1 | import {ActionTypes} from '../constants'; 2 | 3 | export const getFile = (id) => { 4 | return (dispatch, getState) => { 5 | return dispatch({ 6 | types: [ActionTypes.FILE, ActionTypes.FILE_SUCCESS, ActionTypes.FILE_FAILED], 7 | promise: (client) => client.fetch('/files/' + id) 8 | }); 9 | }; 10 | }; 11 | 12 | export const getComments = (id) => { 13 | return (dispatch, getState) => { 14 | return dispatch({ 15 | types: [ActionTypes.COMMENTS, ActionTypes.COMMENTS_SUCCESS, ActionTypes.COMMENTS_FAILED], 16 | promise: (client) => client.fetch('/comments/' + id) 17 | }); 18 | }; 19 | }; 20 | 21 | export const postComment = (id, message) => { 22 | return (dispatch, getState) => { 23 | return dispatch({ 24 | types: [ActionTypes.POSTCOMMENT, ActionTypes.POSTCOMMENT_SUCCESS, ActionTypes.POSTCOMMENT_FAILED], 25 | promise: (client) => client.postComment(id, message) 26 | }); 27 | }; 28 | }; 29 | 30 | 31 | export const postFile = (file) => { 32 | return (dispatch, getState) => { 33 | return dispatch({ 34 | types: [ActionTypes.FILE, ActionTypes.FILE_SUCCESS, ActionTypes.FILE_FAILED], 35 | promise: (client) => client.postFile(file) 36 | }); 37 | }; 38 | }; 39 | 40 | export const postCode = (text, language) => { 41 | return (dispatch, getState) => { 42 | return dispatch({ 43 | types: [ActionTypes.FILE, ActionTypes.FILE_SUCCESS, ActionTypes.FILE_FAILED], 44 | promise: (client) => client.postCode(text, language) 45 | }); 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/actions/users.js: -------------------------------------------------------------------------------- 1 | import {ActionTypes} from '../constants'; 2 | 3 | export const loginFacebook = (data) => { 4 | return (dispatch, getState) => { 5 | return dispatch({ 6 | types: [ActionTypes.LOGIN, ActionTypes.LOGIN_SUCCESS, ActionTypes.LOGIN_FAILED], 7 | promise: (client) => client.fetch('/login-facebook', {method: 'post', body: JSON.stringify(data), withCredentials: 'include', headers: { 8 | 'Accept': 'application/json', 9 | 'Content-Type': 'application/json' 10 | }}) 11 | }); 12 | }; 13 | }; 14 | 15 | export const login = (email, password) => { 16 | return (dispatch, getState) => { 17 | return dispatch({ 18 | types: [ActionTypes.LOGIN, ActionTypes.LOGIN_SUCCESS, ActionTypes.LOGIN_FAILED], 19 | promise: (client) => client.fetch('/login', {method: 'post', body: JSON.stringify({email: email, password: password}), withCredentials: 'include', headers: { 20 | 'Accept': 'application/json', 21 | 'Content-Type': 'application/json' 22 | }}) 23 | }); 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /src/apiClient.jsx: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import config from '../config'; 3 | const BASE = config.apiUrl || 'http://localhost:8101'; 4 | 5 | export default function apiClient(token) { 6 | return { 7 | token: token, 8 | fetch: function(url, options) { 9 | if(!options) { 10 | options = {}; 11 | } 12 | if(this.token) { 13 | options.headers = {Authorization: 'Bearer ' + this.token}; 14 | } 15 | return fetch(BASE + url, options); 16 | }, 17 | 18 | postCode: function(text, language) { 19 | 20 | let options = {method: 'post', body: JSON.stringify({text: text, language: language}), headers: {"content-type": "application/json"}}; 21 | let url = '/upload/code/anon'; 22 | if(this.token) { 23 | url = '/upload/code'; 24 | options.headers.Authorization = 'Bearer ' + this.token; 25 | } 26 | return fetch(BASE + url, options); 27 | }, 28 | 29 | postComment: function(id, text) { 30 | let options = {method: 'post', body: JSON.stringify({text: text}), headers: {"content-type": "application/json"}}; 31 | let url = '/comments/' + id + '/anon'; 32 | if(this.token) { 33 | url = '/comments/' + id; 34 | options.headers.Authorization = 'Bearer ' + this.token; 35 | } 36 | return fetch(BASE + url, options); 37 | }, 38 | 39 | postFile: function(files) { 40 | let data = new FormData(); 41 | files.forEach(file => { 42 | data.append('file', file) 43 | }) 44 | let options = {method: 'post', body: data}; 45 | let url = '/upload/anon'; 46 | if(this.token) { 47 | url = '/upload' 48 | options.headers = {Authorization: 'Bearer ' + this.token}; 49 | } 50 | return fetch(BASE + url, options); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/assets/css/animation.css: -------------------------------------------------------------------------------- 1 | /* 2 | Animation example, for spinners 3 | */ 4 | .animate-spin { 5 | -moz-animation: spin 2s infinite linear; 6 | -o-animation: spin 2s infinite linear; 7 | -webkit-animation: spin 2s infinite linear; 8 | animation: spin 2s infinite linear; 9 | display: inline-block; 10 | } 11 | @-moz-keyframes spin { 12 | 0% { 13 | -moz-transform: rotate(0deg); 14 | -o-transform: rotate(0deg); 15 | -webkit-transform: rotate(0deg); 16 | transform: rotate(0deg); 17 | } 18 | 19 | 100% { 20 | -moz-transform: rotate(359deg); 21 | -o-transform: rotate(359deg); 22 | -webkit-transform: rotate(359deg); 23 | transform: rotate(359deg); 24 | } 25 | } 26 | @-webkit-keyframes spin { 27 | 0% { 28 | -moz-transform: rotate(0deg); 29 | -o-transform: rotate(0deg); 30 | -webkit-transform: rotate(0deg); 31 | transform: rotate(0deg); 32 | } 33 | 34 | 100% { 35 | -moz-transform: rotate(359deg); 36 | -o-transform: rotate(359deg); 37 | -webkit-transform: rotate(359deg); 38 | transform: rotate(359deg); 39 | } 40 | } 41 | @-o-keyframes spin { 42 | 0% { 43 | -moz-transform: rotate(0deg); 44 | -o-transform: rotate(0deg); 45 | -webkit-transform: rotate(0deg); 46 | transform: rotate(0deg); 47 | } 48 | 49 | 100% { 50 | -moz-transform: rotate(359deg); 51 | -o-transform: rotate(359deg); 52 | -webkit-transform: rotate(359deg); 53 | transform: rotate(359deg); 54 | } 55 | } 56 | @-ms-keyframes spin { 57 | 0% { 58 | -moz-transform: rotate(0deg); 59 | -o-transform: rotate(0deg); 60 | -webkit-transform: rotate(0deg); 61 | transform: rotate(0deg); 62 | } 63 | 64 | 100% { 65 | -moz-transform: rotate(359deg); 66 | -o-transform: rotate(359deg); 67 | -webkit-transform: rotate(359deg); 68 | transform: rotate(359deg); 69 | } 70 | } 71 | @keyframes spin { 72 | 0% { 73 | -moz-transform: rotate(0deg); 74 | -o-transform: rotate(0deg); 75 | -webkit-transform: rotate(0deg); 76 | transform: rotate(0deg); 77 | } 78 | 79 | 100% { 80 | -moz-transform: rotate(359deg); 81 | -o-transform: rotate(359deg); 82 | -webkit-transform: rotate(359deg); 83 | transform: rotate(359deg); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/assets/css/fontello-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-time:before { content: '\e800'; } /* '' */ 3 | .icon-click:before { content: '\e801'; } /* '' */ 4 | .icon-comment:before { content: '\e802'; } /* '' */ 5 | .icon-view:before { content: '\e803'; } /* '' */ 6 | .icon-doc:before { content: '\e804'; } /* '' */ 7 | .icon-calendar:before { content: '\e805'; } /* '' */ 8 | .icon-twitter-circled:before { content: '\e806'; } /* '' */ 9 | .icon-facebook-circled:before { content: '\e807'; } /* '' */ 10 | .icon-github-circled:before { content: '\e808'; } /* '' */ -------------------------------------------------------------------------------- /src/assets/css/fontello-embedded.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('data:application/octet-stream;base64,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') format('woff'), 4 | url('data:application/octet-stream;base64,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') format('truetype'); 5 | } 6 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 7 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 8 | /* 9 | @media screen and (-webkit-min-device-pixel-ratio:0) { 10 | @font-face { 11 | font-family: 'fontello'; 12 | src: url('../font/fontello.svg?18015564#fontello') format('svg'); 13 | } 14 | } 15 | */ 16 | 17 | [class^="icon-"]:before, [class*=" icon-"]:before { 18 | font-family: "fontello"; 19 | font-style: normal; 20 | font-weight: normal; 21 | speak: none; 22 | 23 | display: inline-block; 24 | text-decoration: inherit; 25 | width: 1em; 26 | margin-right: .2em; 27 | text-align: center; 28 | /* opacity: .8; */ 29 | 30 | /* For safety - reset parent styles, that can break glyph codes*/ 31 | font-variant: normal; 32 | text-transform: none; 33 | 34 | /* fix buttons height, for twitter bootstrap */ 35 | line-height: 1em; 36 | 37 | /* Animation center compensation - margins should be symmetric */ 38 | /* remove if not needed */ 39 | margin-left: .2em; 40 | 41 | /* you can be more comfortable with increased icons size */ 42 | /* font-size: 120%; */ 43 | 44 | /* Uncomment for 3D effect */ 45 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 46 | } 47 | .icon-time:before { content: '\e800'; } /* '' */ 48 | .icon-click:before { content: '\e801'; } /* '' */ 49 | .icon-comment:before { content: '\e802'; } /* '' */ 50 | .icon-view:before { content: '\e803'; } /* '' */ 51 | .icon-doc:before { content: '\e804'; } /* '' */ 52 | .icon-calendar:before { content: '\e805'; } /* '' */ 53 | .icon-twitter-circled:before { content: '\e806'; } /* '' */ 54 | .icon-facebook-circled:before { content: '\e807'; } /* '' */ 55 | .icon-github-circled:before { content: '\e808'; } /* '' */ -------------------------------------------------------------------------------- /src/assets/css/fontello-ie7-codes.css: -------------------------------------------------------------------------------- 1 | 2 | .icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 3 | .icon-click { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 4 | .icon-comment { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 5 | .icon-view { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 6 | .icon-doc { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 7 | .icon-calendar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 8 | .icon-twitter-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 9 | .icon-facebook-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 10 | .icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /src/assets/css/fontello-ie7.css: -------------------------------------------------------------------------------- 1 | [class^="icon-"], [class*=" icon-"] { 2 | font-family: 'fontello'; 3 | font-style: normal; 4 | font-weight: normal; 5 | 6 | /* fix buttons height */ 7 | line-height: 1em; 8 | 9 | /* you can be more comfortable with increased icons size */ 10 | /* font-size: 120%; */ 11 | } 12 | 13 | .icon-time { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 14 | .icon-click { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 15 | .icon-comment { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 16 | .icon-view { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 17 | .icon-doc { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 18 | .icon-calendar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 19 | .icon-twitter-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 20 | .icon-facebook-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } 21 | .icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -------------------------------------------------------------------------------- /src/assets/css/fontello.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('../font/fontello.eot?9639373'); 4 | src: url('../font/fontello.eot?9639373#iefix') format('embedded-opentype'), 5 | url('../font/fontello.woff2?9639373') format('woff2'), 6 | url('../font/fontello.woff?9639373') format('woff'), 7 | url('../font/fontello.ttf?9639373') format('truetype'), 8 | url('../font/fontello.svg?9639373#fontello') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 13 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 14 | /* 15 | @media screen and (-webkit-min-device-pixel-ratio:0) { 16 | @font-face { 17 | font-family: 'fontello'; 18 | src: url('../font/fontello.svg?9639373#fontello') format('svg'); 19 | } 20 | } 21 | */ 22 | 23 | [class^="icon-"]:before, [class*=" icon-"]:before { 24 | font-family: "fontello"; 25 | font-style: normal; 26 | font-weight: normal; 27 | speak: none; 28 | 29 | display: inline-block; 30 | text-decoration: inherit; 31 | width: 1em; 32 | margin-right: .2em; 33 | text-align: center; 34 | /* opacity: .8; */ 35 | 36 | /* For safety - reset parent styles, that can break glyph codes*/ 37 | font-variant: normal; 38 | text-transform: none; 39 | 40 | /* fix buttons height, for twitter bootstrap */ 41 | line-height: 1em; 42 | 43 | /* Animation center compensation - margins should be symmetric */ 44 | /* remove if not needed */ 45 | margin-left: .2em; 46 | 47 | /* you can be more comfortable with increased icons size */ 48 | /* font-size: 120%; */ 49 | 50 | /* Font smoothing. That was taken from TWBS */ 51 | -webkit-font-smoothing: antialiased; 52 | -moz-osx-font-smoothing: grayscale; 53 | 54 | /* Uncomment for 3D effect */ 55 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 56 | } 57 | 58 | .icon-time:before { content: '\e800'; } /* '' */ 59 | .icon-click:before { content: '\e801'; } /* '' */ 60 | .icon-comment:before { content: '\e802'; } /* '' */ 61 | .icon-view:before { content: '\e803'; } /* '' */ 62 | .icon-doc:before { content: '\e804'; } /* '' */ 63 | .icon-calendar:before { content: '\e805'; } /* '' */ 64 | .icon-twitter-circled:before { content: '\e806'; } /* '' */ 65 | .icon-facebook-circled:before { content: '\e807'; } /* '' */ 66 | .icon-github-circled:before { content: '\e808'; } /* '' */ -------------------------------------------------------------------------------- /src/assets/css/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment, 8 | .hljs-quote { 9 | color: #969896; 10 | } 11 | 12 | /* Tomorrow Red */ 13 | .hljs-variable, 14 | .hljs-template-variable, 15 | .hljs-tag, 16 | .hljs-name, 17 | .hljs-selector-id, 18 | .hljs-selector-class, 19 | .hljs-regexp, 20 | .hljs-deletion { 21 | color: #cc6666; 22 | } 23 | 24 | /* Tomorrow Orange */ 25 | .hljs-number, 26 | .hljs-built_in, 27 | .hljs-builtin-name, 28 | .hljs-literal, 29 | .hljs-type, 30 | .hljs-params, 31 | .hljs-meta, 32 | .hljs-link { 33 | color: #de935f; 34 | } 35 | 36 | /* Tomorrow Yellow */ 37 | .hljs-attribute { 38 | color: #f0c674; 39 | } 40 | 41 | /* Tomorrow Green */ 42 | .hljs-string, 43 | .hljs-symbol, 44 | .hljs-bullet, 45 | .hljs-addition { 46 | color: #b5bd68; 47 | } 48 | 49 | /* Tomorrow Blue */ 50 | .hljs-title, 51 | .hljs-section { 52 | color: #81a2be; 53 | } 54 | 55 | /* Tomorrow Purple */ 56 | .hljs-keyword, 57 | .hljs-selector-tag { 58 | color: #b294bb; 59 | } 60 | 61 | .hljs { 62 | display: block; 63 | overflow-x: auto; 64 | background: #1d1f21; 65 | color: #c5c8c6; 66 | padding: 0.5em; 67 | } 68 | 69 | .hljs-emphasis { 70 | font-style: italic; 71 | } 72 | 73 | .hljs-strong { 74 | font-weight: bold; 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/css/video.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes zx-bounce-in{0%{-webkit-transform:scale(0);transform:scale(0)}60%{-webkit-transform:scale(1.2);transform:scale(1.2)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes zx-bounce-in{0%{-webkit-transform:scale(0);transform:scale(0)}60%{-webkit-transform:scale(1.2);transform:scale(1.2)}100%{-webkit-transform:scale(1);transform:scale(1)}}.zx-show-up{-webkit-animation:zx-show-up .3s ease-out;animation:zx-show-up .3s ease-out;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes zx-show-up{0%{-webkit-transform:translate3d(0,60px,0) scale(.95);transform:translate3d(0,60px,0) scale(.95);opacity:0}100%{-webkit-transform:translate3d(0,0,0) scale(1);transform:translate3d(0,0,0) scale(1);opacity:1}}@keyframes zx-show-up{0%{-webkit-transform:translate3d(0,60px,0) scale(.95);transform:translate3d(0,60px,0) scale(.95);opacity:0}100%{-webkit-transform:translate3d(0,0,0) scale(1);transform:translate3d(0,0,0) scale(1);opacity:1}}.zx-fade-in{-webkit-animation:zx-fade-in .3s ease-out;animation:zx-fade-in .3s ease-out;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes zx-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes zx-fade-in{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes zx-fade-out{0%{opacity:1}100%{opacity:0}}@keyframes zx-fade-out{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes zx-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes zx-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes zx-breath{0%{-webkit-transform:scale(.5);transform:scale(.5)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes zx-breath{0%{-webkit-transform:scale(.5);transform:scale(.5)}100%{-webkit-transform:scale(1);transform:scale(1)}}.r5-wraper{width:auto;min-width:200px;position:relative;overflow:hidden}.r5-wraper *,.r5-wraper :after,.r5-wraper :before{box-sizing:border-box}.r5-wraper ul{list-style:none;padding:0;margin:0}.r5-wraper .r5-content{position:absolute;width:100%;top:0;left:0;pointer-events:none}.r5-wraper .r5-content *{pointer-events:auto}.r5-wraper .r5-overlay{width:100%;height:100%;position:absolute;top:0;left:0;-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:.5}.r5-wraper .r5-overlay svg{width:100px;height:100px;cursor:pointer;position:absolute;top:50%;left:50%;margin-left:-50px;margin-top:-50px}.r5-wraper .r5-overlay:hover{opacity:.9}.r5-wraper button{background:0 0;border:none;outline:0;opacity:.8;-webkit-transition:opacity .3s ease;transition:opacity .3s ease}.r5-wraper button:hover{opacity:1}.r5-wraper .r5-controls,.r5-wraper .r5-seekbar-wraper{width:100%}.r5-wraper .r5-controls-hidden{display:none}.r5-wraper .r5-controls{text-align:left}.r5-wraper .r5-controls .r5-panel{padding-left:5px;padding-right:5px}.r5-wraper .r5-pull-right{float:right}.r5-wraper .r5-timecode{font-size:12px;margin-left:10px;position:relative;top:-.5em;display:inline}.r5-wraper .r5-seekbar-wraper{height:3px;width:100%;margin-bottom:5px;position:relative;overflow:hidden;background-color:rgba(255,255,255,.3);-webkit-transition:height .3s ease;transition:height .3s ease}.r5-wraper .r5-seekbar-wraper input[type=range]{width:100%;position:absolute;top:-5px;opacity:0;cursor:pointer}.r5-wraper .r5-seekbar-wraper:hover{height:5px}.r5-wraper .r5-seekbar{background-color:#44FFB4;-webkit-transition:width .1s;transition:width .1s}.r5-wraper .r5-seekbar-loaded{background-color:rgba(255,255,255,.5)}.r5-wraper .r5-seekbar,.r5-wraper .r5-seekbar-loaded{position:absolute;top:0;left:0;height:8px}.r5-wraper .r5-volume{width:35px;display:inline-block;position:relative}.r5-wraper .r5-volume .r5-volume-inner{position:absolute;left:31px;top:12px;z-index:2;background-color:rgba(255,255,255,.5);height:6px;opacity:0;visibility:hidden;-webkit-transition:visibility 0s .3s,opacity .3s ease;transition:visibility 0s .3s,opacity .3s ease}.r5-wraper .r5-volume .r5-volume-bar,.r5-wraper .r5-volume input{position:absolute;top:0;left:0}.r5-wraper .r5-volume input{width:100%;margin-top:-5px;opacity:0}.r5-wraper .r5-volume .r5-volume-bar{width:100%;height:6px;background-color:#44FFB4}.r5-wraper .r5-volume:hover .r5-volume-inner{-webkit-transition:visibility 0s,opacity .3s ease;transition:visibility 0s,opacity .3s ease;opacity:1;visibility:visible}.r5-wraper .r5-volume:hover+.r5-timecode{display:none}.r5-wraper .r5-subtitle{position:relative;display:inline-block}.r5-wraper .r5-subtitle:hover .r5-subtitle-menu{visibility:visible;opacity:1;-webkit-transition:visibility 0s,opacity .3s ease;transition:visibility 0s,opacity .3s ease}.r5-wraper .r5-subtitle-menu{position:absolute;padding-bottom:20px;bottom:20px;left:-5px;opacity:0;visibility:hidden;-webkit-transition:visibility 0s .3s,opacity .3s ease;transition:visibility 0s .3s,opacity .3s ease}.r5-wraper ::cue{line-height:1.4;opacity:.8;background:rgba(0,0,0,.3)}.r5-wraper .r5-controls.r5-controls--overlay{color:#fff;padding:0 10px;position:absolute;bottom:0;left:0;background:-webkit-linear-gradient(transparent,rgba(0,0,0,.5)) repeat-x;background:linear-gradient(transparent,rgba(0,0,0,.5)) repeat-x}.r5-wraper .r5-controls.r5-controls--overlay button{color:#fff}.r5-wraper .r5-controls.r5-controls--overlay button.active{color:#44FFB4}.r5-wraper .r5-controls.r5-controls--overlay.r5-auto-hide{opacity:0;-webkit-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.r5-wraper .r5-controls.r5-controls--fixed{height:45px;margin-top:-3px;background-color:#329478;color:#fff;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.r5-wraper .r5-controls.r5-controls--fixed button{color:#fff}.r5-wraper .r5-controls.r5-controls--fixed .r5-seekbar-wraper{height:5px;position:relative;overflow:hidden}.r5-wraper .r5-controls.r5-controls--fixed .r5-seekbar-wraper:hover{height:5px}.r5-wraper:hover .r5-controls--overlay.r5-auto-hide{opacity:1;-webkit-transition:opacity .3s ease-in;transition:opacity .3s ease-in} 2 | -------------------------------------------------------------------------------- /src/assets/font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/font/fontello.eot -------------------------------------------------------------------------------- /src/assets/font/fontello.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2016 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/assets/font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/font/fontello.ttf -------------------------------------------------------------------------------- /src/assets/font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/font/fontello.woff -------------------------------------------------------------------------------- /src/assets/font/fontello.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/font/fontello.woff2 -------------------------------------------------------------------------------- /src/assets/img/Video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/img/Video.jpg -------------------------------------------------------------------------------- /src/assets/img/dropicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/img/dropicon.png -------------------------------------------------------------------------------- /src/assets/img/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/img/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/img/home-cloud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/img/home-code.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/img/home-laptop.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/img/home-web.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 24 | -------------------------------------------------------------------------------- /src/assets/img/menulogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/img/menulogo.png -------------------------------------------------------------------------------- /src/assets/img/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/js/dropzone.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/js/dropzone.js -------------------------------------------------------------------------------- /src/assets/js/homejs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevAlien/dripr-ui/0b06bd4d7946a5ac6ed0700569aabf06efcb6de4/src/assets/js/homejs -------------------------------------------------------------------------------- /src/assets/st.less: -------------------------------------------------------------------------------- 1 | @import "~elemental/less/elemental"; 2 | 3 | 4 | 5 | 6 | @body-bg: #1b232e; 7 | @text-color: #777; 8 | @brand-primary: #00A743; 9 | @heading-color: #777; 10 | // Site Explicit Variables 11 | // ------------------------------ 12 | 13 | 14 | // navigation 15 | 16 | @nav-brand-size: 32px; 17 | @navbar-height: 50px; 18 | 19 | 20 | // code 21 | 22 | @inline-code-color: @gray; 23 | @inline-code-bg: fade(@inline-code-color, 7%); 24 | 25 | 26 | 27 | 28 | 29 | // Type 30 | // ------------------------------ 31 | 32 | p { 33 | margin-bottom: 1em; 34 | margin-top: 0; 35 | } 36 | h2 { margin-top: 3em; } 37 | h2 + .lead { 38 | margin-top: 0; 39 | } 40 | h3 { margin-top: 3em; } 41 | 42 | 43 | 44 | 45 | 46 | // Form 47 | // ------------------------------ 48 | 49 | .FormLabel { 50 | font-size: @font-size-sm; 51 | font-weight: @font-weight-bold; 52 | } 53 | 54 | 55 | 56 | // Page Wrapper 57 | // ------------------------------ 58 | 59 | .page-wrapper { 60 | .display-flex(); 61 | .flex-direction(column); 62 | min-height: 100vh; 63 | } 64 | .page-body { 65 | .flex-grow(1); 66 | } 67 | 68 | 69 | 70 | 71 | // Primary Navigation 72 | // ------------------------------ 73 | 74 | // base 75 | 76 | .primary-nav { 77 | .translate3d(0,0,0); // prevent repaints on scroll 78 | background-color: fade(@body-bg, 95%); 79 | box-shadow: 0 1px 0 fade(black, 17%); 80 | font-size: @font-size-sm; 81 | line-height: @navbar-height; 82 | height: @navbar-height; 83 | position: fixed; 84 | top:0; 85 | text-transform: uppercase; 86 | text-align: center; 87 | width: 100%; 88 | z-index: @zindex-dropdown-button + 1; 89 | } 90 | 91 | // item 92 | 93 | .primary-nav__item { 94 | .transition( all 140ms ); 95 | .translate3d(0,0,0); 96 | border-bottom: 1px solid transparent; 97 | color: @text-color; 98 | cursor: pointer; 99 | display: block; 100 | padding: 0 1.5vw; 101 | 102 | &:active, 103 | &:hover, 104 | &:focus { 105 | color: @text-color; 106 | text-decoration: none; 107 | 108 | > .primary-nav__item-inner { 109 | box-shadow: inset 0 -3px 0 @brand-primary; 110 | } 111 | } 112 | 113 | &.active { 114 | color: @text-color; 115 | font-weight: 500; 116 | } 117 | } 118 | .primary-nav__item-inner { 119 | .transition( all 140ms ); 120 | display: inline-block; 121 | 122 | .active > & { 123 | box-shadow: inset 0 -3px 0 @brand-primary; 124 | } 125 | } 126 | 127 | // brand 128 | 129 | .primary-nav__brand { 130 | // .square(@nav-brand-size); 131 | display: inline-block; 132 | left: 0; 133 | margin-top: -(@nav-brand-size / 2); 134 | margin-left: 1.2em; 135 | margin-right: 1.2em; 136 | position: absolute; 137 | top: 50%; 138 | vertical-align: middle; 139 | z-index: 2; 140 | 141 | &.right { 142 | left: auto; 143 | right: 0; 144 | } 145 | 146 | &.special { 147 | .size(87px, 65px); 148 | left: -15px; 149 | margin: 0; 150 | overflow: hidden; 151 | top: -13px; 152 | } 153 | } 154 | .primary-nav__brand-src { 155 | .img-responsive(); 156 | } 157 | 158 | // trigger for the mobile menu 159 | 160 | .primary-nav-menu-trigger { 161 | .transition( all 240ms ); 162 | background: none; 163 | border: none; 164 | display: inline-block; 165 | padding-left: 2em; 166 | padding-right: 2em; 167 | position: relative; 168 | outline: none; 169 | z-index: 4; 170 | -webkit-appearance: none; 171 | 172 | &:focus { 173 | color: @gray-light; 174 | } 175 | } 176 | .primary-nav-menu-trigger-icon { 177 | display: inline-block; 178 | margin-right: 10px; 179 | vertical-align: middle; 180 | } 181 | .primary-nav-menu-trigger-label { 182 | display: inline-block; 183 | font-size: 16px; 184 | font-weight: 500; 185 | vertical-align: middle; 186 | text-transform: uppercase; 187 | } 188 | 189 | // MOBILE ONLY 190 | 191 | @media (max-width: @screen-xs-max) { 192 | .adsMobile { 193 | display: none; 194 | } 195 | .primary-nav-menu { 196 | .square(100%); 197 | .transition( all 240ms ); 198 | background-color: fade(@body-bg, 95%); 199 | left: 0; 200 | overflow: auto; 201 | position: fixed; 202 | top: 0; // account for dropshadow 203 | z-index: 3; 204 | 205 | .primary-nav__item { 206 | .translate3d(50px, 0, 0); 207 | color: @gray; 208 | font-size: 2em; 209 | font-weight: 100; 210 | letter-spacing: 0.25em; 211 | opacity: 0; 212 | 213 | &.active { 214 | color: black; 215 | } 216 | } 217 | 218 | // animate the nav items in 219 | &.is-visible .primary-nav__item { 220 | .transition( all 240ms cubic-bezier(0.645, 0.045, 0.355, 1) ); 221 | .translate3d(0, 0, 0); 222 | opacity: 1; 223 | 224 | &:nth-child(1) { .transition-delay(50ms); } 225 | &:nth-child(2) { .transition-delay(100ms); } 226 | &:nth-child(3) { .transition-delay(150ms); } 227 | &:nth-child(4) { .transition-delay(200ms); } 228 | &:nth-child(5) { .transition-delay(250ms); } 229 | &:nth-child(6) { .transition-delay(300ms); } 230 | &:nth-child(7) { .transition-delay(350ms); } 231 | } 232 | } 233 | .primary-nav-menu-inner { 234 | .translate(0, -50%); 235 | left: 0; 236 | margin: 0 auto; 237 | position: absolute; 238 | right: 0; 239 | top: 50%; 240 | width: 250px; 241 | } 242 | .primary-nav-menu.is-hidden { 243 | visibility: hidden; 244 | opacity: 0; 245 | } 246 | .primary-nav-menu.is-visible { 247 | visibility: visible; 248 | opacity: 1; 249 | } 250 | } 251 | 252 | // TABLET AND UP 253 | 254 | @media (min-width: @screen-sm) { 255 | .primary-nav-menu { 256 | display: inline-block; 257 | } 258 | 259 | // item 260 | 261 | .primary-nav__item { 262 | display: inline-block; 263 | } 264 | .primary-nav-menu-trigger { 265 | display: none; 266 | } 267 | } 268 | 269 | 270 | 271 | 272 | // Page Body 273 | // ------------------------------ 274 | 275 | .page-body { 276 | padding: (@navbar-height) 0; 277 | } 278 | 279 | 280 | 281 | 282 | 283 | // Page Footer 284 | // ------------------------------ 285 | 286 | .page-footer { 287 | background-color: fade(@body-bg, 95%); 288 | box-shadow: 0 -1px 0 fade(black, 17%); 289 | color: @gray-light; 290 | padding: 2em 0; 291 | text-align: center; 292 | 293 | a { 294 | color: @brand-primary; 295 | 296 | &:hover, 297 | &:focus { 298 | color: lighten(@brand-primary, 10%); 299 | outline: none; 300 | } 301 | } 302 | } 303 | .page-footer__icon { 304 | font-size: 1.125em; 305 | } 306 | 307 | 308 | 309 | 310 | 311 | // Animation 312 | // ------------------------------ 313 | 314 | 315 | 316 | // nav menu items 317 | 318 | @-webkit-keyframes navMenuItems { 319 | from { opacity: 0; -webkit-transform: translate3d(0, 1000px, 0); } 320 | to { opacity: 1; -webkit-transform: none; } 321 | } 322 | @keyframes navMenuItems { 323 | from { opacity: 0; transform: translate3d(0, 1000px, 0); } 324 | to { opacity: 1; transform: none; } 325 | } 326 | 327 | 328 | 329 | 330 | // Banner Home 331 | // ------------------------------ 332 | 333 | 334 | // base 335 | .demo-banner { 336 | font-size: 16px; 337 | padding-bottom: 60px; 338 | padding-top: 60px; 339 | 340 | .demo-container { 341 | position: relative; 342 | } 343 | 344 | a { 345 | color: @brand-primary; 346 | 347 | &:hover, 348 | &:focus { 349 | color: lighten(@brand-primary, 10%); 350 | outline: none; 351 | } 352 | } 353 | } 354 | 355 | 356 | // headings 357 | .demo-banner__heading-2 { 358 | font-size: 32px; 359 | font-weight: 300; 360 | // margin: 0 !important; 361 | } 362 | 363 | 364 | // buttons 365 | .demo-banner__buttons { 366 | margin-top: 60px; 367 | } 368 | 369 | .Button--demo-primary { 370 | background: white; 371 | border: none; 372 | color: @brand-primary; 373 | font-size: 18px; 374 | } 375 | a.Button--demo-link { 376 | color: white; 377 | font-size: 18px; 378 | font-weight: 300; 379 | 380 | &:hover, 381 | &:focus { 382 | color: white; 383 | } 384 | } 385 | 386 | 387 | // lists 388 | .demo-banner-list { 389 | line-height: 1.3; 390 | list-style: none; 391 | margin: 2em 0 0; 392 | padding: 0; 393 | 394 | > li { 395 | margin: 0 0 15px 15px; 396 | padding: 0 0 0 15px; 397 | position: relative; 398 | 399 | &::before { 400 | .square(4px); 401 | background-color: rgba(0, 0, 0, 0.5); 402 | border-radius: 50%; 403 | content: " "; 404 | left: 0; 405 | position: absolute; 406 | top: 8px; 407 | } 408 | } 409 | } 410 | 411 | 412 | // divider 413 | .demo-banner-divider { 414 | line-height: 1; 415 | margin-bottom: 30px; 416 | margin-top: 60px; 417 | text-align: center; 418 | 419 | &::before { 420 | background-color: rgba(0, 0, 0, 0.1); 421 | content: " "; 422 | height: 1px; 423 | left: (@grid-gutter-width / 2); 424 | left: 0; 425 | position: absolute; 426 | right: (@grid-gutter-width / 2); 427 | } 428 | } 429 | .demo-banner-divider-inner { 430 | background-color: mix(@brand-primary, @body-bg, 10%); 431 | padding: 0 1em; 432 | position: relative; 433 | top: -.5em; 434 | text-transform: uppercase; 435 | } 436 | 437 | 438 | // nav 439 | .demo-banner-nav__col { 440 | text-align: center; 441 | } 442 | .demo-banner-nav__item { 443 | display: block; 444 | text-align: center; 445 | 446 | &:hover, 447 | &:focus { 448 | text-decoration: none; 449 | 450 | .demo-banner-nav__label-inner { 451 | border-bottom-color: currentColor; 452 | } 453 | } 454 | } 455 | .demo-banner-nav__icon { 456 | margin-bottom: 1em; 457 | } 458 | .demo-banner-nav__label { 459 | color: @text-color; 460 | } 461 | .demo-banner-nav__label-inner { 462 | border-bottom: 1px solid transparent; 463 | display: inline-block; 464 | } 465 | 466 | 467 | // primary 468 | .demo-banner--primary { 469 | #gradient .directional( spin(@brand-primary,7%), spin(@brand-primary,-7%), 135deg ); 470 | background-color: @brand-primary; 471 | color: white; 472 | margin-top: -(@navbar-height); 473 | padding-bottom: 100px; 474 | padding-top: 100px; 475 | 476 | .demo-container { 477 | position: relative; 478 | } 479 | .demo-banner__heading-1 { 480 | color: white; 481 | font-size: 60px; 482 | margin: 0; 483 | } 484 | .demo-banner__heading-2 { 485 | color: white; 486 | opacity: .75; 487 | } 488 | @media (max-width: 480px) { 489 | .demo-banner__heading-1 { 490 | font-size: 48px; 491 | letter-spacing: -0.01em; 492 | margin-bottom: 20px; 493 | } 494 | .demo-banner__heading-2 { font-size: 30px; } 495 | .demo-banner__button { 496 | display: block; 497 | font-size: 16px; 498 | line-height: 3; 499 | height: auto; 500 | } 501 | } 502 | } 503 | .demo-banner-illustration { 504 | .animation( fadeIn 2s ); 505 | .square(180px); 506 | //background: url('../images/hero-bg.svg') no-repeat left top; 507 | background-size: 180px, 180px; 508 | left: -54px; 509 | position: absolute; 510 | top: -56px; 511 | 512 | @media (min-width: 481px) { 513 | .square(240px); 514 | background-size: 240px, 240px; 515 | left: -82px; 516 | top: -75px; 517 | } 518 | } 519 | 520 | 521 | // secondary 522 | .demo-banner--secondary { 523 | background-color: mix(@brand-primary, @body-bg, 10%); 524 | 525 | .demo-banner__heading-2 { 526 | margin-top: 0; 527 | } 528 | } 529 | 530 | 531 | // tertiary 532 | .demo-banner--tertiary { 533 | margin-bottom: -(@navbar-height * 2); 534 | 535 | .demo-banner__heading-2 { 536 | margin-top: 0; 537 | } 538 | } 539 | 540 | 541 | // points 542 | .demo-banner-points { 543 | margin-top: 20px; 544 | } 545 | 546 | 547 | 548 | 549 | // Code examples 550 | // ------------------------------ 551 | 552 | 553 | // base 554 | 555 | .code-example { 556 | margin-bottom: 2em; 557 | margin-top: 2em; 558 | } 559 | 560 | 561 | // common 562 | 563 | .code-example__pre, 564 | .code-example__example { 565 | padding: 20px; 566 | } 567 | 568 | 569 | // wrap the example component(s) in a white box 570 | 571 | .code-example__example { 572 | .border-top-radius(@border-radius-base); 573 | background-color: white; 574 | border: 1px solid @code-border-color; 575 | 576 | // remove the border radius when there's corresponding code 577 | &:first-child { 578 | .border-bottom-radius( 0 ); 579 | } 580 | } 581 | 582 | 583 | // example heading to provide context if necessary 584 | 585 | .code-example__example__heading { 586 | border-bottom: 1px solid rgba(0, 0, 0, 0.06); 587 | color: @gray-light; 588 | font-size: @font-size-sm; 589 | font-weight: 500; 590 | margin-bottom: 20px; 591 | padding-bottom: 20px; 592 | text-transform: uppercase; 593 | } 594 | 595 | 596 | // format and present the code 597 | 598 | .code-example__pre { 599 | background: @code-bg-color; 600 | border: 1px solid @code-border-color; 601 | border-top: none; 602 | margin: 0; 603 | overflow-x: auto; 604 | -webkit-overflow-scrolling: touch; 605 | 606 | &:last-child { 607 | .border-bottom-radius( @border-radius-base ); 608 | } 609 | } 610 | 611 | 612 | // format interior elements 613 | 614 | .code-example__example-element { 615 | border-radius: .3em; 616 | padding: .5em 1.5em; 617 | } 618 | .code-example__example-element--block { 619 | & + & { 620 | margin-top: 1em; 621 | } 622 | } 623 | .code-example__example-element--inline { 624 | & + & { 625 | margin-top: 1em; 626 | } 627 | 628 | @media (min-width: @screen-sm) { 629 | display: inline-block; 630 | vertical-align: text-bottom; 631 | 632 | & + & { 633 | margin-left: 1em; 634 | margin-top: 0; 635 | } 636 | } 637 | } 638 | .code-example__example-element--primary-bg { 639 | background-color: @app-primary; 640 | } 641 | 642 | 643 | // inline code (not for use with Prism) 644 | 645 | .inline-code { 646 | background-color: @inline-code-bg; 647 | border-radius: .3em; 648 | color: @inline-code-color; 649 | font-size: 90%; 650 | padding: 0 4px; 651 | white-space: nowrap; 652 | } 653 | .inline-code--list-item { 654 | display: inline-block; 655 | margin-top: 4px; 656 | } 657 | 658 | 659 | .code-example--glyph__icon { 660 | text-align: center; 661 | } 662 | 663 | .code-example--glyph__icon-name { 664 | font-size: 10px; 665 | text-overflow: ellipsis; 666 | } 667 | 668 | 669 | // Usage table 670 | 671 | .usage-table { 672 | min-height: 1px; 673 | overflow-x: auto; 674 | -webkit-overflow-scrolling: touch; 675 | 676 | td, th { 677 | line-height: 1.4; 678 | vertical-align: top; 679 | } 680 | } 681 | 682 | // cell types 683 | .usage-table__prop { 684 | color: #266d90; 685 | font-family: @font-family-monospace; 686 | font-size: 90%; 687 | } 688 | .usage-table__type, 689 | .usage-table__default { 690 | color: @gray-light; 691 | font-family: @font-family-monospace; 692 | font-size: 90%; 693 | } 694 | .usage-table__type { 695 | color: #bf2a5c; 696 | // color: darken(@app-primary, 15%); 697 | } 698 | 699 | .usage-table__description { 700 | min-width: 200px; 701 | } 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | .sidebar { 710 | position: fixed; 711 | top: 51px; 712 | bottom: 0px; 713 | right: 0px; 714 | width: 300px; 715 | background-color: rgb(53, 59, 68); 716 | display: flex; 717 | -webkit-box-orient: vertical; 718 | -webkit-box-direction: normal; 719 | -webkit-flex-direction: column; 720 | -ms-flex-direction: column; 721 | flex-direction: column; 722 | -webkit-box-flex: 1; 723 | -webkit-flex: 1; 724 | -ms-flex: 1; 725 | flex: 1; 726 | 727 | .comments { 728 | //border: 1px solid red; 729 | -webkit-box-flex: 1; 730 | -webkit-flex: 1 1 0; 731 | -ms-flex: 1 1 0; 732 | flex: 1 1 0; 733 | overflow: auto; 734 | } 735 | 736 | .bottom { 737 | flex: 0 auto; 738 | } 739 | } 740 | 741 | 742 | 743 | /* COMMENTS */ 744 | #comments { 745 | // position: absolute; 746 | // top: 3em; 747 | // bottom: 0; 748 | // width: 300px; 749 | padding: 0 20px; 750 | // //background: #FFF url('http://goo.gl/2kwnF') center; 751 | // display: block; 752 | // overflow: auto; 753 | // 754 | // box-shadow: 0 0 1em 0 rgba(0, 0, 0, .3); 755 | // -o-box-shadow: 0 0 1em 0 rgba(0, 0, 0, .3); 756 | // -moz-box-shadow: 0 0 1em 0 rgba(0, 0, 0, .3); 757 | // -webkit-box-shadow: 0 0 1em 0 rgba(0, 0, 0, .3); 758 | } 759 | // .links-open { 760 | // left: 0; 761 | // right: 0; 762 | // } 763 | // .links-close { 764 | // left: -360px; 765 | // right: 360px; 766 | // } 767 | #comments h1 { 768 | position: relative; 769 | width: 75px; 770 | height: 25px; 771 | margin: auto; 772 | display: block; 773 | 774 | font-size: 120%; 775 | color: #6C6; 776 | line-height: 25px; 777 | text-align: center; 778 | text-transform: uppercase; 779 | } 780 | #comments h1:before { 781 | content: ''; 782 | position: absolute; 783 | top: 0; 784 | right: 75px; 785 | bottom: 0; 786 | width: 75px; 787 | height: 0; 788 | margin: auto; 789 | border-top: 2px solid #6C6; 790 | display: block; 791 | } 792 | #comments h1:after { 793 | content: ''; 794 | position: absolute; 795 | top: 0; 796 | left: 75px; 797 | bottom: 0; 798 | width: 75px; 799 | height: 0; 800 | margin: auto; 801 | border-top: 2px solid #6C6; 802 | display: block; 803 | } 804 | #comments h2 { 805 | position: relative; 806 | width: 75px; 807 | height: 25px; 808 | margin: 15px auto auto auto; 809 | display: block; 810 | 811 | font-size: 120%; 812 | color: rgb(174, 182, 187); 813 | line-height: 25px; 814 | text-align: center; 815 | text-transform: uppercase; 816 | } 817 | #comments h2:before { 818 | content: ''; 819 | position: absolute; 820 | top: 0; 821 | right: 75px; 822 | bottom: 0; 823 | width: 75px; 824 | height: 0; 825 | margin: auto; 826 | border-top: 2px solid rgb(174, 182, 187); 827 | display: block; 828 | } 829 | #comments h2:after { 830 | content: ''; 831 | position: absolute; 832 | top: 0; 833 | left: 75px; 834 | bottom: 0; 835 | width: 75px; 836 | height: 0; 837 | margin: auto; 838 | border-top: 2px solid rgb(174, 182, 187); 839 | display: block; 840 | } 841 | #comments li { 842 | cursor: default; 843 | position: relative; 844 | padding: 6px 10px 9px 10px; 845 | margin-bottom: 5px; 846 | border-radius: 3px; 847 | display: block; 848 | 849 | 850 | 851 | font-size: 90%; 852 | background: #545a64; 853 | 854 | color: #FFF; 855 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 856 | margin-right: 20px; 857 | } 858 | #comments li:nth-child(even) { 859 | background-color: #242a32; 860 | margin-left:20px; 861 | margin-right:0px; 862 | } 863 | #comments li:last-child { 864 | margin-bottom: 0; 865 | } 866 | 867 | #comments li h3 { 868 | float: left; 869 | height: 1.3em; 870 | display: block; 871 | 872 | line-height: 1.3em; 873 | margin-top: initial; 874 | color: #FFF; 875 | } 876 | #comments li span.time { 877 | float: right; 878 | height: 1.3em; 879 | display: block; 880 | 881 | font-size: 76%; 882 | line-height: 1.3em; 883 | } 884 | 885 | #comments li p { 886 | margin-top: 22px; 887 | width: 100%; 888 | display: block; 889 | overflow: hidden; 890 | } 891 | /* COMMENTS */ 892 | 893 | .comments .no-comments { 894 | cursor: default; 895 | position: absolute; 896 | top: 50%; 897 | margin-top:50px; 898 | width:260px; 899 | margin-left:20px; 900 | text-align: center; 901 | padding: 6px 10px 9px 10px; 902 | margin-bottom: 5px; 903 | border-radius: 3px; 904 | display: block; 905 | 906 | font-size: 90%; 907 | background: #545a64; 908 | 909 | color: #FFF; 910 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 911 | } 912 | 913 | 914 | /* LIST */ 915 | .listContainer { 916 | margin:20px; 917 | font-size: 0.8em; 918 | 919 | } 920 | .month { 921 | color: #777; 922 | } 923 | .monthBar { 924 | border:0; 925 | height:1px; 926 | color: #777; 927 | background: #333; 928 | background-image: linear-gradient(to right, #333, #777, #333); 929 | } 930 | .cont { 931 | color: #777; 932 | position: relative; 933 | float: left; 934 | margin: 20px; 935 | } 936 | .img { 937 | 938 | width: 270px; 939 | height: 140px; 940 | background-position: 50% 50%; 941 | background-repeat:no-repeat; 942 | background-size: cover; 943 | border-radius: 3px; 944 | } 945 | .listDetail{ 946 | padding:10px; 947 | } 948 | 949 | 950 | //Home 951 | 952 | .ondrop { 953 | .home-card { 954 | background-color: #414448!important; 955 | } 956 | } 957 | 958 | .hljs { 959 | text-align: initial; 960 | } 961 | 962 | .loader { 963 | width: 100px; 964 | height: 40px; 965 | position: absolute; 966 | top: 50%; 967 | left: 50%; 968 | margin: -20px -50px; 969 | } 970 | .loader div { 971 | width: 20px; 972 | height: 20px; 973 | background: #FFF; 974 | border-radius: 50%; 975 | position: absolute; 976 | 977 | } 978 | .d1 { animation: animate 2s linear infinite;} 979 | .d2 { animation: animate 2s linear infinite -.4s; } 980 | .d3 {animation: animate 2s linear infinite -.8s; } 981 | .d4 { animation: animate 2s linear infinite -1.2s; } 982 | .d5 { animation: animate 2s linear infinite -1.6s; } 983 | @-webkit-keyframes animate { 984 | 0% { left: 100px; top:0} 985 | 80% { left: 0; top:0;} 986 | 85% { left: 0; top: -20px; width: 20px; height: 20px;} 987 | 90% { width: 40px; height: 15px; } 988 | 95% { left: 100px; top: -20px; width: 20px; height: 20px;} 989 | 100% { left: 100px; top:0; } 990 | } 991 | 992 | 993 | .paper { 994 | display: block; 995 | margin: 50px auto; 996 | width: 700px; 997 | border: 1px solid #fff; 998 | padding: 50px 0; 999 | background: #fff; 1000 | box-shadow: 0 10px 15px -10px rgba(0, 0, 0, .5) ,0 0 25px 0 rgba(0, 0, 0, .1), inset 0 0 15px 0 rgba(0, 0, 0, .05); 1001 | background-image: linear-gradient(90deg, #DCEDF5 0, #DCEDF5 1px); 1002 | background-size: 1px 1px; 1003 | background-position: 50px 0; 1004 | background-repeat: repeat-y; 1005 | text-align: left; 1006 | } 1007 | 1008 | .paper_in { 1009 | display: block; 1010 | padding: 0 50px 0 75px; 1011 | font-size: 16px; 1012 | line-height: 32px; 1013 | color: #444; 1014 | border-top: 1px solid #eee; 1015 | background-image: linear-gradient(0deg, #eee 0, #eee 1px, transparent 1px, transparent); 1016 | background-size: 1px 32px; 1017 | outline: 0; 1018 | } 1019 | 1020 | .paper_in * { 1021 | color: #444 !important; 1022 | margin: 0 !important; 1023 | padding: 0 !important; 1024 | font-size: 16px !important; 1025 | line-height: 32px !important; 1026 | } 1027 | -------------------------------------------------------------------------------- /src/assets/sty.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato:300,400,700'); 2 | /* 3 | font: white; 4 | button: 3D7EB6; 5 | */ 6 | html, 7 | body { 8 | width: 100%; 9 | height: 100%; 10 | background-color: #26252B; 11 | } 12 | 13 | body { 14 | position: absolute; 15 | font-family: 'Lato','Helvetica Neue',Arial,sans-serif; 16 | color: #fff; 17 | webkit-tap-highlight-color: #222; 18 | top:0; 19 | /* fallback */ 20 | background-color: #26252B; 21 | background-repeat: repeat-x; 22 | 23 | /* Safari 4-5, Chrome 1-9 */ 24 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#26252B), to(#161618)); 25 | 26 | /* Safari 5.1, Chrome 10+ */ 27 | background: -webkit-linear-gradient(top, #161618, #26252B); 28 | 29 | /* Firefox 3.6+ */ 30 | background: -moz-linear-gradient(top, #161618, #26252B); 31 | 32 | /* IE 10 */ 33 | background: -ms-linear-gradient(top, #161618, #26252B); 34 | 35 | /* Opera 11.10+ */ 36 | background: -o-linear-gradient(top, #161618, #26252B); 37 | } 38 | 39 | /* workaround modal-open padding issue */ 40 | body.modal-open { 41 | padding-right:0 !important; 42 | } 43 | 44 | hr { 45 | max-width: 120px; 46 | border-width: 3px; 47 | opacity: 0.08; 48 | margin-top: 25px; 49 | margin-bottom: 25px; 50 | } 51 | 52 | hr.light { 53 | border-color: #fff; 54 | opacity: 1; 55 | } 56 | 57 | hr.primary { 58 | border-color: #007FFF; 59 | opacity: 1; 60 | } 61 | 62 | a { 63 | color: #fff; 64 | -webkit-transition: all .35s; 65 | -moz-transition: all .35s; 66 | transition: all .35s; 67 | } 68 | 69 | a:hover, 70 | a:focus { 71 | color: #5D6F7D; 72 | outline: 0; 73 | } 74 | 75 | p { 76 | font-size: 17px; 77 | line-height: 1.7; 78 | margin-bottom: 20px; 79 | font-family:'Helvetica Neue',Arial,sans-serif; 80 | } 81 | 82 | .text-normal { 83 | font-family:'Helvetica Neue',Arial,sans-serif; 84 | } 85 | 86 | .wide-space { 87 | letter-spacing: 1.6px; 88 | } 89 | 90 | .icon-lg { 91 | font-size: 50px; 92 | line-height: 18px; 93 | } 94 | 95 | .bg-primary { 96 | background-color: #007FFF; 97 | } 98 | 99 | .bg-alt { 100 | background-color: #fff; 101 | } 102 | 103 | .text-faded { 104 | color: rgba(245,245,245,0.7); 105 | } 106 | 107 | .text-dark { 108 | color: #001A33; 109 | } 110 | 111 | .text-light { 112 | color: #fff; 113 | } 114 | 115 | .text-light:hover,.text-light:focus { 116 | color: #eee; 117 | text-decoration:none; 118 | } 119 | 120 | .text-primary { 121 | color: #007FFF; 122 | } 123 | 124 | section { 125 | padding: 70px 0; 126 | } 127 | 128 | aside { 129 | padding: 45px 0; 130 | } 131 | 132 | footer { 133 | padding: 45px; 134 | padding-top: 40px; 135 | background-color: #e0e0e3; 136 | } 137 | 138 | footer h4, footer h6 { 139 | color:#fff; 140 | } 141 | 142 | .navbar-default { 143 | border-color: rgba(35,35,35,.05); 144 | -webkit-transition: all .4s; 145 | -moz-transition: all .4s; 146 | transition: all .4s; 147 | } 148 | 149 | .navbar-brand { 150 | -webkit-transition: padding .2s ease-in; 151 | -moz-transition: padding .2s ease-in; 152 | transition: padding .2s ease-in; 153 | } 154 | 155 | .navbar-brand:hover, 156 | .navbar-brand:focus { 157 | color: #fff; 158 | } 159 | 160 | .navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover { 161 | background-color: #001A33; 162 | } 163 | .navbar-default .navbar-toggle, .navbar-default .navbar-collapse { 164 | border-color: transparent; 165 | } 166 | 167 | .navbar-default .nav > li>a.menu, 168 | .navbar-default .nav>li>a.menu:focus { 169 | font-size: 12px; 170 | color: #fff; 171 | text-transform: uppercase; 172 | } 173 | 174 | .navbar-default .nav>li>a.menu:hover, 175 | .navbar-default .nav>li>a.menu:focus:hover { 176 | color: #007FFF; 177 | } 178 | 179 | .navbar-default .nav .nav-link.active, 180 | .navbar-default .nav .nav-link.active:focus { 181 | color: #007FFF !important; 182 | background-color: transparent; 183 | } 184 | 185 | /* responsive nav */ 186 | @media(max-width:48em) { 187 | .navbar-default .navbar-nav>.nav-item { 188 | float: none; 189 | margin-left: .1rem; 190 | } 191 | .navbar-default .navbar-nav { 192 | float:none !important; 193 | } 194 | .navbar-default .navbar-brand img { 195 | display:inline; 196 | } 197 | } 198 | 199 | @media(min-width:48em) { 200 | .navbar-default { 201 | border-color: transparent; 202 | min-height: 60px; 203 | } 204 | 205 | .navbar-brand { 206 | padding: 12px; 207 | } 208 | 209 | .navbar-default .navbar-brand:hover, 210 | .navbar-default .navbar-brand:focus { 211 | color: #fff; 212 | } 213 | 214 | .navbar-default .nav > li>a.menu, 215 | .navbar-default .nav>li>a.menu:focus { 216 | color: #fff; 217 | padding-left: 5px; 218 | padding-top: 21px; 219 | letter-spacing: 1.1px; 220 | } 221 | 222 | .navbar-default .nav > li>a.menu:hover, 223 | .navbar-default .nav>li>a.menu:focus:hover { 224 | color: #fff; 225 | } 226 | 227 | } 228 | 229 | header { 230 | position: relative; 231 | min-height: auto; 232 | text-align: center; 233 | color: #fff; 234 | width: 100%; 235 | } 236 | 237 | header .header-content { 238 | position: relative; 239 | width: 100%; 240 | padding: 100px 15px; 241 | text-align: center; 242 | z-index: 2; 243 | } 244 | 245 | header .header-content .inner h1 { 246 | margin-top: 0; 247 | margin-bottom: 0; 248 | } 249 | 250 | header .header-content .inner p { 251 | margin-bottom: 50px; 252 | font-size: 16px; 253 | font-weight: 300; 254 | color: #222; 255 | } 256 | 257 | @media(min-width:34em) { 258 | .icon-lg { 259 | font-size: 80px; 260 | } 261 | 262 | header { 263 | min-height: 100%; 264 | } 265 | 266 | header .header-content { 267 | position: absolute; 268 | top: 150px; 269 | padding: 0 50px; 270 | } 271 | 272 | header .header-content .inner { 273 | margin-right: auto; 274 | margin-left: auto; 275 | max-width: 1000px; 276 | } 277 | 278 | header .header-content .inner h1 { 279 | font-size: 53px; 280 | } 281 | 282 | header .header-content .inner p { 283 | margin-right: auto; 284 | margin-left: auto; 285 | max-width: 80%; 286 | font-size: 18px; 287 | } 288 | 289 | section { 290 | min-height: 600px; 291 | padding: 100px 0; 292 | } 293 | 294 | } 295 | 296 | .list-group-item, .card-footer { 297 | background-color:transparent; 298 | } 299 | 300 | textarea { 301 | resize: none; 302 | } 303 | 304 | .call-to-action h2 { 305 | margin: 0 auto 20px; 306 | } 307 | 308 | .btn.btn-primary { 309 | background-color: #3D7EB6; 310 | border:1px solid #3D7EB6; 311 | -webkit-transition: all .35s; 312 | -moz-transition: all .35s; 313 | transition: all .35s; 314 | color: white; 315 | } 316 | 317 | .btn.btn-primary:hover { 318 | opacity: 0.7; 319 | } 320 | 321 | .btn-xl { 322 | padding: 15px 30px; 323 | font-size: 20px; 324 | } 325 | 326 | ::-moz-selection { 327 | text-shadow: none; 328 | color: #fff; 329 | background: #222; 330 | } 331 | 332 | ::selection { 333 | text-shadow: none; 334 | color: #fff; 335 | background: #222; 336 | } 337 | 338 | img::selection { 339 | color: #fff; 340 | background: 0 0; 341 | } 342 | 343 | img::-moz-selection { 344 | color: #fff; 345 | background: 0 0; 346 | } 347 | 348 | 349 | .home-icon { 350 | width: 50px; 351 | height: 50px; 352 | fill: currentColor; 353 | } 354 | 355 | .home-icon .icon-stroke { 356 | stroke: currentColor; 357 | } 358 | 359 | .home-icon .icon-fill { 360 | fill: currentColor; 361 | } 362 | .home-about { 363 | color: #3D7EB6; 364 | } 365 | 366 | .home-sub { 367 | color: #fff!important; 368 | } 369 | .home-icon-selected { 370 | cursor: pointer; 371 | width: 50px; 372 | height: 50px; 373 | color: #3D7EB6; 374 | fill: currentColor; 375 | } 376 | 377 | .home-icon-logo { 378 | width: 70px; 379 | } 380 | .home-feature { 381 | font-size: 14px; 382 | } 383 | 384 | .home-feature-selected { 385 | color: #3D7EB6; 386 | } 387 | .home-feature-selected>div { 388 | color: white; 389 | font-size: 14px; 390 | } 391 | .home-feature { 392 | color: #5D6F7D; 393 | } 394 | 395 | .home-feature:hover { 396 | color: #3D7EB6; 397 | cursor: pointer; 398 | } 399 | .home-feature:hover >div { 400 | color: white; 401 | } 402 | 403 | .f-web-input, .f-web-input:focus { 404 | border: #3D7EB6 1px solid; 405 | background-color: #26252B; 406 | } 407 | 408 | .f-code-text, .f-code-text:focus { 409 | border: #3D7EB6 1px solid; 410 | background-color: #26252B; 411 | margin-bottom: 10px; 412 | } 413 | 414 | .f-code-select, .f-code-select:focus { 415 | border: #3D7EB6 1px solid; 416 | background-color: #26252B; 417 | } 418 | .f-upload-container { 419 | border: #3D7EB6 1px solid; 420 | background-color: #26252B; 421 | width:100%; 422 | height:200px; 423 | border-radius: 5px; 424 | padding-top: 75px; 425 | } 426 | 427 | .home-title { 428 | font-weight: 300;margin-bottom: 50px!important; 429 | } 430 | .home-title-b { 431 | font-weight: 700; 432 | } 433 | .home-subtitle { 434 | font-size: 24px; 435 | font-weight: 300; 436 | } 437 | 438 | #first { 439 | height: 95vh; 440 | } 441 | .no { 442 | display:none; 443 | } 444 | 445 | .cont { 446 | color: #777; 447 | position: relative; 448 | float: left; 449 | margin: 20px; 450 | } 451 | .img { 452 | 453 | width: 270px; 454 | height: 140px; 455 | background-position: 50% 50%; 456 | background-repeat:no-repeat; 457 | background-size: cover; 458 | border-radius: 3px; 459 | } 460 | .cont:hover .listDetail { display:block; } 461 | .listDetail{ 462 | text-align: left; 463 | display:none; 464 | padding:10px; 465 | height:100%; 466 | background-color: #3D7EB6; 467 | opacity: 0.8; 468 | color: #fff; 469 | font-size: 14px; 470 | font-weight: 300 471 | } 472 | 473 | .list-container { 474 | margin-top: 80px; 475 | } 476 | 477 | .single-file:hover { 478 | text-decoration: none; 479 | } 480 | 481 | .file { 482 | margin-top: 80px; 483 | margin-left: 4%; 484 | margin-right: 4%; 485 | text-align: center; 486 | } 487 | 488 | .file img { 489 | max-width: 100%; 490 | border-radius: 3px; 491 | } 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | .sidebar { 503 | position: fixed; 504 | top: 80px; 505 | bottom: 0px; 506 | right: 0px; 507 | width: 300px; 508 | background-color: #3D7EB6; 509 | display: flex; 510 | -webkit-box-orient: vertical; 511 | -webkit-box-direction: normal; 512 | -webkit-flex-direction: column; 513 | -ms-flex-direction: column; 514 | flex-direction: column; 515 | -webkit-box-flex: 1; 516 | -webkit-flex: 1; 517 | -ms-flex: 1; 518 | flex: 1; 519 | 520 | } 521 | 522 | .file-info-title { 523 | font-weight: 300; 524 | } 525 | 526 | .file-info div { 527 | padding-top: 5px; 528 | } 529 | .file-info i { 530 | font-size: 17px; 531 | font-weight: 200; 532 | } 533 | 534 | .sidebar .file-info { 535 | width: 300px; 536 | background-color: #5D6F7D; 537 | text-align: left; 538 | font-size: 12px; 539 | font-weight: 400; 540 | padding: 15px; 541 | line-height: 20px; 542 | } 543 | 544 | .sidebar .comments { 545 | -webkit-box-flex: 1; 546 | -webkit-flex: 1 1 0; 547 | -ms-flex: 1 1 0; 548 | flex: 1 1 0; 549 | overflow: auto; 550 | } 551 | 552 | .sidebar .bottom { 553 | flex: 0 auto; 554 | } 555 | /* COMMENTS */ 556 | #comments { 557 | padding: 20px 10px; 558 | 559 | } 560 | 561 | #comments li { 562 | cursor: default; 563 | position: relative; 564 | padding: 6px 10px 9px 10px; 565 | margin-bottom: 5px; 566 | border-radius: 3px; 567 | display: block; 568 | 569 | 570 | 571 | font-size: 90%; 572 | 573 | color: #FFF; 574 | } 575 | 576 | 577 | #comments li h3 { 578 | float: left; 579 | height: 16px; 580 | display: block; 581 | font-size:16px; 582 | line-height: 16px; 583 | margin-top: initial; 584 | color: #FFF; 585 | } 586 | #comments li span.time { 587 | float: right; 588 | display: block; 589 | 590 | font-size: 10px; 591 | font-weight: 200; 592 | } 593 | 594 | #comments li p { 595 | text-align: left; 596 | margin-top: 22px; 597 | width: 100%; 598 | display: block; 599 | overflow: hidden; 600 | font-size: 14px; 601 | font-weight: 200; 602 | } 603 | /* COMMENTS */ 604 | 605 | .comments .no-comments { 606 | cursor: default; 607 | position: absolute; 608 | top: 50%; 609 | margin-top:50px; 610 | width:260px; 611 | margin-left:20px; 612 | text-align: center; 613 | padding: 6px 10px 9px 10px; 614 | margin-bottom: 5px; 615 | border-radius: 3px; 616 | display: block; 617 | 618 | font-size: 90%; 619 | background: #545a64; 620 | 621 | color: #FFF; 622 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 623 | } 624 | 625 | 626 | .comment-form { 627 | padding: 10px; 628 | } 629 | 630 | .comment-input { 631 | height: 100px; 632 | } 633 | 634 | .comment-btn { 635 | margin-top: 1px; 636 | height: 98px; 637 | } 638 | 639 | .header-logo { 640 | width: 70px; 641 | height: 32px; 642 | fill: currentColor; 643 | } 644 | 645 | .header-logo .icon-stroke { 646 | stroke: currentColor; 647 | } 648 | 649 | .header-logo .icon-fill { 650 | fill: currentColor; 651 | } 652 | 653 | .card-block { 654 | padding: 1.25rem 0; 655 | } 656 | 657 | .dload-btn:hover, .dload-btn:focus { 658 | color: inherit; 659 | text-decoration: none; 660 | } 661 | 662 | .nav-social { 663 | padding-top: 17px; 664 | } 665 | .nav-social a:hover { 666 | text-decoration: none; 667 | } 668 | 669 | .file pre{ 670 | text-align: left; 671 | } -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | require('babel-core/polyfill'); 2 | import ga from 'ga-react-router' 3 | import React from 'react'; 4 | import {render} from 'react-dom'; 5 | import configureStore from './store/configureStore'; 6 | import Root from './components/Root'; 7 | import {createHistory} from 'history'; 8 | import {ActionTypes} from './constants'; 9 | import getRoutes from './routes'; 10 | import {Router} from 'react-router'; 11 | import {syncReduxAndRouter} from 'redux-simple-router'; 12 | import apiClient from './apiClient'; 13 | 14 | 15 | const store = configureStore(window.$STATE, apiClient(window.$STATE.app.authInfo)); 16 | const history = createHistory(); 17 | const routes = getRoutes(store); 18 | history.listen(location => { 19 | ga('set', 'page', location.pathname) 20 | ga('send', 'pageview'); 21 | }); 22 | 23 | store.dispatch({type: ActionTypes.REHYDRATE}); 24 | syncReduxAndRouter(history, store); 25 | 26 | render( 27 | 28 | 29 | , document.getElementById('root')); 30 | -------------------------------------------------------------------------------- /src/components/DevTools.jsx: -------------------------------------------------------------------------------- 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 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/components/HtmlDocument.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import serialize from 'serialize-javascript'; 3 | import {ActionTypes} from '../constants'; 4 | import Helmet from 'react-helmet'; 5 | import asd from 'file!../assets/sty.css'; 6 | export default class HtmlDocument extends React.Component { 7 | static propTypes = { 8 | webpackStats: React.PropTypes.object.isRequired, 9 | content: React.PropTypes.string.isRequired, 10 | store: React.PropTypes.object.isRequired 11 | } 12 | 13 | render() { 14 | let head = Helmet.rewind(); 15 | const {content, store, webpackStats} = this.props; 16 | const {app} = store.getState(); 17 | 18 | store.dispatch({type: ActionTypes.DEHYDRATE}); 19 | const state = store.getState(); 20 | const dehydratedState = 'window.$STATE=' + serialize(state); 21 | 22 | let styles = [].concat( 23 | webpackStats.vendor.css, 24 | webpackStats.main.css 25 | ); 26 | 27 | let scripts = [].concat( 28 | webpackStats.vendor.js, 29 | webpackStats.main.js 30 | ); 31 | 32 | return ( 33 | 34 | 35 | 36 | {app.title} 37 | 38 | 39 | 40 | 41 | 42 | {head.title.toComponent()} 43 | {head.meta.toComponent()} 44 | {head.link.toComponent()} 45 | {styles.map((href, key) => )} 46 | 47 | ' 17 | }}> 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/components/file/CommentsBar.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router'; 4 | import {Form, FormInput, FormField, Button, Spinner} from 'elemental'; 5 | import XScript from '../XScript'; 6 | import {getComments, postComment} from '../../../actions/files'; 7 | import nodeify from 'nodeify'; 8 | import moment from 'moment'; 9 | 10 | @connect((state) => { 11 | return {file: state.files.data} 12 | }, (dispatch) => ({dispatch, updatePath})) 13 | export default class CommentsBar extends React.Component { 14 | static propTypes = { 15 | file: PropTypes.object, 16 | fileId: PropTypes.string, 17 | dispatch: PropTypes.func.isRequired 18 | } 19 | 20 | constructor(props, context) { 21 | super(props, context); 22 | this.state = { 23 | comments: [], 24 | loading: true, 25 | comment: '' 26 | } 27 | } 28 | 29 | componentDidMount() { 30 | nodeify(this.props.dispatch(getComments(this.props.fileId)), this.getComments.bind(this)); 31 | } 32 | 33 | getComments(error, result) { 34 | this.setState({comments: result.result, loading: false}); 35 | } 36 | 37 | newComment(error, result) { 38 | let comments = this.state.comments; 39 | comments.push(result.result) 40 | this.setState({comments: comments}); 41 | } 42 | 43 | generateComment(comment) { 44 | return ( 45 |
  • 46 |

    {comment.name}

    47 | {moment(comment.createdAt).fromNow()} 48 |

    {comment.text.split("\n").map(function(item) { 49 | return ( 50 | {item}
    51 | ) 52 | })}

    53 |
  • 54 | ) 55 | } 56 | 57 | handleChange(event) { 58 | this.setState({comment: event.target.value}); 59 | } 60 | 61 | handleSubmit() { 62 | var value = this.state.comment; 63 | if(value.length > 0) { 64 | 65 | let comments = this.state.comments; 66 | nodeify(this.props.dispatch(postComment(this.props.fileId, value)), this.newComment.bind(this)); 67 | this.setState({comment: ''}); 68 | } 69 | } 70 | 71 | capitalizeFirstLetter(string) { 72 | return string.charAt(0).toUpperCase() + string.slice(1); 73 | } 74 | 75 | render() { 76 | let commentsContent; 77 | if (this.state.loading) { 78 | commentsContent = ( 79 |
    80 | ) 81 | } else if (this.state.comments.length > 0) { 82 | let comments = []; 83 | this.state.comments.forEach((comment) => { 84 | comments.push(this.generateComment(comment)); 85 | }) 86 | commentsContent = ( 87 | 90 | ) 91 | } else { 92 | commentsContent = ( 93 |
    There are no comments at the moment, write yours!
    94 | ) 95 | } 96 | return ( 97 |
    98 |
    99 |
    100 | {!this.props.file.title &&
    Here
    } 101 |
    {moment(this.props.file.createdAt).format('MMMM Do YYYY, h:mm:ss a')}
    102 |
    {this.capitalizeFirstLetter(this.props.file.type)}
    103 |
    {this.props.file.views ? this.props.file.views : "0"} Views
    104 |
    {this.props.file.comments ? this.props.file.comments + ' Comments' : '0 Comments'}
    105 |
    106 |
    107 |
    108 | {commentsContent} 109 |
    110 |
    111 |
    112 | 38 | 41 |
    Submit
    42 |
    43 |
    44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/components/home/HomeDownload.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | 3 | import IconDownloadWin from '../icons/IconDownloadWin'; 4 | import IconDownloadMac from '../icons/IconDownloadMac'; 5 | 6 | export default class HomeDownload extends React.Component { 7 | render() { 8 | return ( 9 |
    10 |
    Download Desktop App
    11 |

    Dripr Desktop is a menubar app, so you will have an icon to interact with the app.
    You will be able to take screenshots and they will be automatically uploaded and you can see the latest files you have uploaded, if you are logged in.

    12 | 13 |
    14 | 20 | 26 | 32 | 33 |
    34 |
    Shortcuts
    35 |

    36 | Mac OSX
    37 | Fullscreen screenshot: CMD + SHIFT + A
    38 | Cropped screenshot: CMD + SHIFT + X
    39 | 40 | Windows
    41 | Fullscreen screenshot: CTRL + SHIFT + A
    42 | Cropped screenshot: CTRL + SHIFT + X 43 |

    44 |
    45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/components/home/HomeUpload.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | import Dropzone from 'react-dropzone'; 3 | import Loader from './loader'; 4 | 5 | export default class HomeUpload extends React.Component { 6 | static propTypes = { 7 | onDrop: PropTypes.func.isRequired, 8 | isUploading: PropTypes.bool.isRequired 9 | } 10 | 11 | constructor(props, context) { 12 | super(props, context); 13 | this.state = { 14 | isDrag: false, 15 | files: [] 16 | } 17 | } 18 | 19 | render() { 20 | let cName = 'f-upload-container'; 21 | let content = Click or Drop a file here; 22 | if (this.state.isDrag) { 23 | cName = cName + ' '; 24 | content = Drop File 25 | } 26 | if (this.props.isUploading) { 27 | content = 28 | } 29 | return ( 30 | 31 | {content} 32 | 33 | ) 34 | } 35 | 36 | onDrop(files) { 37 | this.setState({files: files}); 38 | this.props.onDrop(files); 39 | } 40 | onDragEnter(files) { 41 | if(!this.state.isDrag) 42 | this.setState({isDrag: true}); 43 | } 44 | 45 | onDragLeave(files) { 46 | if(this.state.isDrag) 47 | this.setState({isDrag: false}); 48 | } 49 | 50 | onDragOver(files) { 51 | if(this.state.isDrag) 52 | this.setState({isDrag: false}); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/components/home/HomeWeb.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | 3 | export default class HomeWeb extends React.Component { 4 | static propTypes = { 5 | createScreenshots: PropTypes.func.isRequired 6 | } 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | text: '' 12 | } 13 | } 14 | 15 | onChange = (event) => { 16 | let value = event.target.value; 17 | this.setState({text: value}); 18 | } 19 | 20 | render() { 21 | return ( 22 |
    23 | Paste URL to take a screenshot of it 24 |
    25 | 26 | 27 | 28 | 29 |
    30 |
    31 | ) 32 | } 33 | 34 | requestScreenshots = () => { 35 | this.props.createScreenshots(this.state.text); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/components/home/loader.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | 3 | export default class Loader extends React.Component { 4 | static propTypes = { 5 | data: PropTypes.array 6 | } 7 | constructor(props, context) { 8 | super(props, context); 9 | 10 | } 11 | render() { 12 | let loader; 13 | if(this.props.data && this.props.data.length > 0) { 14 | loader = (
    15 |

    Uploading {this.props.data.length} file...

    16 |
    {this.props.data.map((file) => { 17 | return file.type.startsWith('image') ? :

    No preview

    18 | })}
    19 |
    ); 20 | } else { 21 | loader = (
    22 |

    We are elaborating your text, if it is a link it will take a bit of time to make all the screenshots

    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    ) 31 | } 32 | return loader 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/components/components/icons/IconDownloadMac.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconDownloadMac = (props) => { 4 | return ( 5 | 8 | 11 | ); 12 | } 13 | 14 | export default IconDownloadMac; -------------------------------------------------------------------------------- /src/components/components/icons/IconDownloadWin.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconDownloadWin = (props) => { 4 | return ( 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | export default IconDownloadWin; -------------------------------------------------------------------------------- /src/components/components/icons/IconHomeCloud.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconHomeCloud = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default IconHomeCloud; -------------------------------------------------------------------------------- /src/components/components/icons/IconHomeCode.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconHomeCode = (props) => { 4 | return ( 5 | 7 | 8 | 9 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 28 | 33 | 36 | 37 | ); 38 | } 39 | 40 | export default IconHomeCode; -------------------------------------------------------------------------------- /src/components/components/icons/IconHomeLaptop.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconHomeLaptop = (props) => { 4 | return ( 5 | 7 | 8 | 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default IconHomeLaptop; -------------------------------------------------------------------------------- /src/components/components/icons/IconHomeWeb.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconHomeWeb = (props) => { 4 | return ( 5 | 7 | 8 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 27 | ); 28 | } 29 | 30 | export default IconHomeWeb; -------------------------------------------------------------------------------- /src/components/components/icons/IconLogo.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const IconLogo = (props) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export default IconLogo; 28 | -------------------------------------------------------------------------------- /src/components/components/list/file.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import moment from 'moment'; 3 | import {Link} from 'react-router'; 4 | 5 | export default class Login extends React.Component { 6 | static propTypes = { 7 | data: PropTypes.object.isRequired 8 | } 9 | constructor(props, context) { 10 | super(props, context); 11 | 12 | } 13 | getStyle(thumbnail) { 14 | if(thumbnail) { 15 | return {backgroundImage: "url('" + (thumbnail) + "')"}; 16 | } else { 17 | return {textAlign: "center", "fontSize": "64px", "paddingTop": "30px", backgroundColor: "#324761"}; 18 | } 19 | } 20 | 21 | getClass(type) { 22 | switch(type.toLowerCase()) { 23 | case 'url': 24 | return 'fa-link img'; 25 | case 'code': 26 | return 'fa-code img'; 27 | case 'video': 28 | return 'fa-video-camera img'; 29 | case 'text': 30 | return 'fa-pencil-square-o img'; 31 | default: 32 | return 'img' 33 | } 34 | } 35 | render() { 36 | let data = this.props.data; 37 | let style = this.getStyle(data.thumbnail || data.url); 38 | let cl = this.getClass(data.type); 39 | return ( 40 |
    41 | 42 |
    43 |
    44 |
    {moment(data.createdAt).format('MMMM Do YYYY, h:mm:ss a')}
    45 |
    {this.capitalizeFirstLetter(data.type)}
    46 |
    {data.views ? data.views : "0"} Views
    47 |
    {data.comments ? data.comments + ' Comments' : '0 Comments'}
    48 |
    49 |
    50 | 51 | 52 |
    53 | 54 | ); 55 | } 56 | 57 | capitalizeFirstLetter(string) { 58 | return string.charAt(0).toUpperCase() + string.slice(1); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/components/login/login.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router'; 4 | import {login} from '../../../actions/users'; 5 | import nodeify from 'nodeify'; 6 | 7 | 8 | @connect(null, (dispatch) => ({dispatch, updatePath})) 9 | export default class Login extends React.Component { 10 | constructor(props, context) { 11 | super(props, context); 12 | this.state = { 13 | inputEmail: '', 14 | inputPassword: '' 15 | }; 16 | } 17 | 18 | render() { 19 | return ( 20 |
    21 |
    22 | 24 |
    25 |
    26 | 28 |
    29 |
    30 | 31 |
    32 |
    33 | 34 | ); 35 | } 36 | 37 | handleEmailChange = e => { 38 | this.setState({ 39 | inputEmail: e.target.value 40 | }); 41 | } 42 | 43 | handlePasswordChange = e => { 44 | this.setState({ 45 | inputPassword: e.target.value 46 | }); 47 | } 48 | 49 | handleSubmit = e => { 50 | e.preventDefault(); 51 | nodeify(this.props.dispatch(login(this.state.inputEmail, this.state.inputPassword)), () => { 52 | window.location = "/"; 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/components/login/signup.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router'; 4 | import {login} from '../../../actions/users'; 5 | import nodeify from 'nodeify'; 6 | 7 | export default class Signup extends React.Component { 8 | constructor(props, context) { 9 | super(props, context); 10 | } 11 | 12 | render() { 13 | return ( 14 |

    Not implemented at the moment, please login with facebook

    15 | ); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/components/components/viewers/Code.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import '../../../assets/css/tomorrow-night.css'; 3 | import Highlight from 'react-highlight'; 4 | 5 | export default class ViewerCode extends React.Component { 6 | static propTypes = { 7 | data: PropTypes.object.isRequired 8 | } 9 | constructor(props, context) { 10 | super(props, context); 11 | 12 | } 13 | render() { 14 | return ( 15 | {this.props.data.text} 16 | ); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/components/components/viewers/Default.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | export default class ViewerDefault extends React.Component { 4 | static propTypes = { 5 | data: PropTypes.object.isRequired 6 | } 7 | constructor(props, context) { 8 | super(props, context); 9 | 10 | } 11 | render() { 12 | return ( 13 |

    Unfortunately we do not handle this file at the moment but you can download it

    Download
    14 | ); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/components/components/viewers/Image.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | export default class ViewerImage extends React.Component { 4 | static propTypes = { 5 | data: PropTypes.object.isRequired 6 | } 7 | constructor(props, context) { 8 | super(props, context); 9 | 10 | } 11 | render() { 12 | return ( 13 | * 14 | ); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/components/components/viewers/Pdf.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import Helmet from 'react-helmet'; 3 | //import PDF from 'react-pdf'; 4 | //require('pdfjs-dist/build/pdf.combined') 5 | 6 | export default class ViewerPdf extends React.Component { 7 | static propTypes = { 8 | data: PropTypes.object.isRequired 9 | } 10 | constructor(props, context) { 11 | super(props, context); 12 | 13 | } 14 | render() { 15 | if(__CLIENT__) 16 | return ( 17 |
    18 |
    19 | {/**/} 20 |
    21 |
    22 | ); 23 | 24 | return (
    ) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/components/components/viewers/Text.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | export default class ViewerText extends React.Component { 4 | static propTypes = { 5 | data: PropTypes.object.isRequired 6 | } 7 | constructor(props, context) { 8 | super(props, context); 9 | 10 | } 11 | render() { 12 | return ( 13 |
    14 |
    {this.props.data.text.split("\n").map(function(item) { 15 | return ({item}
    ) 16 | })}
    17 |
    18 | ); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/components/components/viewers/Url.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {Button} from 'elemental'; 3 | 4 | export default class ViewerUrl extends React.Component { 5 | static propTypes = { 6 | data: PropTypes.object.isRequired 7 | } 8 | constructor(props, context) { 9 | super(props, context); 10 | 11 | } 12 | render() { 13 | return ( 14 |
    15 |

    We made some screenshots for {this.props.data.text}

    16 |
    You can download a zip containing the screenshot and below that there's a preview
    17 |
    18 |
    19 | * 20 |
    21 | ); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/components/components/viewers/Video.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import '../../../assets/css/video.css'; 3 | import Video from "react-h5-video"; 4 | 5 | export default class ViewerVideo extends React.Component { 6 | static propTypes = { 7 | data: PropTypes.object.isRequired 8 | } 9 | constructor(props, context) { 10 | super(props, context); 11 | 12 | } 13 | render() { 14 | return ( 15 | 16 | ); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/components/pages/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '../../assets/sty.css'; 3 | import Header from '../components/Header' 4 | import {loadUser} from '../../actions/data'; 5 | import nodeify from 'nodeify'; 6 | import Helmet from 'react-helmet'; 7 | 8 | export default class App extends React.Component { 9 | 10 | render() { 11 | return ( 12 |
    13 | 22 |
    23 |
    {this.props.children}
    24 |
    25 | ); 26 | } 27 | } 28 | 29 | App.onEnter = store => (nextState, replaceState, callback) => { 30 | const {id} = nextState.params; 31 | const {app} = store.getState(); 32 | if(!app.user && app.authInfo) { 33 | return nodeify(store.dispatch(loadUser()), callback); 34 | } 35 | 36 | return callback(); 37 | }; 38 | -------------------------------------------------------------------------------- /src/components/pages/Downloads.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {loginFacebook} from '../../actions/users'; 4 | import nodeify from 'nodeify'; 5 | import {Button} from 'elemental' 6 | import FacebookLogin from 'react-facebook-login2'; 7 | import LoginForm from '../components/login/login'; 8 | import SignupForm from '../components/login/signup'; 9 | import config from '../../../config'; 10 | 11 | @connect(null, (dispatch) => ({dispatch})) 12 | export default class Downloads extends React.Component { 13 | static propTypes = { 14 | dispatch: PropTypes.func.isRequired 15 | } 16 | 17 | constructor(props, context) { 18 | super(props, context); 19 | this.state = { 20 | mac: 'https://files.dripr.io/DriprMacOSX.zip', 21 | win32: 'https://files.dripr.io/DriprWin32.zip', 22 | win64: 'https://files.dripr.io/DriprWin64.zip' 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 |
    29 |

    Here you can download our Desktop App

    30 |

    It is a menubar app, so you will have a little icon and there you can manage the app

    31 | 32 | 33 | 34 | 35 | 36 | 37 |
    38 |
    39 |

    Shortcuts

    40 |

    Mac OSX

    41 |
    Fullscreen screenshot: CMD + SHIFT + A
    42 |
    Cropped screenshot: CMD + SHIFT + X
    43 | 44 |

    Windows

    45 |
    Fullscreen screenshot: CTRL + SHIFT + A
    46 |
    Cropped screenshot: CTRL + SHIFT + X
    47 |
    48 |
    49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/pages/File.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router'; 4 | import {getFile} from '../../actions/files'; 5 | import nodeify from 'nodeify'; 6 | import Helmet from 'react-helmet'; 7 | 8 | const { Container, Card, Row, Col, FormInput } = require('elemental'); 9 | 10 | import ViewerText from '../components/viewers/Text' 11 | import ViewerUrl from '../components/viewers/Url' 12 | import ViewerVideo from '../components/viewers/Video' 13 | import ViewerCode from '../components/viewers/Code' 14 | import ViewerImage from '../components/viewers/Image' 15 | import ViewerDefault from '../components/viewers/Default' 16 | import ViewerPdf from '../components/viewers/Pdf' 17 | import CommentsBar from '../components/file/CommentsBar' 18 | 19 | @connect(state => { 20 | return { 21 | file: state.files.data, 22 | isMobile: state.app.isMobile 23 | }}, { 24 | updatePath 25 | }) 26 | export default class File extends React.Component { 27 | static propTypes = { 28 | file: PropTypes.object.isRequired, 29 | isMobile: PropTypes.bool.isRequired, 30 | updatePath: PropTypes.func.isRequired 31 | } 32 | 33 | constructor(props, context) { 34 | super(props, context); 35 | 36 | this.state = { 37 | mobileMenuIsVisible: false, 38 | windowHeight: this.props.isMobile ? 700 : 1200, 39 | windowWidth: this.props.isMobile ? 400: 800 40 | }; 41 | } 42 | componentDidMount () { 43 | if(__CLIENT__) { 44 | window.addEventListener('resize', this.handleResize.bind(this)); 45 | } 46 | 47 | } 48 | 49 | componentWillUnmount () { 50 | if(__CLIENT__) 51 | window.removeEventListener('resize', this.handleResize.bind(this)); 52 | } 53 | 54 | handleResize () { 55 | this.setState({ 56 | windowHeight: window.innerHeight, 57 | windowWidth: window.innerWidth 58 | }); 59 | } 60 | 61 | render() { 62 | if(!this.props.file) 63 | return(

    Error

    ); 64 | 65 | let viewer; 66 | switch(this.props.file.type) { 67 | case 'text': 68 | viewer = this.props.file.text ? : ; 69 | break; 70 | case 'url': 71 | viewer = ; 72 | break; 73 | case 'video': 74 | viewer = ; 75 | break; 76 | case 'code': 77 | viewer = ; 78 | break; 79 | case 'image': 80 | viewer = ; 81 | break; 82 | case 'pdfa': //do not want to activate it right now 83 | viewer = ; 84 | break; 85 | default: 86 | viewer = ; 87 | break; 88 | } 89 | let colStyle = {padding: '50px 5px', textAlign: 'center'}; 90 | let bar; 91 | 92 | if(!__SERVER__ && (this.state.windowWidth > 768 && !this.props.isMobile)) { 93 | colStyle.marginRight = '300px'; 94 | colStyle.padding = '50px 30px'; 95 | bar = (); 96 | } 97 | 98 | return ( 99 |
    100 | 105 | 106 | 107 | {viewer} 108 | 109 | 110 | {bar} 111 |
    112 | ); 113 | } 114 | 115 | getMetaTags(file) { 116 | let metas = [ 117 | {"itemprop": "name", "content": "Dripr.io"}, 118 | {"itemprop": "description", "content": "Dripr.io upload files, videos, code and take screenshots with the desktop app"}, 119 | {"name": "twitter:card", "content": "Photo"}, 120 | {"name": "twitter:site", "content": "@driprio"}, 121 | {"name": "twitter:title", "content": "Dripr.io"}, 122 | {"name": "twitter:description", "content": "Dripr.io upload files, videos, code and take screenshots with the desktop app"}, 123 | {"name": "twitter:creator", "content": "@driprio"}, 124 | {"property": "og:title", "content": "Dripr.io"}, 125 | {"property": "og:type", "content": "website"}, 126 | {"property": "og:url", "content": "https://dripr.io/file/" + file.hash}, 127 | {"property": "og:description", "content": "Dripr.io upload files, videos, code and take screenshots with the desktop app"}, 128 | {"property": "og:site_name", "content": "Dripr.io"}, 129 | ]; 130 | if(file.thumbnail) { 131 | metas.push({"itemprop": "image", "content": file.thumbnail}); 132 | metas.push({"name": "twitter:image:src", "content": file.thumbnail}) 133 | metas.push({"property": "og:image", "content": file.thumbnail}) 134 | } 135 | 136 | return metas; 137 | } 138 | } 139 | 140 | File.onEnter = store => (nextState, replaceState, callback) => { 141 | const {id} = nextState.params; 142 | 143 | nodeify(store.dispatch(getFile(id)), callback); 144 | }; 145 | -------------------------------------------------------------------------------- /src/components/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router'; 4 | import {postFile, postCode} from '../../actions/files'; 5 | import nodeify from 'nodeify'; 6 | import {ActionTypes} from '../../constants'; 7 | import Dropzone from 'react-dropzone'; 8 | import urlDropIcon from 'file!../../assets/img/dropicon.png' 9 | import Loader from '../components/home/loader'; 10 | const { Container, Card, Row, Col, InputGroup, FormInput, Button, FormSelect, FormField, Alert } = require('elemental'); 11 | 12 | import IconHomeCloud from '../components/icons/IconHomeCloud'; 13 | import IconHomeWeb from '../components/icons/IconHomeWeb'; 14 | import IconHomeCode from '../components/icons/IconHomeCode'; 15 | import IconHomeLaptop from '../components/icons/IconHomeLaptop'; 16 | import IconLogo from '../components/icons/IconLogo'; 17 | 18 | import HomeUpload from '../components/home/HomeUpload'; 19 | import HomeWeb from '../components/home/HomeWeb'; 20 | import HomeCode from '../components/home/HomeCode'; 21 | import HomeAbout from '../components/home/HomeAbout'; 22 | import HomeDownload from '../components/home/HomeDownload'; 23 | 24 | @connect(state => { 25 | return {files: state.files}; 26 | }, (dispatch) => ({dispatch, updatePath})) 27 | export default class Home extends React.Component { 28 | static propTypes = { 29 | files: PropTypes.object, 30 | location: PropTypes.object 31 | } 32 | 33 | constructor(props, context) { 34 | super(props, context); 35 | this.state = { 36 | selected: props.location.query.section || 'upload', error: false, textType: 'text'}; 37 | this.onDrop = this.onDrop.bind(this); 38 | } 39 | 40 | componentWillReceiveProps(nextProps) { 41 | if (nextProps.location) { 42 | this.setState({selected: nextProps.location.query.section}); 43 | } 44 | console.log('next', nextProps) 45 | } 46 | 47 | 48 | render() { 49 | return (
    50 |
    51 |
    52 |

    Share files, the simple way

    53 | We're completely free and there is no need to sign up. But if you want unlimited file uploads and lots of awesome features - It take seconds to sign up with facebook 54 |
    55 |
    56 |
    57 |
    58 |
    59 | 60 |
    Upload File
    61 |
    62 |
    63 |
    64 |
    65 | 66 |
    Web Screenshot
    67 |
    68 |
    69 |
    70 |
    71 | 72 |
    Code Snippet
    73 |
    74 |
    75 |
    76 |
    77 | 78 |
    About dripr
    79 |
    80 |
    81 |
    82 |
    83 | 84 |
    Our App
    85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 | 99 |
    100 |
    101 | 102 |
    103 |
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
    ); 110 | } 111 | 112 | changeView(selected) { 113 | this.setState({selected: selected}); 114 | } 115 | 116 | isValidUrl(value) { 117 | return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value ); 118 | } 119 | 120 | onDrop(files) { 121 | this.setState({files: files}); 122 | nodeify(this.props.dispatch(postFile(files)), (err, value) => { 123 | if (err) 124 | return console.log(err); 125 | 126 | this.props.dispatch(updatePath('/file/' + value.result.hash)); 127 | }) 128 | } 129 | 130 | clickCreateUrl = (value) => { 131 | if(this.isValidUrl(value)) { 132 | this.setState({error: false}); 133 | nodeify(this.props.dispatch(postCode(value, 'text')), (err, value) => { 134 | if (err) 135 | return console.log(err); 136 | 137 | this.props.dispatch(updatePath('/file/' + value.result.hash)); 138 | }) 139 | } else { 140 | this.setState({error: 'You must provide a valid URL so that we can provide you some screenshots.'}) 141 | } 142 | } 143 | 144 | clickSubmit = (text, textType) => { 145 | if(text.length < 20) { 146 | return {error: 'You must enter a text of at least 20 characters.'}; 147 | } 148 | 149 | nodeify(this.props.dispatch(postCode(text, textType)), (err, value) => { 150 | if (err) 151 | return console.log(err); 152 | 153 | this.props.dispatch(updatePath('/file/' + value.result.hash)); 154 | }); 155 | 156 | return true; 157 | } 158 | } 159 | 160 | Home.contextTypes = { 161 | store: PropTypes.object 162 | } 163 | 164 | Home.onEnterLogout = store => (nextState, replaceState, callback) => { 165 | store.dispatch({type: ActionTypes.USER_LOGOUT}); 166 | window.location = "/" 167 | }; 168 | -------------------------------------------------------------------------------- /src/components/pages/List.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {updatePath} from 'redux-simple-router' 4 | import {getData} from '../../actions/data'; 5 | import '../../assets/css/fontello-embedded.css' 6 | import nodeify from 'nodeify'; 7 | import moment from 'moment'; 8 | import File from '../components/list/file' 9 | @connect(state => { 10 | return { 11 | data: state.data.data 12 | }; 13 | }, {updatePath}) 14 | export default class List extends React.Component { 15 | static propTypes = { 16 | data: PropTypes.array.isRequired, 17 | updatePath: PropTypes.func.isRequired 18 | } 19 | 20 | constructor (props, context) { 21 | super(props, context); 22 | 23 | this.state = { 24 | inputValue: '' 25 | }; 26 | } 27 | 28 | render () { 29 | var el = {}; 30 | var elements = []; 31 | this.props.data.forEach((data) => { 32 | var idDate = moment(data.createdAt).format("YMM") 33 | if(!el[idDate]) { el[idDate] = []} 34 | el[idDate].push(); 35 | }) 36 | for (var key in el) { 37 | if (el.hasOwnProperty(key)) { 38 | elements.push(
    {el[key]}
    ) 39 | } 40 | } 41 | return( 42 |
    43 | {elements.reverse()} 44 |
    45 | ); 46 | } 47 | } 48 | 49 | 50 | List.onEnter = store => (nextState, replaceState, callback) => { 51 | nodeify(store.dispatch(getData()), callback); 52 | }; 53 | -------------------------------------------------------------------------------- /src/components/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {loginFacebook} from '../../actions/users'; 4 | import nodeify from 'nodeify'; 5 | 6 | import FacebookLogin from 'react-facebook-login2'; 7 | import LoginForm from '../components/login/login'; 8 | import SignupForm from '../components/login/signup'; 9 | import config from '../../../config'; 10 | const { Container, Card, Row, Col } = require('elemental'); 11 | 12 | @connect(null, (dispatch) => ({dispatch})) 13 | export default class Login extends React.Component { 14 | static propTypes = { 15 | dispatch: PropTypes.func.isRequired 16 | } 17 | responseFacebook(data) { 18 | nodeify(this.props.dispatch(loginFacebook(data)), (err, result) => { 19 | if (result && result.type === 'LOGIN_SUCCESS') { 20 | window.location = "/"; 21 | } 22 | // MANAGE ERRORS 23 | }); 24 | 25 | } 26 | constructor(props, context) { 27 | super(props, context); 28 | this.state = { 29 | selected: 'login' 30 | }; 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 |
    37 |
    38 | 39 |
    40 |
    41 |
    42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/pages/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class NotFound extends React.Component { 4 | render() { 5 | return ( 6 |
    Not Found
    7 | ); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | import keyMirror from 'keymirror'; 2 | 3 | export const ActionTypes = keyMirror({ 4 | DEHYDRATE: null, 5 | REHYDRATE: null, 6 | GET_USER: null, 7 | ASD: null, 8 | GET_FILE: null, 9 | LOGIN: null, 10 | LOGIN_SUCCESS: null, 11 | LOGIN_FAILED: null, 12 | DATA: null, 13 | DATA_SUCCESS: null, 14 | DATA_FAILED: null, 15 | FILE: null, 16 | FILE_SUCCESS: null, 17 | FILE_FAILED: null, 18 | GETUSER: null, 19 | GETUSER_SUCCESS: null, 20 | GETUSER_FAILED: null, 21 | USER_LOGOUT: null, 22 | COMMENTS: null, 23 | COMMENTS_SUCCESS: null, 24 | COMMENTS_FAILED: null, 25 | POSTCOMMENT: null, 26 | POSTCOMMENT_SUCCESS: null, 27 | POSTCOMMENT_FAILED: null 28 | }); 29 | -------------------------------------------------------------------------------- /src/reducers/app.js: -------------------------------------------------------------------------------- 1 | import {handleActions} from 'redux-actions'; 2 | import {ActionTypes} from '../constants'; 3 | 4 | function writeCookie(name,value,days) { 5 | var date, expires; 6 | if (days) { 7 | date = new Date(); 8 | date.setTime(date.getTime()+(days*24*60*60*1000)); 9 | expires = "; expires="+date.toGMTString();} 10 | else{ 11 | expires = ""; 12 | } 13 | document.cookie = name+"="+value+expires+"; path=/"; 14 | } 15 | 16 | export default handleActions({ 17 | [ActionTypes.DEHYDRATE]: (state, action) => ({...state, 18 | title: state.title, 19 | csrfToken: state.csrfToken, 20 | isLogin: state.isLogin, 21 | fetchForServerRendering: false 22 | }), 23 | [ActionTypes.USER_LOGOUT]: state => { 24 | writeCookie('driprauth', '', -2); 25 | return { 26 | ...state, 27 | user: false, 28 | authInfo: false, 29 | loggedIn: false 30 | } 31 | }, 32 | [ActionTypes.LOGIN]: state => { 33 | return { 34 | ...state, 35 | loading: true 36 | } 37 | }, 38 | [ActionTypes.LOGIN_SUCCESS]: (state, action) => { 39 | writeCookie('driprauth', action.result.accessToken, 900); 40 | return { 41 | ...state, 42 | loading: false, 43 | loaded: true, 44 | authInfo: action.result.accessToken, 45 | loggedIn: true, 46 | user: { 47 | name: action.result.name, 48 | email: action.result.email, 49 | id: action.result.id 50 | } 51 | 52 | }}, 53 | [ActionTypes.LOGIN_FAILED]: state => { 54 | return { 55 | ...state, 56 | loading: false, 57 | loaded: false 58 | }}, 59 | 60 | [ActionTypes.GETUSER]: state => { 61 | return { 62 | ...state, 63 | loading: true 64 | } 65 | }, 66 | [ActionTypes.GETUSER_SUCCESS]: (state, action) => { 67 | return { 68 | ...state, 69 | loading: false, 70 | loaded: true, 71 | user: { 72 | name: action.result.name, 73 | email: action.result.email, 74 | id: action.result.id 75 | } 76 | 77 | }}, 78 | [ActionTypes.GETUSER_FAILED]: state => { 79 | return { 80 | ...state, 81 | loading: false, 82 | loaded: false 83 | }} 84 | }, {}); 85 | -------------------------------------------------------------------------------- /src/reducers/data.js: -------------------------------------------------------------------------------- 1 | import {handleActions} from 'redux-actions'; 2 | import Immutable, {OrderedMap} from 'immutable'; 3 | import {ActionTypes} from '../constants'; 4 | 5 | export default handleActions({ 6 | [ActionTypes.DATA]: state => { 7 | return { 8 | ...state, 9 | loading: true 10 | } 11 | }, 12 | [ActionTypes.DATA_SUCCESS]: (state, action) => { 13 | return { 14 | ...state, 15 | loading: false, 16 | loaded: true, 17 | data: action.result 18 | }}, 19 | [ActionTypes.DATA_FAILED]: state => { 20 | return { 21 | ...state, 22 | loading: false, 23 | loaded: false 24 | }} 25 | }, { 26 | }); 27 | -------------------------------------------------------------------------------- /src/reducers/files.js: -------------------------------------------------------------------------------- 1 | import {handleActions} from 'redux-actions'; 2 | import Immutable, {OrderedMap} from 'immutable'; 3 | import {ActionTypes} from '../constants'; 4 | 5 | export default handleActions({ 6 | [ActionTypes.FILE]: state => { 7 | return { 8 | ...state, 9 | loading: true 10 | } 11 | }, 12 | [ActionTypes.FILE_SUCCESS]: (state, action) => { 13 | return { 14 | ...state, 15 | loading: false, 16 | loaded: true, 17 | data: action.result 18 | }}, 19 | [ActionTypes.FILE_FAILED]: state => { 20 | 21 | return { 22 | ...state, 23 | loading: false, 24 | loaded: false 25 | }}, 26 | 27 | [ActionTypes.COMMENTS]: state => { 28 | return { 29 | ...state, 30 | loadingComments: true 31 | } 32 | }, 33 | [ActionTypes.COMMENTS_SUCCESS]: (state, action) => { 34 | return { 35 | ...state, 36 | loadingComments: false, 37 | loadedComments: true, 38 | comments: action.result 39 | }}, 40 | [ActionTypes.COMMENTS_FAILED]: state => { 41 | 42 | return { 43 | ...state, 44 | loadingComments: false, 45 | loadedComments: false 46 | }}, 47 | 48 | [ActionTypes.GET_FILE]: (state, action) => { 49 | if (!action.payload || action.error) return state; 50 | 51 | const {payload} = action; 52 | 53 | return payload; 54 | } 55 | }, { 56 | }); 57 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | 3 | import {routeReducer} from 'redux-simple-router'; 4 | import users from './users'; 5 | import app from './app'; 6 | import files from './files'; 7 | import data from './data'; 8 | export default combineReducers({ 9 | routing: routeReducer, 10 | app, 11 | users, 12 | files, 13 | data 14 | }); 15 | -------------------------------------------------------------------------------- /src/reducers/users.js: -------------------------------------------------------------------------------- 1 | import {handleActions} from 'redux-actions'; 2 | import Immutable, {OrderedMap} from 'immutable'; 3 | import {ActionTypes} from '../constants'; 4 | 5 | function writeCookie(name,value,days) { 6 | var date, expires; 7 | if (days) { 8 | date = new Date(); 9 | date.setTime(date.getTime()+(days*24*60*60*1000)); 10 | expires = "; expires="+date.toGMTString();} 11 | else{ 12 | expires = ""; 13 | } 14 | document.cookie = name+"="+value+expires+"; path=/"; 15 | } 16 | 17 | function readCookie(name) { 18 | var i, c, ca, nameEQ = name + "="; 19 | ca = document.cookie.split(';'); 20 | for(i=0;i < ca.length;i++) { 21 | c = ca[i]; 22 | while (c.charAt(0)==' ') { 23 | c = c.substring(1,c.length); 24 | } 25 | if (c.indexOf(nameEQ) == 0) { 26 | return c.substring(nameEQ.length,c.length); 27 | } 28 | } 29 | return ''; 30 | } 31 | 32 | export default handleActions({ 33 | 34 | 35 | // , 36 | // [ActionTypes.DEHYDRATE]: state => { 37 | // console.log('ads') 38 | // return { 39 | // store: state.store.valueSeq().toJS() 40 | // }} 41 | // , 42 | // 43 | // [ActionTypes.ASD]: (state, action) => { 44 | // console.log('adsd') 45 | // console.log(action) 46 | // console.log(state) 47 | // return { 48 | // ...state, 49 | // store: state.store.set('asd', {'hell': 'yeah'}) 50 | // }}, 51 | // 52 | // [ActionTypes.REHYDRATE]: state => ({ 53 | // store: new OrderedMap().withMutations(store => { 54 | // state.store.forEach(item => { 55 | // store.set(item.id, Immutable.fromJS(item)); 56 | // }); 57 | // }) 58 | // }), 59 | // 60 | // [ActionTypes.GET_USER]: (state, action) => { 61 | // if (!action.payload || action.error) return state; 62 | // 63 | // const {payload} = action; 64 | // 65 | // return { 66 | // ...state, 67 | // store: state.store.set(payload.login, Immutable.fromJS(payload)) 68 | // }; 69 | // } 70 | }, { 71 | }); 72 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import App from './components/pages/App'; 2 | import Home from './components/pages/Home'; 3 | import Login from './components/pages/Login'; 4 | import List from './components/pages/List'; 5 | import File from './components/pages/File'; 6 | import NotFound from './components/pages/NotFound'; 7 | import Downloads from './components/pages/Downloads' 8 | export default function getRoutes(store) { 9 | return { 10 | component: App, 11 | childRoutes: [ 12 | { 13 | path: '/', 14 | component: Home 15 | }, 16 | { 17 | path: '/logout', 18 | component: Home, 19 | onEnter: Home.onEnterLogout(store) 20 | }, 21 | { 22 | path: '/login', 23 | component: Login 24 | }, 25 | { 26 | path: '/downloads', 27 | component: Downloads 28 | }, 29 | { 30 | path: '/list', 31 | component: List, 32 | onEnter: List.onEnter(store) 33 | }, 34 | { 35 | path: '/file/:id', 36 | name: 'dhd', 37 | component: File, 38 | onEnter: File.onEnter(store) 39 | }, 40 | { 41 | path: '*', 42 | component: NotFound 43 | } 44 | ], 45 | onEnter: App.onEnter(store) 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {renderToString, renderToStaticMarkup} from 'react-dom/server'; 3 | import {match, RoutingContext} from 'react-router'; 4 | import getRoutes from './routes'; 5 | import cookie from 'cookie' 6 | import HtmlDocument from './components/HtmlDocument'; 7 | import configureStore from './store/configureStore.prod'; 8 | import Root from './components/Root.prod'; 9 | import apiClient from './apiClient'; 10 | 11 | function isMobile(userAgent) { 12 | if (userAgent.match(/Android/i) || userAgent.match(/webOS/i) || userAgent.match(/iPhone/i) || userAgent.match(/iPad/i) || userAgent.match(/iPod/i) || userAgent.match(/BlackBerry/i) || userAgent.match(/Windows Phone/i)) { 13 | return true; 14 | } else { 15 | return false; 16 | } 17 | } 18 | 19 | export default function createHtmlResponse({webpackStats, request}, callback) { 20 | const cookies = cookie.parse(request.headers.cookie || ''); 21 | const initialState = { 22 | app: { 23 | status: 200, 24 | title: 'Dripr', 25 | fetchForServerRendering: true, 26 | authInfo: cookies.driprauth, 27 | loggedIn: cookies.driprauth ? true : false, 28 | isMobile: isMobile(request.headers['user-agent']) 29 | } 30 | }; 31 | //console.log(request.headers) 32 | 33 | const store = configureStore(initialState, apiClient(cookies.driprauth)); 34 | 35 | const routes = getRoutes(store); 36 | 37 | match({ 38 | routes, 39 | location: request.url 40 | }, (err, redirectLocation, routerState) => { 41 | if (err) return callback(err); 42 | 43 | if (redirectLocation) { 44 | return callback(null, { 45 | status: 302, 46 | url: redirectLocation.pathname + redirectLocation.search 47 | }); 48 | } 49 | 50 | if (!routerState) { 51 | return callback(null, { 52 | status: 404 53 | }); 54 | } 55 | 56 | const status = store.getState().app.status; 57 | 58 | const content = renderToString( 59 | 60 | 61 | 62 | ); 63 | 64 | const html = renderToStaticMarkup( 65 | 69 | ); 70 | 71 | callback(null, { 72 | status, 73 | body: '' + html 74 | }); 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/store/composeStore.js: -------------------------------------------------------------------------------- 1 | import {createStore, compose} from 'redux'; 2 | import rootReducer from '../reducers'; 3 | import middlewares from './middlewares'; 4 | 5 | export default function composeStore(initialState, client, ...functions) { 6 | const store = compose( 7 | middlewares(client), 8 | ...functions 9 | )(createStore)(rootReducer, initialState); 10 | 11 | return store; 12 | } 13 | -------------------------------------------------------------------------------- /src/store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | import {persistState} from 'redux-devtools'; 2 | import DevTools from '../components/DevTools'; 3 | import composeStore from './composeStore'; 4 | 5 | export default function configureStore(initialState, client) { 6 | const store = composeStore( 7 | initialState, 8 | client, 9 | DevTools.instrument(), 10 | persistState( 11 | window.location.href.match( 12 | /[?&]debug_session=([^&]+)\b/ 13 | ) 14 | ) 15 | ); 16 | 17 | if (module.hot) { 18 | module.hot.accept('../reducers', () => 19 | store.replaceReducer(require('../reducers')) 20 | ); 21 | } 22 | 23 | return store; 24 | } 25 | -------------------------------------------------------------------------------- /src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'development') { 2 | module.exports = require('./configureStore.dev'); 3 | } else { 4 | module.exports = require('./configureStore.prod'); 5 | } 6 | -------------------------------------------------------------------------------- /src/store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | import composeStore from './composeStore'; 2 | 3 | export default function configureStore(initialState, client) { 4 | return composeStore(initialState, client); 5 | } 6 | -------------------------------------------------------------------------------- /src/store/middlewares.js: -------------------------------------------------------------------------------- 1 | import {applyMiddleware} from 'redux'; 2 | import promise from 'redux-promise'; 3 | import thunk from 'redux-thunk'; 4 | 5 | export default function(client) { 6 | return applyMiddleware( 7 | clientMiddleware(client), 8 | thunk, 9 | promise 10 | ); 11 | } 12 | 13 | function clientMiddleware(client) { 14 | return ({dispatch, getState}) => { 15 | return next => action => { 16 | if (typeof action === 'function') { 17 | return action(dispatch, getState); 18 | } 19 | 20 | const { promise, types, ...rest } = action; // eslint-disable-line no-redeclare 21 | if (!promise) { 22 | return next(action); 23 | } 24 | 25 | const [REQUEST, SUCCESS, FAILURE] = types; 26 | next({...rest, type: REQUEST}); 27 | return promise(client).then(filterError).then(parseJSON).then( 28 | (result) => next({...rest, result, type: SUCCESS}), 29 | (error) => next({...rest, error, type: FAILURE}) 30 | ).catch((error)=> { 31 | console.error('MIDDLEWARE ERROR:', error); 32 | next({...rest, error, type: FAILURE}); 33 | }); 34 | }; 35 | }; 36 | } 37 | 38 | function parseJSON(res) { 39 | return res.json(); 40 | } 41 | 42 | 43 | function filterError(res) { 44 | if (res.status < 200 || res.status > 300) { 45 | const contentType = res.headers.get('Content-Type'); 46 | 47 | if (!~contentType.indexOf('json')) { 48 | return Promise.reject(new ResponseError(res)); 49 | } 50 | 51 | return res.json().then(json => { 52 | return json; 53 | }, noop).then(json => { 54 | return Promise.reject(new ResponseError(res, json)); 55 | }); 56 | } 57 | 58 | return res; 59 | } 60 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | 3 | module.exports = require('./webpack/prod.config').default; 4 | -------------------------------------------------------------------------------- /webpack/config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | import merge from 'lodash/object/merge'; 4 | import fs from 'graceful-fs'; 5 | import config from '../config'; 6 | 7 | const commonConfig = { 8 | resolve: { 9 | extensions: ['', '.js', '.jsx', '.json'] 10 | }, 11 | module: { 12 | loaders: [ 13 | { 14 | test: /\.json$/, 15 | loader: 'json' 16 | } 17 | ] 18 | }, 19 | plugins: [ 20 | new webpack.EnvironmentPlugin([ 21 | 'NODE_ENV' 22 | ]), 23 | new webpack.DefinePlugin({ 24 | GA_TRACKING_CODE: JSON.stringify(config.config.googleAnalytics), 25 | __ADSOPTIMAL__: JSON.stringify(config.config.adsOptimal) 26 | }) 27 | ], 28 | progress: true 29 | }; 30 | 31 | export const client = merge({}, commonConfig, { 32 | entry: { 33 | main: [ 34 | './src/client' 35 | ], 36 | vendor: [ 37 | 'react', 38 | 'react-router', 39 | 'immutable' 40 | ] 41 | }, 42 | output: { 43 | path: path.join(__dirname, '../public/build'), 44 | filename: '[name].js', 45 | chunkFilename: '[name].js', 46 | publicPath: '/build/' 47 | }, 48 | plugins: commonConfig.plugins.concat([ 49 | new webpack.DefinePlugin({ 50 | 'typeof window': JSON.stringify('object') 51 | }) 52 | ]) 53 | }); 54 | 55 | export const server = merge({}, commonConfig, { 56 | entry: { 57 | main: [ 58 | './src/server' 59 | ] 60 | }, 61 | target: 'node', 62 | output: { 63 | library: true, 64 | libraryTarget: 'commonjs2', 65 | pathinfo: true, 66 | path: path.join(__dirname, '../server/build'), 67 | filename: '[name].js', 68 | chunkFilename: '[name].js', 69 | publicPath: '/build/' 70 | }, 71 | externals: fs.readdirSync(path.join(__dirname, '../node_modules')) 72 | .map(key => new RegExp(`^${key}`)) 73 | }); 74 | -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/object/merge'; 2 | import webpack from 'webpack'; 3 | import fs from 'graceful-fs'; 4 | import path from 'path'; 5 | import * as config from './config'; 6 | import writeStats from './utils/write-stats'; 7 | import notifyStats from './utils/notify-stats'; 8 | var Visualizer = require('webpack-visualizer-plugin'); 9 | const babelrc = JSON.parse(fs.readFileSync(path.join(__dirname, '../.babelrc'), 'utf8')); 10 | 11 | export const client = merge({}, config.client, { 12 | devtool: 'eval', 13 | entry: { 14 | main: [ 15 | 'webpack-hot-middleware/client' 16 | ].concat(config.client.entry.main) 17 | }, 18 | module: { 19 | loaders: config.client.module.loaders.concat([ 20 | { 21 | test: /\.jsx?$/, 22 | loader: 'babel', 23 | exclude: /node_modules/, 24 | query: merge({}, babelrc, { 25 | plugins: [ 26 | 'react-transform' 27 | ], 28 | extra: { 29 | 'react-transform': { 30 | transforms: [ 31 | { 32 | transform: 'react-transform-hmr', 33 | imports: ['react'], 34 | locals: ['module'] 35 | } 36 | ] 37 | } 38 | } 39 | }) 40 | }, 41 | { 42 | test: /\.less$/, 43 | loader: "style!css!less" 44 | }, 45 | { 46 | test: /\.css$/, 47 | loader: 'style!css' 48 | } 49 | ]) 50 | }, 51 | plugins: config.client.plugins.concat([ 52 | new Visualizer(), 53 | // hot reload 54 | new webpack.HotModuleReplacementPlugin(), 55 | new webpack.NoErrorsPlugin(), 56 | new webpack.DefinePlugin({ 57 | __CLIENT__: true, 58 | __SERVER__: false, 59 | __DEVELOPMENT__: true, 60 | __DEVTOOLS__: true, 61 | __CONFIG__: config // <-------- ENABLE redux-devtools HERE 62 | }), 63 | // optimize 64 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'), 65 | new webpack.optimize.DedupePlugin(), 66 | new webpack.optimize.OccurenceOrderPlugin(), 67 | 68 | // stats 69 | function() { 70 | this.plugin('done', writeStats); 71 | this.plugin('done', notifyStats); 72 | } 73 | ]) 74 | }); 75 | 76 | export const server = merge({}, config.server, { 77 | module: { 78 | loaders: config.server.module.loaders.concat([ 79 | { 80 | test: /\.jsx?$/, 81 | loader: 'babel', 82 | exclude: /node_modules/ 83 | }, 84 | { 85 | test: /\.css$/, 86 | loader: 'null' 87 | }, 88 | { 89 | test: /\.less$/, 90 | loader: 'null' 91 | } 92 | ]) 93 | }, 94 | plugins: config.server.plugins.concat([ 95 | new webpack.NoErrorsPlugin(), 96 | new webpack.DefinePlugin({ 97 | __CLIENT__: false, 98 | __SERVER__: true, 99 | __DEVELOPMENT__: true, 100 | __DEVTOOLS__: true, 101 | __CONFIG__: config // <-------- ENABLE redux-devtools HERE 102 | }), 103 | // optimize 104 | new webpack.optimize.DedupePlugin(), 105 | new webpack.optimize.OccurenceOrderPlugin() 106 | ]) 107 | }); 108 | 109 | export default [client, server]; 110 | -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/object/merge'; 2 | import webpack from 'webpack'; 3 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 4 | import * as config from './config'; 5 | import writeStats from './utils/write-stats'; 6 | 7 | export const client = merge({}, config.client, { 8 | devtool: 'source-map', 9 | output: { 10 | filename: '[name]-[hash:8].js', 11 | chunkFilename: '[name]-[chunkhash:8].js' 12 | }, 13 | module: { 14 | loaders: config.client.module.loaders.concat([ 15 | { 16 | test: /\.jsx?$/, 17 | loaders: ['babel'], 18 | exclude: /node_modules/ 19 | }, 20 | { 21 | test: /\.less$/, 22 | loader: ExtractTextPlugin.extract('style', 'css!less') 23 | }, 24 | { 25 | test: /\.css$/, 26 | loader: ExtractTextPlugin.extract('style', 'css') 27 | } 28 | ]) 29 | }, 30 | plugins: config.client.plugins.concat([ 31 | // extract css files 32 | new ExtractTextPlugin('[name]-[contenthash:8].css', { 33 | allChunks: true 34 | }), 35 | new webpack.DefinePlugin({ 36 | __CLIENT__: true, 37 | __SERVER__: false, 38 | __DEVELOPMENT__: false, 39 | __DEVTOOLS__: false, 40 | __CONFIG__: config // <-------- ENABLE redux-devtools HERE 41 | }), 42 | // optimize 43 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor-[hash:8].js'), 44 | new webpack.optimize.DedupePlugin(), 45 | new webpack.optimize.OccurenceOrderPlugin(), 46 | new webpack.optimize.UglifyJsPlugin({ 47 | compress: { 48 | warnings: false 49 | } 50 | }), 51 | // stats 52 | function() { 53 | this.plugin('done', writeStats); 54 | } 55 | ]) 56 | }); 57 | 58 | export const server = merge({}, config.server, { 59 | devtool: 'source-map', 60 | module: { 61 | loaders: config.server.module.loaders.concat([ 62 | { 63 | test: /\.jsx?$/, 64 | loader: 'babel', 65 | exclude: /node_modules/ 66 | }, 67 | { 68 | test: /\.css$/, 69 | loader: 'null' 70 | }, 71 | { 72 | test: /\.less$/, 73 | loader: 'null' 74 | } 75 | ]) 76 | }, 77 | plugins: config.server.plugins.concat([ 78 | new webpack.NoErrorsPlugin(), 79 | new webpack.DefinePlugin({ 80 | __CLIENT__: false, 81 | __SERVER__: true, 82 | __DEVELOPMENT__: false, 83 | __DEVTOOLS__: false, 84 | __CONFIG__: config // <-------- ENABLE redux-devtools HERE 85 | }), 86 | // optimize 87 | new webpack.optimize.DedupePlugin(), 88 | new webpack.optimize.OccurenceOrderPlugin(), 89 | new webpack.optimize.UglifyJsPlugin({ 90 | compress: { 91 | warnings: false 92 | } 93 | }) 94 | ]) 95 | }); 96 | 97 | export default [client, server]; 98 | -------------------------------------------------------------------------------- /webpack/server.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | 3 | module.exports = require('./dev.config').server; 4 | -------------------------------------------------------------------------------- /webpack/utils/mkdirs.js: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import pathFn from 'path'; 3 | 4 | function mkdirs(path) { 5 | if (fs.existsSync(path)) return; 6 | 7 | let parent = pathFn.dirname(path); 8 | 9 | if (!fs.existsSync(parent)) { 10 | mkdirs(parent); 11 | } 12 | 13 | fs.mkdirSync(path); 14 | } 15 | 16 | export default mkdirs; 17 | -------------------------------------------------------------------------------- /webpack/utils/notify-stats.js: -------------------------------------------------------------------------------- 1 | function notifyError(err) { 2 | console.log('\x07' + err); 3 | } 4 | 5 | function notifyWarning(warn) { 6 | console.log(warn); 7 | } 8 | 9 | function notifyStats(stats) { 10 | var json = stats.toJson(); 11 | 12 | if (json.errors.length) { 13 | notifyError(json.errors[0]); 14 | // json.errors.forEach(notifyError); 15 | } else if (json.warnings.length) { 16 | json.warnings.forEach(notifyWarning); 17 | } else { 18 | console.log(stats.toString({ 19 | chunks: false, 20 | colors: true 21 | })); 22 | } 23 | } 24 | 25 | export default notifyStats; 26 | -------------------------------------------------------------------------------- /webpack/utils/write-stats.js: -------------------------------------------------------------------------------- 1 | import fs from 'graceful-fs'; 2 | import pathFn from 'path'; 3 | import mkdirs from './mkdirs'; 4 | 5 | const STATS_FILENAME = 'webpack-stats.json'; 6 | 7 | function writeStats(stats) { 8 | let publicPath = this.options.output.publicPath; 9 | let json = stats.toJson(); 10 | let chunks = json.assetsByChunkName; 11 | let content = {}; 12 | 13 | Object.keys(chunks).forEach(key => { 14 | let assets = chunks[key]; 15 | if (!Array.isArray(assets)) assets = [assets]; 16 | 17 | let chunkContent = {}; 18 | 19 | assets.forEach(asset => { 20 | let extname = pathFn.extname(asset).substring(1); 21 | 22 | if (!chunkContent.hasOwnProperty(extname)) { 23 | chunkContent[extname] = []; 24 | } 25 | 26 | chunkContent[extname].push(publicPath + asset); 27 | }); 28 | 29 | content[key] = chunkContent; 30 | }); 31 | 32 | mkdirs(this.options.output.path); 33 | fs.writeFileSync(pathFn.join(this.options.output.path, STATS_FILENAME), JSON.stringify(content)); 34 | } 35 | 36 | module.exports = writeStats; 37 | --------------------------------------------------------------------------------