diff --git a/Assets/logo-devmagic.png b/Assets/logo-devmagic.png new file mode 100644 index 0000000..eb48a1a Binary files /dev/null and b/Assets/logo-devmagic.png differ diff --git a/project/index.html b/project/index.html new file mode 100644 index 0000000..f705e65 --- /dev/null +++ b/project/index.html @@ -0,0 +1,61 @@ + + + + + + + DevMagic + + + + +
+ + DevMagic + +
+
+
+
+

Encurte seus links.

+

Links são longos. Encurte os links que você deseja compartilhar, e acompanhe enquanto viajam através da internet.

+
+ + + +
+
+
+
+
+

TOP 5

+ +
+
+
+
+
+

HITS

+ 35.731.571 +

Cliques em links

+
+
+
+ + + \ No newline at end of file diff --git a/project/scripts/landing.js b/project/scripts/landing.js new file mode 100644 index 0000000..5ac835e --- /dev/null +++ b/project/scripts/landing.js @@ -0,0 +1,18 @@ +//Import other JS files used for landing page +import {fetchUrls} from './topFive.js'; +import {changeForm, verifyButtonAction} from './shortener.js'; + +//Prevents form from submiting +document.querySelector('#shorten-form').addEventListener("submit", ev => {ev.preventDefault()}); + +//Verifies button condition on clicking +document.querySelector('#btn-link').addEventListener("click", verifyButtonAction); + +//Erases shortened link +document.querySelector('#btn-cancel').addEventListener("click", function(){changeForm("shorten")}); + +//Functions activated on page loading +window.addEventListener("load", function(){ + changeForm("shorten"); + fetchUrls(); +}); diff --git a/project/scripts/shortener.js b/project/scripts/shortener.js new file mode 100644 index 0000000..e30eddc --- /dev/null +++ b/project/scripts/shortener.js @@ -0,0 +1,111 @@ +//Validates if a given string is a valid URL, demands full path +function validateURL(link) +{ + let url; + try { + url = new URL(link); + } catch (_) { + return false; + } + + return (url.protocol === "http:" || url.protocol === "https:"); +} + +//Makes a shortened link, based on example's structure +function makeShortLink(link) +{ + const alphabet = "abcdefghijklmnopqrstuvwxyz"; + link = "http://chr.dc/"; + for(let x = 0; x < 6; x++) + { + link += alphabet[Math.floor(Math.random() * alphabet.length)]; + } + return link; +} + +//Adds animation to the form +function animateFade() +{ + let formItems = document.querySelectorAll('.shortener-form input, .shortener-form button'); + removeFade(); + for(let item of formItems) + { + //#btn-cancel receives partial fading, other receive total fading + item.classList.add(item.id == "btn-cancel" ? "animate-fade-partial" : "animate-fade-total"); + } +} + +//Removes animation from the form +function removeFade() +{ + let formItems = document.querySelectorAll('.shortener-form input, .shortener-form button'); + for(let item of formItems) + { + item.className = ''; + } +} + +//Changes input and button text and actions for the received action +export function changeForm(action) +{ + let txtLink = document.querySelector('#txt-link'); + let btnLink = document.querySelector('#btn-link'); + let btnCancel = document.querySelector('#btn-cancel'); + if(action === "shorten") + { + txtLink.placeholder = "Cole o seu link aqui"; + txtLink.value = ""; + btnCancel.style.display = "none"; + btnLink.innerText = "ENCURTAR"; + btnLink.dataset.action = "shorten"; + } + else if(action === "copy") + { + txtLink.placeholder = ""; + btnLink.innerText = "COPIAR"; + btnLink.dataset.action = "copy"; + btnCancel.style.display = "initial"; + setTimeout(removeFade, 500); + } +} + +//Gets a link, then returns the shortened version +function shortenLink() +{ + let link = document.querySelector('#txt-link'); + if(validateURL(link.value)) + { + animateFade(); + setTimeout(() => { + link.value = makeShortLink(link.value); + changeForm("copy"); + }, 500); + } + else + { + link.value = ""; + link.placeholder = "Insira um link válido e completo!"; + } +} + +//Copies shortened link to the user's clipboard +function copyLink() +{ + let link = document.querySelector('#txt-link').value; + navigator.clipboard.writeText(link) + .then(changeForm("shorten")); +} + +//Check if the button is ready for shorten or for copying +export function verifyButtonAction() +{ + let btnLink = document.querySelector('#btn-link'); + if(btnLink.dataset.action === "shorten") + { + shortenLink(); + } + else if(btnLink.dataset.action === "copy") + { + copyLink(); + } +} \ No newline at end of file diff --git a/project/scripts/topFive.js b/project/scripts/topFive.js new file mode 100644 index 0000000..79e99c4 --- /dev/null +++ b/project/scripts/topFive.js @@ -0,0 +1,51 @@ +//Add the top 5 urls to the page +function listTopFive(tops) +{ + let list = document.querySelector('.top-link-list'); + list.innerHTML = ""; + for(let top of tops) + { + list.innerHTML += ` +
+

