├── .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 | 23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 | 34 |
35 |
36 | 37 |
38 |
39 |
40 | 41 |
    42 | 43 |
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 | 52 |
    53 |
    54 | ${tarea.tarea} 55 |
    56 |
    57 | 58 | 59 | 60 |
    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 | 20 | 21 |
    22 |
    23 | 24 | ¡Lo sentimos! Su navegador no provee soporte para canvas. 25 | 26 |
    27 | 28 |
    29 |
    30 | 31 | 32 |
    33 | 34 |
    35 | 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    43 | 44 | Descargar Meme 45 |
    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 | 35 | 36 |
    37 |

    Datos Asistencia

    38 |
    39 |

    Los datos se están cargando...

    40 | Cargando... 41 |
    42 | 45 | 50 | 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 | 39 | 40 |
    41 |
    42 |

    JavaScript - Evento

    43 |
    44 |
    45 | 46 | 47 |
    48 |
    49 | 50 | 51 |
    52 |
    53 | 54 | 55 |
    56 |
    57 | 58 | 59 |
    60 |
    61 | 62 | 68 |
    69 |
    70 | 71 |
    72 |
    73 | 75 |
    76 |
    77 | 78 |
    79 |
    80 | 81 |
    82 |
    83 |
    84 |
    85 | 86 | 87 |
    88 | 89 | 90 |
    91 |
    92 |
    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 | 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 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    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. --------------------------------------------------------------------------------