├── img ├── png │ ├── 5.png │ ├── 15.png │ ├── 25.png │ ├── pomodoro.png │ ├── pomodoro_w.png │ ├── readme │ │ ├── figma.png │ │ └── readme_header.png │ ├── bg_dark_pattern.png │ └── bg_light_pattern.png ├── favicon.ico ├── pomodoro.png ├── favicon-16x16.png ├── favicon-32x32.png ├── icons │ ├── tasks_icon.png │ ├── statistics_icon.png │ ├── heart.svg │ ├── credit_card.svg │ ├── cursor.svg │ ├── card_mastercard.svg │ ├── folder_orange.svg │ ├── folder_open_green.svg │ ├── image_blue.svg │ ├── image_dark.svg │ ├── image_purple.svg │ ├── image_yellow.svg │ ├── code_blue.svg │ ├── code_green.svg │ ├── code_purple.svg │ ├── code_yellow.svg │ ├── card_paypal.svg │ ├── file_blue.svg │ ├── file_green.svg │ ├── file_light.svg │ ├── file_purple.svg │ ├── file_yellow.svg │ ├── docs_blue.svg │ ├── docs_green.svg │ ├── docs_orange.svg │ ├── docs_purple.svg │ ├── docs_yellow.svg │ ├── card_visa.svg │ ├── card_amex.svg │ ├── key_dot_dark.svg │ ├── paypal.svg │ ├── key_new.svg │ ├── key_bracket.svg │ ├── card_googlepay.svg │ ├── key_esc_dark.svg │ ├── binary_blue.svg │ ├── binary_green.svg │ ├── binary_orange.svg │ ├── binary_purple.svg │ ├── binary_yellow.svg │ ├── key_pro.svg │ ├── key_del_dark.svg │ ├── key_enter_light.svg │ ├── key_hello_world.svg │ ├── key_directions_dark.svg │ ├── key_paypal.svg │ ├── card_applepay.svg │ ├── key_save.svg │ └── key_soon.svg ├── webp │ ├── achievement.webp │ ├── certificate.webp │ └── avatar_mouredev.webp ├── svg │ ├── check.svg │ ├── delete.svg │ ├── edit.svg │ ├── cursor.svg │ ├── menu.svg │ ├── window_buttons_dark.svg │ ├── window_buttons_light.svg │ ├── mouredevpro_symbol_light.svg │ ├── github.svg │ ├── key_pro.svg │ ├── key_enter_light.svg │ ├── 15.svg │ ├── mouredevpro_light.svg │ ├── 5.svg │ ├── mouredevpro_stroke.svg │ └── 25.svg ├── social │ ├── x.svg │ ├── twitch.svg │ ├── tiktok.svg │ ├── youtube.svg │ ├── linkedin.svg │ ├── instagram.svg │ ├── discord.svg │ ├── github.svg │ ├── github_blue.svg │ ├── github_green.svg │ └── github_yellow.svg └── logo_mouredev │ ├── mouredevpro_symbol_light.svg │ ├── mouredevpro_light.svg │ └── mouredevpro_stroke.svg ├── audio └── oye_como_va.mp3 ├── fonts ├── muli-2 │ ├── Muli-1GDlj.ttf │ ├── MuliBold-YzEVy.ttf │ ├── MuliItalic-Rpvnv.ttf │ ├── MuliLight-lg9VZ.ttf │ ├── MuliSemibold-BW0dG.ttf │ ├── MuliBoldItalic-rgx08.ttf │ ├── MuliExtralight-ywL02.ttf │ ├── MuliLightItalic-nRvDR.ttf │ ├── MuliExtraLightItalic-9YKBZ.ttf │ └── MuliSemiBoldItalic-8MarZ.ttf └── Poppins │ ├── Poppins-Bold.ttf │ ├── Poppins-Thin.ttf │ ├── Poppins-Black.ttf │ ├── Poppins-Italic.ttf │ ├── Poppins-Light.ttf │ ├── Poppins-Medium.ttf │ ├── Poppins-Regular.ttf │ ├── Poppins-ExtraBold.ttf │ ├── Poppins-SemiBold.ttf │ ├── Poppins-BlackItalic.ttf │ ├── Poppins-BoldItalic.ttf │ ├── Poppins-ExtraLight.ttf │ ├── Poppins-LightItalic.ttf │ ├── Poppins-MediumItalic.ttf │ ├── Poppins-ThinItalic.ttf │ ├── Poppins-SemiBoldItalic.ttf │ ├── Poppins-ExtraBoldItalic.ttf │ ├── Poppins-ExtraLightItalic.ttf │ └── OFL.txt ├── script ├── worker.js ├── main.js ├── settings.js ├── data.js ├── dragdrop.js ├── proxy_data.js ├── taskList.js └── pomodoro.js ├── LICENSE ├── css ├── reset.css └── variables.css ├── Estilos.md └── README.md /img/png/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/5.png -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/favicon.ico -------------------------------------------------------------------------------- /img/png/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/15.png -------------------------------------------------------------------------------- /img/png/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/25.png -------------------------------------------------------------------------------- /img/pomodoro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/pomodoro.png -------------------------------------------------------------------------------- /img/png/pomodoro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/pomodoro.png -------------------------------------------------------------------------------- /audio/oye_como_va.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/audio/oye_como_va.mp3 -------------------------------------------------------------------------------- /img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/favicon-16x16.png -------------------------------------------------------------------------------- /img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/favicon-32x32.png -------------------------------------------------------------------------------- /img/png/pomodoro_w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/pomodoro_w.png -------------------------------------------------------------------------------- /img/icons/tasks_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/icons/tasks_icon.png -------------------------------------------------------------------------------- /img/png/readme/figma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/readme/figma.png -------------------------------------------------------------------------------- /img/webp/achievement.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/webp/achievement.webp -------------------------------------------------------------------------------- /img/webp/certificate.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/webp/certificate.webp -------------------------------------------------------------------------------- /fonts/muli-2/Muli-1GDlj.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/Muli-1GDlj.ttf -------------------------------------------------------------------------------- /img/png/bg_dark_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/bg_dark_pattern.png -------------------------------------------------------------------------------- /img/png/bg_light_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/bg_light_pattern.png -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Bold.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Thin.ttf -------------------------------------------------------------------------------- /img/icons/statistics_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/icons/statistics_icon.png -------------------------------------------------------------------------------- /img/webp/avatar_mouredev.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/webp/avatar_mouredev.webp -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Black.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Italic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Light.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Medium.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-Regular.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliBold-YzEVy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliBold-YzEVy.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliItalic-Rpvnv.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliItalic-Rpvnv.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliLight-lg9VZ.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliLight-lg9VZ.ttf -------------------------------------------------------------------------------- /img/png/readme/readme_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/img/png/readme/readme_header.png -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-ExtraBold.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliSemibold-BW0dG.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliSemibold-BW0dG.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliBoldItalic-rgx08.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliBoldItalic-rgx08.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliExtralight-ywL02.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliExtralight-ywL02.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliLightItalic-nRvDR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliLightItalic-nRvDR.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Poppins/Poppins-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/Poppins/Poppins-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliExtraLightItalic-9YKBZ.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliExtraLightItalic-9YKBZ.ttf -------------------------------------------------------------------------------- /fonts/muli-2/MuliSemiBoldItalic-8MarZ.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProyectosWebComunidadMoureDev/PomodoroWeb/HEAD/fonts/muli-2/MuliSemiBoldItalic-8MarZ.ttf -------------------------------------------------------------------------------- /img/svg/check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/svg/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/icons/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/icons/credit_card.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /img/social/x.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/twitch.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/tiktok.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/icons/cursor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /img/svg/cursor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /img/svg/menu.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/youtube.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/linkedin.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/icons/card_mastercard.svg: -------------------------------------------------------------------------------- 1 | 3 | Mastercard 4 | 6 | 8 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /script/worker.js: -------------------------------------------------------------------------------- 1 | let totalSegundos; 2 | 3 | // Recibir el tiempo inicial desde el script principal 4 | self.onmessage = function (e) { 5 | if (e.data.action === "start") { 6 | totalSegundos = e.data.time; 7 | speed = e.data.speed; 8 | runTimer(); 9 | } else if (e.data.action === "pause") { 10 | clearInterval(self.interval); 11 | } 12 | }; 13 | 14 | // Función que maneja el temporizador 15 | function runTimer() { 16 | self.interval = setInterval(() => { 17 | if (totalSegundos > 0) { 18 | totalSegundos--; 19 | self.postMessage({ timeLeft: totalSegundos }); 20 | } else { 21 | clearInterval(self.interval); 22 | self.postMessage({ finished: true }); 23 | } 24 | }, speed); 25 | } -------------------------------------------------------------------------------- /img/icons/folder_orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /img/icons/folder_open_green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 17 | 19 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /img/icons/image_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /img/icons/image_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /img/icons/image_purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /img/icons/image_yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /img/svg/window_buttons_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /img/svg/window_buttons_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /img/icons/code_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /img/icons/code_green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /img/icons/code_purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /img/icons/code_yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /img/icons/card_paypal.svg: -------------------------------------------------------------------------------- 1 | 3 | PayPal 4 | 6 | 8 | 10 | 12 | 14 | -------------------------------------------------------------------------------- /img/icons/file_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/file_green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/file_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/file_purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/file_yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/social/instagram.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/icons/docs_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/docs_green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/docs_orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/docs_purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /img/icons/docs_yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2025] [ProyectosWebComunidadMoureDev](https://github.com/ProyectosWebComunidadMoureDev) 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. -------------------------------------------------------------------------------- /img/social/discord.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /css/reset.css: -------------------------------------------------------------------------------- 1 | /* reset.css */ 2 | *, 3 | *::before, 4 | *::after { 5 | box-sizing: border-box; 6 | line-height: 1.75rem; 7 | text-decoration: none; 8 | color: inherit; 9 | margin: 0; 10 | font-family: var(--font-family-main); 11 | /*font-size: var(--font-size-large);*/ 12 | } 13 | 14 | *::selection { 15 | color: var(--yellow-color); 16 | background: var(--white-color); 17 | text-shadow: 2px 2px var(--bg-color-alt); 18 | } 19 | *:focus-visible { 20 | outline: 5px solid var(--blue-color); 21 | outline-offset: 6px; 22 | } 23 | 24 | body { 25 | background-color: var(--bg-color-main); 26 | color: var(--text-color-main); 27 | } 28 | 29 | main { 30 | display: flex; 31 | flex-direction: column; 32 | justify-content: start; 33 | align-items: center; 34 | width: 100%; 35 | } 36 | 37 | ul, 38 | li { 39 | margin: 0; 40 | padding: 0; 41 | list-style: none; 42 | font-size: var(--font-size-medium); 43 | color: var(--text-color-main); 44 | line-height: 1.5; 45 | justify-content: center; 46 | } 47 | 48 | button { 49 | cursor: pointer; 50 | background-color: transparent; 51 | font-size: var(--font-size-xsmall); 52 | border: 0; 53 | } -------------------------------------------------------------------------------- /script/main.js: -------------------------------------------------------------------------------- 1 | const menuBtn= document.querySelector(".btn-menu"); 2 | const menuContent= document.querySelector(".menu-content"); 3 | 4 | 5 | // Evento click para cerrar el menu al clickar fuera de el 6 | window.document.addEventListener("click",(event)=>{ 7 | if(!menuContent.contains(event.target) && !menuBtn.contains(event.target)){ 8 | closeMenu(); 9 | } 10 | }) 11 | 12 | // Evento para cerrar al presionar la tecla Escape 13 | window.document.addEventListener("keydown",(event)=>{ 14 | if(event.key==="Escape"){ 15 | closeMenu(); 16 | } 17 | }) 18 | // Evento click en el Menu para mostrar y ocultar el contenido 19 | menuBtn.addEventListener("click", ()=>{ 20 | toggleMenu(); 21 | }) 22 | 23 | //Función para abrir el menú 24 | function openMenu(){ 25 | menuContent.classList.add("show"); 26 | //Actualiza el aria-expanded 27 | menuBtn.setAttribute("aria-expanded", "true") 28 | menuContent.focus(); 29 | } 30 | 31 | //Función para cerrar el menú 32 | function closeMenu(){ 33 | menuContent.classList.remove("show"); 34 | menuBtn.setAttribute("aria-expanded", "false") 35 | } 36 | // Función para alternar entre abierto y cerrado 37 | 38 | function toggleMenu(){ 39 | const ariaExpanded = menuBtn.getAttribute('aria-expanded')==="true" 40 | if(ariaExpanded){ 41 | closeMenu(); 42 | }else{ 43 | openMenu(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /img/icons/card_visa.svg: -------------------------------------------------------------------------------- 1 | 3 | Visa 4 | 6 | 8 | 11 | -------------------------------------------------------------------------------- /img/icons/card_amex.svg: -------------------------------------------------------------------------------- 1 | 3 | American Express 4 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | -------------------------------------------------------------------------------- /img/icons/key_dot_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/social/github.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/github_blue.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/github_green.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /img/social/github_yellow.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /css/variables.css: -------------------------------------------------------------------------------- 1 | /* variables.css */ 2 | :root { 3 | --bg-color-main: #1A1A1A; /* Color de Fondo */ 4 | --bg-color-alt: #212121; /* Color de Fondo Secundario */ 5 | --gray-color:#515151; 6 | --blue-color: #3EB0F9; /* Color Azul */ 7 | --green-color: #00aa47; /* Color Verde */ 8 | --red-color: #FF0000; /* Color Rojo */ 9 | --orange-color: #ff5500; /* Color Naranja */ 10 | --yellow-color: #ffa61e; /* Color Amarillo */ 11 | --purple-color: #7D00FE; /* Color Morado */ 12 | --white-color: #f5f5f5; /* Color Blanco */ 13 | --clock_color: #3EB0F9; 14 | 15 | --font-family-main: "Poppins"; 16 | --text-color-main: #f5f5f5; 17 | 18 | --font-size-xxsmall: 0.5rem; 19 | --font-size-xsmall: 0.75rem; 20 | --font-size-small: 0.875rem; 21 | --font-size-medium: 1rem; 22 | --font-size-regular: 1.25rem; 23 | --font-size-large: 1.5rem; 24 | --font-size-xlarge: 1.75rem; 25 | --font-size-xxlarge: 2.5rem; 26 | --font-size-xxxlarge: 4.5rem; 27 | 28 | --segment_width: 45px; 29 | --segment_height: 10px; 30 | --digits_space: 80px; 31 | 32 | /* Breakpoints para Media Queries */ 33 | --breakpoint-xs: 320px; /* Smartphones pequeños */ 34 | --breakpoint-sm: 576px; /* Smartphones medianos */ 35 | --breakpoint-md: 768px; /* Smartphones grandes/tablets pequeñas */ 36 | --breakpoint-lg: 992px; /* Tablets grandes/laptops pequeñas */ 37 | --breakpoint-xl: 1200px; /* Desktops/laptops */ 38 | --breakpoint-xxl: 1400px; /* Pantallas grandes */ 39 | } 40 | -------------------------------------------------------------------------------- /Estilos.md: -------------------------------------------------------------------------------- 1 | # Pomodoro Web 2 | 3 | ## Colores 4 | 5 | |Colores|Variable|Valor| | 6 | |---|---|---|---| 7 | |Color de Fondo|--bg-color-main|#1A1A1A|![png](https://github.com/user-attachments/assets/5dca7bd8-9449-4d6b-aae1-461ee6f2dc31)| 8 | |Color de Fondo Secundario|--bg-color-alt|#212121|![png](https://github.com/user-attachments/assets/6006aab6-30aa-4305-869b-19ce1e27a9ef)| 9 | |Color Azul|--blue-color|#3EB0F9|![png](https://github.com/user-attachments/assets/e4df9a63-de61-4569-8e8f-cc3f0be422af)| 10 | |Color Verde|--green-color|#00aa47ff|![png](https://github.com/user-attachments/assets/15e73ee0-bdea-4dfb-9ace-81a0e65744ee)| 11 | |Color Rojo|--red-color|#FF0000|![png](https://github.com/user-attachments/assets/d5341286-bafc-4367-bc82-2d833f84df84)| 12 | |Color Naranja|--orange-color|#ff5500|![png](https://github.com/user-attachments/assets/16b55385-6e73-4aa1-9859-43e8c56ebf0c)| 13 | |Color Amarillo|--yellow-color|#FFA61E|![png](https://github.com/user-attachments/assets/34515361-454d-4ecc-a176-84d102f6e6e8)| 14 | |Color Morado|--purple-color|#7D00FE|![png](https://github.com/user-attachments/assets/522d19a5-b38d-4cd4-8714-77f392dc7143)| 15 | |Color Blanco|--white-color|#f5f5f5|![png](https://github.com/user-attachments/assets/40d81961-0a04-46ee-8fee-dc441b9423c6)| 16 | 17 | ## Fuentes 18 | 19 | |Elemento|Variable|Valor|Link| 20 | |---|---|---|---| 21 | |Fuente Principal|--font-family-main|"Poppins"|https://fonts.google.com/specimen/Poppins| 22 | |Color de Fuente|--text-color-main|#f5f5f5|![png](https://github.com/user-attachments/assets/40d81961-0a04-46ee-8fee-dc441b9423c6)| 23 | 24 | 25 | # Otros estilos 26 | 27 | |Elemento|Variable|Valor|| 28 | |---|---|---|---| 29 | |Border Radius|--border-radius|15px|| 30 | -------------------------------------------------------------------------------- /img/icons/paypal.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /img/icons/key_new.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 17 | -------------------------------------------------------------------------------- /img/icons/key_bracket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/card_googlepay.svg: -------------------------------------------------------------------------------- 1 | 3 | Google Pay 4 | 6 | 8 | 11 | 14 | 17 | 20 | 23 | -------------------------------------------------------------------------------- /img/svg/mouredevpro_symbol_light.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | 14 | 16 | 17 | 19 | 21 | 23 | 25 | 26 | -------------------------------------------------------------------------------- /img/logo_mouredev/mouredevpro_symbol_light.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | 14 | 16 | 17 | 19 | 21 | 23 | 25 | 26 | -------------------------------------------------------------------------------- /img/icons/key_esc_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /img/svg/github.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /script/settings.js: -------------------------------------------------------------------------------- 1 | import {TIMER_CONFIG, POMODORO, initPomodoro, updatePomodoroModes} from "./pomodoro.js" 2 | import {mySettingsData, checkLocalStorageData, updateSettingsData} from "./data.js" 3 | 4 | const wt_minutes_input = document.getElementById("worktime_minutes"); 5 | const bt_minutes_input = document.getElementById("breaktime_minutes"); 6 | const rt_minutes_input = document.getElementById("resttime_minutes"); 7 | const auto_start = document.getElementById("auto_start"); 8 | const music = document.getElementById("music"); 9 | const rest_interval = document.getElementById("rest_interval"); 10 | 11 | 12 | const btn_settings = document.getElementById("btn_settings"); 13 | const close_btn = document.getElementById("settings-close"); 14 | const save_settings = document.getElementById("save-settings"); 15 | const cancel_settings = document.getElementById("cancel-settings"); 16 | 17 | btn_settings.addEventListener("click", () => { 18 | load_data(); 19 | document.getElementById("settings").style.display = "block"; 20 | }); 21 | 22 | close_btn.addEventListener("click", () => { 23 | close_modal(); 24 | }); 25 | 26 | function close_modal() { 27 | document.getElementById("settings").style.display = "none"; 28 | } 29 | 30 | save_settings.addEventListener("click", () => { 31 | save_data(); 32 | }) 33 | 34 | cancel_settings.addEventListener("click", () => { 35 | close_modal(); 36 | }); 37 | 38 | window.addEventListener("load", () => { 39 | load_data(); 40 | updatePomodoroModes(); 41 | }); 42 | 43 | function save_data() { 44 | TIMER_CONFIG.work.minutes = parseInt(wt_minutes_input.value); 45 | TIMER_CONFIG.break.minutes = parseInt(bt_minutes_input.value); 46 | TIMER_CONFIG.rest.minutes = parseInt(rt_minutes_input.value); 47 | POMODORO.completeCycle = rest_interval.value; 48 | POMODORO.auto_start = auto_start.checked; 49 | POMODORO.music = music.checked; 50 | mySettingsData.workMode = TIMER_CONFIG.work.minutes; 51 | mySettingsData.breakMode = TIMER_CONFIG.break.minutes; 52 | mySettingsData.restMode = TIMER_CONFIG.rest.minutes; 53 | mySettingsData.autoStart = POMODORO.auto_start; 54 | mySettingsData.completeCycle = POMODORO.completeCycle; 55 | mySettingsData.music = POMODORO.music; 56 | close_modal(); 57 | updateSettingsData(); 58 | updatePomodoroModes(); 59 | } 60 | 61 | function load_data() { 62 | checkLocalStorageData(); 63 | TIMER_CONFIG.work.minutes = mySettingsData.workMode; 64 | TIMER_CONFIG.break.minutes = mySettingsData.breakMode; 65 | TIMER_CONFIG.rest.minutes = mySettingsData.restMode; 66 | wt_minutes_input.value = mySettingsData.workMode; 67 | bt_minutes_input.value = mySettingsData.breakMode; 68 | rt_minutes_input.value = mySettingsData.restMode; 69 | rest_interval.value = mySettingsData.completeCycle; 70 | auto_start.checked = mySettingsData.autoStart; 71 | music.checked = mySettingsData.music; 72 | } 73 | -------------------------------------------------------------------------------- /img/icons/binary_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/binary_green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/binary_orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/binary_purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/binary_yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 33 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/icons/key_pro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 38 | 39 | 41 | 43 | 45 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /img/svg/key_pro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 38 | 39 | 41 | 43 | 45 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /img/icons/key_del_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 46 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /script/data.js: -------------------------------------------------------------------------------- 1 | import {updatePomodoroModes} from "./pomodoro.js" 2 | import { POMODORO, TIMER_CONFIG } from "./pomodoro.js"; 3 | 4 | const statisticsData = { 5 | cyclesCompleted: 0, 6 | workTime: { completed: 0, nocompleted: 0 }, 7 | breakTime: { completed: 0, nocompleted: 0 }, 8 | restTime: { completed: 0, nocompleted: 0 }, 9 | tasks: { completed: 0, nocompleted: 0 }, 10 | tasksList: [ 11 | { 12 | taskid:1, 13 | taskName: "Aprender JavaScript", 14 | completed: false, 15 | createdAt: "2025-02-08T12:34:56.789Z", 16 | totalWorkTime: null 17 | } 18 | ] 19 | }; 20 | 21 | const settingsData = { 22 | workMode : 25, 23 | breakMode: 5, 24 | restMode: 15, 25 | autoStart: true, 26 | completeCycle: 4, 27 | music: true 28 | } 29 | 30 | const tasksData = [ 31 | { 32 | taskid:1, 33 | taskName: "Aprender JavaScript", 34 | completed: false, 35 | createdAt: "2025-02-08T12:34:56.789Z", 36 | totalWorkTime: null, 37 | active: true 38 | } 39 | ] 40 | 41 | let myStatisticsData = {} 42 | let mySettingsData = {} 43 | let myTasksData = [] 44 | 45 | 46 | function checkLocalStorageData() { 47 | const localStorageData = JSON.parse(localStorage.getItem('statisticsData')); 48 | const localStorageSettings = JSON.parse(localStorage.getItem('settingsData')); 49 | const localStorageTasks = JSON.parse(localStorage.getItem('tasksData')); 50 | if (!localStorageData) { 51 | console.log("No hay datos statisticsData en el localStorage, se guardan los datos por defecto."); 52 | localStorage.setItem('statisticsData', JSON.stringify(statisticsData)); 53 | } 54 | if (!localStorageSettings) { 55 | console.log("No hay datos settingsData en el localStorage, se guardan los datos por defecto"); 56 | localStorage.setItem('settingsData', JSON.stringify(settingsData)); 57 | } 58 | if (!localStorageTasks) { 59 | console.log("No hay datos tasksData en el localStorage, se guardan los datos por defecto"); 60 | localStorage.setItem('tasksData', JSON.stringify(tasksData)); 61 | } 62 | 63 | myStatisticsData = JSON.parse(localStorage.getItem('statisticsData')); 64 | mySettingsData = JSON.parse(localStorage.getItem('settingsData')); 65 | myTasksData = JSON.parse(localStorage.getItem('tasksData')); 66 | } 67 | 68 | function updateSettingsData() { 69 | localStorage.setItem('settingsData', JSON.stringify(mySettingsData)); 70 | localStorage.setItem('statisticsData', JSON.stringify(myStatisticsData)); 71 | localStorage.setItem('tasksData', JSON.stringify(myTasksData)); 72 | updatePomodoroModes(); 73 | } 74 | 75 | function pomodoroCompleted(mode, completed) { 76 | if (!(mode in TIMER_CONFIG)) { 77 | console.error("El modo no existe"); 78 | return; 79 | } 80 | const modeProperty = mode + "Time"; 81 | if (!completed) { 82 | myStatisticsData[modeProperty].nocompleted += 1; 83 | } else { 84 | myStatisticsData[modeProperty].nocompleted -= 1; 85 | myStatisticsData[modeProperty].completed += 1; 86 | } 87 | updateSettingsData(); 88 | } 89 | 90 | export { myTasksData ,mySettingsData, checkLocalStorageData, updateSettingsData, pomodoroCompleted } 91 | 92 | -------------------------------------------------------------------------------- /img/svg/key_enter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 41 | 43 | 45 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /img/icons/key_enter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 41 | 43 | 45 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /img/svg/15.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 89 | -------------------------------------------------------------------------------- /img/icons/key_hello_world.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 42 | 44 | 46 | 47 | 49 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /img/svg/mouredevpro_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /img/logo_mouredev/mouredevpro_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /img/icons/key_directions_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 55 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 66 | 67 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /img/svg/5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 95 | -------------------------------------------------------------------------------- /img/icons/key_paypal.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /img/svg/mouredevpro_stroke.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 12 | 13 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 29 | 31 | 33 | 35 | 36 | -------------------------------------------------------------------------------- /img/logo_mouredev/mouredevpro_stroke.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 12 | 13 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 29 | 31 | 33 | 35 | 36 | -------------------------------------------------------------------------------- /img/icons/card_applepay.svg: -------------------------------------------------------------------------------- 1 | Apple Pay -------------------------------------------------------------------------------- /img/icons/key_save.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 17 | -------------------------------------------------------------------------------- /fonts/Poppins/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /img/svg/25.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 98 | -------------------------------------------------------------------------------- /img/icons/key_soon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 17 | -------------------------------------------------------------------------------- /script/dragdrop.js: -------------------------------------------------------------------------------- 1 | // Selecciona todos los elementos con la clase draggable 2 | const draggables = document.querySelectorAll('.draggable'); 3 | const tasks_window = document.getElementById("tasks"); 4 | /* const tab_tasks = document.getElementById("tab-tasks"); */ 5 | const draggableWrappers = document.querySelectorAll('.draggable-wrapper'); 6 | const draggable_icons = document.querySelectorAll('.draggable_icon'); 7 | const tasks_screen = document.getElementById("tasks"); 8 | // Añadir tab_tasks_resp para soporte responsive 9 | const tab_tasks_resp = document.getElementById("tab-tasks-resp"); 10 | 11 | // Selecciona el elemento con el id pomodoro_core 12 | const container = document.getElementById('pomodoro_core'); 13 | 14 | // Resetea el zIndex para que la última ventana siempre esté arriba 15 | function resetzIndex() { 16 | const allWindows = [...document.querySelectorAll('.screen')]; 17 | allWindows.forEach((window) => { 18 | window.style.zIndex = 2; 19 | }); 20 | } 21 | 22 | // Por cada elemento draggable de la lista de draggables 23 | draggables.forEach(draggable => { 24 | // Selecciona el header de la ventana 25 | const header = draggable.querySelector('.header'); 26 | 27 | // Botones de control de ventanas 28 | const minimize = draggable.querySelector('.minimize'); 29 | const maximize = draggable.querySelector('.maximize'); 30 | const close = draggable.querySelector('.close'); 31 | // Si tiene header le añadimos el evento mousedown al header 32 | if (header) { 33 | header.addEventListener('mousedown', onMouseDown); 34 | } else { 35 | // En caso contrario el evento controla toda la ventana 36 | draggable.addEventListener('mousedown', onMouseDown); 37 | } 38 | 39 | // Si la ventana tiene botones de control se les añade el evento 40 | if (minimize) { 41 | minimize.addEventListener("click", function () { 42 | draggable.classList.toggle("minimize"); 43 | tasks_window.hidden = true; 44 | /* tab_tasks.hidden = false; */ 45 | tab_tasks_resp.hidden = false; 46 | }); 47 | close.addEventListener("click", function(){ 48 | draggable.classList.toggle("minimize"); 49 | tasks_window.hidden = true; 50 | /* tab_tasks.hidden = false; */ 51 | tab_tasks_resp.hidden = false; 52 | }); 53 | } 54 | 55 | function onMouseDown(event) { 56 | // Prevenir comportamiento por defecto 57 | event.preventDefault(); 58 | 59 | document.addEventListener('mousemove', onMouseMove); 60 | document.addEventListener('mouseup', onMouseUp); 61 | resetzIndex(); 62 | if (draggable.classList.contains("draggable")) { 63 | draggable.style.zIndex = 9; 64 | } 65 | const rect = draggable.getBoundingClientRect(); 66 | const containerRect = container.getBoundingClientRect(); 67 | 68 | const offsetX = event.clientX - rect.left; 69 | const offsetY = event.clientY - rect.top; 70 | 71 | function onMouseMove(event) { 72 | let newX = event.clientX - offsetX; 73 | let newY = event.clientY - offsetY; 74 | 75 | // Corregir límites del contenedor 76 | newX = Math.max(containerRect.left, Math.min(newX, containerRect.right - rect.width)); 77 | newY = Math.max(containerRect.top, Math.min(newY, containerRect.bottom - rect.height)); 78 | 79 | draggable.style.left = `${newX}px`; 80 | draggable.style.top = `${newY}px`; 81 | } 82 | 83 | function onMouseUp() { 84 | document.removeEventListener('mousemove', onMouseMove); 85 | document.removeEventListener('mouseup', onMouseUp); 86 | } 87 | } 88 | 89 | }); 90 | 91 | draggable_icons.forEach(icon => { 92 | let isDragging = false; 93 | let icon_offsetX = 0; 94 | let icon_offsetY = 0; 95 | icon.addEventListener('dblclick', (e) => { 96 | const taskScreen = document.getElementById('tasks'); 97 | taskScreen.classList.remove('minimize'); 98 | tasks_screen.style.display = "block"; 99 | }); 100 | icon.addEventListener('mousedown', (e) => { 101 | e.preventDefault(); 102 | icon.style.cursor = 'grab'; 103 | isDragging = true; 104 | // Calculamos las posiciones iniciales del mouse relativo al contenedor 105 | icon_offsetX = e.clientX - icon.getBoundingClientRect().left; 106 | icon_offsetY = e.clientY - icon.getBoundingClientRect().top; 107 | // Cambiar el cursor durante el arrastre 108 | icon.style.cursor = 'grab'; 109 | }); 110 | // Movimiento del mouse mientras arrastramos 111 | document.addEventListener('mousemove', (e) => { 112 | if (isDragging) { 113 | // Calculamos las nuevas posiciones usando el desplazamiento del mouse 114 | const newX = e.clientX - icon_offsetX; 115 | const newY = e.clientY - icon_offsetY - container.getBoundingClientRect().top - 125; 116 | icon.style.left = `${newX}px`; 117 | icon.style.top = `${newY}px`; 118 | } 119 | }); 120 | 121 | // Cuando se suelta el mouse, detenemos el arrastre 122 | document.addEventListener('mouseup', () => { 123 | if (isDragging) { 124 | isDragging = false; 125 | icon.style.cursor = 'pointer'; // Restauramos el cursor normal 126 | } 127 | }); 128 | }); 129 | 130 | -------------------------------------------------------------------------------- /script/proxy_data.js: -------------------------------------------------------------------------------- 1 | const estatisticsData = { 2 | cyclesCompleted: 0, 3 | workTime: { completed: 0, nocompleted: 0 }, 4 | breakTime: { completed: 0, nocompleted: 0 }, 5 | restTime: { completed: 0, nocompleted: 0 }, 6 | tasks: { completed: 0, nocompleted: 0 }, 7 | tasksList: [ 8 | { 9 | taskid:1, 10 | taskName: "Aprender JavaScript", 11 | completed: false, 12 | createdAt: "2025-02-08T12:34:56.789Z", 13 | totalWorkTime: null 14 | } 15 | ] 16 | }; 17 | 18 | 19 | /** 20 | * observerObjectData convierte un objeto en un Proxy que vigila los accesos y modificaciones. 21 | * - Lanza un error si se accede a una propiedad inexistente. 22 | * - Verifica que el tipo de dato sea correcto antes de modificarlo. 23 | * - Guarda automáticamente los cambios en localStorage. 24 | */ 25 | function observerObjectData(object) { 26 | return new Proxy(object, { 27 | get(object, property) { 28 | // Si la propiedad no existe, lanza un error 29 | if (!(property in object)) { 30 | throw new Error(`La propiedad "${prop}" no existe en initialData`); 31 | } 32 | 33 | let value = object[ property ] 34 | 35 | // Si el valor es un objeto, lo hace observable también (usando recursividad) 36 | 37 | return ((typeof value === "object") && (value !== null)) 38 | ? observerObjectData(value) 39 | : value; 40 | 41 | }, 42 | 43 | // Intercepta cuando se intenta modificar una propiedad del objeto observado 44 | set(object, property, value) { 45 | // Verifica que el tipo de dato sea el mismo que el original 46 | if (typeof object[ property ] !== typeof value) { 47 | throw new Error(`Tipo de dato incorrecto para la propiedad "${property}"`) 48 | } 49 | 50 | // Muestra un mensaje en la consola indicando que se ha actualizado 51 | console.log(`El objeto estatisticsData ha sido actualizado y almacenado en localStorage.`); 52 | 53 | // Asigna el nuevo valor a la propiedad 54 | object[ property ] = value; 55 | 56 | // Guarda los cambios en localStorage 57 | localStorage.setItem('storageData', JSON.stringify(estatisticsData)); 58 | 59 | return true; 60 | }, 61 | }) 62 | } 63 | 64 | /** 65 | * Crea una versión observable de estatisticsData usando observerObjectData. 66 | * - Esto permite detectar accesos y modificaciones en el objeto. 67 | * - Cualquier cambio en proxyData se reflejará en estatisticsData y se guardará en localStorage automáticamente. 68 | * - Es importante trabajar con proxyData en lugar de estatisticsData para mantener la vigilancia activa. 69 | */ 70 | 71 | 72 | const proxyData = observerObjectData(estatisticsData); 73 | 74 | /** 75 | * updateDataFromLocalStorage recupera los datos de localStorage y actualiza estatisticsData. 76 | * - Si hay datos guardados, los parsea y los aplica a estatisticsData. 77 | * - Mantiene la referencia original del objeto para que el Proxy siga funcionando. 78 | * - Actualiza arrays y objetos sin perder propiedades existentes. 79 | */ 80 | function updateDataFromLocalStorage() { 81 | const dataFromStorage = localStorage.getItem('storageData'); 82 | 83 | if (dataFromStorage) { 84 | const parsedData = JSON.parse(dataFromStorage); 85 | 86 | // Actualiza las propiedades de estatisticsData con los datos guardados 87 | for (let key in estatisticsData) { 88 | if (parsedData.hasOwnProperty(key)) { 89 | if (Array.isArray(estatisticsData[ key ])) { 90 | estatisticsData[ key ].length = 0; // Vacía el array 91 | estatisticsData[ key ].push(...parsedData[ key ]); // Copia los nuevos valores 92 | } else if (typeof estatisticsData[ key ] === 'object' && estatisticsData[ key ] !== null) { 93 | Object.assign(estatisticsData[ key ], parsedData[ key ]); // Copia propiedades sin perder la referencia 94 | } else { 95 | estatisticsData[ key ] = parsedData[ key ]; // Reemplaza valores primitivos 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | updateDataFromLocalStorage(estatisticsData); 103 | 104 | function addModeTimeProxyData(modeTime, completed = true, nocompleted = false) { 105 | 106 | if (nocompleted) proxyData[modeTime].nocompleted++; 107 | 108 | (completed) ? proxyData[modeTime].completed++ : proxyData[modeTime].nocompleted--; 109 | 110 | }; 111 | 112 | function addCyclesCompleted() { 113 | proxyData.cyclesCompleted++ 114 | } 115 | 116 | 117 | function addTaskToProxyData(object){ 118 | if (typeof object !== "object"){ 119 | throw new Error('El argumento tiene que ser un objeto'); 120 | } 121 | proxyData.tasksList.push(object) 122 | console.log('Se ha añadido una nueva tarea'); 123 | } 124 | 125 | function deleteTaskProxyData(id){ 126 | const taskIndex = proxyData.tasksList.findIndex(task => task.taskid=== id); 127 | 128 | 129 | if(taskIndex!==-1){ 130 | proxyData.tasksList.splice(taskIndex, 1); 131 | console.log(`Tarea con taskid ${taskid} eliminada.`); 132 | }else{ 133 | console.warn(`Tarea con taskid ${taskid} no encontrada.`); 134 | } 135 | } 136 | 137 | 138 | addModeTimeProxyData("workTime",true); 139 | addModeTimeProxyData(true); 140 | 141 | export{ 142 | 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![Header](img/png/readme/readme_header.png) 3 | # Proyecto Pomodoro Web 4 | Proyecto colaborativo de la comunidad **MoureDev** 5 | 6 | --- 7 | 8 | ## #️⃣ Descripción del Proyecto 9 | Este proyecto colaborativo tiene como objetivo desarrollar una **aplicación web Pomodoro** para gestionar el tiempo de manera eficiente, mejorando la productividad y la concentración mediante la técnica Pomodoro. 10 | 11 | Utilizaremos **HTML**, **CSS** y **JavaScript** como tecnologías base, fomentando el aprendizaje continuo y la colaboración entre desarrolladores de distintos niveles de experiencia, a nivel internacional. 12 | 13 | --- 14 | 15 | ## ❓ ¿Qué es la Técnica Pomodoro? 16 | La **técnica Pomodoro** es un método de gestión del tiempo que divide el trabajo en bloques de **25 minutos de trabajo intenso**, seguidos de **descansos cortos de 5 minutos**. Después de **4 Pomodoros**, se recomienda tomar un descanso más largo de **15 a 30 minutos** para recargar energías. 17 | 18 | Esta técnica es ideal para mejorar la productividad y mantener un equilibrio entre trabajo y descanso. 19 | 20 | --- 21 | 22 | ## 🚀 Objetivo del Proyecto 23 | Crear una **aplicación web interactiva** que permita a los usuarios gestionar su tiempo utilizando la técnica Pomodoro, con funcionalidades clave como: 24 | 25 | - ✅ Temporizador Pomodoro interactivo. 26 | - 📊 Visualización de métricas y estadísticas de productividad. 27 | - 📝 Gestión de tareas y hábitos (en futuras iteraciones). 28 | 29 | --- 30 | 31 | ## 🛠️ Tecnologías y Herramientas Utilizadas 32 | - **HTML5**: Para la estructura del contenido. 33 | - **CSS3**: Para los estilos visuales, diseño y disposición. 34 | - **JavaScript**: Para la funcionalidad interactiva (temporizador, botones, etc.). 35 | 36 | --- 37 | 38 | ## 🎨 Diseño del Proyecto 39 | Dado que todos los colaboradores somos parte de la comunidad de **Brais Moure (@mouredev)**, decidimos emular el estilo visual de la página [MoureDev Pro](https://mouredev.pro/) para mantener coherencia y profesionalismo en el diseño. 40 | 41 | El diseño del proyecto está disponible en **Figma**: 42 | 🔗 [Figma Design](https://www.figma.com/design/EpLTpcJeaTzadVa837k7Fg/Untitled?node-id=1-2&t=NL8yOo005ZTLetNr-1). 43 | 44 | 📸 **Vista Previa del Diseño**: 45 | ![Diseño figma](img/png/readme/figma.png) 46 | 47 | --- 48 | 49 | ## 👩‍💻 ¿Quiénes Pueden Participar? 50 | Este proyecto es **abierto a todos los niveles de experiencia**. ¡Todos son bienvenidos! 51 | 52 | - 🐣 **Principiantes**: Una gran oportunidad para aprender los fundamentos de desarrollo web y trabajar en equipo. 53 | - 🧑‍💻 **Intermedios**: Profundiza tus conocimientos en **JavaScript** y prácticas avanzadas de **CSS**. 54 | - 🦸‍♂️ **Avanzados**: Lidera, guía y ayuda a otros desarrolladores en el proyecto, revisando código y proponiendo mejoras. 55 | 56 | --- 57 | ## 👩‍💻 ¿Quiénes pueden participar? 58 | Todos los niveles de experiencia son bienvenidos. 59 | - **Principiantes**: Perfecto para aprender los fundamentos y trabajar en equipo. 60 | - **Intermedios**: Profundiza en JavaScript y prácticas avanzadas de CSS. 61 | - **Avanzados**: Guía, revisa código y guía a otros desarrolladores. 62 | 63 | --- 64 | 65 | ## ⁉️ ¿Cómo contribuir? 66 | 1️⃣ **Haz un fork del repositorio** para poder realizar cambios en tu propio espacio. Para ello: 67 | - Ve al repositorio original en GitHub: [PomodoroWeb](https://github.com/ProyectosWebComunidadMoureDev/PomodoroWeb) 68 | - Haz clic en el botón **Fork** para crear una copia en tu repositorio en gitHub: 69 | 70 | 71 | [![Fork](https://img.shields.io/github/forks/ProyectosWebComunidadMoureDev/PomodoroWeb?style=social)](https://github.com/ProyectosWebComunidadMoureDev/PomodoroWeb/fork) 72 | 73 | - Esto creará una copia del repositorio en tu cuenta de GitHub. 74 | 75 | 76 | 77 | 2️⃣ **Clona tu fork** y familiarízate con los archivos existentes: 78 | 79 | git clone https://github.com/TU_USUARIO/PomodoroWeb 80 | 81 | 3️⃣ **Contribuye** creando nuevas funcionalidades, mejorando el diseño o refactorizando el código. 82 | 83 | 4️⃣ Confirma y sube tus cambios: 84 | 85 | git commit -m "Descripción breve del cambio" 86 | git push origin main 87 | 88 | 5️⃣ Abre un **Pull Request** desde tu fork hacia el repositorio original para revisión. Para hacerlo: 89 | - Ve a la página de tu fork en GitHub. 90 | - Haz clic en **Compare & pull request**. 91 | - Escribe una descripción clara de los cambios y envía el Pull Request. 92 | 93 | 6️⃣ **Explora las issues** en la sección correspondiente. Busca algo interesante para empezar o crea tu propia issue si detectas algo que mejorar. 94 | 95 | ## 📄 Licencia 96 | 97 | Este proyecto está licenciado bajo la Licencia MIT. Consulta el archivo [LICENSE](LICENSE) para más detalles. 98 | 99 | ## Organización del Trabajo 100 | ### 📁 Carpetas y Archivos 101 | - `index.html`: Archivo principal del proyecto. 102 | - `style.css`: Estilos de la página. 103 | - `script.js`: Funciones y lógica del temporizador. 104 | 105 | ### 📋 Desarrollo por Fases 106 | - **Fase 1**: Maquetación básica con HTML y CSS. 107 | - **Fase 2**: Lógica del temporizador en JavaScript. 108 | - **Fase 3**: Mejoras y personalización (ejemplo: añadir sonidos o estadísticas). 109 | 110 | ## 📌 Recursos Adicionales 111 | - [Documentación de HTML](https://developer.mozilla.org/es/docs/Web/HTML) 112 | - [Documentación de CSS](https://developer.mozilla.org/es/docs/Web/CSS) 113 | - [Documentación de JavaScript](https://developer.mozilla.org/es/docs/Web/JavaScript) 114 | 115 | ## 🌐 Proyectos Paralelos 116 | Este proyecto está acompañado por dos desarrollos paralelos que comparten la misma funcionalidad básica, pero en diferentes plataformas: 117 | 118 | - [Proyecto Swift](https://github.com/kontroldev/Proyecto_1_Pomodoro) 119 | - [Proyecto Kotlin](https://github.com/juanppdev/Proyecto_1_Pomodoro) 120 | --- 121 | ### ¡Colaboremos, aprendamos y divirtámonos desarrollando juntos! 🚀 122 | ¡Gracias por contribuir y formar parte de este proyecto! 💪 123 | -------------------------------------------------------------------------------- /script/taskList.js: -------------------------------------------------------------------------------- 1 | import {myTasksData, checkLocalStorageData, updateSettingsData} from "./data.js" 2 | 3 | /* const tabTasks = document.getElementById('tab-tasks'); */ 4 | const tabTasksresp = document.getElementById('tab-tasks-resp'); 5 | const tasksWindow = document.getElementById('tasks'); 6 | const taskWindowControls = document.getElementById('tasks-window-controls'); 7 | const tasksList = document.getElementById('task-list'); 8 | const moreItems = document.getElementById('more-items'); 9 | const formNewTask = document.getElementById('add-task'); 10 | const formConfirmDeleteTask = document.getElementById('task-deleteConfirmation'); 11 | 12 | let draggedItem = null; 13 | 14 | function loadTasks() { 15 | for (let i = 0; i < myTasksData.length; i++) { 16 | const task = myTasksData[i]; 17 | 18 | console.log(task.active); 19 | if (task.active) { 20 | const item = document.createElement('li'); 21 | item.classList.add("task"); 22 | item.innerHTML = ` 23 | 24 | ${task.taskName} 25 |
26 | 27 | 28 | 29 |
30 | `; 31 | insertTask(item); 32 | } 33 | } 34 | } 35 | 36 | //insert new task 37 | const insertTask = (item) => { 38 | const tasksItems = tasksWindow.querySelectorAll('li'); 39 | // busca la ultima tarea sin terminar para inserta la nueva tarea 40 | let lastNonFinishedTask = null; 41 | for (let i = 0; i < tasksItems.length; i++) { 42 | if (!tasksItems[i].classList.contains('task-finished')) { 43 | lastNonFinishedTask = tasksItems[i]; 44 | } 45 | } 46 | //insertamos la tarea 47 | if (lastNonFinishedTask) { 48 | tasksList.insertBefore(item, lastNonFinishedTask.nextSibling); 49 | } else { 50 | tasksList.appendChild(item); 51 | } 52 | } 53 | 54 | //edit task 55 | const editTask = (form, itemIndex) => { 56 | const itemsArray = Array.from(tasksList.children); 57 | const itemToEdit = itemsArray[itemIndex]; 58 | 59 | itemToEdit.querySelector('.task-text').innerText = form.querySelector('#task-name').value; 60 | 61 | if (form.querySelector('#task-finish').checked) { 62 | itemToEdit.classList.add('task-finished'); 63 | itemToEdit.draggable = false; 64 | } else { 65 | itemToEdit.classList.remove('task-finished'); 66 | itemToEdit.draggable = true; 67 | } 68 | } 69 | 70 | //delete task 71 | const deleteTask = (item) => { 72 | formConfirmDeleteTask.querySelector("#task-delete-name").innerText = item.querySelector(".task-text").innerText; 73 | tasksList.hidden = true; 74 | moreItems.hidden = true; 75 | 76 | formConfirmDeleteTask.addEventListener('click', (e) => { 77 | if (e.target.type === 'button') { 78 | closeForm(formConfirmDeleteTask); 79 | } else if (e.target.type === 'submit') { 80 | e.preventDefault(); 81 | const itemId = parseInt(item.querySelector(".task-id").ariaLabel); 82 | /* myTasksData = myTasksData.filter(task => task.taskid !== itemId); */ 83 | 84 | const index = myTasksData.findIndex(task => task.taskid === itemId); 85 | if (index !== -1) { 86 | myTasksData[index].active = false; 87 | } 88 | 89 | updateSettingsData(); 90 | item.remove(); 91 | closeForm(formConfirmDeleteTask); 92 | } else { 93 | return; 94 | } 95 | }); 96 | } 97 | 98 | //add task 99 | const addNewTask = (form) => { 100 | const tasksItems = tasksWindow.querySelectorAll('li'); 101 | 102 | // se crea el nuevo elemento 103 | const taskName = form.querySelector('#task-name').value.trim(); 104 | const taskStateFinished = form.querySelector('#task-finish').checked; 105 | const finishClass = taskStateFinished ? "task-finished" : ""; 106 | const item = document.createElement('li'); 107 | 108 | console.log(tasksList.getAttribute('data-minimized')); 109 | console.log(tasksItems); 110 | 111 | item.classList.add("task"); 112 | if (tasksList.getAttribute('data-minimized') === "true" && !tasksItems[1].classList.contains('task-finished')) { 113 | item.style.display = 'none'; 114 | } 115 | if (finishClass) { item.classList.add(finishClass) } 116 | item.draggable = taskStateFinished ? false : true; 117 | 118 | item.innerHTML = ` 119 | ${taskName} 120 |
121 | 122 | 123 | 124 |
125 | `; 126 | //insertamos la tarea 127 | insertTask(item); 128 | 129 | // Añadimos eventos drag and drop 130 | item.addEventListener("dragstart", (e) => { 131 | draggedItem = item; 132 | e.dataTransfer.effectAllowed = "move"; 133 | }); 134 | 135 | item.addEventListener("dragover", (e) => { 136 | e.preventDefault(); 137 | }); 138 | 139 | item.addEventListener("drop", (e) => { 140 | e.preventDefault(); 141 | if (draggedItem !== item) { 142 | const draggedIndex = Array.from(tasksList.children).indexOf(draggedItem); 143 | const targetIndex = Array.from(tasksList.children).indexOf(item); 144 | 145 | if (draggedIndex < targetIndex) { 146 | tasksList.insertBefore(draggedItem, item.nextSibling); 147 | } else { 148 | tasksList.insertBefore(draggedItem, item); 149 | } 150 | } 151 | }); 152 | const newTaskId = myTasksData.length > 0 ? myTasksData[myTasksData.length - 1].taskid + 1 : 1; 153 | myTasksData.push({ 154 | taskid: newTaskId, 155 | taskName: taskName, 156 | completed: false, 157 | active: true 158 | }); 159 | updateSettingsData(); 160 | } 161 | 162 | //submit form 163 | const submitAddForm = (form) => { 164 | const itemIndex = form.getAttribute('data-item'); 165 | if (itemIndex === null) { 166 | addNewTask(form); 167 | } else { 168 | editTask(form, itemIndex); 169 | } 170 | closeForm(form); 171 | }; 172 | 173 | //close form 174 | const closeForm = (form) => { 175 | form.removeAttribute('data-submit'); 176 | //form.reset(); 177 | form.hidden = true; 178 | tasksList.hidden = false; 179 | if (tasksList.getAttribute('data-minimized') === "true") { 180 | moreItems.hidden = false; 181 | } 182 | } 183 | 184 | //open form 185 | const openAddForm = (itemToEdit = "") => { 186 | tasksList.hidden = true; 187 | formNewTask.hidden = false; 188 | moreItems.hidden = true 189 | 190 | if (!itemToEdit) { 191 | formNewTask.reset(); 192 | formNewTask.removeAttribute('data-item'); 193 | } else { 194 | const itemsArray = Array.from(tasksList.children); 195 | const itemIndex = itemsArray.indexOf(itemToEdit); 196 | formNewTask.setAttribute('data-item', itemIndex); 197 | formNewTask.querySelector('#task-name').value = itemToEdit.querySelector('.task-text').innerText; 198 | if (itemToEdit.classList.contains('task-finished')) { 199 | formNewTask.querySelector('#task-finish').checked = true; 200 | } 201 | } 202 | } 203 | 204 | // Formulario 205 | const handleFormEvent = (event) => { 206 | const form = event.target.closest('form'); 207 | if (event.target.type === 'checkbox') { 208 | return; 209 | } else if (event.type === 'submit') { 210 | event.preventDefault(); 211 | submitAddForm(form); 212 | } else if (event.target.matches('button[class="form-cancel"]')) { 213 | closeForm(form); 214 | } 215 | } 216 | 217 | // Items de tareas 218 | const handleTaskControl = (e) => { 219 | const itemClicked = e.target.closest('li'); 220 | if (itemClicked.id === 'new-task') { 221 | openAddForm(); 222 | } else { 223 | const taskButton = e.target.closest('button'); 224 | if (!taskButton) { 225 | return 226 | } else if (taskButton.classList.contains('finish')) { 227 | itemClicked.classList.add('task-finished'); 228 | 229 | if (tasksList.getAttribute('data-minimized') === "true") { 230 | itemClicked.style.display = 'none'; 231 | } 232 | } else if (taskButton.classList.contains('delete')) { 233 | tasksList.hidden = true; 234 | formConfirmDeleteTask.hidden = false; 235 | deleteTask(itemClicked); 236 | } else if (taskButton.classList.contains('edit')) { 237 | openAddForm(itemClicked); 238 | } 239 | } 240 | } 241 | 242 | //close Task Window 243 | const closeTaskWindow = () => { 244 | /* tabTasks.hidden = false; */ 245 | tabTasksresp.hidden = false; 246 | tasksWindow.hidden = true; 247 | closeForm(formNewTask); 248 | closeForm(formConfirmDeleteTask); 249 | } 250 | //maximize Task Window 251 | const maximizeTaskWindow = () => { 252 | const tasks = document.querySelectorAll(".task"); 253 | for (let i = 0; i < tasks.length; i++) { 254 | tasks[i].style.display = 'flex'; 255 | } 256 | tasksList.setAttribute('data-minimized', 'false'); 257 | moreItems.hidden = true; 258 | } 259 | //minimize TAsk Window 260 | const minimizeTaskWindow = () => { 261 | const tasks = document.querySelectorAll(".task"); 262 | if (tasks.length > 0) { 263 | const startIndex = tasks[0].classList.contains('task-finished') ? 0 : 1; 264 | for (let i = startIndex; i < tasks.length; i++) { 265 | tasks[i].style.display = 'none'; 266 | } 267 | tasksList.setAttribute('data-minimized', 'true'); 268 | moreItems.hidden = false; 269 | } 270 | } 271 | 272 | // controles de la ventana 273 | const handleWindowControl = (e) => { 274 | const action = e.target.id; 275 | 276 | switch (action) { 277 | case "tasks-close": 278 | closeTaskWindow(); 279 | break; 280 | case "tasks-maximize": 281 | maximizeTaskWindow(); 282 | break; 283 | case "tasks-minimize": 284 | minimizeTaskWindow(); 285 | break; 286 | default: 287 | break; 288 | } 289 | } 290 | 291 | //---------------------// 292 | // EVENT LISTENERS // 293 | //---------------------// 294 | 295 | // Pestaña 296 | 297 | /* tabTasks.addEventListener('click', () => { 298 | tabTasks.hidden = true; 299 | tabTasksresp.hidden = true; 300 | tasksWindow.hidden = false; 301 | }); */ 302 | tabTasksresp.addEventListener('click', () => { 303 | /* tabTasks.hidden = true; */ 304 | tabTasksresp.hidden = true; 305 | tasksWindow.hidden = false; 306 | }); 307 | 308 | taskWindowControls.addEventListener("click", handleWindowControl); 309 | tasksList.addEventListener("click", handleTaskControl); 310 | formNewTask.addEventListener('click', handleFormEvent); 311 | formNewTask.addEventListener('submit', handleFormEvent); 312 | 313 | 314 | window.addEventListener("load", () => { 315 | checkLocalStorageData(); 316 | loadTasks(); 317 | }); -------------------------------------------------------------------------------- /script/pomodoro.js: -------------------------------------------------------------------------------- 1 | import {mySettingsData, pomodoroCompleted} from "./data.js" 2 | /* ************** * OBJETOS ** *********** */ 3 | const TIMER_CONFIG = { 4 | work: { minutes: 25, color: '--blue-color', button: `worktime_btn`, button_resp: `worktime_btn_resp` }, 5 | break: { minutes: 5, color: '--orange-color', button: `breaktime_btn`, button_resp: `breaktime_btn_resp` }, 6 | rest: { minutes: 15, color: '--green-color', button: `resttime_btn`, button_resp: `resttime_btn_resp` } 7 | }; 8 | 9 | const POMODORO = { 10 | mode : "work", 11 | roundsCompleted: 0, 12 | totalSeconds: 0, 13 | remainingTime: 0, 14 | completeCycle: 4, 15 | speed_clock: 1000, 16 | speed_miniclock: 200, 17 | auto_start: true, 18 | music: true, 19 | isRunning: false 20 | }; 21 | 22 | 23 | 24 | 25 | const CIRCLES = { 26 | work: { circle: null, complete: null, circumference: 0 }, 27 | break: { circle: null, complete: null, circumference: 0 }, 28 | rest: { circle: null, complete: null, circumference: 0 } 29 | }; 30 | 31 | const WORKERS = { 32 | worker: { 33 | time: () => POMODORO.remainingTime, 34 | speed: POMODORO.speed_clock, 35 | instance: null, 36 | onMessage: (e) => { 37 | if (e.data.finished) { 38 | timerComplete(); 39 | } else { 40 | POMODORO.remainingTime = e.data.timeLeft; 41 | setProgress((e.data.timeLeft*100)/POMODORO.totalSeconds); 42 | formatClock(e.data.timeLeft); 43 | } 44 | } 45 | }, 46 | miniworker: { 47 | time: () => POMODORO.remainingTime * (POMODORO.speed_clock / POMODORO.speed_miniclock), 48 | speed: POMODORO.speed_miniclock, 49 | instance: null, 50 | onMessage: (e) => { 51 | if (!e.data.finished) { 52 | const circle = CIRCLES[POMODORO.mode].complete; 53 | if (circle) { 54 | circle.style.strokeDashoffset = (POMODORO.remainingTime + e.data.timeLeft); 55 | } 56 | POMODORO.remainingTime = e.data.timeLeft/(POMODORO.speed_clock / POMODORO.speed_miniclock); 57 | setProgress((POMODORO.remainingTime*100)/POMODORO.totalSeconds); 58 | } 59 | } 60 | } 61 | }; 62 | 63 | 64 | // Segmentos del display 65 | const NUMBERS = { 66 | 0: "1110111", 67 | 1: "0010010", 68 | 2: "1011101", 69 | 3: "1011011", 70 | 4: "0111010", 71 | 5: "1101011", 72 | 6: "1101111", 73 | 7: "1010010", 74 | 8: "1111111", 75 | 9: "1111011" 76 | }; 77 | /* ************** * OBJETOS ** *********** */ 78 | 79 | /* DOM */ 80 | /* ************** * CIRCULAR PROGRESS ** *********** */ 81 | const TIMER = document.getElementById('timer'); 82 | // Selecciona cada uno de los círculos 83 | const circle = document.querySelector(".progress-ring__circle"); 84 | const outcircle = document.querySelector(".out-ring__circle"); 85 | const incircle = document.querySelector(".in-ring__circle"); 86 | 87 | CIRCLES.work.circle = document.querySelector(".worktime_circle"); 88 | CIRCLES.work.complete = document.getElementById("wt_complete_circle"); 89 | CIRCLES.break.circle = document.querySelector(".breaktime_circle"); 90 | CIRCLES.break.complete = document.getElementById("bt_complete_circle"); 91 | CIRCLES.rest.circle = document.querySelector(".resttime_circle"); 92 | CIRCLES.rest.complete = document.getElementById("rt_complete_circle"); 93 | 94 | const wt_circle = document.querySelector(".worktime_circle"); 95 | const wt_complete_circle = document.getElementById("wt_complete_circle"); 96 | const bt_circle = document.querySelector(".breaktime_circle"); 97 | const bt_complete_circle = document.getElementById("bt_complete_circle"); 98 | const rt_circle = document.querySelector(".resttime_circle"); 99 | const rt_complete_circle = document.getElementById("rt_complete_circle"); 100 | 101 | 102 | // Radio del círculo 103 | const radius = circle.r.baseVal.value; 104 | const inradius = incircle.r.baseVal.value; 105 | const outradius = outcircle.r.baseVal.value; 106 | // Radio del círculo 107 | const wt_radius = wt_circle.r.baseVal.value; 108 | const bt_radius = bt_circle.r.baseVal.value; 109 | const rt_radius = rt_circle.r.baseVal.value; 110 | const wt_circumference = 2 * Math.PI * wt_radius; 111 | const bt_circumference = 2 * Math.PI * bt_radius; 112 | const rt_circumference = 2 * Math.PI * rt_radius; 113 | wt_circle.style.strokeDasharray = `${wt_circumference} ${wt_circumference}`; 114 | bt_circle.style.strokeDasharray = `${bt_circumference} ${bt_circumference}`; 115 | rt_circle.style.strokeDasharray = `${rt_circumference} ${rt_circumference}`; 116 | 117 | // Perímetro del círculo (longitud del trazo) 118 | const circumference = 2 * Math.PI * radius; 119 | const incircumference = 2 * Math.PI * inradius; 120 | const outcircumference = 2 * Math.PI * outradius; 121 | 122 | circle.style.strokeDashoffset = circumference; 123 | incircle.style.strokeDashoffset = incircumference; 124 | outcircle.style.strokeDashoffset = outcircumference; 125 | 126 | const inoffset = incircumference - 1 * incircumference; 127 | incircle.style.strokeDashoffset = inoffset; 128 | 129 | const outoffset = outcircumference - 1 * outcircumference; 130 | outcircle.style.strokeDashoffset = outoffset; 131 | 132 | // Establecer el perímetro como stroke-dasharray 133 | circle.style.strokeDasharray = `${circumference} ${circumference}`; 134 | incircle.style.strokeDasharray = `${incircumference} ${incircumference}`; 135 | outcircle.style.strokeDasharray = `${outcircumference} ${outcircumference}`; 136 | 137 | /* ************** ** SEGMENTS ** **************** */ 138 | const segments = document.querySelectorAll('#clock span'); 139 | const separator = document.getElementById("separator"); 140 | const separator_two = document.getElementById("separator_two"); 141 | /* ************** ** POMODORO MODES ** **************** */ 142 | const worktime_btn = document.getElementById(TIMER_CONFIG.work.button); 143 | const worktime_btn_resp = document.getElementById(TIMER_CONFIG.work.button_resp); 144 | const breaktime_btn = document.getElementById(TIMER_CONFIG.break.button); 145 | const breaktime_btn_resp = document.getElementById(TIMER_CONFIG.break.button_resp); 146 | const resttime_btn = document.getElementById(TIMER_CONFIG.rest.button); 147 | const resttime_btn_resp = document.getElementById(TIMER_CONFIG.rest.button_resp); 148 | /* ************** ** POMODORO BUTTONS ** **************** */ 149 | const start_btn = document.getElementById("startbutton"); 150 | const pause_btn = document.getElementById("pausebutton"); 151 | const clock = document.getElementById("clock"); 152 | // ALARMA 153 | const ALARM_WARNING = document.getElementById('alarm'); 154 | /* DOM */ 155 | 156 | /* ************** * VARIABLES ** *********** */ 157 | let worker = null; 158 | let miniworker = null; 159 | 160 | // Minutos y Segundos del timer 161 | let pomodoroMins = 0; 162 | let pomodoroSecs = 0; 163 | let totalSeconds = 0; 164 | let leftSeconds = 0; 165 | 166 | /* ************** * VARIABLES ** *********** */ 167 | 168 | /* ************** ** LISTENERS ** **************** */ 169 | worktime_btn.addEventListener("click", function () {setMode("work");}); 170 | worktime_btn_resp.addEventListener("click", function () {setMode("work");}); 171 | breaktime_btn.addEventListener("click", function () {setMode("break");}); 172 | breaktime_btn_resp.addEventListener("click", function () {setMode("break");}); 173 | resttime_btn.addEventListener("click", function () {setMode("rest");}); 174 | resttime_btn_resp.addEventListener("click", function () {setMode("rest");}); 175 | start_btn.addEventListener("click", function () {startPomodoro();}); 176 | pause_btn.addEventListener("click", function () {pausePomodoro();}); 177 | TIMER.addEventListener('mouseover', function () {stopAlarm();}); 178 | /* ************** ** LISTENERS ** **************** */ 179 | 180 | /* ************** ** FUNCTIONS ** **************** */ 181 | function setMode(mode) { 182 | stopWorker(); 183 | if (!(mode in TIMER_CONFIG)) { 184 | console.error("Modo inválido"); 185 | return; 186 | } 187 | POMODORO.mode = mode; 188 | POMODORO.totalSeconds = TIMER_CONFIG[POMODORO.mode].minutes * 60; 189 | POMODORO.remainingTime = POMODORO.totalSeconds; 190 | desactiveButton(start_btn); 191 | desactiveButton(pause_btn); 192 | pause_btn.disabled = true; 193 | changeColor(TIMER_CONFIG[mode].color); 194 | formatClock(POMODORO.totalSeconds); 195 | setProgress(100); 196 | } 197 | 198 | function desactiveButton(btn) { 199 | btn.classList.remove("active"); 200 | btn.disabled = false; 201 | } 202 | function activeButton(btn) { 203 | btn.classList.add("active"); 204 | btn.disabled = true; 205 | } 206 | function formatClock(seconds) { 207 | let pomodoroMins = Math.floor(seconds / 60); 208 | let pomodoroSecs = seconds % 60; 209 | let minutesStr = pomodoroMins.toString().padStart(2, '0'); 210 | let secondsStr = pomodoroSecs.toString().padStart(2, '0'); 211 | updateclock(minutesStr[0], minutesStr[1], secondsStr[0], secondsStr[1]); 212 | blink_separators(); 213 | } 214 | function changeColor(colorVar) { 215 | let color = `var(${colorVar})`; 216 | [circle, outcircle, incircle].forEach(el => el.style.stroke = color); 217 | [separator, separator_two].forEach(el => el.style.backgroundColor = color); 218 | segments.forEach(el => { el.style.backgroundColor = color; }); 219 | [worktime_btn, breaktime_btn, resttime_btn, worktime_btn_resp, breaktime_btn_resp, resttime_btn_resp].forEach(btn => { 220 | btn.style.border = `1px solid var(--gray-color)`; 221 | }); 222 | const activeButtons = { 223 | 'work': [worktime_btn, worktime_btn_resp], 224 | 'break': [breaktime_btn, breaktime_btn_resp], 225 | 'rest': [resttime_btn, resttime_btn_resp] 226 | }[POMODORO.mode]; 227 | if (activeButtons) { 228 | activeButtons.forEach(btn => { 229 | btn.style.border = `3px solid ${color}`; 230 | }); 231 | } 232 | } 233 | function startPomodoro() { 234 | if (POMODORO.mode in TIMER_CONFIG) { 235 | pomodoroCompleted(POMODORO.mode, false); 236 | } 237 | activeButton(start_btn); 238 | desactiveButton(pause_btn); 239 | stopWorker(); 240 | startWorkers(); 241 | POMODORO.isRunning = true; 242 | } 243 | function pausePomodoro() { 244 | activeButton(pause_btn); 245 | desactiveButton(start_btn); 246 | stopWorker(); 247 | POMODORO.isRunning = false; 248 | } 249 | function timerComplete() { 250 | stopWorker(); 251 | POMODORO.isRunning = false; 252 | POMODORO.mode === "work" && POMODORO.roundsCompleted++; 253 | playAlarm(); 254 | pomodoroCompleted(POMODORO.mode, true); 255 | if (POMODORO.roundsCompleted > 0 && POMODORO.roundsCompleted % POMODORO.completeCycle === 0) { 256 | setMode("rest"); 257 | POMODORO.roundsCompleted = 0; 258 | } else { 259 | if (POMODORO.mode == "work") { 260 | setMode("break"); 261 | if (POMODORO.auto_start) { 262 | setTimeout(() => { 263 | startPomodoro(); 264 | }, 2000); 265 | } 266 | } else { 267 | setMode("work"); 268 | } 269 | } 270 | } 271 | function startWorkers() { 272 | POMODORO.remainingTime = TIMER_CONFIG[POMODORO.mode].minutes * 60; 273 | Object.entries(WORKERS).forEach(([key, config]) => { 274 | config.instance = new Worker("./script/worker.js"); 275 | config.instance.onmessage = config.onMessage; 276 | config.instance.postMessage({ 277 | action: "start", 278 | time: config.time(), 279 | speed: config.speed 280 | }); 281 | }); 282 | }; 283 | function stopWorker() { 284 | Object.values(WORKERS).forEach(config => { 285 | if (config.instance) { 286 | config.instance.terminate(); 287 | config.instance = null; 288 | } 289 | }); 290 | } 291 | function updateclock(m, mm, s, ss) { 292 | draw_number(parseInt(m), "first_minutes"); 293 | draw_number(parseInt(mm), "second_minutes"); 294 | draw_number(parseInt(s), "first_seconds"); 295 | draw_number(parseInt(ss), "second_seconds"); 296 | } 297 | function draw_number(number, id) { 298 | const segments = NUMBERS[number]; 299 | const digit = document.getElementById(id); 300 | const elements = digit.querySelectorAll('span'); 301 | elements.forEach((element, index) => { 302 | if (segments[index] == 0) { 303 | element.classList.add("novisible"); 304 | } else { 305 | element.classList.remove("novisible"); 306 | } 307 | }); 308 | } 309 | function blink_separators() { 310 | const opacity = POMODORO.remainingTime % 2 ? 0.6 : 1; 311 | [separator, separator_two].forEach(el => el.style.opacity = opacity); 312 | } 313 | function setProgress(percentage) { 314 | const offset = (percentage / 100) * circumference; 315 | circle.style.strokeDashoffset = offset; 316 | } 317 | 318 | function blockModes() { 319 | worktime_btn.disabled = true; 320 | breaktime_btn.disabled = true; 321 | resttime_btn.disabled = true; 322 | } 323 | 324 | function unlockModes() { 325 | worktime_btn.disabled = false; 326 | breaktime_btn.disabled = false; 327 | resttime_btn.disabled = false; 328 | } 329 | function playAlarm() { 330 | if (POMODORO.music) { 331 | ALARM_WARNING.play(); 332 | } 333 | } 334 | function stopAlarm() { 335 | ALARM_WARNING.pause(); 336 | ALARM_WARNING.currentTime = 0; 337 | } 338 | /* ************** ** FUNCTIONS ** **************** */ 339 | 340 | window.addEventListener("load", () => { 341 | setMode("work"); 342 | }); 343 | function initPomodoro() { 344 | setProgress(100); 345 | setCircleOffsets('work', wt_circle, wt_complete_circle, wt_circumference); 346 | setCircleOffsets('break', bt_circle, bt_complete_circle, bt_circumference); 347 | setCircleOffsets('rest', rt_circle, rt_complete_circle, rt_circumference); 348 | POMODORO.totalSeconds = TIMER_CONFIG[POMODORO.mode].minutes * 60; 349 | } 350 | 351 | function updatePomodoroModes() { 352 | initPomodoro(); 353 | ['work', 'break', 'rest'].forEach(mode => { 354 | const element = document.getElementById(`${mode}time_mins`); 355 | element.innerText = TIMER_CONFIG[mode].minutes.toString().padStart(2, '0'); 356 | }); 357 | formatClock(TIMER_CONFIG[POMODORO.mode].minutes*60); 358 | } 359 | 360 | function setCircleOffsets(mode, circle, complete_circle, circumference) { 361 | const minutes = TIMER_CONFIG[mode].minutes; 362 | const offset = (minutes / 60) * circumference; 363 | circle.style.strokeDashoffset = circumference - offset; 364 | complete_circle.style.strokeDashoffset = minutes * 60 * (1 + POMODORO.speed_clock/POMODORO.speed_miniclock); 365 | } 366 | 367 | 368 | export {TIMER_CONFIG, POMODORO, initPomodoro, updatePomodoroModes}; --------------------------------------------------------------------------------