├── .gitignore
├── 01 Non blocking
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ ├── apiIceAndFire.js
│ ├── main.js
│ ├── mapper.js
│ └── printService.js
├── 02 How to avoid blocking
├── README.md
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ ├── apiIceAndFire.js
│ ├── main.js
│ ├── mapper.js
│ └── printService.js
├── 03 Run to completion
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 04 Operation Order
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 05 User interactions
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 06 Source Timers
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 07 Timer Gotchas
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 08 Not always async
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ ├── main.js
│ └── utils.js
├── 09 Web Workers
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ ├── background.js
│ ├── inline.js
│ └── longTask.js
├── 10 Event listeners are sync
├── README.md
├── package-lock.json
├── package.json
└── src
│ ├── content
│ └── site.css
│ ├── index.html
│ └── js
│ └── main.js
├── 11 Axios
├── 00_seed
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 01_get
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 02_post
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 03_callbacks
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 04_promises
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 05_all
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── authorAPI.js
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ └── view
│ │ └── uiBuilder.js
├── 06_multiple_params
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── authorAPI.js
│ │ └── bookAPI.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ ├── pages
│ │ ├── booksearch.html
│ │ └── booksearch
│ │ │ └── main.js
│ │ └── view
│ │ └── uiBuilder.js
├── 07_interceptors
│ ├── README.md
│ ├── README_99.MD
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── accessAPI.js
│ │ ├── authorAPI.js
│ │ ├── bookAPI.js
│ │ └── interceptors.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ ├── pages
│ │ ├── access.html
│ │ ├── access
│ │ │ └── main.js
│ │ ├── booksearch.html
│ │ └── booksearch
│ │ │ └── main.js
│ │ └── view
│ │ └── uiBuilder.js
├── 07b_interceptors
│ ├── README.MD
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── interceptors.js
│ │ └── loginAPI.js
│ │ ├── app.js
│ │ └── asyncApi.js
├── 08_cancel_token
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── accessAPI.js
│ │ ├── authorAPI.js
│ │ ├── bookAPI.js
│ │ └── interceptors.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ ├── pages
│ │ ├── access.html
│ │ ├── access
│ │ │ └── main.js
│ │ ├── booksearch.html
│ │ └── booksearch
│ │ │ └── main.js
│ │ └── view
│ │ └── uiBuilder.js
├── 08b_cancel_token
│ ├── README.MD
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── interceptors.js
│ │ └── loginAPI.js
│ │ ├── app.js
│ │ └── asyncApi.js
├── 09_patch
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── API
│ │ ├── accessAPI.js
│ │ ├── authorAPI.js
│ │ ├── bookAPI.js
│ │ └── interceptors.js
│ │ ├── app.js
│ │ ├── content
│ │ └── site.css
│ │ ├── pages
│ │ ├── access.html
│ │ ├── access
│ │ │ └── main.js
│ │ ├── bookpatch.html
│ │ ├── bookpatch
│ │ │ └── main.js
│ │ ├── booksearch.html
│ │ └── booksearch
│ │ │ └── main.js
│ │ └── view
│ │ └── uiBuilder.js
└── 10_delete
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── API
│ ├── accessAPI.js
│ ├── authorAPI.js
│ ├── bookAPI.js
│ └── interceptors.js
│ ├── app.js
│ ├── content
│ └── site.css
│ ├── pages
│ ├── access.html
│ ├── access
│ │ └── main.js
│ ├── bookpatch.html
│ ├── bookpatch
│ │ └── main.js
│ ├── booksearch.html
│ └── booksearch
│ │ └── main.js
│ └── view
│ └── uiBuilder.js
├── 12 Promises
├── 00_start
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ ├── Drawer.js
│ │ ├── Mapper.js
│ │ ├── main.js
│ │ └── weatherAPI.js
├── 01_promise_chaining
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ ├── Drawer.js
│ │ ├── Mapper.js
│ │ ├── main.js
│ │ └── weatherAPI.js
├── 02_promise_all
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ ├── Drawer.js
│ │ ├── Mapper.js
│ │ ├── main.js
│ │ └── weatherAPI.js
├── 03_avoiding_boilerplate_XMLHttpRequest
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ ├── Drawer.js
│ │ ├── Mapper.js
│ │ ├── httpClient.js
│ │ ├── main.js
│ │ └── weatherAPI.js
└── 04_promise_race
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ ├── content
│ └── site.css
│ └── js
│ ├── Drawer.js
│ ├── Mapper.js
│ ├── httpClient.js
│ ├── main.js
│ └── weatherAPI.js
├── 13 Fetch
├── 00_start
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ └── uiBuilder.js
├── 01_createRequestFactory
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── content
│ │ └── site.css
│ │ └── js
│ │ ├── bookAPI.js
│ │ ├── main.js
│ │ └── uiBuilder.js
└── 01_requestFactory
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── content
│ └── site.css
│ └── js
│ ├── bookAPI.js
│ ├── main.js
│ └── uiBuilder.js
├── 14 Async await
├── 00_start
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── api
│ │ ├── BookApiClient.js
│ │ └── bookAPI.js
│ │ └── app.js
├── 01_errors
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── api
│ │ ├── BookApiClient.js
│ │ └── bookAPI.js
│ │ └── app.js
├── 02_parallel
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── api
│ │ ├── BookApiClient.js
│ │ └── bookAPI.js
│ │ └── app.js
└── 03_sequential
│ ├── .babelrc
│ ├── README.md
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ ├── api
│ ├── BookApiClient.js
│ └── bookAPI.js
│ └── app.js
├── LICENSE
├── README.md
├── auth
├── credentialResolver.js
├── index.js
├── package-lock.json
└── package.json
├── docker-compose.yml
├── server-in-memory
├── README.md
├── package-lock.json
├── package.json
├── routes
│ ├── cars.js
│ ├── data
│ │ └── cars.json
│ └── users.js
└── server.js
├── server-mock
├── config
│ └── routes.json
├── mock-data
│ └── data.json
├── package-lock.json
└── package.json
└── server-mongo
├── .dockerignore
├── .env
├── .vscode
└── launch.json
├── Dockerfile
├── README.md
├── Routes
├── accessRoutes.js
├── authorRoutes.js
└── bookRoutes.js
├── Tests
├── bookControllerTests.js
└── bookIntegrationTests.js
├── app.js
├── controllers
├── accessController.js
├── accessControllerBusiness.js
├── authorController.js
├── authorControllerBusiness.js
├── bookByIdController.js
├── bookController.js
└── bookControllerBusiness.js
├── gulpfile.js
├── middlewares
└── bookMiddleware.js
├── models
├── authorModel.js
└── bookModel.js
├── package-lock.json
├── package.json
├── parsers
└── bookParser.js
└── utils
└── randomNumber.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | # .env
59 |
60 | # Build folders parcel
61 | dist/
62 | .cache
63 |
64 | .DS_store
65 |
--------------------------------------------------------------------------------
/01 Non blocking/README.md:
--------------------------------------------------------------------------------
1 | ## In order to get the code working:
2 |
3 | * Open terminal at the same level as `package.json`, and type:
4 | ```bash
5 | $ npm install
6 | ```
7 | * After installation, we can run the application using:
8 | ```bash
9 | $ npm install
10 | ```
11 | * To get results on the open browser app:
12 | * house: `House Lannister of Casterly Rock`
13 | * character input: `Jaime Lannister`
14 |
15 | ## In this demo we are going to study the non blocking nature of JavaScript
16 |
17 | * Just one thing can happen per time in JavaScript. But the required time to get data loaded is really short. Is not just to have a really good network capabilities, there is another reason.
18 |
19 | * Looking into the code this reason is not so obviously.We can think looking the `main.js` code that:
20 | * While this request `service.getHousesByName(houseInput.value, housesRequestConfig);` does not end...
21 | * This one `service.getCharactersByName(characterInput.value, charactersRequestConfig);` is not going to start
22 |
23 | * If we already have been working with callbacks, we are use to initialize a request and then proceed with the execution.
24 |
25 | * But if just one thing per time it's executed, how is this possible?
26 |
27 | * Opening the developer tools on Chrome, open the network tab. Here we can watch that the requests are sending concurrently.
28 |
--------------------------------------------------------------------------------
/01 Non blocking/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/01 Non blocking/src/content/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, Helvetica, sans-serif;
3 | margin: 0;
4 | }
5 |
6 | .content {
7 | margin: 3rem;
8 | }
9 |
10 | header {
11 | text-align: center;
12 | background-color: black;
13 | }
14 |
15 | header img {
16 | display: inline-block;
17 | min-width: 30%;
18 | max-height: 150px;
19 | }
20 |
21 | span {
22 | margin: auto 0;
23 | }
24 |
25 | button {
26 | border-radius: 3px / 5px;
27 | background-color: rgba(220, 220, 220, 0.9);
28 | border: 1.5px solid rgba(90,90,90,0.4);
29 | }
30 |
31 | button:hover {
32 | background-color: rgba(220, 220, 220, 0.2);
33 | }
34 |
35 | input {
36 | display: inline-block;
37 | border-radius: 3px / 5px;
38 | border: 1.5px solid rgba(90,90,90,0.4);
39 | background: rgba(220,220,220,0.16);
40 | padding: 0.2rem 0.4rem;
41 | margin-left: 0.5rem;
42 | width: 15rem;
43 | }
44 |
45 | .search {
46 | display: flex;
47 | flex-flow: row;
48 | }
49 |
50 | .search.search_item {
51 | flex-grow: 2;
52 | }
53 |
54 | .search.search_button {
55 | align-content: flex-end;
56 | }
57 |
58 | .display {
59 | display: flex;
60 | }
61 |
62 | .display .houses {
63 | flex-grow: 2;
64 | }
65 |
66 | .display .characters {
67 | flex-grow: 1;
68 | }
69 |
--------------------------------------------------------------------------------
/01 Non blocking/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | House
20 |
21 |
22 |
23 | Character
24 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/01 Non blocking/src/js/apiIceAndFire.js:
--------------------------------------------------------------------------------
1 | const baseRequestFactory = ({err, callback}) => {
2 | const req = new XMLHttpRequest();
3 | req.onload = callback;
4 | req.onerror = err;
5 | return req;
6 | };
7 |
8 | let baseRequests = new WeakMap();
9 |
10 | const getRequest = (config) => {
11 | let baseRequest = baseRequests.get(config);
12 | if (!baseRequest) {
13 | baseRequest = baseRequestFactory(config);
14 | baseRequests.set(config, baseRequest);
15 | }
16 | return baseRequest;
17 | };
18 |
19 | const sendGetRequest = (req, url) => {
20 | req.open('get', url , true);
21 | req.send();
22 | };
23 |
24 | export default class Service {
25 | constructor() {
26 | this.baseUrl = 'https://www.anapioficeandfire.com/api';
27 | }
28 |
29 | getHouseById(id, config) {
30 | const req = getRequest(config);
31 | const url = `${this.baseUrl}/houses/${id}`;
32 | sendGetRequest(req, url);
33 | }
34 |
35 | getCharacterById(id, config) {
36 | const req = getRequest(config);
37 | const url = `${this.baseUrl}/characters/${id}`;
38 | sendGetRequest(req, url);
39 | }
40 |
41 | getHousesByName(name, config) {
42 | const req = getRequest(config);
43 | const url = `${this.baseUrl}/houses?name=${name}`;
44 | sendGetRequest(req, url);
45 | }
46 |
47 | getCharactersByName(name, config) {
48 | const req = getRequest(config);
49 | const url = `${this.baseUrl}/characters?name=${name}`;
50 | sendGetRequest(req, url);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/01 Non blocking/src/js/mapper.js:
--------------------------------------------------------------------------------
1 | export const housesMap = (houses) => (
2 | houses.map(h => ({
3 | name: h.name,
4 | words: h.words,
5 | region: h.region,
6 | coatOfArms: h.coatOfArms
7 | }))
8 | );
9 |
10 | export const caracthersMap = (characters) => (
11 | characters.map(c => ({
12 | name: c.name,
13 | born: c.born,
14 | aliases: [...c.aliases]
15 | }))
16 | );
--------------------------------------------------------------------------------
/01 Non blocking/src/js/printService.js:
--------------------------------------------------------------------------------
1 | const createElement = (field, fieldValue, tag) => {
2 | const element = document.createElement(tag);
3 | element.innerHTML = `${field}: ${fieldValue}`;
4 | return element;
5 | }
6 |
7 | const createList = (entries) => {
8 | const ulElement = document.createElement('ul');
9 | entries.forEach((e) => {
10 | const liElement = document.createElement('li');
11 | liElement.innerHTML = e;
12 | ulElement.appendChild(liElement);
13 | });
14 |
15 | return ulElement;
16 | }
17 |
18 | const appendChildren = (node, ...children) => {
19 | children.forEach((c) => node.appendChild(c));
20 | }
21 |
22 | export default class Printer {
23 | printHouses(houses, container) {
24 | houses.forEach(h => this.printHouse(h, container));
25 | }
26 |
27 | printHouse(house, container) {
28 | const node = document.createElement('div');
29 | const name = createElement('House name', house.name, 'h3');
30 | const words = createElement('Words', house.words, 'h4');
31 | appendChildren(node, name, words);
32 | document.getElementById(container).appendChild(node);
33 | }
34 |
35 | printCharacters(characters, container) {
36 | characters.forEach(c => this.printCharacter(c, container));
37 | }
38 |
39 | printCharacter(character, container) {
40 | const node = document.createElement('div');
41 | const name = createElement('Character name', character.name, 'h3');
42 | const born = createElement('Born', character.born, 'h4');
43 | const aliases = createList(character.aliases);
44 | appendChildren(node, name, born, aliases);
45 | document.getElementById(container).appendChild(node);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/README.md:
--------------------------------------------------------------------------------
1 | ## In order to get the code working:
2 |
3 | * Open terminal at the same level as `package.json`, and type:
4 | ```bash
5 | $ npm install
6 | ```
7 | * After installation, we can run the application using:
8 | ```bash
9 | $ npm install
10 | ```
11 | * To get results on the open browser app:
12 | * house: `House Lannister of Casterly Rock`
13 | * character input: `Jaime Lannister`
14 |
15 | ## In this demo we are going to stablish the blocking nature of JavaScript
16 |
17 | ## Steps.
18 |
19 | ### 1. Open `src/js/apiIceAndFire.js`
20 |
21 | ```diff
22 | const sendGetRequest = (req, url) => {
23 | - req.open('get', url, true);
24 | + req.open('get', url, false);
25 | req.send();
26 | };
27 |
28 | ```
29 | * This way `XHR` work SYNC.
30 |
31 | ### 2. Now if we change these values again we can watch that goes on parallel
32 |
33 | ```diff apiIceAndFire.js
34 | const sendGetRequest = (req, url) => {
35 | + req.open('get', url, true);
36 | - req.open('get', url, false);
37 | req.send();
38 | };
39 |
40 | ```
41 | * Open and show results on developer tools.
42 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/src/content/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, Helvetica, sans-serif;
3 | margin: 0;
4 | }
5 |
6 | .content {
7 | margin: 3rem;
8 | }
9 |
10 | header {
11 | text-align: center;
12 | background-color: black;
13 | }
14 |
15 | header img {
16 | display: inline-block;
17 | min-width: 30%;
18 | max-height: 150px;
19 | }
20 |
21 | span {
22 | margin: auto 0;
23 | }
24 |
25 | button {
26 | border-radius: 3px / 5px;
27 | background-color: rgba(220, 220, 220, 0.9);
28 | border: 1.5px solid rgba(90,90,90,0.4);
29 | }
30 |
31 | button:hover {
32 | background-color: rgba(220, 220, 220, 0.2);
33 | }
34 |
35 | input {
36 | display: inline-block;
37 | border-radius: 3px / 5px;
38 | border: 1.5px solid rgba(90,90,90,0.4);
39 | background: rgba(220,220,220,0.16);
40 | padding: 0.2rem 0.4rem;
41 | margin-left: 0.5rem;
42 | width: 15rem;
43 | }
44 |
45 | .search {
46 | display: flex;
47 | flex-flow: row;
48 | }
49 |
50 | .search.search_item {
51 | flex-grow: 2;
52 | }
53 |
54 | .search.search_button {
55 | align-content: flex-end;
56 | }
57 |
58 | .display {
59 | display: flex;
60 | }
61 |
62 | .display .houses {
63 | flex-grow: 2;
64 | }
65 |
66 | .display .characters {
67 | flex-grow: 1;
68 | }
69 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | House
20 |
21 |
22 |
23 | Character
24 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/src/js/apiIceAndFire.js:
--------------------------------------------------------------------------------
1 | const baseRequestFactory = ({err, callback}) => {
2 | const req = new XMLHttpRequest();
3 | req.onload = callback;
4 | req.onerror = err;
5 | return req;
6 | };
7 |
8 | let baseRequests = new WeakMap();
9 |
10 | const getRequest = (config) => {
11 | let baseRequest = baseRequests.get(config);
12 | if (!baseRequest) {
13 | baseRequest = baseRequestFactory(config);
14 | baseRequests.set(config, baseRequest);
15 | }
16 | return baseRequest;
17 | };
18 |
19 | const sendGetRequest = (req, url) => {
20 | req.open('get', url, true);
21 | req.send();
22 | };
23 |
24 | export default class Service {
25 | constructor() {
26 | this.baseUrl = 'https://www.anapioficeandfire.com/api';
27 | }
28 |
29 | getHouseById(id, config) {
30 | const req = getRequest(config);
31 | const url = `${this.baseUrl}/houses/${id}`;
32 | sendGetRequest(req, url);
33 | }
34 |
35 | getCharacterById(id, config) {
36 | const req = getRequest(config);
37 | const url = `${this.baseUrl}/characters/${id}`;
38 | sendGetRequest(req, url);
39 | }
40 |
41 | getHousesByName(name, config) {
42 | const req = getRequest(config);
43 | const url = `${this.baseUrl}/houses?name=${name}`;
44 | sendGetRequest(req, url);
45 | }
46 |
47 | getCharactersByName(name, config) {
48 | const req = getRequest(config);
49 | const url = `${this.baseUrl}/characters?name=${name}`;
50 | sendGetRequest(req, url);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/02 How to avoid blocking/src/js/mapper.js:
--------------------------------------------------------------------------------
1 | export const housesMap = (houses) => (
2 | houses.map(h => ({
3 | name: h.name,
4 | words: h.words,
5 | region: h.region,
6 | coatOfArms: h.coatOfArms
7 | }))
8 | );
9 |
10 | export const caracthersMap = (characters) => (
11 | characters.map(c => ({
12 | name: c.name,
13 | born: c.born,
14 | aliases: [...c.aliases]
15 | }))
16 | );
--------------------------------------------------------------------------------
/02 How to avoid blocking/src/js/printService.js:
--------------------------------------------------------------------------------
1 | const createElement = (field, fieldValue, tag) => {
2 | const element = document.createElement(tag);
3 | element.innerHTML = `${field}: ${fieldValue}`;
4 | return element;
5 | }
6 |
7 | const createList = (entries) => {
8 | const ulElement = document.createElement('ul');
9 | entries.forEach((e) => {
10 | const liElement = document.createElement('li');
11 | liElement.innerHTML = e;
12 | ulElement.appendChild(liElement);
13 | });
14 |
15 | return ulElement;
16 | }
17 |
18 | const appendChildren = (node, ...children) => {
19 | children.forEach((c) => node.appendChild(c));
20 | }
21 |
22 | export default class Printer {
23 | printHouses(houses, container) {
24 | houses.forEach(h => this.printHouse(h, container));
25 | }
26 |
27 | printHouse(house, container) {
28 | const node = document.createElement('div');
29 | const name = createElement('House name', house.name, 'h3');
30 | const words = createElement('Words', house.words, 'h4');
31 | appendChildren(node, name, words);
32 | document.getElementById(container).appendChild(node);
33 | }
34 |
35 | printCharacters(characters, container) {
36 | characters.forEach(c => this.printCharacter(c, container));
37 | }
38 |
39 | printCharacter(character, container) {
40 | const node = document.createElement('div');
41 | const name = createElement('Character name', character.name, 'h3');
42 | const born = createElement('Born', character.born, 'h4');
43 | const aliases = createList(character.aliases);
44 | appendChildren(node, name, born, aliases);
45 | document.getElementById(container).appendChild(node);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/03 Run to completion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/03 Run to completion/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/03 Run to completion/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/03 Run to completion/src/js/main.js:
--------------------------------------------------------------------------------
1 | document.onreadystatechange = () => {
2 | if(document.readyState === 'complete') {
3 | const btn = document.getElementById("button");
4 | btn.addEventListener('click', (evt) => {
5 | evt.stopPropagation();
6 | modifyPage();
7 | blockingOperation();
8 | });
9 |
10 | const modifyPage = () => {
11 | // modify page
12 | document.body.style.backgroundColor = 'lime';
13 | const p = document.createElement("p");
14 | p.innerText = "let's add some text to the page";
15 | document.body.appendChild(p);
16 | };
17 |
18 | const blockingOperation = () => {
19 | // simulate blocking / long running operation
20 | const start = Date.now();
21 | const delaySeconds = 10;
22 | while (Date.now() < start + delaySeconds * 1000) {}
23 | }
24 | }
25 | };
26 |
27 |
--------------------------------------------------------------------------------
/04 Operation Order/README.md:
--------------------------------------------------------------------------------
1 | ## 1. Lets have a look to this code
2 |
3 | ```javascript
4 | document.onreadystatechange = () => {
5 | if(document.readyState === 'complete') {
6 |
7 | console.log('1');
8 |
9 | setTimeout(() => {
10 |
11 | console.log('2');
12 |
13 | setTimeout(() => {
14 |
15 | console.log('3');
16 |
17 | }, 0);
18 |
19 | console.log('4');
20 |
21 | }, 0);
22 |
23 | console.log('5');
24 |
25 | }
26 | };
27 |
28 | ```
29 |
30 |
31 | ## 2. What is the order of the prinrt numbers in console?
32 |
33 | 1, 5, 2, 4, 3
34 |
35 | * `setTimeout`, is another function that plans another async function, as web request would do.
36 | * We obtain this result due to run to completion.
37 | * The browser gives to these function a min value of 4ms.
38 |
--------------------------------------------------------------------------------
/04 Operation Order/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/04 Operation Order/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/04 Operation Order/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/04 Operation Order/src/js/main.js:
--------------------------------------------------------------------------------
1 | document.onreadystatechange = () => {
2 | if(document.readyState === 'complete') {
3 |
4 | console.log('1');
5 |
6 | setTimeout(() => {
7 |
8 | console.log('2');
9 |
10 | setTimeout(() => {
11 |
12 | console.log('3');
13 |
14 | }, 0);
15 |
16 | console.log('4');
17 |
18 | }, 0);
19 |
20 | console.log('5');
21 |
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/05 User interactions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/05 User interactions/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
31 | .drag {
32 | background-color: cyan;
33 | width: 200px;
34 | height: 200px;
35 | }
36 |
37 | .drop {
38 | border: 1px solid black;
39 | width: 300px;
40 | height: 300px;
41 | float: left;
42 | }
43 |
--------------------------------------------------------------------------------
/05 User interactions/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | User interactin demo
10 |
11 |
12 |
13 | Drag and drop
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/05 User interactions/src/js/main.js:
--------------------------------------------------------------------------------
1 | document.onreadystatechange = () => {
2 | if (document.readyState === 'complete') {
3 | const dragStart = (event) => {
4 | event.dataTransfer.setData('text/plain', event.target.id);
5 | console.log('dragStart', event);
6 | };
7 |
8 | const dragElement = document.getElementById('drag-element');
9 | dragElement.addEventListener('dragstart', dragStart);
10 |
11 | const drop = (event) => {
12 | const id = event.dataTransfer.getData('text');
13 | console.log('drop', id);
14 | event.target.appendChild(dragElement);
15 | };
16 |
17 | const dragOver = (event) => {
18 | event.preventDefault();
19 | event.dataTransfer.dropEffect = 'move';
20 | console.log({
21 | x: event.pageX,
22 | y: event.pageY
23 | });
24 | };
25 |
26 | const dropElementA = document.getElementById('drop-element-a');
27 | const dropElementB = document.getElementById('drop-element-b');
28 |
29 | dropElementA.addEventListener('dragover', dragOver);
30 | dropElementB.addEventListener('dragover', dragOver);
31 |
32 | dropElementA.addEventListener('drop', drop);
33 | dropElementB.addEventListener('drop', drop);
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/06 Source Timers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/06 Source Timers/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/06 Source Timers/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/06 Source Timers/src/js/main.js:
--------------------------------------------------------------------------------
1 | // Si lo dejamos así no aparece, ningún texto. ¿Por qué? La razón es que todavía
2 | // no se ha cargado el resto del documento, y que por tanto no existe el elemento
3 | // div con id = content. Evidenetemente, esto es porque esta, cargado en la head.
4 | // Lo que podemos hacer es un pequeño delay para que ese contenido este disponible.
5 |
6 | // document.getElementById('content').innerHTML = 'Main content from JS';
7 |
8 | // Now works
9 | setTimeout(() => {
10 | document.getElementById('content').innerHTML = 'Main content from JS';
11 | }, 100);
12 |
13 | //
14 |
--------------------------------------------------------------------------------
/07 Timer Gotchas/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/07 Timer Gotchas/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/07 Timer Gotchas/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/07 Timer Gotchas/src/js/main.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | let repitions = 0;
3 | const totalRepetions = 1000;
4 | const requestDelay = 10;
5 | let totalDelay = 0;
6 |
7 | const testDelay = () => {
8 | if (repitions++ > totalRepetions) {
9 | const avarage = totalDelay / totalRepetions;
10 | alert(`
11 | Request delay: ${requestDelay},
12 | Avarage delay: ${avarage}
13 | `);
14 | return;
15 | }
16 |
17 | const start = new Date();
18 | setTimeout(() => {
19 | const delay = new Date() - start;
20 | totalDelay += delay;
21 | testDelay();
22 | }, requestDelay);
23 | };
24 |
25 | testDelay();
26 | })();
27 |
--------------------------------------------------------------------------------
/08 Not always async/README.md:
--------------------------------------------------------------------------------
1 | ## Challange ¿Cuantos pequeños programas?
2 | * Folder structure:
3 |
4 | 08 Not Always Async/
5 | ├── src/
6 | │ ├── content/
7 | | | ├── site.css
8 | │ ├── js/
9 | | | ├── utils.js
10 | | | ├── main.js
11 | │ ├── index.html
12 | │ └── bootstrap-theme.min.css
13 | ├── gulpfile.js
14 | ├── package.json
15 |
16 | ## Steps.
17 |
18 | ### 1. Open `main.js`.
19 |
20 | * Challange -> How many little programs? // 1
21 | * What it's going to be show up on console? // 2, 4, 6
22 |
23 | * We can ask ourselves if this function is an async function or not. The fact is that there is no way to know it, without know how `forEach` function is implemented.
24 |
25 | * We can wrap the function with `setTimeout`, and we obtain another thing.
26 |
--------------------------------------------------------------------------------
/08 Not always async/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/08 Not always async/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/08 Not always async/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/08 Not always async/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { forEach } from './utils';
2 |
3 | (() => {
4 | const numbers = [1, 2, 3];
5 | console.log('start');
6 | forEach(numbers, (number) => console.log(number * 2)); // async or not async
7 | console.log('end');
8 | })();
9 |
--------------------------------------------------------------------------------
/08 Not always async/src/js/utils.js:
--------------------------------------------------------------------------------
1 | export const forEach = (items, callback) => {
2 | for (const item of items) {
3 | // callback(item);
4 | setTimeout(() => {
5 | callback(item);
6 | }, 0);
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/09 Web Workers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/09 Web Workers/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
31 | progress {
32 | width: 80%;
33 | height: 20px;
34 | -webkit-appearance: none;
35 | }
36 |
37 | progress::-webkit-progress-bar {
38 | background: lightgrey;
39 | border-radius: 20px;
40 | border: 1px solid gray;
41 | }
42 |
43 | progress::-webkit-progress-value {
44 | background: #21F7F7;
45 | border-radius: 20px;
46 | }
47 |
--------------------------------------------------------------------------------
/09 Web Workers/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/09 Web Workers/src/js/background.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const progress = document.getElementById('worker-progress');
3 |
4 | const longTaskWorker = new Worker('./longTask.js');
5 |
6 | longTaskWorker.onmessage = (message) => {
7 | progress.value = message.data;
8 | };
9 | })();
10 |
--------------------------------------------------------------------------------
/09 Web Workers/src/js/inline.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const longTask = (progressCalback) => {
3 | for (let step = 0; step < 10; step++) {
4 | progressCalback(step * 10);
5 | /*
6 | Use the developer tools to show that the console log is not blocked
7 | */
8 | console.log(step);
9 | const start = Date.now();
10 | const seconds = 1;
11 | const waitUntil = start + seconds * 1000;
12 | while (Date.now() < waitUntil) {}
13 | }
14 | };
15 |
16 | const progress = document.getElementById('worker-progress');
17 |
18 | longTask((percentage) => {
19 | progress.value = percentage;
20 | });
21 | })();
22 |
--------------------------------------------------------------------------------
/09 Web Workers/src/js/longTask.js:
--------------------------------------------------------------------------------
1 | const longTask = (progressCalback) => {
2 | for (let step = 0; step <= 10; step++) {
3 | progressCalback(step * 10);
4 | /*
5 | Use the developer tools to show that the console log is not blocked
6 | */
7 | console.log(step);
8 | const start = Date.now();
9 | const seconds = 1;
10 | const waitUntil = start + seconds * 1000;
11 | while (Date.now() < waitUntil) {}
12 | }
13 | };
14 |
15 | longTask(postMessage);
16 |
--------------------------------------------------------------------------------
/10 Event listeners are sync/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startupmain",
3 | "version": "1.0.0",
4 | "description": "Demos start up point",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel ./src/index.html"
8 | },
9 | "author": "Jaime Salas",
10 | "license": "ISC",
11 | "dependencies": {
12 | "parcel": "^1.10.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/10 Event listeners are sync/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/10 Event listeners are sync/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/10 Event listeners are sync/src/js/main.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const btn = document.getElementById('btn');
3 | const logger = (message) => (evt) => {
4 | evt.stopPropagation();
5 | console.log(message);
6 | };
7 | btn.addEventListener('click', logger('first click'));
8 | btn.addEventListener('click', logger('second click'));
9 |
10 | setTimeout(() => {
11 | console.log('pre-click');
12 | btn.click();
13 | console.log('post-click');
14 | }, 0);
15 | })();
16 | // (function () {
17 | // const btn = document.getElementById('btn');
18 |
19 | // btn.addEventListener('click', function () {
20 | // console.log('click-1');
21 | // });
22 |
23 | // btn.addEventListener('click', function () {
24 | // console.log('click-2');
25 | // });
26 |
27 | // setTimeout(function () {
28 | // console.log('pre-click');
29 | // btn.click(); // De manera síncrona, llama a cada uno de los handlers.
30 | // console.log('post-click');
31 | // }, 0);
32 | // })();
33 |
--------------------------------------------------------------------------------
/11 Axios/00_seed/README.md:
--------------------------------------------------------------------------------
1 | ## 0 Base
2 |
3 | ### 1. We are going to start our examples from here. For that purpose we have this folder structure
4 |
5 |
6 | |--src
7 | | |-- API
8 | | |-- content
9 | | |-- view
10 | |--|-- app.js
11 | |--index.html
12 |
13 | ### 2. We start from a mock service. And just verify we can render the list of books
14 |
15 | ```javascript src/API/bookAPI.js
16 | export const getBooks = () => {
17 | return [
18 | { title: 'testA', author: 'testA', genre: 'fiction', read: true },
19 | { title: 'testB', author: 'testB', genre: 'fiction', read: true }
20 | ];
21 | };
22 | ```
23 |
24 | ### 3. The index html looks this way
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Document
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ### 4. app.js has this look
47 |
48 | ```javascript
49 | import * as bookAPI from './API/bookAPI';
50 | import { appendElement, createList } from './view/uiBuilder';
51 |
52 | document.addEventListener('DOMContentLoaded', () => {
53 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
54 | buttonRetrieveBooks.addEventListener('click', (event) => {
55 | event.stopPropagation();
56 | const books = bookAPI.getBooks()
57 | .map(b => `${b.title}, ${b.author}`);
58 | const bookList = createList(books);
59 | appendElement('books-container', bookList);
60 | });
61 | });
62 | ```
63 |
--------------------------------------------------------------------------------
/11 Axios/00_seed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/11 Axios/00_seed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/00_seed/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | export const getBooks = () => {
2 | return [
3 | { title: 'testA', author: 'testA', genre: 'fiction', read: true },
4 | { title: 'testB', author: 'testB', genre: 'fiction', read: true }
5 | ];
6 | };
--------------------------------------------------------------------------------
/11 Axios/00_seed/src/app.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './API/bookAPI';
2 | import { appendElement, createList } from './view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
6 | buttonRetrieveBooks.addEventListener('click', (event) => {
7 | event.stopPropagation();
8 | const books = bookAPI.getBooks()
9 | .map(b => `${b.title}, ${b.author}`);
10 | const bookList = createList(books);
11 | appendElement('books-container', bookList);
12 | });
13 | });
--------------------------------------------------------------------------------
/11 Axios/00_seed/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/00_seed/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/01_get/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/11 Axios/01_get/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.12.4"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/01_get/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = () => {
5 | axios.get('http://localhost/api/books')
6 | .then((result) => {
7 | let titles = [];
8 | result.data.forEach((item) => {
9 | titles.push(item.title);
10 | });
11 | const list = createList(titles);
12 | appendElement('books-container', list);
13 | })
14 | .catch((err) => console.log(err));
15 | };
--------------------------------------------------------------------------------
/11 Axios/01_get/src/app.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './API/bookAPI';
2 | import { appendElement, createList } from './view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
6 | buttonRetrieveBooks.addEventListener('click', (event) => {
7 | event.stopPropagation();
8 | bookAPI.getBooks();
9 | });
10 | });
--------------------------------------------------------------------------------
/11 Axios/01_get/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/01_get/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/02_post/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/11 Axios/02_post/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/02_post/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | const errorHandler = (err) => console.log(err);
5 |
6 | export const getBooks = () => {
7 | axios.get('http://localhost:8000/api/books')
8 | .then((result) => {
9 | let titles = [];
10 | result.data.forEach((item) => {
11 | titles.push(item.title);
12 | });
13 | const list = createList(titles);
14 | appendElement('books-container', list);
15 | })
16 | .catch(errorHandler);
17 | };
18 |
19 | export const postBook = (book) => {
20 | axios.post('http://localhost:8000/api/books', book)
21 | .then(() => getBooks())
22 | .catch(errorHandler);
23 | };
--------------------------------------------------------------------------------
/11 Axios/02_post/src/app.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './API/bookAPI';
2 | import { appendElement, createList } from './view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
6 | buttonRetrieveBooks.addEventListener('click', (event) => {
7 | event.stopPropagation();
8 | bookAPI.getBooks();
9 | });
10 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit');
11 | conatinerAddBookFormSubmit.addEventListener('click', (event) => {
12 | event.preventDefault();
13 | event.stopPropagation();
14 | const book = {
15 | title: document.getElementById('title').value,
16 | author: document.getElementById('author').value,
17 | genre: document.getElementById('genre').value,
18 | read: document.getElementById('read').checked
19 | };
20 | bookAPI.postBook(book);
21 | });
22 | });
--------------------------------------------------------------------------------
/11 Axios/02_post/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/02_post/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.8.4",
15 | "@babel/plugin-proposal-class-properties": "^7.8.3",
16 | "parcel": "^1.12.4"
17 | },
18 | "dependencies": {
19 | "axios": "^0.18.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = (err, success) => {
5 | axios.get('http://localhost:8000/api/books')
6 | .then(success)
7 | .catch(err);
8 | };
9 |
10 | export const postBook = (book) => (err, success) => {
11 | axios.post('http://localhost:8000/api/books', book)
12 | .then(success)
13 | .catch(err);
14 | };
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/src/app.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './API/bookAPI';
2 | import { appendElement, createList } from './view/uiBuilder';
3 |
4 | const onSuccess = (result) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement('books-container', list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | document.addEventListener('DOMContentLoaded', () => {
17 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
18 | buttonRetrieveBooks.addEventListener('click', (event) => {
19 | event.stopPropagation();
20 | // bookAPI.getBooks();
21 | bookAPI.getBooks(onError, onSuccess);
22 | });
23 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit');
24 | conatinerAddBookFormSubmit.addEventListener('click', (event) => {
25 | event.preventDefault();
26 | event.stopPropagation();
27 | const book = {
28 | title: document.getElementById('title').value,
29 | author: document.getElementById('author').value,
30 | genre: document.getElementById('genre').value,
31 | read: document.getElementById('read').checked
32 | };
33 | // bookAPI.postBook(book);
34 | bookAPI.postBook(book)
35 | (
36 | onError,
37 | (result) => bookAPI.getBooks(onError, onSuccess)
38 | );
39 | });
40 | });
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/03_callbacks/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/04_promises/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/11 Axios/04_promises/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/04_promises/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.8.4",
15 | "@babel/plugin-proposal-class-properties": "^7.8.3",
16 | "parcel": "^1.12.4"
17 | },
18 | "dependencies": {
19 | "axios": "^0.18.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/11 Axios/04_promises/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = () => (
5 | axios.get('http://localhost:8000/api/books')
6 | );
7 |
8 | export const postBook = (book) => (
9 | axios.post('http://localhost:8000/api/books', book)
10 | );
--------------------------------------------------------------------------------
/11 Axios/04_promises/src/app.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './API/bookAPI';
2 | import { appendElement, createList } from './view/uiBuilder';
3 |
4 | const onSuccess = (result) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement('books-container', list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | document.addEventListener('DOMContentLoaded', () => {
17 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
18 | buttonRetrieveBooks.addEventListener('click', (event) => {
19 | event.stopPropagation();
20 | // bookAPI.getBooks(onError, onSuccess);
21 | bookAPI.getBooks()
22 | .then(onSuccess)
23 | .catch(onError);
24 | });
25 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit');
26 | conatinerAddBookFormSubmit.addEventListener('click', (event) => {
27 | event.preventDefault();
28 | event.stopPropagation();
29 | const book = {
30 | title: document.getElementById('title').value,
31 | author: document.getElementById('author').value,
32 | genre: document.getElementById('genre').value,
33 | read: document.getElementById('read').checked
34 | };
35 | // bookAPI.postBook(book)
36 | // (
37 | // onError,
38 | // (result) => bookAPI.getBooks(onError, onSuccess)
39 | // );
40 | bookAPI.postBook(book)
41 | .then(() => bookAPI.getBooks())
42 | .then(onSuccess)
43 | .catch(onError);
44 | });
45 | });
--------------------------------------------------------------------------------
/11 Axios/04_promises/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/04_promises/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/05_all/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/11 Axios/05_all/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/11 Axios/05_all/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.8.4",
15 | "@babel/plugin-proposal-class-properties": "^7.8.3",
16 | "parcel": "^1.12.4"
17 | },
18 | "dependencies": {
19 | "axios": "^0.18.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/11 Axios/05_all/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/05_all/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = (err, success) => (
5 | axios.get('http://localhost:8000/api/books')
6 | );
7 |
8 | export const postBook = (book) => (err, success) => (
9 | axios.post('http://localhost:8000/api/books', book)
10 | );
--------------------------------------------------------------------------------
/11 Axios/05_all/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/05_all/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
35 |
36 | Book search
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.8.4",
15 | "@babel/plugin-proposal-class-properties": "^7.8.3",
16 | "parcel": "^1.12.4"
17 | },
18 | "dependencies": {
19 | "axios": "^0.18.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = () => (
5 | axios.get('http://localhost:8000/api/books')
6 | );
7 |
8 | export const getBooksByParams = (params) => (
9 | axios.get('http://localhost:8000/api/books', params)
10 | );
11 |
12 | export const postBook = (book) => () => (
13 | axios.post('http://localhost:8000/api/books', book)
14 | );
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/pages/booksearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book search
12 |
13 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/pages/booksearch/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from '../../API/bookAPI';
2 | import { appendElement, createList } from '../../view/uiBuilder';
3 |
4 | const onSuccess = (result, container) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement(container, list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | const parseParams = () => {
17 | let obj = { params: {} };
18 |
19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value;
20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value;
21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value;
22 |
23 | return obj;
24 | };
25 |
26 | document.addEventListener('DOMContentLoaded', () => {
27 | const buttonSearchBooks = document.getElementById('button-search-books');
28 | buttonSearchBooks.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | bookAPI.getBooksByParams(parseParams())
31 | .then((result) => onSuccess(result, 'books-container'))
32 | .catch(onError);
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/06_multiple_params/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/README_99.MD:
--------------------------------------------------------------------------------
1 | ## We're going to create a new page that will contain a login form, if we're authenticated, the response will return a JWT, that we will use to call an API.
2 |
3 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
35 |
36 | Book search
37 | Access
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/API/accessAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import setUp from './interceptors';
3 |
4 | export const grantAccess = () => (
5 | axios.get('http://localhost:8000/api/access')
6 | );
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBooks = () => (
5 | axios.get('http://localhost:8000/api/books')
6 | );
7 |
8 | export const getBooksByParams = (params) => (
9 | axios.get('http://localhost:8000/api/books', params)
10 | );
11 |
12 | export const postBook = (book) => () => (
13 | axios.post('http://localhost:8000/api/books', book)
14 | );
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const setUp = () => {
4 | axios.interceptors.request.use((config) => {
5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods'
6 | return config;
7 | }, (err) => {
8 | return Promise.reject(err);
9 | });
10 |
11 | axios.interceptors.response.use((response) => {
12 | // Do something with data
13 | console.log(response);
14 | // TODO: Catch response. Simulate on server different status
15 | return response;
16 | }, (error) => {
17 | console.log(error.response.status);
18 | console.log(error.response.statusText);
19 | window.location = "/index.html"; // Play with both
20 | //return Promise.reject(error.response);
21 | });
22 | };
23 |
24 | export default setUp();
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/pages/access.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | LEMONCODE 16/17 Async JavaScript jQuery
8 |
9 |
10 |
11 |
12 |
13 | Access page
14 | The access is random so good luck!!
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/pages/access/main.js:
--------------------------------------------------------------------------------
1 | import * as accessAPI from '../../API/accessAPI';
2 | import {createTextEntry, appendElement} from '../../view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | console.log('hello main')
6 | const buttonAccess = document.getElementById('button-access');
7 | buttonAccess.addEventListener('click', (event) => {
8 | event.stopPropagation();
9 | accessAPI.grantAccess()
10 | .then((result) => {
11 | const text = createTextEntry(result.data.message);
12 | appendElement('container', text);
13 | })
14 | .catch((err) => console.log(err));
15 | });
16 | });
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/pages/booksearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book search
12 |
13 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/pages/booksearch/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from '../../API/bookAPI';
2 | import { appendElement, createList } from '../../view/uiBuilder';
3 |
4 | const onSuccess = (result, container) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement(container, list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | const parseParams = () => {
17 | let obj = { params: {} };
18 |
19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value;
20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value;
21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value;
22 |
23 | return obj;
24 | };
25 |
26 | document.addEventListener('DOMContentLoaded', () => {
27 | const buttonSearchBooks = document.getElementById('button-search-books');
28 | buttonSearchBooks.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | bookAPI.getBooksByParams(parseParams())
31 | .then((result) => onSuccess(result, 'books-container'))
32 | .catch(onError);
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/07_interceptors/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
15 |
16 | export const createTextEntry = (text) => {
17 | let textEntry = document.createElement('p');
18 | textEntry.appendChild(document.createTextNode(text));
19 | return textEntry;
20 | };
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Axios Interceptors example
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | // const setUp = () => {
4 | // axios.interceptors.request.use((config) => {
5 | // config.headers['GEN_TOKEN'] = '000aks1243545iopods'
6 | // return config;
7 | // }, (err) => {
8 | // return Promise.reject(err);
9 | // });
10 |
11 | // axios.interceptors.response.use((response) => {
12 | // // Do something with data
13 | // console.log(response);
14 | // // TODO: Catch response. Simulate on server different status
15 | // return response;
16 | // }, (error) => {
17 | // console.log(error.response.status);
18 | // console.log(error.response.statusText);
19 | // window.location = "/index.html"; // Play with both
20 | // //return Promise.reject(error.response);
21 | // });
22 | // };
23 |
24 | // export default setUp();
25 |
26 | export const setUpRequest = (token) => {
27 | axios.interceptors.request.use((config) => {
28 | config.headers['Authorization'] = `Bearer ${token}`;
29 | return config;
30 | }, (err) => {
31 | return Promise.reject(err);
32 | });
33 | };
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/src/API/loginAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export default ({username, password}) => (
4 | axios.post('http://localhost:8887/login', {username, password})
5 | );
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/src/app.js:
--------------------------------------------------------------------------------
1 | import { submitButtonPromise } from './asyncApi';
2 | import login from './API/loginAPI';
3 | import { setUpRequest } from './API/interceptors';
4 | import axios from 'axios';
5 |
6 | const readCredentials = () => {
7 | const username = document.getElementById('username').value;
8 | const password = document.getElementById('password').value;
9 | return {
10 | username,
11 | password,
12 | };
13 | };
14 |
15 | document.addEventListener('DOMContentLoaded', () => {
16 | submitButtonPromise('click', 'login')
17 | .then(() => {
18 | const credentials = readCredentials();
19 | return login(credentials)
20 | })
21 | .then((result) => {
22 | const { access_token } = result.data;
23 | setUpRequest(access_token);
24 | })
25 | .catch((err) => console.log(err));
26 |
27 | document.getElementById('cars')
28 | .addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | axios.get('http://localhost:3050/api/cars')
31 | .then((result) => console.log(result))
32 | .catch((err) => console.log(err));
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/07b_interceptors/src/asyncApi.js:
--------------------------------------------------------------------------------
1 | export const submitButtonPromise = (event, targetId) => (
2 | new Promise((resolve, _) => {
3 | document.getElementById(targetId)
4 | .addEventListener(event, (evt) => {
5 | evt.stopPropagation();
6 | evt.preventDefault();
7 | resolve();
8 | });
9 | })
10 | );
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
35 |
36 | Book search
37 | Access
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/API/accessAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import setUp from './interceptors';
3 |
4 | export const grantAccess = () => (
5 | axios.get('http://localhost:8000/api/access')
6 | );
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | // export const getBooks = () => (
5 | // axios.get('http://localhost:8000/api/books')
6 | // );
7 |
8 | export const getBooks = (cancelSettings) => (
9 | axios.get('http://localhost:8000/api/books', cancelSettings)
10 | );
11 |
12 | export const getBooksByParams = (params) => (
13 | axios.get('http://localhost:8000/api/books', params)
14 | );
15 |
16 | export const postBook = (book) => () => (
17 | axios.post('http://localhost:8000/api/books', book)
18 | );
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const setUp = () => {
4 | axios.interceptors.request.use((config) => {
5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods'
6 | return config;
7 | }, (err) => {
8 | return Promise.reject(err);
9 | });
10 |
11 | axios.interceptors.response.use((response) => {
12 | // Do something with data
13 | console.log(response);
14 | // TODO: Catch response. Simulate on server different status
15 | return response;
16 | }, (error) => {
17 | console.log(error.response.status);
18 | console.log(error.response.statusText);
19 | window.location = "/index.html"; // Play with both
20 | //return Promise.reject(error.response);
21 | });
22 | };
23 |
24 | export default setUp();
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/pages/access.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | LEMONCODE 16/17 Async JavaScript jQuery
8 |
9 |
10 |
11 |
12 |
13 | Access page
14 | The access is random so good luck!!
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/pages/access/main.js:
--------------------------------------------------------------------------------
1 | import * as accessAPI from '../../API/accessAPI';
2 | import {createTextEntry, appendElement} from '../../view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | console.log('hello main')
6 | const buttonAccess = document.getElementById('button-access');
7 | buttonAccess.addEventListener('click', (event) => {
8 | event.stopPropagation();
9 | accessAPI.grantAccess()
10 | .then((result) => {
11 | const text = createTextEntry(result.data.message);
12 | appendElement('container', text);
13 | })
14 | .catch((err) => console.log(err));
15 | });
16 | });
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/pages/booksearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book search
12 |
13 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/pages/booksearch/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from '../../API/bookAPI';
2 | import { appendElement, createList } from '../../view/uiBuilder';
3 |
4 | const onSuccess = (result, container) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement(container, list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | const parseParams = () => {
17 | let obj = { params: {} };
18 |
19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value;
20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value;
21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value;
22 |
23 | return obj;
24 | };
25 |
26 | document.addEventListener('DOMContentLoaded', () => {
27 | const buttonSearchBooks = document.getElementById('button-search-books');
28 | buttonSearchBooks.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | bookAPI.getBooksByParams(parseParams())
31 | .then((result) => onSuccess(result, 'books-container'))
32 | .catch(onError);
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/08_cancel_token/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
15 |
16 | export const createTextEntry = (text) => {
17 | let textEntry = document.createElement('p');
18 | textEntry.appendChild(document.createTextNode(text));
19 | return textEntry;
20 | };
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Axios Interceptors example
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const setUpRequest = (token) => {
4 | axios.interceptors.request.use((config) => {
5 | config.headers['Authorization'] = `Bearer ${token}`;
6 | return config;
7 | }, (err) => {
8 | return Promise.reject(err);
9 | });
10 | };
11 |
12 | export const setUpResponse = () => {
13 | axios.interceptors.response.use((response) => {
14 | // Do something with data
15 | console.log(response);
16 | return response;
17 | }, (error) => {
18 | if (err.response && error.response.status === 401) {
19 | console.log('you are not allowed');
20 | }
21 | return Promise.reject('I am canceled');
22 | });
23 | }
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/src/API/loginAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export default ({username, password}) => (
4 | axios.post('http://localhost:8887/login', {username, password})
5 | );
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/src/app.js:
--------------------------------------------------------------------------------
1 | import { submitButtonPromise } from './asyncApi';
2 | import login from './API/loginAPI';
3 | import { setUpRequest, setUpResponse } from './API/interceptors';
4 | import axios from 'axios';
5 |
6 | const readCredentials = () => {
7 | const username = document.getElementById('username').value;
8 | const password = document.getElementById('password').value;
9 | return {
10 | username,
11 | password,
12 | };
13 | };
14 |
15 | const getCars = (cancelSettings) => (
16 | axios.get('http://localhost:3050/api/cars', cancelSettings)
17 | );
18 |
19 | document.addEventListener('DOMContentLoaded', () => {
20 | setUpResponse();
21 | submitButtonPromise('click', 'login')
22 | .then(() => {
23 | const credentials = readCredentials();
24 | return login(credentials)
25 | })
26 | .then((result) => {
27 | const { access_token } = result.data;
28 | setUpRequest(access_token);
29 | })
30 | .catch((err) => console.log(err));
31 |
32 | document.getElementById('cars')
33 | .addEventListener('click', (event) => {
34 | event.stopPropagation();
35 | const CancelToken = axios.CancelToken;
36 | let source = CancelToken.source();
37 | getCars({ cancelToken: source.token })
38 | .then((result) => console.log(result))
39 | .catch((err) => console.log(err));
40 |
41 | source.cancel();
42 | });
43 | });
--------------------------------------------------------------------------------
/11 Axios/08b_cancel_token/src/asyncApi.js:
--------------------------------------------------------------------------------
1 | export const submitButtonPromise = (event, targetId) => (
2 | new Promise((resolve, _) => {
3 | document.getElementById(targetId)
4 | .addEventListener(event, (evt) => {
5 | evt.stopPropagation();
6 | evt.preventDefault();
7 | resolve();
8 | });
9 | })
10 | );
--------------------------------------------------------------------------------
/11 Axios/09_patch/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
35 |
36 | Book search
37 | Book patch
38 | Access
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/API/accessAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import setUp from './interceptors';
3 |
4 | export const grantAccess = () => (
5 | axios.get('http://localhost:8000/api/access')
6 | );
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBookById = (bookId) => (
5 | axios.get(`http://localhost:8000/api/books/${bookId}`)
6 | );
7 |
8 | export const getBooks = (cancelSettings) => (
9 | axios.get('http://localhost:8000/api/books', cancelSettings)
10 | );
11 |
12 | export const getBooksByParams = (params) => (
13 | axios.get('http://localhost:8000/api/books', params)
14 | );
15 |
16 | export const postBook = (book) => (
17 | axios.post('http://localhost:8000/api/books', book)
18 | );
19 |
20 | export const patchBook = (bookId, book) => (
21 | axios.patch(`http://localhost:8000/api/books/${bookId}`, book)
22 | );
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const setUp = () => {
4 | axios.interceptors.request.use((config) => {
5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods'
6 | return config;
7 | }, (err) => {
8 | return Promise.reject(err);
9 | });
10 |
11 | axios.interceptors.response.use((response) => {
12 | // Do something with data
13 | console.log(response);
14 | // TODO: Catch response. Simulate on server different status
15 | return response;
16 | }, (error) => {
17 | console.log(error.response.status);
18 | console.log(error.response.statusText);
19 | window.location = "/index.html"; // Play with both
20 | //return Promise.reject(error.response);
21 | });
22 | };
23 |
24 | export default setUp();
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/pages/access.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | LEMONCODE 16/17 Async JavaScript jQuery
8 |
9 |
10 |
11 |
12 |
13 | Access page
14 | The access is random so good luck!!
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/pages/access/main.js:
--------------------------------------------------------------------------------
1 | import * as accessAPI from '../../API/accessAPI';
2 | import {createTextEntry, appendElement} from '../../view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | const buttonAccess = document.getElementById('button-access');
6 | buttonAccess.addEventListener('click', (event) => {
7 | event.stopPropagation();
8 | accessAPI.grantAccess()
9 | .then((result) => {
10 | const text = createTextEntry(result.data.message);
11 | appendElement('container', text);
12 | })
13 | .catch((err) => console.log(err));
14 | });
15 | });
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/pages/bookpatch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book patch
12 |
13 |
34 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/pages/booksearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book search
12 |
13 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/pages/booksearch/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from '../../API/bookAPI';
2 | import { appendElement, createList } from '../../view/uiBuilder';
3 |
4 | const onSuccess = (result, container) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement(container, list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | const parseParams = () => {
17 | let obj = { params: {} };
18 |
19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value;
20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value;
21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value;
22 |
23 | return obj;
24 | };
25 |
26 | document.addEventListener('DOMContentLoaded', () => {
27 | const buttonSearchBooks = document.getElementById('button-search-books');
28 | buttonSearchBooks.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | bookAPI.getBooksByParams(parseParams())
31 | .then((result) => onSuccess(result, 'books-container'))
32 | .catch(onError);
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/09_patch/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
15 |
16 | export const createTextEntry = (text) => {
17 | let textEntry = document.createElement('p');
18 | textEntry.appendChild(document.createTextNode(text));
19 | return textEntry;
20 | };
21 |
22 | export const createElement = (element) => {
23 | const div = document.createElement('div');
24 |
25 | for (const key in element) {
26 | if (element.hasOwnProperty(key) && key !== '_id' && key !== '__v') {
27 | const value = element[key];
28 | let p = document.createElement('p');
29 | p.appendChild(document.createTextNode(`${key}: ${value}`));
30 | div.appendChild(p);
31 | }
32 | }
33 |
34 | return div;
35 | }
36 |
37 | export const cleanElement = (target) => document.getElementById(target).innerHTML = '';
--------------------------------------------------------------------------------
/11 Axios/10_delete/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
35 |
36 | Book search
37 | Book patch
38 | Access
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_get_started",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "parcel": "^1.8.1"
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/API/accessAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import setUp from './interceptors';
3 |
4 | export const grantAccess = () => (
5 | axios.get('http://localhost:8000/api/access')
6 | );
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/API/authorAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const getAuthors = () => (
4 | axios.get('http://localhost:8000/api/authors')
5 | );
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/API/bookAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { appendElement, createList } from '../view/uiBuilder';
3 |
4 | export const getBookById = (bookId) => (
5 | axios.get(`http://localhost:8000/api/books/${bookId}`)
6 | );
7 |
8 | export const getBooks = (cancelSettings) => (
9 | axios.get('http://localhost:8000/api/books', cancelSettings)
10 | );
11 |
12 | export const getBooksByParams = (params) => (
13 | axios.get('http://localhost:8000/api/books', params)
14 | );
15 |
16 | export const postBook = (book) => (
17 | axios.post('http://localhost:8000/api/books', book)
18 | );
19 |
20 | export const patchBook = (bookId, book) => (
21 | axios.patch(`http://localhost:8000/api/books/${bookId}`, book)
22 | );
23 |
24 | export const deleteBookById = (bookId) => (
25 | axios.delete(`http://localhost:8000/api/books/${bookId}`)
26 | );
27 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/API/interceptors.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const setUp = () => {
4 | axios.interceptors.request.use((config) => {
5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods'
6 | return config;
7 | }, (err) => {
8 | return Promise.reject(err);
9 | });
10 |
11 | axios.interceptors.response.use((response) => {
12 | // Do something with data
13 | console.log(response);
14 | // TODO: Catch response. Simulate on server different status
15 | return response;
16 | }, (error) => {
17 | console.log(error.response.status);
18 | console.log(error.response.statusText);
19 | window.location = "/index.html"; // Play with both
20 | //return Promise.reject(error.response);
21 | });
22 | };
23 |
24 | export default setUp();
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/pages/access.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | LEMONCODE 16/17 Async JavaScript jQuery
8 |
9 |
10 |
11 |
12 |
13 | Access page
14 | The access is random so good luck!!
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/pages/access/main.js:
--------------------------------------------------------------------------------
1 | import * as accessAPI from '../../API/accessAPI';
2 | import {createTextEntry, appendElement} from '../../view/uiBuilder';
3 |
4 | document.addEventListener('DOMContentLoaded', () => {
5 | const buttonAccess = document.getElementById('button-access');
6 | buttonAccess.addEventListener('click', (event) => {
7 | event.stopPropagation();
8 | accessAPI.grantAccess()
9 | .then((result) => {
10 | const text = createTextEntry(result.data.message);
11 | appendElement('container', text);
12 | })
13 | .catch((err) => console.log(err));
14 | });
15 | });
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/pages/bookpatch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book patch
12 |
13 |
35 |
36 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/pages/booksearch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | Book search
12 |
13 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/pages/booksearch/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from '../../API/bookAPI';
2 | import { appendElement, createList } from '../../view/uiBuilder';
3 |
4 | const onSuccess = (result, container) => {
5 | let titles = [];
6 | // Remove data to watch how error is handle.
7 | result.data.forEach((item) => {
8 | titles.push(item.title);
9 | });
10 | const list = createList(titles);
11 | appendElement(container, list);
12 | };
13 |
14 | const onError = (error) => console.log(error);
15 |
16 | const parseParams = () => {
17 | let obj = { params: {} };
18 |
19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value;
20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value;
21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value;
22 |
23 | return obj;
24 | };
25 |
26 | document.addEventListener('DOMContentLoaded', () => {
27 | const buttonSearchBooks = document.getElementById('button-search-books');
28 | buttonSearchBooks.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | bookAPI.getBooksByParams(parseParams())
31 | .then((result) => onSuccess(result, 'books-container'))
32 | .catch(onError);
33 | });
34 | });
--------------------------------------------------------------------------------
/11 Axios/10_delete/src/view/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
7 | list.appendChild(listItem);
8 | });
9 | return list;
10 | };
11 |
12 | export const appendElement = (target, item) => {
13 | document.getElementById(target).appendChild(item);
14 | };
15 |
16 | export const createTextEntry = (text) => {
17 | let textEntry = document.createElement('p');
18 | textEntry.appendChild(document.createTextNode(text));
19 | return textEntry;
20 | };
21 |
22 | export const createElement = (element) => {
23 | const div = document.createElement('div');
24 |
25 | for (const key in element) {
26 | if (element.hasOwnProperty(key) && key !== '_id' && key !== '__v') {
27 | const value = element[key];
28 | let p = document.createElement('p');
29 | p.appendChild(document.createTextNode(`${key}: ${value}`));
30 | div.appendChild(p);
31 | }
32 | }
33 |
34 | return div;
35 | }
36 |
37 | export const cleanElement = (target) => document.getElementById(target).innerHTML = '';
--------------------------------------------------------------------------------
/12 Promises/00_start/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/12 Promises/00_start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | API weather
10 |
11 |
12 |
13 | API Rest
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12 Promises/00_start/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promises_demo",
3 | "version": "1.0.0",
4 | "description": "Promises ES6",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Promises",
12 | "ES6"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.8.4",
18 | "@babel/plugin-proposal-class-properties": "^7.8.3",
19 | "parcel": "^1.12.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/12 Promises/00_start/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/12 Promises/00_start/src/js/Mapper.js:
--------------------------------------------------------------------------------
1 | export class Mapper {
2 | currentWeather(data) {
3 | return data.weather.map((element) => ({
4 | description: element.description,
5 | icon: element.icon,
6 | main: element.main
7 | }));
8 | }
9 |
10 | forecastWeather(data) {
11 | return data.list.map((element) => ({
12 | date: element.dt_txt.split(' ')[0],
13 | time: element.dt_txt.split(' ')[1],
14 | weather: element.weather
15 | }));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/12 Promises/00_start/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { Drawer } from './Drawer';
2 | import { Mapper } from './Mapper';
3 | import * as weatherAPI from './weatherAPI';
4 |
5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => {
6 | const data = JSON.parse(resultJSON);
7 | const weatherSummaryEntities = mapper.currentWeather(data);
8 | drawer.drawWeather(weatherSummaryEntities);
9 | };
10 |
11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => {
12 | const data = JSON.parse(resultJSON);
13 | const forecastSummaryEntities = mapper.forecastWeather(data);
14 | drawer.drawForecastResult(forecastSummaryEntities);
15 | }
16 |
17 | const errorHandler = (err) => console.log(err);
18 |
19 | document.addEventListener('DOMContentLoaded', () => {
20 | const button = document.getElementById('button-weather');
21 | const drawer = new Drawer('results');
22 | const mapper = new Mapper();
23 |
24 | button.addEventListener('click', (event) => {
25 | event.stopPropagation();
26 | const cityInput = document.getElementById('input-city');
27 | if (cityInput.value) {
28 | weatherAPI.getCurrentWeather({ name: cityInput.value })
29 | .then(handlerCurrentWeatherSuccess(mapper, drawer))
30 | .catch(errorHandler);
31 |
32 | weatherAPI.getForecastWeather({ name: cityInput.value })
33 | .then(handlerForecastWeatherSuccess(mapper, drawer))
34 | .catch(errorHandler);
35 | }
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/12 Promises/00_start/src/js/weatherAPI.js:
--------------------------------------------------------------------------------
1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6';
2 |
3 | export const getCurrentWeather = (city) => {
4 | let promise = new Promise((resolve, reject) => {
5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
6 | const client = new XMLHttpRequest();
7 | client.onload = (event) => resolve(event.target.responseText);;
8 | client.onerror = (event) => reject(event.target.statusText);
9 | client.open('get', uri); // By default async
10 | client.send();
11 | });
12 |
13 | return promise;
14 | };
15 |
16 | export const getForecastWeather = (city) => {
17 | let promise = new Promise((resolve, reject) => {
18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
19 | const client = new XMLHttpRequest();
20 | client.onload = (event) => resolve(event.target.responseText);
21 | client.onerror = (event) => reject(event.target.statusText);
22 | client.open('get', uri); // By default async
23 | client.send();
24 | });
25 |
26 | return promise;
27 | };
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | API weather
10 |
11 |
12 |
13 | API Rest
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promises_demo",
3 | "version": "1.0.0",
4 | "description": "Promises ES6",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Promises",
12 | "ES6"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.8.4",
18 | "@babel/plugin-proposal-class-properties": "^7.8.3",
19 | "parcel": "^1.12.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/src/js/Mapper.js:
--------------------------------------------------------------------------------
1 | export class Mapper {
2 | currentWeather(data) {
3 | return data.weather.map((element) => ({
4 | description: element.description,
5 | icon: element.icon,
6 | main: element.main
7 | }));
8 | }
9 |
10 | forecastWeather(data) {
11 | return data.list.map((element) => ({
12 | date: element.dt_txt.split(' ')[0],
13 | time: element.dt_txt.split(' ')[1],
14 | weather: element.weather
15 | }));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { Drawer } from './Drawer';
2 | import { Mapper } from './Mapper';
3 | import * as weatherAPI from './weatherAPI';
4 |
5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => {
6 | const data = JSON.parse(resultJSON);
7 | const weatherSummaryEntities = mapper.currentWeather(data);
8 | drawer.drawWeather(weatherSummaryEntities);
9 | };
10 |
11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => {
12 | const data = JSON.parse(resultJSON);
13 | const forecastSummaryEntities = mapper.forecastWeather(data);
14 | drawer.drawForecastResult(forecastSummaryEntities);
15 | }
16 |
17 | const forecastWeatherRequest = (city) => (
18 | weatherAPI.getForecastWeather(city)
19 | );
20 |
21 | const errorHandler = (err) => console.log(err);
22 |
23 | document.addEventListener('DOMContentLoaded', () => {
24 | const button = document.getElementById('button-weather');
25 | const drawer = new Drawer('results');
26 | const mapper = new Mapper();
27 |
28 | button.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | const cityInput = document.getElementById('input-city');
31 | if (cityInput.value) {
32 | weatherAPI.getCurrentWeather({ name: cityInput.value })
33 | .then((resultJSON) => {
34 | handlerCurrentWeatherSuccess(mapper, drawer)(resultJSON);
35 | return forecastWeatherRequest({ name: cityInput.value });
36 | })
37 | .then(handlerForecastWeatherSuccess(mapper, drawer))
38 | .catch(errorHandler);
39 | }
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/12 Promises/01_promise_chaining/src/js/weatherAPI.js:
--------------------------------------------------------------------------------
1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6';
2 |
3 | export const getCurrentWeather = (city) => {
4 | let promise = new Promise((resolve, reject) => {
5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
6 | const client = new XMLHttpRequest();
7 | client.onload = (event) => resolve(event.target.responseText);;
8 | client.onerror = (event) => reject(event.target.statusText);
9 | client.open('get', uri); // By default async
10 | client.send();
11 | });
12 |
13 | return promise;
14 | };
15 |
16 | export const getForecastWeather = (city) => {
17 | let promise = new Promise((resolve, reject) => {
18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
19 | const client = new XMLHttpRequest();
20 | client.onload = (event) => resolve(event.target.responseText);
21 | client.onerror = (event) => reject(event.target.statusText);
22 | client.open('get', uri); // By default async
23 | client.send();
24 | });
25 |
26 | return promise;
27 | };
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | API weather
10 |
11 |
12 |
13 | API Rest
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promises_demo",
3 | "version": "1.0.0",
4 | "description": "Promises ES6",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Promises",
12 | "ES6"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.8.4",
18 | "@babel/plugin-proposal-class-properties": "^7.8.3",
19 | "parcel": "^1.12.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/src/js/Mapper.js:
--------------------------------------------------------------------------------
1 | export class Mapper {
2 | currentWeather(data) {
3 | return data.weather.map((element) => ({
4 | description: element.description,
5 | icon: element.icon,
6 | main: element.main
7 | }));
8 | }
9 |
10 | forecastWeather(data) {
11 | return data.list.map((element) => ({
12 | date: element.dt_txt.split(' ')[0],
13 | time: element.dt_txt.split(' ')[1],
14 | weather: element.weather
15 | }));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { Drawer } from './Drawer';
2 | import { Mapper } from './Mapper';
3 | import * as weatherAPI from './weatherAPI';
4 |
5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => {
6 | const data = JSON.parse(resultJSON);
7 | const weatherSummaryEntities = mapper.currentWeather(data);
8 | drawer.drawWeather(weatherSummaryEntities);
9 | };
10 |
11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => {
12 | const data = JSON.parse(resultJSON);
13 | const forecastSummaryEntities = mapper.forecastWeather(data);
14 | drawer.drawForecastResult(forecastSummaryEntities);
15 | }
16 |
17 | const forecastWeatherRequest = (city) => (
18 | weatherAPI.getForecastWeather(city)
19 | );
20 |
21 | const errorHandler = (err) => console.log(err);
22 |
23 | document.addEventListener('DOMContentLoaded', () => {
24 | const button = document.getElementById('button-weather');
25 | const drawer = new Drawer('results');
26 | const mapper = new Mapper();
27 |
28 | button.addEventListener('click', (event) => {
29 | event.stopPropagation();
30 | const cityInput = document.getElementById('input-city');
31 | if (cityInput.value) {
32 | Promise.all([
33 | weatherAPI.getCurrentWeather({ name: cityInput.value }),
34 | weatherAPI.getForecastWeather({ name: cityInput.value })
35 | ])
36 | .catch(errorHandler)
37 | .then((results) => {
38 | handlerCurrentWeatherSuccess(mapper, drawer)(results[0]);
39 | handlerForecastWeatherSuccess(mapper, drawer)(results[1]);
40 | });
41 | }
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/12 Promises/02_promise_all/src/js/weatherAPI.js:
--------------------------------------------------------------------------------
1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6';
2 |
3 | export const getCurrentWeather = (city) => {
4 | let promise = new Promise((resolve, reject) => {
5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
6 | const client = new XMLHttpRequest();
7 | client.onload = (event) => resolve(event.target.responseText);;
8 | client.onerror = (event) => reject(event.target.statusText);
9 | client.open('get', uri); // By default async
10 | client.send();
11 | });
12 |
13 | return promise;
14 | };
15 |
16 | export const getForecastWeather = (city) => {
17 | let promise = new Promise((resolve, reject) => {
18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
19 | const client = new XMLHttpRequest();
20 | client.onload = (event) => resolve(event.target.responseText);
21 | client.onerror = (event) => reject(event.target.statusText);
22 | client.open('get', uri); // By default async
23 | client.send();
24 | });
25 |
26 | return promise;
27 | };
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | API weather
10 |
11 |
12 |
13 | API Rest
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promises_demo",
3 | "version": "1.0.0",
4 | "description": "Promises ES6",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Promises",
12 | "ES6"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.8.4",
18 | "@babel/plugin-proposal-class-properties": "^7.8.3",
19 | "parcel": "^1.12.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/Mapper.js:
--------------------------------------------------------------------------------
1 | export class Mapper {
2 | currentWeather(data) {
3 | return data.weather.map((element) => ({
4 | description: element.description,
5 | icon: element.icon,
6 | main: element.main
7 | }));
8 | }
9 |
10 | forecastWeather(data) {
11 | return data.list.map((element) => ({
12 | date: element.dt_txt.split(' ')[0],
13 | time: element.dt_txt.split(' ')[1],
14 | weather: element.weather
15 | }));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/httpClient.js:
--------------------------------------------------------------------------------
1 | const ajax = (method, url, args) => (
2 | new Promise((resolve, reject) => {
3 | const client = new XMLHttpRequest();
4 | let uri = url;
5 | let params;
6 | // TRIBUTE: https://plainjs.com/javascript/ajax/send-ajax-get-and-post-requests-47/
7 | if (method === 'POST') {
8 | params = typeof args == 'string' ? args : Object.keys(args).map(
9 | (k) => (encodeURIComponent(k) + '=' + encodeURIComponent(args[k]))
10 | ).join('&');
11 | }
12 | client.open(method, uri);
13 | if (method === 'POST') {
14 | client.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
15 | client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
16 | }
17 | client.onload = (event) => {
18 | resolve(event.target.responseText);
19 | };
20 | client.onerror = (event) => {
21 | reject(event.target.statusText);
22 | };
23 | if (method === 'POST') {
24 | client.send(params);
25 | } else {
26 | client.send();
27 | }
28 | })
29 | );
30 |
31 | export const httpClient = {
32 | 'get': (url, args) => ajax('GET', url, args),
33 | 'post': (url, args) => ajax('POST', url, args),
34 | };
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { Drawer } from './Drawer';
2 | import { Mapper } from './Mapper';
3 | import * as weatherAPI from './weatherAPI';
4 |
5 | // const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => {
6 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (data) => {
7 | // const data = JSON.parse(resultJSON);
8 | const weatherSummaryEntities = mapper.currentWeather(data);
9 | drawer.drawWeather(weatherSummaryEntities);
10 | };
11 |
12 | // const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => {
13 | const handlerForecastWeatherSuccess = (mapper, drawer) => (data) => {
14 | // const data = JSON.parse(resultJSON);
15 | const forecastSummaryEntities = mapper.forecastWeather(data);
16 | drawer.drawForecastResult(forecastSummaryEntities);
17 | }
18 |
19 | const forecastWeatherRequest = (city) => (
20 | weatherAPI.getForecastWeather(city)
21 | );
22 |
23 | const errorHandler = (err) => console.log(err);
24 |
25 | document.addEventListener('DOMContentLoaded', () => {
26 | const button = document.getElementById('button-weather');
27 | const drawer = new Drawer('results');
28 | const mapper = new Mapper();
29 |
30 | button.addEventListener('click', (event) => {
31 | event.stopPropagation();
32 | const cityInput = document.getElementById('input-city');
33 | if (cityInput.value) {
34 | Promise.all([
35 | weatherAPI.getCurrentWeather({ name: cityInput.value }),
36 | weatherAPI.getForecastWeather({ name: cityInput.value })
37 | ])
38 | .catch(errorHandler)
39 | .then((results) => {
40 | handlerCurrentWeatherSuccess(mapper, drawer)(results[0]);
41 | handlerForecastWeatherSuccess(mapper, drawer)(results[1]);
42 | });
43 | }
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/weatherAPI.js:
--------------------------------------------------------------------------------
1 | import { httpClient } from './httpClient';
2 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6';
3 |
4 | export const getCurrentWeather = (city) => {
5 | // let promise = new Promise((resolve, reject) => {
6 | // const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
7 | // const client = new XMLHttpRequest();
8 | // client.onload = (event) => resolve(event.target.responseText);;
9 | // client.onerror = (event) => reject(event.target.statusText);
10 | // client.open('get', uri); // By default async
11 | // client.send();
12 | // });
13 |
14 | // return promise;
15 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
16 | return httpClient.get(uri);
17 | };
18 |
19 | export const getForecastWeather = (city) => {
20 | // let promise = new Promise((resolve, reject) => {
21 | // const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
22 | // const client = new XMLHttpRequest();
23 | // client.onload = (event) => resolve(event.target.responseText);
24 | // client.onerror = (event) => reject(event.target.statusText);
25 | // client.open('get', uri); // By default async
26 | // client.send();
27 | // });
28 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
29 | return httpClient.get(uri);
30 | };
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-proposal-class-properties"
4 | ]
5 | }
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | API weather
10 |
11 |
12 |
13 | API Rest
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promises_demo",
3 | "version": "1.0.0",
4 | "description": "Promises ES6",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Promises",
12 | "ES6"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.8.4",
18 | "@babel/plugin-proposal-class-properties": "^7.8.3",
19 | "parcel": "^1.12.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
11 | .container p {
12 | max-width: 70%;
13 | font-size: 0.9em;
14 | margin-top: 0.2em;
15 | margin-bottom: 0.2em;
16 | height: 2.3em;
17 | }
18 |
19 | .container p img {
20 | max-width: 95%;
21 | max-height: 95%;
22 | position: relative;
23 | top: 0.7em;
24 | left: 1em;
25 | }
26 |
27 | .container ul {
28 | list-style: none;
29 | }
30 |
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/src/js/Mapper.js:
--------------------------------------------------------------------------------
1 | export class Mapper {
2 | currentWeather(data) {
3 | return data.weather.map((element) => ({
4 | description: element.description,
5 | icon: element.icon,
6 | main: element.main
7 | }));
8 | }
9 |
10 | forecastWeather(data) {
11 | return data.list.map((element) => ({
12 | date: element.dt_txt.split(' ')[0],
13 | time: element.dt_txt.split(' ')[1],
14 | weather: element.weather
15 | }));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/src/js/httpClient.js:
--------------------------------------------------------------------------------
1 | const ajax = (method, url, args) => (
2 | new Promise((resolve, reject) => {
3 | const client = new XMLHttpRequest();
4 | let uri = url;
5 | let params;
6 | // TRIBUTE: https://plainjs.com/javascript/ajax/send-ajax-get-and-post-requests-47/
7 | if (method === 'POST') {
8 | params = typeof args == 'string' ? args : Object.keys(args).map(
9 | (k) => (encodeURIComponent(k) + '=' + encodeURIComponent(args[k]))
10 | ).join('&');
11 | }
12 | client.open(method, uri);
13 | if (method === 'POST') {
14 | client.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
15 | client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
16 | }
17 | client.onload = (event) => {
18 | // TODO: Check status code.
19 | const result = JSON.parse(event.target.response);
20 | resolve(result);
21 | };
22 | client.onerror = (event) => {
23 | // TODO: Check status code.
24 | const result = JSON.parse(event.target.responseText);
25 | reject(result);
26 | };
27 | if (method === 'POST') {
28 | client.send(params);
29 | } else {
30 | client.send();
31 | }
32 | })
33 | );
34 |
35 | export const httpClient = {
36 | 'get': (url, args) => ajax('GET', url, args),
37 | 'post': (url, args) => ajax('POST', url, args),
38 | };
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/src/js/main.js:
--------------------------------------------------------------------------------
1 | import { Drawer } from './Drawer';
2 | import { Mapper } from './Mapper';
3 | import * as weatherAPI from './weatherAPI';
4 |
5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (data) => {
6 | const weatherSummaryEntities = mapper.currentWeather(data);
7 | drawer.drawWeather(weatherSummaryEntities);
8 | };
9 |
10 | const handlerForecastWeatherSuccess = (mapper, drawer) => (data) => {
11 | const forecastSummaryEntities = mapper.forecastWeather(data);
12 | drawer.drawForecastResult(forecastSummaryEntities);
13 | }
14 |
15 | const forecastWeatherRequest = (city) => (
16 | weatherAPI.getForecastWeather(city)
17 | );
18 |
19 | const errorHandler = (err) => console.log(err);
20 |
21 | document.addEventListener('DOMContentLoaded', () => {
22 | const button = document.getElementById('button-weather');
23 | const drawer = new Drawer('results');
24 | const mapper = new Mapper();
25 |
26 | button.addEventListener('click', (event) => {
27 | event.stopPropagation();
28 | const cityInput = document.getElementById('input-city');
29 | if (cityInput.value) {
30 | Promise.race([
31 | weatherAPI.getCurrentWeather({ name: cityInput.value }),
32 | weatherAPI.getForecastWeather({ name: cityInput.value })
33 | ])
34 | .catch(errorHandler)
35 | .then((data) => {
36 | if (data.hasOwnProperty('weather')) {
37 | handlerCurrentWeatherSuccess(mapper, drawer)(data);
38 | } else {
39 | handlerForecastWeatherSuccess(mapper, drawer)(data);
40 | }
41 | });
42 | }
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/12 Promises/04_promise_race/src/js/weatherAPI.js:
--------------------------------------------------------------------------------
1 | import { httpClient } from './httpClient';
2 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6';
3 |
4 | export const getCurrentWeather = (city) => {
5 | // let promise = new Promise((resolve, reject) => {
6 | // const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
7 | // const client = new XMLHttpRequest();
8 | // client.onload = (event) => resolve(event.target.responseText);;
9 | // client.onerror = (event) => reject(event.target.statusText);
10 | // client.open('get', uri); // By default async
11 | // client.send();
12 | // });
13 |
14 | // return promise;
15 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`;
16 | return httpClient.get(uri);
17 | };
18 |
19 | export const getForecastWeather = (city) => {
20 | // let promise = new Promise((resolve, reject) => {
21 | // const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
22 | // const client = new XMLHttpRequest();
23 | // client.onload = (event) => resolve(event.target.responseText);
24 | // client.onerror = (event) => reject(event.target.statusText);
25 | // client.open('get', uri); // By default async
26 | // client.send();
27 | // });
28 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`;
29 | return httpClient.get(uri);
30 | };
--------------------------------------------------------------------------------
/13 Fetch/00_start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Async fetch
7 |
8 |
9 |
10 |
11 | Fetch
12 |
13 |
14 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/13 Fetch/00_start/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fetch-fundamentals",
3 | "version": "0.0.0",
4 | "description": "A repository to learn the fetch basics",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel index.html",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "fetch",
12 | "async",
13 | "javascript"
14 | ],
15 | "author": "Jaime Salas",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "parcel": "^1.12.3"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/13 Fetch/00_start/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
--------------------------------------------------------------------------------
/13 Fetch/00_start/src/js/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 |
7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
8 |
9 | list.appendChild(listItem);
10 | });
11 |
12 | return list;
13 | };
14 |
15 | export const appendElement = (target, item) => {
16 | document.getElementById(target).appendChild(item);
17 | };
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Async fetch
7 |
8 |
9 |
10 |
11 | Fetch
12 |
13 |
14 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_createrequestfactory",
3 | "version": "1.0.0",
4 | "description": "Playing with fetch demo",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Fetch"
12 | ],
13 | "author": "Jaime Salas",
14 | "license": "MIT",
15 | "devDependencies": {
16 | "parcel": "^1.8.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/src/js/bookAPI.js:
--------------------------------------------------------------------------------
1 | const createRequest = (url, options) => {
2 | const _options = (options) ? options : {};
3 | console.log(_options);
4 | return fetch(url, _options)
5 | .then((response) => {
6 | if (!response.ok) {
7 | throw Error(response.statusText);
8 | }
9 | return response.json();
10 | });
11 | };
12 |
13 | const baseURL = 'http://localhost:8000/api/books';
14 |
15 | export const retrieveBooks = () => {
16 | return createRequest(baseURL, {
17 | method: 'GET',
18 | mode: 'cors',
19 | headers: {
20 | 'Accept': 'application/json',
21 | 'Content-Type': 'application/json'
22 | },
23 | });
24 | };
25 |
26 | export const retrieveBook = (bookId) => {
27 | const url = `${baseURL}/${bookId}`;
28 | return createRequest(url, {
29 | method: 'GET',
30 | mode: 'cors',
31 | headers: {
32 | 'Accept': 'application/json',
33 | 'Content-Type': 'application/json'
34 | },
35 | });
36 | }
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/src/js/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './bookAPI';
2 |
3 | document.addEventListener('DOMContentLoaded', () => {
4 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
5 | buttonRetrieveBooks.addEventListener('click', (event) => {
6 | event.stopPropagation();
7 | bookAPI.retrieveBook('noexists')
8 | .then((result) => console.log(result))
9 | .catch((err) => console.log(err.message, 'main'));
10 | // bookAPI.retrieveBooks()
11 | // .then((result) => console.log(result))
12 | // .catch((err) => console.log(err.message, 'main'));
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/13 Fetch/01_createRequestFactory/src/js/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 |
7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
8 |
9 | list.appendChild(listItem);
10 | });
11 |
12 | return list;
13 | };
14 |
15 | export const appendElement = (target, item) => {
16 | document.getElementById(target).appendChild(item);
17 | };
18 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LEMONCODE 16/17 Async JavaScript jQuery
7 |
8 |
9 |
10 |
11 | AJAX jQuery
12 |
13 |
14 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "00_createrequestfactory",
3 | "version": "1.0.0",
4 | "description": "Playing with fetch demo",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Fetch"
12 | ],
13 | "author": "Jaime Salas",
14 | "license": "MIT",
15 | "devDependencies": {
16 | "parcel": "^1.8.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/src/content/site.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway');
2 |
3 | body {
4 | font-family: 'Raleway', sans-serif;
5 | }
6 |
7 | .container {
8 | padding: 2em;
9 | }
10 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/src/js/bookAPI.js:
--------------------------------------------------------------------------------
1 | const baseURL = 'http://localhost:8000/api/books';
2 |
3 | const requestOptionsFactory = (headers) => (...args) => {
4 | const [method, mode] = [...args];
5 | return {
6 | method,
7 | mode,
8 | headers
9 | };
10 | };
11 | const requestJson = requestOptionsFactory(new Headers({ 'Content-Type': 'application/json' }));
12 |
13 | const handleErrors = (response) => {
14 | if (!response.ok) {
15 | throw Error(response.statusText);
16 | }
17 | return response;
18 | };
19 |
20 | const fetchJson = (request) => fetch(request)
21 | .then(handleErrors)
22 | .then((response) => response.json());
23 |
24 | export const retrieveBooks = () => {
25 | return fetchJson(
26 | new Request(baseURL, requestJson('GET', 'cors'))
27 | );
28 | };
29 |
30 | export const retrieveBook = (bookId) => {
31 | const url = `${baseURL}/${bookId}`;
32 | return fetchJson(
33 | new Request(url, requestJson('GET', 'cors'))
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/src/js/main.js:
--------------------------------------------------------------------------------
1 | import * as bookAPI from './bookAPI';
2 |
3 | document.addEventListener('DOMContentLoaded', () => {
4 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books');
5 | buttonRetrieveBooks.addEventListener('click', (event) => {
6 | event.stopPropagation();
7 | bookAPI.retrieveBook('noexists')
8 | .then((result) => console.log(result))
9 | .catch((err) => console.log(err.message));
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/13 Fetch/01_requestFactory/src/js/uiBuilder.js:
--------------------------------------------------------------------------------
1 | export const createList = (elements) => {
2 | let list = document.createElement('ul');
3 |
4 | elements.forEach((element) => {
5 | let listItem = document.createElement('li');
6 |
7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data.
8 |
9 | list.appendChild(listItem);
10 | });
11 |
12 | return list;
13 | };
14 |
15 | export const appendElement = (target, item) => {
16 | document.getElementById(target).appendChild(item);
17 | };
18 |
--------------------------------------------------------------------------------
/14 Async await/00_start/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-transform-runtime"
4 | ]
5 | }
--------------------------------------------------------------------------------
/14 Async await/00_start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/14 Async await/00_start/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "async_await_demos",
3 | "version": "1.0.0",
4 | "description": "Async await tutorial",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Async",
12 | "await",
13 | "native"
14 | ],
15 | "author": "Jaime Salas",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/plugin-transform-runtime": "^7.8.3",
20 | "@babel/runtime": "^7.8.4",
21 | "parcel": "^1.12.4"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/14 Async await/00_start/src/api/BookApiClient.js:
--------------------------------------------------------------------------------
1 | export class BookApiClient {
2 | async fetchBooks() {
3 | const url = 'http://localhost:8000/api/books/';
4 | const response = await fetch(url);
5 | return await response.json();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/14 Async await/00_start/src/api/bookAPI.js:
--------------------------------------------------------------------------------
1 | export async function showBooks() {
2 | const url = 'http://localhost:8000/api/books/';
3 | const response = await fetch(url);
4 | // const books = await response.json();
5 | // return books;
6 | return await response.json();
7 | }
8 |
9 | export const functionExpressionShowBooks = async () => {
10 | const url = 'http://localhost:8000/api/books/';
11 | const response = await fetch(url);
12 | return await response.json();
13 | }
14 |
--------------------------------------------------------------------------------
/14 Async await/00_start/src/app.js:
--------------------------------------------------------------------------------
1 | import "regenerator-runtime/runtime";
2 | // async function showBooks() {
3 | // const url = 'http://localhost:8000/api/books/';
4 | // const response = await fetch(url);
5 | // const books = await response.json();
6 | // console.log(books);
7 | // }
8 |
9 | // showBooks();
10 | import { showBooks, functionExpressionShowBooks } from './api/bookAPI';
11 | import { BookApiClient } from './api/BookApiClient';
12 | // async function main() {
13 | // const books = await showBooks();
14 | // console.log(books);
15 | // }
16 |
17 | // main();
18 | // functionExpressionShowBooks()
19 | // .then((bs) => console.log(bs));
20 | (async() => {
21 | const bookAPI = new BookApiClient();
22 | const books = await bookAPI.fetchBooks();
23 | console.log(books);
24 | })();
25 |
--------------------------------------------------------------------------------
/14 Async await/01_errors/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-transform-runtime"
4 | ]
5 | }
--------------------------------------------------------------------------------
/14 Async await/01_errors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/14 Async await/01_errors/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "async_await_demos",
3 | "version": "1.0.0",
4 | "description": "Async await tutorial",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Async",
12 | "await",
13 | "native"
14 | ],
15 | "author": "Jaime Salas",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/plugin-transform-runtime": "^7.8.3",
20 | "parcel": "^1.12.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/14 Async await/01_errors/src/api/BookApiClient.js:
--------------------------------------------------------------------------------
1 | export class BookApiClient {
2 | async fetchBooks() {
3 | const url = 'http://localhost:8000/api/books/';
4 | const response = await fetch(url);
5 | return await response.json();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/14 Async await/01_errors/src/api/bookAPI.js:
--------------------------------------------------------------------------------
1 | export async function showBooks(url) {
2 | const response = await fetch(url);
3 | if (!response.ok) {
4 | const err = await response.text();
5 | throw Error(err);
6 | }
7 | const body = await response.json();
8 | return body;
9 | }
10 |
11 | export const functionExpressionShowBooks = async () => {
12 | const url = 'http://localhost:8000/api/books/';
13 | const response = await fetch(url);
14 | return await response.json();
15 | }
16 |
--------------------------------------------------------------------------------
/14 Async await/01_errors/src/app.js:
--------------------------------------------------------------------------------
1 | import { showBooks, functionExpressionShowBooks } from './api/bookAPI';
2 | import { BookApiClient } from './api/BookApiClient';
3 |
4 | const url = 'http://localhost:8000/api/boos';
5 | const url2 = 'http://localhost:8000/api/books/5';
6 |
7 | // showBooks(url)
8 | // .then((books) => console.log(books))
9 | // .catch((err) => console.log(err));
10 | (async () => {
11 | try {
12 | const response = await showBooks(url2);
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | })();
17 |
--------------------------------------------------------------------------------
/14 Async await/02_parallel/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-transform-runtime"
4 | ]
5 | }
--------------------------------------------------------------------------------
/14 Async await/02_parallel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/14 Async await/02_parallel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "async_await_demos",
3 | "version": "1.0.0",
4 | "description": "Async await tutorial",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Async",
12 | "await",
13 | "native"
14 | ],
15 | "author": "Jaime Salas",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/plugin-transform-runtime": "^7.8.3",
20 | "parcel": "^1.12.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/14 Async await/02_parallel/src/api/BookApiClient.js:
--------------------------------------------------------------------------------
1 | export class BookApiClient {
2 | async fetchBooks() {
3 | const url = 'http://localhost:8000/api/books/';
4 | const response = await fetch(url);
5 | return await response.json();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/14 Async await/02_parallel/src/api/bookAPI.js:
--------------------------------------------------------------------------------
1 | export async function showBooks(url) {
2 | const response = await fetch(url);
3 | if (!response.ok) {
4 | const err = await response.text();
5 | throw Error(err);
6 | }
7 | const body = await response.json();
8 | return body;
9 | }
10 |
11 | export async function fetchFromAPI(endpoint) {
12 | const url = `http://localhost:8000/api${endpoint}`;
13 | const response = await fetch(url);
14 | return await response.json();
15 | }
16 |
17 | export const functionExpressionShowBooks = async () => {
18 | const url = 'http://localhost:8000/api/books/';
19 | const response = await fetch(url);
20 | return await response.json();
21 | }
22 |
--------------------------------------------------------------------------------
/14 Async await/02_parallel/src/app.js:
--------------------------------------------------------------------------------
1 | import { showBooks, functionExpressionShowBooks, fetchFromAPI } from './api/bookAPI';
2 | import { BookApiClient } from './api/BookApiClient';
3 |
4 | const show = async () => {
5 | const [books, authors] = await Promise.all([
6 | fetchFromAPI('/books/'),
7 | fetchFromAPI('/authors/'),
8 | ]);
9 |
10 | console.log(`Books: ${books.length}, Authors: ${authors.length}`);
11 | };
12 |
13 | show();
--------------------------------------------------------------------------------
/14 Async await/03_sequential/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-transform-runtime"
4 | ]
5 | }
--------------------------------------------------------------------------------
/14 Async await/03_sequential/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/14 Async await/03_sequential/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "async_await_demos",
3 | "version": "1.0.0",
4 | "description": "Async await tutorial",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "parcel index.html"
9 | },
10 | "keywords": [
11 | "Async",
12 | "await",
13 | "native"
14 | ],
15 | "author": "Jaime Salas",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/plugin-transform-runtime": "^7.8.3",
20 | "parcel": "^1.12.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/14 Async await/03_sequential/src/api/BookApiClient.js:
--------------------------------------------------------------------------------
1 | export class BookApiClient {
2 | async fetchBooks() {
3 | const url = 'http://localhost:8000/api/books/';
4 | const response = await fetch(url);
5 | return await response.json();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/14 Async await/03_sequential/src/api/bookAPI.js:
--------------------------------------------------------------------------------
1 | export async function showBooks(url) {
2 | const response = await fetch(url);
3 | if (!response.ok) {
4 | const err = await response.text();
5 | throw Error(err);
6 | }
7 | const body = await response.json();
8 | return body;
9 | }
10 |
11 | export async function fetchFromAPI(endpoint) {
12 | const url = `http://localhost:8000/api${endpoint}`;
13 | const response = await fetch(url);
14 | return await response.json();
15 | }
16 |
17 | export const functionExpressionShowBooks = async () => {
18 | const url = 'http://localhost:8000/api/books/';
19 | const response = await fetch(url);
20 | return await response.json();
21 | }
22 |
--------------------------------------------------------------------------------
/14 Async await/03_sequential/src/app.js:
--------------------------------------------------------------------------------
1 | import { showBooks, functionExpressionShowBooks, fetchFromAPI } from './api/bookAPI';
2 | import { BookApiClient } from './api/BookApiClient';
3 |
4 | const show = async () => {
5 | const [books, authors] = await Promise.all([
6 | fetchFromAPI('/books/'),
7 | fetchFromAPI('/authors/'),
8 | ]);
9 |
10 | console.log(`Books: ${books.length}, Authors: ${authors.length}`);
11 | };
12 |
13 | show();
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Jaime Salas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # async-javascript-fundamentals
2 |
3 | Getting started with the concurrency model on JavaScript
4 |
5 | # About Basefactor + Lemoncode
6 |
7 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
8 |
9 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
10 |
11 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
12 |
13 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
--------------------------------------------------------------------------------
/auth/credentialResolver.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 |
3 | const users = [
4 | { id: 1, username: 'admin', password: 'admin' },
5 | { id: 2, username: 'guest', password: 'guest' },
6 | ];
7 |
8 | exports.resolveLogin = (username, password) => {
9 | let status;
10 | let response;
11 | let emptyCredentials = !username || !password;
12 | if (emptyCredentials) {
13 | status = 400;
14 | response = 'You need a username and password';
15 | }
16 |
17 | if (!emptyCredentials) {
18 | const user = users.find((u) =>
19 | u.username === username && u.password === password
20 | );
21 |
22 | if (!user) {
23 | status = 401;
24 | response = 'User not found';
25 | } else {
26 | const token = jwt.sign(
27 | {
28 | sub: user.id,
29 | username: user.username
30 | },
31 | 'mysupersecretkey',
32 | { expiresIn: '3 hours' }
33 | );
34 |
35 | status = 200;
36 | response = { access_token: token };
37 | }
38 | }
39 |
40 | return {
41 | status,
42 | response,
43 | }
44 | };
--------------------------------------------------------------------------------
/auth/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const cors = require('cors');
4 | const { resolveLogin } = require('./credentialResolver');
5 |
6 | const app = express();
7 | const PORT = process.env.PORT || 8887;
8 |
9 | app.use(bodyParser.json());
10 | app.use(cors());
11 |
12 | app.post('/login', (req, res) => {
13 | const { username, password } = req.body;
14 | const { status, response } = resolveLogin(username, password);
15 | res.status(status)
16 | .send(response);
17 | });
18 |
19 | app.get('*', (_, res) => {
20 | res.sendStatus(404);
21 | });
22 |
23 | app.listen(PORT, () => {
24 | console.log(`Server is running on ${PORT}`);
25 | });
--------------------------------------------------------------------------------
/auth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth",
3 | "version": "0.0.0",
4 | "description": "auth server for demo purpose",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "JWT",
12 | "nodejs"
13 | ],
14 | "author": "Jaime Salas",
15 | "license": "MIT",
16 | "dependencies": {
17 | "body-parser": "^1.18.3",
18 | "cors": "^2.8.5",
19 | "express": "^4.16.4",
20 | "jsonwebtoken": "^8.5.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | app:
4 | container_name: book-server
5 | environment:
6 | - ENVIRONMENT=mongodb:27017
7 | build: ./server-mongo
8 | ports:
9 | - "8000:8000"
10 | depends_on:
11 | - mongodb
12 | mongodb:
13 | image: mongo:3.2
14 | container_name: 'mongodb'
15 | volumes:
16 | - mongodbdata:/data/db
17 | ports:
18 | - "27017:27017"
19 | volumes:
20 | mongodbdata:
--------------------------------------------------------------------------------
/server-in-memory/README.md:
--------------------------------------------------------------------------------
1 | ## This is going to be our protected API.
2 |
3 | * To protect this API, we're going to use _express-jwt_.
4 | * We're sync _auth_ and _API_ by using in both places the same secret.
5 |
6 | ```javascript
7 | const jwtCheck = expressjwt({
8 | secret: 'mysupersecretkey',
9 | })
10 |
11 |
12 | app.use('/api/users', jwtCheck, users);
13 | app.use('/api/cars', jwtCheck, cars);
14 | ```
15 | * If the user is not authenticated will not get response from API
--------------------------------------------------------------------------------
/server-in-memory/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server_support_service_demos",
3 | "version": "0.0.0",
4 | "description": "API that expose data for client",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "express",
12 | "node",
13 | "rest",
14 | "api"
15 | ],
16 | "author": "Jaime Salas",
17 | "license": "MIT",
18 | "dependencies": {
19 | "body-parser": "^1.18.3",
20 | "cookie-parser": "^1.4.4",
21 | "cors": "^2.8.5",
22 | "express": "^4.16.4",
23 | "express-jwt": "^5.3.1"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/server-in-memory/routes/data/cars.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "car_id": 1,
4 | "name": "ABX",
5 | "brand": "Tokiota",
6 | "year_release": "1999"
7 | },
8 | {
9 | "car_id": 2,
10 | "name": "AZE",
11 | "brand": "Tokiota",
12 | "year_release": "1995"
13 | },
14 | {
15 | "car_id": 3,
16 | "name": "Colola",
17 | "brand": "GIA",
18 | "year_release": "2015"
19 | },
20 | {
21 | "car_id": 57,
22 | "name": "Maverick",
23 | "brand": "Foo",
24 | "year_release": "1965"
25 | },
26 | {
27 | "car_id": 59,
28 | "name": "Calel",
29 | "brand": "HMI",
30 | "year_release": "1975"
31 | },
32 | {
33 | "car_id": 61,
34 | "name": "Green Roaster",
35 | "brand": "HMI",
36 | "year_release": "1976"
37 | }
38 | ]
--------------------------------------------------------------------------------
/server-in-memory/routes/users.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* GET users listing. */
5 | router.get('/', (req, res) => {
6 | res.send('respond with a resource');
7 | });
8 |
9 | module.exports = router;
--------------------------------------------------------------------------------
/server-in-memory/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | path = require('path'),
3 | cookieParser = require('cookie-parser'),
4 | bodyParser = require('body-parser'),
5 | cors = require('cors'),
6 | expressjwt = require('express-jwt');
7 |
8 | const users = require('./routes/users');
9 | const cars = require('./routes/cars');
10 |
11 | const app = express();
12 |
13 | // setup
14 | app.use(bodyParser.json());
15 | app.use(bodyParser.urlencoded({ extended: false }));
16 | app.use(cookieParser());
17 | app.use(cors());
18 | const jwtCheck = expressjwt({
19 | secret: 'mysupersecretkey',
20 | });
21 |
22 | app.use('/api/users', jwtCheck, users);
23 | app.use('/api/cars', jwtCheck, cars);
24 |
25 |
26 | app.set('port', process.env.PORT || 3050);
27 | app.listen(app.get('port'));
28 |
29 | console.log('Listening on port: ' + app.get('port'));
30 |
31 | module.exports = app;
32 |
--------------------------------------------------------------------------------
/server-mock/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/*": "/$1"
3 | }
--------------------------------------------------------------------------------
/server-mock/mock-data/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "authors": [
3 | {
4 | "id": 1,
5 | "name": "Laika",
6 | "lastName": "Dog",
7 | "birthDate": "date"
8 | }
9 | ],
10 | "books": [
11 | {
12 | "id": 1,
13 | "title": "bone",
14 | "author": "Laika",
15 | "genre": "comedy",
16 | "read": true
17 | },
18 | {
19 | "title": "Lord of flies",
20 | "author": "William Goldwing",
21 | "genre": "Drama",
22 | "read": "on",
23 | "id": 2
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/server-mock/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server-mock",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "json-server --routes ./config/routes.json --watch mock-data/data.json --port 8000",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "json-server": "^0.16.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/server-mongo/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | Tests/
3 |
--------------------------------------------------------------------------------
/server-mongo/.env:
--------------------------------------------------------------------------------
1 | ENVIRONMENT=localhost
--------------------------------------------------------------------------------
/server-mongo/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible Node.js debug attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceRoot}/app.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/server-mongo/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10.16.0
2 | WORKDIR /usr/src/app
3 |
4 | COPY . .
5 |
6 | RUN npm install
7 |
8 | EXPOSE 8000
9 | CMD ["npm", "start"]
--------------------------------------------------------------------------------
/server-mongo/README.md:
--------------------------------------------------------------------------------
1 | ## To start mongo with docker:
2 |
3 | > Reference: https://www.thepolyglotdeveloper.com/2019/01/getting-started-mongodb-docker-container-deployment/
4 | > Reference: https://dev.to/jay97/docker-compose-an-express-and-mongo-app-aai
5 | > Refereence: https://nodejs.org/de/docs/guides/nodejs-docker-webapp/
6 |
7 | ```bash
8 | docker-compose up -d
9 | ```
10 |
11 | ```bash
12 | docker exec -it mongodb bash
13 | ```
14 |
15 | * This will open a terminal from where we can connect directly to mongo:
16 |
17 | ```bash
18 | mongo
19 | ```
20 |
21 |
22 |
23 | ## To start mongo with installed insatance windows:
24 |
25 | * mongod --dbpath D:/mongodb/data
26 |
27 | ### Open a new terminal and query the related collections. To start a new terminal we have to use mongo
28 |
29 | ```bash
30 | mongo
31 | ```
32 |
33 | ### Now that is open, we can inspect the related databases and collections
34 |
35 | ```bash
36 | > show dbs
37 | ```
38 |
39 | ```bash
40 | > use
41 | ```
42 |
43 | ```bash
44 | > show
45 | ```
46 |
47 | ```bash
48 | > db..find()
49 | ```
50 |
51 | * Remove element by Id from mongo console
52 | ```javascript
53 | db.books.remove({"_id": ObjectId("5d322fcba8a0243b90fd2f95")});
54 | ```
--------------------------------------------------------------------------------
/server-mongo/Routes/accessRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express'),
2 | getNumber = require('../utils/randomNumber').getNumber;
3 | resolveAccess = require('../controllers/accessControllerBusiness').resolveAccess;
4 | accessRouter = express.Router();
5 |
6 | const routes = () => {
7 | const accessController = require('../controllers/accessController')(resolveAccess, getNumber);
8 | accessRouter.route('/')
9 | .get(accessController.get);
10 | return accessRouter;
11 | };
12 |
13 | module.exports = routes;
14 |
--------------------------------------------------------------------------------
/server-mongo/Routes/authorRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express'),
2 | authorRouter = express.Router();
3 | // TODO: Create authorParser
4 |
5 | const routes = function (Author) { // TODO: Inject Author
6 | const authorController = require('../controllers/authorController')(Author);
7 | // Create controller to handle these requests.
8 | authorRouter.route('/')
9 | .post(authorController.post)
10 | .get(authorController.get);
11 |
12 | return authorRouter;
13 | };
14 |
15 | module.exports = routes;
16 |
--------------------------------------------------------------------------------
/server-mongo/Routes/bookRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express'),
2 | bookRouter = express.Router(),
3 | bookParser = require('../parsers/bookParser'),
4 | bookIdMiddleware = require('../middlewares/bookMiddleware').bookIdMiddleware;
5 |
6 | const routes = (Book) => {
7 | const bookController = require('../controllers/bookController')(Book, bookParser);
8 | const bookIdController = require('../controllers/bookByIdController')();
9 | bookRouter.route('/')
10 | .post(bookController.post)
11 | .get(bookController.get);
12 |
13 | bookRouter.use('/:bookId', bookIdMiddleware(Book));
14 |
15 | bookRouter.route('/:bookId')
16 | .get(bookIdController.get)
17 | .put(bookIdController.put)
18 | .patch(bookIdController.patch)
19 | .delete(bookIdController.delete); // Removes whatever book that it finds on middleware
20 |
21 | return bookRouter;
22 | };
23 |
24 | module.exports = routes;
25 |
--------------------------------------------------------------------------------
/server-mongo/Tests/bookControllerTests.js:
--------------------------------------------------------------------------------
1 | const should = require('should'),
2 | sinon = require('sinon');
3 |
4 | // We do not need an instance of mocha, because it is currently running on gulp
5 | describe('Book controller tests', function() {
6 | describe('Post', function () {
7 | it('should not allow an empty title on book', function () {
8 | // Arrange
9 | var Book = function (book) {
10 | this.save = function(){}
11 | };
12 |
13 | var req = {
14 | body: {
15 | author: 'Jhon Doe'
16 | }
17 | };
18 |
19 | var res = {
20 | status: sinon.spy(), //
21 | send: sinon.spy() // Tracks what is called, what is called with, how many times...
22 | };
23 |
24 | var bookController = require('../controllers/bookController')(Book);
25 |
26 | // Act
27 | bookController.post(req, res);
28 |
29 | // Assert
30 | res.status.calledWith(400).should.equal(true, 'Bad status ' + res.status.args[0][0]); // Array with the arguments that this function was called
31 | res.send.calledWith('Title is required').should.equal(true);
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/server-mongo/Tests/bookIntegrationTests.js:
--------------------------------------------------------------------------------
1 | const should = require('should'),
2 | request = require('supertest'),
3 | app = require('../app.js'),
4 | mongoose = require('mongoose'),
5 | Book = mongoose.model('Book'), // I can pull directly from mongoose because is loaded on app.js
6 | agent = request.agent(app); // Something from supertest to execute all our http calls.
7 |
8 | describe('Book crud Test', function () { // Go through our test database
9 | it('should allow a book to be posted and return a read and _id', function (done) {
10 | const bookPost = {
11 | title: 'new Book',
12 | author: 'Jhon Doe',
13 | genre: 'Fiction'
14 | };
15 |
16 | agent.post('/api/books')
17 | .send(bookPost)
18 | .expect(200)
19 | .end(function (err, results) {
20 | results.body.read.should.equal(false);
21 | results.body.should.have.property('_id');
22 | done(); // Lets supertest this is done so move on.
23 | });
24 | });
25 |
26 | afterEach(function (done){
27 | Book.remove().exec();
28 | done();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/server-mongo/controllers/accessController.js:
--------------------------------------------------------------------------------
1 | // https://javascriptweblog.wordpress.com/2010/07/26/no-more-ifs-alternatives-to-statement-branching-in-javascript/
2 | const accessController = (resolveAccess, getNumber) => {
3 | return {
4 | get: (req, res) => resolveAccess(getNumber)(res)
5 | };
6 | };
7 |
8 | module.exports = accessController;
9 |
--------------------------------------------------------------------------------
/server-mongo/controllers/accessControllerBusiness.js:
--------------------------------------------------------------------------------
1 | const mockAccess = {
2 | min: 1,
3 | max: 5,
4 | allowedValue: 3,
5 | forbiddenStatus: 403,
6 | notAllowedStatus: 405,
7 | };
8 |
9 | const mapStatuses = new Map([
10 | [mockAccess.forbiddenStatus, { message: 'Request rejected' }],
11 | [mockAccess.notAllowedStatus, { message: 'User without priviliges' }],
12 | [mockAccess.allowedValue, { message: 'Granted access' }]
13 | ]);
14 |
15 | const resolveStatusValue = (number) => {
16 | let statusValue;
17 | if(number > mockAccess.allowedValue) {
18 | statusValue = mockAccess.forbiddenStatus;
19 | } else if (number < mockAccess.allowedValue) {
20 | statusValue = mockAccess.notAllowedStatus;
21 | } else {
22 | statusValue = mockAccess.allowedValue;
23 | }
24 | return statusValue;
25 | };
26 |
27 | module.exports = {
28 | resolveAccess: (getNumber) => (res) => {
29 | const statusValue = resolveStatusValue(
30 | getNumber(mockAccess.min, mockAccess.max)
31 | );
32 | res.send(
33 | mapStatuses.get(statusValue)
34 | );
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/server-mongo/controllers/authorController.js:
--------------------------------------------------------------------------------
1 |
2 | const authorController = (Author) => {
3 | const business = require('./authorControllerBusiness')(Author);
4 |
5 | return {
6 | post: (req, res) => {
7 | (req.body.name && req.body.lastName) ?
8 | business.insertAuthor(req, res) :
9 | business.handleNotValidAuthor(res);
10 | },
11 | get: (req, res) => business.retrieveAuthors({}, res),
12 | }
13 | };
14 |
15 | module.exports = authorController;
16 |
--------------------------------------------------------------------------------
/server-mongo/controllers/authorControllerBusiness.js:
--------------------------------------------------------------------------------
1 | const authorControllerBusiness = (Author) => ({
2 | insertAuthor: (req, res) => {
3 | const author = new Author(req.body);
4 | author.save();
5 | res.status(201);
6 | res.send(author);
7 | },
8 | handleNotValidAuthor: (res) => {
9 | res.status(400);
10 | res.send('Name and Last Name is required.'); // TODO: Move to errors file.
11 | },
12 | retrieveAuthors: (query, res) => {
13 | Author.find(query, (err, authors) => {
14 | (err) ?
15 | res.status(500).send(err) :
16 | res.json(authors);
17 | });
18 | },
19 | });
20 |
21 | module.exports = authorControllerBusiness;
22 |
--------------------------------------------------------------------------------
/server-mongo/controllers/bookByIdController.js:
--------------------------------------------------------------------------------
1 | const bookIdController = () => {
2 | const populateBook = (req) => {
3 | req.book.title = req.body.title;
4 | req.book.author = req.body.author;
5 | req.book.genre = req.body.genre;
6 | req.book.read = req.body.read;
7 | // http://node.green/#ESNEXT-candidate--stage-3--object-rest-spread-properties
8 | // req.book = {...req.body}
9 | };
10 |
11 | const handleUpdateBook = (req, res) => {
12 | req.book.save((err) => {
13 | console.log(err);
14 | (err) ?
15 | res.status(500).send(err) :
16 | res.json(req.book);
17 | });
18 | };
19 |
20 | const populatePatchBook = (req) => {
21 | for (var variable in req.body) {
22 | if (req.body.hasOwnProperty(variable) && !req.body['_id']) {
23 | req.book[variable] = req.body[variable];
24 | }
25 | }
26 | }
27 |
28 | const handleRemoveBook = (req, res) => {
29 | req.book.remove((err) => {
30 | if (err) {
31 | res.status(500).send(err);
32 | } else {
33 | res.status(204).send('Remove');
34 | }
35 | });
36 | };
37 |
38 | return {
39 | get: (req, res) => res.json(req.book),
40 | put: (req, res) => {
41 | populateBook(req);
42 | handleUpdateBook(req, res);
43 | },
44 | patch: (req, res) => {
45 | populatePatchBook(req);
46 | handleUpdateBook(req, res);
47 | },
48 | delete: (req, res) => handleRemoveBook(req, res),
49 | };
50 | };
51 |
52 | module.exports = bookIdController ;
53 |
--------------------------------------------------------------------------------
/server-mongo/controllers/bookController.js:
--------------------------------------------------------------------------------
1 | const bookController = (Book, bookParser) => { // This way I can inject the Book that I want to test it.
2 | const business = require('./bookControllerBusiness')(Book);
3 |
4 | return {
5 | post: (req, res) => {
6 | (req.body.title) ?
7 | business.insertBook(req, res) :
8 | business.handleNotValidBook(res);
9 | },
10 | get: (req, res) => {
11 | const query = bookParser.queryParse(req);
12 | business.retrieveBooks(query, res);
13 | },
14 | };
15 | }
16 |
17 | module.exports = bookController;
18 |
--------------------------------------------------------------------------------
/server-mongo/controllers/bookControllerBusiness.js:
--------------------------------------------------------------------------------
1 | const bookControllerBusiness = (Book) => ({
2 | insertBook: (req, res) => {
3 | const book = new Book(req.body);
4 | book.save();
5 | res.status(201);
6 | res.send(book);
7 | },
8 | handleNotValidBook: (res) => {
9 | res.status(400);
10 | res.send('Title is required');
11 | },
12 | retrieveBooks: (query, res) => {
13 | Book.find(query, (err, books) => {
14 | (err) ?
15 | res.status(500).sen(err) :
16 | res.json(books);
17 | });
18 | }
19 | });
20 |
21 | module.exports = bookControllerBusiness;
22 |
--------------------------------------------------------------------------------
/server-mongo/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp'),
2 | nodemon = require('gulp-nodemon'),
3 | gulpMocha = require('gulp-mocha'),
4 | env = require('gulp-env'), // Let us manipulate the environment variables in Gulp, so that we can setup a test environment.
5 | supertest = require('supertest'); // This one allow us to make http calls
6 |
7 | gulp.task('default', function() {
8 | nodemon({
9 | script: 'app.js',
10 | ext: 'js',
11 | env: {
12 | PORT:8000
13 | },
14 | ignore: ['./node_modules/**']
15 | })
16 | .on('restart', function() {
17 | console.log('Restarting');
18 | });
19 | });
20 |
21 | gulp.task('tests', function (){
22 | /*
23 | When we get into our app.js I can do a process.env and pull in our environement,
24 | so it'll either be prod, dev or test and that will be governed by Gulp execution.
25 | */
26 | env({vars: {ENV:'Test'}});
27 | gulp.src('Tests/*.js', { read: false })
28 | .pipe(gulpMocha({reporter: 'nyan'}));
29 | });
30 |
--------------------------------------------------------------------------------
/server-mongo/middlewares/bookMiddleware.js:
--------------------------------------------------------------------------------
1 | const bookIdMiddleware = (Book) => (req, res, next) => {
2 | Book.findById(req.params.bookId, (err, book) => {
3 | if (err) {
4 | res.status(500).send(err);
5 | } else if (book) {
6 | req.book = book;
7 | next();
8 | } else {
9 | res.status(404).send('no book found');
10 | }
11 | });
12 | };
13 |
14 | module.exports = {
15 | bookIdMiddleware
16 | }
17 |
--------------------------------------------------------------------------------
/server-mongo/models/authorModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose'),
2 | Schema = mongoose.Schema;
3 |
4 | var authorModel = new Schema({
5 | name: {
6 | type: String
7 | },
8 | lastName: {
9 | type: String
10 | },
11 | birthDate: {
12 | type: Date
13 | }
14 | });
15 |
16 | module.exports = mongoose.model('Author', authorModel);
17 |
--------------------------------------------------------------------------------
/server-mongo/models/bookModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose'),
2 | Schema = mongoose.Schema;
3 |
4 | var bookModel = new Schema({
5 | title: {
6 | type: String
7 | },
8 | author: {
9 | type: String
10 | },
11 | genre: {
12 | type: String
13 | },
14 | read: {
15 | type: Boolean,
16 | default: false
17 | }
18 | });
19 |
20 | module.exports = mongoose.model('Book', bookModel);
21 |
--------------------------------------------------------------------------------
/server-mongo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rest_node",
3 | "version": "1.0.0",
4 | "description": "Create RESTful service with node express and mongo",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "gulp",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "Jaime Salas",
11 | "license": "ISC",
12 | "dependencies": {
13 | "body-parser": "^1.15.2",
14 | "cors": "^2.8.1",
15 | "dotenv": "^8.0.0",
16 | "express": "^4.14.0",
17 | "gulp": "~3.9.1",
18 | "gulp-mocha": "~3.0.1",
19 | "gulp-nodemon": "~2.2.1",
20 | "mongoose": "^4.6.4",
21 | "should": "^11.1.1",
22 | "sinon": "^1.17.6"
23 | },
24 | "devDependencies": {
25 | "gulp-env": "~0.4.0",
26 | "supertest": "^2.0.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server-mongo/parsers/bookParser.js:
--------------------------------------------------------------------------------
1 | const queryParse = function (req) {
2 | // Check if it is an empty object.
3 | // if( Object.keys(query).length > 0) {
4 | // console.log(query);
5 | // }
6 |
7 | const query = {}; // The request query string on JSON format!!! We can do casting on TypeScript
8 |
9 | if(req.query.genre) {
10 | query.genre = req.query.genre;
11 | }
12 |
13 | if(req.query.author) {
14 | query.author = req.query.author;
15 | }
16 |
17 | if(req.query.title) {
18 | query.title = req.query.title;
19 | }
20 |
21 | if(req.query.read) {
22 | query.read = req.query.read;
23 | }
24 |
25 | console.log(query);
26 |
27 | return query;
28 | };
29 |
30 | module.exports = {
31 | queryParse
32 | };
33 |
--------------------------------------------------------------------------------
/server-mongo/utils/randomNumber.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getNumber: (min, max) => Math.floor(Math.random()*(max - min)) + min
3 | };
4 |
--------------------------------------------------------------------------------