├── .prettierignore ├── .prettierrc ├── netlify.toml ├── public ├── favicon.ico ├── lorem-ipsum-generator-screenshot.png ├── manifest.json └── index.html ├── src ├── App.test.jsx ├── Footer.css ├── setupProxy.js ├── index.css ├── index.jsx ├── Footer.jsx ├── lambda │ ├── generate-lorem-ipsum.test.js │ ├── generate-lorem-ipsum.js │ └── words.js ├── logo.svg ├── App.jsx ├── App.css └── serviceWorker.js ├── .gitignore ├── .github └── FUNDING.yml ├── .eslintrc.js ├── package.json └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | built-lambda/ 2 | node_modules/ 3 | public/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "yarn build" 3 | functions = "built-lambda" 4 | publish = "build" 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/greys-anatomy-lorem-ipsum-generator/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/lorem-ipsum-generator-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/greys-anatomy-lorem-ipsum-generator/HEAD/public/lorem-ipsum-generator-screenshot.png -------------------------------------------------------------------------------- /src/App.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/Footer.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | font-weight: bold; 3 | padding: 1em; 4 | background-color: #1e2020; 5 | color: #c4e4e7; 6 | } 7 | 8 | p { 9 | font-size: 0.8em; 10 | } 11 | 12 | a { 13 | color: #b83132; 14 | text-decoration: none; 15 | } 16 | 17 | a:hover { 18 | color: #c93737; 19 | } 20 | -------------------------------------------------------------------------------- /src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require('http-proxy-middleware'); 2 | 3 | module.exports = function (app) { 4 | app.use( 5 | proxy('/.netlify/functions/', { 6 | target: 'http://localhost:9000/', 7 | pathRewrite: { 8 | '^/\\.netlify/functions': '', 9 | }, 10 | }), 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /built-lambda 12 | 13 | /.netlify 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /src/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Footer.css'; 3 | 4 | export default function Footer() { 5 | return ( 6 |
7 |

Grey's Anatomy Lorem Ipsum Generator

8 |

9 | By: Monica Powell •{' '} 10 | 11 | View Code 12 | 13 |

14 |

15 | Copyright to "Grey's Anatomy" is held by various outside 16 | entities and is provided here for educational purposes only. 17 |

18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: m0nica # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: m0nica # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true, 6 | 'jest/globals': true, 7 | }, 8 | extends: ['plugin:react/recommended', 'airbnb', 'prettier'], 9 | globals: { 10 | Atomics: 'readonly', 11 | SharedArrayBuffer: 'readonly', 12 | }, 13 | parserOptions: { 14 | ecmaFeatures: { 15 | jsx: true, 16 | }, 17 | ecmaVersion: 2018, 18 | sourceType: 'module', 19 | }, 20 | plugins: ['react', 'jest'], 21 | rules: { 22 | 'jest/no-disabled-tests': 'warn', 23 | 'jest/no-focused-tests': 'error', 24 | 'jest/no-identical-title': 'error', 25 | 'jest/prefer-to-have-length': 'warn', 26 | 'jest/valid-expect': 'error', 27 | }, 28 | ignorePatterns: ['node_modules', 'public', 'built-lambda'], 29 | }; 30 | -------------------------------------------------------------------------------- /src/lambda/generate-lorem-ipsum.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | handler, 3 | generateWords, 4 | generateParagraphs, 5 | } from './generate-lorem-ipsum'; 6 | 7 | test('generateParagraphs', () => { 8 | expect(typeof generateParagraphs(50)).toBe('string'); 9 | 10 | expect( 11 | generateParagraphs(11) 12 | .join(',') 13 | .split('

') 14 | ).toHaveLength(12); 15 | 16 | expect( 17 | generateParagraphs(500) 18 | .join(',') 19 | .split('