${top.shortUrl}

+

${top.hits}

+
+ `; + } +} + +//Selects the top 5 urls by hits +function fillTopFive(allURLs) +{ + let top = []; + let qtTop = 0; + while(qtTop < 5 && allURLs.length >= 5) + { + for(let url of allURLs) + { + if(url === allURLs[0]) + { + top.push(url); + } + else + { + if(url.hits > top[qtTop].hits) + { + top[qtTop] = url; + } + } + } + allURLs.splice(allURLs.lastIndexOf(top[qtTop]), 1); + qtTop++; + } + //console.log(top); + listTopFive(top); +} + +//Fetches url array +export function fetchUrls() +{ + fetch('../../Assets/urls.json') + .then(resp => {return resp.json()}) + .then(data => {fillTopFive(data)}); +} diff --git a/project/styles/animations.css b/project/styles/animations.css new file mode 100644 index 0000000..de0e245 --- /dev/null +++ b/project/styles/animations.css @@ -0,0 +1,34 @@ +/*Fade animation*/ +/*Fading in the middle of animation*/ +@keyframes fade-total{ + 50%{ + color: rgba(0, 0, 0, 0); + } + + 100%{ + color: rgba(1); + } +} + +/*Fade-in from the start*/ +@keyframes fade-partial{ + 0%{ + color: rgba(0, 0, 0, 0); + } + + 100%{ + color: rgba(1); + } +} + +.animate-fade-total{ + animation-name: fade-total; + animation-duration: 1000ms; + animation-fill-mode: backwards; +} + +.animate-fade-partial{ + animation-name: fade-partial; + animation-duration: 1000ms; + animation-fill-mode: backwards; +} \ No newline at end of file diff --git a/project/styles/landing.css b/project/styles/landing.css new file mode 100644 index 0000000..df8181b --- /dev/null +++ b/project/styles/landing.css @@ -0,0 +1,368 @@ +@import url('./main.css'); +@import url('./animations.css'); + +body{ + display: flex; + flex-direction: column; +} + +header, footer{ + width: 100vw; + background-color: var(--color-white); +} + +/*Header style*/ +header{ + display: flex; + flex-direction: row; + padding: 1rem 0; +} + +header a{ + margin: auto; + width: 12rem; +} + +header a img{ + width: 100%; +} + +/*Content display*/ +main{ + width: 100vw; +} + +.shortener-container, .top-five-container, .hits-container{ + width: 100%; +} + +/*Shortener area*/ +.shortener-container{ + background-image: url('/Assets/background-home.jpg'); + background-repeat: no-repeat; + background-attachment: fixed; + background-position: center; + display: flex; + flex-direction: row; +} + +.shortener-content{ + width: 50%; + margin: 8rem auto; + text-align: center; +} + +.shortener-content h1{ + font-size: 2.25rem; + color: var(--color-white); + font-family: 'Roboto Slab'; +} + +.shortener-content p{ + font-size: 1rem; + color: var(--color-white); + font-family: 'Roboto'; + margin-top: 1rem; +} + +.shortener-form{ + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + margin-top: 4rem; +} + +.shortener-form input, .shortener-form button{ + outline: none; + border: none; + font-family: 'Roboto'; + font-size: 1rem; +} + +.shortener-form input{ + width: 73%; + background-color: transparent; + border-bottom: var(--color-link-shortener) solid 2px; + color: var(--color-link-shortener); + padding: 0.5rem; +} + +.shortener-form input::placeholder{ + color: var(--color-link-shortener); +} + +.shortener-form button +{ + cursor: pointer; +} + +.shortener-form button#btn-cancel +{ + margin-left: -2rem; + background-color: transparent; + color: var(--color-white); + width: 1rem; + height: 1rem; + text-align: center; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.shortener-form button:not(#btn-cancel){ + width: 25%; + min-width: 4rem; + color: var(--color-white); + background-color: var(--color-link-shortener); + padding: 0.5rem; +} + +/*Top 5 area*/ +.top-five-container{ + background-color: var(--color-white); + display: flex; + flex-direction: row; + position: relative; +} + +.top-five-content{ + margin: 3rem auto; + display: flex; + flex-direction: column; + width: 50%; +} + +.top-five-content h2{ + font-family: 'Roboto Slab'; + font-size: 2rem; + text-align: center; + color: var(--color-red); + margin-bottom: 2rem; +} + +.top-link-list div{ + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.top-link-list p{ + font-size: 1rem; + font-family: 'Roboto'; + margin: 0.5rem 0; +} + +.top-link-list p a{ + text-decoration: none; + color: var(--color-red); + text-align: left; + font-weight: bold; +} + +.top-link-list p + p{ + text-align: right; + color: var(--color-support-text); +} + +.top-link-list div + div{ + border-top: var(--color-line-background) solid 1px; + padding-top: 0.4rem; +} + +/*Triangle arrow*/ +.triangle{ + border-top: var(--color-white) solid 2rem; + border-bottom: transparent solid 2rem; + border-left: transparent solid 3rem; + border-right: transparent solid 3rem; + height: 0; + width: 0; + background: none; + position: absolute; + bottom: -4rem; + left: 50%; + margin-left: -3rem; +} + +/*Hits area*/ +.hits-container{ + background-color: var(--color-line-background); + display: flex; + flex-direction: row; +} + +.hits-content{ + margin: 3rem auto; + display: flex; + flex-direction: column; + text-align: center; + align-items: center; +} + +.hits-content h2, .hits-content span{ + color: var(--color-red); + font-size: 2rem; +} + +.hits-content h2{ + margin-bottom: 2rem; + font-family: 'Roboto Slab'; +} + +.hits-content span, .hits-content p{ + font-family: 'Roboto'; +} + +.hits-content span{ + background-color: var(--color-white); + padding: 0.5rem; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.hits-content p{ + color: var(--color-support-text); + font-size: 1rem; + margin-top: 0.8rem; +} + +/*Footer style*/ +footer{ + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 1rem 20vw; +} + +footer .icon-Chaordic{ + width: 6rem; +} + +footer .icon-Chaordic a{ + width: 100%; +} + +footer .icon-Chaordic a img{ + width: 100%; +} + +footer .icon-Facebook-Twitter{ + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 6rem; +} + +.icon-Facebook-Twitter a{ + width: 2rem; +} + +.icon-Facebook-Twitter a img{ + width: 100%; +} + +@media screen and (max-width: 768px) { + /*Header style*/ + header a{ + width: 8rem; + } + + /*Content display*/ + /*Shortener area*/ + .shortener-content{ + width: 80%; + margin: 5.33 auto; + } + + .shortener-content h1{ + font-size: 1.5rem; + } + + .shortener-content p{ + font-size: 0.66rem; + margin-top: 0.66rem; + } + + .shortener-form{ + margin-top: 2.66rem; + } + + .shortener-form input, .shortener-form button{ + font-size: 0.66rem; + } + + .shortener-form button#btn-cancel + { + margin-left: -1.33rem; + width: 0.66rem; + height: 0.66rem; + } + + /*Top 5 area*/ + .top-five-content{ + width: 80%; + } + + .top-five-content h2{ + font-size: 1.33rem; + } + + .top-link-list p{ + font-size: 0.66rem; + } + + .top-link-list div + div{ + padding-top: initial; + } + + /*Triangle arrow*/ + .triangle{ + border-top: var(--color-white) solid 1.33rem; + border-bottom: transparent solid 1.33rem; + border-left: transparent solid 2rem; + border-right: transparent solid 2rem; + height: 0; + width: 0; + background: none; + position: absolute; + bottom: -2.66rem; + margin-left: -2rem; + } + + /*Hits area*/ + .hits-content h2, .hits-content span{ + font-size: 1.33rem; + } + + .hits-content h2{ + margin-bottom: 1.33rem; + } + + .hits-content p{ + font-size: 0.66rem; + margin-top: 0.53; + } + + /*Footer style*/ + footer{ + padding: 1rem; + } + + .icon-Chaordic a, .icon-Facebook-Twitter{ + width: 4rem; + } + + .icon-Facebook-Twitter{ + justify-content: space-around; + } + + .icon-Facebook-Twitter a{ + width: 1.8rem; + } +} \ No newline at end of file diff --git a/project/styles/main.css b/project/styles/main.css new file mode 100644 index 0000000..8b51301 --- /dev/null +++ b/project/styles/main.css @@ -0,0 +1,22 @@ +/*Fonts Roboto and Roboto Slab imported*/ +@import url('https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab&display=swap'); + +/*Definition of color variables*/ +:root{ + --color-red: #AA1423; + --color-support-text: #777; + --color-line-background: #EEE; + --color-white: #FFF; + --color-link-shortener: #FF6E14; +} + +*{ + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body{ + width: 100vw; + min-height: 100vh; +} \ No newline at end of file