├── .gitignore
├── .vscode
└── settings.json
├── App1-Lista-Tareas
├── css
│ └── estilos.css
├── index.html
└── js
│ └── script.js
├── App2-Creador-Memes
├── css
│ └── estilos.css
├── index.html
└── js
│ ├── memes.js
│ └── script.js
├── App3-Widget-Clima
├── index.html
└── package.json
├── App4-Gestor-Registro-Eventos
├── cliente
│ ├── css
│ │ └── estilos.css
│ ├── estadisticas.html
│ ├── images
│ │ └── loading.gif
│ ├── index.html
│ ├── js
│ │ ├── estadisticas.js
│ │ ├── general.js
│ │ ├── script.js
│ │ ├── solicitudesApi.js
│ │ ├── ubicacion.js
│ │ └── validacionFormulario.js
│ └── ubicacion.html
└── servidor
│ ├── app.js
│ └── package.json
├── App5-Juego-Emojis
├── css
│ └── estilos.css
├── images
│ └── emoji.png
├── index.html
└── js
│ └── script.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node
3 | # Edit at https://www.gitignore.io/?templates=node
4 |
5 | ### Node ###
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # Diagnostic reports (https://nodejs.org/api/report.html)
15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 | *.pid.lock
22 |
23 | # Directory for instrumented libs generated by jscoverage/JSCover
24 | lib-cov
25 |
26 | # Coverage directory used by tools like istanbul
27 | coverage
28 | *.lcov
29 |
30 | # nyc test coverage
31 | .nyc_output
32 |
33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 |
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 |
39 | # node-waf configuration
40 | .lock-wscript
41 |
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 |
45 | # Dependency directories
46 | node_modules/
47 | jspm_packages/
48 |
49 | # TypeScript v1 declaration files
50 | typings/
51 |
52 | # TypeScript cache
53 | *.tsbuildinfo
54 |
55 | # Optional npm cache directory
56 | .npm
57 |
58 | # Optional eslint cache
59 | .eslintcache
60 |
61 | # Optional REPL history
62 | .node_repl_history
63 |
64 | # Output of 'npm pack'
65 | *.tgz
66 |
67 | # Yarn Integrity file
68 | .yarn-integrity
69 |
70 | # dotenv environment variables file
71 | .env
72 | .env.test
73 |
74 | # parcel-bundler cache (https://parceljs.org/)
75 | .cache
76 |
77 | # next.js build output
78 | .next
79 |
80 | # nuxt.js build output
81 | .nuxt
82 |
83 | # rollup.js default build output
84 | dist/
85 |
86 | # Uncomment the public line if your project uses Gatsby
87 | # https://nextjs.org/blog/next-9-1#public-directory-support
88 | # https://create-react-app.dev/docs/using-the-public-folder/#docsNav
89 | # public
90 |
91 | # Storybook build outputs
92 | .out
93 | .storybook-out
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # Temporary folders
108 | tmp/
109 | temp/
110 |
111 | # End of https://www.gitignore.io/api/node
112 |
113 | package-lock.json
114 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "liveServer.settings.port": 4500,
3 | "liveServer.settings.root": "/",
4 | "liveServer.settings.CustomBrowser" : "chrome",
5 | "liveServer.settings.AdvanceCustomBrowserCmdLine": "chrome --incognito --remote-debugging-port=9222",
6 | "liveServer.settings.NoBrowser" : false,
7 | "liveServer.settings.ignoreFiles" : [
8 | ".vscode/**",
9 | "**/*.scss",
10 | "**/*.sass"
11 | ],
12 | "liveServer.settings.donotShowInfoMsg": true,
13 | "editor.fontSize": 25,
14 | "editor.minimap.enabled": false,
15 | "editor.wordWrap": "on",
16 | "window.zoomLevel": 0
17 | }
--------------------------------------------------------------------------------
/App1-Lista-Tareas/css/estilos.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | }
4 |
5 | .area-contenido {
6 | padding: 40px 15px;
7 | }
8 |
9 | .area-entrada {
10 | margin: 25px;
11 | }
12 |
13 | .texto-tarea {
14 | display: flex;
15 | align-items: center;
16 | height: 40px;
17 | }
18 |
19 | .oculto {
20 | display: none;
21 | }
22 |
23 | .area-icono-eliminacion {
24 | display: flex;
25 | align-items: center;
26 | height: 40px;
27 | }
28 |
29 | .icono-eliminacion {
30 | color: red;
31 | }
32 |
33 | .tarea-completada {
34 | text-decoration: line-through;
35 | }
36 |
37 | .caja-comprobacion {
38 | margin-top: 15px;
39 | }
40 |
--------------------------------------------------------------------------------
/App1-Lista-Tareas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Gestor Lista Tareas
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 | Agregar
37 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
56 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/App1-Lista-Tareas/js/script.js:
--------------------------------------------------------------------------------
1 | class ListaTareas{
2 | constructor(){
3 | this.tareas = JSON.parse(localStorage.getItem('tareas'));
4 |
5 | if (!this.tareas) {
6 | this.tareas = [
7 | {tarea: 'Aprender JavaScript', completado: false},
8 | {tarea: 'Aprender Python', completado: false},
9 | {tarea: 'Aprender C++', completado: true}
10 | ];
11 | }
12 |
13 | this.cargarTareas();
14 | this.agregarEventListeners();
15 | }
16 |
17 | agregarEventListeners() {
18 | document.getElementById('recordatorio').addEventListener('keypress', (evento) => {
19 | if(evento.keyCode == 13){
20 | this.agregarTarea(evento.target.value);
21 | evento.target.value = '';
22 | }
23 | });
24 | }
25 |
26 | cargarTareas() {
27 | localStorage.setItem('tareas', JSON.stringify(this.tareas));
28 | let htmlTareas = this.tareas.reduce((html, tarea, indice) => html += this.generarHtmlTarea(tarea, indice), '');
29 | document.getElementById('listaTareas').innerHTML = htmlTareas;
30 | }
31 |
32 | cambiarEstadoTarea(indice) {
33 | this.tareas[indice].completado = !this.tareas[indice].completado;
34 | this.cargarTareas();
35 | }
36 |
37 | eliminarTarea(evento, indice) {
38 | evento.preventDefault();
39 | this.tareas.splice(indice, 1);
40 | this.cargarTareas();
41 | }
42 |
43 | generarHtmlTarea(tarea, indice) {
44 | return `
45 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ${tarea.tarea}
55 |
56 |
61 |
62 |
63 |
64 | `;
65 | }
66 |
67 | agregarTarea(tarea) {
68 | let padre = document.getElementById('recordatorio').parentElement;
69 |
70 | if (tarea !== ''){
71 | padre.classList.remove('has-error');
72 |
73 | let nuevaTarea = {
74 | tarea,
75 | completado: false
76 | };
77 |
78 | this.tareas.push(nuevaTarea);
79 | this.cargarTareas();
80 | } else {
81 | padre.classList.add('has-error');
82 | }
83 | }
84 |
85 | agregarTareaClick() {
86 | let recordatorio = document.getElementById('recordatorio');
87 | let tarea = recordatorio.value;
88 | if (tarea){
89 | this.agregarTarea(tarea);
90 | recordatorio.value = '';
91 | }
92 | }
93 | }
94 |
95 | let listaTareas;
96 |
97 | window.addEventListener('load', () => {
98 | listaTareas = new ListaTareas();
99 | })
100 |
--------------------------------------------------------------------------------
/App2-Creador-Memes/css/estilos.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 70px;
3 | }
4 |
5 | .principal{
6 | display: flex;
7 | flex-direction: row;
8 | flex-wrap: wrap;
9 | justify-content: space-around;
10 | }
11 |
12 | .area-canvas {
13 | flex: 2;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | margin: 10px;
18 | }
19 |
20 | .imagen-canvas {
21 | border: 2px solid #000000;
22 | }
23 |
24 | .area-entrada {
25 | flex: 1;
26 | display: flex;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: center;
30 | margin: 10px;
31 | }
32 |
33 | .form-group {
34 | width: 90%;
35 | }
36 |
37 | .boton-descarga {
38 | margin-top: 10px;
39 | }
40 |
--------------------------------------------------------------------------------
/App2-Creador-Memes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Creador Memes App
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 | ¡Lo sentimos! Su navegador no provee soporte para canvas.
25 |
26 |
27 |
28 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/App2-Creador-Memes/js/memes.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fhernd/JavaScript-Curso/dddd992a8b20e069583051a68a337a156ee42e25/App2-Creador-Memes/js/memes.js
--------------------------------------------------------------------------------
/App2-Creador-Memes/js/script.js:
--------------------------------------------------------------------------------
1 | const ANCHO_DISPOSITIVO = window.innerWidth;
2 |
3 | class CreadorMemes{
4 | constructor() {
5 | this.imagenCanvas = document.getElementById('imagenCanvas');
6 | this.imagen = document.getElementById('imagen');
7 | this.textoSuperior = document.getElementById('textoSuperior');
8 | this.textoInferior = document.getElementById('textoInferior');
9 | this.btnDescargarMeme = document.getElementById('descargarMeme');
10 |
11 | this.crearCanvas();
12 | this.agregarEventListeners();
13 | }
14 |
15 | crearCanvas() {
16 | let alto = Math.min(480, ANCHO_DISPOSITIVO - 30);
17 | let ancho = Math.min(640, ANCHO_DISPOSITIVO - 30);
18 |
19 | this.imagenCanvas.height = alto;
20 | this.imagenCanvas.width = ancho;
21 | }
22 |
23 | agregarEventListeners() {
24 | this.crearMeme = this.crearMeme.bind(this);
25 | this.descargarMeme = this.descargarMeme.bind(this);
26 | let entradas = [this.textoSuperior, this.textoInferior, this.imagen];
27 |
28 | entradas.forEach(e => e.addEventListener('keyup', this.crearMeme));
29 | entradas.forEach(e => e.addEventListener('change', this.crearMeme));
30 | this.btnDescargarMeme.addEventListener('click', this.descargarMeme);
31 | }
32 |
33 | crearMeme() {
34 | let contexto = this.imagenCanvas.getContext('2d');
35 |
36 | if(this.imagen.files && this.imagen.files[0]){
37 | let lector = new FileReader();
38 |
39 | lector.onload = () => {
40 | let image = new Image();
41 |
42 | image.onload = () => {
43 | this.imagenCanvas.height = image.height;
44 | this.imagenCanvas.width = image.width;
45 |
46 | contexto.clearRect(0, 0, this.imagenCanvas.height, this.imagenCanvas.width);
47 | contexto.drawImage(image, 0, 0);
48 |
49 | let tamagnioFuente = ((this.imagenCanvas.width + this.imagenCanvas.height)/2) * 4 / 100;
50 | contexto.font = `${tamagnioFuente}pt sans-serif`;
51 | contexto.textAlign = 'center';
52 | contexto.textBaseline = 'top';
53 |
54 | contexto.lineJoin = 'round';
55 | contexto.lineWidth = tamagnioFuente/5;
56 | contexto.strokeStyle = 'black';
57 | contexto.fillStyle = 'white';
58 |
59 | let textoArriba = this.textoSuperior.value.toUpperCase();
60 | let textoAbajo = this.textoInferior.value.toUpperCase();
61 |
62 | contexto.strokeText(textoArriba, this.imagenCanvas.width/2, this.imagenCanvas.height*(5/100));
63 | contexto.fillText(textoArriba, this.imagenCanvas.width/2, this.imagenCanvas.height*(5/100));
64 |
65 | contexto.strokeText(textoAbajo, this.imagenCanvas.width/2, this.imagenCanvas.height*(90/100));
66 | contexto.fillText(textoAbajo, this.imagenCanvas.width/2, this.imagenCanvas.height*(90/100));
67 |
68 | this.redimensionarCanvas(this.imagenCanvas.height, this.imagenCanvas.width);
69 | };
70 |
71 | image.src = lector.result;
72 | };
73 |
74 | lector.readAsDataURL(this.imagen.files[0]);
75 | }
76 | }
77 |
78 | redimensionarCanvas(alto, ancho) {
79 | this.imagenCanvas.style.height = `${alto}px`;
80 | this.imagenCanvas.style.width = `${ancho}px`;
81 |
82 | while(alto > Math.min(1000, ANCHO_DISPOSITIVO - 30)
83 | && ancho > Math.min(1000, ANCHO_DISPOSITIVO - 30)){
84 | alto /= 2;
85 | ancho /= 2;
86 | this.imagenCanvas.style.height = `${alto}px`;
87 | this.imagenCanvas.style.width = `${ancho}px`;
88 | }
89 | }
90 |
91 | descargarMeme() {
92 | if(!this.imagen.files[0]){
93 | this.imagen.parentElement.classList.add('has-error');
94 | return;
95 | }
96 |
97 | if (this.textoInferior.value === ''){
98 | this.imagen.parentElement.classList.add('has-error');
99 | this.textoInferior.parentElement.classList.add('has-error');
100 | return;
101 | }
102 |
103 | this.imagen.parentElement.classList.remove('has-error');
104 | this.textoInferior.parentElement.classList.remove('has-error');
105 |
106 | let fuenteImagen = this.imagenCanvas.toDataURL('image/png');
107 | let atributo = document.createAttribute('href');
108 | atributo.value = fuenteImagen.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
109 | this.btnDescargarMeme.setAttributeNode(atributo);
110 | }
111 | }
112 |
113 | new CreadorMemes();
114 |
--------------------------------------------------------------------------------
/App3-Widget-Clima/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | App3 - Widget Clima
7 |
8 |
9 |
14 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/App3-Widget-Clima/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app3-widget-clima",
3 | "version": "1.0.0",
4 | "description": "Widget para mostrar el estado del clima.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "widget",
11 | "clima",
12 | "html",
13 | "javascript"
14 | ],
15 | "author": "John Ortiz Ordoñez",
16 | "license": "ISC",
17 | "dependencies": {
18 | "x-weather": "^2.3.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/css/estilos.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 78px;
3 | }
4 |
5 | .area-formulario{
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | justify-content: center;
10 | }
11 |
12 | .titulo {
13 | margin: 20px;
14 | }
15 |
16 | .form-group {
17 | min-width: 500px;
18 | }
19 |
20 | @media only screen and (max-width: 736px) {
21 | .form-group {
22 | min-width: 90vw;
23 | }
24 | }
25 |
26 | .indicador-carga {
27 | max-width: 50px;
28 | max-height: 50px;
29 | }
30 |
31 | .area-estadisticas {
32 | margin: 25px;
33 | max-width: 600px;
34 | }
35 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/estadisticas.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | App Registro Eventos
8 |
10 |
11 |
12 |
13 |
14 |
15 |
34 |
35 |
36 |
37 |
Datos Asistencia
38 |
39 |
Los datos se están cargando...
40 |
41 |
42 |
43 |
No se ha podido cargar los datos de estadísticas. Intente refrescando la página.
44 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fhernd/JavaScript-Curso/dddd992a8b20e069583051a68a337a156ee42e25/App4-Gestor-Registro-Eventos/cliente/images/loading.gif
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | App Registro Eventos
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
38 |
39 |
40 |
93 |
94 |
95 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/estadisticas.js:
--------------------------------------------------------------------------------
1 | class Estadisticas{
2 | constructor() {
3 | this.indicadorCarga = document.querySelector('#indicadorCarga');
4 | this.errorCarga = document.querySelector('#errorCarga');
5 |
6 | this.tabsEstadisticas = document.querySelector('#tabsEstadisticas');
7 | this.tabProfesion = document.querySelector('#tabProfesion');
8 | this.tabExperiencia = document.querySelector('#tabExperiencia');
9 | this.tabEdad = document.querySelector('#tabEdad');
10 |
11 | this.areaEstadisticas = document.querySelector('#areaEstadisticas');
12 | this.graficoProfesion = document.querySelector('#graficoProfesion');
13 | this.graficoExperiencia = document.querySelector('#graficoExperiencia');
14 | this.graficoEdad = document.querySelector('#graficoEdad');
15 |
16 | this.datosEstadisticas;
17 | this.cargarDatosEstadisticas();
18 | this.agregarEventListeners();
19 | }
20 |
21 | cargarDatosEstadisticas() {
22 | solicitudApi('estadisticas')
23 | .then(respuesta => {
24 | this.datosEstadisticas = respuesta;
25 |
26 | this.indicadorCarga.classList.add('hidden');
27 | this.tabsEstadisticas.classList.remove('hidden');
28 | this.areaEstadisticas.classList.remove('hidden');
29 |
30 | this.cargarDatosProfesion();
31 | })
32 | .catch(() => {
33 | this.indicadorCarga.classList.add('hidden');
34 | this.errorCarga.classList.remove('hidden');
35 | });
36 | }
37 |
38 | agregarEventListeners() {
39 | this.tabProfesion.addEventListener('click', this.cargarDatosProfesion.bind(this));
40 | this.tabExperiencia.addEventListener('click', this.cargarDatosExperiencia.bind(this));
41 | this.tabEdad.addEventListener('click', this.cargarDatosEdad.bind(this));
42 | }
43 |
44 | cargarDatosProfesion(evento = null){
45 | if(evento){
46 | evento.preventDefault();
47 | }
48 |
49 | this.ocultarGraficos();
50 | this.graficoProfesion.classList.remove('hidden');
51 | this.tabProfesion.classList.add('active');
52 |
53 | const data = {
54 | datasets: [{
55 | data: this.datosEstadisticas.profesion,
56 | backgroundColor: [
57 | 'rgba(255, 99, 132, 0.6)',
58 | 'rgba(54, 162, 235, 0.6)',
59 | 'rgba(255, 206, 86, 0.6)',
60 | 'rgba(75, 192, 192, 0.6)'
61 | ],
62 | borderColor: ['white', 'white', 'white', 'white']
63 | }],
64 | labels: ['Estudiante', 'Desarrollador', 'Ingeniero', 'Otro']
65 | };
66 |
67 | new Chart(this.graficoProfesion, {type: 'pie', data});
68 | }
69 |
70 | cargarDatosExperiencia(evento = null){
71 | if(evento){
72 | evento.preventDefault();
73 | }
74 |
75 | this.ocultarGraficos();
76 | this.graficoExperiencia.classList.remove('hidden');
77 | this.tabExperiencia.classList.add('active');
78 |
79 | const data = {
80 | datasets: [{
81 | data: this.datosEstadisticas.experiencia,
82 | backgroundColor: [
83 | 'rgba(255, 99, 132, 0.6)',
84 | 'rgba(54, 162, 235, 0.6)',
85 | 'rgba(255, 206, 86, 0.6)'
86 | ],
87 | borderColor: ['white', 'white', 'white']
88 | }],
89 | labels: ['Principiante', 'Intermedio', 'Avanzado']
90 | };
91 |
92 | new Chart(this.graficoExperiencia, {type: 'pie', data});
93 | }
94 |
95 | cargarDatosEdad(evento = null){
96 | if(evento){
97 | evento.preventDefault();
98 | }
99 |
100 | this.ocultarGraficos();
101 | this.graficoEdad.classList.remove('hidden');
102 | this.tabEdad.classList.add('active');
103 |
104 | const data = {
105 | datasets: [{
106 | data: this.datosEstadisticas.edad,
107 | backgroundColor: [
108 | 'rgba(255, 99, 132, 0.6)',
109 | 'rgba(54, 162, 235, 0.6)',
110 | 'rgba(255, 206, 86, 0.6)'
111 | ],
112 | borderColor: ['white', 'white', 'white']
113 | }],
114 | labels: ['Grupo 40 Años', 'Grupo 30 Años', 'Grupo 20 Años']
115 | };
116 |
117 | new Chart(this.graficoEdad, {type: 'pie', data});
118 | }
119 |
120 | ocultarGraficos() {
121 | this.graficoProfesion.classList.add('hidden');
122 | this.graficoExperiencia.classList.add('hidden');
123 | this.graficoEdad.classList.add('hidden');
124 |
125 | this.tabProfesion.classList.remove('active');
126 | this.tabExperiencia.classList.remove('active');
127 | this.tabEdad.classList.remove('active');
128 | }
129 | }
130 |
131 | window.addEventListener('load', () => {
132 | new Estadisticas();
133 | });
134 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/general.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fhernd/JavaScript-Curso/dddd992a8b20e069583051a68a337a156ee42e25/App4-Gestor-Registro-Eventos/cliente/js/general.js
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/script.js:
--------------------------------------------------------------------------------
1 | class RegistroEvento{
2 | constructor() {
3 | this.frmRegistroEvento = document.querySelector('#frmRegistroEvento');
4 | this.nombre = document.querySelector('#nombre');
5 | this.email = document.querySelector('#email');
6 | this.telefonoMovil = document.querySelector('#telefonoMovil');
7 | this.edad = document.querySelector('#edad');
8 | this.profesion = document.querySelector('#profesion');
9 | this.experiencia = document.querySelector('#experiencia');
10 | this.expectativas = document.querySelector('#expectativas');
11 | this.registrarEvento = document.querySelector('#registrarEvento');
12 | this.indicadorCarga = document.querySelector('#indicadorCarga');
13 |
14 | this.frmRegistroEvento.addEventListener('submit', evento => {
15 | this.subirFormulario(evento);
16 | });
17 | }
18 |
19 | subirFormulario(evento){
20 | event.preventDefault();
21 |
22 | const datosFormulario = this.obtenerDatosFormulario();
23 | const resultadoValidacion = validarDatosFormularioRegistroEvento(datosFormulario);
24 |
25 | if (resultadoValidacion.esValido){
26 | this.removerErroresCampos();
27 | this.prepararEnvioDatos(datosFormulario);
28 | } else {
29 | this.removerErroresCampos();
30 | this.resaltarCamposConErrores(resultadoValidacion.resultado);
31 | }
32 | }
33 |
34 | obtenerDatosFormulario() {
35 | return {
36 | nombre: this.nombre.value,
37 | email: this.email.value,
38 | telefonoMovil: this.telefonoMovil.value,
39 | edad: this.edad.value,
40 | profesion: this.profesion.value,
41 | experiencia: parseInt(document.querySelector('input[name="experiencia"]:checked').value),
42 | expectativas: this.expectativas.value
43 | };
44 | }
45 |
46 | removerErroresCampos() {
47 | this.nombre.parentElement.classList.remove('has-error');
48 | this.email.parentElement.classList.remove('has-error');
49 | this.telefonoMovil.parentElement.classList.remove('has-error');
50 | this.edad.parentElement.classList.remove('has-error');
51 | this.profesion.parentElement.classList.remove('has-error');
52 | this.experiencia.parentElement.classList.remove('has-error');
53 | }
54 |
55 | resaltarCamposConErrores(resultado) {
56 | if(!resultado.nombre){
57 | this.nombre.parentElement.classList.add('has-error');
58 | }
59 | if(!resultado.email){
60 | this.email.parentElement.classList.add('has-error');
61 | }
62 | if(!resultado.telefonoMovil){
63 | this.telefonoMovil.parentElement.classList.add('has-error');
64 | }
65 | if(!resultado.edad){
66 | this.edad.parentElement.classList.add('has-error');
67 | }
68 | if(!resultado.profesion){
69 | this.profesion.parentElement.classList.add('has-error');
70 | }
71 | if(!resultado.experiencia){
72 | this.experiencia.parentElement.classList.add('has-error');
73 | }
74 | }
75 |
76 | prepararEnvioDatos(datosFormulario){
77 | this.registrarEvento.classList.add('hidden');
78 | this.indicadorCarga.classList.remove('hidden');
79 |
80 | solicitudApi('registro', datosFormulario, 'POST')
81 | .then(respuesta => {
82 | this.registrarEvento.classList.remove('hidden');
83 | this.indicadorCarga.classList.add('hidden');
84 | this.removerDatosFormulario();
85 | alertify.alert(respuesta.mensaje);
86 | })
87 | .catch(() => {
88 | this.registrarEvento.classList.remove('hidden');
89 | this.indicadorCarga.classList.add('hidden');
90 | });
91 | }
92 |
93 | removerDatosFormulario(){
94 | this.nombre.value = '';
95 | this.email.value = '';
96 | this.telefonoMovil.value = '';
97 | this.edad.value = '';
98 | this.profesion.value = 'Estudiante';
99 | this.experiencia.checked = true;
100 | this.expectativas.value = '';
101 | }
102 | }
103 |
104 | window.addEventListener('load', () => {
105 | new RegistroEvento();
106 | });
107 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/solicitudesApi.js:
--------------------------------------------------------------------------------
1 | function solicitudApi(ruta, body={}, method='GET'){
2 | let solicitud = new Promise((resolve, reject) => {
3 | const headers = new Headers({
4 | 'Content-Type': 'application/json'
5 | });
6 |
7 | const detallesSolicitud = {
8 | method,
9 | mode: 'cors',
10 | headers
11 | };
12 |
13 | if(method !== 'GET'){
14 | detallesSolicitud.body = JSON.stringify(body);
15 | }
16 |
17 | function gestionarErrores(respuesta){
18 | if (respuesta.ok){
19 | return respuesta.json();
20 | } else {
21 | throw Error(respuesta.statusError);
22 | }
23 | }
24 |
25 | fetch(`http://localhost:3900/${ruta}`, detallesSolicitud)
26 | .then(gestionarErrores)
27 | .then(resolve)
28 | .then(reject);
29 | });
30 |
31 | const temporizador = new Promise((resolve, reject) => {
32 | setTimeout(reject, 7000, 'La solicitud no se pudo completar.');
33 | });
34 |
35 | return new Promise((resolve, reject) => {
36 | Promise.race([solicitud, temporizador])
37 | .then(resolve)
38 | .catch(reject);
39 | });
40 | }
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/ubicacion.js:
--------------------------------------------------------------------------------
1 | function inicializarMap(){
2 | let mapa = new google.maps.Map(document.getElementById('mapaUbicacionEvento'), {
3 | zoom: 15,
4 | center: {lat: 4.6097102, lng: -74.081749}
5 | });
6 |
7 | let marcador = new google.maps.Marker({
8 | map: mapa,
9 | draggable: true,
10 | animation: google.maps.Animation.DROP,
11 | position: {lat: 4.6097102, lng: -74.081749}
12 | });
13 |
14 | marcador.addListener('click', () => {
15 | ventanaInformacion.open(mapa, marcador);
16 | });
17 |
18 | let ventanaInformacion = new google.maps.InfoWindow({
19 | content: 'El evento se realizará en Bogotá Colombia.'
20 | });
21 |
22 | ventanaInformacion.open(mapa, marcador);
23 | }
24 |
25 | window.addEventListener('load', () => {
26 | const scriptMapa = document.createElement('script');
27 | const llaveApi = '<>';
28 | scriptMapa.src = `https://maps.googleapis.com/maps/api/js?key=${llaveApi}&callback=inicializarMap`;
29 | document.body.appendChild(scriptMapa);
30 | });
31 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/js/validacionFormulario.js:
--------------------------------------------------------------------------------
1 | function validarDatosFormularioRegistroEvento(datosFormulario){
2 | const resultado = {
3 | nombre: validarNombre(datosFormulario.nombre),
4 | email: validarEmail(datosFormulario.email),
5 | telefonoMovil: validarTelefonoMovil(datosFormulario.telefonoMovil),
6 | edad: validarEdad(datosFormulario.edad),
7 | profesion: validarProfesion(datosFormulario.profesion),
8 | experiencia: validarExperiencia(datosFormulario.experiencia)
9 | };
10 |
11 | let campo;
12 | let esValido = true;
13 |
14 | for(campo in resultado){
15 | esValido = esValido && resultado[campo];
16 | }
17 |
18 | return {
19 | esValido,
20 | resultado
21 | };
22 | }
23 |
24 | function validarNombre(nombre){
25 | return nombre.length > 3;
26 | }
27 |
28 | function validarEmail(email){
29 | const regexEmail = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
30 |
31 | return regexEmail.test(email);
32 | }
33 |
34 | function validarTelefonoMovil(telefonoMovil){
35 | const regexTelefonoMovil = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
36 |
37 | return regexTelefonoMovil.test(telefonoMovil);
38 | }
39 |
40 | function validarEdad(edad){
41 | return edad >= 10 && edad <= 50;
42 | }
43 |
44 | function validarProfesion(profesion){
45 | const profesionesDisponibles = ['estudiante', 'desarrollador', 'ingeniero', 'otro'];
46 |
47 | return profesionesDisponibles.indexOf(profesion) > -1;
48 | }
49 |
50 | function validarExperiencia(experiencia){
51 | return experiencia >= 1 && experiencia <= 3;
52 | }
53 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/cliente/ubicacion.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | App Registro Eventos
8 |
10 |
11 |
12 |
13 |
14 |
15 |
34 |
35 |
36 |
37 |
38 |
Acerca de
39 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis ex congue metus imperdiet
40 | finibus. Pellentesque sed ipsum ullamcorper, pulvinar lacus in, euismod turpis. Vestibulum iaculis
41 | turpis sed enim sodales pretium. Praesent viverra accumsan venenatis. Curabitur molestie urna in neque
42 | ultricies eleifend. Sed sit amet lacinia eros, id accumsan massa. Nam volutpat augue dapibus fermentum
43 | iaculis. Phasellus fermentum enim at tellus ultricies lacinia. Fusce eget ex eu velit scelerisque
44 | vulputate. Nunc ut justo magna. Etiam mi risus, dictum id eleifend eu, varius semper sem.
45 |
46 |
47 |
Ubicación del evento
48 |
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/servidor/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const http = require('http');
4 |
5 | let app = express();
6 | app.set('port', 3900);
7 | app.use(bodyParser.json());
8 |
9 | app.use(function(req, res, next) {
10 | res.header('Access-Control-Allow-Origin', '*');
11 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
12 | next();
13 | });
14 |
15 | app.get('/', function(req, res) {
16 | res.send('Bienvenido a la API de Registro de Eventos.');
17 | });
18 |
19 | app.post('/registro', (req, res, next) => {
20 | let campo;
21 | for(campo in req.body){
22 | if(!req.body[campo] && campo !== 'expectativas'){
23 | res.status(400).json({});
24 | return next();
25 | }
26 | }
27 |
28 | setTimeout(() => {
29 | res.status(200).json({'mensaje': `El usuario ${req.body.nombre} se ha registrado de forma satisfactoria.`});
30 | }, 5000);
31 | });
32 |
33 | app.get('/estadisticas', (req, res, next) => {
34 | setTimeout(() => {
35 | res.status(200).json({
36 | profesion: [40, 35, 25, 30],
37 | experiencia: [10, 30, 20],
38 | edad: [40, 30, 20]
39 | });
40 | }, 5000);
41 | });
42 |
43 | let servidor = http.Server(app);
44 | servidor.listen(app.get('port'), function() {
45 | console.log(`El servidor Express se está ejecutando en el puerto ${app.get('port')}.`);
46 | });
47 |
--------------------------------------------------------------------------------
/App4-Gestor-Registro-Eventos/servidor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "servidor",
3 | "version": "1.0.0",
4 | "description": "Backend para aplicación de Registro de Eventos.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "backend",
11 | "servidor",
12 | "aplicación"
13 | ],
14 | "author": "John Ortiz Ordoñez",
15 | "license": "ISC",
16 | "dependencies": {
17 | "body-parser": "^1.19.0",
18 | "express": "^4.17.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/App5-Juego-Emojis/css/estilos.css:
--------------------------------------------------------------------------------
1 | img {
2 | position: absolute;
3 | }
4 |
5 | .panel {
6 | border: 2px solid gray;
7 | min-height: 500px;
8 | }
--------------------------------------------------------------------------------
/App5-Juego-Emojis/images/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fhernd/JavaScript-Curso/dddd992a8b20e069583051a68a337a156ee42e25/App5-Juego-Emojis/images/emoji.png
--------------------------------------------------------------------------------
/App5-Juego-Emojis/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Emoji Matching Game
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Emoji Matching Game
20 |
Instrucciones: Clic sobre el emoji faltante en el panel derecho
21 |
22 |
23 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/App5-Juego-Emojis/js/script.js:
--------------------------------------------------------------------------------
1 | class EmojiMatchingGame{
2 | constructor() {
3 | this.panelIzquierdo = document.getElementById('panelIzquierdo');
4 | this.panelDerecho = document.getElementById('panelDerecho');
5 | this.body = document.getElementsByTagName('body')[0];
6 | this.cantidadEmojis = 5;
7 | this.imagenEmoji = 'images/emoji.png';
8 |
9 | this.generarEmojis();
10 | this.body.onclick = this.terminarJuego.bind(this);
11 | }
12 |
13 | generarEmojis() {
14 | for(let i = 1; i < this.cantidadEmojis; ++i){
15 | let nuevoEmoji = document.createElement('img');
16 | nuevoEmoji.src = this.imagenEmoji;
17 | let x = Math.floor(Math.random() * 380);
18 | let y = Math.floor(Math.random() * 400);
19 | nuevoEmoji.style.left = `${x}px`;
20 | nuevoEmoji.style.top = `${y}px`;
21 |
22 | this.panelIzquierdo.appendChild(nuevoEmoji);
23 | }
24 |
25 | this.configurarPaneles();
26 | }
27 |
28 | configurarPaneles() {
29 | let clonacionEmojis = this.panelIzquierdo.cloneNode(true);
30 | clonacionEmojis.removeChild(clonacionEmojis.lastChild);
31 |
32 | this.panelDerecho.appendChild(clonacionEmojis);
33 |
34 | this.panelIzquierdo.lastChild.onclick = this.pasarSiguienteNivel.bind(this);
35 | }
36 |
37 | pasarSiguienteNivel(evento) {
38 | evento.stopPropagation();
39 |
40 | while(this.panelIzquierdo.hasChildNodes()){
41 | this.panelIzquierdo.removeChild(this.panelIzquierdo.lastChild);
42 | }
43 |
44 | while(this.panelDerecho.hasChildNodes()){
45 | this.panelDerecho.removeChild(this.panelDerecho.lastChild);
46 | }
47 |
48 | this.cantidadEmojis += 3;
49 |
50 | this.generarEmojis();
51 | }
52 |
53 | terminarJuego() {
54 | alertify.alert("¡Has perdido!");
55 |
56 | this.panelIzquierdo.lastChild.onclick = null;
57 | this.body.onclick = null;
58 | }
59 | }
60 |
61 | window.addEventListener('load', () => {
62 | new EmojiMatchingGame();
63 | });
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # JavaScript - Curso
4 |
5 | > **Autor:** John Ortiz Ordoñez - [https://www.linkedin.com/in/john-ortiz-ordo%C3%B1ez/](https://www.linkedin.com/in/john-ortiz-ordo%C3%B1ez/)
6 | > **Mail**: johnortizo@outlook.com
7 | > **Canal YouTube**: [http://bit.ly/2SiCmCJ](http://bit.ly/2SiCmCJ)
8 |
9 | ## 1. Aplicación - Lista de Tareas
10 |
11 | Aplicación para la gestión de lista de tareas pendientes por realizar. El usuario puede ingresar las tareas pendientes por realizar, puede marcar las que ya ha completado, y también le facilita su eliminación.
12 |
13 | ## 2. Aplicación - Creador de Memes
14 |
15 | Aplicación para la creación de memes. El usuario puede seleccionar una imagen desde su sistema y especificar un texto personalizado para la parte superior e inferior de la imagen. Al final, el usuario puede descarga la imagen en formato PNG.
16 |
17 | ## 3. Widget - Estado Metereológico
18 |
19 | Uso de Web Components para la integración de componentes HTML en aplicaciones Web. Uso de NodeJS para resolución de dependencias.
20 |
21 | ## 4. Aplicación - Gestor de Registro de Eventos
22 |
23 | Aplicación para el registro de usuarios a un evento de JavaScript. Los datos ingresados por el usuario se validan antes de ser enviados al servidor para ser procesados. Uso de NodeJS para la creación de una API básica para el procesamiento de datos: creación y lectura.
24 |
25 | ## 5. Juego - Emoji Matching Game
26 |
27 | Juego interactivo para marcar un emoji faltante. El usuario debe seleccionar la imagen faltante para avanzar al siguiente nivel. En caso de fallar en la selección, se termina el juego.
--------------------------------------------------------------------------------