') 20 | ).toHaveLength(501); 21 | }); 22 | 23 | test('generateWords', () => { 24 | expect(typeof generateWords(50)).toBe('string'); 25 | expect(generateWords(50).split(',')).toBeTruthy(); 26 | }); 27 | 28 | test('handler', async () => { 29 | const event = { 30 | queryStringParameters: { 31 | paragraphs: '4', 32 | words: '2', 33 | }, 34 | }; 35 | const context = 'context'; 36 | const callback = (error, response) => { 37 | expect(response.statusCode).toEqual(200); 38 | expect(typeof response.body).toBe('string'); 39 | }; 40 | await handler(event, context, callback); 41 | }); 42 | -------------------------------------------------------------------------------- /src/lambda/generate-lorem-ipsum.js: -------------------------------------------------------------------------------- 1 | import WORDS from './words'; 2 | 3 | export function getRandomInt() { 4 | return Math.floor(Math.random() * Math.floor(WORDS.length)); 5 | } 6 | 7 | export function generateWords(wordCount) { 8 | const words = Array(wordCount) 9 | .fill() 10 | .map(() => WORDS[getRandomInt()]); 11 | 12 | return `

${words.join(' ')}

`; 13 | } 14 | 15 | export function generateParagraphs(paragraphCount) { 16 | return Array(paragraphCount) 17 | .fill() 18 | .map(() => generateWords(50)); 19 | } 20 | 21 | export function generateLoremIpsum(isParagraph, count) { 22 | return isParagraph ? generateParagraphs(count) : generateWords(count); 23 | } 24 | 25 | export function handler(event, context, callback) { 26 | const { queryStringParameters } = event; 27 | const { paragraphs = 0, words = 0 } = queryStringParameters; 28 | 29 | let isParagraph = Boolean(paragraphs); 30 | let count; 31 | 32 | if (paragraphs >= 1) { 33 | count = paragraphs; 34 | } else if (words > 0) { 35 | count = words; 36 | } else { 37 | isParagraph = true; 38 | count = 4; 39 | } 40 | 41 | let response; 42 | 43 | try { 44 | response = isParagraph 45 | ? generateLoremIpsum(isParagraph, parseInt(count, 10)).join(' ') 46 | : generateLoremIpsum(isParagraph, parseInt(count, 10)); 47 | } catch (error) { 48 | // eslint-disable-next-line no-console 49 | console.warn(error); 50 | } 51 | 52 | callback(null, { 53 | statusCode: 200, 54 | body: JSON.stringify({ msg: response }), 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-lambda", 3 | "version": "0.4.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.19.0", 7 | "clipboard": "^2.0.4", 8 | "html-react-parser": "^0.7.1", 9 | "http-proxy-middleware": "^0.19.0", 10 | "react": "^16.8.6", 11 | "react-dom": "^16.8.6", 12 | "react-scripts": "^3.0.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "start:lambda": "netlify-lambda serve src/lambda", 17 | "build": "run-p build:**", 18 | "build:app": "react-scripts build", 19 | "build:lambda": "netlify-lambda build src/lambda", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject", 22 | "lint": "prettier --write '**/*{.js,.jsx}' && eslint --fix '**/*{.js,.jsx}' --ignore-pattern node_modules/" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "@babel/plugin-transform-object-assign": "^7.0.0", 35 | "babel-loader": "8.0.6", 36 | "eslint": "^6.8.0", 37 | "eslint-config-airbnb": "^18.1.0", 38 | "eslint-config-prettier": "^6.11.0", 39 | "eslint-plugin-import": "^2.20.1", 40 | "eslint-plugin-jest": "^23.8.2", 41 | "eslint-plugin-jsx-a11y": "^6.2.3", 42 | "eslint-plugin-react": "^7.19.0", 43 | "eslint-plugin-react-hooks": "^2.5.0", 44 | "netlify-lambda": "^1.4.5", 45 | "npm-run-all": "^4.1.5", 46 | "prettier": "1.19.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 17 | 26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | 47 | 48 | 53 | 54 | Grey's Anatomy Lorem Ipsum 55 | 56 | 57 | 60 |
61 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `greys-anatomy-lorem-ipsum-generator` is a website that generates lorem ipsum text inspired by [Grey's Anatomy](https://greysanatomy.fandom.com/wiki/Grey%27s_Anatomy_Universe_Wiki) and medical terms. Lorem ipsum is nonsense text that can be used as place holder copy in designs. Additional terms can be added in the [`/lambda/words.js`](https://github.com/M0nica/greys-anatomy-lorem-ipsum-generator/blob/master/src/lambda/words.js) file 2 | 3 | 4 | ![screenshot of lorem ipsum generator](https://github.com/M0nica/greys-anatomy-lorem-ipsum-generator/blob/master/public/lorem-ipsum-generator-screenshot.png?raw=true) 5 | 6 | 7 | This project's infra boilerplate was[create-react-app-lambda](https://github.com/netlify/create-react-app-lambda) which was created from latest versions of [Create React App v3](https://github.com/facebookincubator/create-react-app) and [netlify-lambda v1](https://github.com/netlify/netlify-lambda). 8 | 9 | The main addition to base Create-React-App is a new folder: `src/lambda`. Each JavaScript file in there will be built for Lambda function deployment in `/built-lambda`, specified in [`netlify.toml`](https://www.netlify.com/docs/netlify-toml-reference/). 10 | 11 | 12 | ## Local Development 13 | 14 | Before developing, fork the repository and run `yarn` from the root of the repo to install all dependencies. 15 | 16 | ### Start each server individually 17 | Both need to be run at the same time, in separate windows, in order for the application to properly run locally 18 | 19 | **Run the functions dev server** 20 | 21 | From inside the project folder, run: 22 | 23 | ``` 24 | yarn start:lambda 25 | ``` 26 | 27 | This will open a local server running at `http://localhost:9000` serving your Lambda functions, updating as you make changes in the `src/lambda` folder. 28 | 29 | You can then access your functions directly at `http://localhost:9000/{function_name}`, but to access them with the app, you'll need to start the app dev server. Under the hood, this uses `react-scripts`' [advanced proxy feature](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#configuring-the-proxy-manually) with the `setupProxy.js` file. 30 | 31 | **Run the app dev server** 32 | 33 | While the functions server is still running, open a new terminal tab and run: 34 | 35 | ``` 36 | yarn start 37 | ``` 38 | 39 | This will start the normal create-react-app dev server and open your app at `http://localhost:3000`. 40 | 41 | Local in-app requests to the relative path `/.netlify/functions/*` will automatically be proxied to the local functions dev server. 42 | 43 | > Note: You can also use [npm-run-all](https://github.com/mysticatea/npm-run-all#readme) to run the functions dev server and app dev server concurrently. Note that you don't need this if you use [`netlify dev`](https://github.com/netlify/netlify-dev-plugin/) as [function builder detection](https://www.netlify.com/blog/2019/04/24/zero-config-yet-technology-agnostic-how-netlify-dev-detectors-work/) does that for you. 44 | 45 | ## Service Worker 46 | 47 | The service worker does not work with lambda functions out of the box. It prevents calling the function and returns the app itself instead ([Read more](https://github.com/facebook/create-react-app/issues/2237#issuecomment-302693219)). To solve this you have to eject and enhance the service worker configuration in the webpack config. Whitelist the path of your lambda function and you are good to go. 48 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import parse, { domToReact } from 'html-react-parser'; 3 | import ClipboardJS from 'clipboard'; 4 | import Footer from './Footer'; 5 | import './App.css'; 6 | 7 | function Quotes() { 8 | const [quotes, setQuotes] = useState({ loading: false, msg: null }); 9 | const { loading, msg } = quotes; 10 | const api = 'generate-lorem-ipsum'; 11 | 12 | const [paragraphCount, setParagraphCount] = useState(4); 13 | 14 | const handleChange = e => { 15 | const num = parseInt(e.target.value, 10); 16 | if (!Number.isNaN(num) && num >= 2 && num <= 50) { 17 | setParagraphCount(num); 18 | } 19 | }; 20 | 21 | function handleClick(event) { 22 | event.preventDefault(); 23 | 24 | setQuotes({ ...quotes, loading: true }); 25 | fetch(`/.netlify/functions/${api}?paragraphs=${paragraphCount}`) 26 | .then(response => response.json()) 27 | .then(json => setQuotes({ ...quotes, msg: json.msg })) 28 | // eslint-disable-next-line no-console 29 | .catch(err => console.warn(err)) 30 | .finally(setQuotes({ ...quotes, loading: false })); 31 | } 32 | 33 | const parseOptions = { 34 | replace({ name, children }) { 35 | if (name === 'p') { 36 | const id = `p${Math.random() 37 | .toString(36) 38 | .substr(2, 5)}`; 39 | 40 | return ( 41 |
42 |

