├── .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 |
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 | 
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 |
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 |
113 |
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 |
--------------------------------------------------------------------------------