{domToReact(children)}

43 |
44 | ); 45 | } 46 | }, 47 | }; 48 | 49 | let btnCopyAll = ''; 50 | 51 | if (msg) { 52 | btnCopyAll = ( 53 | 56 | ); 57 | } 58 | 59 | return ( 60 | <> 61 |
62 | 81 |
82 | 83 | {msg && btnCopyAll} 84 |
{msg && parse(msg, parseOptions)}
85 | 86 | ); 87 | } 88 | 89 | function App() { 90 | new ClipboardJS('#btn-copy-all', { 91 | text() { 92 | return [].map 93 | .call(document.querySelectorAll('#paragraphs p'), p => { 94 | return p.textContent; 95 | }) 96 | .join('\n'); 97 | }, 98 | }); 99 | 100 | new ClipboardJS('.btn-copy'); 101 | 102 | return ( 103 |
104 |
105 |

106 | Grey's Anatomy 107 |
108 | Lorem Ipsum Generator 109 |

110 | 111 |
112 |
114 | ); 115 | } 116 | 117 | export default App; 118 | -------------------------------------------------------------------------------- /src/lambda/words.js: -------------------------------------------------------------------------------- 1 | const greysAnatomyCharacters = [ 2 | 'Derek', 3 | 'Shepherd', 4 | 'Meredith', 5 | 'Grey', 6 | 'Mark', 7 | 'Sloan', 8 | 'Yang', 9 | 'Callie', 10 | 'Izzie', 11 | 'Arizona', 12 | 'Bailey', 13 | "O'Malley", 14 | 'Teddy', 15 | 'Denny', 16 | 'Duquette', 17 | 'Maggie', 18 | 'Adele', 19 | 'Webber', 20 | 'Karev', 21 | 'Seattle', 22 | 'Mercy West', 23 | 'Alexandra', 24 | 'Dr. Lexie Grey', 25 | 'April', 26 | 'Dr. April Kepner', 27 | 'Dr. Arizona Robbins', 28 | 'Calliope', 29 | 'Dr. Callie Torres', 30 | 'Cristina', 31 | 'Dr.Cristina Yang', 32 | 'Dr. Cristina Yang', 33 | 'Erica', 34 | 'Dr. Erica Hahn', 35 | 'Ellis', 36 | 'Meredith’s mother', 37 | 'Torres', 38 | 'Isobel', 39 | 'Dr. Izzie Stevens', 40 | 'Lucy', 41 | 'Dr. Lucy Fields', 42 | 'Shane', 43 | 'Dr. Shane Ross', 44 | 'Thatcher', 45 | 'Meredith’s father', 46 | 'Tucker', 47 | 'Tyler', 48 | 'Tyler Christian', 49 | 'Margaret', 50 | 'Dr. Margaret Campbell', 51 | 'Dr. Meredith Grey', 52 | 'Miranda', 53 | 'Dr. Miranda Bailey', 54 | 'Olivia', 55 | 'Olivia Harper', 56 | 'Sadie', 57 | 'Dr. Sadie Harris', 58 | 'Sydney', 59 | 'Dr. Sydney Heron', 60 | 'Theodora', 61 | 'Dr. Teddy Altman', 62 | 'Virginia', 63 | 'Dr. Virginia Dixon', 64 | 'Zola', 65 | 'Alex', 66 | 'Dr. Alex Karev', 67 | 'Benjamin', 68 | 'Dr. Ben Warren', 69 | 'Colin', 70 | 'Dr. Colin Marlowe', 71 | 'Dr. Derek Shepherd', 72 | 'Finn', 73 | 'Dr. Finn Dandridge', 74 | 'George', 75 | 'Dr. George O’Malley', 76 | 'Jackson', 77 | 'Dr. Jackson Avery', 78 | 'Dr. Mark Sloan', 79 | 'Norman', 80 | 'Dr. Norman Shales', 81 | 'Preston', 82 | 'Dr. Preston Burke', 83 | 'Richard', 84 | 'Dr. Richard Webber', 85 | 'Robert', 86 | 'Dr. Robert Stark', 87 | 'Owen', 88 | 'Dr. Owen Hunt', 89 | ]; 90 | const greysAnatomyWords = [ 91 | ...greysAnatomyCharacters, 92 | 'Chief', 93 | 'Surgery', 94 | 'surgicial', 95 | 'surgeon', 96 | 'orthopedics', 97 | 'hospital', 98 | 'cried', 99 | 'push one of epi', 100 | "he's in V-Fib", 101 | "she's in V-Fib", 102 | 'intubate him', 103 | 'intubate her', 104 | 'whipple', 105 | 'get him up to CT', 106 | 'get her up to CT', 107 | '10-blade', 108 | 'start a central line', 109 | 'my person', 110 | 'cardio', 111 | 'benign', 112 | 'surgical resident', 113 | 'a trauma surgical fellow', 114 | 'chief of pediatric surgery', 115 | 'orthopedic surgeon', 116 | 'cardiac surgical fellow', 117 | 'a surgical resident', 118 | 'a cardiac surgeon', 119 | 'an OB-GYN', 120 | 'a general surgeon', 121 | 'a nurse', 122 | 'an intern', 123 | 'a surgery resident', 124 | 'an anesthesiologist', 125 | 'a neurosurgeon', 126 | 'a veterinarian', 127 | 'a plastic surgeon', 128 | 'chief of surgery', 129 | 'a pediatric surgeon', 130 | 'chief of plastic surgery', 131 | 'my person', 132 | ]; 133 | const fillerWords = [ 134 | 'actual', 135 | 'actually', 136 | 'amazing', 137 | 'anyway', 138 | 'apparently', 139 | 'approximately', 140 | 'badly', 141 | 'basically', 142 | 'begin', 143 | 'certainly', 144 | 'clearly', 145 | 'completely', 146 | 'definitely', 147 | 'easily', 148 | 'effectively', 149 | 'entirely', 150 | 'especially', 151 | 'essentially', 152 | 'exactly', 153 | 'extremely', 154 | 'fairly', 155 | 'frankly', 156 | 'frequently', 157 | 'fully', 158 | 'generally', 159 | 'hardly', 160 | 'heavily', 161 | 'highly', 162 | 'hopefully', 163 | 'just', 164 | 'largely', 165 | 'like', 166 | 'literally', 167 | 'maybe', 168 | 'might', 169 | 'most', 170 | 'mostly', 171 | 'much', 172 | 'necessarily', 173 | 'nicely', 174 | 'obviously', 175 | 'ok', 176 | 'okay', 177 | 'particularly', 178 | 'perhaps', 179 | 'possibly', 180 | 'practically', 181 | 'primarily', 182 | 'probably', 183 | 'precisely', 184 | 'quite', 185 | 'rather', 186 | 'real', 187 | 'really', 188 | 'relatively', 189 | 'right', 190 | 'seriously', 191 | 'significantly', 192 | 'simply', 193 | 'slightly', 194 | 'so', 195 | 'specifically', 196 | 'start', 197 | 'strongly', 198 | 'stuff', 199 | 'surely', 200 | 'things', 201 | 'too', 202 | 'totally', 203 | 'truly', 204 | 'try', 205 | 'typically', 206 | 'ultimately', 207 | 'usually', 208 | 'very', 209 | 'virtually', 210 | 'whatever', 211 | 'well', 212 | 'whenever', 213 | 'wherever', 214 | 'whoever', 215 | 'widely', 216 | ]; 217 | 218 | export default [...greysAnatomyWords, ...fillerWords]; 219 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | } 8 | 9 | .App-header { 10 | background-color: #7dc4ca73; 11 | min-height: 80vh; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: calc(10px + 2vmin); 17 | color: #1e2020; 18 | } 19 | 20 | .App-link { 21 | color: #61dafb; 22 | } 23 | 24 | p { 25 | margin: 2em; 26 | line-height: 1.5; 27 | } 28 | 29 | .ipsum { 30 | max-width: 800px; 31 | text-align: left; 32 | } 33 | 34 | .inputSelection { 35 | margin-top: 1em; 36 | } 37 | 38 | #numberInput { 39 | margin: 1em; 40 | padding: 1em; 41 | width: 50px; 42 | } 43 | #grey-title { 44 | text-decoration: underline; 45 | text-decoration-color: red; 46 | } 47 | 48 | .btn-copy, 49 | .button { 50 | display: inline-block; 51 | text-align: center; 52 | vertical-align: middle; 53 | padding: 12px 24px; 54 | border: 1px solid #a12727; 55 | border-radius: 8px; 56 | background: #ff4a4a; 57 | background: -webkit-gradient( 58 | linear, 59 | left top, 60 | left bottom, 61 | from(#ff4a4a), 62 | to(#992727) 63 | ); 64 | background: -moz-linear-gradient(top, #ff4a4a, #992727); 65 | background: linear-gradient(to bottom, #ff4a4a, #992727); 66 | -webkit-box-shadow: #ff5959 0px 0px 40px 0px; 67 | -moz-box-shadow: #ff5959 0px 0px 40px 0px; 68 | box-shadow: #ff5959 0px 0px 40px 0px; 69 | text-shadow: #591717 1px 1px 1px; 70 | font: normal normal bold 20px arial; 71 | color: #ffffff; 72 | text-decoration: none; 73 | margin: 1em; 74 | } 75 | .btn-copy:hover, 76 | .btn-copy:focus, 77 | .button:hover, 78 | .button:focus { 79 | background: #ff5959; 80 | background: -webkit-gradient( 81 | linear, 82 | left top, 83 | left bottom, 84 | from(#ff5959), 85 | to(#b62f2f) 86 | ); 87 | background: -moz-linear-gradient(top, #ff5959, #b62f2f); 88 | background: linear-gradient(to bottom, #ff5959, #b62f2f); 89 | color: #ffffff; 90 | text-decoration: none; 91 | } 92 | .btn-copy:active, 93 | .button:active { 94 | background: #982727; 95 | background: -webkit-gradient( 96 | linear, 97 | left top, 98 | left bottom, 99 | from(#982727), 100 | to(#982727) 101 | ); 102 | background: -moz-linear-gradient(top, #982727, #982727); 103 | background: linear-gradient(to bottom, #982727, #982727); 104 | } 105 | .button:before { 106 | content: '\0000a0'; 107 | display: inline-block; 108 | height: 24px; 109 | width: 24px; 110 | line-height: 24px; 111 | margin: 0 4px -6px -4px; 112 | position: relative; 113 | top: 0px; 114 | left: 0px; 115 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFAklEQVRIibWVeWyURRjGfzPft1ev3e1B7fYyWLQQrQsxNEUsKCiGy3hSj+AfIiLBIxL/AbSKiTEiAdRgPFE5NCaCB3gUQYvikRjQAgqNoizFbrtluy60LPsd4x/ttywFjIn6JJNM5pn3fWaeeWdGAJL/EU5y+xy8/W/7+llI6fRXgxuY6vF6rysuKRnj8XqH2WAn+/ujHd3d3x8zjHfbYPsmMIfGOn0xZBBAvgIIuCEUCi0bWV9/fkV9PVJKlFIACCEwDYP21la+27Xrp597ehY+Ay1nc8IRyOB10D1u96rLxo6dd8GkScQjEfbt3Gn/dPRotOfEiQ4pBCW5uaGLSkpCdRMmyPzCQr7dvNne3Nb21Ce23fzDWUQyAm+AfMftfrVryhRlzJ+vtldXG/cJsSkMEwUUDFqqeyFnKoxdpOvrv6ittawHH1R7GxrUQimXuU7lk2cIrBdibmT0aJWeNUtt8Pv/nAh3OOf0GExs1rRHF2nao3Og1omdC9PfGTas15o9W31ZU6PuhKazCch1ENgZDB61Z8xQnxYWnmyEWxwOkBsCgd19K1eq3sWL1QKP5/1sbgFM3lZebpjTpqlVubmHKwZ2KwGZKVMJs0eWlRV2HTjAe/H4uh2w0eEA22NZui8cJq+sDAk5WRzPw/bWzs7ViSNHuKq4uGL8wOKcvAMocrluDCjFj4cPpz6CFQyUXubAtHQaolHo7kYbrKYs2H/a9opdBw+atT4fJULc7BA6wBrwFgpRp3p72Xfy5P5D0L5GyqkBv/8e2zAw02lyDaNKdHUhYjEu0vXwS17vJlvT6FMqvTuReOI32Bvp79+rJRLh4ZpWh2nmAP06gIACj2nmGb29JG37F8AsLCq6e8bq1TPRNOjshGgUdeIEMhplbihUTDA4k2CQuMvF7i1bIh/AwkbTbLd7e8NFllUA5AHH9YyRQgxYMXgrpcsFbjcYBtg2WBa0tUFLC3g8p8zxeBCDFSOEACFQQkiUkhmLkpBMud39Lr+/oCgWq8Ky5KFodP1r8+adb5mmtAyD2pycC68sLfVaeXm8Hosl48nk73ZHB31g/wqfLwIZcLlqZCBATzx+nHQ65ZSqBPjY691pjxqlWvz+Y/UQGhzXnfZZVdUee9o0ZYbDqlnTtjHwTjm8XAIVW4uLTxq1tepuKb9xFp+poq5U6t2Ey8UllZV502HO4LDpNN3ng/JyCIXQNM3O5gDbq2kPjK6udu9Xiohtb8Gx2hGIwZv7YrFEaXU1l1dWPvwQ1JF1mWwp7XQwSCoYRJ3+YspmGDeupub+QGkpH3d1Jb6CdVmxp0ReFOL+Q42NKt3UpN4vKzvUDGGHe1qI6UtdrrWPaNraa2C6E7cUxm8cPjxm3X672jFmjLoKlmQnP01gJuhrfL63um66SRn33qu+DIf7ntP1x5fCeVOy5j0F8kmoWu7xLN/R0GBYCxaovddeq26V8sPB8swg+z+QAHeAd7zP9/Kkq6++7YJwmHgsxoE9e1KdR460JZPJgwgh84PBmvMqKuourqvT8/Pz+ebrr3mhtfWDjZZ1Vwri5xLI7GgEyNuEmHPpiBFPjLniiuKKkSORmoayB6YJKTHTadrb2vi0tbXnk46O5VvhWSDFP/jRMrgECidDU3Fu7vXllZWjCvLzC0yliCUSx9sjkf0H0+ktW+HtFPxxrhxnWPQ38Gb5mwL6z5V0KIYmlv9l/y9CEv1bjuKpEgAAAABJRU5ErkJggg==') 116 | no-repeat left center transparent; 117 | background-size: 100% 100%; 118 | } 119 | 120 | #btn-copy-all { 121 | margin-left: 10px; 122 | } 123 | #btn-copy-all:before { 124 | background-image: none; 125 | content: none; 126 | } 127 | 128 | .paragraph { 129 | position: relative; 130 | padding-right: 51px; 131 | } 132 | .btn-copy { 133 | font-size: 10px; 134 | padding: 6px 12px; 135 | position: absolute; 136 | top: 50%; 137 | right: 24px; 138 | margin-top: -13px; 139 | } 140 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' 13 | // [::1] is the IPv6 localhost address. 14 | || window.location.hostname === '[::1]' 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | || window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, 18 | ), 19 | ); 20 | 21 | export function register(config) { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Let's check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl, config); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' 44 | + 'worker. To learn more, visit https://goo.gl/SC7cgQ', 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl, config); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl, config) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then((registration) => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | 70 | // Execute callback 71 | if (config && config.onUpdate) { 72 | config.onUpdate(registration); 73 | } 74 | } else { 75 | // At this point, everything has been precached. 76 | // It's the perfect time to display a 77 | // "Content is cached for offline use." message. 78 | console.log('Content is cached for offline use.'); 79 | 80 | // Execute callback 81 | if (config && config.onSuccess) { 82 | config.onSuccess(registration); 83 | } 84 | } 85 | } 86 | }; 87 | }; 88 | }) 89 | .catch((error) => { 90 | console.error('Error during service worker registration:', error); 91 | }); 92 | } 93 | 94 | function checkValidServiceWorker(swUrl, config) { 95 | // Check if the service worker can be found. If it can't reload the page. 96 | fetch(swUrl) 97 | .then((response) => { 98 | // Ensure service worker exists, and that we really are getting a JS file. 99 | if ( 100 | response.status === 404 101 | || response.headers.get('content-type').indexOf('javascript') === -1 102 | ) { 103 | // No service worker found. Probably a different app. Reload the page. 104 | navigator.serviceWorker.ready.then((registration) => { 105 | registration.unregister().then(() => { 106 | window.location.reload(); 107 | }); 108 | }); 109 | } else { 110 | // Service worker found. Proceed as normal. 111 | registerValidSW(swUrl, config); 112 | } 113 | }) 114 | .catch(() => { 115 | console.log( 116 | 'No internet connection found. App is running in offline mode.', 117 | ); 118 | }); 119 | } 120 | 121 | export function unregister() { 122 | if ('serviceWorker' in navigator) { 123 | navigator.serviceWorker.ready.then((registration) => { 124 | registration.unregister(); 125 | }); 126 | } 127 | } 128 | --------------------------------------------------------------------------------